diff --git a/config.py b/config.py
index 1ce7b1445..6b7159205 100644
--- a/config.py
+++ b/config.py
@@ -27,6 +27,8 @@ expansionName = "YC120.3"
expansionVersion = "1.8"
evemonMinVersion = "4081"
+minItemSearchLength = 3
+
pyfaPath = None
savePath = None
saveDB = None
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/requirements.txt b/requirements.txt
index 2b38e8685..7a2e5f65f 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -7,4 +7,5 @@ sqlalchemy >= 1.0.5
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)