From 3944545721aa3d5689cf44e0b91787e67b7f7f44 Mon Sep 17 00:00:00 2001 From: blitzmann Date: Sun, 27 May 2018 14:58:14 -0400 Subject: [PATCH] Support for Abyssal module loading base item attributes --- eos/db/gamedata/queries.py | 24 +++++++++++++++++++++++- eos/db/migrations/upgrade28.py | 18 ++++++++++++++++++ eos/db/saveddata/module.py | 2 ++ eos/saveddata/module.py | 12 +++++++++++- 4 files changed, 54 insertions(+), 2 deletions(-) create mode 100644 eos/db/migrations/upgrade28.py diff --git a/eos/db/gamedata/queries.py b/eos/db/gamedata/queries.py index 737169897..e7fbca61c 100644 --- a/eos/db/gamedata/queries.py +++ b/eos/db/gamedata/queries.py @@ -17,8 +17,9 @@ # along with eos. If not, see . # =============================================================================== -from sqlalchemy.orm import join, exc, aliased +from sqlalchemy.orm import join, exc, aliased, joinedload, subqueryload from sqlalchemy.sql import and_, or_, select +from sqlalchemy.inspection import inspect import eos.config from eos.db import gamedata_session @@ -97,6 +98,27 @@ def getItem(lookfor, eager=None): return item +def getItemWithBaseItemAttribute(lookfor, baseItemID, eager=None): + # A lot of this is described in more detail in #1597 + item = gamedata_session.query(Item).get(lookfor) + base = getItem(baseItemID) + + # we have to load all attributes for this object, otherwise we'll lose access to them when we expunge. + # todo: figure out a way to eagerly load all these via the query... + for x in inspect(Item).relationships.keys(): + getattr(item, x) + + # Copy over the attributes from the base, but ise the items attributes when there's an overlap + # WARNING: the attribute object still has the old typeID. I don't believe we access this typeID anywhere in the code, + # but should keep this in mind for now. + item._Item__attributes = {**base.attributes, **item.attributes} + + # Expunge the item form the session. This is required to have different Abyssal / Base combinations loaded in memory. + # Without expunging it, once one Abyssal Web is created, SQLAlchmey will use it for all others. We don't want this, + # we want to generate a completely new object to work with + gamedata_session.expunge(item) + return item + @cachedQuery(1, "lookfor") def getItems(lookfor, eager=None): """ diff --git a/eos/db/migrations/upgrade28.py b/eos/db/migrations/upgrade28.py new file mode 100644 index 000000000..2ca735414 --- /dev/null +++ b/eos/db/migrations/upgrade28.py @@ -0,0 +1,18 @@ +""" +Migration 28 + +- adds baseItemID and mutaplasmidID to modules table +""" +import sqlalchemy + + +def upgrade(saveddata_engine): + try: + saveddata_engine.execute("SELECT baseItemID FROM modules LIMIT 1") + except sqlalchemy.exc.DatabaseError: + saveddata_engine.execute("ALTER TABLE modules ADD COLUMN baseItemID INT;") + + try: + saveddata_engine.execute("SELECT mutaplasmidID FROM modules LIMIT 1") + except sqlalchemy.exc.DatabaseError: + saveddata_engine.execute("ALTER TABLE modules ADD COLUMN mutaplasmidID INT;") diff --git a/eos/db/saveddata/module.py b/eos/db/saveddata/module.py index f84de348e..ae503f120 100644 --- a/eos/db/saveddata/module.py +++ b/eos/db/saveddata/module.py @@ -30,6 +30,8 @@ modules_table = Table("modules", saveddata_meta, Column("ID", Integer, primary_key=True), Column("fitID", Integer, ForeignKey("fits.ID"), nullable=False, index=True), Column("itemID", Integer, nullable=True), + Column("baseItemID", Integer, nullable=True), + Column("mutaplasmidID", Integer, nullable=True), Column("dummySlot", Integer, nullable=True, default=None), Column("chargeID", Integer), Column("state", Integer, CheckConstraint("state >= -1"), CheckConstraint("state <= 2")), diff --git a/eos/saveddata/module.py b/eos/saveddata/module.py index 560caddf5..cd68e58fe 100644 --- a/eos/saveddata/module.py +++ b/eos/saveddata/module.py @@ -76,6 +76,7 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut): def __init__(self, item): """Initialize a module from the program""" self.__item = item + self.__baseItem = None if item is not None and self.isInvalid: raise ValueError("Passed item is not a Module") @@ -90,6 +91,7 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut): def init(self): """Initialize a module from the database and validate""" self.__item = None + self.__baseItem = None self.__charge = None # we need this early if module is invalid and returns early @@ -101,6 +103,13 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut): pyfalog.error("Item (id: {0}) does not exist", self.itemID) return + if self.baseItemID: + self.__item = eos.db.getItemWithBaseItemAttribute(self.itemID, self.baseItemID) + self.__baseItem = eos.db.getItem(self.baseItemID) + if self.__baseItem is None: + pyfalog.error("Base Item (id: {0}) does not exist", self.itemID) + return + if self.isInvalid: pyfalog.error("Item (id: {0}) is not a Module", self.itemID) return @@ -128,10 +137,11 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut): if self.__item: self.__itemModifiedAttributes.original = self.__item.attributes - self.__itemModifiedAttributes.mutators = self.mutators self.__itemModifiedAttributes.overrides = self.__item.overrides self.__hardpoint = self.__calculateHardpoint(self.__item) self.__slot = self.__calculateSlot(self.__item) + self.__itemModifiedAttributes.mutators = self.mutators + if self.__charge: self.__chargeModifiedAttributes.original = self.__charge.attributes self.__chargeModifiedAttributes.overrides = self.__charge.overrides