diff --git a/eos/saveddata/ship.py b/eos/saveddata/ship.py
index 1e3fd41f0..10b047c71 100644
--- a/eos/saveddata/ship.py
+++ b/eos/saveddata/ship.py
@@ -71,6 +71,11 @@ class Ship(ItemAttrShortcut, HandledItem):
def item(self):
return self.__item
+ @property
+ def name(self):
+ # NOTE: add name property
+ return self.__item.name
+
@property
def itemModifiedAttributes(self):
return self.__itemModifiedAttributes
diff --git a/gui/mainFrame.py b/gui/mainFrame.py
index 7af37d875..60e2b7f26 100644
--- a/gui/mainFrame.py
+++ b/gui/mainFrame.py
@@ -72,7 +72,7 @@ from service.update import Update
from eos.modifiedAttributeDict import ModifiedAttributeDict
from eos.db.saveddata.loadDefaultDatabaseValues import DefaultDatabaseValues
from eos.db.saveddata.queries import getFit as db_getFit
-from service.port import Port
+from service.port import Port, IPortUser
from service.settings import HTMLExportSettings
from time import gmtime, strftime
@@ -137,7 +137,7 @@ class OpenFitsThread(threading.Thread):
wx.CallAfter(self.callback)
-class MainFrame(wx.Frame):
+class MainFrame(wx.Frame, IPortUser):
__instance = None
@classmethod
@@ -419,8 +419,7 @@ class MainFrame(wx.Frame):
format_ = dlg.GetFilterIndex()
path = dlg.GetPath()
if format_ == 0:
- sPort = Port.getInstance()
- output = sPort.exportXml(None, fit)
+ output = Port.exportXml(None, fit)
if '.' not in os.path.basename(path):
path += ".xml"
else:
@@ -790,7 +789,6 @@ class MainFrame(wx.Frame):
def fileImportDialog(self, event):
"""Handles importing single/multiple EVE XML / EFT cfg fit files"""
- sPort = Port.getInstance()
dlg = wx.FileDialog(
self,
"Open One Or More Fitting Files",
@@ -804,10 +802,13 @@ class MainFrame(wx.Frame):
"Importing fits",
" " * 100, # set some arbitrary spacing to create width in window
parent=self,
- style=wx.PD_APP_MODAL | wx.PD_ELAPSED_TIME
+ style=wx.PD_CAN_ABORT
+ | wx.PD_SMOOTH
+ | wx.PD_ELAPSED_TIME
+ | wx.PD_APP_MODAL
)
- self.progressDialog.message = None
- sPort.importFitsThreaded(dlg.GetPaths(), self.fileImportCallback)
+ # self.progressDialog.message = None
+ Port.importFitsThreaded(dlg.GetPaths(), self)
self.progressDialog.ShowModal()
try:
dlg.Destroy()
@@ -839,9 +840,12 @@ class MainFrame(wx.Frame):
"Backing up %d fits to: %s" % (max_, filePath),
maximum=max_,
parent=self,
- style=wx.PD_APP_MODAL | wx.PD_ELAPSED_TIME,
+ style=wx.PD_CAN_ABORT
+ | wx.PD_SMOOTH
+ | wx.PD_ELAPSED_TIME
+ | wx.PD_APP_MODAL
)
- Port().backupFits(filePath, self.backupCallback)
+ Port.backupFits(filePath, self)
self.progressDialog.ShowModal()
def exportHtml(self, event):
@@ -879,7 +883,17 @@ class MainFrame(wx.Frame):
else:
self.progressDialog.Update(info)
- def fileImportCallback(self, action, data=None):
+ def onPortProcessStart(self):
+ # flag for progress dialog.
+ self.__progress_flag = True
+ # 2017/03/29 NOTE: implementation like interface
+ def onPortProcessing(self, action, data=None):
+ wx.CallAfter(
+ self._onPortProcessing, action, data
+ )
+ return self.__progress_flag
+
+ def _onPortProcessing(self, action, data):
"""
While importing fits from file, the logic calls back to this function to
update progress bar to show activity. XML files can contain multiple
@@ -893,22 +907,39 @@ class MainFrame(wx.Frame):
1: Replace message with data
other: Close dialog and handle based on :action (-1 open fits, -2 display error)
"""
-
- if action is None:
- self.progressDialog.Pulse()
- elif action == 1 and data != self.progressDialog.message:
- self.progressDialog.message = data
- self.progressDialog.Pulse(data)
- else:
+ _message = None
+ if action & IPortUser.ID_ERROR:
self.closeProgressDialog()
- if action == -1:
- self._openAfterImport(data)
- elif action == -2:
- dlg = wx.MessageDialog(self,
- "The following error was generated\n\n%s\n\nBe aware that already processed fits were not saved" % data,
- "Import Error", wx.OK | wx.ICON_ERROR)
- if dlg.ShowModal() == wx.ID_OK:
- return
+ _message = "Import Error" if action & IPortUser.PROCESS_IMPORT else "Export Error"
+ dlg = wx.MessageDialog(self,
+ "The following error was generated\n\n%s\n\nBe aware that already processed fits were not saved" % data,
+ _message, wx.OK | wx.ICON_ERROR)
+# if dlg.ShowModal() == wx.ID_OK:
+# return
+ dlg.ShowModal()
+ return
+
+ # data is str
+ if action & IPortUser.PROCESS_IMPORT:
+ if action & IPortUser.ID_PULSE:
+ _message = ()
+ # update message
+ elif action & IPortUser.ID_UPDATE: # and data != self.progressDialog.message:
+ _message = data
+
+ if _message is not None:
+ self.__progress_flag, _unuse = self.progressDialog.Pulse(_message)
+ else:
+ self.closeProgressDialog()
+ if action & IPortUser.ID_DONE:
+ self._openAfterImport(data)
+ # data is tuple(int, str)
+ elif action & IPortUser.PROCESS_EXPORT:
+ if action & IPortUser.ID_DONE:
+ self.closeProgressDialog()
+ else:
+ self.__progress_flag, _unuse = self.progressDialog.Update(data[0], data[1])
+
def _openAfterImport(self, fits):
if len(fits) > 0:
diff --git a/gui/notesView.py b/gui/notesView.py
index d4f8703cc..031e64941 100644
--- a/gui/notesView.py
+++ b/gui/notesView.py
@@ -40,8 +40,8 @@ class NotesView(wx.Panel):
self.saveTimer.Start(1000, True)
def delayedSave(self, event):
- sFit = Fit.getInstance()
- fit = sFit.getFit(self.lastFitId)
- newNotes = self.editNotes.GetValue()
- fit.notes = newNotes
+ fit = Fit.getInstance().getFit(self.lastFitId)
+ # NOTE: encounter a situation where fit is None
+ if fit is not None:
+ fit.notes = self.editNotes.GetValue()
wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=fit.ID))
diff --git a/service/port.py b/service/port.py
index 3e923ed0f..a63dafd7e 100644
--- a/service/port.py
+++ b/service/port.py
@@ -1,3 +1,4 @@
+# coding: utf-8
# =============================================================================
# Copyright (C) 2014 Ryan Holmes
#
@@ -71,7 +72,123 @@ INV_FLAG_DRONEBAY = 87
INV_FLAG_FIGHTER = 158
+from utils.strfunctions import sequential_rep, replaceLTGT
+# -- 170327 Ignored description --
+localized_pattern = re.compile(r'([^\*]+)\*')
+
+
+def _resolveShip(fitting, sMkt):
+ fitobj = Fit()
+ # 2017/03/29 NOTE:
+ # if fit name contained "<" or ">" then reprace to named html entity by EVE client
+ fitobj.name = replaceLTGT(fitting.getAttribute("name"))
+ # Maelstrom
+ shipType = fitting.getElementsByTagName("shipType").item(0).getAttribute("value")
+ emergency = None
+ matches = localized_pattern.match(shipType)
+ if matches:
+ # emergency cache
+ emergency = matches.group(2)
+ # expect an official name
+ shipType = matches.group(1)
+ limit = 2
+ while True:
+ must_retry = False
+ try:
+ try:
+ fitobj.ship = Ship(sMkt.getItem(shipType))
+ except ValueError:
+ fitobj.ship = Citadel(sMkt.getItem(shipType))
+ except Exception as e:
+ pyfalog.warning("Caught exception on _resolveShip")
+ pyfalog.error(e)
+ shipType = emergency
+ must_retry = True
+ limit -= 1
+ if not must_retry or limit is 0:
+ break
+ # True means localized
+ return matches is not None, fitobj
+
+def _resolveModule(hardware, sMkt, b_localized):
+ moduleName = hardware.getAttribute("type")
+ emergency = None
+ if b_localized:
+ emergency = localized_pattern.sub("\g<2>", moduleName)
+ # expect an official name
+ moduleName = localized_pattern.sub("\g<1>", moduleName)
+ item = None
+ limit = 2
+ while True:
+ must_retry = False
+ try:
+ item = sMkt.getItem(moduleName, eager="group.category")
+ except Exception as e:
+ pyfalog.warning("Caught exception on _resolveModule")
+ pyfalog.error(e)
+ moduleName = emergency
+ must_retry = True
+ limit -= 1
+ if not must_retry or limit is 0:
+ break
+ return item
+
+
+class UserCancelException(Exception):
+ """when user cancel on port processing."""
+ pass
+
+
+from abc import ABCMeta, abstractmethod
+
+
+class IPortUser:
+
+ __metaclass__ = ABCMeta
+
+ ID_PULSE = 1
+ # Pulse the progress bar
+ ID_UPDATE = ID_PULSE << 1
+ # Replace message with data: update messate
+ ID_DONE = ID_PULSE << 2
+ # open fits: import process done
+ ID_ERROR = ID_PULSE << 3
+ # display error: raise some error
+
+ PROCESS_IMPORT = ID_PULSE << 4
+ # means import process.
+ PROCESS_EXPORT = ID_PULSE << 5
+ # means import process.
+
+ @abstractmethod
+ def onPortProcessing(self, action, data=None):
+ """
+ While importing fits from file, the logic calls back to this function to
+ update progress bar to show activity. XML files can contain multiple
+ ships with multiple fits, whereas EFT cfg files contain many fits of
+ a single ship. When iterating through the files, we update the message
+ when we start a new file, and then Pulse the progress bar with every fit
+ that is processed.
+
+ action : a flag that lets us know how to deal with :data
+ None: Pulse the progress bar
+ 1: Replace message with data
+ other: Close dialog and handle based on :action (-1 open fits, -2 display error)
+ """
+
+ """return: True is continue process, False is cancel."""
+ pass
+
+ def onPortProcessStart(self):
+ pass
+
+
class Port(object):
+ """
+ 2017/03/31 NOTE: About change
+ 1. want to keep the description recorded in fit
+ 2. i think should not write wx.CallAfter in here
+ """
instance = None
@classmethod
@@ -82,19 +199,28 @@ class Port(object):
return cls.instance
@staticmethod
- def backupFits(path, callback):
+ def backupFits(path, iportuser):
pyfalog.debug("Starting backup fits thread.")
- thread = FitBackupThread(path, callback)
- thread.start()
+# thread = FitBackupThread(path, callback)
+# thread.start()
+ threading.Thread(
+ target=PortProcessing.backupFits,
+ args=(path, iportuser)
+ ).start()
@staticmethod
- def importFitsThreaded(paths, callback):
+ def importFitsThreaded(paths, iportuser):
+ """param iportuser: IPortUser implemented class"""
pyfalog.debug("Starting import fits thread.")
- thread = FitImportThread(paths, callback)
- thread.start()
+# thread = FitImportThread(paths, iportuser)
+# thread.start()
+ threading.Thread(
+ target=PortProcessing.importFitsFromFile,
+ args=(paths, iportuser)
+ ).start()
@staticmethod
- def importFitFromFiles(paths, callback=None):
+ def importFitFromFiles(paths, iportuser=None):
"""
Imports fits from file(s). First processes all provided paths and stores
assembled fits into a list. This allows us to call back to the GUI as
@@ -104,96 +230,104 @@ class Port(object):
defcodepage = locale.getpreferredencoding()
sFit = svcFit.getInstance()
- fits = []
- for path in paths:
- if callback: # Pulse
- pyfalog.debug("Processing file:\n{0}", path)
- wx.CallAfter(callback, 1, "Processing file:\n%s" % path)
+ fit_list = []
+ try:
+ for path in paths:
+ if iportuser: # Pulse
+ msg = "Processing file:\n%s" % path
+ pyfalog.debug(msg)
+ PortProcessing.notify(iportuser, IPortUser.PROCESS_IMPORT | IPortUser.ID_UPDATE, msg)
+ #wx.CallAfter(callback, 1, msg)
- file_ = open(path, "r")
- srcString = file_.read()
+ file_ = open(path, "r")
+ srcString = file_.read()
- if len(srcString) == 0: # ignore blank files
- pyfalog.debug("File is blank.")
- continue
+ if len(srcString) == 0: # ignore blank files
+ pyfalog.debug("File is blank.")
+ continue
- codec_found = None
- # If file had ANSI encoding, decode it to unicode using detection
- # of BOM header or if there is no header try default
- # codepage then fallback to utf-16, cp1252
+ codec_found = None
+ # If file had ANSI encoding, decode it to unicode using detection
+ # of BOM header or if there is no header try default
+ # codepage then fallback to utf-16, cp1252
- if isinstance(srcString, str):
- savebom = None
+ if isinstance(srcString, str):
+ savebom = None
- encoding_map = (
- ('\xef\xbb\xbf', 'utf-8'),
- ('\xff\xfe\0\0', 'utf-32'),
- ('\0\0\xfe\xff', 'UTF-32BE'),
- ('\xff\xfe', 'utf-16'),
- ('\xfe\xff', 'UTF-16BE'))
+ encoding_map = (
+ ('\xef\xbb\xbf', 'utf-8'),
+ ('\xff\xfe\0\0', 'utf-32'),
+ ('\0\0\xfe\xff', 'UTF-32BE'),
+ ('\xff\xfe', 'utf-16'),
+ ('\xfe\xff', 'UTF-16BE'))
- for bom, encoding in encoding_map:
- if srcString.startswith(bom):
- codec_found = encoding
- savebom = bom
+ for bom, encoding in encoding_map:
+ if srcString.startswith(bom):
+ codec_found = encoding
+ savebom = bom
+
+ if codec_found is None:
+ pyfalog.info("Unicode BOM not found in file {0}.", path)
+ attempt_codecs = (defcodepage, "utf-8", "utf-16", "cp1252")
+
+ for page in attempt_codecs:
+ try:
+ pyfalog.info("Attempting to decode file {0} using {1} page.", path, page)
+ srcString = unicode(srcString, page)
+ codec_found = page
+ pyfalog.info("File {0} decoded using {1} page.", path, page)
+ except UnicodeDecodeError:
+ pyfalog.info("Error unicode decoding {0} from page {1}, trying next codec", path, page)
+ else:
+ break
+ else:
+ pyfalog.info("Unicode BOM detected in {0}, using {1} page.", path, codec_found)
+ srcString = unicode(srcString[len(savebom):], codec_found)
+
+ else:
+ # nasty hack to detect other transparent utf-16 loading
+ if srcString[0] == '<' and 'utf-16' in srcString[:128].lower():
+ codec_found = "utf-16"
+ else:
+ codec_found = "utf-8"
if codec_found is None:
- pyfalog.info("Unicode BOM not found in file {0}.", path)
- attempt_codecs = (defcodepage, "utf-8", "utf-16", "cp1252")
+ return False, "Proper codec could not be established for %s" % path
- for page in attempt_codecs:
- try:
- pyfalog.info("Attempting to decode file {0} using {1} page.", path, page)
- srcString = unicode(srcString, page)
- codec_found = page
- pyfalog.info("File {0} decoded using {1} page.", path, page)
- except UnicodeDecodeError:
- pyfalog.info("Error unicode decoding {0} from page {1}, trying next codec", path, page)
- else:
- break
- else:
- pyfalog.info("Unicode BOM detected in {0}, using {1} page.", path, codec_found)
- srcString = unicode(srcString[len(savebom):], codec_found)
+ try:
+ _, fitsImport = Port.importAuto(srcString, path, iportuser=iportuser, encoding=codec_found)
+ fit_list += fitsImport
+ except xml.parsers.expat.ExpatError:
+ pyfalog.warning("Malformed XML in:\n{0}", path)
+ return False, "Malformed XML in %s" % path
- else:
- # nasty hack to detect other transparent utf-16 loading
- if srcString[0] == '<' and 'utf-16' in srcString[:128].lower():
- codec_found = "utf-16"
- else:
- codec_found = "utf-8"
+ # IDs = [] # NOTE: what use for IDs?
+ numFits = len(fit_list)
+ for idx, fit in enumerate(fit_list):
+ # Set some more fit attributes and save
+ fit.character = sFit.character
+ fit.damagePattern = sFit.pattern
+ fit.targetResists = sFit.targetResists
+ db.save(fit)
+ # IDs.append(fit.ID)
+ if iportuser: # Pulse
+ pyfalog.debug("Processing complete, saving fits to database: {0}/{1}", idx + 1, numFits)
+ PortProcessing.notify(
+ iportuser, IPortUser.PROCESS_IMPORT | IPortUser.ID_UPDATE,
+ "Processing complete, saving fits to database\n(%d/%d) %s" % (idx + 1, numFits, fit.ship.name)
+ )
- if codec_found is None:
- return False, "Proper codec could not be established for %s" % path
+ except UserCancelException:
+ return False, "Processing has been canceled.\n"
+ except Exception as e:
+ pyfalog.critical("Unknown exception processing: {0}", path)
+ pyfalog.critical(e)
+ # TypeError: not all arguments converted during string formatting
+# return False, "Unknown Error while processing {0}" % path
+ return False, "Unknown Error while processing %s\n\n message: %s" % (path, e.message)
- try:
- _, fitsImport = Port.importAuto(srcString, path, callback=callback, encoding=codec_found)
- fits += fitsImport
- except xml.parsers.expat.ExpatError:
- pyfalog.warning("Malformed XML in:\n{0}", path)
- return False, "Malformed XML in %s" % path
- except Exception as e:
- pyfalog.critical("Unknown exception processing: {0}", path)
- pyfalog.critical(e)
- return False, "Unknown Error while processing {0}" % path
+ return True, fit_list
- IDs = []
- numFits = len(fits)
- for i, fit in enumerate(fits):
- # Set some more fit attributes and save
- fit.character = sFit.character
- fit.damagePattern = sFit.pattern
- fit.targetResists = sFit.targetResists
- db.save(fit)
- IDs.append(fit.ID)
- if callback: # Pulse
- pyfalog.debug("Processing complete, saving fits to database: {0}/{1}", i + 1, numFits)
- wx.CallAfter(
- callback, 1,
- "Processing complete, saving fits to database\n(%d/%d)" %
- (i + 1, numFits)
- )
-
- return True, fits
@staticmethod
def importFitFromBuffer(bufferStr, activeFit=None):
@@ -228,7 +362,9 @@ class Port(object):
fit['ship']['id'] = ofit.ship.item.ID
fit['ship']['name'] = ''
- fit['description'] = "" % ofit.ID
+ # 2017/03/29 NOTE: "<" or "<" is Ignored
+ # fit['description'] = "" % ofit.ID
+ fit['description'] = ofit.notes if ofit.notes is not None else ""
fit['items'] = []
slotNum = {}
@@ -302,7 +438,7 @@ class Port(object):
return json.dumps(fit)
@classmethod
- def importAuto(cls, string, path=None, activeFit=None, callback=None, encoding=None):
+ def importAuto(cls, string, path=None, activeFit=None, iportuser=None, encoding=None):
# Get first line and strip space symbols of it to avoid possible detection errors
firstLine = re.split("[\n\r]+", string.strip(), maxsplit=1)[0]
firstLine = firstLine.strip()
@@ -310,9 +446,9 @@ class Port(object):
# If XML-style start of tag encountered, detect as XML
if re.match("<", firstLine):
if encoding:
- return "XML", cls.importXml(string, callback, encoding)
+ return "XML", cls.importXml(string, iportuser, encoding)
else:
- return "XML", cls.importXml(string, callback)
+ return "XML", cls.importXml(string, iportuser)
# If JSON-style start, parse as CREST/JSON
if firstLine[0] == '{':
@@ -323,7 +459,7 @@ class Port(object):
if re.match("\[.*\]", firstLine) and path is not None:
filename = os.path.split(path)[1]
shipName = filename.rsplit('.')[0]
- return "EFT Config", cls.importEftCfg(shipName, string, callback)
+ return "EFT Config", cls.importEftCfg(shipName, string, iportuser)
# If no file is specified and there's comma between brackets,
# consider that we have [ship, setup name] and detect like eft export format
@@ -335,22 +471,26 @@ class Port(object):
@staticmethod
def importCrest(str_):
- fit = json.loads(str_)
- sMkt = Market.getInstance()
- f = Fit()
- f.name = fit['name']
+ sMkt = Market.getInstance()
+ fitobj = Fit()
+ refobj = json.loads(str_)
+ items = refobj['items']
+ fitobj.name = refobj['name']
+ # 2017/03/29: read description
+ # "<" and ">" is replace to "<", ">" by EVE client
+ fitobj.notes = refobj['description']
try:
+ refobj = refobj['ship']['id']
try:
- f.ship = Ship(sMkt.getItem(fit['ship']['id']))
+ fitobj.ship = Ship(sMkt.getItem(refobj))
except ValueError:
- f.ship = Citadel(sMkt.getItem(fit['ship']['id']))
+ fitobj.ship = Citadel(sMkt.getItem(refobj))
except:
pyfalog.warning("Caught exception in importCrest")
return None
- items = fit['items']
items.sort(key=lambda k: k['flag'])
moduleList = []
@@ -360,14 +500,14 @@ class Port(object):
if module['flag'] == INV_FLAG_DRONEBAY:
d = Drone(item)
d.amount = module['quantity']
- f.drones.append(d)
+ fitobj.drones.append(d)
elif module['flag'] == INV_FLAG_CARGOBAY:
c = Cargo(item)
c.amount = module['quantity']
- f.cargo.append(c)
+ fitobj.cargo.append(c)
elif module['flag'] == INV_FLAG_FIGHTER:
fighter = Fighter(item)
- f.fighters.append(fighter)
+ fitobj.fighters.append(fighter)
else:
try:
m = Module(item)
@@ -377,8 +517,8 @@ class Port(object):
continue
# Add subsystems before modules to make sure T3 cruisers have subsystems installed
if item.category.name == "Subsystem":
- if m.fits(f):
- f.modules.append(m)
+ if m.fits(fitobj):
+ fitobj.modules.append(m)
else:
if m.isValidState(State.ACTIVE):
m.state = State.ACTIVE
@@ -390,13 +530,13 @@ class Port(object):
continue
# Recalc to get slot numbers correct for T3 cruisers
- svcFit.getInstance().recalc(f)
+ svcFit.getInstance().recalc(fitobj)
for module in moduleList:
- if module.fits(f):
- f.modules.append(module)
+ if module.fits(fitobj):
+ fitobj.modules.append(module)
- return f
+ return fitobj
@staticmethod
def importDna(string):
@@ -635,7 +775,7 @@ class Port(object):
return fit
@staticmethod
- def importEftCfg(shipname, contents, callback=None):
+ def importEftCfg(shipname, contents, iportuser=None):
"""Handle import from EFT config store file"""
# Check if we have such ship in database, bail if we don't
@@ -648,7 +788,7 @@ class Port(object):
# If client didn't take care of encoding file contents into Unicode,
# do it using fallback encoding ourselves
if isinstance(contents, str):
- contents = unicode(contents, "cp1252")
+ contents = unicode(contents, locale.getpreferredencoding())
fits = [] # List for fits
fitIndices = [] # List for starting line numbers for each fit
@@ -671,14 +811,14 @@ class Port(object):
try:
# Create fit object
- f = Fit()
+ fitobj = Fit()
# Strip square brackets and pull out a fit name
- f.name = fitLines[0][1:-1]
+ fitobj.name = fitLines[0][1:-1]
# Assign ship to fitting
try:
- f.ship = Ship(sMkt.getItem(shipname))
+ fitobj.ship = Ship(sMkt.getItem(shipname))
except ValueError:
- f.ship = Citadel(sMkt.getItem(shipname))
+ fitobj.ship = Citadel(sMkt.getItem(shipname))
moduleList = []
for x in range(1, len(fitLines)):
@@ -689,6 +829,8 @@ class Port(object):
# Parse line into some data we will need
misc = re.match("(Drones|Implant|Booster)_(Active|Inactive)=(.+)", line)
cargo = re.match("Cargohold=(.+)", line)
+ # 2017/03/27 NOTE: store description from EFT
+ description = re.match("Description=(.+)", line)
if misc:
entityType = misc.group(1)
@@ -713,11 +855,11 @@ class Port(object):
d.amountActive = droneAmount
elif entityState == "Inactive":
d.amountActive = 0
- f.drones.append(d)
+ fitobj.drones.append(d)
elif droneItem.category.name == "Fighter": # EFT saves fighter as drones
ft = Fighter(droneItem)
ft.amount = int(droneAmount) if ft.amount <= ft.fighterSquadronMaxSize else ft.fighterSquadronMaxSize
- f.fighters.append(ft)
+ fitobj.fighters.append(ft)
else:
continue
elif entityType == "Implant":
@@ -735,7 +877,7 @@ class Port(object):
imp.active = True
elif entityState == "Inactive":
imp.active = False
- f.implants.append(imp)
+ fitobj.implants.append(imp)
elif entityType == "Booster":
# Bail if we can't get item or it's not from implant category
try:
@@ -752,7 +894,7 @@ class Port(object):
b.active = True
elif entityState == "Inactive":
b.active = False
- f.boosters.append(b)
+ fitobj.boosters.append(b)
# If we don't have any prefixes, then it's a module
elif cargo:
cargoData = re.match("(.+),([0-9]+)", cargo.group(1))
@@ -767,7 +909,10 @@ class Port(object):
# Add Cargo to the fitting
c = Cargo(item)
c.amount = cargoAmount
- f.cargo.append(c)
+ fitobj.cargo.append(c)
+ # 2017/03/27 NOTE: store description from EFT
+ elif description:
+ fitobj.notes = description.group(1).replace("|", "\n")
else:
withCharge = re.match("(.+),(.+)", line)
modName = withCharge.group(1) if withCharge else line
@@ -784,10 +929,10 @@ class Port(object):
# Add subsystems before modules to make sure T3 cruisers have subsystems installed
if modItem.category.name == "Subsystem":
- if m.fits(f):
- f.modules.append(m)
+ if m.fits(fitobj):
+ fitobj.modules.append(m)
else:
- m.owner = f
+ m.owner = fitobj
# Activate mod if it is activable
if m.isValidState(State.ACTIVE):
m.state = State.ACTIVE
@@ -804,17 +949,21 @@ class Port(object):
moduleList.append(m)
# Recalc to get slot numbers correct for T3 cruisers
- svcFit.getInstance().recalc(f)
+ svcFit.getInstance().recalc(fitobj)
for module in moduleList:
- if module.fits(f):
- f.modules.append(module)
+ if module.fits(fitobj):
+ fitobj.modules.append(module)
# Append fit to list of fits
- fits.append(f)
+ fits.append(fitobj)
+
+ if iportuser: # NOTE: Send current processing status
+ PortProcessing.notify(
+ iportuser, IPortUser.PROCESS_IMPORT | IPortUser.ID_UPDATE,
+ "%s:\n%s" % (fitobj.ship.name, fitobj.name)
+ )
- if callback:
- wx.CallAfter(callback, None)
# Skip fit silently if we get an exception
except Exception as e:
pyfalog.error("Caught exception on fit.")
@@ -824,93 +973,95 @@ class Port(object):
return fits
@staticmethod
- def importXml(text, callback=None, encoding="utf-8"):
- sMkt = Market.getInstance()
+ def importXml(text, iportuser=None, encoding="utf-8"):
+ sMkt = Market.getInstance()
doc = xml.dom.minidom.parseString(text.encode(encoding))
fittings = doc.getElementsByTagName("fittings").item(0)
fittings = fittings.getElementsByTagName("fitting")
- fits = []
+ fit_list = []
+
+ for fitting in (fittings):
+ b_localized, fitobj = _resolveShip(fitting, sMkt)
+ # -- 170327 Ignored description --
+ # read description from exported xml. (EVE client, EFT)
+ description = fitting.getElementsByTagName("description").item(0).getAttribute("value")
+ if description is None:
+ description = ""
+ elif len(description):
+ # convert
to "\n" and remove html tags.
+ description = sequential_rep(description, r"<(br|BR)>", "\n",r"<[^<>]+>", "")
+# description = re.sub(r"<(br|BR)>", "\n", description)
+# description = re.sub(r"<[^<>]+>", "", description)
+ fitobj.notes = description
- for i, fitting in enumerate(fittings):
- f = Fit()
- f.name = fitting.getAttribute("name")
- # Maelstrom
- shipType = fitting.getElementsByTagName("shipType").item(0).getAttribute("value")
- try:
- try:
- f.ship = Ship(sMkt.getItem(shipType))
- except ValueError:
- f.ship = Citadel(sMkt.getItem(shipType))
- except Exception as e:
- pyfalog.warning("Caught exception on importXml")
- pyfalog.error(e)
- continue
hardwares = fitting.getElementsByTagName("hardware")
moduleList = []
for hardware in hardwares:
try:
- moduleName = hardware.getAttribute("type")
- try:
- item = sMkt.getItem(moduleName, eager="group.category")
- except Exception as e:
- pyfalog.warning("Caught exception on importXml")
- pyfalog.error(e)
+ item = _resolveModule(hardware, sMkt, b_localized)
+ if not item:
continue
- if item:
- if item.category.name == "Drone":
- d = Drone(item)
- d.amount = int(hardware.getAttribute("qty"))
- f.drones.append(d)
- elif item.category.name == "Fighter":
- ft = Fighter(item)
- ft.amount = int(hardware.getAttribute("qty")) if ft.amount <= ft.fighterSquadronMaxSize else ft.fighterSquadronMaxSize
- f.fighters.append(ft)
- elif hardware.getAttribute("slot").lower() == "cargo":
- # although the eve client only support charges in cargo, third-party programs
- # may support items or "refits" in cargo. Support these by blindly adding all
- # cargo, not just charges
- c = Cargo(item)
- c.amount = int(hardware.getAttribute("qty"))
- f.cargo.append(c)
- else:
- try:
- m = Module(item)
- # When item can't be added to any slot (unknown item or just charge), ignore it
- except ValueError:
- pyfalog.warning("item can't be added to any slot (unknown item or just charge), ignore it")
- continue
- # Add subsystems before modules to make sure T3 cruisers have subsystems installed
- if item.category.name == "Subsystem":
- if m.fits(f):
- m.owner = f
- f.modules.append(m)
- else:
- if m.isValidState(State.ACTIVE):
- m.state = State.ACTIVE
- moduleList.append(m)
+ if item.category.name == "Drone":
+ d = Drone(item)
+ d.amount = int(hardware.getAttribute("qty"))
+ fitobj.drones.append(d)
+ elif item.category.name == "Fighter":
+ ft = Fighter(item)
+ ft.amount = int(hardware.getAttribute("qty")) if ft.amount <= ft.fighterSquadronMaxSize else ft.fighterSquadronMaxSize
+ fitobj.fighters.append(ft)
+ elif hardware.getAttribute("slot").lower() == "cargo":
+ # although the eve client only support charges in cargo, third-party programs
+ # may support items or "refits" in cargo. Support these by blindly adding all
+ # cargo, not just charges
+ c = Cargo(item)
+ c.amount = int(hardware.getAttribute("qty"))
+ fitobj.cargo.append(c)
+ else:
+ try:
+ m = Module(item)
+ # When item can't be added to any slot (unknown item or just charge), ignore it
+ except ValueError:
+ pyfalog.warning("item can't be added to any slot (unknown item or just charge), ignore it")
+ continue
+ # Add subsystems before modules to make sure T3 cruisers have subsystems installed
+ if item.category.name == "Subsystem":
+ if m.fits(fitobj):
+ m.owner = fitobj
+ fitobj.modules.append(m)
+ else:
+ if m.isValidState(State.ACTIVE):
+ m.state = State.ACTIVE
+
+ moduleList.append(m)
except KeyboardInterrupt:
pyfalog.warning("Keyboard Interrupt")
continue
# Recalc to get slot numbers correct for T3 cruisers
- svcFit.getInstance().recalc(f)
+ svcFit.getInstance().recalc(fitobj)
for module in moduleList:
- if module.fits(f):
- module.owner = f
- f.modules.append(module)
+ if module.fits(fitobj):
+ module.owner = fitobj
+ fitobj.modules.append(module)
- fits.append(f)
- if callback:
- wx.CallAfter(callback, None)
+ fit_list.append(fitobj)
+ if iportuser: # NOTE: Send current processing status
+ PortProcessing.notify(
+ iportuser, IPortUser.PROCESS_IMPORT | IPortUser.ID_UPDATE,
+ "analysis :%s\n%s" % (fitobj.ship.name, fitobj.name)
+ )
- return fits
+ return fit_list
@staticmethod
def _exportEftBase(fit):
+ """Basically EFT format does not require blank lines
+ also, it's OK to arrange modules randomly?
+ """
offineSuffix = " /OFFLINE"
export = "[%s, %s]\n" % (fit.ship.item.name, fit.name)
stuff = {}
@@ -1038,10 +1189,13 @@ class Port(object):
return dna + "::"
- @classmethod
- def exportXml(cls, callback=None, *fits):
+ @staticmethod
+ def exportXml(iportuser=None, *fits):
doc = xml.dom.minidom.Document()
fittings = doc.createElement("fittings")
+ # fit count
+ fit_count = len(fits)
+ fittings.setAttribute("count", "%s" % fit_count)
doc.appendChild(fittings)
sFit = svcFit.getInstance()
@@ -1051,10 +1205,18 @@ class Port(object):
fitting.setAttribute("name", fit.name)
fittings.appendChild(fitting)
description = doc.createElement("description")
- description.setAttribute("value", "")
+ # -- 170327 Ignored description --
+ try:
+ notes = fit.notes # unicode
+ description.setAttribute(
+ "value", re.sub("[\r\n]+", "
", notes) if notes is not None else ""
+ )
+ except Exception as e:
+ pyfalog.warning("read description is failed, msg=%s\n" % e.args)
+
fitting.appendChild(description)
shipType = doc.createElement("shipType")
- shipType.setAttribute("value", fit.ship.item.name)
+ shipType.setAttribute("value", fit.ship.name)
fitting.appendChild(shipType)
charges = {}
@@ -1113,12 +1275,17 @@ class Port(object):
hardware.setAttribute("slot", "cargo")
hardware.setAttribute("type", name)
fitting.appendChild(hardware)
- except:
- print("Failed on fitID: %d" % fit.ID)
+ except Exception as e:
+# print("Failed on fitID: %d" % fit.ID)
+ pyfalog.error("Failed on fitID: %d, message: %s" % e.message)
continue
finally:
- if callback:
- wx.CallAfter(callback, i)
+ if iportuser:
+ PortProcessing.notify(
+ iportuser, IPortUser.PROCESS_EXPORT | IPortUser.ID_UPDATE,
+ (i, "convert to xml (%s/%s) %s" % (i + 1, fit_count, fit.ship.name))
+ )
+# wx.CallAfter(callback, i, "(%s/%s) %s" % (i, fit_count, fit.ship.name))
return doc.toprettyxml()
@@ -1169,37 +1336,34 @@ class Port(object):
return export
-class FitBackupThread(threading.Thread):
- def __init__(self, path, callback):
- threading.Thread.__init__(self)
- self.path = path
- self.callback = callback
-
- def run(self):
- path = self.path
- sFit = svcFit.getInstance()
- sPort = Port.getInstance()
- backedUpFits = sPort.exportXml(self.callback, *sFit.getAllFits())
- backupFile = open(path, "w", encoding="utf-8")
- backupFile.write(backedUpFits)
- backupFile.close()
-
+class PortProcessing(object):
+ """Port Processing class """
+ @staticmethod
+ def backupFits(path, iportuser):
+ success = True
+ try:
+ iportuser.onPortProcessStart()
+ backedUpFits = Port.exportXml(iportuser, *svcFit.getInstance().getAllFits())
+ backupFile = open(path, "w", encoding="utf-8")
+ backupFile.write(backedUpFits)
+ backupFile.close()
+ except UserCancelException:
+ success = False
# Send done signal to GUI
- wx.CallAfter(self.callback, -1)
+# wx.CallAfter(callback, -1, "Done.")
+ flag = IPortUser.ID_ERROR if not success else IPortUser.ID_DONE
+ iportuser.onPortProcessing(IPortUser.PROCESS_EXPORT | flag,
+ "User canceled or some error occurrence." if not success else "Done.")
+ @staticmethod
+ def importFitsFromFile(paths, iportuser):
+ iportuser.onPortProcessStart()
+ success, result = Port.importFitFromFiles(paths, iportuser)
+ flag = IPortUser.ID_ERROR if not success else IPortUser.ID_DONE
+ iportuser.onPortProcessing(IPortUser.PROCESS_IMPORT | flag, result)
-class FitImportThread(threading.Thread):
- def __init__(self, paths, callback):
- threading.Thread.__init__(self)
- self.paths = paths
- self.callback = callback
+ @staticmethod
+ def notify(iportuser, flag, data):
+ if not iportuser.onPortProcessing(flag, data):
+ raise UserCancelException
- def run(self):
- sPort = Port.getInstance()
- success, result = sPort.importFitFromFiles(self.paths, self.callback)
-
- if not success: # there was an error during processing
- pyfalog.error("Error while processing file import: {0}", result)
- wx.CallAfter(self.callback, -2, result)
- else: # Send done signal to GUI
- wx.CallAfter(self.callback, -1, result)
diff --git a/utils/strfunctions.py b/utils/strfunctions.py
new file mode 100644
index 000000000..a1decbd1c
--- /dev/null
+++ b/utils/strfunctions.py
@@ -0,0 +1,34 @@
+'''
+ string manipulation module
+'''
+import re
+
+
+def sequential_rep(text_, *args):
+ """
+ params
+ text_: string content
+ args : , , , , ...
+
+ return
+ empty string when text_ length was zero or invalid.
+ """
+
+ if not text_ or not len(text_):
+ return ""
+
+ arg_len = len(args)
+ i = 0
+ while i < arg_len:
+ text_ = re.sub(args[i], args[i + 1], text_)
+ i += 2
+
+ return text_
+
+def replaceLTGT(text_):
+ """if fit name contained "<" or ">" then reprace to named html entity by EVE client.
+
+ for fit name.
+ """
+ return text_.replace("<", "<").replace(">", ">") if isinstance(text_, unicode) else text_
+