tradedangerous 12.7.6__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.
Files changed (87) hide show
  1. py.typed +1 -0
  2. trade.py +49 -0
  3. tradedangerous/__init__.py +43 -0
  4. tradedangerous/cache.py +1381 -0
  5. tradedangerous/cli.py +136 -0
  6. tradedangerous/commands/TEMPLATE.py +74 -0
  7. tradedangerous/commands/__init__.py +244 -0
  8. tradedangerous/commands/buildcache_cmd.py +102 -0
  9. tradedangerous/commands/buy_cmd.py +427 -0
  10. tradedangerous/commands/commandenv.py +372 -0
  11. tradedangerous/commands/exceptions.py +94 -0
  12. tradedangerous/commands/export_cmd.py +150 -0
  13. tradedangerous/commands/import_cmd.py +222 -0
  14. tradedangerous/commands/local_cmd.py +243 -0
  15. tradedangerous/commands/market_cmd.py +207 -0
  16. tradedangerous/commands/nav_cmd.py +252 -0
  17. tradedangerous/commands/olddata_cmd.py +270 -0
  18. tradedangerous/commands/parsing.py +221 -0
  19. tradedangerous/commands/rares_cmd.py +298 -0
  20. tradedangerous/commands/run_cmd.py +1521 -0
  21. tradedangerous/commands/sell_cmd.py +262 -0
  22. tradedangerous/commands/shipvendor_cmd.py +60 -0
  23. tradedangerous/commands/station_cmd.py +68 -0
  24. tradedangerous/commands/trade_cmd.py +181 -0
  25. tradedangerous/commands/update_cmd.py +67 -0
  26. tradedangerous/corrections.py +55 -0
  27. tradedangerous/csvexport.py +234 -0
  28. tradedangerous/db/__init__.py +27 -0
  29. tradedangerous/db/adapter.py +192 -0
  30. tradedangerous/db/config.py +107 -0
  31. tradedangerous/db/engine.py +259 -0
  32. tradedangerous/db/lifecycle.py +332 -0
  33. tradedangerous/db/locks.py +208 -0
  34. tradedangerous/db/orm_models.py +500 -0
  35. tradedangerous/db/paths.py +113 -0
  36. tradedangerous/db/utils.py +661 -0
  37. tradedangerous/edscupdate.py +565 -0
  38. tradedangerous/edsmupdate.py +474 -0
  39. tradedangerous/formatting.py +210 -0
  40. tradedangerous/fs.py +156 -0
  41. tradedangerous/gui.py +1146 -0
  42. tradedangerous/mapping.py +133 -0
  43. tradedangerous/mfd/__init__.py +103 -0
  44. tradedangerous/mfd/saitek/__init__.py +3 -0
  45. tradedangerous/mfd/saitek/directoutput.py +678 -0
  46. tradedangerous/mfd/saitek/x52pro.py +195 -0
  47. tradedangerous/misc/checkpricebounds.py +287 -0
  48. tradedangerous/misc/clipboard.py +49 -0
  49. tradedangerous/misc/coord64.py +83 -0
  50. tradedangerous/misc/csvdialect.py +57 -0
  51. tradedangerous/misc/derp-sentinel.py +35 -0
  52. tradedangerous/misc/diff-system-csvs.py +159 -0
  53. tradedangerous/misc/eddb.py +81 -0
  54. tradedangerous/misc/eddn.py +349 -0
  55. tradedangerous/misc/edsc.py +437 -0
  56. tradedangerous/misc/edsm.py +121 -0
  57. tradedangerous/misc/importeddbstats.py +54 -0
  58. tradedangerous/misc/prices-json-exp.py +179 -0
  59. tradedangerous/misc/progress.py +194 -0
  60. tradedangerous/plugins/__init__.py +249 -0
  61. tradedangerous/plugins/edcd_plug.py +371 -0
  62. tradedangerous/plugins/eddblink_plug.py +861 -0
  63. tradedangerous/plugins/edmc_batch_plug.py +133 -0
  64. tradedangerous/plugins/spansh_plug.py +2647 -0
  65. tradedangerous/prices.py +211 -0
  66. tradedangerous/submit-distances.py +422 -0
  67. tradedangerous/templates/Added.csv +37 -0
  68. tradedangerous/templates/Category.csv +17 -0
  69. tradedangerous/templates/RareItem.csv +143 -0
  70. tradedangerous/templates/TradeDangerous.sql +338 -0
  71. tradedangerous/tools.py +40 -0
  72. tradedangerous/tradecalc.py +1302 -0
  73. tradedangerous/tradedb.py +2320 -0
  74. tradedangerous/tradeenv.py +313 -0
  75. tradedangerous/tradeenv.pyi +109 -0
  76. tradedangerous/tradeexcept.py +131 -0
  77. tradedangerous/tradeorm.py +183 -0
  78. tradedangerous/transfers.py +192 -0
  79. tradedangerous/utils.py +243 -0
  80. tradedangerous/version.py +16 -0
  81. tradedangerous-12.7.6.dist-info/METADATA +106 -0
  82. tradedangerous-12.7.6.dist-info/RECORD +87 -0
  83. tradedangerous-12.7.6.dist-info/WHEEL +5 -0
  84. tradedangerous-12.7.6.dist-info/entry_points.txt +3 -0
  85. tradedangerous-12.7.6.dist-info/licenses/LICENSE +373 -0
  86. tradedangerous-12.7.6.dist-info/top_level.txt +2 -0
  87. tradegui.py +24 -0
@@ -0,0 +1,211 @@
1
+ # --------------------------------------------------------------------
2
+ # Copyright (C) Oliver 'kfsone' Smith 2014 <oliver@kfs.org>:
3
+ # Copyright (C) Bernd 'Gazelle' Gollesch 2016, 2017
4
+ # Copyright (C) Stefan 'Tromador' Morrell 2025
5
+ # Copyright (C) Jonathan 'eyeonus' Jones 2018-2025
6
+ #
7
+ # You are free to use, redistribute, or even print and eat a copy of
8
+ # this software so long as you include this copyright notice.
9
+ # I guarantee there is at least one bug neither of us knew about.
10
+ # --------------------------------------------------------------------
11
+ # TradeDangerous :: Modules :: Generate TradeDangerous.prices
12
+
13
+ import sys
14
+ from sqlalchemy.orm import Session
15
+ from .db import orm_models as SA
16
+ from .tradeexcept import TradeException
17
+
18
+
19
+ class Element: # TODO: enum?
20
+ basic = 1 << 0
21
+ supply = 1 << 1
22
+ timestamp = 1 << 2
23
+ full = basic | supply | timestamp
24
+ blanks = 1 << 31
25
+
26
+
27
+ ######################################################################
28
+ # Main
29
+
30
+ def dumpPrices(
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
+ ):
38
+ """
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.
42
+ """
43
+
44
+ withTimes = elementMask & Element.timestamp
45
+
46
+ # ORM queries to build lookup dicts
47
+ systems = dict(
48
+ session.query(SA.System.system_id, SA.System.name).all()
49
+ )
50
+
51
+ stations = {
52
+ ID: [name, systems.get(sysID)]
53
+ for ID, name, sysID in session.query(
54
+ SA.Station.station_id, SA.Station.name, SA.Station.system_id
55
+ ).all()
56
+ }
57
+
58
+ categories = dict(
59
+ session.query(SA.Category.category_id, SA.Category.name).all()
60
+ )
61
+
62
+ items = {
63
+ ID: [name, catID, categories[catID]]
64
+ for ID, name, catID in session.query(
65
+ SA.Item.item_id, SA.Item.name, SA.Item.category_id
66
+ ).all()
67
+ }
68
+
69
+ # find longest item name (for formatting)
70
+ longestName = max(items.values(), key=lambda ent: len(ent[0]))
71
+ longestNameLen = len(longestName[0])
72
+
73
+ defaultDemandVal = 0 if defaultZero else -1
74
+
75
+ # Build the main query
76
+ q = (
77
+ session.query(
78
+ SA.StationItem.station_id,
79
+ SA.Item.item_id,
80
+ SA.StationItem.demand_price,
81
+ SA.StationItem.supply_price,
82
+ SA.StationItem.demand_units,
83
+ SA.StationItem.demand_level,
84
+ SA.StationItem.supply_units,
85
+ SA.StationItem.supply_level,
86
+ SA.StationItem.modified,
87
+ SA.Item.name,
88
+ SA.Item.category_id,
89
+ SA.Category.name.label("category_name"),
90
+ SA.Station.name.label("station_name"),
91
+ SA.System.name.label("system_name"),
92
+ )
93
+ .join(SA.Item, SA.Item.item_id == SA.StationItem.item_id)
94
+ .join(SA.Category, SA.Category.category_id == SA.Item.category_id)
95
+ .join(SA.Station, SA.Station.station_id == SA.StationItem.station_id)
96
+ .join(SA.System, SA.System.system_id == SA.Station.system_id)
97
+ .order_by(SA.Station.station_id, SA.Category.name, SA.Item.ui_order)
98
+ )
99
+
100
+ if stationID:
101
+ q = q.filter(SA.StationItem.station_id == stationID)
102
+
103
+ # Set up output
104
+ if not file:
105
+ file = sys.stdout
106
+
107
+ if stationID:
108
+ stationSet = str(stations[stationID])
109
+ else:
110
+ stationSet = "ALL Systems/Stations"
111
+
112
+ file.write(
113
+ "# TradeDangerous prices for {}\n"
114
+ "\n"
115
+ "# REMOVE ITEMS THAT DON'T APPEAR IN THE UI\n"
116
+ "# ORDER IS REMEMBERED: Move items around within categories "
117
+ "to match the game UI\n"
118
+ "\n"
119
+ "# File syntax:\n"
120
+ "# <item name> <sell> <buy> [<demand> <supply> [<timestamp>]]\n"
121
+ "# Use '?' for demand/supply when you don't know/care,\n"
122
+ "# Use '-' for demand/supply to indicate unavailable,\n"
123
+ "# Otherwise use a number followed by L, M or H, e.g.\n"
124
+ "# 1L, 23M or 30000H\n"
125
+ "# If you omit the timestamp, the current time will be used when "
126
+ "the file is loaded.\n"
127
+ "\n".format(stationSet)
128
+ )
129
+
130
+ levelDesc = "?0LMH"
131
+ maxCrWidth = 7
132
+ levelWidth = 9
133
+
134
+ outFmt = (
135
+ " {{:<{width}}}"
136
+ " {{:>{crwidth}}}"
137
+ " {{:>{crwidth}}}"
138
+ " {{:>{lvlwidth}}}"
139
+ " {{:>{lvlwidth}}}".format(
140
+ width=longestNameLen,
141
+ crwidth=maxCrWidth,
142
+ lvlwidth=levelWidth,
143
+ )
144
+ )
145
+ if withTimes:
146
+ outFmt += " {}"
147
+ outFmt += "\n"
148
+ header = outFmt.format(
149
+ "Item Name",
150
+ "SellCr", "BuyCr",
151
+ "Demand", "Supply",
152
+ "Timestamp",
153
+ )
154
+ file.write('#' + header[1:])
155
+
156
+ naIQL = "-"
157
+ unkIQL = "?"
158
+ defIQL = "?" if not defaultZero else "-"
159
+
160
+ # Main loop — stream results instead of preloading
161
+ output = ""
162
+ lastStn, lastCat = None, None
163
+ for row in q.yield_per(1000).execution_options(stream_results=True):
164
+ stnID = row.station_id
165
+ station = row.station_name
166
+ system = row.system_name
167
+ item = row.name
168
+ catID = row.category_id
169
+ category = row.category_name
170
+
171
+ # Guard against bad system names
172
+ if not system:
173
+ raise TradeException(
174
+ f"Station {station} (ID {stnID}) is linked to a system with no name."
175
+ )
176
+
177
+ if stnID != lastStn:
178
+ file.write(output)
179
+ output = f"\n\n@ {system.upper()}/{station}\n"
180
+ lastStn = stnID
181
+ lastCat = None
182
+
183
+ if catID != lastCat:
184
+ output += f" + {category}\n"
185
+ lastCat = catID
186
+
187
+ demandCr = row.demand_price or 0
188
+ supplyCr = row.supply_price or 0
189
+ demandUnits = row.demand_units or defaultDemandVal
190
+ demandLevel = row.demand_level or defaultDemandVal
191
+ supplyUnits = row.supply_units or defaultDemandVal
192
+ supplyLevel = row.supply_level or defaultDemandVal
193
+
194
+ # Demand/supply formatting
195
+ if supplyCr > 0:
196
+ demandStr = defIQL if demandCr <= 0 else unkIQL
197
+ supplyStr = (
198
+ naIQL if supplyLevel == 0
199
+ else (f"{supplyUnits if supplyUnits >= 0 else '?'}{levelDesc[supplyLevel+1]}")
200
+ )
201
+ else:
202
+ demandStr = (
203
+ naIQL if demandCr == 0 or demandLevel == 0
204
+ else (f"{demandUnits if demandUnits >= 0 else '?'}{levelDesc[demandLevel+1]}")
205
+ )
206
+ supplyStr = naIQL
207
+
208
+ modified = row.modified or ""
209
+ output += outFmt.format(item, demandCr, supplyCr, demandStr, supplyStr, modified)
210
+
211
+ file.write(output)
@@ -0,0 +1,422 @@
1
+ #!/usr/bin/env python3.6
2
+ # Deprecated
3
+ # Website no longer exists
4
+
5
+ """
6
+ Small tool to submit new star data to EDStarCoordinator.
7
+
8
+ Use:
9
+ submit-distances.py "system name"
10
+ """
11
+
12
+ #
13
+ # NOTE: This tool is very hacky. If someone wants to clean it up,
14
+ # and submit a diff, that'd be greatly appreciated!
15
+ #
16
+
17
+ import argparse
18
+ import os
19
+ import random
20
+ import re
21
+ import requests
22
+ import sys
23
+ import tradedb
24
+ import tradeenv
25
+
26
+ from tradedangerous.misc.edsc import StarSubmission, StarSubmissionResult, SubmissionError
27
+ from tradedangerous.misc.clipboard import SystemNameClip
28
+
29
+
30
+ standardStars = [
31
+ "SOL",
32
+ "NEW YEMBO",
33
+ "VESUVIT",
34
+ "HIP 79884",
35
+ "ASELLUS AUSTRALIS",
36
+ ]
37
+
38
+
39
+ sys.stderr.write("*** WARNING: submit-distances.py is deprecated; if you rely on it, please post a github issue\n")
40
+
41
+
42
+ ############################################################################
43
+
44
+
45
+ class UsageError(Exception):
46
+ def __init__(self, argv, error):
47
+ self.argv, self.error = argv, error
48
+
49
+ def __str__(self):
50
+ return error + "\n" + argv.format_usage()
51
+
52
+ def parse_arguments():
53
+ parser = argparse.ArgumentParser(
54
+ description='Submit star distances to the EDSC project.',
55
+ )
56
+ parser.add_argument(
57
+ 'origin',
58
+ help='System to submit distances for.',
59
+ type=str,
60
+ )
61
+ parser.add_argument(
62
+ '--pick',
63
+ help='Randomly select N existing systems from gamma or ealier',
64
+ required=False,
65
+ type=int,
66
+ )
67
+ parser.add_argument(
68
+ '--cmdr',
69
+ required=False,
70
+ help='Specify your commander name.',
71
+ type=str,
72
+ default=os.environ.get('CMDR', None)
73
+ )
74
+ parser.add_argument(
75
+ '--test',
76
+ help='Use the EDSC Test Database.',
77
+ required=False,
78
+ action='store_true',
79
+ )
80
+ parser.add_argument(
81
+ '--detail', '-v',
82
+ help='Output additional detail.',
83
+ action='count',
84
+ default=0,
85
+ )
86
+ parser.add_argument(
87
+ '--debug', '-w',
88
+ help='Enable debugging output,',
89
+ action='count',
90
+ default=0,
91
+ )
92
+ parser.add_argument(
93
+ '--no-update',
94
+ help='Disallow distances from an existing system.',
95
+ action='store_false',
96
+ default=True,
97
+ dest='allowUpdate',
98
+ )
99
+ parser.add_argument(
100
+ '--extra-file',
101
+ help='File to read/write extra stars to.',
102
+ type=str,
103
+ default='data/extra-stars.txt',
104
+ dest='extraFile',
105
+ )
106
+ parser.add_argument(
107
+ 'destinations',
108
+ help='System or systems to measure distance to.',
109
+ default=[],
110
+ nargs='*',
111
+ )
112
+
113
+ argv = parser.parse_args(sys.argv[1:])
114
+ argv.origin = argv.origin.upper()
115
+ if argv.origin.startswith('@'):
116
+ argv.origin = argv.origin[1:]
117
+
118
+ if not argv.cmdr:
119
+ raise UsageError(argv, "No commander name specified")
120
+
121
+ return argv
122
+
123
+ def get_system(argv, tdb):
124
+ system = tdb.systemByName.get(argv.origin.upper(), None)
125
+ if not system:
126
+ return argv.origin, None
127
+
128
+ if not argv.allowUpdate:
129
+ raise UsageError(
130
+ argv,
131
+ "System '{}' already exists.\n"
132
+ .format(systemName)
133
+ )
134
+
135
+ if argv.detail:
136
+ print("EXISTING SYSTEM:", argv.origin)
137
+
138
+ return argv.origin, system
139
+
140
+ def pick_destinations(argv, tdb):
141
+ numSystems = len(tdb.systemByName)
142
+ if numSystems < 1:
143
+ raise UsageError(
144
+ argv,
145
+ "Can't --pick random systems: "
146
+ "Your TD database doesn't contain any systems."
147
+ )
148
+ num = min(argv.pick, numSystems)
149
+ systems = tdb.systemByName
150
+ try:
151
+ gamma = tdb.lookupAdded("Gamma")
152
+ except KeyError:
153
+ gamma = 20
154
+ destinations = random.sample([
155
+ sysName for sysName, system in systems.items()
156
+ if system.addedID <= gamma and \
157
+ not sysName in standardStars
158
+ ], num)
159
+
160
+ return destinations
161
+
162
+ def get_outliers(argv):
163
+ outliers = set()
164
+ try:
165
+ with open(argv.extraFile, "r", encoding="utf-8") as input:
166
+ for line in input:
167
+ name = line.partition('#')[0].strip().upper()
168
+ if name and name != argv.origin:
169
+ outliers.add(name)
170
+ except FileNotFoundError:
171
+ pass
172
+ outliers = list(outliers)
173
+ random.shuffle(outliers)
174
+ return outliers
175
+
176
+ def get_distances(argv, clip, stars):
177
+ distances = []
178
+ for star in stars:
179
+ starNo = len(distances) + 1
180
+ # Check it's not already in the list
181
+ star = star.upper()
182
+ if star in distances:
183
+ continue
184
+
185
+ clip.copy_text(star)
186
+
187
+ if len(stars) > 1:
188
+ prefix = "{:>2}/{:2}: ".format(starNo, len(stars))
189
+ else:
190
+ prefix = ""
191
+ dist = input(prefix + "Distance to {}: ".format(star))
192
+ if dist == 'q':
193
+ return distances, 'q'
194
+
195
+ if dist:
196
+ distances.append({
197
+ 'name': star.upper(),
198
+ 'dist': float(dist),
199
+ })
200
+
201
+ return distances, 'end'
202
+
203
+ def check_system(argv, tdb, tdbSys, name):
204
+ system = tdb.systemByName.get(name.upper(), None)
205
+ if system and not tdbSys:
206
+ print("KNOWN SYSTEM")
207
+ return
208
+ if system:
209
+ print("KNOWN SYSTEM: {:.2f} ly".format(
210
+ tdbSys.distanceTo(system)
211
+ ))
212
+
213
+ def add_extra_stars(argv, extraStars):
214
+ if not extraStars:
215
+ return
216
+ if argv.detail:
217
+ print("Saving {} to {}".format(
218
+ str(extraStars), argv.extraFile,
219
+ ))
220
+ try:
221
+ with open(argv.extraFile, "a", encoding="utf-8") as output:
222
+ for star in extraStars:
223
+ print(star, file=output)
224
+ except FileNotFoundError:
225
+ pass
226
+
227
+ def submit_distances(argv, tdb, distances):
228
+ system = argv.origin
229
+ cmdr = argv.cmdr
230
+ mode = "TEST" if argv.test else "Live"
231
+
232
+ print()
233
+ print("System:", system)
234
+ print("Database:", mode)
235
+ print("Distances:")
236
+ for ref in distances:
237
+ print(" {}: {:.02f} ly".format(
238
+ ref['name'], ref['dist']
239
+ ))
240
+ print()
241
+
242
+ ok = input("Does this look correct (y/n)? ")
243
+ if ok != 'y':
244
+ print("Stopped")
245
+ return
246
+
247
+ print()
248
+ print("Submitting {} {}".format(mode, system))
249
+
250
+ sub = StarSubmission(
251
+ star=system,
252
+ commander=cmdr,
253
+ refs=distances,
254
+ test=argv.test,
255
+ )
256
+ resp = sub.submit()
257
+
258
+ result = StarSubmissionResult(star=system, response=resp)
259
+ print(str(result))
260
+ if result.valid:
261
+ # Check for systems we can add
262
+ trilats = set()
263
+ for sysName in result.systems.keys():
264
+ code, coord = result.systems[sysName]
265
+ sysName = sysName.upper()
266
+ # Does it have a distance
267
+ if isinstance(coord, (list, tuple)):
268
+ x, y, z = coord
269
+ system = tdb.systemByName.get(sysName, None)
270
+ if system:
271
+ tdb.updateLocalSystem(system, sysName, x, y, z)
272
+ else:
273
+ tdb.addLocalSystem(sysName, x, y, z)
274
+ if result.recheck:
275
+ return list(result.recheck.keys())
276
+ return None
277
+
278
+ def do_rechecks(argv, clip, rechecks):
279
+ print("\aSome systems need their distances rechecked:")
280
+
281
+ distances, term = get_distances(argv, clip, rechecks)
282
+ return distances
283
+
284
+ def send_and_check_distances(argv, tdb, clip, distances):
285
+ if not distances:
286
+ if argv.detail:
287
+ print("No distances, no submission.")
288
+ return False
289
+
290
+ while distances:
291
+ rechecks = submit_distances(argv, tdb, distances)
292
+ if not rechecks:
293
+ break
294
+
295
+ distances = do_rechecks(argv, clip, rechecks)
296
+
297
+ return True
298
+
299
+ def process_destinations(argv, tdb):
300
+ clip = SystemNameClip()
301
+
302
+ print("Distances from {}:".format(argv.origin))
303
+ distances, _ = get_distances(argv, clip, argv.destinations)
304
+ send_and_check_distances(argv, tdb, clip, distances)
305
+
306
+ ############################################################################
307
+
308
+ def main():
309
+ argv = parse_arguments()
310
+
311
+ tdenv = tradeenv.TradeEnv(properties=argv)
312
+ tdb = tradedb.TradeDB(tdenv)
313
+
314
+ system, tdbSys = get_system(argv, tdb)
315
+
316
+ if argv.pick:
317
+ argv.destinations.extend(pick_destinations(argv, tdb))
318
+
319
+ if argv.destinations:
320
+ process_destinations(argv, tdb)
321
+ return
322
+
323
+ print("Add EDSC Star Distances for \"{}\"".format(system))
324
+ print()
325
+ print("You will now be prompted for distances to various stars.")
326
+ print()
327
+ print(
328
+ "At each prompt, the star name will be copied into your paste buffer. "
329
+ "You should alt-tab into the game and paste the name into the Galaxy "
330
+ "Map's search box. Then alt-tab back and enter the distance value."
331
+ )
332
+ print()
333
+ print(
334
+ "At each prompt enter a ly distance (e.g. 123.45), q to stop, "
335
+ "or leave the line empty if you don't want to enter data for "
336
+ "this star."
337
+ )
338
+ print(
339
+ "5 distances are required for EDSC to make a first guess at a "
340
+ "star's location. You can submit more to increase the accuracy "
341
+ "but the only time you need to submit more than 10 is when you "
342
+ "are trying to submit corrections."
343
+ )
344
+ print()
345
+
346
+ clip = SystemNameClip()
347
+
348
+ print()
349
+ print("""
350
+ ===================================================
351
+ STANDARD STARS: (q to skip to the next section)
352
+
353
+ These are stars with well-known positions.
354
+ ===================================================
355
+ """)
356
+ distances, term = get_distances(argv, clip, standardStars)
357
+ if distances:
358
+ send_and_check_distances(argv, tdb, clip, distances)
359
+
360
+ outliers = get_outliers(argv)
361
+ if outliers:
362
+ print("""
363
+ ===================================================
364
+ EXTRA STARS: (q to skip to the next section)
365
+
366
+ Stars from {}.
367
+ ===================================================
368
+ """.format(argv.extraFile))
369
+ distances, term = get_distances(argv, clip, outliers)
370
+ if distances:
371
+ send_and_check_distances(argv, tdb, clip, distances)
372
+
373
+ print("""
374
+ ===================================================
375
+ CHOOSE YOUR OWN: (q to stop)
376
+
377
+ Specify additional stars.
378
+
379
+ Prefix names with a '+' to add them to
380
+ {}.
381
+ ===================================================
382
+ """.format(argv.extraFile))
383
+ distances = []
384
+ newOutliers = []
385
+ while True:
386
+ star = input("Enter star name: ")
387
+ star = star.strip().upper()
388
+ if not star or star == 'Q':
389
+ break
390
+ # Remove surrounding quotes
391
+ save = False
392
+ if star.startswith('+'):
393
+ save = True
394
+ star = star[1:].strip()
395
+ star = re.sub(r'\s+', ' ', star)
396
+ star = re.sub(r"''+", "'", star)
397
+ star = re.sub(r'^("|\')+\s*(.*)\s*\1+', r'\2', star)
398
+ if star.find('"') >= 0:
399
+ print("Invalid star name")
400
+ continue
401
+ if star.startswith('+'):
402
+ save = True
403
+ star = star[1:].strip()
404
+ for ref in distances:
405
+ if ref['name'] == star:
406
+ print("'{}' is already listed.")
407
+ continue
408
+ check_system(argv, tdb, tdbSys, star)
409
+ extras, term = get_distances(argv, clip, [star])
410
+ if term != 'q' and len(extras) > 0:
411
+ distances.extend(extras)
412
+ if save and star not in outliers and star not in newOutliers:
413
+ newOutliers.append(star)
414
+
415
+ if send_and_check_distances(argv, tdb, clip, distances):
416
+ add_extra_stars(argv, newOutliers)
417
+
418
+ if __name__ == "__main__":
419
+ try:
420
+ main()
421
+ except (SubmissionError, UsageError) as e:
422
+ print(str(e))
@@ -0,0 +1,37 @@
1
+ unq:name
2
+ 'Alpha1'
3
+ 'Alpha2'
4
+ 'Alpha3'
5
+ 'Alpha4'
6
+ 'Beta1'
7
+ 'Beta1 (unverified)'
8
+ 'Beta1 (unverified)-Inferred'
9
+ 'Beta1-Inferred'
10
+ 'Beta2'
11
+ 'Beta2 (outside Beta3)'
12
+ 'Beta2 (outside Beta3)-Inferred'
13
+ 'Beta2 (unverified)'
14
+ 'Beta2 (unverified)-Inferred'
15
+ 'Beta2-Inferred'
16
+ 'Beta3'
17
+ 'Beta3 (unverified)'
18
+ 'Beta3 (unverified)-Inferred'
19
+ 'Beta3-Inferred'
20
+ 'Beyond The Pill (unverified)-Inferred'
21
+ 'EDSM'
22
+ 'Gamma'
23
+ 'Gamma (unverified)'
24
+ 'Gamma (unverified)-Inferred'
25
+ 'Gamma-Inferred'
26
+ 'Gamma1'
27
+ 'Gamma1 (unverified)'
28
+ 'Gamma1 (unverified)-Inferred'
29
+ 'Gamma1-Inferred'
30
+ 'Journal'
31
+ 'Local'
32
+ 'netLog'
33
+ 'Not Present'
34
+ 'Premium Beta1'
35
+ 'Premium Beta2'
36
+ 'Release 1.00-EDStar'
37
+ 'undefined-Inferred'
@@ -0,0 +1,17 @@
1
+ unq:category_id,name
2
+ 1,Metals
3
+ 2,Minerals
4
+ 3,Chemicals
5
+ 4,Foods
6
+ 5,Textiles
7
+ 6,Industrial Materials
8
+ 7,Medicines
9
+ 8,Legal Drugs
10
+ 9,Machinery
11
+ 10,Technology
12
+ 11,Weapons
13
+ 12,Consumer Items
14
+ 13,Slavery
15
+ 14,Waste
16
+ 15,NonMarketable
17
+ 16,Salvage