Merge remote-tracking branch 'origin/master' into singularity

This commit is contained in:
blitzmann
2017-04-16 19:02:17 -04:00
275 changed files with 10511 additions and 1966 deletions

186
.appveyor.yml Normal file
View File

@@ -0,0 +1,186 @@
environment:
global:
# SDK v7.0 MSVC Express 2008's SetEnv.cmd script will fail if the
# /E:ON and /V:ON options are not enabled in the batch script intepreter
# See: http://stackoverflow.com/a/13751649/163740
CMD_IN_ENV: "cmd /E:ON /V:ON /C .\\appveyor\\run_with_env.cmd"
matrix:
# Python 2.7.10 is the latest version and is not pre-installed.
- PYTHON: "C:\\Python27.10"
PYTHON_VERSION: "2.7.10"
PYTHON_ARCH: "32"
#- PYTHON: "C:\\Python27.10-x64"
# PYTHON_VERSION: "2.7.10"
# PYTHON_ARCH: "64"
# Pre-installed Python versions, which Appveyor may upgrade to
# a later point release.
# See: http://www.appveyor.com/docs/installed-software#python
#- PYTHON: "C:\\Python27"
# PYTHON_VERSION: "2.7.x" # currently 2.7.9
# PYTHON_ARCH: "32"
#- PYTHON: "C:\\Python27-x64"
# PYTHON_VERSION: "2.7.x" # currently 2.7.9
# PYTHON_ARCH: "64"
#- PYTHON: "C:\\Python33"
# PYTHON_VERSION: "3.3.x" # currently 3.3.5
# PYTHON_ARCH: "32"
#- PYTHON: "C:\\Python33-x64"
# PYTHON_VERSION: "3.3.x" # currently 3.3.5
# PYTHON_ARCH: "64"
#- PYTHON: "C:\\Python34"
# PYTHON_VERSION: "3.4.x" # currently 3.4.3
# PYTHON_ARCH: "32"
#- PYTHON: "C:\\Python34-x64"
# PYTHON_VERSION: "3.4.x" # currently 3.4.3
# PYTHON_ARCH: "64"
# Python versions not pre-installed
# Python 2.6.6 is the latest Python 2.6 with a Windows installer
# See: https://github.com/ogrisel/python-appveyor-demo/issues/10
#- PYTHON: "C:\\Python266"
# PYTHON_VERSION: "2.6.6"
# PYTHON_ARCH: "32"
#- PYTHON: "C:\\Python266-x64"
# PYTHON_VERSION: "2.6.6"
# PYTHON_ARCH: "64"
#- PYTHON: "C:\\Python35"
# PYTHON_VERSION: "3.5.0"
# PYTHON_ARCH: "32"
#- PYTHON: "C:\\Python35-x64"
# PYTHON_VERSION: "3.5.0"
# PYTHON_ARCH: "64"
# Major and minor releases (i.e x.0.0 and x.y.0) prior to 3.3.0 use
# a different naming scheme.
#- PYTHON: "C:\\Python270"
# PYTHON_VERSION: "2.7.0"
# PYTHON_ARCH: "32"
#- PYTHON: "C:\\Python270-x64"
# PYTHON_VERSION: "2.7.0"
# PYTHON_ARCH: "64"
install:
# If there is a newer build queued for the same PR, cancel this one.
# The AppVeyor 'rollout builds' option is supposed to serve the same
# purpose but it is problematic because it tends to cancel builds pushed
# directly to master instead of just PR builds (or the converse).
# credits: JuliaLang developers.
- ps: if ($env:APPVEYOR_PULL_REQUEST_NUMBER -and $env:APPVEYOR_BUILD_NUMBER -ne ((Invoke-RestMethod `
https://ci.appveyor.com/api/projects/$env:APPVEYOR_ACCOUNT_NAME/$env:APPVEYOR_PROJECT_SLUG/history?recordsNumber=50).builds | `
Where-Object pullRequestId -eq $env:APPVEYOR_PULL_REQUEST_NUMBER)[0].buildNumber) { `
throw "There are newer queued builds for this pull request, failing early." }
# Install wxPython
- 'ECHO Downloading wxPython.'
- "appveyor DownloadFile https://goo.gl/yvO8PB -FileName C:\\wxpython.exe"
#- "appveyor DownloadFile https://goo.gl/Uj0jV3 -FileName C:\\wxpython64.exe"
- 'ECHO Install wxPython'
- "C:\\wxpython.exe /SP- /VERYSILENT /NORESTART"
#- "C:\\wxpython64.exe /SP- /VERYSILENT /NORESTART"
- ECHO "Filesystem root:"
- ps: "ls \"C:/\""
- ECHO "Filesystem pyfa root:"
- ps: "ls \"C:\\projects\\pyfa\\\""
- ECHO "Installed SDKs:"
- ps: "ls \"C:/Program Files/Microsoft SDKs/Windows\""
# Install Python (from the official .msi of http://python.org) and pip when
# not already installed.
# - ps: if (-not(Test-Path($env:PYTHON))) { & appveyor\install.ps1 }
# Prepend newly installed Python to the PATH of this build (this cannot be
# done from inside the powershell script as it would require to restart
# the parent CMD process).
- "SET PATH=%PYTHON%;%PYTHON%\\Scripts;%PATH%"
# Check that we have the expected version and architecture for Python
- "python --version"
- "python -c \"import struct; print(struct.calcsize('P') * 8)\""
# Upgrade to the latest version of pip to avoid it displaying warnings
# about it being out of date.
- "pip install --disable-pip-version-check --user --upgrade pip"
# Install the build dependencies of the project. If some dependencies contain
# compiled extensions and are not provided as pre-built wheel packages,
# pip will build them from source using the MSVC compiler matching the
# target Python version and architecture
# C:\\projects\\eve-gnosis\\
- ECHO "Install pip requirements:"
- "pip install -r requirements.txt"
- "pip install -r requirements_test.txt"
- "pip install -r requirements_build_windows.txt"
build_script:
# Build the compiled extension
# - "python setup.py build"
- ECHO "Build pyfa:"
#- copy C:\projects\pyfa\dist_assets\win\pyfa.spec C:\projects\pyfa\pyfa.spec
- "python C:\\projects\\pyfa\\setup.py build"
#- ECHO "Build pyfa (Debug):"
#- copy C:\projects\pyfa\dist_assets\win\pyfa_debug.spec C:\projects\pyfa\pyfa_debug.spec
#- "pyinstaller.exe --clean --noconfirm --windowed --upx-dir=C:\\projects\\pyfa\\scripts\\upx.exe C:\\projects\\pyfa\\pyfa_debug.spec"
build: on
after_build:
- ps: "ls \"./\""
#- ps: "ls \"C:\\projects\\pyfa\\build\\pyfa\\\""
- ps: "ls \"C:\\projects\\pyfa\\build\\\""
- ps: "ls \"C:\\projects\\pyfa\\build\\exe.win32-2.7\\\""
# Zip
# APPVEYOR_PULL_REQUEST_NUMBER -and $env:APPVEYOR_BUILD_NUMBER
#- 7z a build.zip -r C:\projects\pyfa\build\pyfa\*.*
- 7z a pyfa.zip -r C:\projects\pyfa\build\exe.win32-2.7\*.*
#- 7z a pyfa_debug.zip -r C:\projects\pyfa\dist\pyfa_debug\*.*
on_success:
# Do nothing right now
test_script:
#- tox
#- "py.test --cov=./"
# Run the project tests
# - "%CMD_IN_ENV% python C:/projects/eve-gnosis/setup.py nosetests"
after_test:
# If tests are successful, create binary packages for the project.
# - "%CMD_IN_ENV% python setup.py bdist_wheel"
# - "%CMD_IN_ENV% python setup.py bdist_wininst"
# - "%CMD_IN_ENV% python setup.py bdist_msi"
# - ps: "ls dist"
artifacts:
# Archive the generated packages in the ci.appveyor.com build report.
- path: pyfa.zip
name: 'pyfa.zip'
#- path: pyfa_debug.zip
# name: Pyfa_debug
#on_success:
# - TODO: upload the content of dist/*.whl to a public wheelhouse
#

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

@@ -1,6 +1,6 @@
# pyfa
[![Join us on Slack!](https://pyfainvite.azurewebsites.net/badge.svg)](https://pyfainvite.azurewebsites.net/)
[![Join us on Slack!](https://pyfainvite.azurewebsites.net/badge.svg)](https://pyfainvite.azurewebsites.net/) [![Build Status](https://travis-ci.org/pyfa-org/Pyfa.svg?branch=master)](https://travis-ci.org/pyfa-org/Pyfa)
![pyfa](https://cloud.githubusercontent.com/assets/3904767/10271512/af385ef2-6ade-11e5-8f67-52b8b1e4c797.PNG)
@@ -42,6 +42,7 @@ If you wish to help with development or simply need to run pyfa through a Python
* `dateutil`
* `matplotlib` (for some Linux distributions you may need to install separate wxPython bindings such as `python-matplotlib-wx`)
* `requests`
* `logbook` >= 1.0.0
## Bug Reporting
The preferred method of reporting bugs is through the project's [GitHub Issues interface](https://github.com/pyfa-org/Pyfa/issues). Alternatively, posting a report in the [pyfa thread](http://forums.eveonline.com/default.aspx?g=posts&t=247609) on the official EVE Online forums is acceptable. Guidelines for bug reporting can be found on [this wiki page](https://github.com/DarkFenX/Pyfa/wiki/Bug-Reporting).
@@ -51,8 +52,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/)

View File

@@ -0,0 +1,13 @@
<code_scheme name="Pyfa">
<option name="LINE_SEPARATOR" value="&#xD;&#xA;" />
<option name="RIGHT_MARGIN" value="165" />
<Python>
<option name="NEW_LINE_AFTER_COLON" value="true" />
<option name="DICT_ALIGNMENT" value="2" />
<option name="DICT_NEW_LINE_AFTER_LEFT_BRACE" value="true" />
<option name="DICT_NEW_LINE_BEFORE_RIGHT_BRACE" value="true" />
<option name="USE_CONTINUATION_INDENT_FOR_ARGUMENTS" value="true" />
<option name="OPTIMIZE_IMPORTS_SORT_NAMES_IN_FROM_IMPORTS" value="true" />
<option name="OPTIMIZE_IMPORTS_JOIN_FROM_IMPORTS_WITH_SAME_SOURCE" value="true" />
</Python>
</code_scheme>

View File

@@ -0,0 +1,54 @@
<profile version="1.0">
<option name="myName" value="Pyfa" />
<inspection_tool class="IgnoreUnusedEntry" enabled="false" level="UNUSED ENTRY" enabled_by_default="false" />
<inspection_tool class="InconsistentLineSeparators" enabled="true" level="ERROR" enabled_by_default="true" />
<inspection_tool class="ProblematicWhitespace" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="PyBehaveInspection" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="PyClassicStyleClassInspection" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="PyCompatibilityInspection" enabled="true" level="WARNING" enabled_by_default="true">
<option name="ourVersions">
<value>
<list size="1">
<item index="0" class="java.lang.String" itemvalue="2.7" />
</list>
</value>
</option>
</inspection_tool>
<inspection_tool class="PyMissingTypeHintsInspection" enabled="true" level="WEAK WARNING" enabled_by_default="true" />
<inspection_tool class="PyPackageRequirementsInspection" enabled="true" level="WARNING" enabled_by_default="true">
<option name="ignoredPackages">
<value>
<list size="1">
<item index="0" class="java.lang.String" itemvalue="wxPython" />
</list>
</value>
</option>
</inspection_tool>
<inspection_tool class="PyPep8Inspection" enabled="true" level="TYPO" enabled_by_default="true">
<option name="ignoredErrors">
<list>
<option value="E203" />
<option value="E127" />
<option value="E128" />
<option value="E126" />
</list>
</option>
</inspection_tool>
<inspection_tool class="PyPep8NamingInspection" enabled="true" level="TYPO" enabled_by_default="true">
<option name="ignoredErrors">
<list>
<option value="N802" />
<option value="N806" />
<option value="N803" />
<option value="N814" />
</list>
</option>
</inspection_tool>
<inspection_tool class="PyShadowingBuiltinsInspection" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="PyShadowingNamesInspection" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="SpellCheckingInspection" enabled="false" level="TYPO" enabled_by_default="false">
<option name="processCode" value="true" />
<option name="processLiterals" value="true" />
<option name="processComments" value="true" />
</inspection_tool>
</profile>

123
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.28.2"
tag = "git"
expansionName = "YC118.10"
expansionVersion = "1.2"
expansionName = "YC119.3"
expansionVersion = "1.0"
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.
@@ -133,40 +106,6 @@ def defPaths(customSavePath):
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
# initialize the settings
from service.settings import EOSSettings
eos.config.settings = EOSSettings.getInstance().EOSSettings # this is kind of confusing, but whatever

5666
dist_assets/cacert.pem Normal file

File diff suppressed because it is too large Load Diff

83
dist_assets/win/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:\\projects\\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

@@ -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:\\projects\\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=True,
console=True,
strip=False,
upx=True,
name='pyfa_debug',
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_debug',
icon='dist_assets/win/pyfa.ico',
)

View File

@@ -1,8 +1,2 @@
version = "0.2.3"
tag = "git"
def test():
import tests.runTests
import unittest
unittest.main(defaultTest="discover", testLoader=tests.runTests.loader)

View File

@@ -10,6 +10,9 @@ gamedata_connectionstring = 'sqlite:///' + unicode(realpath(join(dirname(abspath
saveddata_connectionstring = 'sqlite:///' + unicode(
realpath(join(dirname(abspath(__file__)), "..", "saveddata", "saveddata.db")), sys.getfilesystemencoding())
settings = {
"setting1": True
}
# Autodetect path, only change if the autodetection bugs out.
path = dirname(unicode(__file__, sys.getfilesystemencoding()))

View File

@@ -20,13 +20,13 @@
import threading
from sqlalchemy import MetaData, create_engine
from sqlalchemy.orm import sessionmaker, scoped_session
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import pool
from sqlalchemy.orm import sessionmaker
import migration
from eos import config
from logbook import Logger
pyfalog = Logger(__name__)
class ReadOnlyException(Exception):
@@ -49,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
@@ -62,16 +64,22 @@ if saveddata_connectionstring is not None:
saveddata_meta = MetaData()
saveddata_meta.bind = saveddata_engine
saveddata_session = sessionmaker(bind=saveddata_engine, autoflush=False, expire_on_commit=False)()
else:
saveddata_meta = None
# Lock controlling any changes introduced to session
sd_lock = threading.Lock()
# Import all the definitions for all our database stuff
from eos.db.gamedata import *
from eos.db.saveddata import *
# noinspection PyPep8
from eos.db.gamedata import alphaClones, attribute, category, effect, group, icon, item, marketGroup, metaData, metaGroup, queries, traits, unit
# noinspection PyPep8
from eos.db.saveddata import booster, cargo, character, crest, damagePattern, databaseRepair, drone, fighter, fit, implant, implantSet, loadDefaultDatabaseValues, miscData, module, override, price, queries, skill, targetResists, user
# Import queries
# noinspection PyPep8
from eos.db.gamedata.queries import *
# noinspection PyPep8
from eos.db.saveddata.queries import *
# If using in memory saveddata, you'll want to reflect it so the data structure is good.

View File

@@ -21,7 +21,7 @@ from sqlalchemy import Column, String, Integer, Table, ForeignKey
from sqlalchemy.orm import relation, mapper, synonym
from eos.db import gamedata_meta
from eos.types import AlphaClone, AlphaCloneSkill
from eos.gamedata import AlphaClone, AlphaCloneSkill
alphaclones_table = Table(
"alphaClones",

View File

@@ -22,7 +22,7 @@ from sqlalchemy.ext.associationproxy import association_proxy
from sqlalchemy.orm import relation, mapper, synonym, deferred
from eos.db import gamedata_meta
from eos.types import Attribute, Icon, AttributeInfo, Unit
from eos.gamedata import Attribute, AttributeInfo, Unit, Icon
typeattributes_table = Table("dgmtypeattribs", gamedata_meta,
Column("value", Float),

View File

@@ -21,7 +21,7 @@ from sqlalchemy import Column, String, Integer, ForeignKey, Boolean, Table
from sqlalchemy.orm import relation, mapper, synonym, deferred
from eos.db import gamedata_meta
from eos.types import Category, Icon
from eos.gamedata import Category, Icon
categories_table = Table("invcategories", gamedata_meta,
Column("categoryID", Integer, primary_key=True),

View File

@@ -22,7 +22,7 @@ from sqlalchemy.ext.associationproxy import association_proxy
from sqlalchemy.orm import mapper, synonym, relation, deferred
from eos.db import gamedata_meta
from eos.types import Effect, EffectInfo
from eos.gamedata import Effect, EffectInfo
typeeffects_table = Table("dgmtypeeffects", gamedata_meta,
Column("typeID", Integer, ForeignKey("invtypes.typeID"), primary_key=True, index=True),

View File

@@ -21,7 +21,7 @@ from sqlalchemy import Column, String, Integer, Boolean, ForeignKey, Table
from sqlalchemy.orm import relation, mapper, synonym, deferred
from eos.db import gamedata_meta
from eos.types import Group, Icon, Category
from eos.gamedata import Category, Group, Icon
groups_table = Table("invgroups", gamedata_meta,
Column("groupID", Integer, primary_key=True),

View File

@@ -21,7 +21,7 @@ from sqlalchemy import Column, String, Integer, Table
from sqlalchemy.orm import mapper, synonym, deferred
from eos.db import gamedata_meta
from eos.types import Icon
from eos.gamedata import Icon
icons_table = Table("icons", gamedata_meta,
Column("iconID", Integer, primary_key=True),

View File

@@ -23,7 +23,7 @@ from sqlalchemy.orm import relation, mapper, synonym, deferred
from sqlalchemy.orm.collections import attribute_mapped_collection
from eos.db import gamedata_meta
from eos.types import Icon, Attribute, Item, Effect, MetaType, Group, Traits
from eos.gamedata import Attribute, Effect, Group, Icon, Item, MetaType, Traits
items_table = Table("invtypes", gamedata_meta,
Column("typeID", Integer, primary_key=True),
@@ -39,8 +39,8 @@ items_table = Table("invtypes", gamedata_meta,
Column("iconID", Integer, ForeignKey("icons.iconID")),
Column("groupID", Integer, ForeignKey("invgroups.groupID"), index=True))
from .metaGroup import metatypes_table # noqa
from .traits import traits_table # noqa
from .metaGroup import metatypes_table # noqa
from .traits import traits_table # noqa
mapper(Item, items_table,
properties={"group": relation(Group, backref="items"),

View File

@@ -21,7 +21,7 @@ from sqlalchemy import Column, String, Integer, Boolean, ForeignKey, Table
from sqlalchemy.orm import relation, mapper, synonym, deferred
from eos.db import gamedata_meta
from eos.types import Item, MarketGroup, Icon
from eos.gamedata import Icon, Item, MarketGroup
marketgroups_table = Table("invmarketgroups", gamedata_meta,
Column("marketGroupID", Integer, primary_key=True),

View File

@@ -21,7 +21,7 @@ from sqlalchemy import Column, Table, String
from sqlalchemy.orm import mapper
from eos.db import gamedata_meta
from eos.types import MetaData
from eos.gamedata import MetaData
metadata_table = Table("metadata", gamedata_meta,
Column("field_name", String, primary_key=True),

View File

@@ -23,7 +23,7 @@ from sqlalchemy.orm import relation, mapper, synonym
from eos.db import gamedata_meta
from eos.db.gamedata.item import items_table
from eos.types import MetaGroup, Item, MetaType
from eos.gamedata import Item, MetaGroup, MetaType
metagroups_table = Table("invmetagroups", gamedata_meta,
Column("metaGroupID", Integer, primary_key=True),

View File

@@ -21,13 +21,11 @@ from sqlalchemy.orm import join, exc
from sqlalchemy.sql import and_, or_, select
import eos.config
# TODO: Unsure which item the code below needs :(
# from eos.gamedata import Item
from eos.gamedata import Attribute
from eos.db import gamedata_session
from eos.db.gamedata.metaGroup import metatypes_table, items_table
from eos.db.gamedata.group import groups_table
from eos.db.util import processEager, processWhere
from eos.types import Item, Category, Group, MarketGroup, AttributeInfo, MetaData, MetaGroup, AlphaClone
from eos.gamedata import AlphaClone, Attribute, Category, Group, Item, MarketGroup, MetaGroup, AttributeInfo, MetaData
configVal = getattr(eos.config, "gamedataCache", None)
if configVal is True:
@@ -252,7 +250,7 @@ def searchItems(nameLike, where=None, join=None, eager=None):
@cachedQuery(2, "where", "itemids")
def getVariations(itemids, where=None, eager=None):
def getVariations(itemids, groupIDs=None, where=None, eager=None):
for itemid in itemids:
if not isinstance(itemid, int):
raise TypeError("All passed item IDs must be integers")
@@ -265,7 +263,17 @@ def getVariations(itemids, where=None, eager=None):
joinon = items_table.c.typeID == metatypes_table.c.typeID
vars = gamedata_session.query(Item).options(*processEager(eager)).join((metatypes_table, joinon)).filter(
filter).all()
return vars
if vars:
return vars
elif groupIDs:
itemfilter = or_(*(groups_table.c.groupID == groupID for groupID in groupIDs))
filter = processWhere(itemfilter, where)
joinon = items_table.c.groupID == groups_table.c.groupID
vars = gamedata_session.query(Item).options(*processEager(eager)).join((groups_table, joinon)).filter(
filter).all()
return vars
@cachedQuery(1, "attr")

View File

@@ -2,7 +2,7 @@ from sqlalchemy import Column, Table, Integer, String, ForeignKey
from sqlalchemy.orm import mapper
from eos.db import gamedata_meta
from eos.types import Traits
from eos.gamedata import Traits
traits_table = Table("invtraits", gamedata_meta,
Column("typeID", Integer, ForeignKey("invtypes.typeID"), primary_key=True),

View File

@@ -21,7 +21,7 @@ from sqlalchemy import Column, Table, Integer, String
from sqlalchemy.orm import mapper, synonym
from eos.db import gamedata_meta
from eos.types import Unit
from eos.gamedata import Unit
groups_table = Table("dgmunits", gamedata_meta,
Column("unitID", Integer, primary_key=True),

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

@@ -4,8 +4,6 @@ Migration 15
- Delete projected modules on citadels
"""
import sqlalchemy
def upgrade(saveddata_engine):
sql = """

View File

@@ -4,8 +4,6 @@ Migration 17
- Moves all fleet boosters to the new schema
"""
import sqlalchemy
def upgrade(saveddata_engine):
from eos.db import saveddata_session

View File

@@ -4,8 +4,6 @@ Migration 19
- Deletes broken references to fits from the commandFits table (see GH issue #844)
"""
import sqlalchemy
def upgrade(saveddata_engine):
from eos.db import saveddata_session

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

@@ -22,7 +22,7 @@ from sqlalchemy.ext.associationproxy import association_proxy
from sqlalchemy.orm import mapper, relation
from eos.db import saveddata_meta
from eos.types import Booster
from eos.saveddata.booster import Booster
boosters_table = Table("boosters", saveddata_meta,
Column("ID", Integer, primary_key=True),

View File

@@ -21,7 +21,7 @@ from sqlalchemy import Table, Column, Integer, ForeignKey
from sqlalchemy.orm import mapper
from eos.db import saveddata_meta
from eos.types import Cargo
from eos.saveddata.cargo import Cargo
cargo_table = Table("cargo", saveddata_meta,
Column("ID", Integer, primary_key=True),

View File

@@ -23,7 +23,9 @@ from sqlalchemy.orm import relation, mapper
from eos.db import saveddata_meta
from eos.db.saveddata.implant import charImplants_table
from eos.effectHandlerHelpers import HandledImplantBoosterList
from eos.types import Character, User, Skill, Implant
from eos.saveddata.implant import Implant
from eos.saveddata.user import User
from eos.saveddata.character import Character, Skill
characters_table = Table("characters", saveddata_meta,
Column("ID", Integer, primary_key=True),

View File

@@ -21,7 +21,7 @@ from sqlalchemy import Table, Column, Integer, String
from sqlalchemy.orm import mapper
from eos.db import saveddata_meta
from eos.types import CrestChar
from eos.saveddata.crestchar import CrestChar
crest_table = Table("crest", saveddata_meta,
Column("ID", Integer, primary_key=True),

View File

@@ -21,7 +21,7 @@ from sqlalchemy import Table, Column, Integer, ForeignKey, String
from sqlalchemy.orm import mapper
from eos.db import saveddata_meta
from eos.types import DamagePattern
from eos.saveddata.damagePattern import DamagePattern
damagePatterns_table = Table("damagePatterns", saveddata_meta,
Column("ID", Integer, primary_key=True),

View File

@@ -17,13 +17,13 @@
# along with pyfa. If not, see <http://www.gnu.org/licenses/>.
# ===============================================================================
import sqlalchemy
import logging
from sqlalchemy.exc import DatabaseError
from logbook import Logger
logger = logging.getLogger(__name__)
pyfalog = Logger(__name__)
class DatabaseCleanup:
class DatabaseCleanup(object):
def __init__(self):
pass
@@ -32,8 +32,8 @@ class DatabaseCleanup:
try:
results = saveddata_engine.execute(query)
return results
except sqlalchemy.exc.DatabaseError:
logger.error("Failed to connect to database or error executing query:\n%s", query)
except DatabaseError:
pyfalog.error("Failed to connect to database or error executing query:\n{0}", query)
return None
@staticmethod
@@ -41,7 +41,7 @@ class DatabaseCleanup:
# 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:
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:
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' = " + str(uniform_damage_pattern_id) + \
" WHERE damagePatternID NOT IN (SELECT ID FROM damagePatterns) OR damagePatternID IS NULL"
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:
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) + \
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:
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,4 +159,81 @@ class DatabaseCleanup:
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']:
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)
if results is None:
return
row = results.first()
if row and row['num']:
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)
pyfalog.error("Database corruption found. Cleaning up {0} records.", delete.rowcount)
for table in ['modules']:
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)
if results is None:
return
row = results.first()
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)
pyfalog.error("Database corruption found. Cleaning up {0} records.", delete.rowcount)
@staticmethod
def NullDamageTargetPatternValues(saveddata_engine):
# Find patterns that have null values
# See issue #954
for profileType in ['damagePatterns', 'targetResists']:
for damageType in ['em', 'thermal', 'kinetic', 'explosive']:
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)
if results is None:
return
row = results.first()
if row and row['num']:
query = "UPDATE '{0}' SET '{1}Amount' = '0' WHERE {1}Amount IS NULL OR {1}Amount = ''".format(profileType,
damageType)
delete = DatabaseCleanup.ExecuteSQLQuery(saveddata_engine, query)
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
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)
if results is None:
return
row = results.first()
if row and row['num'] > 1:
query = "DELETE FROM damagePatterns WHERE name = 'Selected Ammo'"
delete = DatabaseCleanup.ExecuteSQLQuery(saveddata_engine, query)
pyfalog.error("Database corruption found. Cleaning up {0} records.", delete.rowcount)

View File

@@ -21,7 +21,7 @@ from sqlalchemy import Table, Column, Integer, ForeignKey, Boolean
from sqlalchemy.orm import mapper
from eos.db import saveddata_meta
from eos.types import Drone
from eos.saveddata.drone import Drone
drones_table = Table("drones", saveddata_meta,
Column("groupID", Integer, primary_key=True),

View File

@@ -21,8 +21,9 @@ from sqlalchemy import Table, Column, Integer, ForeignKey, Boolean
from sqlalchemy.orm import mapper, relation
from eos.db import saveddata_meta
from eos.types import Fighter, Fit
from eos.types import FighterAbility
from eos.saveddata.fighterAbility import FighterAbility
from eos.saveddata.fighter import Fighter
from eos.saveddata.fit import Fit
fighters_table = Table("fighters", saveddata_meta,
Column("groupID", Integer, primary_key=True),

View File

@@ -32,8 +32,17 @@ from eos.db.saveddata.implant import fitImplants_table
from eos.db.saveddata.module import modules_table
from eos.effectHandlerHelpers import HandledModuleList, HandledImplantBoosterList, HandledProjectedModList, \
HandledDroneCargoList, HandledProjectedDroneList
from eos.types import Fit as es_Fit, Module, User, Booster, Drone, Fighter, Cargo, Implant, Character, DamagePattern, \
TargetResists, ImplantLocation
from eos.saveddata.implant import Implant
from eos.saveddata.character import Character
from eos.saveddata.user import User
from eos.saveddata.fighter import Fighter
from eos.saveddata.fit import Fit as es_Fit, ImplantLocation
from eos.saveddata.drone import Drone
from eos.saveddata.booster import Booster
from eos.saveddata.module import Module
from eos.saveddata.cargo import Cargo
from eos.saveddata.damagePattern import DamagePattern
from eos.saveddata.targetResists import TargetResists
fits_table = Table("fits", saveddata_meta,
Column("ID", Integer, primary_key=True),
@@ -131,7 +140,7 @@ mapper(es_Fit, fits_table,
"_Fit__modules": relation(
Module,
collection_class=HandledModuleList,
primaryjoin=and_(modules_table.c.fitID == fits_table.c.ID, modules_table.c.projected == False), # noqa
primaryjoin=and_(modules_table.c.fitID == fits_table.c.ID, modules_table.c.projected == False), # noqa
order_by=modules_table.c.position,
cascade='all, delete, delete-orphan'),
"_Fit__projectedModules": relation(
@@ -139,7 +148,7 @@ mapper(es_Fit, fits_table,
collection_class=HandledProjectedModList,
cascade='all, delete, delete-orphan',
single_parent=True,
primaryjoin=and_(modules_table.c.fitID == fits_table.c.ID, modules_table.c.projected == True)), # noqa
primaryjoin=and_(modules_table.c.fitID == fits_table.c.ID, modules_table.c.projected == True)), # noqa
"owner": relation(
User,
backref="fits"),
@@ -155,13 +164,13 @@ mapper(es_Fit, fits_table,
collection_class=HandledDroneCargoList,
cascade='all, delete, delete-orphan',
single_parent=True,
primaryjoin=and_(drones_table.c.fitID == fits_table.c.ID, drones_table.c.projected == False)), # noqa
primaryjoin=and_(drones_table.c.fitID == fits_table.c.ID, drones_table.c.projected == False)), # noqa
"_Fit__fighters": relation(
Fighter,
collection_class=HandledDroneCargoList,
cascade='all, delete, delete-orphan',
single_parent=True,
primaryjoin=and_(fighters_table.c.fitID == fits_table.c.ID, fighters_table.c.projected == False)), # noqa
primaryjoin=and_(fighters_table.c.fitID == fits_table.c.ID, fighters_table.c.projected == False)), # noqa
"_Fit__cargo": relation(
Cargo,
collection_class=HandledDroneCargoList,
@@ -173,13 +182,13 @@ mapper(es_Fit, fits_table,
collection_class=HandledProjectedDroneList,
cascade='all, delete, delete-orphan',
single_parent=True,
primaryjoin=and_(drones_table.c.fitID == fits_table.c.ID, drones_table.c.projected == True)), # noqa
primaryjoin=and_(drones_table.c.fitID == fits_table.c.ID, drones_table.c.projected == True)), # noqa
"_Fit__projectedFighters": relation(
Fighter,
collection_class=HandledProjectedDroneList,
cascade='all, delete, delete-orphan',
single_parent=True,
primaryjoin=and_(fighters_table.c.fitID == fits_table.c.ID, fighters_table.c.projected == True)), # noqa
primaryjoin=and_(fighters_table.c.fitID == fits_table.c.ID, fighters_table.c.projected == True)), # noqa
"_Fit__implants": relation(
Implant,
collection_class=HandledImplantBoosterList,

View File

@@ -21,7 +21,7 @@ from sqlalchemy import Table, Column, Integer, ForeignKey, Boolean
from sqlalchemy.orm import mapper
from eos.db import saveddata_meta
from eos.types import Implant
from eos.saveddata.implant import Implant
implants_table = Table("implants", saveddata_meta,
Column("ID", Integer, primary_key=True),

View File

@@ -23,7 +23,8 @@ from sqlalchemy.orm import relation, mapper
from eos.db import saveddata_meta
from eos.db.saveddata.implant import implantsSetMap_table
from eos.effectHandlerHelpers import HandledImplantBoosterList
from eos.types import Implant, ImplantSet
from eos.saveddata.implant import Implant
from eos.saveddata.implantSet import ImplantSet
implant_set_table = Table("implantSets", saveddata_meta,
Column("ID", Integer, primary_key=True),

View File

@@ -26,7 +26,7 @@ class ImportError(Exception):
pass
class DefaultDatabaseValues():
class DefaultDatabaseValues(object):
def __init__(self):
pass

View File

@@ -21,7 +21,7 @@ from sqlalchemy import Column, Table, String
from sqlalchemy.orm import mapper
from eos.db import saveddata_meta
from eos.types import MiscData
from eos.saveddata.miscData import MiscData
miscdata_table = Table("miscdata", saveddata_meta,
Column("fieldName", String, primary_key=True),

View File

@@ -21,7 +21,8 @@ from sqlalchemy import Table, Column, Integer, ForeignKey, CheckConstraint, Bool
from sqlalchemy.orm import relation, mapper
from eos.db import saveddata_meta
from eos.types import Module, Fit
from eos.saveddata.module import Module
from eos.saveddata.fit import Fit
modules_table = Table("modules", saveddata_meta,
Column("ID", Integer, primary_key=True),

View File

@@ -21,7 +21,7 @@ from sqlalchemy import Table, Column, Integer, Float
from sqlalchemy.orm import mapper
from eos.db import saveddata_meta
from eos.types import Override
from eos.saveddata.override import Override
overrides_table = Table("overrides", saveddata_meta,
Column("itemID", Integer, primary_key=True, index=True),

View File

@@ -21,7 +21,7 @@ from sqlalchemy import Table, Column, Float, Integer
from sqlalchemy.orm import mapper
from eos.db import saveddata_meta
from eos.types import Price
from eos.saveddata.price import Price
prices_table = Table("prices", saveddata_meta,
Column("typeID", Integer, primary_key=True),

View File

@@ -22,8 +22,17 @@ from sqlalchemy.sql import and_
from eos.db import saveddata_session, sd_lock
from eos.db.saveddata.fit import projectedFits_table
from eos.db.util import processEager, processWhere
from eos.saveddata.price import Price
from eos.saveddata.user import User
from eos.saveddata.crestchar import CrestChar
from eos.saveddata.damagePattern import DamagePattern
from eos.saveddata.targetResists import TargetResists
from eos.saveddata.character import Character
from eos.saveddata.implantSet import ImplantSet
from eos.saveddata.fit import Fit
from eos.saveddata.miscData import MiscData
from eos.saveddata.override import Override
from eos.types import *
import eos.config
configVal = getattr(eos.config, "saveddataCache", None)
@@ -59,6 +68,7 @@ if configVal is True:
def checkAndReturn(*args, **kwargs):
useCache = kwargs.pop("useCache", True)
cacheKey = []
items = None
cacheKey.extend(args)
for keyword in keywords:
cacheKey.append(kwargs.get(keyword))

View File

@@ -21,7 +21,7 @@ from sqlalchemy import Table, Column, Integer, ForeignKey
from sqlalchemy.orm import mapper
from eos.db import saveddata_meta
from eos.types import Skill
from eos.saveddata.character import Skill
skills_table = Table("characterSkills", saveddata_meta,
Column("characterID", ForeignKey("characters.ID"), primary_key=True, index=True),

View File

@@ -21,7 +21,7 @@ from sqlalchemy import Table, Column, Integer, Float, ForeignKey, String
from sqlalchemy.orm import mapper
from eos.db import saveddata_meta
from eos.types import TargetResists
from eos.saveddata.targetResists import TargetResists
targetResists_table = Table("targetResists", saveddata_meta,
Column("ID", Integer, primary_key=True),

View File

@@ -21,7 +21,7 @@ from sqlalchemy import Table, Column, Integer, String, Boolean
from sqlalchemy.orm import mapper
from eos.db import saveddata_meta
from eos.types import User
from eos.saveddata.user import User
users_table = Table("users", saveddata_meta,
Column("ID", Integer, primary_key=True),

View File

@@ -17,12 +17,9 @@
# along with eos. If not, see <http://www.gnu.org/licenses/>.
# ===============================================================================
# from sqlalchemy.orm.attributes import flag_modified
import logging
from logbook import Logger
import eos.db
logger = logging.getLogger(__name__)
pyfalog = Logger(__name__)
class HandledList(list):
@@ -139,10 +136,6 @@ class HandledModuleList(HandledList):
self.remove(mod)
return
# fix for #529, where a module may be in incorrect state after CCP changes mechanics of module
if not mod.isValidState(mod.state):
mod.state = eos.types.State.ONLINE
def insert(self, index, mod):
mod.position = index
i = index
@@ -162,7 +155,7 @@ class HandledModuleList(HandledList):
def toDummy(self, index):
mod = self[index]
if not mod.isEmpty:
dummy = eos.types.Module.buildEmpty(mod.slot)
dummy = mod.buildEmpty(mod.slot)
dummy.position = index
self[index] = dummy
@@ -205,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)
@@ -229,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"
@@ -15,8 +15,6 @@ def handler(fit, module, context):
# Skip if there is no damage pattern. Example: projected ships or fleet boosters
if damagePattern:
# logger.debug("Damage Pattern: %f/%f/%f/%f", damagePattern.emAmount, damagePattern.thermalAmount, damagePattern.kineticAmount, damagePattern.explosiveAmount)
# logger.debug("Original Armor Resists: %f/%f/%f/%f", fit.ship.getModifiedItemAttr('armorEmDamageResonance'), fit.ship.getModifiedItemAttr('armorThermalDamageResonance'), fit.ship.getModifiedItemAttr('armorKineticDamageResonance'), fit.ship.getModifiedItemAttr('armorExplosiveDamageResonance'))
# Populate a tuple with the damage profile modified by current armor resists.
baseDamageTaken = (
@@ -25,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
@@ -41,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 = [
@@ -50,7 +48,6 @@ def handler(fit, module, context):
(2, baseDamageTaken[2] * RAHResistance[2], RAHResistance[2]),
(1, baseDamageTaken[1] * RAHResistance[1], RAHResistance[1]),
]
# logger.debug("Damage taken this cycle: %f/%f/%f/%f", damagePattern_tuples[0][1], damagePattern_tuples[3][1], damagePattern_tuples[2][1], damagePattern_tuples[1][1])
# Sort the tuple to drop the highest damage value to the bottom
sortedDamagePattern_tuples = sorted(damagePattern_tuples, key=lambda damagePattern: damagePattern[1])
@@ -80,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):
@@ -90,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
@@ -98,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.
@@ -113,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

@@ -3,4 +3,7 @@ type = "passive"
def handler(fit, src, context):
fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Information Command Specialist"), "commandBonusHidden", src.getModifiedItemAttr("eliteBonusCommandDestroyer1"), skill="Command Destroyers")
fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Information Command Specialist"),
"commandBonusHidden",
src.getModifiedItemAttr("eliteBonusCommandDestroyer1"),
skill="Command Destroyers")

View File

@@ -6,5 +6,11 @@ type = "passive"
def handler(fit, src, context):
fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Remote Armor Repair Systems"), "falloffEffectiveness", src.getModifiedItemAttr("eliteBonusLogistics1"), skill="Logistics Cruisers")
fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Remote Armor Repair Systems"), "maxRange", src.getModifiedItemAttr("eliteBonusLogistics1"), skill="Logistics Cruisers")
fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Remote Armor Repair Systems"),
"falloffEffectiveness",
src.getModifiedItemAttr("eliteBonusLogistics1"),
skill="Logistics Cruisers")
fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Remote Armor Repair Systems"),
"maxRange",
src.getModifiedItemAttr("eliteBonusLogistics1"),
skill="Logistics Cruisers")

View File

@@ -1,5 +1,5 @@
# Not used by any item
from eos.types import State
from eos.saveddata.module import State
type = "active", "projected"

View File

@@ -2,7 +2,7 @@
#
# Used by:
# Modules from group: Energy Neutralizer (51 of 51)
from eos.types import State
from eos.saveddata.module import State
type = "active", "projected"

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,7 @@
#
# Used by:
# Drones from group: Energy Neutralizer Drone (3 of 3)
from eos.types import State
from eos.saveddata.module import State
type = "active", "projected"

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

@@ -6,5 +6,9 @@ type = "passive"
def handler(fit, src, context):
fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Remote Armor Repair Systems"), "falloffEffectiveness", src.getModifiedItemAttr("roleBonusRepairRange"))
fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Remote Armor Repair Systems"), "maxRange", src.getModifiedItemAttr("roleBonusRepairRange"))
fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Remote Armor Repair Systems"),
"falloffEffectiveness",
src.getModifiedItemAttr("roleBonusRepairRange"))
fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Remote Armor Repair Systems"),
"maxRange",
src.getModifiedItemAttr("roleBonusRepairRange"))

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

@@ -6,8 +6,18 @@ type = "passive"
def handler(fit, src, context):
fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Armored Command") or mod.item.requiresSkill("Information Command"), "warfareBuff4Value", src.getModifiedItemAttr("shipBonusForceAuxiliaryA4"), skill="Amarr Carrier")
fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Armored Command") or mod.item.requiresSkill("Information Command"), "warfareBuff3Value", src.getModifiedItemAttr("shipBonusForceAuxiliaryA4"), skill="Amarr Carrier")
fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Armored Command") or mod.item.requiresSkill("Information Command"), "warfareBuff1Value", src.getModifiedItemAttr("shipBonusForceAuxiliaryA4"), skill="Amarr Carrier")
fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Armored Command") or mod.item.requiresSkill("Information Command"), "buffDuration", src.getModifiedItemAttr("shipBonusForceAuxiliaryA4"), skill="Amarr Carrier")
fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Armored Command") or mod.item.requiresSkill("Information Command"), "warfareBuff2Value", src.getModifiedItemAttr("shipBonusForceAuxiliaryA4"), skill="Amarr Carrier")
fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Armored Command") or
mod.item.requiresSkill("Information Command"),
"warfareBuff4Value", src.getModifiedItemAttr("shipBonusForceAuxiliaryA4"), skill="Amarr Carrier")
fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Armored Command") or
mod.item.requiresSkill("Information Command"),
"warfareBuff3Value", src.getModifiedItemAttr("shipBonusForceAuxiliaryA4"), skill="Amarr Carrier")
fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Armored Command") or
mod.item.requiresSkill("Information Command"),
"warfareBuff1Value", src.getModifiedItemAttr("shipBonusForceAuxiliaryA4"), skill="Amarr Carrier")
fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Armored Command") or
mod.item.requiresSkill("Information Command"),
"buffDuration", src.getModifiedItemAttr("shipBonusForceAuxiliaryA4"), skill="Amarr Carrier")
fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Armored Command") or
mod.item.requiresSkill("Information Command"),
"warfareBuff2Value", src.getModifiedItemAttr("shipBonusForceAuxiliaryA4"), skill="Amarr Carrier")

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

@@ -1,5 +1,5 @@
# Not used by any item
from eos.types import State
from eos.saveddata.module import State
type = "active", "projected"

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,5 +1,5 @@
# Not used by any item
from eos.types import State
from eos.saveddata.module import State
# Not used by any item
runTime = "early"

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

@@ -2,8 +2,10 @@
#
# Used by:
# Modules from group: Tractor Beam (4 of 4)
from eos.config import settings
type = "active"
def handler(fit, module, context):
print settings['setting1']
pass

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

@@ -3,7 +3,7 @@
# Used by:
# Modules named like: Warp Scrambler (26 of 26)
from eos.types import State
from eos.saveddata.module import State
runTime = "early"
type = "projected", "active"

View File

@@ -1,4 +1,4 @@
class Enum():
class Enum(object):
def __init__(self):
pass

View File

@@ -19,6 +19,8 @@
class EqBase(object):
ID = None
def __eq__(self, other):
return type(self) == type(other) and self.ID == other.ID

View File

@@ -18,7 +18,6 @@
# ===============================================================================
import re
import traceback
from sqlalchemy.orm import reconstructor
@@ -30,6 +29,10 @@ try:
except ImportError:
from utils.compat import OrderedDict
from logbook import Logger
pyfalog = Logger(__name__)
class Effect(EqBase):
"""
@@ -64,6 +67,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
@@ -138,7 +143,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
@@ -156,36 +161,30 @@ class Effect(EqBase):
"""
try:
self.__effectModule = effectModule = __import__('eos.effects.' + self.handlerName, fromlist=True)
try:
self.__handler = getattr(effectModule, "handler")
except AttributeError:
print "effect {} exists, but no handler".format(self.handlerName)
raise
try:
self.__runTime = getattr(effectModule, "runTime") or "normal"
except AttributeError:
self.__runTime = "normal"
try:
self.__activeByDefault = getattr(effectModule, "activeByDefault")
except AttributeError:
self.__activeByDefault = True
try:
t = getattr(effectModule, "type")
except AttributeError:
t = None
self.__handler = getattr(effectModule, "handler", effectDummy)
self.__runTime = getattr(effectModule, "runTime", "normal")
self.__activeByDefault = getattr(effectModule, "activeByDefault", True)
t = getattr(effectModule, "type", None)
t = t if isinstance(t, tuple) or t is None else (t,)
self.__type = t
except (ImportError, AttributeError) as e:
except (ImportError) as e:
# Effect probably doesn't exist, so create a dummy effect and flag it with a warning.
self.__handler = effectDummy
self.__runTime = "normal"
self.__activeByDefault = True
self.__type = None
pyfalog.warning("ImportError generating handler: {0}", e)
except (AttributeError) as e:
# Effect probably exists but there is an issue with it. Turn it into a dummy effect so we can continue, but flag it with an error.
self.__handler = effectDummy
self.__runTime = "normal"
self.__activeByDefault = True
self.__type = None
pyfalog.error("AttributeError generating handler: {0}", e)
except Exception as e:
traceback.print_exc(e)
pyfalog.critical("Exception generating handler:")
pyfalog.critical(e)
self.__generated = True
@@ -243,9 +242,11 @@ class Item(EqBase):
return self.__attributes
def getAttribute(self, key):
def getAttribute(self, key, default=None):
if key in self.attributes:
return self.attributes[key].value
else:
return default
def isType(self, type):
for effect in self.effects.itervalues():
@@ -283,8 +284,6 @@ class Item(EqBase):
@property
def requiredSkills(self):
if self.__requiredSkills is None:
# This import should be here to make sure it's fully initialized
from eos import db
requiredSkills = OrderedDict()
self.__requiredSkills = requiredSkills
# Map containing attribute IDs we may need for required skills
@@ -295,7 +294,7 @@ class Item(EqBase):
# { attributeID : attributeValue }
skillAttrs = {}
# Get relevant attribute values from db (required skill IDs and levels) for our item
for attrInfo in db.directAttributeRequest((self.ID,), tuple(combinedAttrIDs)):
for attrInfo in eos.db.directAttributeRequest((self.ID,), tuple(combinedAttrIDs)):
attrID = attrInfo[1]
attrVal = attrInfo[2]
skillAttrs[attrID] = attrVal
@@ -306,7 +305,7 @@ class Item(EqBase):
skillID = int(skillAttrs[srqIDAtrr])
skillLvl = skillAttrs[srqLvlAttr]
# Fetch item from database and fill map
item = db.getItem(skillID)
item = eos.db.getItem(skillID)
requiredSkills[item] = skillLvl
return self.__requiredSkills
@@ -339,18 +338,20 @@ class Item(EqBase):
# thus keep old mechanism for now
except KeyError:
# Define race map
map = {1: "caldari",
2: "minmatar",
4: "amarr",
5: "sansha", # Caldari + Amarr
6: "blood", # Minmatar + Amarr
8: "gallente",
9: "guristas", # Caldari + Gallente
10: "angelserp", # Minmatar + Gallente, final race depends on the order of skills
12: "sisters", # Amarr + Gallente
16: "jove",
32: "sansha", # Incrusion Sansha
128: "ore"}
map = {
1 : "caldari",
2 : "minmatar",
4 : "amarr",
5 : "sansha", # Caldari + Amarr
6 : "blood", # Minmatar + Amarr
8 : "gallente",
9 : "guristas", # Caldari + Gallente
10 : "angelserp", # Minmatar + Gallente, final race depends on the order of skills
12 : "sisters", # Amarr + Gallente
16 : "jove",
32 : "sansha", # Incrusion Sansha
128: "ore"
}
# Race is None by default
race = None
# Check primary and secondary required skills' races
@@ -420,7 +421,7 @@ class Item(EqBase):
def __repr__(self):
return "Item(ID={}, name={}) at {}".format(
self.ID, self.name, hex(id(self))
self.ID, self.name, hex(id(self))
)
@@ -474,7 +475,7 @@ class Icon(EqBase):
class MarketGroup(EqBase):
def __repr__(self):
return u"MarketGroup(ID={}, name={}, parent={}) at {}".format(
self.ID, self.name, getattr(self.parent, "name", None), self.name, hex(id(self))
self.ID, self.name, getattr(self.parent, "name", None), self.name, hex(id(self))
).encode('utf8')

View File

@@ -62,7 +62,7 @@ class Data(object):
def parseString(self, dataString):
if not isinstance(dataString, basestring):
return (Constant(dataString),)
return Constant(dataString),
dataList = []
for data in dataString.split(";"):
@@ -93,7 +93,8 @@ class Constant(object):
def __iter__(self):
yield self.value
def isConstant(self):
@staticmethod
def isConstant():
return True
@@ -114,5 +115,6 @@ class Range(object):
i += 1
yield current
def isConstant(self):
@staticmethod
def isConstant():
return False

View File

@@ -20,7 +20,10 @@
from math import log, sin, radians, exp
from eos.graph import Graph
from eos.types import Hardpoint, State
from eos.saveddata.module import State, Hardpoint
from logbook import Logger
pyfalog = Logger(__name__)
class FitDpsGraph(Graph):
@@ -38,7 +41,7 @@ class FitDpsGraph(Graph):
fit = self.fit
total = 0
distance = data["distance"] * 1000
abssort = lambda val: -abs(val - 1)
abssort = lambda _val: -abs(_val - 1)
for mod in fit.modules:
if not mod.isEmpty and mod.state >= State.ACTIVE:
@@ -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)
@@ -95,7 +99,8 @@ class FitDpsGraph(Graph):
return total
def calculateMissileMultiplier(self, mod, data):
@staticmethod
def calculateMissileMultiplier(mod, data):
targetSigRad = data["signatureRadius"]
targetVelocity = data["velocity"]
explosionRadius = mod.getModifiedChargeAttr("aoeCloudSize")
@@ -126,7 +131,8 @@ class FitDpsGraph(Graph):
multiplier = min(1, (float(targetSigRad) / dmgScaling) ** 2)
return multiplier
def calculateFighterMissileMultiplier(self, ability, data):
@staticmethod
def calculateFighterMissileMultiplier(ability, data):
prefix = ability.attrPrefix
targetSigRad = data["signatureRadius"]
@@ -156,7 +162,8 @@ class FitDpsGraph(Graph):
return min(sigRadiusFactor, velocityFactor, 1)
def calculateTurretChanceToHit(self, mod, data):
@staticmethod
def calculateTurretChanceToHit(mod, data):
distance = data["distance"] * 1000
tracking = mod.getModifiedItemAttr("trackingSpeed")
turretOptimal = mod.maxRange
@@ -171,7 +178,8 @@ class FitDpsGraph(Graph):
return 0.5 ** (trackingEq + rangeEq)
def calculateModuleMultiplier(self, mod, data):
@staticmethod
def calculateModuleMultiplier(mod, data):
# Simplified formula, we make some assumptions about the module
# This is basically the calculateTurretChanceToHit without tracking values
distance = data["distance"] * 1000
@@ -179,4 +187,4 @@ class FitDpsGraph(Graph):
turretFalloff = mod.falloff
rangeEq = ((max(0, distance - turretOptimal)) / turretFalloff) ** 2
return 0.5 ** (rangeEq)
return 0.5 ** rangeEq

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