Move out DNA format into separate file
This commit is contained in:
176
service/port/dna.py
Normal file
176
service/port/dna.py
Normal file
@@ -0,0 +1,176 @@
|
|||||||
|
# =============================================================================
|
||||||
|
# Copyright (C) 2014 Ryan Holmes
|
||||||
|
#
|
||||||
|
# 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
|
||||||
|
from collections import OrderedDict
|
||||||
|
|
||||||
|
from logbook import Logger
|
||||||
|
|
||||||
|
from eos.saveddata.cargo import Cargo
|
||||||
|
from eos.saveddata.citadel import Citadel
|
||||||
|
from eos.saveddata.drone import Drone
|
||||||
|
from eos.saveddata.fighter import Fighter
|
||||||
|
from eos.saveddata.fit import Fit
|
||||||
|
from eos.saveddata.module import Module, State, Slot
|
||||||
|
from eos.saveddata.ship import Ship
|
||||||
|
from service.fit import Fit as svcFit
|
||||||
|
from service.market import Market
|
||||||
|
|
||||||
|
|
||||||
|
pyfalog = Logger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
def importDna(string):
|
||||||
|
sMkt = Market.getInstance()
|
||||||
|
|
||||||
|
ids = list(map(int, re.findall(r'\d+', string)))
|
||||||
|
for id_ in ids:
|
||||||
|
try:
|
||||||
|
try:
|
||||||
|
try:
|
||||||
|
Ship(sMkt.getItem(sMkt.getItem(id_)))
|
||||||
|
except ValueError:
|
||||||
|
Citadel(sMkt.getItem(sMkt.getItem(id_)))
|
||||||
|
except ValueError:
|
||||||
|
Citadel(sMkt.getItem(id_))
|
||||||
|
string = string[string.index(str(id_)):]
|
||||||
|
break
|
||||||
|
except:
|
||||||
|
pyfalog.warning("Exception caught in importDna")
|
||||||
|
pass
|
||||||
|
string = string[:string.index("::") + 2]
|
||||||
|
info = string.split(":")
|
||||||
|
|
||||||
|
f = Fit()
|
||||||
|
try:
|
||||||
|
try:
|
||||||
|
f.ship = Ship(sMkt.getItem(int(info[0])))
|
||||||
|
except ValueError:
|
||||||
|
f.ship = Citadel(sMkt.getItem(int(info[0])))
|
||||||
|
f.name = "{0} - DNA Imported".format(f.ship.item.name)
|
||||||
|
except UnicodeEncodeError:
|
||||||
|
def logtransform(s_):
|
||||||
|
if len(s_) > 10:
|
||||||
|
return s_[:10] + "..."
|
||||||
|
return s_
|
||||||
|
|
||||||
|
pyfalog.exception("Couldn't import ship data {0}", [logtransform(s) for s in info])
|
||||||
|
return None
|
||||||
|
|
||||||
|
moduleList = []
|
||||||
|
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 == "Fighter":
|
||||||
|
ft = Fighter(item)
|
||||||
|
ft.amount = int(amount) if ft.amount <= ft.fighterSquadronMaxSize else ft.fighterSquadronMaxSize
|
||||||
|
if ft.fits(f):
|
||||||
|
f.fighters.append(ft)
|
||||||
|
elif item.category.name == "Charge":
|
||||||
|
c = Cargo(item)
|
||||||
|
c.amount = int(amount)
|
||||||
|
f.cargo.append(c)
|
||||||
|
else:
|
||||||
|
for i in range(int(amount)):
|
||||||
|
try:
|
||||||
|
m = Module(item)
|
||||||
|
except:
|
||||||
|
pyfalog.warning("Exception caught in importDna")
|
||||||
|
continue
|
||||||
|
# Add subsystems before modules to make sure T3 cruisers have subsystems installed
|
||||||
|
if item.category.name == "Subsystem":
|
||||||
|
if m.fits(f):
|
||||||
|
f.modules.append(m)
|
||||||
|
else:
|
||||||
|
m.owner = f
|
||||||
|
if m.isValidState(State.ACTIVE):
|
||||||
|
m.state = State.ACTIVE
|
||||||
|
moduleList.append(m)
|
||||||
|
|
||||||
|
# Recalc to get slot numbers correct for T3 cruisers
|
||||||
|
svcFit.getInstance().recalc(f)
|
||||||
|
|
||||||
|
for module in moduleList:
|
||||||
|
if module.fits(f):
|
||||||
|
module.owner = f
|
||||||
|
if module.isValidState(State.ACTIVE):
|
||||||
|
module.state = State.ACTIVE
|
||||||
|
f.modules.append(module)
|
||||||
|
|
||||||
|
return f
|
||||||
|
|
||||||
|
|
||||||
|
def exportDna(fit):
|
||||||
|
dna = str(fit.shipID)
|
||||||
|
subsystems = [] # EVE cares which order you put these in
|
||||||
|
mods = OrderedDict()
|
||||||
|
charges = OrderedDict()
|
||||||
|
sFit = svcFit.getInstance()
|
||||||
|
for mod in fit.modules:
|
||||||
|
if not mod.isEmpty:
|
||||||
|
if mod.slot == Slot.SUBSYSTEM:
|
||||||
|
subsystems.append(mod)
|
||||||
|
continue
|
||||||
|
if mod.itemID not in mods:
|
||||||
|
mods[mod.itemID] = 0
|
||||||
|
mods[mod.itemID] += 1
|
||||||
|
|
||||||
|
if mod.charge and sFit.serviceFittingOptions["exportCharges"]:
|
||||||
|
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")):
|
||||||
|
dna += ":{0};1".format(subsystem.itemID)
|
||||||
|
|
||||||
|
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 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)
|
||||||
|
|
||||||
|
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 cargo.item.ID not 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 + "::"
|
||||||
@@ -46,12 +46,11 @@ from eos.saveddata.citadel import Citadel
|
|||||||
from eos.saveddata.fit import Fit, ImplantLocation
|
from eos.saveddata.fit import Fit, ImplantLocation
|
||||||
from service.market import Market
|
from service.market import Market
|
||||||
from utils.strfunctions import sequential_rep, replace_ltgt
|
from utils.strfunctions import sequential_rep, replace_ltgt
|
||||||
from abc import ABCMeta, abstractmethod
|
|
||||||
|
|
||||||
from service.esi import Esi
|
from service.esi import Esi
|
||||||
|
from service.port.dna import exportDna, importDna
|
||||||
from service.port.eft import EftPort, SLOT_ORDER as EFT_SLOT_ORDER
|
from service.port.eft import EftPort, SLOT_ORDER as EFT_SLOT_ORDER
|
||||||
from service.port.shared import processing_notify
|
from service.port.shared import IPortUser, UserCancelException, processing_notify
|
||||||
from collections import OrderedDict
|
|
||||||
|
|
||||||
|
|
||||||
class ESIExportException(Exception):
|
class ESIExportException(Exception):
|
||||||
@@ -165,50 +164,6 @@ def _resolve_module(hardware, sMkt, b_localized):
|
|||||||
return item
|
return item
|
||||||
|
|
||||||
|
|
||||||
class UserCancelException(Exception):
|
|
||||||
"""when user cancel on port processing."""
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
class IPortUser(metaclass=ABCMeta):
|
|
||||||
|
|
||||||
ID_PULSE = 1
|
|
||||||
# Pulse the progress bar
|
|
||||||
ID_UPDATE = ID_PULSE << 1
|
|
||||||
# Replace message with data: update messate
|
|
||||||
ID_DONE = ID_PULSE << 2
|
|
||||||
# open fits: import process done
|
|
||||||
ID_ERROR = ID_PULSE << 3
|
|
||||||
# display error: raise some error
|
|
||||||
|
|
||||||
PROCESS_IMPORT = ID_PULSE << 4
|
|
||||||
# means import process.
|
|
||||||
PROCESS_EXPORT = ID_PULSE << 5
|
|
||||||
# means import process.
|
|
||||||
|
|
||||||
@abstractmethod
|
|
||||||
def on_port_processing(self, action, data=None):
|
|
||||||
"""
|
|
||||||
While importing fits from file, the logic calls back to this function to
|
|
||||||
update progress bar to show activity. XML files can contain multiple
|
|
||||||
ships with multiple fits, whereas EFT cfg files contain many fits of
|
|
||||||
a single ship. When iterating through the files, we update the message
|
|
||||||
when we start a new file, and then Pulse the progress bar with every fit
|
|
||||||
that is processed.
|
|
||||||
|
|
||||||
action : a flag that lets us know how to deal with :data
|
|
||||||
None: Pulse the progress bar
|
|
||||||
1: Replace message with data
|
|
||||||
other: Close dialog and handle based on :action (-1 open fits, -2 display error)
|
|
||||||
"""
|
|
||||||
|
|
||||||
"""return: True is continue process, False is cancel."""
|
|
||||||
pass
|
|
||||||
|
|
||||||
def on_port_process_start(self):
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
class Port(object):
|
class Port(object):
|
||||||
"""Service which houses all import/export format functions"""
|
"""Service which houses all import/export format functions"""
|
||||||
instance = None
|
instance = None
|
||||||
@@ -272,6 +227,7 @@ class Port(object):
|
|||||||
success, result = Port.importFitFromFiles(paths, iportuser)
|
success, result = Port.importFitFromFiles(paths, iportuser)
|
||||||
flag = IPortUser.ID_ERROR if not success else IPortUser.ID_DONE
|
flag = IPortUser.ID_ERROR if not success else IPortUser.ID_DONE
|
||||||
iportuser.on_port_processing(IPortUser.PROCESS_IMPORT | flag, result)
|
iportuser.on_port_processing(IPortUser.PROCESS_IMPORT | flag, result)
|
||||||
|
|
||||||
threading.Thread(
|
threading.Thread(
|
||||||
target=importFitsFromFileWorkerFunc,
|
target=importFitsFromFileWorkerFunc,
|
||||||
args=(paths, iportuser)
|
args=(paths, iportuser)
|
||||||
@@ -548,100 +504,6 @@ class Port(object):
|
|||||||
|
|
||||||
return fitobj
|
return fitobj
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def importDna(string):
|
|
||||||
sMkt = Market.getInstance()
|
|
||||||
|
|
||||||
ids = list(map(int, re.findall(r'\d+', string)))
|
|
||||||
for id_ in ids:
|
|
||||||
try:
|
|
||||||
try:
|
|
||||||
try:
|
|
||||||
Ship(sMkt.getItem(sMkt.getItem(id_)))
|
|
||||||
except ValueError:
|
|
||||||
Citadel(sMkt.getItem(sMkt.getItem(id_)))
|
|
||||||
except ValueError:
|
|
||||||
Citadel(sMkt.getItem(id_))
|
|
||||||
string = string[string.index(str(id_)):]
|
|
||||||
break
|
|
||||||
except:
|
|
||||||
pyfalog.warning("Exception caught in importDna")
|
|
||||||
pass
|
|
||||||
string = string[:string.index("::") + 2]
|
|
||||||
info = string.split(":")
|
|
||||||
|
|
||||||
f = Fit()
|
|
||||||
try:
|
|
||||||
try:
|
|
||||||
f.ship = Ship(sMkt.getItem(int(info[0])))
|
|
||||||
except ValueError:
|
|
||||||
f.ship = Citadel(sMkt.getItem(int(info[0])))
|
|
||||||
f.name = "{0} - DNA Imported".format(f.ship.item.name)
|
|
||||||
except UnicodeEncodeError:
|
|
||||||
def logtransform(s_):
|
|
||||||
if len(s_) > 10:
|
|
||||||
return s_[:10] + "..."
|
|
||||||
return s_
|
|
||||||
|
|
||||||
pyfalog.exception("Couldn't import ship data {0}", [logtransform(s) for s in info])
|
|
||||||
return None
|
|
||||||
|
|
||||||
moduleList = []
|
|
||||||
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 == "Fighter":
|
|
||||||
ft = Fighter(item)
|
|
||||||
ft.amount = int(amount) if ft.amount <= ft.fighterSquadronMaxSize else ft.fighterSquadronMaxSize
|
|
||||||
if ft.fits(f):
|
|
||||||
f.fighters.append(ft)
|
|
||||||
elif item.category.name == "Charge":
|
|
||||||
c = Cargo(item)
|
|
||||||
c.amount = int(amount)
|
|
||||||
f.cargo.append(c)
|
|
||||||
else:
|
|
||||||
for i in range(int(amount)):
|
|
||||||
try:
|
|
||||||
m = Module(item)
|
|
||||||
except:
|
|
||||||
pyfalog.warning("Exception caught in importDna")
|
|
||||||
continue
|
|
||||||
# Add subsystems before modules to make sure T3 cruisers have subsystems installed
|
|
||||||
if item.category.name == "Subsystem":
|
|
||||||
if m.fits(f):
|
|
||||||
f.modules.append(m)
|
|
||||||
else:
|
|
||||||
m.owner = f
|
|
||||||
if m.isValidState(State.ACTIVE):
|
|
||||||
m.state = State.ACTIVE
|
|
||||||
moduleList.append(m)
|
|
||||||
|
|
||||||
# Recalc to get slot numbers correct for T3 cruisers
|
|
||||||
svcFit.getInstance().recalc(f)
|
|
||||||
|
|
||||||
for module in moduleList:
|
|
||||||
if module.fits(f):
|
|
||||||
module.owner = f
|
|
||||||
if module.isValidState(State.ACTIVE):
|
|
||||||
module.state = State.ACTIVE
|
|
||||||
f.modules.append(module)
|
|
||||||
|
|
||||||
return f
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def importEft(eftString):
|
|
||||||
return EftPort.importEft(eftString)
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def importEftCfg(shipname, contents, iportuser=None):
|
|
||||||
return EftPort.importEftCfg(shipname, contents, iportuser)
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def importXml(text, iportuser=None):
|
def importXml(text, iportuser=None):
|
||||||
# type: (str, IPortUser) -> list[eos.saveddata.fit.Fit]
|
# type: (str, IPortUser) -> list[eos.saveddata.fit.Fit]
|
||||||
@@ -738,63 +600,6 @@ class Port(object):
|
|||||||
|
|
||||||
return fit_list
|
return fit_list
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def exportEft(cls, fit, options):
|
|
||||||
return EftPort.exportEft(fit, options)
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def exportDna(fit):
|
|
||||||
dna = str(fit.shipID)
|
|
||||||
subsystems = [] # EVE cares which order you put these in
|
|
||||||
mods = OrderedDict()
|
|
||||||
charges = OrderedDict()
|
|
||||||
sFit = svcFit.getInstance()
|
|
||||||
for mod in fit.modules:
|
|
||||||
if not mod.isEmpty:
|
|
||||||
if mod.slot == Slot.SUBSYSTEM:
|
|
||||||
subsystems.append(mod)
|
|
||||||
continue
|
|
||||||
if mod.itemID not in mods:
|
|
||||||
mods[mod.itemID] = 0
|
|
||||||
mods[mod.itemID] += 1
|
|
||||||
|
|
||||||
if mod.charge and sFit.serviceFittingOptions["exportCharges"]:
|
|
||||||
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")):
|
|
||||||
dna += ":{0};1".format(subsystem.itemID)
|
|
||||||
|
|
||||||
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 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)
|
|
||||||
|
|
||||||
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 cargo.item.ID not 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 + "::"
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def exportXml(iportuser=None, *fits):
|
def exportXml(iportuser=None, *fits):
|
||||||
doc = xml.dom.minidom.Document()
|
doc = xml.dom.minidom.Document()
|
||||||
@@ -944,3 +749,25 @@ class Port(object):
|
|||||||
export = export[:-1]
|
export = export[:-1]
|
||||||
|
|
||||||
return export
|
return export
|
||||||
|
|
||||||
|
# EFT-related methods
|
||||||
|
@staticmethod
|
||||||
|
def importEft(eftString):
|
||||||
|
return EftPort.importEft(eftString)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def importEftCfg(shipname, contents, iportuser=None):
|
||||||
|
return EftPort.importEftCfg(shipname, contents, iportuser)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def exportEft(cls, fit, options):
|
||||||
|
return EftPort.exportEft(fit, options)
|
||||||
|
|
||||||
|
# DNA-related methods
|
||||||
|
@staticmethod
|
||||||
|
def importDna(string):
|
||||||
|
return importDna(string)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def exportDna(fit):
|
||||||
|
return exportDna(fit)
|
||||||
|
|||||||
@@ -56,8 +56,9 @@ class IPortUser(metaclass=ABCMeta):
|
|||||||
None: Pulse the progress bar
|
None: Pulse the progress bar
|
||||||
1: Replace message with data
|
1: Replace message with data
|
||||||
other: Close dialog and handle based on :action (-1 open fits, -2 display error)
|
other: Close dialog and handle based on :action (-1 open fits, -2 display error)
|
||||||
return: True is continue process, False is cancel.
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
"""return: True is continue process, False is cancel."""
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def on_port_process_start(self):
|
def on_port_process_start(self):
|
||||||
|
|||||||
Reference in New Issue
Block a user