Handle invalid implants and boosters. Uses a different method to ensure implant and booster slot is not duplicated. Still need to modify existing databases to remove Booster table constraint. Reverts a previous commit: "Gracefully handle invalid boosters in database (both itemIDs that don't exist as well as non-booster items). Implants need a little more work" (aaa5a6ae18)
This commit is contained in:
@@ -29,7 +29,7 @@ boosters_table = Table("boosters", saveddata_meta,
|
||||
Column("itemID", Integer),
|
||||
Column("fitID", Integer, ForeignKey("fits.ID"), nullable = False),
|
||||
Column("active", Boolean),
|
||||
UniqueConstraint("itemID", "fitID"))
|
||||
)
|
||||
|
||||
activeSideEffects_table = Table("boostersActiveSideEffects", saveddata_meta,
|
||||
Column("boosterID", ForeignKey("boosters.ID"), primary_key = True),
|
||||
|
||||
@@ -60,7 +60,7 @@ mapper(Fit, fits_table,
|
||||
primaryjoin = and_(cargo_table.c.fitID == fits_table.c.ID)),
|
||||
"_Fit__projectedDrones" : relation(Drone, collection_class = HandledProjectedDroneList, cascade='all, delete, delete-orphan', single_parent=True,
|
||||
primaryjoin = and_(drones_table.c.fitID == fits_table.c.ID, drones_table.c.projected == True)),
|
||||
"_Fit__implants" : relation(Implant, collection_class = HandledImplantBoosterList, cascade='all, delete, delete-orphan', single_parent=True,
|
||||
"_Fit__implants" : relation(Implant, collection_class = HandledImplantBoosterList, cascade='all, delete, delete-orphan', backref='fit', single_parent=True,
|
||||
primaryjoin = fitImplants_table.c.fitID == fits_table.c.ID,
|
||||
secondaryjoin = fitImplants_table.c.implantID == Implant.ID,
|
||||
secondary = fitImplants_table),
|
||||
|
||||
@@ -164,57 +164,37 @@ class HandledDroneCargoList(HandledList):
|
||||
for o in self.find(item):
|
||||
return o
|
||||
|
||||
def append(self, obj):
|
||||
HandledList.append(self, obj)
|
||||
def append(self, thing):
|
||||
HandledList.append(self, thing)
|
||||
|
||||
if obj.isInvalid:
|
||||
if thing.isInvalid:
|
||||
# we must flag it as modified, otherwise it will not be removed from the database
|
||||
flag_modified(obj, "itemID")
|
||||
self.remove(obj)
|
||||
flag_modified(thing, "itemID")
|
||||
self.remove(thing)
|
||||
|
||||
class HandledImplantBoosterList(HandledList):
|
||||
def __init__(self):
|
||||
self.__slotCache = {}
|
||||
def append(self, thing):
|
||||
if thing.isInvalid:
|
||||
HandledList.append(self, thing)
|
||||
self.remove(thing)
|
||||
|
||||
def append(self, implant):
|
||||
try:
|
||||
if self.__slotCache.has_key(implant.slot):
|
||||
raise ValueError("Implant/Booster slot already in use, remove the old one first or set replace = True")
|
||||
self.__slotCache[implant.slot] = implant
|
||||
HandledList.append(self, implant)
|
||||
except:
|
||||
# if anything goes wrong, simply remove the item
|
||||
eos.db.remove(implant)
|
||||
# if needed, remove booster that was occupying slot
|
||||
oldObj = next((m for m in self if m.slot == thing.slot), None)
|
||||
if oldObj:
|
||||
self.remove(oldObj)
|
||||
|
||||
def remove(self, implant):
|
||||
HandledList.remove(self, implant)
|
||||
del self.__slotCache[implant.slot]
|
||||
# While we deleted this implant, in edge case seems like not all references
|
||||
# to it are removed and object still lives in session; forcibly remove it,
|
||||
# or otherwise when adding the same booster twice booster's table (typeID, fitID)
|
||||
# constraint will report database integrity error
|
||||
# TODO: make a proper fix, probably by adjusting fit-boosters sqlalchemy relationships
|
||||
eos.db.remove(implant)
|
||||
HandledList.append(self, thing)
|
||||
|
||||
def freeSlot(self, slot):
|
||||
if hasattr(slot, "slot"):
|
||||
slot = slot.slot
|
||||
|
||||
try:
|
||||
implant = self.__slotCache[slot]
|
||||
except KeyError:
|
||||
return False
|
||||
try:
|
||||
self.remove(implant)
|
||||
except ValueError:
|
||||
return False
|
||||
return True
|
||||
def remove(self, thing):
|
||||
# We must flag it as modified, otherwise it not be removed from the database
|
||||
flag_modified(thing, "itemID")
|
||||
HandledList.remove(self, thing)
|
||||
|
||||
class HandledProjectedModList(HandledList):
|
||||
def append(self, proj):
|
||||
if proj.isInvalid:
|
||||
# we must include it before we remove it. doing it this way ensures
|
||||
# rows and relationships in databse are removed as well
|
||||
# rows and relationships in database are removed as well
|
||||
HandledList.append(self, proj)
|
||||
self.remove(proj)
|
||||
|
||||
|
||||
@@ -20,39 +20,48 @@
|
||||
from eos.modifiedAttributeDict import ModifiedAttributeDict, ItemAttrShortcut
|
||||
from eos.effectHandlerHelpers import HandledItem
|
||||
from sqlalchemy.orm import reconstructor, validates
|
||||
import eos.db
|
||||
|
||||
class Booster(HandledItem, ItemAttrShortcut):
|
||||
def __init__(self, item):
|
||||
self.__slot = self.__calculateSlot(item)
|
||||
self.itemID = item.ID
|
||||
self.__item = item
|
||||
self.__invalid = False
|
||||
self.itemID = item.ID if item is not None else None
|
||||
self.active = True
|
||||
self.build()
|
||||
|
||||
@reconstructor
|
||||
def init(self):
|
||||
"""Initialize a booster from the database and validate"""
|
||||
self.__item = None
|
||||
self.__invalid = False
|
||||
|
||||
if self.itemID:
|
||||
# if item does not exist, set invalid
|
||||
item = eos.db.getItem(self.itemID)
|
||||
if item is None:
|
||||
self.__invalid = True
|
||||
self.__item = item
|
||||
|
||||
self.build()
|
||||
|
||||
def build(self):
|
||||
self.__itemModifiedAttributes = ModifiedAttributeDict()
|
||||
self.__itemModifiedAttributes.original = self.__item.attributes
|
||||
self.__sideEffects = []
|
||||
for effect in self.item.effects.itervalues():
|
||||
if effect.isType("boosterSideEffect"):
|
||||
s = SideEffect(self)
|
||||
s.effect = effect
|
||||
s.active = effect.ID in self.__activeSideEffectIDs
|
||||
self.__sideEffects.append(s)
|
||||
if self.__item and self.__item.group.name != "Booster":
|
||||
self.__invalid = True
|
||||
|
||||
def __fetchItemInfo(self):
|
||||
import eos.db
|
||||
item = eos.db.getItem(self.itemID)
|
||||
if item:
|
||||
self.__item = item
|
||||
self.__itemModifiedAttributes = ModifiedAttributeDict()
|
||||
self.__sideEffects = []
|
||||
|
||||
if self.__item:
|
||||
self.__itemModifiedAttributes.original = self.__item.attributes
|
||||
self.__slot = self.__calculateSlot(self.__item)
|
||||
self.build()
|
||||
else:
|
||||
raise ValueError("Invalid item as Booster:", self.itemID)
|
||||
|
||||
for effect in self.__item.effects.itervalues():
|
||||
if effect.isType("boosterSideEffect"):
|
||||
s = SideEffect(self)
|
||||
s.effect = effect
|
||||
s.active = effect.ID in self.__activeSideEffectIDs
|
||||
self.__sideEffects.append(s)
|
||||
|
||||
def iterSideEffects(self):
|
||||
return self.__sideEffects.__iter__()
|
||||
@@ -66,23 +75,18 @@ class Booster(HandledItem, ItemAttrShortcut):
|
||||
|
||||
@property
|
||||
def itemModifiedAttributes(self):
|
||||
if self.__item is None:
|
||||
self.__fetchItemInfo()
|
||||
|
||||
return self.__itemModifiedAttributes
|
||||
|
||||
@property
|
||||
def slot(self):
|
||||
if self.__item is None:
|
||||
self.__fetchItemInfo()
|
||||
def isInvalid(self):
|
||||
return self.__invalid
|
||||
|
||||
@property
|
||||
def slot(self):
|
||||
return self.__slot
|
||||
|
||||
@property
|
||||
def item(self):
|
||||
if self.__item is None:
|
||||
self.__fetchItemInfo()
|
||||
|
||||
return self.__item
|
||||
|
||||
def __calculateSlot(self, item):
|
||||
|
||||
@@ -20,46 +20,54 @@
|
||||
from eos.modifiedAttributeDict import ModifiedAttributeDict, ItemAttrShortcut
|
||||
from eos.effectHandlerHelpers import HandledItem
|
||||
from sqlalchemy.orm import validates, reconstructor
|
||||
import eos.db
|
||||
|
||||
class Implant(HandledItem, ItemAttrShortcut):
|
||||
def __init__(self, item):
|
||||
self.__slot = self.__calculateSlot(item)
|
||||
self.__item = item
|
||||
self.itemID = item.ID
|
||||
self.__invalid = False
|
||||
self.itemID = item.ID if item is not None else None
|
||||
self.active = True
|
||||
self.__itemModifiedAttributes = ModifiedAttributeDict()
|
||||
self.__itemModifiedAttributes.original = self.item.attributes
|
||||
self.build()
|
||||
|
||||
@reconstructor
|
||||
def init(self):
|
||||
self.__item = None
|
||||
self.__invalid = False
|
||||
|
||||
if self.itemID:
|
||||
# if item does not exist, set invalid
|
||||
item = eos.db.getItem(self.itemID)
|
||||
if item is None:
|
||||
self.__invalid = True
|
||||
self.__item = item
|
||||
|
||||
self.build()
|
||||
|
||||
def build(self):
|
||||
if self.__item and self.__item.category.name != "Implant":
|
||||
self.__invalid = True
|
||||
|
||||
def __fetchItemInfo(self):
|
||||
import eos.db
|
||||
self.__item = eos.db.getItem(self.itemID)
|
||||
self.__itemModifiedAttributes = ModifiedAttributeDict()
|
||||
self.__itemModifiedAttributes.original = self.__item.attributes
|
||||
self.__slot = self.__calculateSlot(self.__item)
|
||||
|
||||
if self.__item:
|
||||
self.__itemModifiedAttributes.original = self.__item.attributes
|
||||
self.__slot = self.__calculateSlot(self.__item)
|
||||
|
||||
@property
|
||||
def itemModifiedAttributes(self):
|
||||
if self.__item is None:
|
||||
self.__fetchItemInfo()
|
||||
|
||||
return self.__itemModifiedAttributes
|
||||
|
||||
@property
|
||||
def slot(self):
|
||||
if self.__item is None:
|
||||
self.__fetchItemInfo()
|
||||
def isInvalid(self):
|
||||
return self.__invalid
|
||||
|
||||
@property
|
||||
def slot(self):
|
||||
return self.__slot
|
||||
|
||||
@property
|
||||
def item(self):
|
||||
if self.__item is None:
|
||||
self.__fetchItemInfo()
|
||||
|
||||
return self.__item
|
||||
|
||||
def __calculateSlot(self, item):
|
||||
|
||||
@@ -274,7 +274,6 @@ class Fit(object):
|
||||
except ValueError:
|
||||
return False
|
||||
|
||||
fit.implants.freeSlot(implant)
|
||||
fit.implants.append(implant)
|
||||
self.recalc(fit)
|
||||
return True
|
||||
@@ -300,7 +299,6 @@ class Fit(object):
|
||||
except ValueError:
|
||||
return False
|
||||
|
||||
fit.boosters.freeSlot(booster)
|
||||
fit.boosters.append(booster)
|
||||
self.recalc(fit)
|
||||
return True
|
||||
|
||||
Reference in New Issue
Block a user