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,437 @@
|
|
|
1
|
+
#! /usr/bin/env python3
|
|
2
|
+
|
|
3
|
+
from collections import namedtuple
|
|
4
|
+
from urllib.request import Request, urlopen
|
|
5
|
+
|
|
6
|
+
import json
|
|
7
|
+
import requests
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def edsc_log(apiCall, params, jsonData=None, error=None):
|
|
11
|
+
"""
|
|
12
|
+
Logs an EDSC query and it's response to tmp/edsc.log
|
|
13
|
+
"""
|
|
14
|
+
try:
|
|
15
|
+
with open("tmp/edsc.log", "a", encoding="utf-8") as fh:
|
|
16
|
+
print('-'*70, file=fh)
|
|
17
|
+
print("API:", apiCall, file=fh)
|
|
18
|
+
print("REQ:", str(params), file=fh)
|
|
19
|
+
if jsonData:
|
|
20
|
+
print("REP:", json.dumps(jsonData, indent=1), file=fh)
|
|
21
|
+
if error:
|
|
22
|
+
print("ERR:", error)
|
|
23
|
+
print(file=fh)
|
|
24
|
+
except FileNotFoundError:
|
|
25
|
+
pass
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class EDSCQueryBase:
|
|
29
|
+
"""
|
|
30
|
+
Base class for creating an EDSC Query class, do not use directly.
|
|
31
|
+
|
|
32
|
+
Derived class must declare "apiCall" which is appended to baseURL
|
|
33
|
+
to form the query URL.
|
|
34
|
+
"""
|
|
35
|
+
|
|
36
|
+
baseURL = "http://edstarcoordinator.com/api.asmx/"
|
|
37
|
+
|
|
38
|
+
def __init__(self, detail=2, test=False, known=1, confidence=0, **kwargs):
|
|
39
|
+
self.url = self.baseURL + self.apiCall
|
|
40
|
+
self.params = {
|
|
41
|
+
'data': {
|
|
42
|
+
'ver': 2,
|
|
43
|
+
'test': test,
|
|
44
|
+
'outputmode': detail,
|
|
45
|
+
'filter': {
|
|
46
|
+
'knownstatus': known,
|
|
47
|
+
'cr': confidence,
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
for k, v in kwargs.items():
|
|
52
|
+
self.params['data']['filter'][k] = v
|
|
53
|
+
|
|
54
|
+
self.jsData = None
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def fetch(self):
|
|
58
|
+
params = json.dumps(self.params).encode('utf-8')
|
|
59
|
+
request = Request(self.url, params, {
|
|
60
|
+
'Content-Type': 'application/json;charset=utf-8',
|
|
61
|
+
'Content-Length': len(params)
|
|
62
|
+
})
|
|
63
|
+
|
|
64
|
+
with urlopen(request, params) as stream:
|
|
65
|
+
self.jsData = stream.read()
|
|
66
|
+
|
|
67
|
+
data = json.loads(self.jsData.decode())['d']
|
|
68
|
+
inputNo = 0
|
|
69
|
+
self.status = data['status']['input'][inputNo]['status']
|
|
70
|
+
|
|
71
|
+
edsc_log(self.apiCall, self.params, data)
|
|
72
|
+
|
|
73
|
+
return data
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
class StarQuery(EDSCQueryBase):
|
|
77
|
+
"""
|
|
78
|
+
Query EDSC Systems.
|
|
79
|
+
"""
|
|
80
|
+
apiCall = "GetSystems"
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
class DistanceQuery(EDSCQueryBase):
|
|
84
|
+
"""
|
|
85
|
+
Request distances from EDSC.
|
|
86
|
+
"""
|
|
87
|
+
apiCall = "GetDistances"
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
class SubmissionError(Exception):
|
|
91
|
+
pass
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
class Status(namedtuple('Status', [
|
|
95
|
+
'source', 'code', 'msg', 'lhs', 'rhs'
|
|
96
|
+
])):
|
|
97
|
+
pass
|
|
98
|
+
|
|
99
|
+
class StarSubmissionResult:
|
|
100
|
+
"""
|
|
101
|
+
Translates a response the json we get back from EDSC when
|
|
102
|
+
we submit a StarSubmission into something less awful to
|
|
103
|
+
work with.
|
|
104
|
+
|
|
105
|
+
Attributes:
|
|
106
|
+
valid
|
|
107
|
+
True or False whether the response looked healthy
|
|
108
|
+
|
|
109
|
+
summary
|
|
110
|
+
A summary of the /parse/; currently either "No Data"
|
|
111
|
+
or "OK". This does NOT represent whether the
|
|
112
|
+
submission itself was successful.
|
|
113
|
+
|
|
114
|
+
systems
|
|
115
|
+
A dict{ system name: [msgnum, coords] } for systems which
|
|
116
|
+
were reported as being added or updated. See
|
|
117
|
+
translateCode for converting these to text.
|
|
118
|
+
'coords' is either None if the system hasn't been
|
|
119
|
+
trilaterated yet, or an array of [x,y,z]
|
|
120
|
+
|
|
121
|
+
distances
|
|
122
|
+
A dict{ lhs system: { rhs system: dist } }. It's not
|
|
123
|
+
really very useful but I didn't want to drop the data
|
|
124
|
+
EDSC was sending back to us, just incase.
|
|
125
|
+
|
|
126
|
+
errors
|
|
127
|
+
A list of SubmissionError objects describing problems
|
|
128
|
+
that were encountered, mostly 304 (distance added but
|
|
129
|
+
failed to verify coordinates) and 305 (distance appears
|
|
130
|
+
to be wrong).
|
|
131
|
+
|
|
132
|
+
recheck
|
|
133
|
+
A dict{ system name: dist } for systems that had a 305
|
|
134
|
+
error for a distance to "star". They still appear in
|
|
135
|
+
the 'errors' attribute, but this is a short-cut for
|
|
136
|
+
prompting the user to double check a distance from their
|
|
137
|
+
current p0.
|
|
138
|
+
|
|
139
|
+
"""
|
|
140
|
+
|
|
141
|
+
codeMap = {
|
|
142
|
+
201: "New Entry",
|
|
143
|
+
202: "System CR increased",
|
|
144
|
+
203: "Coordinates calculated",
|
|
145
|
+
301: "Distance added",
|
|
146
|
+
302: "Distance CR increased",
|
|
147
|
+
303: "Added verification",
|
|
148
|
+
304: "OK: Needs more data",
|
|
149
|
+
305: "Distance appears to be wrong",
|
|
150
|
+
401: "CALCULATED",
|
|
151
|
+
402: "No solution found, more data needed"
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
def __init__(self, star, response):
|
|
156
|
+
self.star = star
|
|
157
|
+
self.valid = False
|
|
158
|
+
self.summary = "No Data"
|
|
159
|
+
self.systems = {}
|
|
160
|
+
self.distances = {}
|
|
161
|
+
self.errors = []
|
|
162
|
+
self.recheck = {}
|
|
163
|
+
|
|
164
|
+
# This line righ there, this is just the beginning of
|
|
165
|
+
# why it's necessary to have this complicated of a
|
|
166
|
+
# wrapper for this json data. Don't forget, we have
|
|
167
|
+
# already peeled off some layers of json around this.
|
|
168
|
+
summary = response['status']['input'][0]['status']
|
|
169
|
+
code, msg = int(summary['statusnum']), summary['msg']
|
|
170
|
+
if code != 0:
|
|
171
|
+
self.valid = False
|
|
172
|
+
self.summary = "Error #{}: {}".format(code, msg)
|
|
173
|
+
self.errors.append(Status(
|
|
174
|
+
'status', code, msg, None, None
|
|
175
|
+
))
|
|
176
|
+
return
|
|
177
|
+
|
|
178
|
+
self.valid = True
|
|
179
|
+
self.summary = "OK"
|
|
180
|
+
|
|
181
|
+
# We get back an array of things called 'system', which
|
|
182
|
+
# allows EDSC to tell us that something got added. It
|
|
183
|
+
# could be p0 or it could be one of the refs or it could
|
|
184
|
+
# be something that was sitting in EDSCs distances buffer.
|
|
185
|
+
sysArray = response['status']['system']
|
|
186
|
+
for ent in sysArray:
|
|
187
|
+
sysName = ent['system'].upper()
|
|
188
|
+
code = int(ent['status']['statusnum'])
|
|
189
|
+
msg = ent['status']['msg']
|
|
190
|
+
if code in [201, 202, 203]:
|
|
191
|
+
self.systems[sysName] = (code, None)
|
|
192
|
+
else:
|
|
193
|
+
self.errors.append(Status(
|
|
194
|
+
'system', code, msg, sysName, None,
|
|
195
|
+
))
|
|
196
|
+
|
|
197
|
+
# Now the pair-wise distance checks. These are either not
|
|
198
|
+
# very useful/duplicate the systems list, or they tell us
|
|
199
|
+
# about conflicts with pre-existing data.
|
|
200
|
+
# Pairs which generate 305 (distance wrong) that relate
|
|
201
|
+
# to P0 will be added to "recheck" so that the caller can
|
|
202
|
+
# ask their user to provide sanity values.
|
|
203
|
+
distArray = response['status']['dist']
|
|
204
|
+
errPairs = set()
|
|
205
|
+
for ent in distArray:
|
|
206
|
+
lhsName = ent['system1'].upper()
|
|
207
|
+
rhsName = ent['system2'].upper()
|
|
208
|
+
code = int(ent['status']['statusnum'])
|
|
209
|
+
msg = ent['status']['msg']
|
|
210
|
+
if code in [301, 302, 303, 304]:
|
|
211
|
+
if lhsName not in self.distances:
|
|
212
|
+
self.distances[lhsName] = {}
|
|
213
|
+
try:
|
|
214
|
+
rhsDists = self.distances[rhsName]
|
|
215
|
+
if lhsName in rhsDists:
|
|
216
|
+
continue
|
|
217
|
+
except KeyError:
|
|
218
|
+
pass
|
|
219
|
+
dist = float(ent['dist'])
|
|
220
|
+
self.distances[lhsName][rhsName] = dist
|
|
221
|
+
if lhsName not in self.systems:
|
|
222
|
+
self.systems[lhsName] = (code, None)
|
|
223
|
+
else:
|
|
224
|
+
if (lhsName,rhsName,code) in errPairs:
|
|
225
|
+
continue
|
|
226
|
+
if (rhsName,lhsName,code) in errPairs:
|
|
227
|
+
continue
|
|
228
|
+
errPairs.add((lhsName,rhsName,code))
|
|
229
|
+
errPairs.add((rhsName,lhsName,code))
|
|
230
|
+
self.errors.append(Status(
|
|
231
|
+
'dist', code, msg, lhsName, rhsName,
|
|
232
|
+
))
|
|
233
|
+
if code == 305:
|
|
234
|
+
if lhsName == star:
|
|
235
|
+
self.recheck[rhsName] = ent['dist']
|
|
236
|
+
elif rhsName == star:
|
|
237
|
+
self.recheck[lhsName] = ent['dist']
|
|
238
|
+
|
|
239
|
+
# Finally we look thru the trilat array which is telling us
|
|
240
|
+
# how the search for p0's coordinates went on a per-ref
|
|
241
|
+
# basis.
|
|
242
|
+
#
|
|
243
|
+
# Successful trilateration will result in an array of
|
|
244
|
+
# trilats that contain code 401 and the coordinates of
|
|
245
|
+
# p0, one per ref. Since we can't tell what the *other*
|
|
246
|
+
# system is, this is a bit stupid. Probably a bug in edsc.
|
|
247
|
+
triArray = response['status']['trilat']
|
|
248
|
+
for ent in triArray:
|
|
249
|
+
sysName = ent['system'].upper()
|
|
250
|
+
code = int(ent['status']['statusnum'])
|
|
251
|
+
if code == 401:
|
|
252
|
+
try:
|
|
253
|
+
system = self.systems[sysName]
|
|
254
|
+
if system[1] is not None:
|
|
255
|
+
continue
|
|
256
|
+
except KeyError:
|
|
257
|
+
system = (code, None)
|
|
258
|
+
assert system[1] is None
|
|
259
|
+
coord = ent['coord']
|
|
260
|
+
x, y, z = coord['x'], coord['y'], coord['z']
|
|
261
|
+
self.systems[sysName] = (system[0], [x,y,z])
|
|
262
|
+
elif code == 402:
|
|
263
|
+
pass
|
|
264
|
+
else:
|
|
265
|
+
self.errors.append(Status(
|
|
266
|
+
'trilat', code, msg, sysName, None,
|
|
267
|
+
))
|
|
268
|
+
|
|
269
|
+
|
|
270
|
+
def __str__(self):
|
|
271
|
+
if not self.valid:
|
|
272
|
+
return "ERROR: {}".format(self.summary)
|
|
273
|
+
|
|
274
|
+
text = ""
|
|
275
|
+
|
|
276
|
+
if not self.errors:
|
|
277
|
+
text += "Success.\n"
|
|
278
|
+
|
|
279
|
+
if self.systems:
|
|
280
|
+
text += "+Updates:\n"
|
|
281
|
+
sysNames = list(self.systems.keys())
|
|
282
|
+
sysNames.sort(key=lambda s: self.systems[s][0])
|
|
283
|
+
for sysName in sysNames:
|
|
284
|
+
code, coords = self.systems[sysName]
|
|
285
|
+
sysText = self.translateCode(code)
|
|
286
|
+
if coords is not None:
|
|
287
|
+
sysText += str(coords)
|
|
288
|
+
text += "|- {:.<30s} {}\n".format(
|
|
289
|
+
sysName, sysText,
|
|
290
|
+
)
|
|
291
|
+
text += "\n"
|
|
292
|
+
|
|
293
|
+
if self.errors:
|
|
294
|
+
text += "+Problems:\n"
|
|
295
|
+
errors = sorted(self.errors, key=lambda e: e.rhs or "")
|
|
296
|
+
errors.sort(key=lambda e: e.lhs or "")
|
|
297
|
+
errors.sort(key=lambda e: e.code)
|
|
298
|
+
for err in errors:
|
|
299
|
+
text += "|- {:.<30s} #{} {}".format(
|
|
300
|
+
err.lhs,
|
|
301
|
+
err.code,
|
|
302
|
+
self.translateCode(err.code),
|
|
303
|
+
)
|
|
304
|
+
if err.rhs:
|
|
305
|
+
text += " <-> " + err.rhs
|
|
306
|
+
text += "\n"
|
|
307
|
+
|
|
308
|
+
return text
|
|
309
|
+
|
|
310
|
+
def translateCode(self, code):
|
|
311
|
+
try:
|
|
312
|
+
return self.codeMap[code]
|
|
313
|
+
except KeyError:
|
|
314
|
+
return "Error #{} (unknown)".format(code)
|
|
315
|
+
|
|
316
|
+
|
|
317
|
+
|
|
318
|
+
class StarSubmission:
|
|
319
|
+
baseURL = "http://edstarcoordinator.com/api.asmx/"
|
|
320
|
+
apiCall = "SubmitDistances"
|
|
321
|
+
|
|
322
|
+
def __init__(
|
|
323
|
+
self, star,
|
|
324
|
+
test=False, commander=None,
|
|
325
|
+
refs=None, distances=None,
|
|
326
|
+
):
|
|
327
|
+
assert isinstance(star, str)
|
|
328
|
+
assert isinstance(test, bool)
|
|
329
|
+
if refs:
|
|
330
|
+
assert isinstance(refs, list)
|
|
331
|
+
self.test = test
|
|
332
|
+
self.name = star.upper()
|
|
333
|
+
self.refs = refs or []
|
|
334
|
+
if distances:
|
|
335
|
+
if isinstance(distances, list):
|
|
336
|
+
for name, dist in distances:
|
|
337
|
+
self.add_distance(name, dist)
|
|
338
|
+
elif isinstance(distances, dict):
|
|
339
|
+
for name, dist in distances.items():
|
|
340
|
+
self.add_distance(name, dist)
|
|
341
|
+
else:
|
|
342
|
+
raise SubmissionError("Invalid distances parameter")
|
|
343
|
+
if commander:
|
|
344
|
+
self.commander = commander
|
|
345
|
+
|
|
346
|
+
def __repr__(self):
|
|
347
|
+
return (
|
|
348
|
+
"StarSubmission("
|
|
349
|
+
"test={}, star=\"{}\", commander=\"{}\", refs={}"
|
|
350
|
+
")".format(
|
|
351
|
+
self.test,
|
|
352
|
+
self.name,
|
|
353
|
+
self.commander,
|
|
354
|
+
self.refs,
|
|
355
|
+
)
|
|
356
|
+
)
|
|
357
|
+
|
|
358
|
+
|
|
359
|
+
def add_distance(self, name, dist):
|
|
360
|
+
assert isinstance(name, str)
|
|
361
|
+
assert isinstance(dist, (float, int))
|
|
362
|
+
assert name.upper() != self.name
|
|
363
|
+
|
|
364
|
+
name = name.upper()
|
|
365
|
+
for i, ref in enumerate(self.refs):
|
|
366
|
+
if ref['name'] == name:
|
|
367
|
+
ref['dist'] = dist
|
|
368
|
+
return
|
|
369
|
+
|
|
370
|
+
self.refs.append({'name': name, 'dist': dist})
|
|
371
|
+
|
|
372
|
+
|
|
373
|
+
def submit(self):
|
|
374
|
+
assert len(self.refs) != 0
|
|
375
|
+
|
|
376
|
+
headers = { 'Content-Type': 'application/json; charset=utf-8' }
|
|
377
|
+
data = {
|
|
378
|
+
'data': {
|
|
379
|
+
'test': self.test,
|
|
380
|
+
'ver': 2,
|
|
381
|
+
'p0': { 'name': self.name },
|
|
382
|
+
'refs': self.refs
|
|
383
|
+
},
|
|
384
|
+
}
|
|
385
|
+
if self.commander:
|
|
386
|
+
data['data']['commander'] = self.commander
|
|
387
|
+
|
|
388
|
+
jsonData = json.dumps(data, indent=None, separators=(',', ':'))
|
|
389
|
+
|
|
390
|
+
url = self.baseURL + self.apiCall
|
|
391
|
+
req = requests.post(
|
|
392
|
+
url,
|
|
393
|
+
headers=headers,
|
|
394
|
+
data=jsonData
|
|
395
|
+
)
|
|
396
|
+
resp = req.text
|
|
397
|
+
if not resp.startswith('{'):
|
|
398
|
+
edsc_log(self.apiCall, repr(self), error=resp)
|
|
399
|
+
raise SubmissionError("Server Side Error: " + resp)
|
|
400
|
+
|
|
401
|
+
try:
|
|
402
|
+
respData = json.loads(resp)
|
|
403
|
+
except Exception:
|
|
404
|
+
edsc_log(self.apiCall, repr(self), error=resp)
|
|
405
|
+
raise SubmissionError("Invalid server response: " + resp)
|
|
406
|
+
edsc_log(self.apiCall, repr(self), respData)
|
|
407
|
+
try:
|
|
408
|
+
innerData = respData['d']
|
|
409
|
+
except KeyError:
|
|
410
|
+
raise SubmissionError("Server Error: " + resp)
|
|
411
|
+
|
|
412
|
+
return innerData
|
|
413
|
+
|
|
414
|
+
|
|
415
|
+
if __name__ == "__main__":
|
|
416
|
+
print("Requesting recent, non-test, coords-known, cr >= 2 stars")
|
|
417
|
+
edsq = StarQuery(test=False, confidence=2, known=1)
|
|
418
|
+
data = edsq.fetch()
|
|
419
|
+
|
|
420
|
+
if edsq.status['statusnum'] != 0:
|
|
421
|
+
raise Exception("Query failed: {} ({})".format(
|
|
422
|
+
edsq.status['msg'],
|
|
423
|
+
edsq.status['statusnum'],
|
|
424
|
+
))
|
|
425
|
+
|
|
426
|
+
date = data['date']
|
|
427
|
+
systems = data['systems']
|
|
428
|
+
|
|
429
|
+
for sysinfo in systems:
|
|
430
|
+
print("{:<30s} {:11f} {:11f} {:11f} {}".format(
|
|
431
|
+
sysinfo['name'].upper(),
|
|
432
|
+
sysinfo['coord'][0],
|
|
433
|
+
sysinfo['coord'][1],
|
|
434
|
+
sysinfo['coord'][2],
|
|
435
|
+
sysinfo['createdate'],
|
|
436
|
+
))
|
|
437
|
+
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
#! /usr/bin/env python
|
|
2
|
+
|
|
3
|
+
"""
|
|
4
|
+
based on edsc.py
|
|
5
|
+
uses EDSM - https://www.edsm.net/api
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import json
|
|
9
|
+
import requests
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def edsm_log(apiCall, url, params, jsonData=None, error=None):
|
|
13
|
+
"""
|
|
14
|
+
Logs an EDSM query and it's response to tmp/edsm.log
|
|
15
|
+
"""
|
|
16
|
+
try:
|
|
17
|
+
with open("tmp/edsm.log", "a", encoding="utf-8") as fh:
|
|
18
|
+
print('-'*70, file=fh)
|
|
19
|
+
print("API:", apiCall, file=fh)
|
|
20
|
+
print("REQ:", str(params), file=fh)
|
|
21
|
+
print("URL:", url, file=fh)
|
|
22
|
+
if jsonData:
|
|
23
|
+
print("REP:", json.dumps(jsonData, indent=1), file=fh)
|
|
24
|
+
if error:
|
|
25
|
+
print("ERR:", error)
|
|
26
|
+
print(file=fh)
|
|
27
|
+
except FileNotFoundError:
|
|
28
|
+
pass
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class EDSMQueryBase:
|
|
32
|
+
"""
|
|
33
|
+
Base class for creating an EDSM Query class, do not use directly.
|
|
34
|
+
|
|
35
|
+
Derived class must declare "apiCall" which is appended to baseURL
|
|
36
|
+
to form the query URL.
|
|
37
|
+
"""
|
|
38
|
+
|
|
39
|
+
baseURL = "https://www.edsm.net/api-v1/"
|
|
40
|
+
|
|
41
|
+
def __init__(self, log=False, known=1, coords=1, **kwargs):
|
|
42
|
+
self.log = log
|
|
43
|
+
self.url = self.baseURL + self.apiCall
|
|
44
|
+
self.params = {
|
|
45
|
+
'known': known,
|
|
46
|
+
'coords': coords,
|
|
47
|
+
}
|
|
48
|
+
for k, v in kwargs.items():
|
|
49
|
+
self.params[k] = v
|
|
50
|
+
|
|
51
|
+
def fetch(self):
|
|
52
|
+
res = requests.get(self.url, self.params)
|
|
53
|
+
self.status = res.status_code
|
|
54
|
+
|
|
55
|
+
try:
|
|
56
|
+
data = res.json()
|
|
57
|
+
except:
|
|
58
|
+
data = None
|
|
59
|
+
pass
|
|
60
|
+
|
|
61
|
+
if self.log:
|
|
62
|
+
edsm_log(self.apiCall, res.url, self.params, data)
|
|
63
|
+
|
|
64
|
+
return data
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
class StarQuerySingle(EDSMQueryBase):
|
|
68
|
+
"""
|
|
69
|
+
Query EDSM System.
|
|
70
|
+
|
|
71
|
+
provide:
|
|
72
|
+
systemName
|
|
73
|
+
"""
|
|
74
|
+
apiCall = "system"
|
|
75
|
+
|
|
76
|
+
class StarQueryMulti(EDSMQueryBase):
|
|
77
|
+
"""
|
|
78
|
+
Query EDSM Systems.
|
|
79
|
+
|
|
80
|
+
provide:
|
|
81
|
+
systemName
|
|
82
|
+
"""
|
|
83
|
+
apiCall = "systems"
|
|
84
|
+
|
|
85
|
+
class StarQuerySphere(EDSMQueryBase):
|
|
86
|
+
"""
|
|
87
|
+
Query EDSM Systems.
|
|
88
|
+
provide:
|
|
89
|
+
systemName or center-coords as x, y and z
|
|
90
|
+
radius N.NN
|
|
91
|
+
"""
|
|
92
|
+
apiCall = "sphere-systems"
|
|
93
|
+
|
|
94
|
+
class DistanceQuery(EDSMQueryBase):
|
|
95
|
+
"""
|
|
96
|
+
Request distances from EDSM.
|
|
97
|
+
"""
|
|
98
|
+
apiCall = "distances"
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
if __name__ == "__main__":
|
|
102
|
+
print("Requesting Azrael, coords-known")
|
|
103
|
+
edsq = StarQuerySingle(log=True, systemName="Azrael")
|
|
104
|
+
data = edsq.fetch()
|
|
105
|
+
print("{:<30s} {:11f} {:11f} {:11f}".format(
|
|
106
|
+
data['name'].upper(),
|
|
107
|
+
data['coords']['x'],
|
|
108
|
+
data['coords']['y'],
|
|
109
|
+
data['coords']['z'],
|
|
110
|
+
))
|
|
111
|
+
|
|
112
|
+
print("Requesting 10ly sphere of Sol, coords-known")
|
|
113
|
+
edsq = StarQuerySphere(systemName="Sol", radius=10)
|
|
114
|
+
data = edsq.fetch()
|
|
115
|
+
for sysinfo in data:
|
|
116
|
+
print("{:<30s} {:11f} {:11f} {:11f}".format(
|
|
117
|
+
sysinfo['name'].upper(),
|
|
118
|
+
sysinfo['coords']['x'],
|
|
119
|
+
sysinfo['coords']['y'],
|
|
120
|
+
sysinfo['coords']['z'],
|
|
121
|
+
))
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import csvexport
|
|
2
|
+
import misc.eddb
|
|
3
|
+
import tradedb
|
|
4
|
+
|
|
5
|
+
# Build an ID=>Name mapping for EDDB systems
|
|
6
|
+
tdb = tradedb.TradeDB()
|
|
7
|
+
systems = {}
|
|
8
|
+
tdSysLookup = tdb.systemByName.get
|
|
9
|
+
for s in misc.eddb.SystemsQuery():
|
|
10
|
+
tdSys = tdSysLookup(s['name'].upper(), None)
|
|
11
|
+
if tdSys and tdSys.stations:
|
|
12
|
+
systems[s['id']] = { stn.dbname.upper(): stn for stn in tdSys.stations }
|
|
13
|
+
|
|
14
|
+
def matching_stations():
|
|
15
|
+
# generator that searches the eddb station set for entries that
|
|
16
|
+
# match tdb entries and yields the tuple (tdStn, eddbStn)
|
|
17
|
+
|
|
18
|
+
for eddbStn in misc.eddb.StationsQuery():
|
|
19
|
+
stationList = systems.get(eddbStn['system_id'], None)
|
|
20
|
+
if not stationList:
|
|
21
|
+
continue
|
|
22
|
+
name = eddbStn['name'].upper()
|
|
23
|
+
tdStn = stationList.get(name, None)
|
|
24
|
+
if tdStn:
|
|
25
|
+
yield tdStn, eddbStn
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
updateStation = tdb.updateLocalStation
|
|
29
|
+
|
|
30
|
+
bool_trans = { None: '?', 0: 'N', 1: 'Y' }
|
|
31
|
+
|
|
32
|
+
updates = 0
|
|
33
|
+
for tdStn, eddbStn in matching_stations():
|
|
34
|
+
mps = eddbStn['max_landing_pad_size'] or '?'
|
|
35
|
+
if updateStation(
|
|
36
|
+
station=tdStn,
|
|
37
|
+
lsFromStar=eddbStn['distance_to_star'],
|
|
38
|
+
maxPadSize=mps,
|
|
39
|
+
market=bool_trans[eddbStn['has_commodities']],
|
|
40
|
+
blackMarket=bool_trans[eddbStn['has_blackmarket']],
|
|
41
|
+
shipyard=bool_trans[eddbStn['has_shipyard']],
|
|
42
|
+
outfitting=bool_trans[eddbStn['has_outfitting']],
|
|
43
|
+
rearm=bool_trans[eddbStn['has_rearm']],
|
|
44
|
+
refuel=bool_trans[eddbStn['has_refuel']],
|
|
45
|
+
repair=bool_trans[eddbStn['has_repair']],
|
|
46
|
+
modified='now',
|
|
47
|
+
commit=False,
|
|
48
|
+
):
|
|
49
|
+
updates += 1
|
|
50
|
+
|
|
51
|
+
if updates:
|
|
52
|
+
tdb.getDB().commit()
|
|
53
|
+
csvexport.exportTableToFile(tdb, tdb.tdenv, "Station")
|
|
54
|
+
print("Updated Station.csv: {} updates".format(updates))
|