diff --git a/EDMarketConnector.py b/EDMarketConnector.py index 0482a8ad..6ef6190b 100755 --- a/EDMarketConnector.py +++ b/EDMarketConnector.py @@ -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: diff --git a/EDMarketConnector.wxs b/EDMarketConnector.wxs index 4461bbdc..c23ad5c6 100644 --- a/EDMarketConnector.wxs +++ b/EDMarketConnector.wxs @@ -51,9 +51,6 @@ - - - @@ -81,9 +78,6 @@ - - - @@ -324,7 +318,6 @@ - @@ -334,7 +327,6 @@ - diff --git a/README.md b/README.md index 59b83f42..391b5687 100644 --- a/README.md +++ b/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. ![Windows screenshot](img/win.png) ![Mac screenshot](img/mac.png) -![Balance screenshot](img/Balance.png) - 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 -------- diff --git a/chart.py b/chart.py deleted file mode 100644 index 128d8cbb..00000000 --- a/chart.py +++ /dev/null @@ -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) diff --git a/config.py b/config.py index a56a8a93..7b14d6f8 100644 --- a/config.py +++ b/config.py @@ -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': diff --git a/prefs.py b/prefs.py index b245dd33..45dab303 100644 --- a/prefs.py +++ b/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() diff --git a/stats.py b/stats.py deleted file mode 100644 index a49854cf..00000000 --- a/stats.py +++ /dev/null @@ -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)