Merge branch 'development' into Remote_Repair_Pane_v2

Conflicts:
	eos/mathUtils.py
	service/crest.py
This commit is contained in:
blitzmann
2017-02-26 01:40:53 -05:00
102 changed files with 6775 additions and 521 deletions

1
.gitignore vendored
View File

@@ -49,7 +49,6 @@ Pyfa.egg-info/
# Usually these files are written by a python script from a template
# before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec
# Installer logs
pip-log.txt

View File

@@ -51,8 +51,7 @@ pyfa is licensed under the GNU GPL v3.0, see LICENSE
## Resources
* Development repository: [https://github.com/pyfa-org/Pyfa](https://github.com/pyfa-org/Pyfa)
* XMPP conference: [pyfa@conference.jabber.org](pyfa@conference.jabber.org)
* [EVE forum thread](http://forums.eveonline.com/default.aspx?g=posts&t=247609)
* [EVE forum thread](https://forums.eveonline.com/default.aspx?g=posts&t=466425)
* [EVE University guide using pyfa](http://wiki.eveuniversity.org/Guide_to_using_PYFA)
* [EVE Online website](http://www.eveonline.com/)

121
config.py
View File

@@ -1,27 +1,28 @@
import os
import sys
# TODO: move all logging back to pyfa.py main loop
# We moved it here just to avoid rebuilding windows skeleton for now (any change to pyfa.py needs it)
import logging
import logging.handlers
from logbook import Logger
pyfalog = Logger(__name__)
# Load variable overrides specific to distribution type
try:
import configforced
except ImportError:
pyfalog.warning("Failed to import: configforced")
configforced = None
# Turns on debug mode
debug = False
# Defines if our saveddata will be in pyfa root or not
saveInRoot = False
# Version data
version = "1.26.1"
version = "1.27.3"
tag = "git"
expansionName = "YC118.10"
expansionVersion = "1.2"
expansionName = "YC119.2"
expansionVersion = "1.4"
evemonMinVersion = "4081"
pyfaPath = None
@@ -30,22 +31,6 @@ saveDB = None
gameDB = None
class StreamToLogger(object):
"""
Fake file-like stream object that redirects writes to a logger instance.
From: http://www.electricmonk.nl/log/2011/08/14/redirect-stdout-and-stderr-to-a-logger-in-python/
"""
def __init__(self, logger, log_level=logging.INFO):
self.logger = logger
self.log_level = log_level
self.linebuf = ''
def write(self, buf):
for line in buf.rstrip().splitlines():
self.logger.log(self.log_level, line.rstrip())
def isFrozen():
if hasattr(sys, 'frozen'):
return True
@@ -58,6 +43,17 @@ def __createDirs(path):
os.makedirs(path)
def getPyfaRoot():
base = getattr(sys.modules['__main__'], "__file__", sys.executable) if isFrozen() else sys.argv[0]
root = os.path.dirname(os.path.realpath(os.path.abspath(base)))
root = unicode(root, sys.getfilesystemencoding())
return root
def getDefaultSave():
return unicode(os.path.expanduser(os.path.join("~", ".pyfa")), sys.getfilesystemencoding())
def defPaths(customSavePath):
global debug
global pyfaPath
@@ -66,65 +62,42 @@ def defPaths(customSavePath):
global gameDB
global saveInRoot
if debug:
logLevel = logging.DEBUG
else:
logLevel = logging.WARN
pyfalog.debug("Configuring Pyfa")
# The main pyfa directory which contains run.py
# Python 2.X uses ANSI by default, so we need to convert the character encoding
pyfaPath = getattr(configforced, "pyfaPath", pyfaPath)
if pyfaPath is None:
pyfaPath = getPyfaPath()
pyfaPath = getPyfaRoot()
# Where we store the saved fits etc, default is the current users home directory
if saveInRoot is True:
savePath = getattr(configforced, "savePath", None)
if savePath is None:
savePath = getPyfaPath("saveddata")
savePath = os.path.join(pyfaPath, "saveddata")
else:
savePath = getattr(configforced, "savePath", None)
if savePath is None:
if customSavePath is None: # customSavePath is not overriden
savePath = os.path.expanduser(os.path.join("~", ".pyfa"))
savePath = getDefaultSave()
else:
savePath = customSavePath
__createDirs(savePath)
if isFrozen():
certName = "cacert.pem"
os.environ["REQUESTS_CA_BUNDLE"] = getPyfaPath(certName).encode('utf8')
os.environ["SSL_CERT_FILE"] = getPyfaPath(certName).encode('utf8')
loggingFormat = '%(asctime)s %(name)-24s %(levelname)-8s %(message)s'
logging.basicConfig(format=loggingFormat, level=logLevel)
handler = logging.handlers.RotatingFileHandler(getSavePath("log.txt"), maxBytes=1000000, backupCount=3)
formatter = logging.Formatter(loggingFormat)
handler.setFormatter(formatter)
logging.getLogger('').addHandler(handler)
logging.info("Starting pyfa")
if hasattr(sys, 'frozen'):
stdout_logger = logging.getLogger('STDOUT')
sl = StreamToLogger(stdout_logger, logging.INFO)
sys.stdout = sl
# This interferes with cx_Freeze's own handling of exceptions. Find a way to fix this.
# stderr_logger = logging.getLogger('STDERR')
# sl = StreamToLogger(stderr_logger, logging.ERROR)
# sys.stderr = sl
os.environ["REQUESTS_CA_BUNDLE"] = os.path.join(pyfaPath, "cacert.pem").encode('utf8')
os.environ["SSL_CERT_FILE"] = os.path.join(pyfaPath, "cacert.pem").encode('utf8')
# The database where we store all the fits etc
saveDB = getSavePath("saveddata.db")
saveDB = os.path.join(savePath, "saveddata.db")
# The database where the static EVE data from the datadump is kept.
# This is not the standard sqlite datadump but a modified version created by eos
# maintenance script
gameDB = getPyfaPath("eve.db")
gameDB = os.path.join(pyfaPath, "eve.db")
# DON'T MODIFY ANYTHING BELOW!
# DON'T MODIFY ANYTHING BELOW
import eos.config
# Caching modifiers, disable all gamedata caching, its unneeded.
@@ -132,41 +105,3 @@ def defPaths(customSavePath):
# saveddata db location modifier, shouldn't ever need to touch this
eos.config.saveddata_connectionstring = "sqlite:///" + saveDB + "?check_same_thread=False"
eos.config.gamedata_connectionstring = "sqlite:///" + gameDB + "?check_same_thread=False"
def getPyfaPath(Append=None):
base = getattr(sys.modules['__main__'], "__file__", sys.executable) if isFrozen() else sys.argv[0]
root = os.path.dirname(os.path.realpath(os.path.abspath(base)))
if Append:
path = parsePath(root, Append)
else:
path = parsePath(root)
return path
def getSavePath(Append=None):
root = savePath
if Append:
path = parsePath(root, Append)
else:
path = parsePath(root)
return path
def parsePath(root, Append=None):
if Append:
path = os.path.join(root, Append)
else:
path = root
if type(path) == str: # leave unicode ones alone
try:
path = path.decode('utf8')
except UnicodeDecodeError:
path = path.decode('windows-1252')
return path

5666
dist_assets/cacert.pem Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -24,6 +24,9 @@ from sqlalchemy.orm import sessionmaker
import migration
from eos import config
from logbook import Logger
pyfalog = Logger(__name__)
class ReadOnlyException(Exception):
@@ -46,7 +49,9 @@ try:
config.gamedata_version = gamedata_session.execute(
"SELECT `field_value` FROM `metadata` WHERE `field_name` LIKE 'client_build'"
).fetchone()[0]
except:
except Exception as e:
pyfalog.warning("Missing gamedata version.")
pyfalog.critical(e)
config.gamedata_version = None
saveddata_connectionstring = config.saveddata_connectionstring

View File

@@ -1,11 +1,11 @@
import logging
from logbook import Logger
import shutil
import time
import config
import migrations
logger = logging.getLogger(__name__)
pyfalog = Logger(__name__)
def getVersion(db):
@@ -37,7 +37,7 @@ def update(saveddata_engine):
for version in xrange(dbVersion, appVersion):
func = migrations.updates[version + 1]
if func:
logger.info("Applying database update: %d", version + 1)
pyfalog.info("Applying database update: {0}", version + 1)
func(saveddata_engine)
# when all is said and done, set version to current

View File

@@ -0,0 +1,10 @@
"""
Migration 21
- Fixes discrepancy in drone table where we may have an amount active that is not equal to the amount in the stack
(we don't support activating only 2/5 drones). See GH issue #728
"""
def upgrade(saveddata_engine):
saveddata_engine.execute("UPDATE drones SET amountActive = amount where amountActive > 0 AND amountActive <> amount;")

View File

@@ -18,9 +18,9 @@
# ===============================================================================
from sqlalchemy.exc import DatabaseError
import logging
from logbook import Logger
logger = logging.getLogger(__name__)
pyfalog = Logger(__name__)
class DatabaseCleanup(object):
@@ -33,7 +33,7 @@ class DatabaseCleanup(object):
results = saveddata_engine.execute(query)
return results
except DatabaseError:
logger.error("Failed to connect to database or error executing query:\n%s", query)
pyfalog.error("Failed to connect to database or error executing query:\n{0}", query)
return None
@staticmethod
@@ -41,7 +41,7 @@ class DatabaseCleanup(object):
# Find orphaned character skills.
# This solves an issue where the character doesn't exist, but skills for that character do.
# See issue #917
logger.debug("Running database cleanup for character skills.")
pyfalog.debug("Running database cleanup for character skills.")
query = "SELECT COUNT(*) AS num FROM characterSkills WHERE characterID NOT IN (SELECT ID from characters)"
results = DatabaseCleanup.ExecuteSQLQuery(saveddata_engine, query)
@@ -53,14 +53,14 @@ class DatabaseCleanup(object):
if row and row['num']:
query = "DELETE FROM characterSkills WHERE characterID NOT IN (SELECT ID from characters)"
delete = DatabaseCleanup.ExecuteSQLQuery(saveddata_engine, query)
logger.error("Database corruption found. Cleaning up %d records.", delete.rowcount)
pyfalog.error("Database corruption found. Cleaning up {0} records.", delete.rowcount)
@staticmethod
def OrphanedFitDamagePatterns(saveddata_engine):
# Find orphaned damage patterns.
# This solves an issue where the damage pattern doesn't exist, but fits reference the pattern.
# See issue #777
logger.debug("Running database cleanup for orphaned damage patterns attached to fits.")
pyfalog.debug("Running database cleanup for orphaned damage patterns attached to fits.")
query = "SELECT COUNT(*) AS num FROM fits WHERE damagePatternID NOT IN (SELECT ID FROM damagePatterns) OR damagePatternID IS NULL"
results = DatabaseCleanup.ExecuteSQLQuery(saveddata_engine, query)
@@ -80,20 +80,20 @@ class DatabaseCleanup(object):
rows = uniform_results.fetchall()
if len(rows) == 0:
logger.error("Missing uniform damage pattern.")
pyfalog.error("Missing uniform damage pattern.")
elif len(rows) > 1:
logger.error("More than one uniform damage pattern found.")
pyfalog.error("More than one uniform damage pattern found.")
else:
uniform_damage_pattern_id = rows[0]['ID']
update_query = "UPDATE 'fits' SET 'damagePatternID' = {} " \
"WHERE damagePatternID NOT IN (SELECT ID FROM damagePatterns) OR damagePatternID IS NULL".format(uniform_damage_pattern_id)
update_results = DatabaseCleanup.ExecuteSQLQuery(saveddata_engine, update_query)
logger.error("Database corruption found. Cleaning up %d records.", update_results.rowcount)
pyfalog.error("Database corruption found. Cleaning up {0} records.", update_results.rowcount)
@staticmethod
def OrphanedFitCharacterIDs(saveddata_engine):
# Find orphaned character IDs. This solves an issue where the character doesn't exist, but fits reference the pattern.
logger.debug("Running database cleanup for orphaned characters attached to fits.")
pyfalog.debug("Running database cleanup for orphaned characters attached to fits.")
query = "SELECT COUNT(*) AS num FROM fits WHERE characterID NOT IN (SELECT ID FROM characters) OR characterID IS NULL"
results = DatabaseCleanup.ExecuteSQLQuery(saveddata_engine, query)
@@ -113,22 +113,22 @@ class DatabaseCleanup(object):
rows = all5_results.fetchall()
if len(rows) == 0:
logger.error("Missing 'All 5' character.")
pyfalog.error("Missing 'All 5' character.")
elif len(rows) > 1:
logger.error("More than one 'All 5' character found.")
pyfalog.error("More than one 'All 5' character found.")
else:
all5_id = rows[0]['ID']
update_query = "UPDATE 'fits' SET 'characterID' = " + str(all5_id) + \
" WHERE characterID not in (select ID from characters) OR characterID IS NULL"
update_results = DatabaseCleanup.ExecuteSQLQuery(saveddata_engine, update_query)
logger.error("Database corruption found. Cleaning up %d records.", update_results.rowcount)
pyfalog.error("Database corruption found. Cleaning up {0} records.", update_results.rowcount)
@staticmethod
def NullDamagePatternNames(saveddata_engine):
# Find damage patterns that are missing the name.
# This solves an issue where the damage pattern ends up with a name that is null.
# See issue #949
logger.debug("Running database cleanup for missing damage pattern names.")
pyfalog.debug("Running database cleanup for missing damage pattern names.")
query = "SELECT COUNT(*) AS num FROM damagePatterns WHERE name IS NULL OR name = ''"
results = DatabaseCleanup.ExecuteSQLQuery(saveddata_engine, query)
@@ -140,14 +140,14 @@ class DatabaseCleanup(object):
if row and row['num']:
query = "DELETE FROM damagePatterns WHERE name IS NULL OR name = ''"
delete = DatabaseCleanup.ExecuteSQLQuery(saveddata_engine, query)
logger.error("Database corruption found. Cleaning up %d records.", delete.rowcount)
pyfalog.error("Database corruption found. Cleaning up {0} records.", delete.rowcount)
@staticmethod
def NullTargetResistNames(saveddata_engine):
# Find target resists that are missing the name.
# This solves an issue where the target resist ends up with a name that is null.
# See issue #949
logger.debug("Running database cleanup for missing target resist names.")
pyfalog.debug("Running database cleanup for missing target resist names.")
query = "SELECT COUNT(*) AS num FROM targetResists WHERE name IS NULL OR name = ''"
results = DatabaseCleanup.ExecuteSQLQuery(saveddata_engine, query)
@@ -159,14 +159,14 @@ class DatabaseCleanup(object):
if row and row['num']:
query = "DELETE FROM targetResists WHERE name IS NULL OR name = ''"
delete = DatabaseCleanup.ExecuteSQLQuery(saveddata_engine, query)
logger.error("Database corruption found. Cleaning up %d records.", delete.rowcount)
pyfalog.error("Database corruption found. Cleaning up {0} records.", delete.rowcount)
@staticmethod
def OrphanedFitIDItemID(saveddata_engine):
# Orphaned items that are missing the fit ID or item ID.
# See issue #954
for table in ['drones', 'cargo', 'fighters']:
logger.debug("Running database cleanup for orphaned %s items.", table)
pyfalog.debug("Running database cleanup for orphaned {0} items.", table)
query = "SELECT COUNT(*) AS num FROM {} WHERE itemID IS NULL OR itemID = '' or itemID = '0' or fitID IS NULL OR fitID = '' or fitID = '0'".format(
table)
results = DatabaseCleanup.ExecuteSQLQuery(saveddata_engine, query)
@@ -180,10 +180,10 @@ class DatabaseCleanup(object):
query = "DELETE FROM {} WHERE itemID IS NULL OR itemID = '' or itemID = '0' or fitID IS NULL OR fitID = '' or fitID = '0'".format(
table)
delete = DatabaseCleanup.ExecuteSQLQuery(saveddata_engine, query)
logger.error("Database corruption found. Cleaning up %d records.", delete.rowcount)
pyfalog.error("Database corruption found. Cleaning up {0} records.", delete.rowcount)
for table in ['modules']:
logger.debug("Running database cleanup for orphaned %s items.", table)
pyfalog.debug("Running database cleanup for orphaned {0} items.", table)
query = "SELECT COUNT(*) AS num FROM {} WHERE itemID = '0' or fitID IS NULL OR fitID = '' or fitID = '0'".format(
table)
results = DatabaseCleanup.ExecuteSQLQuery(saveddata_engine, query)
@@ -196,7 +196,7 @@ class DatabaseCleanup(object):
if row and row['num']:
query = "DELETE FROM {} WHERE itemID = '0' or fitID IS NULL OR fitID = '' or fitID = '0'".format(table)
delete = DatabaseCleanup.ExecuteSQLQuery(saveddata_engine, query)
logger.error("Database corruption found. Cleaning up %d records.", delete.rowcount)
pyfalog.error("Database corruption found. Cleaning up {0} records.", delete.rowcount)
@staticmethod
def NullDamageTargetPatternValues(saveddata_engine):
@@ -204,7 +204,7 @@ class DatabaseCleanup(object):
# See issue #954
for profileType in ['damagePatterns', 'targetResists']:
for damageType in ['em', 'thermal', 'kinetic', 'explosive']:
logger.debug("Running database cleanup for null %s values. (%s)", profileType, damageType)
pyfalog.debug("Running database cleanup for null {0} values. ({1})", profileType, damageType)
query = "SELECT COUNT(*) AS num FROM {0} WHERE {1}Amount IS NULL OR {1}Amount = ''".format(profileType,
damageType)
results = DatabaseCleanup.ExecuteSQLQuery(saveddata_engine, query)
@@ -218,13 +218,13 @@ class DatabaseCleanup(object):
query = "UPDATE '{0}' SET '{1}Amount' = '0' WHERE {1}Amount IS NULL OR Amount = ''".format(profileType,
damageType)
delete = DatabaseCleanup.ExecuteSQLQuery(saveddata_engine, query)
logger.error("Database corruption found. Cleaning up %d records.", delete.rowcount)
pyfalog.error("Database corruption found. Cleaning up {0} records.", delete.rowcount)
@staticmethod
def DuplicateSelectedAmmoName(saveddata_engine):
# Orphaned items that are missing the fit ID or item ID.
# See issue #954
logger.debug("Running database cleanup for duplicated selected ammo profiles.")
pyfalog.debug("Running database cleanup for duplicated selected ammo profiles.")
query = "SELECT COUNT(*) AS num FROM damagePatterns WHERE name = 'Selected Ammo'"
results = DatabaseCleanup.ExecuteSQLQuery(saveddata_engine, query)
@@ -236,4 +236,4 @@ class DatabaseCleanup(object):
if row and row['num'] > 1:
query = "DELETE FROM damagePatterns WHERE name = 'Selected Ammo'"
delete = DatabaseCleanup.ExecuteSQLQuery(saveddata_engine, query)
logger.error("Database corruption found. Cleaning up %d records.", delete.rowcount)
pyfalog.error("Database corruption found. Cleaning up {0} records.", delete.rowcount)

View File

@@ -17,9 +17,9 @@
# along with eos. If not, see <http://www.gnu.org/licenses/>.
# ===============================================================================
import logging
from logbook import Logger
logger = logging.getLogger(__name__)
pyfalog = Logger(__name__)
class HandledList(list):
@@ -198,7 +198,7 @@ class HandledImplantBoosterList(HandledList):
# if needed, remove booster that was occupying slot
oldObj = next((m for m in self if m.slot == thing.slot), None)
if oldObj:
logging.info("Slot %d occupied with %s, replacing with %s", thing.slot, oldObj.item.name, thing.item.name)
pyfalog.info("Slot {0} occupied with {1}, replacing with {2}", thing.slot, oldObj.item.name, thing.item.name)
oldObj.itemID = 0 # hack to remove from DB. See GH issue #324
self.remove(oldObj)
@@ -222,7 +222,7 @@ class HandledProjectedModList(HandledList):
oldEffect = next((m for m in self if m.item.group.name == "Effect Beacon"), None)
if oldEffect:
logging.info("System effect occupied with %s, replacing with %s", oldEffect.item.name, proj.item.name)
pyfalog.info("System effect occupied with {0}, replacing with {1}", oldEffect.item.name, proj.item.name)
self.remove(oldEffect)
HandledList.append(self, proj)

View File

@@ -2,9 +2,9 @@
#
# Used by:
# Module: Reactive Armor Hardener
import logging
from logbook import Logger
logger = logging.getLogger(__name__)
pyfalog = Logger(__name__)
runTime = "late"
type = "active"
@@ -23,7 +23,7 @@ def handler(fit, module, context):
damagePattern.kineticAmount * fit.ship.getModifiedItemAttr('armorKineticDamageResonance'),
damagePattern.explosiveAmount * fit.ship.getModifiedItemAttr('armorExplosiveDamageResonance'),
)
# logger.debug("Damage Adjusted for Armor Resists: %f/%f/%f/%f", baseDamageTaken[0], baseDamageTaken[1], baseDamageTaken[2], baseDamageTaken[3])
# pyfalog.debug("Damage Adjusted for Armor Resists: %f/%f/%f/%f", baseDamageTaken[0], baseDamageTaken[1], baseDamageTaken[2], baseDamageTaken[3])
resistanceShiftAmount = module.getModifiedItemAttr(
'resistanceShiftAmount') / 100 # The attribute is in percent and we want a fraction
@@ -39,7 +39,7 @@ def handler(fit, module, context):
cycleList = []
loopStart = -20
for num in range(50):
# logger.debug("Starting cycle %d.", num)
# pyfalog.debug("Starting cycle %d.", num)
# The strange order is to emulate the ingame sorting when different types have taken the same amount of damage.
# This doesn't take into account stacking penalties. In a few cases fitting a Damage Control causes an inaccurate result.
damagePattern_tuples = [
@@ -77,7 +77,7 @@ def handler(fit, module, context):
RAHResistance[sortedDamagePattern_tuples[1][0]] = sortedDamagePattern_tuples[1][2] + change1
RAHResistance[sortedDamagePattern_tuples[2][0]] = sortedDamagePattern_tuples[2][2] + change2
RAHResistance[sortedDamagePattern_tuples[3][0]] = sortedDamagePattern_tuples[3][2] + change3
# logger.debug("Resistances shifted to %f/%f/%f/%f", RAHResistance[0], RAHResistance[1], RAHResistance[2], RAHResistance[3])
# pyfalog.debug("Resistances shifted to %f/%f/%f/%f", RAHResistance[0], RAHResistance[1], RAHResistance[2], RAHResistance[3])
# See if the current RAH profile has been encountered before, indicating a loop.
for i, val in enumerate(cycleList):
@@ -87,7 +87,7 @@ def handler(fit, module, context):
abs(RAHResistance[2] - val[2]) <= tolerance and \
abs(RAHResistance[3] - val[3]) <= tolerance:
loopStart = i
# logger.debug("Loop found: %d-%d", loopStart, num)
# pyfalog.debug("Loop found: %d-%d", loopStart, num)
break
if loopStart >= 0:
break
@@ -95,7 +95,7 @@ def handler(fit, module, context):
cycleList.append(list(RAHResistance))
if loopStart < 0:
logger.error("Reactive Armor Hardener failed to find equilibrium. Damage profile after armor: %f/%f/%f/%f",
pyfalog.error("Reactive Armor Hardener failed to find equilibrium. Damage profile after armor: {0}/{1}/{2}/{3}",
baseDamageTaken[0], baseDamageTaken[1], baseDamageTaken[2], baseDamageTaken[3])
# Average the profiles in the RAH loop, or the last 20 if it didn't find a loop.
@@ -110,7 +110,7 @@ def handler(fit, module, context):
average[i] = round(average[i] / numCycles, 3)
# Set the new resistances
# logger.debug("Setting new resist profile: %f/%f/%f/%f", average[0], average[1], average[2],average[3])
# pyfalog.debug("Setting new resist profile: %f/%f/%f/%f", average[0], average[1], average[2],average[3])
for i, attr in enumerate((
'armorEmDamageResonance', 'armorThermalDamageResonance', 'armorKineticDamageResonance',
'armorExplosiveDamageResonance')):

View File

@@ -1,7 +1,7 @@
# ammoInfluenceCapNeed
#
# Used by:
# Items from category: Charge (465 of 899)
# Items from category: Charge (465 of 912)
# Charges from group: Frequency Crystal (185 of 185)
# Charges from group: Hybrid Charge (209 of 209)
type = "passive"

View File

@@ -1,7 +1,7 @@
# ammoInfluenceRange
#
# Used by:
# Items from category: Charge (571 of 899)
# Items from category: Charge (571 of 912)
type = "passive"

View File

@@ -1,7 +1,7 @@
# ammoSpeedMultiplier
#
# Used by:
# Charges from group: Festival Charges (9 of 9)
# Charges from group: Festival Charges (22 of 22)
# Charges from group: Interdiction Probe (2 of 2)
# Charges from group: Survey Probe (3 of 3)
type = "passive"

View File

@@ -3,7 +3,6 @@
# Used by:
# Modules from group: Armor Coating (202 of 202)
# Modules from group: Armor Plating Energized (187 of 187)
# Modules named like: QA Multiship Module Players (4 of 4)
type = "passive"

View File

@@ -1,7 +1,7 @@
# armorRepairAmountBonusSubcap
#
# Used by:
# Implants named like: Grade Asklepian (15 of 16)
# Implants named like: grade Asklepian (15 of 18)
type = "passive"

View File

@@ -2,7 +2,6 @@
#
# Used by:
# Modules from group: Data Miners (15 of 16)
# Module: QA Cross Protocol Analyzer
type = "active"

View File

@@ -2,7 +2,6 @@
#
# Used by:
# Modules from group: Automated Targeting System (6 of 6)
# Module: QA Damage Module
type = "passive"

View File

@@ -2,8 +2,6 @@
#
# Used by:
# Modules from group: Heat Sink (18 of 18)
# Modules named like: QA Multiship Module Players (4 of 4)
# Module: QA Damage Module
type = "passive"

View File

@@ -2,7 +2,6 @@
#
# Used by:
# Modules from group: Tracking Enhancer (10 of 10)
# Module: QA Damage Module
type = "passive"

View File

@@ -2,7 +2,6 @@
#
# Used by:
# Modules from group: Tracking Enhancer (10 of 10)
# Module: QA Damage Module
type = "passive"

View File

@@ -2,7 +2,6 @@
#
# Used by:
# Modules from group: Tracking Enhancer (10 of 10)
# Module: QA Damage Module
type = "passive"

View File

@@ -2,8 +2,6 @@
#
# Used by:
# Modules from group: Magnetic Field Stabilizer (14 of 14)
# Modules named like: QA Multiship Module Players (4 of 4)
# Module: QA Damage Module
type = "passive"

View File

@@ -6,13 +6,13 @@ type = "passive"
def handler(fit, src, context):
fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Mining Foreman"), "warfareBuff4Multiplier",
fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Mining Foreman"), "warfareBuff4Value",
src.getModifiedItemAttr("shipBonusICS2"), skill="Industrial Command Ships")
fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Mining Foreman"), "warfareBuff1Multiplier",
fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Mining Foreman"), "warfareBuff1Value",
src.getModifiedItemAttr("shipBonusICS2"), skill="Industrial Command Ships")
fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Mining Foreman"), "buffDuration",
src.getModifiedItemAttr("shipBonusICS2"), skill="Industrial Command Ships")
fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Mining Foreman"), "warfareBuff3Multiplier",
fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Mining Foreman"), "warfareBuff3Value",
src.getModifiedItemAttr("shipBonusICS2"), skill="Industrial Command Ships")
fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Mining Foreman"), "warfareBuff2Multiplier",
fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Mining Foreman"), "warfareBuff2Value",
src.getModifiedItemAttr("shipBonusICS2"), skill="Industrial Command Ships")

View File

@@ -2,7 +2,6 @@
#
# Used by:
# Modules from group: Ballistic Control system (17 of 17)
# Modules named like: QA Multiship Module Players (4 of 4)
type = "passive"

View File

@@ -6,7 +6,6 @@
# Modules from group: Reactor Control Unit (22 of 22)
# Modules from group: Shield Recharger (4 of 4)
# Modules named like: Flux Coil (12 of 12)
# Modules named like: QA Multiship Module Players (4 of 4)
type = "passive"

View File

@@ -1,4 +1,4 @@
# remoteWeaponDisruptEntity
# npcEntityWeaponDisruptor
#
# Used by:
# Drones named like: TD (3 of 3)

View File

@@ -11,8 +11,6 @@
# Modules from group: Smart Bomb (118 of 118)
# Modules from group: Warp Disrupt Field Generator (7 of 7)
# Modules named like: Ancillary Remote (8 of 8)
# Module: QA Remote Armor Repair System - 5 Players
# Module: QA Shield Transporter - 5 Players
# Module: Reactive Armor Hardener
# Module: Target Spectrum Breaker
type = "overheat"

View File

@@ -2,8 +2,6 @@
#
# Used by:
# Modules from group: Gyrostabilizer (13 of 13)
# Modules named like: QA Multiship Module Players (4 of 4)
# Module: QA Damage Module
type = "passive"

View File

@@ -1,7 +1,4 @@
# scanStrengthBonusPercentActivate
#
# Used by:
# Module: QA ECCM
# Not used by any item
type = "active"

View File

@@ -1,7 +1,7 @@
# scanStrengthBonusPercentPassive
#
# Used by:
# Implants named like: High grade (20 of 61)
# Implants named like: High grade (20 of 66)
type = "passive"

View File

@@ -1,8 +1,7 @@
# setBonusAsklepian
#
# Used by:
# Implants named like: Grade Asklepian (16 of 16)
# Implants named like: grade Asklepian Omega (2 of 2)
# Implants named like: grade Asklepian (18 of 18)
runTime = "early"
type = "passive"

View File

@@ -1,7 +1,4 @@
# shieldTransfer
#
# Used by:
# Module: QA Shield Transporter - 5 Players
# Not used by any item
type = "projected", "active"

View File

@@ -1,4 +1,4 @@
# remoteGuidanceDisruptFalloff
# shipModuleGuidanceDisruptor
#
# Used by:
# Variations of module: Guidance Disruptor I (6 of 6)

View File

@@ -1,4 +1,4 @@
# remoteTrackingAssistFalloff
# shipModuleRemoteTrackingComputer
#
# Used by:
# Modules from group: Remote Tracking Computer (8 of 8)

View File

@@ -1,4 +1,4 @@
# remoteTrackingDisruptFalloff
# shipModuleTrackingDisruptor
#
# Used by:
# Variations of module: Tracking Disruptor I (6 of 6)

View File

@@ -2,7 +2,6 @@
#
# Used by:
# Modules from group: Signal Amplifier (7 of 7)
# Module: QA Damage Module
type = "passive"

View File

@@ -1,10 +0,0 @@
# standardMissilesSkillBoostMissileVelocityBonus
#
# Used by:
# Skill: Defender Missiles
type = "passive"
def handler(fit, skill, context):
fit.modules.filteredChargeBoost(lambda mod: mod.charge.requiresSkill("Defender Missiles"),
"maxVelocity", skill.getModifiedItemAttr("missileVelocityBonus") * skill.level)

View File

@@ -3,7 +3,6 @@
# Used by:
# Implants named like: Inherent Implants 'Noble' Repair Proficiency RP (6 of 6)
# Modules named like: Auxiliary Nano Pump (8 of 8)
# Modules named like: QA Multiship Module Players (4 of 4)
# Implant: Imperial Navy Modified 'Noble' Implant
type = "passive"

View File

@@ -3,7 +3,6 @@
# Used by:
# Modules from group: Nanofiber Internal Structure (7 of 7)
# Modules from group: Reinforced Bulkhead (8 of 8)
# Modules named like: QA Multiship Module Players (4 of 4)
type = "passive"

View File

@@ -1,7 +1,4 @@
# targetArmorRepair
#
# Used by:
# Module: QA Remote Armor Repair System - 5 Players
# Not used by any item
type = "projected", "active"

View File

@@ -1,15 +0,0 @@
# Not used by any item
type = "projected", "active"
def handler(fit, container, context):
if "projected" in context:
fit.modules.filteredItemMultiply(lambda mod: mod.item.requiresSkill("Gunnery"),
"trackingSpeed", container.getModifiedItemAttr("trackingSpeedMultiplier"),
stackingPenalties=True, penaltyGroup="postMul")
fit.modules.filteredItemMultiply(lambda mod: mod.item.requiresSkill("Gunnery"),
"maxRange", container.getModifiedItemAttr("maxRangeMultiplier"),
stackingPenalties=True, penaltyGroup="postMul")
fit.modules.filteredItemMultiply(lambda mod: mod.item.requiresSkill("Gunnery"),
"falloff", container.getModifiedItemAttr("fallofMultiplier"),
stackingPenalties=True, penaltyGroup="postMul")

View File

@@ -3,7 +3,7 @@
# Used by:
# Modules from group: Missile Launcher Heavy (12 of 12)
# Modules from group: Missile Launcher Rocket (15 of 15)
# Modules named like: Launcher (151 of 151)
# Modules named like: Launcher (153 of 153)
type = 'active', "projected"

View File

@@ -18,7 +18,6 @@
# ===============================================================================
import re
import traceback
from sqlalchemy.orm import reconstructor
@@ -30,6 +29,9 @@ try:
except ImportError:
from utils.compat import OrderedDict
from logbook import Logger
pyfalog = Logger(__name__)
class Effect(EqBase):
"""
@@ -45,9 +47,6 @@ class Effect(EqBase):
# Filter to change names of effects to valid python method names
nameFilter = re.compile("[^A-Za-z0-9]")
def __init__(self):
pass
@reconstructor
def init(self):
"""
@@ -67,6 +66,8 @@ class Effect(EqBase):
if not self.__generated:
self.__generateHandler()
pyfalog.debug("Generating effect: {0} ({1}) [runTime: {2}]", self.name, self.effectID, self.runTime)
return self.__handler
@property
@@ -141,7 +142,7 @@ class Effect(EqBase):
@property
def isImplemented(self):
"""
Wether this effect is implemented in code or not,
Whether this effect is implemented in code or not,
unimplemented effects simply do nothing at all when run
"""
return self.handler != effectDummy
@@ -182,13 +183,16 @@ class Effect(EqBase):
t = t if isinstance(t, tuple) or t is None else (t,)
self.__type = t
except (ImportError, AttributeError):
except (ImportError, AttributeError) as e:
self.__handler = effectDummy
self.__runTime = "normal"
self.__activeByDefault = True
self.__type = None
pyfalog.debug("ImportError or AttributeError generating handler:")
pyfalog.debug(e)
except Exception as e:
traceback.print_exc(e)
pyfalog.critical("Exception generating handler:")
pyfalog.critical(e)
self.__generated = True
@@ -210,9 +214,6 @@ class Item(EqBase):
MOVE_ATTR_INFO = None
def __init__(self):
pass
@classmethod
def getMoveAttrInfo(cls):
info = getattr(cls, "MOVE_ATTR_INFO", None)
@@ -453,8 +454,6 @@ class Category(EqBase):
class AlphaClone(EqBase):
def __init__(self):
pass
@reconstructor
def init(self):
@@ -483,8 +482,6 @@ class Icon(EqBase):
class MarketGroup(EqBase):
def __init__(self):
pass
def __repr__(self):
return u"MarketGroup(ID={}, name={}, parent={}) at {}".format(
@@ -497,9 +494,6 @@ class MetaGroup(EqBase):
class MetaType(EqBase):
def __init__(self):
pass
pass

View File

@@ -21,6 +21,9 @@ from math import log, sin, radians, exp
from eos.graph import Graph
from eos.saveddata.module import State, Hardpoint
from logbook import Logger
pyfalog = Logger(__name__)
class FitDpsGraph(Graph):
@@ -65,8 +68,9 @@ class FitDpsGraph(Graph):
bonus = values[i]
val *= 1 + (bonus - 1) * exp(- i ** 2 / 7.1289)
data[attr] = val
except:
pass
except Exception as e:
pyfalog.critical("Caught exception in calcDPS.")
pyfalog.critical(e)
for mod in fit.modules:
dps, _ = mod.damageStats(fit.targetResists)

View File

@@ -17,7 +17,7 @@
# along with eos. If not, see <http://www.gnu.org/licenses/>.
# ===============================================================================
import logging
from logbook import Logger
from sqlalchemy.orm import reconstructor, validates
@@ -25,7 +25,7 @@ import eos.db
from eos.effectHandlerHelpers import HandledItem
from eos.modifiedAttributeDict import ModifiedAttributeDict, ItemAttrShortcut
logger = logging.getLogger(__name__)
pyfalog = Logger(__name__)
class Booster(HandledItem, ItemAttrShortcut):
@@ -47,11 +47,11 @@ class Booster(HandledItem, ItemAttrShortcut):
if self.itemID:
self.__item = eos.db.getItem(self.itemID)
if self.__item is None:
logger.error("Item (id: %d) does not exist", self.itemID)
pyfalog.error("Item (id: {0}) does not exist", self.itemID)
return
if self.isInvalid:
logger.error("Item (id: %d) is not a Booser", self.itemID)
pyfalog.error("Item (id: {0}) is not a Booster", self.itemID)
return
self.build()

View File

@@ -18,7 +18,7 @@
# ===============================================================================
import sys
import logging
from logbook import Logger
from sqlalchemy.orm import validates, reconstructor
@@ -26,7 +26,7 @@ import eos.db
from eos.effectHandlerHelpers import HandledItem
from eos.modifiedAttributeDict import ModifiedAttributeDict, ItemAttrShortcut
logger = logging.getLogger(__name__)
pyfalog = Logger(__name__)
class Cargo(HandledItem, ItemAttrShortcut):
@@ -47,7 +47,7 @@ class Cargo(HandledItem, ItemAttrShortcut):
if self.itemID:
self.__item = eos.db.getItem(self.itemID)
if self.__item is None:
logger.error("Item (id: %d) does not exist", self.itemID)
pyfalog.error("Item (id: {0}) does not exist", self.itemID)
return
self.__itemModifiedAttributes = ModifiedAttributeDict()

View File

@@ -18,7 +18,7 @@
# ===============================================================================
import logging
from logbook import Logger
from itertools import chain
from sqlalchemy.orm import validates, reconstructor
@@ -27,7 +27,7 @@ import eos
import eos.db
from eos.effectHandlerHelpers import HandledItem, HandledImplantBoosterList
logger = logging.getLogger(__name__)
pyfalog = Logger(__name__)
class Character(object):
@@ -140,7 +140,17 @@ class Character(object):
@property
def name(self):
return self.savedName if not self.isDirty else "{} *".format(self.savedName)
name = self.savedName
if self.isDirty:
name += " *"
if self.alphaCloneID:
clone = eos.db.getAlphaClone(self.alphaCloneID)
type = clone.alphaCloneName.split()[1]
name += u' (\u03B1{})'.format(type[0].upper())
return name
@name.setter
def name(self, name):

View File

@@ -17,16 +17,17 @@
# along with eos. If not, see <http://www.gnu.org/licenses/>.
# ===============================================================================
import logging
from logbook import Logger
from eos.saveddata.ship import Ship
logger = logging.getLogger(__name__)
pyfalog = Logger(__name__)
class Citadel(Ship):
def validate(self, item):
if item.category.name != "Structure":
pyfalog.error("Passed item '{0}' (category: {1}) is not under Structure category", item.name, item.category.name)
raise ValueError(
'Passed item "%s" (category: (%s)) is not under Structure category' % (item.name, item.category.name))

View File

@@ -17,7 +17,7 @@
# along with eos. If not, see <http://www.gnu.org/licenses/>.
# ===============================================================================
import logging
from logbook import Logger
from sqlalchemy.orm import validates, reconstructor
@@ -25,7 +25,7 @@ import eos.db
from eos.effectHandlerHelpers import HandledItem, HandledCharge
from eos.modifiedAttributeDict import ModifiedAttributeDict, ItemAttrShortcut, ChargeAttrShortcut
logger = logging.getLogger(__name__)
pyfalog = Logger(__name__)
class Drone(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
@@ -53,11 +53,11 @@ class Drone(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
if self.itemID:
self.__item = eos.db.getItem(self.itemID)
if self.__item is None:
logger.error("Item (id: %d) does not exist", self.itemID)
pyfalog.error("Item (id: {0}) does not exist", self.itemID)
return
if self.isInvalid:
logger.error("Item (id: %d) is not a Drone", self.itemID)
pyfalog.error("Item (id: {0}) is not a Drone", self.itemID)
return
self.build()

View File

@@ -17,7 +17,7 @@
# along with eos. If not, see <http://www.gnu.org/licenses/>.
# ===============================================================================
import logging
from logbook import Logger
from sqlalchemy.orm import validates, reconstructor
@@ -27,7 +27,7 @@ from eos.modifiedAttributeDict import ModifiedAttributeDict, ItemAttrShortcut, C
from eos.saveddata.fighterAbility import FighterAbility
from eos.saveddata.module import Slot
logger = logging.getLogger(__name__)
pyfalog = Logger(__name__)
class Fighter(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
@@ -61,11 +61,11 @@ class Fighter(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
if self.itemID:
self.__item = eos.db.getItem(self.itemID)
if self.__item is None:
logger.error("Item (id: %d) does not exist", self.itemID)
pyfalog.error("Item (id: {0}) does not exist", self.itemID)
return
if self.isInvalid:
logger.error("Item (id: %d) is not a Fighter", self.itemID)
pyfalog.error("Item (id: {0}) is not a Fighter", self.itemID)
return
self.build()

View File

@@ -17,11 +17,11 @@
# along with eos. If not, see <http://www.gnu.org/licenses/>.
# ===============================================================================
import logging
from logbook import Logger
from sqlalchemy.orm import reconstructor
logger = logging.getLogger(__name__)
pyfalog = Logger(__name__)
class FighterAbility(object):
@@ -59,7 +59,7 @@ class FighterAbility(object):
if self.effectID:
self.__effect = next((x for x in self.fighter.item.effects.itervalues() if x.ID == self.effectID), None)
if self.__effect is None:
logger.error("Effect (id: %d) does not exist", self.effectID)
pyfalog.error("Effect (id: {0}) does not exist", self.effectID)
return
self.build()

View File

@@ -33,9 +33,9 @@ from eos.saveddata.character import Character
from eos.saveddata.citadel import Citadel
from eos.saveddata.module import Module, State, Slot, Hardpoint
from utils.timer import Timer
import logging
from logbook import Logger
logger = logging.getLogger(__name__)
pyfalog = Logger(__name__)
class ImplantLocation(Enum):
@@ -84,7 +84,7 @@ class Fit(object):
if self.shipID:
item = eos.db.getItem(self.shipID)
if item is None:
logger.error("Item (id: %d) does not exist", self.shipID)
pyfalog.error("Item (id: {0}) does not exist", self.shipID)
return
try:
@@ -97,7 +97,7 @@ class Fit(object):
# change all instances in source). Remove this at some point
self.extraAttributes = self.__ship.itemModifiedAttributes
except ValueError:
logger.error("Item (id: %d) is not a Ship", self.shipID)
pyfalog.error("Item (id: {0}) is not a Ship", self.shipID)
return
if self.modeID and self.__ship:
@@ -457,7 +457,7 @@ class Fit(object):
self.commandBonuses[warfareBuffID] = (runTime, value, module, effect)
def __runCommandBoosts(self, runTime="normal"):
logger.debug("Applying gang boosts for %r", self)
pyfalog.debug("Applying gang boosts for {0}", self)
for warfareBuffID in self.commandBonuses.keys():
# Unpack all data required to run effect properly
effect_runTime, value, thing, effect = self.commandBonuses[warfareBuffID]
@@ -639,21 +639,21 @@ class Fit(object):
del self.commandBonuses[warfareBuffID]
def calculateModifiedAttributes(self, targetFit=None, withBoosters=False, dirtyStorage=None):
timer = Timer(u'Fit: {}, {}'.format(self.ID, self.name), logger)
logger.debug("Starting fit calculation on: %r, withBoosters: %s", self, withBoosters)
timer = Timer(u'Fit: {}, {}'.format(self.ID, self.name), pyfalog)
pyfalog.debug("Starting fit calculation on: {0}, withBoosters: {1}", self, withBoosters)
shadow = False
if targetFit and not withBoosters:
logger.debug("Applying projections to target: %r", targetFit)
pyfalog.debug("Applying projections to target: {0}", targetFit)
projectionInfo = self.getProjectionInfo(targetFit.ID)
logger.debug("ProjectionInfo: %s", projectionInfo)
pyfalog.debug("ProjectionInfo: {0}", projectionInfo)
if self == targetFit:
copied = self # original fit
shadow = True
# Don't inspect this, we genuinely want to reassign self
# noinspection PyMethodFirstArgAssignment
self = deepcopy(self)
logger.debug("Handling self projection - making shadow copy of fit. %r => %r", copied, self)
pyfalog.debug("Handling self projection - making shadow copy of fit. {0} => {1}", copied, self)
# we delete the fit because when we copy a fit, flush() is
# called to properly handle projection updates. However, we do
# not want to save this fit to the database, so simply remove it
@@ -688,7 +688,7 @@ class Fit(object):
# projection have modifying stuff applied, such as gang boosts and other
# local modules that may help
if self.__calculated and not projected and not withBoosters:
logger.debug("Fit has already been calculated and is not projected, returning: %r", self)
pyfalog.debug("Fit has already been calculated and is not projected, returning: {0}", self)
return
for runTime in ("early", "normal", "late"):
@@ -739,6 +739,10 @@ class Fit(object):
# targetFit.register(item, origin=self)
item.calculateModifiedAttributes(targetFit, runTime, False, True)
if len(self.commandBonuses) > 0:
pyfalog.info("Command bonuses applied.")
pyfalog.debug(self.commandBonuses)
if not withBoosters and self.commandBonuses:
self.__runCommandBoosts(runTime)
@@ -756,7 +760,7 @@ class Fit(object):
timer.checkpoint('Done with fit calculation')
if shadow:
logger.debug("Delete shadow fit object")
pyfalog.debug("Delete shadow fit object")
del self
def fill(self):

View File

@@ -17,7 +17,7 @@
# along with eos. If not, see <http://www.gnu.org/licenses/>.
# ===============================================================================
import logging
from logbook import Logger
from sqlalchemy.orm import validates, reconstructor
@@ -25,7 +25,7 @@ import eos.db
from eos.effectHandlerHelpers import HandledItem
from eos.modifiedAttributeDict import ModifiedAttributeDict, ItemAttrShortcut
logger = logging.getLogger(__name__)
pyfalog = Logger(__name__)
class Implant(HandledItem, ItemAttrShortcut):
@@ -46,11 +46,11 @@ class Implant(HandledItem, ItemAttrShortcut):
if self.itemID:
self.__item = eos.db.getItem(self.itemID)
if self.__item is None:
logger.error("Item (id: %d) does not exist", self.itemID)
pyfalog.error("Item (id: {0}) does not exist", self.itemID)
return
if self.isInvalid:
logger.error("Item (id: %d) is not an Implant", self.itemID)
pyfalog.error("Item (id: {0}) is not an Implant", self.itemID)
return
self.build()

View File

@@ -17,7 +17,7 @@
# along with eos. If not, see <http://www.gnu.org/licenses/>.
# ===============================================================================
import logging
from logbook import Logger
from sqlalchemy.orm import validates, reconstructor
from math import floor
@@ -28,7 +28,7 @@ from eos.enum import Enum
from eos.modifiedAttributeDict import ModifiedAttributeDict, ItemAttrShortcut, ChargeAttrShortcut
from eos.saveddata.citadel import Citadel
logger = logging.getLogger(__name__)
pyfalog = Logger(__name__)
class State(Enum):
@@ -94,11 +94,11 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
if self.itemID:
self.__item = eos.db.getItem(self.itemID)
if self.__item is None:
logger.error("Item (id: %d) does not exist", self.itemID)
pyfalog.error("Item (id: {0}) does not exist", self.itemID)
return
if self.isInvalid:
logger.error("Item (id: %d) is not a Module", self.itemID)
pyfalog.error("Item (id: {0}) is not a Module", self.itemID)
return
if self.chargeID:

View File

@@ -17,14 +17,14 @@
# along with eos. If not, see <http://www.gnu.org/licenses/>.
# ===============================================================================
import logging
from logbook import Logger
from sqlalchemy.orm import reconstructor
import eos.db
from eos.eqBase import EqBase
logger = logging.getLogger(__name__)
pyfalog = Logger(__name__)
class Override(EqBase):
@@ -43,13 +43,13 @@ class Override(EqBase):
if self.attrID:
self.__attr = eos.db.getAttributeInfo(self.attrID)
if self.__attr is None:
logger.error("Attribute (id: %d) does not exist", self.attrID)
pyfalog.error("Attribute (id: {0}) does not exist", self.attrID)
return
if self.itemID:
self.__item = eos.db.getItem(self.itemID)
if self.__item is None:
logger.error("Item (id: %d) does not exist", self.itemID)
pyfalog.error("Item (id: {0}) does not exist", self.itemID)
return
@property

View File

@@ -17,14 +17,14 @@
# along with eos. If not, see <http://www.gnu.org/licenses/>.
# ===============================================================================
import logging
from logbook import Logger
import eos.db
from eos.effectHandlerHelpers import HandledItem
from eos.modifiedAttributeDict import ModifiedAttributeDict, ItemAttrShortcut, cappingAttrKeyCache
from eos.saveddata.mode import Mode
logger = logging.getLogger(__name__)
pyfalog = Logger(__name__)
class Ship(ItemAttrShortcut, HandledItem):
@@ -63,6 +63,7 @@ class Ship(ItemAttrShortcut, HandledItem):
def validate(self, item):
if item.category.name != "Ship":
pyfalog.error("Passed item '{0}' (category: {1}) is not under Ship category", item.name, item.category.name)
raise ValueError(
'Passed item "%s" (category: (%s)) is not under Ship category' % (item.name, item.category.name))

View File

@@ -18,6 +18,9 @@
# ===============================================================================
import re
from logbook import Logger
pyfalog = Logger(__name__)
class TargetResists(object):
@@ -43,7 +46,7 @@ class TargetResists(object):
type, data = line.rsplit('=', 1)
type, data = type.strip(), data.split(',')
except:
# Data isn't in correct format, continue to next line
pyfalog.warning("Data isn't in correct format, continue to next line.")
continue
if type != "TargetResists":
@@ -59,6 +62,7 @@ class TargetResists(object):
assert 0 <= val <= 100
fields["%sAmount" % cls.DAMAGE_TYPES[index]] = val / 100
except:
pyfalog.warning("Caught unhandled exception in import patterns.")
continue
if len(fields) == 4: # Avoid possible blank lines

BIN
eve.db

Binary file not shown.

View File

@@ -20,13 +20,15 @@
import cStringIO
import os.path
import zipfile
from config import parsePath
# noinspection PyPackageRequirements
import wx
import config
from logbook import Logger
logging = Logger(__name__)
try:
from collections import OrderedDict
except ImportError:
@@ -35,8 +37,10 @@ except ImportError:
class BitmapLoader(object):
try:
archive = zipfile.ZipFile(config.getPyfaPath('imgs.zip'), 'r')
archive = zipfile.ZipFile(os.path.join(config.pyfaPath, 'imgs.zip'), 'r')
logging.info("Using zipped image files.")
except IOError:
logging.info("Using local image files.")
archive = None
cachedBitmaps = OrderedDict()
@@ -78,7 +82,7 @@ class BitmapLoader(object):
filename = "{0}.png".format(name)
if cls.archive:
path = parsePath(location, filename)
path = os.path.join(location, filename)
if os.sep != "/" and os.sep in path:
path = path.replace(os.sep, "/")
@@ -89,7 +93,7 @@ class BitmapLoader(object):
except KeyError:
print("Missing icon file from zip: {0}".format(path))
else:
path = config.getPyfaPath('imgs' + os.sep + location + os.sep + filename)
path = os.path.join(config.pyfaPath, 'imgs' + os.sep + location + os.sep + filename)
if os.path.exists(path):
return wx.Image(path)

View File

@@ -52,6 +52,7 @@ class AmountChanger(wx.Dialog):
def change(self, event):
if self.input.GetLineText(0).strip() == '':
event.Skip()
self.Close()
return
sFit = Fit.getInstance()
@@ -68,6 +69,7 @@ class AmountChanger(wx.Dialog):
wx.PostEvent(mainFrame, GE.FitChanged(fitID=fitID))
event.Skip()
self.Close()
# checks to make sure it's valid number
@staticmethod

View File

@@ -33,7 +33,7 @@ from gui.builtinViewColumns.state import State
from gui.bitmapLoader import BitmapLoader
import gui.builtinViews.emptyView
from gui.utils.exportHtml import exportHtml
from logging import getLogger
from logbook import Logger
from gui.chromeTabs import EVT_NOTEBOOK_PAGE_CHANGED
from service.fit import Fit
@@ -41,7 +41,7 @@ from service.market import Market
import gui.globalEvents as GE
logger = getLogger(__name__)
pyfalog = Logger(__name__)
# Tab spawning handler
@@ -61,8 +61,9 @@ class FitSpawner(gui.multiSwitch.TabSpawner):
self.multiSwitch.SetSelection(index)
wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=event.fitID))
break
except:
pass
except Exception as e:
pyfalog.critical("Caught exception in fitSelected")
pyfalog.critical(e)
if count < 0:
startup = getattr(event, "startup", False) # see OpenFitsThread in gui.mainFrame
sFit = Fit.getInstance()
@@ -278,6 +279,7 @@ class FittingView(d.Display):
sFit.refreshFit(self.getActiveFit())
wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.activeFitID))
except wx._core.PyDeadObjectError:
pyfalog.warning("Caught dead object")
pass
event.Skip()
@@ -414,7 +416,7 @@ class FittingView(d.Display):
wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.mainFrame.getActiveFit()))
else:
logger.error("Missing module position for: %s", str(getattr(mod2, "ID", "Unknown")))
pyfalog.error("Missing module position for: {0}", str(getattr(mod2, "ID", "Unknown")))
def generateMods(self):
"""
@@ -483,7 +485,7 @@ class FittingView(d.Display):
self.Show(self.activeFitID is not None and self.activeFitID == event.fitID)
except wx._core.PyDeadObjectError:
pass
pyfalog.warning("Caught dead object")
finally:
event.Skip()
@@ -636,15 +638,17 @@ class FittingView(d.Display):
if 'wxMac' in wx.PlatformInfo:
try:
self.MakeSnapshot()
except:
pass
except Exception as e:
pyfalog.critical("Failed to make snapshot")
pyfalog.critical(e)
def OnShow(self, event):
if event.GetShow():
try:
self.MakeSnapshot()
except:
pass
except Exception as e:
pyfalog.critical("Failed to make snapshot")
pyfalog.critical(e)
event.Skip()
def Snapshot(self):
@@ -669,8 +673,9 @@ class FittingView(d.Display):
sFit = Fit.getInstance()
try:
fit = sFit.getFit(self.activeFitID)
except:
return
except Exception as e:
pyfalog.critical("Failed to get fit")
pyfalog.critical(e)
if fit is None:
return

View File

@@ -33,6 +33,8 @@ from service.fit import Fit
from service.character import Character
from service.network import AuthenticationError, TimeoutError
from service.market import Market
from logbook import Logger
pyfalog = Logger(__name__)
class CharacterTextValidor(BaseValidator):
@@ -55,6 +57,7 @@ class CharacterTextValidor(BaseValidator):
return True
except ValueError, e:
pyfalog.error(e)
wx.MessageBox(u"{}".format(e), "Error")
textCtrl.SetFocus()
return False
@@ -628,11 +631,16 @@ class APIView(wx.Panel):
try:
activeChar = self.charEditor.entityEditor.getActiveEntity()
list = sChar.apiCharList(activeChar.ID, self.inputID.GetLineText(0), self.inputKey.GetLineText(0))
except AuthenticationError:
self.stStatus.SetLabel("Authentication failure. Please check keyID and vCode combination.")
except TimeoutError:
self.stStatus.SetLabel("Request timed out. Please check network connectivity and/or proxy settings.")
except AuthenticationError, e:
msg = "Authentication failure. Please check keyID and vCode combination."
pyfalog.info(msg)
self.stStatus.SetLabel(msg)
except TimeoutError, e:
msg = "Request timed out. Please check network connectivity and/or proxy settings."
pyfalog.info(msg)
self.stStatus.SetLabel(msg)
except Exception, e:
pyfalog.error(e)
self.stStatus.SetLabel("Error:\n%s" % e.message)
else:
self.charChoice.Clear()
@@ -655,6 +663,7 @@ class APIView(wx.Panel):
sChar.apiFetch(activeChar.ID, charName)
self.stStatus.SetLabel("Successfully fetched %s\'s skills from EVE API." % charName)
except Exception, e:
pyfalog.error("Unable to retrieve {0}\'s skills. Error message:\n{1}", charName, e)
self.stStatus.SetLabel("Unable to retrieve %s\'s skills. Error message:\n%s" % (charName, e))

View File

@@ -24,6 +24,8 @@ import gui.globalEvents as GE
import gui.mainFrame
from service.character import Character
from service.fit import Fit
from logbook import Logger
pyfalog = Logger(__name__)
class CharacterSelection(wx.Panel):
@@ -112,9 +114,10 @@ class CharacterSelection(wx.Panel):
if charName:
try:
sChar.apiFetch(self.getActiveCharacter(), charName)
except:
except Exception as e:
# can we do a popup, notifying user of API error?
pass
pyfalog.error("API fetch error")
pyfalog.error(e)
self.refreshCharacterList()
def charChanged(self, event):

View File

@@ -25,9 +25,11 @@ import gui.utils.colorUtils as colorUtils
import gui.utils.drawUtils as drawUtils
import gui.utils.fonts as fonts
from gui.bitmapLoader import BitmapLoader
from logbook import Logger
from service.fit import Fit
pyfalog = Logger(__name__)
_PageChanging, EVT_NOTEBOOK_PAGE_CHANGING = wx.lib.newevent.NewEvent()
_PageChanged, EVT_NOTEBOOK_PAGE_CHANGED = wx.lib.newevent.NewEvent()
_PageAdding, EVT_NOTEBOOK_PAGE_ADDING = wx.lib.newevent.NewEvent()
@@ -1093,8 +1095,9 @@ class PFTabsContainer(wx.Panel):
self.previewTab = tab
self.previewTimer.Start(500, True)
break
except:
pass
except Exception as e:
pyfalog.critical("Exception caught in CheckTabPreview.")
pyfalog.critical(e)
def CheckAddHighlighted(self, x, y):
"""

View File

@@ -19,9 +19,9 @@
# noinspection PyPackageRequirements
import wx
import logging
from logbook import Logger
logger = logging.getLogger(__name__)
pyfalog = Logger(__name__)
class ContextMenu(object):
@@ -121,7 +121,7 @@ class ContextMenu(object):
debug_end = len(cls._ids)
if debug_end - debug_start:
logger.debug("%d new IDs created for this menu" % (debug_end - debug_start))
pyfalog.debug("{0} new IDs created for this menu", (debug_end - debug_start))
return rootMenu if empty is False else None
@@ -180,27 +180,27 @@ class ContextMenu(object):
# noinspection PyUnresolvedReferences
from gui.builtinContextMenus import ( # noqa: E402,F401
ammoPattern,
amount,
cargo,
changeAffectingSkills,
damagePattern,
droneRemoveStack,
droneSplit,
factorReload,
fighterAbilities,
implantSets,
itemRemove,
itemStats,
marketJump,
metaSwap,
moduleAmmoPicker,
moduleGlobalAmmoPicker,
openFit,
priceClear,
# moduleGlobalAmmoPicker,
moduleAmmoPicker,
itemStats,
damagePattern,
marketJump,
droneSplit,
itemRemove,
droneRemoveStack,
ammoPattern,
project,
factorReload,
whProjector,
cargo,
shipJump,
changeAffectingSkills,
tacticalMode,
targetResists,
whProjector
priceClear,
amount,
metaSwap,
implantSets,
fighterAbilities,
)

View File

@@ -14,6 +14,9 @@ from eos.db import getItem
from gui.display import Display
import gui.globalEvents as GE
from logbook import Logger
pyfalog = Logger(__name__)
if 'wxMac' not in wx.PlatformInfo or ('wxMac' in wx.PlatformInfo and wx.VERSION >= (3, 0)):
from service.crest import Crest, CrestModes
@@ -147,7 +150,9 @@ class CrestFittings(wx.Frame):
self.fitTree.populateSkillTree(fittings)
del waitDialog
except requests.exceptions.ConnectionError:
self.statusbar.SetStatusText("Connection error, please check your internet connection")
msg = "Connection error, please check your internet connection"
pyfalog.error(msg)
self.statusbar.SetStatusText(msg)
def importFitting(self, event):
selection = self.fitView.fitSelection
@@ -173,7 +178,9 @@ class CrestFittings(wx.Frame):
try:
sCrest.delFitting(self.getActiveCharacter(), data['fittingID'])
except requests.exceptions.ConnectionError:
self.statusbar.SetStatusText("Connection error, please check your internet connection")
msg = "Connection error, please check your internet connection"
pyfalog.error(msg)
self.statusbar.SetStatusText(msg)
class ExportToEve(wx.Frame):
@@ -281,9 +288,12 @@ class ExportToEve(wx.Frame):
text = json.loads(res.text)
self.statusbar.SetStatusText(text['message'], 1)
except ValueError:
pyfalog.warning("Value error on loading JSON.")
self.statusbar.SetStatusText("", 1)
except requests.exceptions.ConnectionError:
self.statusbar.SetStatusText("Connection error, please check your internet connection", 1)
msg = "Connection error, please check your internet connection"
pyfalog.error(msg)
self.statusbar.SetStatusText(msg)
class CrestMgmt(wx.Dialog):
@@ -405,8 +415,9 @@ class FittingsTreeView(wx.Panel):
cargo = Cargo(getItem(item['type']['id']))
cargo.amount = item['quantity']
list.append(cargo)
except:
pass
except Exception as e:
pyfalog.critical("Exception caught in displayFit")
pyfalog.critical(e)
self.parent.fitView.fitSelection = selection
self.parent.fitView.update(list)

View File

@@ -147,6 +147,7 @@ class DroneView(Display):
def _merge(self, src, dst):
sFit = Fit.getInstance()
fitID = self.mainFrame.getActiveFit()
if sFit.mergeDrones(fitID, self.drones[src], self.drones[dst]):
wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=fitID))

94
gui/errorDialog.py Normal file
View File

@@ -0,0 +1,94 @@
# ===============================================================================
# Copyright (C) 2010 Diego Duclos
#
# 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 <http://www.gnu.org/licenses/>.
# ===============================================================================
import wx
import sys
import gui.utils.fonts as fonts
import config
class ErrorFrame(wx.Frame):
def __init__(self, exception, tb):
wx.Frame.__init__(self, None, id=wx.ID_ANY, title="pyfa error", pos=wx.DefaultPosition, size=wx.Size(500, 400),
style=wx.DEFAULT_FRAME_STYLE ^ wx.RESIZE_BORDER | wx.STAY_ON_TOP)
desc = "pyfa has experienced an unexpected error. Below is the " \
"Traceback that contains crucial information about how this " \
"error was triggered. Please contact the developers with " \
"the information provided through the EVE Online forums " \
"or file a GitHub issue."
self.SetSizeHintsSz(wx.DefaultSize, wx.DefaultSize)
if 'wxMSW' in wx.PlatformInfo:
self.SetBackgroundColour(wx.SystemSettings.GetColour(wx.SYS_COLOUR_BTNFACE))
mainSizer = wx.BoxSizer(wx.VERTICAL)
headSizer = wx.BoxSizer(wx.HORIZONTAL)
self.headingText = wx.StaticText(self, wx.ID_ANY, "Error!", wx.DefaultPosition, wx.DefaultSize, wx.ALIGN_CENTRE)
self.headingText.SetFont(wx.Font(14, 74, 90, 92, False))
headSizer.Add(self.headingText, 1, wx.ALL, 5)
mainSizer.Add(headSizer, 0, wx.EXPAND, 5)
mainSizer.Add(wx.StaticLine(self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LI_HORIZONTAL), 0, wx.EXPAND | wx.ALL, 5)
descSizer = wx.BoxSizer(wx.HORIZONTAL)
self.descText = wx.TextCtrl(self, wx.ID_ANY, desc, wx.DefaultPosition, wx.DefaultSize,
wx.TE_AUTO_URL | wx.TE_MULTILINE | wx.TE_READONLY | wx.BORDER_NONE | wx.TRANSPARENT_WINDOW)
self.descText.SetFont(wx.Font(fonts.BIG, wx.SWISS, wx.NORMAL, wx.NORMAL))
descSizer.Add(self.descText, 1, wx.ALL, 5)
mainSizer.Add(descSizer, 1, wx.EXPAND, 5)
self.eveForums = wx.HyperlinkCtrl(self, wx.ID_ANY, "EVE Forums Thread", "https://forums.eveonline.com/default.aspx?g=posts&t=466425",
wx.DefaultPosition, wx.DefaultSize, wx.HL_DEFAULT_STYLE)
mainSizer.Add(self.eveForums, 0, wx.ALL, 2)
self.eveForums = wx.HyperlinkCtrl(self, wx.ID_ANY, "Github Issues", "https://github.com/pyfa-org/Pyfa/issues",
wx.DefaultPosition, wx.DefaultSize, wx.HL_DEFAULT_STYLE)
mainSizer.Add(self.eveForums, 0, wx.ALL, 2)
# mainSizer.AddSpacer((0, 5), 0, wx.EXPAND, 5)
self.errorTextCtrl = wx.TextCtrl(self, wx.ID_ANY, "", wx.DefaultPosition, wx.DefaultSize, wx.TE_MULTILINE | wx.TE_READONLY | wx.TE_RICH2 | wx.TE_DONTWRAP)
self.errorTextCtrl.SetFont(wx.Font(8, wx.FONTFAMILY_TELETYPE, wx.NORMAL, wx.NORMAL))
mainSizer.Add(self.errorTextCtrl, 0, wx.EXPAND | wx.LEFT | wx.RIGHT, 5)
self.errorTextCtrl.AppendText("pyfa root: ")
self.errorTextCtrl.AppendText(config.pyfaPath or "Unknown")
self.errorTextCtrl.AppendText('\n')
self.errorTextCtrl.AppendText("save path: ")
self.errorTextCtrl.AppendText(config.savePath or "Unknown")
self.errorTextCtrl.AppendText('\n')
self.errorTextCtrl.AppendText("fs encoding: ")
self.errorTextCtrl.AppendText(sys.getfilesystemencoding())
self.errorTextCtrl.AppendText('\n\n')
self.errorTextCtrl.AppendText(tb)
self.SetSizer(mainSizer)
mainSizer.Layout()
self.Layout()
self.Centre(wx.BOTH)
self.Show()

View File

@@ -18,8 +18,7 @@
# =============================================================================
import os
import logging
import imp
from logbook import Logger
# noinspection PyPackageRequirements
import wx
@@ -30,19 +29,31 @@ import gui.mainFrame
import gui.globalEvents as GE
from gui.graph import Graph
from gui.bitmapLoader import BitmapLoader
from config import parsePath
# Don't actually import the thing, since it takes for fucking ever
pyfalog = Logger(__name__)
try:
imp.find_module('matplotlib')
import matplotlib as mpl
mpl_version = int(mpl.__version__[0])
if mpl_version >= 2:
mpl.use('wxagg')
mplImported = True
else:
mplImported = False
from matplotlib.patches import Patch
from matplotlib.backends.backend_wxagg import FigureCanvasWxAgg as Canvas
from matplotlib.figure import Figure
graphFrame_enabled = True
mplImported = True
except ImportError:
Patch = mpl = Canvas = Figure = None
graphFrame_enabled = False
mplImported = False
logger = logging.getLogger(__name__)
pyfalog = Logger(__name__)
class GraphFrame(wx.Frame):
@@ -73,7 +84,7 @@ class GraphFrame(wx.Frame):
self.legendFix = False
if not graphFrame_enabled:
logger.info("Problems importing matplotlib; continuing without graphs")
pyfalog.info("Problems importing matplotlib; continuing without graphs")
return
try:
@@ -81,7 +92,7 @@ class GraphFrame(wx.Frame):
except:
cache_dir = os.path.expanduser(os.path.join("~", ".matplotlib"))
cache_file = parsePath(cache_dir, 'fontList.cache')
cache_file = os.path.join(cache_dir, 'fontList.cache')
if os.access(cache_dir, os.W_OK | os.X_OK) and os.path.isfile(cache_file):
# remove matplotlib font cache, see #234
@@ -244,6 +255,7 @@ class GraphFrame(wx.Frame):
self.subplot.plot(x, y)
legend.append(fit.name)
except:
pyfalog.warning("Invalid values in '{0}'", fit.name)
self.SetStatusText("Invalid values in '%s'" % fit.name)
self.canvas.draw()
return
@@ -282,7 +294,7 @@ class GraphFrame(wx.Frame):
selected_color = legend_colors[i]
except:
selected_color = None
legend2.append(self.Patch(color=selected_color, label=i_name), )
legend2.append(Patch(color=selected_color, label=i_name), )
if len(legend2) > 0:
leg = self.subplot.legend(handles=legend2)

View File

@@ -62,13 +62,13 @@ class ItemStatsDialog(wx.Dialog):
):
wx.Dialog.__init__(
self,
gui.mainFrame.MainFrame.getInstance(),
wx.ID_ANY,
title="Item stats",
pos=pos,
size=size,
style=wx.CAPTION | wx.CLOSE_BOX | wx.MINIMIZE_BOX | wx.MAXIMIZE_BOX | wx.RESIZE_BORDER | wx.SYSTEM_MENU
self,
gui.mainFrame.MainFrame.getInstance(),
wx.ID_ANY,
title="Item stats",
pos=pos,
size=size,
style=wx.CAPTION | wx.CLOSE_BOX | wx.MINIMIZE_BOX | wx.MAXIMIZE_BOX | wx.RESIZE_BORDER | wx.SYSTEM_MENU
)
empty = getattr(victim, "isEmpty", False)
@@ -197,6 +197,10 @@ class ItemStatsContainer(wx.Panel):
self.affectedby = ItemAffectedBy(self.nbContainer, stuff, item)
self.nbContainer.AddPage(self.affectedby, "Affected by")
if config.debug:
self.properties = ItemProperties(self.nbContainer, stuff, item, context)
self.nbContainer.AddPage(self.properties, "Properties")
self.nbContainer.Bind(wx.EVT_LEFT_DOWN, self.mouseHit)
self.SetSizer(mainSizer)
self.Layout()
@@ -256,7 +260,7 @@ class ItemDescription(wx.Panel):
# Strip URLs
desc = re.sub("<( *)a(.*?)>(?P<inside>.*?)<( *)/( *)a( *)>", "\g<inside>", desc)
desc = "<body bgcolor='" + bgcolor.GetAsString(wx.C2S_HTML_SYNTAX) + "' text='" + fgcolor.GetAsString(
wx.C2S_HTML_SYNTAX) + "' >" + desc + "</body>"
wx.C2S_HTML_SYNTAX) + "' >" + desc + "</body>"
self.description.SetPage(desc)
@@ -358,13 +362,13 @@ class ItemParams(wx.Panel):
writer = csv.writer(exportFile, delimiter=',')
writer.writerow(
[
"ID",
"Internal Name",
"Friendly Name",
"Modified Value",
"Base Value",
]
[
"ID",
"Internal Name",
"Friendly Name",
"Modified Value",
"Base Value",
]
)
for attribute in self.attrValues:
@@ -395,13 +399,13 @@ class ItemParams(wx.Panel):
attribute_modified_value = self.attrValues[attribute]
writer.writerow(
[
attribute_id,
attribute_name,
attribute_displayname,
attribute_modified_value,
attribute_value,
]
[
attribute_id,
attribute_name,
attribute_displayname,
attribute_modified_value,
attribute_value,
]
)
def PopulateList(self):
@@ -496,17 +500,19 @@ class ItemParams(wx.Panel):
attribute = Attribute.getInstance().getAttributeInfo(value)
return "%s (%d)" % (attribute.name.capitalize(), value)
trans = {"Inverse Absolute Percent": (lambda: (1 - value) * 100, unitName),
"Inversed Modifier Percent": (lambda: (1 - value) * 100, unitName),
"Modifier Percent": (
lambda: ("%+.2f" if ((value - 1) * 100) % 1 else "%+d") % ((value - 1) * 100), unitName),
"Volume": (lambda: value, u"m\u00B3"),
"Sizeclass": (lambda: value, ""),
"Absolute Percent": (lambda: (value * 100), unitName),
"Milliseconds": (lambda: value / 1000.0, unitName),
"typeID": (itemIDCallback, ""),
"groupID": (groupIDCallback, ""),
"attributeID": (attributeIDCallback, "")}
trans = {
"Inverse Absolute Percent" : (lambda: (1 - value) * 100, unitName),
"Inversed Modifier Percent": (lambda: (1 - value) * 100, unitName),
"Modifier Percent" : (
lambda: ("%+.2f" if ((value - 1) * 100) % 1 else "%+d") % ((value - 1) * 100), unitName),
"Volume" : (lambda: value, u"m\u00B3"),
"Sizeclass" : (lambda: value, ""),
"Absolute Percent" : (lambda: (value * 100), unitName),
"Milliseconds" : (lambda: value / 1000.0, unitName),
"typeID" : (itemIDCallback, ""),
"groupID" : (groupIDCallback, ""),
"attributeID" : (attributeIDCallback, "")
}
override = trans.get(unitDisplayName)
if override is not None:
@@ -689,17 +695,18 @@ class ItemCompare(wx.Panel):
attribute = Attribute.getInstance().getAttributeInfo(value)
return "%s (%d)" % (attribute.name.capitalize(), value)
trans = {"Inverse Absolute Percent": (lambda: (1 - value) * 100, unitName),
"Inversed Modifier Percent": (lambda: (1 - value) * 100, unitName),
"Modifier Percent": (
lambda: ("%+.2f" if ((value - 1) * 100) % 1 else "%+d") % ((value - 1) * 100), unitName),
"Volume": (lambda: value, u"m\u00B3"),
"Sizeclass": (lambda: value, ""),
"Absolute Percent": (lambda: (value * 100), unitName),
"Milliseconds": (lambda: value / 1000.0, unitName),
"typeID": (itemIDCallback, ""),
"groupID": (groupIDCallback, ""),
"attributeID": (attributeIDCallback, "")}
trans = {
"Inverse Absolute Percent" : (lambda: (1 - value) * 100, unitName),
"Inversed Modifier Percent": (lambda: (1 - value) * 100, unitName),
"Modifier Percent" : (lambda: ("%+.2f" if ((value - 1) * 100) % 1 else "%+d") % ((value - 1) * 100), unitName),
"Volume" : (lambda: value, u"m\u00B3"),
"Sizeclass" : (lambda: value, ""),
"Absolute Percent" : (lambda: (value * 100), unitName),
"Milliseconds" : (lambda: value / 1000.0, unitName),
"typeID" : (itemIDCallback, ""),
"groupID" : (groupIDCallback, ""),
"attributeID" : (attributeIDCallback, "")
}
override = trans.get(unitDisplayName)
if override is not None:
@@ -849,7 +856,7 @@ class ItemEffects(wx.Panel):
If effect file does not exist, create it
"""
file_ = config.getPyfaPath(os.path.join("eos", "effects", "%s.py" % event.GetText().lower()))
file_ = os.path.join(config.pyfaPath, "eos", "effects", "%s.py" % event.GetText().lower())
if not os.path.isfile(file_):
open(file_, 'a').close()
@@ -1067,7 +1074,7 @@ class ItemAffectedBy(wx.Panel):
item = afflictor.item
items[attrName].append(
(type(afflictor), afflictor, item, modifier, amount, getattr(afflictor, "projected", False)))
(type(afflictor), afflictor, item, modifier, amount, getattr(afflictor, "projected", False)))
# Make sure projected fits are on top
rootOrder = container.keys()
@@ -1295,3 +1302,97 @@ class ItemAffectedBy(wx.Panel):
treeitem = self.affectedBy.AppendItem(child, display, attrIcon)
self.affectedBy.SetPyData(treeitem, saved)
self.treeItems.append(treeitem)
class ItemProperties(wx.Panel):
def __init__(self, parent, stuff, item, context=None):
wx.Panel.__init__(self, parent)
mainSizer = wx.BoxSizer(wx.VERTICAL)
self.paramList = AutoListCtrl(self, wx.ID_ANY,
style=wx.LC_REPORT | wx.LC_SINGLE_SEL | wx.LC_VRULES | wx.NO_BORDER)
mainSizer.Add(self.paramList, 1, wx.ALL | wx.EXPAND, 0)
self.SetSizer(mainSizer)
self.toggleView = 1
self.stuff = stuff
self.item = item
self.attrInfo = {}
self.attrValues = {}
self._fetchValues()
self.m_staticline = wx.StaticLine(self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LI_HORIZONTAL)
mainSizer.Add(self.m_staticline, 0, wx.EXPAND)
bSizer = wx.BoxSizer(wx.HORIZONTAL)
self.totalAttrsLabel = wx.StaticText(self, wx.ID_ANY, u" ", wx.DefaultPosition, wx.DefaultSize, 0)
bSizer.Add(self.totalAttrsLabel, 0, wx.ALIGN_CENTER_VERTICAL | wx.RIGHT)
mainSizer.Add(bSizer, 0, wx.ALIGN_RIGHT)
self.PopulateList()
def _fetchValues(self):
if self.stuff is None:
self.attrInfo.clear()
self.attrValues.clear()
self.attrInfo.update(self.item.attributes)
self.attrValues.update(self.item.attributes)
elif self.stuff.item == self.item:
self.attrInfo.clear()
self.attrValues.clear()
self.attrInfo.update(self.stuff.item.attributes)
self.attrValues.update(self.stuff.itemModifiedAttributes)
elif self.stuff.charge == self.item:
self.attrInfo.clear()
self.attrValues.clear()
self.attrInfo.update(self.stuff.charge.attributes)
self.attrValues.update(self.stuff.chargeModifiedAttributes)
# When item for stats window no longer exists, don't change anything
else:
return
def PopulateList(self):
self.paramList.InsertColumn(0, "Attribute")
self.paramList.InsertColumn(1, "Current Value")
self.paramList.SetColumnWidth(0, 110)
self.paramList.SetColumnWidth(1, 1500)
self.paramList.setResizeColumn(0)
if self.stuff:
names = dir(self.stuff)
else:
names = dir(self.item)
names = [a for a in names if not (a.startswith('__') and a.endswith('__'))]
idNameMap = {}
idCount = 0
for name in names:
try:
if self.stuff:
attrName = name.title()
value = getattr(self.stuff, name)
else:
attrName = name.title()
value = getattr(self.item, name)
except Exception as e:
# TODO: Add logging to this.
# We couldn't get a property for some reason. Skip it for now.
print(e)
continue
index = self.paramList.InsertStringItem(sys.maxint, attrName)
# index = self.paramList.InsertImageStringItem(sys.maxint, attrName)
idNameMap[idCount] = attrName
self.paramList.SetItemData(index, idCount)
idCount += 1
valueUnit = str(value)
self.paramList.SetStringItem(index, 1, valueUnit)
self.paramList.SortItems(lambda id1, id2: cmp(idNameMap[id1], idNameMap[id2]))
self.paramList.RefreshRows()
self.totalAttrsLabel.SetLabel("%d attributes. " % idCount)
self.Layout()

View File

@@ -19,7 +19,7 @@
import sys
import os.path
import logging
from logbook import Logger
import sqlalchemy
# noinspection PyPackageRequirements
@@ -94,7 +94,7 @@ except ImportError as e:
print("Error loading Attribute Editor: %s.\nAccess to Attribute Editor is disabled." % e.message)
disableOverrideEditor = True
logger = logging.getLogger(__name__)
pyfalog = Logger(__name__)
# dummy panel(no paint no erasebk)
@@ -145,6 +145,7 @@ class MainFrame(wx.Frame):
return cls.__instance if cls.__instance is not None else MainFrame()
def __init__(self, title="pyfa"):
pyfalog.debug("Initialize MainFrame")
self.title = title
wx.Frame.__init__(self, None, wx.ID_ANY, self.title)
@@ -399,7 +400,7 @@ class MainFrame(wx.Frame):
try:
dlg.Destroy()
except PyDeadObjectError:
logger.error("Tried to destroy an object that doesn't exist in <showDamagePatternEditor>.")
pyfalog.error("Tried to destroy an object that doesn't exist in <showDamagePatternEditor>.")
def showImplantSetEditor(self, event):
ImplantSetEditorDlg(self)
@@ -427,7 +428,7 @@ class MainFrame(wx.Frame):
try:
dlg.Destroy()
except PyDeadObjectError:
logger.error("Tried to destroy an object that doesn't exist in <showExportDialog>.")
pyfalog.error("Tried to destroy an object that doesn't exist in <showExportDialog>.")
return
with open(path, "w", encoding="utf-8") as openfile:
@@ -437,7 +438,7 @@ class MainFrame(wx.Frame):
try:
dlg.Destroy()
except PyDeadObjectError:
logger.error("Tried to destroy an object that doesn't exist in <showExportDialog>.")
pyfalog.error("Tried to destroy an object that doesn't exist in <showExportDialog>.")
def showPreferenceDialog(self, event):
dlg = PreferenceDialog(self)
@@ -734,7 +735,7 @@ class MainFrame(wx.Frame):
try:
fits = Port().importFitFromBuffer(clipboard, self.getActiveFit())
except:
logger.error("Attempt to import failed:\n%s", clipboard)
pyfalog.error("Attempt to import failed:\n{0}", clipboard)
else:
self._openAfterImport(fits)
@@ -754,7 +755,7 @@ class MainFrame(wx.Frame):
try:
dlg.Destroy()
except PyDeadObjectError:
logger.error("Tried to destroy an object that doesn't exist in <exportToClipboard>.")
pyfalog.error("Tried to destroy an object that doesn't exist in <exportToClipboard>.")
def exportSkillsNeeded(self, event):
""" Exports skills needed for active fit and active character """
@@ -811,7 +812,7 @@ class MainFrame(wx.Frame):
try:
dlg.Destroy()
except PyDeadObjectError:
logger.error("Tried to destroy an object that doesn't exist in <fileImportDialog>.")
pyfalog.error("Tried to destroy an object that doesn't exist in <fileImportDialog>.")
def backupToXml(self, event):
""" Back up all fits to EVE XML file """

View File

@@ -26,6 +26,9 @@ import gui.graphFrame
import gui.globalEvents as GE
from gui.bitmapLoader import BitmapLoader
from logbook import Logger
pyfalog = Logger(__name__)
if 'wxMac' not in wx.PlatformInfo or ('wxMac' in wx.PlatformInfo and wx.VERSION >= (3, 0)):
from service.crest import Crest
from service.crest import CrestModes
@@ -33,6 +36,7 @@ if 'wxMac' not in wx.PlatformInfo or ('wxMac' in wx.PlatformInfo and wx.VERSION
class MainMenuBar(wx.MenuBar):
def __init__(self, mainFrame):
pyfalog.debug("Initialize MainMenuBar")
self.characterEditorId = wx.NewId()
self.damagePatternEditorId = wx.NewId()
self.targetResistsEditorId = wx.NewId()
@@ -166,6 +170,7 @@ class MainMenuBar(wx.MenuBar):
self.mainFrame.Bind(GE.FIT_CHANGED, self.fitChanged)
def fitChanged(self, event):
pyfalog.debug("fitChanged triggered")
enable = event.fitID is not None
self.Enable(wx.ID_SAVEAS, enable)
self.Enable(wx.ID_COPY, enable)

View File

@@ -26,6 +26,9 @@ import gui.PFSearchBox as SBox
from gui.cachingImageList import CachingImageList
from gui.contextMenu import ContextMenu
from gui.bitmapLoader import BitmapLoader
from logbook import Logger
pyfalog = Logger(__name__)
ItemSelected, ITEM_SELECTED = wx.lib.newevent.NewEvent()
@@ -56,6 +59,7 @@ class MetaButton(wx.ToggleButton):
class MarketBrowser(wx.Panel):
def __init__(self, parent):
wx.Panel.__init__(self, parent)
pyfalog.debug("Initialize marketBrowser")
vbox = wx.BoxSizer(wx.VERTICAL)
self.SetSizer(vbox)
@@ -134,6 +138,7 @@ class SearchBox(SBox.PFSearchBox):
class MarketTree(wx.TreeCtrl):
def __init__(self, parent, marketBrowser):
wx.TreeCtrl.__init__(self, parent, style=wx.TR_DEFAULT_STYLE | wx.TR_HIDE_ROOT)
pyfalog.debug("Initialize marketTree")
self.root = self.AddRoot("root")
self.imageList = CachingImageList(16, 16)
@@ -182,7 +187,9 @@ class MarketTree(wx.TreeCtrl):
iconId = self.addImage(sMkt.getIconByMarketGroup(childMktGrp))
try:
childId = self.AppendItem(root, childMktGrp.name, iconId, data=wx.TreeItemData(childMktGrp.ID))
except:
except Exception as e:
pyfalog.debug("Error appending item.")
pyfalog.debug(e)
continue
if sMkt.marketGroupHasTypesCheck(childMktGrp) is False:
self.AppendItem(childId, "dummy")
@@ -226,6 +233,7 @@ class ItemView(Display):
def __init__(self, parent, marketBrowser):
Display.__init__(self, parent)
pyfalog.debug("Initialize ItemView")
marketBrowser.Bind(wx.EVT_TREE_SEL_CHANGED, self.selectionMade)
self.unfilteredStore = set()
@@ -252,6 +260,7 @@ class ItemView(Display):
self.metaMap = self.makeReverseMetaMap()
# Fill up recently used modules set
pyfalog.debug("Fill up recently used modules set")
for itemID in self.sMkt.serviceMarketRecentlyUsedModules["pyfaMarketRecentlyUsedModules"]:
self.recentlyUsedModules.add(self.sMkt.getItem(itemID))

View File

@@ -25,6 +25,9 @@ from wx.lib.intctrl import IntCtrl
from gui.utils.clipboard import toClipboard, fromClipboard
from gui.builtinViews.entityEditor import EntityEditor, BaseValidator
from service.damagePattern import DamagePattern, ImportError
from logbook import Logger
pyfalog = Logger(__name__)
class DmgPatternTextValidor(BaseValidator):
@@ -47,6 +50,7 @@ class DmgPatternTextValidor(BaseValidator):
return True
except ValueError as e:
pyfalog.error(e)
wx.MessageBox(u"{}".format(e), "Error")
textCtrl.SetFocus()
return False
@@ -256,9 +260,13 @@ class DmgPatternEditorDlg(wx.Dialog):
sDP.importPatterns(text)
self.stNotice.SetLabel("Patterns successfully imported from clipboard")
except ImportError as e:
pyfalog.error(e)
self.stNotice.SetLabel(str(e))
except Exception:
self.stNotice.SetLabel("Could not import from clipboard: unknown errors")
except Exception as e:
msg = "Could not import from clipboard: unknown errors"
pyfalog.warning(msg)
pyfalog.error(e)
self.stNotice.SetLabel(msg)
finally:
self.entityEditor.refreshEntityList()
else:

View File

@@ -1,5 +1,5 @@
import csv
import logging
from logbook import Logger
# noinspection PyPackageRequirements
import wx
@@ -21,7 +21,7 @@ import gui.PFSearchBox as SBox
from gui.marketBrowser import SearchBox
from gui.bitmapLoader import BitmapLoader
logger = logging.getLogger(__name__)
pyfalog = Logger(__name__)
class AttributeEditor(wx.Frame):
@@ -270,7 +270,7 @@ class AttributeGrid(wxpg.PropertyGrid):
self.itemView.updateItems()
logger.debug('%s changed to "%s"' % (p.GetName(), p.GetValueAsString()))
pyfalog.debug('{0} changed to "{1}"', p.GetName(), p.GetValueAsString())
def OnPropGridSelect(self, event):
pass

View File

@@ -23,6 +23,9 @@ from service.targetResists import TargetResists
from gui.bitmapLoader import BitmapLoader
from gui.utils.clipboard import toClipboard, fromClipboard
from gui.builtinViews.entityEditor import EntityEditor, BaseValidator
from logbook import Logger
pyfalog = Logger(__name__)
class TargetResistsTextValidor(BaseValidator):
@@ -45,6 +48,7 @@ class TargetResistsTextValidor(BaseValidator):
return True
except ValueError as e:
pyfalog.error(e)
wx.MessageBox(u"{}".format(e), "Error")
textCtrl.SetFocus()
return False
@@ -230,10 +234,14 @@ class ResistsEditorDlg(wx.Dialog):
except ValueError:
editObj.SetForegroundColour(wx.RED)
self.stNotice.SetLabel("Incorrect Formatting (decimals only)")
msg = "Incorrect Formatting (decimals only)"
pyfalog.warning(msg)
self.stNotice.SetLabel(msg)
except AssertionError:
editObj.SetForegroundColour(wx.RED)
self.stNotice.SetLabel("Incorrect Range (must be 0-100)")
msg = "Incorrect Range (must be 0-100)"
pyfalog.warning(msg)
self.stNotice.SetLabel(msg)
finally: # Refresh for color changes to take effect immediately
self.Refresh()
@@ -271,9 +279,13 @@ class ResistsEditorDlg(wx.Dialog):
sTR.importPatterns(text)
self.stNotice.SetLabel("Patterns successfully imported from clipboard")
except ImportError as e:
pyfalog.error(e)
self.stNotice.SetLabel(str(e))
except Exception:
self.stNotice.SetLabel("Could not import from clipboard: unknown errors")
except Exception as e:
msg = "Could not import from clipboard:"
pyfalog.warning(msg)
pyfalog.error(e)
self.stNotice.SetLabel(msg)
finally:
self.entityEditor.refreshEntityList()
else:

View File

@@ -17,7 +17,7 @@
# along with pyfa. If not, see <http://www.gnu.org/licenses/>.
# =============================================================================
import logging
from logbook import Logger
# noinspection PyPackageRequirements
import wx
@@ -26,7 +26,7 @@ from gui.builtinViews.implantEditor import BaseImplantEditorView
from gui.utils.clipboard import toClipboard, fromClipboard
from gui.builtinViews.entityEditor import EntityEditor, BaseValidator
logger = logging.getLogger(__name__)
pyfalog = Logger(__name__)
class ImplantTextValidor(BaseValidator):
@@ -49,6 +49,7 @@ class ImplantTextValidor(BaseValidator):
return True
except ValueError as e:
pyfalog.error(e)
wx.MessageBox(u"{}".format(e), "Error")
textCtrl.SetFocus()
return False
@@ -198,9 +199,10 @@ class ImplantSetEditorDlg(wx.Dialog):
self.stNotice.SetLabel("Patterns successfully imported from clipboard")
self.showInput(False)
except ImportError as e:
pyfalog.error(e)
self.stNotice.SetLabel(str(e))
except Exception as e:
logging.exception("Unhandled Exception")
pyfalog.error(e)
self.stNotice.SetLabel("Could not import from clipboard: unknown errors")
finally:
self.updateChoices()

View File

@@ -21,6 +21,8 @@ import gui.utils.animEffects as animEffects
from gui.PFListPane import PFListPane
from gui.contextMenu import ContextMenu
from gui.bitmapLoader import BitmapLoader
from logbook import Logger
pyfalog = Logger(__name__)
FitRenamed, EVT_FIT_RENAMED = wx.lib.newevent.NewEvent()
FitSelected, EVT_FIT_SELECTED = wx.lib.newevent.NewEvent()
@@ -684,6 +686,7 @@ class ShipBrowser(wx.Panel):
self.lpane.Freeze()
self.lpane.RemoveAllChildren()
pyfalog.debug("Populate ship category list.")
if len(self.categoryList) == 0:
# set cache of category list
self.categoryList = list(sMkt.getShipRoot())
@@ -692,7 +695,8 @@ class ShipBrowser(wx.Panel):
# set map & cache of fittings per category
for cat in self.categoryList:
itemIDs = [x.ID for x in cat.items]
self.categoryFitCache[cat.ID] = sFit.countFitsWithShip(itemIDs) > 1
num = sFit.countFitsWithShip(itemIDs)
self.categoryFitCache[cat.ID] = num > 0
for ship in self.categoryList:
if self.filterShipsWithNoFits and not self.categoryFitCache[ship.ID]:
@@ -938,7 +942,7 @@ class ShipBrowser(wx.Panel):
if fits:
for fit in fits:
shipTrait = fit.ship.traits.traitText if (fit.ship.traits is not None) else ""
shipTrait = fit.ship.item.traits.traitText if (fit.ship.item.traits is not None) else ""
# empty string if no traits
self.lpane.AddWidget(FitItem(

View File

@@ -4,7 +4,12 @@ import time
import wx
from service.settings import HTMLExportSettings
from service.fit import Fit
from service.port import Port
from service.market import Market
from logbook import Logger
from eos.db import getFit
pyfalog = Logger(__name__)
class exportHtml(object):
@@ -184,6 +189,7 @@ class exportHtmlThread(threading.Thread):
groupFits = 0
for ship in ships:
fits = sFit.getFitsWithShip(ship.ID)
if len(fits) > 0:
groupFits += len(fits)
@@ -192,10 +198,11 @@ class exportHtmlThread(threading.Thread):
return
fit = fits[0]
try:
dnaFit = sFit.exportDna(fit[0])
dnaFit = Port.exportDna(getFit(fit[0]))
HTMLgroup += ' <li><a data-dna="' + dnaFit + '" target="_blank">' + ship.name + ": " + \
fit[1] + '</a></li>\n'
except:
pyfalog.warning("Failed to export line")
pass
finally:
if self.callback:
@@ -214,10 +221,12 @@ class exportHtmlThread(threading.Thread):
if self.stopRunning:
return
try:
dnaFit = sFit.exportDna(fit[0])
dnaFit = Port.exportDna(getFit(fit[0]))
print dnaFit
HTMLship += ' <li><a data-dna="' + dnaFit + '" target="_blank">' + fit[
1] + '</a></li>\n'
except:
pyfalog.warning("Failed to export line")
continue
finally:
if self.callback:
@@ -266,10 +275,11 @@ class exportHtmlThread(threading.Thread):
if self.stopRunning:
return
try:
dnaFit = sFit.exportDna(fit[0])
dnaFit = Port.exportDna(getFit(fit[0]))
HTML += '<a class="outOfGameBrowserLink" target="_blank" href="' + dnaUrl + dnaFit + '">' + ship.name + ': ' + \
fit[1] + '</a><br> \n'
except:
pyfalog.error("Failed to export line")
continue
finally:
if self.callback:

182
pyfa.py
View File

@@ -19,11 +19,17 @@
# ==============================================================================
import sys
import os
import os.path
import re
import config
from optparse import OptionParser, BadOptionError, AmbiguousOptionError
from logbook import TimedRotatingFileHandler, Logger, StreamHandler, NestedSetup, FingersCrossedHandler, NullHandler, \
CRITICAL, ERROR, WARNING, DEBUG, INFO
pyfalog = Logger(__name__)
class PassThroughOptionParser(OptionParser):
"""
@@ -36,9 +42,30 @@ class PassThroughOptionParser(OptionParser):
try:
OptionParser._process_args(self, largs, rargs, values)
except (BadOptionError, AmbiguousOptionError) as e:
pyfalog.error("Bad startup option passed.")
largs.append(e.opt_str)
class LoggerWriter:
def __init__(self, level):
# self.level is really like using log.debug(message)
# at least in my case
self.level = level
def write(self, message):
# if statement reduces the amount of newlines that are
# printed to the logger
if message not in {'\n', ' '}:
self.level(message.replace("\n", ""))
def flush(self):
# create a flush method so things can be flushed when
# the system wants to. Not sure if simply 'printing'
# sys.stderr is the correct way to do it, but it seemed
# to work properly for me.
self.level(sys.stderr)
# Parse command line options
usage = "usage: %prog [--root]"
parser = PassThroughOptionParser(usage=usage)
@@ -47,9 +74,23 @@ parser.add_option("-w", "--wx28", action="store_true", dest="force28", help="For
parser.add_option("-d", "--debug", action="store_true", dest="debug", help="Set logger to debug level.", default=False)
parser.add_option("-t", "--title", action="store", dest="title", help="Set Window Title", default=None)
parser.add_option("-s", "--savepath", action="store", dest="savepath", help="Set the folder for savedata", default=None)
parser.add_option("-l", "--logginglevel", action="store", dest="logginglevel", help="Set desired logging level [Critical|Error|Warning|Info|Debug]", default="Error")
(options, args) = parser.parse_args()
if options.logginglevel == "Critical":
options.logginglevel = CRITICAL
elif options.logginglevel == "Error":
options.logginglevel = ERROR
elif options.logginglevel == "Warning":
options.logginglevel = WARNING
elif options.logginglevel == "Info":
options.logginglevel = INFO
elif options.logginglevel == "Debug":
options.logginglevel = DEBUG
else:
options.logginglevel = ERROR
if not hasattr(sys, 'frozen'):
if sys.version_info < (2, 6) or sys.version_info > (3, 0):
@@ -114,14 +155,6 @@ if __name__ == "__main__":
options.title = "pyfa %s%s - Python Fitting Assistant" % (config.version, "" if config.tag.lower() != 'git' else " (git)")
config.debug = options.debug
# convert to unicode if it is set
if options.savepath is not None:
options.savepath = unicode(options.savepath)
config.defPaths(options.savepath)
# Basic logging initialization
import logging
logging.basicConfig()
# Import everything
# noinspection PyPackageRequirements
@@ -129,17 +162,128 @@ if __name__ == "__main__":
import os
import os.path
import eos.db
# noinspection PyUnresolvedReferences
import service.prefetch # noqa: F401
from gui.mainFrame import MainFrame
try:
# convert to unicode if it is set
if options.savepath is not None:
options.savepath = unicode(options.savepath)
config.defPaths(options.savepath)
# Make sure the saveddata db exists
if not os.path.exists(config.savePath):
os.mkdir(config.savePath)
# Basic logging initialization
eos.db.saveddata_meta.create_all()
# Logging levels:
'''
logbook.CRITICAL
logbook.ERROR
logbook.WARNING
logbook.INFO
logbook.DEBUG
logbook.NOTSET
'''
pyfa = wx.App(False)
MainFrame(options.title)
pyfa.MainLoop()
if options.debug:
savePath_filename = "Pyfa_debug.log"
else:
savePath_filename = "Pyfa.log"
savePath_Destination = os.path.join(config.savePath, savePath_filename)
try:
if options.debug:
logging_mode = "Debug"
logging_setup = NestedSetup([
# make sure we never bubble up to the stderr handler
# if we run out of setup handling
NullHandler(),
StreamHandler(
sys.stdout,
bubble=False,
level=options.logginglevel
),
TimedRotatingFileHandler(
savePath_Destination,
level=0,
backup_count=3,
bubble=True,
date_format='%Y-%m-%d',
),
])
else:
logging_mode = "User"
logging_setup = NestedSetup([
# make sure we never bubble up to the stderr handler
# if we run out of setup handling
NullHandler(),
FingersCrossedHandler(
TimedRotatingFileHandler(
savePath_Destination,
level=0,
backup_count=3,
bubble=False,
date_format='%Y-%m-%d',
),
action_level=ERROR,
buffer_size=1000,
# pull_information=True,
# reset=False,
)
])
except:
logging_mode = "Console Only"
logging_setup = NestedSetup([
# make sure we never bubble up to the stderr handler
# if we run out of setup handling
NullHandler(),
StreamHandler(
sys.stdout,
bubble=False
)
])
import eos.db
# noinspection PyUnresolvedReferences
import service.prefetch # noqa: F401
# Make sure the saveddata db exists
if not os.path.exists(config.savePath):
os.mkdir(config.savePath)
eos.db.saveddata_meta.create_all()
except Exception, e:
import traceback
from gui.errorDialog import ErrorFrame
tb = traceback.format_exc()
pyfa = wx.App(False)
ErrorFrame(e, tb)
pyfa.MainLoop()
sys.exit()
with logging_setup.threadbound():
# Don't redirect if frozen
if not hasattr(sys, 'frozen'):
# Output all stdout (print) messages as warnings
try:
sys.stdout = LoggerWriter(pyfalog.warning)
except ValueError, Exception:
pyfalog.critical("Cannot access log file. Continuing without writing stdout to log.")
if not options.debug:
# Output all stderr (stacktrace) messages as critical
try:
sys.stderr = LoggerWriter(pyfalog.critical)
except ValueError, Exception:
pyfalog.critical("Cannot access log file. Continuing without writing stderr to log.")
pyfalog.info("Starting Pyfa")
pyfalog.info("Running in logging mode: {0}", logging_mode)
if hasattr(sys, 'frozen') and options.debug:
pyfalog.critical("Running in frozen mode with debug turned on. Forcing all output to be written to log.")
from gui.mainFrame import MainFrame
pyfa = wx.App(False)
MainFrame(options.title)
pyfa.MainLoop()

83
pyfa.spec Normal file
View File

@@ -0,0 +1,83 @@
# -*- mode: python -*-
# Note: This script is provided AS-IS for those that may be interested.
# pyfa does not currently support pyInstaller (or any other build process) 100% at the moment
# Command line to build:
# (Run from directory where pyfa.py and pyfa.spec lives.)
# c:\Python27\scripts\pyinstaller.exe --clean --noconfirm --windowed --upx-dir=.\scripts\upx.exe pyfa.spec
# Don't forget to change the path to where your pyfa.py and pyfa.spec lives
# pathex=['C:\\Users\\Ebag333\\Documents\\GitHub\\Ebag333\\Pyfa'],
import os
block_cipher = None
added_files = [
( 'imgs/gui/*.png', 'imgs/gui' ),
( 'imgs/gui/*.gif', 'imgs/gui' ),
( 'imgs/icons/*.png', 'imgs/icons' ),
( 'imgs/renders/*.png', 'imgs/renders' ),
( 'dist_assets/win/pyfa.ico', '.' ),
( 'dist_assets/cacert.pem', '.' ),
( 'eve.db', '.' ),
( 'README.md', '.' ),
( 'LICENSE', '.' ),
]
import_these = []
# Walk eos.effects and add all effects so we can import them properly
for root, folders, files in os.walk("eos/effects"):
for file_ in files:
if file_.endswith(".py") and not file_.startswith("_"):
mod_name = "{}.{}".format(
root.replace("/", "."),
file_.split(".py")[0],
)
import_these.append(mod_name)
a = Analysis(
['pyfa.py'],
pathex=['C:\\Users\\Ebag333\\Documents\\GitHub\\Ebag333\\Pyfa'],
binaries=[],
datas=added_files,
hiddenimports=import_these,
hookspath=[],
runtime_hooks=[],
excludes=[],
win_no_prefer_redirects=False,
win_private_assemblies=False,
cipher=block_cipher,
)
pyz = PYZ(
a.pure,
a.zipped_data,
cipher=block_cipher,
)
exe = EXE(pyz,
a.scripts,
exclude_binaries=True,
debug=False,
console=False,
strip=False,
upx=True,
name='pyfa',
icon='dist_assets/win/pyfa.ico',
onefile=False,
)
coll = COLLECT(
exe,
a.binaries,
a.zipfiles,
a.datas,
strip=False,
upx=True,
onefile=False,
name='pyfa',
icon='dist_assets/win/pyfa.ico',
)

View File

@@ -1,3 +1,4 @@
logbook
matplotlib
PyYAML
python-dateutil

View File

@@ -0,0 +1,9 @@
PyInstaller >= 3.2.1
cycler >= 0.10.0
functools32 >= 3.2.3
future >= 0.16.0
numpy >= 1.12.
pyparsing >= 2.1.10
pypiwin32 >= 219
pytz >= 2016.10
six >= 1.10.0

View File

@@ -0,0 +1,8 @@
PyInstaller >= 3.2.1
cycler >= 0.10.0
functools32 >= 3.2.3
future >= 0.16.0
numpy >= 1.12.
pyparsing >= 2.1.10
pytz >= 2016.10
six

View File

@@ -0,0 +1,10 @@
PyInstaller >= 3.2.1
cycler >= 0.10.0
functools32 >= 3.2.3
future >= 0.16.0
numpy >= 1.12.
pyparsing >= 2.1.10
pypiwin32 >= 219
pytz >= 2016.10
six >= 1.10.0
tornado

BIN
scripts/upx.exe Normal file

Binary file not shown.

View File

@@ -20,7 +20,7 @@
import copy
import itertools
import json
import logging
from logbook import Logger
import threading
from codecs import open
from xml.etree import ElementTree
@@ -39,7 +39,7 @@ from eos.saveddata.character import Character as es_Character
from eos.saveddata.module import Slot as es_Slot, Module as es_Module
from eos.saveddata.fighter import Fighter as es_Fighter
logger = logging.getLogger(__name__)
pyfalog = Logger(__name__)
class CharacterImportThread(threading.Thread):
@@ -72,7 +72,7 @@ class CharacterImportThread(threading.Thread):
charFile = open(path, mode='r').read()
doc = minidom.parseString(charFile)
if doc.documentElement.tagName not in ("SerializableCCPCharacter", "SerializableUriCharacter"):
logger.error("Incorrect EVEMon XML sheet")
pyfalog.error("Incorrect EVEMon XML sheet")
raise RuntimeError("Incorrect EVEMon XML sheet")
name = doc.getElementsByTagName("name")[0].firstChild.nodeValue
skill_els = doc.getElementsByTagName("skill")
@@ -84,16 +84,17 @@ class CharacterImportThread(threading.Thread):
"level": int(skill.getAttribute("level")),
})
else:
logger.error("Attempted to import unknown skill %s (ID: %s) (Level: %s)",
skill.getAttribute("name"),
skill.getAttribute("typeID"),
skill.getAttribute("level"),
)
pyfalog.error(
"Attempted to import unknown skill {0} (ID: {1}) (Level: {2})",
skill.getAttribute("name"),
skill.getAttribute("typeID"),
skill.getAttribute("level"),
)
char = sCharacter.new(name + " (EVEMon)")
sCharacter.apiUpdateCharSheet(char.ID, skills)
except Exception, e:
logger.error("Exception on character import:")
logger.error(e)
pyfalog.error("Exception on character import:")
pyfalog.error(e)
continue
wx.CallAfter(self.callback)
@@ -304,7 +305,7 @@ class Character(object):
@staticmethod
def rename(char, newName):
if char.name in ("All 0", "All 5"):
logger.info("Cannot rename built in characters.")
pyfalog.info("Cannot rename built in characters.")
else:
char.name = newName
eos.db.commit()
@@ -404,7 +405,7 @@ class Character(object):
def addImplant(charID, itemID):
char = eos.db.getCharacter(charID)
if char.ro:
logger.error("Trying to add implant to read-only character")
pyfalog.error("Trying to add implant to read-only character")
return
implant = es_Implant(eos.db.getItem(itemID))

View File

@@ -1,5 +1,6 @@
# noinspection PyPackageRequirements
import wx
from logbook import Logger
import logging
import threading
import copy
@@ -14,7 +15,7 @@ from service.settings import CRESTSettings
from service.server import StoppableHTTPServer, AuthHandler
from service.pycrest.eve import EVE
logger = logging.getLogger(__name__)
pyfalog = Logger(__name__)
class Servers(Enum):
@@ -152,17 +153,17 @@ class Crest(object):
def logout(self):
"""Logout of implicit character"""
logging.debug("Character logout")
pyfalog.debug("Character logout")
self.implicitCharacter = None
wx.PostEvent(self.mainFrame, GE.SsoLogout(type=self.settings.get('mode')))
def stopServer(self):
logging.debug("Stopping Server")
pyfalog.debug("Stopping Server")
self.httpd.stop()
self.httpd = None
def startServer(self):
logging.debug("Starting server")
pyfalog.debug("Starting server")
if self.httpd:
self.stopServer()
time.sleep(1)
@@ -182,10 +183,10 @@ class Crest(object):
raise Exception("Could not parse out querystring parameters.")
if message['state'][0] != self.state:
logger.warn("OAUTH state mismatch")
pyfalog.warn("OAUTH state mismatch")
raise Exception("OAUTH State Mismatch.")
logger.debug("Handling CREST login with: %s" % message)
pyfalog.debug("Handling CREST login with: {0}", message)
if 'access_token' in message: # implicit
eve = copy.deepcopy(self.eve)
@@ -199,7 +200,7 @@ class Crest(object):
eve()
info = eve.whoami()
logger.debug("Got character info: %s" % info)
pyfalog.debug("Got character info: {0}", info)
self.implicitCharacter = CrestChar(info['CharacterID'], info['CharacterName'])
self.implicitCharacter.eve = eve
@@ -212,7 +213,7 @@ class Crest(object):
eve()
info = eve.whoami()
logger.debug("Got character info: %s" % info)
pyfalog.debug("Got character info: {0}", info)
# check if we have character already. If so, simply replace refresh_token
char = self.getCrestCharacter(int(info['CharacterID']))

View File

@@ -18,7 +18,7 @@
# ===============================================================================
import copy
import logging
from logbook import Logger
import eos.db
from eos.saveddata.booster import Booster as es_Booster
@@ -36,7 +36,7 @@ from service.character import Character
from service.damagePattern import DamagePattern
from service.settings import SettingsProvider
logger = logging.getLogger(__name__)
pyfalog = Logger(__name__)
class Fit(object):
@@ -50,6 +50,7 @@ class Fit(object):
return cls.instance
def __init__(self):
pyfalog.debug("Initialize Fit class")
self.pattern = DamagePattern.getInstance().getDamagePattern("Uniform")
self.targetResists = None
self.character = saveddata_Character.getAll5()
@@ -727,7 +728,13 @@ class Fit(object):
fit.drones.remove(d1)
d2.amount += d1.amount
d2.amountActive += d1.amountActive if d1.amountActive > 0 else -d2.amountActive
d2.amountActive += d1.amountActive
# If we have less than the total number of drones active, make them all active. Fixes #728
# This could be removed if we ever add an enhancement to make drone stacks partially active.
if d2.amount > d2.amountActive:
d2.amountActive = d2.amount
eos.db.commit()
self.recalc(fit)
return True
@@ -1016,7 +1023,7 @@ class Fit(object):
self.recalc(fit)
def recalc(self, fit, withBoosters=True):
logger.debug("=" * 10 + "recalc" + "=" * 10)
pyfalog.info("=" * 10 + "recalc" + "=" * 10)
if fit.factorReload is not self.serviceFittingOptions["useGlobalForceReload"]:
fit.factorReload = self.serviceFittingOptions["useGlobalForceReload"]
fit.clear()

View File

@@ -19,7 +19,7 @@
import re
import threading
import logging
from logbook import Logger
import Queue
# noinspection PyPackageRequirements
@@ -41,7 +41,7 @@ try:
except ImportError:
from utils.compat import OrderedDict
logger = logging.getLogger(__name__)
pyfalog = Logger(__name__)
# Event which tells threads dependent on Market that it's initialized
mktRdy = threading.Event()
@@ -50,6 +50,7 @@ mktRdy = threading.Event()
class ShipBrowserWorkerThread(threading.Thread):
def __init__(self):
threading.Thread.__init__(self)
pyfalog.debug("Initialize ShipBrowserWorkerThread.")
self.name = "ShipBrowser"
def run(self):
@@ -73,24 +74,29 @@ class ShipBrowserWorkerThread(threading.Thread):
cache[id_] = set_
wx.CallAfter(callback, (id_, set_))
except:
pass
except Exception as e:
pyfalog.critical("Callback failed.")
pyfalog.critical(e)
finally:
try:
queue.task_done()
except:
pass
except Exception as e:
pyfalog.critical("Queue task done failed.")
pyfalog.critical(e)
class PriceWorkerThread(threading.Thread):
def __init__(self):
threading.Thread.__init__(self)
self.name = "PriceWorker"
pyfalog.debug("Initialize PriceWorkerThread.")
def run(self):
pyfalog.debug("Run start")
self.queue = Queue.Queue()
self.wait = {}
self.processUpdates()
pyfalog.debug("Run end")
def processUpdates(self):
queue = self.queue
@@ -125,6 +131,7 @@ class SearchWorkerThread(threading.Thread):
def __init__(self):
threading.Thread.__init__(self)
self.name = "SearchWorker"
pyfalog.debug("Initialize SearchWorkerThread.")
def run(self):
self.cv = threading.Condition()
@@ -440,7 +447,7 @@ class Market(object):
else:
raise TypeError("Need Item object, integer, float or string as argument")
except:
logger.error("Could not get item: %s", identity)
pyfalog.error("Could not get item: {0}", identity)
raise
return item
@@ -833,8 +840,9 @@ class Market(object):
def cb():
try:
callback(requests)
except Exception:
pass
except Exception as e:
pyfalog.critical("Callback failed.")
pyfalog.critical(e)
eos.db.commit()
self.priceWorkerThread.trigger(requests, cb)
@@ -849,8 +857,9 @@ class Market(object):
def cb():
try:
callback(item)
except:
pass
except Exception as e:
pyfalog.critical("Callback failed.")
pyfalog.critical(e)
self.priceWorkerThread.setToWait(item.ID, cb)

View File

@@ -20,7 +20,7 @@
import re
import os
import xml.dom
import logging
from logbook import Logger
import collections
import json
import threading
@@ -50,7 +50,7 @@ from service.market import Market
if 'wxMac' not in wx.PlatformInfo or ('wxMac' in wx.PlatformInfo and wx.VERSION >= (3, 0)):
from service.crest import Crest
logger = logging.getLogger("pyfa.service.port")
pyfalog = Logger(__name__)
try:
from collections import OrderedDict
@@ -134,21 +134,21 @@ class Port(object):
savebom = bom
if codec_found is None:
logger.info("Unicode BOM not found in file %s.", path)
pyfalog.info("Unicode BOM not found in file {0}.", path)
attempt_codecs = (defcodepage, "utf-8", "utf-16", "cp1252")
for page in attempt_codecs:
try:
logger.info("Attempting to decode file %s using %s page.", path, page)
pyfalog.info("Attempting to decode file {0} using {1} page.", path, page)
srcString = unicode(srcString, page)
codec_found = page
logger.info("File %s decoded using %s page.", path, page)
pyfalog.info("File {0} decoded using {1} page.", path, page)
except UnicodeDecodeError:
logger.info("Error unicode decoding %s from page %s, trying next codec", path, page)
pyfalog.info("Error unicode decoding {0} from page {1}, trying next codec", path, page)
else:
break
else:
logger.info("Unicode BOM detected in %s, using %s page.", path, codec_found)
pyfalog.info("Unicode BOM detected in {0}, using {1} page.", path, codec_found)
srcString = unicode(srcString[len(savebom):], codec_found)
else:
@@ -166,9 +166,10 @@ class Port(object):
fits += fitsImport
except xml.parsers.expat.ExpatError:
return False, "Malformed XML in %s" % path
except Exception:
logger.exception("Unknown exception processing: %s", path)
return False, "Unknown Error while processing %s" % path
except Exception as e:
pyfalog.critical("Unknown exception processing: {0}", path)
pyfalog.critical(e)
return False, "Unknown Error while processing {0}" % path
IDs = []
numFits = len(fits)
@@ -340,6 +341,7 @@ class Port(object):
except ValueError:
f.ship = Citadel(sMkt.getItem(fit['ship']['id']))
except:
pyfalog.warning("Caught exception in importCrest")
return None
items = fit['items']
@@ -365,6 +367,7 @@ class Port(object):
m = Module(item)
# When item can't be added to any slot (unknown item or just charge), ignore it
except ValueError:
pyfalog.debug("Item can't be added to any slot (unknown item or just charge)")
continue
# Add subsystems before modules to make sure T3 cruisers have subsystems installed
if item.category.name == "Subsystem":
@@ -377,6 +380,7 @@ class Port(object):
moduleList.append(m)
except:
pyfalog.warning("Could not process module.")
continue
# Recalc to get slot numbers correct for T3 cruisers
@@ -405,6 +409,7 @@ class Port(object):
string = string[string.index(str(id_)):]
break
except:
pyfalog.warning("Exception caught in importDna")
pass
string = string[:string.index("::") + 2]
info = string.split(":")
@@ -422,7 +427,7 @@ class Port(object):
return s_[:10] + "..."
return s_
logger.exception("Couldn't import ship data %r", [logtransform(s) for s in info])
pyfalog.exception("Couldn't import ship data {0}", [logtransform(s) for s in info])
return None
moduleList = []
@@ -449,6 +454,7 @@ class Port(object):
try:
m = Module(item)
except:
pyfalog.warning("Exception caught in importDna")
continue
# Add subsystems before modules to make sure T3 cruisers have subsystems installed
if item.category.name == "Subsystem":
@@ -497,6 +503,7 @@ class Port(object):
fit.ship = Citadel(ship)
fit.name = fitName
except:
pyfalog.warning("Exception caught in importEft")
return
# maintain map of drones and their quantities
@@ -537,6 +544,7 @@ class Port(object):
item = sMkt.getItem(modName, eager="group.category")
except:
# if no data can be found (old names)
pyfalog.warning("no data can be found (old names)")
continue
if item.category.name == "Drone":
@@ -563,7 +571,7 @@ class Port(object):
elif "boosterness" in item.attributes:
fit.boosters.append(Booster(item))
else:
logger.error("Failed to import implant: %s", line)
pyfalog.error("Failed to import implant: {0}", line)
# elif item.category.name == "Subsystem":
# try:
# subsystem = Module(item)
@@ -689,6 +697,7 @@ class Port(object):
try:
droneItem = sMkt.getItem(droneName, eager="group.category")
except:
pyfalog.warning("Cannot get item.")
continue
if droneItem.category.name == "Drone":
# Add drone to the fitting
@@ -710,6 +719,7 @@ class Port(object):
try:
implantItem = sMkt.getItem(entityData, eager="group.category")
except:
pyfalog.warning("Cannot get item.")
continue
if implantItem.category.name != "Implant":
continue
@@ -725,6 +735,7 @@ class Port(object):
try:
boosterItem = sMkt.getItem(entityData, eager="group.category")
except:
pyfalog.warning("Cannot get item.")
continue
# All boosters have implant category
if boosterItem.category.name != "Implant":
@@ -745,6 +756,7 @@ class Port(object):
try:
item = sMkt.getItem(cargoName)
except:
pyfalog.warning("Cannot get item.")
continue
# Add Cargo to the fitting
c = Cargo(item)
@@ -758,6 +770,7 @@ class Port(object):
try:
modItem = sMkt.getItem(modName)
except:
pyfalog.warning("Cannot get item.")
continue
# Create module
@@ -779,6 +792,7 @@ class Port(object):
if chargeItem.category.name == "Charge":
m.charge = chargeItem
except:
pyfalog.warning("Cannot get item.")
pass
# Append module to fit
moduleList.append(m)
@@ -797,6 +811,7 @@ class Port(object):
wx.CallAfter(callback, None)
# Skip fit silently if we get an exception
except Exception:
pyfalog.error("Caught exception on fit.")
pass
return fits
@@ -821,6 +836,7 @@ class Port(object):
except ValueError:
f.ship = Citadel(sMkt.getItem(shipType))
except:
pyfalog.warning("Caught exception on importXml")
continue
hardwares = fitting.getElementsByTagName("hardware")
moduleList = []
@@ -830,6 +846,7 @@ class Port(object):
try:
item = sMkt.getItem(moduleName, eager="group.category")
except:
pyfalog.warning("Caught exception on importXml")
continue
if item:
if item.category.name == "Drone":
@@ -852,6 +869,7 @@ class Port(object):
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":
@@ -865,6 +883,7 @@ class Port(object):
moduleList.append(m)
except KeyboardInterrupt:
pyfalog.warning("Keyboard Interrupt")
continue
# Recalc to get slot numbers correct for T3 cruisers
@@ -1171,7 +1190,7 @@ class FitImportThread(threading.Thread):
success, result = sPort.importFitFromFiles(self.paths, self.callback)
if not success: # there was an error during processing
logger.error("Error while processing file import: %s", result)
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)

View File

@@ -25,9 +25,9 @@ from eos.db import migration
from eos.db.saveddata.loadDefaultDatabaseValues import DefaultDatabaseValues
from eos.db.saveddata.databaseRepair import DatabaseCleanup
import logging
from logbook import Logger
logger = logging.getLogger(__name__)
pyfalog = Logger(__name__)
# Make sure the saveddata db exists
if config.savePath and not os.path.exists(config.savePath):
@@ -35,14 +35,16 @@ if config.savePath and not os.path.exists(config.savePath):
if config.saveDB and os.path.isfile(config.saveDB):
# If database exists, run migration after init'd database
pyfalog.debug("Run database migration.")
db.saveddata_meta.create_all()
migration.update(db.saveddata_engine)
# Import default database values
# Import values that must exist otherwise Pyfa breaks
pyfalog.debug("Import Required Database Values.")
DefaultDatabaseValues.importRequiredDefaults()
# Finds and fixes database corruption issues.
logging.debug("Starting database validation.")
pyfalog.debug("Starting database validation.")
database_cleanup_instance = DatabaseCleanup()
database_cleanup_instance.OrphanedCharacterSkills(db.saveddata_engine)
database_cleanup_instance.OrphanedFitCharacterIDs(db.saveddata_engine)
@@ -52,7 +54,7 @@ if config.saveDB and os.path.isfile(config.saveDB):
database_cleanup_instance.OrphanedFitIDItemID(db.saveddata_engine)
database_cleanup_instance.NullDamageTargetPatternValues(db.saveddata_engine)
database_cleanup_instance.DuplicateSelectedAmmoName(db.saveddata_engine)
logging.debug("Completed database validation.")
pyfalog.debug("Completed database validation.")
else:
# If database does not exist, do not worry about migration. Simply

View File

@@ -24,6 +24,10 @@ from xml.dom import minidom
from eos import db
from service.network import Network, TimeoutError
from service.fit import Fit
from logbook import Logger
pyfalog = Logger(__name__)
VALIDITY = 24 * 60 * 60 # Price validity period, 24 hours
REREQUEST = 4 * 60 * 60 # Re-request delay for failed fetches, 4 hours
@@ -114,6 +118,7 @@ class Price(object):
# If getting or processing data returned any errors
except TimeoutError:
# Timeout error deserves special treatment
pyfalog.warning("Price fetch timout")
for typeID in priceMap.keys():
priceobj = priceMap[typeID]
priceobj.time = time.time() + TIMEOUT
@@ -121,6 +126,7 @@ class Price(object):
del priceMap[typeID]
except:
# all other errors will pass and continue onward to the REREQUEST delay
pyfalog.warning("Caught exception in fetchPrices")
pass
# if we get to this point, then we've got an error. Set to REREQUEST delay

View File

@@ -1,12 +1 @@
import logging
class NullHandler(logging.Handler):
def emit(self, record):
pass
logger = logging.getLogger('pycrest')
logger.addHandler(NullHandler())
version = "0.0.1"

View File

@@ -1,5 +1,5 @@
import base64
import logging
from logbook import Logger
import os
import re
import time
@@ -20,7 +20,7 @@ except ImportError: # pragma: no cover
# noinspection PyPep8Naming
import cPickle as pickle
logger = logging.getLogger("pycrest.eve")
pyfalog = Logger(__name__)
cache_re = re.compile(r'max-age=([0-9]+)')
@@ -43,7 +43,7 @@ class FileCache(APICache):
os.mkdir(self.path, 0o700)
def _getpath(self, key):
return config.parsePath(self.path, str(hash(key)) + '.cache')
return os.path.join(self.path, str(hash(key)) + '.cache')
def put(self, key, value):
with open(self._getpath(key), 'wb') as f:
@@ -58,6 +58,7 @@ class FileCache(APICache):
with open(self._getpath(key), 'rb') as f:
return pickle.loads(zlib.decompress(f.read()))
except IOError as ex:
pyfalog.debug("IO error opening zip file. (May not exist yet)")
if ex.errno == 2: # file does not exist (yet)
return None
else:
@@ -69,6 +70,8 @@ class FileCache(APICache):
try:
os.unlink(self._getpath(key))
except OSError as ex:
pyfalog.debug("Caught exception in invalidate")
pyfalog.debug(ex)
if ex.errno == 2: # does not exist
pass
else:
@@ -116,7 +119,7 @@ class APIConnection(object):
self.cache = DictCache()
def get(self, resource, params=None):
logger.debug('Getting resource %s', resource)
pyfalog.debug('Getting resource {0}', resource)
if params is None:
params = {}
@@ -136,18 +139,18 @@ class APIConnection(object):
key = (resource, frozenset(self._session.headers.items()), frozenset(prms.items()))
cached = self.cache.get(key)
if cached and cached['cached_until'] > time.time():
logger.debug('Cache hit for resource %s (params=%s)', resource, prms)
pyfalog.debug('Cache hit for resource {0} (params={1})', resource, prms)
return cached
elif cached:
logger.debug('Cache stale for resource %s (params=%s)', resource, prms)
pyfalog.debug('Cache stale for resource {0} (params={1})', resource, prms)
self.cache.invalidate(key)
else:
logger.debug('Cache miss for resource %s (params=%s', resource, prms)
pyfalog.debug('Cache miss for resource {0} (params={1})', resource, prms)
logger.debug('Getting resource %s (params=%s)', resource, prms)
pyfalog.debug('Getting resource {0} (params={1})', resource, prms)
res = self._session.get(resource, params=prms)
if res.status_code != 200:
raise APIException("Got unexpected status code from server: %i" % res.status_code)
raise APIException("Got unexpected status code from server: {0}" % res.status_code)
ret = res.json()

View File

@@ -1,12 +1,11 @@
import BaseHTTPServer
import urlparse
import socket
import logging
import threading
from logbook import Logger
from service.settings import CRESTSettings
logger = logging.getLogger(__name__)
pyfalog = Logger(__name__)
# noinspection PyPep8
HTML = '''
@@ -34,6 +33,7 @@ HTML = '''
<h1>pyfa</h1>
{0}
</div>
<script type="text/javascript">
function extractFromHash(name, hash) {{
var match = hash.match(new RegExp(name + "=([^&]+)"));
@@ -42,13 +42,24 @@ function extractFromHash(name, hash) {{
var hash = window.location.hash;
var token = extractFromHash("access_token", hash);
var step2 = extractFromHash("step2", hash);
if (token){{
var redirect = window.location.origin.concat('/?', window.location.hash.substr(1));
window.location = redirect;
function doRedirect() {{
if (token){{
// implicit authentication
var redirect = window.location.origin.concat('/?', window.location.hash.substr(1), '&step=2');
window.location = redirect;
}}
else {{
// user-defined
var redirect = window.location.href + '&step=2';
window.location = redirect;
}}
}}
else {{
console.log("do nothing");
// do redirect if we are not already on step 2
if (window.location.href.indexOf('step=2') == -1) {{
setTimeout(doRedirect(), 1000);
}}
</script>
</body>
@@ -61,14 +72,20 @@ class AuthHandler(BaseHTTPServer.BaseHTTPRequestHandler):
def do_GET(self):
if self.path == "/favicon.ico":
return
parsed_path = urlparse.urlparse(self.path)
parts = urlparse.parse_qs(parsed_path.query)
msg = ""
step2 = 'step' in parts
try:
self.server.callback(parts)
msg = "If you see this message then it means you should be logged into CREST. You may close this window and return to the application."
if step2:
self.server.callback(parts)
msg = "If you see this message then it means you should be logged into CREST. You may close this window and return to the application."
else:
# For implicit mode, we have to serve up the page which will take the hash and redirect useing a querystring
msg = "Processing response from EVE Online"
except Exception, ex:
msg = "<h2>Error</h2>\n<p>{}</p>".format(ex.message)
finally:
@@ -76,7 +93,9 @@ class AuthHandler(BaseHTTPServer.BaseHTTPRequestHandler):
self.end_headers()
self.wfile.write(HTML.format(msg))
self.server.stop()
if step2:
# Only stop once if we've received something in the querystring
self.server.stop()
def log_message(self, format, *args):
return
@@ -90,7 +109,7 @@ class StoppableHTTPServer(BaseHTTPServer.HTTPServer):
# Allow listening for x seconds
sec = self.settings.get('timeout')
logger.debug("Running server for %d seconds", sec)
pyfalog.debug("Running server for {0} seconds", sec)
self.socket.settimeout(1)
self.max_tries = sec / self.socket.gettimeout()
@@ -104,16 +123,17 @@ class StoppableHTTPServer(BaseHTTPServer.HTTPServer):
sock.settimeout(None)
return sock, addr
except socket.timeout:
pyfalog.warning("Server timed out waiting for connection")
pass
def stop(self):
self.run = False
def handle_timeout(self):
# logger.debug("Number of tries: %d" % self.tries)
pyfalog.debug("Number of tries: {0}", self.tries)
self.tries += 1
if self.tries == self.max_tries:
logger.debug("Server timed out waiting for connection")
pyfalog.debug("Server timed out waiting for connection")
self.stop()
def serve(self, callback=None):
@@ -122,6 +142,7 @@ class StoppableHTTPServer(BaseHTTPServer.HTTPServer):
try:
self.handle_request()
except TypeError:
pyfalog.debug("Caught exception in serve")
pass
self.server_close()

View File

@@ -25,7 +25,7 @@ import config
class SettingsProvider(object):
BASE_PATH = config.getSavePath("settings")
BASE_PATH = os.path.join(config.savePath, 'settings')
settings = {}
_instance = None
@@ -44,7 +44,7 @@ class SettingsProvider(object):
s = self.settings.get(area)
if s is None:
p = config.parsePath(self.BASE_PATH, area)
p = os.path.join(self.BASE_PATH, area)
if not os.path.exists(p):
info = {}

View File

@@ -29,6 +29,9 @@ import dateutil.parser
import config
from service.network import Network
from service.settings import UpdateSettings
from logbook import Logger
pyfalog = Logger(__name__)
class CheckUpdateThread(threading.Thread):
@@ -83,6 +86,7 @@ class CheckUpdateThread(threading.Thread):
wx.CallAfter(self.callback, release) # Singularity -> Singularity
break
except:
pyfalog.warning("Caught exception in run")
pass
@staticmethod

View File

@@ -0,0 +1,12 @@
from eos.mathUtils import floorFloat
def test_floorFloat():
assert type(floorFloat(1)) is not float
assert type(floorFloat(1)) is int
assert type(floorFloat(1.1)) is not float
assert type(floorFloat(1.1)) is int
assert floorFloat(1.1) == 1
assert floorFloat(1.9) == 1
assert floorFloat(1.5) == 1
assert floorFloat(-1.5) == -2

View File

@@ -0,0 +1,9 @@
from gui.aboutData import versionString, licenses, developers, credits, description
def test_aboutData():
assert versionString.__len__() > 0
assert licenses.__len__() > 0
assert developers.__len__() > 0
assert credits.__len__() > 0
assert description.__len__() > 0

Some files were not shown because too many files have changed in this diff Show More