oh god this isn't gonna work

Merge branch 'ebag_importchanges' into test_import

Conflicts:
	config.py
	eos/db/saveddata/queries.py
	eos/effects/chargebonuswarfarecharge.py
	eos/effects/elitebonuscommandshipinformationhiddencs3.py
	eos/effects/elitebonuslogisticremotearmorrepairoptimalfalloff1.py
	eos/effects/energydestabilizationnew.py
	eos/effects/iceharvestingdroneoperationdurationbonus.py
	eos/effects/miningforemanstrengthbonus.py
	eos/effects/modulebonuswarfarelinkarmor.py
	eos/effects/modulebonuswarfarelinkinfo.py
	eos/effects/modulebonuswarfarelinkmining.py
	eos/effects/modulebonuswarfarelinkshield.py
	eos/effects/modulebonuswarfarelinkskirmish.py
	eos/effects/moduletitaneffectgenerator.py
	eos/effects/remotehullrepair.py
	eos/effects/rolebonusremotearmorrepairoptimalfalloff.py
	eos/effects/shipbonusforceauxiliarya4warfarelinksbonus.py
	eos/effects/shipmodesmallmissiledamagepostdiv.py
	eos/effects/structureenergyneutralizerfalloff.py
	eos/effects/structuremoduleeffectstasiswebifier.py
	eos/effects/structurerigmaxtargets.py
	eos/effects/subsystembonusamarrdefensiveinformationwarfarehidden.py
	eos/effects/subsystembonuscaldaridefensiveinformationwarfarehidden.py
	eos/effects/subsystembonusgallentedefensiveinformationwarfarehidden.py
	eos/effects/techtwocommandburstbonus.py
	eos/saveddata/fighter.py
	eos/saveddata/fit.py
	eos/saveddata/module.py
	eve.db
	gui/bitmapLoader.py
	gui/builtinContextMenus/itemStats.py
	gui/builtinStatsViews/miningyieldViewFull.py
	gui/builtinViewColumns/misc.py
	gui/builtinViews/__init__.py
	gui/builtinViews/fittingView.py
	gui/contextMenu.py
	gui/graphFrame.py
	gui/itemStats.py
	gui/mainFrame.py
	gui/marketBrowser.py
	service/__init__.py
	service/character.py
	service/fit.py
	service/port.py
	service/prefetch.py
	service/pycrest/eve.py
	service/settings.py
This commit is contained in:
blitzman
2017-01-23 21:06:12 -05:00
284 changed files with 8161 additions and 7135 deletions

View File

@@ -1,4 +1,4 @@
#===============================================================================
# =============================================================================
# Copyright (C) 2014 Ryan Holmes
#
# This file is part of pyfa.
@@ -15,19 +15,29 @@
#
# You should have received a copy of the GNU General Public License
# along with pyfa. If not, see <http://www.gnu.org/licenses/>.
#===============================================================================
# =============================================================================
import re
import os
import xml.dom
from eos.types import State, Slot, Module, Cargo, Fit, Ship, Drone, Implant, Booster, Citadel, Fighter
import service
import wx
import logging
import config
import collections
import json
import threading
import locale
from codecs import open
import xml.parsers.expat
from eos import db
from service.fit import Fit as svcFit
import wx
from eos.types import State, Slot, Module, Cargo, Ship, Drone, Implant, Booster, Citadel, Fighter, Fit
from service.crest import Crest
from service.market import Market
logger = logging.getLogger("pyfa.service.port")
@@ -38,18 +48,133 @@ except ImportError:
EFT_SLOT_ORDER = [Slot.LOW, Slot.MED, Slot.HIGH, Slot.RIG, Slot.SUBSYSTEM]
INV_FLAGS = {
Slot.LOW: 11,
Slot.MED: 19,
Slot.HIGH: 27,
Slot.RIG: 92,
Slot.SUBSYSTEM: 125}
Slot.LOW: 11,
Slot.MED: 19,
Slot.HIGH: 27,
Slot.RIG: 92,
Slot.SUBSYSTEM: 125
}
INV_FLAG_CARGOBAY = 5
INV_FLAG_DRONEBAY = 87
INV_FLAG_FIGHTER = 158
class Port(object):
def backupFits(self, path, callback):
thread = FitBackupThread(path, callback)
thread.start()
def importFitsThreaded(self, paths, callback):
thread = FitImportThread(paths, callback)
thread.start()
def importFitFromFiles(self, paths, callback=None):
"""
Imports fits from file(s). First processes all provided paths and stores
assembled fits into a list. This allows us to call back to the GUI as
fits are processed as well as when fits are being saved.
returns
"""
defcodepage = locale.getpreferredencoding()
sFit = svcFit.getInstance()
fits = []
for path in paths:
if callback: # Pulse
wx.CallAfter(callback, 1, "Processing file:\n%s" % path)
file_ = open(path, "r")
srcString = file_.read()
if len(srcString) == 0: # ignore blank files
continue
codec_found = None
# If file had ANSI encoding, decode it to unicode using detection
# of BOM header or if there is no header try default
# codepage then fallback to utf-16, cp1252
if isinstance(srcString, str):
encoding_map = (
('\xef\xbb\xbf', 'utf-8'),
('\xff\xfe\0\0', 'utf-32'),
('\0\0\xfe\xff', 'UTF-32BE'),
('\xff\xfe', 'utf-16'),
('\xfe\xff', 'UTF-16BE'))
for bom, encoding in encoding_map:
if srcString.startswith(bom):
codec_found = encoding
savebom = bom
if codec_found is None:
logger.info("Unicode BOM not found in file %s.", path)
attempt_codecs = (defcodepage, "utf-8", "utf-16", "cp1252")
for page in attempt_codecs:
try:
logger.info("Attempting to decode file %s using %s page.", path, page)
srcString = unicode(srcString, page)
codec_found = page
logger.info("File %s decoded using %s page.", path, page)
except UnicodeDecodeError:
logger.info("Error unicode decoding %s from page %s, trying next codec", path, page)
else:
break
else:
logger.info("Unicode BOM detected in %s, using %s page.", path, codec_found)
srcString = unicode(srcString[len(savebom):], codec_found)
else:
# nasty hack to detect other transparent utf-16 loading
if srcString[0] == '<' and 'utf-16' in srcString[:128].lower():
codec_found = "utf-16"
else:
codec_found = "utf-8"
if codec_found is None:
return False, "Proper codec could not be established for %s" % path
try:
_, fitsImport = Port.importAuto(srcString, path, callback=callback, encoding=codec_found)
fits += fitsImport
except xml.parsers.expat.ExpatError:
return False, "Malformed XML in %s" % path
except Exception:
logger.exception("Unknown exception processing: %s", path)
return False, "Unknown Error while processing %s" % path
IDs = []
numFits = len(fits)
for i, fit in enumerate(fits):
# Set some more fit attributes and save
fit.character = sFit.character
fit.damagePattern = sFit.pattern
fit.targetResists = sFit.targetResists
db.save(fit)
IDs.append(fit.ID)
if callback: # Pulse
wx.CallAfter(
callback, 1,
"Processing complete, saving fits to database\n(%d/%d)" %
(i + 1, numFits)
)
return True, fits
def importFitFromBuffer(self, bufferStr, activeFit=None):
sFit = svcFit.getInstance()
_, fits = Port.importAuto(bufferStr, activeFit=activeFit)
for fit in fits:
fit.character = sFit.character
fit.damagePattern = sFit.pattern
fit.targetResists = sFit.targetResists
db.save(fit)
return fits
"""Service which houses all import/export format functions"""
@classmethod
def exportCrest(cls, ofit, callback=None):
# A few notes:
@@ -58,19 +183,19 @@ class Port(object):
nested_dict = lambda: collections.defaultdict(nested_dict)
fit = nested_dict()
sCrest = service.Crest.getInstance()
sFit = service.Fit.getInstance()
sCrest = Crest.getInstance()
sFit = svcFit.getInstance()
eve = sCrest.eve
# max length is 50 characters
name = ofit.name[:47] + '...' if len(ofit.name) > 50 else ofit.name
fit['name'] = name
fit['ship']['href'] = "%sinventory/types/%d/"%(eve._authed_endpoint, ofit.ship.item.ID)
fit['ship']['href'] = "%sinventory/types/%d/" % (eve._authed_endpoint, ofit.ship.item.ID)
fit['ship']['id'] = ofit.ship.item.ID
fit['ship']['name'] = ''
fit['description'] = "<pyfa:%d />"%ofit.ID
fit['description'] = "<pyfa:%d />" % ofit.ID
fit['items'] = []
slotNum = {}
@@ -87,20 +212,20 @@ class Port(object):
slot = int(module.getModifiedItemAttr("subSystemSlot"))
item['flag'] = slot
else:
if not slot in slotNum:
if slot not in slotNum:
slotNum[slot] = INV_FLAGS[slot]
item['flag'] = slotNum[slot]
slotNum[slot] += 1
item['quantity'] = 1
item['type']['href'] = "%sinventory/types/%d/"%(eve._authed_endpoint, module.item.ID)
item['type']['href'] = "%sinventory/types/%d/" % (eve._authed_endpoint, module.item.ID)
item['type']['id'] = module.item.ID
item['type']['name'] = ''
fit['items'].append(item)
if module.charge and sFit.serviceFittingOptions["exportCharges"]:
if not module.chargeID in charges:
if module.chargeID not in charges:
charges[module.chargeID] = 0
# `or 1` because some charges (ie scripts) are without qty
charges[module.chargeID] += module.numCharges or 1
@@ -109,7 +234,7 @@ class Port(object):
item = nested_dict()
item['flag'] = INV_FLAG_CARGOBAY
item['quantity'] = cargo.amount
item['type']['href'] = "%sinventory/types/%d/"%(eve._authed_endpoint, cargo.item.ID)
item['type']['href'] = "%sinventory/types/%d/" % (eve._authed_endpoint, cargo.item.ID)
item['type']['id'] = cargo.item.ID
item['type']['name'] = ''
fit['items'].append(item)
@@ -118,7 +243,7 @@ class Port(object):
item = nested_dict()
item['flag'] = INV_FLAG_CARGOBAY
item['quantity'] = amount
item['type']['href'] = "%sinventory/types/%d/"%(eve._authed_endpoint, chargeID)
item['type']['href'] = "%sinventory/types/%d/" % (eve._authed_endpoint, chargeID)
item['type']['id'] = chargeID
item['type']['name'] = ''
fit['items'].append(item)
@@ -127,7 +252,7 @@ class Port(object):
item = nested_dict()
item['flag'] = INV_FLAG_DRONEBAY
item['quantity'] = drone.amount
item['type']['href'] = "%sinventory/types/%d/"%(eve._authed_endpoint, drone.item.ID)
item['type']['href'] = "%sinventory/types/%d/" % (eve._authed_endpoint, drone.item.ID)
item['type']['id'] = drone.item.ID
item['type']['name'] = ''
fit['items'].append(item)
@@ -136,7 +261,7 @@ class Port(object):
item = nested_dict()
item['flag'] = INV_FLAG_FIGHTER
item['quantity'] = fighter.amountActive
item['type']['href'] = "%sinventory/types/%d/"%(eve._authed_endpoint, fighter.item.ID)
item['type']['href'] = "%sinventory/types/%d/" % (eve._authed_endpoint, fighter.item.ID)
item['type']['id'] = fighter.item.ID
item['type']['name'] = fighter.item.name
fit['items'].append(item)
@@ -176,9 +301,9 @@ class Port(object):
return "DNA", (cls.importDna(string),)
@staticmethod
def importCrest(str):
fit = json.loads(str)
sMkt = service.Market.getInstance()
def importCrest(str_):
fit = json.loads(str_)
sMkt = Market.getInstance()
f = Fit()
f.name = fit['name']
@@ -229,7 +354,7 @@ class Port(object):
continue
# Recalc to get slot numbers correct for T3 cruisers
service.Fit.getInstance().recalc(f)
svcFit.getInstance().recalc(f)
for module in moduleList:
if module.fits(f):
@@ -239,19 +364,19 @@ class Port(object):
@staticmethod
def importDna(string):
sMkt = service.Market.getInstance()
sMkt = Market.getInstance()
ids = map(int, re.findall(r'\d+', string))
for id in ids:
for id_ in ids:
try:
try:
try:
Ship(sMkt.getItem(sMkt.getItem(id)))
Ship(sMkt.getItem(sMkt.getItem(id_)))
except ValueError:
Citadel(sMkt.getItem(sMkt.getItem(id)))
Citadel(sMkt.getItem(sMkt.getItem(id_)))
except ValueError:
Citadel(sMkt.getItem(id))
string = string[string.index(str(id)):]
Citadel(sMkt.getItem(id_))
string = string[string.index(str(id_)):]
break
except:
pass
@@ -265,12 +390,13 @@ class Port(object):
except ValueError:
f.ship = Citadel(sMkt.getItem(int(info[0])))
f.name = "{0} - DNA Imported".format(f.ship.item.name)
except UnicodeEncodeError as e:
def logtransform(s):
if len(s) > 10:
return s[:10] + "..."
return s
logger.exception("Couldn't import ship data %r", [ logtransform(s) for s in info ])
except UnicodeEncodeError:
def logtransform(s_):
if len(s_) > 10:
return s_[:10] + "..."
return s_
logger.exception("Couldn't import ship data %r", [logtransform(s) for s in info])
return None
moduleList = []
@@ -309,7 +435,7 @@ class Port(object):
moduleList.append(m)
# Recalc to get slot numbers correct for T3 cruisers
service.Fit.getInstance().recalc(f)
svcFit.getInstance().recalc(f)
for module in moduleList:
if module.fits(f):
@@ -322,7 +448,7 @@ class Port(object):
@staticmethod
def importEft(eftString):
sMkt = service.Market.getInstance()
sMkt = Market.getInstance()
offineSuffix = " /OFFLINE"
fit = Fit()
@@ -389,20 +515,20 @@ class Port(object):
if item.category.name == "Drone":
extraAmount = int(extraAmount) if extraAmount is not None else 1
if not modName in droneMap:
if modName not in droneMap:
droneMap[modName] = 0
droneMap[modName] += extraAmount
elif item.category.name == "Fighter":
extraAmount = int(extraAmount) if extraAmount is not None else 1
fighterItem = Fighter(item)
if (extraAmount > fighterItem.fighterSquadronMaxSize): #Amount bigger then max fightergroup size
if (extraAmount > fighterItem.fighterSquadronMaxSize): # Amount bigger then max fightergroup size
extraAmount = fighterItem.fighterSquadronMaxSize
if fighterItem.fits(fit):
fit.fighters.append(fighterItem)
if len(modExtra) == 2 and item.category.name != "Drone" and item.category.name != "Fighter":
extraAmount = int(extraAmount) if extraAmount is not None else 1
if not modName in cargoMap:
if modName not in cargoMap:
cargoMap[modName] = 0
cargoMap[modName] += extraAmount
elif item.category.name == "Implant":
@@ -446,13 +572,13 @@ class Port(object):
moduleList.append(m)
# Recalc to get slot numbers correct for T3 cruisers
service.Fit.getInstance().recalc(fit)
svcFit.getInstance().recalc(fit)
for m in moduleList:
if m.fits(fit):
m.owner = fit
if not m.isValidState(m.state):
print "Error: Module", m, "cannot have state", m.state
print("Error: Module", m, "cannot have state", m.state)
fit.modules.append(m)
@@ -473,7 +599,7 @@ class Port(object):
"""Handle import from EFT config store file"""
# Check if we have such ship in database, bail if we don't
sMkt = service.Market.getInstance()
sMkt = Market.getInstance()
try:
sMkt.getItem(shipname)
except:
@@ -632,7 +758,7 @@ class Port(object):
moduleList.append(m)
# Recalc to get slot numbers correct for T3 cruisers
service.Fit.getInstance().recalc(f)
svcFit.getInstance().recalc(f)
for module in moduleList:
if module.fits(f):
@@ -651,7 +777,7 @@ class Port(object):
@staticmethod
def importXml(text, callback=None, encoding="utf-8"):
sMkt = service.Market.getInstance()
sMkt = Market.getInstance()
doc = xml.dom.minidom.parseString(text.encode(encoding))
fittings = doc.getElementsByTagName("fittings").item(0)
@@ -716,7 +842,7 @@ class Port(object):
continue
# Recalc to get slot numbers correct for T3 cruisers
service.Fit.getInstance().recalc(f)
svcFit.getInstance().recalc(f)
for module in moduleList:
if module.fits(f):
@@ -734,12 +860,13 @@ class Port(object):
offineSuffix = " /OFFLINE"
export = "[%s, %s]\n" % (fit.ship.item.name, fit.name)
stuff = {}
sFit = service.Fit.getInstance()
sFit = svcFit.getInstance()
for module in fit.modules:
slot = module.slot
if not slot in stuff:
if slot not in stuff:
stuff[slot] = []
curr = module.item.name if module.item else ("[Empty %s slot]" % Slot.getName(slot).capitalize() if slot is not None else "")
curr = module.item.name if module.item \
else ("[Empty %s slot]" % Slot.getName(slot).capitalize() if slot is not None else "")
if module.charge and sFit.serviceFittingOptions["exportCharges"]:
curr += ", %s" % module.charge.name
if module.state == State.OFFLINE:
@@ -816,29 +943,27 @@ class Port(object):
if mod.slot == Slot.SUBSYSTEM:
subsystems.append(mod)
continue
if not mod.itemID in mods:
if mod.itemID not in mods:
mods[mod.itemID] = 0
mods[mod.itemID] += 1
if mod.charge:
if not mod.chargeID in charges:
if mod.chargeID not in charges:
charges[mod.chargeID] = 0
# `or 1` because some charges (ie scripts) are without qty
charges[mod.chargeID] += mod.numCharges or 1
for subsystem in sorted(subsystems, key=lambda mod: mod.getModifiedItemAttr("subSystemSlot")):
for subsystem in sorted(subsystems, key=lambda mod_: mod_.getModifiedItemAttr("subSystemSlot")):
dna += ":{0};1".format(subsystem.itemID)
for mod in mods:
dna += ":{0};{1}".format(mod, mods[mod])
# drones are known to be in split stacks
groupedDrones = OrderedDict()
for drone in fit.drones:
groupedDrones[drone.itemID] = groupedDrones.get(drone.itemID, 0) + drone.amount
dna += ":{0};{1}".format(drone.itemID, drone.amount)
for droneItemID in groupedDrones:
dna += ":{0};{1}".format(droneItemID, groupedDrones[droneItemID])
for fighter in fit.fighters:
dna += ":{0};{1}".format(fighter.itemID, fighter.amountActive)
for fighter in fit.fighters:
dna += ":{0};{1}".format(fighter.itemID, fighter.amountActive)
@@ -850,7 +975,7 @@ class Port(object):
# as being "Fitted" to whatever slot they are for, and it causes an corruption error in the
# client when trying to save the fit
if cargo.item.category.name == "Charge":
if not cargo.item.ID in charges:
if cargo.item.ID not in charges:
charges[cargo.item.ID] = 0
charges[cargo.item.ID] += cargo.amount
@@ -864,7 +989,7 @@ class Port(object):
doc = xml.dom.minidom.Document()
fittings = doc.createElement("fittings")
doc.appendChild(fittings)
sFit = service.Fit.getInstance()
sFit = svcFit.getInstance()
for i, fit in enumerate(fits):
try:
@@ -890,7 +1015,7 @@ class Port(object):
# Order of subsystem matters based on this attr. See GH issue #130
slotId = module.getModifiedItemAttr("subSystemSlot") - 125
else:
if not slot in slotNum:
if slot not in slotNum:
slotNum[slot] = 0
slotId = slotNum[slot]
@@ -904,7 +1029,7 @@ class Port(object):
fitting.appendChild(hardware)
if module.charge and sFit.serviceFittingOptions["exportCharges"]:
if not module.charge.name in charges:
if module.charge.name not in charges:
charges[module.charge.name] = 0
# `or 1` because some charges (ie scripts) are without qty
charges[module.charge.name] += module.numCharges or 1
@@ -924,7 +1049,7 @@ class Port(object):
fitting.appendChild(hardware)
for cargo in fit.cargo:
if not cargo.item.name in charges:
if cargo.item.name not in charges:
charges[cargo.item.name] = 0
charges[cargo.item.name] += cargo.amount
@@ -935,7 +1060,7 @@ class Port(object):
hardware.setAttribute("type", name)
fitting.appendChild(hardware)
except:
print "Failed on fitID: %d"%fit.ID
print("Failed on fitID: %d" % fit.ID)
continue
finally:
if callback:
@@ -947,13 +1072,12 @@ class Port(object):
def exportMultiBuy(fit):
export = "%s\n" % (fit.ship.item.name)
stuff = {}
sFit = service.Fit.getInstance()
sFit = svcFit.getInstance()
for module in fit.modules:
slot = module.slot
if not slot in stuff:
if slot not in stuff:
stuff[slot] = []
curr = "%s\n" % module.item.name if module.item else (
"")
curr = "%s\n" % module.item.name if module.item else ""
if module.charge and sFit.serviceFittingOptions["exportCharges"]:
curr += "%s x%s\n" % (module.charge.name, module.numCharges)
stuff[slot].append(curr)
@@ -989,3 +1113,39 @@ class Port(object):
export = export[:-1]
return export
class FitBackupThread(threading.Thread):
def __init__(self, path, callback):
threading.Thread.__init__(self)
self.path = path
self.callback = callback
def run(self):
path = self.path
sFit = svcFit.getInstance()
allFits = map(lambda x: x[0], sFit.getAllFits())
backedUpFits = sFit.exportXml(self.callback, *allFits)
backupFile = open(path, "w", encoding="utf-8")
backupFile.write(backedUpFits)
backupFile.close()
# Send done signal to GUI
wx.CallAfter(self.callback, -1)
class FitImportThread(threading.Thread):
def __init__(self, paths, callback):
threading.Thread.__init__(self)
self.paths = paths
self.callback = callback
def run(self):
sFit = svcFit.getInstance()
success, result = sFit.importFitFromFiles(self.paths, self.callback)
if not success: # there was an error during processing
logger.error("Error while processing file import: %s", result)
wx.CallAfter(self.callback, -2, result)
else: # Send done signal to GUI
wx.CallAfter(self.callback, -1, result)