diff --git a/config.py b/config.py
index 78d575b1c..4184bd33d 100644
--- a/config.py
+++ b/config.py
@@ -30,6 +30,8 @@ expansionName = "YC120.3"
expansionVersion = "1.8"
evemonMinVersion = "4081"
+minItemSearchLength = 3
+
pyfaPath = None
savePath = None
saveDB = None
diff --git a/gui/builtinItemStatsViews/itemAttributes.py b/gui/builtinItemStatsViews/itemAttributes.py
index 9e18ce0ff..e40710c45 100644
--- a/gui/builtinItemStatsViews/itemAttributes.py
+++ b/gui/builtinItemStatsViews/itemAttributes.py
@@ -1,4 +1,3 @@
-import sys
import csv
import config
@@ -203,7 +202,7 @@ class ItemParams(wx.Panel):
else:
attrIcon = self.imageList.Add(BitmapLoader.getBitmap("7_15", "icons"))
- index = self.paramList.InsertItem(sys.maxsize, attrName, attrIcon)
+ index = self.paramList.InsertItem(self.paramList.GetItemCount(), attrName, attrIcon)
idNameMap[idCount] = attrName
self.paramList.SetItemData(index, idCount)
idCount += 1
diff --git a/gui/builtinItemStatsViews/itemCompare.py b/gui/builtinItemStatsViews/itemCompare.py
index f91a81e2a..9e9ca53af 100644
--- a/gui/builtinItemStatsViews/itemCompare.py
+++ b/gui/builtinItemStatsViews/itemCompare.py
@@ -1,5 +1,3 @@
-import sys
-
# noinspection PyPackageRequirements
import wx
@@ -147,7 +145,7 @@ class ItemCompare(wx.Panel):
self.paramList.SetColumnWidth(len(self.attrs) + 1, 60)
for item in self.items:
- i = self.paramList.InsertItem(sys.maxsize, item.name)
+ i = self.paramList.InsertItem(self.paramList.GetItemCount(), item.name)
for x, attr in enumerate(self.attrs.keys()):
if attr in item.attributes:
info = self.attrs[attr]
diff --git a/gui/builtinItemStatsViews/itemEffects.py b/gui/builtinItemStatsViews/itemEffects.py
index 8c3537ad0..e586786a6 100644
--- a/gui/builtinItemStatsViews/itemEffects.py
+++ b/gui/builtinItemStatsViews/itemEffects.py
@@ -1,4 +1,3 @@
-import sys
import os
import subprocess
import config
@@ -51,7 +50,7 @@ class ItemEffects(wx.Panel):
names.sort()
for name in names:
- index = self.effectList.InsertItem(sys.maxsize, name)
+ index = self.effectList.InsertItem(self.effectList.GetItemCount(), name)
if effects[name].isImplemented:
if effects[name].activeByDefault:
diff --git a/gui/builtinItemStatsViews/itemProperties.py b/gui/builtinItemStatsViews/itemProperties.py
index 8a7fdacbd..afc8c2645 100644
--- a/gui/builtinItemStatsViews/itemProperties.py
+++ b/gui/builtinItemStatsViews/itemProperties.py
@@ -1,5 +1,3 @@
-import sys
-
# noinspection PyPackageRequirements
import wx
@@ -79,7 +77,7 @@ class ItemProperties(wx.Panel):
attrName = name.title()
value = getattr(self.item, name)
- index = self.paramList.InsertItem(sys.maxsize, attrName)
+ index = self.paramList.InsertItem(self.paramList.GetItemCount(), attrName)
# index = self.paramList.InsertImageStringItem(sys.maxint, attrName)
idNameMap[idCount] = attrName
self.paramList.SetItemData(index, idCount)
diff --git a/gui/builtinMarketBrowser/itemView.py b/gui/builtinMarketBrowser/itemView.py
index eb0d6bb51..7a26cfed1 100644
--- a/gui/builtinMarketBrowser/itemView.py
+++ b/gui/builtinMarketBrowser/itemView.py
@@ -1,5 +1,6 @@
import wx
+import config
import gui.builtinMarketBrowser.pfSearchBox as SBox
from gui.contextMenu import ContextMenu
from gui.display import Display
@@ -170,10 +171,6 @@ class ItemView(Display):
if len(realsearch) == 0:
self.selectionMade()
return
- # Show nothing if query is too short
- elif len(realsearch) < 3:
- self.clearSearch()
- return
self.marketBrowser.searchMode = True
self.sMkt.searchItems(search, self.populateSearch)
diff --git a/gui/display.py b/gui/display.py
index 312f5916b..8ee42692e 100644
--- a/gui/display.py
+++ b/gui/display.py
@@ -17,7 +17,6 @@
# along with pyfa. If not, see .
# =============================================================================
-import sys
# noinspection PyPackageRequirements
import wx
import gui.mainFrame
@@ -182,13 +181,13 @@ class Display(wx.ListCtrl):
if listItemCount < stuffItemCount:
for i in range(stuffItemCount - listItemCount):
- self.InsertItem(sys.maxsize, "")
+ self.InsertItem(self.GetItemCount(), "")
if listItemCount > stuffItemCount:
if listItemCount - stuffItemCount > 20 > stuffItemCount:
self.DeleteAllItems()
for i in range(stuffItemCount):
- self.InsertItem(sys.maxsize, "")
+ self.InsertItem(self.GetItemCount(), "")
else:
for i in range(listItemCount - stuffItemCount):
self.DeleteItem(self.getLastItem())
diff --git a/requirements.txt b/requirements.txt
index d43bebe51..d93548e3a 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -8,4 +8,5 @@ esipy == 0.3.3
markdown2
packaging
roman
-beautifulsoup4
\ No newline at end of file
+beautifulsoup4
+PyYAML
diff --git a/service/jargon/__init__.py b/service/jargon/__init__.py
new file mode 100644
index 000000000..447c917e3
--- /dev/null
+++ b/service/jargon/__init__.py
@@ -0,0 +1,21 @@
+# =============================================================================
+# Copyright (C) 2018 Filip Sufitchi
+#
+# 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 .jargon import Jargon
+from .loader import JargonLoader
diff --git a/service/jargon/defaults.yaml b/service/jargon/defaults.yaml
new file mode 100644
index 000000000..0217bc87c
--- /dev/null
+++ b/service/jargon/defaults.yaml
@@ -0,0 +1,90 @@
+1: I
+2: II
+aar: Ancillary Armor Repairer
+ab: Afterburner
+ac: Autocannon
+am: Antimatter
+anp: Adaptive Nano Plating
+acr: Ancillary Current Router
+arty: Artillery
+asb: Ancillary Shield Booster
+bcs: Ballistic Control System
+bcu: Ballistic Control System
+boosh: Micro Jump Field Generator
+ccc: Capacitor Control Circuit
+cn: Caldari Navy
+cnam: Caldari Navy Antimatter
+cpr: Capacitor Power Relay
+cpu: Co-Processor
+coproc: Co-Processor
+dc: Damage Control
+dcu: Damage Control
+disco: Smartbomb
+eanm: Energized Adaptive Nano Membrane
+enam: Energized Adaptive Nano Membrane
+eccm: Sensor Booster
+fn: Federation Navy
+fnam: Federation Navy Antimatter
+gd: Guidance Disruptor
+ham: Heavy Assault Missile
+haml: Heavy Assault Missile Launcher
+hm: Heavy Missile
+hml: Heavy Missile Launcher
+istab: Inertial Stabilizer
+in: Imperial Navy
+inmf: Imperial Navy Multifrequency
+jam: ECM
+lar: Large Armor Repairer
+laar: Large Ancillary Armor Repairer
+lasb: Large Ancillary Shield Booster
+lm: Light Missile
+lmjd: Large Micro Jump Drive
+lml: Light Missile Launcher
+lo: Liquid Ozone
+lse: Large Shield Extender
+maar: Medium Ancillary Armor Repairer
+masb: Medium Ancillary Shield Booster
+mf: Multifrequency
+md: Guidance Disruptor
+mjfg: Micro Jump Field Generator
+mar: Medium Armor Repairer
+mfs: Magnetic Field Stabilizer
+mmjd: Medium Micro Jump Drive
+mjd: Micro Jump Drive
+mlu: Mining Laser Upgrade
+msb: Medium Shield Booster
+mse: Medium Shield Extender
+mwd: Microwarpdrive
+odi: Overdrive Injector
+point: Warp Disruptor
+pdu: Power Diagnostic Unit
+pp: Phased Plasma
+rcu: Reactor Control Unit
+rf: Republic Fleet
+rhml: Rapid Heavy Missile Launcher
+rl: Rocket Launcher
+rlml: Rapid Light Missile Launcher
+rr: Remote # Hacky, for shield, armor, and cap
+rtc: Remote Tracking Computer
+rtl: Rapid Torpedo Launcher
+sar: Small Armor Repairer
+saar: Small Ancillary Armor Repairer
+sasb: Small Ancillary Shield Booster
+sb: Sensor Booster # Or smartbomb? :/
+sebo: Sensor Booster
+sd: Sensor Dampener
+sg: Stasis Grappler
+ssb: Small Shield Booster
+sse: Small Shield Extender
+spr: Shield Power Relay
+sw: Stasis Webifier
+tc: Tracking Computer
+td: Tracking Disruptor
+te: Tracking enhancer
+tl: Remote Tracking Computer
+tp: Target Painter
+wcs: Warp Core Stabilizer
+web: stasis
+xl: X-Large
+xlasb: X-Large Ancillary Shield Booster
+xlsb: X-Large Shield Booster
diff --git a/service/jargon/header.yaml b/service/jargon/header.yaml
new file mode 100644
index 000000000..031effff6
--- /dev/null
+++ b/service/jargon/header.yaml
@@ -0,0 +1,14 @@
+# This is the default Pyfa jargon file.
+#
+# It is essentially a giant set of find/replace statements in order to translate
+# abbreviated Eve community terms into more useful full terms. It is intended
+# for translation of strings such as "haml 2" "into "Heavy Assault Missile Launcher II"..
+#
+# These abbreviations are not case-sensitive. If abbreviations collide, the
+# later one is used.
+#
+# Abbreviations with spaces are not supported.
+#
+# Syntax:
+#
+# abbreviation: full name
diff --git a/service/jargon/jargon.py b/service/jargon/jargon.py
new file mode 100644
index 000000000..0a5065faf
--- /dev/null
+++ b/service/jargon/jargon.py
@@ -0,0 +1,47 @@
+# =============================================================================
+# Copyright (C) 2018 Filip Sufitchi
+#
+# 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 .
+# =============================================================================
+
+import config
+import pkg_resources
+
+class Jargon(object):
+ def __init__(self, rawdata: dict):
+ self._rawdata = rawdata
+
+ # copy the data to lowercase keys, ignore blank keys
+ self._data = {str(k).lower():v for k,v in rawdata.items() if k}
+
+ def get(self, term: str) -> str:
+ return self._data.get(term.lower())
+
+ def get_rawdata() -> dict:
+ return self._rawdata
+
+ def apply(self, query):
+ query_words = query.split()
+ parts = []
+
+ for word in query_words:
+ replacement = self.get(word)
+ if replacement:
+ parts.append(replacement)
+ else:
+ parts.append(word)
+
+ return ' '.join(parts)
diff --git a/service/jargon/loader.py b/service/jargon/loader.py
new file mode 100644
index 000000000..11e95b7c4
--- /dev/null
+++ b/service/jargon/loader.py
@@ -0,0 +1,81 @@
+# =============================================================================
+# Copyright (C) 2018 Filip Sufitchi
+#
+# 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 .
+# =============================================================================
+
+import os
+import config
+import yaml
+
+from .jargon import Jargon
+from .resources import DEFAULT_DATA, DEFAULT_HEADER
+
+JARGON_PATH = os.path.join(config.savePath, 'jargon.yaml')
+
+class JargonLoader(object):
+ def __init__(self, jargon_path: str):
+ self.jargon_path = jargon_path
+ self._jargon_mtime = 0 # type: int
+ self._jargon = None # type: Jargon
+
+ def save_jargon(self, data: Jargon):
+ rawdata = data.get_rawdata()
+ with open(JARGON_PATH, 'w') as f:
+ yaml.dump(rawdata, stream=f, default_flow_style=False)
+
+ def get_jargon(self) -> Jargon:
+ if self._is_stale():
+ self._load_jargon()
+ return self._jargon
+
+ def _is_stale(self):
+ return (not self._jargon or not self._jargon_mtime or
+ self.jargon_mtime != self._get_jargon_file_mtime())
+
+ def _load_jargon(self):
+ with open(JARGON_PATH) as f:
+ rawdata = yaml.load(f)
+ self.jargon_mtime = self._get_jargon_file_mtime()
+ self._jargon = Jargon(rawdata)
+
+ def _get_jargon_file_mtime(self) -> int:
+ if not os.path.exists(self.jargon_path):
+ return 0
+ return os.stat(self.jargon_path).st_mtime
+
+ @staticmethod
+ def init_user_jargon(jargon_path):
+ values = yaml.load(DEFAULT_DATA)
+ if os.path.exists(jargon_path):
+ with open(jargon_path) as f:
+ custom_values = yaml.load(f)
+ if custom_values:
+ values.update(custom_values)
+ with open(jargon_path, 'w') as f:
+ f.write(DEFAULT_HEADER)
+ f.write('\n\n')
+ yaml.dump(values, stream=f, default_flow_style=False)
+
+ _instance = None
+ @staticmethod
+ def instance(jargon_path=None):
+ if not JargonLoader._instance:
+ jargon_path = jargon_path or JARGON_PATH
+ JargonLoader._instance = JargonLoader(jargon_path)
+ return JargonLoader._instance
+
+JargonLoader.init_user_jargon(JARGON_PATH)
diff --git a/service/jargon/resources.py b/service/jargon/resources.py
new file mode 100644
index 000000000..f6c631b0e
--- /dev/null
+++ b/service/jargon/resources.py
@@ -0,0 +1,23 @@
+# =============================================================================
+# Copyright (C) 2018 Filip Sufitchi
+#
+# 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 .
+# =============================================================================
+
+import pkg_resources
+
+DEFAULT_DATA = pkg_resources.resource_string(__name__, 'defaults.yaml').decode()
+DEFAULT_HEADER = pkg_resources.resource_string(__name__, 'header.yaml').decode()
diff --git a/service/market.py b/service/market.py
index 06fd18465..70794f79b 100644
--- a/service/market.py
+++ b/service/market.py
@@ -30,6 +30,7 @@ import config
import eos.db
from service import conversions
from service.settings import SettingsProvider
+from service.jargon import JargonLoader
from eos.gamedata import Category as types_Category, Group as types_Group, Item as types_Item, MarketGroup as types_MarketGroup, \
MetaGroup as types_MetaGroup, MetaType as types_MetaType
@@ -40,7 +41,6 @@ pyfalog = Logger(__name__)
# Event which tells threads dependent on Market that it's initialized
mktRdy = threading.Event()
-
class ShipBrowserWorkerThread(threading.Thread):
def __init__(self):
threading.Thread.__init__(self)
@@ -83,7 +83,10 @@ class SearchWorkerThread(threading.Thread):
def __init__(self):
threading.Thread.__init__(self)
self.name = "SearchWorker"
- pyfalog.debug("Initialize SearchWorkerThread.")
+ self.jargonLoader = JargonLoader.instance()
+ # load the jargon while in an out-of-thread context, to spot any problems while in the main thread
+ self.jargonLoader.get_jargon()
+ self.jargonLoader.get_jargon().apply('test string')
def run(self):
self.cv = threading.Condition()
@@ -110,13 +113,25 @@ class SearchWorkerThread(threading.Thread):
else:
filter_ = None
- results = eos.db.searchItems(request, where=filter_,
- join=(types_Item.group, types_Group.category),
- eager=("icon", "group.category", "metaGroup", "metaGroup.parent"))
+
+ jargon_request = self.jargonLoader.get_jargon().apply(request)
+
+
+ results = []
+ if len(request) >= config.minItemSearchLength:
+ results = eos.db.searchItems(request, where=filter_,
+ join=(types_Item.group, types_Group.category),
+ eager=("icon", "group.category", "metaGroup", "metaGroup.parent"))
+
+ jargon_results = []
+ if len(jargon_request) >= config.minItemSearchLength:
+ jargon_results = eos.db.searchItems(jargon_request, where=filter_,
+ join=(types_Item.group, types_Group.category),
+ eager=("icon", "group.category", "metaGroup", "metaGroup.parent"))
items = set()
# Return only published items, consult with Market service this time
- for item in results:
+ for item in [*results, *jargon_results]:
if sMkt.getPublicityByItem(item):
items.add(item)
wx.CallAfter(callback, items)