mirror of
https://github.com/EDCD/EDMarketConnector.git
synced 2025-04-13 07:47:14 +03:00
Add command-line program.
This commit is contained in:
parent
9cad8aed9d
commit
b0f60af38d
114
EDMC.py
Executable file
114
EDMC.py
Executable file
@ -0,0 +1,114 @@
|
||||
#!/usr/bin/python
|
||||
#
|
||||
# Command-line interface. Requires prior setup through the GUI.
|
||||
#
|
||||
|
||||
import argparse
|
||||
import sys
|
||||
from time import time, sleep
|
||||
|
||||
import l10n
|
||||
import companion
|
||||
import bpc
|
||||
import outfitting
|
||||
import loadout
|
||||
import coriolis
|
||||
import shipyard
|
||||
import eddb
|
||||
import prefs
|
||||
from config import appcmdname, appversion, config
|
||||
|
||||
|
||||
l10n.Translations().install_dummy()
|
||||
EDDB = eddb.EDDB()
|
||||
|
||||
SERVER_RETRY = 5 # retry pause for Companion servers [s]
|
||||
EXIT_SUCCESS, EXIT_SERVER, EXIT_CREDENTIALS, EXIT_VERIFICATION, EXIT_NOT_DOCKED, EXIT_SYS_ERR = range(6)
|
||||
|
||||
try:
|
||||
# arg parsing
|
||||
parser = argparse.ArgumentParser(prog=appcmdname, description='Prints the current system and station (if docked) to stdout and optionally writes ship loadout and/or station data to file. Requires prior setup through the accompanying GUI app.')
|
||||
parser.add_argument('-v', '--version', help='print program version and exit', action='store_const', const=True)
|
||||
parser.add_argument('-c', metavar='FILE', help='write ship loadout to FILE in Coriolis json format')
|
||||
parser.add_argument('-e', metavar='FILE', help='write ship loadout to FILE in E:D Shipyard format')
|
||||
parser.add_argument('-m', metavar='FILE', help='write station commodity market data to FILE in CSV format')
|
||||
parser.add_argument('-o', metavar='FILE', help='write station outfitting data to FILE in CSV format')
|
||||
parser.add_argument('-s', metavar='FILE', help='write station shipyard data to FILE in CSV format')
|
||||
args = parser.parse_args()
|
||||
if args.version:
|
||||
print '%.2f' % (float(''.join(appversion.split('.')[:3])) / 100) # just first three digits
|
||||
sys.exit(EXIT_SUCCESS)
|
||||
|
||||
session = companion.Session()
|
||||
session.login(config.get('username'), config.get('password'))
|
||||
|
||||
querytime = int(time())
|
||||
data = session.query()
|
||||
config.set('querytime', querytime)
|
||||
|
||||
# Validation
|
||||
if not data.get('commander') or not data['commander'].get('name','').strip():
|
||||
sys.stderr.write('Who are you?!\n')
|
||||
sys.exit(EXIT_SERVER)
|
||||
elif not data.get('lastSystem') or not data['lastSystem'].get('name','').strip() or not data.get('lastStarport') or not data['lastStarport'].get('name','').strip():
|
||||
sys.stderr.write('Where are you?!\n') # Shouldn't happen
|
||||
sys.exit(EXIT_SERVER)
|
||||
elif not data.get('ship') or not data['ship'].get('modules') or not data['ship'].get('name','').strip():
|
||||
sys.stderr.write('What are you flying?!\n') # Shouldn't happen
|
||||
sys.exit(EXIT_SERVER)
|
||||
elif (args.m or args.o or args.s) and not data['commander'].get('docked'):
|
||||
print data['lastSystem']['name']
|
||||
sys.stderr.write("You're not docked at a station!\n")
|
||||
sys.exit(EXIT_NOT_DOCKED)
|
||||
|
||||
# stuff we can do when not docked
|
||||
if args.c:
|
||||
coriolis.export(data, args.c)
|
||||
if args.e:
|
||||
loadout.export(data, args.e)
|
||||
|
||||
# Finally - the data looks sane and we're docked at a station
|
||||
print '%s,%s' % (data['lastSystem']['name'], data['lastStarport']['name'])
|
||||
(station_id, has_shipyard, has_outfitting) = EDDB.station(data['lastSystem']['name'], data['lastStarport']['name'])
|
||||
|
||||
if not data['lastStarport'].get('commodities') and not has_outfitting and not has_shipyard:
|
||||
sys.stderr.write("Station doesn't have anything!\n")
|
||||
sys.exit(EXIT_SUCCESS)
|
||||
|
||||
if args.m:
|
||||
if data['lastStarport'].get('commodities'):
|
||||
# Fixup anomalies in the commodity data
|
||||
session.fixup(data['lastStarport']['commodities'])
|
||||
bpc.export(data, True, args.m)
|
||||
else:
|
||||
sys.stderr.write("Station doesn't have a market\n")
|
||||
|
||||
if args.o:
|
||||
if has_outfitting:
|
||||
outfitting.export(data, args.o)
|
||||
else:
|
||||
sys.stderr.write("Station doesn't supply outfitting\n")
|
||||
|
||||
if args.s:
|
||||
if has_shipyard:
|
||||
if not data['lastStarport'].get('ships'):
|
||||
sleep(SERVER_RETRY)
|
||||
data = session.query()
|
||||
if data['lastStarport'].get('ships'):
|
||||
shipyard.export(data, args.s)
|
||||
else:
|
||||
sys.stderr.write("Couldn't retrieve shipyard info\n")
|
||||
else:
|
||||
sys.stderr.write("Station doesn't have a shipyard\n")
|
||||
|
||||
sys.exit(EXIT_SUCCESS)
|
||||
|
||||
except companion.ServerError as e:
|
||||
sys.stderr.write('Server is down\n')
|
||||
sys.exit(EXIT_SERVER_DOWN)
|
||||
except companion.CredentialsError as e:
|
||||
sys.stderr.write('Invalid Credentials\n')
|
||||
sys.exit(EXIT_CREDENTIALS)
|
||||
except companion.VerificationRequired:
|
||||
sys.stderr.write('Verification Required\n')
|
||||
sys.exit(EXIT_VERIFICATION)
|
@ -84,6 +84,9 @@
|
||||
<Component Guid="{9A0CB8A2-7167-492F-A185-0BDF7E9F5C01}">
|
||||
<File KeyPath="yes" Source="SourceDir\EDMarketConnector.VisualElementsManifest.xml" />
|
||||
</Component>
|
||||
<Component Guid="*">
|
||||
<File KeyPath="yes" Source="SourceDir\EDMC.exe" />
|
||||
</Component>
|
||||
<Component Guid="*">
|
||||
<File KeyPath="yes" Source="SourceDir\fr.strings" />
|
||||
</Component>
|
||||
@ -359,6 +362,7 @@
|
||||
<ComponentRef Id="cacert.pem" />
|
||||
<ComponentRef Id="EDMarketConnector.ico" />
|
||||
<ComponentRef Id="EDMarketConnector.VisualElementsManifest.xml" />
|
||||
<ComponentRef Id="EDMC.exe" />
|
||||
<ComponentRef Id="fr.strings" />
|
||||
<ComponentRef Id="it.strings" />
|
||||
<ComponentRef Id="library.zip" />
|
||||
|
28
README.md
28
README.md
@ -103,6 +103,34 @@ Linux:
|
||||
* Requires the Python “imaging-tk”, “iniparse” and “requests” modules. On Debian-based systems install these with `sudo apt-get install python-imaging-tk python-iniparse python-requests` .
|
||||
* Run with `./EDMarketConnector.py` .
|
||||
|
||||
Command-line
|
||||
--------
|
||||
|
||||
The command-line program `EDMC.py` writes the current system and station (if docked) to stdout and optionally
|
||||
writes ship loadout and/or station data to file. This program requires that the user has performed [setup](#setup) and verification through the app.
|
||||
|
||||
Arguments:
|
||||
|
||||
```
|
||||
-h, --help show this help message and exit
|
||||
-v, --version print program version and exit
|
||||
-c FILE write ship loadout to FILE in Coriolis json format
|
||||
-e FILE write ship loadout to FILE in E:D Shipyard format
|
||||
-m FILE write station commodity market data to FILE in CSV format
|
||||
-o FILE write station outfitting data to FILE in CSV format
|
||||
-s FILE write station shipyard data to FILE in CSV format
|
||||
```
|
||||
|
||||
The program returns one of the following exit codes. Further information may be written to stderr.
|
||||
<ol start="0">
|
||||
<li>Success. Note that this doesn't necesssarily mean that any requested output files have been produced - for example if the current station doesn't support the facilities for which data was requested.</li>
|
||||
<li>Server is down.</li>
|
||||
<li>Invalid Credentials.</li>
|
||||
<li>Verification Required.</li>
|
||||
<li>Not docked. You have requested station data but the user is not docked at a station.</li>
|
||||
<li>I/O or other OS error.</li>
|
||||
</ol>
|
||||
|
||||
|
||||
Packaging for distribution
|
||||
--------
|
||||
|
5
bpc.py
5
bpc.py
@ -14,11 +14,12 @@ bracketmap = { 0: '',
|
||||
2: 'Med',
|
||||
3: 'High', }
|
||||
|
||||
def export(data, csv=False):
|
||||
def export(data, csv=False, filename=None):
|
||||
|
||||
querytime = config.getint('querytime') or int(time.time())
|
||||
|
||||
filename = join(config.get('outdir'), '%s.%s.%s.%s' % (data['lastSystem']['name'].strip(), data['lastStarport']['name'].strip(), time.strftime('%Y-%m-%dT%H.%M.%S', time.localtime(querytime)), csv and 'csv' or 'bpc'))
|
||||
if not filename:
|
||||
filename = join(config.get('outdir'), '%s.%s.%s.%s' % (data['lastSystem']['name'].strip(), data['lastStarport']['name'].strip(), time.strftime('%Y-%m-%dT%H.%M.%S', time.localtime(querytime)), csv and 'csv' or 'bpc'))
|
||||
|
||||
timestamp = time.strftime('%Y-%m-%dT%H:%M:%SZ', time.gmtime(querytime))
|
||||
header = 'System;Station;Commodity;Sell;Buy;Demand;;Supply;;Date;\n'
|
||||
|
32
companion.py
32
companion.py
@ -1,6 +1,3 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
import requests
|
||||
from collections import defaultdict
|
||||
from cookielib import LWPCookieJar
|
||||
@ -14,6 +11,7 @@ import time
|
||||
if __debug__:
|
||||
from traceback import print_exc
|
||||
|
||||
from shipyard import ship_map
|
||||
from config import config
|
||||
|
||||
holdoff = 60 # be nice
|
||||
@ -47,34 +45,6 @@ commodity_map= {
|
||||
'Terrain Enrichment Systems' : 'Land Enrichment Systems',
|
||||
}
|
||||
|
||||
ship_map = {
|
||||
'adder' : 'Adder',
|
||||
'anaconda' : 'Anaconda',
|
||||
'asp' : 'Asp',
|
||||
'cobramkiii' : 'Cobra Mk III',
|
||||
'diamondback' : 'Diamondback Scout',
|
||||
'diamondbackxl' : 'Diamondback Explorer',
|
||||
'eagle' : 'Eagle',
|
||||
'empire_courier' : 'Imperial Courier',
|
||||
'empire_eagle' : 'Imperial Eagle',
|
||||
'empire_fighter' : 'Imperial Fighter',
|
||||
'empire_trader' : 'Imperial Clipper',
|
||||
'federation_dropship' : 'Federal Dropship',
|
||||
'federation_dropship_mkii' : 'Federal Assault Ship',
|
||||
'federation_gunship' : 'Federal Gunship',
|
||||
'federation_fighter' : 'F63 Condor',
|
||||
'ferdelance' : 'Fer-de-Lance',
|
||||
'hauler' : 'Hauler',
|
||||
'orca' : 'Orca',
|
||||
'python' : 'Python',
|
||||
'sidewinder' : 'Sidewinder',
|
||||
'type6' : 'Type-6 Transporter',
|
||||
'type7' : 'Type-7 Transporter',
|
||||
'type9' : 'Type-9 Heavy',
|
||||
'viper' : 'Viper',
|
||||
'vulture' : 'Vulture',
|
||||
}
|
||||
|
||||
|
||||
# Companion API sometimes returns an array as a json array, sometimes as a json object indexed by "int".
|
||||
# This seems to depend on whether the there are 'gaps' in the Cmdr's data - i.e. whether the array is sparse.
|
||||
|
@ -7,6 +7,7 @@ from sys import platform
|
||||
appname = 'EDMarketConnector'
|
||||
applongname = 'E:D Market Connector'
|
||||
appversion = '1.8.3.0'
|
||||
appcmdname = 'EDMC'
|
||||
|
||||
|
||||
if platform=='darwin':
|
||||
|
13
coriolis.py
13
coriolis.py
@ -9,7 +9,7 @@ import time
|
||||
|
||||
from config import config
|
||||
import outfitting
|
||||
import companion
|
||||
import shipyard
|
||||
|
||||
|
||||
slot_map = {
|
||||
@ -33,7 +33,7 @@ slot_map = {
|
||||
# https://raw.githubusercontent.com/jamesremuscat/EDDN/master/schemas/outfitting-v1.0-draft.json
|
||||
# http://cdn.coriolis.io/schemas/ship-loadout/2.json
|
||||
|
||||
ship_map = dict(companion.ship_map)
|
||||
ship_map = dict(shipyard.ship_map)
|
||||
ship_map['asp'] = 'Asp Explorer'
|
||||
|
||||
standard_map = OrderedDict([ # in output order
|
||||
@ -74,11 +74,11 @@ fixup_map = {
|
||||
}
|
||||
|
||||
|
||||
def export(data):
|
||||
def export(data, filename=None):
|
||||
|
||||
querytime = config.getint('querytime') or int(time.time())
|
||||
|
||||
ship = companion.ship_map.get(data['ship']['name'].lower(), data['ship']['name'])
|
||||
ship = shipyard.ship_map.get(data['ship']['name'].lower(), data['ship']['name'])
|
||||
|
||||
loadout = OrderedDict([ # Mimic Coriolis export ordering
|
||||
('$schema', 'http://cdn.coriolis.io/schemas/ship-loadout/2.json#'),
|
||||
@ -170,6 +170,11 @@ def export(data):
|
||||
# Construct description
|
||||
string = json.dumps(loadout, indent=2)
|
||||
|
||||
if filename:
|
||||
with open(filename, 'wt') as h:
|
||||
h.write(string)
|
||||
return
|
||||
|
||||
# Look for last ship of this type
|
||||
regexp = re.compile(re.escape(ship) + '\.\d\d\d\d\-\d\d\-\d\dT\d\d\.\d\d\.\d\d\.json')
|
||||
oldfiles = sorted([x for x in os.listdir(config.get('outdir')) if regexp.match(x)])
|
||||
|
2
eddn.py
2
eddn.py
@ -9,7 +9,7 @@ from sys import platform
|
||||
import time
|
||||
|
||||
from config import applongname, appversion, config
|
||||
from companion import ship_map
|
||||
from shipyard import ship_map
|
||||
import outfitting
|
||||
|
||||
upload = 'http://eddn-gateway.elite-markets.net:8080/upload/'
|
||||
|
@ -8,7 +8,8 @@ from sys import platform
|
||||
import time
|
||||
|
||||
from config import config
|
||||
from companion import ship_map, commodity_map
|
||||
from companion import commodity_map
|
||||
from shipyard import ship_map
|
||||
|
||||
|
||||
logfile = None
|
||||
|
6
l10n.py
6
l10n.py
@ -19,6 +19,10 @@ class Translations:
|
||||
def __init__(self):
|
||||
self.translations = {}
|
||||
|
||||
def install_dummy(self):
|
||||
# For when translation is not desired or not available
|
||||
__builtin__.__dict__['_'] = lambda x: unicode(x).replace(u'{CR}', u'\n') # Promote strings to Unicode for consistency
|
||||
|
||||
def install(self):
|
||||
path = join(self.respath(), 'L10n')
|
||||
available = self.available()
|
||||
@ -38,7 +42,7 @@ class Translations:
|
||||
lang = Translations.FALLBACK
|
||||
|
||||
if lang not in self.available():
|
||||
__builtin__.__dict__['_'] = lambda x: unicode(x).replace(u'{CR}', u'\n') # Promote strings to Unicode for consistency
|
||||
self.install_dummy()
|
||||
else:
|
||||
regexp = re.compile(r'\s*"([^"]+)"\s*=\s*"([^"]+)"\s*;\s*$')
|
||||
comment= re.compile(r'\s*/\*.*\*/\s*$')
|
||||
|
@ -8,7 +8,7 @@ import time
|
||||
|
||||
from config import config
|
||||
import outfitting
|
||||
from companion import ship_map
|
||||
from shipyard import ship_map
|
||||
|
||||
|
||||
# API slot names to E:D Shipyard slot names
|
||||
@ -28,7 +28,7 @@ slot_map = {
|
||||
'fueltank' : 'FS',
|
||||
}
|
||||
|
||||
def export(data):
|
||||
def export(data, filename=None):
|
||||
|
||||
def class_rating(module):
|
||||
if 'guidance' in module:
|
||||
@ -86,6 +86,11 @@ def export(data):
|
||||
string += '%s: %s\n' % (slot, name)
|
||||
string += '---\nCargo : %d T\nFuel : %d T\n' % (data['ship']['cargo']['capacity'], data['ship']['fuel']['capacity'])
|
||||
|
||||
if filename:
|
||||
with open(filename, 'wt') as h:
|
||||
h.write(string)
|
||||
return
|
||||
|
||||
# Look for last ship of this type
|
||||
regexp = re.compile(re.escape(ship) + '\.\d\d\d\d\-\d\d\-\d\dT\d\d\.\d\d\.\d\d\.txt')
|
||||
oldfiles = sorted([x for x in os.listdir(config.get('outdir')) if regexp.match(x)])
|
||||
|
@ -8,8 +8,10 @@ import json
|
||||
import os
|
||||
from os.path import exists, isfile
|
||||
import sys
|
||||
import time
|
||||
|
||||
from companion import ship_map
|
||||
from shipyard import ship_map
|
||||
from config import config
|
||||
|
||||
|
||||
outfile = 'outfitting.csv'
|
||||
@ -322,6 +324,31 @@ def lookup(module):
|
||||
return new
|
||||
|
||||
|
||||
def export(data, filename):
|
||||
|
||||
querytime = config.getint('querytime') or int(time.time())
|
||||
|
||||
assert data['lastSystem'].get('name')
|
||||
assert data['lastStarport'].get('name')
|
||||
|
||||
timestamp = time.strftime('%Y-%m-%dT%H:%M:%SZ', time.gmtime(querytime))
|
||||
header = 'System,Station,Category,Name,Mount,Guidance,Ship,Class,Rating,Date\n'
|
||||
rowheader = '%s,%s' % (data['lastSystem']['name'], data['lastStarport']['name'])
|
||||
|
||||
h = open(filename, 'wt')
|
||||
h.write(header)
|
||||
for v in data['lastStarport'].get('modules', {}).itervalues():
|
||||
try:
|
||||
m = lookup(v)
|
||||
if m:
|
||||
h.write('%s,%s,%s,%s,%s,%s,%s,%s,%s\n' % (rowheader, m['category'], m['name'], m.get('mount',''), m.get('guidance',''), m.get('ship',''), m['class'], m['rating'], timestamp))
|
||||
except AssertionError as e:
|
||||
if __debug__: print 'Outfitting: %s' % e # Silently skip unrecognized modules
|
||||
except:
|
||||
if __debug__: raise
|
||||
h.close()
|
||||
|
||||
|
||||
# add all the modules
|
||||
def addmodules(data):
|
||||
if not data.get('lastStarport'):
|
||||
|
8
setup.py
8
setup.py
@ -51,8 +51,10 @@ if sys.platform=='darwin':
|
||||
|
||||
|
||||
APP = 'EDMarketConnector.py'
|
||||
APPCMD = 'EDMC.py'
|
||||
APPNAME = re.search(r"^appname\s*=\s*'(.+)'", file('config.py').read(), re.MULTILINE).group(1)
|
||||
APPLONGNAME = re.search(r"^applongname\s*=\s*'(.+)'", file('config.py').read(), re.MULTILINE).group(1)
|
||||
APPCMDNAME = re.search(r"^appcmdname\s*=\s*'(.+)'", file('config.py').read(), re.MULTILINE).group(1)
|
||||
VERSION = re.search(r"^appversion\s*=\s*'(.+)'", file('config.py').read(), re.MULTILINE).group(1)
|
||||
SHORTVERSION = ''.join(VERSION.split('.')[:3])
|
||||
|
||||
@ -119,6 +121,12 @@ setup(
|
||||
'company_name': 'Marginal', # WinSparkle
|
||||
'other_resources': [(24, 1, open(APPNAME+'.manifest').read())],
|
||||
} ],
|
||||
console = [ {'dest_base': APPCMDNAME,
|
||||
'script': APPCMD,
|
||||
'copyright': u'© 2015 Jonathan Harris',
|
||||
'name': APPNAME,
|
||||
'company_name': 'Marginal',
|
||||
} ],
|
||||
data_files = DATA_FILES,
|
||||
options = OPTIONS,
|
||||
setup_requires = [sys.platform=='darwin' and 'py2app' or 'py2exe'],
|
||||
|
Loading…
x
Reference in New Issue
Block a user