Moved item override and fit porting to services.
Also sprinkled static methods around Fit service and other PEP8 goodness because why not.
This commit is contained in:
@@ -23,7 +23,6 @@ from sqlalchemy.sql import and_, or_, select, func
|
||||
from sqlalchemy.orm import join, exc
|
||||
from eos.types import Item, Category, Group, MarketGroup, AttributeInfo, MetaData, MetaGroup
|
||||
from eos.db.util import processEager, processWhere
|
||||
from eos.itemMapping import itemMapping
|
||||
import eos.config
|
||||
|
||||
configVal = getattr(eos.config, "gamedataCache", None)
|
||||
@@ -87,8 +86,6 @@ def getItem(lookfor, eager=None):
|
||||
itemNameMap[lookfor] = item.ID
|
||||
else:
|
||||
raise TypeError("Need integer or string as argument")
|
||||
if item.name in itemMapping:
|
||||
item = getItem(itemMapping[item.name], eager)
|
||||
return item
|
||||
|
||||
groupNameMap = {}
|
||||
|
||||
@@ -1,30 +0,0 @@
|
||||
# Map of items that will convert to a new item (for old item names / skinned ships)
|
||||
# Key must be str with exact name of item to be converted, value can be str (name)
|
||||
# or int (id) of item to be converted to
|
||||
itemMapping = {
|
||||
"Aliastra Catalyst": "Catalyst",
|
||||
"Inner Zone Shipping Catalyst": "Catalyst",
|
||||
"Intaki Syndicate Catalyst": "Catalyst",
|
||||
"InterBus Catalyst": "Catalyst",
|
||||
"Quafe Catalyst": "Catalyst",
|
||||
"Nefantar Thrasher": "Thrasher",
|
||||
"Sarum Magnate": "Magnate",
|
||||
"Sukuuvestaa Heron": "Heron:",
|
||||
"Inner Zone Shipping Imicus": "Imicus",
|
||||
"Vherokior Probe": "Probe",
|
||||
"Miasmos Quafe Ultra Edition": "Miasmos",
|
||||
"Miasmos Quafe Ultramarine Edition": "Miasmos",
|
||||
"Miasmos Amastris Edition": "Miasmos",
|
||||
"Tash-Murkon Magnate": "Magnate",
|
||||
"Scorpion Ishukone Watch": "Scorpion",
|
||||
"Incursus Aliastra Edition": "Incursus",
|
||||
"Merlin Nugoeihuvi Edition": "Merlin",
|
||||
"Police Pursuit Comet": "Federation Navy Comet",
|
||||
"Punisher Kador Edition": "Punisher",
|
||||
"Rifter Krusual Edition": "Rifter",
|
||||
"Abaddon Kador Edition": "Abaddon",
|
||||
"Hyperion Aliastra Edition": "Hyperion",
|
||||
"Maelstrom Krusual Edition": "Maelstrom",
|
||||
"Rokh Nugoeihuvi Edition": "Rokh",
|
||||
"Mammoth Nefantar Edition": "Mammoth"
|
||||
}
|
||||
@@ -27,8 +27,6 @@ from copy import deepcopy
|
||||
from math import sqrt, log, asinh
|
||||
from eos.types import Drone, Cargo, Ship, Character, State, Slot, Module, Implant, Booster, Skill
|
||||
from eos.saveddata.module import State
|
||||
import re
|
||||
import xml.dom
|
||||
import time
|
||||
|
||||
try:
|
||||
@@ -69,662 +67,6 @@ class Fit(object):
|
||||
self.timestamp = time.time()
|
||||
self.build()
|
||||
|
||||
@classmethod
|
||||
def importAuto(cls, string, sourceFileName=None, activeFit=None):
|
||||
# Get first line and strip space symbols of it
|
||||
# to avoid possible detection errors
|
||||
firstLine = re.split("[\n\r]+", string.strip(), maxsplit=1)[0]
|
||||
firstLine = firstLine.strip()
|
||||
|
||||
# If string is from in-game copy of fitting window
|
||||
# We match " power" instead of "High power" in case a fit has no high modules
|
||||
if " power" in firstLine and activeFit is not None:
|
||||
return "FIT", (cls.importFittingWindow(string, activeFit),)
|
||||
# If we have "<url=fitting", fit is coming from eve chat
|
||||
# Gather data and send to DNA
|
||||
chatDna = re.search("<url=fitting:(.*::)>.*</url>", firstLine)
|
||||
if chatDna:
|
||||
return "DNA", (cls.importDna(chatDna.group(1)),)
|
||||
# If we have a CREST kill link
|
||||
killLink = re.search("http://public-crest.eveonline.com/killmails/(.*)/", firstLine)
|
||||
if killLink:
|
||||
return "CREST", (cls.importCrest(tuple(killLink.group(1).split("/"))),)
|
||||
# If we have "<url=killReport", fit is killmail from eve chat
|
||||
killReport = re.search("<url=killReport:(.*)>.*</url>", firstLine)
|
||||
if killReport:
|
||||
return "CREST", (cls.importCrest(tuple(killReport.group(1).split(":"))),)
|
||||
# If XML-style start of tag encountered, detect as XML
|
||||
if re.match("<", firstLine):
|
||||
return "XML", cls.importXml(string)
|
||||
# If we've got source file name which is used to describe ship name
|
||||
# and first line contains something like [setup name], detect as eft config file
|
||||
elif re.match("\[.*\]", firstLine) and sourceFileName is not None:
|
||||
shipName = sourceFileName.rsplit('.')[0]
|
||||
return "EFT Config", cls.importEftCfg(shipName, string)
|
||||
# If no file is specified and there's comma between brackets,
|
||||
# consider that we have [ship, setup name] and detect like eft export format
|
||||
elif re.match("\[.*,.*\]", firstLine):
|
||||
return "EFT", (cls.importEft(string),)
|
||||
# Use DNA format for all other cases
|
||||
else:
|
||||
return "DNA", (cls.importDna(string),)
|
||||
|
||||
@classmethod
|
||||
def importFittingWindow(cls, string, activeFit):
|
||||
from eos import db
|
||||
activeFit = db.getFit(activeFit)
|
||||
|
||||
# if the current fit has mods, do not mess with it. Instead, make new fit
|
||||
if activeFit.modCount > 0:
|
||||
fit = Fit()
|
||||
fit.ship = Ship(db.getItem(activeFit.ship.item.ID))
|
||||
fit.name = "%s (copy)"%activeFit.name
|
||||
else:
|
||||
fit = activeFit
|
||||
lines = re.split('[\n\r]+', string)
|
||||
|
||||
droneMap = {}
|
||||
cargoMap = {}
|
||||
modules = []
|
||||
|
||||
for i in range(1, len(lines)):
|
||||
line = lines[i].strip()
|
||||
if not line:
|
||||
continue
|
||||
|
||||
try:
|
||||
amount, modName = line.split("x ")
|
||||
amount = int(amount)
|
||||
item = db.getItem(modName, eager="group.category")
|
||||
except:
|
||||
# if no data can be found (old names)
|
||||
continue
|
||||
|
||||
if item.category.name == "Drone":
|
||||
if not modName in droneMap:
|
||||
droneMap[modName] = 0
|
||||
droneMap[modName] += amount
|
||||
elif item.category.name == "Charge":
|
||||
if not modName in cargoMap:
|
||||
cargoMap[modName] = 0
|
||||
cargoMap[modName] += amount
|
||||
else:
|
||||
for i in xrange(amount):
|
||||
try:
|
||||
m = Module(item)
|
||||
except ValueError:
|
||||
continue
|
||||
# If we are importing T3 ship, we must apply subsystems first, then
|
||||
# calcModAttr() to get the ship slots
|
||||
if m.slot == Slot.SUBSYSTEM and m.fits(fit):
|
||||
fit.modules.append(m)
|
||||
else:
|
||||
modules.append(m)
|
||||
|
||||
fit.clear()
|
||||
fit.calculateModifiedAttributes()
|
||||
|
||||
for m in modules:
|
||||
# we check to see if module fits as a basic sanity check
|
||||
# if it doesn't then the imported fit is most likely invalid
|
||||
# (ie: user tried to import Legion fit to a Rifter)
|
||||
if m.fits(fit):
|
||||
fit.modules.append(m)
|
||||
if m.isValidState(State.ACTIVE):
|
||||
m.state = State.ACTIVE
|
||||
m.owner = fit #not sure why this is required when it's not for other import methods, but whatever
|
||||
else:
|
||||
return
|
||||
|
||||
for droneName in droneMap:
|
||||
d = Drone(db.getItem(droneName))
|
||||
d.amount = droneMap[droneName]
|
||||
fit.drones.append(d)
|
||||
|
||||
for cargoName in cargoMap:
|
||||
c = Cargo(db.getItem(cargoName))
|
||||
c.amount = cargoMap[cargoName]
|
||||
fit.cargo.append(c)
|
||||
|
||||
return fit
|
||||
|
||||
@classmethod
|
||||
def importCrest(cls, info):
|
||||
from eos import db
|
||||
import urllib2
|
||||
import json
|
||||
|
||||
try:
|
||||
response = urllib2.urlopen("https://public-crest.eveonline.com/killmails/%s/%s/" % info)
|
||||
except:
|
||||
return
|
||||
|
||||
kill = (json.loads(response.read()))['victim']
|
||||
|
||||
fit = Fit()
|
||||
fit.ship = Ship(db.getItem(kill['shipType']['name']))
|
||||
fit.name = "CREST: %s's %s" % (kill['character']['name'], kill['shipType']['name'])
|
||||
|
||||
# sort based on flag to get proper rack position
|
||||
items = sorted(kill['items'], key=lambda k: k['flag'])
|
||||
|
||||
# We create a relation between module flag and module position on fit at time of append:
|
||||
# this allows us to know which module to apply charges to if need be (see below)
|
||||
flagMap = {}
|
||||
|
||||
# Charges may show up before or after the module. We process modules first,
|
||||
# storing any charges that are fitted in a dict and noting their flag (module).
|
||||
charges = {}
|
||||
|
||||
for mod in items:
|
||||
if mod['flag'] == 5: # throw out cargo
|
||||
continue
|
||||
|
||||
item = db.getItem(mod['itemType']['name'], eager="group.category")
|
||||
|
||||
if item.category.name == "Drone":
|
||||
d = Drone(item)
|
||||
d.amount = mod['quantityDropped'] if 'quantityDropped' in mod else mod['quantityDestroyed']
|
||||
fit.drones.append(d)
|
||||
elif item.category.name == "Charge":
|
||||
charges[mod['flag']] = item
|
||||
else:
|
||||
m = Module(item)
|
||||
if m.isValidState(State.ACTIVE):
|
||||
m.state = State.ACTIVE
|
||||
fit.modules.append(m)
|
||||
flagMap[mod['flag']] = fit.modules.index(m)
|
||||
|
||||
for flag, item in charges.items():
|
||||
# we do not need to verify valid charge as it comes directly from CCP
|
||||
fit.modules[flagMap[flag]].charge = item
|
||||
|
||||
return fit
|
||||
|
||||
@classmethod
|
||||
def importDna(cls, string):
|
||||
from eos import db
|
||||
info = string.split(":")
|
||||
f = Fit()
|
||||
f.ship = Ship(db.getItem(int(info[0])))
|
||||
f.name = "{0} - DNA Imported".format(f.ship.item.name)
|
||||
for itemInfo in info[1:]:
|
||||
if itemInfo:
|
||||
itemID, amount = itemInfo.split(";")
|
||||
item = db.getItem(int(itemID), eager="group.category")
|
||||
|
||||
if item.category.name == "Drone":
|
||||
d = Drone(item)
|
||||
d.amount = int(amount)
|
||||
f.drones.append(d)
|
||||
elif item.category.name == "Charge":
|
||||
c = Cargo(item)
|
||||
c.amount = int(amount)
|
||||
f.cargo.append(c)
|
||||
else:
|
||||
for i in xrange(int(amount)):
|
||||
try:
|
||||
m = Module(item)
|
||||
f.modules.append(m)
|
||||
except:
|
||||
pass
|
||||
if m.isValidState(State.ACTIVE):
|
||||
m.state = State.ACTIVE
|
||||
|
||||
return f
|
||||
|
||||
@classmethod
|
||||
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()
|
||||
else:
|
||||
shipType = info[0].strip()
|
||||
fitName = "Imported %s" % shipType
|
||||
|
||||
try:
|
||||
fit.ship = Ship(db.getItem(shipType))
|
||||
fit.name = fitName
|
||||
except:
|
||||
return
|
||||
|
||||
# maintain map of drones and their quantities
|
||||
droneMap = {}
|
||||
cargoMap = {}
|
||||
for i in range(1, len(lines)):
|
||||
ammoName = None
|
||||
extraAmount = 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(",")
|
||||
# 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
|
||||
extraAmount = 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:
|
||||
# if no data can be found (old names)
|
||||
continue
|
||||
|
||||
if item.category.name == "Drone":
|
||||
extraAmount = int(extraAmount) if extraAmount is not None else 1
|
||||
if not modName in droneMap:
|
||||
droneMap[modName] = 0
|
||||
droneMap[modName] += extraAmount
|
||||
if len(modExtra) == 2 and item.category.name != "Drone":
|
||||
extraAmount = int(extraAmount) if extraAmount is not None else 1
|
||||
if not modName in cargoMap:
|
||||
cargoMap[modName] = 0
|
||||
cargoMap[modName] += extraAmount
|
||||
elif item.category.name == "Implant":
|
||||
fit.implants.append(Implant(item))
|
||||
else:
|
||||
try:
|
||||
m = Module(item)
|
||||
except ValueError:
|
||||
continue
|
||||
if ammoName:
|
||||
try:
|
||||
ammo = db.getItem(ammoName)
|
||||
if m.isValidCharge(ammo) and m.charge is None:
|
||||
m.charge = ammo
|
||||
except:
|
||||
pass
|
||||
|
||||
if setOffline == True and m.isValidState(State.OFFLINE):
|
||||
m.state = State.OFFLINE
|
||||
elif m.isValidState(State.ACTIVE):
|
||||
m.state = State.ACTIVE
|
||||
|
||||
fit.modules.append(m)
|
||||
|
||||
for droneName in droneMap:
|
||||
d = Drone(db.getItem(droneName))
|
||||
d.amount = droneMap[droneName]
|
||||
fit.drones.append(d)
|
||||
|
||||
for cargoName in cargoMap:
|
||||
c = Cargo(db.getItem(cargoName))
|
||||
c.amount = cargoMap[cargoName]
|
||||
fit.cargo.append(c)
|
||||
|
||||
return fit
|
||||
|
||||
@classmethod
|
||||
def importEftCfg(cls, shipname, contents):
|
||||
"""Handle import from EFT config store file"""
|
||||
# Check if we have such ship in database, bail if we don't
|
||||
from eos import db
|
||||
try:
|
||||
db.getItem(shipname)
|
||||
except:
|
||||
return
|
||||
# If client didn't take care of encoding file contents into Unicode,
|
||||
# do it using fallback encoding ourselves
|
||||
if isinstance(contents, str):
|
||||
contents = unicode(contents, "cp1252")
|
||||
# List for fits
|
||||
fits = []
|
||||
# List for starting line numbers for each fit
|
||||
fitIndices = []
|
||||
# Separate string into lines
|
||||
lines = re.split('[\n\r]+', contents)
|
||||
for line in lines:
|
||||
# Detect fit header
|
||||
if line[:1] == "[" and line[-1:] == "]":
|
||||
# Line index where current fit starts
|
||||
startPos = lines.index(line)
|
||||
fitIndices.append(startPos)
|
||||
|
||||
for i, startPos in enumerate(fitIndices):
|
||||
# End position is last file line if we're trying to get it for last fit,
|
||||
# or start position of next fit minus 1
|
||||
endPos = len(lines) if i == len(fitIndices) - 1 else fitIndices[i + 1]
|
||||
# Finally, get lines for current fitting
|
||||
fitLines = lines[startPos:endPos]
|
||||
try:
|
||||
# Create fit object
|
||||
f = Fit()
|
||||
# Strip square brackets and pull out a fit name
|
||||
f.name = fitLines[0][1:-1]
|
||||
# Assign ship to fitting
|
||||
f.ship = Ship(db.getItem(shipname))
|
||||
|
||||
for i in range(1, len(fitLines)):
|
||||
line = fitLines[i]
|
||||
if not line:
|
||||
continue
|
||||
# Parse line into some data we will need
|
||||
misc = re.match("(Drones|Implant|Booster)_(Active|Inactive)=(.+)",line)
|
||||
cargo = re.match("Cargohold=(.+)",line)
|
||||
if misc:
|
||||
entityType = misc.group(1)
|
||||
entityState = misc.group(2)
|
||||
entityData = misc.group(3)
|
||||
if entityType == "Drones":
|
||||
droneData = re.match("(.+),([0-9]+)", entityData)
|
||||
# Get drone name and attempt to detect drone number
|
||||
droneName = droneData.group(1) if droneData else entityData
|
||||
droneAmount = int(droneData.group(2)) if droneData else 1
|
||||
# Bail if we can't get item or it's not from drone category
|
||||
try:
|
||||
droneItem = db.getItem(droneName, eager="group.category")
|
||||
except:
|
||||
continue
|
||||
if droneItem.category.name != "Drone":
|
||||
continue
|
||||
# Add drone to the fitting
|
||||
d = Drone(droneItem)
|
||||
d.amount = droneAmount
|
||||
if entityState == "Active":
|
||||
d.amountActive = droneAmount
|
||||
elif entityState == "Inactive":
|
||||
d.amountActive = 0
|
||||
f.drones.append(d)
|
||||
elif entityType == "Implant":
|
||||
# Bail if we can't get item or it's not from implant category
|
||||
try:
|
||||
implantItem = db.getItem(entityData, eager="group.category")
|
||||
except:
|
||||
continue
|
||||
if implantItem.category.name != "Implant":
|
||||
continue
|
||||
# Add implant to the fitting
|
||||
imp = Implant(implantItem)
|
||||
if entityState == "Active":
|
||||
imp.active = True
|
||||
elif entityState == "Inactive":
|
||||
imp.active = False
|
||||
f.implants.append(imp)
|
||||
elif entityType == "Booster":
|
||||
# Bail if we can't get item or it's not from implant category
|
||||
try:
|
||||
boosterItem = db.getItem(entityData, eager="group.category")
|
||||
except:
|
||||
continue
|
||||
# All boosters have implant category
|
||||
if boosterItem.category.name != "Implant":
|
||||
continue
|
||||
# Add booster to the fitting
|
||||
b = Booster(boosterItem)
|
||||
if entityState == "Active":
|
||||
b.active = True
|
||||
elif entityState == "Inactive":
|
||||
b.active = False
|
||||
f.boosters.append(b)
|
||||
# If we don't have any prefixes, then it's a module
|
||||
elif cargo:
|
||||
cargoData = re.match("(.+),([0-9]+)", cargo.group(1))
|
||||
cargoName = cargoData.group(1) if cargoData else cargo.group(1)
|
||||
cargoAmount = int(cargoData.group(2)) if cargoData else 1
|
||||
# Bail if we can't get item
|
||||
try:
|
||||
item = db.getItem(cargoName)
|
||||
except:
|
||||
continue
|
||||
# Add Cargo to the fitting
|
||||
c = Cargo(item)
|
||||
c.amount = cargoAmount
|
||||
f.cargo.append(c)
|
||||
else:
|
||||
withCharge = re.match("(.+),(.+)", line)
|
||||
modName = withCharge.group(1) if withCharge else line
|
||||
chargeName = withCharge.group(2) if withCharge else None
|
||||
# If we can't get module item, skip it
|
||||
try:
|
||||
modItem = db.getItem(modName)
|
||||
except:
|
||||
continue
|
||||
|
||||
# Create module and activate it if it's activable
|
||||
m = Module(modItem)
|
||||
if m.isValidState(State.ACTIVE):
|
||||
m.state = State.ACTIVE
|
||||
# Add charge to mod if applicable, on any errors just don't add anything
|
||||
if chargeName:
|
||||
try:
|
||||
chargeItem = db.getItem(chargeName, eager="group.category")
|
||||
if chargeItem.category.name == "Charge":
|
||||
m.charge = chargeItem
|
||||
except:
|
||||
pass
|
||||
# Append module to fit
|
||||
f.modules.append(m)
|
||||
# Append fit to list of fits
|
||||
fits.append(f)
|
||||
# Skip fit silently if we get an exception
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
return fits
|
||||
|
||||
@classmethod
|
||||
def importXml(cls, text):
|
||||
doc = xml.dom.minidom.parseString(text.encode("utf-8"))
|
||||
fittings = doc.getElementsByTagName("fittings").item(0)
|
||||
fittings = fittings.getElementsByTagName("fitting")
|
||||
fits = []
|
||||
from eos import db
|
||||
for fitting in fittings:
|
||||
f = Fit()
|
||||
f.name = fitting.getAttribute("name")
|
||||
# <localized hint="Maelstrom">Maelstrom</localized>
|
||||
shipType = fitting.getElementsByTagName("shipType").item(0).getAttribute("value")
|
||||
try:
|
||||
f.ship = Ship(db.getItem(shipType))
|
||||
except:
|
||||
continue
|
||||
hardwares = fitting.getElementsByTagName("hardware")
|
||||
for hardware in hardwares:
|
||||
try:
|
||||
moduleName = hardware.getAttribute("type")
|
||||
try:
|
||||
item = db.getItem(moduleName, eager="group.category")
|
||||
except:
|
||||
continue
|
||||
if item:
|
||||
if item.category.name == "Drone":
|
||||
d = Drone(item)
|
||||
d.amount = int(hardware.getAttribute("qty"))
|
||||
f.drones.append(d)
|
||||
elif hardware.getAttribute("slot").lower() == "cargo":
|
||||
# although the eve client only support charges in cargo, third-party programs
|
||||
# may support items or "refits" in cargo. Support these by blindly adding all
|
||||
# cargo, not just charges
|
||||
c = Cargo(item)
|
||||
c.amount = int(hardware.getAttribute("qty"))
|
||||
f.cargo.append(c)
|
||||
else:
|
||||
try:
|
||||
m = Module(item)
|
||||
# When item can't be added to any slot (unknown item or just charge), ignore it
|
||||
except ValueError:
|
||||
continue
|
||||
if m.isValidState(State.ACTIVE):
|
||||
m.state = State.ACTIVE
|
||||
|
||||
f.modules.append(m)
|
||||
except KeyboardInterrupt:
|
||||
continue
|
||||
fits.append(f)
|
||||
|
||||
return fits
|
||||
|
||||
EXPORT_ORDER_EFT = [Slot.LOW, Slot.MED, Slot.HIGH, Slot.RIG, Slot.SUBSYSTEM]
|
||||
def exportEft(self):
|
||||
offineSuffix = " /OFFLINE"
|
||||
export = "[%s, %s]\n" % (self.ship.item.name, self.name)
|
||||
stuff = {}
|
||||
for module in self.modules:
|
||||
slot = module.slot
|
||||
if not slot 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 "")
|
||||
if module.charge:
|
||||
curr += ", %s" % module.charge.name
|
||||
if module.state == State.OFFLINE:
|
||||
curr += offineSuffix
|
||||
curr += "\n"
|
||||
stuff[slot].append(curr)
|
||||
|
||||
for slotType in self.EXPORT_ORDER_EFT:
|
||||
data = stuff.get(slotType)
|
||||
if data is not None:
|
||||
export += "\n"
|
||||
for curr in data:
|
||||
export += curr
|
||||
|
||||
if len(self.drones) > 0:
|
||||
export += "\n\n"
|
||||
for drone in self.drones:
|
||||
export += "%s x%s\n" % (drone.item.name, drone.amount)
|
||||
if len(self.cargo) > 0:
|
||||
for cargo in self.cargo:
|
||||
export += "%s x%s\n" % (cargo.item.name, cargo.amount)
|
||||
|
||||
if export[-1] == "\n":
|
||||
export = export[:-1]
|
||||
|
||||
return export
|
||||
|
||||
def exportEftImps(self):
|
||||
export = self.exportEft()
|
||||
if len(self.implants) > 0:
|
||||
export += "\n\n\n"
|
||||
for implant in self.implants:
|
||||
export += "%s\n" % (implant.item.name)
|
||||
|
||||
if export[-1] == "\n":
|
||||
export = export[:-1]
|
||||
|
||||
return export
|
||||
|
||||
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 cargo in self.cargo:
|
||||
# DNA format is a simple/dumb format. As CCP uses the slot information of the item itself
|
||||
# without designating slots in the DNA standard, we need to make sure we only include
|
||||
# charges in the DNA export. If modules were included, the EVE Client will interpret these
|
||||
# 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:
|
||||
charges[cargo.item.ID] = 0
|
||||
charges[cargo.item.ID] += cargo.amount
|
||||
|
||||
for charge in charges:
|
||||
dna += ":{0};{1}".format(charge, charges[charge])
|
||||
|
||||
return dna + "::"
|
||||
|
||||
@classmethod
|
||||
def exportXml(cls, *fits):
|
||||
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)
|
||||
fittings.appendChild(fitting)
|
||||
description = doc.createElement("description")
|
||||
description.setAttribute("value", "")
|
||||
fitting.appendChild(description)
|
||||
shipType = doc.createElement("shipType")
|
||||
shipType.setAttribute("value", fit.ship.item.name)
|
||||
fitting.appendChild(shipType)
|
||||
|
||||
charges = {}
|
||||
slotNum = {}
|
||||
for module in fit.modules:
|
||||
if module.isEmpty:
|
||||
continue
|
||||
|
||||
slot = module.slot
|
||||
if not slot in slotNum: slotNum[slot] = 0
|
||||
slotId = slotNum[slot]
|
||||
slotNum[slot] += 1
|
||||
hardware = doc.createElement("hardware")
|
||||
hardware.setAttribute("type", module.item.name)
|
||||
slotName = Slot.getName(slot).lower()
|
||||
slotName = slotName if slotName != "high" else "hi"
|
||||
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)
|
||||
hardware.setAttribute("slot", "drone bay")
|
||||
hardware.setAttribute("type", drone.item.name)
|
||||
fitting.appendChild(hardware)
|
||||
|
||||
for cargo in fit.cargo:
|
||||
if not cargo.item.name in charges:
|
||||
charges[cargo.item.name] = 0
|
||||
charges[cargo.item.name] += cargo.amount
|
||||
|
||||
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)
|
||||
|
||||
return doc.toprettyxml()
|
||||
|
||||
@reconstructor
|
||||
def init(self):
|
||||
self.build()
|
||||
|
||||
173
service/fit.py
173
service/fit.py
@@ -27,13 +27,15 @@ from codecs import open
|
||||
import eos.db
|
||||
import eos.types
|
||||
|
||||
from eos.types import State, Slot, Module, Cargo
|
||||
from eos.types import State, Slot
|
||||
|
||||
from service.market import Market
|
||||
from service.damagePattern import DamagePattern
|
||||
from service.character import Character
|
||||
from service.fleet import Fleet
|
||||
from service.settings import SettingsProvider
|
||||
from service.port import Port
|
||||
|
||||
|
||||
class FitBackupThread(threading.Thread):
|
||||
def __init__(self, path, callback):
|
||||
@@ -51,6 +53,7 @@ class FitBackupThread(threading.Thread):
|
||||
backupFile.close()
|
||||
wx.CallAfter(self.callback)
|
||||
|
||||
|
||||
class FitImportThread(threading.Thread):
|
||||
def __init__(self, paths, callback):
|
||||
threading.Thread.__init__(self)
|
||||
@@ -67,8 +70,10 @@ class FitImportThread(threading.Thread):
|
||||
importedFits += pathImported
|
||||
wx.CallAfter(self.callback, importedFits)
|
||||
|
||||
|
||||
class Fit(object):
|
||||
instance = None
|
||||
|
||||
@classmethod
|
||||
def getInstance(cls):
|
||||
if cls.instance is None:
|
||||
@@ -92,10 +97,11 @@ class Fit(object):
|
||||
"rackLabels": False,
|
||||
"compactSkills": False}
|
||||
|
||||
self.serviceFittingOptions = SettingsProvider.getInstance().getSettings("pyfaServiceFittingOptions", serviceFittingDefaultOptions)
|
||||
self.serviceFittingOptions = SettingsProvider.getInstance().getSettings(
|
||||
"pyfaServiceFittingOptions", serviceFittingDefaultOptions)
|
||||
|
||||
|
||||
def getAllFits(self):
|
||||
@staticmethod
|
||||
def getAllFits():
|
||||
fits = eos.db.getFitList()
|
||||
names = []
|
||||
for fit in fits:
|
||||
@@ -103,17 +109,19 @@ class Fit(object):
|
||||
|
||||
return names
|
||||
|
||||
def getFitsWithShip(self, id):
|
||||
''' Lists fits of shipID, used with shipBrowser '''
|
||||
fits = eos.db.getFitsWithShip(id)
|
||||
@staticmethod
|
||||
def getFitsWithShip(shipID):
|
||||
""" Lists fits of shipID, used with shipBrowser """
|
||||
fits = eos.db.getFitsWithShip(shipID)
|
||||
names = []
|
||||
for fit in fits:
|
||||
names.append((fit.ID, fit.name, fit.booster, fit.timestamp))
|
||||
|
||||
return names
|
||||
|
||||
def getBoosterFits(self):
|
||||
''' Lists fits flagged as booster '''
|
||||
@staticmethod
|
||||
def getBoosterFits():
|
||||
""" Lists fits flagged as booster """
|
||||
fits = eos.db.getBoosterFits()
|
||||
names = []
|
||||
for fit in fits:
|
||||
@@ -121,20 +129,22 @@ class Fit(object):
|
||||
|
||||
return names
|
||||
|
||||
def countFitsWithShip(self, id):
|
||||
count = eos.db.countFitsWithShip(id)
|
||||
@staticmethod
|
||||
def countFitsWithShip(shipID):
|
||||
count = eos.db.countFitsWithShip(shipID)
|
||||
return count
|
||||
|
||||
def groupHasFits(self, id):
|
||||
def groupHasFits(self, groupID):
|
||||
sMkt = Market.getInstance()
|
||||
grp = sMkt.getGroup(id, eager=("items", "group"))
|
||||
grp = sMkt.getGroup(groupID, eager=("items", "group"))
|
||||
items = sMkt.getItemsByGroup(grp)
|
||||
for item in items:
|
||||
if self.countFitsWithShip(item.ID) > 0:
|
||||
return True
|
||||
return False
|
||||
|
||||
def getModule(self, fitID, pos):
|
||||
@staticmethod
|
||||
def getModule(fitID, pos):
|
||||
fit = eos.db.getFit(fitID)
|
||||
return fit.modules[pos]
|
||||
|
||||
@@ -149,12 +159,14 @@ class Fit(object):
|
||||
self.recalc(fit)
|
||||
return fit.ID
|
||||
|
||||
def toggleBoostFit(self, fitID):
|
||||
@staticmethod
|
||||
def toggleBoostFit(fitID):
|
||||
fit = eos.db.getFit(fitID)
|
||||
fit.booster = not fit.booster
|
||||
eos.db.commit()
|
||||
|
||||
def renameFit(self, fitID, newName):
|
||||
@staticmethod
|
||||
def renameFit(fitID, newName):
|
||||
fit = eos.db.getFit(fitID)
|
||||
fit.name = newName
|
||||
eos.db.commit()
|
||||
@@ -167,13 +179,15 @@ class Fit(object):
|
||||
|
||||
eos.db.remove(fit)
|
||||
|
||||
def copyFit(self, fitID):
|
||||
@staticmethod
|
||||
def copyFit(fitID):
|
||||
fit = eos.db.getFit(fitID)
|
||||
newFit = copy.deepcopy(fit)
|
||||
eos.db.save(newFit)
|
||||
return newFit.ID
|
||||
|
||||
def clearFit(self, fitID):
|
||||
@staticmethod
|
||||
def clearFit(fitID):
|
||||
if fitID is None:
|
||||
return None
|
||||
|
||||
@@ -181,8 +195,9 @@ class Fit(object):
|
||||
fit.clear()
|
||||
return fit
|
||||
|
||||
def removeProjectedData(self, fitID):
|
||||
'''Removes projection relation from ships that have fitID as projection. See GitHub issue #90'''
|
||||
@staticmethod
|
||||
def removeProjectedData(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)
|
||||
|
||||
@@ -234,11 +249,14 @@ class Fit(object):
|
||||
fit.inited = True
|
||||
return fit
|
||||
|
||||
def searchFits(self, name):
|
||||
@staticmethod
|
||||
def searchFits(name):
|
||||
results = eos.db.searchFits(name)
|
||||
fits = []
|
||||
for fit in results:
|
||||
fits.append((fit.ID, fit.name, fit.ship.item.ID, fit.ship.item.name, fit.booster, fit.timestamp))
|
||||
fits.append((
|
||||
fit.ID, fit.name, fit.ship.item.ID, fit.ship.item.name, fit.booster,
|
||||
fit.timestamp))
|
||||
return fits
|
||||
|
||||
def addImplant(self, fitID, itemID):
|
||||
@@ -297,7 +315,8 @@ class Fit(object):
|
||||
fit = eos.db.getFit(fitID)
|
||||
|
||||
if isinstance(thing, int):
|
||||
thing = eos.db.getItem(thing, eager=("attributes", "group.category"))
|
||||
thing = eos.db.getItem(thing,
|
||||
eager=("attributes", "group.category"))
|
||||
|
||||
if isinstance(thing, eos.types.Fit):
|
||||
if thing.ID == fitID:
|
||||
@@ -397,53 +416,53 @@ class Fit(object):
|
||||
eos.db.commit()
|
||||
return numSlots != len(fit.modules)
|
||||
|
||||
def moveCargoToModule(self, fitID, moduleIdx, cargoIdx, copy = False):
|
||||
'''
|
||||
def moveCargoToModule(self, fitID, moduleIdx, cargoIdx, copyMod=False):
|
||||
"""
|
||||
Moves cargo to fitting window. Can either do a copy, move, or swap with current module
|
||||
If we try to copy/move into a spot with a non-empty module, we swap instead.
|
||||
|
||||
To avoid redundancy in converting Cargo item, this function does the
|
||||
sanity checks as opposed to the GUI View. This is different than how the
|
||||
normal .swapModules() does things, which is mostly a blind swap.
|
||||
'''
|
||||
"""
|
||||
fit = eos.db.getFit(fitID)
|
||||
|
||||
module = fit.modules[moduleIdx]
|
||||
|
||||
# Gather modules and convert Cargo item to Module, silently return if not a module
|
||||
try:
|
||||
cargoP = Module(fit.cargo[cargoIdx].item)
|
||||
cargoP = eos.types.Module(fit.cargo[cargoIdx].item)
|
||||
cargoP.owner = fit
|
||||
if cargoP.isValidState(State.ACTIVE):
|
||||
cargoP.state = State.ACTIVE
|
||||
except:
|
||||
return
|
||||
|
||||
if cargoP.slot != module.slot: # can't swap modules to different racks
|
||||
if cargoP.slot != module.slot: # can't swap modules to different racks
|
||||
return
|
||||
|
||||
# remove module that we are trying to move cargo to
|
||||
fit.modules.remove(module)
|
||||
|
||||
if not cargoP.fits(fit): #if cargo doesn't fit, rollback and return
|
||||
if not cargoP.fits(fit): # if cargo doesn't fit, rollback and return
|
||||
fit.modules.insert(moduleIdx, module)
|
||||
return
|
||||
|
||||
fit.modules.insert(moduleIdx, cargoP)
|
||||
|
||||
if not copy: # remove existing cargo if not cloning
|
||||
fit.cargo.remove(fit.cargo[cargoIdx])
|
||||
if not copyMod: # remove existing cargo if not cloning
|
||||
fit.cargo.remove(fit.cargo[cargoIdx])
|
||||
|
||||
if not module.isEmpty: # if module is placeholder, we don't want to convert/add it
|
||||
moduleP = Cargo(module.item)
|
||||
if not module.isEmpty: # if module is placeholder, we don't want to convert/add it
|
||||
moduleP = eos.types.Cargo(module.item)
|
||||
moduleP.amount = 1
|
||||
fit.cargo.insert(cargoIdx, moduleP)
|
||||
|
||||
eos.db.commit()
|
||||
self.recalc(fit)
|
||||
|
||||
|
||||
def swapModules(self, fitID, src, dst):
|
||||
@staticmethod
|
||||
def swapModules(fitID, src, dst):
|
||||
fit = eos.db.getFit(fitID)
|
||||
# Gather modules
|
||||
srcMod = fit.modules[src]
|
||||
@@ -458,16 +477,16 @@ class Fit(object):
|
||||
eos.db.commit()
|
||||
|
||||
def cloneModule(self, fitID, src, dst):
|
||||
'''
|
||||
"""
|
||||
Clone a module from src to dst
|
||||
|
||||
This will overwrite dst! Checking for empty module must be
|
||||
done at a higher level
|
||||
'''
|
||||
"""
|
||||
fit = eos.db.getFit(fitID)
|
||||
# Gather modules
|
||||
srcMod = fit.modules[src]
|
||||
dstMod = fit.modules[dst] # should be a placeholder module
|
||||
dstMod = fit.modules[dst] # should be a placeholder module
|
||||
|
||||
new = copy.deepcopy(srcMod)
|
||||
new.owner = fit
|
||||
@@ -479,12 +498,13 @@ class Fit(object):
|
||||
eos.db.commit()
|
||||
self.recalc(fit)
|
||||
|
||||
def addCargo(self, fitID, itemID, amount=1, replace = False):
|
||||
'''Adds cargo via typeID of item. If replace = True, we replace amount with
|
||||
def addCargo(self, fitID, itemID, amount=1, replace=False):
|
||||
"""
|
||||
Adds cargo via typeID of item. If replace = True, we replace amount with
|
||||
given parameter, otherwise we increment
|
||||
'''
|
||||
"""
|
||||
|
||||
if fitID == None:
|
||||
if fitID is None:
|
||||
return False
|
||||
|
||||
fit = eos.db.getFit(fitID)
|
||||
@@ -525,7 +545,7 @@ class Fit(object):
|
||||
return True
|
||||
|
||||
def addDrone(self, fitID, itemID):
|
||||
if fitID == None:
|
||||
if fitID is None:
|
||||
return False
|
||||
|
||||
fit = eos.db.getFit(fitID)
|
||||
@@ -551,7 +571,7 @@ class Fit(object):
|
||||
return False
|
||||
|
||||
def mergeDrones(self, fitID, d1, d2, projected=False):
|
||||
if fitID == None:
|
||||
if fitID is None:
|
||||
return False
|
||||
|
||||
fit = eos.db.getFit(fitID)
|
||||
@@ -569,7 +589,8 @@ class Fit(object):
|
||||
self.recalc(fit)
|
||||
return True
|
||||
|
||||
def splitDrones(self, fit, d, amount, l):
|
||||
@staticmethod
|
||||
def splitDrones(fit, d, amount, l):
|
||||
total = d.amount
|
||||
active = d.amountActive > 0
|
||||
d.amount = amount
|
||||
@@ -582,14 +603,14 @@ class Fit(object):
|
||||
eos.db.commit()
|
||||
|
||||
def splitProjectedDroneStack(self, fitID, d, amount):
|
||||
if fitID == None:
|
||||
if fitID is None:
|
||||
return False
|
||||
|
||||
fit = eos.db.getFit(fitID)
|
||||
self.splitDrones(fit, d, amount, fit.projectedDrones)
|
||||
|
||||
def splitDroneStack(self, fitID, d, amount):
|
||||
if fitID == None:
|
||||
if fitID is None:
|
||||
return False
|
||||
|
||||
fit = eos.db.getFit(fitID)
|
||||
@@ -650,7 +671,8 @@ class Fit(object):
|
||||
fit.character = self.character = eos.db.getCharacter(charID)
|
||||
self.recalc(fit)
|
||||
|
||||
def isAmmo(self, itemID):
|
||||
@staticmethod
|
||||
def isAmmo(itemID):
|
||||
return eos.db.getItem(itemID).category.name == "Charge"
|
||||
|
||||
def setAmmo(self, fitID, ammoID, modules):
|
||||
@@ -666,7 +688,8 @@ class Fit(object):
|
||||
|
||||
self.recalc(fit)
|
||||
|
||||
def getDamagePattern(self, fitID):
|
||||
@staticmethod
|
||||
def getDamagePattern(fitID):
|
||||
if fitID is None:
|
||||
return
|
||||
|
||||
@@ -700,27 +723,30 @@ class Fit(object):
|
||||
fit.damagePattern = dp
|
||||
self.recalc(fit)
|
||||
|
||||
def exportFit(self, fitID):
|
||||
fit = eos.db.getFit(fitID)
|
||||
return fit.exportEft()
|
||||
@staticmethod
|
||||
def exportFit(fitID):
|
||||
return Port.exportEft(fitID)
|
||||
|
||||
def exportEftImps(self, fitID):
|
||||
fit = eos.db.getFit(fitID)
|
||||
return fit.exportEftImps()
|
||||
@staticmethod
|
||||
def exportEftImps(fitID):
|
||||
return Port.exportEftImps(fitID)
|
||||
|
||||
def exportDna(self, fitID):
|
||||
fit = eos.db.getFit(fitID)
|
||||
return fit.exportDna()
|
||||
@staticmethod
|
||||
def exportDna(fitID):
|
||||
return Port.exportDna(fitID)
|
||||
|
||||
def exportXml(self, *fitIDs):
|
||||
fits = map(lambda id: eos.db.getFit(id), fitIDs)
|
||||
return eos.types.Fit.exportXml(*fits)
|
||||
@staticmethod
|
||||
def exportXml(*fitIDs):
|
||||
fits = map(lambda fitID: eos.db.getFit(fitID), fitIDs)
|
||||
return Port.exportXml(*fits)
|
||||
|
||||
def backupFits(self, path, callback):
|
||||
@staticmethod
|
||||
def backupFits(path, callback):
|
||||
thread = FitBackupThread(path, callback)
|
||||
thread.start()
|
||||
|
||||
def importFitsThreaded(self, paths, callback):
|
||||
@staticmethod
|
||||
def importFitsThreaded(paths, callback):
|
||||
thread = FitImportThread(paths, callback)
|
||||
thread.start()
|
||||
|
||||
@@ -739,20 +765,21 @@ class Fit(object):
|
||||
except UnicodeDecodeError:
|
||||
srcString = unicode(srcString, "cp1252")
|
||||
|
||||
type, fits = eos.types.Fit.importAuto(srcString, filename)
|
||||
_, fits = Port.importAuto(srcString, filename)
|
||||
for fit in fits:
|
||||
fit.character = self.character
|
||||
fit.damagePattern = self.pattern
|
||||
return fits
|
||||
|
||||
def importFitFromBuffer(self, buffer, activeFit=None):
|
||||
type,fits = eos.types.Fit.importAuto(buffer, activeFit=activeFit)
|
||||
def importFitFromBuffer(self, bufferStr, activeFit=None):
|
||||
_, fits = Port.importAuto(bufferStr, activeFit=activeFit)
|
||||
for fit in fits:
|
||||
fit.character = self.character
|
||||
fit.damagePattern = self.pattern
|
||||
return fits
|
||||
|
||||
def saveImportedFits(self, fits):
|
||||
@staticmethod
|
||||
def saveImportedFits(fits):
|
||||
IDs = []
|
||||
for fit in fits:
|
||||
eos.db.save(fit)
|
||||
@@ -760,7 +787,8 @@ class Fit(object):
|
||||
|
||||
return IDs
|
||||
|
||||
def checkStates(self, fit, base):
|
||||
@staticmethod
|
||||
def checkStates(fit, base):
|
||||
changed = False
|
||||
for mod in fit.modules:
|
||||
if mod != base:
|
||||
@@ -783,7 +811,8 @@ class Fit(object):
|
||||
base.state = proposedState
|
||||
for mod in modules:
|
||||
if mod != base:
|
||||
mod.state = self.__getProposedState(mod, click, proposedState)
|
||||
mod.state = self.__getProposedState(mod, click,
|
||||
proposedState)
|
||||
|
||||
eos.db.commit()
|
||||
fit = eos.db.getFit(fitID)
|
||||
@@ -804,13 +833,13 @@ class Fit(object):
|
||||
projectedMap = {State.OVERHEATED: State.ACTIVE,
|
||||
State.ACTIVE: State.OFFLINE,
|
||||
State.OFFLINE: State.ACTIVE,
|
||||
State.ONLINE: State.ACTIVE} # Just in case
|
||||
State.ONLINE: State.ACTIVE} # Just in case
|
||||
|
||||
def __getProposedState(self, mod, click, proposedState=None):
|
||||
if mod.slot in (Slot.RIG, Slot.SUBSYSTEM) or mod.isEmpty:
|
||||
return State.ONLINE
|
||||
|
||||
currState = state = mod.state
|
||||
currState = mod.state
|
||||
transitionMap = self.projectedMap if mod.projected else self.localMap
|
||||
if proposedState is not None:
|
||||
state = proposedState
|
||||
@@ -821,7 +850,7 @@ class Fit(object):
|
||||
else:
|
||||
state = transitionMap[currState]
|
||||
if not mod.isValidState(state):
|
||||
state =- 1
|
||||
state = -1
|
||||
|
||||
if mod.isValidState(state):
|
||||
return state
|
||||
|
||||
@@ -25,8 +25,6 @@ import Queue
|
||||
|
||||
import eos.db
|
||||
import eos.types
|
||||
from eos.itemMapping import itemMapping
|
||||
|
||||
from service.settings import SettingsProvider, ProxySettings
|
||||
|
||||
try:
|
||||
@@ -153,6 +151,35 @@ class Market():
|
||||
self.shipBrowserWorkerThread.daemon = True
|
||||
self.shipBrowserWorkerThread.start()
|
||||
|
||||
# Item overrides. Key must be str of item name,
|
||||
# value str of name or int of ID to convert it to
|
||||
self.ITEMS_OVERRIDE = {
|
||||
"Aliastra Catalyst": "Catalyst",
|
||||
"Inner Zone Shipping Catalyst": "Catalyst",
|
||||
"Intaki Syndicate Catalyst": "Catalyst",
|
||||
"InterBus Catalyst": "Catalyst",
|
||||
"Quafe Catalyst": "Catalyst",
|
||||
"Nefantar Thrasher": "Thrasher",
|
||||
"Sarum Magnate": "Magnate",
|
||||
"Sukuuvestaa Heron": "Heron:",
|
||||
"Inner Zone Shipping Imicus": "Imicus",
|
||||
"Vherokior Probe": "Probe",
|
||||
"Miasmos Quafe Ultra Edition": "Miasmos",
|
||||
"Miasmos Quafe Ultramarine Edition": "Miasmos",
|
||||
"Miasmos Amastris Edition": "Miasmos",
|
||||
"Tash-Murkon Magnate": "Magnate",
|
||||
"Scorpion Ishukone Watch": "Scorpion",
|
||||
"Incursus Aliastra Edition": "Incursus",
|
||||
"Merlin Nugoeihuvi Edition": "Merlin",
|
||||
"Police Pursuit Comet": "Federation Navy Comet",
|
||||
"Punisher Kador Edition": "Punisher",
|
||||
"Rifter Krusual Edition": "Rifter",
|
||||
"Abaddon Kador Edition": "Abaddon",
|
||||
"Hyperion Aliastra Edition": "Hyperion",
|
||||
"Maelstrom Krusual Edition": "Maelstrom",
|
||||
"Rokh Nugoeihuvi Edition": "Rokh",
|
||||
"Mammoth Nefantar Edition": "Mammoth" }
|
||||
|
||||
# Items' group overrides
|
||||
self.customGroups = set()
|
||||
# Limited edition ships
|
||||
@@ -214,7 +241,7 @@ class Market():
|
||||
"Guristas Shuttle": False}
|
||||
|
||||
# do not publish anything that we convert
|
||||
for name, _ in itemMapping.iteritems():
|
||||
for name in self.ITEMS_OVERRIDE:
|
||||
self.ITEMS_FORCEPUBLISHED[name] = False
|
||||
|
||||
# List of groups which are forcibly published
|
||||
@@ -319,6 +346,8 @@ class Market():
|
||||
item = eos.db.getItem(id, *args, **kwargs)
|
||||
else:
|
||||
raise TypeError("Need Item object, integer, float or string as argument")
|
||||
if item.name in self.ITEMS_OVERRIDE:
|
||||
item = self.getItem(self.ITEMS_OVERRIDE[item.name], *args, **kwargs)
|
||||
return item
|
||||
|
||||
def getGroup(self, identity, *args, **kwargs):
|
||||
|
||||
719
service/port.py
Normal file
719
service/port.py
Normal file
@@ -0,0 +1,719 @@
|
||||
#===============================================================================
|
||||
# Copyright (C) 2010 Diego Duclos
|
||||
#
|
||||
# This file is part of pyfa.
|
||||
#
|
||||
# pyfa is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# pyfa is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# 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 xml.dom
|
||||
import urllib2
|
||||
import json
|
||||
|
||||
from eos.types import State, Slot, Module, Cargo, Fit, Ship, Drone, Implant, Booster
|
||||
import service
|
||||
|
||||
try:
|
||||
from collections import OrderedDict
|
||||
except ImportError:
|
||||
from gui.utils.compat import OrderedDict
|
||||
|
||||
|
||||
class Port(object):
|
||||
"""Service which houses all import/export format functions"""
|
||||
|
||||
@classmethod
|
||||
def importAuto(cls, string, sourceFileName=None, activeFit=None):
|
||||
# Get first line and strip space symbols of it to avoid possible detection errors
|
||||
firstLine = re.split("[\n\r]+", string.strip(), maxsplit=1)[0]
|
||||
firstLine = firstLine.strip()
|
||||
|
||||
# If string is from in-game copy of fitting window
|
||||
# We match " power" instead of "High power" in case a fit has no high modules
|
||||
if " power" in firstLine and activeFit is not None:
|
||||
return "FIT", (cls.importFittingWindow(string, activeFit),)
|
||||
|
||||
# If we have "<url=fitting", fit is coming from eve chat
|
||||
# Gather data and send to DNA
|
||||
chatDna = re.search("<url=fitting:(.*::)>.*</url>", firstLine)
|
||||
if chatDna:
|
||||
return "DNA", (cls.importDna(chatDna.group(1)),)
|
||||
|
||||
# If we have a CREST kill link
|
||||
killLink = re.search("http://public-crest.eveonline.com/killmails/(.*)/", firstLine)
|
||||
if killLink:
|
||||
return "CREST", (cls.importCrest(tuple(killLink.group(1).split("/"))),)
|
||||
|
||||
# If we have "<url=killReport", fit is killmail from eve chat
|
||||
killReport = re.search("<url=killReport:(.*)>.*</url>", firstLine)
|
||||
if killReport:
|
||||
return "CREST", (cls.importCrest(tuple(killReport.group(1).split(":"))),)
|
||||
|
||||
# If XML-style start of tag encountered, detect as XML
|
||||
if re.match("<", firstLine):
|
||||
return "XML", cls.importXml(string)
|
||||
|
||||
# If we've got source file name which is used to describe ship name
|
||||
# and first line contains something like [setup name], detect as eft config file
|
||||
if re.match("\[.*\]", firstLine) and sourceFileName is not None:
|
||||
shipName = sourceFileName.rsplit('.')[0]
|
||||
return "EFT Config", cls.importEftCfg(shipName, string)
|
||||
|
||||
# If no file is specified and there's comma between brackets,
|
||||
# consider that we have [ship, setup name] and detect like eft export format
|
||||
if re.match("\[.*,.*\]", firstLine):
|
||||
return "EFT", (cls.importEft(string),)
|
||||
|
||||
# Use DNA format for all other cases
|
||||
return "DNA", (cls.importDna(string),)
|
||||
|
||||
@staticmethod
|
||||
def importFittingWindow(string, activeFit):
|
||||
sMkt = service.Market.getInstance()
|
||||
sFit = service.Fit.getInstance()
|
||||
|
||||
activeFit = sFit.getFit(activeFit)
|
||||
|
||||
# if the current fit has mods, do not mess with it. Instead, make new fit
|
||||
if activeFit.modCount > 0:
|
||||
fit = Fit()
|
||||
fit.ship = Ship(sMkt.getItem(activeFit.ship.item.ID))
|
||||
fit.name = "%s (copy)" % activeFit.name
|
||||
else:
|
||||
fit = activeFit
|
||||
lines = re.split('[\n\r]+', string)
|
||||
|
||||
droneMap = {}
|
||||
cargoMap = {}
|
||||
modules = []
|
||||
|
||||
for i in range(1, len(lines)):
|
||||
line = lines[i].strip()
|
||||
if not line:
|
||||
continue
|
||||
|
||||
try:
|
||||
amount, modName = line.split("x ")
|
||||
amount = int(amount)
|
||||
item = sMkt.getItem(modName, eager="group.category")
|
||||
except:
|
||||
# if no data can be found (old names)
|
||||
continue
|
||||
|
||||
if item.category.name == "Drone":
|
||||
if not modName in droneMap:
|
||||
droneMap[modName] = 0
|
||||
droneMap[modName] += amount
|
||||
elif item.category.name == "Charge":
|
||||
if not modName in cargoMap:
|
||||
cargoMap[modName] = 0
|
||||
cargoMap[modName] += amount
|
||||
else:
|
||||
for _ in xrange(amount):
|
||||
try:
|
||||
m = Module(item)
|
||||
except ValueError:
|
||||
continue
|
||||
# If we are importing T3 ship, we must apply subsystems first, then
|
||||
# calcModAttr() to get the ship slots
|
||||
if m.slot == Slot.SUBSYSTEM and m.fits(fit):
|
||||
fit.modules.append(m)
|
||||
else:
|
||||
modules.append(m)
|
||||
|
||||
fit.clear()
|
||||
fit.calculateModifiedAttributes()
|
||||
|
||||
for m in modules:
|
||||
# we check to see if module fits as a basic sanity check
|
||||
# if it doesn't then the imported fit is most likely invalid
|
||||
# (ie: user tried to import Legion fit to a Rifter)
|
||||
if m.fits(fit):
|
||||
fit.modules.append(m)
|
||||
if m.isValidState(State.ACTIVE):
|
||||
m.state = State.ACTIVE
|
||||
m.owner = fit # not sure why this is required when it's not for other import methods, but whatever
|
||||
else:
|
||||
return
|
||||
|
||||
for droneName in droneMap:
|
||||
d = Drone(sMkt.getItem(droneName))
|
||||
d.amount = droneMap[droneName]
|
||||
fit.drones.append(d)
|
||||
|
||||
for cargoName in cargoMap:
|
||||
c = Cargo(sMkt.getItem(cargoName))
|
||||
c.amount = cargoMap[cargoName]
|
||||
fit.cargo.append(c)
|
||||
|
||||
return fit
|
||||
|
||||
@staticmethod
|
||||
def importCrest(info):
|
||||
sMkt = service.Market.getInstance()
|
||||
try:
|
||||
# @todo: proxy
|
||||
response = urllib2.urlopen("https://public-crest.eveonline.com/killmails/%s/%s/" % info)
|
||||
except:
|
||||
return
|
||||
|
||||
kill = (json.loads(response.read()))['victim']
|
||||
|
||||
fit = Fit()
|
||||
fit.ship = Ship(sMkt.getItem(kill['shipType']['name']))
|
||||
fit.name = "CREST: %s's %s" % (kill['character']['name'], kill['shipType']['name'])
|
||||
|
||||
# sort based on flag to get proper rack position
|
||||
items = sorted(kill['items'], key=lambda k: k['flag'])
|
||||
|
||||
# We create a relation between module flag and module position on fit at time of append:
|
||||
# this allows us to know which module to apply charges to if need be (see below)
|
||||
flagMap = {}
|
||||
|
||||
# Charges may show up before or after the module. We process modules first,
|
||||
# storing any charges that are fitted in a dict and noting their flag (module).
|
||||
charges = {}
|
||||
|
||||
for mod in items:
|
||||
if mod['flag'] == 5: # throw out cargo
|
||||
continue
|
||||
|
||||
item = sMkt.getItem(mod['itemType']['name'], eager="group.category")
|
||||
|
||||
if item.category.name == "Drone":
|
||||
d = Drone(item)
|
||||
d.amount = mod['quantityDropped'] if 'quantityDropped' in mod else mod['quantityDestroyed']
|
||||
fit.drones.append(d)
|
||||
elif item.category.name == "Charge":
|
||||
charges[mod['flag']] = item
|
||||
else:
|
||||
m = Module(item)
|
||||
if m.isValidState(State.ACTIVE):
|
||||
m.state = State.ACTIVE
|
||||
fit.modules.append(m)
|
||||
flagMap[mod['flag']] = fit.modules.index(m)
|
||||
|
||||
for flag, item in charges.items():
|
||||
# we do not need to verify valid charge as it comes directly from CCP
|
||||
fit.modules[flagMap[flag]].charge = item
|
||||
|
||||
return fit
|
||||
|
||||
@staticmethod
|
||||
def importDna(string):
|
||||
sMkt = service.Market.getInstance()
|
||||
info = string.split(":")
|
||||
|
||||
f = Fit()
|
||||
f.ship = Ship(sMkt.getItem(int(info[0])))
|
||||
f.name = "{0} - DNA Imported".format(f.ship.item.name)
|
||||
|
||||
for itemInfo in info[1:]:
|
||||
if itemInfo:
|
||||
itemID, amount = itemInfo.split(";")
|
||||
item = sMkt.getItem(int(itemID), eager="group.category")
|
||||
|
||||
if item.category.name == "Drone":
|
||||
d = Drone(item)
|
||||
d.amount = int(amount)
|
||||
f.drones.append(d)
|
||||
elif item.category.name == "Charge":
|
||||
c = Cargo(item)
|
||||
c.amount = int(amount)
|
||||
f.cargo.append(c)
|
||||
else:
|
||||
for i in xrange(int(amount)):
|
||||
try:
|
||||
m = Module(item)
|
||||
f.modules.append(m)
|
||||
except:
|
||||
pass
|
||||
if m.isValidState(State.ACTIVE):
|
||||
m.state = State.ACTIVE
|
||||
|
||||
return f
|
||||
|
||||
@staticmethod
|
||||
def importEft(eftString):
|
||||
sMkt = service.Market.getInstance()
|
||||
offineSuffix = " /OFFLINE"
|
||||
|
||||
fit = Fit()
|
||||
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()
|
||||
else:
|
||||
shipType = info[0].strip()
|
||||
fitName = "Imported %s" % shipType
|
||||
|
||||
try:
|
||||
ship = sMkt.getItem(shipType)
|
||||
fit.ship = Ship(ship)
|
||||
fit.name = fitName
|
||||
except:
|
||||
return
|
||||
|
||||
# maintain map of drones and their quantities
|
||||
droneMap = {}
|
||||
cargoMap = {}
|
||||
for i in range(1, len(lines)):
|
||||
ammoName = None
|
||||
extraAmount = None
|
||||
|
||||
line = lines[i].strip()
|
||||
if not line:
|
||||
continue
|
||||
|
||||
setOffline = line.endswith(offineSuffix)
|
||||
if setOffline is True:
|
||||
# remove offline suffix from line
|
||||
line = line[:len(line) - len(offineSuffix)]
|
||||
|
||||
modAmmo = line.split(",")
|
||||
# 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
|
||||
extraAmount = 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 = sMkt.getItem(modName, eager="group.category")
|
||||
except:
|
||||
# if no data can be found (old names)
|
||||
continue
|
||||
|
||||
if item.category.name == "Drone":
|
||||
extraAmount = int(extraAmount) if extraAmount is not None else 1
|
||||
if not modName in droneMap:
|
||||
droneMap[modName] = 0
|
||||
droneMap[modName] += extraAmount
|
||||
if len(modExtra) == 2 and item.category.name != "Drone":
|
||||
extraAmount = int(extraAmount) if extraAmount is not None else 1
|
||||
if not modName in cargoMap:
|
||||
cargoMap[modName] = 0
|
||||
cargoMap[modName] += extraAmount
|
||||
elif item.category.name == "Implant":
|
||||
fit.implants.append(Implant(item))
|
||||
else:
|
||||
try:
|
||||
m = Module(item)
|
||||
except ValueError:
|
||||
continue
|
||||
if ammoName:
|
||||
try:
|
||||
ammo = sMkt.getItem(ammoName)
|
||||
if m.isValidCharge(ammo) and m.charge is None:
|
||||
m.charge = ammo
|
||||
except:
|
||||
pass
|
||||
|
||||
if setOffline is True and m.isValidState(State.OFFLINE):
|
||||
m.state = State.OFFLINE
|
||||
elif m.isValidState(State.ACTIVE):
|
||||
m.state = State.ACTIVE
|
||||
|
||||
fit.modules.append(m)
|
||||
|
||||
for droneName in droneMap:
|
||||
d = Drone(sMkt.getItem(droneName))
|
||||
d.amount = droneMap[droneName]
|
||||
fit.drones.append(d)
|
||||
|
||||
for cargoName in cargoMap:
|
||||
c = Cargo(sMkt.getItem(cargoName))
|
||||
c.amount = cargoMap[cargoName]
|
||||
fit.cargo.append(c)
|
||||
|
||||
return fit
|
||||
|
||||
@staticmethod
|
||||
def importEftCfg(shipname, contents):
|
||||
"""Handle import from EFT config store file"""
|
||||
|
||||
# Check if we have such ship in database, bail if we don't
|
||||
sMkt = service.Market.getInstance()
|
||||
try:
|
||||
sMkt.getItem(shipname)
|
||||
except:
|
||||
return
|
||||
|
||||
# If client didn't take care of encoding file contents into Unicode,
|
||||
# do it using fallback encoding ourselves
|
||||
if isinstance(contents, str):
|
||||
contents = unicode(contents, "cp1252")
|
||||
|
||||
fits = [] # List for fits
|
||||
fitIndices = [] # List for starting line numbers for each fit
|
||||
lines = re.split('[\n\r]+', contents) # Separate string into lines
|
||||
|
||||
for line in lines:
|
||||
# Detect fit header
|
||||
if line[:1] == "[" and line[-1:] == "]":
|
||||
# Line index where current fit starts
|
||||
startPos = lines.index(line)
|
||||
fitIndices.append(startPos)
|
||||
|
||||
for i, startPos in enumerate(fitIndices):
|
||||
# End position is last file line if we're trying to get it for last fit,
|
||||
# or start position of next fit minus 1
|
||||
endPos = len(lines) if i == len(fitIndices) - 1 else fitIndices[i + 1]
|
||||
|
||||
# Finally, get lines for current fitting
|
||||
fitLines = lines[startPos:endPos]
|
||||
|
||||
try:
|
||||
# Create fit object
|
||||
f = Fit()
|
||||
# Strip square brackets and pull out a fit name
|
||||
f.name = fitLines[0][1:-1]
|
||||
# Assign ship to fitting
|
||||
f.ship = Ship(sMkt.getItem(shipname))
|
||||
|
||||
for x in range(1, len(fitLines)):
|
||||
line = fitLines[x]
|
||||
if not line:
|
||||
continue
|
||||
|
||||
# Parse line into some data we will need
|
||||
misc = re.match("(Drones|Implant|Booster)_(Active|Inactive)=(.+)", line)
|
||||
cargo = re.match("Cargohold=(.+)", line)
|
||||
|
||||
if misc:
|
||||
entityType = misc.group(1)
|
||||
entityState = misc.group(2)
|
||||
entityData = misc.group(3)
|
||||
if entityType == "Drones":
|
||||
droneData = re.match("(.+),([0-9]+)", entityData)
|
||||
# Get drone name and attempt to detect drone number
|
||||
droneName = droneData.group(1) if droneData else entityData
|
||||
droneAmount = int(droneData.group(2)) if droneData else 1
|
||||
# Bail if we can't get item or it's not from drone category
|
||||
try:
|
||||
droneItem = sMkt.getItem(droneName, eager="group.category")
|
||||
except:
|
||||
continue
|
||||
if droneItem.category.name != "Drone":
|
||||
continue
|
||||
# Add drone to the fitting
|
||||
d = Drone(droneItem)
|
||||
d.amount = droneAmount
|
||||
if entityState == "Active":
|
||||
d.amountActive = droneAmount
|
||||
elif entityState == "Inactive":
|
||||
d.amountActive = 0
|
||||
f.drones.append(d)
|
||||
elif entityType == "Implant":
|
||||
# Bail if we can't get item or it's not from implant category
|
||||
try:
|
||||
implantItem = sMkt.getItem(entityData, eager="group.category")
|
||||
except:
|
||||
continue
|
||||
if implantItem.category.name != "Implant":
|
||||
continue
|
||||
# Add implant to the fitting
|
||||
imp = Implant(implantItem)
|
||||
if entityState == "Active":
|
||||
imp.active = True
|
||||
elif entityState == "Inactive":
|
||||
imp.active = False
|
||||
f.implants.append(imp)
|
||||
elif entityType == "Booster":
|
||||
# Bail if we can't get item or it's not from implant category
|
||||
try:
|
||||
boosterItem = sMkt.getItem(entityData, eager="group.category")
|
||||
except:
|
||||
continue
|
||||
# All boosters have implant category
|
||||
if boosterItem.category.name != "Implant":
|
||||
continue
|
||||
# Add booster to the fitting
|
||||
b = Booster(boosterItem)
|
||||
if entityState == "Active":
|
||||
b.active = True
|
||||
elif entityState == "Inactive":
|
||||
b.active = False
|
||||
f.boosters.append(b)
|
||||
# If we don't have any prefixes, then it's a module
|
||||
elif cargo:
|
||||
cargoData = re.match("(.+),([0-9]+)", cargo.group(1))
|
||||
cargoName = cargoData.group(1) if cargoData else cargo.group(1)
|
||||
cargoAmount = int(cargoData.group(2)) if cargoData else 1
|
||||
# Bail if we can't get item
|
||||
try:
|
||||
item = sMkt.getItem(cargoName)
|
||||
except:
|
||||
continue
|
||||
# Add Cargo to the fitting
|
||||
c = Cargo(item)
|
||||
c.amount = cargoAmount
|
||||
f.cargo.append(c)
|
||||
else:
|
||||
withCharge = re.match("(.+),(.+)", line)
|
||||
modName = withCharge.group(1) if withCharge else line
|
||||
chargeName = withCharge.group(2) if withCharge else None
|
||||
# If we can't get module item, skip it
|
||||
try:
|
||||
modItem = sMkt.getItem(modName)
|
||||
except:
|
||||
continue
|
||||
|
||||
# Create module and activate it if it's activable
|
||||
m = Module(modItem)
|
||||
if m.isValidState(State.ACTIVE):
|
||||
m.state = State.ACTIVE
|
||||
# Add charge to mod if applicable, on any errors just don't add anything
|
||||
if chargeName:
|
||||
try:
|
||||
chargeItem = sMkt.getItem(chargeName, eager="group.category")
|
||||
if chargeItem.category.name == "Charge":
|
||||
m.charge = chargeItem
|
||||
except:
|
||||
pass
|
||||
# Append module to fit
|
||||
f.modules.append(m)
|
||||
# Append fit to list of fits
|
||||
fits.append(f)
|
||||
# Skip fit silently if we get an exception
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
return fits
|
||||
|
||||
@staticmethod
|
||||
def importXml(text):
|
||||
sMkt = service.Market.getInstance()
|
||||
|
||||
doc = xml.dom.minidom.parseString(text.encode("utf-8"))
|
||||
fittings = doc.getElementsByTagName("fittings").item(0)
|
||||
fittings = fittings.getElementsByTagName("fitting")
|
||||
fits = []
|
||||
for fitting in fittings:
|
||||
f = Fit()
|
||||
f.name = fitting.getAttribute("name")
|
||||
# <localized hint="Maelstrom">Maelstrom</localized>
|
||||
shipType = fitting.getElementsByTagName("shipType").item(0).getAttribute("value")
|
||||
try:
|
||||
f.ship = Ship(sMkt.getItem(shipType))
|
||||
except:
|
||||
continue
|
||||
hardwares = fitting.getElementsByTagName("hardware")
|
||||
for hardware in hardwares:
|
||||
try:
|
||||
moduleName = hardware.getAttribute("type")
|
||||
try:
|
||||
item = sMkt.getItem(moduleName, eager="group.category")
|
||||
except:
|
||||
continue
|
||||
if item:
|
||||
if item.category.name == "Drone":
|
||||
d = Drone(item)
|
||||
d.amount = int(hardware.getAttribute("qty"))
|
||||
f.drones.append(d)
|
||||
elif hardware.getAttribute("slot").lower() == "cargo":
|
||||
# although the eve client only support charges in cargo, third-party programs
|
||||
# may support items or "refits" in cargo. Support these by blindly adding all
|
||||
# cargo, not just charges
|
||||
c = Cargo(item)
|
||||
c.amount = int(hardware.getAttribute("qty"))
|
||||
f.cargo.append(c)
|
||||
else:
|
||||
try:
|
||||
m = Module(item)
|
||||
# When item can't be added to any slot (unknown item or just charge), ignore it
|
||||
except ValueError:
|
||||
continue
|
||||
if m.isValidState(State.ACTIVE):
|
||||
m.state = State.ACTIVE
|
||||
|
||||
f.modules.append(m)
|
||||
except KeyboardInterrupt:
|
||||
continue
|
||||
fits.append(f)
|
||||
|
||||
return fits
|
||||
|
||||
@staticmethod
|
||||
def exportEft(fitID):
|
||||
sFit = service.Fit.getInstance()
|
||||
fit = sFit.getFit(fitID)
|
||||
|
||||
offineSuffix = " /OFFLINE"
|
||||
export = "[%s, %s]\n" % (fit.ship.item.name, fit.name)
|
||||
stuff = {}
|
||||
for module in fit.modules:
|
||||
slot = module.slot
|
||||
if not slot 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 "")
|
||||
if module.charge:
|
||||
curr += ", %s" % module.charge.name
|
||||
if module.state == State.OFFLINE:
|
||||
curr += offineSuffix
|
||||
curr += "\n"
|
||||
stuff[slot].append(curr)
|
||||
|
||||
for slotType in [Slot.LOW, Slot.MED, Slot.HIGH, Slot.RIG, Slot.SUBSYSTEM]:
|
||||
data = stuff.get(slotType)
|
||||
if data is not None:
|
||||
export += "\n"
|
||||
for curr in data:
|
||||
export += curr
|
||||
|
||||
if len(fit.drones) > 0:
|
||||
export += "\n\n"
|
||||
for drone in fit.drones:
|
||||
export += "%s x%s\n" % (drone.item.name, drone.amount)
|
||||
if len(fit.cargo) > 0:
|
||||
for cargo in fit.cargo:
|
||||
export += "%s x%s\n" % (cargo.item.name, cargo.amount)
|
||||
|
||||
if export[-1] == "\n":
|
||||
export = export[:-1]
|
||||
|
||||
return export
|
||||
|
||||
@classmethod
|
||||
def exportEftImps(cls, fitID):
|
||||
export = cls.exportEft(fitID)
|
||||
|
||||
sFit = service.Fit.getInstance()
|
||||
fit = sFit.getFit(fitID)
|
||||
|
||||
if len(fit.implants) > 0:
|
||||
export += "\n\n\n"
|
||||
for implant in fit.implants:
|
||||
export += "%s\n" % implant.item.name
|
||||
|
||||
if export[-1] == "\n":
|
||||
export = export[:-1]
|
||||
|
||||
return export
|
||||
|
||||
@staticmethod
|
||||
def exportDna(fitID):
|
||||
sFit = service.Fit.getInstance()
|
||||
fit = sFit.getFit(fitID)
|
||||
|
||||
dna = str(fit.shipID)
|
||||
mods = OrderedDict()
|
||||
charges = OrderedDict()
|
||||
for mod in fit.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 fit.drones:
|
||||
dna += ":{0};{1}".format(drone.itemID, drone.amount)
|
||||
|
||||
for cargo in fit.cargo:
|
||||
# DNA format is a simple/dumb format. As CCP uses the slot information of the item itself
|
||||
# without designating slots in the DNA standard, we need to make sure we only include
|
||||
# charges in the DNA export. If modules were included, the EVE Client will interpret these
|
||||
# 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:
|
||||
charges[cargo.item.ID] = 0
|
||||
charges[cargo.item.ID] += cargo.amount
|
||||
|
||||
for charge in charges:
|
||||
dna += ":{0};{1}".format(charge, charges[charge])
|
||||
|
||||
return dna + "::"
|
||||
|
||||
@classmethod
|
||||
def exportXml(cls, *fits):
|
||||
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)
|
||||
fittings.appendChild(fitting)
|
||||
description = doc.createElement("description")
|
||||
description.setAttribute("value", "")
|
||||
fitting.appendChild(description)
|
||||
shipType = doc.createElement("shipType")
|
||||
shipType.setAttribute("value", fit.ship.item.name)
|
||||
fitting.appendChild(shipType)
|
||||
|
||||
charges = {}
|
||||
slotNum = {}
|
||||
for module in fit.modules:
|
||||
if module.isEmpty:
|
||||
continue
|
||||
|
||||
slot = module.slot
|
||||
if not slot in slotNum:
|
||||
slotNum[slot] = 0
|
||||
slotId = slotNum[slot]
|
||||
slotNum[slot] += 1
|
||||
hardware = doc.createElement("hardware")
|
||||
hardware.setAttribute("type", module.item.name)
|
||||
slotName = Slot.getName(slot).lower()
|
||||
slotName = slotName if slotName != "high" else "hi"
|
||||
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)
|
||||
hardware.setAttribute("slot", "drone bay")
|
||||
hardware.setAttribute("type", drone.item.name)
|
||||
fitting.appendChild(hardware)
|
||||
|
||||
for cargo in fit.cargo:
|
||||
if not cargo.item.name in charges:
|
||||
charges[cargo.item.name] = 0
|
||||
charges[cargo.item.name] += cargo.amount
|
||||
|
||||
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)
|
||||
|
||||
return doc.toprettyxml()
|
||||
Reference in New Issue
Block a user