mirror of
https://github.com/EDCD/EDMarketConnector.git
synced 2025-04-13 15:57:14 +03:00
Remove "Statistics" menu item and "Cmdr statistics" output option - stats no longer available with E:D 1.3.08 update.
Ref #21
This commit is contained in:
parent
b098674c3b
commit
dad66605eb
@ -23,8 +23,6 @@ import eddn
|
||||
import loadout
|
||||
import coriolis
|
||||
import flightlog
|
||||
import stats
|
||||
import chart
|
||||
import prefs
|
||||
from config import appname, applongname, config
|
||||
|
||||
@ -85,7 +83,6 @@ class AppWindow:
|
||||
# https://www.tcl.tk/man/tcl/TkCmd/menu.htm
|
||||
apple_menu = tk.Menu(menubar, name='apple')
|
||||
apple_menu.add_command(label="About %s" % applongname, command=lambda:self.w.call('tk::mac::standardAboutPanel'))
|
||||
apple_menu.add_command(label="Statistics", command=lambda:stats.StatsDialog(self.w, self.session))
|
||||
apple_menu.add_command(label="Check for Update", command=lambda:self.updater.checkForUpdates())
|
||||
menubar.add_cascade(menu=apple_menu)
|
||||
window_menu = tk.Menu(menubar, name='window')
|
||||
@ -98,7 +95,6 @@ class AppWindow:
|
||||
self.w.protocol("WM_DELETE_WINDOW", self.w.withdraw) # close button shouldn't quit app
|
||||
else:
|
||||
file_menu = tk.Menu(menubar, tearoff=tk.FALSE)
|
||||
file_menu.add_command(label="Statistics", command=lambda:stats.StatsDialog(self.w, self.session))
|
||||
file_menu.add_command(label="Check for Update", command=lambda:self.updater.checkForUpdates())
|
||||
file_menu.add_command(label="Settings", command=lambda:prefs.PreferencesDialog(self.w, self.login))
|
||||
file_menu.add_separator()
|
||||
@ -210,8 +206,6 @@ class AppWindow:
|
||||
self.w.after(shipyard_retry * 1000, lambda:self.getandsend(retrying=True))
|
||||
|
||||
# Stuff we can do while waiting for retry
|
||||
if config.getint('output') & config.OUT_STAT:
|
||||
chart.export(data)
|
||||
if config.getint('output') & config.OUT_LOG:
|
||||
flightlog.export(data)
|
||||
if config.getint('output') & config.OUT_SHIP_EDS:
|
||||
@ -229,8 +223,6 @@ class AppWindow:
|
||||
h.write(json.dumps(data, indent=2, sort_keys=True))
|
||||
|
||||
if not retrying:
|
||||
if config.getint('output') & config.OUT_STAT:
|
||||
chart.export(data)
|
||||
if config.getint('output') & config.OUT_LOG:
|
||||
flightlog.export(data)
|
||||
if config.getint('output') & config.OUT_SHIP_EDS:
|
||||
|
@ -51,9 +51,6 @@
|
||||
<Component Guid="{433C38E1-F736-4546-AA83-FCD8B0AAA39B}">
|
||||
<File KeyPath="yes" Source="SourceDir\_ctypes.pyd" />
|
||||
</Component>
|
||||
<Component Guid="{BFF92B3F-2F13-4892-87A0-49F8DDF54A60}">
|
||||
<File KeyPath="yes" Source="SourceDir\_elementtree.pyd" />
|
||||
</Component>
|
||||
<Component Guid="{45803711-A2A6-4DA8-8219-F625DE6DB33E}">
|
||||
<File KeyPath="yes" Source="SourceDir\_hashlib.pyd" />
|
||||
</Component>
|
||||
@ -81,9 +78,6 @@
|
||||
<Component Guid="{A18814B6-B491-42AB-A433-2AD66A823AD7}">
|
||||
<File KeyPath="yes" Source="SourceDir\library.zip" />
|
||||
</Component>
|
||||
<Component Guid="{017F51FB-BC1F-43C9-8089-6E60005F6989}">
|
||||
<File KeyPath="yes" Source="SourceDir\lxml.etree.pyd" />
|
||||
</Component>
|
||||
<Component Guid="{87A99AAA-792F-4092-9D00-5106D99D00AD}">
|
||||
<File KeyPath="yes" Source="SourceDir\pyexpat.pyd" />
|
||||
</Component>
|
||||
@ -324,7 +318,6 @@
|
||||
<Feature Id='Complete' Level='1'>
|
||||
<ComponentRef Id="MainExecutable" />
|
||||
<ComponentRef Id="_ctypes.pyd" />
|
||||
<ComponentRef Id="_elementtree.pyd" />
|
||||
<ComponentRef Id="_hashlib.pyd" />
|
||||
<ComponentRef Id="_socket.pyd" />
|
||||
<ComponentRef Id="_ssl.pyd" />
|
||||
@ -334,7 +327,6 @@
|
||||
<ComponentRef Id="EDMarketConnector.ico" />
|
||||
<ComponentRef Id="EDMarketConnector.VisualElementsManifest.xml" />
|
||||
<ComponentRef Id="library.zip" />
|
||||
<ComponentRef Id="lxml.etree.pyd" />
|
||||
<ComponentRef Id="pyexpat.pyd" />
|
||||
<ComponentRef Id="python27.dll" />
|
||||
<ComponentRef Id="select.pyd" />
|
||||
|
12
README.md
12
README.md
@ -5,14 +5,12 @@ This app downloads commodity market and other data from the game [Elite: Dangero
|
||||
|
||||
* sends the data to the [Elite Dangerous Data Network](http://eddn-gateway.elite-markets.net/) (“EDDN”) from where you and others can use it via online trading tools such as [eddb](http://eddb.io/), [Elite Trade Net](http://etn.io/), [Inara](http://inara.cz), etc.
|
||||
* saves the data to files on your computer that you can load into trading tools such as [Slopey's BPC Market Tool](https://forums.frontier.co.uk/showthread.php?t=76081), [Trade Dangerous](https://bitbucket.org/kfsone/tradedangerous/wiki/Home), [Thrudd's Trading Tools](http://www.elitetradingtool.co.uk/), [Inara](http://inara.cz), etc.
|
||||
* saves a record of your ship loadout, flight log and/or statistics.
|
||||
* saves a record of your ship loadout and/or flight log.
|
||||
|
||||
The user-interface is deliberately minimal - when you land at a station just switch to the app and press the “Update” button or press Enter to automatically download and transmit and/or save your choice of data.
|
||||
|
||||
 
|
||||
|
||||

|
||||
|
||||
|
||||
Installation
|
||||
--------
|
||||
@ -62,15 +60,8 @@ This app can save a variety of data in a variety of formats:
|
||||
* Flight log
|
||||
* Adds a record of your location, ship and cargo to a file that you can open in a text editor or a spreadsheet program such as Excel. Note: Don't edit, rename or move this file - take a copy if you wish to change it.
|
||||
|
||||
* Cmdr statstics
|
||||
* Generates a number of charts of your progress in an Excel file. Note: Don't edit, rename or move this file.
|
||||
|
||||
By default these files will be placed in your Documents folder. Since this app will create a lot of files if you use it for a while you may wish to create a separate folder for the files and tell the app to place them there.
|
||||
|
||||
Statistics
|
||||
--------
|
||||
Choose the “Statistics” item from the menu to view your Cmdrs's statistics. The statistics shown are largely the same
|
||||
as those available from the right-hand panel in-game, plus a few additions.
|
||||
|
||||
Uninstall
|
||||
--------
|
||||
@ -105,7 +96,6 @@ 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` .
|
||||
|
||||
On all platforms charting Cmdr statistics additionally requires verison 2.3.0 or later of the [openpyxl](https://pypi.python.org/pypi/openpyxl) module.
|
||||
|
||||
Packaging for distribution
|
||||
--------
|
||||
|
206
chart.py
206
chart.py
@ -1,206 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
#
|
||||
# Creates an Excel spreadsheet graphing player stats
|
||||
#
|
||||
# Requires openpyxl >= 2.3
|
||||
#
|
||||
|
||||
try:
|
||||
import lxml._elementpath # Explicit dependency for py2exe
|
||||
import openpyxl
|
||||
if map(int, openpyxl.__version__.split('.')[:2]) < [2,3]:
|
||||
raise ImportError()
|
||||
have_openpyxl = True
|
||||
except:
|
||||
have_openpyxl = False
|
||||
|
||||
import datetime
|
||||
import time
|
||||
import re
|
||||
|
||||
from os.path import isfile, join
|
||||
from config import config
|
||||
|
||||
|
||||
dataseries = [
|
||||
{
|
||||
'name': 'Combat',
|
||||
'axes': ['Quantity', 'Profit [CR]'],
|
||||
'keys': [
|
||||
('Bounties', ['stats', 'combat', 'bounty', 'qty']),
|
||||
('Profit from bounties', ['stats', 'combat', 'bounty', 'value'], True),
|
||||
('Bonds', ['stats', 'combat', 'bond', 'qty']),
|
||||
('Profit from bonds', ['stats', 'combat', 'bond', 'value'], True),
|
||||
('Assassinations', ['stats', 'missions', 'assassin', 'missionsCompleted']),
|
||||
('Profit from assassin', ['stats', 'missions', 'assassin', 'creditsEarned'], True),
|
||||
('Hunting', ['stats', 'missions', 'bountyHunter', 'missionsCompleted']),
|
||||
('Profit from hunting', ['stats', 'missions', 'bountyHunter', 'creditsEarned'], True),
|
||||
],
|
||||
},
|
||||
{
|
||||
'name': 'Trade',
|
||||
'axes': ['Quantity', 'Profit [CR]'],
|
||||
'keys': [
|
||||
('Profit from trading', ['stats', 'trade', 'profit'], True),
|
||||
('Commodities traded', ['stats', 'trade', 'qty']),
|
||||
('Profit from smuggling', ['stats', 'blackMarket', 'profit'], True),
|
||||
('Commodities smuggled', ['stats', 'blackMarket', 'qty']),
|
||||
('Profit from mining', ['stats', 'mining', 'profit'], True),
|
||||
('Fragments mined', ['stats', 'mining', 'qty']),
|
||||
('Fragments converted', ['stats', 'mining', 'converted', 'qty']),
|
||||
],
|
||||
},
|
||||
{
|
||||
'name': 'Explorer',
|
||||
'axes': ['Quantity', 'Profit [CR]'],
|
||||
'keys': [
|
||||
('Profits from exploration', ['stats', 'explore', 'creditsEarned'], True),
|
||||
('Discovery scans', ['stats', 'explore', 'scanSoldLevels', 'lev_0']),
|
||||
('Level 2 detailed scans', ['stats', 'explore', 'scanSoldLevels', 'lev_1']),
|
||||
('Level 3 detailed scans', ['stats', 'explore', 'scanSoldLevels', 'lev_2']),
|
||||
('Bodies first discovered', ['stats', 'explore', 'bodiesFirstDiscovered']),
|
||||
('Hyperspace jumps', ['stats', 'explore', 'hyperspaceJumps']),
|
||||
]
|
||||
},
|
||||
{
|
||||
'name': 'Crime',
|
||||
'axes': ['Quantity', 'Profit [CR]'],
|
||||
'keys': [
|
||||
('Fines', ['stats', 'crime', 'fine', 'qty']),
|
||||
('Lifetime fine value', ['stats', 'crime', 'fine', 'value'], True),
|
||||
('Bounties', ['stats', 'crime', 'bounty', 'qty']),
|
||||
('Lifetime bounty value', ['stats', 'crime', 'bounty', 'value'], True),
|
||||
('Profit from cargo', ['stats', 'crime', 'stolenCargo', 'value'], True),
|
||||
('Stolen cargo', ['stats', 'crime', 'stolenCargo', 'qty']),
|
||||
('Profit from goods', ['stats', 'stolenGoods', 'profit'], True),
|
||||
('Stolen goods', ['stats', 'stolenGoods', 'qty']),
|
||||
],
|
||||
},
|
||||
{
|
||||
'name': 'NPC',
|
||||
'axes': 'Quantity',
|
||||
'prefix': ['stats', 'NPC', 'kills', 'ranks'],
|
||||
'keys': [('Harmless', 'r0'), ('Mostly Harmless', 'r1'), ('Novice', 'r2'), ('Competent', 'r3'), ('Expert', 'r4'), ('Master', 'r5'), ('Dangerous', 'r6'), ('Deadly', 'r7'), ('Elite', 'r8'), ('Capital', 'rArray')],
|
||||
},
|
||||
{
|
||||
'name': 'PVP',
|
||||
'axes': 'Quantity',
|
||||
'prefix': ['stats', 'PVP', 'kills', 'ranks'],
|
||||
'keys': [('Harmless', 'r0'), ('Mostly Harmless', 'r1'), ('Novice', 'r2'), ('Competent', 'r3'), ('Expert', 'r4'), ('Master', 'r5'), ('Dangerous', 'r6'), ('Deadly', 'r7'), ('Elite', 'r8'), ('Capital', 'rArray')],
|
||||
},
|
||||
{
|
||||
'name': 'Balance',
|
||||
'axes': ['Quantity', '[CR]'],
|
||||
'keys': [
|
||||
('Current balance', ['commander', 'credits'], True),
|
||||
('Spent on ships', ['stats', 'ship', 'spend', 'ships'], True),
|
||||
('Spent on outfitting', ['stats', 'ship', 'spend', 'modules'], True),
|
||||
('Spent on repairs', ['stats', 'ship', 'spend', 'repair'], True),
|
||||
('Insurance claims', ['stats', 'ship', 'insurance', 'claims']),
|
||||
('Total claim costs', ['stats', 'ship', 'insurance', 'value'], True),
|
||||
],
|
||||
},
|
||||
# {
|
||||
# 'name': 'Vanish',
|
||||
# 'prefix': ['stats', 'vanishCounters'],
|
||||
# 'keys': ['amongPeers', 'inDanger', 'inDangerWithPeers', 'isNotDying', 'noPeers', 'notInDanger'],
|
||||
# },
|
||||
]
|
||||
|
||||
|
||||
def export(data, csv=False):
|
||||
|
||||
if not have_openpyxl: return False
|
||||
|
||||
TITLE_F = openpyxl.styles.Font(bold=True)
|
||||
TITLE_A = openpyxl.styles.Alignment(horizontal='right')
|
||||
|
||||
querytime = config.getint('querytime') or int(time.time())
|
||||
|
||||
filename = join(config.get('outdir'), 'Cmdr %s.xlsx' % re.sub(r'[\\/:*?"<>|]', '_', data['commander']['name']))
|
||||
if not isfile(filename):
|
||||
wb = openpyxl.Workbook()
|
||||
try:
|
||||
wb.active.title = 'Combat' # Workbook is created with one sheet - rename it
|
||||
except:
|
||||
pass # except that it isn't under 2.30b1
|
||||
else:
|
||||
wb = openpyxl.load_workbook(filename)
|
||||
|
||||
for thing in dataseries:
|
||||
legends = [x[0] for x in thing['keys']]
|
||||
keys = [x[1] for x in thing['keys']]
|
||||
if thing.get('axes') and isinstance(thing['axes'], (list, tuple)):
|
||||
y2_axis = [len(x)>2 and x[2] for x in thing['keys']]
|
||||
else:
|
||||
y2_axis = None
|
||||
|
||||
if thing['name'] in wb:
|
||||
ws = wb[thing['name']]
|
||||
else:
|
||||
ws = wb.create_sheet(title=thing['name'])
|
||||
|
||||
# Add header row
|
||||
if ws.max_row <= 1: # Returns 1 for empty sheet
|
||||
ws.append(['Date'] + legends)
|
||||
for i in range(ws.max_column):
|
||||
ws.column_dimensions[openpyxl.utils.get_column_letter(i+1)].width = 17
|
||||
for row in ws.get_squared_range(1, 1, ws.max_column, 1):
|
||||
for cell in row:
|
||||
cell.font = TITLE_F
|
||||
cell.alignment = TITLE_A
|
||||
|
||||
# Add data row
|
||||
vals = [datetime.datetime.fromtimestamp(querytime)]
|
||||
mydata = data
|
||||
if thing.get('prefix'):
|
||||
for key in thing['prefix']:
|
||||
mydata = mydata[key]
|
||||
for key2 in keys:
|
||||
if isinstance(key2, basestring):
|
||||
vals.append(mydata.get(key2, 0))
|
||||
else:
|
||||
value = mydata
|
||||
for key in key2:
|
||||
value = value.get(key, 0)
|
||||
if not value: break
|
||||
vals.append(value)
|
||||
ws.append(vals)
|
||||
ws.cell(row=ws.max_row, column=1).number_format = 'yyyy-mm-dd hh:mm:ss' # just a string, not a style
|
||||
|
||||
dates = openpyxl.chart.Reference(ws, 1, 2, 1, ws.max_row)
|
||||
|
||||
chart = openpyxl.chart.ScatterChart()
|
||||
chart.title = thing['name']
|
||||
chart.width, chart.height = 60, 30 # in cm!
|
||||
chart.scatterStyle = 'lineMarker'
|
||||
chart.set_categories(dates)
|
||||
chart.x_axis.number_format = ('yyyy-mm-dd') # date only
|
||||
chart.x_axis.majorGridlines = None
|
||||
|
||||
if y2_axis:
|
||||
chart.y_axis.majorGridlines = None # prefer grid lines on secondary axis
|
||||
chart2 = openpyxl.chart.ScatterChart()
|
||||
chart2.scatterStyle = 'lineMarker'
|
||||
# Hack - second chart must have different axis ID
|
||||
chart2.y_axis = openpyxl.chart.axis.NumericAxis(axId=30, crossAx=10, axPos='r', crosses='max')
|
||||
|
||||
for i in range(len(keys)):
|
||||
series = openpyxl.chart.Series(openpyxl.chart.Reference(ws, i+2, 1, i+2, ws.max_row), dates, title_from_data=True)
|
||||
series.marker.symbol = 'diamond'
|
||||
if y2_axis and y2_axis[i]:
|
||||
series.dLbls = openpyxl.chart.label.DataLabels([openpyxl.chart.label.DataLabel(idx=ws.max_row-2, dLblPos='r', showSerName=True)])
|
||||
chart2.series.append(series)
|
||||
else:
|
||||
series.dLbls = openpyxl.chart.label.DataLabels([openpyxl.chart.label.DataLabel(idx=0, dLblPos='l', showSerName=True)])
|
||||
chart.series.append(series)
|
||||
if y2_axis:
|
||||
chart.y_axis.title = thing['axes'][0]
|
||||
chart2.y_axis.title = thing['axes'][1]
|
||||
chart.z_axis = chart2.y_axis
|
||||
chart += chart2
|
||||
elif thing['axes']:
|
||||
chart.y_axis.title = thing['axes']
|
||||
ws.add_chart(chart, 'B2')
|
||||
|
||||
wb.save(filename)
|
@ -71,7 +71,7 @@ class Config:
|
||||
OUT_CSV = 8
|
||||
OUT_SHIP_EDS = 16
|
||||
OUT_LOG = 32
|
||||
OUT_STAT = 64
|
||||
#OUT_STAT = 64 # No longer available
|
||||
OUT_SHIP_CORIOLIS = 128
|
||||
|
||||
if platform=='darwin':
|
||||
|
15
prefs.py
15
prefs.py
@ -9,7 +9,6 @@ import ttk
|
||||
import tkFileDialog
|
||||
|
||||
from config import config
|
||||
from chart import have_openpyxl
|
||||
|
||||
|
||||
if platform=='win32':
|
||||
@ -78,7 +77,7 @@ class PreferencesDialog(tk.Toplevel):
|
||||
outframe.grid(padx=10, pady=10, sticky=tk.NSEW)
|
||||
outframe.columnconfigure(0, weight=1)
|
||||
|
||||
output = config.getint('output') or (config.OUT_EDDN | config.OUT_SHIP_EDS | config.OUT_STAT)
|
||||
output = config.getint('output') or (config.OUT_EDDN | config.OUT_SHIP_EDS)
|
||||
ttk.Label(outframe, text="Please choose what data to save").grid(row=0, columnspan=2, padx=5, pady=3, sticky=tk.W)
|
||||
self.out_eddn= tk.IntVar(value = (output & config.OUT_EDDN) and 1 or 0)
|
||||
ttk.Checkbutton(outframe, text="Send station data to the Elite Dangerous Data Network", variable=self.out_eddn).grid(row=1, columnspan=2, padx=5, sticky=tk.W)
|
||||
@ -94,15 +93,13 @@ class PreferencesDialog(tk.Toplevel):
|
||||
ttk.Checkbutton(outframe, text="Ship loadout in Coriolis format", variable=self.out_ship_coriolis, command=self.outvarchanged).grid(row=6, columnspan=2, padx=5, sticky=tk.W)
|
||||
self.out_log = tk.IntVar(value = (output & config.OUT_LOG ) and 1 or 0)
|
||||
ttk.Checkbutton(outframe, text="Flight log", variable=self.out_log, command=self.outvarchanged).grid(row=7, columnspan=2, padx=5, sticky=tk.W)
|
||||
self.out_stat= tk.IntVar(value = have_openpyxl and (output & config.OUT_STAT) and 1 or 0)
|
||||
ttk.Checkbutton(outframe, text="Cmdr statistics", variable=self.out_stat, command=self.outvarchanged, state=have_openpyxl and tk.NORMAL or tk.DISABLED).grid(row=8, columnspan=2, padx=5, sticky=tk.W)
|
||||
|
||||
ttk.Label(outframe, text=(platform=='darwin' and 'Where:' or 'File location:')).grid(row=9, padx=5, pady=(5,0), sticky=tk.NSEW)
|
||||
ttk.Label(outframe, text=(platform=='darwin' and 'Where:' or 'File location:')).grid(row=8, padx=5, pady=(5,0), sticky=tk.NSEW)
|
||||
self.outbutton = ttk.Button(outframe, text=(platform=='darwin' and 'Change...' or 'Browse...'), command=self.outbrowse)
|
||||
self.outbutton.grid(row=9, column=1, padx=5, pady=(5,0), sticky=tk.NSEW)
|
||||
self.outbutton.grid(row=8, column=1, padx=5, pady=(5,0), sticky=tk.NSEW)
|
||||
self.outdir = ttk.Entry(outframe)
|
||||
self.outdir.insert(0, config.get('outdir'))
|
||||
self.outdir.grid(row=10, columnspan=2, padx=5, pady=5, sticky=tk.EW)
|
||||
self.outdir.grid(row=9, columnspan=2, padx=5, pady=5, sticky=tk.EW)
|
||||
self.outvarchanged()
|
||||
|
||||
privacyframe = ttk.LabelFrame(frame, text='Privacy')
|
||||
@ -129,7 +126,7 @@ class PreferencesDialog(tk.Toplevel):
|
||||
#self.wait_window(self) # causes duplicate events on OSX
|
||||
|
||||
def outvarchanged(self):
|
||||
local = self.out_bpc.get() or self.out_td.get() or self.out_csv.get() or self.out_ship_eds.get() or self.out_ship_coriolis.get() or self.out_log.get() or self.out_stat.get()
|
||||
local = self.out_bpc.get() or self.out_td.get() or self.out_csv.get() or self.out_ship_eds.get() or self.out_ship_coriolis.get() or self.out_log.get()
|
||||
self.outbutton['state'] = local and tk.NORMAL or tk.DISABLED
|
||||
self.outdir['state'] = local and 'readonly' or tk.DISABLED
|
||||
|
||||
@ -168,7 +165,7 @@ class PreferencesDialog(tk.Toplevel):
|
||||
credentials = (config.get('username'), config.get('password'))
|
||||
config.set('username', self.username.get().strip())
|
||||
config.set('password', self.password.get().strip())
|
||||
config.set('output', (self.out_eddn.get() and config.OUT_EDDN or 0) + (self.out_bpc.get() and config.OUT_BPC or 0) + (self.out_td.get() and config.OUT_TD or 0) + (self.out_csv.get() and config.OUT_CSV or 0) + (self.out_ship_eds.get() and config.OUT_SHIP_EDS or 0) + (self.out_log.get() and config.OUT_LOG or 0) + (self.out_stat.get() and config.OUT_STAT or 0) + (self.out_ship_coriolis.get() and config.OUT_SHIP_CORIOLIS or 0))
|
||||
config.set('output', (self.out_eddn.get() and config.OUT_EDDN or 0) + (self.out_bpc.get() and config.OUT_BPC or 0) + (self.out_td.get() and config.OUT_TD or 0) + (self.out_csv.get() and config.OUT_CSV or 0) + (self.out_ship_eds.get() and config.OUT_SHIP_EDS or 0) + (self.out_log.get() and config.OUT_LOG or 0) + (self.out_ship_coriolis.get() and config.OUT_SHIP_CORIOLIS or 0))
|
||||
config.set('outdir', self.outdir.get().strip())
|
||||
config.set('anonymous', self.out_anon.get())
|
||||
self.destroy()
|
||||
|
417
stats.py
417
stats.py
@ -1,417 +0,0 @@
|
||||
import numbers
|
||||
from operator import attrgetter
|
||||
from sys import platform
|
||||
import time
|
||||
if __debug__:
|
||||
from traceback import print_exc
|
||||
|
||||
import Tkinter as tk
|
||||
import ttk
|
||||
|
||||
import companion
|
||||
import prefs
|
||||
|
||||
|
||||
# Hack to fix notebook page background. Doesn't seem possible to do this with styles.
|
||||
if platform == 'darwin':
|
||||
from platform import mac_ver
|
||||
PAGEFG = 'systemButtonText'
|
||||
PAGEBG = map(int, mac_ver()[0].split('.')) >= [10,10] and '#dbdbdb' or '#dfdfdf' # want e2 or e5 on screen
|
||||
elif platform == 'win32':
|
||||
PAGEFG = 'SystemWindowText'
|
||||
PAGEBG = 'SystemWindow' # typically white
|
||||
|
||||
|
||||
class StatsDialog(tk.Toplevel):
|
||||
|
||||
def __init__(self, parent, session):
|
||||
tk.Toplevel.__init__(self, parent)
|
||||
|
||||
self.parent = parent
|
||||
self.session = session
|
||||
self.title('Statistics')
|
||||
|
||||
if parent.winfo_viewable():
|
||||
self.transient(parent)
|
||||
|
||||
# position over parent
|
||||
if platform!='darwin' or parent.winfo_rooty()>0: # http://core.tcl.tk/tk/tktview/c84f660833546b1b84e7
|
||||
self.geometry("+%d+%d" % (parent.winfo_rootx(), parent.winfo_rooty()))
|
||||
|
||||
# remove decoration
|
||||
self.resizable(tk.FALSE, tk.FALSE)
|
||||
if platform=='win32':
|
||||
self.attributes('-toolwindow', tk.TRUE)
|
||||
elif platform=='darwin':
|
||||
# http://wiki.tcl.tk/13428
|
||||
parent.call('tk::unsupported::MacWindowStyle', 'style', self, 'utility')
|
||||
|
||||
frame = ttk.Frame(self)
|
||||
frame.grid(sticky=tk.NSEW)
|
||||
|
||||
self.status = ttk.Label(frame, text='Fetching data...')
|
||||
self.status.grid(padx=10, pady=10)
|
||||
|
||||
# wait for window to appear on screen before calling grab_set
|
||||
self.wait_visibility()
|
||||
self.grab_set()
|
||||
self.update() # update_idletasks() isn't cutting it
|
||||
|
||||
self.showstats()
|
||||
|
||||
# callback after verification code
|
||||
def verify(self, code):
|
||||
try:
|
||||
self.session.verify(code)
|
||||
except Exception as e:
|
||||
if __debug__: print_exc()
|
||||
self.status['text'] = str(e)
|
||||
else:
|
||||
self.showstats()
|
||||
|
||||
def showstats(self):
|
||||
try:
|
||||
data = self.session.query()
|
||||
except companion.VerificationRequired:
|
||||
return prefs.AuthenticationDialog(self.parent, self.verify)
|
||||
except companion.ServerError as e:
|
||||
self.status['text'] = str(e)
|
||||
return
|
||||
except Exception as e:
|
||||
if __debug__: print_exc()
|
||||
self.status['text'] = str(e)
|
||||
return
|
||||
|
||||
if not data.get('commander') or not data['commander'].get('name','').strip():
|
||||
self.status['text'] = "Who are you?!" # Shouldn't happen
|
||||
elif not data.get('ship') or not data['ship'].get('name','').strip():
|
||||
self.status['text'] = "What are you flying?!" # Shouldn't happen
|
||||
elif not data.get('stats'):
|
||||
self.status['text'] = "No stats available?!" # Shouldn't happen
|
||||
else:
|
||||
StatsResults(self.parent, data)
|
||||
self.destroy()
|
||||
|
||||
|
||||
class StatsResults(tk.Toplevel):
|
||||
|
||||
RANKS = {
|
||||
# http://elite-dangerous.wikia.com/wiki/Federation#Ranks
|
||||
'federation' : ['None', 'Recruit', 'Cadet', 'Midshipman', 'Petty Officer', 'Chief Petty Officer', 'Warrant Officer', 'Ensign', 'Lieutenant', 'Lieutenant Commander', 'Post Commander', 'Post Captain', 'Rear Admiral', 'Vice Admiral', 'Admiral'],
|
||||
# http://elite-dangerous.wikia.com/wiki/Empire#Ranks
|
||||
'empire' : ['None', 'Outsider', 'Serf', 'Master', 'Squire', 'Knight', 'Lord', 'Baron', 'Viscount', 'Count', 'Earl', 'Marquis', 'Duke', 'Prince', 'King'],
|
||||
# http://elite-dangerous.wikia.com/wiki/Pilots_Federation
|
||||
'combat' : ['Harmless', 'Mostly Harmless', 'Novice', 'Competent', 'Expert', 'Master', 'Dangerous', 'Deadly', 'Elite'],
|
||||
'trade' : ['Penniless', 'Mostly Penniless', 'Pedlar', 'Dealer', 'Merchant', 'Broker', 'Entrepreneur', 'Tycoon', 'Elite'],
|
||||
'explore' : ['Aimless', 'Mostly Aimless', 'Scout', 'Surveyor', 'Trailblazer', 'Pathfinder', 'Ranger', 'Pioneer', 'Elite'],
|
||||
'power' : ['None', 'Rating 1', 'Rating 2', 'Rating 3', 'Rating 4', 'Rating 5'],
|
||||
}
|
||||
|
||||
def __init__(self, parent, data):
|
||||
tk.Toplevel.__init__(self, parent)
|
||||
|
||||
self.parent = parent
|
||||
self.title('Cmdr ' + data['commander']['name'])
|
||||
|
||||
if parent.winfo_viewable():
|
||||
self.transient(parent)
|
||||
|
||||
# position over parent
|
||||
if platform!='darwin' or parent.winfo_rooty()>0: # http://core.tcl.tk/tk/tktview/c84f660833546b1b84e7
|
||||
self.geometry("+%d+%d" % (parent.winfo_rootx(), parent.winfo_rooty()))
|
||||
|
||||
# remove decoration
|
||||
self.resizable(tk.FALSE, tk.FALSE)
|
||||
if platform=='win32':
|
||||
self.attributes('-toolwindow', tk.TRUE)
|
||||
elif platform=='darwin':
|
||||
# http://wiki.tcl.tk/13428
|
||||
parent.call('tk::unsupported::MacWindowStyle', 'style', self, 'utility')
|
||||
if map(int, mac_ver()[0].split('.')) >= [10,10]:
|
||||
# Hack for tab appearance with 8.5 on Yosemite. For proper fix see
|
||||
# https://github.com/tcltk/tk/commit/55c4dfca9353bbd69bbcec5d63bf1c8dfb461e25
|
||||
style = ttk.Style().configure('TNotebook.Tab', padding=(12,10,12,2))
|
||||
|
||||
frame = ttk.Frame(self)
|
||||
frame.grid(sticky=tk.NSEW)
|
||||
|
||||
CR = 'CR'
|
||||
|
||||
notebook = ttk.Notebook(frame)
|
||||
notebook.grid(padx=10, pady=10, sticky=tk.NSEW)
|
||||
|
||||
page = self.addpage(notebook)
|
||||
self.addranking(page, data, 'combat')
|
||||
self.addpagespacer(page)
|
||||
self.addpageheader(page, ['Statistics'])
|
||||
for thing in [
|
||||
('Bounties claimed', ['stats', 'combat', 'bounty', 'qty']),
|
||||
('Profit from bounties', ['stats', 'combat', 'bounty', 'value'], CR),
|
||||
('Combat bonds', ['stats', 'combat', 'bond', 'qty']),
|
||||
('Profit from combat bonds', ['stats', 'combat', 'bond', 'value'], CR),
|
||||
('Assassinations', ['stats', 'missions', 'assassin', 'missionsCompleted']),
|
||||
('Profit from assassinations', ['stats', 'missions', 'assassin', 'creditsEarned'], CR),
|
||||
('Highest single assassination', ['stats', 'missions', 'assassin', 'highestEarnings', 'value'], CR),
|
||||
('Bounty hunting', ['stats', 'missions', 'bountyHunter', 'missionsCompleted']),
|
||||
('Profit from bounty hunting', ['stats', 'missions', 'bountyHunter', 'creditsEarned'], CR),
|
||||
('Highest single bounty', ['stats', 'missions', 'bountyHunter', 'highestEarnings', 'value'], CR),
|
||||
]:
|
||||
self.addstat(page, data, *thing)
|
||||
notebook.add(page, text='Combat')
|
||||
|
||||
page = self.addpage(notebook)
|
||||
self.addranking(page, data, 'trade')
|
||||
self.addpagespacer(page)
|
||||
self.addpageheader(page, ['Trading'])
|
||||
for thing in [
|
||||
('Market network', ['stats', 'trade', 'marketIds'], len),
|
||||
('Trading profit', ['stats', 'trade', 'profit'], CR),
|
||||
('Commodities traded', ['stats', 'trade', 'qty']),
|
||||
('Average profit', (['stats', 'trade', 'profit'], ['stats', 'trade', 'count']), CR),
|
||||
('Highest single transaction', ['stats', 'trade', 'largestProfit', 'value'], CR),
|
||||
]:
|
||||
self.addstat(page, data, *thing)
|
||||
try:
|
||||
if not data['stats']['trade']['largestProfit']['qty']: raise Exception()
|
||||
self.addpagerow(page, ['', '%d %s' % (data['stats']['trade']['largestProfit']['qty'], companion.commodity_map.get(data['stats']['trade']['largestProfit']['commodity'], data['stats']['trade']['largestProfit']['commodity']))])
|
||||
except:
|
||||
self.addpagespacer(page)
|
||||
|
||||
self.addpageheader(page, ['Smuggling'])
|
||||
for thing in [
|
||||
('Black Market network', ['stats', 'blackMarket', 'marketIds'], len),
|
||||
('Black Market profit', ['stats', 'blackMarket', 'profit'], CR),
|
||||
('Commodities smuggled', ['stats', 'blackMarket', 'qty']),
|
||||
('Average profit', (['stats', 'blackMarket', 'profit'], ['stats', 'blackMarket', 'count']), CR),
|
||||
('Highest single transaction', ['stats', 'blackMarket', 'largestProfit', 'value'], CR),
|
||||
]:
|
||||
self.addstat(page, data, *thing)
|
||||
try:
|
||||
if not data['stats']['blackMarket']['largestProfit']['qty']: raise Exception()
|
||||
self.addpagerow(page, ['', '%d %s' % (data['stats']['blackMarket']['largestProfit']['qty'], companion.commodity_map.get(data['stats']['blackMarket']['largestProfit']['commodity'], data['stats']['blackMarket']['largestProfit']['commodity']))])
|
||||
except:
|
||||
self.addpagespacer(page)
|
||||
|
||||
self.addpageheader(page, ['Mining'])
|
||||
for thing in [
|
||||
('Profit from mining', ['stats', 'mining', 'profit'], CR),
|
||||
('Fragments mined', ['stats', 'mining', 'qty']),
|
||||
('Converted', ['stats', 'mining', 'converted', 'qty']),
|
||||
]:
|
||||
self.addstat(page, data, *thing)
|
||||
notebook.add(page, text='Trade')
|
||||
|
||||
page = self.addpage(notebook)
|
||||
self.addranking(page, data, 'explore')
|
||||
self.addpagespacer(page)
|
||||
self.addpageheader(page, ['Statistics'])
|
||||
for thing in [
|
||||
('Systems visited', ['stats', 'explore', 'visited', 'starsystem'], len),
|
||||
('Profits from exploration', ['stats', 'explore', 'creditsEarned'], CR),
|
||||
('Discovery scans', ['stats', 'explore', 'scanSoldLevels', 'lev_0']),
|
||||
('Level 2 detailed scans', ['stats', 'explore', 'scanSoldLevels', 'lev_1']),
|
||||
('Level 3 detailed scans', ['stats', 'explore', 'scanSoldLevels', 'lev_2']),
|
||||
('Bodies first discovered', ['stats', 'explore', 'bodiesFirstDiscovered']),
|
||||
('Highest single transaction', ['stats', 'explore', 'highestPayout'], CR),
|
||||
('Hyperspace jumps', ['stats', 'explore', 'hyperspaceJumps']),
|
||||
('Distance travelled', ['stats', 'explore', 'totalHyperspaceDistance'], 'Ly'),
|
||||
('Farthest distance from home', ['stats', 'explore', 'greatestDistanceFromStart'], 'Ly'),
|
||||
]:
|
||||
self.addstat(page, data, *thing)
|
||||
notebook.add(page, text='Explorer')
|
||||
|
||||
page = self.addpage(notebook, ['Faction', 'Rank'], align=tk.W)
|
||||
try:
|
||||
for category in ['federation', 'empire', 'power']:
|
||||
self.addpagerow(page, [category.capitalize(), self.ranktitle(category, data['commander']['rank'][category])], align=tk.W)
|
||||
except:
|
||||
if __debug__: print_exc()
|
||||
self.addpagespacer(page)
|
||||
self.addpageheader(page, ['Crime'])
|
||||
for thing in [
|
||||
('Fines issued', ['stats', 'crime', 'fine', 'qty']),
|
||||
('Lifetime fines value', ['stats', 'crime', 'fine', 'value'], CR),
|
||||
('Bounties claimed', ['stats', 'crime', 'bounty', 'qty']),
|
||||
('Lifetime bounty value', ['stats', 'crime', 'bounty', 'value'], CR),
|
||||
('Highest bounty issued', ['stats', 'crime', 'bounty', 'highest', 'value'], CR),
|
||||
]:
|
||||
self.addstat(page, data, *thing)
|
||||
self.addpagespacer(page)
|
||||
self.addpageheader(page, ['Piracy'])
|
||||
if data['stats'].get('crime',{}).get('stolenCargo'):
|
||||
for thing in [
|
||||
('Profit from stolen cargo', ['stats', 'crime', 'stolenCargo', 'value'], CR),
|
||||
('Cargo stolen', ['stats', 'crime', 'stolenCargo', 'qty']),
|
||||
]:
|
||||
self.addstat(page, data, *thing)
|
||||
for thing in [
|
||||
('Profit from stolen goods', ['stats', 'stolenGoods', 'profit'], CR),
|
||||
('Goods stolen', ['stats', 'stolenGoods', 'qty']),
|
||||
('Average profit', (['stats', 'stolenGoods', 'profit'], ['stats', 'stolenGoods', 'count']), CR),
|
||||
('Highest single transaction', ['stats', 'stolenGoods', 'largestProfit', 'value'], CR),
|
||||
]:
|
||||
self.addstat(page, data, *thing)
|
||||
try:
|
||||
if not data['stats']['stolenGoods']['largestProfit']['qty']: raise Exception()
|
||||
self.addpagerow(page, ['', '%d %s' % (data['stats']['stolenGoods']['largestProfit']['qty'], companion.commodity_map.get(data['stats']['stolenGoods']['largestProfit']['commodity'], data['stats']['stolenGoods']['largestProfit']['commodity']))])
|
||||
except:
|
||||
pass
|
||||
notebook.add(page, text='Rep')
|
||||
|
||||
page = self.addpage(notebook, ['Ship', 'System', 'Station'], align=tk.W)
|
||||
try:
|
||||
current = data['commander'].get('currentShipId')
|
||||
# 'ships' can be an array or a dict indexed by str(int). Perhaps the latter if you've sold ships?
|
||||
for key, ship in enumerate(companion.listify(data.get('ships'))):
|
||||
if ship:
|
||||
self.addpagerow(page, [companion.ship_map.get(ship['name'], ship['name']) + (key==current and ' *' or ''),
|
||||
ship['starsystem']['name'], ship['station']['name']], align=tk.W)
|
||||
except:
|
||||
if __debug__: print_exc()
|
||||
notebook.add(page, text='Ships')
|
||||
|
||||
page = self.addpage(notebook, ['Rank', 'NPC', 'PVP'])
|
||||
try:
|
||||
npc = data['stats']['NPC']['kills']['ranks']
|
||||
pvp = data['stats']['PVP']['kills']['ranks']
|
||||
self.addpagerow(page, ['Capital', npc.get('rArray', 0), pvp.get('rArray', 0)])
|
||||
for rank in range(len(self.RANKS['combat'])-1, -1, -1):
|
||||
self.addpagerow(page, [self.RANKS['combat'][rank], npc.get('r%d' % rank, 0), pvp.get('r%d' % rank, 0)])
|
||||
except:
|
||||
if __debug__: print_exc()
|
||||
notebook.add(page, text='Kills')
|
||||
|
||||
page = self.addpage(notebook, ['Finance'])
|
||||
for thing in [
|
||||
('Highest balance', ['stats', 'wealth', 'maxCredits'], CR),
|
||||
('Current balance', ['commander', 'credits'], CR),
|
||||
('Current loan', ['commander', 'debt'], CR),
|
||||
]:
|
||||
self.addstat(page, data, *thing)
|
||||
|
||||
self.addpagespacer(page)
|
||||
self.addpageheader(page, ['Statistics'])
|
||||
try:
|
||||
self.addpagerow(page, ['Current assets', '{:,} CR'.format(int(data['commander']['credits'] + data['ship']['value']['total']))])
|
||||
except:
|
||||
if __debug__: print_exc()
|
||||
for thing in [
|
||||
('Credits spent on ships', ['stats', 'ship', 'spend', 'ships'], CR),
|
||||
('Credits spent on outfitting', ['stats', 'ship', 'spend', 'modules'], CR),
|
||||
('Credits spent on repairs', ['stats', 'ship', 'spend', 'repair'], CR),
|
||||
('Credits spent on fuel', ['stats', 'ship', 'spend', 'fuel'], CR),
|
||||
('Credits spent on munitions', ['stats', 'ship', 'spend', 'ammo'], CR),
|
||||
('Insurance claims', ['stats', 'ship', 'insurance', 'claims']),
|
||||
('Total claim costs', ['stats', 'ship', 'insurance', 'value'], CR),
|
||||
]:
|
||||
self.addstat(page, data, *thing)
|
||||
notebook.add(page, text='Balance')
|
||||
|
||||
if platform!='darwin':
|
||||
buttonframe = ttk.Frame(frame)
|
||||
buttonframe.grid(padx=10, pady=(0,10), sticky=tk.NSEW)
|
||||
buttonframe.columnconfigure(0, weight=1)
|
||||
ttk.Label(buttonframe).grid(row=0, column=0) # spacer
|
||||
ttk.Button(buttonframe, text='OK', command=self.destroy).grid(row=0, column=1, sticky=tk.E)
|
||||
|
||||
# wait for window to appear on screen before calling grab_set
|
||||
self.wait_visibility()
|
||||
self.grab_set()
|
||||
|
||||
|
||||
def addranking(self, parent, data, category):
|
||||
try:
|
||||
rank = data['commander']['rank'].get(category)
|
||||
progress = companion.listify(data['stats']['ranks'].get(category))
|
||||
if not rank or not progress:
|
||||
self.addpageheader(parent, ['Rank'])
|
||||
self.addpagerow(parent, [self.ranktitle(category, rank)])
|
||||
else:
|
||||
self.addpageheader(parent, ['Rank', 'Achieved', 'Elapsed'])
|
||||
while rank > 0:
|
||||
if rank>=len(progress) or not progress[rank] or not progress[rank]['ts']:
|
||||
self.addpagerow(parent, [self.ranktitle(category, rank)])
|
||||
else:
|
||||
self.addpagerow(parent, [self.ranktitle(category, rank),
|
||||
time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(progress[rank]['ts'])),
|
||||
self.elapsed(progress[rank]['gt'])])
|
||||
rank -= 1
|
||||
except:
|
||||
if __debug__: print_exc()
|
||||
|
||||
def addstat(self, parent, data, category, content, transform=None):
|
||||
# category can be a simple type, a list of keys into data, or a (dividend,divsior) pair of lists of keys
|
||||
try:
|
||||
if isinstance(content, list):
|
||||
value = data
|
||||
for key in content:
|
||||
value = value[key]
|
||||
elif isinstance(content, tuple):
|
||||
divisor = data
|
||||
for key in content[1]:
|
||||
divisor = divisor[key]
|
||||
if divisor:
|
||||
dividend = data
|
||||
for key in content[0]:
|
||||
dividend = dividend[key]
|
||||
value = dividend / divisor
|
||||
else:
|
||||
value = 0
|
||||
else:
|
||||
value = content
|
||||
if transform is None:
|
||||
value = '{:,}'.format(int(value))
|
||||
elif isinstance(transform, basestring):
|
||||
value = '{:,}'.format(int(value)) + ' ' + transform
|
||||
else:
|
||||
value = '{:,}'.format(int(transform(value)))
|
||||
except:
|
||||
if __debug__: print_exc()
|
||||
value = isinstance(transform, basestring) and '0 '+transform or '0'
|
||||
self.addpagerow(parent, [category, value])
|
||||
|
||||
def addpage(self, parent, content=[], align=tk.E):
|
||||
if platform in ['darwin', 'win32']:
|
||||
page = tk.Frame(parent, bg=PAGEBG)
|
||||
else:
|
||||
page =ttk.Frame(parent)
|
||||
page.grid(pady=10, sticky=tk.NSEW)
|
||||
page.columnconfigure(0, weight=1)
|
||||
if content:
|
||||
self.addpageheader(page, content, align=align)
|
||||
return page
|
||||
|
||||
def addpageheader(self, parent, content, align=tk.E):
|
||||
#if parent.grid_size()[1]: # frame not empty - add spacer
|
||||
# self.addpagerow(parent, [''])
|
||||
self.addpagerow(parent, content, align=align)
|
||||
ttk.Separator(parent, orient=tk.HORIZONTAL).grid(columnspan=3, padx=10, pady=2, sticky=tk.EW)
|
||||
|
||||
def addpagespacer(self, parent):
|
||||
self.addpagerow(parent, [''])
|
||||
|
||||
def addpagerow(self, parent, content, align=tk.E):
|
||||
for i in range(len(content)):
|
||||
if platform in ['darwin', 'win32']:
|
||||
label = tk.Label(parent, text=content[i], fg=PAGEFG, bg=PAGEBG)
|
||||
else:
|
||||
label =ttk.Label(parent, text=content[i])
|
||||
if i == 0:
|
||||
label.grid(padx=10, sticky=tk.W)
|
||||
row = parent.grid_size()[1]-1
|
||||
elif i == 2:
|
||||
label.grid(row=row, column=i, padx=10, sticky=align)
|
||||
else:
|
||||
label.grid(row=row, column=i, padx=10, sticky=align, columnspan=4-len(content))
|
||||
|
||||
|
||||
def ranktitle(self, category, rank):
|
||||
if rank is None:
|
||||
return 'None'
|
||||
elif not category in self.RANKS or rank >= len(self.RANKS[category]):
|
||||
return 'Rank %d' % rank
|
||||
else:
|
||||
return self.RANKS[category][rank]
|
||||
|
||||
def elapsed(self, game_time):
|
||||
return '%3dh%02dm' % ((game_time // 3600) % 3600, (game_time // 60) % 60)
|
Loading…
x
Reference in New Issue
Block a user