From 74bf3fbc8b096b7a918a9e6c1964df0f6c5fe67b Mon Sep 17 00:00:00 2001 From: DarkPhoenix Date: Mon, 19 Nov 2018 15:31:13 +0300 Subject: [PATCH 01/11] Move mutation export to another module --- service/port/eft.py | 14 ++------------ service/port/muta.py | 38 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 40 insertions(+), 12 deletions(-) create mode 100644 service/port/muta.py diff --git a/service/port/eft.py b/service/port/eft.py index fc8576d01..83f066f66 100644 --- a/service/port/eft.py +++ b/service/port/eft.py @@ -32,9 +32,9 @@ 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.muta import exportMutant from service.port.shared import IPortUser, processing_notify from enum import Enum @@ -157,17 +157,7 @@ def exportEft(fit, options): 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)) + mutationLines.append(exportMutant(mutant, firstPrefix='[{}] '.format(mutantReference), prefix=' ')) if mutationLines: sections.append('\n'.join(mutationLines)) diff --git a/service/port/muta.py b/service/port/muta.py new file mode 100644 index 000000000..df0519c6e --- /dev/null +++ b/service/port/muta.py @@ -0,0 +1,38 @@ +# ============================================================================= +# 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 eos.db.gamedata.queries import getAttributeInfo +from gui.utils.numberFormatter import roundToPrec + + +def exportMutant(mutant, firstPrefix='', prefix=''): + exportLines = [] + mutatedAttrs = {} + for attrID, mutator in mutant.mutators.items(): + attrName = getAttributeInfo(attrID).name + mutatedAttrs[attrName] = mutator.value + exportLines.append('{}{}'.format(firstPrefix, mutant.baseItem.name)) + exportLines.append('{}{}'.format(prefix, 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)) + exportLines.append('{}{}'.format(prefix, customAttrsLine)) + return '\n'.join(exportLines) From fa9b3be41ccf284488bb14747d358520bcd996b2 Mon Sep 17 00:00:00 2001 From: DarkPhoenix Date: Mon, 19 Nov 2018 16:57:19 +0300 Subject: [PATCH 02/11] Move fetch function to shared --- service/port/eft.py | 24 +++++------------------- service/port/muta.py | 15 +++++++++++++++ service/port/shared.py | 21 +++++++++++++++++++++ 3 files changed, 41 insertions(+), 19 deletions(-) diff --git a/service/port/eft.py b/service/port/eft.py index 83f066f66..44e4351b6 100644 --- a/service/port/eft.py +++ b/service/port/eft.py @@ -35,7 +35,7 @@ from eos.saveddata.fit import Fit from service.fit import Fit as svcFit from service.market import Market from service.port.muta import exportMutant -from service.port.shared import IPortUser, processing_notify +from service.port.shared import IPortUser, fetchItem, processing_notify from enum import Enum @@ -536,7 +536,7 @@ def _importGetMutationData(lines): if attrInfo is None: continue mutaAttrs[attrInfo.ID] = value - mutaItem = _fetchItem(mutaName) + mutaItem = fetchItem(mutaName) if mutaItem is None: continue data[ref] = (mutaItem, mutaAttrs) @@ -577,7 +577,7 @@ def _importCreateFit(lines): shipType = m.group('shipType').strip() fitName = m.group('fitName').strip() try: - ship = _fetchItem(shipType) + ship = fetchItem(shipType) try: fit.ship = Ship(ship) except ValueError: @@ -589,20 +589,6 @@ def _importCreateFit(lines): 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] @@ -657,7 +643,7 @@ class Section: class BaseItemSpec: def __init__(self, typeName): - item = _fetchItem(typeName, eagerCat=True) + item = fetchItem(typeName, eagerCat=True) if item is None: raise EftImportError self.typeName = typeName @@ -694,7 +680,7 @@ class RegularItemSpec(BaseItemSpec): def __fetchCharge(self, chargeName): if chargeName: - charge = _fetchItem(chargeName, eagerCat=True) + charge = fetchItem(chargeName, eagerCat=True) if not charge or charge.category.name != 'Charge': charge = None else: diff --git a/service/port/muta.py b/service/port/muta.py index df0519c6e..7d36f27a0 100644 --- a/service/port/muta.py +++ b/service/port/muta.py @@ -20,6 +20,7 @@ from eos.db.gamedata.queries import getAttributeInfo from gui.utils.numberFormatter import roundToPrec +from service.port.shared import fetchItem def exportMutant(mutant, firstPrefix='', prefix=''): @@ -36,3 +37,17 @@ def exportMutant(mutant, firstPrefix='', prefix=''): for a in sorted(mutatedAttrs)) exportLines.append('{}{}'.format(prefix, customAttrsLine)) return '\n'.join(exportLines) + + +def importMutant(lines): + try: + baseName = lines[0] + except IndexError: + return None + baseName = baseName.strip() + mutant = fetchItem(baseName) + # try: + # mutaName = lines[1] + # except IndexError: + # return mutant + diff --git a/service/port/shared.py b/service/port/shared.py index a21a81d63..214a7f3fe 100644 --- a/service/port/shared.py +++ b/service/port/shared.py @@ -20,6 +20,13 @@ from abc import ABCMeta, abstractmethod +from logbook import Logger + +from service.market import Market + + +pyfalog = Logger(__name__) + class UserCancelException(Exception): """when user cancel on port processing.""" @@ -68,3 +75,17 @@ class IPortUser(metaclass=ABCMeta): def processing_notify(iportuser, flag, data): if not iportuser.on_port_processing(flag, data): raise UserCancelException + + +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.shared: unable to fetch item "{}"'.format(typeName)) + return None + if sMkt.getPublicityByItem(item): + return item + else: + return None From 0294684bb826bbc1ccc8d37f7f565287898fb870 Mon Sep 17 00:00:00 2001 From: DarkPhoenix Date: Mon, 19 Nov 2018 17:47:48 +0300 Subject: [PATCH 03/11] Implement import of separate mutated items --- service/port/muta.py | 52 ++++++++++++++++++++++++++++++++++++++------ 1 file changed, 45 insertions(+), 7 deletions(-) diff --git a/service/port/muta.py b/service/port/muta.py index 7d36f27a0..868253da0 100644 --- a/service/port/muta.py +++ b/service/port/muta.py @@ -18,7 +18,8 @@ # ============================================================================= -from eos.db.gamedata.queries import getAttributeInfo +from eos.db.gamedata.queries import getAttributeInfo, getDynamicItem +from eos.saveddata.module import Module from gui.utils.numberFormatter import roundToPrec from service.port.shared import fetchItem @@ -40,14 +41,51 @@ def exportMutant(mutant, firstPrefix='', prefix=''): def importMutant(lines): + # Fetch base item type try: baseName = lines[0] except IndexError: return None - baseName = baseName.strip() - mutant = fetchItem(baseName) - # try: - # mutaName = lines[1] - # except IndexError: - # return mutant + baseType = fetchItem(baseName.strip()) + # Fetch mutaplasmid item type and actual item + try: + mutaName = lines[1] + except IndexError: + return _makeModule(baseType) + mutaplasmidType = fetchItem(mutaName.strip()) + if mutaplasmidType is None: + return _makeModule(baseType) + mutaplasmid = getDynamicItem(mutaplasmidType.ID) + module = _makeModule(mutaplasmid.resultingItem, baseType, mutaplasmid) + # Process mutated attribute values + try: + mutaAttrsLine = lines[2] + except IndexError: + return module + mutaAttrs = {} + 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 + for attrID, mutator in module.mutators.items(): + if attrID in mutaAttrs: + mutator.value = mutaAttrs[attrID] + return module + + +def _makeModule(item, *args, **kwargs): + try: + return Module(item, *args, **kwargs) + except ValueError: + return None From 58daf2a54348ea4152e03ce455a487fb2bef8556 Mon Sep 17 00:00:00 2001 From: Ryan Holmes Date: Tue, 20 Nov 2018 00:49:04 -0500 Subject: [PATCH 04/11] work on getting abyssal modules imported via clipboard --- gui/esiFittings.py | 2 +- gui/fitCommands/__init__.py | 3 +- .../calc/fitImportAbyssalModule.py | 60 +++++++++++++++++++ gui/fitCommands/guiImportAbyssalModule.py | 35 +++++++++++ gui/mainFrame.py | 10 +++- service/port/port.py | 34 ++++++----- 6 files changed, 126 insertions(+), 18 deletions(-) create mode 100644 gui/fitCommands/calc/fitImportAbyssalModule.py create mode 100644 gui/fitCommands/guiImportAbyssalModule.py diff --git a/gui/esiFittings.py b/gui/esiFittings.py index 20fc2ed74..9d14ad8fe 100644 --- a/gui/esiFittings.py +++ b/gui/esiFittings.py @@ -131,7 +131,7 @@ class EveFittings(wx.Frame): return data = self.fitTree.fittingsTreeCtrl.GetItemData(selection) sPort = Port.getInstance() - fits = sPort.importFitFromBuffer(data) + import_type, fits = sPort.importFitFromBuffer(data) self.mainFrame._openAfterImport(fits) def deleteFitting(self, event): diff --git a/gui/fitCommands/__init__.py b/gui/fitCommands/__init__.py index 145201559..e78cb4922 100644 --- a/gui/fitCommands/__init__.py +++ b/gui/fitCommands/__init__.py @@ -32,4 +32,5 @@ from .guiChangeDroneQty import GuiChangeDroneQty from .guiChangeProjectedDroneQty import GuiChangeProjectedDroneQty from .guiToggleDrone import GuiToggleDroneCommand from .guiFitRename import GuiFitRenameCommand -from .guiChangeImplantLocation import GuiChangeImplantLocation \ No newline at end of file +from .guiChangeImplantLocation import GuiChangeImplantLocation +from .guiImportAbyssalModule import GuiImportAbyssalModuleCommand \ No newline at end of file diff --git a/gui/fitCommands/calc/fitImportAbyssalModule.py b/gui/fitCommands/calc/fitImportAbyssalModule.py new file mode 100644 index 000000000..3bcecdb9e --- /dev/null +++ b/gui/fitCommands/calc/fitImportAbyssalModule.py @@ -0,0 +1,60 @@ +import wx +from eos.saveddata.module import Module, State +import eos.db +from logbook import Logger +from service.fit import Fit +pyfalog = Logger(__name__) + + +class FitImportAbyssalCommand(wx.Command): + """" + Fitting command that takes in a complete Abyssal module and adds it to a fit + """ + def __init__(self, fitID, module): + wx.Command.__init__(self, True) + self.fitID = fitID + self.module = module + self.new_position = None + self.change = None + self.replace_cmd = None + + def Do(self): + sFit = Fit.getInstance() + fitID = self.fitID + fit = eos.db.getFit(fitID) + + # this is essentially the same as the FitAddModule command. possibly look into centralizing this functionality somewhere? + 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 + sFit.checkStates(fit, self.module) + + # 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 is not None: + cmd = FitRemoveModuleCommand(self.fitID, [self.new_position]) + cmd.Do() + return True diff --git a/gui/fitCommands/guiImportAbyssalModule.py b/gui/fitCommands/guiImportAbyssalModule.py new file mode 100644 index 000000000..81dbb81b4 --- /dev/null +++ b/gui/fitCommands/guiImportAbyssalModule.py @@ -0,0 +1,35 @@ +import wx +import eos.db +import gui.mainFrame +from gui import globalEvents as GE +from .calc.fitImportAbyssalModule import FitImportAbyssalCommand +from service.fit import Fit +from logbook import Logger +pyfalog = Logger(__name__) + + +class GuiImportAbyssalModuleCommand(wx.Command): + def __init__(self, fitID, module): + wx.Command.__init__(self, True, "Abyssal Module Import: {}".format(module)) + self.mainFrame = gui.mainFrame.MainFrame.getInstance() + self.sFit = Fit.getInstance() + self.fitID = fitID + self.module = module + self.internal_history = wx.CommandProcessor() + + def Do(self): + pyfalog.debug("{} Do()".format(self)) + + if self.internal_history.Submit(FitImportAbyssalCommand(self.fitID, self.module)): + self.sFit.recalc(self.fitID) + wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.fitID, action="modadd")) + 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")) + return True diff --git a/gui/mainFrame.py b/gui/mainFrame.py index 2a3665f74..8eed38635 100644 --- a/gui/mainFrame.py +++ b/gui/mainFrame.py @@ -73,6 +73,7 @@ from service.fit import Fit from service.port import EfsPort, IPortUser, Port from service.settings import HTMLExportSettings, SettingsProvider from service.update import Update +import gui.fitCommands as cmd disableOverrideEditor = False @@ -728,12 +729,17 @@ class MainFrame(wx.Frame): def importFromClipboard(self, event): clipboard = fromClipboard() + activeFit = self.getActiveFit() try: - fits = Port().importFitFromBuffer(clipboard, self.getActiveFit()) + import_type, data = Port().importFitFromBuffer(clipboard, activeFit) + if import_type == "Abyssal": + # we've imported an Abyssal module, need to fire off the command to add it to the fit + self.command.Submit(cmd.GuiImportAbyssalModuleCommand(activeFit, data[0])) + return # no need to do anything else except: pyfalog.error("Attempt to import failed:\n{0}", clipboard) else: - self._openAfterImport(fits) + self._openAfterImport(data) def exportToClipboard(self, event): CopySelectDict = {CopySelectDialog.copyFormatEft: self.clipboardEft, diff --git a/service/port/port.py b/service/port/port.py index 560af14ea..2fc7a13d7 100644 --- a/service/port/port.py +++ b/service/port/port.py @@ -37,6 +37,7 @@ 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 +from service.port.muta import importMutant pyfalog = Logger(__name__) @@ -188,18 +189,20 @@ class Port(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 + import_type, fits = Port.importAuto(bufferStr, activeFit=activeFit) + + if import_type != 'Abyssal': + 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 import_type, fits @classmethod def importAuto(cls, string, path=None, activeFit=None, iportuser=None): @@ -228,8 +231,11 @@ class Port(object): if re.match("\[.*,.*\]", firstLine): return "EFT", (cls.importEft(string),) - # Use DNA format for all other cases - return "DNA", (cls.importDna(string),) + try: + return "Abyssal", (importMutant(string.split("\n")),) + except: + # Use DNA format for all other cases + return "DNA", (cls.importDna(string),) # EFT-related methods @staticmethod From 05e895e7b79c4debd62bdad7fae3e888a660ee32 Mon Sep 17 00:00:00 2001 From: DarkPhoenix Date: Tue, 20 Nov 2018 12:51:49 +0300 Subject: [PATCH 05/11] Move out mutated module parsing from EFT to separate module --- service/port/eft.py | 89 +++++++++++++++++++------------------------- service/port/muta.py | 34 ++++++----------- service/port/port.py | 4 +- 3 files changed, 52 insertions(+), 75 deletions(-) diff --git a/service/port/eft.py b/service/port/eft.py index 44e4351b6..a95f5f100 100644 --- a/service/port/eft.py +++ b/service/port/eft.py @@ -22,7 +22,7 @@ import re from logbook import Logger -from eos.db.gamedata.queries import getAttributeInfo, getDynamicItem +from eos.db.gamedata.queries import getDynamicItem from eos.saveddata.cargo import Cargo from eos.saveddata.citadel import Citadel from eos.saveddata.booster import Booster @@ -34,7 +34,7 @@ from eos.saveddata.ship import Ship from eos.saveddata.fit import Fit from service.fit import Fit as svcFit from service.market import Market -from service.port.muta import exportMutant +from service.port.muta import parseMutant, renderMutant from service.port.shared import IPortUser, fetchItem, processing_notify from enum import Enum @@ -157,7 +157,7 @@ def exportEft(fit, options): if mutants and options & Options.MUTATIONS.value: for mutantReference in sorted(mutants): mutant = mutants[mutantReference] - mutationLines.append(exportMutant(mutant, firstPrefix='[{}] '.format(mutantReference), prefix=' ')) + mutationLines.append(renderMutant(mutant, firstPrefix='[{}] '.format(mutantReference), prefix=' ')) if mutationLines: sections.append('\n'.join(mutationLines)) @@ -497,59 +497,48 @@ def _importPrepareString(eftString): return lines +mutantHeaderPattern = re.compile('^\[(?P\d+)\](?P.*)') + + def _importGetMutationData(lines): data = {} + # Format: {ref: [lines]} + mutaLinesMap = {} + currentMutaRef = None + currentMutaLines = [] consumedIndices = set() - for i in range(len(lines)): - line = lines[i] - m = re.match('^\[(?P\d+)\]', line) + + def completeMutaLines(): + if currentMutaRef is not None and currentMutaLines: + mutaLinesMap[currentMutaRef] = currentMutaLines + + for i, line in enumerate(lines): + m = re.match(mutantHeaderPattern, line) + # Start and reset at header 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 + completeMutaLines() + currentMutaRef = int(m.group('ref')) + currentMutaLines = [] + currentMutaLines.append(m.group('tail')) + consumedIndices.add(i) + # Reset at blank line + elif not line: + completeMutaLines() + currentMutaRef = None + currentMutaLines = [] + elif currentMutaRef is not None: + currentMutaLines.append(line) + consumedIndices.add(i) + else: + completeMutaLines() + # Clear mutant info from source for i in sorted(consumedIndices, reverse=True): del lines[i] + # Run parsing + data = {} + for ref, mutaLines in mutaLinesMap.items(): + _, mutaType, mutaAttrs = parseMutant(mutaLines) + data[ref] = (mutaType, mutaAttrs) return data diff --git a/service/port/muta.py b/service/port/muta.py index 868253da0..dc1b4492c 100644 --- a/service/port/muta.py +++ b/service/port/muta.py @@ -18,13 +18,12 @@ # ============================================================================= -from eos.db.gamedata.queries import getAttributeInfo, getDynamicItem -from eos.saveddata.module import Module +from eos.db.gamedata.queries import getAttributeInfo from gui.utils.numberFormatter import roundToPrec from service.port.shared import fetchItem -def exportMutant(mutant, firstPrefix='', prefix=''): +def renderMutant(mutant, firstPrefix='', prefix=''): exportLines = [] mutatedAttrs = {} for attrID, mutator in mutant.mutators.items(): @@ -40,28 +39,28 @@ def exportMutant(mutant, firstPrefix='', prefix=''): return '\n'.join(exportLines) -def importMutant(lines): +def parseMutant(lines): # Fetch base item type try: baseName = lines[0] except IndexError: return None baseType = fetchItem(baseName.strip()) + if baseType is None: + return None, None, {} # Fetch mutaplasmid item type and actual item try: mutaName = lines[1] except IndexError: - return _makeModule(baseType) - mutaplasmidType = fetchItem(mutaName.strip()) - if mutaplasmidType is None: - return _makeModule(baseType) - mutaplasmid = getDynamicItem(mutaplasmidType.ID) - module = _makeModule(mutaplasmid.resultingItem, baseType, mutaplasmid) + return baseType, None, {} + mutaType = fetchItem(mutaName.strip()) + if mutaType is None: + return baseType, None, {} # Process mutated attribute values try: mutaAttrsLine = lines[2] except IndexError: - return module + return baseType, mutaType, {} mutaAttrs = {} pairs = [p.strip() for p in mutaAttrsLine.split(',')] for pair in pairs: @@ -77,15 +76,4 @@ def importMutant(lines): if attrInfo is None: continue mutaAttrs[attrInfo.ID] = value - for attrID, mutator in module.mutators.items(): - if attrID in mutaAttrs: - mutator.value = mutaAttrs[attrID] - return module - - -def _makeModule(item, *args, **kwargs): - try: - return Module(item, *args, **kwargs) - except ValueError: - return None - + return baseType, mutaType, mutaAttrs diff --git a/service/port/port.py b/service/port/port.py index 2fc7a13d7..a544cecf4 100644 --- a/service/port/port.py +++ b/service/port/port.py @@ -37,7 +37,7 @@ 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 -from service.port.muta import importMutant +from service.port.muta import parseMutant pyfalog = Logger(__name__) @@ -232,7 +232,7 @@ class Port(object): return "EFT", (cls.importEft(string),) try: - return "Abyssal", (importMutant(string.split("\n")),) + return "Abyssal", (parseMutant(string.split("\n")),) except: # Use DNA format for all other cases return "DNA", (cls.importDna(string),) From c619efa68e4ea211b73bdad1e2a2e89fe1c5de26 Mon Sep 17 00:00:00 2001 From: DarkPhoenix Date: Tue, 20 Nov 2018 13:03:27 +0300 Subject: [PATCH 06/11] Change the way DNA import is handled --- service/port/port.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/service/port/port.py b/service/port/port.py index a544cecf4..e82e3630b 100644 --- a/service/port/port.py +++ b/service/port/port.py @@ -231,11 +231,16 @@ class Port(object): if re.match("\[.*,.*\]", firstLine): return "EFT", (cls.importEft(string),) + # Check if string is in DNA format + if re.match("\d+(:\d+(;\d+))*::", firstLine): + return "DNA", (cls.importDna(string),) + + # Assume that we import stand-alone abyssal module if all else fails try: return "Abyssal", (parseMutant(string.split("\n")),) except: - # Use DNA format for all other cases - return "DNA", (cls.importDna(string),) + pass + # EFT-related methods @staticmethod From 59d9d47a56a59d273dabe7ef03b8a6a6f0c76364 Mon Sep 17 00:00:00 2001 From: DarkPhoenix Date: Tue, 20 Nov 2018 13:38:38 +0300 Subject: [PATCH 07/11] Do some work on mutated module commands to support new parsing format --- gui/fitCommands/__init__.py | 2 +- .../calc/fitImportAbyssalModule.py | 60 ------------- .../calc/fitImportMutatedModule.py | 86 +++++++++++++++++++ ...salModule.py => guiImportMutatedModule.py} | 15 ++-- gui/mainFrame.py | 9 +- service/port/port.py | 10 +-- 6 files changed, 106 insertions(+), 76 deletions(-) delete mode 100644 gui/fitCommands/calc/fitImportAbyssalModule.py create mode 100644 gui/fitCommands/calc/fitImportMutatedModule.py rename gui/fitCommands/{guiImportAbyssalModule.py => guiImportMutatedModule.py} (63%) diff --git a/gui/fitCommands/__init__.py b/gui/fitCommands/__init__.py index e78cb4922..3501e8089 100644 --- a/gui/fitCommands/__init__.py +++ b/gui/fitCommands/__init__.py @@ -33,4 +33,4 @@ from .guiChangeProjectedDroneQty import GuiChangeProjectedDroneQty from .guiToggleDrone import GuiToggleDroneCommand from .guiFitRename import GuiFitRenameCommand from .guiChangeImplantLocation import GuiChangeImplantLocation -from .guiImportAbyssalModule import GuiImportAbyssalModuleCommand \ No newline at end of file +from .guiImportMutatedModule import GuiImportMutatedModuleCommand diff --git a/gui/fitCommands/calc/fitImportAbyssalModule.py b/gui/fitCommands/calc/fitImportAbyssalModule.py deleted file mode 100644 index 3bcecdb9e..000000000 --- a/gui/fitCommands/calc/fitImportAbyssalModule.py +++ /dev/null @@ -1,60 +0,0 @@ -import wx -from eos.saveddata.module import Module, State -import eos.db -from logbook import Logger -from service.fit import Fit -pyfalog = Logger(__name__) - - -class FitImportAbyssalCommand(wx.Command): - """" - Fitting command that takes in a complete Abyssal module and adds it to a fit - """ - def __init__(self, fitID, module): - wx.Command.__init__(self, True) - self.fitID = fitID - self.module = module - self.new_position = None - self.change = None - self.replace_cmd = None - - def Do(self): - sFit = Fit.getInstance() - fitID = self.fitID - fit = eos.db.getFit(fitID) - - # this is essentially the same as the FitAddModule command. possibly look into centralizing this functionality somewhere? - 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 - sFit.checkStates(fit, self.module) - - # 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 is not None: - cmd = FitRemoveModuleCommand(self.fitID, [self.new_position]) - cmd.Do() - return True diff --git a/gui/fitCommands/calc/fitImportMutatedModule.py b/gui/fitCommands/calc/fitImportMutatedModule.py new file mode 100644 index 000000000..0ded55105 --- /dev/null +++ b/gui/fitCommands/calc/fitImportMutatedModule.py @@ -0,0 +1,86 @@ +import wx +from eos.saveddata.module import Module, State +import eos.db +from eos.db.gamedata.queries import getDynamicItem +from logbook import Logger +from service.fit import Fit +pyfalog = Logger(__name__) + + +class FitImportMutatedCommand(wx.Command): + """" + Fitting command that takes info about mutated module, composes it and adds it to a fit + """ + def __init__(self, fitID, baseItem, mutaItem, attrMap): + wx.Command.__init__(self, True) + self.fitID = fitID + self.baseItem = baseItem + self.mutaItem = mutaItem + self.attrMap = attrMap + self.new_position = None + self.change = None + self.replace_cmd = None + + def Do(self): + sFit = Fit.getInstance() + fitID = self.fitID + fit = eos.db.getFit(fitID) + + if self.baseItem is None: + pyfalog.warning("Unable to build non-mutated module: no base item to build from") + return False + + mutaplasmid = getDynamicItem(self.mutaItem.ID) + # Try to build simple item even though no mutaplasmid found + if mutaplasmid is None: + try: + module = Module(self.baseItem) + except ValueError: + pyfalog.warning("Unable to build non-mutated module: typeID {}", self.baseItem.id) + return False + else: + try: + module = Module(mutaplasmid.resultingItem, self.baseItem, mutaplasmid) + except ValueError: + pass + else: + for attrID, mutator in module.mutators.items(): + if attrID in self.attrMap: + mutator.value = self.attrMap[attrID] + + + # this is essentially the same as the FitAddModule command. possibly look into centralizing this functionality somewhere? + if module.fits(fit): + pyfalog.debug("Adding {} as module for fit {}", module, fit) + module.owner = fit + numSlots = len(fit.modules) + fit.modules.append(module) + if module.isValidState(State.ACTIVE): + 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 + sFit.checkStates(fit, module) + + # fit.fill() + eos.db.commit() + + self.change = numSlots != len(fit.modules) + self.new_position = 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 is not None: + cmd = FitRemoveModuleCommand(self.fitID, [self.new_position]) + cmd.Do() + return True diff --git a/gui/fitCommands/guiImportAbyssalModule.py b/gui/fitCommands/guiImportMutatedModule.py similarity index 63% rename from gui/fitCommands/guiImportAbyssalModule.py rename to gui/fitCommands/guiImportMutatedModule.py index 81dbb81b4..ecc84f7ff 100644 --- a/gui/fitCommands/guiImportAbyssalModule.py +++ b/gui/fitCommands/guiImportMutatedModule.py @@ -2,25 +2,28 @@ import wx import eos.db import gui.mainFrame from gui import globalEvents as GE -from .calc.fitImportAbyssalModule import FitImportAbyssalCommand +from .calc.fitImportMutatedModule import FitImportMutatedCommand from service.fit import Fit from logbook import Logger pyfalog = Logger(__name__) -class GuiImportAbyssalModuleCommand(wx.Command): - def __init__(self, fitID, module): - wx.Command.__init__(self, True, "Abyssal Module Import: {}".format(module)) +class GuiImportMutatedModuleCommand(wx.Command): + + def __init__(self, fitID, baseItem, mutaItem, attrMap): + wx.Command.__init__(self, True, "Mutated Module Import: {} {} {}".format(baseItem.id, mutaItem.id, attrMap)) self.mainFrame = gui.mainFrame.MainFrame.getInstance() self.sFit = Fit.getInstance() self.fitID = fitID - self.module = module + self.baseItem = baseItem + self.mutaItem = mutaItem + self.attrMap = attrMap self.internal_history = wx.CommandProcessor() def Do(self): pyfalog.debug("{} Do()".format(self)) - if self.internal_history.Submit(FitImportAbyssalCommand(self.fitID, self.module)): + if self.internal_history.Submit(FitImportMutatedCommand(self.fitID, self.baseItem, self.mutaItem, self.attrMap)): self.sFit.recalc(self.fitID) wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.fitID, action="modadd")) return True diff --git a/gui/mainFrame.py b/gui/mainFrame.py index 8eed38635..5f3aa6441 100644 --- a/gui/mainFrame.py +++ b/gui/mainFrame.py @@ -731,15 +731,16 @@ class MainFrame(wx.Frame): clipboard = fromClipboard() activeFit = self.getActiveFit() try: - import_type, data = Port().importFitFromBuffer(clipboard, activeFit) - if import_type == "Abyssal": + importType, importData = Port().importFitFromBuffer(clipboard, activeFit) + # If it's mutated item - make sure there's at least base item specified + if importType == "MutatedItem": # we've imported an Abyssal module, need to fire off the command to add it to the fit - self.command.Submit(cmd.GuiImportAbyssalModuleCommand(activeFit, data[0])) + self.command.Submit(cmd.GuiImportMutatedModuleCommand(activeFit, *importData[0])) return # no need to do anything else except: pyfalog.error("Attempt to import failed:\n{0}", clipboard) else: - self._openAfterImport(data) + self._openAfterImport(importData) def exportToClipboard(self, event): CopySelectDict = {CopySelectDialog.copyFormatEft: self.clipboardEft, diff --git a/service/port/port.py b/service/port/port.py index e82e3630b..cad3dfae2 100644 --- a/service/port/port.py +++ b/service/port/port.py @@ -189,10 +189,10 @@ class Port(object): # TODO: catch the exception? # activeFit is reserved?, bufferStr is unicode? (assume only clipboard string? sFit = svcFit.getInstance() - import_type, fits = Port.importAuto(bufferStr, activeFit=activeFit) + importType, importData = Port.importAuto(bufferStr, activeFit=activeFit) - if import_type != 'Abyssal': - for fit in fits: + if importType != "MutatedItem": + for fit in importData: fit.character = sFit.character fit.damagePattern = sFit.pattern fit.targetResists = sFit.targetResists @@ -202,7 +202,7 @@ class Port(object): useCharImplants = sFit.serviceFittingOptions["useCharacterImplantsByDefault"] fit.implantLocation = ImplantLocation.CHARACTER if useCharImplants else ImplantLocation.FIT db.save(fit) - return import_type, fits + return importType, importData @classmethod def importAuto(cls, string, path=None, activeFit=None, iportuser=None): @@ -237,7 +237,7 @@ class Port(object): # Assume that we import stand-alone abyssal module if all else fails try: - return "Abyssal", (parseMutant(string.split("\n")),) + return "MutatedItem", (parseMutant(string.split("\n")),) except: pass From 60ecc95049d2699acb1f7e59a5e1dc0e7de42922 Mon Sep 17 00:00:00 2001 From: DarkPhoenix Date: Tue, 20 Nov 2018 13:48:54 +0300 Subject: [PATCH 08/11] Fix command code --- gui/fitCommands/calc/fitImportMutatedModule.py | 9 ++++++--- gui/fitCommands/guiImportMutatedModule.py | 2 +- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/gui/fitCommands/calc/fitImportMutatedModule.py b/gui/fitCommands/calc/fitImportMutatedModule.py index 0ded55105..d3c182d70 100644 --- a/gui/fitCommands/calc/fitImportMutatedModule.py +++ b/gui/fitCommands/calc/fitImportMutatedModule.py @@ -30,19 +30,22 @@ class FitImportMutatedCommand(wx.Command): pyfalog.warning("Unable to build non-mutated module: no base item to build from") return False - mutaplasmid = getDynamicItem(self.mutaItem.ID) + + mutaplasmid = getDynamicItem(getattr(self.mutaItem, 'ID')) # Try to build simple item even though no mutaplasmid found if mutaplasmid is None: try: module = Module(self.baseItem) except ValueError: - pyfalog.warning("Unable to build non-mutated module: typeID {}", self.baseItem.id) + pyfalog.warning("Unable to build non-mutated module: {}", self.baseItem) return False + # Build mutated module otherwise else: try: module = Module(mutaplasmid.resultingItem, self.baseItem, mutaplasmid) except ValueError: - pass + pyfalog.warning("Unable to build mutated module: {} {}", self.baseItem, self.mutaItem) + return False else: for attrID, mutator in module.mutators.items(): if attrID in self.attrMap: diff --git a/gui/fitCommands/guiImportMutatedModule.py b/gui/fitCommands/guiImportMutatedModule.py index ecc84f7ff..9187adf7e 100644 --- a/gui/fitCommands/guiImportMutatedModule.py +++ b/gui/fitCommands/guiImportMutatedModule.py @@ -11,7 +11,7 @@ pyfalog = Logger(__name__) class GuiImportMutatedModuleCommand(wx.Command): def __init__(self, fitID, baseItem, mutaItem, attrMap): - wx.Command.__init__(self, True, "Mutated Module Import: {} {} {}".format(baseItem.id, mutaItem.id, attrMap)) + wx.Command.__init__(self, True, "Mutated Module Import: {} {} {}".format(baseItem, mutaItem, attrMap)) self.mainFrame = gui.mainFrame.MainFrame.getInstance() self.sFit = Fit.getInstance() self.fitID = fitID From b9c36758945a9c9e41f63180513a72f0ba715072 Mon Sep 17 00:00:00 2001 From: DarkPhoenix Date: Tue, 20 Nov 2018 13:51:49 +0300 Subject: [PATCH 09/11] Fix case when only module name is passed Still crashes and query cache, will fix later --- gui/fitCommands/calc/fitImportMutatedModule.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gui/fitCommands/calc/fitImportMutatedModule.py b/gui/fitCommands/calc/fitImportMutatedModule.py index d3c182d70..426f8245b 100644 --- a/gui/fitCommands/calc/fitImportMutatedModule.py +++ b/gui/fitCommands/calc/fitImportMutatedModule.py @@ -31,7 +31,7 @@ class FitImportMutatedCommand(wx.Command): return False - mutaplasmid = getDynamicItem(getattr(self.mutaItem, 'ID')) + mutaplasmid = getDynamicItem(getattr(self.mutaItem, 'ID', None)) # Try to build simple item even though no mutaplasmid found if mutaplasmid is None: try: From 37a084e1d1ef9c4dbb379f67c4c85e9bc841acac Mon Sep 17 00:00:00 2001 From: DarkPhoenix Date: Tue, 20 Nov 2018 13:54:07 +0300 Subject: [PATCH 10/11] Fit importing plain modules in command code --- gui/fitCommands/calc/fitImportMutatedModule.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/gui/fitCommands/calc/fitImportMutatedModule.py b/gui/fitCommands/calc/fitImportMutatedModule.py index 426f8245b..3a28c68ea 100644 --- a/gui/fitCommands/calc/fitImportMutatedModule.py +++ b/gui/fitCommands/calc/fitImportMutatedModule.py @@ -30,8 +30,12 @@ class FitImportMutatedCommand(wx.Command): pyfalog.warning("Unable to build non-mutated module: no base item to build from") return False - - mutaplasmid = getDynamicItem(getattr(self.mutaItem, 'ID', None)) + try: + mutaTypeID = self.mutaItem.ID + except AttributeError: + mutaplasmid = None + else: + mutaplasmid = getDynamicItem(mutaTypeID) # Try to build simple item even though no mutaplasmid found if mutaplasmid is None: try: From 3907c8c29af6392ad0a7fbf47e9739f7462d803f Mon Sep 17 00:00:00 2001 From: DarkPhoenix Date: Tue, 20 Nov 2018 19:19:14 +0300 Subject: [PATCH 11/11] Change how compiled regex is used --- service/port/eft.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/service/port/eft.py b/service/port/eft.py index a95f5f100..a1e99f45c 100644 --- a/service/port/eft.py +++ b/service/port/eft.py @@ -513,7 +513,7 @@ def _importGetMutationData(lines): mutaLinesMap[currentMutaRef] = currentMutaLines for i, line in enumerate(lines): - m = re.match(mutantHeaderPattern, line) + m = mutantHeaderPattern.match(line) # Start and reset at header line if m: completeMutaLines()