Merge branch 'master' into cargo

Conflicts:
	eos/saveddata/fit.py
This commit is contained in:
blitzmann
2014-05-03 20:46:32 -04:00
24 changed files with 162 additions and 60 deletions

View File

@@ -66,7 +66,7 @@ from eos.db.saveddata.queries import getUser, getCharacter, getFit, getFitsWithS
getCharacterList, getPrice, getDamagePatternList, getDamagePattern, \
getFitList, getFleetList, getFleet, save, remove, commit, add, \
getCharactersForUser, getMiscData, getSquadsIDsWithFitID, getWing, \
getSquad, getBoosterFits
getSquad, getBoosterFits, getProjectedFits
#If using in memory saveddata, you'll want to reflect it so the data structure is good.
if config.saveddata_connectionstring == "sqlite:///:memory:":

View File

@@ -21,6 +21,7 @@ from eos.db.util import processEager, processWhere
from eos.db import saveddata_session, sd_lock
from eos.types import User, Character, Fit, Price, DamagePattern, Fleet, MiscData, Wing, Squad
from eos.db.saveddata.fleet import squadmembers_table
from eos.db.saveddata.fit import projectedFits_table
from sqlalchemy.sql import and_
import eos.config
@@ -360,7 +361,15 @@ def getSquadsIDsWithFitID(fitID):
return squads
else:
raise TypeError("Need integer as argument")
def getProjectedFits(fitID):
if isinstance(fitID, int):
with sd_lock:
filter = and_(projectedFits_table.c.sourceID == fitID, Fit.ID == projectedFits_table.c.victimID)
fits = saveddata_session.query(Fit).filter(filter).all()
return fits
else:
raise TypeError("Need integer as argument")
def add(stuff):
with sd_lock:

View File

@@ -196,10 +196,12 @@ class Fit(object):
def importEft(cls, eftString):
from eos import db
offineSuffix = " /OFFLINE"
fit = cls()
eftString = eftString.strip()
lines = re.split('[\n\r]+', eftString)
info = lines[0][1:-1].split(",", 1)
if len(info) == 2:
shipType = info[0].strip()
fitName = info[1].strip()
@@ -212,28 +214,46 @@ class Fit(object):
fit.name = fitName
except:
return
# maintain map of drones and their quantities
droneMap = {}
for i in range(1, len(lines)):
ammoName = None
droneAmount = None
line = lines[i].strip()
if not line:
continue
setOffline = line.endswith(offineSuffix)
if setOffline == True:
# remove offline suffix from line
line = line[:len(line) - len(offineSuffix)]
modAmmo = line.split(",")
modDrone = modAmmo[0].split(" x")
if len(modAmmo) == 2: ammoName = modAmmo[1].strip()
else: ammoName = None
modName = modDrone[0].strip()
if len(modDrone) == 2: droneAmount = modDrone[1].strip()
else: droneAmount = None
# matches drone and cargo with x{qty}
modExtra = modAmmo[0].split(" x")
if len(modAmmo) == 2:
# line with a module and ammo
ammoName = modAmmo[1].strip()
modName = modAmmo[0].strip()
elif len(modExtra) == 2:
# line with drone/cargo and qty
droneAmount = modExtra[1].strip()
modName = modExtra[0].strip()
else:
# line with just module
modName = modExtra[0].strip()
try:
# get item information. If we are on a Drone/Cargo line, throw out cargo
item = db.getItem(modName, eager="group.category")
except:
try:
item = db.getItem(modAmmo[0], eager="group.category")
except:
if len(modExtra) == 2 and item.category.name != "Drone":
continue
except:
# if no data can be found (old names)
continue
if item.category.name == "Drone":
droneAmount = int(droneAmount) if droneAmount is not None else 1
@@ -249,7 +269,9 @@ class Fit(object):
continue
if ammoName:
try:
m.charge = db.getItem(ammoName)
ammo = db.getItem(ammoName)
if m.isValidCharge(ammo) and m.charge is None:
m.charge = ammo
except:
pass
@@ -442,9 +464,7 @@ class Fit(object):
f.modules.append(m)
except KeyboardInterrupt:
continue
fits.append(f)
fits.append(f)
return fits
@@ -496,18 +516,28 @@ class Fit(object):
def exportDna(self):
dna = str(self.shipID)
mods = OrderedDict()
charges = OrderedDict()
for mod in self.modules:
if not mod.isEmpty:
if not mod.itemID in mods:
mods[mod.itemID] = 0
mods[mod.itemID] += 1
if mod.charge:
if not mod.chargeID in charges:
charges[mod.chargeID] = 0
# `or 1` because some charges (ie scripts) are without qty
charges[mod.chargeID] += mod.numShots or 1
for mod in mods:
dna += ":{0};{1}".format(mod, mods[mod])
for drone in self.drones:
dna += ":{0};{1}".format(drone.itemID, drone.amount)
for charge in charges:
dna += ":{0};{1}".format(charge, charges[charge])
return dna + "::"
@classmethod
@@ -515,7 +545,6 @@ class Fit(object):
doc = xml.dom.minidom.Document()
fittings = doc.createElement("fittings")
doc.appendChild(fittings)
for fit in fits:
fitting = doc.createElement("fitting")
fitting.setAttribute("name", fit.name)
@@ -527,6 +556,7 @@ class Fit(object):
shipType.setAttribute("value", fit.ship.item.name)
fitting.appendChild(shipType)
charges = {}
slotNum = {}
for module in fit.modules:
if module.isEmpty:
@@ -543,6 +573,12 @@ class Fit(object):
hardware.setAttribute("slot", "%s slot %d" % (slotName, slotId))
fitting.appendChild(hardware)
if module.charge:
if not module.charge.name in charges:
charges[module.charge.name] = 0
# `or 1` because some charges (ie scripts) are without qty
charges[module.charge.name] += module.numShots or 1
for drone in fit.drones:
hardware = doc.createElement("hardware")
hardware.setAttribute("qty", "%d" % drone.amount)
@@ -550,13 +586,19 @@ class Fit(object):
hardware.setAttribute("type", drone.item.name)
fitting.appendChild(hardware)
for name, qty in charges.items():
hardware = doc.createElement("hardware")
hardware.setAttribute("qty", "%d" % qty)
hardware.setAttribute("slot", "cargo")
hardware.setAttribute("type", name)
fitting.appendChild(hardware)
for cargo in fit.cargo:
hardware = doc.createElement("hardware")
hardware.setAttribute("qty", "%d" % cargo.amount)
hardware.setAttribute("slot", "cargo")
hardware.setAttribute("type", cargo.item.name)
fitting.appendChild(hardware)
return doc.toprettyxml()
@reconstructor

View File

@@ -569,7 +569,7 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
context = ("module",)
projected = False
if self.charge is not None and not projected:
if self.charge is not None:
for effect in self.charge.effects.itervalues():
if effect.runTime == runTime:
effect.handler(fit, self, ("moduleCharge",))

View File

@@ -22,6 +22,10 @@ from eos.effectHandlerHelpers import HandledItem
class Ship(ItemAttrShortcut, HandledItem):
def __init__(self, item):
if item.category.name != "Ship":
raise ValueError('Passed item "%s" (category: (%s)) is not under Ship category'%(item.name, item.category.name))
self.__item = item
self.__itemModifiedAttributes = ModifiedAttributeDict()
if not isinstance(item, int):

View File

@@ -99,6 +99,9 @@ if __name__ == "__main__":
# We don't care about some kind of rows, filter it out if so
if not isIgnored(jsonName, row):
instance = tables[jsonName]()
# fix for issue 80
if jsonName is "icons" and "res:/UI/Texture/Icons/" in str(row['iconFile']):
row['iconFile'] = row['iconFile'].replace('res:/UI/Texture/Icons/','').replace('.png','')
for k, v in row.iteritems():
setattr(instance, fieldMap.get(k, k), v)

View File

@@ -19,8 +19,16 @@
import config
versionString = "{0} {1} - {2} {3}".format(config.version, config.tag, config.expansionName, config.expansionVersion)
license = "pyfa is released under GNU GPLv3"
licenseLocation = "gpl.txt"
developers = ("\n cncfanatics \t(Sakari Orisi)\n" , " DarkPhoenix \t(Kadesh Priestess)\n", " Darriele \t(Darriele)")
credits = (("Entity (Entity) \t\tCapacitor calculations / EVEAPI python lib / Reverence"), ("Aurora \t\tMaths"), ("Corollax (Aamrr) \tVarious EOS/pyfa improvements"))
description = "Pyfa (the Python Fitting Assistant) is a standalone application able to create and simulate fittings for EVE-Online SciFi MMORPG with a very high degree of accuracy.\nPyfa can be virtually ran on all platforms where python and wxwidgets are supported.\n\n\nAll EVE-Online related materials are property of CCP hf.\n\nSilk Icons Set by famfamfam.com released under Creative Commons Attribution 2.5 License\n\nFat Cow Icons by fatcow.com released under Creative Commons Attribution 3.0 License"
licenses = (
"pyfa is released under GNU GPLv3 - see included gpl.txt",
"All EVE-Online related materials are property of CCP hf.",
"Silk Icons Set by famfamfam.com - Creative Commons Attribution 2.5 License",
"Fat Cow Icons by fatcow.com - Creative Commons Attribution 3.0 License"
)
developers = ("blitzmann \t(Sable Blitzmann)", "cncfanatics \t(Sakari Orisi)" , "DarkPhoenix \t(Kadesh Priestess) (Project Lead)", "Darriele \t(Darriele)")
credits = ("Entity (Entity) \t\tCapacitor calculations / EVEAPI python lib / Reverence", "Aurora \t\t\tMaths", "Corollax (Aamrr) \tVarious EOS / pyfa improvements")
description = (
"Pyfa (the Python Fitting Assistant) is an open-source standalone application able to "
"create and simulate fittings for EVE-Online SciFi MMORPG with a very high degree of "
"accuracy. Pyfa can run on all platforms where Python and wxWidgets are supported."
)

View File

@@ -1,3 +1,4 @@
# -*- coding: utf-8 -*-
from gui.contextMenu import ContextMenu
import gui.mainFrame
import service
@@ -112,7 +113,7 @@ class ModuleAmmoPicker(ContextMenu):
def addSeperator(self, m, text):
id = wx.NewId()
m.Append(id, "--- %s ---" % text)
m.Append(id, u'%s' % text)
m.Enable(id, False)
def getSubMenu(self, context, selection, menu, i):
@@ -132,6 +133,11 @@ class ModuleAmmoPicker(ContextMenu):
sub = None
self.charges.sort(key=self.turretSorter)
for charge in self.charges:
# fix issue 71 - will probably have to change if CCP adds more Orbital ammo
if "Orbital" in charge.name:
item = self.addCharge(m, charge)
items.append(item)
continue
currBase = charge.name.rsplit()[-2:]
currRange = charge.getAttribute("weaponRangeMultiplier")
if nameBase is None or range != currRange or nameBase != currBase:
@@ -156,6 +162,7 @@ class ModuleAmmoPicker(ContextMenu):
if sub is not None:
self.addSeperator(sub, "More Damage")
for item in items:
m.AppendItem(item)

View File

@@ -233,10 +233,23 @@ class FittingView(d.Display):
event.Skip()
def fitRemoved(self, event):
'''If fit is removed and active, the page is deleted.
We also refresh the fit of the new current page in case
delete fit caused change in stats (projected)
'''
fitID = event.fitID
if fitID == self.getActiveFit():
self.parent.DeletePage(self.parent.GetPageIndex(self))
try:
# Sometimes there is no active page after deletion, hence the try block
cFit = service.Fit.getInstance()
cFit.refreshFit(self.getActiveFit())
wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.activeFitID))
except wx._core.PyDeadObjectError:
pass
event.Skip()
def fitRenamed(self, event):

View File

@@ -26,9 +26,13 @@ import wx.lib.mixins.listctrl as listmix
import wx.html
from eos.types import Ship, Module, Skill, Booster, Implant, Drone
from gui.utils.numberFormatter import formatAmount
from collections import OrderedDict
import service
try:
from collections import OrderedDict
except ImportError:
from gui.utils.compat import OrderedDict
class ItemStatsDialog(wx.Dialog):
counter = 0
def __init__(self, victim, fullContext=None, pos = wx.DefaultPosition, size = wx.DefaultSize, maximized = False):

View File

@@ -243,14 +243,12 @@ class MainFrame(wx.Frame):
info = wx.AboutDialogInfo()
info.Name = "pyfa"
info.Version = gui.aboutData.versionString
info.Description = wordwrap(gui.aboutData.description + "\n\n\nDevelopers: " +
"".join(gui.aboutData.developers) +
"\n\nAdditional credits:\n " +
"\n ".join(gui.aboutData.credits)
+ "\n\nLicense: " +
gui.aboutData.license +
" - see included " +
gui.aboutData.licenseLocation +
info.Description = wordwrap(gui.aboutData.description + "\n\nDevelopers:\n\t" +
"\n\t".join(gui.aboutData.developers) +
"\n\nAdditional credits:\n\t" +
"\n\t".join(gui.aboutData.credits) +
"\n\nLicenses:\n\t" +
"\n\t".join(gui.aboutData.licenses) +
"\n\nPython: \t" + sys.version +
"\nwxPython: \t" + wx.__version__ +
"\nSQLAlchemy: \t" + sqlalchemy.__version__,
@@ -364,7 +362,7 @@ class MainFrame(wx.Frame):
# Export HTML
self.Bind(wx.EVT_MENU, self.exportHtml, id=menuBar.exportHtmlId)
# Preference dialog
self.Bind(wx.EVT_MENU, self.showPreferenceDialog, id = menuBar.preferencesId)
self.Bind(wx.EVT_MENU, self.showPreferenceDialog, id=wx.ID_PREFERENCES)
# User guide
self.Bind(wx.EVT_MENU, self.goWiki, id = menuBar.wikiId)
# EVE Forums
@@ -384,6 +382,7 @@ class MainFrame(wx.Frame):
self.additionstab2 = wx.NewId()
self.additionstab3 = wx.NewId()
self.additionstab4 = wx.NewId()
self.additionstab5 = wx.NewId()
# Close Page
self.Bind(wx.EVT_MENU, self.CloseCurrentPage, id=self.closePageId)
@@ -396,6 +395,7 @@ class MainFrame(wx.Frame):
self.Bind(wx.EVT_MENU, self.AdditionsTabSelect, id = self.additionstab2)
self.Bind(wx.EVT_MENU, self.AdditionsTabSelect, id = self.additionstab3)
self.Bind(wx.EVT_MENU, self.AdditionsTabSelect, id = self.additionstab4)
self.Bind(wx.EVT_MENU, self.AdditionsTabSelect, id = self.additionstab5)
actb = [(wx.ACCEL_CTRL, ord('T'), self.addPageId),
(wx.ACCEL_CMD, ord('T'), self.addPageId),
@@ -416,10 +416,12 @@ class MainFrame(wx.Frame):
(wx.ACCEL_CTRL, ord('2'), self.additionstab2),
(wx.ACCEL_CTRL, ord('3'), self.additionstab3),
(wx.ACCEL_CTRL, ord('4'), self.additionstab4),
(wx.ACCEL_CTRL, ord('5'), self.additionstab5),
(wx.ACCEL_CMD, ord('1'), self.additionstab1),
(wx.ACCEL_CMD, ord('2'), self.additionstab2),
(wx.ACCEL_CMD, ord('3'), self.additionstab3),
(wx.ACCEL_CMD, ord('4'), self.additionstab4)
(wx.ACCEL_CMD, ord('4'), self.additionstab4),
(wx.ACCEL_CMD, ord('5'), self.additionstab5)
]
atable = wx.AcceleratorTable(actb)
self.SetAcceleratorTable(atable)
@@ -434,7 +436,8 @@ class MainFrame(wx.Frame):
selTab = 2
if event.GetId() == self.additionstab4:
selTab = 3
if event.GetId() == self.additionstab5:
selTab = 4
if selTab is not None:
self.additionsPane.notebook.SetSelection(selTab)

View File

@@ -33,7 +33,6 @@ class MainMenuBar(wx.MenuBar):
self.exportSkillsNeededId = wx.NewId()
self.importCharacterId = wx.NewId()
self.exportHtmlId = wx.NewId()
self.preferencesId = wx.NewId()
self.wikiId = wx.NewId()
self.forumId = wx.NewId()
@@ -59,7 +58,6 @@ class MainMenuBar(wx.MenuBar):
fileMenu.AppendSeparator()
fileMenu.Append(wx.ID_EXIT)
# Edit menu
editMenu = wx.Menu()
self.Append(editMenu, "&Edit")
@@ -67,7 +65,6 @@ class MainMenuBar(wx.MenuBar):
#editMenu.Append(wx.ID_UNDO)
#editMenu.Append(wx.ID_REDO)
copyText = "&To Clipboard" + ("\tCTRL+C" if 'wxMSW' in wx.PlatformInfo else "")
pasteText = "&From Clipboard" + ("\tCTRL+V" if 'wxMSW' in wx.PlatformInfo else "")
editMenu.Append(wx.ID_COPY, copyText, "Export a fit to the clipboard")
@@ -89,13 +86,9 @@ class MainMenuBar(wx.MenuBar):
graphFrameItem.SetBitmap(bitmapLoader.getBitmap("graphs_small", "icons"))
windowMenu.AppendItem(graphFrameItem)
#=======================================================================
# DISABLED FOR RC2 Release
#
preferencesItem = wx.MenuItem(windowMenu, self.preferencesId, "Preferences\tCTRL+P")
preferencesItem = wx.MenuItem(windowMenu, wx.ID_PREFERENCES, "Preferences\tCTRL+P")
preferencesItem.SetBitmap(bitmapLoader.getBitmap("preferences_small", "icons"))
windowMenu.AppendItem(preferencesItem)
#=======================================================================
# Help menu
helpMenu = wx.Menu()
@@ -108,8 +101,6 @@ class MainMenuBar(wx.MenuBar):
if config.debug:
helpMenu.Append( self.mainFrame.widgetInspectMenuID, "Open Widgets Inspect tool", "Open Widgets Inspect tool")
self.mainFrame.Bind(GE.FIT_CHANGED, self.fitChanged)
def fitChanged(self, event):

View File

@@ -132,20 +132,28 @@ class exportHtmlThread(threading.Thread):
if len(fits) > 0:
groupFits += len(fits)
# Ship group header
HTMLship = (
' <li data-role="collapsible" data-iconpos="right" data-shadow="false" data-corners="false">\n'
' <h2>' + ship.name + ' <span class="ui-li-count">'+str(len(fits))+'</span></h2>\n'
' <ul data-role="listview" data-shadow="false" data-inset="true" data-corners="false">\n')
for fit in fits:
if len(fits) == 1:
if self.stopRunning:
return;
return
fit = fits[0]
dnaFit = sFit.exportDna(fit[0])
HTMLship += ' <li><a data-dna="' + dnaFit + '" target="_blank">' + fit[1] + '</a></li>\n'
HTMLgroup += (
' <li><a data-dna="' + dnaFit + '" target="_blank">' + ship.name + ": " + fit[1] + '</a></li>\n')
else:
# Ship group header
HTMLship = (
' <li data-role="collapsible" data-iconpos="right" data-shadow="false" data-corners="false">\n'
' <h2>' + ship.name + ' <span class="ui-li-count">'+str(len(fits))+'</span></h2>\n'
' <ul data-role="listview" data-shadow="false" data-inset="true" data-corners="false">\n')
HTMLgroup += HTMLship + (' </ul>\n'
' </li>\n')
for fit in fits:
if self.stopRunning:
return
dnaFit = sFit.exportDna(fit[0])
HTMLship += ' <li><a data-dna="' + dnaFit + '" target="_blank">' + fit[1] + '</a></li>\n'
HTMLgroup += HTMLship + (' </ul>\n'
' </li>\n')
if groupFits > 0:
# Market group header
HTML += (

View File

@@ -162,6 +162,8 @@ class Fit(object):
fit = eos.db.getFit(fitID)
sFlt = Fleet.getInstance()
sFlt.removeAssociatedFleetData(fit)
self.removeProjectedData(fitID)
eos.db.remove(fit)
def copyFit(self, fitID):
@@ -177,7 +179,15 @@ class Fit(object):
fit = eos.db.getFit(fitID)
fit.clear()
return fit
def removeProjectedData(self, fitID):
'''Removes projection relation from ships that have fitID as projection. See GitHub issue #90'''
fit = eos.db.getFit(fitID)
fits = eos.db.getProjectedFits(fitID)
for projectee in fits:
projectee.projectedFits.remove(fit)
def toggleFactorReload(self, fitID):
if fitID is None:
return None

Binary file not shown.

After

Width:  |  Height:  |  Size: 756 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 914 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 698 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 662 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 852 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 794 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 776 B