From 3521b3887d47057a86901b3f4d36ddacea01a1d6 Mon Sep 17 00:00:00 2001 From: blitzmann Date: Wed, 9 Sep 2015 19:08:38 -0400 Subject: [PATCH 01/15] Separate characters from Fit relationship and create new character session. At this point, changes to characters do not persist. Efforts must still be made to focus on creating a character save function as well as ensure characters that are dirty are loaded correctly for fresh fits. --- eos/db/__init__.py | 6 ++++-- eos/db/saveddata/fit.py | 3 --- eos/db/saveddata/queries.py | 16 +++++++--------- eos/saveddata/fit.py | 4 +++- 4 files changed, 14 insertions(+), 15 deletions(-) diff --git a/eos/db/__init__.py b/eos/db/__init__.py index 8867d63c5..55d261645 100644 --- a/eos/db/__init__.py +++ b/eos/db/__init__.py @@ -58,7 +58,9 @@ if saveddata_connectionstring is not None: saveddata_meta = MetaData() saveddata_meta.bind = saveddata_engine - saveddata_session = sessionmaker(bind=saveddata_engine, autoflush=False, expire_on_commit=False)() + base = sessionmaker(bind=saveddata_engine, autoflush=False, expire_on_commit=False) + saveddata_session = base() + character_session = base() # Lock controlling any changes introduced to session sd_lock = threading.Lock() @@ -75,7 +77,7 @@ from eos.db.saveddata.queries import getUser, getCharacter, getFit, getFitsWithS getFitList, getFleetList, getFleet, save, remove, commit, add, \ getCharactersForUser, getMiscData, getSquadsIDsWithFitID, getWing, \ getSquad, getBoosterFits, getProjectedFits, getTargetResistsList, getTargetResists,\ - clearPrices, countAllFits + clearPrices, countAllFits, getCharacter #If using in memory saveddata, you'll want to reflect it so the data structure is good. if config.saveddata_connectionstring == "sqlite:///:memory:": diff --git a/eos/db/saveddata/fit.py b/eos/db/saveddata/fit.py index a40d8c7fb..c55a76551 100644 --- a/eos/db/saveddata/fit.py +++ b/eos/db/saveddata/fit.py @@ -138,9 +138,6 @@ mapper(Fit, fits_table, primaryjoin=fitImplants_table.c.fitID == fits_table.c.ID, secondaryjoin=fitImplants_table.c.implantID == Implant.ID, secondary=fitImplants_table), - "_Fit__character": relation( - Character, - backref="fits"), "_Fit__damagePattern": relation(DamagePattern), "_Fit__targetResists": relation(TargetResists), "projectedOnto": relationship( diff --git a/eos/db/saveddata/queries.py b/eos/db/saveddata/queries.py index b71f6b98a..5c10b6b9b 100644 --- a/eos/db/saveddata/queries.py +++ b/eos/db/saveddata/queries.py @@ -18,7 +18,7 @@ #=============================================================================== from eos.db.util import processEager, processWhere -from eos.db import saveddata_session, sd_lock +from eos.db import saveddata_session, character_session, sd_lock from eos.types import User, Character, Fit, Price, DamagePattern, Fleet, MiscData, Wing, Squad, TargetResists from eos.db.saveddata.fleet import squadmembers_table from eos.db.saveddata.fit import projectedFits_table @@ -144,16 +144,14 @@ def getUser(lookfor, eager=None): def getCharacter(lookfor, eager=None): if isinstance(lookfor, int): if eager is None: - with sd_lock: - character = saveddata_session.query(Character).get(lookfor) + character = character_session.query(Character).get(lookfor) else: eager = processEager(eager) - with sd_lock: - character = saveddata_session.query(Character).options(*eager).filter(Character.ID == lookfor).first() + character = character_session.query(Character).options(*eager).filter(Character.ID == lookfor).first() + print character elif isinstance(lookfor, basestring): eager = processEager(eager) - with sd_lock: - character = saveddata_session.query(Character).options(*eager).filter(Character.name == lookfor).first() + character = character_session.query(Character).options(*eager).filter(Character.name == lookfor).first() else: raise TypeError("Need integer or string as argument") return character @@ -161,14 +159,14 @@ def getCharacter(lookfor, eager=None): def getCharacterList(eager=None): eager = processEager(eager) with sd_lock: - characters = saveddata_session.query(Character).options(*eager).all() + characters = character_session.query(Character).options(*eager).all() return characters def getCharactersForUser(lookfor, eager=None): if isinstance(lookfor, int): eager = processEager(eager) with sd_lock: - characters = saveddata_session.query(Character).options(*eager).filter(Character.ownerID == lookfor).all() + characters = character_session.query(Character).options(*eager).filter(Character.ownerID == lookfor).all() else: raise TypeError("Need integer as argument") return characters diff --git a/eos/saveddata/fit.py b/eos/saveddata/fit.py index 29df2dbcf..5237067a2 100644 --- a/eos/saveddata/fit.py +++ b/eos/saveddata/fit.py @@ -97,7 +97,7 @@ class Fit(object): self.__mode = self.ship.validateModeItem(item) else: self.__mode = self.ship.validateModeItem(None) - + self.__character = eos.db.saveddata.queries.getCharacter(self.characterID) self.build() def build(self): @@ -166,6 +166,8 @@ class Fit(object): @character.setter def character(self, char): self.__character = char + if char is not None: + self.characterID = char.ID @property def ship(self): From ca34217f3bb09cf0c07f2af94f563cc09a6c50e7 Mon Sep 17 00:00:00 2001 From: blitzmann Date: Wed, 9 Sep 2015 20:33:15 -0400 Subject: [PATCH 02/15] Added character saving. Deleting characters is broken for now due to using wrong session, but I believe creating character works. Further tests needed --- eos/config.py | 2 +- eos/db/saveddata/queries.py | 1 - eos/saveddata/character.py | 6 +++- .../changeAffectingSkills.py | 1 + gui/mainFrame.py | 8 +++++ gui/mainMenuBar.py | 3 ++ service/character.py | 36 ++++++++++++++++++- 7 files changed, 53 insertions(+), 4 deletions(-) diff --git a/eos/config.py b/eos/config.py index 35e2fe0dc..042605466 100644 --- a/eos/config.py +++ b/eos/config.py @@ -5,7 +5,7 @@ debug = False gamedataCache = True saveddataCache = True gamedata_connectionstring = 'sqlite:///' + unicode(realpath(join(dirname(abspath(__file__)), "..", "staticdata", "eve.db")), sys.getfilesystemencoding()) -saveddata_connectionstring = 'sqlite:///:memory:' +saveddata_connectionstring = 'sqlite:///' + unicode(realpath(join(dirname(abspath(__file__)), "..", "saveddata", "saveddata.db")), sys.getfilesystemencoding()) #Autodetect path, only change if the autodetection bugs out. path = dirname(unicode(__file__, sys.getfilesystemencoding())) diff --git a/eos/db/saveddata/queries.py b/eos/db/saveddata/queries.py index 5c10b6b9b..c67638c1a 100644 --- a/eos/db/saveddata/queries.py +++ b/eos/db/saveddata/queries.py @@ -148,7 +148,6 @@ def getCharacter(lookfor, eager=None): else: eager = processEager(eager) character = character_session.query(Character).options(*eager).filter(Character.ID == lookfor).first() - print character elif isinstance(lookfor, basestring): eager = processEager(eager) character = character_session.query(Character).options(*eager).filter(Character.name == lookfor).first() diff --git a/eos/saveddata/character.py b/eos/saveddata/character.py index 84e5a6763..3b6bd3643 100644 --- a/eos/saveddata/character.py +++ b/eos/saveddata/character.py @@ -21,6 +21,7 @@ from sqlalchemy.orm import validates, reconstructor from eos.effectHandlerHelpers import HandledItem +import eos.db import eos class Character(object): @@ -33,7 +34,6 @@ class Character(object): @classmethod def getSkillList(cls): if cls.__itemList is None: - import eos.db cls.__itemList = eos.db.getItemsByCategory("Skill") return cls.__itemList @@ -150,6 +150,10 @@ class Character(object): def implants(self): return self.__implants + @property + def isDirty(self): + return getattr(self, "dirty", False) + def iterSkills(self): for item in self.getSkillList(): yield self.getSkill(item) diff --git a/gui/builtinContextMenus/changeAffectingSkills.py b/gui/builtinContextMenus/changeAffectingSkills.py index 32530bdb6..6615d42eb 100644 --- a/gui/builtinContextMenus/changeAffectingSkills.py +++ b/gui/builtinContextMenus/changeAffectingSkills.py @@ -94,6 +94,7 @@ class ChangeAffectingSkills(ContextMenu): fitID = self.mainFrame.getActiveFit() self.sFit.changeChar(fitID, self.charID) + wx.PostEvent(self.mainFrame, GE.CharListUpdated()) wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=fitID)) ChangeAffectingSkills.register() diff --git a/gui/mainFrame.py b/gui/mainFrame.py index 83bff353a..90eebfac5 100644 --- a/gui/mainFrame.py +++ b/gui/mainFrame.py @@ -418,6 +418,8 @@ class MainFrame(wx.Frame): self.Bind(wx.EVT_MENU, self.goWiki, id = menuBar.wikiId) # EVE Forums self.Bind(wx.EVT_MENU, self.goForums, id = menuBar.forumId) + # Save current character + self.Bind(wx.EVT_MENU, self.saveChar, id = menuBar.saveCharId) #Clipboard exports self.Bind(wx.EVT_MENU, self.exportToClipboard, id=wx.ID_COPY) @@ -482,6 +484,12 @@ class MainFrame(wx.Frame): atable = wx.AcceleratorTable(actb) self.SetAcceleratorTable(atable) + def saveChar(self, event): + sChr = service.Character.getInstance() + charID = self.charSelection.getActiveCharacter() + sChr.saveCharacter(charID) + wx.PostEvent(self, GE.CharListUpdated()) + def AdditionsTabSelect(self, event): selTab = self.additionsSelect.index(event.GetId()) diff --git a/gui/mainMenuBar.py b/gui/mainMenuBar.py index 06e1dfabf..756bec594 100644 --- a/gui/mainMenuBar.py +++ b/gui/mainMenuBar.py @@ -36,6 +36,7 @@ class MainMenuBar(wx.MenuBar): self.exportHtmlId = wx.NewId() self.wikiId = wx.NewId() self.forumId = wx.NewId() + self.saveCharId = wx.NewId() self.mainFrame = gui.mainFrame.MainFrame.getInstance() @@ -70,6 +71,8 @@ class MainMenuBar(wx.MenuBar): pasteText = "&From Clipboard" + ("\tCTRL+V" if 'wxMSW' in wx.PlatformInfo else "") editMenu.Append(wx.ID_COPY, copyText, "Export a fit to the clipboard") editMenu.Append(wx.ID_PASTE, pasteText, "Import a fit from the clipboard") + editMenu.AppendSeparator() + editMenu.Append(self.saveCharId, "Save Character") # Character menu windowMenu = wx.Menu() diff --git a/service/character.py b/service/character.py index 1f2c78900..0b4ffdef7 100644 --- a/service/character.py +++ b/service/character.py @@ -191,12 +191,46 @@ class Character(object): # Flush incase all0 & all5 weren't in the db yet eos.db.commit() sFit = service.Fit.getInstance() - return map(lambda c: (c.ID, c.name, c == sFit.character), eos.db.getCharacterList()) + + for thing in eos.db.character_session.dirty: + if isinstance(thing, eos.types.Skill): + if not thing.character.isDirty: + thing.character.dirty = True + + return map(lambda c: (c.ID, c.name if not c.isDirty else "{} *".format(c.name), c == sFit.character), eos.db.getCharacterList()) def getCharacter(self, charID): char = eos.db.getCharacter(charID) return char + def saveCharacter(self, charID): + """ + Saves the edited character. + + I feel like there should be a better way to specifically tell + SQLAlchemy to only flush and commit a specific object. But this magic + function doesn't seem to exist, so instead we basically gather a list + of our characters, expunge the entire session, and then add the + edited character all by themself. After committing, we simply add our + characters back to the session. + + Without doing this, this would save all edited characters, which is + not something that should happen. + """ + + char = eos.db.getCharacter(charID) + if not char.isDirty: + return + charList = eos.db.getCharacterList() + + eos.db.character_session.expunge_all() + eos.db.character_session.add(char) + eos.db.character_session.commit() + char.dirty = False + + for char in charList: + eos.db.character_session.add(char) + def getSkillGroups(self): cat = eos.db.getCategory(16) groups = [] From c72f049ccdf101a88db8814a8d73e0f670887a12 Mon Sep 17 00:00:00 2001 From: blitzmann Date: Wed, 9 Sep 2015 22:41:44 -0400 Subject: [PATCH 03/15] Fix character rename, delete, and copy. --- service/character.py | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/service/character.py b/service/character.py index 0b4ffdef7..5ad7c0f25 100644 --- a/service/character.py +++ b/service/character.py @@ -219,8 +219,6 @@ class Character(object): """ char = eos.db.getCharacter(charID) - if not char.isDirty: - return charList = eos.db.getCharacterList() eos.db.character_session.expunge_all() @@ -260,11 +258,15 @@ class Character(object): def rename(self, charID, newName): char = eos.db.getCharacter(charID) char.name = newName - eos.db.commit() + self.saveCharacter(charID) def new(self): + #@todo: seems setting skills on a new character doesn't trigger the dirty setting. Probably goes for character copy too char = eos.types.Character("New Character") - eos.db.save(char) + eos.db.character_session.add(char) + # We can flush this single character to the DB to get an ID. It will be + # committed with the rename function + eos.db.character_session.flush([char]) return char.ID def getCharName(self, charID): @@ -273,12 +275,15 @@ class Character(object): def copy(self, charID): char = eos.db.getCharacter(charID) newChar = copy.deepcopy(char) - eos.db.save(newChar) + eos.db.character_session.add(newChar) + eos.db.character_session.flush([newChar]) return newChar.ID def delete(self, charID): + # It is easier to remove the character from the main session, so banish + # it from the character session and include it over there. char = eos.db.getCharacter(charID) - eos.db.commit() + eos.db.character_session.expunge(char) eos.db.remove(char) def getApiDetails(self, charID): From 89c011d37ef48c87917cd832cf8b64be9829cbb4 Mon Sep 17 00:00:00 2001 From: blitzmann Date: Sat, 12 Sep 2015 10:30:51 -0400 Subject: [PATCH 04/15] Minor modifications. --- eos/saveddata/character.py | 40 ++++++++++++++++++-------------------- gui/characterEditor.py | 2 +- service/character.py | 14 +++---------- 3 files changed, 23 insertions(+), 33 deletions(-) diff --git a/eos/saveddata/character.py b/eos/saveddata/character.py index 3b6bd3643..d9a9d5d59 100644 --- a/eos/saveddata/character.py +++ b/eos/saveddata/character.py @@ -25,8 +25,6 @@ import eos.db import eos class Character(object): - __all5 = None - __all0 = None __itemList = None __itemIDMap = None __itemNameMap = None @@ -66,29 +64,30 @@ class Character(object): @classmethod def getAll5(cls): - if cls.__all5 is None: - import eos.db - all5 = eos.db.getCharacter("All 5") - if all5 is None: - all5 = Character("All 5") - all5.defaultLevel = 5 - eos.db.add(all5) + all5 = eos.db.getCharacter("All 5") - cls.__all5 = all5 - return cls.__all5 + if all5 is None: + # We do not have to be afraid of committing here and saving + # edited character data. If this ever runs, it will be during the + # get character list phase when pyfa first starts + all5 = Character("All 5") + all5.defaultLevel = 5 + eos.db.character_session.add(all5) + eos.db.character_session.commit() + + return all5 @classmethod def getAll0(cls): - if cls.__all0 is None: - import eos.db - all0 = eos.db.getCharacter("All 0") - if all0 is None: - all0 = Character("All 0") - all0.defaultLevel = None - eos.db.add(all0) + all0 = eos.db.getCharacter("All 0") - cls.__all0 = all0 - return cls.__all0 + if all0 is None: + all0 = Character("All 0") + all0.defaultLevel = None + eos.db.character_session.add(all0) + eos.db.character_session.commit() + + return all0 def __init__(self, name): self.name = name @@ -109,7 +108,6 @@ class Character(object): del self.__skills[:] self.__skillIdMap.clear() for skillRow in skills: - self.addSkill(Skill(skillRow["typeID"], skillRow["level"])) @property diff --git a/gui/characterEditor.py b/gui/characterEditor.py index f036313db..1d3a2c400 100644 --- a/gui/characterEditor.py +++ b/gui/characterEditor.py @@ -652,7 +652,7 @@ class APIView (wx.Panel): sChar = service.Character.getInstance() try: - list = sChar.charList(self.Parent.Parent.getActiveCharacter(), self.inputID.GetLineText(0), self.inputKey.GetLineText(0)) + list = sChar.apiCharList(self.Parent.Parent.getActiveCharacter(), self.inputID.GetLineText(0), self.inputKey.GetLineText(0)) except service.network.AuthenticationError, e: self.stStatus.SetLabel("Authentication failure. Please check keyID and vCode combination.") except service.network.TimeoutError, e: diff --git a/service/character.py b/service/character.py index 5ad7c0f25..5b66224d8 100644 --- a/service/character.py +++ b/service/character.py @@ -171,25 +171,19 @@ class Character(object): thread.start() def all0(self): - all0 = eos.types.Character.getAll0() - eos.db.commit() - return all0 + return eos.types.Character.getAll0() def all0ID(self): return self.all0().ID def all5(self): - all5 = eos.types.Character.getAll5() - eos.db.commit() - return all5 + return eos.types.Character.getAll5() def all5ID(self): return self.all5().ID def getCharacterList(self): baseChars = [eos.types.Character.getAll0(), eos.types.Character.getAll5()] - # Flush incase all0 & all5 weren't in the db yet - eos.db.commit() sFit = service.Fit.getInstance() for thing in eos.db.character_session.dirty: @@ -298,7 +292,7 @@ class Character(object): id, key, default, _ = self.getApiDetails(charID) return id is not "" and key is not "" and default is not "" - def charList(self, charID, userID, apiKey): + def apiCharList(self, charID, userID, apiKey): char = eos.db.getCharacter(charID) char.apiID = userID @@ -345,8 +339,6 @@ class Character(object): else: skill.level = level - eos.db.commit() - def addImplant(self, charID, itemID): char = eos.db.getCharacter(charID) implant = eos.types.Implant(eos.db.getItem(itemID)) From 0b9cdaa9440d9e7163ce553e594299a38e5a5e4a Mon Sep 17 00:00:00 2001 From: blitzmann Date: Sat, 12 Sep 2015 10:59:44 -0400 Subject: [PATCH 05/15] Change the way skills are loaded. We now load all skills when character is created, instead of just creating character and loading skills on demand. This fixes a bug in which new characters were not saving their skills to the database. --- eos/saveddata/character.py | 20 +++++++++----------- service/character.py | 14 ++++++++------ 2 files changed, 17 insertions(+), 17 deletions(-) diff --git a/eos/saveddata/character.py b/eos/saveddata/character.py index d9a9d5d59..40c7dfacf 100644 --- a/eos/saveddata/character.py +++ b/eos/saveddata/character.py @@ -70,8 +70,7 @@ class Character(object): # We do not have to be afraid of committing here and saving # edited character data. If this ever runs, it will be during the # get character list phase when pyfa first starts - all5 = Character("All 5") - all5.defaultLevel = 5 + all5 = Character("All 5", 5) eos.db.character_session.add(all5) eos.db.character_session.commit() @@ -83,18 +82,21 @@ class Character(object): if all0 is None: all0 = Character("All 0") - all0.defaultLevel = None eos.db.character_session.add(all0) eos.db.character_session.commit() return all0 - def __init__(self, name): + def __init__(self, name, defaultLevel=None): self.name = name self.__owner = None - self.defaultLevel = None + self.defaultLevel = defaultLevel self.__skills = [] self.__skillIdMap = {} + + for item in self.getSkillList(): + self.addSkill(Skill(item.ID, self.defaultLevel)) + self.__implants = eos.saveddata.fit.HandledImplantBoosterList() self.apiKey = None @@ -135,11 +137,7 @@ class Character(object): skill = self.__skillIdMap.get(item.ID) if skill is None: - if self.defaultLevel is None: - skill = Skill(item, 0, False, False) - else: - skill = Skill(item, self.defaultLevel, False, True) - + skill = Skill(item, self.defaultLevel, False, True) self.addSkill(skill) return skill @@ -201,7 +199,7 @@ class Character(object): else: return val class Skill(HandledItem): - def __init__(self, item, level = 0, ro = False, learned = True): + def __init__(self, item, level=0, ro=False, learned=True): self.__item = item if not isinstance(item, int) else None self.itemID = item.ID if not isinstance(item, int) else item self.__level = level if learned else None diff --git a/service/character.py b/service/character.py index 5b66224d8..aab3089c2 100644 --- a/service/character.py +++ b/service/character.py @@ -190,6 +190,8 @@ class Character(object): if isinstance(thing, eos.types.Skill): if not thing.character.isDirty: thing.character.dirty = True + elif thing in eos.db.character_session.dirty: + thing.dirty = True return map(lambda c: (c.ID, c.name if not c.isDirty else "{} *".format(c.name), c == sFit.character), eos.db.getCharacterList()) @@ -249,10 +251,8 @@ class Character(object): skill = eos.db.getCharacter(charID).getSkill(skillID) return skill.level if skill.learned else "Not learned" - def rename(self, charID, newName): - char = eos.db.getCharacter(charID) - char.name = newName - self.saveCharacter(charID) + def getCharName(self, charID): + return eos.db.getCharacter(charID).name def new(self): #@todo: seems setting skills on a new character doesn't trigger the dirty setting. Probably goes for character copy too @@ -263,8 +263,10 @@ class Character(object): eos.db.character_session.flush([char]) return char.ID - def getCharName(self, charID): - return eos.db.getCharacter(charID).name + def rename(self, charID, newName): + char = eos.db.getCharacter(charID) + char.name = newName + self.saveCharacter(charID) def copy(self, charID): char = eos.db.getCharacter(charID) From 466327006721b43c75fbb1a7bc5f39f0860b2ff6 Mon Sep 17 00:00:00 2001 From: blitzmann Date: Sat, 12 Sep 2015 11:02:32 -0400 Subject: [PATCH 06/15] Since we now have a list of skills by default on our character, remove the iterSkills iterator which helped fetch skills that were not previously loaded --- eos/saveddata/character.py | 21 +++++++++++---------- eos/saveddata/fleet.py | 2 +- 2 files changed, 12 insertions(+), 11 deletions(-) diff --git a/eos/saveddata/character.py b/eos/saveddata/character.py index 40c7dfacf..051fdb7c8 100644 --- a/eos/saveddata/character.py +++ b/eos/saveddata/character.py @@ -120,6 +120,10 @@ class Character(object): def owner(self, owner): self.__owner = owner + @property + def skills(self): + return self.__skills + def addSkill(self, skill): self.__skills.append(skill) self.__skillIdMap[skill.itemID] = skill @@ -150,40 +154,37 @@ class Character(object): def isDirty(self): return getattr(self, "dirty", False) - def iterSkills(self): - for item in self.getSkillList(): - yield self.getSkill(item) - def filteredSkillIncrease(self, filter, *args, **kwargs): - for element in self.iterSkills(): + for element in self.skills: if filter(element): element.increaseItemAttr(*args, **kwargs) def filteredSkillMultiply(self, filter, *args, **kwargs): - for element in self.iterSkills(): + for element in self.skills: if filter(element): element.multiplyItemAttr(*args, **kwargs) def filteredSkillBoost(self, filter, *args, **kwargs): - for element in self.iterSkills(): + for element in self.skills: if filter(element): element.boostItemAttr(*args, **kwargs) def calculateModifiedAttributes(self, fit, runTime, forceProjected = False): if forceProjected: return - for skill in self.iterSkills(): + for skill in self.skills: fit.register(skill) skill.calculateModifiedAttributes(fit, runTime) def clear(self): - for skill in self.iterSkills(): + for skill in self.skills: skill.clear() def __deepcopy__(self, memo): copy = Character("%s copy" % self.name) copy.apiKey = self.apiKey copy.apiID = self.apiID - for skill in self.iterSkills(): + + for skill in self.skills: copy.addSkill(Skill(skill.itemID, skill.level, False, skill.learned)) return copy diff --git a/eos/saveddata/fleet.py b/eos/saveddata/fleet.py index f1445ae3a..c1db0e97e 100644 --- a/eos/saveddata/fleet.py +++ b/eos/saveddata/fleet.py @@ -247,7 +247,7 @@ class Store(object): dict.clear() # Go through everything which can be used as gang booster - for thing in chain(fitBooster.modules, fitBooster.implants, fitBooster.character.iterSkills(), (fitBooster.ship,)): + for thing in chain(fitBooster.modules, fitBooster.implants, fitBooster.character.skills, (fitBooster.ship,)): if thing.item is None: continue for effect in thing.item.effects.itervalues(): From 1402ceec6351380eb56119ebac9e58bec77beca4 Mon Sep 17 00:00:00 2001 From: blitzmann Date: Sat, 12 Sep 2015 13:15:59 -0400 Subject: [PATCH 07/15] Fix API stuff. This should theoretically work, but it's untested. --- service/character.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/service/character.py b/service/character.py index aab3089c2..30f3d7a32 100644 --- a/service/character.py +++ b/service/character.py @@ -255,7 +255,6 @@ class Character(object): return eos.db.getCharacter(charID).name def new(self): - #@todo: seems setting skills on a new character doesn't trigger the dirty setting. Probably goes for character copy too char = eos.types.Character("New Character") eos.db.character_session.add(char) # We can flush this single character to the DB to get an ID. It will be @@ -326,12 +325,12 @@ class Character(object): sheet = auth.character(charID).CharacterSheet() dbChar.apiUpdateCharSheet(sheet.skills) - eos.db.commit() + self.saveCharacter(dbChar.ID) def apiUpdateCharSheet(self, charID, skills): char = eos.db.getCharacter(charID) char.apiUpdateCharSheet(skills) - eos.db.commit() + self.saveCharacter(char.ID) def changeLevel(self, charID, skillID, level): char = eos.db.getCharacter(charID) From 1584586fd2944e3d658a9037d39de58a94cb52b7 Mon Sep 17 00:00:00 2001 From: blitzmann Date: Sun, 13 Sep 2015 16:51:36 -0400 Subject: [PATCH 08/15] Try a different approach to temp skill levels --- eos/db/saveddata/queries.py | 10 +++++----- eos/saveddata/character.py | 12 ++++++++++-- service/character.py | 36 ++---------------------------------- 3 files changed, 17 insertions(+), 41 deletions(-) diff --git a/eos/db/saveddata/queries.py b/eos/db/saveddata/queries.py index c67638c1a..718da544a 100644 --- a/eos/db/saveddata/queries.py +++ b/eos/db/saveddata/queries.py @@ -144,13 +144,13 @@ def getUser(lookfor, eager=None): def getCharacter(lookfor, eager=None): if isinstance(lookfor, int): if eager is None: - character = character_session.query(Character).get(lookfor) + character = saveddata_session.query(Character).get(lookfor) else: eager = processEager(eager) - character = character_session.query(Character).options(*eager).filter(Character.ID == lookfor).first() + character = saveddata_session.query(Character).options(*eager).filter(Character.ID == lookfor).first() elif isinstance(lookfor, basestring): eager = processEager(eager) - character = character_session.query(Character).options(*eager).filter(Character.name == lookfor).first() + character = saveddata_session.query(Character).options(*eager).filter(Character.name == lookfor).first() else: raise TypeError("Need integer or string as argument") return character @@ -158,14 +158,14 @@ def getCharacter(lookfor, eager=None): def getCharacterList(eager=None): eager = processEager(eager) with sd_lock: - characters = character_session.query(Character).options(*eager).all() + characters = saveddata_session.query(Character).options(*eager).all() return characters def getCharactersForUser(lookfor, eager=None): if isinstance(lookfor, int): eager = processEager(eager) with sd_lock: - characters = character_session.query(Character).options(*eager).filter(Character.ownerID == lookfor).all() + characters = saveddata_session.query(Character).options(*eager).filter(Character.ownerID == lookfor).all() else: raise TypeError("Need integer as argument") return characters diff --git a/eos/saveddata/character.py b/eos/saveddata/character.py index 051fdb7c8..9ede83232 100644 --- a/eos/saveddata/character.py +++ b/eos/saveddata/character.py @@ -154,6 +154,12 @@ class Character(object): def isDirty(self): return getattr(self, "dirty", False) + def saveLevels(self): + for skill in self.skills: + skill.__level = skill.level + self.dirty = False + eos.db.commit() + def filteredSkillIncrease(self, filter, *args, **kwargs): for element in self.skills: if filter(element): @@ -204,6 +210,7 @@ class Skill(HandledItem): self.__item = item if not isinstance(item, int) else None self.itemID = item.ID if not isinstance(item, int) else item self.__level = level if learned else None + self.activeLevel = self.__level self.commandBonus = 0 self.build(ro) @@ -222,7 +229,7 @@ class Skill(HandledItem): @property def level(self): - return self.__level or 0 + return self.activeLevel or 0 @level.setter def level(self, level): @@ -232,7 +239,8 @@ class Skill(HandledItem): if hasattr(self, "_Skill__ro") and self.__ro == True: raise ReadOnlyException() - self.__level = level + self.activeLevel = level + self.character.dirty = True @property def item(self): diff --git a/service/character.py b/service/character.py index 30f3d7a32..ebe65c899 100644 --- a/service/character.py +++ b/service/character.py @@ -186,13 +186,6 @@ class Character(object): baseChars = [eos.types.Character.getAll0(), eos.types.Character.getAll5()] sFit = service.Fit.getInstance() - for thing in eos.db.character_session.dirty: - if isinstance(thing, eos.types.Skill): - if not thing.character.isDirty: - thing.character.dirty = True - elif thing in eos.db.character_session.dirty: - thing.dirty = True - return map(lambda c: (c.ID, c.name if not c.isDirty else "{} *".format(c.name), c == sFit.character), eos.db.getCharacterList()) def getCharacter(self, charID): @@ -200,30 +193,8 @@ class Character(object): return char def saveCharacter(self, charID): - """ - Saves the edited character. - - I feel like there should be a better way to specifically tell - SQLAlchemy to only flush and commit a specific object. But this magic - function doesn't seem to exist, so instead we basically gather a list - of our characters, expunge the entire session, and then add the - edited character all by themself. After committing, we simply add our - characters back to the session. - - Without doing this, this would save all edited characters, which is - not something that should happen. - """ - char = eos.db.getCharacter(charID) - charList = eos.db.getCharacterList() - - eos.db.character_session.expunge_all() - eos.db.character_session.add(char) - eos.db.character_session.commit() - char.dirty = False - - for char in charList: - eos.db.character_session.add(char) + char.saveLevels() def getSkillGroups(self): cat = eos.db.getCategory(16) @@ -256,10 +227,7 @@ class Character(object): def new(self): char = eos.types.Character("New Character") - eos.db.character_session.add(char) - # We can flush this single character to the DB to get an ID. It will be - # committed with the rename function - eos.db.character_session.flush([char]) + eos.db.save(char) return char.ID def rename(self, charID, newName): From 2c7bfd14b6b35d24b6a02aa3614fc45248fb0176 Mon Sep 17 00:00:00 2001 From: blitzmann Date: Sun, 13 Sep 2015 16:53:42 -0400 Subject: [PATCH 09/15] Revert "Separate characters from Fit relationship..." This reverts commit 3521b3887d47057a86901b3f4d36ddacea01a1d6. Conflicts: eos/db/saveddata/queries.py --- eos/db/__init__.py | 6 ++---- eos/db/saveddata/fit.py | 3 +++ eos/db/saveddata/queries.py | 11 +++++++---- eos/saveddata/fit.py | 4 +--- 4 files changed, 13 insertions(+), 11 deletions(-) diff --git a/eos/db/__init__.py b/eos/db/__init__.py index 55d261645..8867d63c5 100644 --- a/eos/db/__init__.py +++ b/eos/db/__init__.py @@ -58,9 +58,7 @@ if saveddata_connectionstring is not None: saveddata_meta = MetaData() saveddata_meta.bind = saveddata_engine - base = sessionmaker(bind=saveddata_engine, autoflush=False, expire_on_commit=False) - saveddata_session = base() - character_session = base() + saveddata_session = sessionmaker(bind=saveddata_engine, autoflush=False, expire_on_commit=False)() # Lock controlling any changes introduced to session sd_lock = threading.Lock() @@ -77,7 +75,7 @@ from eos.db.saveddata.queries import getUser, getCharacter, getFit, getFitsWithS getFitList, getFleetList, getFleet, save, remove, commit, add, \ getCharactersForUser, getMiscData, getSquadsIDsWithFitID, getWing, \ getSquad, getBoosterFits, getProjectedFits, getTargetResistsList, getTargetResists,\ - clearPrices, countAllFits, getCharacter + clearPrices, countAllFits #If using in memory saveddata, you'll want to reflect it so the data structure is good. if config.saveddata_connectionstring == "sqlite:///:memory:": diff --git a/eos/db/saveddata/fit.py b/eos/db/saveddata/fit.py index c55a76551..a40d8c7fb 100644 --- a/eos/db/saveddata/fit.py +++ b/eos/db/saveddata/fit.py @@ -138,6 +138,9 @@ mapper(Fit, fits_table, primaryjoin=fitImplants_table.c.fitID == fits_table.c.ID, secondaryjoin=fitImplants_table.c.implantID == Implant.ID, secondary=fitImplants_table), + "_Fit__character": relation( + Character, + backref="fits"), "_Fit__damagePattern": relation(DamagePattern), "_Fit__targetResists": relation(TargetResists), "projectedOnto": relationship( diff --git a/eos/db/saveddata/queries.py b/eos/db/saveddata/queries.py index 718da544a..b71f6b98a 100644 --- a/eos/db/saveddata/queries.py +++ b/eos/db/saveddata/queries.py @@ -18,7 +18,7 @@ #=============================================================================== from eos.db.util import processEager, processWhere -from eos.db import saveddata_session, character_session, sd_lock +from eos.db import saveddata_session, sd_lock from eos.types import User, Character, Fit, Price, DamagePattern, Fleet, MiscData, Wing, Squad, TargetResists from eos.db.saveddata.fleet import squadmembers_table from eos.db.saveddata.fit import projectedFits_table @@ -144,13 +144,16 @@ def getUser(lookfor, eager=None): def getCharacter(lookfor, eager=None): if isinstance(lookfor, int): if eager is None: - character = saveddata_session.query(Character).get(lookfor) + with sd_lock: + character = saveddata_session.query(Character).get(lookfor) else: eager = processEager(eager) - character = saveddata_session.query(Character).options(*eager).filter(Character.ID == lookfor).first() + with sd_lock: + character = saveddata_session.query(Character).options(*eager).filter(Character.ID == lookfor).first() elif isinstance(lookfor, basestring): eager = processEager(eager) - character = saveddata_session.query(Character).options(*eager).filter(Character.name == lookfor).first() + with sd_lock: + character = saveddata_session.query(Character).options(*eager).filter(Character.name == lookfor).first() else: raise TypeError("Need integer or string as argument") return character diff --git a/eos/saveddata/fit.py b/eos/saveddata/fit.py index 5237067a2..29df2dbcf 100644 --- a/eos/saveddata/fit.py +++ b/eos/saveddata/fit.py @@ -97,7 +97,7 @@ class Fit(object): self.__mode = self.ship.validateModeItem(item) else: self.__mode = self.ship.validateModeItem(None) - self.__character = eos.db.saveddata.queries.getCharacter(self.characterID) + self.build() def build(self): @@ -166,8 +166,6 @@ class Fit(object): @character.setter def character(self, char): self.__character = char - if char is not None: - self.characterID = char.ID @property def ship(self): From 045031e8ae62d431bffd9cd9214e80564d9bcb09 Mon Sep 17 00:00:00 2001 From: blitzmann Date: Sun, 13 Sep 2015 16:55:32 -0400 Subject: [PATCH 10/15] Revert "Fix character rename, delete, and copy." This reverts commit c72f049ccdf101a88db8814a8d73e0f670887a12. Conflicts: service/character.py --- service/character.py | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/service/character.py b/service/character.py index ebe65c899..06e2c43c9 100644 --- a/service/character.py +++ b/service/character.py @@ -233,20 +233,17 @@ class Character(object): def rename(self, charID, newName): char = eos.db.getCharacter(charID) char.name = newName - self.saveCharacter(charID) + eos.db.commit() def copy(self, charID): char = eos.db.getCharacter(charID) newChar = copy.deepcopy(char) - eos.db.character_session.add(newChar) - eos.db.character_session.flush([newChar]) + eos.db.save(newChar) return newChar.ID def delete(self, charID): - # It is easier to remove the character from the main session, so banish - # it from the character session and include it over there. char = eos.db.getCharacter(charID) - eos.db.character_session.expunge(char) + eos.db.commit() eos.db.remove(char) def getApiDetails(self, charID): From 9ae5cfbab294f4bd72e9a4625e1fcc0073153b2c Mon Sep 17 00:00:00 2001 From: blitzmann Date: Sun, 13 Sep 2015 16:57:22 -0400 Subject: [PATCH 11/15] Revert "Fix API stuff. This should theoretically work, but it's untested." This reverts commit 1402ceec6351380eb56119ebac9e58bec77beca4. --- service/character.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/service/character.py b/service/character.py index 06e2c43c9..3a170ac03 100644 --- a/service/character.py +++ b/service/character.py @@ -226,6 +226,7 @@ class Character(object): return eos.db.getCharacter(charID).name def new(self): + #@todo: seems setting skills on a new character doesn't trigger the dirty setting. Probably goes for character copy too char = eos.types.Character("New Character") eos.db.save(char) return char.ID @@ -290,12 +291,12 @@ class Character(object): sheet = auth.character(charID).CharacterSheet() dbChar.apiUpdateCharSheet(sheet.skills) - self.saveCharacter(dbChar.ID) + eos.db.commit() def apiUpdateCharSheet(self, charID, skills): char = eos.db.getCharacter(charID) char.apiUpdateCharSheet(skills) - self.saveCharacter(char.ID) + eos.db.commit() def changeLevel(self, charID, skillID, level): char = eos.db.getCharacter(charID) From b8d01d5ecb99815d2ea416af2962d8709d00e1db Mon Sep 17 00:00:00 2001 From: blitzmann Date: Sun, 13 Sep 2015 16:59:59 -0400 Subject: [PATCH 12/15] Fix a few things --- eos/saveddata/character.py | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/eos/saveddata/character.py b/eos/saveddata/character.py index 9ede83232..182ad2893 100644 --- a/eos/saveddata/character.py +++ b/eos/saveddata/character.py @@ -71,8 +71,7 @@ class Character(object): # edited character data. If this ever runs, it will be during the # get character list phase when pyfa first starts all5 = Character("All 5", 5) - eos.db.character_session.add(all5) - eos.db.character_session.commit() + eos.db.save(all5) return all5 @@ -82,8 +81,7 @@ class Character(object): if all0 is None: all0 = Character("All 0") - eos.db.character_session.add(all0) - eos.db.character_session.commit() + eos.db.save(all0) return all0 @@ -210,7 +208,6 @@ class Skill(HandledItem): self.__item = item if not isinstance(item, int) else None self.itemID = item.ID if not isinstance(item, int) else item self.__level = level if learned else None - self.activeLevel = self.__level self.commandBonus = 0 self.build(ro) @@ -222,6 +219,7 @@ class Skill(HandledItem): def build(self, ro): self.__ro = ro self.__suppressed = False + self.activeLevel = self.__level @property def learned(self): From f2b4400834c7d6d9b090cff11b20315580673e81 Mon Sep 17 00:00:00 2001 From: blitzmann Date: Sun, 13 Sep 2015 19:25:32 -0400 Subject: [PATCH 13/15] Do auto-save when editing in character editor. --- eos/saveddata/character.py | 20 ++++++++---- gui/characterEditor.py | 64 ++++++++++++++++++++++---------------- gui/characterSelection.py | 1 - service/character.py | 1 - 4 files changed, 52 insertions(+), 34 deletions(-) diff --git a/eos/saveddata/character.py b/eos/saveddata/character.py index 182ad2893..bc98de0f1 100644 --- a/eos/saveddata/character.py +++ b/eos/saveddata/character.py @@ -91,6 +91,7 @@ class Character(object): self.defaultLevel = defaultLevel self.__skills = [] self.__skillIdMap = {} + self.dirtySkills = set() for item in self.getSkillList(): self.addSkill(Skill(item.ID, self.defaultLevel)) @@ -103,6 +104,7 @@ class Character(object): self.__skillIdMap = {} for skill in self.__skills: self.__skillIdMap[skill.itemID] = skill + self.dirtySkills = set() def apiUpdateCharSheet(self, skills): del self.__skills[:] @@ -150,12 +152,12 @@ class Character(object): @property def isDirty(self): - return getattr(self, "dirty", False) + return len(self.dirtySkills) > 0 def saveLevels(self): - for skill in self.skills: - skill.__level = skill.level - self.dirty = False + for skill in self.dirtySkills: + skill.saveLevel() + self.dirtySkills = set() eos.db.commit() def filteredSkillIncrease(self, filter, *args, **kwargs): @@ -221,9 +223,12 @@ class Skill(HandledItem): self.__suppressed = False self.activeLevel = self.__level + def saveLevel(self): + self.__level = self.activeLevel + @property def learned(self): - return self.__level is not None + return self.activeLevel is not None @property def level(self): @@ -238,7 +243,10 @@ class Skill(HandledItem): raise ReadOnlyException() self.activeLevel = level - self.character.dirty = True + if self.activeLevel == self.__level: + self.character.dirtySkills.remove(self) + else: + self.character.dirtySkills.add(self) @property def item(self): diff --git a/gui/characterEditor.py b/gui/characterEditor.py index 1d3a2c400..e94ee98d9 100644 --- a/gui/characterEditor.py +++ b/gui/characterEditor.py @@ -34,6 +34,8 @@ class CharacterEditor(wx.Frame): wx.Frame.__init__ (self, parent, id=wx.ID_ANY, title=u"pyfa: Character Editor", pos=wx.DefaultPosition, size=wx.Size(641, 600), style=wx.DEFAULT_FRAME_STYLE|wx.FRAME_FLOAT_ON_PARENT|wx.TAB_TRAVERSAL) + self.mainFrame = parent + i = wx.IconFromBitmap(bitmapLoader.getBitmap("character_small", "icons")) self.SetIcon(i) @@ -45,8 +47,6 @@ class CharacterEditor(wx.Frame): self.navSizer = wx.BoxSizer(wx.HORIZONTAL) sChar = service.Character.getInstance() - charList = sChar.getCharacterList() - charList.sort(key=lambda t: t[1]) self.btnSave = wx.Button(self, wx.ID_SAVE) self.btnSave.Hide() @@ -56,14 +56,17 @@ class CharacterEditor(wx.Frame): self.characterRename.Hide() self.characterRename.Bind(wx.EVT_TEXT_ENTER, self.processRename) - self.skillTreeChoice = wx.Choice(self, wx.ID_ANY, style=0) + self.charChoice = wx.Choice(self, wx.ID_ANY, style=0) + self.navSizer.Add(self.charChoice, 1, wx.ALL | wx.EXPAND, 5) + self.refreshCharacterList() + + ''' for id, name, active in charList: - i = self.skillTreeChoice.Append(name, id) + i = self.charChoice.Append(name, id) if active: - self.skillTreeChoice.SetSelection(i) - - self.navSizer.Add(self.skillTreeChoice, 1, wx.ALL | wx.EXPAND, 5) + self.charChoice.SetSelection(i) + ''' buttons = (("new", wx.ART_NEW), ("rename", bitmapLoader.getBitmap("rename", "icons")), @@ -128,7 +131,16 @@ class CharacterEditor(wx.Frame): self.registerEvents() - self.mainFrame = gui.mainFrame.MainFrame.getInstance() + + def refreshCharacterList(self, event=None): + sChar = service.Character.getInstance() + charList = sChar.getCharacterList() + self.charChoice.Clear() + + for id, name, active in charList: + i = self.charChoice.Append(name, id) + if active: + self.charChoice.SetSelection(i) def editingFinished(self, event): del self.disableWin @@ -137,7 +149,8 @@ class CharacterEditor(wx.Frame): def registerEvents(self): self.Bind(wx.EVT_CLOSE, self.closeEvent) - self.skillTreeChoice.Bind(wx.EVT_CHOICE, self.charChanged) + self.Bind(GE.CHAR_LIST_UPDATED, self.refreshCharacterList) + self.charChoice.Bind(wx.EVT_CHOICE, self.charChanged) def closeEvent(self, event): del self.disableWin @@ -182,14 +195,14 @@ class CharacterEditor(wx.Frame): event.Skip() def getActiveCharacter(self): - selection = self.skillTreeChoice.GetCurrentSelection() - return self.skillTreeChoice.GetClientData(selection) if selection is not None else None + selection = self.charChoice.GetCurrentSelection() + return self.charChoice.GetClientData(selection) if selection is not None else None def new(self, event): sChar = service.Character.getInstance() charID = sChar.new() - id = self.skillTreeChoice.Append(sChar.getCharName(charID), charID) - self.skillTreeChoice.SetSelection(id) + id = self.charChoice.Append(sChar.getCharName(charID), charID) + self.charChoice.SetSelection(id) self.unrestrict() self.btnSave.SetLabel("Create") self.rename(None) @@ -198,9 +211,9 @@ class CharacterEditor(wx.Frame): def rename(self, event): if event is not None: self.btnSave.SetLabel("Rename") - self.skillTreeChoice.Hide() + self.charChoice.Hide() self.characterRename.Show() - self.navSizer.Replace(self.skillTreeChoice, self.characterRename) + self.navSizer.Replace(self.charChoice, self.characterRename) self.characterRename.SetFocus() for btn in (self.btnNew, self.btnCopy, self.btnRename, self.btnDelete): btn.Hide() @@ -225,9 +238,9 @@ class CharacterEditor(wx.Frame): charID = self.getActiveCharacter() sChar.rename(charID, newName) - self.skillTreeChoice.Show() + self.charChoice.Show() self.characterRename.Hide() - self.navSizer.Replace(self.characterRename, self.skillTreeChoice) + self.navSizer.Replace(self.characterRename, self.charChoice) for btn in (self.btnNew, self.btnCopy, self.btnRename, self.btnDelete): btn.Show() self.navSizer.Add(btn, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 2) @@ -235,16 +248,13 @@ class CharacterEditor(wx.Frame): self.navSizer.Remove(self.btnSave) self.btnSave.Hide() self.navSizer.Layout() - selection = self.skillTreeChoice.GetCurrentSelection() - self.skillTreeChoice.Delete(selection) - self.skillTreeChoice.Insert(newName, selection, charID) - self.skillTreeChoice.SetSelection(selection) + self.refreshCharacterList() def copy(self, event): sChar = service.Character.getInstance() charID = sChar.copy(self.getActiveCharacter()) - id = self.skillTreeChoice.Append(sChar.getCharName(charID), charID) - self.skillTreeChoice.SetSelection(id) + id = self.charChoice.Append(sChar.getCharName(charID), charID) + self.charChoice.SetSelection(id) self.unrestrict() self.btnSave.SetLabel("Copy") self.rename(None) @@ -253,9 +263,9 @@ class CharacterEditor(wx.Frame): def delete(self, event): sChar = service.Character.getInstance() sChar.delete(self.getActiveCharacter()) - sel = self.skillTreeChoice.GetSelection() - self.skillTreeChoice.Delete(sel) - self.skillTreeChoice.SetSelection(sel - 1) + sel = self.charChoice.GetSelection() + self.charChoice.Delete(sel) + self.charChoice.SetSelection(sel - 1) newSelection = self.getActiveCharacter() if sChar.getCharName(newSelection) in ("All 0", "All 5"): self.restrict() @@ -383,6 +393,8 @@ class SkillTreeView (wx.Panel): self.skillTreeListCtrl.SetItemText(selection, "Level %d" % level if isinstance(level, int) else level, 1) sChar.changeLevel(charID, skillID, level) + sChar.saveCharacter(charID) + wx.PostEvent(self.Parent.Parent, GE.CharListUpdated()) event.Skip() diff --git a/gui/characterSelection.py b/gui/characterSelection.py index 855882e78..c9328750a 100644 --- a/gui/characterSelection.py +++ b/gui/characterSelection.py @@ -81,7 +81,6 @@ class CharacterSelection(wx.Panel): choice.Clear() charList = sChar.getCharacterList() - sChar.getCharacterList() picked = False for id, name, active in charList: diff --git a/service/character.py b/service/character.py index 3a170ac03..f109ead05 100644 --- a/service/character.py +++ b/service/character.py @@ -226,7 +226,6 @@ class Character(object): return eos.db.getCharacter(charID).name def new(self): - #@todo: seems setting skills on a new character doesn't trigger the dirty setting. Probably goes for character copy too char = eos.types.Character("New Character") eos.db.save(char) return char.ID From bd53785667df93bb896bbc083b05ebfa43eee577 Mon Sep 17 00:00:00 2001 From: blitzmann Date: Mon, 14 Sep 2015 20:35:36 -0400 Subject: [PATCH 14/15] Allow all 5 and 0 to change skills from affecting skill menu --- eos/saveddata/character.py | 4 ++++ gui/builtinContextMenus/changeAffectingSkills.py | 4 ++-- service/character.py | 3 +++ 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/eos/saveddata/character.py b/eos/saveddata/character.py index bc98de0f1..dfe7c1fe0 100644 --- a/eos/saveddata/character.py +++ b/eos/saveddata/character.py @@ -155,8 +155,12 @@ class Character(object): return len(self.dirtySkills) > 0 def saveLevels(self): + if self == self.getAll5() or self == self.getAll0(): + raise ReadOnlyException("This character is read-only") + for skill in self.dirtySkills: skill.saveLevel() + self.dirtySkills = set() eos.db.commit() diff --git a/gui/builtinContextMenus/changeAffectingSkills.py b/gui/builtinContextMenus/changeAffectingSkills.py index 6615d42eb..077584e95 100644 --- a/gui/builtinContextMenus/changeAffectingSkills.py +++ b/gui/builtinContextMenus/changeAffectingSkills.py @@ -21,8 +21,8 @@ class ChangeAffectingSkills(ContextMenu): self.charID = fit.character.ID - if self.sChar.getCharName(self.charID) in ("All 0", "All 5"): - return False + #if self.sChar.getCharName(self.charID) in ("All 0", "All 5"): + # return False if srcContext == "fittingShip": fitID = self.mainFrame.getActiveFit() diff --git a/service/character.py b/service/character.py index f109ead05..6e11b2c31 100644 --- a/service/character.py +++ b/service/character.py @@ -193,6 +193,9 @@ class Character(object): return char def saveCharacter(self, charID): + """Save edited skills""" + if charID == self.all5ID() or charID == self.all0ID(): + return char = eos.db.getCharacter(charID) char.saveLevels() From 3ea4439b8d6d93724e901a29d017ee499cf19664 Mon Sep 17 00:00:00 2001 From: blitzmann Date: Fri, 18 Sep 2015 11:59:52 -0400 Subject: [PATCH 15/15] Allow changing all 5 and 0 from editor --- eos/saveddata/character.py | 2 +- gui/characterEditor.py | 13 +++++-------- 2 files changed, 6 insertions(+), 9 deletions(-) diff --git a/eos/saveddata/character.py b/eos/saveddata/character.py index dfe7c1fe0..747eea2e3 100644 --- a/eos/saveddata/character.py +++ b/eos/saveddata/character.py @@ -247,7 +247,7 @@ class Skill(HandledItem): raise ReadOnlyException() self.activeLevel = level - if self.activeLevel == self.__level: + if self.activeLevel == self.__level and self in self.character.dirtySkills: self.character.dirtySkills.remove(self) else: self.character.dirtySkills.add(self) diff --git a/gui/characterEditor.py b/gui/characterEditor.py index e94ee98d9..1ffa921bb 100644 --- a/gui/characterEditor.py +++ b/gui/characterEditor.py @@ -160,7 +160,7 @@ class CharacterEditor(wx.Frame): def restrict(self): self.btnRename.Enable(False) self.btnDelete.Enable(False) - self.aview.stDisabledTip.Show(True) + self.aview.stDisabledTip.Show() self.aview.inputID.Enable(False) self.aview.inputKey.Enable(False) self.aview.charChoice.Enable(False) @@ -172,7 +172,7 @@ class CharacterEditor(wx.Frame): def unrestrict(self): self.btnRename.Enable(True) self.btnDelete.Enable(True) - self.aview.stDisabledTip.Show(False) + self.aview.stDisabledTip.Hide() self.aview.inputID.Enable(True) self.aview.inputKey.Enable(True) self.aview.btnFetchCharList.Enable(True) @@ -376,12 +376,8 @@ class SkillTreeView (wx.Panel): sChar = service.Character.getInstance() charID = self.Parent.Parent.getActiveCharacter() sMkt = service.Market.getInstance() - if sChar.getCharName(charID) not in ("All 0", "All 5"): - self.levelChangeMenu.selection = sMkt.getItem(self.skillTreeListCtrl.GetPyData(item)) - self.PopupMenu(self.levelChangeMenu) - else: - self.statsMenu.selection = sMkt.getItem(self.skillTreeListCtrl.GetPyData(item)) - self.PopupMenu(self.statsMenu) + self.levelChangeMenu.selection = sMkt.getItem(self.skillTreeListCtrl.GetPyData(item)) + self.PopupMenu(self.levelChangeMenu) def changeLevel(self, event): level = self.levelIds.get(event.Id) @@ -559,6 +555,7 @@ class APIView (wx.Panel): u"Please select another character or make a new one.", style=wx.ALIGN_CENTER ) self.stDisabledTip.Wrap( -1 ) hintSizer.Add( self.stDisabledTip, 0, wx.TOP | wx.BOTTOM, 10 ) + self.stDisabledTip.Hide() hintSizer.AddStretchSpacer() pmainSizer.Add(hintSizer, 0, wx.EXPAND, 5)