Merge branch 'master' into ammo_graph

# Conflicts:
#	gui/builtinMarketBrowser/itemView.py
#	service/market.py
This commit is contained in:
DarkPhoenix
2020-02-04 01:43:52 +03:00
70 changed files with 650 additions and 4687 deletions

View File

@@ -1,126 +1,76 @@
image: Visual Studio 2019
clone_depth: 400
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: "C:\\Python36"
PYTHON_VERSION: "3.6.x"
PYTHON_ARCH: "32"
init:
- ps: iex ((new-object net.webclient).DownloadString('https://raw.githubusercontent.com/appveyor/ci/master/scripts/enable-rdp.ps1'))
- PYTHON: "C:\\Python38-x64"
# Should be enabled only for build process debugging
# init:
# - ps: iex ((new-object net.webclient).DownloadString('https://raw.githubusercontent.com/appveyor/ci/master/scripts/enable-rdp.ps1'))
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." }
- ps: echo("OS version:")
- ps: "[System.Environment]::OSVersion.Version"
- ECHO "Filesystem root:"
- ps: "ls \"C:/\""
- ps: echo("Filesystem - root:")
- ps: "ls \"C:\\\""
- ECHO "Filesystem projects root:"
- ps: echo("Filesystem - projects root:")
- ps: "ls \"C:\\projects\\\""
- ECHO "Filesystem pyfa root:"
- ps: "ls \"C:\\projects\\$env:APPVEYOR_PROJECT_SLUG\""
- ps: echo("Filesystem - pyfa root:")
- ps: "ls \"C:\\projects\\$env:APPVEYOR_PROJECT_SLUG\\\""
- ECHO "Installed SDKs:"
- ps: "ls \"C:/Program Files/Microsoft SDKs/Windows\""
- ps: echo("Filesystem - installed SDKs:")
- ps: "ls \"C:\\Program Files (x86)\\Windows Kits\\\""
# 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%"
- cmd: "SET PATH=%PYTHON%;%PYTHON%\\Scripts;%PATH%"
- "python --version"
- "python -c \"import struct; print(struct.calcsize('P') * 8)\""
- cmd: "python --version"
- cmd: "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"
- cmd: "python -m pip install --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
- ECHO "Install pip requirements:"
- "python -m pip install -r requirements.txt"
- "python -m pip install PyInstaller"
- ps: echo("Install pip requirements:")
- cmd: "python -m pip install -r requirements.txt"
- cmd: "python -m pip install PyInstaller==3.6"
before_build:
# directory that will contain the built files
- ps: $env:PYFA_DIST_DIR = "c:\projects\$env:APPVEYOR_PROJECT_SLUG\dist"
- ps: $env:PYFA_VERSION = (python ./scripts/dump_version.py)
- ps: echo("pyfa version ")
- ps: echo ($env:PYFA_VERSION)
- ps: echo("pyfa version $env:PYFA_VERSION")
build_script:
- ECHO "Build pyfa:"
- ps: echo("Build pyfa:")
# Build gamedata DB
- "python db_update.py"
##########
# PyInstaller - create binaries for pyfa
##########
- cmd: "python db_update.py"
# Build command for PyInstaller
- "python -m PyInstaller --noupx --clean --windowed --noconsole -y pyfa.spec"
- cmd: "python -m PyInstaller --noupx --clean --windowed --noconsole -y pyfa.spec"
# Copy over manifest (See pyfa-org/pyfa#1622)
- ps: xcopy /y dist_assets\win\pyfa.exe.manifest $env:PYFA_DIST_DIR\pyfa\
# Not really sure if this is needed, but why not
- ps: xcopy /y dist_assets\win\Microsoft.VC90.CRT.manifest $env:PYFA_DIST_DIR\pyfa\
##########
# InnoScript EXE building
# This is in a separate script because I don't feel like copying over the logic to AppVeyor script right now...
##########
- "python dist_assets/win/dist.py"
# InnoScript EXE building. This is in a separate script because I don't feel like copying over the logic to AppVeyor script right now...
- cmd: "python dist_assets/win/dist.py"
- ps: dir $env:PYFA_DIST_DIR/
#- 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\\$env:APPVEYOR_PROJECT_SLUG\\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\*.*
- ps: 7z a "pyfa-$env:PYFA_VERSION-win.zip" -r "$env:PYFA_DIST_DIR\pyfa\*.*"
#- 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"
# Ha... we're just building
artifacts:
# Archive the generated packages in the ci.appveyor.com build report.
- path: pyfa*-win.zip
- path: pyfa*-win.exe
#- path: pyfa_debug.zip
# name: Pyfa_debug
deploy:
tag: $(pyfa_version)
@@ -128,10 +78,9 @@ deploy:
description: 'Release description'
provider: GitHub
auth_token:
secure: BfNHO66ff5hVx2O2ORbl49X0U/5h2V2T0IuRZDwm7fd1HvsVluF0wRCbl29oRp1M
secure: X+U3hOAMTt7HGXCR/LXaGNF6qyhUXetrjz5+xlWiNJQ3XEdzhZZmHK75m0Hm6qre
draft: true
force_update: false
# deploy on tag push only
on:
APPVEYOR_REPO_TAG: true # deploy on tag push only
#on_success:
# - TODO: upload the content of dist/*.whl to a public wheelhouse
#
APPVEYOR_REPO_TAG: true

View File

@@ -3,13 +3,12 @@ language: python
git:
depth: 400
python:
- 3.6
- 3.8
matrix:
include:
- os: osx
osx_image: xcode7.3
osx_image: xcode11.3
language: generic
env: PYTHON=3.6.1
before_install:
- bash scripts/setup-osx.sh
install:
@@ -22,7 +21,7 @@ before_deploy:
deploy:
provider: releases
api_key:
secure: Xfu0xApoB0zUPLXl29aYUulVC3iA4/3bXQwwADKCfAKZwxgNon4dLbO7Rie5/7Ukf2POL0KwmRaQGN3kOr+XSoIVTE4M5sXxnhiaaLGKQ+48hDizLE6JuXcZGJvkxUaghaTzIdCwHsG7VGBsPfQgfGsjJcfBp8tFNLmRyM/Jpsr8T6BR2MxtBIEUVy8zrOWFNZqnmWrY2pWMsB9fYt3JFNdpqeIgRAYqbBsBcZQ1MngLTi3ztuYS5IaF+lk06RrnBlHmUsJu/5nCvIpvPvD0i2BLZ3Uu0+Fn+8QWUgjJEL9MNseXZMXynu05xd8YRk7Ajc9CUrzQIIbAktyteYp85kE3pUJHmrMLcXhh7nqkwttR5/47Zwa3OLJLJFKBxMx6wY5jFkJjkV08850B7aWrmTFl/Eqc3Q5nZMuiEt3wFRbjxHi9h1mTN/fkxfRRHg8u3ENGPR+ZPiFC3J18qtks/B/hsKjjHvZP1i79OYlET4V/zyLyyQkCbpDaARQANuotLYJyZ7tH+KWEyRsvTi0M9Yev9mNNw6aI4vzh4HfkEhvcvnWnYwckPj1dnjQ573Qpw0Z9wsconoWfHAn+hBDt3+YLMrrFZl++mCRskHH1mZChX3aGMDi49zD0kfxBUkYPOAhguc6PwudBxHUZP+O6T/SoHylff6EizCE/k5dGeAk=
secure: D8tBW0kyHlKf/sXS69aIuexsYTx9auY2DzudKFlfcvdzqat4N2XZqZbZCTVd7YVvptQ8Dj0oZ/p3KUxEGpnJZmlTeJL142rpM/qaNd6wOIMy2yUde/aZl+W9JLFNQp7KHutM+MxObYLzJGihx/8YsupmFx6lxgdngGDXtXYZe/ruDIWDs92ShoKJ4vlce9Csm7eGKv7wv6Z6V9sD5FS3E9J8xdWStHxsbrkPBOflmG+uHU09dpEqzUm+ZYROIoTwig1Xbw3fw+gfjmNrfdSU4fAJcVZI1hrgoenZyJbMfhI2Ej/nZdbZgaXcZNF/eUpqOGgbPe1JljqFnHTbexcE+LPBVyAToScsGMpByHhig67DrZ0nk9gSZoC6CPNl5YS6xub+5dncMJ3P5L03DOGYRu4SL9NczbeuQyKuea7+JPP/8VLwfFDSEqbNEAmgzABAzrdfano+VXtuBuE/Tiy5eE7le9hJu6aSQoKW1SA3cUhMsmr2amzdO96sh+PN8FA1oNr45Yuy0pqOj4SUIkb8JUy4th7vgdhljEkSxrHDK1UcHpxUTp+IIUZkZVVk50aH68dQZxGwSTVOeRxpjrTcEf7VCGaM98qxi/ZK4RW6Ewiq0eo0AxwEeB2Zm841lycGPR/406vM9/ZBzv5IhELIdDdVWTk+dGjJBXB8z5hPJOg=
file_glob: true
file: "dist/pyfa-*.zip"
skip_cleanup: true

View File

@@ -49,6 +49,8 @@ def DBInMemory_test():
gamedata_version = gamedata_session.execute(
"SELECT `field_value` FROM `metadata` WHERE `field_name` LIKE 'client_build'"
).fetchone()[0]
except (KeyboardInterrupt, SystemExit):
raise
except Exception as e:
print("Missing gamedata version.")
gamedata_version = None

View File

@@ -233,6 +233,8 @@ def defLogging():
# reset=False,
)
])
except (KeyboardInterrupt, SystemExit):
raise
except:
print("Critical error attempting to setup logging. Falling back to console only.")
logging_setup = NestedSetup([

View File

@@ -40,7 +40,7 @@ def db_needs_update():
try:
with open(os.path.join(JSON_DIR, 'phobos', 'metadata.json')) as f:
data_version = next((r['field_value'] for r in json.load(f) if r['field_name'] == 'client_build'))
except KeyboardInterrupt:
except (KeyboardInterrupt, SystemExit):
raise
# If we have no source data - return None; should not update in this case
except:
@@ -61,7 +61,7 @@ def db_needs_update():
db_schema_version = int(row[0])
cursor.close()
db.close()
except KeyboardInterrupt:
except (KeyboardInterrupt, SystemExit):
raise
except:
print('Error when fetching gamedata DB metadata')

View File

@@ -1,13 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
<noInheritable/>
<assemblyIdentity
type="win32"
name="Microsoft.VC90.CRT"
version="9.0.21022.8"
processorArchitecture="x86"
publicKeyToken="1fc8b3b9a1e18e3b"/>
<file name="MSVCR90.DLL"/>
<file name="MSVCM90.DLL"/>
<file name="MSVCP90.DLL"/>
</assembly>

View File

@@ -14,7 +14,7 @@ with open("version.yml", 'r') as file:
os.environ["PYFA_DIST_DIR"] = os.path.join(os.getcwd(), 'dist')
os.environ["PYFA_VERSION"] = version
iscc = "C:\Program Files (x86)\Inno Setup 5\ISCC.exe" # inno script location via wine
iscc = "C:\Program Files (x86)\Inno Setup 6\ISCC.exe"
source = os.path.join(os.environ["PYFA_DIST_DIR"], "pyfa")

View File

@@ -15,10 +15,6 @@
#define MyAppURL "https://github.com/pyfa-org/Pyfa/"
#define MyAppExeName "pyfa.exe"
; What version starts with the new structure (1.x.0). This is used to determine if we run directory structure cleanup
#define MajorVersionFlag 2
#define MinorVersionFlag 0
#ifndef MyOutputFile
#define MyOutputFile LowerCase(StringChange(MyAppName+'-'+MyAppVersion+'-win', " ", "-"))
#endif
@@ -30,7 +26,6 @@
#endif
[Setup]
; NOTE: The value of AppId uniquely identifies this application.
; Do not use the same AppId value in installers for other applications.
; (To generate a new GUID, click Tools | Generate GUID inside the IDE.)
@@ -41,6 +36,9 @@ AppPublisher={#MyAppPublisher}
AppPublisherURL={#MyAppURL}
AppSupportURL={#MyAppURL}
AppUpdatesURL={#MyAppURL}
ArchitecturesAllowed=x64
ArchitecturesInstallIn64BitMode=x64
CloseApplications=yes
DefaultDirName={pf}\{#MyAppName}
DefaultGroupName={#MyAppName}
AllowNoIcons=yes
@@ -49,7 +47,6 @@ OutputDir={#MyOutputDir}
OutputBaseFilename={#MyOutputFile}
SetupIconFile={#MyAppDir}\pyfa.ico
SolidCompression=yes
CloseApplications=yes
[Languages]
Name: "english"; MessagesFile: "compiler:Default.isl"
@@ -83,6 +80,7 @@ Type: files; Name: "{app}\*.pyc"
[Code]
/////////////////////////////////////////////////////////////////////
function IsAppRunning(const FileName : string): Boolean;
var
FSWbemLocator: Variant;
@@ -99,6 +97,7 @@ begin
FSWbemLocator := Unassigned;
end;
/////////////////////////////////////////////////////////////////////
procedure RemoveFromVirtualStore;
var
VirtualStore,FileName,FilePath:String;
@@ -115,6 +114,7 @@ begin
end;
end;
/////////////////////////////////////////////////////////////////////
function PrepareToInstall(var NeedsRestart: Boolean): String;
begin
if(IsAppRunning( 'pyfa.exe' )) then
@@ -127,54 +127,61 @@ begin
end
end;
function GetUninstallString: string;
/////////////////////////////////////////////////////////////////////
function GetUninstallString(): String;
var
sUnInstPath: string;
sUnInstPath: String;
sUnInstallString: String;
begin
Result := '';
sUnInstPath := ExpandConstant('Software\Microsoft\Windows\CurrentVersion\Uninstall\{{3DA39096-C08D-49CD-90E0-1D177F32C8AA}_is1'); //Your App GUID/ID
sUnInstallString := '';
if not RegQueryStringValue(HKLM, sUnInstPath, 'UninstallString', sUnInstallString) then
RegQueryStringValue(HKCU, sUnInstPath, 'UninstallString', sUnInstallString);
if not RegQueryStringValue(HKCU, sUnInstPath, 'UninstallString', sUnInstallString) then
if not RegQueryStringValue(HKLM32, sUnInstPath, 'UninstallString', sUnInstallString) then
RegQueryStringValue(HKCU32, sUnInstPath, 'UninstallString', sUnInstallString);
Result := sUnInstallString;
end;
function IsUpgrade: Boolean;
/////////////////////////////////////////////////////////////////////
function UnInstallOldVersion(): Integer;
var
sUnInstallString: String;
iResultCode: Integer;
begin
// Return Values:
// 1 - uninstall string is empty
// 2 - error executing the UnInstallString
// 3 - successfully executed the UnInstallString
// default return value
Result := 0;
// get the uninstall string of the old app
sUnInstallString := GetUninstallString();
if sUnInstallString <> '' then begin
sUnInstallString := RemoveQuotes(sUnInstallString);
if Exec(sUnInstallString, '/SILENT /NORESTART /SUPPRESSMSGBOXES','', SW_HIDE, ewWaitUntilTerminated, iResultCode) then
Result := 3
else
Result := 2;
end else
Result := 1;
end;
/////////////////////////////////////////////////////////////////////
function IsUpgrade(): Boolean;
begin
Result := (GetUninstallString() <> '');
end;
function InitializeSetup: Boolean;
var
V: Integer;
iResultCode: Integer;
sUnInstallString: string;
iOldVersionMajor: Cardinal;
iOldVersionMinor: Cardinal;
/////////////////////////////////////////////////////////////////////
procedure CurStepChanged(CurStep: TSetupStep);
begin
Result := True; // in case when no previous version is found
if RegValueExists(HKEY_LOCAL_MACHINE,'Software\Microsoft\Windows\CurrentVersion\Uninstall\{3DA39096-C08D-49CD-90E0-1D177F32C8AA}_is1', 'UninstallString') then //Your App GUID/ID
if (CurStep=ssInstall) then
begin
RegQueryDWordValue(HKEY_LOCAL_MACHINE,
'Software\Microsoft\Windows\CurrentVersion\Uninstall\{3DA39096-C08D-49CD-90E0-1D177F32C8AA}_is1',
'MajorVersion', iOldVersionMajor);
RegQueryDWordValue(HKEY_LOCAL_MACHINE,
'Software\Microsoft\Windows\CurrentVersion\Uninstall\{3DA39096-C08D-49CD-90E0-1D177F32C8AA}_is1',
'MinorVersion', iOldVersionMinor);
if (iOldVersionMajor < {#MajorVersionFlag}) or ((iOldVersionMajor = {#MajorVersionFlag}) and (iOldVersionMinor < {#MinorVersionFlag})) then // If old version with old structure is installed.
if (IsUpgrade()) then
begin
V := MsgBox(ExpandConstant('An old version of pyfa was detected. Due to recent changes in the application structure, you must uninstall the previous version first. This will not affect your user data (saved fittings, characters, etc.). Do you want to uninstall now?'), mbInformation, MB_YESNO); //Custom Message if App installed
if V = IDYES then
begin
sUnInstallString := GetUninstallString();
sUnInstallString := RemoveQuotes(sUnInstallString);
Exec(ExpandConstant(sUnInstallString), '', '', SW_SHOW, ewWaitUntilTerminated, iResultCode);
Result := True; //if you want to proceed after uninstall
//Exit; //if you want to quit after uninstall
end
else
Result := False; //when older version present and not uninstalled
UnInstallOldVersion();
end;
end;
end;

View File

@@ -8,11 +8,6 @@
</requestedPrivileges>
</security>
</trustInfo>
<dependency>
<dependentAssembly>
<assemblyIdentity type="win32" name="Microsoft.VC90.CRT" version="9.0.21022.8" processorArchitecture="x86" publicKeyToken="1fc8b3b9a1e18e3b"></assemblyIdentity>
</dependentAssembly>
</dependency>
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
<application>
<supportedOS Id="{e2011457-1546-43c5-a5fe-008deee3d3f0}"/>

View File

@@ -20,7 +20,6 @@ added_files = [
('../../service/jargon/*.yaml', 'service/jargon'),
('../../dist_assets/win/pyfa.ico', '.'),
('../../dist_assets/win/pyfa.exe.manifest', '.'),
('../../dist_assets/win/Microsoft.VC90.CRT.manifest', '.'),
(requests.certs.where(), '.'), # is this needed anymore?
('../../eve.db', '.'),
('../../README.md', '.'),

View File

@@ -1,83 +0,0 @@
# -*- 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

@@ -56,6 +56,8 @@ try:
config.gamedata_date = gamedata_session.execute(
"SELECT `field_value` FROM `metadata` WHERE `field_name` LIKE 'dump_time'"
).fetchone()[0]
except (KeyboardInterrupt, SystemExit):
raise
except Exception as e:
pyfalog.warning("Missing gamedata version.")
pyfalog.critical(e)

View File

@@ -33,9 +33,13 @@ def upgrade(saveddata_engine):
try:
saveddata_session.execute(commandFits_table.insert(),
{"boosterID": value, "boostedID": boosted, "active": 1})
except (KeyboardInterrupt, SystemExit):
raise
except Exception:
pass
saveddata_session.commit()
except (KeyboardInterrupt, SystemExit):
raise
except:
# Shouldn't fail unless you have updated database without the old fleet schema and manually modify the database version
# If it does, simply fail. Fleet data migration isn't critically important here

View File

@@ -4235,6 +4235,8 @@ def upgrade(saveddata_engine):
# And last but not least, delete the last subsystem
saveddata_engine.execute("DELETE FROM modules WHERE ID = ?", (oldModules[4][0],))
except (KeyboardInterrupt, SystemExit):
raise
except:
# if something fails, fuck it, we tried. It'll default to the generic conversion below
continue

View File

@@ -560,6 +560,8 @@ def commit():
with sd_lock:
try:
saveddata_session.commit()
except (KeyboardInterrupt, SystemExit):
raise
except Exception:
saveddata_session.rollback()
exc_info = sys.exc_info()
@@ -570,6 +572,8 @@ def flush():
with sd_lock:
try:
saveddata_session.flush()
except (KeyboardInterrupt, SystemExit):
raise
except Exception:
saveddata_session.rollback()
exc_info = sys.exc_info()

View File

@@ -42,7 +42,7 @@ class Effect4(BaseEffect):
shieldBoosting
Used by:
Modules from group: Shield Booster (97 of 97)
Modules from group: Shield Booster (89 of 89)
"""
runTime = 'late'
@@ -147,7 +147,7 @@ class Effect27(BaseEffect):
armorRepair
Used by:
Modules from group: Armor Repair Unit (108 of 108)
Modules from group: Armor Repair Unit (100 of 100)
"""
runTime = 'late'
@@ -1762,7 +1762,7 @@ class Effect598(BaseEffect):
ammoSpeedMultiplier
Used by:
Charges from group: Festival Charges (27 of 28)
Charges from group: Festival Charges (28 of 28)
Charges from group: Interdiction Probe (2 of 2)
Charges from group: Structure Festival Charges (2 of 2)
Special Edition Assetss from group: Festival Charges Expired (4 of 4)
@@ -9584,7 +9584,7 @@ class Effect3200(BaseEffect):
Used by:
Modules from group: Ancillary Armor Repairer (7 of 7)
Modules from group: Armor Repair Unit (108 of 108)
Modules from group: Armor Repair Unit (100 of 100)
"""
type = 'overheat'
@@ -9602,7 +9602,7 @@ class Effect3201(BaseEffect):
Used by:
Modules from group: Ancillary Shield Booster (8 of 8)
Modules from group: Shield Booster (97 of 97)
Modules from group: Shield Booster (89 of 89)
"""
type = 'overheat'
@@ -23589,7 +23589,7 @@ class Effect5994(BaseEffect):
resistanceKillerHullAll
Used by:
Modules named like: Polarized (12 of 18)
Modules named like: Polarized (12 of 15)
"""
type = 'passive'
@@ -23606,7 +23606,7 @@ class Effect5995(BaseEffect):
resistanceKillerShieldArmorAll
Used by:
Modules named like: Polarized (12 of 18)
Modules named like: Polarized (12 of 15)
"""
type = 'passive'
@@ -32691,6 +32691,8 @@ class Effect6871(BaseEffect):
# via https://forums.eveonline.com/default.aspx?g=posts&t=515826
try:
bonus = max(0, min(50.0, (src.owner.character.secStatus * 10)))
except (KeyboardInterrupt, SystemExit):
raise
except:
bonus = None
@@ -36227,7 +36229,7 @@ class Effect8011(BaseEffect):
shieldHpBonusPostPercentHpLocationShip
Used by:
Implants named like: grade Nirvana (10 of 12)
Implants named like: grade Nirvana (15 of 18)
"""
type = 'passive'
@@ -36242,7 +36244,7 @@ class Effect8013(BaseEffect):
setBonusNirvana
Used by:
Implants named like: grade Nirvana (12 of 12)
Implants named like: grade Nirvana (18 of 18)
"""
runTime = 'early'

View File

@@ -193,6 +193,8 @@ class Effect(EqBase):
self.__dealsDamage = False
self.__type = None
pyfalog.error("AttributeError generating handler: {0}", e)
except (KeyboardInterrupt, SystemExit):
raise
except Exception as e:
self.__handler = eos.effects.DummyEffect.handler
self.__runTime = "normal"
@@ -210,6 +212,8 @@ class Effect(EqBase):
try:
return self.__effectDef.get(key, None)
except (KeyboardInterrupt, SystemExit):
raise
except:
return getattr(self.__effectDef, key, None)

View File

@@ -118,15 +118,15 @@ BUILTINS = OrderedDict([
# Source: ticket #2067
(-86, ('[NPC][Invasion][Invading Precursor Entities]Dread', 0, 417, 0, 583)),
(-87, ('[NPC][Invasion][Invading Precursor Entities]Normal Subcaps', 0, 610, 0, 390)),
(-88, ('[NPC][Invasion][Invading Precursor Entities]Subcaps w/missiles 0% spool up', 363, 160, 363, 115)),
(-89, ('[NPC][Invasion][Invading Precursor Entities]Subcaps w/missiles 50% spool up', 286, 249, 286, 179)),
(-90, ('[NPC][Invasion][Invading Precursor Entities]Subcaps w/missiles 100% spool up', 236, 307, 236, 221)),
(-88, ('[NPC][Invasion][Invading Precursor Entities]Subcaps w/missiles 0% spool up', 367, 155, 367, 112)),
(-89, ('[NPC][Invasion][Invading Precursor Entities]Subcaps w/missiles 50% spool up', 291, 243, 291, 175)),
(-90, ('[NPC][Invasion][Invading Precursor Entities]Subcaps w/missiles 100% spool up', 241, 301, 241, 217)),
(-91, ('[NPC][Invasion][Retaliating Amarr Entities]Dread/Subcaps', 583, 417, 0, 0)),
(-92, ('[NPC][Invasion][Retaliating Caldari Entities]Dread', 1000, 0, 0, 0)),
(-93, ('[NPC][Invasion][Retaliating Caldari Entities]Subcaps', 511, 21, 29, 439)),
(-93, ('[NPC][Invasion][Retaliating Caldari Entities]Subcaps', 511, 21, 29, 440)),
(-94, ('[NPC][Invasion][Retaliating Gallente Entities]Dread/Subcaps', 0, 417, 583, 0)),
(-95, ('[NPC][Invasion][Retaliating Minmatar Entities]Dread', 0, 0, 583, 417)),
(-96, ('[NPC][Invasion][Retaliating Minmatar Entities]Subcaps', 302, 132, 330, 236)),
(-96, ('[NPC][Invasion][Retaliating Minmatar Entities]Subcaps', 302, 136, 328, 234)),
(-97, ('[NPC][Mission]Amarr Empire', 4464, 3546, 97, 0)),
(-98, ('[NPC][Mission]Caldari State', 0, 2139, 4867, 0)),
(-99, ('[NPC][Mission]CONCORD', 336, 134, 212, 412)),
@@ -260,6 +260,8 @@ class DamagePattern:
line = line.split('#', 1)[0] # allows for comments
type, data = line.rsplit('=', 1)
type, data = type.strip(), data.split(',')
except (KeyboardInterrupt, SystemExit):
raise
except:
# Data isn't in correct format, continue to next line
continue
@@ -274,6 +276,8 @@ class DamagePattern:
for index, val in enumerate(data):
try:
fields["%sAmount" % cls.DAMAGE_TYPES[index]] = int(val)
except (KeyboardInterrupt, SystemExit):
raise
except:
continue

View File

@@ -73,6 +73,8 @@ class Mutator(EqBase):
self.dynamicAttribute = next(a for a in self.module.mutaplasmid.attributes if a.attributeID == self.attrID)
# base attribute links to the base ite's attribute for this mutated definition (contains original, base value)
self.baseAttribute = self.module.item.attributes[self.dynamicAttribute.name]
except (KeyboardInterrupt, SystemExit):
raise
except:
self.module = None

View File

@@ -294,6 +294,8 @@ class TargetProfile:
line = line.split('#', 1)[0] # allows for comments
type, data = line.rsplit('=', 1)
type, data = type.strip(), [d.strip() for d in data.split(',')]
except (KeyboardInterrupt, SystemExit):
raise
except:
pyfalog.warning("Data isn't in correct format, continue to next line.")
continue
@@ -312,6 +314,8 @@ class TargetProfile:
try:
assert 0 <= val <= 100
fields["%sAmount" % cls.DAMAGE_TYPES[index]] = val / 100
except (KeyboardInterrupt, SystemExit):
raise
except:
pyfalog.warning("Caught unhandled exception in import patterns.")
continue

View File

@@ -54,6 +54,8 @@ try:
except ImportError as e:
pyfalog.warning('Matplotlib failed to import. Likely missing or incompatible version.')
graphFrame_enabled = False
except (KeyboardInterrupt, SystemExit):
raise
except Exception:
# We can get exceptions deep within matplotlib. Catch those. See GH #1046
tb = traceback.format_exc()
@@ -71,6 +73,8 @@ class GraphCanvasPanel(wx.Panel):
# Remove matplotlib font cache, see #234
try:
cache_dir = mpl._get_cachedir()
except (KeyboardInterrupt, SystemExit):
raise
except:
cache_dir = os.path.expanduser(os.path.join('~', '.matplotlib'))
cache_file = os.path.join(cache_dir, 'fontList.cache')
@@ -168,6 +172,8 @@ class GraphCanvasPanel(wx.Panel):
legendData.append((color, lineStyle, source.shortName))
else:
legendData.append((color, lineStyle, '{} vs {}'.format(source.shortName, target.shortName)))
except (KeyboardInterrupt, SystemExit):
raise
except Exception:
pyfalog.warning('Failed to plot "{}" vs "{}"'.format(source.name, '' if target is None else target.name))
self.canvas.draw()
@@ -241,6 +247,8 @@ class GraphCanvasPanel(wx.Panel):
src=source,
tgt=target)
addYMark(y)
except (KeyboardInterrupt, SystemExit):
raise
except Exception:
pyfalog.warning('Failed to get X mark for "{}" vs "{}"'.format(source.name, '' if target is None else target.name))
# Silently skip this mark, otherwise other marks and legend display will fail

View File

@@ -112,6 +112,8 @@ class ItemAffectedBy(wx.Panel):
else:
try:
self.affectedBy.CollapseAll()
except (KeyboardInterrupt, SystemExit):
raise
except:
pass
@@ -171,7 +173,7 @@ class ItemAffectedBy(wx.Panel):
def sortAttrDisplayName(self, attr):
info = self.stuff.item.attributes.get(attr)
if info and info.displayName != "":
if info and info.displayName:
return info.displayName
return attr
@@ -251,7 +253,7 @@ class ItemAffectedBy(wx.Panel):
for attrName in attrOrder:
attrInfo = self.stuff.item.attributes.get(attrName)
displayName = attrInfo.displayName if attrInfo and attrInfo.displayName != "" else attrName
displayName = attrInfo.displayName if attrInfo and attrInfo.displayName else attrName
if attrInfo:
if attrInfo.iconID is not None:
@@ -444,7 +446,7 @@ class ItemAffectedBy(wx.Panel):
attrModifier = "-"
attrAmount = -attrAmount
attributes.append((attrName, (displayName if displayName != "" else attrName), attrModifier,
attributes.append((attrName, (displayName if displayName else attrName), attrModifier,
attrAmount, penalized, attrIcon))
attrSorted = sorted(attributes, key=lambda attribName: attribName[0])
@@ -454,14 +456,14 @@ class ItemAffectedBy(wx.Panel):
if self.showRealNames:
display = "%s %s %.2f %s" % (attrName, attrModifier, attrAmount, penalized)
saved = "%s %s %.2f %s" % (
displayName if displayName != "" else attrName,
displayName if displayName else attrName,
attrModifier,
attrAmount,
penalized
)
else:
display = "%s %s %.2f %s" % (
displayName if displayName != "" else attrName,
displayName if displayName else attrName,
attrModifier,
attrAmount,
penalized

View File

@@ -86,6 +86,8 @@ class ItemProperties(wx.Panel):
valueUnit = str(value)
self.paramList.SetItem(index, 1, valueUnit)
except (KeyboardInterrupt, SystemExit):
raise
except:
# TODO: Add logging to this.
# We couldn't get a property for some reason. Skip it for now.

View File

@@ -48,7 +48,6 @@ class ItemView(Display):
self.Bind(wx.EVT_LIST_ITEM_ACTIVATED, self.itemActivated)
self.Bind(wx.EVT_LIST_BEGIN_DRAG, self.startDrag)
# Make reverse map, used by sorter
self.active = []
def delaySearch(self, evt):
@@ -178,7 +177,7 @@ class ItemView(Display):
return
self.marketBrowser.mode = 'search'
self.sMkt.searchItems(search, self.populateSearch)
self.sMkt.searchItems(search, self.populateSearch, 'market')
def clearSearch(self, event=None):
# Wipe item store and update everything to accomodate with it
@@ -201,7 +200,6 @@ class ItemView(Display):
self.setToggles()
self.filterItemStore()
def contextMenu(self, event):
clickedPos = self.getRowByAbs(event.Position)
self.ensureSelection(clickedPos)
@@ -235,11 +233,9 @@ class ItemView(Display):
# Re-sort stuff
if self.marketBrowser.mode != 'recent':
items.sort(key=self.sMkt.itemSort)
for i, item in enumerate(items[:9]):
# set shortcut info for first 9 modules
item.marketShortcut = i + 1
Display.refresh(self, items)
def columnBackground(self, colItem, item):

View File

@@ -63,6 +63,8 @@ class MarketTree(wx.TreeCtrl):
iconId = self.addImage(sMkt.getIconByMarketGroup(childMktGrp))
try:
childId = self.AppendItem(root, childMktGrp.name, iconId, data=childMktGrp.ID)
except (KeyboardInterrupt, SystemExit):
raise
except Exception as e:
pyfalog.debug("Error appending item.")
pyfalog.debug(e)

View File

@@ -66,6 +66,8 @@ class FitSpawner(gui.multiSwitch.TabSpawner):
self.multiSwitch.SetSelection(index)
wx.PostEvent(self.mainFrame, GE.FitChanged(fitIDs=(event.fitID,)))
break
except (KeyboardInterrupt, SystemExit):
raise
except Exception as e:
pyfalog.critical("Caught exception in fitSelected")
pyfalog.critical(e)
@@ -812,6 +814,8 @@ class FittingView(d.Display):
if self and not self.IsShown():
try:
self.MakeSnapshot()
except (KeyboardInterrupt, SystemExit):
raise
except Exception as e:
pyfalog.critical("Failed to make snapshot")
pyfalog.critical(e)
@@ -837,6 +841,8 @@ class FittingView(d.Display):
sFit = Fit.getInstance()
try:
fit = sFit.getFit(self.activeFitID)
except (KeyboardInterrupt, SystemExit):
raise
except Exception as e:
pyfalog.critical("Failed to get fit")
pyfalog.critical(e)

View File

@@ -309,7 +309,7 @@ class ItemView(d.Display):
self.clearSearch()
return
sMkt.searchItems(search, self.populateSearch, ["Implant"])
sMkt.searchItems(search, self.populateSearch, 'implants')
def populateSearch(self, items):
if not self.IsShown():

View File

@@ -475,6 +475,8 @@ class SkillTreeView(wx.Panel):
if skill:
skill.setLevel(level, ignoreRestrict=True)
except (KeyboardInterrupt, SystemExit):
raise
except Exception as e:
pyfalog.error(e)
with wx.MessageDialog(self, "There was an error importing skills, please see log file", "Error", wx.ICON_ERROR) as dlg:
@@ -839,7 +841,7 @@ class APIView(wx.Panel):
def getActiveCharacter(self):
selection = self.charChoice.GetCurrentSelection()
return self.charChoice.GetClientData(selection) if selection is not -1 else None
return self.charChoice.GetClientData(selection) if selection != -1 else None
def ssoListChanged(self, event):
if not self: # todo: fix event not unbinding properly

View File

@@ -122,7 +122,7 @@ class CharacterSelection(wx.Panel):
def getActiveCharacter(self):
selection = self.charChoice.GetCurrentSelection()
return self.charChoice.GetClientData(selection) if selection is not -1 else None
return self.charChoice.GetClientData(selection) if selection != -1 else None
def refreshCharacterList(self, event=None):
choice = self.charChoice
@@ -303,7 +303,6 @@ class CharacterSelection(wx.Panel):
if tabulationLevel == 0:
for item, subReqs in reqs.items():
skillsMap = self._buildSkillsTooltipCondensed(subReqs, item.name, 1, skillsMap)
sorted(skillsMap, key=skillsMap.get)
else:
for name, info in reqs.items():
level, ID, more = info
@@ -323,3 +322,25 @@ class CharacterSelection(wx.Panel):
skillsMap = self._buildSkillsTooltipCondensed(more, currItem, tabulationLevel + 1, skillsMap)
return skillsMap
def _buildSkillsTooltipSuperCondensed(self, reqs, currItem="", tabulationLevel=0, skillsMap=None):
allReqs = {}
implicitReqs = {}
def traverseReqs(itemReqs, topLevel=True):
for skillName, (skillLevel, skillTypeID, subReqs) in itemReqs.items():
if (skillTypeID, skillName) not in allReqs or allReqs[(skillTypeID, skillName)] < skillLevel:
allReqs[(skillTypeID, skillName)] = skillLevel
if not topLevel and (skillTypeID not in implicitReqs or implicitReqs[skillTypeID] < skillLevel):
implicitReqs[skillTypeID] = skillLevel
traverseReqs(subReqs, topLevel=False)
for item, itemReqs in reqs.items():
traverseReqs(itemReqs)
newReqs = {}
for (skillTypeID, skillName), skillLevel in allReqs.items():
if skillTypeID not in implicitReqs or implicitReqs[skillTypeID] < skillLevel:
newReqs[skillName] = skillLevel, skillTypeID
return newReqs

View File

@@ -1138,6 +1138,8 @@ class _TabsContainer(wx.Panel):
self.preview_tab = tab
self.preview_timer.Start(500, True)
break
except (KeyboardInterrupt, SystemExit):
raise
except:
pass

View File

@@ -125,6 +125,8 @@ class EveFittings(AuxiliaryFrame):
# Can't do this in a finally because then it obscures the message dialog
del waitDialog # noqa: F821
ESIExceptionHandler(self, ex)
except (KeyboardInterrupt, SystemExit):
raise
except Exception as ex:
del waitDialog # noqa: F821
raise ex
@@ -302,6 +304,8 @@ class ExportToEve(AuxiliaryFrame):
except APIException as ex:
try:
ESIExceptionHandler(self, ex)
except (KeyboardInterrupt, SystemExit):
raise
except Exception as ex:
self.statusbar.SetStatusText("ERROR", 0)
self.statusbar.SetStatusText("{} - {}".format(res.status_code, res.reason), 1)
@@ -381,6 +385,8 @@ class SsoCharacterMgmt(AuxiliaryFrame):
try:
sEsi = Esi.getInstance()
sEsi.login()
except (KeyboardInterrupt, SystemExit):
raise
except Exception as ex:
ESIServerExceptionHandler(self, ex)
@@ -457,6 +463,8 @@ class FittingsTreeView(wx.Panel):
cargo = Cargo(getItem(item['type_id']))
cargo.amount = item['quantity']
list.append(cargo)
except (KeyboardInterrupt, SystemExit):
raise
except Exception as e:
pyfalog.critical("Exception caught in displayFit")
pyfalog.critical(e)

View File

@@ -99,12 +99,14 @@ class PFPanel(wx.Panel):
class OpenFitsThread(threading.Thread):
def __init__(self, fits, callback):
threading.Thread.__init__(self)
self.name = "LoadingOpenFits"
self.mainFrame = MainFrame.getInstance()
self.callback = callback
self.fits = fits
self.running = True
self.start()
def run(self):
@@ -118,10 +120,15 @@ class OpenFitsThread(threading.Thread):
# We use 1 for all fits except the last one where we use 2 so that we
# have correct calculations displayed at startup
for fitID in self.fits[:-1]:
wx.PostEvent(self.mainFrame, FitSelected(fitID=fitID, startup=1))
if self.running:
wx.PostEvent(self.mainFrame, FitSelected(fitID=fitID, startup=1))
wx.PostEvent(self.mainFrame, FitSelected(fitID=self.fits[-1], startup=2))
wx.CallAfter(self.callback)
if self.running:
wx.PostEvent(self.mainFrame, FitSelected(fitID=self.fits[-1], startup=2))
wx.CallAfter(self.callback)
def stop(self):
self.running = False
# todo: include IPortUser again
@@ -251,10 +258,12 @@ class MainFrame(wx.Frame):
fit = sFit.getFit(id, basic=True)
if fit is None:
fits.remove(id)
except (KeyboardInterrupt, SystemExit):
raise
except:
fits.remove(id)
if not self.prevOpenFits['enabled'] or len(fits) is 0:
if not self.prevOpenFits['enabled'] or len(fits) == 0:
# add blank page if there are no fits to be loaded
self.fitMultiSwitch.AddPage()
return
@@ -747,9 +756,12 @@ class MainFrame(wx.Frame):
activeFit = self.getActiveFit()
try:
importType, importData = Port().importFitFromBuffer(clipboard, activeFit)
if importType == "MutatedItem":
# we've imported an Abyssal module, need to fire off the command to add it to the fit
self.command.Submit(cmd.GuiImportLocalMutatedModuleCommand(activeFit, *importData[0]))
if importType == "FittingItem":
baseItem, mutaplasmidItem, mutations = importData[0]
if mutaplasmidItem:
self.command.Submit(cmd.GuiImportLocalMutatedModuleCommand(activeFit, baseItem, mutaplasmidItem, mutations))
else:
self.command.Submit(cmd.GuiAddLocalModuleCommand(activeFit, baseItem.ID))
return
if importType == "AdditionsDrones":
if self.command.Submit(cmd.GuiImportLocalDronesCommand(activeFit, [(i.ID, a) for i, a in importData[0]])):
@@ -771,6 +783,8 @@ class MainFrame(wx.Frame):
if self.command.Submit(cmd.GuiImportCargosCommand(activeFit, [(i.ID, a) for i, a in importData[0]])):
self.additionsPane.select("Cargo")
return
except (KeyboardInterrupt, SystemExit):
raise
except:
pyfalog.error("Attempt to import failed:\n{0}", clipboard)
else:

View File

@@ -265,6 +265,8 @@ class DmgPatternEditor(AuxiliaryFrame):
except ImportError as e:
pyfalog.error(e)
self.stNotice.SetLabel(str(e))
except (KeyboardInterrupt, SystemExit):
raise
except Exception as e:
msg = "Could not import from clipboard: unknown errors"
pyfalog.warning(msg)

View File

@@ -168,10 +168,7 @@ class ItemView(d.Display):
def __init__(self, parent):
d.Display.__init__(self, parent)
sMkt = Market.getInstance()
self.things = sMkt.getItemsWithOverrides()
self.items = self.things
self.activeItems = []
self.searchBox = parent.Parent.Parent.searchBox
# Bind search actions
@@ -180,20 +177,16 @@ class ItemView(d.Display):
self.searchBox.Bind(SBox.EVT_CANCEL_BTN, self.clearSearch)
self.searchBox.Bind(SBox.EVT_TEXT, self.scheduleSearch)
self.update(self.items)
self.update(Market.getInstance().getItemsWithOverrides())
def clearSearch(self, event=None):
if event:
self.searchBox.Clear()
self.items = self.things
self.update(self.items)
self.update(Market.getInstance().getItemsWithOverrides())
def updateItems(self, updateDisplay=False):
sMkt = Market.getInstance()
self.things = sMkt.getItemsWithOverrides()
self.items = self.things
if updateDisplay:
self.update(self.things)
self.update(Market.getInstance().getItemsWithOverrides())
def scheduleSearch(self, event=None):
sMkt = Market.getInstance()
@@ -206,12 +199,40 @@ class ItemView(d.Display):
self.clearSearch()
return
sMkt.searchItems(search, self.populateSearch, False)
sMkt.searchItems(search, self.populateSearch, 'everything')
def itemSort(self, item):
sMkt = Market.getInstance()
isFittable = item.group.name in sMkt.FIT_GROUPS or item.category.name in sMkt.FIT_CATEGORIES
catname = sMkt.getCategoryByItem(item).name
try:
mktgrpid = sMkt.getMarketGroupByItem(item).ID
except AttributeError:
mktgrpid = -1
pyfalog.warning("unable to find market group for {}".format(item.name))
parentname = sMkt.getParentItemByItem(item).name
# Get position of market group
metagrpid = sMkt.getMetaGroupIdByItem(item)
metatab = sMkt.META_MAP_REVERSE_INDICES.get(metagrpid)
metalvl = item.metaLevel or 0
return not isFittable, catname, mktgrpid, parentname, metatab, metalvl, item.name
def populateSearch(self, items):
self.items = list(items)
self.update(items)
def populate(self, items):
if len(items) > 0:
self.unselectAll()
items.sort(key=self.itemSort)
self.activeItems = items
d.Display.populate(self, items)
def refresh(self, items):
if len(items) > 1:
items.sort(key=self.itemSort)
d.Display.refresh(self, items)
class AttributeGrid(wxpg.PropertyGrid):
def __init__(self, parent):
@@ -236,7 +257,7 @@ class AttributeGrid(wxpg.PropertyGrid):
self.Clear()
self.btn.Enable(True)
sel = event.EventObject.GetFirstSelected()
self.item = item = self.itemView.items[sel]
self.item = item = self.itemView.activeItems[sel]
for key in sorted(item.attributes.keys()):
override = item.overrides.get(key, None)

View File

@@ -211,6 +211,8 @@ class ImplantSetEditor(AuxiliaryFrame):
except ImportError as e:
pyfalog.error(e)
self.stNotice.SetLabel(str(e))
except (KeyboardInterrupt, SystemExit):
raise
except Exception as e:
pyfalog.error(e)
self.stNotice.SetLabel("Could not import from clipboard: unknown errors")

View File

@@ -352,6 +352,8 @@ class TargetProfileEditor(AuxiliaryFrame):
except ImportError as e:
pyfalog.error(e)
self.stNotice.SetLabel(str(e))
except (KeyboardInterrupt, SystemExit):
raise
except Exception as e:
msg = "Could not import from clipboard:"
pyfalog.warning(msg)

View File

@@ -70,6 +70,8 @@ class exportHtmlThread(threading.Thread):
except IOError as ex:
pyfalog.warning("Failed to write to " + settings.getPath())
pass
except (KeyboardInterrupt, SystemExit):
raise
except Exception as ex:
pass
@@ -226,6 +228,8 @@ class exportHtmlThread(threading.Thread):
HTMLfit += ' </ul>\n </li>\n'
HTMLship += HTMLfit
except (KeyboardInterrupt, SystemExit):
raise
except:
pyfalog.warning("Failed to export line")
continue
@@ -281,6 +285,8 @@ class exportHtmlThread(threading.Thread):
HTML += '<a class="outOfGameBrowserLink" target="_blank" href="' + dnaUrl + dnaFit + '">' \
+ ship.name + ': ' + \
fit[1] + '</a><br> \n'
except (KeyboardInterrupt, SystemExit):
raise
except:
pyfalog.error("Failed to export line")
continue

15
pyfa.py
View File

@@ -151,5 +151,18 @@ if __name__ == "__main__":
else:
pyfa.MainLoop()
# TODO: Add some thread cleanup code here. Right now we bail, and that can lead to orphaned threads or threads not properly exiting.
# When main loop is over, threads have 5 seconds to comply...
import threading
from utils.timer import CountdownTimer
timer = CountdownTimer(5)
stoppableThreads = []
for t in threading.enumerate():
if t is not threading.main_thread() and hasattr(t, 'stop'):
stoppableThreads.append(t)
t.stop()
for t in stoppableThreads:
t.join(timeout=timer.remainder())
# Nah, just kidding, no way to terminate threads - just try to exit
sys.exit()

View File

@@ -31,7 +31,6 @@ if os_name == 'Windows':
added_files.extend([
('dist_assets/win/pyfa.ico', '.'),
('dist_assets/win/pyfa.exe.manifest', '.'),
('dist_assets/win/Microsoft.VC90.CRT.manifest', '.')
])
icon = 'dist_assets/win/pyfa.ico'

View File

@@ -1,13 +1,12 @@
wxPython == 4.0.7.post2
logbook >= 1.0.0
matplotlib == 3.0.3
numpy==1.16.4
matplotlib >= 3.1.2
python-dateutil
requests >= 2.0.0
sqlalchemy >= 1.3.0
cryptography >= 2.3
markdown2 == 2.3.5
packaging == 16.8
roman == 2.0.0
beautifulsoup4 == 4.6.0
markdown2 >= 2.3.5
packaging >= 16.8
roman >= 2.0.0
beautifulsoup4 >= 4.6.0
pyyaml >= 5.1

View File

@@ -119,7 +119,7 @@ def calc_outerscore(innerscore_dict, pereffect_totalaffected, weight):
def validate_string(s):
try:
s.encode('ascii')
except KeyboardInterrupt:
except (KeyboardInterrupt, SystemExit):
raise
except Exception:
return False

View File

@@ -394,6 +394,8 @@ def main(old, new, groups=True, effects=True, attributes=True, renames=True):
new_cursor.execute(query)
for row in new_cursor:
new_meta[row[0]] = row[1]
except (KeyboardInterrupt, SystemExit):
raise
except:
pass
# Print jobs

View File

@@ -1,11 +1,14 @@
#!/usr/bin/env bash
echo "Pyfa version (ENV):"
echo "${PYFA_VERSION}"
echo "Pyfa version (YAML):"
cat version.yml
echo "Building distributive..."
python3 -m PyInstaller -y --clean --windowed dist_assets/mac/pyfa.spec
echo "Compressing distributive..."
cd dist
zip -r "pyfa-$PYFA_VERSION-mac.zip" pyfa.app
echo "Uploading distributive to transfer.sh..."
curl --connect-timeout 30 --max-time 300 --upload-file "pyfa-$PYFA_VERSION-mac.zip" https://transfer.sh/ || echo 'upload to transfer.sh failed'
echo -e "\n"
md5 -r "pyfa-$PYFA_VERSION-mac.zip"

View File

@@ -1,8 +1,9 @@
#!/usr/bin/env bash
wget "https://www.python.org/ftp/python/${PYTHON}/python-${PYTHON}-macosx10.6.pkg"
sudo installer -pkg python-${PYTHON}-macosx10.6.pkg -target /
sudo python3 -m ensurepip --upgrade
# A manual check that the correct version of Python is running.
echo "Python version:"
python3 --version
echo "Upgrading pip..."
python3 -m pip install --upgrade pip
echo "Installing app requirements..."
python3 -m pip install -r requirements.txt
python3 -m pip install PyInstaller
echo "Installing packaging tools..."
python3 -m pip install PyInstaller==3.6

File diff suppressed because it is too large Load Diff

View File

@@ -1,89 +0,0 @@
Electronic Subsystem Technology,Core Subsystem Technology
Amarr Electronic Systems,Amarr Core Systems
Gallente Electronic Systems,Gallente Core Systems
Caldari Electronic Systems,Caldari Core Systems
Minmatar Electronic Systems,Minmatar Core Systems
Datacore - Electronic Subsystems Engineering,Datacore - Core Subsystems Engineering
Malfunctioning Electromechanical Component,Malfunctioning Power Cores
Wrecked Electromechanical Component,Wrecked Power Cores
Intact Electromechanical Component,Intact Power Cores
Legion Electronics - Energy Parasitic Complex,Legion Core - Energy Parasitic Complex
Legion Electronics - Tactical Targeting Network,Legion Core - Dissolution Sequencer
Legion Electronics - Dissolution Sequencer,Legion Core - Dissolution Sequencer
Legion Electronics - Emergent Locus Analyzer,Legion Core - Dissolution Sequencer
Tengu Electronics - Obfuscation Manifold,Tengu Core - Obfuscation Manifold
Tengu Electronics - CPU Efficiency Gate,Tengu Core - Electronic Efficiency Gate
Tengu Electronics - Dissolution Sequencer,Tengu Core - Electronic Efficiency Gate
Tengu Electronics - Emergent Locus Analyzer,Tengu Core - Electronic Efficiency Gate
Proteus Electronics - Friction Extension Processor,Proteus Core - Friction Extension Processor
Proteus Electronics - CPU Efficiency Gate,Proteus Core - Electronic Efficiency Gate
Proteus Electronics - Dissolution Sequencer,Proteus Core - Electronic Efficiency Gate
Proteus Electronics - Emergent Locus Analyzer,Proteus Core - Electronic Efficiency Gate
Loki Electronics - Immobility Drivers,Loki Core - Immobility Drivers
Loki Electronics - Tactical Targeting Network,Loki Core - Dissolution Sequencer
Loki Electronics - Dissolution Sequencer,Loki Core - Dissolution Sequencer
Loki Electronics - Emergent Locus Analyzer,Loki Core - Dissolution Sequencer
Tengu Engineering - Power Core Multiplier,Tengu Core - Augmented Graviton Reactor
Tengu Engineering - Augmented Capacitor Reservoir,Tengu Core - Augmented Graviton Reactor
Tengu Engineering - Capacitor Regeneration Matrix,Tengu Core - Augmented Graviton Reactor
Tengu Engineering - Supplemental Coolant Injector,Tengu Core - Obfuscation Manifold
Proteus Engineering - Power Core Multiplier,Proteus Core - Augmented Fusion Reactor
Proteus Engineering - Augmented Capacitor Reservoir,Proteus Core - Augmented Fusion Reactor
Proteus Engineering - Capacitor Regeneration Matrix,Proteus Core - Augmented Fusion Reactor
Proteus Engineering - Supplemental Coolant Injector,Proteus Core - Friction Extension Processor
Loki Engineering - Power Core Multiplier,Loki Core - Augmented Nuclear Reactor
Loki Engineering - Augmented Capacitor Reservoir,Loki Core - Augmented Nuclear Reactor
Loki Engineering - Capacitor Regeneration Matrix,Loki Core - Augmented Nuclear Reactor
Loki Engineering - Supplemental Coolant Injector,Loki Core - Immobility Drivers
Legion Engineering - Power Core Multiplier,Legion Core - Augmented Antimatter Reactor
Legion Engineering - Augmented Capacitor Reservoir,Legion Core - Augmented Antimatter Reactor
Legion Engineering - Capacitor Regeneration Matrix,Legion Core - Augmented Antimatter Reactor
Legion Engineering - Supplemental Coolant Injector,Legion Core - Energy Parasitic Complex
Legion Propulsion - Chassis Optimization,Legion Propulsion - Intercalated Nanofibers
Legion Propulsion - Fuel Catalyst,Legion Propulsion - Wake Limiter
Legion Propulsion - Wake Limiter,Legion Propulsion - Wake Limiter
Legion Propulsion - Interdiction Nullifier,Legion Propulsion - Interdiction Nullifier
Tengu Propulsion - Intercalated Nanofibers,Tengu Propulsion - Chassis Optimization
Tengu Propulsion - Gravitational Capacitor,Tengu Propulsion - Chassis Optimization
Tengu Propulsion - Fuel Catalyst,Tengu Propulsion - Fuel Catalyst
Tengu Propulsion - Interdiction Nullifier,Tengu Propulsion - Interdiction Nullifier
Proteus Propulsion - Wake Limiter,Proteus Propulsion - Localized Injectors
Proteus Propulsion - Localized Injectors,Proteus Propulsion - Localized Injectors
Proteus Propulsion - Gravitational Capacitor,Proteus Propulsion - Hyperspatial Optimization
Proteus Propulsion - Interdiction Nullifier,Proteus Propulsion - Interdiction Nullifier
Loki Propulsion - Chassis Optimization,Loki Propulsion - Intercalated Nanofibers
Loki Propulsion - Intercalated Nanofibers,Loki Propulsion - Intercalated Nanofibers
Loki Propulsion - Fuel Catalyst,Loki Propulsion - Wake Limiter
Loki Propulsion - Interdiction Nullifier,Loki Propulsion - Interdiction Nullifier
Legion Defensive - Adaptive Augmenter,Legion Defensive - Augmented Plating
Legion Defensive - Nanobot Injector,Legion Defensive - Nanobot Injector
Legion Defensive - Augmented Plating,Legion Defensive - Augmented Plating
Legion Defensive - Warfare Processor,Legion Defensive - Augmented Plating
Tengu Defensive - Adaptive Shielding,Tengu Defensive - Supplemental Screening
Tengu Defensive - Amplification Node,Tengu Defensive - Amplification Node
Tengu Defensive - Supplemental Screening,Tengu Defensive - Supplemental Screening
Tengu Defensive - Warfare Processor,Tengu Defensive - Supplemental Screening
Loki Defensive - Adaptive Shielding,Loki Defensive - Augmented Durability
Loki Defensive - Adaptive Augmenter,Loki Defensive - Augmented Durability
Loki Defensive - Amplification Node,Loki Defensive - Adaptive Defense Node
Loki Defensive - Warfare Processor,Loki Defensive - Augmented Durability
Proteus Defensive - Adaptive Augmenter,Proteus Defensive - Augmented Plating
Proteus Defensive - Nanobot Injector,Proteus Defensive - Nanobot Injector
Proteus Defensive - Augmented Plating,Proteus Defensive - Augmented Plating
Proteus Defensive - Warfare Processor,Proteus Defensive - Augmented Plating
Legion Offensive - Drone Synthesis Projector,Legion Offensive - Assault Optimization
Legion Offensive - Assault Optimization,Legion Offensive - Assault Optimization
Legion Offensive - Liquid Crystal Magnifiers,Legion Offensive - Liquid Crystal Magnifiers
Legion Offensive - Covert Reconfiguration,Legion Offensive - Liquid Crystal Magnifiers
Tengu Offensive - Accelerated Ejection Bay,Tengu Offensive - Accelerated Ejection Bay
Tengu Offensive - Rifling Launcher Pattern,Tengu Offensive - Accelerated Ejection Bay
Tengu Offensive - Magnetic Infusion Basin,Tengu Offensive - Magnetic Infusion Basin
Tengu Offensive - Covert Reconfiguration,Tengu Offensive - Accelerated Ejection Bay
Proteus Offensive - Dissonic Encoding Platform,Proteus Offensive - Hybrid Encoding Platform
Proteus Offensive - Hybrid Propulsion Armature,Proteus Offensive - Hybrid Encoding Platform
Proteus Offensive - Drone Synthesis Projector,Proteus Offensive - Drone Synthesis Projector
Proteus Offensive - Covert Reconfiguration,Proteus Offensive - Hybrid Encoding Platform
Loki Offensive - Turret Concurrence Registry,Loki Offensive - Projectile Scoping Array
Loki Offensive - Projectile Scoping Array,Loki Offensive - Projectile Scoping Array
Loki Offensive - Hardpoint Efficiency Configuration,Loki Offensive - Launcher Efficiency Configuration
Loki Offensive - Covert Reconfiguration,Loki Offensive - Projectile Scoping Array
1 Electronic Subsystem Technology Core Subsystem Technology
2 Amarr Electronic Systems Amarr Core Systems
3 Gallente Electronic Systems Gallente Core Systems
4 Caldari Electronic Systems Caldari Core Systems
5 Minmatar Electronic Systems Minmatar Core Systems
6 Datacore - Electronic Subsystems Engineering Datacore - Core Subsystems Engineering
7 Malfunctioning Electromechanical Component Malfunctioning Power Cores
8 Wrecked Electromechanical Component Wrecked Power Cores
9 Intact Electromechanical Component Intact Power Cores
10 Legion Electronics - Energy Parasitic Complex Legion Core - Energy Parasitic Complex
11 Legion Electronics - Tactical Targeting Network Legion Core - Dissolution Sequencer
12 Legion Electronics - Dissolution Sequencer Legion Core - Dissolution Sequencer
13 Legion Electronics - Emergent Locus Analyzer Legion Core - Dissolution Sequencer
14 Tengu Electronics - Obfuscation Manifold Tengu Core - Obfuscation Manifold
15 Tengu Electronics - CPU Efficiency Gate Tengu Core - Electronic Efficiency Gate
16 Tengu Electronics - Dissolution Sequencer Tengu Core - Electronic Efficiency Gate
17 Tengu Electronics - Emergent Locus Analyzer Tengu Core - Electronic Efficiency Gate
18 Proteus Electronics - Friction Extension Processor Proteus Core - Friction Extension Processor
19 Proteus Electronics - CPU Efficiency Gate Proteus Core - Electronic Efficiency Gate
20 Proteus Electronics - Dissolution Sequencer Proteus Core - Electronic Efficiency Gate
21 Proteus Electronics - Emergent Locus Analyzer Proteus Core - Electronic Efficiency Gate
22 Loki Electronics - Immobility Drivers Loki Core - Immobility Drivers
23 Loki Electronics - Tactical Targeting Network Loki Core - Dissolution Sequencer
24 Loki Electronics - Dissolution Sequencer Loki Core - Dissolution Sequencer
25 Loki Electronics - Emergent Locus Analyzer Loki Core - Dissolution Sequencer
26 Tengu Engineering - Power Core Multiplier Tengu Core - Augmented Graviton Reactor
27 Tengu Engineering - Augmented Capacitor Reservoir Tengu Core - Augmented Graviton Reactor
28 Tengu Engineering - Capacitor Regeneration Matrix Tengu Core - Augmented Graviton Reactor
29 Tengu Engineering - Supplemental Coolant Injector Tengu Core - Obfuscation Manifold
30 Proteus Engineering - Power Core Multiplier Proteus Core - Augmented Fusion Reactor
31 Proteus Engineering - Augmented Capacitor Reservoir Proteus Core - Augmented Fusion Reactor
32 Proteus Engineering - Capacitor Regeneration Matrix Proteus Core - Augmented Fusion Reactor
33 Proteus Engineering - Supplemental Coolant Injector Proteus Core - Friction Extension Processor
34 Loki Engineering - Power Core Multiplier Loki Core - Augmented Nuclear Reactor
35 Loki Engineering - Augmented Capacitor Reservoir Loki Core - Augmented Nuclear Reactor
36 Loki Engineering - Capacitor Regeneration Matrix Loki Core - Augmented Nuclear Reactor
37 Loki Engineering - Supplemental Coolant Injector Loki Core - Immobility Drivers
38 Legion Engineering - Power Core Multiplier Legion Core - Augmented Antimatter Reactor
39 Legion Engineering - Augmented Capacitor Reservoir Legion Core - Augmented Antimatter Reactor
40 Legion Engineering - Capacitor Regeneration Matrix Legion Core - Augmented Antimatter Reactor
41 Legion Engineering - Supplemental Coolant Injector Legion Core - Energy Parasitic Complex
42 Legion Propulsion - Chassis Optimization Legion Propulsion - Intercalated Nanofibers
43 Legion Propulsion - Fuel Catalyst Legion Propulsion - Wake Limiter
44 Legion Propulsion - Wake Limiter Legion Propulsion - Wake Limiter
45 Legion Propulsion - Interdiction Nullifier Legion Propulsion - Interdiction Nullifier
46 Tengu Propulsion - Intercalated Nanofibers Tengu Propulsion - Chassis Optimization
47 Tengu Propulsion - Gravitational Capacitor Tengu Propulsion - Chassis Optimization
48 Tengu Propulsion - Fuel Catalyst Tengu Propulsion - Fuel Catalyst
49 Tengu Propulsion - Interdiction Nullifier Tengu Propulsion - Interdiction Nullifier
50 Proteus Propulsion - Wake Limiter Proteus Propulsion - Localized Injectors
51 Proteus Propulsion - Localized Injectors Proteus Propulsion - Localized Injectors
52 Proteus Propulsion - Gravitational Capacitor Proteus Propulsion - Hyperspatial Optimization
53 Proteus Propulsion - Interdiction Nullifier Proteus Propulsion - Interdiction Nullifier
54 Loki Propulsion - Chassis Optimization Loki Propulsion - Intercalated Nanofibers
55 Loki Propulsion - Intercalated Nanofibers Loki Propulsion - Intercalated Nanofibers
56 Loki Propulsion - Fuel Catalyst Loki Propulsion - Wake Limiter
57 Loki Propulsion - Interdiction Nullifier Loki Propulsion - Interdiction Nullifier
58 Legion Defensive - Adaptive Augmenter Legion Defensive - Augmented Plating
59 Legion Defensive - Nanobot Injector Legion Defensive - Nanobot Injector
60 Legion Defensive - Augmented Plating Legion Defensive - Augmented Plating
61 Legion Defensive - Warfare Processor Legion Defensive - Augmented Plating
62 Tengu Defensive - Adaptive Shielding Tengu Defensive - Supplemental Screening
63 Tengu Defensive - Amplification Node Tengu Defensive - Amplification Node
64 Tengu Defensive - Supplemental Screening Tengu Defensive - Supplemental Screening
65 Tengu Defensive - Warfare Processor Tengu Defensive - Supplemental Screening
66 Loki Defensive - Adaptive Shielding Loki Defensive - Augmented Durability
67 Loki Defensive - Adaptive Augmenter Loki Defensive - Augmented Durability
68 Loki Defensive - Amplification Node Loki Defensive - Adaptive Defense Node
69 Loki Defensive - Warfare Processor Loki Defensive - Augmented Durability
70 Proteus Defensive - Adaptive Augmenter Proteus Defensive - Augmented Plating
71 Proteus Defensive - Nanobot Injector Proteus Defensive - Nanobot Injector
72 Proteus Defensive - Augmented Plating Proteus Defensive - Augmented Plating
73 Proteus Defensive - Warfare Processor Proteus Defensive - Augmented Plating
74 Legion Offensive - Drone Synthesis Projector Legion Offensive - Assault Optimization
75 Legion Offensive - Assault Optimization Legion Offensive - Assault Optimization
76 Legion Offensive - Liquid Crystal Magnifiers Legion Offensive - Liquid Crystal Magnifiers
77 Legion Offensive - Covert Reconfiguration Legion Offensive - Liquid Crystal Magnifiers
78 Tengu Offensive - Accelerated Ejection Bay Tengu Offensive - Accelerated Ejection Bay
79 Tengu Offensive - Rifling Launcher Pattern Tengu Offensive - Accelerated Ejection Bay
80 Tengu Offensive - Magnetic Infusion Basin Tengu Offensive - Magnetic Infusion Basin
81 Tengu Offensive - Covert Reconfiguration Tengu Offensive - Accelerated Ejection Bay
82 Proteus Offensive - Dissonic Encoding Platform Proteus Offensive - Hybrid Encoding Platform
83 Proteus Offensive - Hybrid Propulsion Armature Proteus Offensive - Hybrid Encoding Platform
84 Proteus Offensive - Drone Synthesis Projector Proteus Offensive - Drone Synthesis Projector
85 Proteus Offensive - Covert Reconfiguration Proteus Offensive - Hybrid Encoding Platform
86 Loki Offensive - Turret Concurrence Registry Loki Offensive - Projectile Scoping Array
87 Loki Offensive - Projectile Scoping Array Loki Offensive - Projectile Scoping Array
88 Loki Offensive - Hardpoint Efficiency Configuration Loki Offensive - Launcher Efficiency Configuration
89 Loki Offensive - Covert Reconfiguration Loki Offensive - Projectile Scoping Array

View File

@@ -1,48 +0,0 @@
# Prints out a fucking large python dictionary for use with the t3c conversion migration.\
# Requires eve-old.db file (which is the previous releases database so that we can lookup the old items)
# See https://community.eveonline.com/news/patch-notes/patch-notes-for-july-2017-release
import csv
import sys
from os.path import abspath, dirname, join, realpath
from sqlalchemy import create_engine
newDB = create_engine('sqlite:///' + unicode(realpath(join(dirname(abspath(__file__)), "..", "eve.db")), sys.getfilesystemencoding()))
oldDB = create_engine('sqlite:///' + unicode(realpath(join(dirname(abspath(__file__)), "..", "eve-old.db")), sys.getfilesystemencoding()))
oldItemMapping = {}
newItemMapping = {}
with open('t3conversionSheet.csv', 'r') as f:
reader = csv.reader(f)
print "conversion = {"
for row in reader:
fromList = []
toList = []
for x in xrange(1, 6):
try:
if (row[0], row[x]) not in oldItemMapping:
item = oldDB.execute("SELECT * FROM invtypes WHERE typeName LIKE ?", ("{}%{}".format(row[0], row[x]),)).first()
oldItemMapping[(row[0], row[x])] = item['typeID']
fromList.append(str(oldItemMapping[(row[0], row[x])]))
except:
pass
for x in xrange(6, 10):
if row[x] not in newItemMapping:
item = newDB.execute("SELECT * FROM invtypes WHERE typeName = ?",
(row[x],)).first()
newItemMapping[row[x]] = item['typeID']
toList.append(str(newItemMapping[row[x]]))
print "\tfrozenset([{}]): ({}),".format(','.join(fromList), ','.join(toList))
print "}"
with open('t3conversionSheetLoose.csv', 'r') as f:
reader = csv.reader(f)
print "conversion2 = {"
for row in reader:
oldItem = oldDB.execute("SELECT typeID FROM invtypes WHERE typeName = ?", (row[0],)).scalar()
newItem = newDB.execute("SELECT typeID FROM invtypes WHERE typeName = ?", (row[1],)).scalar()
print "\t{}: {},".format(oldItem, newItem)
print "}"

Binary file not shown.

View File

@@ -45,11 +45,13 @@ pyfalog = Logger(__name__)
class CharacterImportThread(threading.Thread):
def __init__(self, paths, callback):
threading.Thread.__init__(self)
self.name = "CharacterImport"
self.paths = paths
self.callback = callback
self.running = True
def run(self):
paths = self.paths
@@ -61,6 +63,8 @@ class CharacterImportThread(threading.Thread):
all_skill_ids.append(skill.itemID)
for path in paths:
if not self.running:
break
try:
charFile = open(path, mode='r').read()
doc = minidom.parseString(charFile)
@@ -86,6 +90,8 @@ class CharacterImportThread(threading.Thread):
)
char = sCharacter.new(name + " (EVEMon)")
sCharacter.apiUpdateCharSheet(char.ID, skills, securitystatus)
except (KeyboardInterrupt, SystemExit):
raise
except Exception as e:
pyfalog.error("Exception on character import:")
pyfalog.error(e)
@@ -93,6 +99,9 @@ class CharacterImportThread(threading.Thread):
wx.CallAfter(self.callback)
def stop(self):
self.running = False
class SkillBackupThread(threading.Thread):
def __init__(self, path, saveFmt, activeFit, callback):
@@ -102,25 +111,32 @@ class SkillBackupThread(threading.Thread):
self.saveFmt = saveFmt
self.activeFit = activeFit
self.callback = callback
self.running = True
def run(self):
path = self.path
sCharacter = Character.getInstance()
if self.saveFmt == "xml" or self.saveFmt == "emp":
backupData = sCharacter.exportXml()
else:
backupData = sCharacter.exportText()
backupData = None
if self.running:
if self.saveFmt == "xml" or self.saveFmt == "emp":
backupData = sCharacter.exportXml()
else:
backupData = sCharacter.exportText()
if self.saveFmt == "emp":
with gzip.open(path, mode='wb') as backupFile:
backupFile.write(backupData.encode())
else:
with open(path, mode='w', encoding='utf-8') as backupFile:
backupFile.write(backupData)
if self.running and backupData is not None:
if self.saveFmt == "emp":
with gzip.open(path, mode='wb') as backupFile:
backupFile.write(backupData.encode())
else:
with open(path, mode='w', encoding='utf-8') as backupFile:
backupFile.write(backupData)
wx.CallAfter(self.callback)
def stop(self):
self.running = False
class Character:
instance = None
@@ -151,6 +167,8 @@ class Character:
data += "Skills required for {}:\n".format(item)
data += "{}{}: {}\n".format(" " * s["indent"], s["skill"], int(s["level"]))
data += "-" * 79 + "\n"
except (KeyboardInterrupt, SystemExit):
raise
except Exception:
pass
@@ -376,8 +394,8 @@ class Character:
char.apiUpdateCharSheet(skills, securitystatus)
eos.db.commit()
@staticmethod
def changeLevel(charID, skillID, level, persist=False, ifHigher=False):
@classmethod
def changeLevel(cls, charID, skillID, level, persist=False, ifHigher=False):
char = eos.db.getCharacter(charID)
skill = char.getSkill(skillID)
@@ -386,10 +404,19 @@ class Character:
if isinstance(level, str) or level > 5 or level < 0:
skill.setLevel(None, persist)
else:
eos.db.commit()
elif skill.level != level:
cls._trainSkillReqs(char, skill, persist)
skill.setLevel(level, persist)
eos.db.commit()
eos.db.commit()
@classmethod
def _trainSkillReqs(cls, char, skill, persist):
for childSkillItem, neededSkillLevel in skill.item.requiredSkills.items():
childSkill = char.getSkill(childSkillItem.ID)
if childSkill.level < neededSkillLevel:
childSkill.setLevel(neededSkillLevel, persist)
cls._trainSkillReqs(char, childSkill, persist)
@staticmethod
def revertLevel(charID, skillID):
@@ -442,13 +469,13 @@ class Character:
if subThing is not None:
if isinstance(thing, es_Fighter) and attr == "charge":
continue
self._checkRequirements(fit, fit.character, subThing, subReqs)
self._checkRequirements(fit.character, subThing, subReqs)
if subReqs:
reqs[subThing] = subReqs
return reqs
def _checkRequirements(self, fit, char, subThing, reqs):
def _checkRequirements(self, char, subThing, reqs):
for req, level in subThing.requiredSkills.items():
name = req.name
ID = req.ID
@@ -456,18 +483,19 @@ class Character:
currLevel, subs = info if info is not None else 0, {}
if level > currLevel and (char is None or char.getSkill(req).level < level):
reqs[name] = (level, ID, subs)
self._checkRequirements(fit, char, req, subs)
self._checkRequirements(char, req, subs)
return reqs
class UpdateAPIThread(threading.Thread):
def __init__(self, charID, callback):
threading.Thread.__init__(self)
self.name = "CheckUpdate"
self.callback = callback
self.charID = charID
self.running = True
def run(self):
try:
@@ -476,18 +504,31 @@ class UpdateAPIThread(threading.Thread):
sEsi = Esi.getInstance()
sChar = Character.getInstance()
ssoChar = sChar.getSsoCharacter(char.ID)
if not self.running:
self.callback[0](self.callback[1])
return
resp = sEsi.getSkills(ssoChar.ID)
if not self.running:
self.callback[0](self.callback[1])
return
# todo: check if alpha. if so, pop up a question if they want to apply it as alpha. Use threading events to set the answer?
char.clearSkills()
for skillRow in resp["skills"]:
char.addSkill(Skill(char, skillRow["skill_id"], skillRow["trained_skill_level"]))
if not self.running:
self.callback[0](self.callback[1])
return
resp = sEsi.getSecStatus(ssoChar.ID)
char.secStatus = resp['security_status']
self.callback[0](self.callback[1])
except (KeyboardInterrupt, SystemExit):
raise
except Exception as ex:
pyfalog.warn(ex)
self.callback[0](self.callback[1], sys.exc_info())
def stop(self):
self.running = False

View File

@@ -1,5 +1,5 @@
"""
Conversion pack for Januray 2020 release
Conversion pack for January 2020 release
"""
CONVERSIONS = {
@@ -10,5 +10,29 @@ CONVERSIONS = {
'Large Clarity Ward Booster I': 'Large Clarity Ward Enduring Shield Booster',
'Large C5-L Emergency Shield Overload I': 'Large C5-L Compact Shield Booster',
'X-Large Clarity Ward Booster I': 'X-Large Clarity Ward Enduring Shield Booster',
'X-Large C5-L Emergency Shield Overload I': 'X-Large C5-L Compact Shield Booster'
'X-Large C5-L Emergency Shield Overload I': 'X-Large C5-L Compact Shield Booster',
'Small I-a Polarized Armor Regenerator': 'Small I-a Enduring Armor Repairer',
'Small \'Accommodation\' Vestment Reconstructer I': 'Small ACM Compact Armor Repairer',
'Medium I-a Polarized Armor Regenerator': 'Medium I-a Enduring Armor Repairer',
'Medium \'Accommodation\' Vestment Reconstructer I': 'Medium ACM Compact Armor Repairer',
'Large I-a Polarized Armor Regenerator': 'Large I-a Enduring Armor Repairer',
'Large \'Accommodation\' Vestment Reconstructer I': 'Large ACM Compact Armor Repairer',
# Entries below were removed from DB, but CCP converted them to another item type
'Small Converse Deflection Catalyzer': 'Small Clarity Ward Enduring Shield Booster',
'Small Neutron Saturation Injector I': 'Small C5-L Compact Shield Booster',
'Medium Converse Deflection Catalyzer': 'Medium Clarity Ward Enduring Shield Booster',
'Medium Neutron Saturation Injector I': 'Medium C5-L Compact Shield Booster',
'Large Converse Deflection Catalyzer': 'Large Clarity Ward Enduring Shield Booster',
'Large Neutron Saturation Injector I': 'Large C5-L Compact Shield Booster',
'X-Large Converse Deflection Catalyzer': 'X-Large Clarity Ward Enduring Shield Booster',
'X-Large Neutron Saturation Injector I': 'X-Large C5-L Compact Shield Booster',
'Small Inefficient Armor Repair Unit': 'Small ACM Compact Armor Repairer',
'Small Automated Carapace Restoration': 'Small I-a Enduring Armor Repairer',
'Medium Inefficient Armor Repair Unit': 'Medium ACM Compact Armor Repairer',
'Medium Automated Carapace Restoration': 'Medium I-a Enduring Armor Repairer',
'Medium Nano Armor Repair Unit I': '\'Meditation\' Medium Armor Repairer I',
'Large Inefficient Armor Repair Unit': 'Large ACM Compact Armor Repairer',
'Large Automated Carapace Restoration': 'Large I-a Enduring Armor Repairer',
# This item was not mentioned, assuming conversion to storyline
'Large \'Reprieve\' Vestment Reconstructer I': '\'Protest\' Large Armor Repairer I'
}

View File

@@ -110,6 +110,8 @@ class ImplantSets:
else:
item = sMkt.getItem(line)
current.implants.append(es_Implant(item))
except (KeyboardInterrupt, SystemExit):
raise
except:
errors += 1
continue

View File

@@ -46,6 +46,7 @@ class ShipBrowserWorkerThread(threading.Thread):
threading.Thread.__init__(self)
pyfalog.debug("Initialize ShipBrowserWorkerThread.")
self.name = "ShipBrowser"
self.running = True
def run(self):
self.queue = queue.Queue()
@@ -60,6 +61,8 @@ class ShipBrowserWorkerThread(threading.Thread):
cache = self.cache
sMkt = Market.getInstance()
while True:
if not self.running:
break
try:
id_, callback = queue.get()
set_ = cache.get(id_)
@@ -68,16 +71,23 @@ class ShipBrowserWorkerThread(threading.Thread):
cache[id_] = set_
wx.CallAfter(callback, (id_, set_))
except (KeyboardInterrupt, SystemExit):
raise
except Exception as e:
pyfalog.critical("Callback failed.")
pyfalog.critical(e)
finally:
try:
queue.task_done()
except (KeyboardInterrupt, SystemExit):
raise
except Exception as e:
pyfalog.critical("Queue task done failed.")
pyfalog.critical(e)
def stop(self):
self.running = False
class SearchWorkerThread(threading.Thread):
def __init__(self):
@@ -87,6 +97,7 @@ class SearchWorkerThread(threading.Thread):
# load the jargon while in an out-of-thread context, to spot any problems while in the main thread
self.jargonLoader.get_jargon()
self.jargonLoader.get_jargon().apply('test string')
self.running = True
def run(self):
self.cv = threading.Condition()
@@ -97,49 +108,70 @@ class SearchWorkerThread(threading.Thread):
cv = self.cv
while True:
if not self.running:
break
cv.acquire()
while self.searchRequest is None:
cv.wait()
request, callback, filterOn = self.searchRequest
request, callback, filterName = self.searchRequest
self.searchRequest = None
cv.release()
sMkt = Market.getInstance()
if filterOn is True:
if filterName == 'market':
# Rely on category data provided by eos as we don't hardcode them much in service
filter_ = or_(types_Category.name.in_(sMkt.SEARCH_CATEGORIES), types_Group.name.in_(sMkt.SEARCH_GROUPS))
elif filterOn: # filter by selected categories
filter_ = types_Category.name.in_(filterOn)
filters = [or_(
types_Category.name.in_(sMkt.SEARCH_CATEGORIES),
types_Group.name.in_(sMkt.SEARCH_GROUPS))]
# Used in implant editor
elif filterName == 'implants':
filters = [types_Category.name == 'Implant']
# Actually not everything, just market search + ships
elif filterName == 'everything':
filters = [
or_(
types_Category.name.in_(sMkt.FIT_CATEGORIES),
types_Group.name.in_(sMkt.FIT_GROUPS)),
or_(
types_Category.name.in_(sMkt.SEARCH_CATEGORIES),
types_Group.name.in_(sMkt.SEARCH_GROUPS))]
else:
filter_ = None
filters = [None]
jargon_request = self.jargonLoader.get_jargon().apply(request)
results = []
all_results = set()
if len(request) >= config.minItemSearchLength:
results = eos.db.searchItems(request, where=filter_,
join=(types_Item.group, types_Group.category),
eager=("group.category", "metaGroup"))
for filter_ in filters:
regular_results = eos.db.searchItems(
request, where=filter_,
join=(types_Item.group, types_Group.category),
eager=("group.category", "metaGroup"))
all_results.update(regular_results)
jargon_results = []
if len(jargon_request) >= config.minItemSearchLength:
jargon_results = eos.db.searchItems(jargon_request, where=filter_,
join=(types_Item.group, types_Group.category),
eager=("group.category", "metaGroup"))
for filter_ in filters:
jargon_results = eos.db.searchItems(
jargon_request, where=filter_,
join=(types_Item.group, types_Group.category),
eager=("group.category", "metaGroup"))
all_results.update(jargon_results)
items = set()
# Return only published items, consult with Market service this time
for item in [*results, *jargon_results]:
for item in all_results:
if sMkt.getPublicityByItem(item):
items.add(item)
wx.CallAfter(callback, items)
wx.CallAfter(callback, list(items))
def scheduleSearch(self, text, callback, filterOn=True):
def scheduleSearch(self, text, callback, filterName=None):
self.cv.acquire()
self.searchRequest = (text, callback, filterOn)
self.searchRequest = (text, callback, filterName)
self.cv.notify()
self.cv.release()
def stop(self):
self.running = False
class Market:
instance = None
@@ -264,8 +296,6 @@ class Market:
self.ITEMS_FORCEDMETAGROUP = {
"'Habitat' Miner I": ("Storyline", "Miner I"),
"'Wild' Miner I": ("Storyline", "Miner I"),
"Medium Nano Armor Repair Unit I": ("Tech I", "Medium Armor Repairer I"),
"Large 'Reprieve' Vestment Reconstructer I": ("Storyline", "Large Armor Repairer I"),
"Khanid Navy Torpedo Launcher": ("Faction", "Torpedo Launcher I"),
"Dark Blood Tracking Disruptor": ("Faction", "Tracking Disruptor I"),
"Dread Guristas Standup Variable Spectrum ECM": ("Structure Faction", "Standup Variable Spectrum ECM I"),
@@ -321,6 +351,7 @@ class Market:
for mgid in mgids:
self.META_MAP_REVERSE_GROUPED[mgid] = i
i += 1
self.META_MAP_REVERSE_INDICES = self.__makeReverseMetaMapIndices()
self.SEARCH_CATEGORIES = (
"Drone",
"Module",
@@ -344,6 +375,8 @@ class Market:
2203 # Structure Modifications
)
self.SHOWN_MARKET_GROUPS = eos.db.getMarketTreeNodeIds(self.ROOT_MARKET_GROUPS)
self.FIT_CATEGORIES = ['Ship']
self.FIT_GROUPS = ['Citadel', 'Engineering Complex', 'Refinery']
# Tell other threads that Market is at their service
mktRdy.set()
@@ -363,6 +396,15 @@ class Market:
rev[value].add(item)
return rev
def __makeReverseMetaMapIndices(self):
revmap = {}
i = 0
for mgids in self.META_MAP.values():
for mgid in mgids:
revmap[mgid] = i
i += 1
return revmap
@staticmethod
def getItem(identity, *args, **kwargs):
"""Get item by its ID or name"""
@@ -382,6 +424,8 @@ class Market:
item = eos.db.getItem(id_, *args, **kwargs)
else:
raise TypeError("Need Item object, integer, float or string as argument")
except (KeyboardInterrupt, SystemExit):
raise
except:
pyfalog.error("Could not get item: {0}", identity)
raise
@@ -756,9 +800,9 @@ class Market:
ships.add(item)
return ships
def searchItems(self, name, callback, filterOn=True):
def searchItems(self, name, callback, filterName=None):
"""Find items according to given text pattern"""
self.searchWorkerThread.scheduleSearch(name, callback, filterOn)
self.searchWorkerThread.scheduleSearch(name, callback, filterName)
@staticmethod
def getItemsWithOverrides():

View File

@@ -92,6 +92,8 @@ class Network:
raise Error(error)
except requests.exceptions.Timeout:
raise TimeoutError()
except (KeyboardInterrupt, SystemExit):
raise
except Exception as error:
raise Error(error)
@@ -117,6 +119,8 @@ class Network:
raise Error(error)
except requests.exceptions.Timeout:
raise TimeoutError()
except (KeyboardInterrupt, SystemExit):
raise
except Exception as error:
raise Error(error)

View File

@@ -59,6 +59,8 @@ def importDna(string, fitName=None):
Citadel(sMkt.getItem(id_))
string = string[string.index(str(id_)):]
break
except (KeyboardInterrupt, SystemExit):
raise
except:
pyfalog.warning("Exception caught in importDna")
pass
@@ -107,6 +109,8 @@ def importDna(string, fitName=None):
for i in range(int(amount)):
try:
m = Module(item)
except (KeyboardInterrupt, SystemExit):
raise
except:
pyfalog.warning("Exception caught in importDna")
continue

View File

@@ -269,6 +269,8 @@ class EfsPort:
else:
modTypeIDSets[mod.slot].append(0)
moduleNameSets[mod.slot].append("Empty Slot")
except (KeyboardInterrupt, SystemExit):
raise
except:
pyfalog.error("Could not find name for module {0}".format(vars(mod)))

View File

@@ -321,6 +321,8 @@ def importEftCfg(shipname, lines, iportuser):
sMkt = Market.getInstance()
try:
sMkt.getItem(shipname)
except (KeyboardInterrupt, SystemExit):
raise
except:
return [] # empty list is expected
@@ -377,6 +379,8 @@ def importEftCfg(shipname, lines, iportuser):
# Bail if we can't get item or it's not from drone category
try:
droneItem = sMkt.getItem(droneName, eager="group.category")
except (KeyboardInterrupt, SystemExit):
raise
except:
pyfalog.warning("Cannot get item.")
continue
@@ -399,6 +403,8 @@ def importEftCfg(shipname, lines, iportuser):
# Bail if we can't get item or it's not from implant category
try:
implantItem = sMkt.getItem(entityData, eager="group.category")
except (KeyboardInterrupt, SystemExit):
raise
except:
pyfalog.warning("Cannot get item.")
continue
@@ -415,6 +421,8 @@ def importEftCfg(shipname, lines, iportuser):
# Bail if we can't get item or it's not from implant category
try:
boosterItem = sMkt.getItem(entityData, eager="group.category")
except (KeyboardInterrupt, SystemExit):
raise
except:
pyfalog.warning("Cannot get item.")
continue
@@ -436,6 +444,8 @@ def importEftCfg(shipname, lines, iportuser):
# Bail if we can't get item
try:
item = sMkt.getItem(cargoName)
except (KeyboardInterrupt, SystemExit):
raise
except:
pyfalog.warning("Cannot get item.")
continue
@@ -453,6 +463,8 @@ def importEftCfg(shipname, lines, iportuser):
# If we can't get module item, skip it
try:
modItem = sMkt.getItem(modName)
except (KeyboardInterrupt, SystemExit):
raise
except:
pyfalog.warning("Cannot get item.")
continue
@@ -475,6 +487,8 @@ def importEftCfg(shipname, lines, iportuser):
chargeItem = sMkt.getItem(chargeName, eager="group.category")
if chargeItem.category.name == "Charge":
m.charge = chargeItem
except (KeyboardInterrupt, SystemExit):
raise
except:
pyfalog.warning("Cannot get item.")
pass
@@ -499,6 +513,8 @@ def importEftCfg(shipname, lines, iportuser):
"%s:\n%s" % (fitobj.ship.name, fitobj.name)
)
except (KeyboardInterrupt, SystemExit):
raise
# Skip fit silently if we get an exception
except Exception as e:
pyfalog.error("Caught exception on fit.")
@@ -593,6 +609,8 @@ def _importCreateFit(lines):
except ValueError:
fit.ship = Citadel(ship)
fit.name = fitName
except (KeyboardInterrupt, SystemExit):
raise
except:
pyfalog.warning('service.port.eft.importEft: exception caught when parsing header')
raise EftImportError

View File

@@ -161,6 +161,8 @@ def importESI(string):
fitobj.ship = Ship(sMkt.getItem(ship))
except ValueError:
fitobj.ship = Citadel(sMkt.getItem(ship))
except (KeyboardInterrupt, SystemExit):
raise
except:
pyfalog.warning("Caught exception in importESI")
return None
@@ -201,6 +203,8 @@ def importESI(string):
moduleList.append(m)
except (KeyboardInterrupt, SystemExit):
raise
except:
pyfalog.warning("Could not process module.")
continue

View File

@@ -177,6 +177,8 @@ class Port:
except UserCancelException:
return False, "Processing has been canceled.\n"
except (KeyboardInterrupt, SystemExit):
raise
except Exception as e:
pyfalog.critical("Unknown exception processing: {0}", path)
pyfalog.critical(e)
@@ -253,11 +255,13 @@ class Port:
# Try to import mutated module
try:
baseItem, mutaplasmidItem, mutations = parseMutant(lines)
except (KeyboardInterrupt, SystemExit):
raise
except:
pass
else:
if baseItem is not None and mutaplasmidItem is not None:
return "MutatedItem", False, ((baseItem, mutaplasmidItem, mutations),)
if baseItem is not None:
return "FittingItem", False, ((baseItem, mutaplasmidItem, mutations),)
# Try to import into one of additions panels
isDrone, droneData = isValidDroneImport(string)
if isDrone:

View File

@@ -84,11 +84,13 @@ def _resolve_ship(fitting, sMkt, b_localized):
ship = Ship(sMkt.getItem(shipType))
except ValueError:
ship = Citadel(sMkt.getItem(shipType))
except (KeyboardInterrupt, SystemExit):
raise
except Exception as e:
pyfalog.warning("Caught exception on _resolve_ship")
pyfalog.error(e)
limit -= 1
if limit is 0:
if limit == 0:
break
shipType = anything
must_retry = True
@@ -128,11 +130,13 @@ def _resolve_module(hardware, sMkt, b_localized):
must_retry = False
try:
item = sMkt.getItem(moduleName, eager="group.category")
except (KeyboardInterrupt, SystemExit):
raise
except Exception as e:
pyfalog.warning("Caught exception on _resolve_module")
pyfalog.error(e)
limit -= 1
if limit is 0:
if limit == 0:
break
moduleName = emergency
must_retry = True
@@ -158,6 +162,8 @@ def importXml(text, iportuser):
for fitting in fittings:
try:
fitobj = _resolve_ship(fitting, sMkt, b_localized)
except (KeyboardInterrupt, SystemExit):
raise
except:
failed += 1
continue
@@ -264,6 +270,8 @@ def exportXml(fits, iportuser, callback):
description.setAttribute(
"value", re.sub("(\r|\n|\r\n)+", "<br>", notes) if notes is not None else ""
)
except (KeyboardInterrupt, SystemExit):
raise
except Exception as e:
pyfalog.warning("read description is failed, msg=%s\n" % e.args)
@@ -328,6 +336,8 @@ def exportXml(fits, iportuser, callback):
hardware.setAttribute("slot", "cargo")
hardware.setAttribute("type", name)
fitting.appendChild(hardware)
except (KeyboardInterrupt, SystemExit):
raise
except Exception as e:
pyfalog.error("Failed on fitID: %d, message: %s" % e.message)
continue

View File

@@ -17,6 +17,8 @@ class PreCheckMessage:
app = wx.App(False)
wx.MessageBox(msg, 'Error', wx.ICON_ERROR | wx.STAY_ON_TOP)
app.MainLoop()
except (KeyboardInterrupt, SystemExit):
raise
except:
pass
finally:
@@ -45,6 +47,8 @@ def version_precheck():
import wx
version_block += "\nwxPython version: {} ({})".format(VERSION_STRING, wx.wxWidgets_version)
except (KeyboardInterrupt, SystemExit):
raise
except:
msg = "pyfa requires wxPython v4.0.0b2+. You can download wxPython from https://wxpython.org/pages/downloads/"
raise PreCheckException(msg)
@@ -56,6 +60,8 @@ def version_precheck():
if (int(saMatch.group(1)), int(saMatch.group(2)), int(saMatch.group(3))) < (1, 0, 5):
raise Exception()
except (KeyboardInterrupt, SystemExit):
raise
except:
msg = "pyfa requires SQLAlchemy v1.0.5+. You can download SQLAlchemy from https://www.sqlalchemy.org/download.html"
raise PreCheckException(msg)
@@ -67,12 +73,16 @@ def version_precheck():
if int(logVersion[0]) < 1:
raise Exception()
except (KeyboardInterrupt, SystemExit):
raise
except:
raise PreCheckException("pyfa requires Logbook version 1.0.0+. You can download Logbook from https://pypi.python.org/pypi/Logbook")
try:
import requests
version_block += "\nRequests version: {}".format(requests.__version__)
except (KeyboardInterrupt, SystemExit):
raise
except:
msg = "pyfa requires the requests module. You can download requests from https://pypi.python.org/pypi/requests"
raise PreCheckException(msg)
@@ -80,6 +90,8 @@ def version_precheck():
try:
import dateutil
version_block += "\nDateutil version: {}".format(dateutil.__version__)
except (KeyboardInterrupt, SystemExit):
raise
except:
msg = "pyfa requires the python-dateutil module. You can download python-dateutil form https://pypi.python.org/pypi/python-dateutil"
raise PreCheckException(msg)

View File

@@ -131,6 +131,8 @@ class Price:
except TimeoutError:
pyfalog.warning("Price fetch timeout for source {}".format(source))
timedOutSources[source] = True
except (KeyboardInterrupt, SystemExit):
raise
except Exception as e:
pyfalog.warn('Failed to fetch prices from price source {}: {}'.format(source, e))
# Sources remove price map items as they fetch info, if none remain then we're done
@@ -176,6 +178,8 @@ class Price:
def cb():
try:
callback(requests)
except (KeyboardInterrupt, SystemExit):
raise
except Exception as e:
pyfalog.critical("Execution of callback from getPrices failed.")
pyfalog.critical(e)
@@ -211,6 +215,8 @@ class Price:
replacementsCheaper[replacee] = replacer
try:
callback(replacementsCheaper)
except (KeyboardInterrupt, SystemExit):
raise
except Exception as e:
pyfalog.critical("Execution of callback from findCheaperReplacements failed.")
pyfalog.critical(e)
@@ -229,11 +235,14 @@ class PriceWorkerThread(threading.Thread):
self.name = "PriceWorker"
self.queue = queue.Queue()
self.wait = {}
self.running = True
pyfalog.debug("Initialize PriceWorkerThread.")
def run(self):
queue = self.queue
while True:
if not self.running:
break
# Grab our data
callback, requests, fetchTimeout, validityOverride = queue.get()
@@ -259,6 +268,9 @@ class PriceWorkerThread(threading.Thread):
callbacks = self.wait.setdefault(price.typeID, [])
callbacks.append(callback)
def stop(self):
self.running = False
# Import market sources only to initialize price source modules, they register on their own
from service.marketSources import evemarketer, evemarketdata, evepraisal # noqa: E402

View File

@@ -88,6 +88,8 @@ class AuthHandler(http.server.BaseHTTPRequestHandler):
# For implicit mode, we have to serve up the page which will take the hash and redirect using a querystring
pyfalog.info("Processing response from EVE Online.")
msg = "Processing response from EVE Online"
except (KeyboardInterrupt, SystemExit):
raise
except Exception as ex:
pyfalog.error("Error logging into EVE")
pyfalog.error(ex)

View File

@@ -69,6 +69,8 @@ class SettingsProvider:
for item in defaults:
if item not in info:
info[item] = defaults[item]
except (KeyboardInterrupt, SystemExit):
raise
except:
info = {}
info.update(defaults)

View File

@@ -41,6 +41,7 @@ class CheckUpdateThread(threading.Thread):
self.callback = callback
self.settings = UpdateSettings.getInstance()
self.network = Network.getInstance()
self.running = True
def run(self):
network = Network.getInstance()
@@ -49,11 +50,13 @@ class CheckUpdateThread(threading.Thread):
try:
response = network.get(
url='https://www.pyfa.io/update_check?pyfa_version={}&client_hash={}'.format(config.version, config.getClientSecret()),
type=network.UPDATE)
type=network.UPDATE, timeout=5)
except (KeyboardInterrupt, SystemExit):
raise
except Exception as e:
response = network.get(
url='https://api.github.com/repos/pyfa-org/Pyfa/releases',
type=network.UPDATE)
type=network.UPDATE, timeout=5)
jsonResponse = response.json()
jsonResponse.sort(
@@ -81,6 +84,8 @@ class CheckUpdateThread(threading.Thread):
wx.CallAfter(self.callback, release, rVersion)
break
except (KeyboardInterrupt, SystemExit):
raise
except Exception as e:
pyfalog.error("Caught exception in run")
pyfalog.error(e)
@@ -90,6 +95,9 @@ class CheckUpdateThread(threading.Thread):
def versiontuple(v):
return tuple(map(int, (v.split("."))))
def stop(self):
self.running = False
class Update:
instance = None

View File

@@ -166173,7 +166173,7 @@
"dogmaAttributes": [
{
"attributeID": 6,
"value": 40.0
"value": 36.0
},
{
"attributeID": 9,
@@ -166185,7 +166185,7 @@
},
{
"attributeID": 50,
"value": 4.0
"value": 5.0
},
{
"attributeID": 73,
@@ -166193,7 +166193,7 @@
},
{
"attributeID": 84,
"value": 72.0
"value": 79.0
},
{
"attributeID": 182,
@@ -166373,7 +166373,7 @@
},
{
"attributeID": 84,
"value": 83.0
"value": 79.0
},
{
"attributeID": 182,
@@ -166397,7 +166397,7 @@
},
{
"attributeID": 633,
"value": 4.0
"value": 1.0
},
{
"attributeID": 1180,
@@ -166533,7 +166533,7 @@
"dogmaAttributes": [
{
"attributeID": 6,
"value": 160.0
"value": 144.0
},
{
"attributeID": 9,
@@ -166553,7 +166553,7 @@
},
{
"attributeID": 84,
"value": 290.0
"value": 317.0
},
{
"attributeID": 182,
@@ -166721,11 +166721,11 @@
},
{
"attributeID": 30,
"value": 120.0
"value": 108.0
},
{
"attributeID": 50,
"value": 21.0
"value": 20.0
},
{
"attributeID": 73,
@@ -166733,7 +166733,7 @@
},
{
"attributeID": 84,
"value": 331.0
"value": 317.0
},
{
"attributeID": 182,
@@ -166757,7 +166757,7 @@
},
{
"attributeID": 633,
"value": 4.0
"value": 1.0
},
{
"attributeID": 1180,
@@ -166983,7 +166983,7 @@
"dogmaAttributes": [
{
"attributeID": 6,
"value": 400.0
"value": 360.0
},
{
"attributeID": 9,
@@ -166995,7 +166995,7 @@
},
{
"attributeID": 50,
"value": 47.0
"value": 49.0
},
{
"attributeID": 73,
@@ -167003,7 +167003,7 @@
},
{
"attributeID": 84,
"value": 725.0
"value": 794.0
},
{
"attributeID": 182,
@@ -167171,11 +167171,11 @@
},
{
"attributeID": 30,
"value": 1800.0
"value": 1620.0
},
{
"attributeID": 50,
"value": 46.0
"value": 40.0
},
{
"attributeID": 73,
@@ -167183,7 +167183,7 @@
},
{
"attributeID": 84,
"value": 828.0
"value": 794.0
},
{
"attributeID": 182,
@@ -167207,7 +167207,7 @@
},
{
"attributeID": 633,
"value": 4.0
"value": 1.0
},
{
"attributeID": 1180,
@@ -473928,7 +473928,7 @@
},
{
"attributeID": 633,
"value": 7.0
"value": 8.0
},
{
"attributeID": 1180,
@@ -474022,7 +474022,7 @@
},
{
"attributeID": 633,
"value": 9.0
"value": 8.0
},
{
"attributeID": 1180,
@@ -474116,7 +474116,7 @@
},
{
"attributeID": 633,
"value": 9.0
"value": 8.0
},
{
"attributeID": 1180,
@@ -474210,7 +474210,7 @@
},
{
"attributeID": 633,
"value": 7.0
"value": 8.0
},
{
"attributeID": 1180,
@@ -474304,7 +474304,7 @@
},
{
"attributeID": 633,
"value": 9.0
"value": 8.0
},
{
"attributeID": 1180,
@@ -474398,7 +474398,7 @@
},
{
"attributeID": 633,
"value": 9.0
"value": 8.0
},
{
"attributeID": 1180,
@@ -474492,7 +474492,7 @@
},
{
"attributeID": 633,
"value": 7.0
"value": 8.0
},
{
"attributeID": 1180,
@@ -474586,7 +474586,7 @@
},
{
"attributeID": 633,
"value": 9.0
"value": 8.0
},
{
"attributeID": 1180,
@@ -474680,7 +474680,7 @@
},
{
"attributeID": 633,
"value": 9.0
"value": 8.0
},
{
"attributeID": 1180,
@@ -563033,7 +563033,7 @@
},
{
"attributeID": 633,
"value": 9.0
"value": 8.0
},
{
"attributeID": 1180,
@@ -563127,7 +563127,7 @@
},
{
"attributeID": 633,
"value": 9.0
"value": 8.0
},
{
"attributeID": 1180,
@@ -563221,7 +563221,7 @@
},
{
"attributeID": 633,
"value": 9.0
"value": 8.0
},
{
"attributeID": 1180,
@@ -780699,7 +780699,7 @@
},
{
"attributeID": 633,
"value": 7.0
"value": 8.0
},
{
"attributeID": 1180,
@@ -780793,7 +780793,7 @@
},
{
"attributeID": 633,
"value": 7.0
"value": 8.0
},
{
"attributeID": 1180,
@@ -780887,7 +780887,7 @@
},
{
"attributeID": 633,
"value": 7.0
"value": 8.0
},
{
"attributeID": 1180,
@@ -783267,7 +783267,7 @@
},
{
"attributeID": 633,
"value": 9.0
"value": 8.0
},
{
"attributeID": 1180,
@@ -783361,7 +783361,7 @@
},
{
"attributeID": 633,
"value": 9.0
"value": 8.0
},
{
"attributeID": 1180,
@@ -783455,7 +783455,7 @@
},
{
"attributeID": 633,
"value": 9.0
"value": 8.0
},
{
"attributeID": 1180,
@@ -1096977,7 +1096977,7 @@
},
{
"attributeID": 633,
"value": 7.0
"value": 6.0
},
{
"attributeID": 1180,
@@ -1097080,7 +1097080,7 @@
},
{
"attributeID": 633,
"value": 7.0
"value": 6.0
},
{
"attributeID": 1180,
@@ -1637789,7 +1637789,7 @@
},
{
"attributeID": 633,
"value": 9.0
"value": 8.0
},
{
"attributeID": 1180,
@@ -1638073,7 +1638073,7 @@
},
{
"attributeID": 633,
"value": 9.0
"value": 8.0
},
{
"attributeID": 1180,
@@ -1638440,7 +1638440,7 @@
},
{
"attributeID": 633,
"value": 9.0
"value": 8.0
},
{
"attributeID": 1180,

View File

@@ -5362,12 +5362,11 @@
"descriptionID": 87829,
"groupID": 40,
"iconID": 84,
"marketGroupID": 610,
"mass": 0.0,
"metaGroupID": 1,
"metaLevel": 2,
"portionSize": 1,
"published": true,
"published": false,
"raceID": 2,
"radius": 500.0,
"techLevel": 1,
@@ -5406,12 +5405,11 @@
"descriptionID": 95300,
"groupID": 40,
"iconID": 84,
"marketGroupID": 610,
"mass": 0.0,
"metaGroupID": 1,
"metaLevel": 1,
"portionSize": 1,
"published": true,
"published": false,
"raceID": 8,
"radius": 500.0,
"techLevel": 1,
@@ -5450,12 +5448,11 @@
"descriptionID": 87832,
"groupID": 40,
"iconID": 84,
"marketGroupID": 611,
"mass": 0.0,
"metaGroupID": 1,
"metaLevel": 2,
"portionSize": 1,
"published": true,
"published": false,
"raceID": 2,
"radius": 500.0,
"techLevel": 1,
@@ -5494,12 +5491,11 @@
"descriptionID": 95295,
"groupID": 40,
"iconID": 84,
"marketGroupID": 611,
"mass": 0.0,
"metaGroupID": 1,
"metaLevel": 1,
"portionSize": 1,
"published": true,
"published": false,
"raceID": 8,
"radius": 500.0,
"techLevel": 1,
@@ -5538,12 +5534,11 @@
"descriptionID": 87835,
"groupID": 40,
"iconID": 84,
"marketGroupID": 612,
"mass": 0.0,
"metaGroupID": 1,
"metaLevel": 2,
"portionSize": 1,
"published": true,
"published": false,
"raceID": 2,
"radius": 500.0,
"techLevel": 1,
@@ -5582,12 +5577,11 @@
"descriptionID": 95315,
"groupID": 40,
"iconID": 84,
"marketGroupID": 612,
"mass": 0.0,
"metaGroupID": 1,
"metaLevel": 1,
"portionSize": 1,
"published": true,
"published": false,
"raceID": 8,
"radius": 500.0,
"techLevel": 1,
@@ -36877,7 +36871,7 @@
"marketGroupID": 1051,
"mass": 500.0,
"metaGroupID": 4,
"metaLevel": 7,
"metaLevel": 8,
"portionSize": 1,
"published": true,
"raceID": 4,
@@ -36898,7 +36892,7 @@
"marketGroupID": 1051,
"mass": 500.0,
"metaGroupID": 4,
"metaLevel": 9,
"metaLevel": 8,
"portionSize": 1,
"published": true,
"raceID": 4,
@@ -36919,7 +36913,7 @@
"marketGroupID": 1051,
"mass": 500.0,
"metaGroupID": 4,
"metaLevel": 9,
"metaLevel": 8,
"portionSize": 1,
"published": true,
"raceID": 4,
@@ -36940,7 +36934,7 @@
"marketGroupID": 1050,
"mass": 500.0,
"metaGroupID": 4,
"metaLevel": 7,
"metaLevel": 8,
"portionSize": 1,
"published": true,
"raceID": 4,
@@ -36961,7 +36955,7 @@
"marketGroupID": 1050,
"mass": 500.0,
"metaGroupID": 4,
"metaLevel": 9,
"metaLevel": 8,
"portionSize": 1,
"published": true,
"raceID": 4,
@@ -36982,7 +36976,7 @@
"marketGroupID": 1050,
"mass": 500.0,
"metaGroupID": 4,
"metaLevel": 9,
"metaLevel": 8,
"portionSize": 1,
"published": true,
"raceID": 4,
@@ -37003,7 +36997,7 @@
"marketGroupID": 1049,
"mass": 500.0,
"metaGroupID": 4,
"metaLevel": 7,
"metaLevel": 8,
"portionSize": 1,
"published": true,
"raceID": 4,
@@ -37024,7 +37018,7 @@
"marketGroupID": 1049,
"mass": 500.0,
"metaGroupID": 4,
"metaLevel": 9,
"metaLevel": 8,
"portionSize": 1,
"published": true,
"raceID": 4,
@@ -37045,7 +37039,7 @@
"marketGroupID": 1049,
"mass": 500.0,
"metaGroupID": 4,
"metaLevel": 9,
"metaLevel": 8,
"portionSize": 1,
"published": true,
"raceID": 4,
@@ -56244,7 +56238,7 @@
"marketGroupID": 1049,
"mass": 500.0,
"metaGroupID": 4,
"metaLevel": 9,
"metaLevel": 8,
"portionSize": 1,
"published": true,
"raceID": 4,
@@ -56265,7 +56259,7 @@
"marketGroupID": 1050,
"mass": 500.0,
"metaGroupID": 4,
"metaLevel": 9,
"metaLevel": 8,
"portionSize": 1,
"published": true,
"raceID": 4,
@@ -56286,7 +56280,7 @@
"marketGroupID": 1051,
"mass": 500.0,
"metaGroupID": 4,
"metaLevel": 9,
"metaLevel": 8,
"portionSize": 1,
"published": true,
"raceID": 4,
@@ -83156,7 +83150,7 @@
"marketGroupID": 1051,
"mass": 500.0,
"metaGroupID": 4,
"metaLevel": 7,
"metaLevel": 8,
"portionSize": 1,
"published": true,
"raceID": 4,
@@ -83177,7 +83171,7 @@
"marketGroupID": 1050,
"mass": 500.0,
"metaGroupID": 4,
"metaLevel": 7,
"metaLevel": 8,
"portionSize": 1,
"published": true,
"raceID": 4,
@@ -83198,7 +83192,7 @@
"marketGroupID": 1049,
"mass": 500.0,
"metaGroupID": 4,
"metaLevel": 7,
"metaLevel": 8,
"portionSize": 1,
"published": true,
"raceID": 4,
@@ -84023,7 +84017,7 @@
"marketGroupID": 1051,
"mass": 500.0,
"metaGroupID": 4,
"metaLevel": 9,
"metaLevel": 8,
"portionSize": 1,
"published": true,
"raceID": 4,
@@ -84044,7 +84038,7 @@
"marketGroupID": 1050,
"mass": 500.0,
"metaGroupID": 4,
"metaLevel": 9,
"metaLevel": 8,
"portionSize": 1,
"published": true,
"raceID": 4,
@@ -84065,7 +84059,7 @@
"marketGroupID": 1049,
"mass": 500.0,
"metaGroupID": 4,
"metaLevel": 9,
"metaLevel": 8,
"portionSize": 1,
"published": true,
"raceID": 4,
@@ -146773,7 +146767,7 @@
"marketGroupID": 1050,
"mass": 500.0,
"metaGroupID": 3,
"metaLevel": 7,
"metaLevel": 6,
"portionSize": 1,
"published": true,
"techLevel": 1,
@@ -146825,7 +146819,7 @@
"marketGroupID": 1051,
"mass": 500.0,
"metaGroupID": 3,
"metaLevel": 7,
"metaLevel": 6,
"portionSize": 1,
"published": true,
"techLevel": 1,
@@ -232483,7 +232477,7 @@
"marketGroupID": 1051,
"mass": 500.0,
"metaGroupID": 4,
"metaLevel": 9,
"metaLevel": 8,
"portionSize": 1,
"published": true,
"raceID": 4,
@@ -232575,7 +232569,7 @@
"marketGroupID": 1050,
"mass": 500.0,
"metaGroupID": 4,
"metaLevel": 9,
"metaLevel": 8,
"portionSize": 1,
"published": true,
"raceID": 4,
@@ -232721,7 +232715,7 @@
"marketGroupID": 1049,
"mass": 500.0,
"metaGroupID": 4,
"metaLevel": 9,
"metaLevel": 8,
"portionSize": 1,
"published": true,
"raceID": 4,
@@ -510303,7 +510297,7 @@
"raceID": 1,
"techLevel": 1,
"typeID": 4529,
"typeName": "Small I-a Polarized Armor Regenerator",
"typeName": "Small I-a Enduring Armor Repairer",
"typeNameID": 77030,
"variationParentTypeID": 523,
"volume": 5.0
@@ -510334,13 +510328,13 @@
"descriptionID": 87515,
"groupID": 62,
"iconID": 80,
"marketGroupID": 1049,
"mass": 500.0,
"metaGroupID": 1,
"metaLevel": 2,
"portionSize": 1,
"published": true,
"published": false,
"raceID": 2,
"radius": 1.0,
"techLevel": 1,
"typeID": 4531,
"typeName": "Small Inefficient Armor Repair Unit",
@@ -510358,13 +510352,13 @@
"marketGroupID": 1049,
"mass": 500.0,
"metaGroupID": 1,
"metaLevel": 4,
"metaLevel": 1,
"portionSize": 1,
"published": true,
"raceID": 4,
"techLevel": 1,
"typeID": 4533,
"typeName": "Small 'Accommodation' Vestment Reconstructer I",
"typeName": "Small ACM Compact Armor Repairer",
"typeNameID": 77032,
"variationParentTypeID": 523,
"volume": 5.0
@@ -510376,13 +510370,13 @@
"descriptionID": 87517,
"groupID": 62,
"iconID": 80,
"marketGroupID": 1049,
"mass": 500.0,
"metaGroupID": 1,
"metaLevel": 3,
"portionSize": 1,
"published": true,
"published": false,
"raceID": 8,
"radius": 1.0,
"techLevel": 1,
"typeID": 4535,
"typeName": "Small Automated Carapace Restoration",
@@ -514477,7 +514471,7 @@
"raceID": 1,
"techLevel": 1,
"typeID": 4569,
"typeName": "Medium I-a Polarized Armor Regenerator",
"typeName": "Medium I-a Enduring Armor Repairer",
"typeNameID": 77034,
"variationParentTypeID": 3528,
"volume": 10.0
@@ -514808,13 +514802,13 @@
"descriptionID": 87519,
"groupID": 62,
"iconID": 80,
"marketGroupID": 1050,
"mass": 500.0,
"metaGroupID": 1,
"metaLevel": 2,
"portionSize": 1,
"published": true,
"published": false,
"raceID": 2,
"radius": 1.0,
"techLevel": 1,
"typeID": 4571,
"typeName": "Medium Inefficient Armor Repair Unit",
@@ -515131,13 +515125,13 @@
"marketGroupID": 1050,
"mass": 500.0,
"metaGroupID": 1,
"metaLevel": 4,
"metaLevel": 1,
"portionSize": 1,
"published": true,
"raceID": 4,
"techLevel": 1,
"typeID": 4573,
"typeName": "Medium 'Accommodation' Vestment Reconstructer I",
"typeName": "Medium ACM Compact Armor Repairer",
"typeNameID": 77036,
"variationParentTypeID": 3528,
"volume": 10.0
@@ -515461,13 +515455,13 @@
"descriptionID": 87521,
"groupID": 62,
"iconID": 80,
"marketGroupID": 1050,
"mass": 500.0,
"metaGroupID": 1,
"metaLevel": 3,
"portionSize": 1,
"published": true,
"published": false,
"raceID": 8,
"radius": 1.0,
"techLevel": 1,
"typeID": 4575,
"typeName": "Medium Automated Carapace Restoration",
@@ -516087,7 +516081,7 @@
"mass": 500.0,
"metaLevel": 4,
"portionSize": 1,
"published": true,
"published": false,
"raceID": 2,
"techLevel": 1,
"typeID": 4579,
@@ -520665,7 +520659,7 @@
"raceID": 1,
"techLevel": 1,
"typeID": 4609,
"typeName": "Large I-a Polarized Armor Regenerator",
"typeName": "Large I-a Enduring Armor Repairer",
"typeNameID": 77039,
"variationParentTypeID": 3538,
"volume": 50.0
@@ -520996,13 +520990,13 @@
"descriptionID": 87524,
"groupID": 62,
"iconID": 80,
"marketGroupID": 1051,
"mass": 500.0,
"metaGroupID": 1,
"metaLevel": 2,
"portionSize": 1,
"published": true,
"published": false,
"raceID": 2,
"radius": 1.0,
"techLevel": 1,
"typeID": 4611,
"typeName": "Large Inefficient Armor Repair Unit",
@@ -521320,13 +521314,13 @@
"marketGroupID": 1051,
"mass": 500.0,
"metaGroupID": 1,
"metaLevel": 4,
"metaLevel": 1,
"portionSize": 1,
"published": true,
"raceID": 4,
"techLevel": 1,
"typeID": 4613,
"typeName": "Large 'Accommodation' Vestment Reconstructer I",
"typeName": "Large ACM Compact Armor Repairer",
"typeNameID": 77041,
"variationParentTypeID": 3538,
"volume": 50.0
@@ -521638,13 +521632,13 @@
"descriptionID": 87526,
"groupID": 62,
"iconID": 80,
"marketGroupID": 1051,
"mass": 500.0,
"metaGroupID": 1,
"metaLevel": 3,
"portionSize": 1,
"published": true,
"published": false,
"raceID": 8,
"radius": 1.0,
"techLevel": 1,
"typeID": 4615,
"typeName": "Large Automated Carapace Restoration",
@@ -522579,7 +522573,7 @@
"mass": 500.0,
"metaLevel": 5,
"portionSize": 1,
"published": true,
"published": false,
"raceID": 4,
"techLevel": 1,
"typeID": 4621,
@@ -602906,12 +602900,11 @@
"descriptionID": 87666,
"groupID": 40,
"iconID": 84,
"marketGroupID": 609,
"mass": 0.0,
"metaGroupID": 1,
"metaLevel": 2,
"portionSize": 1,
"published": true,
"published": false,
"raceID": 2,
"radius": 500.0,
"techLevel": 1,
@@ -602975,12 +602968,11 @@
"descriptionID": 95311,
"groupID": 40,
"iconID": 84,
"marketGroupID": 609,
"mass": 0.0,
"metaGroupID": 1,
"metaLevel": 1,
"portionSize": 1,
"published": true,
"published": false,
"raceID": 8,
"radius": 500.0,
"techLevel": 1,

View File

@@ -1,10 +1,10 @@
[
{
"field_name": "client_build",
"field_value": 1651039
"field_value": 1655072
},
{
"field_name": "dump_time",
"field_value": 1579607729
"field_value": 1580211149
}
]

View File

@@ -7,7 +7,7 @@ passenv = CI TRAVIS TRAVIS_*
deps =
-rrequirements.txt
-rrequirements_test.txt
basepython = python3.6
basepython = python3.8
commands = py.test -vv --cov Pyfa tests2/
[testenv:pep8]

View File

@@ -35,3 +35,16 @@ class Timer:
def __exit__(self, type, value, traceback):
self.checkpoint('finished')
pass
class CountdownTimer:
def __init__(self, timeout):
self.timeout = timeout
self.start = time.time()
def elapsed(self):
return time.time() - self.start
def remainder(self):
return max(self.timeout - self.elapsed(), 0)

View File

@@ -1 +1 @@
version: v2.16.0
version: v2.17.0