diff --git a/gui/mainFrame.py b/gui/mainFrame.py
index fcc901b3b..777c608b5 100644
--- a/gui/mainFrame.py
+++ b/gui/mainFrame.py
@@ -877,19 +877,19 @@ class MainFrame(wx.Frame, IPortUser):
else:
self.progressDialog.Update(info)
- def onPortProcessStart(self):
+ def on_port_process_start(self):
# flag for progress dialog.
self.__progress_flag = True
- def onPortProcessing(self, action, data=None):
+ def on_port_processing(self, action, data=None):
# 2017/03/29 NOTE: implementation like interface
wx.CallAfter(
- self._onPortProcessing, action, data
+ self._on_port_processing, action, data
)
return self.__progress_flag
- def _onPortProcessing(self, action, data):
+ def _on_port_processing(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
diff --git a/service/port.py b/service/port.py
index b2b7163c1..b7bdd95ac 100644
--- a/service/port.py
+++ b/service/port.py
@@ -47,7 +47,7 @@ from eos.saveddata.ship import Ship
from eos.saveddata.citadel import Citadel
from eos.saveddata.fit import Fit
from service.market import Market
-from utils.strfunctions import sequential_rep, replaceLTGT
+from utils.strfunctions import sequential_rep, replace_ltgt
from abc import ABCMeta, abstractmethod
if 'wxMac' not in wx.PlatformInfo or ('wxMac' in wx.PlatformInfo and wx.VERSION >= (3, 0)):
@@ -73,25 +73,39 @@ INV_FLAG_CARGOBAY = 5
INV_FLAG_DRONEBAY = 87
INV_FLAG_FIGHTER = 158
+# 2017/04/05 NOTE: simple validation, for xml file
+RE_XML_START = r'<\?xml\s+version="1.0"\s+\?>'
# -- 170327 Ignored description --
-localized_pattern = re.compile(r'([^\*]+)\*')
+RE_LTGT = "&(lt|gt);"
+L_MARK = "<localized hint=""
+LOCALIZED_PATTERN = re.compile(r'([^\*]+)\*')
-def _resolveShip(fitting, sMkt):
+def _extract_matche(t):
+ m = LOCALIZED_PATTERN.match(t)
+ # hint attribute, text content
+ return m.group(1), m.group(2)
+
+
+def _resolve_ship(fitting, sMkt, b_localized):
+ # type: (xml.dom.minidom.Element, Market, bool) -> eos.saveddata.fit.Fit
+
fitobj = Fit()
+ fitobj.name = fitting.getAttribute("name")
# 2017/03/29 NOTE:
# if fit name contained "<" or ">" then reprace to named html entity by EVE client
- fitobj.name = replaceLTGT(fitting.getAttribute("name"))
+ # if re.search(RE_LTGT, fitobj.name):
+ if "<" in fitobj.name or ">" in fitobj.name:
+ fitobj.name = replace_ltgt(fitobj.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)
+ # emergency = None
+ if b_localized:
+ # expect an official name, emergency cache
+ shipType, emergency = _extract_matche(shipType)
+
limit = 2
while True:
must_retry = False
@@ -101,24 +115,28 @@ def _resolveShip(fitting, sMkt):
except ValueError:
fitobj.ship = Citadel(sMkt.getItem(shipType))
except Exception as e:
- pyfalog.warning("Caught exception on _resolveShip")
+ pyfalog.warning("Caught exception on _resolve_ship")
pyfalog.error(e)
+ limit -= 1
+ if limit is 0:
+ break
shipType = emergency
must_retry = True
- limit -= 1
- if not must_retry or limit is 0:
+ if not must_retry:
break
- # True means localized
- return matches is not None, fitobj
+
+ return fitobj
-def _resolveModule(hardware, sMkt, b_localized):
+def _resolve_module(hardware, sMkt, b_localized):
+ # type: (xml.dom.minidom.Element, Market, bool) -> eos.saveddata.module.Module
+
moduleName = hardware.getAttribute("type")
- emergency = None
+ # emergency = None
if b_localized:
- emergency = localized_pattern.sub("\g<2>", moduleName)
- # expect an official name
- moduleName = localized_pattern.sub("\g<1>", moduleName)
+ # expect an official name, emergency cache
+ moduleName, emergency = _extract_matche(moduleName)
+
item = None
limit = 2
while True:
@@ -126,12 +144,14 @@ def _resolveModule(hardware, sMkt, b_localized):
try:
item = sMkt.getItem(moduleName, eager="group.category")
except Exception as e:
- pyfalog.warning("Caught exception on _resolveModule")
+ pyfalog.warning("Caught exception on _resolve_module")
pyfalog.error(e)
+ limit -= 1
+ if limit is 0:
+ break
moduleName = emergency
must_retry = True
- limit -= 1
- if not must_retry or limit is 0:
+ if not must_retry:
break
return item
@@ -160,7 +180,7 @@ class IPortUser:
# means import process.
@abstractmethod
- def onPortProcessing(self, action, data=None):
+ def on_port_processing(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
@@ -178,7 +198,7 @@ class IPortUser:
"""return: True is continue process, False is cancel."""
pass
- def onPortProcessStart(self):
+ def on_port_process_start(self):
pass
@@ -189,6 +209,7 @@ class Port(object):
2. i think should not write wx.CallAfter in here
"""
instance = None
+ __tag_replace_flag = True
@classmethod
def getInstance(cls):
@@ -197,6 +218,16 @@ class Port(object):
return cls.instance
+ @classmethod
+ def set_tag_replace(cls, b):
+ cls.__tag_replace_flag = b
+
+ @classmethod
+ def is_tag_replace(cls):
+ # might there is a person who wants to hold tags.
+ # (item link in EVE client etc. When importing again to EVE)
+ return cls.__tag_replace_flag
+
@staticmethod
def backupFits(path, iportuser):
pyfalog.debug("Starting backup fits thread.")
@@ -209,7 +240,12 @@ class Port(object):
@staticmethod
def importFitsThreaded(paths, iportuser):
- """param iportuser: IPortUser implemented class"""
+ # type: (tuple, IPortUser) -> None
+ """
+ :param paths: fits data file path list.
+ :param iportuser: IPortUser implemented class.
+ :rtype: None
+ """
pyfalog.debug("Starting import fits thread.")
# thread = FitImportThread(paths, iportuser)
# thread.start()
@@ -238,8 +274,8 @@ class Port(object):
PortProcessing.notify(iportuser, IPortUser.PROCESS_IMPORT | IPortUser.ID_UPDATE, msg)
# wx.CallAfter(callback, 1, msg)
- file_ = open(path, "r")
- srcString = file_.read()
+ with open(path, "r") as file_:
+ srcString = file_.read()
if len(srcString) == 0: # ignore blank files
pyfalog.debug("File is blank.")
@@ -442,7 +478,7 @@ class Port(object):
firstLine = firstLine.strip()
# If XML-style start of tag encountered, detect as XML
- if re.match("<", firstLine):
+ if re.match(RE_XML_START, firstLine):
if encoding:
return "XML", cls.importXml(string, iportuser, encoding)
else:
@@ -975,12 +1011,16 @@ class Port(object):
sMkt = Market.getInstance()
doc = xml.dom.minidom.parseString(text.encode(encoding))
+ # NOTE:
+ # When L_MARK is included at this point,
+ # Decided to be localized data
+ b_localized = L_MARK in text
fittings = doc.getElementsByTagName("fittings").item(0)
fittings = fittings.getElementsByTagName("fitting")
fit_list = []
for fitting in (fittings):
- b_localized, fitobj = _resolveShip(fitting, sMkt)
+ fitobj = _resolve_ship(fitting, sMkt, b_localized)
# -- 170327 Ignored description --
# read description from exported xml. (EVE client, EFT)
description = fitting.getElementsByTagName("description").item(0).getAttribute("value")
@@ -988,16 +1028,17 @@ class Port(object):
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)
+ if Port.is_tag_replace():
+ description = replace_ltgt(
+ sequential_rep(description, r"<(br|BR)>", "\n", r"<[^<>]+>", "")
+ )
fitobj.notes = description
hardwares = fitting.getElementsByTagName("hardware")
moduleList = []
for hardware in hardwares:
try:
- item = _resolveModule(hardware, sMkt, b_localized)
+ item = _resolve_module(hardware, sMkt, b_localized)
if not item:
continue
@@ -1340,7 +1381,7 @@ class PortProcessing(object):
def backupFits(path, iportuser):
success = True
try:
- iportuser.onPortProcessStart()
+ iportuser.on_port_process_start()
backedUpFits = Port.exportXml(iportuser, *svcFit.getInstance().getAllFits())
backupFile = open(path, "w", encoding="utf-8")
backupFile.write(backedUpFits)
@@ -1350,17 +1391,17 @@ class PortProcessing(object):
# Send done signal to GUI
# wx.CallAfter(callback, -1, "Done.")
flag = IPortUser.ID_ERROR if not success else IPortUser.ID_DONE
- iportuser.onPortProcessing(IPortUser.PROCESS_EXPORT | flag,
+ iportuser.on_port_processing(IPortUser.PROCESS_EXPORT | flag,
"User canceled or some error occurrence." if not success else "Done.")
@staticmethod
def importFitsFromFile(paths, iportuser):
- iportuser.onPortProcessStart()
+ iportuser.on_port_process_start()
success, result = Port.importFitFromFiles(paths, iportuser)
flag = IPortUser.ID_ERROR if not success else IPortUser.ID_DONE
- iportuser.onPortProcessing(IPortUser.PROCESS_IMPORT | flag, result)
+ iportuser.on_port_processing(IPortUser.PROCESS_IMPORT | flag, result)
@staticmethod
def notify(iportuser, flag, data):
- if not iportuser.onPortProcessing(flag, data):
+ if not iportuser.on_port_processing(flag, data):
raise UserCancelException
diff --git a/utils/stopwatch.py b/utils/stopwatch.py
new file mode 100644
index 000000000..c14339e90
--- /dev/null
+++ b/utils/stopwatch.py
@@ -0,0 +1,107 @@
+# coding: utf-8
+
+import time
+import os
+
+# 2017/04/05:
+class Stopwatch(object):
+ """
+ --- on python console ---
+import re
+from utils.stopwatch import Stopwatch
+# measurement re.sub
+def m_re_sub(t, set_count, executes, texts):
+ t.reset()
+ while set_count:
+ set_count -= 1
+ with t:
+ while executes:
+ executes -= 1
+ ret = re.sub("[a|s]+", "-", texts)
+ # stat string
+ return str(t)
+
+# measurementor
+stpwth = Stopwatch("test")
+# statistics loop: 1000(exec re.sub: 100000)
+m_re_sub(stpwth, 1000, 100000, "asdfadsasdaasdfadsasda")
+
+----------- records -----------
+ text: "asdfadsasda"
+ 'elapsed record(ms): min=0.000602411446948, max=220.85578571'
+ 'elapsed record(ms): min=0.000602411446948, max=217.331377504'
+
+ text: "asdfadsasdaasdfadsasda"
+ 'elapsed record(ms): min=0.000602411446948, max=287.784902967'
+ 'elapsed record(ms): min=0.000602411432737, max=283.653264016'
+
+ NOTE: about max
+ The value is large only at the first execution,
+ Will it be optimized, after that it will be significantly smaller
+ """
+
+ # time.clock() is μs? 1/1000ms
+ # https://docs.python.jp/2.7/library/time.html#time.clock
+ _tfunc = time.clock if os.name == "nt" else time.time
+
+ def __init__(self, name='', logger=None):
+ self.name = name
+ self.start = Stopwatch._tfunc()
+ self.__last = self.start
+ # __last field is means last checkpoint system clock value?
+ self.logger = logger
+ self.reset()
+
+ @property
+ def stat(self):
+ # :return: (float, float)
+ return (self.min, self.max)
+
+ @property
+ def elapsed(self):
+ # :return: time as ms
+ return (Stopwatch._tfunc() - self.start) * 1000
+
+ @property
+ def last(self):
+ return self.__last * 1000
+
+ def __update_stat(self, v):
+ # :param v: unit of ms
+ if self.min == 0.0: self.min = v
+ if self.max < v: self.max = v
+ if self.min > v: self.min = v
+
+ def checkpoint(self, name=''):
+ span = self.elapsed
+ self.__update_stat(span)
+ text = u'Stopwatch("{tname}") - {checkpoint} - {last:.6f}ms ({elapsed:.12f}ms elapsed)'.format(
+ tname=self.name,
+ checkpoint=unicode(name, "utf-8"),
+ last=self.last,
+ elapsed=span
+ ).strip()
+ self.__last = Stopwatch._tfunc()
+ if self.logger:
+ self.logger.debug(text)
+ else:
+ print(text)
+
+ def reset(self):
+ self.min = 0.0
+ self.max = 0.0
+
+ def __enter__(self):
+ self.start = Stopwatch._tfunc()
+ return self
+
+ def __exit__(self, type, value, traceback):
+ # https://docs.python.org/2.7/reference/datamodel.html?highlight=__enter__#object.__exit__
+ # If the context was exited without an exception, all three arguments will be None
+ self.checkpoint('finished')
+ # ex: "type=None, value=None, traceback=None"
+ # print "type=%s, value=%s, traceback=%s" % (type, value, traceback)
+ return True
+
+ def __repr__(self):
+ return "elapsed record(ms): min=%s, max=%s" % self.stat
diff --git a/utils/strfunctions.py b/utils/strfunctions.py
index cd769d20b..ec6013284 100644
--- a/utils/strfunctions.py
+++ b/utils/strfunctions.py
@@ -5,30 +5,26 @@ import re
def sequential_rep(text_, *args):
+ # type: (basestring, *list) -> basestring
"""
- params
- text_: string content
- args : , , , , ...
-
- return
- empty string when text_ length was zero or invalid.
+ :param text_: string content
+ :param args: like , , , , ...
+ :return: if text_ length was zero or invalid parameters then no manipulation to text_
"""
-
- 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
+ if arg_len % 2 == 0 and isinstance(text_, basestring) and len(text_) > 0:
+ i = 0
+ while i < arg_len:
+ text_ = re.sub(args[i], args[i + 1], text_)
+ i += 2
return text_
-def replaceLTGT(text_):
+def replace_ltgt(text_):
+ # type: (basestring) -> basestring
"""if fit name contained "<" or ">" then reprace to named html entity by EVE client.
-
- for fit name.
+ :param text_: string content of fit name from exported by EVE client.
+ :return: if text_ is not instance of basestring then no manipulation to text_.
"""
- return text_.replace("<", "<").replace(">", ">") if isinstance(text_, unicode) else text_
+ return text_.replace("<", "<").replace(">", ">") if isinstance(text_, basestring) else text_