1
0
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:
Jonathan Harris 2015-11-07 05:05:25 +00:00
parent 9cad8aed9d
commit b0f60af38d
13 changed files with 211 additions and 43 deletions

114
EDMC.py Executable file
View 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)

View File

@ -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" />

View File

@ -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
View File

@ -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'

View File

@ -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.

View File

@ -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':

View File

@ -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)])

View File

@ -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/'

View File

@ -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

View File

@ -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*$')

View File

@ -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)])

View File

@ -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'):

View File

@ -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'],