diff --git a/gui/builtinContextMenus/insuranceOptions.py b/gui/builtinContextMenus/insuranceOptions.py new file mode 100644 index 0000000000..a726d308e1 --- /dev/null +++ b/gui/builtinContextMenus/insuranceOptions.py @@ -0,0 +1,46 @@ +import wx +import gui.mainFrame +from gui.contextMenu import ContextMenu +from service.settings import InsuranceMenuSettings + + +class InsuranceOptions(ContextMenu): + def __init__(self): + self.mainFrame = gui.mainFrame.MainFrame.getInstance() + self.settings = InsuranceMenuSettings.getInstance() + self.optionList = ["Cost", "Payout", "Difference"] + + def display(self, srcContext, selection): + return srcContext in ("insuranceViewFull") + + def getText(self, itmContext, selection): + return "Column Display (Requires Restart)" + + def addOption(self, menu, option): + label = option + id = ContextMenu.nextID() + self.optionIds[id] = option + menuItem = wx.MenuItem(menu, id, label, kind=wx.ITEM_CHECK) + menu.Bind(wx.EVT_MENU, self.handleMode, menuItem) + return menuItem + + def getSubMenu(self, context, selection, rootMenu, i, pitem): + msw = True if "wxMSW" in wx.PlatformInfo else False + self.context = context + self.optionIds = {} + + sub = wx.Menu() + + for option in self.optionList: + menuItem = self.addOption(rootMenu if msw else sub, option) + sub.Append(menuItem) + menuItem.Check(self.settings.get(option.lower())) + + return sub + + def handleMode(self, event): + option = self.optionIds[event.Id] + self.settings.set(option.lower(), event.Int) + + +InsuranceOptions.register() diff --git a/gui/builtinPreferenceViews/pyfaStatViewPreferences.py b/gui/builtinPreferenceViews/pyfaStatViewPreferences.py index e9e0d8cb09..41810e2e16 100644 --- a/gui/builtinPreferenceViews/pyfaStatViewPreferences.py +++ b/gui/builtinPreferenceViews/pyfaStatViewPreferences.py @@ -99,6 +99,12 @@ def populatePanel(self, panel): self.rbOutgoing.SetSelection(self.settings.get('outgoing')) rbSizerRow3.Add(self.rbOutgoing, 1, wx.TOP | wx.RIGHT, 5) self.rbOutgoing.Bind(wx.EVT_RADIOBOX, self.OnOutgoingChange) + + self.rbInsurance = wx.RadioBox(panel, -1, "Insurance", wx.DefaultPosition, wx.DefaultSize, ['None', 'Minimal', 'Full'], 1, wx.RA_SPECIFY_COLS) + self.rbInsurance.EnableItem(1, False) + self.rbInsurance.SetSelection(self.settings.get('insurance')) + rbSizerRow3.Add(self.rbInsurance, 1, wx.TOP | wx.RIGHT, 5) + self.rbInsurance.Bind(wx.EVT_RADIOBOX, self.OnInsuranceChange) # We don't have views for these.....yet ''' self.rbMining = wx.RadioBox(panel, -1, "Mining", wx.DefaultPosition, wx.DefaultSize, @@ -143,6 +149,9 @@ def OnPriceChange(self, event): def OnOutgoingChange(self, event): self.settings.set('outgoing', event.GetInt()) + def OnInsuranceChange(self, event): + self.settings.set('insurance', event.GetInt()) + def OnMiningYieldChange(self, event): self.settings.set('miningyield', event.GetInt()) diff --git a/gui/builtinStatsViews/__init__.py b/gui/builtinStatsViews/__init__.py index 8769c7aefa..dd4a542e40 100644 --- a/gui/builtinStatsViews/__init__.py +++ b/gui/builtinStatsViews/__init__.py @@ -9,4 +9,5 @@ "targetingMiscViewMinimal", "priceViewFull", "priceViewMinimal", + "insuranceViewFull", ] diff --git a/gui/builtinStatsViews/insuranceViewFull.py b/gui/builtinStatsViews/insuranceViewFull.py new file mode 100644 index 0000000000..7cda109583 --- /dev/null +++ b/gui/builtinStatsViews/insuranceViewFull.py @@ -0,0 +1,101 @@ +import wx +from gui.statsView import StatsView +from gui.utils.numberFormatter import formatAmount +from service.insurance import Insurance +from service.settings import InsuranceMenuSettings + + +class InsuranceViewFull(StatsView): + name = "insuranceViewFull" + + def __init__(self, parent): + StatsView.__init__(self) + self.parent = parent + self.insuranceLevels = None + self.settings = InsuranceMenuSettings.getInstance() + self.displayColumns = self.getDisplayColumns(self.settings) + + def getDisplayColumns(self, settings): + return {'cost': self.settings.get("cost"), 'payout': self.settings.get("payout"), 'difference': self.settings.get("difference")} + + ''' Future use when repopulate can be called during runtime, might need rewrite from changing displayColumns from list to dict + def settingsDiffer(self, settings): + newColumns = self.getDisplayColumns(settings) + if self.displayColumns == newColumns: + return False + self.displayColumns = newColumns + return True + ''' + + def getHeaderText(self, fit): + return "Insurance" + + def newBoxText(self, grid, contentPanel, text): + box = wx.BoxSizer(wx.VERTICAL) + grid.Add(box, 0, wx.ALIGN_TOP) + box.Add(wx.StaticText(contentPanel, wx.ID_ANY, text), 0, wx.ALIGN_CENTER) + + def newBoxLabel(self, grid, contentPanel, labeltype, label): + lbl = wx.StaticText(contentPanel, wx.ID_ANY, "0.00 ISK") + setattr(self, "labelInsurance{}{}".format(labeltype, label), lbl) + box = wx.BoxSizer(wx.VERTICAL) + grid.Add(box, 0, wx.ALIGN_TOP) + box.Add(lbl, 0, wx.ALIGN_LEFT) + + def populatePanel(self, contentPanel, headerPanel): + contentSizer = contentPanel.GetSizer() + self.panel = contentPanel + self.headerPanel = headerPanel + + columnCount = sum(self.displayColumns.values()) + 1 + + gridInsuranceValues = wx.GridSizer(6, columnCount, 0, 0) + contentSizer.Add(gridInsuranceValues, 0, wx.EXPAND | wx.ALL, 0) + + self.newBoxText(gridInsuranceValues, contentPanel, "Level") + + if (self.settings.get("cost")): + self.newBoxText(gridInsuranceValues, contentPanel, "Cost") + if (self.settings.get("payout")): + self.newBoxText(gridInsuranceValues, contentPanel, "Payout") + if (self.settings.get("difference")): + self.newBoxText(gridInsuranceValues, contentPanel, "Difference") + + for level in ["Basic", "Bronze", "Silver", "Gold", "Platinum"]: + self.newBoxText(gridInsuranceValues, contentPanel, level) + if (self.settings.get("cost")): + self.newBoxLabel(gridInsuranceValues, contentPanel, "Cost", level) + if (self.settings.get("payout")): + self.newBoxLabel(gridInsuranceValues, contentPanel, "Payout", level) + if (self.settings.get("difference")): + self.newBoxLabel(gridInsuranceValues, contentPanel, "Difference", level) + + def refreshPanel(self, fit): + if fit is not None: + sInsurance = Insurance.getInstance() + self.insuranceLevels = sInsurance.getInsurance(fit.ship.item.ID) + + # Currently populate is only called on init from statsPane.py, so a restart is required for repopulate + # Could also create the 6 different configurations and enable/disable, but it looks like work is being + # done to add runtime repopulation of panels, so I'm going to just require restart for column view change + # to take effect, and then enable this function when the changes for runtime repopulation go live + # if self.settingsDiffer(self.settings): + # self.populatePanel(self.panel, self.headerPanel, True) + + self.refreshInsurancePanelPrices() + self.panel.Layout() + + def refreshInsurancePanelPrices(self): + if self.insuranceLevels: + for index, label in enumerate(["Basic", "Bronze", "Silver", "Gold", "Platinum"]): + cost = self.insuranceLevels[index].get('cost') + payout = self.insuranceLevels[index].get('payout') + if self.displayColumns["cost"]: + getattr(self, "labelInsuranceCost%s" % label).SetLabel("%s ISK" % formatAmount(cost, 3, 3, 9, currency=True)) + if self.displayColumns["payout"]: + getattr(self, "labelInsurancePayout%s" % label).SetLabel("%s ISK" % formatAmount(payout, 3, 3, 9, currency=True)) + if self.displayColumns["difference"]: + getattr(self, "labelInsuranceDifference%s" % label).SetLabel("%s ISK" % formatAmount(payout - cost, 3, 3, 9, currency=True)) + + +InsuranceViewFull.register() diff --git a/gui/contextMenu.py b/gui/contextMenu.py index 08ea258d5b..8ec354f657 100644 --- a/gui/contextMenu.py +++ b/gui/contextMenu.py @@ -210,4 +210,5 @@ def getBitmap(self, context, selection): commandFits, tabbedFits, mutaplasmids, + insuranceOptions, ) diff --git a/gui/statsPane.py b/gui/statsPane.py index b3984ccfa9..c33b9a16f2 100644 --- a/gui/statsPane.py +++ b/gui/statsPane.py @@ -44,6 +44,7 @@ class StatsPane(wx.Panel): "capacitor", "targetingMisc", "price", + "insurance", ] # Don't have these....yet.... diff --git a/gui/statsView.py b/gui/statsView.py index dc4c0276b0..50d2c45edb 100644 --- a/gui/statsView.py +++ b/gui/statsView.py @@ -55,4 +55,5 @@ def refreshPanel(self, fit): priceViewMinimal, outgoingViewFull, outgoingViewMinimal, + insuranceViewFull, ) diff --git a/pyfa.py b/pyfa.py index 740d443abf..49d8a041f8 100755 --- a/pyfa.py +++ b/pyfa.py @@ -138,6 +138,11 @@ def _process_args(self, largs, rargs, values): mf = MainFrame(options.title) ErrorHandler.SetParent(mf) + from service.insurance import Insurance + # Needs to be performed after wx init + sInsurance = Insurance.getInstance() + sInsurance.apiFetch(lambda response: pyfalog.debug(str(response))) + if options.profile_path: profile_path = os.path.join(options.profile_path, 'pyfa-{}.profile'.format(datetime.datetime.now().strftime('%Y%m%d_%H%M%S'))) pyfalog.debug("Starting pyfa with a profiler, saving to {}".format(profile_path)) diff --git a/service/esi.py b/service/esi.py index 4b5f8198f8..d51f8d99c7 100644 --- a/service/esi.py +++ b/service/esi.py @@ -77,6 +77,10 @@ def getSsoCharacter(self, id): eos.db.commit() return char + def getInsurance(self): + resp = super().getInsurance() + return resp.json() + def getSkills(self, id): char = self.getSsoCharacter(id) resp = super().getSkills(char) diff --git a/service/esiAccess.py b/service/esiAccess.py index bbddc63848..00c7a54850 100644 --- a/service/esiAccess.py +++ b/service/esiAccess.py @@ -71,6 +71,7 @@ class ESIEndpoints(Enum): CHAR_SKILLS = "/v4/characters/{character_id}/skills/" CHAR_FITTINGS = "/v1/characters/{character_id}/fittings/" CHAR_DEL_FIT = "/v1/characters/{character_id}/fittings/{fitting_id}/" + INSURANCE = "/v1/insurance/prices/" class EsiAccess(object): @@ -109,6 +110,9 @@ def oauth_authorize(self): def oauth_token(self): return '%s/oauth/token' % self.sso_url + def getInsurance(self): + return self.get(None, ESIEndpoints.INSURANCE) + def getSkills(self, char): return self.get(char, ESIEndpoints.CHAR_SKILLS, character_id=char.characterID) @@ -269,7 +273,8 @@ def _after_request(self, resp): return resp def get(self, ssoChar, endpoint, *args, **kwargs): - self._before_request(ssoChar) + if ssoChar: + self._before_request(ssoChar) endpoint = endpoint.format(**kwargs) return self._after_request(self._session.get("{}{}".format(self.esi_url, endpoint))) diff --git a/service/insurance.py b/service/insurance.py new file mode 100644 index 0000000000..e68c73fc44 --- /dev/null +++ b/service/insurance.py @@ -0,0 +1,57 @@ +import sys +import wx +import threading +from logbook import Logger +from service.esi import Esi + +pyfalog = Logger(__name__) + + +class Insurance(): + instance = None + + @classmethod + def getInstance(cls): + if cls.instance is None: + cls.instance = Insurance() + + return cls.instance + + def __init__(self): + self.allInsurance = None + + def apiFetch(self, callerCallback): + thread = InsuranceApiThread((self.insuranceApiCallback, callerCallback)) + thread.start() + + # Modify the Insurance class with data from the threaded api call if there were no errors + def insuranceApiCallback(self, response, callerCallback, e=None): + if e: + wx.CallAfter(callerCallback, e) + else: + self.allInsurance = response + wx.CallAfter(callerCallback, response) + + def getInsurance(self, typeID): + if self.allInsurance: + # Search the insurance list for the first item that has the typeID we are looking for + try: + return next(iter([item for item in self.allInsurance if item.get('type_id') == typeID]))['levels'] + except StopIteration: + return None + + +class InsuranceApiThread(threading.Thread): + def __init__(self, callback): + threading.Thread.__init__(self) + self.name = "InsuranceImport" + self.callback = callback + + def run(self): + try: + sEsi = Esi.getInstance() + resp = sEsi.getInsurance() + self.callback[0](resp, self.callback[1]) + except Exception as ex: + pyfalog.warn(ex) + self.callback[0](None, self.callback[1], sys.exc_info()) diff --git a/service/settings.py b/service/settings.py index 75bdb54057..8b7bbeb875 100644 --- a/service/settings.py +++ b/service/settings.py @@ -431,6 +431,7 @@ def __init__(self): "miningyield" : 2, "drones" : 2, "outgoing" : 2, + "insurance" : 2, } # We don't have these....yet @@ -448,6 +449,36 @@ def set(self, type, value): self.serviceStatViewDefaultSettings[type] = value +class InsuranceMenuSettings(object): + _instance = None + + @classmethod + def getInstance(cls): + if cls._instance is None: + cls._instance = InsuranceMenuSettings() + + return cls._instance + + def __init__(self): + # mode + # 0 - Do not add to total + # 1 - Add to total + InsuranceMenuDefaultSettings = { + "cost" : 1, + "payout" : 1, + "difference" : 1 + } + + self.InsuranceMenuDefaultSettings = SettingsProvider.getInstance().getSettings("pyfaInsuranceMenuSettings", + InsuranceMenuDefaultSettings) + + def get(self, type): + return self.InsuranceMenuDefaultSettings[type] + + def set(self, type, value): + self.InsuranceMenuDefaultSettings[type] = value + + class PriceMenuSettings(object): _instance = None