tradedangerous 10.17.0__py3-none-any.whl → 11.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 (74) hide show
  1. tradedangerous/__init__.py +4 -4
  2. tradedangerous/cache.py +178 -142
  3. tradedangerous/cli.py +2 -7
  4. tradedangerous/commands/TEMPLATE.py +1 -2
  5. tradedangerous/commands/__init__.py +2 -4
  6. tradedangerous/commands/buildcache_cmd.py +6 -11
  7. tradedangerous/commands/buy_cmd.py +11 -12
  8. tradedangerous/commands/commandenv.py +16 -15
  9. tradedangerous/commands/exceptions.py +6 -4
  10. tradedangerous/commands/export_cmd.py +2 -4
  11. tradedangerous/commands/import_cmd.py +3 -5
  12. tradedangerous/commands/local_cmd.py +16 -25
  13. tradedangerous/commands/market_cmd.py +9 -8
  14. tradedangerous/commands/nav_cmd.py +17 -25
  15. tradedangerous/commands/olddata_cmd.py +9 -15
  16. tradedangerous/commands/parsing.py +9 -6
  17. tradedangerous/commands/rares_cmd.py +9 -10
  18. tradedangerous/commands/run_cmd.py +25 -26
  19. tradedangerous/commands/sell_cmd.py +9 -9
  20. tradedangerous/commands/shipvendor_cmd.py +4 -7
  21. tradedangerous/commands/station_cmd.py +8 -14
  22. tradedangerous/commands/trade_cmd.py +5 -10
  23. tradedangerous/commands/update_cmd.py +10 -7
  24. tradedangerous/commands/update_gui.py +1 -3
  25. tradedangerous/corrections.py +1 -3
  26. tradedangerous/csvexport.py +8 -8
  27. tradedangerous/edscupdate.py +4 -6
  28. tradedangerous/edsmupdate.py +4 -4
  29. tradedangerous/formatting.py +53 -40
  30. tradedangerous/fs.py +6 -6
  31. tradedangerous/gui.py +53 -62
  32. tradedangerous/jsonprices.py +8 -16
  33. tradedangerous/mapping.py +4 -3
  34. tradedangerous/mfd/__init__.py +2 -4
  35. tradedangerous/mfd/saitek/__init__.py +0 -1
  36. tradedangerous/mfd/saitek/directoutput.py +8 -11
  37. tradedangerous/mfd/saitek/x52pro.py +5 -7
  38. tradedangerous/misc/checkpricebounds.py +2 -3
  39. tradedangerous/misc/clipboard.py +2 -3
  40. tradedangerous/misc/coord64.py +2 -1
  41. tradedangerous/misc/derp-sentinel.py +1 -1
  42. tradedangerous/misc/diff-system-csvs.py +3 -0
  43. tradedangerous/misc/eddb.py +1 -3
  44. tradedangerous/misc/eddn.py +2 -2
  45. tradedangerous/misc/edsc.py +7 -14
  46. tradedangerous/misc/edsm.py +1 -8
  47. tradedangerous/misc/importeddbstats.py +2 -1
  48. tradedangerous/misc/prices-json-exp.py +7 -5
  49. tradedangerous/misc/progress.py +2 -2
  50. tradedangerous/plugins/__init__.py +2 -2
  51. tradedangerous/plugins/edapi_plug.py +13 -19
  52. tradedangerous/plugins/edcd_plug.py +4 -5
  53. tradedangerous/plugins/eddblink_plug.py +11 -15
  54. tradedangerous/plugins/edmc_batch_plug.py +3 -5
  55. tradedangerous/plugins/journal_plug.py +2 -1
  56. tradedangerous/plugins/netlog_plug.py +5 -5
  57. tradedangerous/plugins/spansh_plug.py +394 -170
  58. tradedangerous/prices.py +19 -20
  59. tradedangerous/submit-distances.py +3 -8
  60. tradedangerous/templates/TradeDangerous.sql +305 -306
  61. tradedangerous/trade.py +12 -5
  62. tradedangerous/tradecalc.py +30 -34
  63. tradedangerous/tradedb.py +140 -206
  64. tradedangerous/tradeenv.py +143 -69
  65. tradedangerous/tradegui.py +4 -2
  66. tradedangerous/transfers.py +23 -20
  67. tradedangerous/version.py +1 -1
  68. {tradedangerous-10.17.0.dist-info → tradedangerous-11.0.1.dist-info}/METADATA +2 -2
  69. tradedangerous-11.0.1.dist-info/RECORD +79 -0
  70. tradedangerous-10.17.0.dist-info/RECORD +0 -79
  71. {tradedangerous-10.17.0.dist-info → tradedangerous-11.0.1.dist-info}/LICENSE +0 -0
  72. {tradedangerous-10.17.0.dist-info → tradedangerous-11.0.1.dist-info}/WHEEL +0 -0
  73. {tradedangerous-10.17.0.dist-info → tradedangerous-11.0.1.dist-info}/entry_points.txt +0 -0
  74. {tradedangerous-10.17.0.dist-info → tradedangerous-11.0.1.dist-info}/top_level.txt +0 -0
@@ -10,14 +10,14 @@
10
10
  # --------------------------------------------------------------------
11
11
 
12
12
  """
13
- TradeDangerous is set of powerful trading tools for Elite Dangerous,
13
+ TradeDangerous is a set of powerful trading tools for Elite Dangerous,
14
14
  organized around one of the most powerful trade run optimizers available.
15
15
 
16
16
  The TRO is a heavy hitter that can calculate complex routes with multiple stops
17
17
  while taking into account the profits you make along the route
18
- The price data in TradeDangerous is either manually entered or crowd sourced
18
+ The price data in TradeDangerous is either manually entered or crowd-sourced
19
19
  from a website such as [Tromador's Trading Dangerously](http://elite.ripz.org "Tromador's Trading Dangerously"), often using a plugin such as the included eddblink.
20
20
  """
21
- from .version import *
21
+ from .version import * # noqa: F401, F403
22
22
 
23
- from .tradeenv import TradeEnv
23
+ from .tradeenv import TradeEnv # noqa: F401
tradedangerous/cache.py CHANGED
@@ -20,18 +20,27 @@
20
20
  # TODO: Split prices into per-system or per-station files so that
21
21
  # we can tell how old data for a specific system is.
22
22
 
23
- from collections import namedtuple
24
- from pathlib import Path
25
- from .tradeexcept import TradeException
23
+ from __future__ import annotations
26
24
 
27
- from . import corrections, utils
25
+ from pathlib import Path
28
26
  import csv
29
- import math
30
27
  import os
31
- from . import prices
32
28
  import re
33
29
  import sqlite3
34
30
  import sys
31
+ import typing
32
+
33
+ from .tradeexcept import TradeException
34
+ from . import corrections, utils
35
+ from . import prices
36
+
37
+
38
+ # For mypy/pylint type checking
39
+ if typing.TYPE_CHECKING:
40
+ from typing import Any, Optional, TextIO
41
+
42
+ from .tradeenv import TradeEnv
43
+
35
44
 
36
45
  ######################################################################
37
46
  # Regular expression patterns. Here be draegons.
@@ -108,18 +117,14 @@ class BuildCacheBaseException(TradeException):
108
117
  error Description of the error
109
118
  """
110
119
 
111
- def __init__(self, fromFile, lineNo, error = None):
120
+ def __init__(self, fromFile: Path, lineNo: int, error: str = None) -> None:
112
121
  self.fileName = fromFile.name
113
122
  self.lineNo = lineNo
114
123
  self.category = "ERROR"
115
124
  self.error = error or "UNKNOWN ERROR"
116
125
 
117
- def __str__(self):
118
- return "{}:{} {} {}".format(
119
- self.fileName, self.lineNo,
120
- self.category,
121
- self.error,
122
- )
126
+ def __str__(self) -> str:
127
+ return f'{self.fileName}:{self.lineNo} {self.category} {self.error}'
123
128
 
124
129
 
125
130
  class UnknownSystemError(BuildCacheBaseException):
@@ -127,9 +132,8 @@ class UnknownSystemError(BuildCacheBaseException):
127
132
  Raised when the file contains an unknown star name.
128
133
  """
129
134
 
130
- def __init__(self, fromFile, lineNo, key):
131
- error = 'Unrecognized SYSTEM: "{}"'.format(key)
132
- super().__init__(fromFile, lineNo, error)
135
+ def __init__(self, fromFile: Path, lineNo: int, key: str) -> None:
136
+ super().__init__(fromFile, lineNo, f'Unrecognized SYSTEM: "{key}"')
133
137
 
134
138
 
135
139
  class UnknownStationError(BuildCacheBaseException):
@@ -137,9 +141,8 @@ class UnknownStationError(BuildCacheBaseException):
137
141
  Raised when the file contains an unknown star/station name.
138
142
  """
139
143
 
140
- def __init__(self, fromFile, lineNo, key):
141
- error = 'Unrecognized STAR/Station: "{}"'.format(key)
142
- super().__init__(fromFile, lineNo, error)
144
+ def __init__(self, fromFile: Path, lineNo: int, key: str) -> None:
145
+ super().__init__(fromFile, lineNo, f'Unrecognized STAR/Station: "{key}"')
143
146
 
144
147
 
145
148
  class UnknownItemError(BuildCacheBaseException):
@@ -149,9 +152,8 @@ class UnknownItemError(BuildCacheBaseException):
149
152
  itemName Key we tried to look up.
150
153
  """
151
154
 
152
- def __init__(self, fromFile, lineNo, itemName):
153
- error = 'Unrecognized item name: "{}"'.format(itemName)
154
- super().__init__(fromFile, lineNo, error)
155
+ def __init__(self, fromFile: Path, lineNo: int, itemName: str) -> None:
156
+ super().__init__(fromFile, lineNo, f'Unrecognized item name: "{itemName}"')
155
157
 
156
158
 
157
159
  class DuplicateKeyError(BuildCacheBaseException):
@@ -159,14 +161,9 @@ class DuplicateKeyError(BuildCacheBaseException):
159
161
  Raised when an item is being redefined.
160
162
  """
161
163
 
162
- def __init__(self, fromFile, lineNo, keyType, keyValue, prevLineNo):
164
+ def __init__(self, fromFile: Path, lineNo: int, keyType: str, keyValue: str, prevLineNo: int) -> None:
163
165
  super().__init__(fromFile, lineNo,
164
- "Second occurrance of {keytype} \"{keyval}\", "
165
- "previous entry at line {prev}.".format(
166
- keytype = keyType,
167
- keyval = keyValue,
168
- prev = prevLineNo
169
- ))
166
+ f'Second occurrance of {keyType} "{keyValue}", previous entry at line {prevLineNo}.')
170
167
 
171
168
 
172
169
  class DeletedKeyError(BuildCacheBaseException):
@@ -175,11 +172,11 @@ class DeletedKeyError(BuildCacheBaseException):
175
172
  corrections file.
176
173
  """
177
174
 
178
- def __init__(self, fromFile, lineNo, keyType, keyValue):
179
- super().__init__(fromFile, lineNo,
180
- "{} '{}' is marked as DELETED and should not be used.".format(
181
- keyType, keyValue
182
- ))
175
+ def __init__(self, fromFile: Path, lineNo: int, keyType: str, keyValue: str) -> None:
176
+ super().__init__(
177
+ fromFile, lineNo,
178
+ f'{keyType} "{keyValue}" is marked as DELETED and should not be used.'
179
+ )
183
180
 
184
181
 
185
182
  class DeprecatedKeyError(BuildCacheBaseException):
@@ -188,25 +185,24 @@ class DeprecatedKeyError(BuildCacheBaseException):
188
185
  name should not appear in the .csv file.
189
186
  """
190
187
 
191
- def __init__(self, fromFile, lineNo, keyType, keyValue, newValue):
192
- super().__init__(fromFile, lineNo,
193
- "{} '{}' is deprecated "
194
- "and should be replaced with '{}'.".format(
195
- keyType, keyValue, newValue
196
- ))
188
+ def __init__(self, fromFile: Path, lineNo: int, keyType: str, keyValue: str, newValue: str) -> None:
189
+ super().__init__(
190
+ fromFile, lineNo,
191
+ f'{keyType} "{keyValue}" is deprecated and should be replaced with "{newValue}".'
192
+ )
197
193
 
198
194
 
199
195
  class MultipleStationEntriesError(DuplicateKeyError):
200
196
  """ Raised when a station appears multiple times in the same file. """
201
197
 
202
- def __init__(self, fromFile, lineNo, facility, prevLineNo):
198
+ def __init__(self, fromFile: Path, lineNo: int, facility: str, prevLineNo: int) -> None:
203
199
  super().__init__(fromFile, lineNo, 'station', facility, prevLineNo)
204
200
 
205
201
 
206
202
  class MultipleItemEntriesError(DuplicateKeyError):
207
203
  """ Raised when one item appears multiple times in the same station. """
208
204
 
209
- def __init__(self, fromFile, lineNo, item, prevLineNo):
205
+ def __init__(self, fromFile: Path, lineNo: int, item: str, prevLineNo: int) -> None:
210
206
  super().__init__(fromFile, lineNo, 'item', item, prevLineNo)
211
207
 
212
208
 
@@ -218,9 +214,8 @@ class SyntaxError(BuildCacheBaseException):
218
214
  text Offending text
219
215
  """
220
216
 
221
- def __init__(self, fromFile, lineNo, problem, text):
222
- error = "{},\ngot: '{}'.".format(problem, text.strip())
223
- super().__init__(fromFile, lineNo, error)
217
+ def __init__(self, fromFile: Path, lineNo: int, problem: str, text: str) -> None:
218
+ super().__init__(fromFile, lineNo, f'{problem},\ngot: "{text.strip()}".')
224
219
 
225
220
 
226
221
  class SupplyError(BuildCacheBaseException):
@@ -228,51 +223,80 @@ class SupplyError(BuildCacheBaseException):
228
223
  Raised when a supply field is incorrectly formatted.
229
224
  """
230
225
 
231
- def __init__(self, fromFile, lineNo, category, problem, value):
232
- error = "Invalid {} supply value: {}. Got: {}". \
233
- format(category, problem, value)
234
- super().__init__(fromFile, lineNo, error)
226
+ def __init__(self, fromFile: Path, lineNo: int, category: str, problem: str, value: Any) -> None:
227
+ super().__init__(fromFile, lineNo, f'Invalid {category} supply value: {problem}. Got: {value}')
228
+
235
229
 
236
230
  ######################################################################
237
231
  # Helpers
238
232
 
239
233
 
240
- def parseSupply(pricesFile, lineNo, category, reading):
234
+ # supply/demand levels are one of '?' for unknown, 'L', 'M' or 'H'
235
+ # for low, medium, or high. We turn these into integer values for
236
+ # ordering convenience, and we include both upper and lower-case
237
+ # so we don't have to sweat ordering.
238
+ #
239
+ SUPPLY_LEVEL_VALUES = {
240
+ '?': -1,
241
+ 'L': 1, 'l': 1,
242
+ 'M': 2, 'm': 2,
243
+ 'H': 3, 'h': 3,
244
+ }
245
+
246
+
247
+ def parseSupply(pricesFile: Path, lineNo: int, category: str, reading: str) -> tuple[int, int]:
248
+ """ Parse a supply specifier which is expected to be in the <number><?, L, M, or H>, and
249
+ returns the units as an integer and a numeric level value suitable for ordering,
250
+ such that ? = -1, L/l = 0, M/m = 1, H/h = 2 """
251
+
252
+ # supply_level <- digit+ level;
253
+ # digit <- [0-9];
254
+ # level <- Unknown / Low / Medium / High;
255
+ # Unknown <- '?';
256
+ # Low <- 'L';
257
+ # Medium <- 'M';
258
+ # High <- 'H';
259
+ if reading == '?':
260
+ return -1, -1
261
+ elif reading == '-':
262
+ return 0, 0
263
+
264
+ # extract the left most digits into unit and the last character into the level reading.
241
265
  units, level = reading[0:-1], reading[-1]
242
- levelNo = "??LMH".find(level.upper()) - 1
243
- if levelNo < -1:
266
+
267
+ # Extract the right most character as the "level" and look up its numeric value.
268
+ levelNo = SUPPLY_LEVEL_VALUES.get(level)
269
+ if levelNo is None:
244
270
  raise SupplyError(
245
271
  pricesFile, lineNo, category, reading,
246
- 'Unrecognized level suffix: "{}": '
247
- "expected one of 'L', 'M', 'H' or '?'".format(
248
- level
249
- )
272
+ f'Unrecognized level suffix: "{level}": expected one of "L", "M", "H" or "?"'
250
273
  )
274
+
275
+ # Expecting a numeric value in units, e.g. 123? -> (units=123, level=?)
251
276
  try:
252
277
  unitsNo = int(units)
253
278
  if unitsNo < 0:
254
- raise ValueError("unsigned unit count")
255
- if unitsNo == 0:
256
- return 0, 0
257
- return unitsNo, levelNo
279
+ # Use the same code-path as if the units fail to parse.
280
+ raise ValueError('negative unit count')
258
281
  except ValueError:
259
- pass
260
-
261
- raise SupplyError(
262
- pricesFile, lineNo, category, reading,
263
- 'Unrecognized units/level value: "{}": '
264
- "expected '-', '?', or a number followed "
265
- "by a level (L, M, H or ?).".format(
266
- level
267
- )
268
- )
282
+ raise SupplyError(
283
+ pricesFile, lineNo, category, reading,
284
+ f'Unrecognized units/level value: "{level}": expected "-", "?", or a number followed by a level (L, M, H or ?).'
285
+ ) from None # don't forward the exception itself
286
+
287
+ # Normalize the units and level when there are no units.
288
+ if unitsNo == 0:
289
+ return 0, 0
290
+
291
+ return unitsNo, levelNo
292
+
269
293
 
270
294
  ######################################################################
271
295
  # Code
272
296
  ######################################################################
273
297
 
274
298
 
275
- def getSystemByNameIndex(cur):
299
+ def getSystemByNameIndex(cur: sqlite3.Cursor) -> dict[str, int]:
276
300
  """ Build station index in STAR/Station notation """
277
301
  cur.execute("""
278
302
  SELECT system_id, UPPER(system.name)
@@ -281,7 +305,7 @@ def getSystemByNameIndex(cur):
281
305
  return { name: ID for (ID, name) in cur }
282
306
 
283
307
 
284
- def getStationByNameIndex(cur):
308
+ def getStationByNameIndex(cur: sqlite3.Cursor) -> dict[str, int]:
285
309
  """ Build station index in STAR/Station notation """
286
310
  cur.execute("""
287
311
  SELECT station_id,
@@ -293,7 +317,7 @@ def getStationByNameIndex(cur):
293
317
  return { name.upper(): ID for (ID, name) in cur }
294
318
 
295
319
 
296
- def getItemByNameIndex(cur):
320
+ def getItemByNameIndex(cur: sqlite3.Cursor) -> dict[str, int]:
297
321
  """
298
322
  Generate item name index.
299
323
  """
@@ -301,10 +325,37 @@ def getItemByNameIndex(cur):
301
325
  return { name: itemID for (itemID, name) in cur }
302
326
 
303
327
 
304
- def processPrices(tdenv, priceFile, db, defaultZero):
328
+ # The return type of process prices is complicated, should probably have been a type
329
+ # in its own right. I'm going to define some aliases to try and persuade IDEs to be
330
+ # more helpful about what it is trying to return.
331
+ if typing.TYPE_CHECKING:
332
+ # A list of the IDs of stations that were modified so they can be updated
333
+ ProcessedStationIds= tuple[tuple[int]]
334
+ ProcessedItem = tuple[
335
+ int, # station ID
336
+ int, # item ID
337
+ Optional[int | float |str], # modified
338
+ int, # demandCR
339
+ int, # demandUnits
340
+ int, # demandLevel
341
+ int, # supplyCr
342
+ int, # supplyUnits
343
+ int, # supplyLevel
344
+ ]
345
+ ProcessedItems = list[ProcessedItem]
346
+ ZeroItems = list[tuple[int, int]] # stationID, itemID
347
+
348
+
349
+ def processPrices(tdenv: TradeEnv, priceFile: Path, db: sqlite3.Connection, defaultZero: bool) -> tuple[ProcessedStationIds, ProcessedItems, ZeroItems, int, int, int, int]:
305
350
  """
306
351
  Yields SQL for populating the database with prices
307
352
  by reading the file handle for price lines.
353
+
354
+ :param tdenv: The environment we're working in
355
+ :param priceFile: File to read
356
+ :param db: SQLite3 database to write to
357
+ :param defaultZero: Whether to create default zero-availability/-demand records for data that's not present
358
+ (if this is a partial update, you don't want this to be False)
308
359
  """
309
360
 
310
361
  DEBUG0, DEBUG1 = tdenv.DEBUG0, tdenv.DEBUG1
@@ -340,20 +391,18 @@ def processPrices(tdenv, priceFile, db, defaultZero):
340
391
  processedSystems = set()
341
392
  processedItems = {}
342
393
  stationItemDates = {}
343
- itemPrefix = ""
344
394
  DELETED = corrections.DELETED
345
- items, zeros, buys, sells = [], [], [], []
395
+ items, zeros = [], []
346
396
 
347
397
  lineNo, localAdd = 0, 0
348
398
  if not ignoreUnknown:
349
-
350
- def ignoreOrWarn(error):
399
+ def ignoreOrWarn(error: Exception) -> None:
351
400
  raise error
352
401
 
353
402
  elif not quiet:
354
403
  ignoreOrWarn = tdenv.WARN
355
404
 
356
- def changeStation(matches):
405
+ def changeStation(matches: re.Match) -> None:
357
406
  nonlocal facility, stationID
358
407
  nonlocal processedStations, processedItems, localAdd
359
408
  nonlocal stationItemDates
@@ -363,20 +412,21 @@ def processPrices(tdenv, priceFile, db, defaultZero):
363
412
  systemNameIn, stationNameIn = matches.group(1, 2)
364
413
  systemName, stationName = systemNameIn.upper(), stationNameIn.upper()
365
414
  corrected = False
366
- facility = "/".join((systemName, stationName))
415
+ facility = f'{systemName}/{stationName}'
367
416
 
368
417
  # Make sure it's valid.
369
418
  stationID = DELETED
370
- newID = stationByName.get(facility, -1)
419
+ newID = stationByName.get(facility, -1) # why -1 and not None?
371
420
  DEBUG0("Selected station: {}, ID={}", facility, newID)
372
421
  if newID is DELETED:
373
422
  DEBUG1("DELETED Station: {}", facility)
374
423
  return
424
+
375
425
  if newID < 0:
376
426
  if utils.checkForOcrDerp(tdenv, systemName, stationName):
377
427
  return
378
428
  corrected = True
379
- altName = sysCorrections.get(systemName, None)
429
+ altName = sysCorrections.get(systemName)
380
430
  if altName is DELETED:
381
431
  DEBUG1("DELETED System: {}", facility)
382
432
  return
@@ -384,21 +434,22 @@ def processPrices(tdenv, priceFile, db, defaultZero):
384
434
  DEBUG1("SYSTEM '{}' renamed '{}'", systemName, altName)
385
435
  systemName, facility = altName, "/".join((altName, stationName))
386
436
 
387
- systemID = systemByName.get(systemName, -1)
437
+ systemID = systemByName.get(systemName, -1) # why -1 and not None?
388
438
  if systemID < 0:
389
439
  ignoreOrWarn(
390
440
  UnknownSystemError(priceFile, lineNo, facility)
391
441
  )
392
442
  return
393
443
 
394
- altStation = stnCorrections.get(facility, None)
395
- if altStation is DELETED:
396
- DEBUG1("DELETED Station: {}", facility)
397
- return
444
+ altStation = stnCorrections.get(facility)
398
445
  if altStation:
446
+ if altStation is DELETED:
447
+ DEBUG1("DELETED Station: {}", facility)
448
+ return
449
+
399
450
  DEBUG1("Station '{}' renamed '{}'", facility, altStation)
400
451
  stationName = altStation.upper()
401
- facility = "/".join((systemName, stationName))
452
+ facility = f'{systemName}/{stationName}'
402
453
 
403
454
  newID = stationByName.get(facility, -1)
404
455
  if newID is DELETED:
@@ -409,7 +460,7 @@ def processPrices(tdenv, priceFile, db, defaultZero):
409
460
  if not ignoreUnknown:
410
461
  DEBUG0(f'Key value: "{list(stationByName.keys())[list(stationByName.values()).index(128893178)]}"')
411
462
  ignoreOrWarn(
412
- UnknownStationError(priceFile, lineNo, facility)
463
+ UnknownStationError(priceFile, lineNo, facility)
413
464
  )
414
465
  return
415
466
  name = utils.titleFixup(stationName)
@@ -430,8 +481,8 @@ def processPrices(tdenv, priceFile, db, defaultZero):
430
481
  """, [systemID, name])
431
482
  newID = inscur.lastrowid
432
483
  stationByName[facility] = newID
433
- tdenv.NOTE("Added local station placeholder for {} (#{})",
434
- facility, newID
484
+ tdenv.NOTE(
485
+ "Added local station placeholder for {} (#{})", facility, newID
435
486
  )
436
487
  localAdd += 1
437
488
  elif newID in processedStations:
@@ -452,7 +503,7 @@ def processPrices(tdenv, priceFile, db, defaultZero):
452
503
  FROM StationItem
453
504
  WHERE station_id = ?
454
505
  """, [stationID])
455
- stationItemDates = {ID: modified for ID, modified in cur}
506
+ stationItemDates = dict(cur)
456
507
 
457
508
  addItem, addZero = items.append, zeros.append
458
509
  getItemID = itemByName.get
@@ -496,7 +547,7 @@ def processPrices(tdenv, priceFile, db, defaultZero):
496
547
  ignoreOrWarn(
497
548
  MultipleItemEntriesError(
498
549
  priceFile, lineNo,
499
- "{}".format(itemName),
550
+ f'{itemName}',
500
551
  processedItems[itemID]
501
552
  )
502
553
  )
@@ -515,26 +566,16 @@ def processPrices(tdenv, priceFile, db, defaultZero):
515
566
  else:
516
567
  newItems += 1
517
568
  if demandString:
518
- if demandString == "?":
519
- demandUnits, demandLevel = -1, -1
520
- elif demandString == "-":
521
- demandUnits, demandLevel = 0, 0
522
- else:
523
- demandUnits, demandLevel = parseSupply(
524
- priceFile, lineNo, 'demand', demandString
525
- )
569
+ demandUnits, demandLevel = parseSupply(
570
+ priceFile, lineNo, 'demand', demandString
571
+ )
526
572
  else:
527
573
  demandUnits, demandLevel = defaultUnits, defaultLevel
528
574
 
529
575
  if demandString and supplyString:
530
- if supplyString == "?":
531
- supplyUnits, supplyLevel = -1, -1
532
- elif supplyString == "-":
533
- supplyUnits, supplyLevel = 0, 0
534
- else:
535
- supplyUnits, supplyLevel = parseSupply(
536
- priceFile, lineNo, 'supply', supplyString
537
- )
576
+ supplyUnits, supplyLevel = parseSupply(
577
+ priceFile, lineNo, 'supply', supplyString
578
+ )
538
579
  else:
539
580
  supplyUnits, supplyLevel = defaultUnits, defaultLevel
540
581
 
@@ -552,32 +593,24 @@ def processPrices(tdenv, priceFile, db, defaultZero):
552
593
  space_cleanup = re.compile(r'\s{2,}').sub
553
594
  for line in priceFile:
554
595
  lineNo += 1
555
- text, _, comment = line.partition('#')
556
- text = text.strip()
557
- # text = space_cleanup(text, ' ').strip()
596
+
597
+ text = line.split('#', 1)[0] # Discard comments
598
+ text = space_cleanup(' ', text).strip() # Remove leading/trailing whitespace, reduce multi-spaces
558
599
  if not text:
559
600
  continue
560
601
 
561
- # replace whitespace with single spaces
562
- if text.find(" "):
563
- # http://stackoverflow.com/questions/2077897
564
- text = ' '.join(text.split())
565
-
566
602
  ########################################
567
603
  # ## "@ STAR/Station" lines.
568
604
  if text.startswith('@'):
569
605
  matches = systemStationRe.match(text)
570
606
  if not matches:
571
- raise SyntaxError("Unrecognized '@' line: {}".format(# pylint: disable=no-value-for-parameter
572
- text
573
- ))
607
+ raise SyntaxError(priceFile, lineNo, "Unrecognized '@' line", text)
574
608
  changeStation(matches)
575
609
  continue
576
610
 
577
611
  if not stationID:
578
612
  # Need a station to process any other type of line.
579
- raise SyntaxError(priceFile, lineNo,
580
- "Expecting '@ SYSTEM / Station' line", text)
613
+ raise SyntaxError(priceFile, lineNo, "Expecting '@ SYSTEM / Station' line", text)
581
614
  if stationID == DELETED:
582
615
  # Ignore all values from a deleted station/system.
583
616
  continue
@@ -592,8 +625,7 @@ def processPrices(tdenv, priceFile, db, defaultZero):
592
625
  # ## "Item sell buy ..." lines.
593
626
  matches = newItemPriceRe.match(text)
594
627
  if not matches:
595
- raise SyntaxError(priceFile, lineNo,
596
- "Unrecognized line/syntax", text)
628
+ raise SyntaxError(priceFile, lineNo, "Unrecognized line/syntax", text)
597
629
 
598
630
  processItemLine(matches)
599
631
 
@@ -610,13 +642,14 @@ def processPrices(tdenv, priceFile, db, defaultZero):
610
642
  stations = tuple((ID,) for ID in processedStations.keys())
611
643
  return stations, items, zeros, newItems, updtItems, ignItems, numSys
612
644
 
645
+
613
646
  ######################################################################
614
647
 
615
648
 
616
- def processPricesFile(tdenv, db, pricesPath, pricesFh = None, defaultZero = False):
649
+ def processPricesFile(tdenv: TradeEnv, db: sqlite3.Connection, pricesPath: Path, pricesFh: Optional[TextIO] = None, defaultZero: bool = False) -> None:
617
650
  tdenv.DEBUG0("Processing Prices file '{}'", pricesPath)
618
651
 
619
- with pricesFh or pricesPath.open('r', encoding = 'utf-8') as pricesFh:
652
+ with pricesFh or pricesPath.open('r', encoding='utf-8') as pricesFh:
620
653
  stations, items, zeros, newItems, updtItems, ignItems, numSys = processPrices(
621
654
  tdenv, pricesFh, db, defaultZero
622
655
  )
@@ -663,7 +696,6 @@ def processPricesFile(tdenv, db, pricesPath, pricesFh = None, defaultZero = Fals
663
696
  # ?, ?, ?
664
697
  # )
665
698
  # """, items)
666
- updatedItems = len(items)
667
699
 
668
700
  tdenv.DEBUG0("Marking populated stations as having a market")
669
701
  db.execute(
@@ -674,8 +706,9 @@ def processPricesFile(tdenv, db, pricesPath, pricesFh = None, defaultZero = Fals
674
706
  ")"
675
707
  )
676
708
 
677
- tdenv.DEBUG0(f'Committing...')
709
+ tdenv.DEBUG0('Committing...')
678
710
  db.commit()
711
+ db.close()
679
712
 
680
713
  changes = " and ".join("{} {}".format(v, k) for k, v in {
681
714
  "new": newItems,
@@ -696,6 +729,7 @@ def processPricesFile(tdenv, db, pricesPath, pricesFh = None, defaultZero = Fals
696
729
  if ignItems:
697
730
  tdenv.NOTE("Ignored {} items with old data", ignItems)
698
731
 
732
+
699
733
  ######################################################################
700
734
 
701
735
 
@@ -749,26 +783,27 @@ def processImportFile(tdenv, db, importPath, tableName):
749
783
  str(importPath), tableName
750
784
  )
751
785
 
752
- fkeySelectStr = ("("
753
- "SELECT {newValue}"
754
- " FROM {table}"
755
- " WHERE {stmt}"
756
- ")"
786
+ fkeySelectStr = (
787
+ "("
788
+ " SELECT {newValue}"
789
+ " FROM {table}"
790
+ " WHERE {stmt}"
791
+ ")"
757
792
  )
758
793
  uniquePfx = "unq:"
759
794
  uniqueLen = len(uniquePfx)
760
795
  ignorePfx = "!"
761
796
 
762
- with importPath.open('r', encoding = 'utf-8') as importFile:
797
+ with importPath.open('r', encoding='utf-8') as importFile:
763
798
  csvin = csv.reader(
764
- importFile, delimiter = ',', quotechar = "'", doublequote = True
799
+ importFile, delimiter=',', quotechar="'", doublequote=True
765
800
  )
766
801
  # first line must be the column names
767
802
  columnDefs = next(csvin)
768
803
  columnCount = len(columnDefs)
769
804
 
770
805
  # split up columns and values
771
- # this is necessqary because the insert might use a foreign key
806
+ # this is necessary because the insert might use a foreign key
772
807
  bindColumns = []
773
808
  bindValues = []
774
809
  joinHelper = []
@@ -817,10 +852,10 @@ def processImportFile(tdenv, db, importPath, tableName):
817
852
  sql_stmt = """
818
853
  INSERT OR REPLACE INTO {table} ({columns}) VALUES({values})
819
854
  """.format(
820
- table = tableName,
821
- columns = ','.join(bindColumns),
822
- values = ','.join(bindValues)
823
- )
855
+ table=tableName,
856
+ columns=','.join(bindColumns),
857
+ values=','.join(bindValues)
858
+ )
824
859
  tdenv.DEBUG0("SQL-Statement: {}", sql_stmt)
825
860
 
826
861
  # Check if there is a deprecation check for this table.
@@ -832,7 +867,7 @@ def processImportFile(tdenv, db, importPath, tableName):
832
867
 
833
868
  # import the data
834
869
  importCount = 0
835
- uniqueIndex = dict()
870
+ uniqueIndex = {}
836
871
 
837
872
  for linein in csvin:
838
873
  if not linein:
@@ -968,6 +1003,7 @@ def buildCache(tdb, tdenv):
968
1003
 
969
1004
  tempDB.commit()
970
1005
  tempDB.close()
1006
+ tdb.close()
971
1007
 
972
1008
  tdenv.DEBUG0("Swapping out db files")
973
1009
 
@@ -1021,7 +1057,7 @@ def importDataFromFile(tdb, tdenv, path, pricesFh = None, reset = False):
1021
1057
  db = tdb.getDB(),
1022
1058
  pricesPath = path,
1023
1059
  pricesFh = pricesFh,
1024
- )
1060
+ )
1025
1061
 
1026
1062
  # If everything worked, we may need to re-build the prices file.
1027
1063
  if path != tdb.pricesPath: