diff --git a/_development/helpers.py b/_development/helpers.py
index 8af8f9733..ad1447371 100644
--- a/_development/helpers.py
+++ b/_development/helpers.py
@@ -100,8 +100,8 @@ def DBInMemory():
import eos.db
# Output debug info to help us troubleshoot Travis
- print((eos.db.saveddata_engine))
- print((eos.db.gamedata_engine))
+ print(eos.db.saveddata_engine)
+ print(eos.db.gamedata_engine)
helper = {
'config': eos.config,
diff --git a/_development/helpers_fits.py b/_development/helpers_fits.py
index a0506827d..347b0622b 100644
--- a/_development/helpers_fits.py
+++ b/_development/helpers_fits.py
@@ -1,7 +1,6 @@
import pytest
# noinspection PyPackageRequirements
-from _development.helpers import DBInMemory as DB, Gamedata, Saveddata
# noinspection PyShadowingNames
diff --git a/_development/helpers_items.py b/_development/helpers_items.py
index 7ae722e23..d77adb026 100644
--- a/_development/helpers_items.py
+++ b/_development/helpers_items.py
@@ -1,7 +1,6 @@
import pytest
# noinspection PyPackageRequirements
-from _development.helpers import DBInMemory as DB, Gamedata, Saveddata
# noinspection PyShadowingNames
diff --git a/config.py b/config.py
index d10dda270..7b366a209 100644
--- a/config.py
+++ b/config.py
@@ -24,7 +24,7 @@ saveInRoot = False
# Version data
-version = "2.4.0"
+version = "2.5.0b1"
tag = "Stable"
expansionName = "YC120.8"
expansionVersion = "1.0"
@@ -229,20 +229,6 @@ def defLogging():
)
])
- with logging_setup.threadbound():
-
- # Output all stdout (print) messages as warnings
- try:
- sys.stdout = LoggerWriter(pyfalog.warning)
- except:
- pyfalog.critical("Cannot redirect. Continuing without writing stdout to log.")
-
- # Output all stderr (stacktrace) messages as critical
- try:
- sys.stderr = LoggerWriter(pyfalog.critical)
- except:
- pyfalog.critical("Cannot redirect. Continuing without writing stderr to log.")
-
class LoggerWriter(object):
def __init__(self, level):
diff --git a/dist_assets/win/dist.py b/dist_assets/win/dist.py
index a34ed3140..ec6da3928 100644
--- a/dist_assets/win/dist.py
+++ b/dist_assets/win/dist.py
@@ -38,7 +38,7 @@ call([
iscc,
os.path.join(os.getcwd(), "dist_assets", "win", "pyfa-setup.iss"),
"/dMyAppVersion=%s" % (config['version']),
- "/dMyAppExpansion=%s" % (expansion),
+ "/dMyAppExpansion=%s" % expansion,
"/dMyAppDir=%s" % source,
"/dMyOutputDir=%s" % os.path.join(os.getcwd(), "dist"),
"/dMyOutputFile=%s" % fileName]) # stdout=devnull, stderr=devnull
diff --git a/eos/db/gamedata/category.py b/eos/db/gamedata/category.py
index c167cf1df..502f26c22 100644
--- a/eos/db/gamedata/category.py
+++ b/eos/db/gamedata/category.py
@@ -17,8 +17,8 @@
# along with eos. If not, see .
# ===============================================================================
-from sqlalchemy import Column, String, Integer, ForeignKey, Boolean, Table
-from sqlalchemy.orm import relation, mapper, synonym, deferred
+from sqlalchemy import Boolean, Column, Integer, String, Table
+from sqlalchemy.orm import deferred, mapper, synonym
from eos.db import gamedata_meta
from eos.gamedata import Category
diff --git a/eos/db/gamedata/item.py b/eos/db/gamedata/item.py
index fba3e9820..df7508e43 100644
--- a/eos/db/gamedata/item.py
+++ b/eos/db/gamedata/item.py
@@ -17,15 +17,15 @@
# along with eos. If not, see .
# ===============================================================================
-from sqlalchemy import Column, String, Integer, Boolean, ForeignKey, Table, Float
+from sqlalchemy import Boolean, Column, Float, ForeignKey, Integer, String, Table
from sqlalchemy.ext.associationproxy import association_proxy
-from sqlalchemy.orm import relation, mapper, synonym, deferred, backref
+from sqlalchemy.orm import backref, deferred, mapper, relation, synonym
from sqlalchemy.orm.collections import attribute_mapped_collection
-from eos.db.gamedata.effect import typeeffects_table
from eos.db import gamedata_meta
-from eos.gamedata import Attribute, Effect, Group, Item, MetaType, Traits, DynamicItemItem, DynamicItem
-from eos.db.gamedata.dynamicAttributes import dynamicApplicable_table, dynamic_table
+from eos.db.gamedata.dynamicAttributes import dynamicApplicable_table
+from eos.db.gamedata.effect import typeeffects_table
+from eos.gamedata import Attribute, DynamicItem, Effect, Group, Item, MetaType, Traits
items_table = Table("invtypes", gamedata_meta,
Column("typeID", Integer, primary_key=True),
diff --git a/eos/db/gamedata/queries.py b/eos/db/gamedata/queries.py
index 90e757788..a026704cb 100644
--- a/eos/db/gamedata/queries.py
+++ b/eos/db/gamedata/queries.py
@@ -17,16 +17,16 @@
# along with eos. If not, see .
# ===============================================================================
-from sqlalchemy.orm import join, exc, aliased, joinedload, subqueryload
-from sqlalchemy.sql import and_, or_, select
from sqlalchemy.inspection import inspect
+from sqlalchemy.orm import aliased, exc, join
+from sqlalchemy.sql import and_, or_, select
import eos.config
from eos.db import gamedata_session
-from eos.db.gamedata.metaGroup import metatypes_table, items_table
from eos.db.gamedata.group import groups_table
+from eos.db.gamedata.metaGroup import items_table, metatypes_table
from eos.db.util import processEager, processWhere
-from eos.gamedata import AlphaClone, Attribute, Category, Group, Item, MarketGroup, MetaGroup, AttributeInfo, MetaData, DynamicItem
+from eos.gamedata import AlphaClone, Attribute, AttributeInfo, Category, DynamicItem, Group, Item, MarketGroup, MetaData, MetaGroup
cache = {}
configVal = getattr(eos.config, "gamedataCache", None)
@@ -396,6 +396,21 @@ def getAbyssalTypes():
return set([r.resultingTypeID for r in gamedata_session.query(DynamicItem.resultingTypeID).distinct()])
+@cachedQuery(1, "itemID")
+def getDynamicItem(itemID, eager=None):
+ try:
+ if isinstance(itemID, int):
+ if eager is None:
+ result = gamedata_session.query(DynamicItem).filter(DynamicItem.ID == itemID).one()
+ else:
+ result = gamedata_session.query(DynamicItem).options(*processEager(eager)).filter(DynamicItem.ID == itemID).one()
+ else:
+ raise TypeError("Need integer as argument")
+ except exc.NoResultFound:
+ result = None
+ return result
+
+
def getRequiredFor(itemID, attrMapping):
Attribute1 = aliased(Attribute)
Attribute2 = aliased(Attribute)
diff --git a/eos/db/saveddata/fit.py b/eos/db/saveddata/fit.py
index 0ae4509e2..dbee2c622 100644
--- a/eos/db/saveddata/fit.py
+++ b/eos/db/saveddata/fit.py
@@ -17,33 +17,32 @@
# along with eos. If not, see .
# ===============================================================================
-from sqlalchemy.ext.associationproxy import association_proxy
-from sqlalchemy.orm.collections import attribute_mapped_collection
-from sqlalchemy.sql import and_
-from sqlalchemy.orm import relation, reconstructor, mapper, relationship
-from sqlalchemy import ForeignKey, Column, Integer, String, Table, Boolean, DateTime
import datetime
-from eos.db import saveddata_meta
-from eos.db import saveddata_session
+from sqlalchemy import Boolean, Column, DateTime, ForeignKey, Integer, String, Table
+from sqlalchemy.ext.associationproxy import association_proxy
+from sqlalchemy.orm import mapper, reconstructor, relation, relationship
+from sqlalchemy.orm.collections import attribute_mapped_collection
+from sqlalchemy.sql import and_
+
+from eos.db import saveddata_meta, saveddata_session
from eos.db.saveddata.cargo import cargo_table
from eos.db.saveddata.drone import drones_table
from eos.db.saveddata.fighter import fighters_table
from eos.db.saveddata.implant import fitImplants_table
from eos.db.saveddata.module import modules_table
-from eos.effectHandlerHelpers import HandledModuleList, HandledImplantBoosterList, HandledProjectedModList, \
- HandledDroneCargoList, HandledProjectedDroneList
-from eos.saveddata.implant import Implant
-from eos.saveddata.character import Character
-from eos.saveddata.user import User
-from eos.saveddata.fighter import Fighter
-from eos.saveddata.fit import Fit as es_Fit, ImplantLocation
-from eos.saveddata.drone import Drone
+from eos.effectHandlerHelpers import HandledDroneCargoList, HandledImplantBoosterList, HandledModuleList, HandledProjectedDroneList, HandledProjectedModList
from eos.saveddata.booster import Booster
-from eos.saveddata.module import Module
from eos.saveddata.cargo import Cargo
+from eos.saveddata.character import Character
from eos.saveddata.damagePattern import DamagePattern
+from eos.saveddata.drone import Drone
+from eos.saveddata.fighter import Fighter
+from eos.saveddata.fit import Fit as es_Fit
+from eos.saveddata.implant import Implant
+from eos.saveddata.module import Module
from eos.saveddata.targetResists import TargetResists
+from eos.saveddata.user import User
fits_table = Table("fits", saveddata_meta,
Column("ID", Integer, primary_key=True),
@@ -132,13 +131,13 @@ class CommandFit(object):
)
-es_Fit._Fit__projectedFits = association_proxy(
+es_Fit.projectedFitDict = association_proxy(
"victimOf", # look at the victimOf association...
"source_fit", # .. and return the source fits
creator=lambda sourceID, source_fit: ProjectedFit(sourceID, source_fit)
)
-es_Fit._Fit__commandFits = association_proxy(
+es_Fit.commandFitDict = association_proxy(
"boostedOf", # look at the boostedOf association...
"booster_fit", # .. and return the booster fit
creator=lambda boosterID, booster_fit: CommandFit(boosterID, booster_fit)
diff --git a/eos/db/saveddata/mutator.py b/eos/db/saveddata/mutator.py
index 11b595025..1ba81e097 100644
--- a/eos/db/saveddata/mutator.py
+++ b/eos/db/saveddata/mutator.py
@@ -17,10 +17,11 @@
# along with eos. If not, see .
# ===============================================================================
-from sqlalchemy import Table, Column, Integer, ForeignKey, Boolean, DateTime, Float
-from sqlalchemy.orm import mapper
import datetime
+from sqlalchemy import Column, DateTime, Float, ForeignKey, Integer, Table
+from sqlalchemy.orm import mapper
+
from eos.db import saveddata_meta
from eos.saveddata.mutator import Mutator
diff --git a/eos/effectHandlerHelpers.py b/eos/effectHandlerHelpers.py
index f890ae0ed..3e9350b66 100644
--- a/eos/effectHandlerHelpers.py
+++ b/eos/effectHandlerHelpers.py
@@ -18,6 +18,7 @@
# ===============================================================================
from logbook import Logger
+from utils.deprecated import deprecated
pyfalog = Logger(__name__)
@@ -113,6 +114,7 @@ class HandledList(list):
class HandledModuleList(HandledList):
+
def append(self, mod):
emptyPosition = float("Inf")
for i in range(len(self)):
@@ -130,6 +132,9 @@ class HandledModuleList(HandledList):
self.remove(mod)
return
+ self.appendIgnoreEmpty(mod)
+
+ def appendIgnoreEmpty(self, mod):
mod.position = len(self)
HandledList.append(self, mod)
if mod.isInvalid:
@@ -163,6 +168,7 @@ class HandledModuleList(HandledList):
mod.position = index
self[index] = mod
+ @deprecated
def freeSlot(self, slot):
for i in range(len(self)):
mod = self[i]
@@ -195,14 +201,20 @@ class HandledImplantBoosterList(HandledList):
self.remove(thing)
return
+ self.makeRoom(thing)
+ HandledList.append(self, thing)
+
+ def makeRoom(self, thing):
# if needed, remove booster that was occupying slot
oldObj = next((m for m in self if m.slot == thing.slot), None)
if oldObj:
- pyfalog.info("Slot {0} occupied with {1}, replacing with {2}", thing.slot, oldObj.item.name, thing.item.name)
+ pyfalog.info("Slot {0} occupied with {1}, replacing with {2}", thing.slot, oldObj.item.name,
+ thing.item.name)
+ itemID = oldObj.itemID
oldObj.itemID = 0 # hack to remove from DB. See GH issue #324
self.remove(oldObj)
-
- HandledList.append(self, thing)
+ return itemID
+ return None
class HandledSsoCharacterList(list):
@@ -228,12 +240,7 @@ class HandledProjectedModList(HandledList):
isSystemEffect = proj.item.group.name == "Effect Beacon"
if isSystemEffect:
- # remove other system effects - only 1 per fit plz
- oldEffect = next((m for m in self if m.item.group.name == "Effect Beacon"), None)
-
- if oldEffect:
- pyfalog.info("System effect occupied with {0}, replacing with {1}", oldEffect.item.name, proj.item.name)
- self.remove(oldEffect)
+ self.makeRoom(proj)
HandledList.append(self, proj)
@@ -241,6 +248,16 @@ class HandledProjectedModList(HandledList):
if not proj.item.isType("projected") and not isSystemEffect:
self.remove(proj)
+ def makeRoom(self, proj):
+ # remove other system effects - only 1 per fit plz
+ oldEffect = next((m for m in self if m.item.group.name == "Effect Beacon"), None)
+
+ if oldEffect:
+ pyfalog.info("System effect occupied with {0}, replacing with {1}", oldEffect.item.name, proj.item.name)
+ self.remove(oldEffect)
+ return oldEffect.itemID
+ return None
+
class HandledProjectedDroneList(HandledDroneCargoList):
def append(self, proj):
diff --git a/eos/effects/modulebonuswarfarelinkarmor.py b/eos/effects/modulebonuswarfarelinkarmor.py
index be5843988..c7cf29ce3 100644
--- a/eos/effects/modulebonuswarfarelinkarmor.py
+++ b/eos/effects/modulebonuswarfarelinkarmor.py
@@ -3,14 +3,14 @@
# Used by:
# Variations of module: Armor Command Burst I (2 of 2)
-'''
+"""
Some documentation:
When the fit is calculated, we gather up all the gang effects and stick them onto the fit. We don't run the actual
effect yet, only give the fit details so that it can run the effect at a later time. We need to do this so that we can
only run the strongest effect. When we are done, one of the last things that we do with the fit is to loop through those
bonuses and actually run the effect. To do this, we have a special argument passed into the effect handler that tells it
which warfareBuffID to run (shouldn't need this right now, but better safe than sorry)
-'''
+"""
type = "active", "gang"
diff --git a/eos/gamedata.py b/eos/gamedata.py
index 7c7f85b8f..9c14f23ac 100644
--- a/eos/gamedata.py
+++ b/eos/gamedata.py
@@ -166,14 +166,14 @@ class Effect(EqBase):
t = t if isinstance(t, tuple) or t is None else (t,)
self.__type = t
- except (ImportError) as e:
+ except ImportError as e:
# Effect probably doesn't exist, so create a dummy effect and flag it with a warning.
self.__handler = effectDummy
self.__runTime = "normal"
self.__activeByDefault = True
self.__type = None
pyfalog.debug("ImportError generating handler: {0}", e)
- except (AttributeError) as e:
+ except AttributeError as e:
# Effect probably exists but there is an issue with it. Turn it into a dummy effect so we can continue, but flag it with an error.
self.__handler = effectDummy
self.__runTime = "normal"
@@ -476,6 +476,10 @@ class Item(EqBase):
def getAbyssalYypes(cls):
cls.ABYSSAL_TYPES = eos.db.getAbyssalTypes()
+ @property
+ def isCharge(self):
+ return self.category.name == "Charge"
+
def __repr__(self):
return "Item(ID={}, name={}) at {}".format(
self.ID, self.name, hex(id(self))
@@ -626,8 +630,8 @@ class Unit(EqBase):
def attributeIDCallback(v):
v = int(v)
if not v: # some attributes come through with a value of 0? See #1387
- return "%d" % (v)
- attribute = eos.db.getAttributeInfo(v, eager=("unit"))
+ return "%d" % v
+ attribute = eos.db.getAttributeInfo(v, eager="unit")
return "%s (%d)" % (attribute.name.capitalize(), v)
def TranslateValue(self, value):
diff --git a/eos/modifiedAttributeDict.py b/eos/modifiedAttributeDict.py
index 06d348b81..d660dd38b 100644
--- a/eos/modifiedAttributeDict.py
+++ b/eos/modifiedAttributeDict.py
@@ -34,10 +34,10 @@ class ItemAttrShortcut(object):
return return_value or default
def getBaseAttrValue(self, key, default=0):
- '''
+ """
Gets base value in this order:
Mutated value > override value > attribute value
- '''
+ """
return_value = self.itemModifiedAttributes.getOriginal(key)
return return_value or default
@@ -382,7 +382,7 @@ class ModifiedAttributeDict(collections.MutableMapping):
if resist:
afflictPenal += "r"
- self.__afflict(attributeName, "%s*" % (afflictPenal), multiplier, multiplier != 1)
+ self.__afflict(attributeName, "%s*" % afflictPenal, multiplier, multiplier != 1)
def boost(self, attributeName, boostFactor, skill=None, *args, **kwargs):
"""Boost value by some percentage"""
diff --git a/eos/saveddata/fighter.py b/eos/saveddata/fighter.py
index 694970343..8c515b945 100644
--- a/eos/saveddata/fighter.py
+++ b/eos/saveddata/fighter.py
@@ -53,6 +53,20 @@ class Fighter(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
self.build()
+ standardAttackActive = False
+ for ability in self.abilities:
+ if ability.effect.isImplemented and ability.effect.handlerName == 'fighterabilityattackm':
+ # Activate "standard attack" if available
+ ability.active = True
+ standardAttackActive = True
+ else:
+ # Activate all other abilities (Neut, Web, etc) except propmods if no standard attack is active
+ if ability.effect.isImplemented and \
+ standardAttackActive is False and \
+ ability.effect.handlerName != 'fighterabilitymicrowarpdrive' and \
+ ability.effect.handlerName != 'fighterabilityevasivemaneuvers':
+ ability.active = True
+
@reconstructor
def init(self):
"""Initialize a fighter from the database and validate"""
diff --git a/eos/saveddata/fit.py b/eos/saveddata/fit.py
index dc183d3c0..e00b522fd 100644
--- a/eos/saveddata/fit.py
+++ b/eos/saveddata/fit.py
@@ -35,7 +35,6 @@ from eos.saveddata.character import Character
from eos.saveddata.citadel import Citadel
from eos.saveddata.module import Module, State, Slot, Hardpoint
from logbook import Logger
-
pyfalog = Logger(__name__)
@@ -258,11 +257,11 @@ class Fit(object):
def projectedFits(self):
# only in extreme edge cases will the fit be invalid, but to be sure do
# not return them.
- return [fit for fit in list(self.__projectedFits.values()) if not fit.isInvalid]
+ return [fit for fit in list(self.projectedFitDict.values()) if not fit.isInvalid]
@property
def commandFits(self):
- return [fit for fit in list(self.__commandFits.values()) if not fit.isInvalid]
+ return [fit for fit in list(self.commandFitDict.values()) if not fit.isInvalid]
def getProjectionInfo(self, fitID):
return self.projectedOnto.get(fitID, None)
@@ -911,6 +910,9 @@ class Fit(object):
Fill this fit's module slots with enough dummy slots so that all slots are used.
This is mostly for making the life of gui's easier.
GUI's can call fill() and then stop caring about empty slots completely.
+
+ todo: want to get rid of using this from the gui/commands, and instead make it a more built-in feature within
+ recalc. Figure out a way to keep track of any changes to slot layout and call this automatically
"""
if self.ship is None:
return
@@ -1603,7 +1605,7 @@ class Fit(object):
eos.db.saveddata_session.refresh(fit)
for fit in self.commandFits:
- copy_ship.__commandFits[fit.ID] = fit
+ copy_ship.commandFitDict[fit.ID] = fit
forceUpdateSavedata(fit)
copyCommandInfo = fit.getCommandInfo(copy_ship.ID)
originalCommandInfo = fit.getCommandInfo(self.ID)
@@ -1611,7 +1613,7 @@ class Fit(object):
forceUpdateSavedata(fit)
for fit in self.projectedFits:
- copy_ship.__projectedFits[fit.ID] = fit
+ copy_ship.projectedFitDict[fit.ID] = fit
forceUpdateSavedata(fit)
copyProjectionInfo = fit.getProjectionInfo(copy_ship.ID)
originalProjectionInfo = fit.getProjectionInfo(self.ID)
diff --git a/eos/saveddata/module.py b/eos/saveddata/module.py
index d95afc5c6..57aacf101 100644
--- a/eos/saveddata/module.py
+++ b/eos/saveddata/module.py
@@ -17,16 +17,15 @@
# along with eos. If not, see .
# ===============================================================================
-from logbook import Logger
-from copy import deepcopy
-
-from sqlalchemy.orm import validates, reconstructor
from math import floor
+from logbook import Logger
+from sqlalchemy.orm import reconstructor, validates
+
import eos.db
-from eos.effectHandlerHelpers import HandledItem, HandledCharge
+from eos.effectHandlerHelpers import HandledCharge, HandledItem
from eos.enum import Enum
-from eos.modifiedAttributeDict import ModifiedAttributeDict, ItemAttrShortcut, ChargeAttrShortcut
+from eos.modifiedAttributeDict import ChargeAttrShortcut, ItemAttrShortcut, ModifiedAttributeDict
from eos.saveddata.citadel import Citadel
from eos.saveddata.mutator import Mutator
@@ -64,6 +63,30 @@ class Slot(Enum):
FS_HEAVY = 15
+ProjectedMap = {
+ State.OVERHEATED: State.ACTIVE,
+ State.ACTIVE: State.OFFLINE,
+ State.OFFLINE: State.ACTIVE,
+ State.ONLINE: State.ACTIVE # Just in case
+}
+
+
+# Old state : New State
+LocalMap = {
+ State.OVERHEATED: State.ACTIVE,
+ State.ACTIVE: State.ONLINE,
+ State.OFFLINE: State.ONLINE,
+ State.ONLINE: State.ACTIVE
+}
+
+
+# For system effects. They should only ever be online or offline
+ProjectedSystem = {
+ State.OFFLINE: State.ONLINE,
+ State.ONLINE: State.OFFLINE
+}
+
+
class Hardpoint(Enum):
NONE = 0
MISSILE = 1
@@ -626,7 +649,7 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
for i in range(5):
itemChargeGroup = self.getModifiedItemAttr('chargeGroup' + str(i), None)
if itemChargeGroup is not None:
- g = eos.db.getGroup(int(itemChargeGroup), eager=("items.attributes"))
+ g = eos.db.getGroup(int(itemChargeGroup), eager="items.attributes")
if g is None:
continue
for singleItem in g.items:
@@ -832,6 +855,36 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
else:
return 0
+ @staticmethod
+ def getProposedState(mod, click, proposedState=None):
+ # todo: instead of passing in module, make this a instanced function.
+ pyfalog.debug("Get proposed state for module.")
+ if mod.slot == Slot.SUBSYSTEM or mod.isEmpty:
+ return State.ONLINE
+
+ if mod.slot == Slot.SYSTEM:
+ transitionMap = ProjectedSystem
+ else:
+ transitionMap = ProjectedMap if mod.projected else LocalMap
+
+ currState = mod.state
+
+ if proposedState is not None:
+ state = proposedState
+ elif click == "right":
+ state = State.OVERHEATED
+ elif click == "ctrl":
+ state = State.OFFLINE
+ else:
+ state = transitionMap[currState]
+ if not mod.isValidState(state):
+ state = -1
+
+ if mod.isValidState(state):
+ return state
+ else:
+ return currState
+
def __deepcopy__(self, memo):
item = self.item
if item is None:
diff --git a/eos/saveddata/ship.py b/eos/saveddata/ship.py
index 1e1dd30d6..a7cbc15b7 100644
--- a/eos/saveddata/ship.py
+++ b/eos/saveddata/ship.py
@@ -131,7 +131,7 @@ class Ship(ItemAttrShortcut, HandledItem):
return None
items = []
- g = eos.db.getGroup("Ship Modifiers", eager=("items.attributes"))
+ g = eos.db.getGroup("Ship Modifiers", eager="items.attributes")
for item in g.items:
# Rely on name detection because race is not reliable
if item.name.lower().startswith(self.item.name.lower()):
diff --git a/gui/attribute_gauge.py b/gui/attribute_gauge.py
index 1057775f7..e275b2dc2 100644
--- a/gui/attribute_gauge.py
+++ b/gui/attribute_gauge.py
@@ -1,10 +1,13 @@
-import copy
import wx
import math
-from gui.utils import color as color_utils
-from gui.utils import draw, anim_effects
-from service.fit import Fit
+from gui.utils import anim_effects
+import math
+
+import wx
+
+from gui.utils import anim_effects
+
# todo: clean class up. Took from pyfa gauge, has a bunch of extra shit we don't need
diff --git a/gui/bitmap_loader.py b/gui/bitmap_loader.py
index 061e05a0c..deb0712f4 100644
--- a/gui/bitmap_loader.py
+++ b/gui/bitmap_loader.py
@@ -19,15 +19,14 @@
import io
import os.path
-import zipfile
from collections import OrderedDict
# noinspection PyPackageRequirements
import wx
+from logbook import Logger
import config
-from logbook import Logger
logging = Logger(__name__)
diff --git a/gui/builtinAdditionPanes/boosterView.py b/gui/builtinAdditionPanes/boosterView.py
index 8923f83de..11bac08ca 100644
--- a/gui/builtinAdditionPanes/boosterView.py
+++ b/gui/builtinAdditionPanes/boosterView.py
@@ -26,6 +26,7 @@ from gui.builtinViewColumns.state import State
from gui.contextMenu import ContextMenu
from gui.utils.staticHelpers import DragDropHelper
from service.fit import Fit
+import gui.fitCommands as cmd
class BoosterViewDrop(wx.DropTarget):
@@ -134,9 +135,7 @@ class BoosterView(d.Display):
event.Skip()
return
- trigger = sFit.addBooster(fitID, event.itemID)
- if trigger:
- wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=fitID))
+ if self.mainFrame.command.Submit(cmd.GuiAddBoosterCommand(fitID, event.itemID)):
self.mainFrame.additionsPane.select("Boosters")
event.Skip()
@@ -150,9 +149,7 @@ class BoosterView(d.Display):
def removeBooster(self, booster):
fitID = self.mainFrame.getActiveFit()
- sFit = Fit.getInstance()
- sFit.removeBooster(fitID, self.origional.index(booster))
- wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=fitID))
+ self.mainFrame.command.Submit(cmd.GuiRemoveBoosterCommand(fitID, self.origional.index(booster)))
def click(self, event):
event.Skip()
@@ -161,9 +158,7 @@ class BoosterView(d.Display):
col = self.getColumn(event.Position)
if col == self.getColIndex(State):
fitID = self.mainFrame.getActiveFit()
- sFit = Fit.getInstance()
- sFit.toggleBooster(fitID, row)
- wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=fitID))
+ self.mainFrame.command.Submit(cmd.GuiToggleBoosterCommand(fitID, row))
def scheduleMenu(self, event):
event.Skip()
diff --git a/gui/builtinAdditionPanes/cargoView.py b/gui/builtinAdditionPanes/cargoView.py
index a5b94f31b..8eb54875f 100644
--- a/gui/builtinAdditionPanes/cargoView.py
+++ b/gui/builtinAdditionPanes/cargoView.py
@@ -26,6 +26,7 @@ import gui.globalEvents as GE
from gui.utils.staticHelpers import DragDropHelper
from service.fit import Fit
from service.market import Market
+import gui.fitCommands as cmd
class CargoViewDrop(wx.DropTarget):
@@ -80,9 +81,7 @@ class CargoView(d.Display):
if data[0] == "fitting":
self.swapModule(x, y, int(data[1]))
elif data[0] == "market":
- sFit = Fit.getInstance()
- sFit.addCargo(self.mainFrame.getActiveFit(), int(data[1]), 1)
- wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.mainFrame.getActiveFit()))
+ self.mainFrame.command.Submit(cmd.GuiAddCargoCommand(self.mainFrame.getActiveFit(), int(data[1])))
def startDrag(self, event):
row = event.GetIndex()
@@ -127,18 +126,14 @@ class CargoView(d.Display):
if not result:
return
- if dstRow != -1: # we're swapping with cargo
- if mstate.cmdDown: # if copying, append to cargo
- sFit.addCargo(self.mainFrame.getActiveFit(), module.item.ID if not module.item.isAbyssal else module.baseItemID)
- else: # else, move / swap
- sFit.moveCargoToModule(self.mainFrame.getActiveFit(), module.position, dstRow)
- else: # dragging to blank spot, append
- sFit.addCargo(self.mainFrame.getActiveFit(), module.item.ID if not module.item.isAbyssal else module.baseItemID)
+ cargoPos = dstRow if dstRow > -1 else None
- if not mstate.cmdDown: # if not copying, remove module
- sFit.removeModule(self.mainFrame.getActiveFit(), module.position)
-
- wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.mainFrame.getActiveFit(), action="moddel", typeID=module.item.ID))
+ self.mainFrame.command.Submit(cmd.GuiModuleToCargoCommand(
+ self.mainFrame.getActiveFit(),
+ module.modPosition,
+ cargoPos,
+ mstate.cmdDown
+ ))
def fitChanged(self, event):
sFit = Fit.getInstance()
diff --git a/gui/builtinAdditionPanes/commandView.py b/gui/builtinAdditionPanes/commandView.py
index 2bfa700c9..ce73e06b8 100644
--- a/gui/builtinAdditionPanes/commandView.py
+++ b/gui/builtinAdditionPanes/commandView.py
@@ -30,6 +30,7 @@ from gui.builtinViewColumns.state import State
from gui.contextMenu import ContextMenu
from gui.utils.staticHelpers import DragDropHelper
from service.fit import Fit
+import gui.fitCommands as cmd
class DummyItem(object):
@@ -100,21 +101,16 @@ class CommandView(d.Display):
keycode = event.GetKeyCode()
if keycode == wx.WXK_DELETE or keycode == wx.WXK_NUMPAD_DELETE:
fitID = self.mainFrame.getActiveFit()
- sFit = Fit.getInstance()
row = self.GetFirstSelected()
if row != -1:
- sFit.removeCommand(fitID, self.get(row))
- wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=fitID))
+ self.mainFrame.command.Submit(cmd.GuiRemoveCommandCommand(fitID, self.get(row).ID))
def handleDrag(self, type, fitID):
# Those are drags coming from pyfa sources, NOT builtin wx drags
if type == "fit":
activeFit = self.mainFrame.getActiveFit()
if activeFit:
- sFit = Fit.getInstance()
- draggedFit = sFit.getFit(fitID)
- sFit.addCommandFit(activeFit, draggedFit)
- wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=activeFit))
+ self.mainFrame.command.Submit(cmd.GuiAddCommandCommand(activeFit, fitID))
def startDrag(self, event):
row = event.GetIndex()
@@ -190,9 +186,7 @@ class CommandView(d.Display):
col = self.getColumn(event.Position)
if col == self.getColIndex(State):
fitID = self.mainFrame.getActiveFit()
- sFit = Fit.getInstance()
- sFit.toggleCommandFit(fitID, item)
- wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=fitID))
+ self.mainFrame.command.Submit(cmd.GuiToggleCommandCommand(fitID, item.ID))
def scheduleMenu(self, event):
event.Skip()
@@ -224,8 +218,6 @@ class CommandView(d.Display):
col = self.getColumn(event.Position)
if col != self.getColIndex(State):
fitID = self.mainFrame.getActiveFit()
- sFit = Fit.getInstance()
thing = self.get(row)
if thing: # thing doesn't exist if it's the dummy value
- sFit.removeCommand(fitID, thing)
- wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=fitID))
+ self.mainFrame.command.Submit(cmd.GuiRemoveCommandCommand(fitID, thing.ID))
diff --git a/gui/builtinAdditionPanes/droneView.py b/gui/builtinAdditionPanes/droneView.py
index e9b893047..2918c5aa4 100644
--- a/gui/builtinAdditionPanes/droneView.py
+++ b/gui/builtinAdditionPanes/droneView.py
@@ -29,6 +29,7 @@ from gui.contextMenu import ContextMenu
from gui.utils.staticHelpers import DragDropHelper
from service.fit import Fit
from service.market import Market
+import gui.fitCommands as cmd
class DroneViewDrop(wx.DropTarget):
@@ -144,10 +145,12 @@ class DroneView(Display):
data[1] is typeID or index of data we want to manipulate
"""
if data[0] == "drone": # we want to merge drones
- srcRow = int(data[1])
- dstRow, _ = self.HitTest((x, y))
- if srcRow != -1 and dstRow != -1:
- self._merge(srcRow, dstRow)
+ pass
+ # remove merge functionality, if people complain in the next while, can add it back
+ # srcRow = int(data[1])
+ # dstRow, _ = self.HitTest((x, y))
+ # if srcRow != -1 and dstRow != -1:
+ # self._merge(srcRow, dstRow)
elif data[0] == "market":
wx.PostEvent(self.mainFrame, ItemSelected(itemID=int(data[1])))
@@ -213,9 +216,7 @@ class DroneView(Display):
event.Skip()
return
- trigger = sFit.addDrone(fitID, event.itemID)
- if trigger:
- wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=fitID))
+ if self.mainFrame.command.Submit(cmd.GuiAddDroneCommand(fitID, event.itemID)):
self.mainFrame.additionsPane.select("Drones")
event.Skip()
@@ -230,9 +231,7 @@ class DroneView(Display):
def removeDrone(self, drone):
fitID = self.mainFrame.getActiveFit()
- sFit = Fit.getInstance()
- sFit.removeDrone(fitID, self.original.index(drone))
- wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=fitID))
+ self.mainFrame.command.Submit(cmd.GuiRemoveDroneCommand(fitID, self.original.index(drone)))
def click(self, event):
event.Skip()
@@ -241,10 +240,8 @@ class DroneView(Display):
col = self.getColumn(event.Position)
if col == self.getColIndex(State):
fitID = self.mainFrame.getActiveFit()
- sFit = Fit.getInstance()
drone = self.drones[row]
- sFit.toggleDrone(fitID, self.original.index(drone))
- wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=fitID))
+ self.mainFrame.command.Submit(cmd.GuiToggleDroneCommand(fitID, self.original.index(drone)))
def scheduleMenu(self, event):
event.Skip()
diff --git a/gui/builtinAdditionPanes/fighterView.py b/gui/builtinAdditionPanes/fighterView.py
index 3292583fd..bf797ae15 100644
--- a/gui/builtinAdditionPanes/fighterView.py
+++ b/gui/builtinAdditionPanes/fighterView.py
@@ -30,6 +30,7 @@ from gui.contextMenu import ContextMenu
from gui.utils.staticHelpers import DragDropHelper
from service.fit import Fit
from service.market import Market
+import gui.fitCommands as cmd
class FighterViewDrop(wx.DropTarget):
@@ -269,11 +270,9 @@ class FighterDisplay(d.Display):
event.Skip()
def addItem(self, event):
- sFit = Fit.getInstance()
fitID = self.mainFrame.getActiveFit()
- trigger = sFit.addFighter(fitID, event.itemID)
- if trigger:
- wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=fitID))
+
+ if self.mainFrame.command.Submit(cmd.GuiAddFighterCommand(fitID, event.itemID)):
self.mainFrame.additionsPane.select("Fighters")
event.Skip()
@@ -288,9 +287,7 @@ class FighterDisplay(d.Display):
def removeFighter(self, fighter):
fitID = self.mainFrame.getActiveFit()
- sFit = Fit.getInstance()
- sFit.removeFighter(fitID, self.original.index(fighter))
- wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=fitID))
+ self.mainFrame.command.Submit(cmd.GuiRemoveFighterCommand(fitID, self.original.index(fighter)))
def click(self, event):
event.Skip()
@@ -299,10 +296,8 @@ class FighterDisplay(d.Display):
col = self.getColumn(event.Position)
if col == self.getColIndex(State):
fitID = self.mainFrame.getActiveFit()
- sFit = Fit.getInstance()
fighter = self.fighters[row]
- sFit.toggleFighter(fitID, self.original.index(fighter))
- wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=fitID))
+ self.mainFrame.command.Submit(cmd.GuiToggleFighterCommand(fitID, self.original.index(fighter)))
def scheduleMenu(self, event):
event.Skip()
diff --git a/gui/builtinAdditionPanes/implantView.py b/gui/builtinAdditionPanes/implantView.py
index b1d7bbe66..323b7c89b 100644
--- a/gui/builtinAdditionPanes/implantView.py
+++ b/gui/builtinAdditionPanes/implantView.py
@@ -28,6 +28,7 @@ import gui.globalEvents as GE
from eos.saveddata.fit import ImplantLocation
from service.fit import Fit
from service.market import Market
+import gui.fitCommands as cmd
class ImplantView(wx.Panel):
@@ -155,9 +156,7 @@ class ImplantDisplay(d.Display):
event.Skip()
return
- trigger = sFit.addImplant(fitID, event.itemID)
- if trigger:
- wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=fitID))
+ if self.mainFrame.command.Submit(cmd.GuiAddImplantCommand(fitID, event.itemID)):
self.mainFrame.additionsPane.select("Implants")
event.Skip()
@@ -175,10 +174,7 @@ class ImplantDisplay(d.Display):
def removeImplant(self, implant):
fitID = self.mainFrame.getActiveFit()
- sFit = Fit.getInstance()
-
- sFit.removeImplant(fitID, self.original.index(implant))
- wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=fitID))
+ self.mainFrame.command.Submit(cmd.GuiRemoveImplantCommand(fitID, self.original.index(implant)))
def click(self, event):
event.Skip()
@@ -192,9 +188,7 @@ class ImplantDisplay(d.Display):
col = self.getColumn(event.Position)
if col == self.getColIndex(State):
fitID = self.mainFrame.getActiveFit()
- sFit = Fit.getInstance()
- sFit.toggleImplant(fitID, row)
- wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=fitID))
+ self.mainFrame.command.Submit(cmd.GuiToggleImplantCommand(fitID, row))
def scheduleMenu(self, event):
event.Skip()
diff --git a/gui/builtinAdditionPanes/projectedView.py b/gui/builtinAdditionPanes/projectedView.py
index 24ca11f3a..09db4d57f 100644
--- a/gui/builtinAdditionPanes/projectedView.py
+++ b/gui/builtinAdditionPanes/projectedView.py
@@ -32,6 +32,7 @@ from gui.contextMenu import ContextMenu
from gui.utils.staticHelpers import DragDropHelper
from service.fit import Fit
from service.market import Market
+import gui.fitCommands as cmd
pyfalog = Logger(__name__)
@@ -100,21 +101,26 @@ class ProjectedView(d.Display):
data[1] is typeID or index of data we want to manipulate
"""
sFit = Fit.getInstance()
+ fitID = self.mainFrame.getActiveFit()
fit = sFit.getFit(self.mainFrame.getActiveFit())
if data[0] == "projected":
# if source is coming from projected, we are trying to combine drones.
- self.mergeDrones(x, y, int(data[1]))
+ pass
+ # removing merge functionality - if people complain about it, can add it back as a command
+ # self.mergeDrones(x, y, int(data[1]))
elif data[0] == "fitting":
dstRow, _ = self.HitTest((x, y))
# Gather module information to get position
module = fit.modules[int(data[1])]
- sFit.project(fit.ID, module)
- wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=fit.ID))
+ self.mainFrame.command.Submit(cmd.GuiAddProjectedCommand(fitID, module.itemID, 'item'))
+ # sFit.project(fit.ID, module)
+ # wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=fit.ID))
elif data[0] == "market":
- sFit = Fit.getInstance()
- sFit.project(fit.ID, int(data[1]))
- wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=fit.ID))
+ # sFit = Fit.getInstance()
+ self.mainFrame.command.Submit(cmd.GuiAddProjectedCommand(fitID, int(data[1]), 'item'))
+ # sFit.project(fit.ID, int(data[1]))
+ # wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=fit.ID))
def kbEvent(self, event):
keycode = event.GetKeyCode()
@@ -131,10 +137,7 @@ class ProjectedView(d.Display):
if type == "fit":
activeFit = self.mainFrame.getActiveFit()
if activeFit:
- sFit = Fit.getInstance()
- draggedFit = sFit.getFit(fitID)
- sFit.project(activeFit, draggedFit)
- wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=activeFit))
+ self.mainFrame.command.Submit(cmd.GuiAddProjectedCommand(activeFit, fitID, 'fit'))
def startDrag(self, event):
row = event.GetIndex()
@@ -319,8 +322,6 @@ class ProjectedView(d.Display):
col = self.getColumn(event.Position)
if col != self.getColIndex(State):
fitID = self.mainFrame.getActiveFit()
- sFit = Fit.getInstance()
thing = self.get(row)
if thing: # thing doesn't exist if it's the dummy value
- sFit.removeProjected(fitID, thing)
- wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=fitID))
+ self.mainFrame.command.Submit(cmd.GuiRemoveProjectedCommand(fitID, thing))
diff --git a/gui/builtinContextMenus/amount.py b/gui/builtinContextMenus/amount.py
index ed23e606a..3a70896f9 100644
--- a/gui/builtinContextMenus/amount.py
+++ b/gui/builtinContextMenus/amount.py
@@ -6,9 +6,11 @@ import gui.globalEvents as GE
import wx
import re
from service.fit import Fit
+from eos.saveddata.drone import Drone
from eos.saveddata.cargo import Cargo as es_Cargo
from eos.saveddata.fighter import Fighter as es_Fighter
from service.settings import ContextMenuSettings
+import gui.fitCommands as cmd
class ChangeAmount(ContextMenu):
@@ -20,7 +22,7 @@ class ChangeAmount(ContextMenu):
if not self.settings.get('amount'):
return False
- return srcContext in ("cargoItem", "projectedFit", "fighterItem", "projectedFighter")
+ return srcContext in ("droneItem", "projectedDrone", "cargoItem", "projectedFit", "fighterItem", "projectedFighter")
def getText(self, itmContext, selection):
return u"Change {0} Quantity".format(itmContext)
@@ -29,7 +31,7 @@ class ChangeAmount(ContextMenu):
thing = selection[0]
mainFrame = gui.mainFrame.MainFrame.getInstance()
fitID = mainFrame.getActiveFit()
-
+ srcContext = fullContext[0]
if isinstance(thing, es_Fit):
value = thing.getProjectionInfo(fitID).amount
else:
@@ -42,14 +44,23 @@ class ChangeAmount(ContextMenu):
return
sFit = Fit.getInstance()
+ fit = sFit.getFit(fitID)
cleanInput = re.sub(r'[^0-9.]', '', dlg.input.GetLineText(0).strip())
if isinstance(thing, es_Cargo):
- sFit.addCargo(fitID, thing.item.ID, int(float(cleanInput)), replace=True)
+ self.mainFrame.command.Submit(cmd.GuiChangeCargoQty(fitID, fit.cargo.index(thing), int(float(cleanInput))))
+ return # no need for post event here
+ elif isinstance(thing, Drone):
+ if srcContext == "droneItem":
+ self.mainFrame.command.Submit(cmd.GuiChangeDroneQty(fitID, fit.drones.index(thing), int(float(cleanInput))))
+ else:
+ self.mainFrame.command.Submit(cmd.GuiChangeProjectedDroneQty(fitID, fit.projectedDrones.index(thing), int(float(cleanInput))))
elif isinstance(thing, es_Fit):
- sFit.changeAmount(fitID, thing, int(float(cleanInput)))
+ self.mainFrame.command.Submit(cmd.GuiChangeProjectedFitQty(fitID, thing.ID, int(float(cleanInput))))
+ return
elif isinstance(thing, es_Fighter):
- sFit.changeActiveFighters(fitID, thing, int(float(cleanInput)))
+ self.mainFrame.command.Submit(cmd.GuiChangeFighterQty(fitID, fit.fighters.index(thing), int(float(cleanInput))))
+ return
wx.PostEvent(mainFrame, GE.FitChanged(fitID=fitID))
diff --git a/gui/builtinContextMenus/boosterSideEffects.py b/gui/builtinContextMenus/boosterSideEffects.py
index a936e9340..d6dbc38c6 100644
--- a/gui/builtinContextMenus/boosterSideEffects.py
+++ b/gui/builtinContextMenus/boosterSideEffects.py
@@ -16,7 +16,7 @@ class BoosterSideEffect(ContextMenu):
# if not self.settings.get('fighterAbilities'):
# return False
- if self.mainFrame.getActiveFit() is None or srcContext not in ("boosterItem"):
+ if self.mainFrame.getActiveFit() is None or srcContext not in "boosterItem":
return False
self.booster = selection[0]
@@ -50,7 +50,7 @@ class BoosterSideEffect(ContextMenu):
if not effect.effect.isImplemented:
continue
menuItem = self.addEffect(rootMenu if msw else sub, effect)
- sub.AppendItem(menuItem)
+ sub.Append(menuItem)
menuItem.Check(effect.active)
return sub
diff --git a/gui/builtinContextMenus/cargo.py b/gui/builtinContextMenus/cargo.py
index 32d6370cf..3bcadfa31 100644
--- a/gui/builtinContextMenus/cargo.py
+++ b/gui/builtinContextMenus/cargo.py
@@ -1,8 +1,7 @@
-from gui.contextMenu import ContextMenu
+import gui.fitCommands as cmd
import gui.mainFrame
-import gui.globalEvents as GE
+from gui.contextMenu import ContextMenu
# noinspection PyPackageRequirements
-import wx
from service.fit import Fit
from service.settings import ContextMenuSettings
@@ -32,13 +31,12 @@ class Cargo(ContextMenu):
return "Add {0} to Cargo".format(itmContext)
def activate(self, fullContext, selection, i):
- sFit = Fit.getInstance()
fitID = self.mainFrame.getActiveFit()
typeID = int(selection[0].ID)
- sFit.addCargo(fitID, typeID)
+
+ self.mainFrame.command.Submit(cmd.GuiAddCargoCommand(fitID, typeID))
self.mainFrame.additionsPane.select("Cargo")
- wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=fitID))
Cargo.register()
diff --git a/gui/builtinContextMenus/cargoAmmo.py b/gui/builtinContextMenus/cargoAmmo.py
index 12ebe552f..b92ba8e3a 100644
--- a/gui/builtinContextMenus/cargoAmmo.py
+++ b/gui/builtinContextMenus/cargoAmmo.py
@@ -1,9 +1,7 @@
-from gui.contextMenu import ContextMenu
+import gui.fitCommands as cmd
import gui.mainFrame
-import gui.globalEvents as GE
-import wx
+from gui.contextMenu import ContextMenu
from service.settings import ContextMenuSettings
-from service.fit import Fit
class CargoAmmo(ContextMenu):
@@ -28,13 +26,10 @@ class CargoAmmo(ContextMenu):
return "Add {0} to Cargo (x1000)".format(itmContext)
def activate(self, fullContext, selection, i):
- sFit = Fit.getInstance()
fitID = self.mainFrame.getActiveFit()
-
typeID = int(selection[0].ID)
- sFit.addCargo(fitID, typeID, 1000)
+ self.mainFrame.command.Submit(cmd.GuiAddCargoCommand(fitID, typeID, 1000))
self.mainFrame.additionsPane.select("Cargo")
- wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=fitID))
CargoAmmo.register()
diff --git a/gui/builtinContextMenus/commandFits.py b/gui/builtinContextMenus/commandFits.py
index 936dd0cc0..97496bb75 100644
--- a/gui/builtinContextMenus/commandFits.py
+++ b/gui/builtinContextMenus/commandFits.py
@@ -1,11 +1,11 @@
# noinspection PyPackageRequirements
import wx
+import gui.fitCommands as cmd
+import gui.mainFrame
+from gui.contextMenu import ContextMenu
from service.fit import Fit
from service.market import Market
-import gui.mainFrame
-import gui.globalEvents as GE
-from gui.contextMenu import ContextMenu
from service.settings import ContextMenuSettings
@@ -25,7 +25,7 @@ class CommandFits(ContextMenu):
if evt is not None:
ids = getattr(evt, 'typeID')
if not isinstance(ids, set):
- ids = set([ids])
+ ids = {ids}
if evt is None or not ids.isdisjoint(cls.commandTypeIDs):
# we are adding or removing an item that defines a command fit. Need to refresh fit list
@@ -97,11 +97,8 @@ class CommandFits(ContextMenu):
event.Skip()
return
- sFit = Fit.getInstance()
fitID = self.mainFrame.getActiveFit()
-
- sFit.addCommandFit(fitID, fit)
- wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=fitID))
+ self.mainFrame.command.Submit(cmd.GuiAddCommandCommand(fitID, fit.ID))
CommandFits.populateFits(None)
diff --git a/gui/builtinContextMenus/droneRemoveStack.py b/gui/builtinContextMenus/droneRemoveStack.py
index c7dae1e03..fad83f1e8 100644
--- a/gui/builtinContextMenus/droneRemoveStack.py
+++ b/gui/builtinContextMenus/droneRemoveStack.py
@@ -1,8 +1,7 @@
-from gui.contextMenu import ContextMenu
+import gui.fitCommands as cmd
import gui.mainFrame
+from gui.contextMenu import ContextMenu
# noinspection PyPackageRequirements
-import wx
-import gui.globalEvents as GE
from service.fit import Fit
from service.settings import ContextMenuSettings
@@ -27,9 +26,7 @@ class ItemRemove(ContextMenu):
fit = sFit.getFit(fitID)
idx = fit.drones.index(selection[0])
- sFit.removeDrone(fitID, idx, numDronesToRemove=fit.drones[idx].amount)
-
- wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=fitID))
+ self.mainFrame.command.Submit(cmd.GuiRemoveDroneCommand(fitID, idx, fit.drones[idx].amount))
ItemRemove.register()
diff --git a/gui/builtinContextMenus/droneSplit.py b/gui/builtinContextMenus/droneSplit.py
index ec52db3f8..462bd7198 100644
--- a/gui/builtinContextMenus/droneSplit.py
+++ b/gui/builtinContextMenus/droneSplit.py
@@ -5,6 +5,7 @@ from service.fit import Fit
# noinspection PyPackageRequirements
import wx
from service.settings import ContextMenuSettings
+import re
class DroneSplit(ContextMenu):
@@ -23,14 +24,93 @@ class DroneSplit(ContextMenu):
def activate(self, fullContext, selection, i):
srcContext = fullContext[0]
- dlg = DroneSpinner(self.mainFrame, selection[0], srcContext)
- dlg.ShowModal()
- dlg.Destroy()
+ drone = selection[0]
+ dlg = DroneStackSplit(self.mainFrame, drone.amount)
+
+ if dlg.ShowModal() == wx.ID_OK:
+
+ if dlg.input.GetLineText(0).strip() == '':
+ return
+
+ sFit = Fit.getInstance()
+ cleanInput = re.sub(r'[^0-9.]', '', dlg.input.GetLineText(0).strip())
+ fitID = self.mainFrame.getActiveFit()
+
+ if srcContext == "droneItem":
+ sFit.splitDroneStack(fitID, drone, int(float(cleanInput)))
+ else:
+ sFit.splitProjectedDroneStack(fitID, drone, int(float(cleanInput)))
+
+ wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=fitID))
+
+ # if isinstance(thing, es_Cargo):
+ # self.mainFrame.command.Submit(
+ # cmd.GuiAddCargoCommand(fitID, thing.item.ID, int(float(cleanInput)), replace=True))
+ # return # no need for post event here
+ # elif isinstance(thing, es_Fit):
+ # sFit.changeAmount(fitID, thing, int(float(cleanInput)))
+ # elif isinstance(thing, es_Fighter):
+ # sFit.changeActiveFighters(fitID, thing, int(float(cleanInput)))
+ #
+ # wx.PostEvent(mainFrame, GE.FitChanged(fitID=fitID))
+ #
+ # dlg = DroneSpinner(self.mainFrame, selection[0], srcContext)
+ # dlg.ShowModal()
+ # dlg.Destroy()
DroneSplit.register()
+class DroneStackSplit(wx.Dialog):
+ def __init__(self, parent, value):
+ wx.Dialog.__init__(self, parent, title="Split Drone Stack")
+ self.SetMinSize((346, 156))
+
+ bSizer1 = wx.BoxSizer(wx.VERTICAL)
+
+ bSizer2 = wx.BoxSizer(wx.VERTICAL)
+ text = wx.StaticText(self, wx.ID_ANY, "New Amount:")
+ bSizer2.Add(text, 0)
+
+ bSizer1.Add(bSizer2, 0, wx.ALL, 10)
+
+ self.input = wx.TextCtrl(self, wx.ID_ANY, style=wx.TE_PROCESS_ENTER)
+ self.input.SetValue(str(value))
+ self.input.SelectAll()
+
+ bSizer1.Add(self.input, 0, wx.LEFT | wx.RIGHT | wx.EXPAND, 15)
+
+ bSizer3 = wx.BoxSizer(wx.VERTICAL)
+ bSizer3.Add(wx.StaticLine(self, wx.ID_ANY), 0, wx.BOTTOM | wx.EXPAND, 15)
+
+ bSizer3.Add(self.CreateStdDialogButtonSizer(wx.OK | wx.CANCEL), 0, wx.EXPAND)
+ bSizer1.Add(bSizer3, 0, wx.ALL | wx.EXPAND, 10)
+
+ self.input.SetFocus()
+ self.input.Bind(wx.EVT_CHAR, self.onChar)
+ self.input.Bind(wx.EVT_TEXT_ENTER, self.processEnter)
+ self.SetSizer(bSizer1)
+ self.CenterOnParent()
+ self.Fit()
+
+ def processEnter(self, evt):
+ self.EndModal(wx.ID_OK)
+
+ # checks to make sure it's valid number
+ @staticmethod
+ def onChar(event):
+ key = event.GetKeyCode()
+
+ acceptable_characters = "1234567890"
+ acceptable_keycode = [3, 22, 13, 8, 127] # modifiers like delete, copy, paste
+ if key in acceptable_keycode or key >= 255 or (key < 255 and chr(key) in acceptable_characters):
+ event.Skip()
+ return
+ else:
+ return False
+
+
class DroneSpinner(wx.Dialog):
def __init__(self, parent, drone, context):
wx.Dialog.__init__(self, parent, title="Select Amount", size=wx.Size(220, 60))
diff --git a/gui/builtinContextMenus/itemRemove.py b/gui/builtinContextMenus/itemRemove.py
index 8e9ede5c6..e5d083471 100644
--- a/gui/builtinContextMenus/itemRemove.py
+++ b/gui/builtinContextMenus/itemRemove.py
@@ -5,6 +5,7 @@ import wx
import gui.globalEvents as GE
from service.fit import Fit
from service.settings import ContextMenuSettings
+import gui.fitCommands as cmd
class ItemRemove(ContextMenu):
@@ -35,26 +36,33 @@ class ItemRemove(ContextMenu):
fit = sFit.getFit(fitID)
if srcContext == "fittingModule":
- for module in selection:
- if module is not None:
- sFit.removeModule(fitID, fit.modules.index(module))
+ modules = [module for module in selection if module is not None]
+ self.mainFrame.command.Submit(cmd.GuiModuleRemoveCommand(fitID, modules))
+ return # the command takes care of the PostEvent
elif srcContext in ("fittingCharge", "projectedCharge"):
- sFit.setAmmo(fitID, None, selection)
+ self.mainFrame.command.Submit(cmd.GuiModuleAddChargeCommand(fitID, None, selection))
+ return
elif srcContext == "droneItem":
- sFit.removeDrone(fitID, fit.drones.index(selection[0]))
+ self.mainFrame.command.Submit(cmd.GuiRemoveDroneCommand(fitID, fit.drones.index(selection[0])))
+ return
elif srcContext == "fighterItem":
- sFit.removeFighter(fitID, fit.fighters.index(selection[0]))
+ self.mainFrame.command.Submit(cmd.GuiRemoveFighterCommand(fitID, fit.fighters.index(selection[0])))
+ return # the command takes care of the PostEvent
elif srcContext == "implantItem":
- sFit.removeImplant(fitID, fit.implants.index(selection[0]))
+ self.mainFrame.command.Submit(cmd.GuiRemoveImplantCommand(fitID, fit.implants.index(selection[0])))
+ return # the command takes care of the PostEvent
elif srcContext == "boosterItem":
- sFit.removeBooster(fitID, fit.boosters.index(selection[0]))
+ self.mainFrame.command.Submit(cmd.GuiRemoveBoosterCommand(fitID, fit.boosters.index(selection[0])))
+ return # the command takes care of the PostEvent
elif srcContext == "cargoItem":
- sFit.removeCargo(fitID, fit.cargo.index(selection[0]))
+ self.mainFrame.command.Submit(cmd.GuiRemoveCargoCommand(fitID, selection[0].itemID))
+ return # the command takes care of the PostEvent
elif srcContext in ("projectedFit", "projectedModule", "projectedDrone", "projectedFighter"):
- sFit.removeProjected(fitID, selection[0])
+ self.mainFrame.command.Submit(cmd.GuiRemoveProjectedCommand(fitID, selection[0]))
+ return # the command takes care of the PostEvent
elif srcContext == "commandFit":
- sFit.removeCommand(fitID, selection[0])
-
+ self.mainFrame.command.Submit(cmd.GuiRemoveCommandCommand(fitID, selection[0].ID))
+ return # the command takes care of the PostEvent
wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=fitID))
diff --git a/gui/builtinContextMenus/metaSwap.py b/gui/builtinContextMenus/metaSwap.py
index a5f6371e3..2cbbb2221 100644
--- a/gui/builtinContextMenus/metaSwap.py
+++ b/gui/builtinContextMenus/metaSwap.py
@@ -3,18 +3,11 @@
# noinspection PyPackageRequirements
import wx
-from service.fit import Fit
-from service.market import Market
+import gui.fitCommands as cmd
import gui.mainFrame
-import gui.globalEvents as GE
from gui.contextMenu import ContextMenu
+from service.market import Market
from service.settings import ContextMenuSettings
-from eos.saveddata.booster import Booster
-from eos.saveddata.module import Module
-from eos.saveddata.drone import Drone
-from eos.saveddata.fighter import Fighter
-from eos.saveddata.implant import Implant
-from eos.saveddata.cargo import Cargo
class MetaSwap(ContextMenu):
@@ -91,7 +84,7 @@ class MetaSwap(ContextMenu):
# Sort items by metalevel, and group within that metalevel
items = list(self.variations)
- print(context)
+
if "implantItem" in context:
# sort implants based on name
items.sort(key=lambda x: x.name)
@@ -122,79 +115,35 @@ class MetaSwap(ContextMenu):
id = ContextMenu.nextID()
mitem = wx.MenuItem(rootMenu, id, item.name)
bindmenu.Bind(wx.EVT_MENU, self.handleModule, mitem)
- self.moduleLookup[id] = item
+
+ self.moduleLookup[id] = item, context
m.Append(mitem)
return m
def handleModule(self, event):
- item = self.moduleLookup.get(event.Id, None)
+ item, context = self.moduleLookup.get(event.Id, None)
if item is None:
event.Skip()
return
- sFit = Fit.getInstance()
fitID = self.mainFrame.getActiveFit()
- fit = sFit.getFit(fitID)
- for selected_item in self.selection:
- if isinstance(selected_item, Module):
- pos = fit.modules.index(selected_item)
- sFit.changeModule(fitID, pos, item.ID)
+ self.mainFrame.command.Submit(cmd.GuiMetaSwapCommand(fitID, context, item.ID, self.selection))
- elif isinstance(selected_item, Drone):
- drone_count = None
+ # for selected_item in self.selection:
- for idx, drone_stack in enumerate(fit.drones):
- if drone_stack is selected_item:
- drone_count = drone_stack.amount
- sFit.removeDrone(fitID, idx, drone_count, False)
- break
-
- if drone_count:
- sFit.addDrone(fitID, item.ID, drone_count, True)
-
- elif isinstance(selected_item, Fighter):
- fighter_count = None
-
- for idx, fighter_stack in enumerate(fit.fighters):
- # Right now fighters always will have max stack size.
- # Including this for future improvement, so if adjustable
- # fighter stacks get added we're ready for it.
- if fighter_stack is selected_item:
- if fighter_stack.amount > 0:
- fighter_count = fighter_stack.amount
- elif fighter_stack.amount == -1:
- fighter_count = fighter_stack.amountActive
- else:
- fighter_count.amount = 0
-
- sFit.removeFighter(fitID, idx, False)
- break
-
- sFit.addFighter(fitID, item.ID, True)
-
- elif isinstance(selected_item, Booster):
- for idx, booster_stack in enumerate(fit.boosters):
- if booster_stack is selected_item:
- sFit.removeBooster(fitID, idx, False)
- sFit.addBooster(fitID, item.ID, True)
- break
-
- elif isinstance(selected_item, Implant):
- for idx, implant_stack in enumerate(fit.implants):
- if implant_stack is selected_item:
- sFit.removeImplant(fitID, idx, False)
- sFit.addImplant(fitID, item.ID, True)
- break
-
- elif isinstance(selected_item, Cargo):
- for idx, cargo_stack in enumerate(fit.cargo):
- if cargo_stack is selected_item:
- sFit.removeCargo(fitID, idx)
- sFit.addCargo(fitID, item.ID, cargo_stack.amount, True)
- break
-
- wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=fitID))
+ #
+ # elif isinstance(selected_item, Drone):
+ # drone_count = None
+ #
+ # for idx, drone_stack in enumerate(fit.drones):
+ # if drone_stack is selected_item:
+ # drone_count = drone_stack.amount
+ # sFit.removeDrone(fitID, idx, drone_count, False)
+ # break
+ #
+ # if drone_count:
+ # sFit.addDrone(fitID, item.ID, drone_count, True)
MetaSwap.register()
diff --git a/gui/builtinContextMenus/moduleAmmoPicker.py b/gui/builtinContextMenus/moduleAmmoPicker.py
index 38a498eb0..f4de653b4 100644
--- a/gui/builtinContextMenus/moduleAmmoPicker.py
+++ b/gui/builtinContextMenus/moduleAmmoPicker.py
@@ -3,13 +3,12 @@
# noinspection PyPackageRequirements
import wx
-from service.fit import Fit
-from service.market import Market
-from eos.saveddata.module import Hardpoint
+import gui.fitCommands as cmd
import gui.mainFrame
-import gui.globalEvents as GE
-from gui.contextMenu import ContextMenu
+from eos.saveddata.module import Hardpoint
from gui.bitmap_loader import BitmapLoader
+from gui.contextMenu import ContextMenu
+from service.market import Market
from service.settings import ContextMenuSettings
@@ -228,11 +227,8 @@ class ModuleAmmoPicker(ContextMenu):
event.Skip()
return
- sFit = Fit.getInstance()
fitID = self.mainFrame.getActiveFit()
-
- sFit.setAmmo(fitID, charge.ID if charge is not None else None, self.modules)
- wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=fitID))
+ self.mainFrame.command.Submit(cmd.GuiModuleAddChargeCommand(fitID, charge.ID if charge is not None else None, self.modules))
ModuleAmmoPicker.register()
diff --git a/gui/builtinContextMenus/moduleGlobalAmmoPicker.py b/gui/builtinContextMenus/moduleGlobalAmmoPicker.py
index 5e516e68f..70a41c351 100644
--- a/gui/builtinContextMenus/moduleGlobalAmmoPicker.py
+++ b/gui/builtinContextMenus/moduleGlobalAmmoPicker.py
@@ -1,11 +1,9 @@
# -*- coding: utf-8 -*-
+import gui.fitCommands as cmd
import gui.mainFrame
-# noinspection PyPackageRequirements
-import wx
-import gui.globalEvents as GE
-from gui.builtinContextMenus.moduleAmmoPicker import ModuleAmmoPicker
from eos.db.saveddata.queries import getFit as db_getFit
-from service.fit import Fit
+# noinspection PyPackageRequirements
+from gui.builtinContextMenus.moduleAmmoPicker import ModuleAmmoPicker
from service.settings import ContextMenuSettings
@@ -28,7 +26,6 @@ class ModuleGlobalAmmoPicker(ModuleAmmoPicker):
event.Skip()
return
- sFit = Fit.getInstance()
fitID = self.mainFrame.getActiveFit()
fit = db_getFit(fitID)
@@ -40,8 +37,7 @@ class ModuleGlobalAmmoPicker(ModuleAmmoPicker):
if mod.itemID == selectedModule.itemID:
allModules.append(mod)
- sFit.setAmmo(fitID, charge.ID if charge is not None else None, allModules)
- wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=fitID))
+ self.mainFrame.command.Submit(cmd.GuiModuleAddChargeCommand(fitID, charge.ID if charge is not None else None, allModules))
def display(self, srcContext, selection):
if not self.settings.get('moduleGlobalAmmoPicker'):
diff --git a/gui/builtinContextMenus/mutaplasmids.py b/gui/builtinContextMenus/mutaplasmids.py
index 253cdac60..98656b840 100644
--- a/gui/builtinContextMenus/mutaplasmids.py
+++ b/gui/builtinContextMenus/mutaplasmids.py
@@ -18,7 +18,7 @@ class MutaplasmidCM(ContextMenu):
# if not self.settings.get('ammoPattern'):
# return False
- if srcContext not in ("fittingModule") or self.mainFrame.getActiveFit() is None:
+ if srcContext not in "fittingModule" or self.mainFrame.getActiveFit() is None:
return False
mod = selection[0]
diff --git a/gui/builtinContextMenus/project.py b/gui/builtinContextMenus/project.py
index f06c29152..6bbb974b3 100644
--- a/gui/builtinContextMenus/project.py
+++ b/gui/builtinContextMenus/project.py
@@ -1,8 +1,7 @@
-from gui.contextMenu import ContextMenu
+import gui.fitCommands as cmd
import gui.mainFrame
-import gui.globalEvents as GE
+from gui.contextMenu import ContextMenu
# noinspection PyPackageRequirements
-import wx
from service.fit import Fit
from service.settings import ContextMenuSettings
@@ -33,12 +32,13 @@ class Project(ContextMenu):
return "Project {0} onto Fit".format(itmContext)
def activate(self, fullContext, selection, i):
- sFit = Fit.getInstance()
fitID = self.mainFrame.getActiveFit()
- trigger = sFit.project(fitID, selection[0])
- if trigger:
- wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=fitID))
- self.mainFrame.additionsPane.select("Projected")
+ self.mainFrame.command.Submit(cmd.GuiAddProjectedCommand(fitID, selection[0].ID, 'item'))
+
+ # trigger = sFit.project(fitID, selection[0])
+ # if trigger:
+ # wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=fitID))
+ # self.mainFrame.additionsPane.select("Projected")
Project.register()
diff --git a/gui/builtinContextMenus/tabbedFits.py b/gui/builtinContextMenus/tabbedFits.py
index c55a90b35..f86562fa6 100644
--- a/gui/builtinContextMenus/tabbedFits.py
+++ b/gui/builtinContextMenus/tabbedFits.py
@@ -3,11 +3,11 @@
# noinspection PyPackageRequirements
import wx
-from service.fit import Fit
+import gui.fitCommands as cmd
import gui.mainFrame
-import gui.globalEvents as GE
-from gui.contextMenu import ContextMenu
from gui.builtinViews.emptyView import BlankPage
+from gui.contextMenu import ContextMenu
+from service.fit import Fit
class TabbedFits(ContextMenu):
@@ -51,17 +51,14 @@ class TabbedFits(ContextMenu):
return m
def handleSelection(self, event):
- sFit = Fit.getInstance()
fitID = self.mainFrame.getActiveFit()
fit = self.fitLookup[event.Id]
if self.context == 'commandView':
- sFit.addCommandFit(fitID, fit)
+ self.mainFrame.command.Submit(cmd.GuiAddCommandCommand(fitID, fit.ID))
elif self.context == 'projected':
- sFit.project(fitID, fit)
-
- wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=fitID))
+ self.mainFrame.command.Submit(cmd.GuiAddProjectedCommand(fitID, fit.ID, 'fit'))
TabbedFits.register()
diff --git a/gui/builtinContextMenus/tacticalMode.py b/gui/builtinContextMenus/tacticalMode.py
index e09b26940..94bfc1076 100644
--- a/gui/builtinContextMenus/tacticalMode.py
+++ b/gui/builtinContextMenus/tacticalMode.py
@@ -1,9 +1,9 @@
# noinspection PyPackageRequirements
import wx
-from gui.contextMenu import ContextMenu
-import gui.mainFrame
-import gui.globalEvents as GE
+import gui.fitCommands as cmd
+import gui.mainFrame
+from gui.contextMenu import ContextMenu
from service.fit import Fit
from service.settings import ContextMenuSettings
@@ -60,10 +60,8 @@ class TacticalMode(ContextMenu):
event.Skip()
return
- sFit = Fit.getInstance()
fitID = self.mainFrame.getActiveFit()
- sFit.setMode(fitID, self.modeIds[event.Id])
- wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=fitID))
+ self.mainFrame.command.Submit(cmd.GuiSetModeCommand(fitID, self.modeIds[event.Id]))
TacticalMode.register()
diff --git a/gui/builtinContextMenus/whProjector.py b/gui/builtinContextMenus/whProjector.py
index ac0bd5c1b..c6e083676 100644
--- a/gui/builtinContextMenus/whProjector.py
+++ b/gui/builtinContextMenus/whProjector.py
@@ -1,13 +1,14 @@
-from gui.contextMenu import ContextMenu
-import gui.mainFrame
-import gui.globalEvents as GE
+import re
+from itertools import chain
+
# noinspection PyPackageRequirements
import wx
+
+import gui.fitCommands as cmd
+import gui.mainFrame
+from gui.contextMenu import ContextMenu
from service.market import Market
-from service.fit import Fit
from service.settings import ContextMenuSettings
-from itertools import chain
-import re
class WhProjector(ContextMenu):
@@ -87,10 +88,8 @@ class WhProjector(ContextMenu):
event.Skip()
return
- sFit = Fit.getInstance()
fitID = self.mainFrame.getActiveFit()
- sFit.project(fitID, swObj)
- wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=fitID))
+ self.mainFrame.command.Submit(cmd.GuiAddProjectedCommand(fitID, swObj.ID, 'item'))
def buildMenu(self, data, local_menu, rootMenu, msw):
for swType in sorted(data):
diff --git a/gui/builtinItemStatsViews/attributeSlider.py b/gui/builtinItemStatsViews/attributeSlider.py
index 69358278c..8de27e347 100644
--- a/gui/builtinItemStatsViews/attributeSlider.py
+++ b/gui/builtinItemStatsViews/attributeSlider.py
@@ -1,9 +1,7 @@
import wx
import wx.lib.newevent
-from gui.attribute_gauge import AttributeGauge
-import eos
-import eos.db
+from gui.attribute_gauge import AttributeGauge
_ValueChanged, EVT_VALUE_CHANGED = wx.lib.newevent.NewEvent()
diff --git a/gui/builtinItemStatsViews/itemMutator.py b/gui/builtinItemStatsViews/itemMutator.py
index 614274e6e..30ba82fd9 100644
--- a/gui/builtinItemStatsViews/itemMutator.py
+++ b/gui/builtinItemStatsViews/itemMutator.py
@@ -1,18 +1,16 @@
# noinspection PyPackageRequirements
-import wx
-
-from service.fit import Fit
-from .attributeSlider import AttributeSlider, EVT_VALUE_CHANGED
-
-import gui.mainFrame
-from gui.contextMenu import ContextMenu
-from .itemAttributes import ItemParams
-from gui.bitmap_loader import BitmapLoader
-import gui.globalEvents as GE
import random
+import wx
from logbook import Logger
+import gui.globalEvents as GE
+import gui.mainFrame
+from gui.bitmap_loader import BitmapLoader
+from service.fit import Fit
+from .attributeSlider import AttributeSlider, EVT_VALUE_CHANGED
+from .itemAttributes import ItemParams
+
pyfalog = Logger(__name__)
diff --git a/gui/builtinMarketBrowser/itemView.py b/gui/builtinMarketBrowser/itemView.py
index 7a26cfed1..82454502a 100644
--- a/gui/builtinMarketBrowser/itemView.py
+++ b/gui/builtinMarketBrowser/itemView.py
@@ -1,16 +1,13 @@
import wx
-
-import config
-import gui.builtinMarketBrowser.pfSearchBox as SBox
-from gui.contextMenu import ContextMenu
-from gui.display import Display
-from service.attribute import Attribute
-from service.fit import Fit
-from gui.utils.staticHelpers import DragDropHelper
-
from logbook import Logger
-from gui.builtinMarketBrowser.events import RECENTLY_USED_MODULES, MAX_RECENTLY_USED_MODULES, ItemSelected
+import gui.builtinMarketBrowser.pfSearchBox as SBox
+from gui.builtinMarketBrowser.events import ItemSelected, MAX_RECENTLY_USED_MODULES, RECENTLY_USED_MODULES
+from gui.contextMenu import ContextMenu
+from gui.display import Display
+from gui.utils.staticHelpers import DragDropHelper
+from service.attribute import Attribute
+from service.fit import Fit
pyfalog = Logger(__name__)
diff --git a/gui/builtinPreferenceViews/pyfaEsiPreferences.py b/gui/builtinPreferenceViews/pyfaEsiPreferences.py
index 7eaf31a4e..39125ed38 100644
--- a/gui/builtinPreferenceViews/pyfaEsiPreferences.py
+++ b/gui/builtinPreferenceViews/pyfaEsiPreferences.py
@@ -1,17 +1,13 @@
# noinspection PyPackageRequirements
import wx
-from gui.preferenceView import PreferenceView
-from gui.bitmap_loader import BitmapLoader
-
import gui.mainFrame
-
+from gui.bitmap_loader import BitmapLoader
+from gui.preferenceView import PreferenceView
from service.settings import EsiSettings
-# noinspection PyPackageRequirements
-from wx.lib.intctrl import IntCtrl
-from service.esi import Esi
+# noinspection PyPackageRequirements
class PFEsiPref(PreferenceView):
diff --git a/gui/builtinPreferenceViews/pyfaGaugePreferences.py b/gui/builtinPreferenceViews/pyfaGaugePreferences.py
index c0ca68a98..7ba91eff3 100644
--- a/gui/builtinPreferenceViews/pyfaGaugePreferences.py
+++ b/gui/builtinPreferenceViews/pyfaGaugePreferences.py
@@ -3,12 +3,9 @@
# noinspection PyPackageRequirements
import wx
-import copy
-from gui.preferenceView import PreferenceView
from gui.bitmap_loader import BitmapLoader
-from gui.utils.color import CalculateTransition
-import gui.utils.draw as drawUtils
+from gui.preferenceView import PreferenceView
###########################################################################
diff --git a/gui/builtinShipBrowser/fitItem.py b/gui/builtinShipBrowser/fitItem.py
index 2f05229d0..c0bd9c07b 100644
--- a/gui/builtinShipBrowser/fitItem.py
+++ b/gui/builtinShipBrowser/fitItem.py
@@ -2,21 +2,21 @@
import re
import time
-import config
import wx
from logbook import Logger
+import config
import gui.builtinShipBrowser.sfBrowserItem as SFItem
-import gui.globalEvents as GE
+import gui.fitCommands as cmd
import gui.mainFrame
import gui.utils.color as colorUtils
import gui.utils.draw as drawUtils
import gui.utils.fonts as fonts
-from .events import ImportSelected, SearchSelected, FitSelected, BoosterListUpdated, Stage3Selected, FitRenamed, FitRemoved
from gui.bitmap_loader import BitmapLoader
from gui.builtinShipBrowser.pfBitmapFrame import PFBitmapFrame
from service.fit import Fit
+from .events import BoosterListUpdated, FitRemoved, FitSelected, ImportSelected, SearchSelected, Stage3Selected
pyfalog = Logger(__name__)
@@ -183,18 +183,14 @@ class FitItem(SFItem.SFBrowserItem):
if activeFit:
sFit = Fit.getInstance()
projectedFit = sFit.getFit(self.fitID)
- sFit.project(activeFit, projectedFit)
- wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=activeFit))
- self.mainFrame.additionsPane.select("Projected")
+ if self.mainFrame.command.Submit(cmd.GuiAddProjectedCommand(activeFit, projectedFit.ID, 'fit')):
+ self.mainFrame.additionsPane.select("Projected")
def OnAddCommandFit(self, event):
activeFit = self.mainFrame.getActiveFit()
if activeFit:
- sFit = Fit.getInstance()
- commandFit = sFit.getFit(self.fitID)
- sFit.addCommandFit(activeFit, commandFit)
- wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=activeFit))
- self.mainFrame.additionsPane.select("Command")
+ if self.mainFrame.command.Submit(cmd.GuiAddCommandCommand(activeFit, self.fitID)):
+ self.mainFrame.additionsPane.select("Command")
def OnMouseCaptureLost(self, event):
""" Destroy drag information (GH issue #479)"""
@@ -325,14 +321,12 @@ class FitItem(SFItem.SFBrowserItem):
self.Refresh()
def renameFit(self, event=None):
- sFit = Fit.getInstance()
self.tcFitName.Show(False)
self.editWasShown = 0
fitName = self.tcFitName.GetValue()
if fitName:
self.fitName = fitName
- sFit.renameFit(self.fitID, self.fitName)
- wx.PostEvent(self.mainFrame, FitRenamed(fitID=self.fitID))
+ self.mainFrame.command.Submit(cmd.GuiFitRenameCommand(self.fitID, self.fitName))
else:
self.tcFitName.SetValue(self.fitName)
diff --git a/gui/builtinShipBrowser/shipItem.py b/gui/builtinShipBrowser/shipItem.py
index 517648ad6..cf1de024c 100644
--- a/gui/builtinShipBrowser/shipItem.py
+++ b/gui/builtinShipBrowser/shipItem.py
@@ -8,11 +8,11 @@ import gui.mainFrame
import gui.utils.color as colorUtils
import gui.utils.draw as drawUtils
import gui.utils.fonts as fonts
-from .events import Stage3Selected, Stage2Selected, Stage1Selected, FitSelected
from gui.bitmap_loader import BitmapLoader
from gui.contextMenu import ContextMenu
from service.fit import Fit
from service.market import Market
+from .events import FitSelected, Stage3Selected
pyfalog = Logger(__name__)
diff --git a/gui/builtinStatsViews/priceViewFull.py b/gui/builtinStatsViews/priceViewFull.py
index 9eb03ea22..767ad958f 100644
--- a/gui/builtinStatsViews/priceViewFull.py
+++ b/gui/builtinStatsViews/priceViewFull.py
@@ -129,15 +129,15 @@ class PriceViewFull(StatsView):
total_price = 0
- if (self.settings.get("ship")):
+ if self.settings.get("ship"):
total_price += ship_price
- if (self.settings.get("modules")):
+ if self.settings.get("modules"):
total_price += module_price
- if (self.settings.get("drones")):
+ if self.settings.get("drones"):
total_price += drone_price + fighter_price
- if (self.settings.get("cargo")):
+ if self.settings.get("cargo"):
total_price += cargo_price
- if (self.settings.get("character")):
+ if self.settings.get("character"):
total_price += booster_price + implant_price
self.labelPriceShip.SetLabel("%s ISK" % formatAmount(ship_price, 3, 3, 9, currency=True))
diff --git a/gui/builtinStatsViews/priceViewMinimal.py b/gui/builtinStatsViews/priceViewMinimal.py
index ec1d5357f..8d6a5ce83 100644
--- a/gui/builtinStatsViews/priceViewMinimal.py
+++ b/gui/builtinStatsViews/priceViewMinimal.py
@@ -125,15 +125,15 @@ class PriceViewMinimal(StatsView):
total_price = 0
- if (self.settings.get("ship")):
+ if self.settings.get("ship"):
total_price += ship_price
- if (self.settings.get("modules")):
+ if self.settings.get("modules"):
total_price += module_price
- if (self.settings.get("drones")):
+ if self.settings.get("drones"):
total_price += drone_price + fighter_price
- if (self.settings.get("cargo")):
+ if self.settings.get("cargo"):
total_price += cargo_price
- if (self.settings.get("character")):
+ if self.settings.get("character"):
total_price += booster_price + implant_price
self.labelPriceShip.SetLabel("%s ISK" % formatAmount(ship_price, 3, 3, 9, currency=True))
diff --git a/gui/builtinViewColumns/price.py b/gui/builtinViewColumns/price.py
index ad99b8fb3..a68614b83 100644
--- a/gui/builtinViewColumns/price.py
+++ b/gui/builtinViewColumns/price.py
@@ -50,10 +50,13 @@ class Price(ViewColumn):
if not price or not price.isValid:
return False
- if isinstance(stuff, Drone) or isinstance(stuff, Cargo):
- price.price *= stuff.amount
+ # Fetch actual price as float to not modify its value on Price object
+ price = price.price
- return formatAmount(price.price, 3, 3, 9, currency=True)
+ if isinstance(stuff, Drone) or isinstance(stuff, Cargo):
+ price *= stuff.amount
+
+ return formatAmount(price, 3, 3, 9, currency=True)
def delayedText(self, mod, display, colItem):
sPrice = ServicePrice.getInstance()
diff --git a/gui/builtinViews/fittingView.py b/gui/builtinViews/fittingView.py
index 458aa4ff2..fe9b73358 100644
--- a/gui/builtinViews/fittingView.py
+++ b/gui/builtinViews/fittingView.py
@@ -21,28 +21,26 @@
import wx
# noinspection PyPackageRequirements
import wx.lib.newevent
-import gui.mainFrame
-from gui.builtinMarketBrowser.events import ItemSelected, ITEM_SELECTED
+from logbook import Logger
+
+import gui.builtinViews.emptyView
import gui.display as d
-from gui.contextMenu import ContextMenu
-from gui.builtinShipBrowser.events import EVT_FIT_RENAMED, EVT_FIT_REMOVED, FitSelected, EVT_FIT_SELECTED
+import gui.fitCommands as cmd
+import gui.globalEvents as GE
+import gui.mainFrame
import gui.multiSwitch
from eos.saveddata.mode import Mode
-from eos.saveddata.module import Module, Slot, Rack
-from gui.builtinViewColumns.state import State
+from eos.saveddata.module import Module, Rack, Slot
from gui.bitmap_loader import BitmapLoader
-import gui.builtinViews.emptyView
-from logbook import Logger
+from gui.builtinMarketBrowser.events import ITEM_SELECTED
+from gui.builtinShipBrowser.events import EVT_FIT_REMOVED, EVT_FIT_RENAMED, EVT_FIT_SELECTED, FitSelected
+from gui.builtinViewColumns.state import State
from gui.chrome_tabs import EVT_NOTEBOOK_PAGE_CHANGED
-
+from gui.contextMenu import ContextMenu
+from gui.utils.staticHelpers import DragDropHelper
from service.fit import Fit
from service.market import Market
-from gui.utils.staticHelpers import DragDropHelper
-import gui.utils.fonts as fonts
-
-import gui.globalEvents as GE
-
pyfalog = Logger(__name__)
@@ -265,7 +263,9 @@ class FittingView(d.Display):
sel = []
row = self.GetFirstSelected()
while row != -1:
- sel.append(self.mods[self.GetItemData(row)])
+ mod = self.mods[self.GetItemData(row)]
+ if mod and not isinstance(mod, Rack):
+ sel.append(mod)
row = self.GetNextSelected(row)
return sel
@@ -358,6 +358,9 @@ class FittingView(d.Display):
self.parent.SetPageTextIcon(pageIndex, text, bitmap)
def appendItem(self, event):
+ """
+ Adds items that are double clicks from the market browser. We handle both modules and ammo
+ """
if not self:
event.Skip()
return
@@ -367,6 +370,7 @@ class FittingView(d.Display):
if fitID is not None:
sFit = Fit.getInstance()
if sFit.isAmmo(itemID):
+ # If we've selected ammo, then apply to the selected module(s)
modules = []
sel = self.GetFirstSelected()
while sel != -1 and sel not in self.blanks:
@@ -376,17 +380,14 @@ class FittingView(d.Display):
sel = self.GetNextSelected(sel)
if len(modules) > 0:
- sFit.setAmmo(fitID, itemID, modules)
- wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=fitID))
+ self.mainFrame.command.Submit(cmd.GuiModuleAddChargeCommand(fitID, itemID, modules))
else:
- populate = sFit.appendModule(fitID, itemID)
- if populate is not None:
- self.slotsChanged()
- wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=fitID, action="modadd", typeID=itemID))
+ self.mainFrame.command.Submit(cmd.GuiModuleAddCommand(fitID, itemID))
event.Skip()
def removeItem(self, event):
+ """Double Left Click - remove module"""
if event.CmdDown():
return
row, _ = self.HitTest(event.Position)
@@ -400,36 +401,22 @@ class FittingView(d.Display):
def removeModule(self, modules):
"""Removes a list of modules from the fit"""
- sFit = Fit.getInstance()
-
if not isinstance(modules, list):
modules = [modules]
- positions = [mod.modPosition for mod in modules]
- result = sFit.removeModule(self.activeFitID, positions)
+ self.mainFrame.command.Submit(cmd.GuiModuleRemoveCommand(self.activeFitID, modules))
- if result is not None:
- self.slotsChanged()
- ids = {mod.item.ID for mod in modules}
- wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.activeFitID, action="moddel", typeID=ids))
-
- def addModule(self, x, y, srcIdx):
- """Add a module from the market browser"""
+ def addModule(self, x, y, itemID):
+ """Add a module from the market browser (from dragging it)"""
dstRow, _ = self.HitTest((x, y))
if dstRow != -1 and dstRow not in self.blanks:
- sFit = Fit.getInstance()
fitID = self.mainFrame.getActiveFit()
mod = self.mods[dstRow]
if not isinstance(mod, Module): # make sure we're not adding something to a T3D Mode
return
- moduleChanged = sFit.changeModule(fitID, self.mods[dstRow].modPosition, srcIdx)
- if moduleChanged is None:
- # the new module doesn't fit in specified slot, try to simply append it
- wx.PostEvent(self.mainFrame, ItemSelected(itemID=srcIdx))
-
- wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.mainFrame.getActiveFit(), action="modadd", typeID=srcIdx))
+ self.mainFrame.command.Submit(cmd.GuiModuleAddCommand(fitID, itemID, self.mods[dstRow].modPosition))
def swapCargo(self, x, y, srcIdx):
"""Swap a module from cargo to fitting window"""
@@ -442,14 +429,11 @@ class FittingView(d.Display):
if not isinstance(module, Module):
return
- sFit = Fit.getInstance()
- fit = sFit.getFit(self.activeFitID)
- typeID = fit.cargo[srcIdx].item.ID
-
- sFit.moveCargoToModule(self.mainFrame.getActiveFit(), module.modPosition, srcIdx,
- mstate.CmdDown() and module.isEmpty)
-
- wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.mainFrame.getActiveFit(), action="modadd", typeID=typeID))
+ self.mainFrame.command.Submit(cmd.GuiCargoToModuleCommand(
+ self.mainFrame.getActiveFit(),
+ module.modPosition,
+ srcIdx,
+ mstate.CmdDown() and module.isEmpty))
def swapItems(self, x, y, srcIdx):
"""Swap two modules in fitting window"""
@@ -457,15 +441,9 @@ class FittingView(d.Display):
sFit = Fit.getInstance()
fit = sFit.getFit(self.activeFitID)
- if mstate.CmdDown():
- clone = True
- else:
- clone = False
-
dstRow, _ = self.HitTest((x, y))
if dstRow != -1 and dstRow not in self.blanks:
-
mod1 = fit.modules[srcIdx]
mod2 = self.mods[dstRow]
@@ -476,13 +454,11 @@ class FittingView(d.Display):
if mod1.slot != mod2.slot:
return
- if getattr(mod2, "modPosition") is not None:
- if clone and mod2.isEmpty and mod1.getModifiedItemAttr("maxGroupFitted", 0) < 1.0:
- sFit.cloneModule(self.mainFrame.getActiveFit(), srcIdx, mod2.modPosition)
- else:
- sFit.swapModules(self.mainFrame.getActiveFit(), srcIdx, mod2.modPosition)
+ clone = mstate.CmdDown() and mod2.isEmpty
- wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.mainFrame.getActiveFit()))
+ fitID = self.mainFrame.getActiveFit()
+ if getattr(mod2, "modPosition") is not None:
+ self.mainFrame.command.Submit(cmd.GuiModuleSwapOrCloneCommand(fitID, srcIdx, mod2.modPosition, clone))
else:
pyfalog.error("Missing module position for: {0}", str(getattr(mod2, "ID", "Unknown")))
@@ -638,18 +614,18 @@ class FittingView(d.Display):
else:
mods = self.getSelectedMods()
- sFit = Fit.getInstance()
fitID = self.mainFrame.getActiveFit()
ctrl = event.cmdDown or event.middleIsDown
click = "ctrl" if ctrl is True else "right" if event.GetButton() == 3 else "left"
- sFit.toggleModulesState(fitID, self.mods[self.GetItemData(row)], mods, click)
+
+ self.mainFrame.command.Submit(cmd.GuiModuleStateChangeCommand(
+ fitID, self.mods[self.GetItemData(row)].modPosition, [mod.modPosition for mod in mods], click))
# update state tooltip
tooltip = self.activeColumns[col].getToolTip(self.mods[self.GetItemData(row)])
if tooltip:
self.SetToolTip(tooltip)
- wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.mainFrame.getActiveFit()))
else:
event.Skip()
diff --git a/gui/characterEditor.py b/gui/characterEditor.py
index c0d8de298..6fe7c809a 100644
--- a/gui/characterEditor.py
+++ b/gui/characterEditor.py
@@ -17,35 +17,30 @@
# along with pyfa. If not, see .
# =============================================================================
+import re
+
+import roman
# noinspection PyPackageRequirements
import wx
import wx.dataview
import wx.lib.agw.hyperlink
-
# noinspection PyPackageRequirements
import wx.lib.newevent
+from logbook import Logger
# noinspection PyPackageRequirements
from wx.dataview import TreeListCtrl
-from gui.bitmap_loader import BitmapLoader
-from gui.contextMenu import ContextMenu
-import gui.globalEvents as GE
-from gui.builtinViews.implantEditor import BaseImplantEditorView
-from gui.builtinViews.entityEditor import EntityEditor, BaseValidator, TextEntryValidatedDialog
-from service.fit import Fit
-from service.character import Character
-from service.esi import Esi
-from service.network import AuthenticationError, TimeoutError
-from service.market import Market
-from logbook import Logger
-
from wx.lib.agw.floatspin import FloatSpin
-
-from gui.utils.clipboard import toClipboard, fromClipboard
-
-import roman
-import re
-import webbrowser
+import gui.globalEvents as GE
+from gui.bitmap_loader import BitmapLoader
+from gui.builtinViews.entityEditor import BaseValidator, EntityEditor, TextEntryValidatedDialog
+from gui.builtinViews.implantEditor import BaseImplantEditorView
+from gui.contextMenu import ContextMenu
+from gui.utils.clipboard import fromClipboard, toClipboard
+from service.character import Character
+from service.esi import Esi
+from service.fit import Fit
+from service.market import Market
pyfalog = Logger(__name__)
diff --git a/gui/chrome_tabs.py b/gui/chrome_tabs.py
index 95d79415d..b50073565 100644
--- a/gui/chrome_tabs.py
+++ b/gui/chrome_tabs.py
@@ -30,7 +30,7 @@ PageAdded, EVT_NOTEBOOK_PAGE_ADDED = wx.lib.newevent.NewEvent()
PageClosed, EVT_NOTEBOOK_PAGE_CLOSED = wx.lib.newevent.NewEvent()
-class VetoAble():
+class VetoAble:
def __init__(self):
self.__vetoed = False
@@ -41,7 +41,7 @@ class VetoAble():
return self.__vetoed
-class NotebookTabChangeEvent():
+class NotebookTabChangeEvent:
def __init__(self, old, new):
self.__old = old
self.__new = new
diff --git a/gui/contextMenu.py b/gui/contextMenu.py
index 08ea258d5..be972b0f8 100644
--- a/gui/contextMenu.py
+++ b/gui/contextMenu.py
@@ -186,7 +186,7 @@ from gui.builtinContextMenus import ( # noqa: E402,F401
itemStats,
damagePattern,
marketJump,
- droneSplit,
+ # droneSplit,
itemRemove,
droneRemoveStack,
ammoPattern,
@@ -202,7 +202,7 @@ from gui.builtinContextMenus import ( # noqa: E402,F401
priceOptions,
amount,
cargoAmmo,
- droneStack,
+ # droneStack,
metaSwap,
implantSets,
fighterAbilities,
diff --git a/gui/copySelectDialog.py b/gui/copySelectDialog.py
index 9f5291026..10eff2647 100644
--- a/gui/copySelectDialog.py
+++ b/gui/copySelectDialog.py
@@ -20,51 +20,84 @@
# noinspection PyPackageRequirements
import wx
+from service.port.eft import EFT_OPTIONS
+from service.settings import SettingsProvider
class CopySelectDialog(wx.Dialog):
copyFormatEft = 0
- copyFormatEftImps = 1
- copyFormatXml = 2
- copyFormatDna = 3
- copyFormatEsi = 4
- copyFormatMultiBuy = 5
- copyFormatEfs = 6
+ copyFormatXml = 1
+ copyFormatDna = 2
+ copyFormatEsi = 3
+ copyFormatMultiBuy = 4
+ copyFormatEfs = 5
def __init__(self, parent):
wx.Dialog.__init__(self, parent, id=wx.ID_ANY, title="Select a format", size=(-1, -1),
style=wx.DEFAULT_DIALOG_STYLE)
mainSizer = wx.BoxSizer(wx.VERTICAL)
- copyFormats = ["EFT", "EFT (Implants)", "XML", "DNA", "ESI", "MultiBuy", "EFS"]
- copyFormatTooltips = {CopySelectDialog.copyFormatEft: "EFT text format",
- CopySelectDialog.copyFormatEftImps: "EFT text format",
- CopySelectDialog.copyFormatXml: "EVE native XML format",
- CopySelectDialog.copyFormatDna: "A one-line text format",
- CopySelectDialog.copyFormatEsi: "A JSON format used for ESI",
- CopySelectDialog.copyFormatMultiBuy: "MultiBuy text format",
- CopySelectDialog.copyFormatEfs: "JSON data format used by EFS"}
- selector = wx.RadioBox(self, wx.ID_ANY, label="Copy to the clipboard using:", choices=copyFormats,
- style=wx.RA_SPECIFY_ROWS)
- selector.Bind(wx.EVT_RADIOBOX, self.Selected)
- for format, tooltip in copyFormatTooltips.items():
- selector.SetItemToolTip(format, tooltip)
+ self.settings = SettingsProvider.getInstance().getSettings("pyfaExport", {"format": 0, "options": 0})
- self.copyFormat = CopySelectDialog.copyFormatEft
- selector.SetSelection(self.copyFormat)
+ self.copyFormats = {
+ "EFT": CopySelectDialog.copyFormatEft,
+ "XML": CopySelectDialog.copyFormatXml,
+ "DNA": CopySelectDialog.copyFormatDna,
+ "ESI": CopySelectDialog.copyFormatEsi,
+ "MultiBuy": CopySelectDialog.copyFormatMultiBuy,
+ "EFS": CopySelectDialog.copyFormatEfs
+ }
- mainSizer.Add(selector, 0, wx.EXPAND | wx.ALL, 5)
+ self.options = {}
+
+ for i, format in enumerate(self.copyFormats.keys()):
+ if i == 0:
+ rdo = wx.RadioButton(self, wx.ID_ANY, format, style=wx.RB_GROUP)
+ else:
+ rdo = wx.RadioButton(self, wx.ID_ANY, format)
+ rdo.Bind(wx.EVT_RADIOBUTTON, self.Selected)
+ if self.settings['format'] == self.copyFormats[format]:
+ rdo.SetValue(True)
+ self.copyFormat = self.copyFormats[format]
+ mainSizer.Add(rdo, 0, wx.EXPAND | wx.ALL, 5)
+
+ if format == "EFT":
+ bsizer = wx.BoxSizer(wx.VERTICAL)
+
+ for x, v in EFT_OPTIONS.items():
+ ch = wx.CheckBox(self, -1, v['name'])
+ self.options[x] = ch
+ if self.settings['options'] & x:
+ ch.SetValue(True)
+ bsizer.Add(ch, 1, wx.EXPAND | wx.TOP | wx.BOTTOM, 3)
+ mainSizer.Add(bsizer, 1, wx.EXPAND | wx.LEFT, 20)
buttonSizer = self.CreateButtonSizer(wx.OK | wx.CANCEL)
if buttonSizer:
mainSizer.Add(buttonSizer, 0, wx.EXPAND | wx.ALL, 5)
+ self.toggleOptions()
self.SetSizer(mainSizer)
self.Fit()
self.Center()
def Selected(self, event):
- self.copyFormat = event.GetSelection()
+ obj = event.GetEventObject()
+ format = obj.GetLabel()
+ self.copyFormat = self.copyFormats[format]
+ self.toggleOptions()
+ self.Fit()
+
+ def toggleOptions(self):
+ for ch in self.options.values():
+ ch.Enable(self.GetSelected() == CopySelectDialog.copyFormatEft)
def GetSelected(self):
return self.copyFormat
+
+ def GetOptions(self):
+ i = 0
+ for x, v in self.options.items():
+ if v.IsChecked():
+ i = i ^ x
+ return i
diff --git a/gui/devTools.py b/gui/devTools.py
index 98a14662c..44012128c 100644
--- a/gui/devTools.py
+++ b/gui/devTools.py
@@ -35,7 +35,7 @@ class DevTools(wx.Dialog):
def __init__(self, parent):
wx.Dialog.__init__(self, parent, id=wx.ID_ANY, title="Damage Pattern Editor", size=wx.Size(400, 240))
-
+ self.mainFrame = parent
self.block = False
self.SetSizeHints(wx.DefaultSize, wx.DefaultSize)
@@ -56,13 +56,19 @@ class DevTools(wx.Dialog):
self.fitTest = wx.Button(self, wx.ID_ANY, "Test fits", wx.DefaultPosition, wx.DefaultSize, 0)
mainSizer.Add(self.fitTest, 0, wx.EXPAND | wx.TOP | wx.BOTTOM, 5)
- self.fitTest .Bind(wx.EVT_BUTTON, self.fit_test)
+ self.fitTest.Bind(wx.EVT_BUTTON, self.fit_test)
+
+ self.cmdPrint = wx.Button(self, wx.ID_ANY, "Command Print", wx.DefaultPosition, wx.DefaultSize, 0)
+ mainSizer.Add(self.cmdPrint, 0, wx.EXPAND | wx.TOP | wx.BOTTOM, 5)
+
+ self.cmdPrint.Bind(wx.EVT_BUTTON, self.cmd_print)
self.SetSizer(mainSizer)
self.Layout()
self.CenterOnParent()
self.Show()
+ print(parent)
def objects_by_id(self, evt):
input = self.id_get.GetValue()
@@ -81,6 +87,11 @@ class DevTools(wx.Dialog):
else:
print(None)
+ def cmd_print(self, evt):
+ print("=" * 20)
+ for x in self.mainFrame.command.GetCommands():
+ print("{}{} {}".format("==> " if x == self.mainFrame.command.GetCurrentCommand() else "", x.GetName(), x))
+
def gc_collect(self, evt):
print(gc.collect())
print(gc.get_debug())
diff --git a/gui/esiFittings.py b/gui/esiFittings.py
index af3da77a5..20fc2ed74 100644
--- a/gui/esiFittings.py
+++ b/gui/esiFittings.py
@@ -15,7 +15,7 @@ import gui.globalEvents as GE
from logbook import Logger
from service.esi import Esi
from service.esiAccess import APIException
-from service.port import ESIExportException
+from service.port.esi import ESIExportException
pyfalog = Logger(__name__)
@@ -319,7 +319,7 @@ class SsoCharacterMgmt(wx.Dialog):
self.Centre(wx.BOTH)
def ssoLogin(self, event):
- if (self):
+ if self:
# todo: these events don't unbind properly when window is closed (?), hence the `if`. Figure out better way of doing this.
self.popCharList()
event.Skip()
@@ -384,7 +384,7 @@ class FittingsTreeView(wx.Panel):
dict = {}
fits = data
for fit in fits:
- if (fit['fitting_id'] in sEsi.fittings_deleted):
+ if fit['fitting_id'] in sEsi.fittings_deleted:
continue
ship = getItem(fit['ship_type_id'])
if ship is None:
diff --git a/gui/fitCommands/__init__.py b/gui/fitCommands/__init__.py
new file mode 100644
index 000000000..ae651d7bd
--- /dev/null
+++ b/gui/fitCommands/__init__.py
@@ -0,0 +1,34 @@
+from .guiToggleModuleState import GuiModuleStateChangeCommand
+from .guiAddModule import GuiModuleAddCommand
+from .guiRemoveModule import GuiModuleRemoveCommand
+from .guiAddCharge import GuiModuleAddChargeCommand
+from .guiSwapCloneModule import GuiModuleSwapOrCloneCommand
+from .guiRemoveCargo import GuiRemoveCargoCommand
+from .guiAddCargo import GuiAddCargoCommand
+from .guiRemoveImplant import GuiRemoveImplantCommand
+from .guiAddImplant import GuiAddImplantCommand
+from .guiAddBooster import GuiAddBoosterCommand
+from .guiRemoveBooster import GuiRemoveBoosterCommand
+from .guiAddCommand import GuiAddCommandCommand
+from .guiRemoveCommand import GuiRemoveCommandCommand
+from .guiSetMode import GuiSetModeCommand
+from .guiToggleCommand import GuiToggleCommandCommand
+from .guiAddProjected import GuiAddProjectedCommand
+from .guiRemoveProjected import GuiRemoveProjectedCommand
+from .guiCargoToModule import GuiCargoToModuleCommand
+from .guiModuleToCargo import GuiModuleToCargoCommand
+from .guiAddFighter import GuiAddFighterCommand
+from .guiRemoveFighter import GuiRemoveFighterCommand
+from .guiMetaSwap import GuiMetaSwapCommand
+from .guiToggleFighter import GuiToggleFighterCommand
+from .guiToggleImplant import GuiToggleImplantCommand
+from .guiToggleBooster import GuiToggleBoosterCommand
+from .guiAddDrone import GuiAddDroneCommand
+from .guiRemoveDrone import GuiRemoveDroneCommand
+from .guiChangeFighterQty import GuiChangeFighterQty
+from .guiChangeCargoQty import GuiChangeCargoQty
+from .guiChangeProjectedFitQty import GuiChangeProjectedFitQty
+from .guiChangeDroneQty import GuiChangeDroneQty
+from .guiChangeProjectedDroneQty import GuiChangeProjectedDroneQty
+from .guiToggleDrone import GuiToggleDroneCommand
+from .guiFitRename import GuiFitRenameCommand
\ No newline at end of file
diff --git a/gui/fitCommands/calc/__init__.py b/gui/fitCommands/calc/__init__.py
new file mode 100644
index 000000000..e69de29bb
diff --git a/gui/fitCommands/calc/fitAddBooster.py b/gui/fitCommands/calc/fitAddBooster.py
new file mode 100644
index 000000000..0470b556b
--- /dev/null
+++ b/gui/fitCommands/calc/fitAddBooster.py
@@ -0,0 +1,49 @@
+import wx
+import eos.db
+from logbook import Logger
+from eos.saveddata.booster import Booster
+pyfalog = Logger(__name__)
+
+
+class FitAddBoosterCommand(wx.Command):
+ """"
+ from sFit.addBooster
+ """
+ def __init__(self, fitID, itemID):
+ wx.Command.__init__(self, True)
+ self.fitID = fitID
+ self.itemID = itemID
+ self.new_index = None
+ self.old_item = None
+
+ def Do(self):
+ pyfalog.debug("Adding booster ({0}) to fit ID: {1}", self.itemID, self.fitID)
+
+ fit = eos.db.getFit(self.fitID)
+ item = eos.db.getItem(self.itemID, eager="attributes")
+
+ if next((x for x in fit.boosters if x.itemID == self.itemID), None):
+ return False # already have item in list of boosters
+
+ try:
+ booster = Booster(item)
+ except ValueError:
+ pyfalog.warning("Invalid item: {0}", self.itemID)
+ return False
+
+ self.old_item = fit.boosters.makeRoom(booster)
+ fit.boosters.append(booster)
+ self.new_index = fit.boosters.index(booster)
+ return True
+
+ def Undo(self):
+ if self.old_item:
+ # If we had an item in the slot previously, add it back.
+ cmd = FitAddBoosterCommand(self.fitID, self.old_item)
+ cmd.Do()
+ return True
+
+ from .fitRemoveBooster import FitRemoveBoosterCommand # Avoid circular import
+ cmd = FitRemoveBoosterCommand(self.fitID, self.new_index)
+ cmd.Do()
+ return True
diff --git a/gui/fitCommands/calc/fitAddCargo.py b/gui/fitCommands/calc/fitAddCargo.py
new file mode 100644
index 000000000..fff3abd26
--- /dev/null
+++ b/gui/fitCommands/calc/fitAddCargo.py
@@ -0,0 +1,43 @@
+import wx
+import eos.db
+from logbook import Logger
+from eos.saveddata.cargo import Cargo
+pyfalog = Logger(__name__)
+
+
+class FitAddCargoCommand(wx.Command):
+ """"
+ from sFit.addCargo
+ """
+ def __init__(self, fitID, itemID, amount=1, replace=False):
+ wx.Command.__init__(self, True, "Cargo add")
+ self.fitID = fitID
+ self.itemID = itemID
+ self.amount = amount # add x amount. If this goes over amount, removes stack
+ self.replace = replace # if this is false, we increment.
+
+ def Do(self):
+ pyfalog.debug("Adding cargo {0} (x{1}) for fit {2}", self.itemID, self.amount, self.fitID)
+
+ fit = eos.db.getFit(self.fitID)
+ item = eos.db.getItem(self.itemID)
+
+ cargo = next((x for x in fit.cargo if x.itemID == self.itemID), None)
+
+ if cargo is None:
+ cargo = Cargo(item)
+ fit.cargo.append(cargo)
+
+ if self.replace:
+ cargo.amount = self.amount
+ else:
+ cargo.amount += self.amount
+
+ eos.db.commit()
+ return True
+
+ def Undo(self):
+ from .fitRemoveCargo import FitRemoveCargoCommand # Avoid circular import
+ cmd = FitRemoveCargoCommand(self.fitID, self.itemID, self.amount)
+ cmd.Do()
+ return True
diff --git a/gui/fitCommands/calc/fitAddCommand.py b/gui/fitCommands/calc/fitAddCommand.py
new file mode 100644
index 000000000..660d7b5a0
--- /dev/null
+++ b/gui/fitCommands/calc/fitAddCommand.py
@@ -0,0 +1,47 @@
+import wx
+import eos.db
+from logbook import Logger
+pyfalog = Logger(__name__)
+
+
+class FitAddCommandCommand(wx.Command): # well that's an unfrtunate name
+ """"
+ from sFit.addCommand
+ """
+ def __init__(self, fitID, commandFitID):
+ wx.Command.__init__(self, True)
+ self.fitID = fitID
+ self.commandFitID = commandFitID
+
+ def Do(self):
+ pyfalog.debug("Projecting command fit ({0}) onto: {1}", self.fitID, self.commandFitID)
+ fit = eos.db.getFit(self.fitID)
+ command = eos.db.getFit(self.commandFitID)
+
+ if not command:
+ # if redoing when the command fit has been deleted, simply fail this command
+ return False
+
+ if command in fit.commandFits:
+ return
+
+ fit.commandFitDict[command.ID] = command
+
+ # this bit is required -- see GH issue # 83
+ eos.db.saveddata_session.flush()
+ eos.db.saveddata_session.refresh(command)
+
+ eos.db.commit()
+ return True
+
+ def Undo(self):
+ command = eos.db.getFit(self.commandFitID)
+
+ if not command:
+ # can't find the command fit, it must have been deleted. Just skip this undo
+ return True
+
+ from .fitRemoveCommand import FitRemoveCommandCommand
+ cmd = FitRemoveCommandCommand(self.fitID, self.commandFitID)
+ cmd.Do()
+ return True
diff --git a/gui/fitCommands/calc/fitAddDrone.py b/gui/fitCommands/calc/fitAddDrone.py
new file mode 100644
index 000000000..a7b8ed41c
--- /dev/null
+++ b/gui/fitCommands/calc/fitAddDrone.py
@@ -0,0 +1,49 @@
+import wx
+import eos.db
+from logbook import Logger
+from eos.saveddata.drone import Drone
+pyfalog = Logger(__name__)
+
+
+class FitAddDroneCommand(wx.Command):
+ """"
+ from sFit.addDrone
+ """
+ def __init__(self, fitID, itemID, amount=1, replace=False):
+ wx.Command.__init__(self, True, "Drone add")
+ self.fitID = fitID
+ self.itemID = itemID
+ self.amount = amount # add x amount. If this goes over amount, removes stack
+ self.replace = replace # if this is false, we increment.
+ self.index = None
+
+ def Do(self):
+ pyfalog.debug("Adding {0} drones ({1}) to fit ID: {2}", self.amount, self.itemID, self.fitID)
+
+ fit = eos.db.getFit(self.fitID)
+ item = eos.db.getItem(self.itemID, eager=("attributes", "group.category"))
+
+ for d in fit.drones.find(item):
+ if d is not None and d.amountActive == 0 and d.amount < max(5, fit.extraAttributes["maxActiveDrones"]):
+ drone = d
+ break
+ else:
+ try:
+ drone = Drone(item)
+ except ValueError:
+ pyfalog.warning("Invalid drone: {}", item)
+ return False
+
+ if not drone.fits(fit):
+ return False
+ fit.drones.append(drone)
+
+ drone.amount += self.amount
+ eos.db.commit()
+ self.index = fit.drones.index(drone)
+ return True
+
+ def Undo(self):
+ from .fitRemoveDrone import FitRemoveDroneCommand # Avoid circular import
+ cmd = FitRemoveDroneCommand(self.fitID, self.index, self.amount)
+ return cmd.Do()
diff --git a/gui/fitCommands/calc/fitAddFighter.py b/gui/fitCommands/calc/fitAddFighter.py
new file mode 100644
index 000000000..42ca59be1
--- /dev/null
+++ b/gui/fitCommands/calc/fitAddFighter.py
@@ -0,0 +1,48 @@
+import wx
+import eos.db
+from logbook import Logger
+from eos.saveddata.fighter import Fighter
+pyfalog = Logger(__name__)
+
+
+class FitAddFighterCommand(wx.Command):
+ """"
+ from sFit.addFighter
+ """
+ def __init__(self, fitID, itemID):
+ wx.Command.__init__(self, True, "Cargo add")
+ self.fitID = fitID
+ self.itemID = itemID
+ self.new_index = None
+
+ def Do(self):
+ fit = eos.db.getFit(self.fitID)
+ item = eos.db.getItem(self.itemID, eager=("attributes", "group.category"))
+
+ try:
+ fighter = Fighter(item)
+ except ValueError:
+ pyfalog.warning("Invalid fighter: {}", item)
+ return False
+
+ if not fighter.fits(fit):
+ return False
+
+ used = fit.getSlotsUsed(fighter.slot)
+ total = fit.getNumSlots(fighter.slot)
+
+ if used >= total:
+ fighter.active = False
+
+ fit.fighters.append(fighter)
+ self.new_index = fit.fighters.index(fighter)
+
+ eos.db.commit()
+
+ return True
+
+ def Undo(self):
+ from .fitRemoveFighter import FitRemoveFighterCommand # Avoid circular import
+ cmd = FitRemoveFighterCommand(self.fitID, self.new_index)
+ cmd.Do()
+ return True
diff --git a/gui/fitCommands/calc/fitAddImplant.py b/gui/fitCommands/calc/fitAddImplant.py
new file mode 100644
index 000000000..4e3fd372c
--- /dev/null
+++ b/gui/fitCommands/calc/fitAddImplant.py
@@ -0,0 +1,48 @@
+import wx
+import eos.db
+from logbook import Logger
+from eos.saveddata.implant import Implant
+pyfalog = Logger(__name__)
+
+
+class FitAddImplantCommand(wx.Command):
+ """"
+ from sFit.addImplant
+ """
+ def __init__(self, fitID, itemID):
+ wx.Command.__init__(self, True, "Cargo add")
+ self.fitID = fitID
+ self.itemID = itemID
+ self.old_item = None
+
+ def Do(self):
+ pyfalog.debug("Adding implant to fit ({0}) for item ID: {1}", self.fitID, self.itemID)
+
+ fit = eos.db.getFit(self.fitID)
+ item = eos.db.getItem(self.itemID, eager="attributes")
+
+ if next((x for x in fit.implants if x.itemID == self.itemID), None):
+ return False # already have item in list of implants
+
+ try:
+ implant = Implant(item)
+ except ValueError:
+ pyfalog.warning("Invalid item: {0}", self.itemID)
+ return False
+
+ self.old_item = fit.implants.makeRoom(implant)
+ fit.implants.append(implant)
+ self.new_index = fit.implants.index(implant)
+ return True
+
+ def Undo(self):
+ if self.old_item:
+ # If we had an item in the slot previously, add it back.
+ cmd = FitAddImplantCommand(self.fitID, self.old_item)
+ cmd.Do()
+ return True
+
+ from .fitRemoveImplant import FitRemoveImplantCommand # Avoid circular import
+ cmd = FitRemoveImplantCommand(self.fitID, self.new_index)
+ cmd.Do()
+ return True
diff --git a/gui/fitCommands/calc/fitAddModule.py b/gui/fitCommands/calc/fitAddModule.py
new file mode 100644
index 000000000..f0d421baf
--- /dev/null
+++ b/gui/fitCommands/calc/fitAddModule.py
@@ -0,0 +1,81 @@
+import wx
+from eos.saveddata.module import Module, State
+import eos.db
+from logbook import Logger
+pyfalog = Logger(__name__)
+
+
+class FitAddModuleCommand(wx.Command):
+ """"
+ Fitting command that appends a module to a fit using the first available slot. In the case of a Subsystem, it checks
+ if there is already a subsystem with the same slot, and runs the replace command instead.
+
+ from sFit.appendModule
+ """
+ def __init__(self, fitID, itemID, mutaplasmidID=None, baseID=None):
+ wx.Command.__init__(self, True)
+ self.fitID = fitID
+ self.itemID = itemID
+ self.mutaplasmidID = mutaplasmidID
+ self.baseID = baseID
+ self.new_position = None
+ self.change = None
+ self.replace_cmd = None
+
+ def Do(self):
+ fitID = self.fitID
+ itemID = self.itemID
+ fit = eos.db.getFit(fitID)
+ item = eos.db.getItem(itemID, eager=("attributes", "group.category"))
+
+ bItem = eos.db.getItem(self.baseID) if self.baseID else None
+ mItem = next((x for x in bItem.mutaplasmids if x.ID == self.mutaplasmidID)) if self.mutaplasmidID else None
+
+ try:
+ self.module = Module(item, bItem, mItem)
+ except ValueError:
+ pyfalog.warning("Invalid module: {}", item)
+ return False
+
+ # If subsystem and we need to replace, run the replace command instead and bypass the rest of this command
+ if self.module.item.category.name == "Subsystem":
+ for mod in fit.modules:
+ if mod.getModifiedItemAttr("subSystemSlot") == self.module.getModifiedItemAttr("subSystemSlot"):
+ from .fitReplaceModule import FitReplaceModuleCommand
+ self.replace_cmd = FitReplaceModuleCommand(self.fitID, mod.modPosition, itemID)
+ return self.replace_cmd.Do()
+
+ if self.module.fits(fit):
+ pyfalog.debug("Adding {} as module for fit {}", self.module, fit)
+ self.module.owner = fit
+ numSlots = len(fit.modules)
+ fit.modules.append(self.module)
+ if self.module.isValidState(State.ACTIVE):
+ self.module.state = State.ACTIVE
+
+ # todo: fix these
+ # As some items may affect state-limiting attributes of the ship, calculate new attributes first
+ # self.recalc(fit)
+ # Then, check states of all modules and change where needed. This will recalc if needed
+ # self.checkStates(fit, m)
+
+ # fit.fill()
+ eos.db.commit()
+
+ self.change = numSlots != len(fit.modules)
+ self.new_position = self.module.modPosition
+ else:
+ return False
+
+ return True
+
+ def Undo(self):
+ # We added a subsystem module, which actually ran the replace command. Run the undo for that guy instead
+ if self.replace_cmd:
+ return self.replace_cmd.Undo()
+
+ from .fitRemoveModule import FitRemoveModuleCommand # Avoid circular import
+ if self.new_position:
+ cmd = FitRemoveModuleCommand(self.fitID, [self.new_position])
+ cmd.Do()
+ return True
diff --git a/gui/fitCommands/calc/fitAddProjectedDrone.py b/gui/fitCommands/calc/fitAddProjectedDrone.py
new file mode 100644
index 000000000..6c2ddf270
--- /dev/null
+++ b/gui/fitCommands/calc/fitAddProjectedDrone.py
@@ -0,0 +1,45 @@
+import wx
+import eos.db
+from logbook import Logger
+from eos.saveddata.drone import Drone
+pyfalog = Logger(__name__)
+
+
+class FitAddProjectedDroneCommand(wx.Command):
+ """"
+ from sFit.project
+ """
+ def __init__(self, fitID, itemID):
+ wx.Command.__init__(self, True)
+ self.fitID = fitID
+ self.itemID = itemID
+ self.index = None
+
+ def Do(self):
+ pyfalog.debug("Projecting fit ({0}) onto: {1}", self.fitID, self.itemID)
+ fit = eos.db.getFit(self.fitID)
+ item = eos.db.getItem(self.itemID)
+
+ drone = None
+ for d in fit.projectedDrones.find(item):
+ if d is None or d.amountActive == d.amount or d.amount >= 5:
+ drone = d
+ break
+
+ if drone is None:
+ drone = Drone(item)
+ if not drone.item.isType("projected"):
+ return False
+ fit.projectedDrones.append(drone)
+
+ self.index = fit.projectedDrones.index(drone)
+ drone.amount += 1
+
+ eos.db.commit()
+ return True
+
+ def Undo(self):
+ from gui.fitCommands.calc.fitRemoveProjectedDrone import FitRemoveProjectedDroneCommand # avoids circular import
+ cmd = FitRemoveProjectedDroneCommand(self.fitID, self.index)
+ cmd.Do()
+ return True
diff --git a/gui/fitCommands/calc/fitAddProjectedEnv.py b/gui/fitCommands/calc/fitAddProjectedEnv.py
new file mode 100644
index 000000000..f896a77df
--- /dev/null
+++ b/gui/fitCommands/calc/fitAddProjectedEnv.py
@@ -0,0 +1,50 @@
+import wx
+from eos.saveddata.module import Module, State
+import eos.db
+from logbook import Logger
+pyfalog = Logger(__name__)
+
+
+class FitAddProjectedEnvCommand(wx.Command):
+ """"
+ from sFit.project
+ """
+ def __init__(self, fitID, itemID):
+ wx.Command.__init__(self, True)
+ self.fitID = fitID
+ self.itemID = itemID
+ self.new_index = None
+ self.old_item = None
+
+ def Do(self):
+ pyfalog.debug("Projecting fit ({0}) onto: {1}", self.fitID, self.itemID)
+ fit = eos.db.getFit(self.fitID)
+ item = eos.db.getItem(self.itemID, eager=("attributes", "group.category"))
+
+ try:
+ module = Module(item)
+ except ValueError:
+ return False
+
+ # todo: thing to check for existing environmental effects
+
+ self.old_item = fit.projectedModules.makeRoom(module)
+
+ module.state = State.ONLINE
+ fit.projectedModules.append(module)
+
+ eos.db.commit()
+ self.new_index = fit.projectedModules.index(module)
+ return True
+
+ def Undo(self):
+ if self.old_item:
+ # If we had an item in the slot previously, add it back.
+ cmd = FitAddProjectedEnvCommand(self.fitID, self.old_item)
+ cmd.Do()
+ return True
+ from gui.fitCommands.calc.fitRemoveProjectedEnv import FitRemoveProjectedEnvCommand # avoids circular import
+ cmd = FitRemoveProjectedEnvCommand(self.fitID, self.itemID)
+ cmd.Do()
+
+ return True
diff --git a/gui/fitCommands/calc/fitAddProjectedFighter.py b/gui/fitCommands/calc/fitAddProjectedFighter.py
new file mode 100644
index 000000000..17d50e14b
--- /dev/null
+++ b/gui/fitCommands/calc/fitAddProjectedFighter.py
@@ -0,0 +1,40 @@
+import wx
+import eos.db
+from logbook import Logger
+from eos.saveddata.fighter import Fighter
+pyfalog = Logger(__name__)
+
+
+class FitAddProjectedFighterCommand(wx.Command):
+ """"
+ from sFit.project
+ """
+ def __init__(self, fitID, itemID):
+ wx.Command.__init__(self, True)
+ self.fitID = fitID
+ self.itemID = itemID
+ self.new_index = None
+
+ def Do(self):
+ pyfalog.debug("Projecting fit ({0}) onto: {1}", self.fitID, self.itemID)
+ fit = eos.db.getFit(self.fitID)
+ item = eos.db.getItem(self.itemID, eager=("attributes", "group.category"))
+
+ try:
+ fighter = Fighter(item)
+ except ValueError:
+ return False
+
+ fit.projectedFighters.append(fighter)
+ # sometimes fighters aren't added because they aren't valid projectionable ones. todo: move that logic into here
+ if fighter in fit.projectedFighters:
+ eos.db.commit()
+ self.new_index = fit.projectedFighters.index(fighter)
+ return True
+ return False
+
+ def Undo(self):
+ from gui.fitCommands.calc.fitRemoveProjectedFighter import FitRemoveProjectedFighterCommand # avoids circular import
+ cmd = FitRemoveProjectedFighterCommand(self.fitID, self.new_index)
+ cmd.Do()
+ return True
diff --git a/gui/fitCommands/calc/fitAddProjectedFit.py b/gui/fitCommands/calc/fitAddProjectedFit.py
new file mode 100644
index 000000000..cd1e1a7f9
--- /dev/null
+++ b/gui/fitCommands/calc/fitAddProjectedFit.py
@@ -0,0 +1,39 @@
+import wx
+import eos.db
+from logbook import Logger
+pyfalog = Logger(__name__)
+
+
+class FitAddProjectedFitCommand(wx.Command):
+ """"
+ from sFit.project
+ """
+ def __init__(self, fitID, projectedFitID):
+ wx.Command.__init__(self, True)
+ self.fitID = fitID
+ self.projectedFitID = projectedFitID
+ self.new_index = None
+ self.old_item = None
+
+ def Do(self):
+ pyfalog.debug("Projecting fit ({0}) onto: {1}", self.fitID, self.projectedFitID)
+ fit = eos.db.getFit(self.fitID)
+ projectedFit = eos.db.getFit(self.projectedFitID)
+
+ if projectedFit is None or projectedFit in fit.projectedFits:
+ return False
+
+ fit.projectedFitDict[projectedFit.ID] = projectedFit
+
+ # this bit is required -- see GH issue # 83
+ eos.db.saveddata_session.flush()
+ eos.db.saveddata_session.refresh(projectedFit)
+
+ eos.db.commit()
+ return True
+
+ def Undo(self):
+ from gui.fitCommands.calc.fitRemoveProjectedFit import FitRemoveProjectedFitCommand # avoids circular import
+ cmd = FitRemoveProjectedFitCommand(self.fitID, self.projectedFitID)
+ cmd.Do()
+ return True
diff --git a/gui/fitCommands/calc/fitAddProjectedModule.py b/gui/fitCommands/calc/fitAddProjectedModule.py
new file mode 100644
index 000000000..6d2ad3f4f
--- /dev/null
+++ b/gui/fitCommands/calc/fitAddProjectedModule.py
@@ -0,0 +1,43 @@
+import wx
+import eos.db
+from logbook import Logger
+from eos.saveddata.module import Module, State
+pyfalog = Logger(__name__)
+
+
+class FitAddProjectedModuleCommand(wx.Command):
+ """"
+ from sFit.project
+ """
+ def __init__(self, fitID, itemID):
+ wx.Command.__init__(self, True)
+ self.fitID = fitID
+ self.itemID = itemID
+ self.new_index = None
+
+ def Do(self):
+ pyfalog.debug("Projecting fit ({0}) onto: {1}", self.fitID, self.itemID)
+ fit = eos.db.getFit(self.fitID)
+ item = eos.db.getItem(self.itemID, eager=("attributes", "group.category"))
+
+ try:
+ module = Module(item)
+ if not module.item.isType("projected"):
+ return False
+ except ValueError:
+ return False
+
+ module.state = State.ACTIVE
+ if not module.canHaveState(module.state, fit):
+ module.state = State.OFFLINE
+ fit.projectedModules.append(module)
+
+ eos.db.commit()
+ self.new_index = fit.projectedModules.index(module)
+ return True
+
+ def Undo(self):
+ from gui.fitCommands.calc.fitRemoveProjectedModule import FitRemoveProjectedModuleCommand # avoids circular import
+ cmd = FitRemoveProjectedModuleCommand(self.fitID, self.new_index)
+ cmd.Do()
+ return True
diff --git a/gui/fitCommands/calc/fitChangeCargoQty.py b/gui/fitCommands/calc/fitChangeCargoQty.py
new file mode 100644
index 000000000..2ba30e085
--- /dev/null
+++ b/gui/fitCommands/calc/fitChangeCargoQty.py
@@ -0,0 +1,27 @@
+import wx
+import eos.db
+from logbook import Logger
+pyfalog = Logger(__name__)
+
+
+class FitChangeCargoQty(wx.Command):
+ def __init__(self, fitID, position, amount=1):
+ wx.Command.__init__(self, True, "Drone add")
+ self.fitID = fitID
+ self.position = position
+ self.amount = amount # add x amount. If this goes over amount, removes stack
+ self.old_amount = None
+
+ def Do(self):
+ pyfalog.debug("Changing cargo ({0}) for fit ({1}) to amount: {2}", self.position, self.fitID, self.amount)
+ fit = eos.db.getFit(self.fitID)
+ cargo = fit.cargo[self.position]
+ self.old_amount = cargo.amount
+ cargo.amount = self.amount
+
+ eos.db.commit()
+ return True
+
+ def Undo(self):
+ cmd = FitChangeCargoQty(self.fitID, self.position, self.old_amount)
+ return cmd.Do()
diff --git a/gui/fitCommands/calc/fitChangeDroneQty.py b/gui/fitCommands/calc/fitChangeDroneQty.py
new file mode 100644
index 000000000..8f7e8e801
--- /dev/null
+++ b/gui/fitCommands/calc/fitChangeDroneQty.py
@@ -0,0 +1,27 @@
+import wx
+import eos.db
+from logbook import Logger
+pyfalog = Logger(__name__)
+
+
+class FitChangeDroneQty(wx.Command):
+ def __init__(self, fitID, position, amount=1):
+ wx.Command.__init__(self, True, "Drone add")
+ self.fitID = fitID
+ self.position = position
+ self.amount = amount # add x amount. If this goes over amount, removes stack
+ self.old_amount = None
+
+ def Do(self):
+ pyfalog.debug("Changing active fighters ({0}) for fit ({1}) to amount: {2}", self.position, self.fitID, self.amount)
+ fit = eos.db.getFit(self.fitID)
+ drone = fit.drones[self.position]
+ self.old_amount = drone.amount
+ drone.amount = self.amount
+
+ eos.db.commit()
+ return True
+
+ def Undo(self):
+ cmd = FitChangeDroneQty(self.fitID, self.position, self.old_amount)
+ return cmd.Do()
diff --git a/gui/fitCommands/calc/fitChangeFighterQty.py b/gui/fitCommands/calc/fitChangeFighterQty.py
new file mode 100644
index 000000000..4cef20034
--- /dev/null
+++ b/gui/fitCommands/calc/fitChangeFighterQty.py
@@ -0,0 +1,30 @@
+import wx
+import eos.db
+from logbook import Logger
+pyfalog = Logger(__name__)
+
+
+class FitChangeFighterQty(wx.Command):
+ """"
+ from sFit.changeActiveFighters
+ """
+ def __init__(self, fitID, position, amount=1):
+ wx.Command.__init__(self, True, "Drone add")
+ self.fitID = fitID
+ self.position = position
+ self.amount = amount # add x amount. If this goes over amount, removes stack
+ self.old_amount = None
+
+ def Do(self):
+ pyfalog.debug("Changing active fighters ({0}) for fit ({1}) to amount: {2}", self.position, self.fitID, self.amount)
+ fit = eos.db.getFit(self.fitID)
+ fighter = fit.fighters[self.position]
+ self.old_amount = fighter.amountActive
+ fighter.amountActive = self.amount
+
+ eos.db.commit()
+ return True
+
+ def Undo(self):
+ cmd = FitChangeFighterQty(self.fitID, self.position, self.old_amount)
+ return cmd.Do()
diff --git a/gui/fitCommands/calc/fitChangeProjectedDroneQty.py b/gui/fitCommands/calc/fitChangeProjectedDroneQty.py
new file mode 100644
index 000000000..3ca926c17
--- /dev/null
+++ b/gui/fitCommands/calc/fitChangeProjectedDroneQty.py
@@ -0,0 +1,27 @@
+import wx
+import eos.db
+from logbook import Logger
+pyfalog = Logger(__name__)
+
+
+class FitChangeProjectedDroneQty(wx.Command):
+ def __init__(self, fitID, position, amount=1):
+ wx.Command.__init__(self, True, "Drone add")
+ self.fitID = fitID
+ self.position = position
+ self.amount = amount # add x amount. If this goes over amount, removes stack
+ self.old_amount = None
+
+ def Do(self):
+ pyfalog.debug("Changing active fighters ({0}) for fit ({1}) to amount: {2}", self.position, self.fitID, self.amount)
+ fit = eos.db.getFit(self.fitID)
+ drone = fit.projectedDrones[self.position]
+ self.old_amount = drone.amount
+ drone.amount = self.amount
+
+ eos.db.commit()
+ return True
+
+ def Undo(self):
+ cmd = FitChangeProjectedDroneQty(self.fitID, self.position, self.old_amount)
+ return cmd.Do()
diff --git a/gui/fitCommands/calc/fitChangeProjectedFitQty.py b/gui/fitCommands/calc/fitChangeProjectedFitQty.py
new file mode 100644
index 000000000..aa75e8491
--- /dev/null
+++ b/gui/fitCommands/calc/fitChangeProjectedFitQty.py
@@ -0,0 +1,36 @@
+import wx
+import eos.db
+from logbook import Logger
+pyfalog = Logger(__name__)
+
+
+class FitChangeProjectedFitQty(wx.Command):
+ """"
+ from sFit.changeAmount
+ """
+ def __init__(self, fitID, pfitID, amount=1):
+ wx.Command.__init__(self, True, "Drone add")
+ self.fitID = fitID
+ self.pfitID = pfitID
+ self.amount = amount
+ self.old_amount = None
+
+ def Do(self):
+ pfit = eos.db.getFit(self.pfitID)
+
+ if not pfit: # fit was deleted
+ return False
+
+ amount = min(20, max(1, self.amount)) # 1 <= a <= 20
+
+ projectionInfo = pfit.getProjectionInfo(self.fitID)
+ if projectionInfo:
+ self.old_amount = projectionInfo.amount
+ projectionInfo.amount = amount
+
+ eos.db.commit()
+ return True
+
+ def Undo(self):
+ cmd = FitChangeProjectedFitQty(self.fitID, self.pfitID, self.old_amount)
+ return cmd.Do()
diff --git a/gui/fitCommands/calc/fitChangeState.py b/gui/fitCommands/calc/fitChangeState.py
new file mode 100644
index 000000000..4354f6c32
--- /dev/null
+++ b/gui/fitCommands/calc/fitChangeState.py
@@ -0,0 +1,75 @@
+import wx
+from logbook import Logger
+
+import gui.mainFrame
+from eos.saveddata.module import Module
+from service.fit import Fit
+import eos.db
+
+pyfalog = Logger(__name__)
+
+
+class FitChangeStatesCommand(wx.Command):
+ """
+ Fitting command that trys to change the state of modules in [positions]. We use the base module to determine the
+ state that we will try to apply for all modules.
+
+
+ """
+ def __init__(self, fitID, baseModPos, positions, click):
+ # todo: instead of modules, needs to be positions. Dead objects are a thing
+ wx.Command.__init__(self, True, "Module State Change")
+ self.mainFrame = gui.mainFrame.MainFrame.getInstance()
+ self.sFit = Fit.getInstance()
+ self.fitID = fitID
+ self.baseModPos = baseModPos
+ self.positions = positions
+ self.click = click
+ self.changed = None
+ self.old_states = {}
+
+ def Do(self):
+ fit = eos.db.getFit(self.fitID)
+
+ baseMod = fit.modules[self.baseModPos]
+
+ # make sure positions only include non-empty positions
+ self.positions = [x for x in self.positions if not fit.modules[x].isEmpty]
+
+ for x in self.positions:
+ self.old_states[x] = fit.modules[x].state
+
+ proposedState = Module.getProposedState(baseMod, self.click)
+ pyfalog.debug("Attempting to change modules to {}", proposedState)
+
+ if proposedState != baseMod.state:
+ pyfalog.debug("Toggle {} state: {} for fit ID: {}", baseMod, proposedState, self.fitID)
+
+ self.changed = True
+ baseMod.state = proposedState
+ for i in [x for x in self.positions if x != self.baseModPos]: # dont consider base module position
+ mod = fit.modules[i]
+ p = Module.getProposedState(mod, self.click, proposedState)
+ mod.state = p
+ if p != mod.state:
+ pyfalog.debug("Toggle {} state: {} for fit ID: {}", mod, p, self.fitID)
+ self.changed = True
+
+ # if we haven't change the state (eg, overheat -> overheat), simply fail the command
+ if self.changed:
+ eos.db.commit()
+ # As some items may affect state-limiting attributes of the ship, calculate new attributes first
+ # self.recalc(fit)
+ # # Then, check states of all modules and change where needed. This will recalc if needed
+ # self.checkStates(fit, base)
+ return True
+ return False
+
+ def Undo(self):
+ # todo: some sanity checking to make sure that we are applying state back to the same modules?
+ fit = self.sFit.getFit(self.fitID)
+ for k, v in self.old_states.items():
+ mod = fit.modules[k]
+ pyfalog.debug("Reverting {} to state {} for fit ID", mod, v, self.fitID)
+ mod.state = v
+ return True
diff --git a/gui/fitCommands/calc/fitCloneModule.py b/gui/fitCommands/calc/fitCloneModule.py
new file mode 100644
index 000000000..519b653ef
--- /dev/null
+++ b/gui/fitCommands/calc/fitCloneModule.py
@@ -0,0 +1,44 @@
+import wx
+import eos.db
+from logbook import Logger
+import copy
+pyfalog = Logger(__name__)
+
+
+class FitCloneModuleCommand(wx.Command):
+ """
+ Clone a module from src to dst
+ This will overwrite dst! Checking for empty module must be
+ done at a higher level
+
+ from sFit.cloneModule
+ """
+ def __init__(self, fitID, src, dst):
+ wx.Command.__init__(self, True, "Module Clone")
+ self.fitID = fitID
+ self.src = src
+ self.dst = dst
+
+ def Do(self):
+ fit = eos.db.getFit(self.fitID)
+ # Gather modules
+ srcMod = fit.modules[self.src]
+ dstMod = fit.modules[self.dst] # should be a placeholder module
+
+ new = copy.deepcopy(srcMod)
+ new.owner = fit
+ if new.fits(fit):
+ pyfalog.debug("Cloning {} from source {} to destination {} for fit ID {}", srcMod, self.src, self.dst, self.fitID)
+ # insert copy if module meets hardpoint restrictions
+ fit.modules.remove(dstMod)
+ fit.modules.insert(self.dst, new)
+
+ eos.db.commit()
+ return True
+ return False
+
+ def Undo(self):
+ from .fitRemoveModule import FitRemoveModuleCommand # Avoid circular import
+ cmd = FitRemoveModuleCommand(self.fitID, [self.dst])
+ cmd.Do()
+ return True
diff --git a/gui/fitCommands/calc/fitRemoveBooster.py b/gui/fitCommands/calc/fitRemoveBooster.py
new file mode 100644
index 000000000..14733c93b
--- /dev/null
+++ b/gui/fitCommands/calc/fitRemoveBooster.py
@@ -0,0 +1,30 @@
+import wx
+import eos.db
+from logbook import Logger
+pyfalog = Logger(__name__)
+
+
+class FitRemoveBoosterCommand(wx.Command):
+ """"
+ from sFit.removeBooster
+ """
+ def __init__(self, fitID, position):
+ wx.Command.__init__(self, True, "Implant remove")
+ self.fitID = fitID
+ self.position = position
+ self.old = None
+
+ def Do(self):
+ pyfalog.debug("Removing booster from position ({0}) for fit ID: {1}", self.position, self.fitID)
+
+ fit = eos.db.getFit(self.fitID)
+ booster = fit.boosters[self.position]
+ self.old = booster.itemID
+ fit.boosters.remove(booster)
+ return True
+
+ def Undo(self):
+ from .fitAddBooster import FitAddBoosterCommand # Avoid circular import
+ cmd = FitAddBoosterCommand(self.fitID, self.old)
+ cmd.Do()
+ return True
diff --git a/gui/fitCommands/calc/fitRemoveCargo.py b/gui/fitCommands/calc/fitRemoveCargo.py
new file mode 100644
index 000000000..779c7c757
--- /dev/null
+++ b/gui/fitCommands/calc/fitRemoveCargo.py
@@ -0,0 +1,47 @@
+import wx
+import eos.db
+from logbook import Logger
+pyfalog = Logger(__name__)
+
+
+class FitRemoveCargoCommand(wx.Command):
+ """"
+ Fitting command that sets the amount for an item within the cargo.
+
+ from sFit.removeCargo
+ """
+ def __init__(self, fitID, itemID, amount=1, stack=False):
+ wx.Command.__init__(self, True, "Cargo remove")
+ self.fitID = fitID
+ self.itemID = itemID
+ self.stack = stack # remove entire stack
+ self.amount = amount # remove x amount. If this goes over amount, removes stack
+ self.old_amount = None
+
+ def Do(self):
+ pyfalog.debug("Removing cargo {0} (x{1}) for fit {2}", self.itemID, self.amount, self.fitID)
+ fit = eos.db.getFit(self.fitID)
+ cargo = next((x for x in fit.cargo if x.itemID == self.itemID), None)
+
+ if cargo is None:
+ return False
+
+ self.old_amount = cargo.amount
+
+ if self.amount >= cargo.amount:
+ self.stack = True # set to full stack, this allows easier logic in the Undo function
+
+ if self.stack or self.amount >= cargo.amount:
+ fit.cargo.remove(cargo)
+ eos.db.commit()
+ return True
+
+ cargo.amount -= self.amount
+ eos.db.commit()
+ return True
+
+ def Undo(self):
+ from gui.fitCommands.calc.fitAddCargo import FitAddCargoCommand # Avoid circular import
+ cmd = FitAddCargoCommand(self.fitID, self.itemID, self.old_amount, True)
+ cmd.Do()
+ return True
diff --git a/gui/fitCommands/calc/fitRemoveCommand.py b/gui/fitCommands/calc/fitRemoveCommand.py
new file mode 100644
index 000000000..29e681b60
--- /dev/null
+++ b/gui/fitCommands/calc/fitRemoveCommand.py
@@ -0,0 +1,37 @@
+import wx
+import eos.db
+from logbook import Logger
+pyfalog = Logger(__name__)
+
+
+class FitRemoveCommandCommand(wx.Command): # well that's an unfortunate name
+ """"
+ from sFit.removeCommand
+ """
+ def __init__(self, fitID, commandFitID):
+ wx.Command.__init__(self, True)
+ self.fitID = fitID
+ self.commandFitID = commandFitID
+
+ def Do(self):
+ pyfalog.debug("Removing command projection from fit ({0}) for: {1}", self.fitID, self.commandFitID)
+ fit = eos.db.getFit(self.fitID)
+ command = eos.db.getFit(self.commandFitID)
+ if not command:
+ return False
+ del fit.commandFitDict[command.ID]
+
+ eos.db.commit()
+ return True
+
+ def Undo(self):
+ command = eos.db.getFit(self.commandFitID)
+
+ if not command:
+ # can't find the command fit, it must have been deleted. Just skip this undo
+ return True
+
+ from .fitAddCommand import FitAddCommandCommand
+ cmd = FitAddCommandCommand(self.fitID, self.commandFitID)
+ cmd.Do()
+ return True
diff --git a/gui/fitCommands/calc/fitRemoveDrone.py b/gui/fitCommands/calc/fitRemoveDrone.py
new file mode 100644
index 000000000..4e1fb0a6a
--- /dev/null
+++ b/gui/fitCommands/calc/fitRemoveDrone.py
@@ -0,0 +1,43 @@
+import wx
+import eos.db
+from logbook import Logger
+pyfalog = Logger(__name__)
+
+
+class FitRemoveDroneCommand(wx.Command):
+ """"
+ from sFit.addDrone
+ """
+ def __init__(self, fitID, position, amount=1):
+ wx.Command.__init__(self, True, "Drone add")
+ self.fitID = fitID
+ self.position = position
+ self.amount = amount # add x amount. If this goes over amount, removes stack
+ self.removed_item = None
+
+ def Do(self):
+ pyfalog.debug("Removing {0} drones for fit ID: {1}", self.amount, self.fitID)
+ fit = eos.db.getFit(self.fitID)
+ d = fit.drones[self.position]
+ d.amount -= self.amount
+ if d.amountActive > 0:
+ d.amountActive -= self.amount
+
+ if d.amount == 0:
+ self.removed_item = d.itemID
+ del fit.drones[self.position]
+
+ eos.db.commit()
+ return True
+
+ def Undo(self):
+ if self.removed_item:
+ from .fitAddDrone import FitAddDroneCommand # Avoid circular import
+ cmd = FitAddDroneCommand(self.fitID, self.removed_item, self.amount)
+ return cmd.Do()
+ else:
+ fit = eos.db.getFit(self.fitID)
+ d = fit.drones[self.position]
+ d.amount += self.amount
+ eos.db.commit()
+ return True
diff --git a/gui/fitCommands/calc/fitRemoveFighter.py b/gui/fitCommands/calc/fitRemoveFighter.py
new file mode 100644
index 000000000..da186bd48
--- /dev/null
+++ b/gui/fitCommands/calc/fitRemoveFighter.py
@@ -0,0 +1,34 @@
+import wx
+from logbook import Logger
+
+import eos.db
+
+pyfalog = Logger(__name__)
+
+
+class FitRemoveFighterCommand(wx.Command):
+ """"
+ Fitting command that removes a module at a specified positions
+
+ from sFit.removeFighter
+ """
+ def __init__(self, fitID: int, position: int):
+ wx.Command.__init__(self, True)
+ self.fitID = fitID
+ self.position = position
+ self.change = None
+ self.removed_item = None
+
+ def Do(self):
+ fitID = self.fitID
+ fit = eos.db.getFit(fitID)
+ f = fit.fighters[self.position]
+ fit.fighters.remove(f)
+ self.removed_item = f.itemID
+ eos.db.commit()
+ return True
+
+ def Undo(self):
+ from gui.fitCommands.calc.fitAddFighter import FitAddFighterCommand # avoids circular import
+ cmd = FitAddFighterCommand(self.fitID, self.removed_item)
+ return cmd.Do()
diff --git a/gui/fitCommands/calc/fitRemoveImplant.py b/gui/fitCommands/calc/fitRemoveImplant.py
new file mode 100644
index 000000000..d0f8101cd
--- /dev/null
+++ b/gui/fitCommands/calc/fitRemoveImplant.py
@@ -0,0 +1,32 @@
+import wx
+import eos.db
+from logbook import Logger
+pyfalog = Logger(__name__)
+
+
+class FitRemoveImplantCommand(wx.Command):
+ """"
+ Fitting command that sets the amount for an item within the cargo.
+
+ from sFit.removeImplant
+ """
+ def __init__(self, fitID, position):
+ wx.Command.__init__(self, True, "Implant remove")
+ self.fitID = fitID
+ self.position = position
+ self.old_implant = None
+
+ def Do(self):
+ pyfalog.debug("Removing implant from position ({0}) for fit ID: {1}", self.position, self.fitID)
+
+ fit = eos.db.getFit(self.fitID)
+ implant = fit.implants[self.position]
+ self.old_implant = implant.itemID
+ fit.implants.remove(implant)
+ return True
+
+ def Undo(self):
+ from gui.fitCommands.calc.fitAddImplant import FitAddImplantCommand # Avoid circular import
+ cmd = FitAddImplantCommand(self.fitID, self.old_implant)
+ cmd.Do()
+ return True
diff --git a/gui/fitCommands/calc/fitRemoveModule.py b/gui/fitCommands/calc/fitRemoveModule.py
new file mode 100644
index 000000000..5134f2f1b
--- /dev/null
+++ b/gui/fitCommands/calc/fitRemoveModule.py
@@ -0,0 +1,59 @@
+import wx
+
+from gui.fitCommands.helpers import ModuleInfoCache
+import eos.db
+from logbook import Logger
+pyfalog = Logger(__name__)
+
+
+class FitRemoveModuleCommand(wx.Command):
+ """"
+ Fitting command that removes a module at a specified positions
+
+ from sFit.removeModule
+ """
+ def __init__(self, fitID: int, positions: list = None):
+ wx.Command.__init__(self, True)
+ self.fitID = fitID
+ self.positions = positions
+ self.modCache = []
+ self.change = None
+
+ def Do(self):
+ fitID = self.fitID
+ fit = eos.db.getFit(fitID)
+
+ pyfalog.debug("Removing module from position ({0}) for fit ID: {1}", self.positions, fitID)
+
+ for x in self.positions:
+ mod = fit.modules[x]
+ if not mod.isEmpty:
+ pyfalog.debug(" -- Removing {}", mod)
+ self.modCache.append(ModuleInfoCache(mod.modPosition, mod.item.ID, mod.state, mod.charge, mod.baseItemID, mod.mutaplasmidID))
+ fit.modules.toDummy(x)
+
+ # if no modules have changes, skip command
+ if not len(self.modCache) > 0:
+ return False
+
+ numSlots = len(fit.modules)
+ # todo: determine if we need to do this still
+ # self.recalc(fit)
+ # self.checkStates(fit, None)
+ # fit.fill()
+ eos.db.commit()
+ self.slotsChanged = numSlots != len(fit.modules)
+ return True
+
+ def Undo(self):
+ pyfalog.debug("Reapplying {} removed module(s) for {}", len(self.modCache), self.fitID)
+
+ from gui.fitCommands.calc.fitReplaceModule import FitReplaceModuleCommand # avoids circular import
+ for mod in self.modCache:
+ pyfalog.debug(" -- {}", mod)
+ # todo, send the state and charge?
+ cmd = FitReplaceModuleCommand(self.fitID, mod.modPosition, mod.itemID)
+ cmd.Do()
+ cmd.module.state = mod.state
+ cmd.module.charge = mod.charge
+ return True
diff --git a/gui/fitCommands/calc/fitRemoveProjectedDrone.py b/gui/fitCommands/calc/fitRemoveProjectedDrone.py
new file mode 100644
index 000000000..9d70ade2e
--- /dev/null
+++ b/gui/fitCommands/calc/fitRemoveProjectedDrone.py
@@ -0,0 +1,42 @@
+import wx
+import eos.db
+from logbook import Logger
+pyfalog = Logger(__name__)
+
+
+# this has the same exact definition that regular projected modules, besides the undo
+class FitRemoveProjectedDroneCommand(wx.Command):
+ """"
+ from sFit.project
+ """
+
+ def __init__(self, fitID, position, stack=False):
+ wx.Command.__init__(self, True)
+ self.fitID = fitID
+ self.position = position
+ self.removed_item = None
+ self.stack = stack
+
+ def Do(self):
+ pyfalog.debug("Removing ({0}) onto: {1}", self.fitID, self.position)
+ fit = eos.db.getFit(self.fitID)
+
+ drone = fit.projectedDrones[self.position]
+ if self.stack:
+ fit.projectedDrones.remove(drone)
+ else:
+ if drone.amount > 1:
+ drone.amount -= 1
+ else:
+ fit.projectedDrones.remove(drone)
+
+ self.drone_item = drone.itemID
+
+ eos.db.commit()
+ return True
+
+ def Undo(self):
+ from gui.fitCommands.calc.fitAddProjectedDrone import FitAddProjectedDroneCommand
+ cmd = FitAddProjectedDroneCommand(self.fitID, self.drone_item)
+ cmd.Do()
+ return True
diff --git a/gui/fitCommands/calc/fitRemoveProjectedEnv.py b/gui/fitCommands/calc/fitRemoveProjectedEnv.py
new file mode 100644
index 000000000..61b6ee939
--- /dev/null
+++ b/gui/fitCommands/calc/fitRemoveProjectedEnv.py
@@ -0,0 +1,35 @@
+import wx
+import eos.db
+from logbook import Logger
+from .fitRemoveProjectedModule import FitRemoveProjectedModuleCommand
+pyfalog = Logger(__name__)
+
+
+# this has the same exact definition that regular rpojected modules, besides the undo
+class FitRemoveProjectedEnvCommand(FitRemoveProjectedModuleCommand):
+ """"
+ from sFit.project
+ """
+
+ def __init__(self, fitID, itemID):
+ wx.Command.__init__(self, True)
+ self.fitID = fitID
+ self.itemID = itemID
+ self.removed_item = None
+
+ def Do(self):
+ pyfalog.debug("Removing ({0}) onto: {1}", self.fitID, self.itemID)
+ fit = eos.db.getFit(self.fitID)
+
+ item = next((x for x in fit.projectedModules if x.itemID == self.itemID), None)
+ self.removed_item = item.itemID
+ fit.projectedModules.remove(item)
+
+ eos.db.commit()
+ return True
+
+ def Undo(self):
+ from gui.fitCommands.calc.fitAddProjectedEnv import FitAddProjectedEnvCommand
+ cmd = FitAddProjectedEnvCommand(self.fitID, self.removed_item)
+ cmd.Do()
+ return True
diff --git a/gui/fitCommands/calc/fitRemoveProjectedFighter.py b/gui/fitCommands/calc/fitRemoveProjectedFighter.py
new file mode 100644
index 000000000..d3550b43e
--- /dev/null
+++ b/gui/fitCommands/calc/fitRemoveProjectedFighter.py
@@ -0,0 +1,34 @@
+import wx
+import eos.db
+from logbook import Logger
+pyfalog = Logger(__name__)
+
+
+# this has the same exact definition that regular rpojected modules, besides the undo
+class FitRemoveProjectedFighterCommand(wx.Command):
+ """"
+ from sFit.project
+ """
+
+ def __init__(self, fitID, position):
+ wx.Command.__init__(self, True)
+ self.fitID = fitID
+ self.position = position
+ self.removed_item = None
+
+ def Do(self):
+ pyfalog.debug("Removing ({0}) onto: {1}", self.fitID, self.position)
+ fit = eos.db.getFit(self.fitID)
+
+ fighter = fit.projectedFighters[self.position]
+ fit.projectedFighters.remove(fighter)
+ self.removed_item = fighter.itemID
+
+ eos.db.commit()
+ return True
+
+ def Undo(self):
+ from gui.fitCommands.calc.fitAddProjectedFighter import FitAddProjectedFighterCommand
+ cmd = FitAddProjectedFighterCommand(self.fitID, self.removed_item)
+ cmd.Do()
+ return True
diff --git a/gui/fitCommands/calc/fitRemoveProjectedFit.py b/gui/fitCommands/calc/fitRemoveProjectedFit.py
new file mode 100644
index 000000000..66d62ab10
--- /dev/null
+++ b/gui/fitCommands/calc/fitRemoveProjectedFit.py
@@ -0,0 +1,37 @@
+import wx
+import eos.db
+from logbook import Logger
+from .fitRemoveProjectedModule import FitRemoveProjectedModuleCommand
+pyfalog = Logger(__name__)
+
+
+# this has the same exact definition that regular rpojected modules, besides the undo
+class FitRemoveProjectedFitCommand(FitRemoveProjectedModuleCommand):
+ """"
+ from sFit.project
+ """
+
+ def __init__(self, fitID, projectedFitID):
+ wx.Command.__init__(self, True)
+ self.fitID = fitID
+ self.projectedFitID = projectedFitID
+
+ def Do(self):
+ pyfalog.debug("Removing ({0}) onto: {1}", self.fitID, self.projectedFitID)
+ fit = eos.db.getFit(self.fitID)
+ projectedFit = eos.db.getFit(self.projectedFitID)
+
+ if projectedFit is None:
+ return False
+
+ del fit.projectedFitDict[projectedFit.ID]
+
+ eos.db.commit()
+ return True
+
+ def Undo(self):
+ # todo: figure out if I need to return false here if the fit doesn't return true (means it was deleted)
+ from gui.fitCommands.calc.fitAddProjectedFit import FitAddProjectedFitCommand
+ cmd = FitAddProjectedFitCommand(self.fitID, self.projectedFitID)
+ cmd.Do()
+ return True
diff --git a/gui/fitCommands/calc/fitRemoveProjectedModule.py b/gui/fitCommands/calc/fitRemoveProjectedModule.py
new file mode 100644
index 000000000..46f8dadba
--- /dev/null
+++ b/gui/fitCommands/calc/fitRemoveProjectedModule.py
@@ -0,0 +1,30 @@
+import wx
+import eos.db
+from logbook import Logger
+pyfalog = Logger(__name__)
+
+
+class FitRemoveProjectedModuleCommand(wx.Command):
+ """"
+ from sFit.project
+ """
+ def __init__(self, fitID, position):
+ wx.Command.__init__(self, True)
+ self.fitID = fitID
+ self.position = position
+ self.removed_item = None
+
+ def Do(self):
+ pyfalog.debug("Removing ({0}) onto: {1}", self.fitID, self.position)
+ fit = eos.db.getFit(self.fitID)
+ self.removed_item = fit.projectedModules[self.position].itemID
+ del fit.projectedModules[self.position]
+
+ eos.db.commit()
+ return True
+
+ def Undo(self):
+ from gui.fitCommands.calc.fitAddProjectedModule import FitAddProjectedModuleCommand
+ cmd = FitAddProjectedModuleCommand(self.fitID, self.removed_item)
+ cmd.Do()
+ return True
diff --git a/gui/fitCommands/calc/fitRename.py b/gui/fitCommands/calc/fitRename.py
new file mode 100644
index 000000000..38a72c519
--- /dev/null
+++ b/gui/fitCommands/calc/fitRename.py
@@ -0,0 +1,26 @@
+import wx
+from logbook import Logger
+
+import eos.db
+
+pyfalog = Logger(__name__)
+
+
+class FitRenameCommand(wx.Command):
+ def __init__(self, fitID, newName):
+ wx.Command.__init__(self, True, "FitRename")
+ self.fitID = fitID
+ self.newName = newName
+ self.oldName = None
+
+ def Do(self):
+ pyfalog.debug("Renaming fit ({0}) to: {1}", self.fitID, self.newName)
+ fit = eos.db.getFit(self.fitID)
+ self.oldName = fit.name
+ fit.name = self.newName
+ eos.db.commit()
+ return True
+
+ def Undo(self):
+ cmd = FitRenameCommand(self.fitID, self.oldName)
+ return cmd.Do()
diff --git a/gui/fitCommands/calc/fitReplaceModule.py b/gui/fitCommands/calc/fitReplaceModule.py
new file mode 100644
index 000000000..19674df80
--- /dev/null
+++ b/gui/fitCommands/calc/fitReplaceModule.py
@@ -0,0 +1,84 @@
+import wx
+from logbook import Logger
+
+import eos.db
+from eos.saveddata.module import Module, State
+from gui.fitCommands.helpers import ModuleInfoCache
+
+pyfalog = Logger(__name__)
+
+
+class FitReplaceModuleCommand(wx.Command):
+ """"
+ Fitting command that changes an existing module into another.
+
+ from sFit.changeModule
+ """
+ def __init__(self, fitID, position, itemID):
+ wx.Command.__init__(self, True, "Change Module")
+ self.fitID = fitID
+ self.itemID = itemID
+ self.position = position
+ self.module = None # the module version of itemID
+ self.old_module = None
+
+ def Do(self):
+ return self.change_module(self.fitID, self.position, self.itemID)
+
+ def Undo(self):
+ if self.old_module is None:
+ fit = eos.db.getFit(self.fitID)
+ fit.modules.toDummy(self.position)
+ return True
+
+ self.change_module(self.fitID, self.position, self.old_module.itemID)
+ self.module.state = self.old_module.state
+ self.module.charge = self.old_module.charge
+ return True
+
+ def change_module(self, fitID, position, itemID):
+ fit = eos.db.getFit(fitID)
+
+ # We're trying to add a charge to a slot, which won't work. Instead, try to add the charge to the module in that slot.
+ # todo: evaluate if this is still a thing
+ # actually, this seems like it should be handled higher up...
+ #
+ # if self.isAmmo(itemID):
+ # module = fit.modules[self.position]
+ # if not module.isEmpty:
+ # self.setAmmo(fitID, itemID, [module])
+ # return True
+
+ pyfalog.debug("Changing position of module from position ({0}) for fit ID: {1}", self.position, fitID)
+
+ item = eos.db.getItem(itemID, eager=("attributes", "group.category"))
+
+ mod = fit.modules[self.position]
+ if not mod.isEmpty:
+ self.old_module = ModuleInfoCache(mod.modPosition, mod.item.ID, mod.state, mod.charge, mod.baseItemID, mod.mutaplasmidID)
+
+ try:
+ self.module = Module(item)
+ except ValueError:
+ pyfalog.warning("Invalid item: {0}", itemID)
+ return False
+
+ if self.module.slot != mod.slot:
+ return False
+
+ # Dummy it out in case the next bit fails
+ fit.modules.toDummy(self.position)
+
+ if self.module.fits(fit):
+ self.module.owner = fit
+ fit.modules.toModule(self.position, self.module)
+ if self.module.isValidState(State.ACTIVE):
+ self.module.state = State.ACTIVE
+
+ # Then, check states of all modules and change where needed. This will recalc if needed
+ # self.checkStates(fit, m)
+
+ # fit.fill()
+ eos.db.commit()
+ return True
+ return False
diff --git a/gui/fitCommands/calc/fitSetCharge.py b/gui/fitCommands/calc/fitSetCharge.py
new file mode 100644
index 000000000..93a6da4ae
--- /dev/null
+++ b/gui/fitCommands/calc/fitSetCharge.py
@@ -0,0 +1,46 @@
+import wx
+from logbook import Logger
+
+import eos.db
+import gui.mainFrame
+from service.fit import Fit
+
+pyfalog = Logger(__name__)
+
+
+class FitSetChargeCommand(wx.Command):
+ def __init__(self, fitID, positions, chargeID=None):
+ # todo: determine if this command really should be used with a group of modules, or a simple per module basis
+ wx.Command.__init__(self, True, "Module Charge Add")
+ self.mainFrame = gui.mainFrame.MainFrame.getInstance()
+ self.sFit = Fit.getInstance()
+ self.fitID = fitID
+ self.chargeID = chargeID
+ self.positions = positions
+ self.cache = None
+
+ def Do(self):
+ return self.__setAmmo(self.positions, self.chargeID)
+
+ def Undo(self):
+ for position, chargeID in self.cache.items():
+ self.__setAmmo([position], chargeID)
+ return True
+
+ def __setAmmo(self, positions, chargeID):
+ fit = eos.db.getFit(self.fitID)
+ self.cache = {fit.modules[i].modPosition: fit.modules[i].chargeID for i in positions}
+ ammo = eos.db.getItem(chargeID) if chargeID else None
+
+ if ammo is not None and not ammo.isCharge:
+ return False
+ result = False
+
+ for pos in positions:
+ mod = fit.modules[pos]
+ if not mod.isEmpty and mod.isValidCharge(ammo):
+ pyfalog.debug("Set ammo {} for {} on fit {}", ammo, mod, self.fitID)
+ result = True
+ mod.charge = ammo
+ eos.db.commit()
+ return result
diff --git a/gui/fitCommands/calc/fitSetMode.py b/gui/fitCommands/calc/fitSetMode.py
new file mode 100644
index 000000000..750be78dd
--- /dev/null
+++ b/gui/fitCommands/calc/fitSetMode.py
@@ -0,0 +1,28 @@
+import wx
+import eos.db
+from logbook import Logger
+pyfalog = Logger(__name__)
+
+
+class FitSetModeCommand(wx.Command):
+ """"
+ from sFit.setMode
+ """
+ def __init__(self, fitID, mode):
+ wx.Command.__init__(self, True, "Cargo add")
+ self.fitID = fitID
+ self.mode = mode
+ self.old_mode = None
+
+ def Do(self):
+ pyfalog.debug("Set mode for fit ID: {0}", self.fitID)
+ fit = eos.db.getFit(self.fitID)
+ self.old_mode = fit.mode
+ fit.mode = self.mode
+ eos.db.commit()
+ return True
+
+ def Undo(self):
+ cmd = FitSetModeCommand(self.fitID, self.old_mode)
+ cmd.Do()
+ return True
diff --git a/gui/fitCommands/calc/fitSwapModule.py b/gui/fitCommands/calc/fitSwapModule.py
new file mode 100644
index 000000000..38ed8d4eb
--- /dev/null
+++ b/gui/fitCommands/calc/fitSwapModule.py
@@ -0,0 +1,38 @@
+import wx
+import eos.db
+from logbook import Logger
+pyfalog = Logger(__name__)
+
+
+class FitSwapModuleCommand(wx.Command):
+ """"
+ from sFit.swapModules
+ """
+ def __init__(self, fitID, src, dst):
+ wx.Command.__init__(self, True, "Module Swap")
+ self.fitID = fitID
+ self.src = src
+ self.dst = dst
+
+ def Do(self):
+ self.__swap(self.fitID, self.src, self.dst)
+ return True
+
+ def Undo(self):
+ self.__swap(self.fitID, self.dst, self.src)
+ return True
+
+ def __swap(self, fitID, src, dst):
+ pyfalog.debug("Swapping modules from source ({0}) to destination ({1}) for fit ID: {1}", src, dst, fitID)
+ fit = eos.db.getFit(fitID)
+ # Gather modules
+ srcMod = fit.modules[src]
+ dstMod = fit.modules[dst]
+
+ # To swap, we simply remove mod and insert at destination.
+ fit.modules.remove(srcMod)
+ fit.modules.insert(dst, srcMod)
+ fit.modules.remove(dstMod)
+ fit.modules.insert(src, dstMod)
+
+ eos.db.commit()
diff --git a/gui/fitCommands/calc/fitToggleBooster.py b/gui/fitCommands/calc/fitToggleBooster.py
new file mode 100644
index 000000000..53b56487b
--- /dev/null
+++ b/gui/fitCommands/calc/fitToggleBooster.py
@@ -0,0 +1,27 @@
+import wx
+import eos.db
+from logbook import Logger
+pyfalog = Logger(__name__)
+
+
+class FitToggleBoosterCommand(wx.Command):
+ """"
+ from sFit.toggleBooster
+ """
+ def __init__(self, fitID, position):
+ wx.Command.__init__(self, True, "Cargo add")
+ self.fitID = fitID
+ self.position = position
+
+ def Do(self):
+ pyfalog.debug("Toggling booster for fit ID: {0}", self.fitID)
+ fit = eos.db.getFit(self.fitID)
+ booster = fit.boosters[self.position]
+ booster.active = not booster.active
+
+ eos.db.commit()
+ return True
+
+ def Undo(self):
+ cmd = FitToggleBoosterCommand(self.fitID, self.position)
+ return cmd.Do()
diff --git a/gui/fitCommands/calc/fitToggleCommand.py b/gui/fitCommands/calc/fitToggleCommand.py
new file mode 100644
index 000000000..c7d5a43ad
--- /dev/null
+++ b/gui/fitCommands/calc/fitToggleCommand.py
@@ -0,0 +1,36 @@
+import wx
+import eos.db
+from logbook import Logger
+pyfalog = Logger(__name__)
+
+
+class FitToggleCommandCommand(wx.Command):
+ """"
+ from sFit.toggleCommandFit
+ """
+ def __init__(self, fitID, commandFitId):
+ wx.Command.__init__(self, True, "Cargo add")
+ self.fitID = fitID
+ self.commandFitID = commandFitId
+
+ def Do(self):
+ pyfalog.debug("Toggle command fit ({0}) for: {1}", self.commandFitID, self.fitID)
+ commandFit = eos.db.getFit(self.commandFitID)
+
+ if not commandFit:
+ pyfalog.debug(" -- Command fit not found, deleted?")
+ return False
+
+ commandInfo = commandFit.getCommandInfo(self.fitID)
+
+ if not commandInfo:
+ pyfalog.debug(" -- Command fit info not found, deleted?")
+ return False
+
+ commandInfo.active = not commandInfo.active
+ eos.db.commit()
+ return True
+
+ def Undo(self):
+ cmd = FitToggleCommandCommand(self.fitID, self.commandFitID)
+ return cmd.Do()
diff --git a/gui/fitCommands/calc/fitToggleDrone.py b/gui/fitCommands/calc/fitToggleDrone.py
new file mode 100644
index 000000000..b281be700
--- /dev/null
+++ b/gui/fitCommands/calc/fitToggleDrone.py
@@ -0,0 +1,30 @@
+import wx
+import eos.db
+from logbook import Logger
+pyfalog = Logger(__name__)
+
+
+class FitToggleDroneCommand(wx.Command):
+ """"
+ from sFit.toggleDrone
+ """
+ def __init__(self, fitID, position):
+ wx.Command.__init__(self, True, "Cargo add")
+ self.fitID = fitID
+ self.position = position
+
+ def Do(self):
+ pyfalog.debug("Toggling drones for fit ID: {0}", self.fitID)
+ fit = eos.db.getFit(self.fitID)
+ d = fit.drones[self.position]
+ if d.amount == d.amountActive:
+ d.amountActive = 0
+ else:
+ d.amountActive = d.amount
+
+ eos.db.commit()
+ return True
+
+ def Undo(self):
+ cmd = FitToggleDroneCommand(self.fitID, self.position)
+ return cmd.Do()
diff --git a/gui/fitCommands/calc/fitToggleFighter.py b/gui/fitCommands/calc/fitToggleFighter.py
new file mode 100644
index 000000000..589b4bdd2
--- /dev/null
+++ b/gui/fitCommands/calc/fitToggleFighter.py
@@ -0,0 +1,27 @@
+import wx
+import eos.db
+from logbook import Logger
+pyfalog = Logger(__name__)
+
+
+class FitToggleFighterCommand(wx.Command):
+ """"
+ from sFit.toggleFighter
+ """
+ def __init__(self, fitID, position):
+ wx.Command.__init__(self, True, "Cargo add")
+ self.fitID = fitID
+ self.position = position
+
+ def Do(self):
+ pyfalog.debug("Toggling fighters for fit ID: {0}", self.fitID)
+ fit = eos.db.getFit(self.fitID)
+ f = fit.fighters[self.position]
+ f.active = not f.active
+
+ eos.db.commit()
+ return True
+
+ def Undo(self):
+ cmd = FitToggleFighterCommand(self.fitID, self.position)
+ return cmd.Do()
diff --git a/gui/fitCommands/calc/fitToggleImplant.py b/gui/fitCommands/calc/fitToggleImplant.py
new file mode 100644
index 000000000..c004c297d
--- /dev/null
+++ b/gui/fitCommands/calc/fitToggleImplant.py
@@ -0,0 +1,27 @@
+import wx
+import eos.db
+from logbook import Logger
+pyfalog = Logger(__name__)
+
+
+class FitToggleImplantCommand(wx.Command):
+ """"
+ from sFit.toggleImplant
+ """
+ def __init__(self, fitID, position):
+ wx.Command.__init__(self, True, "Cargo add")
+ self.fitID = fitID
+ self.position = position
+
+ def Do(self):
+ pyfalog.debug("Toggling implant for fit ID: {0}", self.fitID)
+ fit = eos.db.getFit(self.fitID)
+ implant = fit.implants[self.position]
+ implant.active = not implant.active
+
+ eos.db.commit()
+ return True
+
+ def Undo(self):
+ cmd = FitToggleImplantCommand(self.fitID, self.position)
+ return cmd.Do()
diff --git a/gui/fitCommands/guiAddBooster.py b/gui/fitCommands/guiAddBooster.py
new file mode 100644
index 000000000..fc732d857
--- /dev/null
+++ b/gui/fitCommands/guiAddBooster.py
@@ -0,0 +1,30 @@
+import wx
+from service.fit import Fit
+
+import gui.mainFrame
+from gui import globalEvents as GE
+from .calc.fitAddBooster import FitAddBoosterCommand
+
+
+class GuiAddBoosterCommand(wx.Command):
+ def __init__(self, fitID, itemID):
+ wx.Command.__init__(self, True, "Booster Add")
+ self.mainFrame = gui.mainFrame.MainFrame.getInstance()
+ self.sFit = Fit.getInstance()
+ self.internal_history = wx.CommandProcessor()
+ self.fitID = fitID
+ self.itemID = itemID
+
+ def Do(self):
+ if self.internal_history.Submit(FitAddBoosterCommand(self.fitID, self.itemID)):
+ self.sFit.recalc(self.fitID)
+ wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.fitID))
+ return True
+ return False
+
+ def Undo(self):
+ for _ in self.internal_history.Commands:
+ self.internal_history.Undo()
+ self.sFit.recalc(self.fitID)
+ wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.fitID))
+ return True
diff --git a/gui/fitCommands/guiAddCargo.py b/gui/fitCommands/guiAddCargo.py
new file mode 100644
index 000000000..4ff0f3447
--- /dev/null
+++ b/gui/fitCommands/guiAddCargo.py
@@ -0,0 +1,30 @@
+import wx
+from service.fit import Fit
+
+import gui.mainFrame
+from gui import globalEvents as GE
+from .calc.fitAddCargo import FitAddCargoCommand
+
+
+class GuiAddCargoCommand(wx.Command):
+ def __init__(self, fitID, itemID, amount=1, replace=False):
+ wx.Command.__init__(self, True, "Cargo Add")
+ self.mainFrame = gui.mainFrame.MainFrame.getInstance()
+ self.sFit = Fit.getInstance()
+ self.internal_history = wx.CommandProcessor()
+ self.fitID = fitID
+ self.itemID = itemID
+ self.amount = amount
+ self.replace = replace
+
+ def Do(self):
+ if self.internal_history.Submit(FitAddCargoCommand(self.fitID, self.itemID, self.amount, self.replace)):
+ wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.fitID))
+ return True
+ return False
+
+ def Undo(self):
+ for _ in self.internal_history.Commands:
+ self.internal_history.Undo()
+ wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.fitID))
+ return True
diff --git a/gui/fitCommands/guiAddCharge.py b/gui/fitCommands/guiAddCharge.py
new file mode 100644
index 000000000..ad6c5a2f2
--- /dev/null
+++ b/gui/fitCommands/guiAddCharge.py
@@ -0,0 +1,31 @@
+import wx
+from service.fit import Fit
+
+import gui.mainFrame
+from gui import globalEvents as GE
+from .calc.fitSetCharge import FitSetChargeCommand
+
+
+class GuiModuleAddChargeCommand(wx.Command):
+ def __init__(self, fitID, itemID, modules):
+ wx.Command.__init__(self, True, "Module Charge Add")
+ self.mainFrame = gui.mainFrame.MainFrame.getInstance()
+ self.sFit = Fit.getInstance()
+ self.internal_history = wx.CommandProcessor()
+ self.fitID = fitID
+ self.itemID = itemID
+ self.positions = [mod.modPosition for mod in modules]
+
+ def Do(self):
+ if self.internal_history.Submit(FitSetChargeCommand(self.fitID, self.positions, self.itemID)):
+ self.sFit.recalc(self.fitID)
+ wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.fitID))
+ return True
+ return False
+
+ def Undo(self):
+ for _ in self.internal_history.Commands:
+ self.internal_history.Undo()
+ self.sFit.recalc(self.fitID)
+ wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.fitID))
+ return True
diff --git a/gui/fitCommands/guiAddCommand.py b/gui/fitCommands/guiAddCommand.py
new file mode 100644
index 000000000..06cfcbe98
--- /dev/null
+++ b/gui/fitCommands/guiAddCommand.py
@@ -0,0 +1,30 @@
+import wx
+from service.fit import Fit
+
+import gui.mainFrame
+from gui import globalEvents as GE
+from .calc.fitAddCommand import FitAddCommandCommand
+
+
+class GuiAddCommandCommand(wx.Command):
+ def __init__(self, fitID, commandFitID):
+ wx.Command.__init__(self, True, "")
+ self.mainFrame = gui.mainFrame.MainFrame.getInstance()
+ self.sFit = Fit.getInstance()
+ self.internal_history = wx.CommandProcessor()
+ self.fitID = fitID
+ self.commandFitID = commandFitID
+
+ def Do(self):
+ if self.internal_history.Submit(FitAddCommandCommand(self.fitID, self.commandFitID)):
+ wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.fitID))
+ self.sFit.recalc(self.fitID)
+ return True
+ return False
+
+ def Undo(self):
+ for _ in self.internal_history.Commands:
+ self.internal_history.Undo()
+ self.sFit.recalc(self.fitID)
+ wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.fitID))
+ return True
diff --git a/gui/fitCommands/guiAddDrone.py b/gui/fitCommands/guiAddDrone.py
new file mode 100644
index 000000000..6549d1e21
--- /dev/null
+++ b/gui/fitCommands/guiAddDrone.py
@@ -0,0 +1,30 @@
+import wx
+from service.fit import Fit
+
+import gui.mainFrame
+from gui import globalEvents as GE
+from .calc.fitAddDrone import FitAddDroneCommand
+
+
+class GuiAddDroneCommand(wx.Command):
+ def __init__(self, fitID, itemID):
+ wx.Command.__init__(self, True, "Cargo Add")
+ self.mainFrame = gui.mainFrame.MainFrame.getInstance()
+ self.sFit = Fit.getInstance()
+ self.internal_history = wx.CommandProcessor()
+ self.fitID = fitID
+ self.itemID = itemID
+
+ def Do(self):
+ cmd = FitAddDroneCommand(self.fitID, self.itemID)
+ if self.internal_history.Submit(cmd):
+ self.sFit.recalc(self.fitID)
+ wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.fitID))
+ return True
+ return False
+
+ def Undo(self):
+ for _ in self.internal_history.Commands:
+ self.internal_history.Undo()
+ wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.fitID))
+ return True
diff --git a/gui/fitCommands/guiAddFighter.py b/gui/fitCommands/guiAddFighter.py
new file mode 100644
index 000000000..01024e706
--- /dev/null
+++ b/gui/fitCommands/guiAddFighter.py
@@ -0,0 +1,30 @@
+import wx
+from service.fit import Fit
+
+import gui.mainFrame
+from gui import globalEvents as GE
+from .calc.fitAddFighter import FitAddFighterCommand
+
+
+class GuiAddFighterCommand(wx.Command):
+ def __init__(self, fitID, itemID):
+ wx.Command.__init__(self, True, "Cargo Add")
+ self.mainFrame = gui.mainFrame.MainFrame.getInstance()
+ self.sFit = Fit.getInstance()
+ self.internal_history = wx.CommandProcessor()
+ self.fitID = fitID
+ self.itemID = itemID
+
+ def Do(self):
+ if self.internal_history.Submit(FitAddFighterCommand(self.fitID, self.itemID)):
+ self.sFit.recalc(self.fitID)
+ wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.fitID))
+ return True
+ return False
+
+ def Undo(self):
+ for _ in self.internal_history.Commands:
+ self.internal_history.Undo()
+ self.sFit.recalc(self.fitID)
+ wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.fitID))
+ return True
diff --git a/gui/fitCommands/guiAddImplant.py b/gui/fitCommands/guiAddImplant.py
new file mode 100644
index 000000000..ffbc1e0a4
--- /dev/null
+++ b/gui/fitCommands/guiAddImplant.py
@@ -0,0 +1,30 @@
+import wx
+from service.fit import Fit
+
+import gui.mainFrame
+from gui import globalEvents as GE
+from .calc.fitAddImplant import FitAddImplantCommand
+
+
+class GuiAddImplantCommand(wx.Command):
+ def __init__(self, fitID, itemID):
+ wx.Command.__init__(self, True, "Implant Add")
+ self.mainFrame = gui.mainFrame.MainFrame.getInstance()
+ self.sFit = Fit.getInstance()
+ self.internal_history = wx.CommandProcessor()
+ self.fitID = fitID
+ self.itemID = itemID
+
+ def Do(self):
+ if self.internal_history.Submit(FitAddImplantCommand(self.fitID, self.itemID)):
+ self.sFit.recalc(self.fitID)
+ wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.fitID))
+ return True
+ return False
+
+ def Undo(self):
+ for _ in self.internal_history.Commands:
+ self.internal_history.Undo()
+ self.sFit.recalc(self.fitID)
+ wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.fitID))
+ return True
diff --git a/gui/fitCommands/guiAddModule.py b/gui/fitCommands/guiAddModule.py
new file mode 100644
index 000000000..f826566f4
--- /dev/null
+++ b/gui/fitCommands/guiAddModule.py
@@ -0,0 +1,68 @@
+import wx
+import eos.db
+import gui.mainFrame
+from gui import globalEvents as GE
+from .calc.fitAddModule import FitAddModuleCommand
+from .calc.fitReplaceModule import FitReplaceModuleCommand
+from .calc.fitSetCharge import FitSetChargeCommand
+from service.fit import Fit
+from logbook import Logger
+pyfalog = Logger(__name__)
+
+
+class GuiModuleAddCommand(wx.Command):
+ def __init__(self, fitID, itemID, position=None):
+ """
+ Handles adding an item, usually a module, to the Fitting Window.
+
+ :param fitID: The fit ID that we are modifying
+ :param itemID: The item that is to be added to the Fitting View. If this turns out to be a charge, we attempt to
+ set the charge on the underlying module (requires position)
+ :param position: Optional. The position in fit.modules that we are attempting to set the item to
+ """
+ wx.Command.__init__(self, True, "Module Add: {}".format(itemID))
+ self.mainFrame = gui.mainFrame.MainFrame.getInstance()
+ self.sFit = Fit.getInstance()
+ self.fitID = fitID
+ self.itemID = itemID
+ self.internal_history = wx.CommandProcessor()
+ self.position = position
+ self.old_mod = None
+
+ def Do(self):
+ pyfalog.debug("{} Do()".format(self))
+ success = False
+ item = eos.db.getItem(self.itemID)
+ if item.isCharge and self.position is not None:
+ pyfalog.debug("Trying to add a charge")
+ success = self.internal_history.Submit(FitSetChargeCommand(self.fitID, [self.position], self.itemID))
+ if not success:
+ pyfalog.debug(" Failed")
+ return False # if it's a charge item and this failed, nothing more we can try.
+ # if we have a position set, try to apply the module to that position
+ elif self.position is not None:
+ pyfalog.debug("Trying to add a module to a specific position")
+ success = self.internal_history.Submit(FitReplaceModuleCommand(self.fitID, self.position, self.itemID))
+ if not success:
+ pyfalog.debug(" Failed")
+ # something went wrong with trying to fit the module into specific location, attempt to append it
+ self.position = None
+
+ # if we're not trying to set module to a position, simply append
+ if self.position is None:
+ pyfalog.debug("Trying to append a module")
+ success = self.internal_history.Submit(FitAddModuleCommand(self.fitID, self.itemID))
+
+ if success:
+ self.sFit.recalc(self.fitID)
+ wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.fitID, action="modadd", typeID=self.itemID))
+ return True
+ return False
+
+ def Undo(self):
+ pyfalog.debug("{} Undo()".format(self))
+ for _ in self.internal_history.Commands:
+ self.internal_history.Undo()
+ self.sFit.recalc(self.fitID)
+ wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.fitID, action="moddel", typeID=self.itemID))
+ return True
diff --git a/gui/fitCommands/guiAddProjected.py b/gui/fitCommands/guiAddProjected.py
new file mode 100644
index 000000000..b2114e111
--- /dev/null
+++ b/gui/fitCommands/guiAddProjected.py
@@ -0,0 +1,56 @@
+import wx
+from service.fit import Fit
+
+import gui.mainFrame
+from gui import globalEvents as GE
+from eos.saveddata.module import Module
+from .calc.fitAddProjectedModule import FitAddProjectedModuleCommand
+from .calc.fitAddProjectedEnv import FitAddProjectedEnvCommand
+from .calc.fitAddProjectedFit import FitAddProjectedFitCommand
+from .calc.fitAddProjectedFighter import FitAddProjectedFighterCommand
+from .calc.fitAddProjectedDrone import FitAddProjectedDroneCommand
+from logbook import Logger
+import eos.db
+pyfalog = Logger(__name__)
+
+
+class GuiAddProjectedCommand(wx.Command):
+ def __init__(self, fitID, id, type='item'):
+ wx.Command.__init__(self, True, "Projected Add")
+ self.mainFrame = gui.mainFrame.MainFrame.getInstance()
+ self.sFit = Fit.getInstance()
+ self.internal_history = wx.CommandProcessor()
+ self.fitID = fitID
+ self.id = id
+ self.type = type
+
+ def Do(self):
+ result = False
+ # since we can project various types, we need to switch of the fit command. We can't do this switch easily in
+ # the fit command since each type might have a different kind of undo, easier to split it out
+ if self.type == 'item':
+ item = eos.db.getItem(self.id, eager=("attributes", "group.category"))
+
+ if item.category.name == "Drone":
+ result = self.internal_history.Submit(FitAddProjectedDroneCommand(self.fitID, self.id))
+ elif item.category.name == "Fighter":
+ result = self.internal_history.Submit(FitAddProjectedFighterCommand(self.fitID, self.id))
+ elif item.group.name in Module.SYSTEM_GROUPS:
+ result = self.internal_history.Submit(FitAddProjectedEnvCommand(self.fitID, self.id))
+ else:
+ result = self.internal_history.Submit(FitAddProjectedModuleCommand(self.fitID, self.id))
+ elif self.type == 'fit':
+ result = self.internal_history.Submit(FitAddProjectedFitCommand(self.fitID, self.id))
+
+ if result:
+ self.sFit.recalc(self.fitID)
+ wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.fitID))
+ return True
+ return False
+
+ def Undo(self):
+ for _ in self.internal_history.Commands:
+ self.internal_history.Undo()
+ self.sFit.recalc(self.fitID)
+ wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.fitID))
+ return True
diff --git a/gui/fitCommands/guiCargoToModule.py b/gui/fitCommands/guiCargoToModule.py
new file mode 100644
index 000000000..a2583e47c
--- /dev/null
+++ b/gui/fitCommands/guiCargoToModule.py
@@ -0,0 +1,77 @@
+import wx
+from service.fit import Fit
+
+import gui.mainFrame
+from gui import globalEvents as GE
+from gui.fitCommands.calc.fitSetCharge import FitSetChargeCommand
+from gui.fitCommands.calc.fitReplaceModule import FitReplaceModuleCommand
+from gui.fitCommands.calc.fitRemoveCargo import FitRemoveCargoCommand
+from .calc.fitAddCargo import FitAddCargoCommand
+from logbook import Logger
+pyfalog = Logger(__name__)
+
+
+class GuiCargoToModuleCommand(wx.Command):
+ """
+ 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.
+ """
+
+ def __init__(self, fitID, moduleIdx, cargoIdx, copy=False):
+ wx.Command.__init__(self, True, "Module State Change")
+ self.mainFrame = gui.mainFrame.MainFrame.getInstance()
+ self.sFit = Fit.getInstance()
+ self.fitID = fitID
+ self.moduleIdx = moduleIdx
+ self.cargoIdx = cargoIdx
+ self.copy = copy
+ self.internal_history = wx.CommandProcessor()
+
+ def Do(self):
+ sFit = Fit.getInstance()
+ fit = sFit.getFit(self.fitID)
+ module = fit.modules[self.moduleIdx]
+ cargo = fit.cargo[self.cargoIdx]
+ result = False
+
+ # We're trying to move a charge from cargo to a slot. Use SetCharge command (don't respect move vs copy)
+ if sFit.isAmmo(cargo.item.ID):
+ result = self.internal_history.Submit(FitSetChargeCommand(self.fitID, [module.modPosition], cargo.item.ID))
+ else:
+
+ pyfalog.debug("Moving cargo item to module for fit ID: {0}", self.fitID)
+
+ self.addCmd = FitReplaceModuleCommand(self.fitID, module.modPosition, cargo.itemID)
+
+ result = self.internal_history.Submit(self.addCmd)
+
+ if not result:
+ # creating module failed for whatever reason
+ return False
+
+ if self.addCmd.old_module is not None:
+ # we're swapping with an existing module, so remove cargo and add module
+ self.removeCmd = FitRemoveCargoCommand(self.fitID, cargo.itemID)
+ result = self.internal_history.Submit(self.removeCmd)
+
+ self.addCargoCmd = FitAddCargoCommand(self.fitID, self.addCmd.old_module.itemID)
+ result = self.internal_history.Submit(self.addCargoCmd)
+ elif not self.copy:
+ # move, not copying, so remove cargo
+ self.removeCmd = FitRemoveCargoCommand(self.fitID, cargo.itemID)
+ result = self.internal_history.Submit(self.removeCmd)
+
+ if result:
+ sFit.recalc(self.fitID)
+ wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.fitID))
+ return result
+
+ def Undo(self):
+ for _ in self.internal_history.Commands:
+ self.internal_history.Undo()
+ self.sFit.recalc(self.fitID)
+ wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.fitID))
+ return True
diff --git a/gui/fitCommands/guiChangeCargoQty.py b/gui/fitCommands/guiChangeCargoQty.py
new file mode 100644
index 000000000..80a906fc3
--- /dev/null
+++ b/gui/fitCommands/guiChangeCargoQty.py
@@ -0,0 +1,33 @@
+import wx
+import gui.mainFrame
+from gui import globalEvents as GE
+from .calc.fitChangeCargoQty import FitChangeCargoQty
+from service.fit import Fit
+from logbook import Logger
+pyfalog = Logger(__name__)
+
+
+class GuiChangeCargoQty(wx.Command):
+ def __init__(self, fitID, position, amount=1):
+ wx.Command.__init__(self, True, "")
+ self.mainFrame = gui.mainFrame.MainFrame.getInstance()
+ self.sFit = Fit.getInstance()
+ self.fitID = fitID
+ self.position = position
+ self.amount = amount
+ self.internal_history = wx.CommandProcessor()
+
+ def Do(self):
+ cmd = FitChangeCargoQty(self.fitID, self.position, self.amount)
+ if self.internal_history.Submit(cmd):
+ self.sFit.recalc(self.fitID)
+ wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.fitID))
+ return True
+ return False
+
+ def Undo(self):
+ for _ in self.internal_history.Commands:
+ self.internal_history.Undo()
+ self.sFit.recalc(self.fitID)
+ wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.fitID))
+ return True
diff --git a/gui/fitCommands/guiChangeDroneQty.py b/gui/fitCommands/guiChangeDroneQty.py
new file mode 100644
index 000000000..5aa2ea12e
--- /dev/null
+++ b/gui/fitCommands/guiChangeDroneQty.py
@@ -0,0 +1,33 @@
+import wx
+import gui.mainFrame
+from gui import globalEvents as GE
+from .calc.fitChangeDroneQty import FitChangeDroneQty
+from service.fit import Fit
+from logbook import Logger
+pyfalog = Logger(__name__)
+
+
+class GuiChangeDroneQty(wx.Command):
+ def __init__(self, fitID, position, amount=1):
+ wx.Command.__init__(self, True, "")
+ self.mainFrame = gui.mainFrame.MainFrame.getInstance()
+ self.sFit = Fit.getInstance()
+ self.fitID = fitID
+ self.position = position
+ self.amount = amount
+ self.internal_history = wx.CommandProcessor()
+
+ def Do(self):
+ cmd = FitChangeDroneQty(self.fitID, self.position, self.amount)
+ if self.internal_history.Submit(cmd):
+ self.sFit.recalc(self.fitID)
+ wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.fitID))
+ return True
+ return False
+
+ def Undo(self):
+ for _ in self.internal_history.Commands:
+ self.internal_history.Undo()
+ self.sFit.recalc(self.fitID)
+ wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.fitID))
+ return True
diff --git a/gui/fitCommands/guiChangeFighterQty.py b/gui/fitCommands/guiChangeFighterQty.py
new file mode 100644
index 000000000..280bd1a16
--- /dev/null
+++ b/gui/fitCommands/guiChangeFighterQty.py
@@ -0,0 +1,34 @@
+import wx
+import gui.mainFrame
+from gui import globalEvents as GE
+from .calc.fitChangeFighterQty import FitChangeFighterQty
+from service.fit import Fit
+from logbook import Logger
+pyfalog = Logger(__name__)
+
+
+class GuiChangeFighterQty(wx.Command):
+ def __init__(self, fitID, position, amount=1):
+ wx.Command.__init__(self, True, "")
+ self.mainFrame = gui.mainFrame.MainFrame.getInstance()
+ self.sFit = Fit.getInstance()
+ self.fitID = fitID
+ self.position = position
+ self.amount = amount
+ self.internal_history = wx.CommandProcessor()
+
+ def Do(self):
+ cmd = FitChangeFighterQty(self.fitID, self.position, self.amount)
+ if self.internal_history.Submit(cmd):
+ self.sFit.recalc(self.fitID)
+ wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.fitID))
+ return True
+ return False
+
+ def Undo(self):
+ pyfalog.debug("{} Undo()".format(self))
+ for _ in self.internal_history.Commands:
+ self.internal_history.Undo()
+ self.sFit.recalc(self.fitID)
+ wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.fitID))
+ return True
diff --git a/gui/fitCommands/guiChangeProjectedDroneQty.py b/gui/fitCommands/guiChangeProjectedDroneQty.py
new file mode 100644
index 000000000..f7ceaa657
--- /dev/null
+++ b/gui/fitCommands/guiChangeProjectedDroneQty.py
@@ -0,0 +1,34 @@
+import wx
+import gui.mainFrame
+from gui import globalEvents as GE
+from .calc.fitChangeProjectedDroneQty import FitChangeProjectedDroneQty
+from service.fit import Fit
+from logbook import Logger
+pyfalog = Logger(__name__)
+
+
+class GuiChangeProjectedDroneQty(wx.Command):
+ def __init__(self, fitID, position, amount=1):
+ wx.Command.__init__(self, True, "")
+ self.mainFrame = gui.mainFrame.MainFrame.getInstance()
+ self.sFit = Fit.getInstance()
+ self.fitID = fitID
+ self.position = position
+ self.amount = amount
+ self.internal_history = wx.CommandProcessor()
+
+ def Do(self):
+ cmd = FitChangeProjectedDroneQty(self.fitID, self.position, self.amount)
+ if self.internal_history.Submit(cmd):
+ self.sFit.recalc(self.fitID)
+ wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.fitID))
+ return True
+ return False
+
+ def Undo(self):
+ pyfalog.debug("{} Undo()".format(self))
+ for _ in self.internal_history.Commands:
+ self.internal_history.Undo()
+ self.sFit.recalc(self.fitID)
+ wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.fitID))
+ return True
diff --git a/gui/fitCommands/guiChangeProjectedFitQty.py b/gui/fitCommands/guiChangeProjectedFitQty.py
new file mode 100644
index 000000000..7014a808b
--- /dev/null
+++ b/gui/fitCommands/guiChangeProjectedFitQty.py
@@ -0,0 +1,34 @@
+import wx
+import gui.mainFrame
+from gui import globalEvents as GE
+from .calc.fitChangeProjectedFitQty import FitChangeProjectedFitQty
+from service.fit import Fit
+from logbook import Logger
+pyfalog = Logger(__name__)
+
+
+class GuiChangeProjectedFitQty(wx.Command):
+ def __init__(self, fitID, pfitID, amount=1):
+ wx.Command.__init__(self, True, "")
+ self.mainFrame = gui.mainFrame.MainFrame.getInstance()
+ self.sFit = Fit.getInstance()
+ self.fitID = fitID
+ self.pfitID = pfitID
+ self.amount = amount
+ self.internal_history = wx.CommandProcessor()
+
+ def Do(self):
+ cmd = FitChangeProjectedFitQty(self.fitID, self.pfitID, self.amount)
+ if self.internal_history.Submit(cmd):
+ self.sFit.recalc(self.fitID)
+ wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.fitID))
+ return True
+ return False
+
+ def Undo(self):
+ pyfalog.debug("{} Undo()".format(self))
+ for _ in self.internal_history.Commands:
+ self.internal_history.Undo()
+ self.sFit.recalc(self.fitID)
+ wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.fitID))
+ return True
diff --git a/gui/fitCommands/guiFitRename.py b/gui/fitCommands/guiFitRename.py
new file mode 100644
index 000000000..b9a7a81d7
--- /dev/null
+++ b/gui/fitCommands/guiFitRename.py
@@ -0,0 +1,30 @@
+import wx
+import gui.mainFrame
+from .calc.fitRename import FitRenameCommand
+from service.fit import Fit
+from logbook import Logger
+from gui.builtinShipBrowser.events import FitRenamed
+pyfalog = Logger(__name__)
+
+
+class GuiFitRenameCommand(wx.Command):
+ def __init__(self, fitID, newName):
+ wx.Command.__init__(self, True)
+ self.mainFrame = gui.mainFrame.MainFrame.getInstance()
+ self.sFit = Fit.getInstance()
+ self.fitID = fitID
+ self.newName = newName
+ self.internal_history = wx.CommandProcessor()
+
+ def Do(self):
+ if self.internal_history.Submit(FitRenameCommand(self.fitID, self.newName)):
+ wx.PostEvent(self.mainFrame, FitRenamed(fitID=self.fitID))
+ return True
+ return False
+
+ def Undo(self):
+ pyfalog.debug("{} Undo()".format(self))
+ for _ in self.internal_history.Commands:
+ self.internal_history.Undo()
+ wx.PostEvent(self.mainFrame, FitRenamed(fitID=self.fitID))
+ return True
diff --git a/gui/fitCommands/guiMetaSwap.py b/gui/fitCommands/guiMetaSwap.py
new file mode 100644
index 000000000..13497a233
--- /dev/null
+++ b/gui/fitCommands/guiMetaSwap.py
@@ -0,0 +1,63 @@
+import wx
+from service.fit import Fit
+
+import gui.mainFrame
+from gui import globalEvents as GE
+from .calc.fitRemoveImplant import FitRemoveImplantCommand
+from .calc.fitAddImplant import FitAddImplantCommand
+from .calc.fitRemoveBooster import FitRemoveBoosterCommand
+from .calc.fitAddBooster import FitAddBoosterCommand
+from .calc.fitRemoveCargo import FitRemoveCargoCommand
+from .calc.fitAddCargo import FitAddCargoCommand
+from .calc.fitReplaceModule import FitReplaceModuleCommand
+from .calc.fitAddFighter import FitAddFighterCommand
+from .calc.fitRemoveFighter import FitRemoveFighterCommand
+
+
+class GuiMetaSwapCommand(wx.Command):
+ def __init__(self, fitID, context, itemID, selection: list):
+ wx.Command.__init__(self, True, "Meta Swap")
+ self.mainFrame = gui.mainFrame.MainFrame.getInstance()
+ self.sFit = Fit.getInstance()
+ self.internal_history = wx.CommandProcessor()
+ self.fitID = fitID
+ self.itemID = itemID
+ self.context = context
+ self.data = []
+ fit = self.sFit.getFit(fitID)
+
+ if context == 'fittingModule':
+ for x in selection:
+ self.data.append(((FitReplaceModuleCommand, fitID, fit.modules.index(x), itemID),),)
+ elif context == 'implantItem':
+ for x in selection:
+ idx = fit.implants.index(x)
+ self.data.append(((FitRemoveImplantCommand, fitID, idx), (FitAddImplantCommand, fitID, itemID)))
+ elif context == 'boosterItem':
+ for x in selection:
+ idx = fit.boosters.index(x)
+ self.data.append(((FitRemoveBoosterCommand, fitID, idx), (FitAddBoosterCommand, fitID, itemID)))
+ elif context == 'cargoItem':
+ for x in selection:
+ self.data.append(((FitRemoveCargoCommand, fitID, x.itemID, 1, True), (FitAddCargoCommand, fitID, itemID, x.amount)))
+ elif context == 'fighterItem':
+ for x in selection:
+ self.data.append(((FitRemoveFighterCommand, fitID, fit.fighters.index(x)), (FitAddFighterCommand, fitID, itemID)))
+ elif context == 'droneItem':
+ raise NotImplementedError()
+
+ def Do(self):
+ for cmds in self.data:
+ for cmd in cmds:
+ self.internal_history.Submit(cmd[0](*cmd[1:]))
+
+ self.sFit.recalc(self.fitID)
+ wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.fitID))
+ return True
+
+ def Undo(self):
+ for _ in self.internal_history.Commands:
+ self.internal_history.Undo()
+ self.sFit.recalc(self.fitID)
+ wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.fitID))
+ return True
diff --git a/gui/fitCommands/guiModuleToCargo.py b/gui/fitCommands/guiModuleToCargo.py
new file mode 100644
index 000000000..66a9f9833
--- /dev/null
+++ b/gui/fitCommands/guiModuleToCargo.py
@@ -0,0 +1,73 @@
+import wx
+from logbook import Logger
+
+import gui.mainFrame
+from gui import globalEvents as GE
+from gui.fitCommands.calc.fitRemoveCargo import FitRemoveCargoCommand
+from gui.fitCommands.calc.fitRemoveModule import FitRemoveModuleCommand
+from gui.fitCommands.calc.fitReplaceModule import FitReplaceModuleCommand
+from service.fit import Fit
+from .calc.fitAddCargo import FitAddCargoCommand
+
+pyfalog = Logger(__name__)
+
+
+class GuiModuleToCargoCommand(wx.Command):
+ def __init__(self, fitID, moduleIdx, cargoIdx, copy=False):
+ wx.Command.__init__(self, True, "Module State Change")
+ self.mainFrame = gui.mainFrame.MainFrame.getInstance()
+ self.sFit = Fit.getInstance()
+ self.fitID = fitID
+ self.moduleIdx = moduleIdx
+ self.cargoIdx = cargoIdx
+ self.copy = copy
+ self.internal_history = wx.CommandProcessor()
+
+ def Do(self):
+ sFit = Fit.getInstance()
+ fit = sFit.getFit(self.fitID)
+ module = fit.modules[self.moduleIdx]
+ result = False
+
+ if self.cargoIdx: # we're swapping with cargo
+ if self.copy: # if copying, simply add item to cargo
+ result = self.internal_history.Submit(FitAddCargoCommand(
+ self.mainFrame.getActiveFit(), module.item.ID if not module.item.isAbyssal else module.baseItemID))
+ else: # otherwise, try to swap by replacing module with cargo item. If successful, remove old cargo and add new cargo
+
+ cargo = fit.cargo[self.cargoIdx]
+ self.modReplaceCmd = FitReplaceModuleCommand(self.fitID, module.modPosition, cargo.itemID)
+
+ result = self.internal_history.Submit(self.modReplaceCmd)
+
+ if not result:
+ # creating module failed for whatever reason
+ return False
+
+ if self.modReplaceCmd.old_module is not None:
+ # we're swapping with an existing module, so remove cargo and add module
+ self.removeCmd = FitRemoveCargoCommand(self.fitID, cargo.itemID)
+ result = self.internal_history.Submit(self.removeCmd)
+
+ self.addCargoCmd = FitAddCargoCommand(self.fitID, self.modReplaceCmd.old_module.itemID)
+ result = self.internal_history.Submit(self.addCargoCmd)
+
+ else: # dragging to blank spot, append
+ result = self.internal_history.Submit(FitAddCargoCommand(self.mainFrame.getActiveFit(),
+ module.item.ID if not module.item.isAbyssal else module.baseItemID))
+
+ if not self.copy: # if not copying, remove module
+ self.internal_history.Submit(FitRemoveModuleCommand(self.mainFrame.getActiveFit(), [self.moduleIdx]))
+
+ if result:
+ sFit.recalc(self.fitID)
+ wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.fitID, action="moddel", typeID=module.item.ID))
+
+ return result
+
+ def Undo(self):
+ for _ in self.internal_history.Commands:
+ self.internal_history.Undo()
+ self.sFit.recalc(self.fitID)
+ wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.fitID))
+ return True
diff --git a/gui/fitCommands/guiRemoveBooster.py b/gui/fitCommands/guiRemoveBooster.py
new file mode 100644
index 000000000..15b504575
--- /dev/null
+++ b/gui/fitCommands/guiRemoveBooster.py
@@ -0,0 +1,30 @@
+import wx
+from service.fit import Fit
+
+import gui.mainFrame
+from gui import globalEvents as GE
+from .calc.fitRemoveBooster import FitRemoveBoosterCommand
+
+
+class GuiRemoveBoosterCommand(wx.Command):
+ def __init__(self, fitID, position):
+ wx.Command.__init__(self, True, "")
+ self.mainFrame = gui.mainFrame.MainFrame.getInstance()
+ self.sFit = Fit.getInstance()
+ self.internal_history = wx.CommandProcessor()
+ self.fitID = fitID
+ self.position = position
+
+ def Do(self):
+ if self.internal_history.Submit(FitRemoveBoosterCommand(self.fitID, self.position)):
+ self.sFit.recalc(self.fitID)
+ wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.fitID))
+ return True
+ return False
+
+ def Undo(self):
+ for _ in self.internal_history.Commands:
+ self.internal_history.Undo()
+ self.sFit.recalc(self.fitID)
+ wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.fitID))
+ return True
diff --git a/gui/fitCommands/guiRemoveCargo.py b/gui/fitCommands/guiRemoveCargo.py
new file mode 100644
index 000000000..f9aa5872a
--- /dev/null
+++ b/gui/fitCommands/guiRemoveCargo.py
@@ -0,0 +1,28 @@
+import wx
+from service.fit import Fit
+
+import gui.mainFrame
+from gui import globalEvents as GE
+from .calc.fitRemoveCargo import FitRemoveCargoCommand
+
+
+class GuiRemoveCargoCommand(wx.Command):
+ def __init__(self, fitID, itemID):
+ wx.Command.__init__(self, True, "Module Charge Add")
+ self.mainFrame = gui.mainFrame.MainFrame.getInstance()
+ self.sFit = Fit.getInstance()
+ self.internal_history = wx.CommandProcessor()
+ self.fitID = fitID
+ self.itemID = itemID
+
+ def Do(self):
+ if self.internal_history.Submit(FitRemoveCargoCommand(self.fitID, self.itemID, stack=True)):
+ wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.fitID))
+ return True
+ return False
+
+ def Undo(self):
+ for _ in self.internal_history.GetCommands():
+ self.internal_history.Undo()
+ wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.fitID))
+ return True
diff --git a/gui/fitCommands/guiRemoveCommand.py b/gui/fitCommands/guiRemoveCommand.py
new file mode 100644
index 000000000..206be23c3
--- /dev/null
+++ b/gui/fitCommands/guiRemoveCommand.py
@@ -0,0 +1,30 @@
+import wx
+from service.fit import Fit
+
+import gui.mainFrame
+from gui import globalEvents as GE
+from .calc.fitRemoveCommand import FitRemoveCommandCommand
+
+
+class GuiRemoveCommandCommand(wx.Command):
+ def __init__(self, fitID, commandFitID):
+ wx.Command.__init__(self, True, "")
+ self.mainFrame = gui.mainFrame.MainFrame.getInstance()
+ self.sFit = Fit.getInstance()
+ self.internal_history = wx.CommandProcessor()
+ self.fitID = fitID
+ self.commandFitID = commandFitID
+
+ def Do(self):
+ if self.internal_history.Submit(FitRemoveCommandCommand(self.fitID, self.commandFitID)):
+ self.sFit.recalc(self.fitID)
+ wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.fitID))
+ return True
+ return False
+
+ def Undo(self):
+ for _ in self.internal_history.Commands:
+ self.internal_history.Undo()
+ self.sFit.recalc(self.fitID)
+ wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.fitID))
+ return True
diff --git a/gui/fitCommands/guiRemoveDrone.py b/gui/fitCommands/guiRemoveDrone.py
new file mode 100644
index 000000000..42e651e3d
--- /dev/null
+++ b/gui/fitCommands/guiRemoveDrone.py
@@ -0,0 +1,31 @@
+import wx
+from service.fit import Fit
+
+import gui.mainFrame
+from gui import globalEvents as GE
+from .calc.fitRemoveDrone import FitRemoveDroneCommand
+
+
+class GuiRemoveDroneCommand(wx.Command):
+ def __init__(self, fitID, position, amount=1):
+ wx.Command.__init__(self, True, "Cargo Add")
+ self.mainFrame = gui.mainFrame.MainFrame.getInstance()
+ self.sFit = Fit.getInstance()
+ self.internal_history = wx.CommandProcessor()
+ self.fitID = fitID
+ self.position = position
+ self.amount = amount
+
+ def Do(self):
+ cmd = FitRemoveDroneCommand(self.fitID, self.position, self.amount)
+ if self.internal_history.Submit(cmd):
+ self.sFit.recalc(self.fitID)
+ wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.fitID))
+ return True
+ return False
+
+ def Undo(self):
+ for _ in self.internal_history.Commands:
+ self.internal_history.Undo()
+ wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.fitID))
+ return True
diff --git a/gui/fitCommands/guiRemoveFighter.py b/gui/fitCommands/guiRemoveFighter.py
new file mode 100644
index 000000000..f1b983ec5
--- /dev/null
+++ b/gui/fitCommands/guiRemoveFighter.py
@@ -0,0 +1,32 @@
+import wx
+from service.fit import Fit
+
+import gui.mainFrame
+from gui import globalEvents as GE
+from .calc.fitRemoveFighter import FitRemoveFighterCommand
+
+
+class GuiRemoveFighterCommand(wx.Command):
+ def __init__(self, fitID, position):
+ wx.Command.__init__(self, True, "Module Remove")
+ self.mainFrame = gui.mainFrame.MainFrame.getInstance()
+ self.sFit = Fit.getInstance()
+ self.fitID = fitID
+ self.position = position
+ self.internal_history = wx.CommandProcessor()
+
+ def Do(self):
+ success = self.internal_history.Submit(FitRemoveFighterCommand(self.fitID, self.position))
+
+ if success:
+ self.sFit.recalc(self.fitID)
+ wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.fitID))
+ return True
+ return False
+
+ def Undo(self):
+ for _ in self.internal_history.Commands:
+ self.internal_history.Undo()
+ self.sFit.recalc(self.fitID)
+ wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.fitID))
+ return True
diff --git a/gui/fitCommands/guiRemoveImplant.py b/gui/fitCommands/guiRemoveImplant.py
new file mode 100644
index 000000000..af9ee74bc
--- /dev/null
+++ b/gui/fitCommands/guiRemoveImplant.py
@@ -0,0 +1,28 @@
+import wx
+from service.fit import Fit
+
+import gui.mainFrame
+from gui import globalEvents as GE
+from .calc.fitRemoveImplant import FitRemoveImplantCommand
+
+
+class GuiRemoveImplantCommand(wx.Command):
+ def __init__(self, fitID, position):
+ wx.Command.__init__(self, True, "")
+ self.mainFrame = gui.mainFrame.MainFrame.getInstance()
+ self.sFit = Fit.getInstance()
+ self.internal_history = wx.CommandProcessor()
+ self.fitID = fitID
+ self.position = position
+
+ def Do(self):
+ if self.internal_history.Submit(FitRemoveImplantCommand(self.fitID, self.position)):
+ wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.fitID))
+ return True
+ return False
+
+ def Undo(self):
+ for _ in self.internal_history.Commands:
+ self.internal_history.Undo()
+ wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.fitID))
+ return True
diff --git a/gui/fitCommands/guiRemoveModule.py b/gui/fitCommands/guiRemoveModule.py
new file mode 100644
index 000000000..944a63160
--- /dev/null
+++ b/gui/fitCommands/guiRemoveModule.py
@@ -0,0 +1,40 @@
+import wx
+from service.fit import Fit
+
+import gui.mainFrame
+from gui import globalEvents as GE
+from .helpers import ModuleInfoCache
+from .calc.fitRemoveModule import FitRemoveModuleCommand
+
+
+class GuiModuleRemoveCommand(wx.Command):
+ def __init__(self, fitID, modules):
+ """
+ Handles removing modules from fit.modules,
+
+ :param fitID: The fit ID that we are modifying
+ :param modules: A list of Module objects that we are attempting to remove.
+ """
+ wx.Command.__init__(self, True, "Module Remove")
+ self.mainFrame = gui.mainFrame.MainFrame.getInstance()
+ self.sFit = Fit.getInstance()
+ self.fitID = fitID
+ self.modCache = [ModuleInfoCache(
+ mod.modPosition, mod.item.ID, mod.state, mod.charge, mod.baseItemID, mod.mutaplasmidID) for mod in modules if not mod.isEmpty]
+ self.internal_history = wx.CommandProcessor()
+
+ def Do(self):
+ success = self.internal_history.Submit(FitRemoveModuleCommand(self.fitID, [mod.modPosition for mod in self.modCache]))
+
+ if success:
+ self.sFit.recalc(self.fitID)
+ wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.fitID, action="moddel", typeID=set([mod.itemID for mod in self.modCache])))
+ return True
+ return False
+
+ def Undo(self):
+ for _ in self.internal_history.Commands:
+ self.internal_history.Undo()
+ self.sFit.recalc(self.fitID)
+ wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.fitID, action="modadd", typeID=set([mod.itemID for mod in self.modCache])))
+ return True
diff --git a/gui/fitCommands/guiRemoveProjected.py b/gui/fitCommands/guiRemoveProjected.py
new file mode 100644
index 000000000..74d1ab308
--- /dev/null
+++ b/gui/fitCommands/guiRemoveProjected.py
@@ -0,0 +1,89 @@
+import wx
+from service.fit import Fit
+
+import gui.mainFrame
+from gui import globalEvents as GE
+from .calc.fitRemoveProjectedModule import FitRemoveProjectedModuleCommand
+from .calc.fitRemoveProjectedEnv import FitRemoveProjectedEnvCommand
+from .calc.fitRemoveProjectedFit import FitRemoveProjectedFitCommand
+from .calc.fitRemoveProjectedFighter import FitRemoveProjectedFighterCommand
+from logbook import Logger
+from .calc.fitRemoveProjectedDrone import FitRemoveProjectedDroneCommand
+
+from eos.saveddata.drone import Drone
+from eos.saveddata.module import Module
+from eos.saveddata.fighter import Fighter
+
+pyfalog = Logger(__name__)
+
+
+class GuiRemoveProjectedCommand(wx.Command):
+ mapping = {
+ 'fit': FitRemoveProjectedFitCommand,
+ 'module': FitRemoveProjectedModuleCommand,
+ 'fighter': FitRemoveProjectedFighterCommand,
+ 'env': FitRemoveProjectedEnvCommand,
+ 'drone': FitRemoveProjectedDroneCommand
+ }
+
+ def __init__(self, fitID, thing):
+ wx.Command.__init__(self, True, "Projected Add")
+ self.mainFrame = gui.mainFrame.MainFrame.getInstance()
+ self.sFit = Fit.getInstance()
+ self.internal_history = wx.CommandProcessor()
+ self.fitID = fitID
+ fit = self.sFit.getFit(fitID)
+
+ if isinstance(thing, Drone):
+ self.data = fit.projectedDrones.index(thing)
+ self.type = 'drone'
+ elif isinstance(thing, Module):
+ # todo: projected stuff should be wrapped in a projected class wrapper for easier maintainence
+ if thing.item.group.name in Module.SYSTEM_GROUPS:
+ self.type = 'env'
+ self.data = thing.itemID
+ else:
+ self.type = 'module'
+ self.data = fit.projectedModules.index(thing)
+ elif isinstance(thing, Fighter):
+ self.data = fit.projectedFighters.index(thing)
+ self.type = 'fighter'
+ else:
+ # todo: fix!
+ self.data = thing.ID
+ self.type = 'fit'
+
+ def Do(self):
+ result = False
+ # since we can project various types, we need to switch of the fit command. We can't do this switch easily in
+ # the fit command since each type might have a different kind of undo, easier to split it out
+
+ cls = self.mapping.get(self.type, None)
+ if cls:
+ cmd = cls(self.fitID, self.data)
+ result = self.internal_history.Submit(cmd)
+
+ # if item.category.name == "Drone":
+ # pyfalog.warn("DRONE REMOVE PROJECTION NOT IMPLEMENTED")
+ # elif item.category.name == "Fighter":
+ # pyfalog.warn("FIGHTER REMOVE PROJECTION NOT IMPLEMENTED")
+ # elif item.group.name in Module.SYSTEM_GROUPS:
+ # result = self.internal_history.Submit(FitRemoveProjectedEnvCommand(self.fitID, self.id))
+ # else:
+ # # attempt a regular module projection
+ #
+ # elif self.type == 'fit':
+ # pyfalog.warn("FIT REMOVE PROJECTION NOT IMPLEMENTED")
+
+ if result:
+ self.sFit.recalc(self.fitID)
+ wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.fitID))
+ return True
+ return False
+
+ def Undo(self):
+ for _ in self.internal_history.Commands:
+ self.internal_history.Undo()
+ self.sFit.recalc(self.fitID)
+ wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.fitID))
+ return True
diff --git a/gui/fitCommands/guiSetMode.py b/gui/fitCommands/guiSetMode.py
new file mode 100644
index 000000000..9639028f9
--- /dev/null
+++ b/gui/fitCommands/guiSetMode.py
@@ -0,0 +1,30 @@
+import wx
+from service.fit import Fit
+
+import gui.mainFrame
+from gui import globalEvents as GE
+from .calc.fitSetMode import FitSetModeCommand
+
+
+class GuiSetModeCommand(wx.Command):
+ def __init__(self, fitID, mode):
+ wx.Command.__init__(self, True, "Cargo Add")
+ self.mainFrame = gui.mainFrame.MainFrame.getInstance()
+ self.sFit = Fit.getInstance()
+ self.internal_history = wx.CommandProcessor()
+ self.fitID = fitID
+ self.mode = mode
+
+ def Do(self):
+ if self.internal_history.Submit(FitSetModeCommand(self.fitID, self.mode)):
+ self.sFit.recalc(self.fitID)
+ wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.fitID))
+ return True
+ return False
+
+ def Undo(self):
+ for _ in self.internal_history.Commands:
+ self.internal_history.Undo()
+ self.sFit.recalc(self.fitID)
+ wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.fitID))
+ return True
diff --git a/gui/fitCommands/guiSwapCloneModule.py b/gui/fitCommands/guiSwapCloneModule.py
new file mode 100644
index 000000000..cbeaf0845
--- /dev/null
+++ b/gui/fitCommands/guiSwapCloneModule.py
@@ -0,0 +1,50 @@
+import wx
+from service.fit import Fit
+
+import gui.mainFrame
+from gui import globalEvents as GE
+from gui.fitCommands.calc.fitSwapModule import FitSwapModuleCommand
+from .calc.fitCloneModule import FitCloneModuleCommand
+from logbook import Logger
+pyfalog = Logger(__name__)
+
+
+class GuiModuleSwapOrCloneCommand(wx.Command):
+
+ def __init__(self, fitID, srcPosition, dstPosition, clone=False):
+ wx.Command.__init__(self, True, "Module State Change")
+ self.mainFrame = gui.mainFrame.MainFrame.getInstance()
+ self.sFit = Fit.getInstance()
+ self.fitID = fitID
+ self.srcPosition = srcPosition
+ self.dstPosition = dstPosition
+ self.clone = clone
+ self.internal_history = wx.CommandProcessor()
+
+ def Do(self):
+ pyfalog.debug("{} Do()".format(self))
+
+ if self.clone:
+ pyfalog.debug("Trying to clone module")
+ if self.internal_history.Submit(FitCloneModuleCommand(self.fitID, self.srcPosition, self.dstPosition)):
+ self.sFit.recalc(self.fitID) # clone needs a recalc
+ wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.fitID))
+ return True
+ else:
+ pyfalog.debug("Trying to Swap module")
+ if self.internal_history.Submit(FitSwapModuleCommand(self.fitID, self.srcPosition, self.dstPosition)):
+ wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.fitID))
+ return True
+
+ return False
+
+ def Undo(self):
+ pyfalog.debug("{} Undo()".format(self))
+ for _ in self.internal_history.Commands:
+ self.internal_history.Undo()
+
+ if self.clone:
+ self.sFit.recalc(self.fitID)
+
+ wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.fitID))
+ return True
diff --git a/gui/fitCommands/guiToggleBooster.py b/gui/fitCommands/guiToggleBooster.py
new file mode 100644
index 000000000..2a586a888
--- /dev/null
+++ b/gui/fitCommands/guiToggleBooster.py
@@ -0,0 +1,30 @@
+import wx
+from service.fit import Fit
+
+import gui.mainFrame
+from gui import globalEvents as GE
+from .calc.fitToggleBooster import FitToggleBoosterCommand
+
+
+class GuiToggleBoosterCommand(wx.Command):
+ def __init__(self, fitID, position):
+ wx.Command.__init__(self, True, "")
+ self.mainFrame = gui.mainFrame.MainFrame.getInstance()
+ self.sFit = Fit.getInstance()
+ self.internal_history = wx.CommandProcessor()
+ self.fitID = fitID
+ self.position = position
+
+ def Do(self):
+ if self.internal_history.Submit(FitToggleBoosterCommand(self.fitID, self.position)):
+ self.sFit.recalc(self.fitID)
+ wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.fitID))
+ return True
+ return False
+
+ def Undo(self):
+ for _ in self.internal_history.Commands:
+ self.internal_history.Undo()
+ self.sFit.recalc(self.fitID)
+ wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.fitID))
+ return True
diff --git a/gui/fitCommands/guiToggleCommand.py b/gui/fitCommands/guiToggleCommand.py
new file mode 100644
index 000000000..50785da26
--- /dev/null
+++ b/gui/fitCommands/guiToggleCommand.py
@@ -0,0 +1,30 @@
+import wx
+from service.fit import Fit
+
+import gui.mainFrame
+from gui import globalEvents as GE
+from .calc.fitToggleCommand import FitToggleCommandCommand
+
+
+class GuiToggleCommandCommand(wx.Command):
+ def __init__(self, fitID, commandFitID):
+ wx.Command.__init__(self, True, "")
+ self.mainFrame = gui.mainFrame.MainFrame.getInstance()
+ self.sFit = Fit.getInstance()
+ self.internal_history = wx.CommandProcessor()
+ self.fitID = fitID
+ self.commandFitID = commandFitID
+
+ def Do(self):
+ if self.internal_history.Submit(FitToggleCommandCommand(self.fitID, self.commandFitID)):
+ self.sFit.recalc(self.fitID)
+ wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.fitID))
+ return True
+ return False
+
+ def Undo(self):
+ for _ in self.internal_history.Commands:
+ self.internal_history.Undo()
+ self.sFit.recalc(self.fitID)
+ wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.fitID))
+ return True
diff --git a/gui/fitCommands/guiToggleDrone.py b/gui/fitCommands/guiToggleDrone.py
new file mode 100644
index 000000000..7913df516
--- /dev/null
+++ b/gui/fitCommands/guiToggleDrone.py
@@ -0,0 +1,30 @@
+import wx
+from service.fit import Fit
+
+import gui.mainFrame
+from gui import globalEvents as GE
+from .calc.fitToggleDrone import FitToggleDroneCommand
+
+
+class GuiToggleDroneCommand(wx.Command):
+ def __init__(self, fitID, position):
+ wx.Command.__init__(self, True, "")
+ self.mainFrame = gui.mainFrame.MainFrame.getInstance()
+ self.sFit = Fit.getInstance()
+ self.internal_history = wx.CommandProcessor()
+ self.fitID = fitID
+ self.position = position
+
+ def Do(self):
+ if self.internal_history.Submit(FitToggleDroneCommand(self.fitID, self.position)):
+ self.sFit.recalc(self.fitID)
+ wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.fitID))
+ return True
+ return False
+
+ def Undo(self):
+ for _ in self.internal_history.Commands:
+ self.internal_history.Undo()
+ self.sFit.recalc(self.fitID)
+ wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.fitID))
+ return True
diff --git a/gui/fitCommands/guiToggleFighter.py b/gui/fitCommands/guiToggleFighter.py
new file mode 100644
index 000000000..f5aee10d6
--- /dev/null
+++ b/gui/fitCommands/guiToggleFighter.py
@@ -0,0 +1,30 @@
+import wx
+from service.fit import Fit
+
+import gui.mainFrame
+from gui import globalEvents as GE
+from .calc.fitToggleFighter import FitToggleFighterCommand
+
+
+class GuiToggleFighterCommand(wx.Command):
+ def __init__(self, fitID, position):
+ wx.Command.__init__(self, True, "")
+ self.mainFrame = gui.mainFrame.MainFrame.getInstance()
+ self.sFit = Fit.getInstance()
+ self.internal_history = wx.CommandProcessor()
+ self.fitID = fitID
+ self.position = position
+
+ def Do(self):
+ if self.internal_history.Submit(FitToggleFighterCommand(self.fitID, self.position)):
+ self.sFit.recalc(self.fitID)
+ wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.fitID))
+ return True
+ return False
+
+ def Undo(self):
+ for _ in self.internal_history.Commands:
+ self.internal_history.Undo()
+ self.sFit.recalc(self.fitID)
+ wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.fitID))
+ return True
diff --git a/gui/fitCommands/guiToggleImplant.py b/gui/fitCommands/guiToggleImplant.py
new file mode 100644
index 000000000..899eff0ee
--- /dev/null
+++ b/gui/fitCommands/guiToggleImplant.py
@@ -0,0 +1,30 @@
+import wx
+from service.fit import Fit
+
+import gui.mainFrame
+from gui import globalEvents as GE
+from .calc.fitToggleImplant import FitToggleImplantCommand
+
+
+class GuiToggleImplantCommand(wx.Command):
+ def __init__(self, fitID, position):
+ wx.Command.__init__(self, True, "")
+ self.mainFrame = gui.mainFrame.MainFrame.getInstance()
+ self.sFit = Fit.getInstance()
+ self.internal_history = wx.CommandProcessor()
+ self.fitID = fitID
+ self.position = position
+
+ def Do(self):
+ if self.internal_history.Submit(FitToggleImplantCommand(self.fitID, self.position)):
+ self.sFit.recalc(self.fitID)
+ wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.fitID))
+ return True
+ return False
+
+ def Undo(self):
+ for _ in self.internal_history.Commands:
+ self.internal_history.Undo()
+ self.sFit.recalc(self.fitID)
+ wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.fitID))
+ return True
diff --git a/gui/fitCommands/guiToggleModuleState.py b/gui/fitCommands/guiToggleModuleState.py
new file mode 100644
index 000000000..71e307f93
--- /dev/null
+++ b/gui/fitCommands/guiToggleModuleState.py
@@ -0,0 +1,33 @@
+import wx
+from service.fit import Fit
+
+import gui.mainFrame
+from gui import globalEvents as GE
+from .calc.fitChangeState import FitChangeStatesCommand
+
+
+class GuiModuleStateChangeCommand(wx.Command):
+ def __init__(self, fitID, baseMod, modules, click):
+ # todo: instead of modules, needs to be positions. Dead objects are a thing
+ wx.Command.__init__(self, True, "Module State Change")
+ self.mainFrame = gui.mainFrame.MainFrame.getInstance()
+ self.sFit = Fit.getInstance()
+ self.fitID = fitID
+ self.baseMod = baseMod
+ self.modules = modules
+ self.click = click
+ self.internal_history = wx.CommandProcessor()
+
+ def Do(self):
+ if self.internal_history.Submit(FitChangeStatesCommand(self.fitID, self.baseMod, self.modules, self.click)):
+ self.sFit.recalc(self.fitID)
+ wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.fitID))
+ return True
+ return False
+
+ def Undo(self):
+ for _ in self.internal_history.Commands:
+ self.internal_history.Undo()
+ self.sFit.recalc(self.fitID)
+ wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.fitID))
+ return True
diff --git a/gui/fitCommands/helpers.py b/gui/fitCommands/helpers.py
new file mode 100644
index 000000000..63aaf4b76
--- /dev/null
+++ b/gui/fitCommands/helpers.py
@@ -0,0 +1,3 @@
+from collections import namedtuple
+
+ModuleInfoCache = namedtuple('ModuleInfoCache', ['modPosition', 'itemID', 'state', 'charge', 'baseID', 'mutaplasmidID'])
diff --git a/gui/mainFrame.py b/gui/mainFrame.py
index e5f9fd830..2a3665f74 100644
--- a/gui/mainFrame.py
+++ b/gui/mainFrame.py
@@ -17,77 +17,62 @@
# along with pyfa. If not, see .
# =============================================================================
-import sys
-import os.path
-from logbook import Logger
-
-import sqlalchemy
-# noinspection PyPackageRequirements
-import wx
-# noinspection PyPackageRequirements
-from wx.lib.wordwrap import wordwrap
-# noinspection PyPackageRequirements
-from wx.lib.inspection import InspectionTool
-import time
-
-from codecs import open
-
-import config
-
-from eos.config import gamedata_version, gamedata_date
import datetime
-
-import gui.aboutData
-from gui.chrome_tabs import ChromeNotebook
-import gui.globalEvents as GE
-
-from gui.bitmap_loader import BitmapLoader
-from gui.mainMenuBar import MainMenuBar
-from gui.additionsPane import AdditionsPane
-from gui.marketBrowser import MarketBrowser
-from gui.builtinMarketBrowser.events import ItemSelected
-from gui.multiSwitch import MultiSwitch
-from gui.statsPane import StatsPane
-from gui.shipBrowser import ShipBrowser
-from gui.builtinShipBrowser.events import FitSelected, ImportSelected, Stage3Selected
-from gui.characterEditor import CharacterEditor
-from gui.characterSelection import CharacterSelection
-from gui.patternEditor import DmgPatternEditorDlg
-from gui.resistsEditor import ResistsEditorDlg
-from gui.setEditor import ImplantSetEditorDlg
-from gui.devTools import DevTools
-from gui.preferenceDialog import PreferenceDialog
-from gui.graphFrame import GraphFrame
-from gui.ssoLogin import SsoLogin
-from gui.copySelectDialog import CopySelectDialog
-from gui.utils.clipboard import toClipboard, fromClipboard
-from gui.updateDialog import UpdateDialog
-# noinspection PyUnresolvedReferences
-from gui.builtinViews import emptyView, entityEditor, fittingView, implantEditor # noqa: F401
-from gui import graphFrame
-
-from service.settings import SettingsProvider
-from service.fit import Fit
-from service.character import Character
-from service.update import Update
-from service.esiAccess import SsoMode
-
-# import this to access override setting
-from eos.modifiedAttributeDict import ModifiedAttributeDict
-from eos.db.saveddata.loadDefaultDatabaseValues import DefaultDatabaseValues
-from eos.db.saveddata.queries import getFit as db_getFit
-from service.port import Port, IPortUser
-from service.efsPort import EfsPort
-from service.settings import HTMLExportSettings
-
+import os.path
+import threading
+import time
+import webbrowser
+from codecs import open
from time import gmtime, strftime
-import threading
-import webbrowser
+# noinspection PyPackageRequirements
+import wx
import wx.adv
+from logbook import Logger
+# noinspection PyPackageRequirements
+# noinspection PyPackageRequirements
+from wx.lib.inspection import InspectionTool
-from service.esi import Esi, LoginMethod
+import config
+import gui.globalEvents as GE
+from eos.config import gamedata_date, gamedata_version
+from eos.db.saveddata.loadDefaultDatabaseValues import DefaultDatabaseValues
+from eos.db.saveddata.queries import getFit as db_getFit
+# import this to access override setting
+from eos.modifiedAttributeDict import ModifiedAttributeDict
+from gui import graphFrame
+from gui.additionsPane import AdditionsPane
+from gui.bitmap_loader import BitmapLoader
+from gui.builtinMarketBrowser.events import ItemSelected
+from gui.builtinShipBrowser.events import FitSelected, ImportSelected, Stage3Selected
+# noinspection PyUnresolvedReferences
+from gui.builtinViews import emptyView, entityEditor, fittingView, implantEditor # noqa: F401
+from gui.characterEditor import CharacterEditor
+from gui.characterSelection import CharacterSelection
+from gui.chrome_tabs import ChromeNotebook
+from gui.copySelectDialog import CopySelectDialog
+from gui.devTools import DevTools
from gui.esiFittings import EveFittings, ExportToEve, SsoCharacterMgmt
+from gui.graphFrame import GraphFrame
+from gui.mainMenuBar import MainMenuBar
+from gui.marketBrowser import MarketBrowser
+from gui.multiSwitch import MultiSwitch
+from gui.patternEditor import DmgPatternEditorDlg
+from gui.preferenceDialog import PreferenceDialog
+from gui.resistsEditor import ResistsEditorDlg
+from gui.setEditor import ImplantSetEditorDlg
+from gui.shipBrowser import ShipBrowser
+from gui.ssoLogin import SsoLogin
+from gui.statsPane import StatsPane
+from gui.updateDialog import UpdateDialog
+from gui.utils.clipboard import fromClipboard, toClipboard
+from service.character import Character
+from service.esi import Esi, LoginMethod
+from service.esiAccess import SsoMode
+from service.fit import Fit
+from service.port import EfsPort, IPortUser, Port
+from service.settings import HTMLExportSettings, SettingsProvider
+from service.update import Update
disableOverrideEditor = False
@@ -246,6 +231,10 @@ class MainFrame(wx.Frame):
self.Bind(GE.EVT_SSO_LOGIN, self.onSSOLogin)
self.Bind(GE.EVT_SSO_LOGGING_IN, self.ShowSsoLogin)
+ @property
+ def command(self) -> wx.CommandProcessor:
+ return Fit.getCommandProcessor(self.getActiveFit())
+
def ShowSsoLogin(self, event):
if getattr(event, "login_mode", LoginMethod.SERVER) == LoginMethod.MANUAL and getattr(event, "sso_mode", SsoMode.AUTO) == SsoMode.AUTO:
dlg = SsoLogin(self)
@@ -517,6 +506,10 @@ class MainFrame(wx.Frame):
self.Bind(wx.EVT_MENU, self.showPreferenceDialog, id=wx.ID_PREFERENCES)
# User guide
self.Bind(wx.EVT_MENU, self.goWiki, id=menuBar.wikiId)
+
+ self.Bind(wx.EVT_MENU, lambda evt: MainFrame.getInstance().command.Undo(), id=wx.ID_UNDO)
+
+ self.Bind(wx.EVT_MENU, lambda evt: MainFrame.getInstance().command.Redo(), id=wx.ID_REDO)
# EVE Forums
self.Bind(wx.EVT_MENU, self.goForums, id=menuBar.forumId)
# Save current character
@@ -705,31 +698,31 @@ class MainFrame(wx.Frame):
else:
self.marketBrowser.search.Focus()
- def clipboardEft(self):
+ def clipboardEft(self, options):
fit = db_getFit(self.getActiveFit())
- toClipboard(Port.exportEft(fit))
+ toClipboard(Port.exportEft(fit, options))
- def clipboardEftImps(self):
+ def clipboardEftImps(self, options):
fit = db_getFit(self.getActiveFit())
toClipboard(Port.exportEftImps(fit))
- def clipboardDna(self):
+ def clipboardDna(self, options):
fit = db_getFit(self.getActiveFit())
toClipboard(Port.exportDna(fit))
- def clipboardEsi(self):
+ def clipboardEsi(self, options):
fit = db_getFit(self.getActiveFit())
toClipboard(Port.exportESI(fit))
- def clipboardXml(self):
+ def clipboardXml(self, options):
fit = db_getFit(self.getActiveFit())
toClipboard(Port.exportXml(None, fit))
- def clipboardMultiBuy(self):
+ def clipboardMultiBuy(self, options):
fit = db_getFit(self.getActiveFit())
toClipboard(Port.exportMultiBuy(fit))
- def clipboardEfs(self):
+ def clipboardEfs(self, options):
fit = db_getFit(self.getActiveFit())
toClipboard(EfsPort.exportEfs(fit, 0))
@@ -744,7 +737,7 @@ class MainFrame(wx.Frame):
def exportToClipboard(self, event):
CopySelectDict = {CopySelectDialog.copyFormatEft: self.clipboardEft,
- CopySelectDialog.copyFormatEftImps: self.clipboardEftImps,
+ # CopySelectDialog.copyFormatEftImps: self.clipboardEftImps,
CopySelectDialog.copyFormatXml: self.clipboardXml,
CopySelectDialog.copyFormatDna: self.clipboardDna,
CopySelectDialog.copyFormatEsi: self.clipboardEsi,
@@ -753,8 +746,13 @@ class MainFrame(wx.Frame):
dlg = CopySelectDialog(self)
dlg.ShowModal()
selected = dlg.GetSelected()
+ options = dlg.GetOptions()
- CopySelectDict[selected]()
+ settings = SettingsProvider.getInstance().getSettings("pyfaExport")
+ settings["format"] = selected
+ settings["options"] = options
+
+ CopySelectDict[selected](options)
try:
dlg.Destroy()
diff --git a/gui/mainMenuBar.py b/gui/mainMenuBar.py
index 9c421f779..c0a28f1dc 100644
--- a/gui/mainMenuBar.py
+++ b/gui/mainMenuBar.py
@@ -90,8 +90,8 @@ class MainMenuBar(wx.MenuBar):
editMenu = wx.Menu()
self.Append(editMenu, "&Edit")
- # editMenu.Append(wx.ID_UNDO)
- # editMenu.Append(wx.ID_REDO)
+ editMenu.Append(wx.ID_UNDO)
+ editMenu.Append(wx.ID_REDO)
editMenu.Append(wx.ID_COPY, "To Clipboard\tCTRL+C", "Export a fit to the clipboard")
editMenu.Append(wx.ID_PASTE, "From Clipboard\tCTRL+V", "Import a fit from the clipboard")
diff --git a/gui/pyfa_gauge.py b/gui/pyfa_gauge.py
index 61379c5b0..c3e3e1c16 100644
--- a/gui/pyfa_gauge.py
+++ b/gui/pyfa_gauge.py
@@ -14,11 +14,10 @@
# ===============================================================================
import copy
+
import wx
-from gui.utils import color as color_utils
-from gui.utils import draw, anim_effects
-from service.fit import Fit
+from gui.utils import anim_effects, color as color_utils, draw
class PyGauge(wx.Window):
diff --git a/pyfa.py b/pyfa.py
index 740d443ab..d73ef0975 100755
--- a/pyfa.py
+++ b/pyfa.py
@@ -19,13 +19,14 @@
# ==============================================================================
+import datetime
import os
import sys
-import traceback
from optparse import AmbiguousOptionError, BadOptionError, OptionParser
-from service.prereqsCheck import PreCheckException, PreCheckMessage, version_precheck, version_block
+
import config
-import datetime
+from service.prereqsCheck import PreCheckException, PreCheckMessage, version_block, version_precheck
+
# ascii_text = '''
# ++++++++++++++++++++++++++++++++++++++++++++++++++
diff --git a/scripts/dynamicattributes.py b/scripts/dynamicattributes.py
index f21a5f96a..b6a2d720b 100644
--- a/scripts/dynamicattributes.py
+++ b/scripts/dynamicattributes.py
@@ -6,13 +6,8 @@ This script will generate a dynamicItemAttributes.json file using res files
import argparse
-import os
-import re
-import sqlite3
import json
-
-from PIL import Image
-
+import os
from shutil import copyfile
parser = argparse.ArgumentParser(description='This script updates module icons for pyfa')
diff --git a/scripts/effectUsedBy.py b/scripts/effectUsedBy.py
index dc231c61c..aa5f878b3 100755
--- a/scripts/effectUsedBy.py
+++ b/scripts/effectUsedBy.py
@@ -410,7 +410,7 @@ else:
if not effect_file.startswith('__'):
file_name, file_extension = effect_file.rsplit('.', 1)
# Ignore non-py files and exclude implementation-specific 'effects'
- if file_extension == "py" and not file_name in ("__init__"):
+ if file_extension == "py" and not file_name in "__init__":
effect_list.append(file_name)
# Stage 2
diff --git a/scripts/icons_update.py b/scripts/icons_update.py
index 0ad4c5359..73813c505 100644
--- a/scripts/icons_update.py
+++ b/scripts/icons_update.py
@@ -6,15 +6,13 @@ This script updates only market/item icons.
import argparse
-import os
-import re
-import sqlite3
import json
+import os
+import sqlite3
+from shutil import copyfile
from PIL import Image
-from shutil import copyfile
-
parser = argparse.ArgumentParser(description='This script updates module icons for pyfa')
parser.add_argument('-e', '--eve', required=True, type=str, help='path to eve\'s ')
parser.add_argument('-s', '--server', required=False, default='tq', type=str, help='which server to use (defaults to tq)')
@@ -67,7 +65,7 @@ graphics = graphicIDsLoader.load(os.path.join(to_path, 'graphicIDs.fsdbinary'))
graphics_py_ob = {}
for x, v in graphics.items():
- if (hasattr(v, 'iconFolder')):
+ if hasattr(v, 'iconFolder'):
graphics_py_ob[x] = v.iconFolder
# Add children to market group list
diff --git a/scripts/itemDiff.py b/scripts/itemDiff.py
index 053e36222..e03f259d6 100755
--- a/scripts/itemDiff.py
+++ b/scripts/itemDiff.py
@@ -19,17 +19,16 @@
#===============================================================================
-'''
+"""
This script is used to compare two different database versions.
It shows removed/changed/new items with list of changed effects,
changed attributes and effects which were renamed
-'''
+"""
import argparse
import os.path
import re
import sqlite3
-import sys
script_dir = os.path.dirname(__file__)
default_old = os.path.join(script_dir, "..", "eve.db")
@@ -282,14 +281,12 @@ def main(old, new, groups=True, effects=True, attributes=True, renames=True):
oldgroup = old_itmdata[item][0]
groupdata = (S["unchanged"], oldgroup, None)
# Set old set of effects and mark all as unchanged
- effectsdata = {}
- effectsdata[S["unchanged"]] = set()
+ effectsdata = {S["unchanged"]: set()}
if effects:
oldeffects = old_itmdata[item][1]
effectsdata[S["unchanged"]].update(oldeffects)
# Set old set of attributes and mark all as unchanged
- attrdata = {}
- attrdata[S["unchanged"]] = {}
+ attrdata = {S["unchanged"]: {}}
if attributes:
oldattrs = old_itmdata[item][2]
for attr in oldattrs:
@@ -307,14 +304,12 @@ def main(old, new, groups=True, effects=True, attributes=True, renames=True):
newgroup = new_itmdata[item][0]
groupdata = (S["unchanged"], None, newgroup)
# Set new set of effects and mark all as unchanged
- effectsdata = {}
- effectsdata[S["unchanged"]] = set()
+ effectsdata = {S["unchanged"]: set()}
if effects:
neweffects = new_itmdata[item][1]
effectsdata[S["unchanged"]].update(neweffects)
# Set new set of attributes and mark all as unchanged
- attrdata = {}
- attrdata[S["unchanged"]] = {}
+ attrdata = {S["unchanged"]: {}}
if attributes:
newattrs = new_itmdata[item][2]
for attr in newattrs:
diff --git a/scripts/jsonToSql.py b/scripts/jsonToSql.py
index add0fe957..24492637a 100755
--- a/scripts/jsonToSql.py
+++ b/scripts/jsonToSql.py
@@ -18,10 +18,9 @@
# License along with eos. If not, see .
#======================================================================
+import functools
import os
import sys
-import functools
-import re
# Add eos root path to sys.path so we can import ourselves
path = os.path.dirname(__file__)
@@ -46,7 +45,6 @@ def main(db, json_path):
eos.config.debug = False
# Now thats done, we can import the eos modules using the config
- import eos.db
import eos.gamedata
# Create the database tables
@@ -241,7 +239,7 @@ def main(db, json_path):
row['iconFile'] = row['iconFile'].lower().replace('modules/', '').replace('.png', '')
if jsonName is 'clonegrades':
- if (row['alphaCloneID'] not in tmp):
+ if row['alphaCloneID'] not in tmp:
cloneParent = eos.gamedata.AlphaClone()
setattr(cloneParent, 'alphaCloneID', row['alphaCloneID'])
setattr(cloneParent, 'alphaCloneName', row['alphaCloneName'])
@@ -249,7 +247,7 @@ def main(db, json_path):
tmp.append(row['alphaCloneID'])
for k, v in row.items():
- if (isinstance(v, str)):
+ if isinstance(v, str):
v = v.strip()
setattr(instance, fieldMap.get(k, k), v)
diff --git a/scripts/sdeReadIcons.py b/scripts/sdeReadIcons.py
index 66010a53c..d099f6bd5 100644
--- a/scripts/sdeReadIcons.py
+++ b/scripts/sdeReadIcons.py
@@ -1,8 +1,8 @@
-'''
+"""
A change to EVE Online's cache format rendered Reverence unable to correctly dump the icons file. As a stop gap, this
reads the offical SDE iconIDs.yaml and populates our own icons.json file. This files should then be transferred to the
other JSON files Phobos dumps before being converted to SQL
-'''
+"""
import yaml
import json
diff --git a/scripts/t3conversions.py b/scripts/t3conversions.py
index 8b6e7e09a..4f2445071 100644
--- a/scripts/t3conversions.py
+++ b/scripts/t3conversions.py
@@ -2,11 +2,11 @@
# Requires eve-old.db file (which is the previous releases database so that we can lookup the old items)
# See https://community.eveonline.com/news/patch-notes/patch-notes-for-july-2017-release
-import sys
-from os.path import realpath, join, dirname, abspath
-from sqlalchemy import MetaData, create_engine
-from sqlalchemy.orm import sessionmaker
import csv
+import sys
+from os.path import abspath, dirname, join, realpath
+
+from sqlalchemy import create_engine
newDB = create_engine('sqlite:///' + unicode(realpath(join(dirname(abspath(__file__)), "..", "eve.db")), sys.getfilesystemencoding()))
oldDB = create_engine('sqlite:///' + unicode(realpath(join(dirname(abspath(__file__)), "..", "eve-old.db")), sys.getfilesystemencoding()))
diff --git a/service/attribute.py b/service/attribute.py
index b4c3453e9..b5ebcae66 100644
--- a/service/attribute.py
+++ b/service/attribute.py
@@ -33,10 +33,10 @@ class Attribute(object):
@staticmethod
def getAttributeInfo(identity):
if isinstance(identity, (int, str)):
- info = eos.db.getAttributeInfo(identity, eager=("unit"))
+ info = eos.db.getAttributeInfo(identity, eager="unit")
elif isinstance(identity, (int, float)):
id_ = int(identity)
- info = eos.db.getAttributeInfo(id_, eager=("unit"))
+ info = eos.db.getAttributeInfo(id_, eager="unit")
else:
info = None
return info
diff --git a/service/character.py b/service/character.py
index 2ff03de67..565d7d329 100644
--- a/service/character.py
+++ b/service/character.py
@@ -337,7 +337,7 @@ class Character(object):
@staticmethod
def getApiDetails(charID):
# todo: fix this (or get rid of?)
- return ("", "", "", [])
+ return "", "", "", []
char = eos.db.getCharacter(charID)
if char.chars is not None:
chars = json.loads(char.chars)
diff --git a/service/esi.py b/service/esi.py
index 4b5f8198f..7b5247bec 100644
--- a/service/esi.py
+++ b/service/esi.py
@@ -138,7 +138,7 @@ class Esi(EsiAccess):
def handleLogin(self, message):
# we already have authenticated stuff for the auto mode
- if (self.settings.get('ssoMode') == SsoMode.AUTO):
+ if self.settings.get('ssoMode') == SsoMode.AUTO:
ssoInfo = message['SSOInfo'][0]
auth_response = json.loads(base64.b64decode(ssoInfo))
else:
diff --git a/service/esiAccess.py b/service/esiAccess.py
index bbddc6384..db1931fa2 100644
--- a/service/esiAccess.py
+++ b/service/esiAccess.py
@@ -1,4 +1,4 @@
-'''
+"""
A lot of the inspiration (and straight up code copying!) for this class comes from EsiPy
Much of the credit goes to the maintainer of that package, Kyria . The reasoning for no
@@ -7,7 +7,7 @@ low level.
Eventually I'll rewrite this to be a bit cleaner and a bit more generic, but for now, it works!
-'''
+"""
# noinspection PyPackageRequirements
from logbook import Logger
@@ -63,7 +63,7 @@ class APIException(Exception):
elif 'message' in self.response:
return 'HTTP Error %s: %s' % (self.status_code,
self.response['message'])
- return 'HTTP Error %s' % (self.status_code)
+ return 'HTTP Error %s' % self.status_code
class ESIEndpoints(Enum):
@@ -89,7 +89,7 @@ class EsiAccess(object):
@property
def sso_url(self):
- if (self.settings.get("ssoMode") == SsoMode.CUSTOM):
+ if self.settings.get("ssoMode") == SsoMode.CUSTOM:
return "https://login.eveonline.com"
return "https://www.pyfa.io"
@@ -136,7 +136,7 @@ class EsiAccess(object):
def getLoginURI(self, redirect=None):
self.state = str(uuid.uuid4())
- if (self.settings.get("ssoMode") == SsoMode.AUTO):
+ if self.settings.get("ssoMode") == SsoMode.AUTO:
args = {
'state': self.state,
'pyfa_version': config.version,
@@ -256,7 +256,7 @@ class EsiAccess(object):
self._session.headers.update(self.get_oauth_header(ssoChar.accessToken))
def _after_request(self, resp):
- if ("warning" in resp.headers):
+ if "warning" in resp.headers:
pyfalog.warn("{} - {}".format(resp.headers["warning"], resp.url))
if resp.status_code >= 400:
diff --git a/service/fit.py b/service/fit.py
index d45219cad..d830dda3e 100644
--- a/service/fit.py
+++ b/service/fit.py
@@ -18,31 +18,48 @@
# ===============================================================================
import copy
-from logbook import Logger
-from time import time
import datetime
+from time import time
+
+import wx
+from logbook import Logger
import eos.db
-from eos.saveddata.booster import Booster as es_Booster
-from eos.saveddata.cargo import Cargo as es_Cargo
from eos.saveddata.character import Character as saveddata_Character
from eos.saveddata.citadel import Citadel as es_Citadel
from eos.saveddata.damagePattern import DamagePattern as es_DamagePattern
from eos.saveddata.drone import Drone as es_Drone
from eos.saveddata.fighter import Fighter as es_Fighter
-from eos.saveddata.implant import Implant as es_Implant
-from eos.saveddata.ship import Ship as es_Ship
-from eos.saveddata.module import Module as es_Module, State, Slot
from eos.saveddata.fit import Fit as FitType, ImplantLocation
+from eos.saveddata.module import Module as es_Module, State
+from eos.saveddata.ship import Ship as es_Ship
from service.character import Character
from service.damagePattern import DamagePattern
+from service.fitDeprecated import FitDeprecated
from service.settings import SettingsProvider
+from utils.deprecated import deprecated
pyfalog = Logger(__name__)
-class Fit(object):
+class DeferRecalc:
+ def __init__(self, fitID):
+ self.fitID = fitID
+ self.sFit = Fit.getInstance()
+
+ def __enter__(self):
+ self._recalc = self.sFit.recalc
+ self.sFit.recalc = lambda x: print('Deferred Recalc')
+
+ def __exit__(self, *args):
+ self.sFit.recalc = self._recalc
+ self.sFit.recalc(self.fitID)
+
+
+# inherits from FitDeprecated so that I can move all the dead shit, but not affect functionality
+class Fit(FitDeprecated):
instance = None
+ processors = {}
@classmethod
def getInstance(cls):
@@ -166,13 +183,6 @@ class Fit(object):
fit.booster = not fit.booster
eos.db.commit()
- @staticmethod
- def renameFit(fitID, newName):
- pyfalog.debug("Renaming fit ({0}) to: {1}", fitID, newName)
- fit = eos.db.getFit(fitID)
- fit.name = newName
- eos.db.commit()
-
@staticmethod
def deleteFit(fitID):
fit = eos.db.getFit(fitID)
@@ -196,12 +206,21 @@ class Fit(object):
eos.db.remove(fit)
+ if fitID in Fit.processors:
+ del Fit.processors[fitID]
+
pyfalog.debug(" Need to refresh {} fits: {}", len(refreshFits), refreshFits)
for fit in refreshFits:
eos.db.saveddata_session.refresh(fit)
eos.db.saveddata_session.commit()
+ @classmethod
+ def getCommandProcessor(cls, fitID):
+ if fitID not in cls.processors:
+ cls.processors[fitID] = wx.CommandProcessor()
+ return cls.processors[fitID]
+
@staticmethod
def copyFit(fitID):
pyfalog.debug("Creating copy of fit ID: {0}", fitID)
@@ -316,142 +335,6 @@ class Fit(object):
fit.notes))
return fits
- def addImplant(self, fitID, itemID, recalc=True):
- pyfalog.debug("Adding implant to fit ({0}) for item ID: {1}", fitID, itemID)
- if fitID is None:
- return False
-
- fit = eos.db.getFit(fitID)
- item = eos.db.getItem(itemID, eager="attributes")
- try:
- implant = es_Implant(item)
- except ValueError:
- pyfalog.warning("Invalid item: {0}", itemID)
- return False
-
- fit.implants.append(implant)
- if recalc:
- self.recalc(fit)
- return True
-
- def removeImplant(self, fitID, position, recalc=True):
- pyfalog.debug("Removing implant from position ({0}) for fit ID: {1}", position, fitID)
- if fitID is None:
- return False
-
- fit = eos.db.getFit(fitID)
- implant = fit.implants[position]
- fit.implants.remove(implant)
- if recalc:
- self.recalc(fit)
- return True
-
- def addBooster(self, fitID, itemID, recalc=True):
- pyfalog.debug("Adding booster ({0}) to fit ID: {1}", itemID, fitID)
- if fitID is None:
- return False
-
- fit = eos.db.getFit(fitID)
- item = eos.db.getItem(itemID, eager="attributes")
- try:
- booster = es_Booster(item)
- except ValueError:
- pyfalog.warning("Invalid item: {0}", itemID)
- return False
-
- fit.boosters.append(booster)
- if recalc:
- self.recalc(fit)
- return True
-
- def removeBooster(self, fitID, position, recalc=True):
- pyfalog.debug("Removing booster from position ({0}) for fit ID: {1}", position, fitID)
- if fitID is None:
- return False
-
- fit = eos.db.getFit(fitID)
- booster = fit.boosters[position]
- fit.boosters.remove(booster)
- if recalc:
- self.recalc(fit)
- return True
-
- def project(self, fitID, thing):
- pyfalog.debug("Projecting fit ({0}) onto: {1}", fitID, thing)
- if fitID is None:
- return
-
- fit = eos.db.getFit(fitID)
-
- if isinstance(thing, int):
- thing = eos.db.getItem(thing,
- eager=("attributes", "group.category"))
-
- if isinstance(thing, es_Module):
- thing = copy.deepcopy(thing)
- fit.projectedModules.append(thing)
- elif isinstance(thing, FitType):
- if thing in fit.projectedFits:
- return
-
- fit.__projectedFits[thing.ID] = thing
-
- # this bit is required -- see GH issue # 83
- eos.db.saveddata_session.flush()
- eos.db.saveddata_session.refresh(thing)
- elif thing.category.name == "Drone":
- drone = None
- for d in fit.projectedDrones.find(thing):
- if d is None or d.amountActive == d.amount or d.amount >= 5:
- drone = d
- break
-
- if drone is None:
- drone = es_Drone(thing)
- fit.projectedDrones.append(drone)
-
- drone.amount += 1
- elif thing.category.name == "Fighter":
- fighter = es_Fighter(thing)
- fit.projectedFighters.append(fighter)
- elif thing.group.name in es_Module.SYSTEM_GROUPS:
- module = es_Module(thing)
- module.state = State.ONLINE
- fit.projectedModules.append(module)
- else:
- try:
- module = es_Module(thing)
- except ValueError:
- return False
- module.state = State.ACTIVE
- if not module.canHaveState(module.state, fit):
- module.state = State.OFFLINE
- fit.projectedModules.append(module)
-
- eos.db.commit()
- self.recalc(fit)
- return True
-
- def addCommandFit(self, fitID, thing):
- pyfalog.debug("Projecting command fit ({0}) onto: {1}", fitID, thing)
- if fitID is None:
- return
-
- fit = eos.db.getFit(fitID)
-
- if thing in fit.commandFits:
- return
-
- fit.__commandFits[thing.ID] = thing
-
- # this bit is required -- see GH issue # 83
- eos.db.saveddata_session.flush()
- eos.db.saveddata_session.refresh(thing)
-
- eos.db.commit()
- self.recalc(fit)
- return True
-
def toggleProjected(self, fitID, thing, click):
pyfalog.debug("Toggling projected on fit ({0}) for: {1}", fitID, thing)
fit = eos.db.getFit(fitID)
@@ -463,7 +346,7 @@ class Fit(object):
elif isinstance(thing, es_Fighter):
thing.active = not thing.active
elif isinstance(thing, es_Module):
- thing.state = self.__getProposedState(thing, click)
+ thing.state = es_Module.getProposedState(thing, click)
if not thing.canHaveState(thing.state, fit):
thing.state = State.OFFLINE
elif isinstance(thing, FitType):
@@ -474,60 +357,6 @@ class Fit(object):
eos.db.commit()
self.recalc(fit)
- def toggleCommandFit(self, fitID, thing):
- pyfalog.debug("Toggle command fit ({0}) for: {1}", fitID, thing)
- fit = eos.db.getFit(fitID)
- commandInfo = thing.getCommandInfo(fitID)
- if commandInfo:
- commandInfo.active = not commandInfo.active
-
- eos.db.commit()
- self.recalc(fit)
-
- def changeAmount(self, fitID, projected_fit, amount):
- """Change amount of projected fits"""
- pyfalog.debug("Changing fit ({0}) for projected fit ({1}) to new amount: {2}", fitID, projected_fit.getProjectionInfo(fitID), amount)
- fit = eos.db.getFit(fitID)
- amount = min(20, max(1, amount)) # 1 <= a <= 20
- projectionInfo = projected_fit.getProjectionInfo(fitID)
- if projectionInfo:
- projectionInfo.amount = amount
-
- eos.db.commit()
- self.recalc(fit)
-
- def changeActiveFighters(self, fitID, fighter, amount):
- pyfalog.debug("Changing active fighters ({0}) for fit ({1}) to amount: {2}", fighter.itemID, fitID, amount)
- fit = eos.db.getFit(fitID)
- fighter.amountActive = amount
-
- eos.db.commit()
- self.recalc(fit)
-
- def removeProjected(self, fitID, thing):
- pyfalog.debug("Removing projection on fit ({0}) from: {1}", fitID, thing)
- fit = eos.db.getFit(fitID)
- if isinstance(thing, es_Drone):
- fit.projectedDrones.remove(thing)
- elif isinstance(thing, es_Module):
- fit.projectedModules.remove(thing)
- elif isinstance(thing, es_Fighter):
- fit.projectedFighters.remove(thing)
- else:
- del fit.__projectedFits[thing.ID]
- # fit.projectedFits.remove(thing)
-
- eos.db.commit()
- self.recalc(fit)
-
- def removeCommand(self, fitID, thing):
- pyfalog.debug("Removing command projection from fit ({0}) for: {1}", fitID, thing)
- fit = eos.db.getFit(fitID)
- del fit.__commandFits[thing.ID]
-
- eos.db.commit()
- self.recalc(fit)
-
def changeMutatedValue(self, mutator, value):
pyfalog.debug("Changing mutated value for {} / {}: {} => {}".format(mutator.module, mutator.module.mutaplasmid, mutator.value, value))
mutator.value = value
@@ -535,64 +364,6 @@ class Fit(object):
eos.db.commit()
return mutator.value
- def appendModule(self, fitID, itemID):
- pyfalog.debug("Appending module for fit ({0}) using item: {1}", fitID, itemID)
- fit = eos.db.getFit(fitID)
- item = eos.db.getItem(itemID, eager=("attributes", "group.category"))
- try:
- m = es_Module(item)
- except ValueError:
- pyfalog.warning("Invalid item: {0}", itemID)
- return False
-
- if m.item.category.name == "Subsystem":
- fit.modules.freeSlot(m.getModifiedItemAttr("subSystemSlot"))
-
- if m.fits(fit):
- m.owner = fit
- numSlots = len(fit.modules)
- fit.modules.append(m)
- if m.isValidState(State.ACTIVE):
- m.state = State.ACTIVE
-
- # As some items may affect state-limiting attributes of the ship, calculate new attributes first
- self.recalc(fit)
- # Then, check states of all modules and change where needed. This will recalc if needed
- self.checkStates(fit, m)
-
- fit.fill()
- eos.db.commit()
-
- return numSlots != len(fit.modules)
- else:
- return None
-
- def removeModule(self, fitID, positions):
- """Removes modules based on a number of positions."""
- pyfalog.debug("Removing module from position ({0}) for fit ID: {1}", positions, fitID)
- fit = eos.db.getFit(fitID)
-
- # Convert scalar value to list
- if not isinstance(positions, list):
- positions = [positions]
-
- modulesChanged = False
- for x in positions:
- if not fit.modules[x].isEmpty:
- fit.modules.toDummy(x)
- modulesChanged = True
-
- # if no modules have changes, report back None
- if not modulesChanged:
- return None
-
- numSlots = len(fit.modules)
- self.recalc(fit)
- self.checkStates(fit, None)
- fit.fill()
- eos.db.commit()
- return numSlots != len(fit.modules)
-
def convertMutaplasmid(self, fitID, position, mutaplasmid):
# this is mostly the same thing as the self.changeModule method, however it initializes an abyssal module with
# the old module as it's base, and then replaces it
@@ -624,256 +395,7 @@ class Fit(object):
else:
return None
- def changeModule(self, fitID, position, newItemID):
- fit = eos.db.getFit(fitID)
- module = fit.modules[position]
-
- # We're trying to add a charge to a slot, which won't work. Instead, try to add the charge to the module in that slot.
- if self.isAmmo(newItemID) and not module.isEmpty:
- self.setAmmo(fitID, newItemID, [module])
- return True
-
- pyfalog.debug("Changing position of module from position ({0}) for fit ID: {1}", position, fitID)
-
- item = eos.db.getItem(newItemID, eager=("attributes", "group.category"))
-
- # Dummy it out in case the next bit fails
- fit.modules.toDummy(position)
- ret = None
- try:
- m = es_Module(item)
- except ValueError:
- pyfalog.warning("Invalid item: {0}", newItemID)
- return False
- if m.slot != module.slot:
- fit.modules.toModule(position, module)
- # Fits, but we selected wrong slot type, so don't want to overwrite because we will append on failure (none)
- ret = None
- elif m.fits(fit):
- m.owner = fit
- fit.modules.toModule(position, m)
- if m.isValidState(State.ACTIVE):
- m.state = State.ACTIVE
-
- # As some items may affect state-limiting attributes of the ship, calculate new attributes first
- self.recalc(fit)
- # Then, check states of all modules and change where needed. This will recalc if needed
- self.checkStates(fit, m)
-
- fit.fill()
- eos.db.commit()
-
- ret = True
- return ret
-
- 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]
- cargo = fit.cargo[cargoIdx]
-
- # We're trying to move a charge from cargo to a slot - try to add charge to dst module. Don't do anything with
- # the charge in the cargo (don't respect move vs copy)
- if self.isAmmo(cargo.item.ID):
- if not module.isEmpty:
- self.setAmmo(fitID, cargo.item.ID, [module])
- return
-
- pyfalog.debug("Moving cargo item to module for fit ID: {0}", fitID)
-
- # Gather modules and convert Cargo item to Module, silently return if not a module
- try:
- cargoP = es_Module(cargo.item)
- cargoP.owner = fit
- if cargoP.isValidState(State.ACTIVE):
- cargoP.state = State.ACTIVE
- except:
- pyfalog.warning("Invalid item: {0}", cargo.item)
- return
-
- 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
- fit.modules.insert(moduleIdx, module)
- return
-
- fit.modules.insert(moduleIdx, cargoP)
-
- if not copyMod: # remove existing cargo if not cloning
- if cargo.amount == 1:
- fit.cargo.remove(cargo)
- else:
- cargo.amount -= 1
-
- if not module.isEmpty: # if module is placeholder, we don't want to convert/add it
- moduleItem = module.item if not module.item.isAbyssal else module.baseItem
- for x in fit.cargo.find(moduleItem):
- x.amount += 1
- break
- else:
- moduleP = es_Cargo(moduleItem)
- moduleP.amount = 1
- fit.cargo.insert(cargoIdx, moduleP)
-
- eos.db.commit()
- self.recalc(fit)
-
- @staticmethod
- def swapModules(fitID, src, dst):
- pyfalog.debug("Swapping modules from source ({0}) to destination ({1}) for fit ID: {1}", src, dst, fitID)
- fit = eos.db.getFit(fitID)
- # Gather modules
- srcMod = fit.modules[src]
- dstMod = fit.modules[dst]
-
- # To swap, we simply remove mod and insert at destination.
- fit.modules.remove(srcMod)
- fit.modules.insert(dst, srcMod)
- fit.modules.remove(dstMod)
- fit.modules.insert(src, dstMod)
-
- 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
- """
- pyfalog.debug("Cloning modules from source ({0}) to destination ({1}) for fit ID: {1}", src, dst, fitID)
- fit = eos.db.getFit(fitID)
- # Gather modules
- srcMod = fit.modules[src]
- dstMod = fit.modules[dst] # should be a placeholder module
-
- new = copy.deepcopy(srcMod)
- new.owner = fit
- if new.fits(fit):
- # insert copy if module meets hardpoint restrictions
- fit.modules.remove(dstMod)
- fit.modules.insert(dst, new)
-
- 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
- given parameter, otherwise we increment
- """
- pyfalog.debug("Adding cargo ({0}) fit ID: {1}", itemID, fitID)
-
- if fitID is None:
- return False
-
- fit = eos.db.getFit(fitID)
- item = eos.db.getItem(itemID)
- cargo = None
-
- # adding from market
- for x in fit.cargo.find(item):
- if x is not None:
- # found item already in cargo, use previous value and remove old
- cargo = x
- fit.cargo.remove(x)
- break
-
- if cargo is None:
- # if we don't have the item already in cargo, use default values
- cargo = es_Cargo(item)
-
- fit.cargo.append(cargo)
- if replace:
- cargo.amount = amount
- else:
- cargo.amount += amount
-
- self.recalc(fit)
- eos.db.commit()
-
- return True
-
- def removeCargo(self, fitID, position):
- pyfalog.debug("Removing cargo from position ({0}) fit ID: {1}", position, fitID)
- if fitID is None:
- return False
-
- fit = eos.db.getFit(fitID)
- charge = fit.cargo[position]
- fit.cargo.remove(charge)
- self.recalc(fit)
- return True
-
- def addFighter(self, fitID, itemID, recalc=True):
- pyfalog.debug("Adding fighters ({0}) to fit ID: {1}", itemID, fitID)
- if fitID is None:
- return False
-
- fit = eos.db.getFit(fitID)
- item = eos.db.getItem(itemID, eager=("attributes", "group.category"))
- if item.category.name == "Fighter":
- fighter = None
- '''
- for d in fit.fighters.find(item):
- if d is not None and d.amountActive == 0 and d.amount < max(5, fit.extraAttributes["maxActiveDrones"]):
- drone = d
- break
- '''
- if fighter is None:
- fighter = es_Fighter(item)
- used = fit.getSlotsUsed(fighter.slot)
- total = fit.getNumSlots(fighter.slot)
- standardAttackActive = False
- for ability in fighter.abilities:
- if ability.effect.isImplemented and ability.effect.handlerName == 'fighterabilityattackm':
- # Activate "standard attack" if available
- ability.active = True
- standardAttackActive = True
- else:
- # Activate all other abilities (Neut, Web, etc) except propmods if no standard attack is active
- if ability.effect.isImplemented and \
- standardAttackActive is False and \
- ability.effect.handlerName != 'fighterabilitymicrowarpdrive' and \
- ability.effect.handlerName != 'fighterabilityevasivemaneuvers':
- ability.active = True
-
- if used >= total:
- fighter.active = False
-
- if fighter.fits(fit) is True:
- fit.fighters.append(fighter)
- else:
- return False
-
- eos.db.commit()
- if recalc:
- self.recalc(fit)
- return True
- else:
- return False
-
- def removeFighter(self, fitID, i, recalc=True):
- pyfalog.debug("Removing fighters from fit ID: {0}", fitID)
- fit = eos.db.getFit(fitID)
- f = fit.fighters[i]
- fit.fighters.remove(f)
-
- eos.db.commit()
- if recalc:
- self.recalc(fit)
- return True
-
+ @deprecated
def addDrone(self, fitID, itemID, numDronesToAdd=1, recalc=True):
pyfalog.debug("Adding {0} drones ({1}) to fit ID: {2}", numDronesToAdd, itemID, fitID)
if fitID is None:
@@ -902,111 +424,6 @@ class Fit(object):
else:
return False
- def mergeDrones(self, fitID, d1, d2, projected=False):
- pyfalog.debug("Merging drones on fit ID: {0}", fitID)
- if fitID is None:
- return False
-
- fit = eos.db.getFit(fitID)
- if d1.item != d2.item:
- return False
-
- if projected:
- fit.projectedDrones.remove(d1)
- else:
- fit.drones.remove(d1)
-
- d2.amount += d1.amount
- d2.amountActive += d1.amountActive
-
- # If we have less than the total number of drones active, make them all active. Fixes #728
- # This could be removed if we ever add an enhancement to make drone stacks partially active.
- if d2.amount > d2.amountActive:
- d2.amountActive = d2.amount
-
- eos.db.commit()
- self.recalc(fit)
- return True
-
- @staticmethod
- def splitDrones(fit, d, amount, l):
- pyfalog.debug("Splitting drones for fit ID: {0}", fit)
- total = d.amount
- active = d.amountActive > 0
- d.amount = amount
- d.amountActive = amount if active else 0
-
- newD = es_Drone(d.item)
- newD.amount = total - amount
- newD.amountActive = newD.amount if active else 0
- l.append(newD)
- eos.db.commit()
-
- def splitProjectedDroneStack(self, fitID, d, amount):
- pyfalog.debug("Splitting projected drone stack for fit ID: {0}", fitID)
- if fitID is None:
- return False
-
- fit = eos.db.getFit(fitID)
- self.splitDrones(fit, d, amount, fit.projectedDrones)
-
- def splitDroneStack(self, fitID, d, amount):
- pyfalog.debug("Splitting drone stack for fit ID: {0}", fitID)
- if fitID is None:
- return False
-
- fit = eos.db.getFit(fitID)
- self.splitDrones(fit, d, amount, fit.drones)
-
- def removeDrone(self, fitID, i, numDronesToRemove=1, recalc=True):
- pyfalog.debug("Removing {0} drones for fit ID: {1}", numDronesToRemove, fitID)
- fit = eos.db.getFit(fitID)
- d = fit.drones[i]
- d.amount -= numDronesToRemove
- if d.amountActive > 0:
- d.amountActive -= numDronesToRemove
-
- if d.amount == 0:
- del fit.drones[i]
-
- eos.db.commit()
- if recalc:
- self.recalc(fit)
- return True
-
- def toggleDrone(self, fitID, i):
- pyfalog.debug("Toggling drones for fit ID: {0}", fitID)
- fit = eos.db.getFit(fitID)
- d = fit.drones[i]
- if d.amount == d.amountActive:
- d.amountActive = 0
- else:
- d.amountActive = d.amount
-
- eos.db.commit()
- self.recalc(fit)
- return True
-
- def toggleFighter(self, fitID, i):
- pyfalog.debug("Toggling fighters for fit ID: {0}", fitID)
- fit = eos.db.getFit(fitID)
- f = fit.fighters[i]
- f.active = not f.active
-
- eos.db.commit()
- self.recalc(fit)
- return True
-
- def toggleImplant(self, fitID, i):
- pyfalog.debug("Toggling implant for fit ID: {0}", fitID)
- fit = eos.db.getFit(fitID)
- implant = fit.implants[i]
- implant.active = not implant.active
-
- eos.db.commit()
- self.recalc(fit)
- return True
-
def toggleImplantSource(self, fitID, source):
pyfalog.debug("Toggling implant source for fit ID: {0}", fitID)
fit = eos.db.getFit(fitID)
@@ -1031,16 +448,6 @@ class Fit(object):
self.recalc(fit)
return True
- def toggleBooster(self, fitID, i):
- pyfalog.debug("Toggling booster for fit ID: {0}", fitID)
- fit = eos.db.getFit(fitID)
- booster = fit.boosters[i]
- booster.active = not booster.active
-
- eos.db.commit()
- self.recalc(fit)
- return True
-
def toggleFighterAbility(self, fitID, ability):
pyfalog.debug("Toggling fighter ability for fit ID: {0}", fitID)
fit = eos.db.getFit(fitID)
@@ -1069,21 +476,8 @@ class Fit(object):
@staticmethod
def isAmmo(itemID):
- return eos.db.getItem(itemID).category.name == "Charge"
-
- def setAmmo(self, fitID, ammoID, modules):
- pyfalog.debug("Set ammo for fit ID: {0}", fitID)
- if fitID is None:
- return
-
- fit = eos.db.getFit(fitID)
- ammo = eos.db.getItem(ammoID) if ammoID else None
-
- for mod in modules:
- if mod.isValidCharge(ammo):
- mod.charge = ammo
-
- self.recalc(fit)
+ # todo: get rid of this form the service, use directly from item
+ return eos.db.getItem(itemID).isCharge
@staticmethod
def getTargetResists(fitID):
@@ -1125,17 +519,6 @@ class Fit(object):
self.recalc(fit)
- def setMode(self, fitID, mode):
- pyfalog.debug("Set mode for fit ID: {0}", fitID)
- if fitID is None:
- return
-
- fit = eos.db.getFit(fitID)
- fit.mode = mode
- eos.db.commit()
-
- self.recalc(fit)
-
def setAsPattern(self, fitID, ammo):
pyfalog.debug("Set as pattern for fit ID: {0}", fitID)
if fitID is None:
@@ -1179,74 +562,6 @@ class Fit(object):
if changed:
self.recalc(fit)
- def toggleModulesState(self, fitID, base, modules, click):
- pyfalog.debug("Toggle module state for fit ID: {0}", fitID)
- changed = False
- proposedState = self.__getProposedState(base, click)
-
- if proposedState != base.state:
- changed = True
- base.state = proposedState
- for mod in modules:
- if mod != base:
- p = self.__getProposedState(mod, click, proposedState)
- mod.state = p
- if p != mod.state:
- changed = True
-
- if changed:
- eos.db.commit()
- fit = eos.db.getFit(fitID)
-
- # As some items may affect state-limiting attributes of the ship, calculate new attributes first
- self.recalc(fit)
- # Then, check states of all modules and change where needed. This will recalc if needed
- self.checkStates(fit, base)
-
- # Old state : New State
- localMap = {
- State.OVERHEATED: State.ACTIVE,
- State.ACTIVE: State.ONLINE,
- State.OFFLINE: State.ONLINE,
- State.ONLINE: State.ACTIVE}
- projectedMap = {
- State.OVERHEATED: State.ACTIVE,
- State.ACTIVE: State.OFFLINE,
- State.OFFLINE: State.ACTIVE,
- State.ONLINE: State.ACTIVE} # Just in case
- # For system effects. They should only ever be online or offline
- projectedSystem = {
- State.OFFLINE: State.ONLINE,
- State.ONLINE: State.OFFLINE}
-
- def __getProposedState(self, mod, click, proposedState=None):
- pyfalog.debug("Get proposed state for module.")
- if mod.slot == Slot.SUBSYSTEM or mod.isEmpty:
- return State.ONLINE
-
- if mod.slot == Slot.SYSTEM:
- transitionMap = self.projectedSystem
- else:
- transitionMap = self.projectedMap if mod.projected else self.localMap
-
- currState = mod.state
-
- if proposedState is not None:
- state = proposedState
- elif click == "right":
- state = State.OVERHEATED
- elif click == "ctrl":
- state = State.OFFLINE
- else:
- state = transitionMap[currState]
- if not mod.isValidState(state):
- state = -1
-
- if mod.isValidState(state):
- return state
- else:
- return currState
-
def refreshFit(self, fitID):
pyfalog.debug("Refresh fit for fit ID: {0}", fitID)
if fitID is None:
@@ -1257,6 +572,8 @@ class Fit(object):
self.recalc(fit)
def recalc(self, fit):
+ if isinstance(fit, int):
+ fit = self.getFit(fit)
start_time = time()
pyfalog.info("=" * 10 + "recalc: {0}" + "=" * 10, fit.name)
@@ -1264,5 +581,5 @@ class Fit(object):
fit.clear()
fit.calculateModifiedAttributes()
-
+ fit.fill()
pyfalog.info("=" * 10 + "recalc time: " + str(time() - start_time) + "=" * 10)
diff --git a/service/fitDeprecated.py b/service/fitDeprecated.py
new file mode 100644
index 000000000..531191b9d
--- /dev/null
+++ b/service/fitDeprecated.py
@@ -0,0 +1,760 @@
+# ===============================================================================
+# 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 .
+# ===============================================================================
+
+import copy
+from logbook import Logger
+
+import eos.db
+from eos.saveddata.booster import Booster as es_Booster
+from eos.saveddata.cargo import Cargo as es_Cargo
+
+from eos.saveddata.drone import Drone as es_Drone
+from eos.saveddata.fighter import Fighter as es_Fighter
+from eos.saveddata.implant import Implant as es_Implant
+from eos.saveddata.module import Module as es_Module, State
+from eos.saveddata.fit import Fit as FitType
+from utils.deprecated import deprecated
+
+pyfalog = Logger(__name__)
+
+
+class FitDeprecated(object):
+
+ @staticmethod
+ @deprecated
+ def renameFit(fitID, newName):
+ pyfalog.debug("Renaming fit ({0}) to: {1}", fitID, newName)
+ fit = eos.db.getFit(fitID)
+ old_name = fit.name
+ fit.name = newName
+ eos.db.commit()
+ return old_name, newName
+
+ @deprecated
+ def toggleDrone(self, fitID, i):
+ pyfalog.debug("Toggling drones for fit ID: {0}", fitID)
+ fit = eos.db.getFit(fitID)
+ d = fit.drones[i]
+ if d.amount == d.amountActive:
+ d.amountActive = 0
+ else:
+ d.amountActive = d.amount
+
+ eos.db.commit()
+ self.recalc(fit)
+ return True
+
+ @deprecated
+ def mergeDrones(self, fitID, d1, d2, projected=False):
+ pyfalog.debug("Merging drones on fit ID: {0}", fitID)
+ if fitID is None:
+ return False
+
+ fit = eos.db.getFit(fitID)
+ if d1.item != d2.item:
+ return False
+
+ if projected:
+ fit.projectedDrones.remove(d1)
+ else:
+ fit.drones.remove(d1)
+
+ d2.amount += d1.amount
+ d2.amountActive += d1.amountActive
+
+ # If we have less than the total number of drones active, make them all active. Fixes #728
+ # This could be removed if we ever add an enhancement to make drone stacks partially active.
+ if d2.amount > d2.amountActive:
+ d2.amountActive = d2.amount
+
+ eos.db.commit()
+ self.recalc(fit)
+ return True
+
+ @staticmethod
+ @deprecated
+ def splitDrones(fit, d, amount, l):
+ pyfalog.debug("Splitting drones for fit ID: {0}", fit)
+ total = d.amount
+ active = d.amountActive > 0
+ d.amount = amount
+ d.amountActive = amount if active else 0
+
+ newD = es_Drone(d.item)
+ newD.amount = total - amount
+ newD.amountActive = newD.amount if active else 0
+ l.append(newD)
+ eos.db.commit()
+
+ @deprecated
+ def splitProjectedDroneStack(self, fitID, d, amount):
+ pyfalog.debug("Splitting projected drone stack for fit ID: {0}", fitID)
+ if fitID is None:
+ return False
+
+ fit = eos.db.getFit(fitID)
+ self.splitDrones(fit, d, amount, fit.projectedDrones)
+
+ @deprecated
+ def splitDroneStack(self, fitID, d, amount):
+ pyfalog.debug("Splitting drone stack for fit ID: {0}", fitID)
+ if fitID is None:
+ return False
+
+ fit = eos.db.getFit(fitID)
+ self.splitDrones(fit, d, amount, fit.drones)
+
+ @deprecated
+ def removeDrone(self, fitID, i, numDronesToRemove=1, recalc=True):
+ pyfalog.debug("Removing {0} drones for fit ID: {1}", numDronesToRemove, fitID)
+ fit = eos.db.getFit(fitID)
+ d = fit.drones[i]
+ d.amount -= numDronesToRemove
+ if d.amountActive > 0:
+ d.amountActive -= numDronesToRemove
+
+ if d.amount == 0:
+ del fit.drones[i]
+
+ eos.db.commit()
+ if recalc:
+ self.recalc(fit)
+ return True
+
+ @deprecated
+ def changeAmount(self, fitID, projected_fit, amount):
+ """Change amount of projected fits"""
+ pyfalog.debug("Changing fit ({0}) for projected fit ({1}) to new amount: {2}", fitID,
+ projected_fit.getProjectionInfo(fitID), amount)
+ fit = eos.db.getFit(fitID)
+ amount = min(20, max(1, amount)) # 1 <= a <= 20
+ projectionInfo = projected_fit.getProjectionInfo(fitID)
+ if projectionInfo:
+ projectionInfo.amount = amount
+
+ eos.db.commit()
+ self.recalc(fit)
+
+ @deprecated
+ def changeActiveFighters(self, fitID, fighter, amount):
+ pyfalog.debug("Changing active fighters ({0}) for fit ({1}) to amount: {2}", fighter.itemID, fitID, amount)
+ fit = eos.db.getFit(fitID)
+ fighter.amountActive = amount
+
+ eos.db.commit()
+ self.recalc(fit)
+
+ @deprecated
+ def addDrone(self, fitID, itemID, numDronesToAdd=1, recalc=True):
+ pyfalog.debug("Adding {0} drones ({1}) to fit ID: {2}", numDronesToAdd, itemID, fitID)
+ if fitID is None:
+ return False
+
+ fit = eos.db.getFit(fitID)
+ item = eos.db.getItem(itemID, eager=("attributes", "group.category"))
+ if item.category.name == "Drone":
+ drone = None
+ for d in fit.drones.find(item):
+ if d is not None and d.amountActive == 0 and d.amount < max(5, fit.extraAttributes["maxActiveDrones"]):
+ drone = d
+ break
+
+ if drone is None:
+ drone = es_Drone(item)
+ if drone.fits(fit) is True:
+ fit.drones.append(drone)
+ else:
+ return False
+ drone.amount += numDronesToAdd
+ eos.db.commit()
+ if recalc:
+ self.recalc(fit)
+ return True
+ else:
+ return False
+
+ @deprecated
+ def addImplant(self, fitID, itemID, recalc=True):
+ pyfalog.debug("Adding implant to fit ({0}) for item ID: {1}", fitID, itemID)
+ if fitID is None:
+ return False
+
+ fit = eos.db.getFit(fitID)
+ item = eos.db.getItem(itemID, eager="attributes")
+ try:
+ implant = es_Implant(item)
+ except ValueError:
+ pyfalog.warning("Invalid item: {0}", itemID)
+ return False
+
+ fit.implants.append(implant)
+ if recalc:
+ self.recalc(fit)
+ return True
+
+ @deprecated
+ def removeImplant(self, fitID, position, recalc=True):
+ pyfalog.debug("Removing implant from position ({0}) for fit ID: {1}", position, fitID)
+ if fitID is None:
+ return False
+
+ fit = eos.db.getFit(fitID)
+ implant = fit.implants[position]
+ fit.implants.remove(implant)
+ if recalc:
+ self.recalc(fit)
+ return True
+
+ @deprecated
+ def addBooster(self, fitID, itemID, recalc=True):
+ pyfalog.debug("Adding booster ({0}) to fit ID: {1}", itemID, fitID)
+ if fitID is None:
+ return False
+
+ fit = eos.db.getFit(fitID)
+ item = eos.db.getItem(itemID, eager="attributes")
+ try:
+ booster = es_Booster(item)
+ except ValueError:
+ pyfalog.warning("Invalid item: {0}", itemID)
+ return False
+
+ fit.boosters.append(booster)
+ if recalc:
+ self.recalc(fit)
+ return True
+
+ @deprecated
+ def removeBooster(self, fitID, position, recalc=True):
+ pyfalog.debug("Removing booster from position ({0}) for fit ID: {1}", position, fitID)
+ if fitID is None:
+ return False
+
+ fit = eos.db.getFit(fitID)
+ booster = fit.boosters[position]
+ fit.boosters.remove(booster)
+ if recalc:
+ self.recalc(fit)
+ return True
+
+ @deprecated
+ def project(self, fitID, thing):
+ pyfalog.debug("Projecting fit ({0}) onto: {1}", fitID, thing)
+ if fitID is None:
+ return
+
+ fit = eos.db.getFit(fitID)
+
+ if isinstance(thing, int):
+ thing = eos.db.getItem(thing,
+ eager=("attributes", "group.category"))
+
+ if isinstance(thing, es_Module):
+ thing = copy.deepcopy(thing)
+ fit.projectedModules.append(thing)
+ elif isinstance(thing, FitType):
+ if thing in fit.projectedFits:
+ return
+
+ fit.projectedFitDict[thing.ID] = thing
+
+ # this bit is required -- see GH issue # 83
+ eos.db.saveddata_session.flush()
+ eos.db.saveddata_session.refresh(thing)
+ elif thing.category.name == "Drone":
+ drone = None
+ for d in fit.projectedDrones.find(thing):
+ if d is None or d.amountActive == d.amount or d.amount >= 5:
+ drone = d
+ break
+
+ if drone is None:
+ drone = es_Drone(thing)
+ fit.projectedDrones.append(drone)
+
+ drone.amount += 1
+ elif thing.category.name == "Fighter":
+ fighter = es_Fighter(thing)
+ fit.projectedFighters.append(fighter)
+ elif thing.group.name in es_Module.SYSTEM_GROUPS:
+ module = es_Module(thing)
+ module.state = State.ONLINE
+ fit.projectedModules.append(module)
+ else:
+ try:
+ module = es_Module(thing)
+ except ValueError:
+ return False
+ module.state = State.ACTIVE
+ if not module.canHaveState(module.state, fit):
+ module.state = State.OFFLINE
+ fit.projectedModules.append(module)
+
+ eos.db.commit()
+ self.recalc(fit)
+ return True
+
+ @deprecated
+ def addCommandFit(self, fitID, thing):
+ pyfalog.debug("Projecting command fit ({0}) onto: {1}", fitID, thing)
+ if fitID is None:
+ return
+
+ fit = eos.db.getFit(fitID)
+
+ if thing in fit.commandFits:
+ return
+
+ fit.commandFitDict[thing.ID] = thing
+
+ # this bit is required -- see GH issue # 83
+ eos.db.saveddata_session.flush()
+ eos.db.saveddata_session.refresh(thing)
+
+ eos.db.commit()
+ self.recalc(fit)
+ return True
+
+ @deprecated
+ def toggleCommandFit(self, fitID, thing):
+ pyfalog.debug("Toggle command fit ({0}) for: {1}", fitID, thing)
+ fit = eos.db.getFit(fitID)
+ commandInfo = thing.getCommandInfo(fitID)
+ if commandInfo:
+ commandInfo.active = not commandInfo.active
+
+ eos.db.commit()
+ self.recalc(fit)
+
+ @deprecated
+ def removeProjected(self, fitID, thing):
+ pyfalog.debug("Removing projection on fit ({0}) from: {1}", fitID, thing)
+ fit = eos.db.getFit(fitID)
+ if isinstance(thing, es_Drone):
+ fit.projectedDrones.remove(thing)
+ elif isinstance(thing, es_Module):
+ fit.projectedModules.remove(thing)
+ elif isinstance(thing, es_Fighter):
+ fit.projectedFighters.remove(thing)
+ else:
+ del fit.projectedFitDict[thing.ID]
+ # fit.projectedFits.remove(thing)
+
+ eos.db.commit()
+ self.recalc(fit)
+
+ @deprecated
+ def removeCommand(self, fitID, thing):
+ pyfalog.debug("Removing command projection from fit ({0}) for: {1}", fitID, thing)
+ fit = eos.db.getFit(fitID)
+ del fit.commandFitDict[thing.ID]
+
+ eos.db.commit()
+ self.recalc(fit)
+
+ @deprecated
+ def appendModule(self, fitID, itemID):
+ pyfalog.debug("Appending module for fit ({0}) using item: {1}", fitID, itemID)
+ fit = eos.db.getFit(fitID)
+ item = eos.db.getItem(itemID, eager=("attributes", "group.category"))
+ try:
+ m = es_Module(item)
+ except ValueError:
+ pyfalog.warning("Invalid item: {0}", itemID)
+ return False
+
+ if m.item.category.name == "Subsystem":
+ fit.modules.freeSlot(m.getModifiedItemAttr("subSystemSlot"))
+
+ if m.fits(fit):
+ m.owner = fit
+ numSlots = len(fit.modules)
+ fit.modules.append(m)
+ if m.isValidState(State.ACTIVE):
+ m.state = State.ACTIVE
+
+ # As some items may affect state-limiting attributes of the ship, calculate new attributes first
+ self.recalc(fit)
+ # Then, check states of all modules and change where needed. This will recalc if needed
+ self.checkStates(fit, m)
+
+ fit.fill()
+ eos.db.commit()
+
+ return numSlots != len(fit.modules), m.modPosition
+ else:
+ return None, None
+
+ @deprecated
+ def removeModule(self, fitID, positions):
+ """Removes modules based on a number of positions."""
+ pyfalog.debug("Removing module from position ({0}) for fit ID: {1}", positions, fitID)
+ fit = eos.db.getFit(fitID)
+
+ # Convert scalar value to list
+ if not isinstance(positions, list):
+ positions = [positions]
+
+ modulesChanged = False
+ for x in positions:
+ if not fit.modules[x].isEmpty:
+ fit.modules.toDummy(x)
+ modulesChanged = True
+
+ # if no modules have changes, report back None
+ if not modulesChanged:
+ return None
+
+ numSlots = len(fit.modules)
+ self.recalc(fit)
+ self.checkStates(fit, None)
+ fit.fill()
+ eos.db.commit()
+ return numSlots != len(fit.modules)
+
+ @deprecated
+ def changeModule(self, fitID, position, newItemID, recalc=True):
+ fit = eos.db.getFit(fitID)
+
+ # We're trying to add a charge to a slot, which won't work. Instead, try to add the charge to the module in that slot.
+ if self.isAmmo(newItemID):
+ module = fit.modules[position]
+ if not module.isEmpty:
+ self.setAmmo(fitID, newItemID, [module])
+ return True
+
+ pyfalog.debug("Changing position of module from position ({0}) for fit ID: {1}", position, fitID)
+
+ item = eos.db.getItem(newItemID, eager=("attributes", "group.category"))
+
+ # Dummy it out in case the next bit fails
+ fit.modules.toDummy(position)
+
+ try:
+ m = es_Module(item)
+ except ValueError:
+ pyfalog.warning("Invalid item: {0}", newItemID)
+ return False
+
+ if m.fits(fit):
+ m.owner = fit
+ fit.modules.toModule(position, m)
+ if m.isValidState(State.ACTIVE):
+ m.state = State.ACTIVE
+
+ if recalc:
+ # As some items may affect state-limiting attributes of the ship, calculate new attributes first
+ self.recalc(fit)
+ # Then, check states of all modules and change where needed. This will recalc if needed
+ self.checkStates(fit, m)
+
+ fit.fill()
+ eos.db.commit()
+
+ return m
+ else:
+ return None
+
+ @deprecated
+ 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]
+ cargo = fit.cargo[cargoIdx]
+
+ # We're trying to move a charge from cargo to a slot - try to add charge to dst module. Don't do anything with
+ # the charge in the cargo (don't respect move vs copy)
+ if self.isAmmo(cargo.item.ID):
+ if not module.isEmpty:
+ self.setAmmo(fitID, cargo.item.ID, [module])
+ return
+
+ pyfalog.debug("Moving cargo item to module for fit ID: {0}", fitID)
+
+ # Gather modules and convert Cargo item to Module, silently return if not a module
+ try:
+ cargoP = es_Module(cargo.item)
+ cargoP.owner = fit
+ if cargoP.isValidState(State.ACTIVE):
+ cargoP.state = State.ACTIVE
+ except:
+ pyfalog.warning("Invalid item: {0}", cargo.item)
+ return
+
+ 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
+ fit.modules.insert(moduleIdx, module)
+ return
+
+ fit.modules.insert(moduleIdx, cargoP)
+
+ if not copyMod: # remove existing cargo if not cloning
+ if cargo.amount == 1:
+ fit.cargo.remove(cargo)
+ else:
+ cargo.amount -= 1
+
+ if not module.isEmpty: # if module is placeholder, we don't want to convert/add it
+ moduleItem = module.item if not module.item.isAbyssal else module.baseItem
+ for x in fit.cargo.find(moduleItem):
+ x.amount += 1
+ break
+ else:
+ moduleP = es_Cargo(moduleItem)
+ moduleP.amount = 1
+ fit.cargo.insert(cargoIdx, moduleP)
+
+ eos.db.commit()
+ self.recalc(fit)
+
+ @staticmethod
+ @deprecated
+ def swapModules(fitID, src, dst):
+ pyfalog.debug("Swapping modules from source ({0}) to destination ({1}) for fit ID: {1}", src, dst, fitID)
+ fit = eos.db.getFit(fitID)
+ # Gather modules
+ srcMod = fit.modules[src]
+ dstMod = fit.modules[dst]
+
+ # To swap, we simply remove mod and insert at destination.
+ fit.modules.remove(srcMod)
+ fit.modules.insert(dst, srcMod)
+ fit.modules.remove(dstMod)
+ fit.modules.insert(src, dstMod)
+
+ eos.db.commit()
+
+ @deprecated
+ 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
+ """
+ pyfalog.debug("Cloning modules from source ({0}) to destination ({1}) for fit ID: {1}", src, dst, fitID)
+ fit = eos.db.getFit(fitID)
+ # Gather modules
+ srcMod = fit.modules[src]
+ dstMod = fit.modules[dst] # should be a placeholder module
+
+ new = copy.deepcopy(srcMod)
+ new.owner = fit
+ if new.fits(fit):
+ # insert copy if module meets hardpoint restrictions
+ fit.modules.remove(dstMod)
+ fit.modules.insert(dst, new)
+
+ eos.db.commit()
+ self.recalc(fit)
+
+ @deprecated
+ 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
+ """
+ pyfalog.debug("Adding cargo ({0}) fit ID: {1}", itemID, fitID)
+
+ if fitID is None:
+ return False
+
+ fit = eos.db.getFit(fitID)
+ item = eos.db.getItem(itemID)
+ cargo = None
+
+ # adding from market
+ for x in fit.cargo.find(item):
+ if x is not None:
+ # found item already in cargo, use previous value and remove old
+ cargo = x
+ fit.cargo.remove(x)
+ break
+
+ if cargo is None:
+ # if we don't have the item already in cargo, use default values
+ cargo = es_Cargo(item)
+
+ fit.cargo.append(cargo)
+ if replace:
+ cargo.amount = amount
+ else:
+ cargo.amount += amount
+
+ self.recalc(fit)
+ eos.db.commit()
+
+ return True
+
+ @deprecated
+ def removeCargo(self, fitID, position):
+ pyfalog.debug("Removing cargo from position ({0}) fit ID: {1}", position, fitID)
+ if fitID is None:
+ return False
+
+ fit = eos.db.getFit(fitID)
+ charge = fit.cargo[position]
+ fit.cargo.remove(charge)
+ self.recalc(fit)
+ return True
+
+ @deprecated
+ def addFighter(self, fitID, itemID, recalc=True):
+ pyfalog.debug("Adding fighters ({0}) to fit ID: {1}", itemID, fitID)
+ if fitID is None:
+ return False
+
+ fit = eos.db.getFit(fitID)
+ item = eos.db.getItem(itemID, eager=("attributes", "group.category"))
+ if item.category.name == "Fighter":
+ fighter = None
+ '''
+ for d in fit.fighters.find(item):
+ if d is not None and d.amountActive == 0 and d.amount < max(5, fit.extraAttributes["maxActiveDrones"]):
+ drone = d
+ break
+ '''
+ if fighter is None:
+ fighter = es_Fighter(item)
+ used = fit.getSlotsUsed(fighter.slot)
+ total = fit.getNumSlots(fighter.slot)
+
+ if used >= total:
+ fighter.active = False
+
+ if fighter.fits(fit) is True:
+ fit.fighters.append(fighter)
+ else:
+ return False
+
+ eos.db.commit()
+ if recalc:
+ self.recalc(fit)
+ return True
+ else:
+ return False
+
+ @deprecated
+ def removeFighter(self, fitID, i, recalc=True):
+ pyfalog.debug("Removing fighters from fit ID: {0}", fitID)
+ fit = eos.db.getFit(fitID)
+ f = fit.fighters[i]
+ fit.fighters.remove(f)
+
+ eos.db.commit()
+ if recalc:
+ self.recalc(fit)
+ return True
+
+ @deprecated
+ def toggleFighter(self, fitID, i):
+ pyfalog.debug("Toggling fighters for fit ID: {0}", fitID)
+ fit = eos.db.getFit(fitID)
+ f = fit.fighters[i]
+ f.active = not f.active
+
+ eos.db.commit()
+ self.recalc(fit)
+ return True
+
+ @deprecated
+ def toggleImplant(self, fitID, i):
+ pyfalog.debug("Toggling implant for fit ID: {0}", fitID)
+ fit = eos.db.getFit(fitID)
+ implant = fit.implants[i]
+ implant.active = not implant.active
+
+ eos.db.commit()
+ self.recalc(fit)
+ return True
+
+ @deprecated
+ def toggleBooster(self, fitID, i):
+ pyfalog.debug("Toggling booster for fit ID: {0}", fitID)
+ fit = eos.db.getFit(fitID)
+ booster = fit.boosters[i]
+ booster.active = not booster.active
+
+ eos.db.commit()
+ self.recalc(fit)
+ return True
+
+ @deprecated
+ def setAmmo(self, fitID, ammoID, modules, recalc=True):
+ pyfalog.debug("Set ammo for fit ID: {0}", fitID)
+ if fitID is None:
+ return
+
+ fit = eos.db.getFit(fitID)
+ ammo = eos.db.getItem(ammoID) if ammoID else None
+
+ for mod in modules:
+ if mod.isValidCharge(ammo):
+ mod.charge = ammo
+
+ if recalc:
+ self.recalc(fit)
+
+ @deprecated
+ def setMode(self, fitID, mode):
+ pyfalog.debug("Set mode for fit ID: {0}", fitID)
+ if fitID is None:
+ return
+
+ fit = eos.db.getFit(fitID)
+ fit.mode = mode
+ eos.db.commit()
+
+ self.recalc(fit)
+
+ @deprecated
+ def toggleModulesState(self, fitID, base, modules, click):
+ pyfalog.debug("Toggle module state for fit ID: {0}", fitID)
+ changed = False
+ proposedState = es_Module.getProposedState(base, click)
+
+ if proposedState != base.state:
+ changed = True
+ base.state = proposedState
+ for mod in modules:
+ if mod != base:
+ p = es_Module.getProposedState(mod, click, proposedState)
+ mod.state = p
+ if p != mod.state:
+ changed = True
+
+ if changed:
+ eos.db.commit()
+ fit = eos.db.getFit(fitID)
+
+ # As some items may affect state-limiting attributes of the ship, calculate new attributes first
+ self.recalc(fit)
+ # Then, check states of all modules and change where needed. This will recalc if needed
+ self.checkStates(fit, base)
diff --git a/service/jargon/jargon.py b/service/jargon/jargon.py
index bc9a0d525..3530d0859 100644
--- a/service/jargon/jargon.py
+++ b/service/jargon/jargon.py
@@ -17,9 +17,6 @@
# along with pyfa. If not, see .
# =============================================================================
-import config
-import pkg_resources
-
class Jargon(object):
def __init__(self, rawdata: dict):
diff --git a/service/market.py b/service/market.py
index 545b1a66f..3e128cbbc 100644
--- a/service/market.py
+++ b/service/market.py
@@ -17,25 +17,22 @@
# along with pyfa. If not, see .
# ===============================================================================
-import re
-import threading
-from logbook import Logger
import queue
-from itertools import chain
+import threading
+from collections import OrderedDict
# noinspection PyPackageRequirements
import wx
+from logbook import Logger
from sqlalchemy.sql import or_
import config
import eos.db
-from service import conversions
-from service.settings import SettingsProvider
-from service.jargon import JargonLoader
-
from eos.gamedata import Category as types_Category, Group as types_Group, Item as types_Item, MarketGroup as types_MarketGroup, \
MetaGroup as types_MetaGroup, MetaType as types_MetaType
-from collections import OrderedDict
+from service import conversions
+from service.jargon import JargonLoader
+from service.settings import SettingsProvider
pyfalog = Logger(__name__)
diff --git a/service/marketSources/evemarketdata.py b/service/marketSources/evemarketdata.py
index be42cb66d..bd1f8f3af 100644
--- a/service/marketSources/evemarketdata.py
+++ b/service/marketSources/evemarketdata.py
@@ -18,12 +18,12 @@
# =============================================================================
import time
-from logbook import Logger
from xml.dom import minidom
-from service.network import Network
-from service.price import Price, VALIDITY, TIMEOUT, TimeoutError
+from logbook import Logger
+from service.network import Network
+from service.price import Price, TIMEOUT, VALIDITY
pyfalog = Logger(__name__)
diff --git a/service/marketSources/evemarketer.py b/service/marketSources/evemarketer.py
index 948b9ec48..a2728ddfb 100644
--- a/service/marketSources/evemarketer.py
+++ b/service/marketSources/evemarketer.py
@@ -18,12 +18,12 @@
# =============================================================================
import time
-from logbook import Logger
from xml.dom import minidom
-from service.network import Network
-from service.price import Price, VALIDITY, TIMEOUT, TimeoutError
+from logbook import Logger
+from service.network import Network
+from service.price import Price, VALIDITY
pyfalog = Logger(__name__)
diff --git a/service/port.py b/service/port.py
deleted file mode 100644
index 5ad5c4ed3..000000000
--- a/service/port.py
+++ /dev/null
@@ -1,1399 +0,0 @@
-# =============================================================================
-# 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 .
-# =============================================================================
-
-import re
-import os
-import xml.dom
-from logbook import Logger
-import collections
-import json
-import threading
-from bs4 import UnicodeDammit
-
-
-from codecs import open
-
-import xml.parsers.expat
-
-from eos import db
-from eos.db.gamedata.queries import getAttributeInfo
-from service.fit import Fit as svcFit
-
-# noinspection PyPackageRequirements
-import wx
-
-from eos.saveddata.cargo import Cargo
-from eos.saveddata.implant import Implant
-from eos.saveddata.booster import Booster
-from eos.saveddata.drone import Drone
-from eos.saveddata.fighter import Fighter
-from eos.saveddata.module import Module, State, Slot
-from eos.saveddata.ship import Ship
-from eos.saveddata.citadel import Citadel
-from eos.saveddata.fit import Fit, ImplantLocation
-from service.market import Market
-from utils.strfunctions import sequential_rep, replace_ltgt
-from abc import ABCMeta, abstractmethod
-
-from service.esi import Esi
-from collections import OrderedDict
-
-
-class ESIExportException(Exception):
- pass
-
-
-pyfalog = Logger(__name__)
-
-EFT_SLOT_ORDER = [Slot.LOW, Slot.MED, Slot.HIGH, Slot.RIG, Slot.SUBSYSTEM, Slot.SERVICE]
-INV_FLAGS = {
- Slot.LOW: 11,
- Slot.MED: 19,
- Slot.HIGH: 27,
- Slot.RIG: 92,
- Slot.SUBSYSTEM: 125,
- Slot.SERVICE: 164
-}
-
-INV_FLAG_CARGOBAY = 5
-INV_FLAG_DRONEBAY = 87
-INV_FLAG_FIGHTER = 158
-
-# 2017/04/05 NOTE: simple validation, for xml file
-RE_XML_START = r'<\?xml\s+version="1.0"\s*\?>'
-
-# -- 170327 Ignored description --
-RE_LTGT = "&(lt|gt);"
-L_MARK = "<localized hint=""
-# <localized hint="([^"]+)">([^\*]+)\*<\/localized>
-LOCALIZED_PATTERN = re.compile(r'([^\*]+)\*')
-
-
-def _extract_match(t):
- m = LOCALIZED_PATTERN.match(t)
- # hint attribute, text content
- return m.group(1), m.group(2)
-
-
-def _resolve_ship(fitting, sMkt, b_localized):
- # type: (xml.dom.minidom.Element, service.market.Market, bool) -> eos.saveddata.fit.Fit
- """ NOTE: Since it is meaningless unless a correct ship object can be constructed,
- process flow changed
- """
- # ------ Confirm ship
- # Maelstrom
- shipType = fitting.getElementsByTagName("shipType").item(0).getAttribute("value")
- anything = None
- if b_localized:
- # expect an official name, emergency cache
- shipType, anything = _extract_match(shipType)
-
- limit = 2
- ship = None
- while True:
- must_retry = False
- try:
- try:
- ship = Ship(sMkt.getItem(shipType))
- except ValueError:
- ship = Citadel(sMkt.getItem(shipType))
- except Exception as e:
- pyfalog.warning("Caught exception on _resolve_ship")
- pyfalog.error(e)
- limit -= 1
- if limit is 0:
- break
- shipType = anything
- must_retry = True
- if not must_retry:
- break
-
- if ship is None:
- raise Exception("cannot resolve ship type.")
-
- fitobj = Fit(ship=ship)
- # ------ Confirm fit name
- anything = fitting.getAttribute("name")
- # 2017/03/29 NOTE:
- # if fit name contained "<" or ">" then reprace to named html entity by EVE client
- # if re.search(RE_LTGT, anything):
- if "<" in anything or ">" in anything:
- anything = replace_ltgt(anything)
- fitobj.name = anything
-
- return fitobj
-
-
-def _resolve_module(hardware, sMkt, b_localized):
- # type: (xml.dom.minidom.Element, service.market.Market, bool) -> eos.saveddata.module.Module
- moduleName = hardware.getAttribute("type")
- emergency = None
- if b_localized:
- # expect an official name, emergency cache
- moduleName, emergency = _extract_match(moduleName)
-
- item = None
- limit = 2
- while True:
- must_retry = False
- try:
- item = sMkt.getItem(moduleName, eager="group.category")
- except Exception as e:
- pyfalog.warning("Caught exception on _resolve_module")
- pyfalog.error(e)
- limit -= 1
- if limit is 0:
- break
- moduleName = emergency
- must_retry = True
- if not must_retry:
- break
- 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):
- """
- 2017/03/31 NOTE: About change
- 1. want to keep the description recorded in fit
- 2. i think should not write wx.CallAfter in here
- """
- instance = None
- __tag_replace_flag = True
-
- @classmethod
- def getInstance(cls):
- if cls.instance is None:
- cls.instance = Port()
-
- return cls.instance
-
- @classmethod
- def set_tag_replace(cls, b):
- cls.__tag_replace_flag = b
-
- @classmethod
- def is_tag_replace(cls):
- # might there is a person who wants to hold tags.
- # (item link in EVE client etc. When importing again to EVE)
- return cls.__tag_replace_flag
-
- @staticmethod
- def backupFits(path, iportuser):
- pyfalog.debug("Starting backup fits thread.")
-# thread = FitBackupThread(path, callback)
-# thread.start()
- threading.Thread(
- target=PortProcessing.backupFits,
- args=(path, iportuser)
- ).start()
-
- @staticmethod
- def importFitsThreaded(paths, iportuser):
- # type: (tuple, IPortUser) -> None
- """
- :param paths: fits data file path list.
- :param iportuser: IPortUser implemented class.
- :rtype: None
- """
- pyfalog.debug("Starting import fits thread.")
-# thread = FitImportThread(paths, iportuser)
-# thread.start()
- threading.Thread(
- target=PortProcessing.importFitsFromFile,
- args=(paths, iportuser)
- ).start()
-
- @staticmethod
- def importFitFromFiles(paths, iportuser=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
- """
-
- sFit = svcFit.getInstance()
-
- fit_list = []
- try:
- for path in paths:
- if iportuser: # Pulse
- msg = "Processing file:\n%s" % path
- pyfalog.debug(msg)
- PortProcessing.notify(iportuser, IPortUser.PROCESS_IMPORT | IPortUser.ID_UPDATE, msg)
- # wx.CallAfter(callback, 1, msg)
-
- with open(path, "rb") as file_:
- srcString = file_.read()
- dammit = UnicodeDammit(srcString)
- srcString = dammit.unicode_markup
-
- if len(srcString) == 0: # ignore blank files
- pyfalog.debug("File is blank.")
- continue
-
- try:
- _, fitsImport = Port.importAuto(srcString, path, iportuser=iportuser)
- fit_list += fitsImport
- except xml.parsers.expat.ExpatError:
- pyfalog.warning("Malformed XML in:\n{0}", path)
- return False, "Malformed XML in %s" % path
-
- # IDs = [] # NOTE: what use for IDs?
- numFits = len(fit_list)
- for idx, fit in enumerate(fit_list):
- # Set some more fit attributes and save
- fit.character = sFit.character
- fit.damagePattern = sFit.pattern
- fit.targetResists = sFit.targetResists
- if len(fit.implants) > 0:
- fit.implantLocation = ImplantLocation.FIT
- else:
- useCharImplants = sFit.serviceFittingOptions["useCharacterImplantsByDefault"]
- fit.implantLocation = ImplantLocation.CHARACTER if useCharImplants else ImplantLocation.FIT
- db.save(fit)
- # IDs.append(fit.ID)
- if iportuser: # Pulse
- pyfalog.debug("Processing complete, saving fits to database: {0}/{1}", idx + 1, numFits)
- PortProcessing.notify(
- iportuser, IPortUser.PROCESS_IMPORT | IPortUser.ID_UPDATE,
- "Processing complete, saving fits to database\n(%d/%d) %s" % (idx + 1, numFits, fit.ship.name)
- )
-
- except UserCancelException:
- return False, "Processing has been canceled.\n"
- except Exception as e:
- pyfalog.critical("Unknown exception processing: {0}", path)
- pyfalog.critical(e)
- # TypeError: not all arguments converted during string formatting
-# return False, "Unknown Error while processing {0}" % path
- return False, "Unknown error while processing %s\n\n Error: %s" % (path, e.message)
-
- return True, fit_list
-
- @staticmethod
- def importFitFromBuffer(bufferStr, activeFit=None):
- # type: (basestring, object) -> object
- # TODO: catch the exception?
- # activeFit is reserved?, bufferStr is unicode? (assume only clipboard string?
- 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
- if len(fit.implants) > 0:
- fit.implantLocation = ImplantLocation.FIT
- else:
- useCharImplants = sFit.serviceFittingOptions["useCharacterImplantsByDefault"]
- fit.implantLocation = ImplantLocation.CHARACTER if useCharImplants else ImplantLocation.FIT
- db.save(fit)
- return fits
-
- """Service which houses all import/export format functions"""
-
- @classmethod
- def exportESI(cls, ofit, callback=None):
- # A few notes:
- # max fit name length is 50 characters
- # Most keys are created simply because they are required, but bogus data is okay
-
- nested_dict = lambda: collections.defaultdict(nested_dict)
- fit = nested_dict()
- sFit = svcFit.getInstance()
-
- # max length is 50 characters
- name = ofit.name[:47] + '...' if len(ofit.name) > 50 else ofit.name
- fit['name'] = name
- fit['ship_type_id'] = ofit.ship.item.ID
-
- # 2017/03/29 NOTE: "<" or "<" is Ignored
- # fit['description'] = "" % ofit.ID
- fit['description'] = ofit.notes[:397] + '...' if len(ofit.notes) > 400 else ofit.notes if ofit.notes is not None else ""
- fit['items'] = []
-
- slotNum = {}
- charges = {}
- for module in ofit.modules:
- if module.isEmpty:
- continue
-
- item = nested_dict()
- slot = module.slot
-
- if slot == Slot.SUBSYSTEM:
- # Order of subsystem matters based on this attr. See GH issue #130
- slot = int(module.getModifiedItemAttr("subSystemSlot"))
- item['flag'] = slot
- else:
- if slot not in slotNum:
- slotNum[slot] = INV_FLAGS[slot]
-
- item['flag'] = slotNum[slot]
- slotNum[slot] += 1
-
- item['quantity'] = 1
- item['type_id'] = module.item.ID
- fit['items'].append(item)
-
- if module.charge and sFit.serviceFittingOptions["exportCharges"]:
- 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
-
- for cargo in ofit.cargo:
- item = nested_dict()
- item['flag'] = INV_FLAG_CARGOBAY
- item['quantity'] = cargo.amount
- item['type_id'] = cargo.item.ID
- fit['items'].append(item)
-
- for chargeID, amount in list(charges.items()):
- item = nested_dict()
- item['flag'] = INV_FLAG_CARGOBAY
- item['quantity'] = amount
- item['type_id'] = chargeID
- fit['items'].append(item)
-
- for drone in ofit.drones:
- item = nested_dict()
- item['flag'] = INV_FLAG_DRONEBAY
- item['quantity'] = drone.amount
- item['type_id'] = drone.item.ID
- fit['items'].append(item)
-
- for fighter in ofit.fighters:
- item = nested_dict()
- item['flag'] = INV_FLAG_FIGHTER
- item['quantity'] = fighter.amountActive
- item['type_id'] = fighter.item.ID
- fit['items'].append(item)
-
- if len(fit['items']) == 0:
- raise ESIExportException("Cannot export fitting: module list cannot be empty.")
-
- return json.dumps(fit)
-
- @classmethod
- def importAuto(cls, string, path=None, activeFit=None, iportuser=None):
- # type: (basestring, basestring, object, IPortUser, basestring) -> object
- # 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 XML-style start of tag encountered, detect as XML
- if re.search(RE_XML_START, firstLine):
- return "XML", cls.importXml(string, iportuser)
-
- # If JSON-style start, parse as CREST/JSON
- if firstLine[0] == '{':
- return "JSON", (cls.importESI(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 path is not None:
- filename = os.path.split(path)[1]
- shipName = filename.rsplit('.')[0]
- return "EFT Config", cls.importEftCfg(shipName, string, iportuser)
-
- # 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 importESI(str_):
-
- sMkt = Market.getInstance()
- fitobj = Fit()
- refobj = json.loads(str_)
- items = refobj['items']
- # "<" and ">" is replace to "<", ">" by EVE client
- fitobj.name = refobj['name']
- # 2017/03/29: read description
- fitobj.notes = refobj['description']
-
- try:
- ship = refobj['ship_type_id']
- try:
- fitobj.ship = Ship(sMkt.getItem(ship))
- except ValueError:
- fitobj.ship = Citadel(sMkt.getItem(ship))
- except:
- pyfalog.warning("Caught exception in importESI")
- return None
-
- items.sort(key=lambda k: k['flag'])
-
- moduleList = []
- for module in items:
- try:
- item = sMkt.getItem(module['type_id'], eager="group.category")
- if not item.published:
- continue
- if module['flag'] == INV_FLAG_DRONEBAY:
- d = Drone(item)
- d.amount = module['quantity']
- fitobj.drones.append(d)
- elif module['flag'] == INV_FLAG_CARGOBAY:
- c = Cargo(item)
- c.amount = module['quantity']
- fitobj.cargo.append(c)
- elif module['flag'] == INV_FLAG_FIGHTER:
- fighter = Fighter(item)
- fitobj.fighters.append(fighter)
- else:
- try:
- m = Module(item)
- # When item can't be added to any slot (unknown item or just charge), ignore it
- except ValueError:
- pyfalog.debug("Item can't be added to any slot (unknown item or just charge)")
- continue
- # Add subsystems before modules to make sure T3 cruisers have subsystems installed
- if item.category.name == "Subsystem":
- if m.fits(fitobj):
- fitobj.modules.append(m)
- else:
- if m.isValidState(State.ACTIVE):
- m.state = State.ACTIVE
-
- moduleList.append(m)
-
- except:
- pyfalog.warning("Could not process module.")
- continue
-
- # Recalc to get slot numbers correct for T3 cruisers
- svcFit.getInstance().recalc(fitobj)
-
- for module in moduleList:
- if module.fits(fitobj):
- fitobj.modules.append(module)
-
- 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):
- sMkt = 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)
- try:
- fit.ship = Ship(ship)
- except ValueError:
- fit.ship = Citadel(ship)
- fit.name = fitName
- except:
- pyfalog.warning("Exception caught in importEft")
- return
-
- # maintain map of drones and their quantities
- droneMap = {}
- cargoMap = {}
- moduleList = []
- 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)
- pyfalog.warning("no data can be found (old names)")
- continue
-
- if not item.published:
- continue
-
- if item.category.name == "Drone":
- extraAmount = int(extraAmount) if extraAmount is not None else 1
- 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
- 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 modName not in cargoMap:
- cargoMap[modName] = 0
- cargoMap[modName] += extraAmount
- elif item.category.name == "Implant":
- if "implantness" in item.attributes:
- fit.implants.append(Implant(item))
- elif "boosterness" in item.attributes:
- fit.boosters.append(Booster(item))
- else:
- pyfalog.error("Failed to import implant: {0}", line)
- # elif item.category.name == "Subsystem":
- # try:
- # subsystem = Module(item)
- # except ValueError:
- # continue
- #
- # if subsystem.fits(fit):
- # fit.modules.append(subsystem)
- else:
- try:
- m = Module(item)
- except ValueError:
- continue
- # Add subsystems before modules to make sure T3 cruisers have subsystems installed
- if item.category.name == "Subsystem":
- if m.fits(fit):
- fit.modules.append(m)
- else:
- 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
-
- moduleList.append(m)
-
- # Recalc to get slot numbers correct for T3 cruisers
- svcFit.getInstance().recalc(fit)
-
- for m in moduleList:
- if m.fits(fit):
- m.owner = fit
- if not m.isValidState(m.state):
- pyfalog.warning("Error: Module {0} cannot have state {1}", m, m.state)
-
- 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, iportuser=None):
- """Handle import from EFT config store file"""
-
- # Check if we have such ship in database, bail if we don't
- sMkt = Market.getInstance()
- try:
- sMkt.getItem(shipname)
- except:
- return [] # empty list is expected
-
- 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
- fitobj = Fit()
- # Strip square brackets and pull out a fit name
- fitobj.name = fitLines[0][1:-1]
- # Assign ship to fitting
- try:
- fitobj.ship = Ship(sMkt.getItem(shipname))
- except ValueError:
- fitobj.ship = Citadel(sMkt.getItem(shipname))
-
- moduleList = []
- 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)
- # 2017/03/27 NOTE: store description from EFT
- description = re.match("Description=(.+)", 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:
- pyfalog.warning("Cannot get item.")
- continue
- if droneItem.category.name == "Drone":
- # Add drone to the fitting
- d = Drone(droneItem)
- d.amount = droneAmount
- if entityState == "Active":
- d.amountActive = droneAmount
- elif entityState == "Inactive":
- d.amountActive = 0
- fitobj.drones.append(d)
- elif droneItem.category.name == "Fighter": # EFT saves fighter as drones
- ft = Fighter(droneItem)
- ft.amount = int(droneAmount) if ft.amount <= ft.fighterSquadronMaxSize else ft.fighterSquadronMaxSize
- fitobj.fighters.append(ft)
- else:
- continue
- 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:
- pyfalog.warning("Cannot get item.")
- 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
- fitobj.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:
- pyfalog.warning("Cannot get item.")
- 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
- fitobj.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:
- pyfalog.warning("Cannot get item.")
- continue
- # Add Cargo to the fitting
- c = Cargo(item)
- c.amount = cargoAmount
- fitobj.cargo.append(c)
- # 2017/03/27 NOTE: store description from EFT
- elif description:
- fitobj.notes = description.group(1).replace("|", "\n")
- 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:
- pyfalog.warning("Cannot get item.")
- continue
-
- # Create module
- m = Module(modItem)
-
- # Add subsystems before modules to make sure T3 cruisers have subsystems installed
- if modItem.category.name == "Subsystem":
- if m.fits(fitobj):
- fitobj.modules.append(m)
- else:
- m.owner = fitobj
- # Activate mod if it is activable
- 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:
- pyfalog.warning("Cannot get item.")
- pass
- # Append module to fit
- moduleList.append(m)
-
- # Recalc to get slot numbers correct for T3 cruisers
- svcFit.getInstance().recalc(fitobj)
-
- for module in moduleList:
- if module.fits(fitobj):
- fitobj.modules.append(module)
-
- # Append fit to list of fits
- fits.append(fitobj)
-
- if iportuser: # NOTE: Send current processing status
- PortProcessing.notify(
- iportuser, IPortUser.PROCESS_IMPORT | IPortUser.ID_UPDATE,
- "%s:\n%s" % (fitobj.ship.name, fitobj.name)
- )
-
- # Skip fit silently if we get an exception
- except Exception as e:
- pyfalog.error("Caught exception on fit.")
- pyfalog.error(e)
- pass
-
- return fits
-
- @staticmethod
- def importXml(text, iportuser=None):
- # type: (basestring, IPortUser, basestring) -> list[eos.saveddata.fit.Fit]
- sMkt = Market.getInstance()
- doc = xml.dom.minidom.parseString(text)
- # NOTE:
- # When L_MARK is included at this point,
- # Decided to be localized data
- b_localized = L_MARK in text
- fittings = doc.getElementsByTagName("fittings").item(0)
- fittings = fittings.getElementsByTagName("fitting")
- fit_list = []
- failed = 0
-
- for fitting in fittings:
- try:
- fitobj = _resolve_ship(fitting, sMkt, b_localized)
- except:
- failed += 1
- continue
-
- # -- 170327 Ignored description --
- # read description from exported xml. (EVE client, EFT)
- description = fitting.getElementsByTagName("description").item(0).getAttribute("value")
- if description is None:
- description = ""
- elif len(description):
- # convert
to "\n" and remove html tags.
- if Port.is_tag_replace():
- description = replace_ltgt(
- sequential_rep(description, r"<(br|BR)>", "\n", r"<[^<>]+>", "")
- )
- fitobj.notes = description
-
- hardwares = fitting.getElementsByTagName("hardware")
- moduleList = []
- for hardware in hardwares:
- try:
- item = _resolve_module(hardware, sMkt, b_localized)
- if not item or not item.published:
- continue
-
- if item.category.name == "Drone":
- d = Drone(item)
- d.amount = int(hardware.getAttribute("qty"))
- fitobj.drones.append(d)
- elif item.category.name == "Fighter":
- ft = Fighter(item)
- ft.amount = int(hardware.getAttribute("qty")) if ft.amount <= ft.fighterSquadronMaxSize else ft.fighterSquadronMaxSize
- fitobj.fighters.append(ft)
- 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"))
- fitobj.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:
- pyfalog.warning("item can't be added to any slot (unknown item or just charge), ignore it")
- continue
- # Add subsystems before modules to make sure T3 cruisers have subsystems installed
- if item.category.name == "Subsystem":
- if m.fits(fitobj):
- m.owner = fitobj
- fitobj.modules.append(m)
- else:
- if m.isValidState(State.ACTIVE):
- m.state = State.ACTIVE
-
- moduleList.append(m)
-
- except KeyboardInterrupt:
- pyfalog.warning("Keyboard Interrupt")
- continue
-
- # Recalc to get slot numbers correct for T3 cruisers
- svcFit.getInstance().recalc(fitobj)
-
- for module in moduleList:
- if module.fits(fitobj):
- module.owner = fitobj
- fitobj.modules.append(module)
-
- fit_list.append(fitobj)
- if iportuser: # NOTE: Send current processing status
- PortProcessing.notify(
- iportuser, IPortUser.PROCESS_IMPORT | IPortUser.ID_UPDATE,
- "Processing %s\n%s" % (fitobj.ship.name, fitobj.name)
- )
-
- return fit_list
-
-
- @classmethod
- def exportEft(cls, fit, mutations=False, implants=False):
- # EFT formatted export is split in several sections, each section is
- # separated from another using 2 blank lines. Sections might have several
- # sub-sections, which are separated by 1 blank line
- sections = []
-
- header = '[{}, {}]'.format(fit.ship.item.name, fit.name)
-
- # Section 1: modules, rigs, subsystems, services
- def formatAttrVal(val):
- if int(val) == val:
- return int(val)
- return val
-
- offineSuffix = ' /OFFLINE'
- modsBySlotType = {}
- sFit = svcFit.getInstance()
- for module in fit.modules:
- slot = module.slot
- slotTypeMods = modsBySlotType.setdefault(slot, [])
- if module.item:
- mutatedMod = bool(module.mutators)
- # if module was mutated, use base item name for export
- if mutatedMod:
- modName = module.baseItem.name
- else:
- modName = module.item.name
- modOfflineSuffix = offineSuffix if module.state == State.OFFLINE else ''
- if module.charge and sFit.serviceFittingOptions['exportCharges']:
- slotTypeMods.append('{}, {}{}'.format(modName, module.charge.name, modOfflineSuffix))
- else:
- slotTypeMods.append('{}{}'.format(modName, modOfflineSuffix))
- if mutatedMod and mutations:
- mutationGrade = module.mutaplasmid.item.name.split(' ', 1)[0].lower()
- mutatedAttrs = {}
- for attrID, mutator in module.mutators.items():
- attrName = getAttributeInfo(attrID).name
- mutatedAttrs[attrName] = mutator.value
- customAttrsLine = ', '.join('{} {}'.format(a, formatAttrVal(mutatedAttrs[a])) for a in sorted(mutatedAttrs))
- slotTypeMods.append(' {}: {}'.format(mutationGrade, customAttrsLine))
- else:
- slotTypeMods.append('[Empty {} slot]'.format(Slot.getName(slot).capitalize() if slot is not None else ''))
- modSection = []
- for slotType in EFT_SLOT_ORDER:
- rackLines = []
- data = modsBySlotType.get(slotType, ())
- for line in data:
- rackLines.append(line)
- if rackLines:
- modSection.append('\n'.join(rackLines))
- if modSection:
- sections.append('\n\n'.join(modSection))
-
- # Section 2: drones, fighters
- minionSection = []
- droneLines = []
- for drone in sorted(fit.drones, key=lambda d: d.item.name):
- droneLines.append('{} x{}'.format(drone.item.name, drone.amount))
- if droneLines:
- minionSection.append('\n'.join(droneLines))
- fighterLines = []
- for fighter in sorted(fit.fighters, key=lambda f: f.item.name):
- fighterLines.append('{} x{}'.format(fighter.item.name, fighter.amountActive))
- if fighterLines:
- minionSection.append('\n'.join(fighterLines))
- if minionSection:
- sections.append('\n\n'.join(minionSection))
-
- # Section 3: implants, boosters
- if implants:
- charSection = []
- implantLines = []
- for implant in fit.implants:
- implantLines.append(implant.item.name)
- if implantLines:
- charSection.append('\n'.join(implantLines))
- boosterLines = []
- for booster in fit.boosters:
- boosterLines.append(booster.item.name)
- if boosterLines:
- charSection.append('\n'.join(boosterLines))
- if charSection:
- sections.append('\n\n'.join(charSection))
-
- # Section 4: cargo
- cargoLines = []
- for cargo in sorted(fit.cargo, key=lambda c: (c.item.group.category.name, c.item.group.name, c.item.name)):
- cargoLines.append('{} x{}'.format(cargo.item.name, cargo.amount))
- if cargoLines:
- sections.append('\n'.join(cargoLines))
-
- return '{}\n\n{}'.format(header, '\n\n\n'.join(sections))
-
- @classmethod
- def exportEftImps(cls, fit):
- return cls.exportEft(fit, implants=True)
-
- @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
- def exportXml(iportuser=None, *fits):
- doc = xml.dom.minidom.Document()
- fittings = doc.createElement("fittings")
- # fit count
- fit_count = len(fits)
- fittings.setAttribute("count", "%s" % fit_count)
- doc.appendChild(fittings)
- sFit = svcFit.getInstance()
-
- for i, fit in enumerate(fits):
- try:
- fitting = doc.createElement("fitting")
- fitting.setAttribute("name", fit.name)
- fittings.appendChild(fitting)
- description = doc.createElement("description")
- # -- 170327 Ignored description --
- try:
- notes = fit.notes # unicode
-
- if notes:
- notes = notes[:397] + '...' if len(notes) > 400 else notes
-
- description.setAttribute(
- "value", re.sub("(\r|\n|\r\n)+", "
", notes) if notes is not None else ""
- )
- except Exception as e:
- pyfalog.warning("read description is failed, msg=%s\n" % e.args)
-
- fitting.appendChild(description)
- shipType = doc.createElement("shipType")
- shipType.setAttribute("value", fit.ship.name)
- fitting.appendChild(shipType)
-
- charges = {}
- slotNum = {}
- for module in fit.modules:
- if module.isEmpty:
- continue
-
- slot = module.slot
-
- if slot == Slot.SUBSYSTEM:
- # Order of subsystem matters based on this attr. See GH issue #130
- slotId = module.getModifiedItemAttr("subSystemSlot") - 125
- else:
- if slot not 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 and sFit.serviceFittingOptions["exportCharges"]:
- 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
-
- 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 fighter in fit.fighters:
- hardware = doc.createElement("hardware")
- hardware.setAttribute("qty", "%d" % fighter.amountActive)
- hardware.setAttribute("slot", "fighter bay")
- hardware.setAttribute("type", fighter.item.name)
- fitting.appendChild(hardware)
-
- for cargo in fit.cargo:
- if cargo.item.name not in charges:
- charges[cargo.item.name] = 0
- charges[cargo.item.name] += cargo.amount
-
- for name, qty in list(charges.items()):
- hardware = doc.createElement("hardware")
- hardware.setAttribute("qty", "%d" % qty)
- hardware.setAttribute("slot", "cargo")
- hardware.setAttribute("type", name)
- fitting.appendChild(hardware)
- except Exception as e:
- # print("Failed on fitID: %d" % fit.ID)
- pyfalog.error("Failed on fitID: %d, message: %s" % e.message)
- continue
- finally:
- if iportuser:
- PortProcessing.notify(
- iportuser, IPortUser.PROCESS_EXPORT | IPortUser.ID_UPDATE,
- (i, "convert to xml (%s/%s) %s" % (i + 1, fit_count, fit.ship.name))
- )
-# wx.CallAfter(callback, i, "(%s/%s) %s" % (i, fit_count, fit.ship.name))
-
- return doc.toprettyxml()
-
- @staticmethod
- def exportMultiBuy(fit):
- export = "%s\n" % fit.ship.item.name
- stuff = {}
- sFit = svcFit.getInstance()
- for module in fit.modules:
- slot = module.slot
- if slot not in stuff:
- stuff[slot] = []
- 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)
-
- for slotType in EFT_SLOT_ORDER:
- data = stuff.get(slotType)
- if data is not None:
- # export += "\n"
- for curr in data:
- export += curr
-
- if len(fit.drones) > 0:
- 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 len(fit.implants) > 0:
- for implant in fit.implants:
- export += "%s\n" % implant.item.name
-
- if len(fit.boosters) > 0:
- for booster in fit.boosters:
- export += "%s\n" % booster.item.name
-
- if len(fit.fighters) > 0:
- for fighter in fit.fighters:
- export += "%s x%s\n" % (fighter.item.name, fighter.amountActive)
-
- if export[-1] == "\n":
- export = export[:-1]
-
- return export
-
-
-class PortProcessing(object):
- """Port Processing class """
- @staticmethod
- def backupFits(path, iportuser):
- success = True
- try:
- iportuser.on_port_process_start()
- backedUpFits = Port.exportXml(iportuser, *svcFit.getInstance().getAllFits())
- backupFile = open(path, "w", encoding="utf-8")
- backupFile.write(backedUpFits)
- backupFile.close()
- except UserCancelException:
- success = False
- # Send done signal to GUI
-# wx.CallAfter(callback, -1, "Done.")
- flag = IPortUser.ID_ERROR if not success else IPortUser.ID_DONE
- iportuser.on_port_processing(IPortUser.PROCESS_EXPORT | flag,
- "User canceled or some error occurrence." if not success else "Done.")
-
- @staticmethod
- def importFitsFromFile(paths, iportuser):
- iportuser.on_port_process_start()
- success, result = Port.importFitFromFiles(paths, iportuser)
- flag = IPortUser.ID_ERROR if not success else IPortUser.ID_DONE
- iportuser.on_port_processing(IPortUser.PROCESS_IMPORT | flag, result)
-
- @staticmethod
- def notify(iportuser, flag, data):
- if not iportuser.on_port_processing(flag, data):
- raise UserCancelException
diff --git a/service/port/__init__.py b/service/port/__init__.py
new file mode 100644
index 000000000..2e884d214
--- /dev/null
+++ b/service/port/__init__.py
@@ -0,0 +1,2 @@
+from .efs import EfsPort
+from .port import Port, IPortUser
diff --git a/service/port/dna.py b/service/port/dna.py
new file mode 100644
index 000000000..bd2645ef8
--- /dev/null
+++ b/service/port/dna.py
@@ -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 .
+# =============================================================================
+
+
+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 + "::"
diff --git a/service/efsPort.py b/service/port/efs.py
similarity index 98%
rename from service/efsPort.py
rename to service/port/efs.py
index d1dd394d9..4420adc90 100755
--- a/service/efsPort.py
+++ b/service/port/efs.py
@@ -1,23 +1,20 @@
-import inspect
-import os
-import platform
-import re
-import sys
-import traceback
import json
-import eos.db
-
+import json
from math import log
+
+from logbook import Logger
+
+import eos.db
from config import version as pyfaVersion
+from eos.db import gamedata_session, getAttributeInfo, getCategory, getGroup
+from eos.effectHandlerHelpers import HandledList
+from eos.enum import Enum
+from eos.gamedata import Attribute, Effect, Group, Item, ItemEffect
+from eos.saveddata.drone import Drone
+from eos.saveddata.module import Hardpoint, Module, Slot, State
from service.fit import Fit
from service.market import Market
-from eos.enum import Enum
-from eos.saveddata.module import Hardpoint, Slot, Module, State
-from eos.saveddata.drone import Drone
-from eos.effectHandlerHelpers import HandledList
-from eos.db import gamedata_session, getItemsByCategory, getCategory, getAttributeInfo, getGroup
-from eos.gamedata import Category, Group, Item, Traits, Attribute, Effect, ItemEffect
-from logbook import Logger
+
pyfalog = Logger(__name__)
@@ -29,7 +26,7 @@ class RigSize(Enum):
CAPITAL = 4
-class EfsPort():
+class EfsPort:
wepTestSet = {}
version = 0.01
@@ -500,7 +497,7 @@ class EfsPort():
# Since the effect modules are fairly opaque a mock test fit is used to test the impact of traits.
# standin class used to prevent . notation causing issues when used as an arg
- class standin():
+ class standin:
pass
tf = standin()
tf.modules = HandledList(turrets + launchers)
diff --git a/service/port/eft.py b/service/port/eft.py
new file mode 100644
index 000000000..fc8576d01
--- /dev/null
+++ b/service/port/eft.py
@@ -0,0 +1,878 @@
+# =============================================================================
+# 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 .
+# =============================================================================
+
+
+import re
+
+from logbook import Logger
+
+from eos.db.gamedata.queries import getAttributeInfo, getDynamicItem
+from eos.saveddata.cargo import Cargo
+from eos.saveddata.citadel import Citadel
+from eos.saveddata.booster import Booster
+from eos.saveddata.drone import Drone
+from eos.saveddata.fighter import Fighter
+from eos.saveddata.implant import Implant
+from eos.saveddata.module import Module, State, Slot
+from eos.saveddata.ship import Ship
+from eos.saveddata.fit import Fit
+from gui.utils.numberFormatter import roundToPrec
+from service.fit import Fit as svcFit
+from service.market import Market
+from service.port.shared import IPortUser, processing_notify
+from enum import Enum
+
+
+pyfalog = Logger(__name__)
+
+
+class Options(Enum):
+ IMPLANTS = 1
+ MUTATIONS = 2
+
+
+MODULE_CATS = ('Module', 'Subsystem', 'Structure Module')
+SLOT_ORDER = (Slot.LOW, Slot.MED, Slot.HIGH, Slot.RIG, Slot.SUBSYSTEM, Slot.SERVICE)
+OFFLINE_SUFFIX = '/OFFLINE'
+
+EFT_OPTIONS = {
+ Options.IMPLANTS.value: {
+ "name": "Implants",
+ "description": "Exports implants"
+ },
+ Options.MUTATIONS.value: {
+ "name": "Mutated Attributes",
+ "description": "Exports Abyssal stats"
+ }
+}
+
+
+def exportEft(fit, options):
+ # EFT formatted export is split in several sections, each section is
+ # separated from another using 2 blank lines. Sections might have several
+ # sub-sections, which are separated by 1 blank line
+ sections = []
+
+ header = '[{}, {}]'.format(fit.ship.item.name, fit.name)
+
+ # Section 1: modules, rigs, subsystems, services
+ modsBySlotType = {}
+ sFit = svcFit.getInstance()
+ for module in fit.modules:
+ modsBySlotType.setdefault(module.slot, []).append(module)
+ modSection = []
+
+ mutants = {} # Format: {reference number: module}
+ mutantReference = 1
+ for slotType in SLOT_ORDER:
+ rackLines = []
+ modules = modsBySlotType.get(slotType, ())
+ for module in modules:
+ if module.item:
+ mutated = bool(module.mutators)
+ # if module was mutated, use base item name for export
+ if mutated:
+ modName = module.baseItem.name
+ else:
+ modName = module.item.name
+ if mutated and options & Options.MUTATIONS.value:
+ mutants[mutantReference] = module
+ mutationSuffix = ' [{}]'.format(mutantReference)
+ mutantReference += 1
+ else:
+ mutationSuffix = ''
+ modOfflineSuffix = ' {}'.format(OFFLINE_SUFFIX) if module.state == State.OFFLINE else ''
+ if module.charge and sFit.serviceFittingOptions['exportCharges']:
+ rackLines.append('{}, {}{}{}'.format(
+ modName, module.charge.name, modOfflineSuffix, mutationSuffix))
+ else:
+ rackLines.append('{}{}{}'.format(modName, modOfflineSuffix, mutationSuffix))
+ else:
+ rackLines.append('[Empty {} slot]'.format(
+ Slot.getName(slotType).capitalize() if slotType is not None else ''))
+ if rackLines:
+ modSection.append('\n'.join(rackLines))
+ if modSection:
+ sections.append('\n\n'.join(modSection))
+
+ # Section 2: drones, fighters
+ minionSection = []
+ droneLines = []
+ for drone in sorted(fit.drones, key=lambda d: d.item.name):
+ droneLines.append('{} x{}'.format(drone.item.name, drone.amount))
+ if droneLines:
+ minionSection.append('\n'.join(droneLines))
+ fighterLines = []
+ for fighter in sorted(fit.fighters, key=lambda f: f.item.name):
+ fighterLines.append('{} x{}'.format(fighter.item.name, fighter.amountActive))
+ if fighterLines:
+ minionSection.append('\n'.join(fighterLines))
+ if minionSection:
+ sections.append('\n\n'.join(minionSection))
+
+ # Section 3: implants, boosters
+ if options & Options.IMPLANTS.value:
+ charSection = []
+ implantLines = []
+ for implant in fit.implants:
+ implantLines.append(implant.item.name)
+ if implantLines:
+ charSection.append('\n'.join(implantLines))
+ boosterLines = []
+ for booster in fit.boosters:
+ boosterLines.append(booster.item.name)
+ if boosterLines:
+ charSection.append('\n'.join(boosterLines))
+ if charSection:
+ sections.append('\n\n'.join(charSection))
+
+ # Section 4: cargo
+ cargoLines = []
+ for cargo in sorted(
+ fit.cargo,
+ key=lambda c: (c.item.group.category.name, c.item.group.name, c.item.name)
+ ):
+ cargoLines.append('{} x{}'.format(cargo.item.name, cargo.amount))
+ if cargoLines:
+ sections.append('\n'.join(cargoLines))
+
+ # Section 5: mutated modules' details
+ mutationLines = []
+ if mutants and options & Options.MUTATIONS.value:
+ for mutantReference in sorted(mutants):
+ mutant = mutants[mutantReference]
+ mutatedAttrs = {}
+ for attrID, mutator in mutant.mutators.items():
+ attrName = getAttributeInfo(attrID).name
+ mutatedAttrs[attrName] = mutator.value
+ mutationLines.append('[{}] {}'.format(mutantReference, mutant.baseItem.name))
+ mutationLines.append(' {}'.format(mutant.mutaplasmid.item.name))
+ # Round to 7th significant number to avoid exporting float errors
+ customAttrsLine = ', '.join(
+ '{} {}'.format(a, roundToPrec(mutatedAttrs[a], 7))
+ for a in sorted(mutatedAttrs))
+ mutationLines.append(' {}'.format(customAttrsLine))
+ if mutationLines:
+ sections.append('\n'.join(mutationLines))
+
+ return '{}\n\n{}'.format(header, '\n\n\n'.join(sections))
+
+
+def importEft(eftString):
+ lines = _importPrepareString(eftString)
+ try:
+ fit = _importCreateFit(lines)
+ except EftImportError:
+ return
+
+ aFit = AbstractFit()
+ aFit.mutations = _importGetMutationData(lines)
+
+ nameChars = '[^,/\[\]]' # Characters which are allowed to be used in name
+ stubPattern = '^\[.+?\]$'
+ modulePattern = '^(?P{0}+?)(,\s*(?P{0}+?))?(?P\s*{1})?(\s*\[(?P\d+?)\])?$'.format(nameChars, OFFLINE_SUFFIX)
+ droneCargoPattern = '^(?P{}+?) x(?P\d+?)$'.format(nameChars)
+
+ sections = []
+ for section in _importSectionIter(lines):
+ for line in section.lines:
+ # Stub line
+ if re.match(stubPattern, line):
+ section.itemSpecs.append(None)
+ continue
+ # Items with quantity specifier
+ m = re.match(droneCargoPattern, line)
+ if m:
+ try:
+ itemSpec = MultiItemSpec(m.group('typeName'))
+ # Items which cannot be fetched are considered as stubs
+ except EftImportError:
+ section.itemSpecs.append(None)
+ else:
+ itemSpec.amount = int(m.group('amount'))
+ section.itemSpecs.append(itemSpec)
+ continue
+ # All other items
+ m = re.match(modulePattern, line)
+ if m:
+ try:
+ itemSpec = RegularItemSpec(m.group('typeName'), chargeName=m.group('chargeName'))
+ # Items which cannot be fetched are considered as stubs
+ except EftImportError:
+ section.itemSpecs.append(None)
+ else:
+ if m.group('offline'):
+ itemSpec.offline = True
+ if m.group('mutation'):
+ itemSpec.mutationIdx = int(m.group('mutation'))
+ section.itemSpecs.append(itemSpec)
+ continue
+ _clearTail(section.itemSpecs)
+ sections.append(section)
+
+ hasDroneBay = any(s.isDroneBay for s in sections)
+ hasFighterBay = any(s.isFighterBay for s in sections)
+ for section in sections:
+ if section.isModuleRack:
+ aFit.addModules(section.itemSpecs)
+ elif section.isImplantRack:
+ for itemSpec in section.itemSpecs:
+ aFit.addImplant(itemSpec)
+ elif section.isDroneBay:
+ for itemSpec in section.itemSpecs:
+ aFit.addDrone(itemSpec)
+ elif section.isFighterBay:
+ for itemSpec in section.itemSpecs:
+ aFit.addFighter(itemSpec)
+ elif section.isCargoHold:
+ for itemSpec in section.itemSpecs:
+ aFit.addCargo(itemSpec)
+ # Mix between different kinds of item specs (can happen when some
+ # blank lines are removed)
+ else:
+ for itemSpec in section.itemSpecs:
+ if itemSpec is None:
+ continue
+ if itemSpec.isModule:
+ aFit.addModule(itemSpec)
+ elif itemSpec.isImplant:
+ aFit.addImplant(itemSpec)
+ elif itemSpec.isDrone and not hasDroneBay:
+ aFit.addDrone(itemSpec)
+ elif itemSpec.isFighter and not hasFighterBay:
+ aFit.addFighter(itemSpec)
+ elif itemSpec.isCargo:
+ aFit.addCargo(itemSpec)
+
+ # Subsystems first because they modify slot amount
+ for m in aFit.subsystems:
+ if m is None:
+ dummy = Module.buildEmpty(aFit.getSlotByContainer(aFit.subsystems))
+ dummy.owner = fit
+ fit.modules.appendIgnoreEmpty(dummy)
+ elif m.fits(fit):
+ m.owner = fit
+ fit.modules.appendIgnoreEmpty(m)
+ svcFit.getInstance().recalc(fit)
+
+ # Other stuff
+ for modRack in (
+ aFit.rigs,
+ aFit.services,
+ aFit.modulesHigh,
+ aFit.modulesMed,
+ aFit.modulesLow,
+ ):
+ for m in modRack:
+ if m is None:
+ dummy = Module.buildEmpty(aFit.getSlotByContainer(modRack))
+ dummy.owner = fit
+ fit.modules.appendIgnoreEmpty(dummy)
+ elif m.fits(fit):
+ m.owner = fit
+ if not m.isValidState(m.state):
+ pyfalog.warning('service.port.eft.importEft: module {} cannot have state {}', m, m.state)
+ fit.modules.appendIgnoreEmpty(m)
+ for implant in aFit.implants:
+ fit.implants.append(implant)
+ for booster in aFit.boosters:
+ fit.boosters.append(booster)
+ for drone in aFit.drones.values():
+ fit.drones.append(drone)
+ for fighter in aFit.fighters:
+ fit.fighters.append(fighter)
+ for cargo in aFit.cargo.values():
+ fit.cargo.append(cargo)
+
+ return fit
+
+
+def importEftCfg(shipname, contents, iportuser):
+ """Handle import from EFT config store file"""
+
+ # Check if we have such ship in database, bail if we don't
+ sMkt = Market.getInstance()
+ try:
+ sMkt.getItem(shipname)
+ except:
+ return [] # empty list is expected
+
+ 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
+ fitobj = Fit()
+ # Strip square brackets and pull out a fit name
+ fitobj.name = fitLines[0][1:-1]
+ # Assign ship to fitting
+ try:
+ fitobj.ship = Ship(sMkt.getItem(shipname))
+ except ValueError:
+ fitobj.ship = Citadel(sMkt.getItem(shipname))
+
+ moduleList = []
+ 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)
+ # 2017/03/27 NOTE: store description from EFT
+ description = re.match("Description=(.+)", 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:
+ pyfalog.warning("Cannot get item.")
+ continue
+ if droneItem.category.name == "Drone":
+ # Add drone to the fitting
+ d = Drone(droneItem)
+ d.amount = droneAmount
+ if entityState == "Active":
+ d.amountActive = droneAmount
+ elif entityState == "Inactive":
+ d.amountActive = 0
+ fitobj.drones.append(d)
+ elif droneItem.category.name == "Fighter": # EFT saves fighter as drones
+ ft = Fighter(droneItem)
+ ft.amount = int(droneAmount) if ft.amount <= ft.fighterSquadronMaxSize else ft.fighterSquadronMaxSize
+ fitobj.fighters.append(ft)
+ else:
+ continue
+ 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:
+ pyfalog.warning("Cannot get item.")
+ 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
+ fitobj.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:
+ pyfalog.warning("Cannot get item.")
+ 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
+ fitobj.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:
+ pyfalog.warning("Cannot get item.")
+ continue
+ # Add Cargo to the fitting
+ c = Cargo(item)
+ c.amount = cargoAmount
+ fitobj.cargo.append(c)
+ # 2017/03/27 NOTE: store description from EFT
+ elif description:
+ fitobj.notes = description.group(1).replace("|", "\n")
+ 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:
+ pyfalog.warning("Cannot get item.")
+ continue
+
+ # Create module
+ m = Module(modItem)
+
+ # Add subsystems before modules to make sure T3 cruisers have subsystems installed
+ if modItem.category.name == "Subsystem":
+ if m.fits(fitobj):
+ fitobj.modules.append(m)
+ else:
+ m.owner = fitobj
+ # Activate mod if it is activable
+ 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:
+ pyfalog.warning("Cannot get item.")
+ pass
+ # Append module to fit
+ moduleList.append(m)
+
+ # Recalc to get slot numbers correct for T3 cruisers
+ svcFit.getInstance().recalc(fitobj)
+
+ for module in moduleList:
+ if module.fits(fitobj):
+ fitobj.modules.append(module)
+
+ # Append fit to list of fits
+ fits.append(fitobj)
+
+ if iportuser: # NOTE: Send current processing status
+ processing_notify(
+ iportuser, IPortUser.PROCESS_IMPORT | IPortUser.ID_UPDATE,
+ "%s:\n%s" % (fitobj.ship.name, fitobj.name)
+ )
+
+ # Skip fit silently if we get an exception
+ except Exception as e:
+ pyfalog.error("Caught exception on fit.")
+ pyfalog.error(e)
+ pass
+
+ return fits
+
+
+def _importPrepareString(eftString):
+ lines = eftString.splitlines()
+ for i in range(len(lines)):
+ lines[i] = lines[i].strip()
+ while lines and not lines[0]:
+ del lines[0]
+ while lines and not lines[-1]:
+ del lines[-1]
+ return lines
+
+
+def _importGetMutationData(lines):
+ data = {}
+ consumedIndices = set()
+ for i in range(len(lines)):
+ line = lines[i]
+ m = re.match('^\[(?P[\d+)\]', line)
+ if m:
+ ref = int(m.group('ref'))
+ # Attempt to apply mutation is useless w/o mutaplasmid, so skip it
+ # altogether if we have no info on it
+ try:
+ mutaName = lines[i + 1]
+ except IndexError:
+ continue
+ else:
+ consumedIndices.add(i)
+ consumedIndices.add(i + 1)
+ # Get custom attribute values
+ mutaAttrs = {}
+ try:
+ mutaAttrsLine = lines[i + 2]
+ except IndexError:
+ pass
+ else:
+ consumedIndices.add(i + 2)
+ pairs = [p.strip() for p in mutaAttrsLine.split(',')]
+ for pair in pairs:
+ try:
+ attrName, value = pair.split(' ')
+ except ValueError:
+ continue
+ try:
+ value = float(value)
+ except (ValueError, TypeError):
+ continue
+ attrInfo = getAttributeInfo(attrName.strip())
+ if attrInfo is None:
+ continue
+ mutaAttrs[attrInfo.ID] = value
+ mutaItem = _fetchItem(mutaName)
+ if mutaItem is None:
+ continue
+ data[ref] = (mutaItem, mutaAttrs)
+ # If we got here, we have seen at least correct reference line and
+ # mutaplasmid name line
+ i += 2
+ # Bonus points for seeing correct attrs line. Worst case we
+ # will have to scan it once again
+ if mutaAttrs:
+ i += 1
+ # Cleanup the lines from mutaplasmid info
+ for i in sorted(consumedIndices, reverse=True):
+ del lines[i]
+ return data
+
+
+def _importSectionIter(lines):
+ section = Section()
+ for line in lines:
+ if not line:
+ if section.lines:
+ yield section
+ section = Section()
+ else:
+ section.lines.append(line)
+ if section.lines:
+ yield section
+
+
+def _importCreateFit(lines):
+ """Create fit and set top-level entity (ship or citadel)."""
+ fit = Fit()
+ header = lines.pop(0)
+ m = re.match('\[(?P[\w\s]+),\s*(?P.+)\]', header)
+ if not m:
+ pyfalog.warning('service.port.eft.importEft: corrupted fit header')
+ raise EftImportError
+ shipType = m.group('shipType').strip()
+ fitName = m.group('fitName').strip()
+ try:
+ ship = _fetchItem(shipType)
+ try:
+ fit.ship = Ship(ship)
+ except ValueError:
+ fit.ship = Citadel(ship)
+ fit.name = fitName
+ except:
+ pyfalog.warning('service.port.eft.importEft: exception caught when parsing header')
+ raise EftImportError
+ return fit
+
+
+def _fetchItem(typeName, eagerCat=False):
+ sMkt = Market.getInstance()
+ eager = 'group.category' if eagerCat else None
+ try:
+ item = sMkt.getItem(typeName, eager=eager)
+ except:
+ pyfalog.warning('service.port.eft: unable to fetch item "{}"'.format(typeName))
+ return None
+ if sMkt.getPublicityByItem(item):
+ return item
+ else:
+ return None
+
+
+def _clearTail(lst):
+ while lst and lst[-1] is None:
+ del lst[-1]
+
+
+class EftImportError(Exception):
+ """Exception class emitted and consumed by EFT importer internally."""
+ ...
+
+
+class Section:
+
+ def __init__(self):
+ self.lines = []
+ self.itemSpecs = []
+ self.__itemDataCats = None
+
+ @property
+ def itemDataCats(self):
+ if self.__itemDataCats is None:
+ cats = set()
+ for itemSpec in self.itemSpecs:
+ if itemSpec is None:
+ continue
+ cats.add(itemSpec.item.category.name)
+ self.__itemDataCats = tuple(sorted(cats))
+ return self.__itemDataCats
+
+ @property
+ def isModuleRack(self):
+ return all(i is None or i.isModule for i in self.itemSpecs)
+
+ @property
+ def isImplantRack(self):
+ return all(i is not None and i.isImplant for i in self.itemSpecs)
+
+ @property
+ def isDroneBay(self):
+ return all(i is not None and i.isDrone for i in self.itemSpecs)
+
+ @property
+ def isFighterBay(self):
+ return all(i is not None and i.isFighter for i in self.itemSpecs)
+
+ @property
+ def isCargoHold(self):
+ return (
+ all(i is not None and i.isCargo for i in self.itemSpecs) and
+ not self.isDroneBay and not self.isFighterBay)
+
+
+class BaseItemSpec:
+
+ def __init__(self, typeName):
+ item = _fetchItem(typeName, eagerCat=True)
+ if item is None:
+ raise EftImportError
+ self.typeName = typeName
+ self.item = item
+
+ @property
+ def isModule(self):
+ return False
+
+ @property
+ def isImplant(self):
+ return False
+
+ @property
+ def isDrone(self):
+ return False
+
+ @property
+ def isFighter(self):
+ return False
+
+ @property
+ def isCargo(self):
+ return False
+
+
+class RegularItemSpec(BaseItemSpec):
+
+ def __init__(self, typeName, chargeName=None):
+ super().__init__(typeName)
+ self.charge = self.__fetchCharge(chargeName)
+ self.offline = False
+ self.mutationIdx = None
+
+ def __fetchCharge(self, chargeName):
+ if chargeName:
+ charge = _fetchItem(chargeName, eagerCat=True)
+ if not charge or charge.category.name != 'Charge':
+ charge = None
+ else:
+ charge = None
+ return charge
+
+ @property
+ def isModule(self):
+ return self.item.category.name in MODULE_CATS
+
+ @property
+ def isImplant(self):
+ return (
+ self.item.category.name == 'Implant' and (
+ 'implantness' in self.item.attributes or
+ 'boosterness' in self.item.attributes))
+
+
+class MultiItemSpec(BaseItemSpec):
+
+ def __init__(self, typeName):
+ super().__init__(typeName)
+ self.amount = 0
+
+ @property
+ def isDrone(self):
+ return self.item.category.name == 'Drone'
+
+ @property
+ def isFighter(self):
+ return self.item.category.name == 'Fighter'
+
+ @property
+ def isCargo(self):
+ return True
+
+
+class AbstractFit:
+
+ def __init__(self):
+ # Modules
+ self.modulesHigh = []
+ self.modulesMed = []
+ self.modulesLow = []
+ self.rigs = []
+ self.subsystems = []
+ self.services = []
+ # Non-modules
+ self.implants = []
+ self.boosters = []
+ self.drones = {} # Format: {item: Drone}
+ self.fighters = []
+ self.cargo = {} # Format: {item: Cargo}
+ # Other stuff
+ self.mutations = {} # Format: {reference: (mutaplamid item, {attr ID: attr value})}
+
+ @property
+ def __slotContainerMap(self):
+ return {
+ Slot.HIGH: self.modulesHigh,
+ Slot.MED: self.modulesMed,
+ Slot.LOW: self.modulesLow,
+ Slot.RIG: self.rigs,
+ Slot.SUBSYSTEM: self.subsystems,
+ Slot.SERVICE: self.services}
+
+ def getContainerBySlot(self, slotType):
+ return self.__slotContainerMap.get(slotType)
+
+ def getSlotByContainer(self, container):
+ slotType = None
+ for k, v in self.__slotContainerMap.items():
+ if v is container:
+ slotType = k
+ break
+ return slotType
+
+ def addModules(self, itemSpecs):
+ modules = []
+ slotTypes = set()
+ for itemSpec in itemSpecs:
+ if itemSpec is None:
+ modules.append(None)
+ continue
+ m = self.__makeModule(itemSpec)
+ if m is None:
+ modules.append(None)
+ continue
+ modules.append(m)
+ slotTypes.add(m.slot)
+ _clearTail(modules)
+ # If all the modules have same slot type, put them to appropriate
+ # container with stubs
+ if len(slotTypes) == 1:
+ slotType = tuple(slotTypes)[0]
+ self.getContainerBySlot(slotType).extend(modules)
+ # Otherwise, put just modules
+ else:
+ for m in modules:
+ if m is None:
+ continue
+ self.getContainerBySlot(m.slot).append(m)
+
+ def addModule(self, itemSpec):
+ if itemSpec is None:
+ return
+ m = self.__makeModule(itemSpec)
+ if m is not None:
+ self.getContainerBySlot(m.slot).append(m)
+
+ def __makeModule(self, itemSpec):
+ # Mutate item if needed
+ m = None
+ if itemSpec.mutationIdx in self.mutations:
+ mutaItem, mutaAttrs = self.mutations[itemSpec.mutationIdx]
+ mutaplasmid = getDynamicItem(mutaItem.ID)
+ if mutaplasmid:
+ try:
+ m = Module(mutaplasmid.resultingItem, itemSpec.item, mutaplasmid)
+ except ValueError:
+ pass
+ else:
+ for attrID, mutator in m.mutators.items():
+ if attrID in mutaAttrs:
+ mutator.value = mutaAttrs[attrID]
+ # If we still don't have item (item is not mutated or we
+ # failed to construct mutated item), try to make regular item
+ if m is None:
+ try:
+ m = Module(itemSpec.item)
+ except ValueError:
+ return None
+
+ if itemSpec.charge is not None and m.isValidCharge(itemSpec.charge):
+ m.charge = itemSpec.charge
+ if itemSpec.offline and m.isValidState(State.OFFLINE):
+ m.state = State.OFFLINE
+ elif m.isValidState(State.ACTIVE):
+ m.state = State.ACTIVE
+ return m
+
+ def addImplant(self, itemSpec):
+ if itemSpec is None:
+ return
+ if 'implantness' in itemSpec.item.attributes:
+ self.implants.append(Implant(itemSpec.item))
+ elif 'boosterness' in itemSpec.item.attributes:
+ self.boosters.append(Booster(itemSpec.item))
+ else:
+ pyfalog.error('Failed to import implant: {}', itemSpec.typeName)
+
+ def addDrone(self, itemSpec):
+ if itemSpec is None:
+ return
+ if itemSpec.item not in self.drones:
+ self.drones[itemSpec.item] = Drone(itemSpec.item)
+ self.drones[itemSpec.item].amount += itemSpec.amount
+
+ def addFighter(self, itemSpec):
+ if itemSpec is None:
+ return
+ fighter = Fighter(itemSpec.item)
+ fighter.amount = itemSpec.amount
+ self.fighters.append(fighter)
+
+ def addCargo(self, itemSpec):
+ if itemSpec is None:
+ return
+ if itemSpec.item not in self.cargo:
+ self.cargo[itemSpec.item] = Cargo(itemSpec.item)
+ self.cargo[itemSpec.item].amount += itemSpec.amount
diff --git a/service/port/esi.py b/service/port/esi.py
new file mode 100644
index 000000000..f1e02d13a
--- /dev/null
+++ b/service/port/esi.py
@@ -0,0 +1,208 @@
+# =============================================================================
+# 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 .
+# =============================================================================
+
+
+import collections
+import json
+
+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
+
+
+class ESIExportException(Exception):
+ pass
+
+
+pyfalog = Logger(__name__)
+
+INV_FLAGS = {
+ Slot.LOW: 11,
+ Slot.MED: 19,
+ Slot.HIGH: 27,
+ Slot.RIG: 92,
+ Slot.SUBSYSTEM: 125,
+ Slot.SERVICE: 164
+}
+
+INV_FLAG_CARGOBAY = 5
+INV_FLAG_DRONEBAY = 87
+INV_FLAG_FIGHTER = 158
+
+
+def exportESI(ofit):
+ # A few notes:
+ # max fit name length is 50 characters
+ # Most keys are created simply because they are required, but bogus data is okay
+
+ nested_dict = lambda: collections.defaultdict(nested_dict)
+ fit = nested_dict()
+ sFit = svcFit.getInstance()
+
+ # max length is 50 characters
+ name = ofit.name[:47] + '...' if len(ofit.name) > 50 else ofit.name
+ fit['name'] = name
+ fit['ship_type_id'] = ofit.ship.item.ID
+
+ # 2017/03/29 NOTE: "<" or "<" is Ignored
+ # fit['description'] = "" % ofit.ID
+ fit['description'] = ofit.notes[:397] + '...' if len(ofit.notes) > 400 else ofit.notes if ofit.notes is not None else ""
+ fit['items'] = []
+
+ slotNum = {}
+ charges = {}
+ for module in ofit.modules:
+ if module.isEmpty:
+ continue
+
+ item = nested_dict()
+ slot = module.slot
+
+ if slot == Slot.SUBSYSTEM:
+ # Order of subsystem matters based on this attr. See GH issue #130
+ slot = int(module.getModifiedItemAttr("subSystemSlot"))
+ item['flag'] = slot
+ else:
+ if slot not in slotNum:
+ slotNum[slot] = INV_FLAGS[slot]
+
+ item['flag'] = slotNum[slot]
+ slotNum[slot] += 1
+
+ item['quantity'] = 1
+ item['type_id'] = module.item.ID
+ fit['items'].append(item)
+
+ if module.charge and sFit.serviceFittingOptions["exportCharges"]:
+ 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
+
+ for cargo in ofit.cargo:
+ item = nested_dict()
+ item['flag'] = INV_FLAG_CARGOBAY
+ item['quantity'] = cargo.amount
+ item['type_id'] = cargo.item.ID
+ fit['items'].append(item)
+
+ for chargeID, amount in list(charges.items()):
+ item = nested_dict()
+ item['flag'] = INV_FLAG_CARGOBAY
+ item['quantity'] = amount
+ item['type_id'] = chargeID
+ fit['items'].append(item)
+
+ for drone in ofit.drones:
+ item = nested_dict()
+ item['flag'] = INV_FLAG_DRONEBAY
+ item['quantity'] = drone.amount
+ item['type_id'] = drone.item.ID
+ fit['items'].append(item)
+
+ for fighter in ofit.fighters:
+ item = nested_dict()
+ item['flag'] = INV_FLAG_FIGHTER
+ item['quantity'] = fighter.amountActive
+ item['type_id'] = fighter.item.ID
+ fit['items'].append(item)
+
+ if len(fit['items']) == 0:
+ raise ESIExportException("Cannot export fitting: module list cannot be empty.")
+
+ return json.dumps(fit)
+
+
+def importESI(str_):
+
+ sMkt = Market.getInstance()
+ fitobj = Fit()
+ refobj = json.loads(str_)
+ items = refobj['items']
+ # "<" and ">" is replace to "<", ">" by EVE client
+ fitobj.name = refobj['name']
+ # 2017/03/29: read description
+ fitobj.notes = refobj['description']
+
+ try:
+ ship = refobj['ship_type_id']
+ try:
+ fitobj.ship = Ship(sMkt.getItem(ship))
+ except ValueError:
+ fitobj.ship = Citadel(sMkt.getItem(ship))
+ except:
+ pyfalog.warning("Caught exception in importESI")
+ return None
+
+ items.sort(key=lambda k: k['flag'])
+
+ moduleList = []
+ for module in items:
+ try:
+ item = sMkt.getItem(module['type_id'], eager="group.category")
+ if not item.published:
+ continue
+ if module['flag'] == INV_FLAG_DRONEBAY:
+ d = Drone(item)
+ d.amount = module['quantity']
+ fitobj.drones.append(d)
+ elif module['flag'] == INV_FLAG_CARGOBAY:
+ c = Cargo(item)
+ c.amount = module['quantity']
+ fitobj.cargo.append(c)
+ elif module['flag'] == INV_FLAG_FIGHTER:
+ fighter = Fighter(item)
+ fitobj.fighters.append(fighter)
+ else:
+ try:
+ m = Module(item)
+ # When item can't be added to any slot (unknown item or just charge), ignore it
+ except ValueError:
+ pyfalog.debug("Item can't be added to any slot (unknown item or just charge)")
+ continue
+ # Add subsystems before modules to make sure T3 cruisers have subsystems installed
+ if item.category.name == "Subsystem":
+ if m.fits(fitobj):
+ fitobj.modules.append(m)
+ else:
+ if m.isValidState(State.ACTIVE):
+ m.state = State.ACTIVE
+
+ moduleList.append(m)
+
+ except:
+ pyfalog.warning("Could not process module.")
+ continue
+
+ # Recalc to get slot numbers correct for T3 cruisers
+ svcFit.getInstance().recalc(fitobj)
+
+ for module in moduleList:
+ if module.fits(fitobj):
+ fitobj.modules.append(module)
+
+ return fitobj
diff --git a/service/port/multibuy.py b/service/port/multibuy.py
new file mode 100644
index 000000000..2bf2d7a8a
--- /dev/null
+++ b/service/port/multibuy.py
@@ -0,0 +1,68 @@
+# =============================================================================
+# 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 .
+# =============================================================================
+
+
+from service.fit import Fit as svcFit
+from service.port.eft import SLOT_ORDER as EFT_SLOT_ORDER
+
+
+def exportMultiBuy(fit):
+ export = "%s\n" % fit.ship.item.name
+ stuff = {}
+ sFit = svcFit.getInstance()
+ for module in fit.modules:
+ slot = module.slot
+ if slot not in stuff:
+ stuff[slot] = []
+ 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)
+
+ for slotType in EFT_SLOT_ORDER:
+ data = stuff.get(slotType)
+ if data is not None:
+ # export += "\n"
+ for curr in data:
+ export += curr
+
+ if len(fit.drones) > 0:
+ 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 len(fit.implants) > 0:
+ for implant in fit.implants:
+ export += "%s\n" % implant.item.name
+
+ if len(fit.boosters) > 0:
+ for booster in fit.boosters:
+ export += "%s\n" % booster.item.name
+
+ if len(fit.fighters) > 0:
+ for fighter in fit.fighters:
+ export += "%s x%s\n" % (fighter.item.name, fighter.amountActive)
+
+ if export[-1] == "\n":
+ export = export[:-1]
+
+ return export
diff --git a/service/port/port.py b/service/port/port.py
new file mode 100644
index 000000000..560af14ea
--- /dev/null
+++ b/service/port/port.py
@@ -0,0 +1,277 @@
+# =============================================================================
+# 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 .
+# =============================================================================
+
+
+import re
+import os
+import threading
+import xml.dom
+import xml.parsers.expat
+from codecs import open
+
+from bs4 import UnicodeDammit
+from logbook import Logger
+
+from eos import db
+from eos.saveddata.fit import ImplantLocation
+from service.fit import Fit as svcFit
+from service.port.dna import exportDna, importDna
+from service.port.eft import exportEft, importEft, importEftCfg
+from service.port.esi import exportESI, importESI
+from service.port.multibuy import exportMultiBuy
+from service.port.shared import IPortUser, UserCancelException, processing_notify
+from service.port.xml import importXml, exportXml
+
+
+pyfalog = Logger(__name__)
+
+# 2017/04/05 NOTE: simple validation, for xml file
+RE_XML_START = r'<\?xml\s+version="1.0"\s*\?>'
+
+
+class Port(object):
+ """Service which houses all import/export format functions"""
+ instance = None
+ __tag_replace_flag = True
+
+ @classmethod
+ def getInstance(cls):
+ if cls.instance is None:
+ cls.instance = Port()
+
+ return cls.instance
+
+ @classmethod
+ def set_tag_replace(cls, b):
+ cls.__tag_replace_flag = b
+
+ @classmethod
+ def is_tag_replace(cls):
+ # might there is a person who wants to hold tags.
+ # (item link in EVE client etc. When importing again to EVE)
+ return cls.__tag_replace_flag
+
+ @staticmethod
+ def backupFits(path, iportuser):
+ pyfalog.debug("Starting backup fits thread.")
+
+ def backupFitsWorkerFunc(path, iportuser):
+ success = True
+ try:
+ iportuser.on_port_process_start()
+ backedUpFits = Port.exportXml(iportuser,
+ *svcFit.getInstance().getAllFits())
+ backupFile = open(path, "w", encoding="utf-8")
+ backupFile.write(backedUpFits)
+ backupFile.close()
+ except UserCancelException:
+ success = False
+ # Send done signal to GUI
+ # wx.CallAfter(callback, -1, "Done.")
+ flag = IPortUser.ID_ERROR if not success else IPortUser.ID_DONE
+ iportuser.on_port_processing(IPortUser.PROCESS_EXPORT | flag,
+ "User canceled or some error occurrence." if not success else "Done.")
+
+ threading.Thread(
+ target=backupFitsWorkerFunc,
+ args=(path, iportuser)
+ ).start()
+
+ @staticmethod
+ def importFitsThreaded(paths, iportuser):
+ # type: (tuple, IPortUser) -> None
+ """
+ :param paths: fits data file path list.
+ :param iportuser: IPortUser implemented class.
+ :rtype: None
+ """
+ pyfalog.debug("Starting import fits thread.")
+
+ def importFitsFromFileWorkerFunc(paths, iportuser):
+ iportuser.on_port_process_start()
+ success, result = Port.importFitFromFiles(paths, iportuser)
+ flag = IPortUser.ID_ERROR if not success else IPortUser.ID_DONE
+ iportuser.on_port_processing(IPortUser.PROCESS_IMPORT | flag, result)
+
+ threading.Thread(
+ target=importFitsFromFileWorkerFunc,
+ args=(paths, iportuser)
+ ).start()
+
+ @staticmethod
+ def importFitFromFiles(paths, iportuser=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
+ """
+
+ sFit = svcFit.getInstance()
+
+ fit_list = []
+ try:
+ for path in paths:
+ if iportuser: # Pulse
+ msg = "Processing file:\n%s" % path
+ pyfalog.debug(msg)
+ processing_notify(iportuser, IPortUser.PROCESS_IMPORT | IPortUser.ID_UPDATE, msg)
+ # wx.CallAfter(callback, 1, msg)
+
+ with open(path, "rb") as file_:
+ srcString = file_.read()
+ dammit = UnicodeDammit(srcString)
+ srcString = dammit.unicode_markup
+
+ if len(srcString) == 0: # ignore blank files
+ pyfalog.debug("File is blank.")
+ continue
+
+ try:
+ _, fitsImport = Port.importAuto(srcString, path, iportuser=iportuser)
+ fit_list += fitsImport
+ except xml.parsers.expat.ExpatError:
+ pyfalog.warning("Malformed XML in:\n{0}", path)
+ return False, "Malformed XML in %s" % path
+
+ # IDs = [] # NOTE: what use for IDs?
+ numFits = len(fit_list)
+ for idx, fit in enumerate(fit_list):
+ # Set some more fit attributes and save
+ fit.character = sFit.character
+ fit.damagePattern = sFit.pattern
+ fit.targetResists = sFit.targetResists
+ if len(fit.implants) > 0:
+ fit.implantLocation = ImplantLocation.FIT
+ else:
+ useCharImplants = sFit.serviceFittingOptions["useCharacterImplantsByDefault"]
+ fit.implantLocation = ImplantLocation.CHARACTER if useCharImplants else ImplantLocation.FIT
+ db.save(fit)
+ # IDs.append(fit.ID)
+ if iportuser: # Pulse
+ pyfalog.debug("Processing complete, saving fits to database: {0}/{1}", idx + 1, numFits)
+ processing_notify(
+ iportuser, IPortUser.PROCESS_IMPORT | IPortUser.ID_UPDATE,
+ "Processing complete, saving fits to database\n(%d/%d) %s" % (idx + 1, numFits, fit.ship.name)
+ )
+
+ except UserCancelException:
+ return False, "Processing has been canceled.\n"
+ except Exception as e:
+ pyfalog.critical("Unknown exception processing: {0}", path)
+ pyfalog.critical(e)
+ # TypeError: not all arguments converted during string formatting
+# return False, "Unknown Error while processing {0}" % path
+ return False, "Unknown error while processing %s\n\n Error: %s" % (path, e.message)
+
+ return True, fit_list
+
+ @staticmethod
+ def importFitFromBuffer(bufferStr, activeFit=None):
+ # type: (str, object) -> object
+ # TODO: catch the exception?
+ # activeFit is reserved?, bufferStr is unicode? (assume only clipboard string?
+ 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
+ if len(fit.implants) > 0:
+ fit.implantLocation = ImplantLocation.FIT
+ else:
+ useCharImplants = sFit.serviceFittingOptions["useCharacterImplantsByDefault"]
+ fit.implantLocation = ImplantLocation.CHARACTER if useCharImplants else ImplantLocation.FIT
+ db.save(fit)
+ return fits
+
+ @classmethod
+ def importAuto(cls, string, path=None, activeFit=None, iportuser=None):
+ # type: (Port, str, str, object, IPortUser) -> object
+ # 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 XML-style start of tag encountered, detect as XML
+ if re.search(RE_XML_START, firstLine):
+ return "XML", cls.importXml(string, iportuser)
+
+ # If JSON-style start, parse as CREST/JSON
+ if firstLine[0] == '{':
+ return "JSON", (cls.importESI(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 path is not None:
+ filename = os.path.split(path)[1]
+ shipName = filename.rsplit('.')[0]
+ return "EFT Config", cls.importEftCfg(shipName, string, iportuser)
+
+ # 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),)
+
+ # EFT-related methods
+ @staticmethod
+ def importEft(eftString):
+ return importEft(eftString)
+
+ @staticmethod
+ def importEftCfg(shipname, contents, iportuser=None):
+ return importEftCfg(shipname, contents, iportuser)
+
+ @classmethod
+ def exportEft(cls, fit, options):
+ return exportEft(fit, options)
+
+ # DNA-related methods
+ @staticmethod
+ def importDna(string):
+ return importDna(string)
+
+ @staticmethod
+ def exportDna(fit):
+ return exportDna(fit)
+
+ # ESI-related methods
+ @staticmethod
+ def importESI(string):
+ return importESI(string)
+
+ @staticmethod
+ def exportESI(fit):
+ return exportESI(fit)
+
+ # XML-related methods
+ @staticmethod
+ def importXml(text, iportuser=None):
+ return importXml(text, iportuser)
+
+ @staticmethod
+ def exportXml(iportuser=None, *fits):
+ return exportXml(iportuser, *fits)
+
+ # Multibuy-related methods
+ @staticmethod
+ def exportMultiBuy(fit):
+ return exportMultiBuy(fit)
diff --git a/service/port/shared.py b/service/port/shared.py
new file mode 100644
index 000000000..a21a81d63
--- /dev/null
+++ b/service/port/shared.py
@@ -0,0 +1,70 @@
+# =============================================================================
+# 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 .
+# =============================================================================
+
+
+from abc import ABCMeta, abstractmethod
+
+
+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
+
+
+def processing_notify(iportuser, flag, data):
+ if not iportuser.on_port_processing(flag, data):
+ raise UserCancelException
diff --git a/service/port/xml.py b/service/port/xml.py
new file mode 100644
index 000000000..54d5a98eb
--- /dev/null
+++ b/service/port/xml.py
@@ -0,0 +1,326 @@
+# =============================================================================
+# 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 .
+# =============================================================================
+
+import re
+import xml.dom
+import xml.parsers.expat
+
+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
+from utils.strfunctions import sequential_rep, replace_ltgt
+
+from service.port.shared import IPortUser, processing_notify
+
+
+pyfalog = Logger(__name__)
+
+# -- 170327 Ignored description --
+RE_LTGT = "&(lt|gt);"
+L_MARK = "<localized hint=""
+# <localized hint="([^"]+)">([^\*]+)\*<\/localized>
+LOCALIZED_PATTERN = re.compile(r'([^\*]+)\*')
+
+
+def _extract_match(t):
+ m = LOCALIZED_PATTERN.match(t)
+ # hint attribute, text content
+ return m.group(1), m.group(2)
+
+
+def _resolve_ship(fitting, sMkt, b_localized):
+ # type: (xml.dom.minidom.Element, service.market.Market, bool) -> eos.saveddata.fit.Fit
+ """ NOTE: Since it is meaningless unless a correct ship object can be constructed,
+ process flow changed
+ """
+ # ------ Confirm ship
+ # Maelstrom
+ shipType = fitting.getElementsByTagName("shipType").item(0).getAttribute("value")
+ anything = None
+ if b_localized:
+ # expect an official name, emergency cache
+ shipType, anything = _extract_match(shipType)
+
+ limit = 2
+ ship = None
+ while True:
+ must_retry = False
+ try:
+ try:
+ ship = Ship(sMkt.getItem(shipType))
+ except ValueError:
+ ship = Citadel(sMkt.getItem(shipType))
+ except Exception as e:
+ pyfalog.warning("Caught exception on _resolve_ship")
+ pyfalog.error(e)
+ limit -= 1
+ if limit is 0:
+ break
+ shipType = anything
+ must_retry = True
+ if not must_retry:
+ break
+
+ if ship is None:
+ raise Exception("cannot resolve ship type.")
+
+ fitobj = Fit(ship=ship)
+ # ------ Confirm fit name
+ anything = fitting.getAttribute("name")
+ # 2017/03/29 NOTE:
+ # if fit name contained "<" or ">" then reprace to named html entity by EVE client
+ # if re.search(RE_LTGT, anything):
+ if "<" in anything or ">" in anything:
+ anything = replace_ltgt(anything)
+ fitobj.name = anything
+
+ return fitobj
+
+
+def _resolve_module(hardware, sMkt, b_localized):
+ # type: (xml.dom.minidom.Element, service.market.Market, bool) -> eos.saveddata.module.Module
+ moduleName = hardware.getAttribute("type")
+ emergency = None
+ if b_localized:
+ # expect an official name, emergency cache
+ moduleName, emergency = _extract_match(moduleName)
+
+ item = None
+ limit = 2
+ while True:
+ must_retry = False
+ try:
+ item = sMkt.getItem(moduleName, eager="group.category")
+ except Exception as e:
+ pyfalog.warning("Caught exception on _resolve_module")
+ pyfalog.error(e)
+ limit -= 1
+ if limit is 0:
+ break
+ moduleName = emergency
+ must_retry = True
+ if not must_retry:
+ break
+ return item
+
+
+def importXml(text, iportuser):
+ from .port import Port
+ # type: (str, IPortUser) -> list[eos.saveddata.fit.Fit]
+ sMkt = Market.getInstance()
+ doc = xml.dom.minidom.parseString(text)
+ # NOTE:
+ # When L_MARK is included at this point,
+ # Decided to be localized data
+ b_localized = L_MARK in text
+ fittings = doc.getElementsByTagName("fittings").item(0)
+ fittings = fittings.getElementsByTagName("fitting")
+ fit_list = []
+ failed = 0
+
+ for fitting in fittings:
+ try:
+ fitobj = _resolve_ship(fitting, sMkt, b_localized)
+ except:
+ failed += 1
+ continue
+
+ # -- 170327 Ignored description --
+ # read description from exported xml. (EVE client, EFT)
+ description = fitting.getElementsByTagName("description").item(0).getAttribute("value")
+ if description is None:
+ description = ""
+ elif len(description):
+ # convert ]
to "\n" and remove html tags.
+ if Port.is_tag_replace():
+ description = replace_ltgt(
+ sequential_rep(description, r"<(br|BR)>", "\n", r"<[^<>]+>", "")
+ )
+ fitobj.notes = description
+
+ hardwares = fitting.getElementsByTagName("hardware")
+ moduleList = []
+ for hardware in hardwares:
+ try:
+ item = _resolve_module(hardware, sMkt, b_localized)
+ if not item or not item.published:
+ continue
+
+ if item.category.name == "Drone":
+ d = Drone(item)
+ d.amount = int(hardware.getAttribute("qty"))
+ fitobj.drones.append(d)
+ elif item.category.name == "Fighter":
+ ft = Fighter(item)
+ ft.amount = int(hardware.getAttribute("qty")) if ft.amount <= ft.fighterSquadronMaxSize else ft.fighterSquadronMaxSize
+ fitobj.fighters.append(ft)
+ 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"))
+ fitobj.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:
+ pyfalog.warning("item can't be added to any slot (unknown item or just charge), ignore it")
+ continue
+ # Add subsystems before modules to make sure T3 cruisers have subsystems installed
+ if item.category.name == "Subsystem":
+ if m.fits(fitobj):
+ m.owner = fitobj
+ fitobj.modules.append(m)
+ else:
+ if m.isValidState(State.ACTIVE):
+ m.state = State.ACTIVE
+
+ moduleList.append(m)
+
+ except KeyboardInterrupt:
+ pyfalog.warning("Keyboard Interrupt")
+ continue
+
+ # Recalc to get slot numbers correct for T3 cruisers
+ svcFit.getInstance().recalc(fitobj)
+
+ for module in moduleList:
+ if module.fits(fitobj):
+ module.owner = fitobj
+ fitobj.modules.append(module)
+
+ fit_list.append(fitobj)
+ if iportuser: # NOTE: Send current processing status
+ processing_notify(
+ iportuser, IPortUser.PROCESS_IMPORT | IPortUser.ID_UPDATE,
+ "Processing %s\n%s" % (fitobj.ship.name, fitobj.name)
+ )
+
+ return fit_list
+
+
+def exportXml(iportuser, *fits):
+ doc = xml.dom.minidom.Document()
+ fittings = doc.createElement("fittings")
+ # fit count
+ fit_count = len(fits)
+ fittings.setAttribute("count", "%s" % fit_count)
+ doc.appendChild(fittings)
+ sFit = svcFit.getInstance()
+
+ for i, fit in enumerate(fits):
+ try:
+ fitting = doc.createElement("fitting")
+ fitting.setAttribute("name", fit.name)
+ fittings.appendChild(fitting)
+ description = doc.createElement("description")
+ # -- 170327 Ignored description --
+ try:
+ notes = fit.notes # unicode
+
+ if notes:
+ notes = notes[:397] + '...' if len(notes) > 400 else notes
+
+ description.setAttribute(
+ "value", re.sub("(\r|\n|\r\n)+", "
", notes) if notes is not None else ""
+ )
+ except Exception as e:
+ pyfalog.warning("read description is failed, msg=%s\n" % e.args)
+
+ fitting.appendChild(description)
+ shipType = doc.createElement("shipType")
+ shipType.setAttribute("value", fit.ship.name)
+ fitting.appendChild(shipType)
+
+ charges = {}
+ slotNum = {}
+ for module in fit.modules:
+ if module.isEmpty:
+ continue
+
+ slot = module.slot
+
+ if slot == Slot.SUBSYSTEM:
+ # Order of subsystem matters based on this attr. See GH issue #130
+ slotId = module.getModifiedItemAttr("subSystemSlot") - 125
+ else:
+ if slot not 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 and sFit.serviceFittingOptions["exportCharges"]:
+ 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
+
+ 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 fighter in fit.fighters:
+ hardware = doc.createElement("hardware")
+ hardware.setAttribute("qty", "%d" % fighter.amountActive)
+ hardware.setAttribute("slot", "fighter bay")
+ hardware.setAttribute("type", fighter.item.name)
+ fitting.appendChild(hardware)
+
+ for cargo in fit.cargo:
+ if cargo.item.name not in charges:
+ charges[cargo.item.name] = 0
+ charges[cargo.item.name] += cargo.amount
+
+ for name, qty in list(charges.items()):
+ hardware = doc.createElement("hardware")
+ hardware.setAttribute("qty", "%d" % qty)
+ hardware.setAttribute("slot", "cargo")
+ hardware.setAttribute("type", name)
+ fitting.appendChild(hardware)
+ except Exception as e:
+ pyfalog.error("Failed on fitID: %d, message: %s" % e.message)
+ continue
+ finally:
+ if iportuser:
+ processing_notify(
+ iportuser, IPortUser.PROCESS_EXPORT | IPortUser.ID_UPDATE,
+ (i, "convert to xml (%s/%s) %s" % (i + 1, fit_count, fit.ship.name))
+ )
+ return doc.toprettyxml()
diff --git a/service/prereqsCheck.py b/service/prereqsCheck.py
index f0e064796..159468591 100644
--- a/service/prereqsCheck.py
+++ b/service/prereqsCheck.py
@@ -1,7 +1,6 @@
-import sys
-import inspect
-import re
import platform
+import re
+import sys
version_block = ''
@@ -10,7 +9,7 @@ class PreCheckException(Exception):
pass
-class PreCheckMessage():
+class PreCheckMessage:
def __init__(self, msg):
# wx may not be installed, in which case print to console. For all other prechecks, should pop up a MessageDialog
try:
diff --git a/service/price.py b/service/price.py
index 2b8cd474b..ebefe15a1 100644
--- a/service/price.py
+++ b/service/price.py
@@ -18,18 +18,17 @@
# =============================================================================
-import time
-import threading
import queue
-from xml.dom import minidom
+import threading
+import time
-from logbook import Logger
import wx
+from logbook import Logger
from eos import db
-from service.network import Network, TimeoutError
from service.fit import Fit
from service.market import Market
+from service.network import TimeoutError
pyfalog = Logger(__name__)
@@ -235,4 +234,5 @@ class PriceWorkerThread(threading.Thread):
self.wait[x.typeID].append(callback)
-from service.marketSources import evemarketer, evemarketdata # noqa: E402
+# Import market sources only to initialize price source modules, they register on their own
+from .marketSources import *
diff --git a/service/update.py b/service/update.py
index 246b520fd..a2f4cf730 100644
--- a/service/update.py
+++ b/service/update.py
@@ -17,21 +17,19 @@
# along with pyfa. If not, see .
# =============================================================================
-import threading
-import json
import calendar
+import threading
-# noinspection PyPackageRequirements
-import wx
# noinspection PyPackageRequirements
import dateutil.parser
+# noinspection PyPackageRequirements
+import wx
+from logbook import Logger
+from packaging.version import Version
import config
from service.network import Network
from service.settings import UpdateSettings
-from logbook import Logger
-from packaging.version import Version
-
pyfalog = Logger(__name__)
diff --git a/tests/test_locale/file_dialog.py b/tests/test_locale/file_dialog.py
index 357037265..f72dccf8c 100644
--- a/tests/test_locale/file_dialog.py
+++ b/tests/test_locale/file_dialog.py
@@ -1,9 +1,9 @@
# noinspection PyPackageRequirements
-import wx
-import sys
import os
import sys
+import wx
+
script_dir = os.path.dirname(os.path.abspath(__file__))
# Add root to python paths, this allows us to import submodules
sys.path.append(os.path.realpath(os.path.join(script_dir, '..', '..')))
diff --git a/tests/test_modules/test_eos/test_gamedata.py b/tests/test_modules/test_eos/test_gamedata.py
index 4a8674339..73aac3945 100644
--- a/tests/test_modules/test_eos/test_gamedata.py
+++ b/tests/test_modules/test_eos/test_gamedata.py
@@ -6,8 +6,6 @@ script_dir = os.path.dirname(os.path.abspath(__file__))
sys.path.append(os.path.realpath(os.path.join(script_dir, '..', '..', '..')))
# noinspection PyPackageRequirements
-from _development.helpers import DBInMemory as DB, Gamedata, Saveddata
-from _development.helpers_fits import RifterFit, KeepstarFit
def test_race(DB, RifterFit, KeepstarFit):
"""
diff --git a/tests/test_modules/test_eos/test_modifiedAttributeDict.py b/tests/test_modules/test_eos/test_modifiedAttributeDict.py
index 649cfaee8..71bc5d892 100644
--- a/tests/test_modules/test_eos/test_modifiedAttributeDict.py
+++ b/tests/test_modules/test_eos/test_modifiedAttributeDict.py
@@ -9,8 +9,6 @@ print(script_dir)
sys.path.append(script_dir)
# noinspection PyPackageRequirements
-from _development.helpers import DBInMemory as DB, Gamedata, Saveddata
-from _development.helpers_fits import RifterFit
def test_multiply_stacking_penalties(DB, Saveddata, RifterFit):
"""
diff --git a/tests/test_modules/test_eos/test_saveddata/test_booster.py b/tests/test_modules/test_eos/test_saveddata/test_booster.py
index 05576c3fc..11df6280d 100644
--- a/tests/test_modules/test_eos/test_saveddata/test_booster.py
+++ b/tests/test_modules/test_eos/test_saveddata/test_booster.py
@@ -7,9 +7,6 @@ script_dir = os.path.dirname(os.path.abspath(__file__))
sys.path.append(os.path.realpath(os.path.join(script_dir, '..', '..', '..', '..')))
# noinspection PyPackageRequirements
-from _development.helpers import DBInMemory as DB, Gamedata, Saveddata
-from _development.helpers_fits import RifterFit, KeepstarFit
-from _development.helpers_items import StrongBluePillBooster
def test_itemModifiedAttributes(DB, StrongBluePillBooster):
diff --git a/tests/test_modules/test_eos/test_saveddata/test_fit_2.py b/tests/test_modules/test_eos/test_saveddata/test_fit_2.py
index e60d43088..894a3e103 100644
--- a/tests/test_modules/test_eos/test_saveddata/test_fit_2.py
+++ b/tests/test_modules/test_eos/test_saveddata/test_fit_2.py
@@ -4,14 +4,11 @@
# This must be done on every test in order to pass in Travis
import os
import sys
-from copy import deepcopy
script_dir = os.path.dirname(os.path.abspath(__file__))
sys.path.append(os.path.realpath(os.path.join(script_dir, '..', '..', '..', '..')))
# noinspection PyPackageRequirements
-from _development.helpers import DBInMemory as DB, Gamedata, Saveddata
-from _development.helpers_fits import RifterFit, KeepstarFit, HeronFit
def test_calculateModifiedAttributes(DB, RifterFit, KeepstarFit):
diff --git a/tests/test_modules/test_service/test_fit.py b/tests/test_modules/test_service/test_fit.py
index b110dd2da..18f5a9e52 100644
--- a/tests/test_modules/test_service/test_fit.py
+++ b/tests/test_modules/test_service/test_fit.py
@@ -6,9 +6,7 @@ script_dir = os.path.dirname(os.path.abspath(__file__))
sys.path.append(os.path.realpath(os.path.join(script_dir, '..', '..', '..')))
# noinspection PyPackageRequirements
-from _development.helpers import DBInMemory as DB, Gamedata, Saveddata
# noinspection PyPackageRequirements
-from _development.helpers_fits import RifterFit, KeepstarFit
from service.fit import Fit
diff --git a/tests/test_unread_desc.py b/tests/test_unread_desc.py
index 5fc54fd14..824475d4f 100644
--- a/tests/test_unread_desc.py
+++ b/tests/test_unread_desc.py
@@ -64,8 +64,8 @@ def print_db_info():
import eos
print()
print("------------ data base connection info ------------")
- print((eos.db.saveddata_engine))
- print((eos.db.gamedata_engine))
+ print(eos.db.saveddata_engine)
+ print(eos.db.gamedata_engine)
print()
diff --git a/utils/deprecated.py b/utils/deprecated.py
new file mode 100644
index 000000000..b3433515e
--- /dev/null
+++ b/utils/deprecated.py
@@ -0,0 +1,17 @@
+import warnings
+import functools
+
+
+def deprecated(func):
+ """This is a decorator which can be used to mark functions
+ as deprecated. It will result in a warning being emitted
+ when the function is used."""
+ @functools.wraps(func)
+ def new_func(*args, **kwargs):
+ warnings.simplefilter('always', DeprecationWarning) # turn off filter
+ warnings.warn("Call to deprecated function {}.".format(func.__name__),
+ category=DeprecationWarning,
+ stacklevel=2)
+ warnings.simplefilter('default', DeprecationWarning) # reset filter
+ return func(*args, **kwargs)
+ return new_func
diff --git a/utils/strfunctions.py b/utils/strfunctions.py
index 8532d9c18..833277d17 100644
--- a/utils/strfunctions.py
+++ b/utils/strfunctions.py
@@ -1,6 +1,6 @@
-'''
+"""
string manipulation module
-'''
+"""
import re