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.
- py.typed +1 -0
- trade.py +49 -0
- tradedangerous/__init__.py +43 -0
- tradedangerous/cache.py +1381 -0
- tradedangerous/cli.py +136 -0
- tradedangerous/commands/TEMPLATE.py +74 -0
- tradedangerous/commands/__init__.py +244 -0
- tradedangerous/commands/buildcache_cmd.py +102 -0
- tradedangerous/commands/buy_cmd.py +427 -0
- tradedangerous/commands/commandenv.py +372 -0
- tradedangerous/commands/exceptions.py +94 -0
- tradedangerous/commands/export_cmd.py +150 -0
- tradedangerous/commands/import_cmd.py +222 -0
- tradedangerous/commands/local_cmd.py +243 -0
- tradedangerous/commands/market_cmd.py +207 -0
- tradedangerous/commands/nav_cmd.py +252 -0
- tradedangerous/commands/olddata_cmd.py +270 -0
- tradedangerous/commands/parsing.py +221 -0
- tradedangerous/commands/rares_cmd.py +298 -0
- tradedangerous/commands/run_cmd.py +1521 -0
- tradedangerous/commands/sell_cmd.py +262 -0
- tradedangerous/commands/shipvendor_cmd.py +60 -0
- tradedangerous/commands/station_cmd.py +68 -0
- tradedangerous/commands/trade_cmd.py +181 -0
- tradedangerous/commands/update_cmd.py +67 -0
- tradedangerous/corrections.py +55 -0
- tradedangerous/csvexport.py +234 -0
- tradedangerous/db/__init__.py +27 -0
- tradedangerous/db/adapter.py +192 -0
- tradedangerous/db/config.py +107 -0
- tradedangerous/db/engine.py +259 -0
- tradedangerous/db/lifecycle.py +332 -0
- tradedangerous/db/locks.py +208 -0
- tradedangerous/db/orm_models.py +500 -0
- tradedangerous/db/paths.py +113 -0
- tradedangerous/db/utils.py +661 -0
- tradedangerous/edscupdate.py +565 -0
- tradedangerous/edsmupdate.py +474 -0
- tradedangerous/formatting.py +210 -0
- tradedangerous/fs.py +156 -0
- tradedangerous/gui.py +1146 -0
- tradedangerous/mapping.py +133 -0
- tradedangerous/mfd/__init__.py +103 -0
- tradedangerous/mfd/saitek/__init__.py +3 -0
- tradedangerous/mfd/saitek/directoutput.py +678 -0
- tradedangerous/mfd/saitek/x52pro.py +195 -0
- tradedangerous/misc/checkpricebounds.py +287 -0
- tradedangerous/misc/clipboard.py +49 -0
- tradedangerous/misc/coord64.py +83 -0
- tradedangerous/misc/csvdialect.py +57 -0
- tradedangerous/misc/derp-sentinel.py +35 -0
- tradedangerous/misc/diff-system-csvs.py +159 -0
- tradedangerous/misc/eddb.py +81 -0
- tradedangerous/misc/eddn.py +349 -0
- tradedangerous/misc/edsc.py +437 -0
- tradedangerous/misc/edsm.py +121 -0
- tradedangerous/misc/importeddbstats.py +54 -0
- tradedangerous/misc/prices-json-exp.py +179 -0
- tradedangerous/misc/progress.py +194 -0
- tradedangerous/plugins/__init__.py +249 -0
- tradedangerous/plugins/edcd_plug.py +371 -0
- tradedangerous/plugins/eddblink_plug.py +861 -0
- tradedangerous/plugins/edmc_batch_plug.py +133 -0
- tradedangerous/plugins/spansh_plug.py +2647 -0
- tradedangerous/prices.py +211 -0
- tradedangerous/submit-distances.py +422 -0
- tradedangerous/templates/Added.csv +37 -0
- tradedangerous/templates/Category.csv +17 -0
- tradedangerous/templates/RareItem.csv +143 -0
- tradedangerous/templates/TradeDangerous.sql +338 -0
- tradedangerous/tools.py +40 -0
- tradedangerous/tradecalc.py +1302 -0
- tradedangerous/tradedb.py +2320 -0
- tradedangerous/tradeenv.py +313 -0
- tradedangerous/tradeenv.pyi +109 -0
- tradedangerous/tradeexcept.py +131 -0
- tradedangerous/tradeorm.py +183 -0
- tradedangerous/transfers.py +192 -0
- tradedangerous/utils.py +243 -0
- tradedangerous/version.py +16 -0
- tradedangerous-12.7.6.dist-info/METADATA +106 -0
- tradedangerous-12.7.6.dist-info/RECORD +87 -0
- tradedangerous-12.7.6.dist-info/WHEEL +5 -0
- tradedangerous-12.7.6.dist-info/entry_points.txt +3 -0
- tradedangerous-12.7.6.dist-info/licenses/LICENSE +373 -0
- tradedangerous-12.7.6.dist-info/top_level.txt +2 -0
- tradegui.py +24 -0
|
@@ -0,0 +1,474 @@
|
|
|
1
|
+
#!/usr/bin/env python3.6
|
|
2
|
+
# Deprecated
|
|
3
|
+
|
|
4
|
+
"""
|
|
5
|
+
based on edscupdate.py without the submit_distance()
|
|
6
|
+
|
|
7
|
+
This tool looks for changes in the EDSM service since the most
|
|
8
|
+
recent "modified" date in the System table or the date supplied
|
|
9
|
+
on the command line.
|
|
10
|
+
|
|
11
|
+
It then tries to do some validation but also requires user
|
|
12
|
+
confirmation.
|
|
13
|
+
|
|
14
|
+
For each star that appears to be new, it copies the name into
|
|
15
|
+
the clipboard so you can paste it into the "SEARCH" box in the
|
|
16
|
+
game to verify that the name is correct.
|
|
17
|
+
|
|
18
|
+
Additionally it shows you the distance from "current system"
|
|
19
|
+
to the star as a way to verify the co-ordinates.
|
|
20
|
+
|
|
21
|
+
This helps to catch cases where people have typo'd system names,
|
|
22
|
+
but given the right coordinates; it also helps catch cases where
|
|
23
|
+
people have used the star name from in-system which sometimes
|
|
24
|
+
differs from the star name in the galaxy map.
|
|
25
|
+
|
|
26
|
+
For each star you can type "y" to accept the star, "n" to skip it
|
|
27
|
+
or "q" to stop recording.
|
|
28
|
+
"""
|
|
29
|
+
|
|
30
|
+
import argparse
|
|
31
|
+
import misc.clipboard
|
|
32
|
+
import misc.edsm
|
|
33
|
+
import os
|
|
34
|
+
import random
|
|
35
|
+
import re
|
|
36
|
+
import sys
|
|
37
|
+
import tradedb
|
|
38
|
+
import tradeenv
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
# Systems we know are bad.
|
|
42
|
+
ignore = []
|
|
43
|
+
|
|
44
|
+
class UsageError(Exception):
|
|
45
|
+
""" Raised when command line usage is invalid. """
|
|
46
|
+
pass
|
|
47
|
+
|
|
48
|
+
def parse_arguments():
|
|
49
|
+
parser = argparse.ArgumentParser(
|
|
50
|
+
description='Review and validate incoming EDSM star data.',
|
|
51
|
+
epilog='Confirmed systems are written to tmp/new.systems.csv.',
|
|
52
|
+
)
|
|
53
|
+
parser.add_argument(
|
|
54
|
+
'refSystem',
|
|
55
|
+
help='*Exact* name of the system you are *currently* in, '
|
|
56
|
+
'used as a reference system for distance validations.',
|
|
57
|
+
type=str,
|
|
58
|
+
metavar='"REFERENCE SYSTEM"',
|
|
59
|
+
default=None,
|
|
60
|
+
nargs='?',
|
|
61
|
+
)
|
|
62
|
+
parser.add_argument(
|
|
63
|
+
'--cmdr',
|
|
64
|
+
required=False,
|
|
65
|
+
help='Specify your commander name.',
|
|
66
|
+
type=str,
|
|
67
|
+
default=os.environ.get('CMDR', None),
|
|
68
|
+
)
|
|
69
|
+
grp = parser.add_mutually_exclusive_group()
|
|
70
|
+
if grp: # for indentation
|
|
71
|
+
grp.add_argument(
|
|
72
|
+
'--random',
|
|
73
|
+
action='store_true',
|
|
74
|
+
required=False,
|
|
75
|
+
help='Show systems in random order, maximum of --max-systems.',
|
|
76
|
+
)
|
|
77
|
+
grp.add_argument(
|
|
78
|
+
'--distance',
|
|
79
|
+
action='store_true',
|
|
80
|
+
required=False,
|
|
81
|
+
help='Select upto 10 systems by proximity.',
|
|
82
|
+
)
|
|
83
|
+
parser.add_argument(
|
|
84
|
+
'--max-systems',
|
|
85
|
+
dest='maxSystems',
|
|
86
|
+
help='Maximum systems to query with --random/--distance.',
|
|
87
|
+
required=False,
|
|
88
|
+
type=int,
|
|
89
|
+
default=10
|
|
90
|
+
)
|
|
91
|
+
parser.add_argument(
|
|
92
|
+
'--max-ly',
|
|
93
|
+
dest='maxLy',
|
|
94
|
+
help='Maximum distance to reference systems (for --distance).',
|
|
95
|
+
required=False,
|
|
96
|
+
type=int,
|
|
97
|
+
default=0
|
|
98
|
+
)
|
|
99
|
+
parser.add_argument(
|
|
100
|
+
'--add-to-local-db', '-A',
|
|
101
|
+
action='store_true',
|
|
102
|
+
required=False,
|
|
103
|
+
help='Add accepted systems to the local database.',
|
|
104
|
+
dest='add',
|
|
105
|
+
)
|
|
106
|
+
parser.add_argument(
|
|
107
|
+
'--date',
|
|
108
|
+
required=False,
|
|
109
|
+
help='Use specified date (YYYY-MM-DD HH:MM:SS format) for '
|
|
110
|
+
'start of update search. '
|
|
111
|
+
'Default is to use the last System modified date.',
|
|
112
|
+
type=str,
|
|
113
|
+
default=None,
|
|
114
|
+
)
|
|
115
|
+
parser.add_argument(
|
|
116
|
+
'--no-splash', '-NS',
|
|
117
|
+
required=False,
|
|
118
|
+
action='store_false',
|
|
119
|
+
help="Don't display the 'splash' text on startup.",
|
|
120
|
+
dest='splash',
|
|
121
|
+
default=True,
|
|
122
|
+
)
|
|
123
|
+
parser.add_argument(
|
|
124
|
+
'--summary',
|
|
125
|
+
required=False,
|
|
126
|
+
help='Check for and report on new systems but do no work.',
|
|
127
|
+
action='store_true',
|
|
128
|
+
)
|
|
129
|
+
parser.add_argument(
|
|
130
|
+
'--detail', '-v',
|
|
131
|
+
help='Increase level of detail in output.',
|
|
132
|
+
default=0,
|
|
133
|
+
required=False,
|
|
134
|
+
action='count',
|
|
135
|
+
)
|
|
136
|
+
parser.add_argument(
|
|
137
|
+
'--debug', '-w',
|
|
138
|
+
help='Enable/raise level of diagnostic output.',
|
|
139
|
+
default=0,
|
|
140
|
+
required=False,
|
|
141
|
+
action='count',
|
|
142
|
+
)
|
|
143
|
+
parser.add_argument(
|
|
144
|
+
'--ref',
|
|
145
|
+
help='Reference system (for --distance).',
|
|
146
|
+
default=None,
|
|
147
|
+
dest='refSys',
|
|
148
|
+
type=str,
|
|
149
|
+
)
|
|
150
|
+
parser.add_argument(
|
|
151
|
+
'--log-edsm',
|
|
152
|
+
required=False,
|
|
153
|
+
help='Log the EDSM request and response in tmp/edsm.log.',
|
|
154
|
+
default=False,
|
|
155
|
+
dest='logEDSM',
|
|
156
|
+
action='store_true',
|
|
157
|
+
)
|
|
158
|
+
parser.add_argument(
|
|
159
|
+
'--yes',
|
|
160
|
+
required=False,
|
|
161
|
+
help='Answer "y" to autoconfirm all EDSM systems.',
|
|
162
|
+
default=False,
|
|
163
|
+
dest='autoOK',
|
|
164
|
+
action='store_true',
|
|
165
|
+
)
|
|
166
|
+
|
|
167
|
+
argv = parser.parse_args(sys.argv[1:])
|
|
168
|
+
if not argv.summary:
|
|
169
|
+
if not argv.refSystem:
|
|
170
|
+
raise UsageError(
|
|
171
|
+
"Must specify a reference system name (when not using "
|
|
172
|
+
"--summary). Be sure to put the name in double quotes, "
|
|
173
|
+
"e.g. \"SOL\" or \"I BOOTIS\".\n"
|
|
174
|
+
)
|
|
175
|
+
if not argv.distance and argv.refSys:
|
|
176
|
+
raise UsageError("--ref requires --distance")
|
|
177
|
+
if not argv.distance and argv.maxLy:
|
|
178
|
+
raise UsageError("--max-ly requires --distance")
|
|
179
|
+
|
|
180
|
+
return argv
|
|
181
|
+
|
|
182
|
+
def is_change(tdb, sysinfo):
|
|
183
|
+
""" Check if a system's EDSM data is different than TDs """
|
|
184
|
+
name = sysinfo['name'] = sysinfo['name'].upper()
|
|
185
|
+
if name in ignore:
|
|
186
|
+
return False
|
|
187
|
+
try:
|
|
188
|
+
x = sysinfo['coords']['x']
|
|
189
|
+
y = sysinfo['coords']['y']
|
|
190
|
+
z = sysinfo['coords']['z']
|
|
191
|
+
place = tdb.systemByName[name]
|
|
192
|
+
if place.posX == x and place.posY == y and place.posZ == z:
|
|
193
|
+
return False
|
|
194
|
+
except KeyError:
|
|
195
|
+
place = None
|
|
196
|
+
sysinfo['place'] = place
|
|
197
|
+
return True
|
|
198
|
+
|
|
199
|
+
def has_position_changed(place, name, x, y, z):
|
|
200
|
+
if not place:
|
|
201
|
+
return False
|
|
202
|
+
|
|
203
|
+
print("! @{} [{},{},{}] changed to @{} [{},{},{}]".format(
|
|
204
|
+
name, x, y, z,
|
|
205
|
+
place.dbname, place.posX, place.posY, place.posZ
|
|
206
|
+
))
|
|
207
|
+
|
|
208
|
+
return True
|
|
209
|
+
|
|
210
|
+
def check_database(tdb, name, x, y, z):
|
|
211
|
+
# is it in the database?
|
|
212
|
+
cur = tdb.query("""
|
|
213
|
+
SELECT name, pos_x, pos_y, pos_z
|
|
214
|
+
FROM System
|
|
215
|
+
WHERE pos_x BETWEEN ? and ?
|
|
216
|
+
AND pos_y BETWEEN ? and ?
|
|
217
|
+
AND pos_z BETWEEN ? and ?
|
|
218
|
+
""", [
|
|
219
|
+
x - 0.5, x + 0.5,
|
|
220
|
+
y - 0.5, y + 0.5,
|
|
221
|
+
z - 0.5, z + 0.5,
|
|
222
|
+
])
|
|
223
|
+
for mname, mx, my, mz in cur:
|
|
224
|
+
print(
|
|
225
|
+
"! @{} [{},{},{}] matches coords for "
|
|
226
|
+
"@{} [{},{},{}]".format(
|
|
227
|
+
name, x, y, z,
|
|
228
|
+
mname, mx, my, mz
|
|
229
|
+
), file=sys.stderr)
|
|
230
|
+
|
|
231
|
+
def get_distance(tdb, startSys, x, y, z):
|
|
232
|
+
distance = tdb.calculateDistance(
|
|
233
|
+
startSys.posX, startSys.posY, startSys.posZ,
|
|
234
|
+
x, y, z
|
|
235
|
+
)
|
|
236
|
+
return float("{:.2f}".format(distance))
|
|
237
|
+
|
|
238
|
+
def get_extras():
|
|
239
|
+
extras = set()
|
|
240
|
+
try:
|
|
241
|
+
with open("data/extra-stars.txt", "r", encoding="utf-8") as fh:
|
|
242
|
+
for line in fh:
|
|
243
|
+
name = line.partition('#')[0].strip().upper()
|
|
244
|
+
if name:
|
|
245
|
+
extras.add(name)
|
|
246
|
+
except FileNotFoundError:
|
|
247
|
+
pass
|
|
248
|
+
return extras
|
|
249
|
+
|
|
250
|
+
def add_to_extras(argv, name):
|
|
251
|
+
with open("data/extra-stars.txt", "a", encoding="utf-8") as fh:
|
|
252
|
+
print(name.upper(), file=fh)
|
|
253
|
+
print("Added {} to data/extra-stars.txt".format(name))
|
|
254
|
+
|
|
255
|
+
def main():
|
|
256
|
+
argv = parse_arguments()
|
|
257
|
+
tdenv = tradeenv.TradeEnv(properties=argv)
|
|
258
|
+
tdenv.quiet = 1
|
|
259
|
+
tdb = tradedb.TradeDB(tdenv)
|
|
260
|
+
|
|
261
|
+
if not argv.summary:
|
|
262
|
+
try:
|
|
263
|
+
argv.startSys = tdb.lookupSystem(argv.refSystem)
|
|
264
|
+
except (LookupError, tradedb.AmbiguityError):
|
|
265
|
+
raise UsageError(
|
|
266
|
+
"Unrecognized system '{}'. Reference System must be an "
|
|
267
|
+
"*exact* name for a system that TD already knows.\n"
|
|
268
|
+
"Did you forget to put double-quotes around the reference "
|
|
269
|
+
"system name?"
|
|
270
|
+
.format(argv.refSystem)
|
|
271
|
+
)
|
|
272
|
+
|
|
273
|
+
if not argv.date:
|
|
274
|
+
argv.date = tdb.query("SELECT MAX(modified) FROM System").fetchone()[0]
|
|
275
|
+
dateRe = re.compile(r'^20\d\d-(0[1-9]|1[012])-(0[1-9]|[12]\d|3[01]) ([01]\d|2[0123]):[0-5]\d:[0-5]\d$')
|
|
276
|
+
if not dateRe.match(argv.date):
|
|
277
|
+
raise UsageError(
|
|
278
|
+
"Invalid date: '{}', expecting YYYY-MM-DD HH:MM:SS format."
|
|
279
|
+
.format(argv.date)
|
|
280
|
+
)
|
|
281
|
+
print("start date: {}".format(argv.date))
|
|
282
|
+
|
|
283
|
+
edsq = misc.edsm.StarQueryMulti(
|
|
284
|
+
log=argv.logEDSM,
|
|
285
|
+
startDateTime=argv.date,
|
|
286
|
+
submitted=1,
|
|
287
|
+
showId=1,
|
|
288
|
+
)
|
|
289
|
+
data = edsq.fetch()
|
|
290
|
+
|
|
291
|
+
systems = data
|
|
292
|
+
|
|
293
|
+
print("{} results".format(len(systems)))
|
|
294
|
+
# Filter out systems we already know that match the EDSM data.
|
|
295
|
+
systems = [
|
|
296
|
+
sysinfo for sysinfo in systems if is_change(tdb, sysinfo)
|
|
297
|
+
]
|
|
298
|
+
print("{} deltas".format(len(systems)))
|
|
299
|
+
|
|
300
|
+
if argv.summary or len(systems) <= 0:
|
|
301
|
+
return
|
|
302
|
+
|
|
303
|
+
systems = [
|
|
304
|
+
sysinfo for sysinfo in systems if 'coords' in sysinfo
|
|
305
|
+
]
|
|
306
|
+
|
|
307
|
+
if argv.random:
|
|
308
|
+
num = min(len(systems), argv.maxSystems)
|
|
309
|
+
systems = random.sample(systems, num)
|
|
310
|
+
|
|
311
|
+
if argv.refSys:
|
|
312
|
+
refSys = tdb.lookupPlace(argv.refSys)
|
|
313
|
+
else:
|
|
314
|
+
refSys = None
|
|
315
|
+
startSys = argv.startSys
|
|
316
|
+
for sysinfo in systems:
|
|
317
|
+
x = sysinfo['coords']['x']
|
|
318
|
+
y = sysinfo['coords']['y']
|
|
319
|
+
z = sysinfo['coords']['z']
|
|
320
|
+
sysinfo['distance'] = get_distance(tdb, startSys, x, y, z)
|
|
321
|
+
if refSys:
|
|
322
|
+
sysinfo['refdist'] = get_distance(tdb, refSys, x, y, z)
|
|
323
|
+
else:
|
|
324
|
+
sysinfo['refdist'] = None
|
|
325
|
+
|
|
326
|
+
if argv.distance:
|
|
327
|
+
if argv.maxLy > 0:
|
|
328
|
+
if refSys:
|
|
329
|
+
systems = [
|
|
330
|
+
sysinfo for sysinfo in systems if sysinfo['refdist'] <= argv.maxLy
|
|
331
|
+
]
|
|
332
|
+
else:
|
|
333
|
+
systems = [
|
|
334
|
+
sysinfo for sysinfo in systems if sysinfo['distance'] <= argv.maxLy
|
|
335
|
+
]
|
|
336
|
+
else:
|
|
337
|
+
if refSys:
|
|
338
|
+
systems.sort(key=lambda sysinfo: sysinfo['refdist'])
|
|
339
|
+
else:
|
|
340
|
+
systems.sort(key=lambda sysinfo: sysinfo['distance'])
|
|
341
|
+
systems = systems[:argv.maxSystems]
|
|
342
|
+
|
|
343
|
+
if argv.splash and not argv.autoOK:
|
|
344
|
+
print(
|
|
345
|
+
"\n"
|
|
346
|
+
"===============================================================\n"
|
|
347
|
+
"\n"
|
|
348
|
+
" The tool will now take you through the stars returned by EDSM\n"
|
|
349
|
+
" that are new or different from your local System.csv.\n"
|
|
350
|
+
"\n"
|
|
351
|
+
" You will be prompted with the name and predicted distance from\n"
|
|
352
|
+
" your current system so you can check for mistakes.\n"
|
|
353
|
+
"\n"
|
|
354
|
+
" The name will be copied into your clipboard so you can alt-tab\n"
|
|
355
|
+
" into the game and paste the name into the Galaxy Map's SEARCH\n"
|
|
356
|
+
" box (under NAVIGATION). Let the map zoom to the system.\n"
|
|
357
|
+
"\n"
|
|
358
|
+
" Check the name and distance, then use the appropriate action.\n"
|
|
359
|
+
"\n"
|
|
360
|
+
" (Use the -NS option to skip this text in future)\n"
|
|
361
|
+
"\n"
|
|
362
|
+
"===============================================================\n"
|
|
363
|
+
"\n"
|
|
364
|
+
)
|
|
365
|
+
|
|
366
|
+
input("Hit enter to continue: ")
|
|
367
|
+
|
|
368
|
+
if not argv.autoOK:
|
|
369
|
+
print("""At the prompt enter:
|
|
370
|
+
q
|
|
371
|
+
to indicate you've suffered enough,
|
|
372
|
+
y
|
|
373
|
+
to accept the name/value,
|
|
374
|
+
n (or anything else)
|
|
375
|
+
to skip the name/value (no confirmation),
|
|
376
|
+
=name (e.g. =SOL)
|
|
377
|
+
to accept the distance but correct spelling,
|
|
378
|
+
""")
|
|
379
|
+
print()
|
|
380
|
+
|
|
381
|
+
extras = get_extras()
|
|
382
|
+
|
|
383
|
+
clip = misc.clipboard.SystemNameClip()
|
|
384
|
+
total = len(systems)
|
|
385
|
+
current = 0
|
|
386
|
+
with open("tmp/new.systems.csv", "w", encoding="utf-8") as output:
|
|
387
|
+
if argv.autoOK:
|
|
388
|
+
commit=False
|
|
389
|
+
else:
|
|
390
|
+
commit=True
|
|
391
|
+
for sysinfo in systems:
|
|
392
|
+
current += 1
|
|
393
|
+
name = sysinfo['name']
|
|
394
|
+
created = sysinfo['date']
|
|
395
|
+
x = sysinfo['coords']['x']
|
|
396
|
+
y = sysinfo['coords']['y']
|
|
397
|
+
z = sysinfo['coords']['z']
|
|
398
|
+
|
|
399
|
+
if not argv.autoOK:
|
|
400
|
+
print(
|
|
401
|
+
"\n"
|
|
402
|
+
"-----------------------------------------------\n"
|
|
403
|
+
"{syidlab:.<12}: {syid}\n"
|
|
404
|
+
"{crealab:.<12}: {crts}\n"
|
|
405
|
+
.format(
|
|
406
|
+
syidlab="ID",
|
|
407
|
+
crealab="Created",
|
|
408
|
+
syid=sysinfo['id'],
|
|
409
|
+
crts=created,
|
|
410
|
+
)
|
|
411
|
+
)
|
|
412
|
+
if refSys:
|
|
413
|
+
print("{reflab:.<12}: {refdist}ly\n".format(
|
|
414
|
+
reflab="Ref Dist",
|
|
415
|
+
refdist=sysinfo['refdist'],
|
|
416
|
+
))
|
|
417
|
+
|
|
418
|
+
check_database(tdb, name, x, y, z)
|
|
419
|
+
|
|
420
|
+
change = has_position_changed(sysinfo['place'], name, x, y, z)
|
|
421
|
+
if change:
|
|
422
|
+
oldDist = startSys.distanceTo(sysinfo['place'])
|
|
423
|
+
print("Old Distance: {:.2f}ly".format(oldDist))
|
|
424
|
+
|
|
425
|
+
distance = sysinfo['distance']
|
|
426
|
+
clip.copy_text(name)
|
|
427
|
+
prompt = "{}/{}: '{}': {:.2f}ly? ".format(
|
|
428
|
+
current, total,
|
|
429
|
+
name,
|
|
430
|
+
distance,
|
|
431
|
+
)
|
|
432
|
+
if argv.autoOK:
|
|
433
|
+
if change:
|
|
434
|
+
ok = "n"
|
|
435
|
+
else:
|
|
436
|
+
ok = "y"
|
|
437
|
+
else:
|
|
438
|
+
ok = input(prompt)
|
|
439
|
+
if ok.lower() == 'q':
|
|
440
|
+
break
|
|
441
|
+
if ok.startswith('='):
|
|
442
|
+
name = ok[1:].strip().upper()
|
|
443
|
+
if name not in extras:
|
|
444
|
+
add_to_extras(argv, name)
|
|
445
|
+
ok = 'y'
|
|
446
|
+
if ok.lower() != 'y':
|
|
447
|
+
continue
|
|
448
|
+
|
|
449
|
+
if argv.add:
|
|
450
|
+
print("Add {:>6}: {:>12} {} {}".format(current, sysinfo['id'], created, name))
|
|
451
|
+
tdb.addLocalSystem(
|
|
452
|
+
name,
|
|
453
|
+
x, y, z,
|
|
454
|
+
added='EDSM',
|
|
455
|
+
modified=created,
|
|
456
|
+
commit=commit
|
|
457
|
+
)
|
|
458
|
+
|
|
459
|
+
print("'{}',{},{},{},'EDSM','{}'".format(
|
|
460
|
+
name, x, y, z, created,
|
|
461
|
+
), file=output)
|
|
462
|
+
if argv.add and not commit:
|
|
463
|
+
tdb.getDB().commit()
|
|
464
|
+
|
|
465
|
+
|
|
466
|
+
if __name__ == "__main__":
|
|
467
|
+
try:
|
|
468
|
+
main()
|
|
469
|
+
except KeyboardInterrupt:
|
|
470
|
+
print("^C")
|
|
471
|
+
except UsageError as e:
|
|
472
|
+
print("ERROR: {}\nSee {} --help for usage help.".format(
|
|
473
|
+
str(e), sys.argv[0]
|
|
474
|
+
))
|
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Provides a library of mechanisms for formatting output text to ensure consistency across
|
|
3
|
+
TradeDangerous and plugin tools,
|
|
4
|
+
"""
|
|
5
|
+
from __future__ import annotations
|
|
6
|
+
|
|
7
|
+
import itertools
|
|
8
|
+
import typing
|
|
9
|
+
|
|
10
|
+
if typing.TYPE_CHECKING:
|
|
11
|
+
from collections.abc import Callable, Iterable
|
|
12
|
+
from typing import Any
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class ColumnFormat:
|
|
16
|
+
"""
|
|
17
|
+
Describes formatting of a column to be populated with data.
|
|
18
|
+
|
|
19
|
+
Member Functions:
|
|
20
|
+
|
|
21
|
+
text()
|
|
22
|
+
Applies all formatting (except qualifier) to the name to
|
|
23
|
+
produce a correctly sized title field.
|
|
24
|
+
|
|
25
|
+
format(value)
|
|
26
|
+
Applies all formatting to key(value) to produce a correctly
|
|
27
|
+
sized value field.
|
|
28
|
+
|
|
29
|
+
Attributes:
|
|
30
|
+
name
|
|
31
|
+
Heading for column to display when calling title()
|
|
32
|
+
e.g. name="Station Name"
|
|
33
|
+
align
|
|
34
|
+
Alignment formatter for .format
|
|
35
|
+
e.g. align='<' or align='>' or align=''
|
|
36
|
+
width
|
|
37
|
+
Numeric value for the width of the column
|
|
38
|
+
e.g. width=5
|
|
39
|
+
qualifier
|
|
40
|
+
Final part of the print format,
|
|
41
|
+
e.g. qualifier='.2f' or qualifier='n'
|
|
42
|
+
pre
|
|
43
|
+
Prefix to the column
|
|
44
|
+
post
|
|
45
|
+
Postfix to the column
|
|
46
|
+
key
|
|
47
|
+
Retrieve the printable name of the item
|
|
48
|
+
pred
|
|
49
|
+
Predicate: Return False to leave this column blank
|
|
50
|
+
|
|
51
|
+
e.g.
|
|
52
|
+
cols = [
|
|
53
|
+
ColumnFormat("Name", "<", "5", '', key=lambda item:item['name']),
|
|
54
|
+
ColumnFormat("Dist", ">", "6", ".2f",
|
|
55
|
+
pre='[',
|
|
56
|
+
post=']'
|
|
57
|
+
key=lambda item:item['dist']),
|
|
58
|
+
]
|
|
59
|
+
rows = [ {'name':'Bob', 'dist':1.5}, {'name':'John', 'dist':23}]
|
|
60
|
+
# print titles
|
|
61
|
+
print(*[col.text() for col in cols])
|
|
62
|
+
for row in rows:
|
|
63
|
+
print(*[col.format(row) for col in cols])
|
|
64
|
+
Produces:
|
|
65
|
+
Name [ Dist]
|
|
66
|
+
Bob [ 1.30]
|
|
67
|
+
John [23.00]
|
|
68
|
+
|
|
69
|
+
"""
|
|
70
|
+
name: str # name of the column
|
|
71
|
+
align: str # format's alignment specifier
|
|
72
|
+
width: int # width specifier
|
|
73
|
+
qualifier: str | None # optional format type specifier e.g. '.2f', 's', 'n'
|
|
74
|
+
pre: str | None # prefix to the column
|
|
75
|
+
post: str | None # postfix to the column
|
|
76
|
+
key: Callable # function to retrieve the printable name of the item
|
|
77
|
+
pred: Callable # predicate: return False to leave this column blank
|
|
78
|
+
|
|
79
|
+
def __init__(
|
|
80
|
+
self,
|
|
81
|
+
name,
|
|
82
|
+
align,
|
|
83
|
+
width,
|
|
84
|
+
qualifier=None,
|
|
85
|
+
pre=None,
|
|
86
|
+
post=None,
|
|
87
|
+
key=lambda item: item,
|
|
88
|
+
pred=lambda item: True,
|
|
89
|
+
) -> None:
|
|
90
|
+
self.name = name
|
|
91
|
+
self.align = align
|
|
92
|
+
self.width = max(int(width), len(name))
|
|
93
|
+
self.qualifier = qualifier or ''
|
|
94
|
+
self.key = key
|
|
95
|
+
self.pre = pre or ''
|
|
96
|
+
self.post = post or ''
|
|
97
|
+
self.pred = pred
|
|
98
|
+
|
|
99
|
+
def __str__(self) -> str:
|
|
100
|
+
return f'{self.pre}{self.name:{self.align}{self.width}}{self.post}'
|
|
101
|
+
text = __str__
|
|
102
|
+
|
|
103
|
+
def format(self, value: str) -> str:
|
|
104
|
+
""" Returns the string formatted with a specific value"""
|
|
105
|
+
if not self.pred(value):
|
|
106
|
+
return f'{self.pre}{"":{self.align}{self.width}}{self.post}'
|
|
107
|
+
return f'{self.pre}{self.key(value):{self.align}{self.width}{self.qualifier}}{self.post}'
|
|
108
|
+
|
|
109
|
+
class RowFormat:
|
|
110
|
+
"""
|
|
111
|
+
Describes an ordered collection of ColumnFormats
|
|
112
|
+
for dispay data from rows, such that calling
|
|
113
|
+
rowFmt.format(rowData)
|
|
114
|
+
will return the result of formatting each column
|
|
115
|
+
against rowData.
|
|
116
|
+
|
|
117
|
+
Member Functions
|
|
118
|
+
|
|
119
|
+
append(col, [after])
|
|
120
|
+
Adds a ColumnFormatter to the end of the row
|
|
121
|
+
If 'after' is specified, tries to insert
|
|
122
|
+
the new column immediately after the first
|
|
123
|
+
column who's name matches after.
|
|
124
|
+
|
|
125
|
+
insert(pos, newCol)
|
|
126
|
+
Inserts a ColumnFormatter at position pos in the list
|
|
127
|
+
|
|
128
|
+
text()
|
|
129
|
+
Returns a list of all the column headings
|
|
130
|
+
|
|
131
|
+
format(rowData):
|
|
132
|
+
Returns a list of applying rowData to all
|
|
133
|
+
of the columns
|
|
134
|
+
|
|
135
|
+
"""
|
|
136
|
+
columns: list[ColumnFormat]
|
|
137
|
+
prefix: str
|
|
138
|
+
suffix: str
|
|
139
|
+
|
|
140
|
+
def __init__(self, prefix: str | None = None, suffix: str | None = None) -> None:
|
|
141
|
+
self.columns = []
|
|
142
|
+
self.prefix = prefix or ""
|
|
143
|
+
self.suffix = suffix or ""
|
|
144
|
+
|
|
145
|
+
def addColumn(self, *args, **kwargs) -> None:
|
|
146
|
+
self.append(ColumnFormat(*args, **kwargs))
|
|
147
|
+
|
|
148
|
+
def append(self, column: ColumnFormat, after: str | None = None) -> 'RowFormat':
|
|
149
|
+
columns = self.columns
|
|
150
|
+
if after:
|
|
151
|
+
for idx, col in enumerate(columns, 1):
|
|
152
|
+
if col.name == after:
|
|
153
|
+
columns.insert(idx, column)
|
|
154
|
+
return self
|
|
155
|
+
columns.append(column)
|
|
156
|
+
return self
|
|
157
|
+
|
|
158
|
+
def insert(self, pos: int, column: ColumnFormat | None) -> None:
|
|
159
|
+
if column is not None:
|
|
160
|
+
self.columns.insert(pos, column)
|
|
161
|
+
|
|
162
|
+
def __str__(self) -> str:
|
|
163
|
+
return f"{self.prefix} {' '.join(str(col) for col in self.columns)}{self.suffix}"
|
|
164
|
+
|
|
165
|
+
text = __str__ # alias
|
|
166
|
+
|
|
167
|
+
def heading(self) -> tuple[str, str]:
|
|
168
|
+
""" Returns a title and the appropriate underline for that text. """
|
|
169
|
+
headline = f"{self}"
|
|
170
|
+
return headline, '-' * len(headline)
|
|
171
|
+
|
|
172
|
+
def format(self, row_data: Any) -> str:
|
|
173
|
+
return f"{self.prefix} {' '.join(col.format(row_data) for col in self.columns)}{self.suffix}"
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
def max_len(iterable: Iterable, key: Callable[[Any], str] = lambda item: item) -> int:
|
|
177
|
+
""" Helper that returns the maximum length of strings produced
|
|
178
|
+
by applying key() to the elements of the given iterable. """
|
|
179
|
+
iterable, readahead = itertools.tee(iter(iterable))
|
|
180
|
+
try:
|
|
181
|
+
next(readahead)
|
|
182
|
+
except StopIteration:
|
|
183
|
+
return 0
|
|
184
|
+
return max(len(key(item)) for item in iterable)
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
if __name__ == '__main__':
|
|
188
|
+
rowFmt = RowFormat(). \
|
|
189
|
+
append(ColumnFormat("Name", '<', '8', key=lambda row: row['name'])). \
|
|
190
|
+
append(ColumnFormat("Dist", '>', '6', '.2f', pre='[', post=']', key=lambda row: row['dist']))
|
|
191
|
+
|
|
192
|
+
rows = [
|
|
193
|
+
{ 'name': 'Bob', 'dist': 6.2, 'age': 30 },
|
|
194
|
+
{ 'name': 'Dave', 'dist': 42, 'age': 18 },
|
|
195
|
+
]
|
|
196
|
+
|
|
197
|
+
def present():
|
|
198
|
+
rowTitle = rowFmt.text()
|
|
199
|
+
print(rowTitle)
|
|
200
|
+
print('-' * len(rowTitle))
|
|
201
|
+
for row in rows:
|
|
202
|
+
print(rowFmt.format(row))
|
|
203
|
+
|
|
204
|
+
print("Simple usage:")
|
|
205
|
+
present()
|
|
206
|
+
|
|
207
|
+
# print()
|
|
208
|
+
# print("Adding age ColumnFormat:")
|
|
209
|
+
# rowFmt.append(after='Name', col=ColumnFormat("Age", '>', 3, pre='|', post='|', key=lambda row: row['age']))
|
|
210
|
+
# present()
|