Merge branch 'test-3' into esi

# Conflicts:
#	eos/saveddata/character.py
#	service/character.py
#	service/eveapi.py
#	service/pycrest/eve.py
This commit is contained in:
blitzmann
2018-03-10 15:32:21 -05:00
113 changed files with 846 additions and 379 deletions

View File

@@ -108,6 +108,7 @@ class SkillBackupThread(threading.Thread):
def run(self):
path = self.path
sCharacter = Character.getInstance()
if self.saveFmt == "xml" or self.saveFmt == "emp":
backupData = sCharacter.exportXml()
else:
@@ -115,14 +116,13 @@ class SkillBackupThread(threading.Thread):
if self.saveFmt == "emp":
with gzip.open(path, mode='wb') as backupFile:
backupFile.write(backupData)
backupFile.write(backupData.encode())
else:
with open(path, mode='w', encoding='utf-8') as backupFile:
backupFile.write(backupData)
wx.CallAfter(self.callback)
class Character(object):
instance = None
skillReqsDict = {}
@@ -246,6 +246,7 @@ class Character(object):
# revert old char
char.revertLevels()
return newChar.ID
@staticmethod
def revertCharacter(charID):

View File

@@ -0,0 +1,18 @@
"""
Conversion pack for ~ Feb 2018 release
"""
CONVERSIONS = {
"Standup AXL Missile Launcher I": "Standup Anticapital Missile Launcher I",
"Standup ASML Missile Launcher I": "Standup Multirole Missile Launcher I",
"Standup Warp Scrambler I": "Standup Focused Warp Disruptor I",
"Standup M-Set Scan Resolution I": "Standup M-Set Enhanced Targeting System I",
"Standup M-Set Scan Resolution II": "Standup M-Set Enhanced Targeting System II",
"Standup AXL-S Missile": "Standup Super-heavy Torpedo",
"Standup AXL-C Missile": "Standup XL Cruise Missile",
"Standup ASML-LD Missile": "Standup Cruise Missile",
"Standup ASML-MD Missile": "Standup Heavy Missile",
"Standup ASML-SD Missile": "Standup Light Missile",
"Standup AM Guided Bomb": "Standup Heavy Guided Bomb",
"Standup AS Guided Bomb": "Standup Light Guided Bomb",
}

View File

@@ -72,21 +72,9 @@ class DamagePattern(object):
eos.db.save(p)
def importPatterns(self, text):
lookup = {}
current = self.getDamagePatternList()
for pattern in current:
lookup[pattern.name] = pattern
imports, num = es_DamagePattern.importPatterns(text)
for pattern in imports:
if pattern.name in lookup:
match = lookup[pattern.name]
match.__dict__.update(pattern.__dict__)
else:
eos.db.save(pattern)
eos.db.commit()
lenImports = len(imports)
if lenImports == 0:
raise ImportError("No patterns found for import")
if lenImports != num:

0
service/eveapi.py Normal file
View File

View File

@@ -408,7 +408,10 @@ class Fit(object):
module.state = State.ONLINE
fit.projectedModules.append(module)
else:
module = es_Module(thing)
try:
module = es_Module(thing)
except ValueError:
return False
module.state = State.ACTIVE
if not module.canHaveState(module.state, fit):
module.state = State.OFFLINE
@@ -1205,10 +1208,12 @@ class Fit(object):
def recalc(self, fit):
start_time = time()
pyfalog.info("=" * 10 + "recalc: {0}" + "=" * 10, fit.name)
if fit.factorReload is not self.serviceFittingOptions["useGlobalForceReload"]:
fit.factorReload = self.serviceFittingOptions["useGlobalForceReload"]
fit.factorReload = self.serviceFittingOptions["useGlobalForceReload"]
fit.clear()
fit.calculateModifiedAttributes()
pyfalog.info("=" * 10 + "recalc time: " + str(time() - start_time) + "=" * 10)

View File

@@ -262,14 +262,6 @@ class Market(object):
# Dictionary of items with forced market group (service assumes they have no
# market group assigned in db, otherwise they'll appear in both original and forced groups)
self.ITEMS_FORCEDMARKETGROUP = {
"'Alpha' Data Analyzer I" : 714,
# Ship Equipment > Electronics and Sensor Upgrades > Scanners > Data and Composition Scanners
"'Codex' Data Analyzer I" : 714,
# Ship Equipment > Electronics and Sensor Upgrades > Scanners > Data and Composition Scanners
"'Daemon' Data Analyzer I" : 714,
# Ship Equipment > Electronics and Sensor Upgrades > Scanners > Data and Composition Scanners
"'Libram' Data Analyzer I" : 714,
# Ship Equipment > Electronics and Sensor Upgrades > Scanners > Data and Composition Scanners
"Advanced Cerebral Accelerator" : 977, # Implants & Boosters > Booster
"Civilian Damage Control" : 615, # Ship Equipment > Hull & Armor > Damage Controls
"Civilian EM Ward Field" : 1695,
@@ -287,8 +279,6 @@ class Market(object):
# Ship Equipment > Hull & Armor > Remote Armor Repairers > Small
"Civilian Small Remote Shield Booster" : 603, # Ship Equipment > Shield > Remote Shield Boosters > Small
"Civilian Stasis Webifier" : 683, # Ship Equipment > Electronic Warfare > Stasis Webifiers
"Civilian Thermic Dissipation Field" : 1692,
# Ship Equipment > Shield > Shield Hardeners > Thermal Shield Hardeners
"Civilian Warp Disruptor" : 1935, # Ship Equipment > Electronic Warfare > Warp Disruptors
"Hardwiring - Zainou 'Sharpshooter' ZMX10" : 1493,
# Implants & Boosters > Implants > Skill Hardwiring > Missile Implants > Implant Slot 06
@@ -306,15 +296,7 @@ class Market(object):
"Prototype Cerebral Accelerator" : 977, # Implants & Boosters > Booster
"Prototype Iris Probe Launcher" : 712, # Ship Equipment > Turrets & Bays > Scan Probe Launchers
"Shadow" : 1310, # Drones > Combat Drones > Fighter Bombers
"Sleeper Data Analyzer I" : 714,
# Ship Equipment > Electronics and Sensor Upgrades > Scanners > Data and Composition Scanners
"Standard Cerebral Accelerator" : 977, # Implants & Boosters > Booster
"Talocan Data Analyzer I" : 714,
# Ship Equipment > Electronics and Sensor Upgrades > Scanners > Data and Composition Scanners
"Terran Data Analyzer I" : 714,
# Ship Equipment > Electronics and Sensor Upgrades > Scanners > Data and Composition Scanners
"Tetrimon Data Analyzer I" : 714
# Ship Equipment > Electronics and Sensor Upgrades > Scanners > Data and Composition Scanners
}
self.ITEMS_FORCEDMARKETGROUP_R = self.__makeRevDict(self.ITEMS_FORCEDMARKETGROUP)

View File

@@ -33,16 +33,17 @@ class EveCentral(object):
name = "eve-central.com"
def __init__(self, types, system, priceMap):
data = []
data = {}
baseurl = "https://eve-central.com/api/marketstat"
data.append(("usesystem", system)) # Use Jita for market
data["usesystem"] = system # Use Jita for market
data["typeid"] = set()
for typeID in types: # Add all typeID arguments
data.append(("typeid", typeID))
data["typeid"].add(typeID)
network = Network.getInstance()
data = network.request(baseurl, network.PRICES, data)
xml = minidom.parse(data)
data = network.request(baseurl, network.PRICES, params=data)
xml = minidom.parseString(data.text)
types = xml.getElementsByTagName("marketstat").item(0).getElementsByTagName("type")
# Cycle through all types we've got from request
for type_ in types:

View File

@@ -33,14 +33,14 @@ class EveMarketData(object):
name = "eve-marketdata.com"
def __init__(self, types, system, priceMap):
data = []
data = {}
baseurl = "https://eve-marketdata.com/api/item_prices.xml"
data.append(("system_id", system)) # Use Jita for market
data.append(("type_ids", ','.join(str(x) for x in types)))
data["system_id"] = system # Use Jita for market
data["type_ids"] = ','.join(str(x) for x in types)
network = Network.getInstance()
data = network.request(baseurl, network.PRICES, data)
xml = minidom.parse(data)
data = network.request(baseurl, network.PRICES, params=data)
xml = minidom.parseString(data.text)
types = xml.getElementsByTagName("eve").item(0).getElementsByTagName("price")
# Cycle through all types we've got from request

View File

@@ -18,9 +18,7 @@
# =============================================================================
import urllib.request
import urllib.error
import urllib.parse
import requests
import socket
from logbook import Logger
@@ -72,7 +70,8 @@ class Network(object):
return cls._instance
def request(self, url, type, data=None):
def request(self, url, type, *args, **kwargs):
# URL is required to be https as of right now
# print "Starting request: %s\n\tType: %s\n\tPost Data: %s"%(url,type,data)
@@ -86,51 +85,49 @@ class Network(object):
# Set up some things for the request
versionString = "{0} {1} - {2} {3}".format(config.version, config.tag, config.expansionName,
config.expansionVersion)
headers = {"User-Agent": "pyfa {0} (Python-urllib2)".format(versionString)}
headers = {"User-Agent": "pyfa {0} (python-requests {1})".format(versionString, requests.__version__)}
# user-agent: pyfa 2.0.0b4 git -YC120.2 1.2 (python-requests 2.18.4)
proxy = NetworkSettings.getInstance().getProxySettings()
if proxy is not None:
# proxy is a tuple of (host, port): (u'192.168.20.1', 3128)
proxy_auth = NetworkSettings.getInstance().getProxyAuthDetails()
# proxy_auth is a tuple of (login, password) or None
if proxy_auth is not None:
# add login:password@ in front of proxy address
proxy_handler = urllib.request.ProxyHandler({
'https': '{0}:{1}@{2}:{3}'.format(
proxy_auth[0], proxy_auth[1], proxy[0], proxy[1])
})
else:
# build proxy handler with no login/pass info
proxy_handler = urllib.request.ProxyHandler({'https': "{0}:{1}".format(proxy[0], proxy[1])})
opener = urllib.request.build_opener(proxy_handler)
urllib.request.install_opener(opener)
else:
# This is a bug fix, explicitly disable possibly previously installed
# opener with proxy, by urllib2.install_opener() a few lines above in code.
# Now this explicitly disables proxy handler, "uninstalling" opener.
# This is used in case when user had proxy enabled, so proxy_handler was already
# installed globally, and then user had disabled the proxy, so we should clear that opener
urllib.request.install_opener(None)
# another option could be installing a default opener:
# urllib2.install_opener(urllib2.build_opener())
# python-requests supports setting proxy for request as parameter to get() / post()
# in a form like: proxies = { 'http': 'http://10.10.1.10:3128', 'https': 'http://10.10.1.10:1080' }
# or with HTTP Basic auth support: proxies = {'http': 'http://user:pass@10.10.1.10:3128/'}
# then you do: requests.get('http://example.org', proxies=proxies)
proxies = None
proxy_settings = NetworkSettings.getInstance().getProxySettings()
# proxy_settings is a tuple of (host, port), like ('192.168.20.1', 3128), or None
if proxy_settings is not None:
# form proxy address in format "http://host:port
proxy_host_port = '{}:{}'.format(proxy_settings[0], proxy_settings[1])
proxy_auth_details = NetworkSettings.getInstance().getProxyAuthDetails()
# proxy_auth_details is a tuple of (login, password), or None
user_pass = ''
if proxy_auth_details is not None:
# construct prefix in form "user:password@"
user_pass = '{}:{}@'.format(proxy_auth_details[0], proxy_auth_details[1])
proxies = {
'http': 'http://' + user_pass + proxy_host_port,
'https': 'http://' + user_pass + proxy_host_port
}
# final form: { 'http': 'http://user:password@host:port', ... }, or
# { 'http': 'http://host:port', ... } if no auth info.
request = urllib.request.Request(url, headers=headers, data=urllib.parse.urlencode(data).encode("utf-8") if data else None)
try:
return urllib.request.urlopen(request)
except urllib.error.HTTPError as error:
resp = requests.get(url, headers=headers, proxies=proxies, **kwargs)
resp.raise_for_status()
return resp
except requests.exceptions.HTTPError as error:
pyfalog.warning("HTTPError:")
pyfalog.warning(error)
if error.code == 404:
if error.response.status_code == 404:
raise RequestError()
elif error.code == 403:
elif error.response.status_code == 403:
raise AuthenticationError()
elif error.code >= 500:
elif error.response.status_code >= 500:
raise ServerError()
raise Error(error)
except urllib.error.URLError as error:
pyfalog.warning("Timed out or other URL error:")
pyfalog.warning(error)
if "timed out" in error.reason:
raise TimeoutError()
else:
raise Error(error)
except requests.exceptions.Timeout:
raise TimeoutError()
except Exception as error:
raise Error(error)

View File

@@ -25,6 +25,8 @@ import collections
import json
import threading
import locale
from bs4 import UnicodeDammit
from codecs import open
@@ -264,7 +266,7 @@ class Port(object):
fits are processed as well as when fits are being saved.
returns
"""
defcodepage = locale.getpreferredencoding()
sFit = svcFit.getInstance()
fit_list = []
@@ -276,63 +278,17 @@ class Port(object):
PortProcessing.notify(iportuser, IPortUser.PROCESS_IMPORT | IPortUser.ID_UPDATE, msg)
# wx.CallAfter(callback, 1, msg)
with open(path, "r") as file_:
with open(path, "rb") as file_:
srcString = file_.read()
dammit = UnicodeDammit(srcString)
srcString = dammit.unicode_markup
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
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'))
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 = str(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 = str(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:
return False, "Proper codec could not be established for %s" % path
try:
_, fitsImport = Port.importAuto(srcString, path, iportuser=iportuser, encoding=codec_found)
_, fitsImport = Port.importAuto(srcString, path, iportuser=iportuser)
fit_list += fitsImport
except xml.parsers.expat.ExpatError:
pyfalog.warning("Malformed XML in:\n{0}", path)
@@ -465,7 +421,7 @@ class Port(object):
return json.dumps(fit)
@classmethod
def importAuto(cls, string, path=None, activeFit=None, iportuser=None, encoding=None):
def importAuto(cls, string, path=None, activeFit=None, iportuser=None):
# type: (basestring, basestring, object, IPortUser, basestring) -> object
# Get first line and strip space symbols of it to avoid possible detection errors
firstLine = re.split("[\n\r]+", string.strip(), maxsplit=1)[0]
@@ -473,10 +429,7 @@ class Port(object):
# If XML-style start of tag encountered, detect as XML
if re.search(RE_XML_START, firstLine):
if encoding:
return "XML", cls.importXml(string, iportuser, encoding)
else:
return "XML", cls.importXml(string, iportuser)
return "XML", cls.importXml(string, iportuser)
# If JSON-style start, parse as CREST/JSON
if firstLine[0] == '{':
@@ -813,11 +766,6 @@ class Port(object):
except:
return [] # empty list is expected
# If client didn't take care of encoding file contents into Unicode,
# do it using fallback encoding ourselves
if isinstance(contents, str):
contents = str(contents, locale.getpreferredencoding())
fits = [] # List for fits
fitIndices = [] # List for starting line numbers for each fit
lines = re.split('[\n\r]+', contents) # Separate string into lines
@@ -1001,10 +949,10 @@ class Port(object):
return fits
@staticmethod
def importXml(text, iportuser=None, encoding="utf-8"):
def importXml(text, iportuser=None):
# type: (basestring, IPortUser, basestring) -> list[eos.saveddata.fit.Fit]
sMkt = Market.getInstance()
doc = xml.dom.minidom.parseString(text.encode(encoding))
doc = xml.dom.minidom.parseString(text)
# NOTE:
# When L_MARK is included at this point,
# Decided to be localized data
@@ -1182,6 +1130,7 @@ class Port(object):
subsystems = [] # EVE cares which order you put these in
mods = OrderedDict()
charges = OrderedDict()
sFit = svcFit.getInstance()
for mod in fit.modules:
if not mod.isEmpty:
if mod.slot == Slot.SUBSYSTEM:
@@ -1191,7 +1140,7 @@ class Port(object):
mods[mod.itemID] = 0
mods[mod.itemID] += 1
if mod.charge:
if mod.charge and sFit.serviceFittingOptions["exportCharges"]:
if mod.chargeID not in charges:
charges[mod.chargeID] = 0
# `or 1` because some charges (ie scripts) are without qty

0
service/pycrest/eve.py Normal file
View File

View File

@@ -72,21 +72,9 @@ class TargetResists(object):
db.save(p)
def importPatterns(self, text):
lookup = {}
current = self.getTargetResistsList()
for pattern in current:
lookup[pattern.name] = pattern
imports, num = es_TargetResists.importPatterns(text)
for pattern in imports:
if pattern.name in lookup:
match = lookup[pattern.name]
match.__dict__.update(pattern.__dict__)
else:
db.save(pattern)
db.commit()
lenImports = len(imports)
if lenImports == 0:
raise ImportError("No patterns found for import")
if lenImports != num:

View File

@@ -48,14 +48,14 @@ class CheckUpdateThread(threading.Thread):
network = Network.getInstance()
try:
response = network.request('https://api.github.com/repos/blitzmann/Pyfa/releases', network.UPDATE)
jsonResponse = json.loads(response.read())
response = network.request('https://api.github.com/repos/pyfa-org/Pyfa/releases', network.UPDATE)
jsonResponse = response.json()
jsonResponse.sort(
key=lambda x: calendar.timegm(dateutil.parser.parse(x['published_at']).utctimetuple()),
reverse=True
)
for release in jsonResponse:
for release in jsonResponse[:5]:
rVersion = Version(release['tag_name'])
cVersion = Version(config.version)
@@ -73,8 +73,8 @@ class CheckUpdateThread(threading.Thread):
if rVersion > cVersion:
wx.CallAfter(self.callback, release, rVersion)
break
break
except Exception as e:
pyfalog.error("Caught exception in run")
pyfalog.error(e)