diff --git a/eos/db/saveddata/databaseRepair.py b/eos/db/saveddata/databaseRepair.py index 2644fd507..6c32695a6 100644 --- a/eos/db/saveddata/databaseRepair.py +++ b/eos/db/saveddata/databaseRepair.py @@ -85,8 +85,7 @@ class DatabaseCleanup: logger.error("More than one uniform damage pattern found.") else: uniform_damage_pattern_id = rows[0]['ID'] - update_query = "UPDATE 'fits' SET 'damagePatternID' = " + str(uniform_damage_pattern_id) + \ - " WHERE damagePatternID NOT IN (SELECT ID FROM damagePatterns) OR damagePatternID IS NULL" + update_query = "UPDATE 'fits' SET 'damagePatternID' = {} WHERE damagePatternID NOT IN (SELECT ID FROM damagePatterns) OR damagePatternID IS NULL".format(uniform_damage_pattern_id) update_results = DatabaseCleanup.ExecuteSQLQuery(saveddata_engine, update_query) logger.error("Database corruption found. Cleaning up %d records.", update_results.rowcount) @@ -160,3 +159,75 @@ class DatabaseCleanup: query = "DELETE FROM targetResists WHERE name IS NULL OR name = ''" delete = DatabaseCleanup.ExecuteSQLQuery(saveddata_engine, query) logger.error("Database corruption found. Cleaning up %d records.", delete.rowcount) + + @staticmethod + def OrphanedFitIDItemID(saveddata_engine): + # Orphaned items that are missing the fit ID or item ID. + # See issue #954 + for table in ['drones', 'cargo', 'fighters']: + logger.debug("Running database cleanup for orphaned %s items.", table) + query = "SELECT COUNT(*) AS num FROM {} WHERE itemID IS NULL OR itemID = '' or itemID = '0' or fitID IS NULL OR fitID = '' or fitID = '0'".format(table) + results = DatabaseCleanup.ExecuteSQLQuery(saveddata_engine, query) + + if results is None: + return + + row = results.first() + + if row and row['num']: + query = "DELETE FROM {} WHERE itemID IS NULL OR itemID = '' or itemID = '0' or fitID IS NULL OR fitID = '' or fitID = '0'".format(table) + delete = DatabaseCleanup.ExecuteSQLQuery(saveddata_engine, query) + logger.error("Database corruption found. Cleaning up %d records.", delete.rowcount) + + for table in ['modules']: + logger.debug("Running database cleanup for orphaned %s items.", table) + query = "SELECT COUNT(*) AS num FROM {} WHERE itemID = '0' or fitID IS NULL OR fitID = '' or fitID = '0'".format(table) + results = DatabaseCleanup.ExecuteSQLQuery(saveddata_engine, query) + + if results is None: + return + + row = results.first() + + if row and row['num']: + query = "DELETE FROM {} WHERE itemID = '0' or fitID IS NULL OR fitID = '' or fitID = '0'".format(table) + delete = DatabaseCleanup.ExecuteSQLQuery(saveddata_engine, query) + logger.error("Database corruption found. Cleaning up %d records.", delete.rowcount) + + @staticmethod + def NullDamageTargetPatternValues(saveddata_engine): + # Find patterns that have null values + # See issue #954 + for profileType in ['damagePatterns', 'targetResists']: + for damageType in ['em', 'thermal', 'kinetic', 'explosive']: + logger.debug("Running database cleanup for null %s values. (%s)", profileType, damageType) + query = "SELECT COUNT(*) AS num FROM {0} WHERE {1}Amount IS NULL OR {1}Amount = ''".format(profileType, damageType) + results = DatabaseCleanup.ExecuteSQLQuery(saveddata_engine, query) + + if results is None: + return + + row = results.first() + + if row and row['num']: + query = "UPDATE '{0}' SET '{1}Amount' = '0' WHERE {1}Amount IS NULL OR Amount = ''".format(profileType, damageType) + delete = DatabaseCleanup.ExecuteSQLQuery(saveddata_engine, query) + logger.error("Database corruption found. Cleaning up %d records.", delete.rowcount) + + @staticmethod + def DuplicateSelectedAmmoName(saveddata_engine): + # Orphaned items that are missing the fit ID or item ID. + # See issue #954 + logger.debug("Running database cleanup for duplicated selected ammo profiles.") + query = "SELECT COUNT(*) AS num FROM damagePatterns WHERE name = 'Selected Ammo'" + results = DatabaseCleanup.ExecuteSQLQuery(saveddata_engine, query) + + if results is None: + return + + row = results.first() + + if row and row['num'] > 1: + query = "DELETE FROM damagePatterns WHERE name = 'Selected Ammo'" + delete = DatabaseCleanup.ExecuteSQLQuery(saveddata_engine, query) + logger.error("Database corruption found. Cleaning up %d records.", delete.rowcount) diff --git a/eos/effects/adaptivearmorhardener.py b/eos/effects/adaptivearmorhardener.py index 33e9f143c..e25fe48d4 100644 --- a/eos/effects/adaptivearmorhardener.py +++ b/eos/effects/adaptivearmorhardener.py @@ -15,8 +15,6 @@ def handler(fit, module, context): # Skip if there is no damage pattern. Example: projected ships or fleet boosters if damagePattern: - # logger.debug("Damage Pattern: %f/%f/%f/%f", damagePattern.emAmount, damagePattern.thermalAmount, damagePattern.kineticAmount, damagePattern.explosiveAmount) - # logger.debug("Original Armor Resists: %f/%f/%f/%f", fit.ship.getModifiedItemAttr('armorEmDamageResonance'), fit.ship.getModifiedItemAttr('armorThermalDamageResonance'), fit.ship.getModifiedItemAttr('armorKineticDamageResonance'), fit.ship.getModifiedItemAttr('armorExplosiveDamageResonance')) # Populate a tuple with the damage profile modified by current armor resists. baseDamageTaken = ( @@ -50,7 +48,6 @@ def handler(fit, module, context): (2, baseDamageTaken[2] * RAHResistance[2], RAHResistance[2]), (1, baseDamageTaken[1] * RAHResistance[1], RAHResistance[1]), ] - # logger.debug("Damage taken this cycle: %f/%f/%f/%f", damagePattern_tuples[0][1], damagePattern_tuples[3][1], damagePattern_tuples[2][1], damagePattern_tuples[1][1]) # Sort the tuple to drop the highest damage value to the bottom sortedDamagePattern_tuples = sorted(damagePattern_tuples, key=lambda damagePattern: damagePattern[1]) diff --git a/eos/effects/elitebonuscommanddestroyerinfohidden1.py b/eos/effects/elitebonuscommanddestroyerinfohidden1.py index 4ffc352e0..cf31b2b1b 100644 --- a/eos/effects/elitebonuscommanddestroyerinfohidden1.py +++ b/eos/effects/elitebonuscommanddestroyerinfohidden1.py @@ -3,4 +3,7 @@ type = "passive" def handler(fit, src, context): - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Information Command Specialist"), "commandBonusHidden", src.getModifiedItemAttr("eliteBonusCommandDestroyer1"), skill="Command Destroyers") + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Information Command Specialist"), + "commandBonusHidden", + src.getModifiedItemAttr("eliteBonusCommandDestroyer1"), + skill="Command Destroyers") diff --git a/eos/effects/elitebonuslogisticremotearmorrepairoptimalfalloff1.py b/eos/effects/elitebonuslogisticremotearmorrepairoptimalfalloff1.py index 7f8e9fec3..cacbd4478 100644 --- a/eos/effects/elitebonuslogisticremotearmorrepairoptimalfalloff1.py +++ b/eos/effects/elitebonuslogisticremotearmorrepairoptimalfalloff1.py @@ -6,5 +6,11 @@ type = "passive" def handler(fit, src, context): - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Remote Armor Repair Systems"), "falloffEffectiveness", src.getModifiedItemAttr("eliteBonusLogistics1"), skill="Logistics Cruisers") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Remote Armor Repair Systems"), "maxRange", src.getModifiedItemAttr("eliteBonusLogistics1"), skill="Logistics Cruisers") + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Remote Armor Repair Systems"), + "falloffEffectiveness", + src.getModifiedItemAttr("eliteBonusLogistics1"), + skill="Logistics Cruisers") + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Remote Armor Repair Systems"), + "maxRange", + src.getModifiedItemAttr("eliteBonusLogistics1"), + skill="Logistics Cruisers") diff --git a/eos/effects/rolebonusremotearmorrepairoptimalfalloff.py b/eos/effects/rolebonusremotearmorrepairoptimalfalloff.py index c286514f2..fae8acfac 100644 --- a/eos/effects/rolebonusremotearmorrepairoptimalfalloff.py +++ b/eos/effects/rolebonusremotearmorrepairoptimalfalloff.py @@ -6,5 +6,9 @@ type = "passive" def handler(fit, src, context): - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Remote Armor Repair Systems"), "falloffEffectiveness", src.getModifiedItemAttr("roleBonusRepairRange")) - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Remote Armor Repair Systems"), "maxRange", src.getModifiedItemAttr("roleBonusRepairRange")) + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Remote Armor Repair Systems"), + "falloffEffectiveness", + src.getModifiedItemAttr("roleBonusRepairRange")) + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Remote Armor Repair Systems"), + "maxRange", + src.getModifiedItemAttr("roleBonusRepairRange")) diff --git a/eos/effects/shipbonusforceauxiliarya4warfarelinksbonus.py b/eos/effects/shipbonusforceauxiliarya4warfarelinksbonus.py index a8378046b..824f57069 100644 --- a/eos/effects/shipbonusforceauxiliarya4warfarelinksbonus.py +++ b/eos/effects/shipbonusforceauxiliarya4warfarelinksbonus.py @@ -6,8 +6,18 @@ type = "passive" def handler(fit, src, context): - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Armored Command") or mod.item.requiresSkill("Information Command"), "warfareBuff4Value", src.getModifiedItemAttr("shipBonusForceAuxiliaryA4"), skill="Amarr Carrier") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Armored Command") or mod.item.requiresSkill("Information Command"), "warfareBuff3Value", src.getModifiedItemAttr("shipBonusForceAuxiliaryA4"), skill="Amarr Carrier") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Armored Command") or mod.item.requiresSkill("Information Command"), "warfareBuff1Value", src.getModifiedItemAttr("shipBonusForceAuxiliaryA4"), skill="Amarr Carrier") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Armored Command") or mod.item.requiresSkill("Information Command"), "buffDuration", src.getModifiedItemAttr("shipBonusForceAuxiliaryA4"), skill="Amarr Carrier") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Armored Command") or mod.item.requiresSkill("Information Command"), "warfareBuff2Value", src.getModifiedItemAttr("shipBonusForceAuxiliaryA4"), skill="Amarr Carrier") + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Armored Command") or + mod.item.requiresSkill("Information Command"), + "warfareBuff4Value", src.getModifiedItemAttr("shipBonusForceAuxiliaryA4"), skill="Amarr Carrier") + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Armored Command") or + mod.item.requiresSkill("Information Command"), + "warfareBuff3Value", src.getModifiedItemAttr("shipBonusForceAuxiliaryA4"), skill="Amarr Carrier") + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Armored Command") or + mod.item.requiresSkill("Information Command"), + "warfareBuff1Value", src.getModifiedItemAttr("shipBonusForceAuxiliaryA4"), skill="Amarr Carrier") + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Armored Command") or + mod.item.requiresSkill("Information Command"), + "buffDuration", src.getModifiedItemAttr("shipBonusForceAuxiliaryA4"), skill="Amarr Carrier") + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Armored Command") or + mod.item.requiresSkill("Information Command"), + "warfareBuff2Value", src.getModifiedItemAttr("shipBonusForceAuxiliaryA4"), skill="Amarr Carrier") diff --git a/eos/gamedata.py b/eos/gamedata.py index e4a4afa1c..10317dab2 100644 --- a/eos/gamedata.py +++ b/eos/gamedata.py @@ -243,9 +243,11 @@ class Item(EqBase): return self.__attributes - def getAttribute(self, key): + def getAttribute(self, key, default=None): if key in self.attributes: return self.attributes[key].value + else: + return default def isType(self, type): for effect in self.effects.itervalues(): diff --git a/eos/modifiedAttributeDict.py b/eos/modifiedAttributeDict.py index d060eb425..e558dc64d 100644 --- a/eos/modifiedAttributeDict.py +++ b/eos/modifiedAttributeDict.py @@ -25,19 +25,19 @@ cappingAttrKeyCache = {} class ItemAttrShortcut(object): - def getModifiedItemAttr(self, key): + def getModifiedItemAttr(self, key, default=None): if key in self.itemModifiedAttributes: return self.itemModifiedAttributes[key] else: - return None + return default class ChargeAttrShortcut(object): - def getModifiedChargeAttr(self, key): + def getModifiedChargeAttr(self, key, default=None): if key in self.chargeModifiedAttributes: return self.chargeModifiedAttributes[key] else: - return None + return default class ModifiedAttributeDict(collections.MutableMapping): diff --git a/eos/saveddata/fit.py b/eos/saveddata/fit.py index 38a807b8d..de1eb2967 100644 --- a/eos/saveddata/fit.py +++ b/eos/saveddata/fit.py @@ -489,10 +489,12 @@ class Fit(object): self.ship.boostItemAttr("armor%sDamageResonance" % damageType, value) if warfareBuffID == 14: # Armor Burst: Rapid Repair: Repair Duration/Capacitor - self.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Remote Armor Repair Systems") or mod.item.requiresSkill("Repair Systems"), - "capacitorNeed", value) - self.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Remote Armor Repair Systems") or mod.item.requiresSkill("Repair Systems"), "duration", - value) + self.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Remote Armor Repair Systems") or + mod.item.requiresSkill("Repair Systems"), + "capacitorNeed", value) + self.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Remote Armor Repair Systems") or + mod.item.requiresSkill("Repair Systems"), + "duration", value) if warfareBuffID == 15: # Armor Burst: Armor Reinforcement: Armor HP self.ship.boostItemAttr("armorHP", value, stackingPenalties=True) @@ -626,7 +628,8 @@ class Fit(object): self.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Shield Emission Systems"), "shieldBonus", value, stackingPenalties=True) if warfareBuffID == 53: # Leviathan Effect Generator : Armor RR penalty - self.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Remote Armor Repair Systems"), "armorDamageAmount", value, stackingPenalties=True) + self.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Remote Armor Repair Systems"), + "armorDamageAmount", value, stackingPenalties=True) if warfareBuffID == 54: # Ragnarok Effect Generator : Laser and Hybrid Optimal penalty groups = ("Energy Weapon", "Hybrid Weapon") diff --git a/gui/builtinViewColumns/baseName.py b/gui/builtinViewColumns/baseName.py index 38f400ee4..c1a291434 100644 --- a/gui/builtinViewColumns/baseName.py +++ b/gui/builtinViewColumns/baseName.py @@ -24,7 +24,8 @@ from eos.saveddata.implant import Implant from eos.saveddata.drone import Drone from eos.saveddata.fighter import Fighter from eos.saveddata.module import Module, Slot, Rack -from service.fit import Fit +from eos.saveddata.fit import Fit +from service.fit import Fit as FitSvc from gui.viewColumn import ViewColumn import gui.mainFrame @@ -57,7 +58,7 @@ class BaseName(ViewColumn): else: return "%s (%s)" % (stuff.name, stuff.ship.item.name) elif isinstance(stuff, Rack): - if Fit.getInstance().serviceFittingOptions["rackLabels"]: + if FitSvc.getInstance().serviceFittingOptions["rackLabels"]: if stuff.slot == Slot.MODE: return u'─ Tactical Mode ─' else: @@ -74,7 +75,7 @@ class BaseName(ViewColumn): else: item = getattr(stuff, "item", stuff) - if Fit.getInstance().serviceFittingOptions["showMarketShortcuts"]: + if FitSvc.getInstance().serviceFittingOptions["showMarketShortcuts"]: marketShortcut = getattr(item, "marketShortcut", None) if marketShortcut: diff --git a/gui/builtinViewColumns/misc.py b/gui/builtinViewColumns/misc.py index 080614a3b..a5188253d 100644 --- a/gui/builtinViewColumns/misc.py +++ b/gui/builtinViewColumns/misc.py @@ -450,30 +450,69 @@ class Miscellanea(ViewColumn): return text, item.name else: return "", None - elif itemGroup in ("Ancillary Armor Repairer", "Ancillary Shield Booster"): - hp = stuff.hpBeforeReload - cycles = stuff.numShots - cycleTime = stuff.rawCycleTime + elif itemGroup in ( + "Ancillary Armor Repairer", + "Ancillary Shield Booster", + "Capacitor Booster", + "Ancillary Remote Armor Repairer", + "Ancillary Remote Shield Booster", + ): + if "Armor" in itemGroup or "Shield" in itemGroup: + boosted_attribute = "HP" + reload_time = item.getAttribute("reloadTime", 0) / 1000 + elif "Capacitor" in itemGroup: + boosted_attribute = "Cap" + reload_time = 10 + else: + boosted_attribute = "" + reload_time = 0 + + cycles = max(stuff.numShots, 0) + cycleTime = max(stuff.rawCycleTime, 0) + + # Get HP or boosted amount + stuff_hp = max(stuff.hpBeforeReload, 0) + armor_hp = stuff.getModifiedItemAttr("armorDamageAmount", 0) + capacitor_hp = stuff.getModifiedChargeAttr("capacitorBonus", 0) + shield_hp = stuff.getModifiedItemAttr("shieldBonus", 0) + hp = max(stuff_hp, armor_hp * cycles, capacitor_hp * cycles, shield_hp * cycles, 0) + if not hp or not cycleTime or not cycles: return "", None + fit = Fit.getInstance().getFit(self.mainFrame.getActiveFit()) ehpTotal = fit.ehp hpTotal = fit.hp useEhp = self.mainFrame.statsPane.nameViewMap["resistancesViewFull"].showEffective - tooltip = "HP restored over duration using charges" - if useEhp: - if itemGroup == "Ancillary Armor Repairer": + tooltip = "{0} restored over duration using charges (plus reload)".format(boosted_attribute) + + if useEhp and boosted_attribute == "HP" and "Remote" not in itemGroup: + if "Ancillary Armor Repairer" in itemGroup: hpRatio = ehpTotal["armor"] / hpTotal["armor"] else: hpRatio = ehpTotal["shield"] / hpTotal["shield"] tooltip = "E{0}".format(tooltip) else: hpRatio = 1 - if itemGroup == "Ancillary Armor Repairer": - hpRatio *= 3 + + if "Ancillary" in itemGroup and "Armor" in itemGroup: + hpRatio *= stuff.getModifiedItemAttr("chargedArmorDamageMultiplier", 1) + ehp = hp * hpRatio + duration = cycles * cycleTime / 1000 - text = "{0} / {1}s".format(formatAmount(ehp, 3, 0, 9), formatAmount(duration, 3, 0, 3)) + for number_of_cycles in {5, 10, 25}: + tooltip = "{0}\n{1} charges lasts {2} seconds ({3} cycles)".format( + tooltip, + formatAmount(number_of_cycles*cycles, 3, 0, 3), + formatAmount((duration+reload_time)*number_of_cycles, 3, 0, 3), + formatAmount(number_of_cycles, 3, 0, 3) + ) + text = "{0} / {1}s (+{2}s)".format( + formatAmount(ehp, 3, 0, 9), + formatAmount(duration, 3, 0, 3), + formatAmount(reload_time, 3, 0, 3) + ) return text, tooltip elif itemGroup == "Armor Resistance Shift Hardener": diff --git a/gui/mainFrame.py b/gui/mainFrame.py index e33c8bac9..7e2eb072f 100644 --- a/gui/mainFrame.py +++ b/gui/mainFrame.py @@ -26,6 +26,7 @@ import wx import time from codecs import open +from wx._core import PyDeadObjectError from wx.lib.wordwrap import wordwrap @@ -70,6 +71,9 @@ from service.port import Port from service.settings import HTMLExportSettings from time import gmtime, strftime +import logging + +logger = logging.getLogger(__name__) import threading import webbrowser @@ -349,6 +353,13 @@ class MainFrame(wx.Frame): info = wx.AboutDialogInfo() info.Name = "pyfa" info.Version = gui.aboutData.versionString + + try: + import matplotlib + matplotlib_version = matplotlib.__version__ + except: + matplotlib_version = None + info.Description = wordwrap(gui.aboutData.description + "\n\nDevelopers:\n\t" + "\n\t".join(gui.aboutData.developers) + "\n\nAdditional credits:\n\t" + @@ -358,7 +369,8 @@ class MainFrame(wx.Frame): "\n\nEVE Data: \t" + gamedata_version + "\nPython: \t\t" + '{}.{}.{}'.format(v.major, v.minor, v.micro) + "\nwxPython: \t" + wx.__version__ + - "\nSQLAlchemy: \t" + sqlalchemy.__version__, + "\nSQLAlchemy: \t" + sqlalchemy.__version__ + + "\nmatplotlib: \t {}".format(matplotlib_version if matplotlib_version else "Not Installed"), 500, wx.ClientDC(self)) if "__WXGTK__" in wx.PlatformInfo: forumUrl = "http://forums.eveonline.com/default.aspx?g=posts&t=466425" @@ -381,7 +393,10 @@ class MainFrame(wx.Frame): def showDamagePatternEditor(self, event): dlg = DmgPatternEditorDlg(self) dlg.ShowModal() - dlg.Destroy() + try: + dlg.Destroy() + except PyDeadObjectError: + logger.error("Tried to destroy an object that doesn't exist in .") def showImplantSetEditor(self, event): ImplantSetEditorDlg(self) @@ -406,14 +421,20 @@ class MainFrame(wx.Frame): path += ".xml" else: print("oops, invalid fit format %d" % format_) - dlg.Destroy() + try: + dlg.Destroy() + except PyDeadObjectError: + logger.error("Tried to destroy an object that doesn't exist in .") return with open(path, "w", encoding="utf-8") as openfile: openfile.write(output) openfile.close() - dlg.Destroy() + try: + dlg.Destroy() + except PyDeadObjectError: + logger.error("Tried to destroy an object that doesn't exist in .") def showPreferenceDialog(self, event): dlg = PreferenceDialog(self) @@ -724,7 +745,10 @@ class MainFrame(wx.Frame): CopySelectDict[selected]() - dlg.Destroy() + try: + dlg.Destroy() + except PyDeadObjectError: + logger.error("Tried to destroy an object that doesn't exist in .") def exportSkillsNeeded(self, event): """ Exports skills needed for active fit and active character """ @@ -778,7 +802,10 @@ class MainFrame(wx.Frame): self.progressDialog.message = None sPort.importFitsThreaded(dlg.GetPaths(), self.fileImportCallback) self.progressDialog.ShowModal() - dlg.Destroy() + try: + dlg.Destroy() + except PyDeadObjectError: + logger.error("Tried to destroy an object that doesn't exist in .") def backupToXml(self, event): """ Back up all fits to EVE XML file """ diff --git a/gui/utils/exportHtml.py b/gui/utils/exportHtml.py index 41fa7584d..194db5a6b 100644 --- a/gui/utils/exportHtml.py +++ b/gui/utils/exportHtml.py @@ -230,7 +230,7 @@ class exportHtmlThread(threading.Thread): HTML += ( '
  • \n' '

    ' + group.groupName + ' ' + str(groupFits) + '

    \n' - '
      \n' + HTMLgroup + + '
        \n' + HTMLgroup + '
      \n' ' ' ) diff --git a/pyfa.py b/pyfa.py index e465b0d5a..4eb9e0b9d 100755 --- a/pyfa.py +++ b/pyfa.py @@ -82,7 +82,8 @@ if not hasattr(sys, 'frozen'): betaFlag = True if saMatch.group(3) == "b" else False saBuild = int(saMatch.group(4)) if not betaFlag else 0 if saMajor == 0 and (saMinor < 5 or (saMinor == 5 and saBuild < 8)): - print("Pyfa requires sqlalchemy 0.5.8 at least but current sqlalchemy version is %s\nYou can download sqlalchemy (0.5.8+) from http://www.sqlalchemy.org/".format(sqlalchemy.__version__)) + print("Pyfa requires sqlalchemy 0.5.8 at least but current sqlalchemy version is %s\n" + "You can download sqlalchemy (0.5.8+) from http://www.sqlalchemy.org/".format(sqlalchemy.__version__)) sys.exit(1) else: print("Unknown sqlalchemy version string format, skipping check") diff --git a/service/prefetch.py b/service/prefetch.py index cf40f90f8..257fe37bc 100644 --- a/service/prefetch.py +++ b/service/prefetch.py @@ -56,6 +56,9 @@ if config.saveDB and os.path.isfile(config.saveDB): database_cleanup_instance.OrphanedFitDamagePatterns(db.saveddata_engine) database_cleanup_instance.NullDamagePatternNames(db.saveddata_engine) database_cleanup_instance.NullTargetResistNames(db.saveddata_engine) + database_cleanup_instance.OrphanedFitIDItemID(db.saveddata_engine) + database_cleanup_instance.NullDamageTargetPatternValues(db.saveddata_engine) + database_cleanup_instance.DuplicateSelectedAmmoName(db.saveddata_engine) logging.debug("Completed database validation.") else: diff --git a/tox.ini b/tox.ini index 2274602e3..dea12ad99 100644 --- a/tox.ini +++ b/tox.ini @@ -12,4 +12,6 @@ commands = py.test -vv --cov Pyfa tests/ [testenv:pep8] deps = flake8 -commands = flake8 --exclude=.svn,CVS,.bzr,.hg,.git,__pycache__,venv,tests,.tox,build,dist,__init__.py --ignore=E126,E127,E128,E501,E731,F401,F403,F405 service gui eos utils config.py pyfa.py --max-line-length=130 +# TODO: Remove F class exceptions once all imports are fixed +# TODO: Remove E731 and convert lambdas to defs +commands = flake8 --exclude=.svn,CVS,.bzr,.hg,.git,__pycache__,venv,tests,.tox,build,dist,__init__.py --ignore=E126,E127,E128,E731,F401,F403,F405 service gui eos utils config.py pyfa.py --max-line-length=165