Merge branch 'master' into singularity

This commit is contained in:
blitzmann
2018-05-20 00:22:24 -04:00
35 changed files with 168 additions and 131 deletions

View File

@@ -24,7 +24,7 @@ saveInRoot = False
# Version data
version = "2.0.1"
version = "2.0.2"
tag = "Stable"
expansionName = "YC120.3"
expansionVersion = "1.8"

View File

@@ -29,9 +29,6 @@ from eos.saveddata.user import User
from eos.saveddata.character import Character, Skill
from eos.saveddata.ssocharacter import SsoCharacter
characters_table = Table("characters", saveddata_meta,
Column("ID", Integer, primary_key=True),
Column("name", String, nullable=False),

View File

@@ -5,6 +5,6 @@ type = "passive"
def handler(fit, src, context):
groups = ("Structure Anti-Subcapital Missile", "Structure Anti-Capital Missile")
fit.modules.filteredItemBoost(lambda mod: mod.charge.group.name in groups,
fit.modules.filteredChargeBoost(lambda mod: mod.charge.group.name in groups,
"aoeVelocity", src.getModifiedItemAttr("structureRigMissileExploVeloBonus"),
stackingPenalties=True)

View File

@@ -4,6 +4,6 @@ type = "passive"
def handler(fit, src, context):
groups = ("Structure Anti-Subcapital Missile", "Structure Anti-Capital Missile")
fit.modules.filteredItemBoost(lambda mod: mod.charge.group.name in groups,
fit.modules.filteredChargeBoost(lambda mod: mod.charge.group.name in groups,
"maxVelocity", src.getModifiedItemAttr("structureRigMissileVelocityBonus"),
stackingPenalties=True)

View File

@@ -17,8 +17,8 @@ def handler(fit, module, context):
# this is such a dirty hack
for mod in fit.modules:
if not mod.isEmpty and mod.state > State.ONLINE and (
mod.item.requiresSkill("Micro Jump Drive Operation")
or mod.item.requiresSkill("High Speed Maneuvering")
mod.item.requiresSkill("Micro Jump Drive Operation") or
mod.item.requiresSkill("High Speed Maneuvering")
):
mod.state = State.ONLINE
if not mod.isEmpty and mod.item.requiresSkill("Micro Jump Drive Operation") and mod.state > State.ONLINE:

View File

@@ -170,7 +170,6 @@ class Character(object):
if x.client == clientHash:
self.__ssoCharacters.remove(x)
def getSsoCharacter(self, clientHash):
return next((x for x in self.__ssoCharacters if x.client == clientHash), None)

View File

@@ -144,10 +144,11 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
return empty
@classmethod
def buildRack(cls, slot):
def buildRack(cls, slot, num=None):
empty = Rack(None)
empty.__slot = slot
empty.dummySlot = slot
empty.num = num
return empty
@property
@@ -801,6 +802,7 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
class Rack(Module):
"""
This is simply the Module class named something else to differentiate
it for app logic. This class does not do anything special
it for app logic. The only thing interesting about it is the num property,
which is the number of slots for this rack
"""
pass
num = None

View File

@@ -33,7 +33,6 @@ class SsoCharacter(object):
self.refreshToken = refreshToken
self.accessTokenExpires = None
@reconstructor
def init(self):
pass

View File

@@ -58,9 +58,9 @@ class ModuleAmmoPicker(ContextMenu):
def turretSorter(self, charge):
damage = 0
range_ = (self.module.getModifiedItemAttr("maxRange")) * \
range_ = (self.module.item.getAttribute("maxRange")) * \
(charge.getAttribute("weaponRangeMultiplier") or 1)
falloff = (self.module.getModifiedItemAttr("falloff")) * \
falloff = (self.module.item.getAttribute("falloff")) * \
(charge.getAttribute("fallofMultiplier") or 1)
for type_ in self.DAMAGE_TYPES:
d = charge.getAttribute("%sDamage" % type_)

View File

@@ -43,13 +43,17 @@ class PFEsiPref(PreferenceView):
rbSizer = wx.BoxSizer(wx.HORIZONTAL)
self.rbMode = wx.RadioBox(panel, -1, "Login Authentication Method", wx.DefaultPosition, wx.DefaultSize,
['Local Server', 'Manual'], 1, wx.RA_SPECIFY_COLS)
self.rbMode.SetItemToolTip(0, "This options starts a local webserver that the web application will call back to with information about the character login.")
self.rbMode.SetItemToolTip(1, "This option prompts users to copy and paste information from the web application to allow for character login. Use this if having issues with the local server.")
self.rbMode.SetItemToolTip(0, "This options starts a local webserver that the web application will call back to"
" with information about the character login.")
self.rbMode.SetItemToolTip(1, "This option prompts users to copy and paste information from the web application "
"to allow for character login. Use this if having issues with the local server.")
self.rbSsoMode = wx.RadioBox(panel, -1, "SSO Mode", wx.DefaultPosition, wx.DefaultSize,
['pyfa.io', 'Custom application'], 1, wx.RA_SPECIFY_COLS)
self.rbSsoMode.SetItemToolTip(0, "This options routes SSO Logins through pyfa.io, allowing you to easily login without any configuration. When in doubt, use this option.")
self.rbSsoMode.SetItemToolTip(1, "This option goes through EVE SSO directly, but requires more configuration. Use this is pyfa.io is blocked for some reason, or if you do not wish to route data throguh pyfa.io.")
self.rbSsoMode.SetItemToolTip(0, "This options routes SSO Logins through pyfa.io, allowing you to easily login "
"without any configuration. When in doubt, use this option.")
self.rbSsoMode.SetItemToolTip(1, "This option goes through EVE SSO directly, but requires more configuration. Use "
"this is pyfa.io is blocked for some reason, or if you do not wish to route data throguh pyfa.io.")
self.rbMode.SetSelection(self.settings.get('loginMode'))
self.rbSsoMode.SetSelection(self.settings.get('ssoMode'))
@@ -70,12 +74,6 @@ class PFEsiPref(PreferenceView):
mainSizer.Add(wx.StaticLine(panel, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LI_HORIZONTAL), 0,
wx.EXPAND, 5)
# self.stInfo = wx.StaticText(panel, wx.ID_ANY,
# u"Using custom applications details will let pyfa to access the SSO under your application, rather than the pyfa application that is automatically set up. This requires you to set up your own ESI client application and accept CCPs License Agreement. Additionally, when setting up your client, make sure the callback url is set to 'http://localhost:6461'. Please see the pyfa wiki for more information regarding this",
# wx.DefaultPosition, wx.DefaultSize, 0)
# self.stInfo.Wrap(dlgWidth)
# mainSizer.Add(self.stInfo, 0, wx.EXPAND | wx.TOP | wx.BOTTOM, 5)
fgAddrSizer = wx.FlexGridSizer(2, 2, 0, 0)
fgAddrSizer.AddGrowableCol(1)
fgAddrSizer.SetFlexibleDirection(wx.BOTH)
@@ -105,10 +103,6 @@ class PFEsiPref(PreferenceView):
mainSizer.Add(fgAddrSizer, 0, wx.EXPAND, 5)
timeoutSizer = wx.BoxSizer(wx.HORIZONTAL)
# self.stTimout = wx.StaticText(panel, wx.ID_ANY, "Timeout (seconds):", wx.DefaultPosition, wx.DefaultSize, 0)
# self.stTimout.Wrap(-1)
#

View File

@@ -150,10 +150,10 @@ class PFNetworkPref(PreferenceView):
proxy = self.settings.autodetect()
if proxy is not None:
addr, port = proxy
txt = addr + ":" + str(port)
addr, port = proxy
txt = addr + ":" + str(port)
else:
txt = "None"
txt = "None"
self.stPSAutoDetected.SetLabel("Auto-detected: " + txt)
self.stPSAutoDetected.Disable()

View File

@@ -129,7 +129,7 @@ class FirepowerViewFull(StatsView):
# Remove effective label
hsizer = self.headerPanel.GetSizer()
hsizer.Hide(self.stEff)
#self.stEff.Destroy()
# self.stEff.Destroy()
# Get the new view
view = StatsView.getView("miningyieldViewFull")(self.parent)

View File

@@ -73,7 +73,7 @@ class BaseName(ViewColumn):
if stuff.slot == Slot.MODE:
return '─ Tactical Mode ─'
else:
return '{} Slots '.format(Slot.getName(stuff.slot).capitalize())
return '{} {} Slot{}'.format(stuff.num, Slot.getName(stuff.slot).capitalize(), '' if stuff.num == 1 else 's')
else:
return ""
elif isinstance(stuff, Module):

View File

@@ -222,12 +222,15 @@ class FittingView(d.Display):
wx.PostEvent(self.mainFrame, FitSelected(fitID=fitID))
def Destroy(self):
# @todo: when wxPython 4.0.2 is release, https://github.com/pyfa-org/Pyfa/issues/1586#issuecomment-390074915
# Make sure to remove the shitty checks that I have to put in place for these handlers to ignore when self is None
print("+++++ Destroy " + repr(self))
print(self.parent.Unbind(EVT_NOTEBOOK_PAGE_CHANGED))
print(self.mainFrame.Unbind(GE.FIT_CHANGED))
print(self.mainFrame.Unbind(EVT_FIT_RENAMED))
print(self.mainFrame.Unbind(EVT_FIT_REMOVED))
print(self.mainFrame.Unbind(ITEM_SELECTED))
# print(self.parent.Unbind(EVT_NOTEBOOK_PAGE_CHANGED))
# print(self.mainFrame.Unbind(GE.FIT_CHANGED, handler=self.fitChanged))
# print(self.mainFrame.Unbind(EVT_FIT_RENAMED, handler=self.fitRenamed ))
# print(self.mainFrame.Unbind(EVT_FIT_REMOVED, handler=self.fitRemoved))
# print(self.mainFrame.Unbind(ITEM_SELECTED, handler=self.appendItem))
d.Display.Destroy(self)
@@ -291,6 +294,9 @@ class FittingView(d.Display):
"""
print('_+_+_+_+_+_ Fit Removed: {} {} activeFitID: {}, eventFitID: {}'.format(repr(self), str(bool(self)), self.activeFitID, event.fitID))
pyfalog.debug("FittingView::fitRemoved")
if not self:
event.Skip()
return
if event.fitID == self.getActiveFit():
pyfalog.debug(" Deleted fit is currently active")
self.parent.DeletePage(self.parent.GetPageIndex(self))
@@ -311,6 +317,9 @@ class FittingView(d.Display):
event.Skip()
def fitRenamed(self, event):
if not self:
event.Skip()
return
fitID = event.fitID
if fitID == self.getActiveFit():
self.updateTab()
@@ -347,6 +356,9 @@ class FittingView(d.Display):
self.parent.SetPageTextIcon(pageIndex, text, bitmap)
def appendItem(self, event):
if not self:
event.Skip()
return
if self.parent.IsActive(self):
itemID = event.itemID
fitID = self.activeFitID
@@ -508,7 +520,7 @@ class FittingView(d.Display):
# second loop modifies self.mods, rewrites self.blanks to represent actual index of blanks
for i, (x, slot) in enumerate(self.blanks):
self.blanks[i] = x + i # modify blanks with actual index
self.mods.insert(x + i, Rack.buildRack(slot))
self.mods.insert(x + i, Rack.buildRack(slot, sum(m.slot == slot for m in self.mods)))
if fit.mode:
# Modes are special snowflakes and need a little manual loving
@@ -516,7 +528,7 @@ class FittingView(d.Display):
# while also marking the mode header position in the Blanks list
if sFit.serviceFittingOptions["rackSlots"]:
self.blanks.append(len(self.mods))
self.mods.append(Rack.buildRack(Slot.MODE))
self.mods.append(Rack.buildRack(Slot.MODE, None))
self.mods.append(fit.mode)
else:
@@ -528,7 +540,9 @@ class FittingView(d.Display):
def fitChanged(self, event):
print('====== Fit Changed: {} {} activeFitID: {}, eventFitID: {}'.format(repr(self), str(bool(self)), self.activeFitID, event.fitID))
if not self:
event.Skip()
return
try:
if self.activeFitID is not None and self.activeFitID == event.fitID:
self.generateMods()

View File

@@ -734,17 +734,14 @@ class APIView(wx.Panel):
self.stDisabledTip.Wrap(-1)
hintSizer.Add(self.stDisabledTip, 0, wx.TOP | wx.BOTTOM, 10)
self.noCharactersTip = wx.StaticText(self, wx.ID_ANY,
"You haven't logging into EVE SSO with any characters yet. Please use the "
"button below to log into EVE.", style=wx.ALIGN_CENTER)
self.noCharactersTip.Wrap(-1)
hintSizer.Add(self.noCharactersTip, 0, wx.TOP | wx.BOTTOM, 10)
self.stDisabledTip.Hide()
hintSizer.AddStretchSpacer()
pmainSizer.Add(hintSizer, 0, wx.EXPAND, 5)
fgSizerInput = wx.FlexGridSizer(3, 2, 0, 0)
fgSizerInput = wx.FlexGridSizer(1, 3, 0, 0)
fgSizerInput.AddGrowableCol(1)
fgSizerInput.SetFlexibleDirection(wx.BOTH)
fgSizerInput.SetNonFlexibleGrowMode(wx.FLEX_GROWMODE_SPECIFIED)
@@ -754,15 +751,28 @@ class APIView(wx.Panel):
fgSizerInput.Add(self.m_staticCharText, 0, wx.ALL | wx.ALIGN_RIGHT | wx.ALIGN_CENTER_VERTICAL, 10)
self.charChoice = wx.Choice(self, wx.ID_ANY, style=0)
fgSizerInput.Add(self.charChoice, 1, wx.ALL | wx.EXPAND, 10)
fgSizerInput.Add(self.charChoice, 1, wx.TOP | wx.BOTTOM | wx.EXPAND, 10)
self.fetchButton = wx.Button(self, wx.ID_ANY, "Get Skills", wx.DefaultPosition, wx.DefaultSize, 0)
self.fetchButton.Bind(wx.EVT_BUTTON, self.fetchSkills)
fgSizerInput.Add(self.fetchButton, 0, wx.ALL | wx.ALIGN_RIGHT | wx.ALIGN_CENTER_VERTICAL, 10)
pmainSizer.Add(fgSizerInput, 0, wx.EXPAND, 5)
pmainSizer.AddStretchSpacer()
self.m_staticline1 = wx.StaticLine(self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LI_HORIZONTAL)
pmainSizer.Add(self.m_staticline1, 0, wx.EXPAND | wx.ALL, 10)
self.noCharactersTip = wx.StaticText(self, wx.ID_ANY, "Don't see your EVE character in the list?", style=wx.ALIGN_CENTER)
self.noCharactersTip.Wrap(-1)
pmainSizer.Add(self.noCharactersTip, 0, wx.CENTER | wx.TOP | wx.BOTTOM, 0)
self.addButton = wx.Button(self, wx.ID_ANY, "Log In with EVE SSO", wx.DefaultPosition, wx.DefaultSize, 0)
self.addButton.Bind(wx.EVT_BUTTON, self.addCharacter)
pmainSizer.Add(self.addButton, 0, wx.ALL | wx.ALIGN_CENTER, 5)
self.stStatus = wx.StaticText(self, wx.ID_ANY, wx.EmptyString)
pmainSizer.Add(self.stStatus, 0, wx.ALL, 5)
pmainSizer.Add(self.addButton, 0, wx.ALL | wx.ALIGN_CENTER, 10)
self.charEditor.mainFrame.Bind(GE.EVT_SSO_LOGOUT, self.ssoListChanged)
self.charEditor.mainFrame.Bind(GE.EVT_SSO_LOGIN, self.ssoListChanged)
self.charEditor.entityEditor.Bind(wx.EVT_CHOICE, self.charChanged)
@@ -776,9 +786,18 @@ class APIView(wx.Panel):
def ssoCharChanged(self, event):
sChar = Character.getInstance()
activeChar = self.charEditor.entityEditor.getActiveEntity()
sChar.setSsoCharacter(activeChar.ID, self.getActiveCharacter())
ssoChar = self.getActiveCharacter()
sChar.setSsoCharacter(activeChar.ID, ssoChar)
self.fetchButton.Enable(ssoChar is not None)
event.Skip()
def fetchSkills(self, evt):
sChar = Character.getInstance()
char = self.charEditor.entityEditor.getActiveEntity()
sChar.apiFetch(char.ID, self.__fetchCallback)
def addCharacter(self, event):
sEsi = Esi.getInstance()
sEsi.login()
@@ -790,17 +809,6 @@ class APIView(wx.Panel):
def ssoListChanged(self, event):
if not self: # todo: fix event not unbinding properly
return
sEsi = Esi.getInstance()
ssoChars = sEsi.getSsoCharacters()
if len(ssoChars) == 0:
self.charChoice.Hide()
self.m_staticCharText.Hide()
self.noCharactersTip.Show()
else:
self.noCharactersTip.Hide()
self.m_staticCharText.Show()
self.charChoice.Show()
self.charChanged(event)
@@ -816,6 +824,8 @@ class APIView(wx.Panel):
sso = sChar.getSsoCharacter(activeChar.ID)
self.fetchButton.Enable(sso is not None)
ssoChars = sEsi.getSsoCharacters()
self.charChoice.Clear()
@@ -827,9 +837,9 @@ class APIView(wx.Panel):
if sso is not None and char.ID == sso.ID:
self.charChoice.SetSelection(currId)
if sso is None:
self.charChoice.SetSelection(noneID)
if sso is None:
self.charChoice.SetSelection(noneID)
#
# if chars:
@@ -853,13 +863,17 @@ class APIView(wx.Panel):
event.Skip()
def __fetchCallback(self, e=None):
charName = self.charChoice.GetString(self.charChoice.GetSelection())
if e is None:
self.stStatus.SetLabel("Successfully fetched {}\'s skills from EVE API.".format(charName))
else:
if e:
exc_type, exc_obj, exc_trace = e
pyfalog.error("Unable to retrieve {0}\'s skills. Error message:\n{1}".format(charName, exc_obj))
self.stStatus.SetLabel("Unable to retrieve {}\'s skills. Error message:\n{}".format(charName, exc_obj))
pyfalog.warn("Error fetching skill information for character")
pyfalog.warn(exc_obj)
wx.MessageBox(
"Error fetching skill information",
"Error", wx.ICON_ERROR | wx.STAY_ON_TOP)
else:
wx.MessageBox(
"Successfully fetched skills", "Success", wx.ICON_INFORMATION | wx.STAY_ON_TOP)
class SecStatusDialog(wx.Dialog):

View File

@@ -170,7 +170,6 @@ class CharacterSelection(wx.Panel):
def charChanged(self, event):
fitID = self.mainFrame.getActiveFit()
charID = self.getActiveCharacter()
sChar = Character.getInstance()
if charID == -1:
# revert to previous character

View File

@@ -1,4 +1,4 @@
#===============================================================================
# ===============================================================================
#
# ToDo: Bug - when selecting close on a tab, sometimes the tab to the right is
# selected, most likely due to determination of mouse position
@@ -11,7 +11,7 @@
# tab index?). This will also help with finding close buttons.
# ToDo: Fix page preview code (PFNotebookPagePreview)
#
#= ==============================================================================
# ===============================================================================
import wx
import wx.lib.newevent
@@ -413,7 +413,7 @@ class _TabRenderer:
mdc.SelectObject(ebmp)
mdc.SetFont(self.font)
textSizeX, textSizeY = mdc.GetTextExtent(self.text)
totalSize = self.left_width + self.right_width + textSizeX + self.close_btn_width / 2 + 16 + self.padding* 2
totalSize = self.left_width + self.right_width + textSizeX + self.close_btn_width / 2 + 16 + self.padding * 2
mdc.SelectObject(wx.NullBitmap)
return totalSize, self.tab_height
@@ -1478,4 +1478,3 @@ if __name__ == "__main__":
top = Frame("Test Chrome Tabs")
top.Show()
app.MainLoop()

View File

@@ -29,7 +29,6 @@ class EveFittings(wx.Frame):
self.mainFrame = parent
mainSizer = wx.BoxSizer(wx.VERTICAL)
sEsi = Esi.getInstance()
characterSelectSizer = wx.BoxSizer(wx.HORIZONTAL)
@@ -119,10 +118,11 @@ class EveFittings(wx.Frame):
pyfalog.error(msg)
self.statusbar.SetStatusText(msg)
except APIException as ex:
del waitDialog # Can't do this in a finally because then it obscures the message dialog
# Can't do this in a finally because then it obscures the message dialog
del waitDialog # noqa: F821
ESIExceptionHandler(self, ex)
except Exception as ex:
del waitDialog
del waitDialog # noqa: F821
raise ex
def importFitting(self, event):
@@ -180,7 +180,6 @@ class ExportToEve(wx.Frame):
self.mainFrame = parent
self.SetBackgroundColour(wx.SystemSettings.GetColour(wx.SYS_COLOUR_BTNFACE))
sEsi = Esi.getInstance()
mainSizer = wx.BoxSizer(wx.VERTICAL)
hSizer = wx.BoxSizer(wx.HORIZONTAL)
@@ -232,7 +231,6 @@ class ExportToEve(wx.Frame):
return self.charChoice.GetClientData(selection) if selection is not None else None
def exportFitting(self, event):
sPort = Port.getInstance()
fitID = self.mainFrame.getActiveFit()
self.statusbar.SetStatusText("", 0)
@@ -242,12 +240,8 @@ class ExportToEve(wx.Frame):
return
self.statusbar.SetStatusText("Sending request and awaiting response", 1)
sEsi = Esi.getInstance()
try:
sFit = Fit.getInstance()
data = sPort.exportESI(sFit.getFit(fitID))
res = sEsi.postFitting(self.getActiveCharacter(), data)
self.statusbar.SetStatusText("", 0)
self.statusbar.SetStatusText("", 1)
@@ -307,7 +301,7 @@ class SsoCharacterMgmt(wx.Dialog):
def ssoLogin(self, event):
if (self):
#todo: these events don't unbind properly when window is closed (?), hence the `if`. Figure out better way of doing this.
# todo: these events don't unbind properly when window is closed (?), hence the `if`. Figure out better way of doing this.
self.popCharList()
event.Skip()
@@ -372,6 +366,9 @@ class FittingsTreeView(wx.Panel):
if (fit['fitting_id'] in sEsi.fittings_deleted):
continue
ship = getItem(fit['ship_type_id'])
if ship is None:
pyfalog.debug('Cannot find ship type id: {}'.format(fit['ship_type_id']))
continue
if ship.name not in dict:
dict[ship.name] = []
dict[ship.name].append(fit)

View File

@@ -1,5 +1,6 @@
import wx
class SsoLogin(wx.Dialog):
def __init__(self, parent):
wx.Dialog.__init__(self, parent, id=wx.ID_ANY, title="SSO Login", size=wx.Size(400, 240))

View File

@@ -62,7 +62,6 @@ class UpdateDialog(wx.Dialog):
self.browser.Bind(wx.html2.EVT_WEBVIEW_NEWWINDOW, self.OnNewWindow)
link_patterns = [
(re.compile("([0-9a-f]{6,40})", re.I), r"https://github.com/pyfa-org/Pyfa/commit/\1"),
(re.compile("#(\d+)", re.I), r"https://github.com/pyfa-org/Pyfa/issues/\1"),
(re.compile("@(\w+)", re.I), r"https://github.com/\1")
]
@@ -71,12 +70,27 @@ class UpdateDialog(wx.Dialog):
extras=['cuddled-lists', 'fenced-code-blocks', 'target-blank-links', 'toc', 'link-patterns'],
link_patterns=link_patterns)
release_markup = markdowner.convert(self.releaseInfo['body'])
# run the text through markup again, this time with the hashing pattern. This is required due to bugs in markdown2:
# https://github.com/trentm/python-markdown2/issues/287
link_patterns = [
(re.compile("([0-9a-f]{6,40})", re.I), r"https://github.com/pyfa-org/Pyfa/commit/\1"),
]
markdowner = markdown2.Markdown(
extras=['cuddled-lists', 'fenced-code-blocks', 'target-blank-links', 'toc', 'link-patterns'],
link_patterns=link_patterns)
# The space here is required, again, due to bug. Again, see https://github.com/trentm/python-markdown2/issues/287
release_markup = markdowner.convert(' ' + release_markup)
self.browser.SetPage(html_tmpl.format(
self.releaseInfo['tag_name'],
releaseDate.strftime('%B %d, %Y'),
"<p class='text-danger'><b>This is a pre-release, be prepared for unstable features</b></p>" if version.is_prerelease else "",
markdowner.convert(self.releaseInfo['body'])
),"")
release_markup
), "")
notesSizer.Add(self.browser, 1, wx.EXPAND | wx.LEFT | wx.RIGHT | wx.TOP, 5)
mainSizer.Add(notesSizer, 1, wx.EXPAND, 5)

View File

@@ -86,7 +86,7 @@ class exportHtmlThread(threading.Thread):
<head>
<title>Pyfa Fittings</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta charset="utf-8" />
<meta charset="utf-8" />
<link rel="stylesheet" href="https://code.jquery.com/mobile/1.4.2/jquery.mobile-1.4.2.min.css" />
<script src="https://code.jquery.com/jquery-1.11.0.min.js"></script>
<script>

View File

@@ -123,6 +123,7 @@ class SkillBackupThread(threading.Thread):
wx.CallAfter(self.callback)
class Character(object):
instance = None
skillReqsDict = {}

View File

@@ -17,7 +17,6 @@ from service.server import StoppableHTTPServer, AuthHandler
from service.settings import EsiSettings
from service.esiAccess import EsiAccess
import wx
from requests import Session
pyfalog = Logger(__name__)
@@ -108,7 +107,8 @@ class Esi(EsiAccess):
serverAddr = None
# always start the local server if user is using client details. Otherwise, start only if they choose to do so.
if self.settings.get('ssoMode') == SsoMode.CUSTOM or self.settings.get('loginMode') == LoginMethod.SERVER:
serverAddr = self.startServer(6461 if self.settings.get('ssoMode') == SsoMode.CUSTOM else 0) # random port, or if it's custom application, use a defined port
# random port, or if it's custom application, use a defined port
serverAddr = self.startServer(6461 if self.settings.get('ssoMode') == SsoMode.CUSTOM else 0)
uri = self.getLoginURI(serverAddr)
webbrowser.open(uri)
wx.PostEvent(self.mainFrame, GE.SsoLoggingIn(sso_mode=self.settings.get('ssoMode'), login_mode=self.settings.get('loginMode')))
@@ -181,4 +181,3 @@ class Esi(EsiAccess):
pyfalog.debug("Handling SSO login with: {0}", message)
self.handleLogin(message)

View File

@@ -18,7 +18,7 @@ import base64
import datetime
from eos.enum import Enum
from service.settings import EsiSettings
from service.settings import EsiSettings, NetworkSettings
from requests import Session
from urllib.parse import urlencode, quote
@@ -85,6 +85,7 @@ class EsiAccess(object):
'pyfa v{}'.format(config.version)
)
})
self._session.proxies = NetworkSettings.getInstance().getProxySettingsInRequestsFormat()
@property
def sso_url(self):
@@ -180,7 +181,7 @@ class EsiAccess(object):
data = {
'grant_type': 'refresh_token',
'refresh_token': refreshToken,
}
}
if self.settings.get('ssoMode') == SsoMode.AUTO:
# data is all we really need, the rest is handled automatically by pyfa.io
@@ -281,4 +282,3 @@ class EsiAccess(object):
self._before_request(ssoChar)
endpoint = endpoint.format(**kwargs)
return self._after_request(self._session.delete("{}{}".format(self.esi_url, endpoint)))

View File

@@ -1209,11 +1209,9 @@ class Fit(object):
start_time = time()
pyfalog.info("=" * 10 + "recalc: {0}" + "=" * 10, fit.name)
fit.factorReload = self.serviceFittingOptions["useGlobalForceReload"]
fit.clear()
fit.calculateModifiedAttributes()
pyfalog.info("=" * 10 + "recalc time: " + str(time() - start_time) + "=" * 10)

View File

@@ -20,17 +20,18 @@
import config
import pkg_resources
class Jargon(object):
def __init__(self, rawdata: dict):
self._rawdata = rawdata
# copy the data to lowercase keys, ignore blank keys
self._data = {str(k).lower():v for k,v in rawdata.items() if k}
self._data = {str(k).lower(): v for k, v in rawdata.items() if k}
def get(self, term: str) -> str:
return self._data.get(term.lower())
def get_rawdata() -> dict:
def get_rawdata(self) -> dict:
return self._rawdata
def apply(self, query):

View File

@@ -26,11 +26,12 @@ from .resources import DEFAULT_DATA, DEFAULT_HEADER
JARGON_PATH = os.path.join(config.savePath, 'jargon.yaml')
class JargonLoader(object):
def __init__(self, jargon_path: str):
self.jargon_path = jargon_path
self._jargon_mtime = 0 # type: int
self._jargon = None # type: Jargon
self._jargon_mtime = 0 # type: int
self._jargon = None # type: Jargon
def get_jargon(self) -> Jargon:
if self._is_stale():
@@ -58,7 +59,7 @@ class JargonLoader(object):
def init_user_jargon(jargon_path):
values = yaml.load(DEFAULT_DATA)
## Disabled for issue/1533; do not overwrite existing user config
# Disabled for issue/1533; do not overwrite existing user config
# if os.path.exists(jargon_path):
# with open(jargon_path) as f:
# custom_values = yaml.load(f)
@@ -72,6 +73,7 @@ class JargonLoader(object):
yaml.dump(values, stream=f, default_flow_style=False)
_instance = None
@staticmethod
def instance(jargon_path=None):
if not JargonLoader._instance:
@@ -79,4 +81,5 @@ class JargonLoader(object):
JargonLoader._instance = JargonLoader(jargon_path)
return JargonLoader._instance
JargonLoader.init_user_jargon(JARGON_PATH)

View File

@@ -41,6 +41,7 @@ pyfalog = Logger(__name__)
# Event which tells threads dependent on Market that it's initialized
mktRdy = threading.Event()
class ShipBrowserWorkerThread(threading.Thread):
def __init__(self):
threading.Thread.__init__(self)
@@ -113,10 +114,8 @@ class SearchWorkerThread(threading.Thread):
else:
filter_ = None
jargon_request = self.jargonLoader.get_jargon().apply(request)
results = []
if len(request) >= config.minItemSearchLength:
results = eos.db.searchItems(request, where=filter_,

View File

@@ -35,7 +35,7 @@ class EveMarketData(object):
def __init__(self, types, system, priceMap):
data = {}
baseurl = "https://eve-marketdata.com/api/item_prices.xml"
data["system_id"] = system # Use Jita for market
data["system_id"] = system # Use Jita for market
data["type_ids"] = ','.join(str(x) for x in types)
network = Network.getInstance()

View File

@@ -93,25 +93,7 @@ class Network(object):
# or with HTTP Basic auth support: proxies = {'http': 'http://user:pass@10.10.1.10:3128/'}
# then you do: requests.get('http://example.org', proxies=proxies)
proxies = None
proxy_settings = NetworkSettings.getInstance().getProxySettings()
# proxy_settings is a tuple of (host, port), like ('192.168.20.1', 3128), or None
if proxy_settings is not None:
# form proxy address in format "http://host:port
proxy_host_port = '{}:{}'.format(proxy_settings[0], proxy_settings[1])
proxy_auth_details = NetworkSettings.getInstance().getProxyAuthDetails()
# proxy_auth_details is a tuple of (login, password), or None
user_pass = ''
if proxy_auth_details is not None:
# construct prefix in form "user:password@"
user_pass = '{}:{}@'.format(proxy_auth_details[0], proxy_auth_details[1])
proxies = {
'http': 'http://' + user_pass + proxy_host_port,
'https': 'http://' + user_pass + proxy_host_port
}
# final form: { 'http': 'http://user:password@host:port', ... }, or
# { 'http': 'http://host:port', ... } if no auth info.
proxies = NetworkSettings.getInstance().getProxySettingsInRequestsFormat()
try:
resp = requests.get(url, headers=headers, proxies=proxies, **kwargs)

View File

@@ -58,6 +58,7 @@ from collections import OrderedDict
class ESIExportException(Exception):
pass
pyfalog = Logger(__name__)
EFT_SLOT_ORDER = [Slot.LOW, Slot.MED, Slot.HIGH, Slot.RIG, Slot.SUBSYSTEM, Slot.SERVICE]
@@ -349,7 +350,6 @@ class Port(object):
nested_dict = lambda: collections.defaultdict(nested_dict)
fit = nested_dict()
sEsi = Esi.getInstance()
sFit = svcFit.getInstance()
# max length is 50 characters

View File

@@ -103,11 +103,18 @@ class AuthHandler(http.server.BaseHTTPRequestHandler):
def log_message(self, format, *args):
return
import socketserver
# http://code.activestate.com/recipes/425210-simple-stoppable-server-using-socket-timeout/
class StoppableHTTPServer(http.server.HTTPServer):
class StoppableHTTPServer(socketserver.TCPServer):
def server_bind(self):
http.server.HTTPServer.server_bind(self)
# Can't use HTTPServer due to reliance on socket.getfqdn() which seems to be bugged.
# See https://github.com/pyfa-org/Pyfa/issues/1560#issuecomment-390095101
socketserver.TCPServer.server_bind(self)
host, port = self.server_address[:2]
self.server_name = host
self.server_port = port
# self.settings = CRESTSettings.getInstance()
# Allow listening for x seconds

View File

@@ -284,6 +284,23 @@ class NetworkSettings(object):
self.serviceNetworkSettings["login"] = login
self.serviceNetworkSettings["password"] = password
def getProxySettingsInRequestsFormat(self) -> dict:
proxies = {}
proxy_settings = self.getProxySettings()
if proxy_settings is not None:
# form proxy address in format "http://host:port
proxy_host_port = '{}:{}'.format(proxy_settings[0], proxy_settings[1])
proxy_auth_details = self.getProxyAuthDetails()
user_pass = ''
if proxy_auth_details is not None:
# construct prefix in form "user:password@"
user_pass = '{}:{}@'.format(proxy_auth_details[0], proxy_auth_details[1])
proxies = {
'http': 'http://' + user_pass + proxy_host_port,
'https': 'http://' + user_pass + proxy_host_port
}
return proxies
class HTMLExportSettings(object):
"""

View File

@@ -49,7 +49,8 @@ class CheckUpdateThread(threading.Thread):
try:
try:
response = network.request('https://www.pyfa.io/update_check?pyfa_version={}&client_hash={}'.format(config.version, config.getClientSecret()), network.UPDATE)
response = network.request('https://www.pyfa.io/update_check?pyfa_version={}&client_hash={}'.format(
config.version, config.getClientSecret()), network.UPDATE)
except Exception as e:
response = network.request('https://api.github.com/repos/pyfa-org/Pyfa/releases', network.UPDATE)

View File

@@ -7,7 +7,7 @@ passenv = CI TRAVIS TRAVIS_*
deps =
-rrequirements.txt
-rrequirements_test.txt
basepython = python2.7
basepython = python3.6
commands = py.test -vv --cov Pyfa tests2/
[testenv:pep8]