diff --git a/.codecov.yml b/.codecov.yml new file mode 100644 index 000000000..2b5037ed5 --- /dev/null +++ b/.codecov.yml @@ -0,0 +1,26 @@ +codecov: + notify: + require_ci_to_pass: yes + +coverage: + precision: 2 + round: down + range: "70...100" + + status: + project: yes + patch: yes + changes: no + +parsers: + gcov: + branch_detection: + conditional: yes + loop: yes + method: no + macro: no + +comment: + layout: "header, diff" + behavior: default + require_changes: no diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 000000000..9344e9905 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,40 @@ +# Set the default behavior, in case people don't have core.autocrlf set. +* text=auto + +# Explicitly declare text files you want to always be normalized and converted +# to native line endings on checkout. +# *.c text +# *.h text + +# Declare files that will always have CRLF line endings on checkout. +# Source files +# ============ +*.pxd text eol=crlf +*.py text eol=crlf +*.py3 text eol=crlf +*.pyw text eol=crlf +*.pyx text eol=crlf +pyfa.py text eol=lf + +# Denote all files that are truly binary and should not be modified. +# Binary files +# ============ +*.db binary +*.p binary +*.pkl binary +*.pyc binary +*.pyd binary +*.pyo binary + +# Note: .db, .p, and .pkl files are associated +# with the python modules ``pickle``, ``dbm.*``, +# ``shelve``, ``marshal``, ``anydbm``, & ``bsddb`` +# (among others). + +# Denote all files that are truly binary and should not be modified. +# Image files +# ============ +*.png binary +*.jpg binary +*.icns binary +*.ico binary diff --git a/.gitignore b/.gitignore index 4b6d3bde8..41586d1b5 100644 --- a/.gitignore +++ b/.gitignore @@ -49,7 +49,6 @@ Pyfa.egg-info/ # Usually these files are written by a python script from a template # before PyInstaller builds the exe, so as to inject date/other infos into it. *.manifest -*.spec # Installer logs pip-log.txt diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 000000000..497b67b08 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,25 @@ +language: python +python: + - '2.7' +env: + - TOXENV=pep8 +addons: + apt: + packages: + # for wxPython: + - python-wxgtk2.8 + - python-wxtools + - wx2.8-doc + - wx2.8-examples + - wx2.8-headers + - wx2.8-i18n +before_install: + - pip install -U tox +install: + - pip install -r requirements.txt + - pip install -r requirements_test.txt +script: + - tox + - py.test --cov=./ +after_success: + - bash <(curl -s https://codecov.io/bash) diff --git a/ISSUE_TEMPLATE.md b/ISSUE_TEMPLATE.md new file mode 100644 index 000000000..621db8b52 --- /dev/null +++ b/ISSUE_TEMPLATE.md @@ -0,0 +1,32 @@ +# Submit a bug report bug report or feature request + +Here you can inform pyfa developers of potential bugs or suggest features / improvements to the project. Please check +to make sure that the bug hasn't been reported or feature requested before submitting. If you have general questions +about the project and want to reach out to the developers personally, please check out out our [Slack] +(https://pyfainvite.azurewebsites.net/). + +--- + +## Bug Report + + +### Expected behavior: + + +### Actual behavior: + + +### Detailed steps to reproduce: + + +### Fits involved in EFT format (Edit > To Clipboard > EFT): + + +### Release or development git branch? Please note the release version or commit hash: + + +### Operating system and version (eg: Windows 10, OS X 10.9, OS X 10.11, Ubuntu 16.10): + + +### Other relevant information: + diff --git a/README.md b/README.md index 4f26244f4..a3b795749 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # pyfa -[![Join us on Slack!](https://pyfainvite.azurewebsites.net/badge.svg)](https://pyfainvite.azurewebsites.net/) +[![Join us on Slack!](https://pyfainvite.azurewebsites.net/badge.svg)](https://pyfainvite.azurewebsites.net/) [![Build Status](https://travis-ci.org/pyfa-org/Pyfa.svg?branch=master)](https://travis-ci.org/pyfa-org/Pyfa) ![pyfa](https://cloud.githubusercontent.com/assets/3904767/10271512/af385ef2-6ade-11e5-8f67-52b8b1e4c797.PNG) @@ -38,7 +38,7 @@ If you wish to help with development or simply need to run pyfa through a Python * Python 2.7 * `wxPython` 2.8/3.0 -* `sqlalchemy` >= 0.6 +* `sqlalchemy` >= 1.0.5 * `dateutil` * `matplotlib` (for some Linux distributions you may need to install separate wxPython bindings such as `python-matplotlib-wx`) * `requests` @@ -51,8 +51,7 @@ pyfa is licensed under the GNU GPL v3.0, see LICENSE ## Resources * Development repository: [https://github.com/pyfa-org/Pyfa](https://github.com/pyfa-org/Pyfa) -* XMPP conference: [pyfa@conference.jabber.org](pyfa@conference.jabber.org) -* [EVE forum thread](http://forums.eveonline.com/default.aspx?g=posts&t=247609) +* [EVE forum thread](https://forums.eveonline.com/default.aspx?g=posts&t=466425) * [EVE University guide using pyfa](http://wiki.eveuniversity.org/Guide_to_using_PYFA) * [EVE Online website](http://www.eveonline.com/) diff --git a/_development/Pyfa_CodeStyle.xml b/_development/Pyfa_CodeStyle.xml new file mode 100644 index 000000000..a7980e4df --- /dev/null +++ b/_development/Pyfa_CodeStyle.xml @@ -0,0 +1,13 @@ + + diff --git a/_development/Pyfa_Inspections.xml b/_development/Pyfa_Inspections.xml new file mode 100644 index 000000000..0a0da06d4 --- /dev/null +++ b/_development/Pyfa_Inspections.xml @@ -0,0 +1,54 @@ + + diff --git a/config.py b/config.py index 0a9c1a799..447e81763 100644 --- a/config.py +++ b/config.py @@ -1,27 +1,28 @@ import os import sys -# TODO: move all logging back to pyfa.py main loop -# We moved it here just to avoid rebuilding windows skeleton for now (any change to pyfa.py needs it) -import logging -import logging.handlers +from logbook import Logger + +pyfalog = Logger(__name__) # Load variable overrides specific to distribution type try: import configforced except ImportError: + pyfalog.warning("Failed to import: configforced") configforced = None + # Turns on debug mode debug = False # Defines if our saveddata will be in pyfa root or not saveInRoot = False # Version data -version = "1.26.1" +version = "1.27.3" tag = "git" -expansionName = "YC118.10" -expansionVersion = "1.2" +expansionName = "YC119.2" +expansionVersion = "1.4" evemonMinVersion = "4081" pyfaPath = None @@ -30,35 +31,28 @@ saveDB = None gameDB = None -class StreamToLogger(object): - """ - Fake file-like stream object that redirects writes to a logger instance. - From: http://www.electricmonk.nl/log/2011/08/14/redirect-stdout-and-stderr-to-a-logger-in-python/ - """ - def __init__(self, logger, log_level=logging.INFO): - self.logger = logger - self.log_level = log_level - self.linebuf = '' - - def write(self, buf): - for line in buf.rstrip().splitlines(): - self.logger.log(self.log_level, line.rstrip()) - def isFrozen(): if hasattr(sys, 'frozen'): return True else: return False + +def __createDirs(path): + if not os.path.exists(path): + os.makedirs(path) + + def getPyfaRoot(): base = getattr(sys.modules['__main__'], "__file__", sys.executable) if isFrozen() else sys.argv[0] root = os.path.dirname(os.path.realpath(os.path.abspath(base))) root = unicode(root, sys.getfilesystemencoding()) return root -def __createDirs(path): - if not os.path.exists(path): - os.makedirs(path) + +def getDefaultSave(): + return unicode(os.path.expanduser(os.path.join("~", ".pyfa")), sys.getfilesystemencoding()) + def defPaths(customSavePath): global debug @@ -68,10 +62,7 @@ def defPaths(customSavePath): global gameDB global saveInRoot - if debug: - logLevel = logging.DEBUG - else: - logLevel = logging.WARN + pyfalog.debug("Configuring Pyfa") # The main pyfa directory which contains run.py # Python 2.X uses ANSI by default, so we need to convert the character encoding @@ -87,36 +78,16 @@ def defPaths(customSavePath): else: savePath = getattr(configforced, "savePath", None) if savePath is None: - if customSavePath is None: # customSavePath is not overriden - savePath = unicode(os.path.expanduser(os.path.join("~", ".pyfa")), - sys.getfilesystemencoding()) + if customSavePath is None: # customSavePath is not overriden + savePath = getDefaultSave() else: savePath = customSavePath __createDirs(savePath) if isFrozen(): - os.environ["REQUESTS_CA_BUNDLE"] = os.path.join(pyfaPath, "cacert.pem") - os.environ["SSL_CERT_FILE"] = os.path.join(pyfaPath, "cacert.pem") - - format = '%(asctime)s %(name)-24s %(levelname)-8s %(message)s' - logging.basicConfig(format=format, level=logLevel) - handler = logging.handlers.RotatingFileHandler(os.path.join(savePath, "log.txt"), maxBytes=1000000, backupCount=3) - formatter = logging.Formatter(format) - handler.setFormatter(formatter) - logging.getLogger('').addHandler(handler) - - logging.info("Starting pyfa") - - if hasattr(sys, 'frozen'): - stdout_logger = logging.getLogger('STDOUT') - sl = StreamToLogger(stdout_logger, logging.INFO) - sys.stdout = sl - - # This interferes with cx_Freeze's own handling of exceptions. Find a way to fix this. - #stderr_logger = logging.getLogger('STDERR') - #sl = StreamToLogger(stderr_logger, logging.ERROR) - #sys.stderr = sl + os.environ["REQUESTS_CA_BUNDLE"] = os.path.join(pyfaPath, "cacert.pem").encode('utf8') + os.environ["SSL_CERT_FILE"] = os.path.join(pyfaPath, "cacert.pem").encode('utf8') # The database where we store all the fits etc saveDB = os.path.join(savePath, "saveddata.db") @@ -126,10 +97,10 @@ def defPaths(customSavePath): # maintenance script gameDB = os.path.join(pyfaPath, "eve.db") - ## DON'T MODIFY ANYTHING BELOW ## + # DON'T MODIFY ANYTHING BELOW import eos.config - #Caching modifiers, disable all gamedata caching, its unneeded. + # Caching modifiers, disable all gamedata caching, its unneeded. eos.config.gamedataCache = False # saveddata db location modifier, shouldn't ever need to touch this eos.config.saveddata_connectionstring = "sqlite:///" + saveDB + "?check_same_thread=False" diff --git a/dist_assets/cacert.pem b/dist_assets/cacert.pem new file mode 100644 index 000000000..06d6150be --- /dev/null +++ b/dist_assets/cacert.pem @@ -0,0 +1,5666 @@ + +# Issuer: O=Equifax OU=Equifax Secure Certificate Authority +# Subject: O=Equifax OU=Equifax Secure Certificate Authority +# Label: "Equifax Secure CA" +# Serial: 903804111 +# MD5 Fingerprint: 67:cb:9d:c0:13:24:8a:82:9b:b2:17:1e:d1:1b:ec:d4 +# SHA1 Fingerprint: d2:32:09:ad:23:d3:14:23:21:74:e4:0d:7f:9d:62:13:97:86:63:3a +# SHA256 Fingerprint: 08:29:7a:40:47:db:a2:36:80:c7:31:db:6e:31:76:53:ca:78:48:e1:be:bd:3a:0b:01:79:a7:07:f9:2c:f1:78 +-----BEGIN CERTIFICATE----- +MIIDIDCCAomgAwIBAgIENd70zzANBgkqhkiG9w0BAQUFADBOMQswCQYDVQQGEwJV +UzEQMA4GA1UEChMHRXF1aWZheDEtMCsGA1UECxMkRXF1aWZheCBTZWN1cmUgQ2Vy +dGlmaWNhdGUgQXV0aG9yaXR5MB4XDTk4MDgyMjE2NDE1MVoXDTE4MDgyMjE2NDE1 +MVowTjELMAkGA1UEBhMCVVMxEDAOBgNVBAoTB0VxdWlmYXgxLTArBgNVBAsTJEVx +dWlmYXggU2VjdXJlIENlcnRpZmljYXRlIEF1dGhvcml0eTCBnzANBgkqhkiG9w0B +AQEFAAOBjQAwgYkCgYEAwV2xWGcIYu6gmi0fCG2RFGiYCh7+2gRvE4RiIcPRfM6f +BeC4AfBONOziipUEZKzxa1NfBbPLZ4C/QgKO/t0BCezhABRP/PvwDN1Dulsr4R+A +cJkVV5MW8Q+XarfCaCMczE1ZMKxRHjuvK9buY0V7xdlfUNLjUA86iOe/FP3gx7kC +AwEAAaOCAQkwggEFMHAGA1UdHwRpMGcwZaBjoGGkXzBdMQswCQYDVQQGEwJVUzEQ +MA4GA1UEChMHRXF1aWZheDEtMCsGA1UECxMkRXF1aWZheCBTZWN1cmUgQ2VydGlm +aWNhdGUgQXV0aG9yaXR5MQ0wCwYDVQQDEwRDUkwxMBoGA1UdEAQTMBGBDzIwMTgw +ODIyMTY0MTUxWjALBgNVHQ8EBAMCAQYwHwYDVR0jBBgwFoAUSOZo+SvSspXXR9gj +IBBPM5iQn9QwHQYDVR0OBBYEFEjmaPkr0rKV10fYIyAQTzOYkJ/UMAwGA1UdEwQF +MAMBAf8wGgYJKoZIhvZ9B0EABA0wCxsFVjMuMGMDAgbAMA0GCSqGSIb3DQEBBQUA +A4GBAFjOKer89961zgK5F7WF0bnj4JXMJTENAKaSbn+2kmOeUJXRmm/kEd5jhW6Y +7qj/WsjTVbJmcVfewCHrPSqnI0kBBIZCe/zuf6IWUrVnZ9NA2zsmWLIodz2uFHdh +1voqZiegDfqnc1zqcPGUIWVEX/r87yloqaKHee9570+sB3c4 +-----END CERTIFICATE----- + +# Issuer: CN=GlobalSign Root CA O=GlobalSign nv-sa OU=Root CA +# Subject: CN=GlobalSign Root CA O=GlobalSign nv-sa OU=Root CA +# Label: "GlobalSign Root CA" +# Serial: 4835703278459707669005204 +# MD5 Fingerprint: 3e:45:52:15:09:51:92:e1:b7:5d:37:9f:b1:87:29:8a +# SHA1 Fingerprint: b1:bc:96:8b:d4:f4:9d:62:2a:a8:9a:81:f2:15:01:52:a4:1d:82:9c +# SHA256 Fingerprint: eb:d4:10:40:e4:bb:3e:c7:42:c9:e3:81:d3:1e:f2:a4:1a:48:b6:68:5c:96:e7:ce:f3:c1:df:6c:d4:33:1c:99 +-----BEGIN CERTIFICATE----- +MIIDdTCCAl2gAwIBAgILBAAAAAABFUtaw5QwDQYJKoZIhvcNAQEFBQAwVzELMAkG +A1UEBhMCQkUxGTAXBgNVBAoTEEdsb2JhbFNpZ24gbnYtc2ExEDAOBgNVBAsTB1Jv +b3QgQ0ExGzAZBgNVBAMTEkdsb2JhbFNpZ24gUm9vdCBDQTAeFw05ODA5MDExMjAw +MDBaFw0yODAxMjgxMjAwMDBaMFcxCzAJBgNVBAYTAkJFMRkwFwYDVQQKExBHbG9i +YWxTaWduIG52LXNhMRAwDgYDVQQLEwdSb290IENBMRswGQYDVQQDExJHbG9iYWxT +aWduIFJvb3QgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDaDuaZ +jc6j40+Kfvvxi4Mla+pIH/EqsLmVEQS98GPR4mdmzxzdzxtIK+6NiY6arymAZavp +xy0Sy6scTHAHoT0KMM0VjU/43dSMUBUc71DuxC73/OlS8pF94G3VNTCOXkNz8kHp +1Wrjsok6Vjk4bwY8iGlbKk3Fp1S4bInMm/k8yuX9ifUSPJJ4ltbcdG6TRGHRjcdG +snUOhugZitVtbNV4FpWi6cgKOOvyJBNPc1STE4U6G7weNLWLBYy5d4ux2x8gkasJ +U26Qzns3dLlwR5EiUWMWea6xrkEmCMgZK9FGqkjWZCrXgzT/LCrBbBlDSgeF59N8 +9iFo7+ryUp9/k5DPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8E +BTADAQH/MB0GA1UdDgQWBBRge2YaRQ2XyolQL30EzTSo//z9SzANBgkqhkiG9w0B +AQUFAAOCAQEA1nPnfE920I2/7LqivjTFKDK1fPxsnCwrvQmeU79rXqoRSLblCKOz +yj1hTdNGCbM+w6DjY1Ub8rrvrTnhQ7k4o+YviiY776BQVvnGCv04zcQLcFGUl5gE +38NflNUVyRRBnMRddWQVDf9VMOyGj/8N7yy5Y0b2qvzfvGn9LhJIZJrglfCm7ymP +AbEVtQwdpf5pLGkkeB6zpxxxYu7KyJesF12KwvhHhm4qxFYxldBniYUr+WymXUad +DKqC5JlR3XC321Y9YeRq4VzW9v493kHMB65jUr9TU/Qr6cf9tveCX4XSQRjbgbME +HMUfpIBvFSDJ3gyICh3WZlXi/EjJKSZp4A== +-----END CERTIFICATE----- + +# Issuer: CN=GlobalSign O=GlobalSign OU=GlobalSign Root CA - R2 +# Subject: CN=GlobalSign O=GlobalSign OU=GlobalSign Root CA - R2 +# Label: "GlobalSign Root CA - R2" +# Serial: 4835703278459682885658125 +# MD5 Fingerprint: 94:14:77:7e:3e:5e:fd:8f:30:bd:41:b0:cf:e7:d0:30 +# SHA1 Fingerprint: 75:e0:ab:b6:13:85:12:27:1c:04:f8:5f:dd:de:38:e4:b7:24:2e:fe +# SHA256 Fingerprint: ca:42:dd:41:74:5f:d0:b8:1e:b9:02:36:2c:f9:d8:bf:71:9d:a1:bd:1b:1e:fc:94:6f:5b:4c:99:f4:2c:1b:9e +-----BEGIN CERTIFICATE----- +MIIDujCCAqKgAwIBAgILBAAAAAABD4Ym5g0wDQYJKoZIhvcNAQEFBQAwTDEgMB4G +A1UECxMXR2xvYmFsU2lnbiBSb290IENBIC0gUjIxEzARBgNVBAoTCkdsb2JhbFNp +Z24xEzARBgNVBAMTCkdsb2JhbFNpZ24wHhcNMDYxMjE1MDgwMDAwWhcNMjExMjE1 +MDgwMDAwWjBMMSAwHgYDVQQLExdHbG9iYWxTaWduIFJvb3QgQ0EgLSBSMjETMBEG +A1UEChMKR2xvYmFsU2lnbjETMBEGA1UEAxMKR2xvYmFsU2lnbjCCASIwDQYJKoZI +hvcNAQEBBQADggEPADCCAQoCggEBAKbPJA6+Lm8omUVCxKs+IVSbC9N/hHD6ErPL +v4dfxn+G07IwXNb9rfF73OX4YJYJkhD10FPe+3t+c4isUoh7SqbKSaZeqKeMWhG8 +eoLrvozps6yWJQeXSpkqBy+0Hne/ig+1AnwblrjFuTosvNYSuetZfeLQBoZfXklq +tTleiDTsvHgMCJiEbKjNS7SgfQx5TfC4LcshytVsW33hoCmEofnTlEnLJGKRILzd +C9XZzPnqJworc5HGnRusyMvo4KD0L5CLTfuwNhv2GXqF4G3yYROIXJ/gkwpRl4pa +zq+r1feqCapgvdzZX99yqWATXgAByUr6P6TqBwMhAo6CygPCm48CAwEAAaOBnDCB +mTAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUm+IH +V2ccHsBqBt5ZtJot39wZhi4wNgYDVR0fBC8wLTAroCmgJ4YlaHR0cDovL2NybC5n +bG9iYWxzaWduLm5ldC9yb290LXIyLmNybDAfBgNVHSMEGDAWgBSb4gdXZxwewGoG +3lm0mi3f3BmGLjANBgkqhkiG9w0BAQUFAAOCAQEAmYFThxxol4aR7OBKuEQLq4Gs +J0/WwbgcQ3izDJr86iw8bmEbTUsp9Z8FHSbBuOmDAGJFtqkIk7mpM0sYmsL4h4hO +291xNBrBVNpGP+DTKqttVCL1OmLNIG+6KYnX3ZHu01yiPqFbQfXf5WRDLenVOavS +ot+3i9DAgBkcRcAtjOj4LaR0VknFBbVPFd5uRHg5h6h+u/N5GJG79G+dwfCMNYxd +AfvDbbnvRG15RjF+Cv6pgsH/76tuIMRQyV+dTZsXjAzlAcmgQWpzU/qlULRuJQ/7 +TBj0/VLZjmmx6BEP3ojY+x1J96relc8geMJgEtslQIxq/H5COEBkEveegeGTLg== +-----END CERTIFICATE----- + +# Issuer: CN=VeriSign Class 3 Public Primary Certification Authority - G3 O=VeriSign, Inc. OU=VeriSign Trust Network/(c) 1999 VeriSign, Inc. - For authorized use only +# Subject: CN=VeriSign Class 3 Public Primary Certification Authority - G3 O=VeriSign, Inc. OU=VeriSign Trust Network/(c) 1999 VeriSign, Inc. - For authorized use only +# Label: "Verisign Class 3 Public Primary Certification Authority - G3" +# Serial: 206684696279472310254277870180966723415 +# MD5 Fingerprint: cd:68:b6:a7:c7:c4:ce:75:e0:1d:4f:57:44:61:92:09 +# SHA1 Fingerprint: 13:2d:0d:45:53:4b:69:97:cd:b2:d5:c3:39:e2:55:76:60:9b:5c:c6 +# SHA256 Fingerprint: eb:04:cf:5e:b1:f3:9a:fa:76:2f:2b:b1:20:f2:96:cb:a5:20:c1:b9:7d:b1:58:95:65:b8:1c:b9:a1:7b:72:44 +-----BEGIN CERTIFICATE----- +MIIEGjCCAwICEQCbfgZJoz5iudXukEhxKe9XMA0GCSqGSIb3DQEBBQUAMIHKMQsw +CQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZl +cmlTaWduIFRydXN0IE5ldHdvcmsxOjA4BgNVBAsTMShjKSAxOTk5IFZlcmlTaWdu +LCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxRTBDBgNVBAMTPFZlcmlT +aWduIENsYXNzIDMgUHVibGljIFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3Jp +dHkgLSBHMzAeFw05OTEwMDEwMDAwMDBaFw0zNjA3MTYyMzU5NTlaMIHKMQswCQYD +VQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlT +aWduIFRydXN0IE5ldHdvcmsxOjA4BgNVBAsTMShjKSAxOTk5IFZlcmlTaWduLCBJ +bmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxRTBDBgNVBAMTPFZlcmlTaWdu +IENsYXNzIDMgUHVibGljIFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkg +LSBHMzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMu6nFL8eB8aHm8b +N3O9+MlrlBIwT/A2R/XQkQr1F8ilYcEWQE37imGQ5XYgwREGfassbqb1EUGO+i2t +KmFZpGcmTNDovFJbcCAEWNF6yaRpvIMXZK0Fi7zQWM6NjPXr8EJJC52XJ2cybuGu +kxUccLwgTS8Y3pKI6GyFVxEa6X7jJhFUokWWVYPKMIno3Nij7SqAP395ZVc+FSBm +CC+Vk7+qRy+oRpfwEuL+wgorUeZ25rdGt+INpsyow0xZVYnm6FNcHOqd8GIWC6fJ +Xwzw3sJ2zq/3avL6QaaiMxTJ5Xpj055iN9WFZZ4O5lMkdBteHRJTW8cs54NJOxWu +imi5V5cCAwEAATANBgkqhkiG9w0BAQUFAAOCAQEAERSWwauSCPc/L8my/uRan2Te +2yFPhpk0djZX3dAVL8WtfxUfN2JzPtTnX84XA9s1+ivbrmAJXx5fj267Cz3qWhMe +DGBvtcC1IyIuBwvLqXTLR7sdwdela8wv0kL9Sd2nic9TutoAWii/gt/4uhMdUIaC +/Y4wjylGsB49Ndo4YhYYSq3mtlFs3q9i6wHQHiT+eo8SGhJouPtmmRQURVyu565p +F4ErWjfJXir0xuKhXFSbplQAz/DxwceYMBo7Nhbbo27q/a2ywtrvAkcTisDxszGt +TxzhT5yvDwyd93gN2PQ1VoDat20Xj50egWTh/sVFuq1ruQp6Tk9LhO5L8X3dEQ== +-----END CERTIFICATE----- + +# Issuer: CN=VeriSign Class 4 Public Primary Certification Authority - G3 O=VeriSign, Inc. OU=VeriSign Trust Network/(c) 1999 VeriSign, Inc. - For authorized use only +# Subject: CN=VeriSign Class 4 Public Primary Certification Authority - G3 O=VeriSign, Inc. OU=VeriSign Trust Network/(c) 1999 VeriSign, Inc. - For authorized use only +# Label: "Verisign Class 4 Public Primary Certification Authority - G3" +# Serial: 314531972711909413743075096039378935511 +# MD5 Fingerprint: db:c8:f2:27:2e:b1:ea:6a:29:23:5d:fe:56:3e:33:df +# SHA1 Fingerprint: c8:ec:8c:87:92:69:cb:4b:ab:39:e9:8d:7e:57:67:f3:14:95:73:9d +# SHA256 Fingerprint: e3:89:36:0d:0f:db:ae:b3:d2:50:58:4b:47:30:31:4e:22:2f:39:c1:56:a0:20:14:4e:8d:96:05:61:79:15:06 +-----BEGIN CERTIFICATE----- +MIIEGjCCAwICEQDsoKeLbnVqAc/EfMwvlF7XMA0GCSqGSIb3DQEBBQUAMIHKMQsw +CQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZl +cmlTaWduIFRydXN0IE5ldHdvcmsxOjA4BgNVBAsTMShjKSAxOTk5IFZlcmlTaWdu +LCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxRTBDBgNVBAMTPFZlcmlT +aWduIENsYXNzIDQgUHVibGljIFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3Jp +dHkgLSBHMzAeFw05OTEwMDEwMDAwMDBaFw0zNjA3MTYyMzU5NTlaMIHKMQswCQYD +VQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlT +aWduIFRydXN0IE5ldHdvcmsxOjA4BgNVBAsTMShjKSAxOTk5IFZlcmlTaWduLCBJ +bmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxRTBDBgNVBAMTPFZlcmlTaWdu +IENsYXNzIDQgUHVibGljIFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkg +LSBHMzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAK3LpRFpxlmr8Y+1 +GQ9Wzsy1HyDkniYlS+BzZYlZ3tCD5PUPtbut8XzoIfzk6AzufEUiGXaStBO3IFsJ ++mGuqPKljYXCKtbeZjbSmwL0qJJgfJxptI8kHtCGUvYynEFYHiK9zUVilQhu0Gbd +U6LM8BDcVHOLBKFGMzNcF0C5nk3T875Vg+ixiY5afJqWIpA7iCXy0lOIAgwLePLm +NxdLMEYH5IBtptiWLugs+BGzOA1mppvqySNb247i8xOOGlktqgLw7KSHZtzBP/XY +ufTsgsbSPZUd5cBPhMnZo0QoBmrXRazwa2rvTl/4EYIeOGM0ZlDUPpNz+jDDZq3/ +ky2X7wMCAwEAATANBgkqhkiG9w0BAQUFAAOCAQEAj/ola09b5KROJ1WrIhVZPMq1 +CtRK26vdoV9TxaBXOcLORyu+OshWv8LZJxA6sQU8wHcxuzrTBXttmhwwjIDLk5Mq +g6sFUYICABFna/OIYUdfA5PVWw3g8dShMjWFsjrbsIKr0csKvE+MW8VLADsfKoKm +fjaF3H48ZwC15DtS4KjrXRX5xm3wrR0OhbepmnMUWluPQSjA1egtTaRezarZ7c7c +2NU8Qh0XwRJdRTjDOPP8hS6DRkiy1yBfkjaP53kPmF6Z6PDQpLv1U70qzlmwr25/ +bLvSHgCwIe34QWKCudiyxLtGUPMxxY8BqHTr9Xgn2uf3ZkPznoM+IKrDNWCRzg== +-----END CERTIFICATE----- + +# Issuer: CN=Entrust.net Certification Authority (2048) O=Entrust.net OU=www.entrust.net/CPS_2048 incorp. by ref. (limits liab.)/(c) 1999 Entrust.net Limited +# Subject: CN=Entrust.net Certification Authority (2048) O=Entrust.net OU=www.entrust.net/CPS_2048 incorp. by ref. (limits liab.)/(c) 1999 Entrust.net Limited +# Label: "Entrust.net Premium 2048 Secure Server CA" +# Serial: 946069240 +# MD5 Fingerprint: ee:29:31:bc:32:7e:9a:e6:e8:b5:f7:51:b4:34:71:90 +# SHA1 Fingerprint: 50:30:06:09:1d:97:d4:f5:ae:39:f7:cb:e7:92:7d:7d:65:2d:34:31 +# SHA256 Fingerprint: 6d:c4:71:72:e0:1c:bc:b0:bf:62:58:0d:89:5f:e2:b8:ac:9a:d4:f8:73:80:1e:0c:10:b9:c8:37:d2:1e:b1:77 +-----BEGIN CERTIFICATE----- +MIIEKjCCAxKgAwIBAgIEOGPe+DANBgkqhkiG9w0BAQUFADCBtDEUMBIGA1UEChML +RW50cnVzdC5uZXQxQDA+BgNVBAsUN3d3dy5lbnRydXN0Lm5ldC9DUFNfMjA0OCBp +bmNvcnAuIGJ5IHJlZi4gKGxpbWl0cyBsaWFiLikxJTAjBgNVBAsTHChjKSAxOTk5 +IEVudHJ1c3QubmV0IExpbWl0ZWQxMzAxBgNVBAMTKkVudHJ1c3QubmV0IENlcnRp +ZmljYXRpb24gQXV0aG9yaXR5ICgyMDQ4KTAeFw05OTEyMjQxNzUwNTFaFw0yOTA3 +MjQxNDE1MTJaMIG0MRQwEgYDVQQKEwtFbnRydXN0Lm5ldDFAMD4GA1UECxQ3d3d3 +LmVudHJ1c3QubmV0L0NQU18yMDQ4IGluY29ycC4gYnkgcmVmLiAobGltaXRzIGxp +YWIuKTElMCMGA1UECxMcKGMpIDE5OTkgRW50cnVzdC5uZXQgTGltaXRlZDEzMDEG +A1UEAxMqRW50cnVzdC5uZXQgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgKDIwNDgp +MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEArU1LqRKGsuqjIAcVFmQq +K0vRvwtKTY7tgHalZ7d4QMBzQshowNtTK91euHaYNZOLGp18EzoOH1u3Hs/lJBQe +sYGpjX24zGtLA/ECDNyrpUAkAH90lKGdCCmziAv1h3edVc3kw37XamSrhRSGlVuX +MlBvPci6Zgzj/L24ScF2iUkZ/cCovYmjZy/Gn7xxGWC4LeksyZB2ZnuU4q941mVT +XTzWnLLPKQP5L6RQstRIzgUyVYr9smRMDuSYB3Xbf9+5CFVghTAp+XtIpGmG4zU/ +HoZdenoVve8AjhUiVBcAkCaTvA5JaJG/+EfTnZVCwQ5N328mz8MYIWJmQ3DW1cAH +4QIDAQABo0IwQDAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNV +HQ4EFgQUVeSB0RGAvtiJuQijMfmhJAkWuXAwDQYJKoZIhvcNAQEFBQADggEBADub +j1abMOdTmXx6eadNl9cZlZD7Bh/KM3xGY4+WZiT6QBshJ8rmcnPyT/4xmf3IDExo +U8aAghOY+rat2l098c5u9hURlIIM7j+VrxGrD9cv3h8Dj1csHsm7mhpElesYT6Yf +zX1XEC+bBAlahLVu2B064dae0Wx5XnkcFMXj0EyTO2U87d89vqbllRrDtRnDvV5b +u/8j72gZyxKTJ1wDLW8w0B62GqzeWvfRqqgnpv55gcR5mTNXuhKwqeBCbJPKVt7+ +bYQLCIt+jerXmCHG8+c8eS9enNFMFY3h7CI3zJpDC5fcgJCNs2ebb0gIFVbPv/Er +fF6adulZkMV8gzURZVE= +-----END CERTIFICATE----- + +# Issuer: CN=Baltimore CyberTrust Root O=Baltimore OU=CyberTrust +# Subject: CN=Baltimore CyberTrust Root O=Baltimore OU=CyberTrust +# Label: "Baltimore CyberTrust Root" +# Serial: 33554617 +# MD5 Fingerprint: ac:b6:94:a5:9c:17:e0:d7:91:52:9b:b1:97:06:a6:e4 +# SHA1 Fingerprint: d4:de:20:d0:5e:66:fc:53:fe:1a:50:88:2c:78:db:28:52:ca:e4:74 +# SHA256 Fingerprint: 16:af:57:a9:f6:76:b0:ab:12:60:95:aa:5e:ba:de:f2:2a:b3:11:19:d6:44:ac:95:cd:4b:93:db:f3:f2:6a:eb +-----BEGIN CERTIFICATE----- +MIIDdzCCAl+gAwIBAgIEAgAAuTANBgkqhkiG9w0BAQUFADBaMQswCQYDVQQGEwJJ +RTESMBAGA1UEChMJQmFsdGltb3JlMRMwEQYDVQQLEwpDeWJlclRydXN0MSIwIAYD +VQQDExlCYWx0aW1vcmUgQ3liZXJUcnVzdCBSb290MB4XDTAwMDUxMjE4NDYwMFoX +DTI1MDUxMjIzNTkwMFowWjELMAkGA1UEBhMCSUUxEjAQBgNVBAoTCUJhbHRpbW9y +ZTETMBEGA1UECxMKQ3liZXJUcnVzdDEiMCAGA1UEAxMZQmFsdGltb3JlIEN5YmVy +VHJ1c3QgUm9vdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKMEuyKr +mD1X6CZymrV51Cni4eiVgLGw41uOKymaZN+hXe2wCQVt2yguzmKiYv60iNoS6zjr +IZ3AQSsBUnuId9Mcj8e6uYi1agnnc+gRQKfRzMpijS3ljwumUNKoUMMo6vWrJYeK +mpYcqWe4PwzV9/lSEy/CG9VwcPCPwBLKBsua4dnKM3p31vjsufFoREJIE9LAwqSu +XmD+tqYF/LTdB1kC1FkYmGP1pWPgkAx9XbIGevOF6uvUA65ehD5f/xXtabz5OTZy +dc93Uk3zyZAsuT3lySNTPx8kmCFcB5kpvcY67Oduhjprl3RjM71oGDHweI12v/ye +jl0qhqdNkNwnGjkCAwEAAaNFMEMwHQYDVR0OBBYEFOWdWTCCR1jMrPoIVDaGezq1 +BE3wMBIGA1UdEwEB/wQIMAYBAf8CAQMwDgYDVR0PAQH/BAQDAgEGMA0GCSqGSIb3 +DQEBBQUAA4IBAQCFDF2O5G9RaEIFoN27TyclhAO992T9Ldcw46QQF+vaKSm2eT92 +9hkTI7gQCvlYpNRhcL0EYWoSihfVCr3FvDB81ukMJY2GQE/szKN+OMY3EU/t3Wgx +jkzSswF07r51XgdIGn9w/xZchMB5hbgF/X++ZRGjD8ACtPhSNzkE1akxehi/oCr0 +Epn3o0WC4zxe9Z2etciefC7IpJ5OCBRLbf1wbWsaY71k5h+3zvDyny67G7fyUIhz +ksLi4xaNmjICq44Y3ekQEe5+NauQrz4wlHrQMz2nZQ/1/I6eYs9HRCwBXbsdtTLS +R9I4LtD+gdwyah617jzV/OeBHRnDJELqYzmp +-----END CERTIFICATE----- + +# Issuer: CN=AddTrust Class 1 CA Root O=AddTrust AB OU=AddTrust TTP Network +# Subject: CN=AddTrust Class 1 CA Root O=AddTrust AB OU=AddTrust TTP Network +# Label: "AddTrust Low-Value Services Root" +# Serial: 1 +# MD5 Fingerprint: 1e:42:95:02:33:92:6b:b9:5f:c0:7f:da:d6:b2:4b:fc +# SHA1 Fingerprint: cc:ab:0e:a0:4c:23:01:d6:69:7b:dd:37:9f:cd:12:eb:24:e3:94:9d +# SHA256 Fingerprint: 8c:72:09:27:9a:c0:4e:27:5e:16:d0:7f:d3:b7:75:e8:01:54:b5:96:80:46:e3:1f:52:dd:25:76:63:24:e9:a7 +-----BEGIN CERTIFICATE----- +MIIEGDCCAwCgAwIBAgIBATANBgkqhkiG9w0BAQUFADBlMQswCQYDVQQGEwJTRTEU +MBIGA1UEChMLQWRkVHJ1c3QgQUIxHTAbBgNVBAsTFEFkZFRydXN0IFRUUCBOZXR3 +b3JrMSEwHwYDVQQDExhBZGRUcnVzdCBDbGFzcyAxIENBIFJvb3QwHhcNMDAwNTMw +MTAzODMxWhcNMjAwNTMwMTAzODMxWjBlMQswCQYDVQQGEwJTRTEUMBIGA1UEChML +QWRkVHJ1c3QgQUIxHTAbBgNVBAsTFEFkZFRydXN0IFRUUCBOZXR3b3JrMSEwHwYD +VQQDExhBZGRUcnVzdCBDbGFzcyAxIENBIFJvb3QwggEiMA0GCSqGSIb3DQEBAQUA +A4IBDwAwggEKAoIBAQCWltQhSWDia+hBBwzexODcEyPNwTXH+9ZOEQpnXvUGW2ul +CDtbKRY654eyNAbFvAWlA3yCyykQruGIgb3WntP+LVbBFc7jJp0VLhD7Bo8wBN6n +tGO0/7Gcrjyvd7ZWxbWroulpOj0OM3kyP3CCkplhbY0wCI9xP6ZIVxn4JdxLZlyl +dI+Yrsj5wAYi56xz36Uu+1LcsRVlIPo1Zmne3yzxbrww2ywkEtvrNTVokMsAsJch +PXQhI2U0K7t4WaPW4XY5mqRJjox0r26kmqPZm9I4XJuiGMx1I4S+6+JNM3GOGvDC ++Mcdoq0Dlyz4zyXG9rgkMbFjXZJ/Y/AlyVMuH79NAgMBAAGjgdIwgc8wHQYDVR0O +BBYEFJWxtPCUtr3H2tERCSG+wa9J/RB7MAsGA1UdDwQEAwIBBjAPBgNVHRMBAf8E +BTADAQH/MIGPBgNVHSMEgYcwgYSAFJWxtPCUtr3H2tERCSG+wa9J/RB7oWmkZzBl +MQswCQYDVQQGEwJTRTEUMBIGA1UEChMLQWRkVHJ1c3QgQUIxHTAbBgNVBAsTFEFk +ZFRydXN0IFRUUCBOZXR3b3JrMSEwHwYDVQQDExhBZGRUcnVzdCBDbGFzcyAxIENB +IFJvb3SCAQEwDQYJKoZIhvcNAQEFBQADggEBACxtZBsfzQ3duQH6lmM0MkhHma6X +7f1yFqZzR1r0693p9db7RcwpiURdv0Y5PejuvE1Uhh4dbOMXJ0PhiVYrqW9yTkkz +43J8KiOavD7/KCrto/8cI7pDVwlnTUtiBi34/2ydYB7YHEt9tTEv2dB8Xfjea4MY +eDdXL+gzB2ffHsdrKpV2ro9Xo/D0UrSpUwjP4E/TelOL/bscVjby/rK25Xa71SJl +pz/+0WatC7xrmYbvP33zGDLKe8bjq2RGlfgmadlVg3sslgf/WSxEo8bl6ancoWOA +WiFeIc9TVPC6b4nbqKqVz4vjccweGyBECMB6tkD9xOQ14R0WHNC8K47Wcdk= +-----END CERTIFICATE----- + +# Issuer: CN=AddTrust External CA Root O=AddTrust AB OU=AddTrust External TTP Network +# Subject: CN=AddTrust External CA Root O=AddTrust AB OU=AddTrust External TTP Network +# Label: "AddTrust External Root" +# Serial: 1 +# MD5 Fingerprint: 1d:35:54:04:85:78:b0:3f:42:42:4d:bf:20:73:0a:3f +# SHA1 Fingerprint: 02:fa:f3:e2:91:43:54:68:60:78:57:69:4d:f5:e4:5b:68:85:18:68 +# SHA256 Fingerprint: 68:7f:a4:51:38:22:78:ff:f0:c8:b1:1f:8d:43:d5:76:67:1c:6e:b2:bc:ea:b4:13:fb:83:d9:65:d0:6d:2f:f2 +-----BEGIN CERTIFICATE----- +MIIENjCCAx6gAwIBAgIBATANBgkqhkiG9w0BAQUFADBvMQswCQYDVQQGEwJTRTEU +MBIGA1UEChMLQWRkVHJ1c3QgQUIxJjAkBgNVBAsTHUFkZFRydXN0IEV4dGVybmFs +IFRUUCBOZXR3b3JrMSIwIAYDVQQDExlBZGRUcnVzdCBFeHRlcm5hbCBDQSBSb290 +MB4XDTAwMDUzMDEwNDgzOFoXDTIwMDUzMDEwNDgzOFowbzELMAkGA1UEBhMCU0Ux +FDASBgNVBAoTC0FkZFRydXN0IEFCMSYwJAYDVQQLEx1BZGRUcnVzdCBFeHRlcm5h +bCBUVFAgTmV0d29yazEiMCAGA1UEAxMZQWRkVHJ1c3QgRXh0ZXJuYWwgQ0EgUm9v +dDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALf3GjPm8gAELTngTlvt +H7xsD821+iO2zt6bETOXpClMfZOfvUq8k+0DGuOPz+VtUFrWlymUWoCwSXrbLpX9 +uMq/NzgtHj6RQa1wVsfwTz/oMp50ysiQVOnGXw94nZpAPA6sYapeFI+eh6FqUNzX +mk6vBbOmcZSccbNQYArHE504B4YCqOmoaSYYkKtMsE8jqzpPhNjfzp/haW+710LX +a0Tkx63ubUFfclpxCDezeWWkWaCUN/cALw3CknLa0Dhy2xSoRcRdKn23tNbE7qzN +E0S3ySvdQwAl+mG5aWpYIxG3pzOPVnVZ9c0p10a3CitlttNCbxWyuHv77+ldU9U0 +WicCAwEAAaOB3DCB2TAdBgNVHQ4EFgQUrb2YejS0Jvf6xCZU7wO94CTLVBowCwYD +VR0PBAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wgZkGA1UdIwSBkTCBjoAUrb2YejS0 +Jvf6xCZU7wO94CTLVBqhc6RxMG8xCzAJBgNVBAYTAlNFMRQwEgYDVQQKEwtBZGRU +cnVzdCBBQjEmMCQGA1UECxMdQWRkVHJ1c3QgRXh0ZXJuYWwgVFRQIE5ldHdvcmsx +IjAgBgNVBAMTGUFkZFRydXN0IEV4dGVybmFsIENBIFJvb3SCAQEwDQYJKoZIhvcN +AQEFBQADggEBALCb4IUlwtYj4g+WBpKdQZic2YR5gdkeWxQHIzZlj7DYd7usQWxH +YINRsPkyPef89iYTx4AWpb9a/IfPeHmJIZriTAcKhjW88t5RxNKWt9x+Tu5w/Rw5 +6wwCURQtjr0W4MHfRnXnJK3s9EK0hZNwEGe6nQY1ShjTK3rMUUKhemPR5ruhxSvC +Nr4TDea9Y355e6cJDUCrat2PisP29owaQgVR1EX1n6diIWgVIEM8med8vSTYqZEX +c4g/VhsxOBi0cQ+azcgOno4uG+GMmIPLHzHxREzGBHNJdmAPx/i9F4BrLunMTA5a +mnkPIAou1Z5jJh5VkpTYghdae9C8x49OhgQ= +-----END CERTIFICATE----- + +# Issuer: CN=AddTrust Public CA Root O=AddTrust AB OU=AddTrust TTP Network +# Subject: CN=AddTrust Public CA Root O=AddTrust AB OU=AddTrust TTP Network +# Label: "AddTrust Public Services Root" +# Serial: 1 +# MD5 Fingerprint: c1:62:3e:23:c5:82:73:9c:03:59:4b:2b:e9:77:49:7f +# SHA1 Fingerprint: 2a:b6:28:48:5e:78:fb:f3:ad:9e:79:10:dd:6b:df:99:72:2c:96:e5 +# SHA256 Fingerprint: 07:91:ca:07:49:b2:07:82:aa:d3:c7:d7:bd:0c:df:c9:48:58:35:84:3e:b2:d7:99:60:09:ce:43:ab:6c:69:27 +-----BEGIN CERTIFICATE----- +MIIEFTCCAv2gAwIBAgIBATANBgkqhkiG9w0BAQUFADBkMQswCQYDVQQGEwJTRTEU +MBIGA1UEChMLQWRkVHJ1c3QgQUIxHTAbBgNVBAsTFEFkZFRydXN0IFRUUCBOZXR3 +b3JrMSAwHgYDVQQDExdBZGRUcnVzdCBQdWJsaWMgQ0EgUm9vdDAeFw0wMDA1MzAx +MDQxNTBaFw0yMDA1MzAxMDQxNTBaMGQxCzAJBgNVBAYTAlNFMRQwEgYDVQQKEwtB +ZGRUcnVzdCBBQjEdMBsGA1UECxMUQWRkVHJ1c3QgVFRQIE5ldHdvcmsxIDAeBgNV +BAMTF0FkZFRydXN0IFB1YmxpYyBDQSBSb290MIIBIjANBgkqhkiG9w0BAQEFAAOC +AQ8AMIIBCgKCAQEA6Rowj4OIFMEg2Dybjxt+A3S72mnTRqX4jsIMEZBRpS9mVEBV +6tsfSlbunyNu9DnLoblv8n75XYcmYZ4c+OLspoH4IcUkzBEMP9smcnrHAZcHF/nX +GCwwfQ56HmIexkvA/X1id9NEHif2P0tEs7c42TkfYNVRknMDtABp4/MUTu7R3AnP +dzRGULD4EfL+OHn3Bzn+UZKXC1sIXzSGAa2Il+tmzV7R/9x98oTaunet3IAIx6eH +1lWfl2royBFkuucZKT8Rs3iQhCBSWxHveNCD9tVIkNAwHM+A+WD+eeSI8t0A65RF +62WUaUC6wNW0uLp9BBGo6zEFlpROWCGOn9Bg/QIDAQABo4HRMIHOMB0GA1UdDgQW +BBSBPjfYkrAfd59ctKtzquf2NGAv+jALBgNVHQ8EBAMCAQYwDwYDVR0TAQH/BAUw +AwEB/zCBjgYDVR0jBIGGMIGDgBSBPjfYkrAfd59ctKtzquf2NGAv+qFopGYwZDEL +MAkGA1UEBhMCU0UxFDASBgNVBAoTC0FkZFRydXN0IEFCMR0wGwYDVQQLExRBZGRU +cnVzdCBUVFAgTmV0d29yazEgMB4GA1UEAxMXQWRkVHJ1c3QgUHVibGljIENBIFJv +b3SCAQEwDQYJKoZIhvcNAQEFBQADggEBAAP3FUr4JNojVhaTdt02KLmuG7jD8WS6 +IBh4lSknVwW8fCr0uVFV2ocC3g8WFzH4qnkuCRO7r7IgGRLlk/lL+YPoRNWyQSW/ +iHVv/xD8SlTQX/D67zZzfRs2RcYhbbQVuE7PnFylPVoAjgbjPGsye/Kf8Lb93/Ao +GEjwxrzQvzSAlsJKsW2Ox5BF3i9nrEUEo3rcVZLJR2bYGozH7ZxOmuASu7VqTITh +4SINhwBk/ox9Yjllpu9CtoAlEmEBqCQTcAARJl/6NVDFSMwGR+gn2HCNX2TmoUQm +XiLsks3/QppEIW1cxeMiHV9HEufOX1362KqxMy3ZdvJOOjMMK7MtkAY= +-----END CERTIFICATE----- + +# Issuer: CN=AddTrust Qualified CA Root O=AddTrust AB OU=AddTrust TTP Network +# Subject: CN=AddTrust Qualified CA Root O=AddTrust AB OU=AddTrust TTP Network +# Label: "AddTrust Qualified Certificates Root" +# Serial: 1 +# MD5 Fingerprint: 27:ec:39:47:cd:da:5a:af:e2:9a:01:65:21:a9:4c:bb +# SHA1 Fingerprint: 4d:23:78:ec:91:95:39:b5:00:7f:75:8f:03:3b:21:1e:c5:4d:8b:cf +# SHA256 Fingerprint: 80:95:21:08:05:db:4b:bc:35:5e:44:28:d8:fd:6e:c2:cd:e3:ab:5f:b9:7a:99:42:98:8e:b8:f4:dc:d0:60:16 +-----BEGIN CERTIFICATE----- +MIIEHjCCAwagAwIBAgIBATANBgkqhkiG9w0BAQUFADBnMQswCQYDVQQGEwJTRTEU +MBIGA1UEChMLQWRkVHJ1c3QgQUIxHTAbBgNVBAsTFEFkZFRydXN0IFRUUCBOZXR3 +b3JrMSMwIQYDVQQDExpBZGRUcnVzdCBRdWFsaWZpZWQgQ0EgUm9vdDAeFw0wMDA1 +MzAxMDQ0NTBaFw0yMDA1MzAxMDQ0NTBaMGcxCzAJBgNVBAYTAlNFMRQwEgYDVQQK +EwtBZGRUcnVzdCBBQjEdMBsGA1UECxMUQWRkVHJ1c3QgVFRQIE5ldHdvcmsxIzAh +BgNVBAMTGkFkZFRydXN0IFF1YWxpZmllZCBDQSBSb290MIIBIjANBgkqhkiG9w0B +AQEFAAOCAQ8AMIIBCgKCAQEA5B6a/twJWoekn0e+EV+vhDTbYjx5eLfpMLXsDBwq +xBb/4Oxx64r1EW7tTw2R0hIYLUkVAcKkIhPHEWT/IhKauY5cLwjPcWqzZwFZ8V1G +87B4pfYOQnrjfxvM0PC3KP0q6p6zsLkEqv32x7SxuCqg+1jxGaBvcCV+PmlKfw8i +2O+tCBGaKZnhqkRFmhJePp1tUvznoD1oL/BLcHwTOK28FSXx1s6rosAx1i+f4P8U +WfyEk9mHfExUE+uf0S0R+Bg6Ot4l2ffTQO2kBhLEO+GRwVY18BTcZTYJbqukB8c1 +0cIDMzZbdSZtQvESa0NvS3GU+jQd7RNuyoB/mC9suWXY6QIDAQABo4HUMIHRMB0G +A1UdDgQWBBQ5lYtii1zJ1IC6WA+XPxUIQ8yYpzALBgNVHQ8EBAMCAQYwDwYDVR0T +AQH/BAUwAwEB/zCBkQYDVR0jBIGJMIGGgBQ5lYtii1zJ1IC6WA+XPxUIQ8yYp6Fr +pGkwZzELMAkGA1UEBhMCU0UxFDASBgNVBAoTC0FkZFRydXN0IEFCMR0wGwYDVQQL +ExRBZGRUcnVzdCBUVFAgTmV0d29yazEjMCEGA1UEAxMaQWRkVHJ1c3QgUXVhbGlm +aWVkIENBIFJvb3SCAQEwDQYJKoZIhvcNAQEFBQADggEBABmrder4i2VhlRO6aQTv +hsoToMeqT2QbPxj2qC0sVY8FtzDqQmodwCVRLae/DLPt7wh/bDxGGuoYQ992zPlm +hpwsaPXpF/gxsxjE1kh9I0xowX67ARRvxdlu3rsEQmr49lx95dr6h+sNNVJn0J6X +dgWTP5XHAeZpVTh/EGGZyeNfpso+gmNIquIISD6q8rKFYqa0p9m9N5xotS1WfbC3 +P6CxB9bpT9zeRXEwMn8bLgn5v1Kh7sKAPgZcLlVAwRv1cEWw3F369nJad9Jjzc9Y +iQBCYz95OdBEsIJuQRno3eDBiFrRHnGTHyQwdOUeqN48Jzd/g66ed8/wMLH/S5no +xqE= +-----END CERTIFICATE----- + +# Issuer: CN=Entrust Root Certification Authority O=Entrust, Inc. OU=www.entrust.net/CPS is incorporated by reference/(c) 2006 Entrust, Inc. +# Subject: CN=Entrust Root Certification Authority O=Entrust, Inc. OU=www.entrust.net/CPS is incorporated by reference/(c) 2006 Entrust, Inc. +# Label: "Entrust Root Certification Authority" +# Serial: 1164660820 +# MD5 Fingerprint: d6:a5:c3:ed:5d:dd:3e:00:c1:3d:87:92:1f:1d:3f:e4 +# SHA1 Fingerprint: b3:1e:b1:b7:40:e3:6c:84:02:da:dc:37:d4:4d:f5:d4:67:49:52:f9 +# SHA256 Fingerprint: 73:c1:76:43:4f:1b:c6:d5:ad:f4:5b:0e:76:e7:27:28:7c:8d:e5:76:16:c1:e6:e6:14:1a:2b:2c:bc:7d:8e:4c +-----BEGIN CERTIFICATE----- +MIIEkTCCA3mgAwIBAgIERWtQVDANBgkqhkiG9w0BAQUFADCBsDELMAkGA1UEBhMC +VVMxFjAUBgNVBAoTDUVudHJ1c3QsIEluYy4xOTA3BgNVBAsTMHd3dy5lbnRydXN0 +Lm5ldC9DUFMgaXMgaW5jb3Jwb3JhdGVkIGJ5IHJlZmVyZW5jZTEfMB0GA1UECxMW +KGMpIDIwMDYgRW50cnVzdCwgSW5jLjEtMCsGA1UEAxMkRW50cnVzdCBSb290IENl +cnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTA2MTEyNzIwMjM0MloXDTI2MTEyNzIw +NTM0MlowgbAxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1FbnRydXN0LCBJbmMuMTkw +NwYDVQQLEzB3d3cuZW50cnVzdC5uZXQvQ1BTIGlzIGluY29ycG9yYXRlZCBieSBy +ZWZlcmVuY2UxHzAdBgNVBAsTFihjKSAyMDA2IEVudHJ1c3QsIEluYy4xLTArBgNV +BAMTJEVudHJ1c3QgUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTCCASIwDQYJ +KoZIhvcNAQEBBQADggEPADCCAQoCggEBALaVtkNC+sZtKm9I35RMOVcF7sN5EUFo +Nu3s/poBj6E4KPz3EEZmLk0eGrEaTsbRwJWIsMn/MYszA9u3g3s+IIRe7bJWKKf4 +4LlAcTfFy0cOlypowCKVYhXbR9n10Cv/gkvJrT7eTNuQgFA/CYqEAOwwCj0Yzfv9 +KlmaI5UXLEWeH25DeW0MXJj+SKfFI0dcXv1u5x609mhF0YaDW6KKjbHjKYD+JXGI +rb68j6xSlkuqUY3kEzEZ6E5Nn9uss2rVvDlUccp6en+Q3X0dgNmBu1kmwhH+5pPi +94DkZfs0Nw4pgHBNrziGLp5/V6+eF67rHMsoIV+2HNjnogQi+dPa2MsCAwEAAaOB +sDCBrTAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zArBgNVHRAEJDAi +gA8yMDA2MTEyNzIwMjM0MlqBDzIwMjYxMTI3MjA1MzQyWjAfBgNVHSMEGDAWgBRo +kORnpKZTgMeGZqTx90tD+4S9bTAdBgNVHQ4EFgQUaJDkZ6SmU4DHhmak8fdLQ/uE +vW0wHQYJKoZIhvZ9B0EABBAwDhsIVjcuMTo0LjADAgSQMA0GCSqGSIb3DQEBBQUA +A4IBAQCT1DCw1wMgKtD5Y+iRDAUgqV8ZyntyTtSx29CW+1RaGSwMCPeyvIWonX9t +O1KzKtvn1ISMY/YPyyYBkVBs9F8U4pN0wBOeMDpQ47RgxRzwIkSNcUesyBrJ6Zua +AGAT/3B+XxFNSRuzFVJ7yVTav52Vr2ua2J7p8eRDjeIRRDq/r72DQnNSi6q7pynP +9WQcCk3RvKqsnyrQ/39/2n3qse0wJcGE2jTSW3iDVuycNsMm4hH2Z0kdkquM++v/ +eu6FSqdQgPCnXEqULl8FmTxSQeDNtGPPAUO6nIPcj2A781q0tHuu2guQOHXvgR1m +0vdXcDazv/wor3ElhVsT/h5/WrQ8 +-----END CERTIFICATE----- + +# Issuer: O=RSA Security Inc OU=RSA Security 2048 V3 +# Subject: O=RSA Security Inc OU=RSA Security 2048 V3 +# Label: "RSA Security 2048 v3" +# Serial: 13297492616345471454730593562152402946 +# MD5 Fingerprint: 77:0d:19:b1:21:fd:00:42:9c:3e:0c:a5:dd:0b:02:8e +# SHA1 Fingerprint: 25:01:90:19:cf:fb:d9:99:1c:b7:68:25:74:8d:94:5f:30:93:95:42 +# SHA256 Fingerprint: af:8b:67:62:a1:e5:28:22:81:61:a9:5d:5c:55:9e:e2:66:27:8f:75:d7:9e:83:01:89:a5:03:50:6a:bd:6b:4c +-----BEGIN CERTIFICATE----- +MIIDYTCCAkmgAwIBAgIQCgEBAQAAAnwAAAAKAAAAAjANBgkqhkiG9w0BAQUFADA6 +MRkwFwYDVQQKExBSU0EgU2VjdXJpdHkgSW5jMR0wGwYDVQQLExRSU0EgU2VjdXJp +dHkgMjA0OCBWMzAeFw0wMTAyMjIyMDM5MjNaFw0yNjAyMjIyMDM5MjNaMDoxGTAX +BgNVBAoTEFJTQSBTZWN1cml0eSBJbmMxHTAbBgNVBAsTFFJTQSBTZWN1cml0eSAy +MDQ4IFYzMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAt49VcdKA3Xtp +eafwGFAyPGJn9gqVB93mG/Oe2dJBVGutn3y+Gc37RqtBaB4Y6lXIL5F4iSj7Jylg +/9+PjDvJSZu1pJTOAeo+tWN7fyb9Gd3AIb2E0S1PRsNO3Ng3OTsor8udGuorryGl +wSMiuLgbWhOHV4PR8CDn6E8jQrAApX2J6elhc5SYcSa8LWrg903w8bYqODGBDSnh +AMFRD0xS+ARaqn1y07iHKrtjEAMqs6FPDVpeRrc9DvV07Jmf+T0kgYim3WBU6JU2 +PcYJk5qjEoAAVZkZR73QpXzDuvsf9/UP+Ky5tfQ3mBMY3oVbtwyCO4dvlTlYMNpu +AWgXIszACwIDAQABo2MwYTAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIB +BjAfBgNVHSMEGDAWgBQHw1EwpKrpRa41JPr/JCwz0LGdjDAdBgNVHQ4EFgQUB8NR +MKSq6UWuNST6/yQsM9CxnYwwDQYJKoZIhvcNAQEFBQADggEBAF8+hnZuuDU8TjYc +HnmYv/3VEhF5Ug7uMYm83X/50cYVIeiKAVQNOvtUudZj1LGqlk2iQk3UUx+LEN5/ +Zb5gEydxiKRz44Rj0aRV4VCT5hsOedBnvEbIvz8XDZXmxpBp3ue0L96VfdASPz0+ +f00/FGj1EVDVwfSQpQgdMWD/YIwjVAqv/qFuxdF6Kmh4zx6CCiC0H63lhbJqaHVO +rSU3lIW+vaHU6rcMSzyd6BIA8F+sDeGscGNz9395nzIlQnQFgCi/vcEkllgVsRch +6YlL2weIZ/QVrXA+L02FO8K32/6YaCOJ4XQP3vTFhGMpG8zLB8kApKnXwiJPZ9d3 +7CAFYd4= +-----END CERTIFICATE----- + +# Issuer: CN=GeoTrust Global CA O=GeoTrust Inc. +# Subject: CN=GeoTrust Global CA O=GeoTrust Inc. +# Label: "GeoTrust Global CA" +# Serial: 144470 +# MD5 Fingerprint: f7:75:ab:29:fb:51:4e:b7:77:5e:ff:05:3c:99:8e:f5 +# SHA1 Fingerprint: de:28:f4:a4:ff:e5:b9:2f:a3:c5:03:d1:a3:49:a7:f9:96:2a:82:12 +# SHA256 Fingerprint: ff:85:6a:2d:25:1d:cd:88:d3:66:56:f4:50:12:67:98:cf:ab:aa:de:40:79:9c:72:2d:e4:d2:b5:db:36:a7:3a +-----BEGIN CERTIFICATE----- +MIIDVDCCAjygAwIBAgIDAjRWMA0GCSqGSIb3DQEBBQUAMEIxCzAJBgNVBAYTAlVT +MRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMRswGQYDVQQDExJHZW9UcnVzdCBHbG9i +YWwgQ0EwHhcNMDIwNTIxMDQwMDAwWhcNMjIwNTIxMDQwMDAwWjBCMQswCQYDVQQG +EwJVUzEWMBQGA1UEChMNR2VvVHJ1c3QgSW5jLjEbMBkGA1UEAxMSR2VvVHJ1c3Qg +R2xvYmFsIENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA2swYYzD9 +9BcjGlZ+W988bDjkcbd4kdS8odhM+KhDtgPpTSEHCIjaWC9mOSm9BXiLnTjoBbdq +fnGk5sRgprDvgOSJKA+eJdbtg/OtppHHmMlCGDUUna2YRpIuT8rxh0PBFpVXLVDv +iS2Aelet8u5fa9IAjbkU+BQVNdnARqN7csiRv8lVK83Qlz6cJmTM386DGXHKTubU +1XupGc1V3sjs0l44U+VcT4wt/lAjNvxm5suOpDkZALeVAjmRCw7+OC7RHQWa9k0+ +bw8HHa8sHo9gOeL6NlMTOdReJivbPagUvTLrGAMoUgRx5aszPeE4uwc2hGKceeoW +MPRfwCvocWvk+QIDAQABo1MwUTAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBTA +ephojYn7qwVkDBF9qn1luMrMTjAfBgNVHSMEGDAWgBTAephojYn7qwVkDBF9qn1l +uMrMTjANBgkqhkiG9w0BAQUFAAOCAQEANeMpauUvXVSOKVCUn5kaFOSPeCpilKIn +Z57QzxpeR+nBsqTP3UEaBU6bS+5Kb1VSsyShNwrrZHYqLizz/Tt1kL/6cdjHPTfS +tQWVYrmm3ok9Nns4d0iXrKYgjy6myQzCsplFAMfOEVEiIuCl6rYVSAlk6l5PdPcF +PseKUgzbFbS9bZvlxrFUaKnjaZC2mqUPuLk/IH2uSrW4nOQdtqvmlKXBx4Ot2/Un +hw4EbNX/3aBd7YdStysVAq45pmp06drE57xNNB6pXE0zX5IJL4hmXXeXxx12E6nV +5fEWCRE11azbJHFwLJhWC9kXtNHjUStedejV0NxPNO3CBWaAocvmMw== +-----END CERTIFICATE----- + +# Issuer: CN=GeoTrust Global CA 2 O=GeoTrust Inc. +# Subject: CN=GeoTrust Global CA 2 O=GeoTrust Inc. +# Label: "GeoTrust Global CA 2" +# Serial: 1 +# MD5 Fingerprint: 0e:40:a7:6c:de:03:5d:8f:d1:0f:e4:d1:8d:f9:6c:a9 +# SHA1 Fingerprint: a9:e9:78:08:14:37:58:88:f2:05:19:b0:6d:2b:0d:2b:60:16:90:7d +# SHA256 Fingerprint: ca:2d:82:a0:86:77:07:2f:8a:b6:76:4f:f0:35:67:6c:fe:3e:5e:32:5e:01:21:72:df:3f:92:09:6d:b7:9b:85 +-----BEGIN CERTIFICATE----- +MIIDZjCCAk6gAwIBAgIBATANBgkqhkiG9w0BAQUFADBEMQswCQYDVQQGEwJVUzEW +MBQGA1UEChMNR2VvVHJ1c3QgSW5jLjEdMBsGA1UEAxMUR2VvVHJ1c3QgR2xvYmFs +IENBIDIwHhcNMDQwMzA0MDUwMDAwWhcNMTkwMzA0MDUwMDAwWjBEMQswCQYDVQQG +EwJVUzEWMBQGA1UEChMNR2VvVHJ1c3QgSW5jLjEdMBsGA1UEAxMUR2VvVHJ1c3Qg +R2xvYmFsIENBIDIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDvPE1A +PRDfO1MA4Wf+lGAVPoWI8YkNkMgoI5kF6CsgncbzYEbYwbLVjDHZ3CB5JIG/NTL8 +Y2nbsSpr7iFY8gjpeMtvy/wWUsiRxP89c96xPqfCfWbB9X5SJBri1WeR0IIQ13hL +TytCOb1kLUCgsBDTOEhGiKEMuzozKmKY+wCdE1l/bztyqu6mD4b5BWHqZ38MN5aL +5mkWRxHCJ1kDs6ZgwiFAVvqgx306E+PsV8ez1q6diYD3Aecs9pYrEw15LNnA5IZ7 +S4wMcoKK+xfNAGw6EzywhIdLFnopsk/bHdQL82Y3vdj2V7teJHq4PIu5+pIaGoSe +2HSPqht/XvT+RSIhAgMBAAGjYzBhMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYE +FHE4NvICMVNHK266ZUapEBVYIAUJMB8GA1UdIwQYMBaAFHE4NvICMVNHK266ZUap +EBVYIAUJMA4GA1UdDwEB/wQEAwIBhjANBgkqhkiG9w0BAQUFAAOCAQEAA/e1K6td +EPx7srJerJsOflN4WT5CBP51o62sgU7XAotexC3IUnbHLB/8gTKY0UvGkpMzNTEv +/NgdRN3ggX+d6YvhZJFiCzkIjKx0nVnZellSlxG5FntvRdOW2TF9AjYPnDtuzywN +A0ZF66D0f0hExghAzN4bcLUprbqLOzRldRtxIR0sFAqwlpW41uryZfspuk/qkZN0 +abby/+Ea0AzRdoXLiiW9l14sbxWZJue2Kf8i7MkCx1YAzUm5s2x7UwQa4qjJqhIF +I8LO57sEAszAR6LkxCkvW0VXiVHuPOtSCP8HNR6fNWpHSlaY0VqFH4z1Ir+rzoPz +4iIprn2DQKi6bA== +-----END CERTIFICATE----- + +# Issuer: CN=GeoTrust Universal CA O=GeoTrust Inc. +# Subject: CN=GeoTrust Universal CA O=GeoTrust Inc. +# Label: "GeoTrust Universal CA" +# Serial: 1 +# MD5 Fingerprint: 92:65:58:8b:a2:1a:31:72:73:68:5c:b4:a5:7a:07:48 +# SHA1 Fingerprint: e6:21:f3:35:43:79:05:9a:4b:68:30:9d:8a:2f:74:22:15:87:ec:79 +# SHA256 Fingerprint: a0:45:9b:9f:63:b2:25:59:f5:fa:5d:4c:6d:b3:f9:f7:2f:f1:93:42:03:35:78:f0:73:bf:1d:1b:46:cb:b9:12 +-----BEGIN CERTIFICATE----- +MIIFaDCCA1CgAwIBAgIBATANBgkqhkiG9w0BAQUFADBFMQswCQYDVQQGEwJVUzEW +MBQGA1UEChMNR2VvVHJ1c3QgSW5jLjEeMBwGA1UEAxMVR2VvVHJ1c3QgVW5pdmVy +c2FsIENBMB4XDTA0MDMwNDA1MDAwMFoXDTI5MDMwNDA1MDAwMFowRTELMAkGA1UE +BhMCVVMxFjAUBgNVBAoTDUdlb1RydXN0IEluYy4xHjAcBgNVBAMTFUdlb1RydXN0 +IFVuaXZlcnNhbCBDQTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAKYV +VaCjxuAfjJ0hUNfBvitbtaSeodlyWL0AG0y/YckUHUWCq8YdgNY96xCcOq9tJPi8 +cQGeBvV8Xx7BDlXKg5pZMK4ZyzBIle0iN430SppyZj6tlcDgFgDgEB8rMQ7XlFTT +QjOgNB0eRXbdT8oYN+yFFXoZCPzVx5zw8qkuEKmS5j1YPakWaDwvdSEYfyh3peFh +F7em6fgemdtzbvQKoiFs7tqqhZJmr/Z6a4LauiIINQ/PQvE1+mrufislzDoR5G2v +c7J2Ha3QsnhnGqQ5HFELZ1aD/ThdDc7d8Lsrlh/eezJS/R27tQahsiFepdaVaH/w +mZ7cRQg+59IJDTWU3YBOU5fXtQlEIGQWFwMCTFMNaN7VqnJNk22CDtucvc+081xd +VHppCZbW2xHBjXWotM85yM48vCR85mLK4b19p71XZQvk/iXttmkQ3CgaRr0BHdCX +teGYO8A3ZNY9lO4L4fUorgtWv3GLIylBjobFS1J72HGrH4oVpjuDWtdYAVHGTEHZ +f9hBZ3KiKN9gg6meyHv8U3NyWfWTehd2Ds735VzZC1U0oqpbtWpU5xPKV+yXbfRe +Bi9Fi1jUIxaS5BZuKGNZMN9QAZxjiRqf2xeUgnA3wySemkfWWspOqGmJch+RbNt+ +nhutxx9z3SxPGWX9f5NAEC7S8O08ni4oPmkmM8V7AgMBAAGjYzBhMA8GA1UdEwEB +/wQFMAMBAf8wHQYDVR0OBBYEFNq7LqqwDLiIJlF0XG0D08DYj3rWMB8GA1UdIwQY +MBaAFNq7LqqwDLiIJlF0XG0D08DYj3rWMA4GA1UdDwEB/wQEAwIBhjANBgkqhkiG +9w0BAQUFAAOCAgEAMXjmx7XfuJRAyXHEqDXsRh3ChfMoWIawC/yOsjmPRFWrZIRc +aanQmjg8+uUfNeVE44B5lGiku8SfPeE0zTBGi1QrlaXv9z+ZhP015s8xxtxqv6fX +IwjhmF7DWgh2qaavdy+3YL1ERmrvl/9zlcGO6JP7/TG37FcREUWbMPEaiDnBTzyn +ANXH/KttgCJwpQzgXQQpAvvLoJHRfNbDflDVnVi+QTjruXU8FdmbyUqDWcDaU/0z +uzYYm4UPFd3uLax2k7nZAY1IEKj79TiG8dsKxr2EoyNB3tZ3b4XUhRxQ4K5RirqN +Pnbiucon8l+f725ZDQbYKxek0nxru18UGkiPGkzns0ccjkxFKyDuSN/n3QmOGKja +QI2SJhFTYXNd673nxE0pN2HrrDktZy4W1vUAg4WhzH92xH3kt0tm7wNFYGm2DFKW +koRepqO1pD4r2czYG0eq8kTaT/kD6PAUyz/zg97QwVTjt+gKN02LIFkDMBmhLMi9 +ER/frslKxfMnZmaGrGiR/9nmUxwPi1xpZQomyB40w11Re9epnAahNt3ViZS82eQt +DF4JbAiXfKM9fJP/P6EUp8+1Xevb2xzEdt+Iub1FBZUbrvxGakyvSOPOrg/Sfuvm +bJxPgWp6ZKy7PtXny3YuxadIwVyQD8vIP/rmMuGNG2+k5o7Y+SlIis5z/iw= +-----END CERTIFICATE----- + +# Issuer: CN=GeoTrust Universal CA 2 O=GeoTrust Inc. +# Subject: CN=GeoTrust Universal CA 2 O=GeoTrust Inc. +# Label: "GeoTrust Universal CA 2" +# Serial: 1 +# MD5 Fingerprint: 34:fc:b8:d0:36:db:9e:14:b3:c2:f2:db:8f:e4:94:c7 +# SHA1 Fingerprint: 37:9a:19:7b:41:85:45:35:0c:a6:03:69:f3:3c:2e:af:47:4f:20:79 +# SHA256 Fingerprint: a0:23:4f:3b:c8:52:7c:a5:62:8e:ec:81:ad:5d:69:89:5d:a5:68:0d:c9:1d:1c:b8:47:7f:33:f8:78:b9:5b:0b +-----BEGIN CERTIFICATE----- +MIIFbDCCA1SgAwIBAgIBATANBgkqhkiG9w0BAQUFADBHMQswCQYDVQQGEwJVUzEW +MBQGA1UEChMNR2VvVHJ1c3QgSW5jLjEgMB4GA1UEAxMXR2VvVHJ1c3QgVW5pdmVy +c2FsIENBIDIwHhcNMDQwMzA0MDUwMDAwWhcNMjkwMzA0MDUwMDAwWjBHMQswCQYD +VQQGEwJVUzEWMBQGA1UEChMNR2VvVHJ1c3QgSW5jLjEgMB4GA1UEAxMXR2VvVHJ1 +c3QgVW5pdmVyc2FsIENBIDIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoIC +AQCzVFLByT7y2dyxUxpZKeexw0Uo5dfR7cXFS6GqdHtXr0om/Nj1XqduGdt0DE81 +WzILAePb63p3NeqqWuDW6KFXlPCQo3RWlEQwAx5cTiuFJnSCegx2oG9NzkEtoBUG +FF+3Qs17j1hhNNwqCPkuwwGmIkQcTAeC5lvO0Ep8BNMZcyfwqph/Lq9O64ceJHdq +XbboW0W63MOhBW9Wjo8QJqVJwy7XQYci4E+GymC16qFjwAGXEHm9ADwSbSsVsaxL +se4YuU6W3Nx2/zu+z18DwPw76L5GG//aQMJS9/7jOvdqdzXQ2o3rXhhqMcceujwb +KNZrVMaqW9eiLBsZzKIC9ptZvTdrhrVtgrrY6slWvKk2WP0+GfPtDCapkzj4T8Fd +IgbQl+rhrcZV4IErKIM6+vR7IVEAvlI4zs1meaj0gVbi0IMJR1FbUGrP20gaXT73 +y/Zl92zxlfgCOzJWgjl6W70viRu/obTo/3+NjN8D8WBOWBFM66M/ECuDmgFz2ZRt +hAAnZqzwcEAJQpKtT5MNYQlRJNiS1QuUYbKHsu3/mjX/hVTK7URDrBs8FmtISgoc +QIgfksILAAX/8sgCSqSqqcyZlpwvWOB94b67B9xfBHJcMTTD7F8t4D1kkCLm0ey4 +Lt1ZrtmhN79UNdxzMk+MBB4zsslG8dhcyFVQyWi9qLo2CQIDAQABo2MwYTAPBgNV +HRMBAf8EBTADAQH/MB0GA1UdDgQWBBR281Xh+qQ2+/CfXGJx7Tz0RzgQKzAfBgNV +HSMEGDAWgBR281Xh+qQ2+/CfXGJx7Tz0RzgQKzAOBgNVHQ8BAf8EBAMCAYYwDQYJ +KoZIhvcNAQEFBQADggIBAGbBxiPz2eAubl/oz66wsCVNK/g7WJtAJDday6sWSf+z +dXkzoS9tcBc0kf5nfo/sm+VegqlVHy/c1FEHEv6sFj4sNcZj/NwQ6w2jqtB8zNHQ +L1EuxBRa3ugZ4T7GzKQp5y6EqgYweHZUcyiYWTjgAA1i00J9IZ+uPTqM1fp3DRgr +Fg5fNuH8KrUwJM/gYwx7WBr+mbpCErGR9Hxo4sjoryzqyX6uuyo9DRXcNJW2GHSo +ag/HtPQTxORb7QrSpJdMKu0vbBKJPfEncKpqA1Ihn0CoZ1Dy81of398j9tx4TuaY +T1U6U+Pv8vSfx3zYWK8pIpe44L2RLrB27FcRz+8pRPPphXpgY+RdM4kX2TGq2tbz +GDVyz4crL2MjhF2EjD9XoIj8mZEoJmmZ1I+XRL6O1UixpCgp8RW04eWe3fiPpm8m +1wk8OhwRDqZsN/etRIcsKMfYdIKz0G9KV7s1KSegi+ghp4dkNl3M2Basx7InQJJV +OCiNUW7dFGdTbHFcJoRNdVq2fmBWqU2t+5sel/MN2dKXVHfaPRK34B7vCAas+YWH +6aLcr34YEoP9VhdBLtUpgn2Z9DH2canPLAEnpQW5qrJITirvn5NSUZU8UnOOVkwX +QMAJKOSLakhT2+zNVVXxxvjpoixMptEmX36vWkzaH6byHCx+rgIW0lbQL1dTR+iS +-----END CERTIFICATE----- + +# Issuer: CN=Visa eCommerce Root O=VISA OU=Visa International Service Association +# Subject: CN=Visa eCommerce Root O=VISA OU=Visa International Service Association +# Label: "Visa eCommerce Root" +# Serial: 25952180776285836048024890241505565794 +# MD5 Fingerprint: fc:11:b8:d8:08:93:30:00:6d:23:f9:7e:eb:52:1e:02 +# SHA1 Fingerprint: 70:17:9b:86:8c:00:a4:fa:60:91:52:22:3f:9f:3e:32:bd:e0:05:62 +# SHA256 Fingerprint: 69:fa:c9:bd:55:fb:0a:c7:8d:53:bb:ee:5c:f1:d5:97:98:9f:d0:aa:ab:20:a2:51:51:bd:f1:73:3e:e7:d1:22 +-----BEGIN CERTIFICATE----- +MIIDojCCAoqgAwIBAgIQE4Y1TR0/BvLB+WUF1ZAcYjANBgkqhkiG9w0BAQUFADBr +MQswCQYDVQQGEwJVUzENMAsGA1UEChMEVklTQTEvMC0GA1UECxMmVmlzYSBJbnRl +cm5hdGlvbmFsIFNlcnZpY2UgQXNzb2NpYXRpb24xHDAaBgNVBAMTE1Zpc2EgZUNv +bW1lcmNlIFJvb3QwHhcNMDIwNjI2MDIxODM2WhcNMjIwNjI0MDAxNjEyWjBrMQsw +CQYDVQQGEwJVUzENMAsGA1UEChMEVklTQTEvMC0GA1UECxMmVmlzYSBJbnRlcm5h +dGlvbmFsIFNlcnZpY2UgQXNzb2NpYXRpb24xHDAaBgNVBAMTE1Zpc2EgZUNvbW1l +cmNlIFJvb3QwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCvV95WHm6h +2mCxlCfLF9sHP4CFT8icttD0b0/Pmdjh28JIXDqsOTPHH2qLJj0rNfVIsZHBAk4E +lpF7sDPwsRROEW+1QK8bRaVK7362rPKgH1g/EkZgPI2h4H3PVz4zHvtH8aoVlwdV +ZqW1LS7YgFmypw23RuwhY/81q6UCzyr0TP579ZRdhE2o8mCP2w4lPJ9zcc+U30rq +299yOIzzlr3xF7zSujtFWsan9sYXiwGd/BmoKoMWuDpI/k4+oKsGGelT84ATB+0t +vz8KPFUgOSwsAGl0lUq8ILKpeeUYiZGo3BxN77t+Nwtd/jmliFKMAGzsGHxBvfaL +dXe6YJ2E5/4tAgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQD +AgEGMB0GA1UdDgQWBBQVOIMPPyw/cDMezUb+B4wg4NfDtzANBgkqhkiG9w0BAQUF +AAOCAQEAX/FBfXxcCLkr4NWSR/pnXKUTwwMhmytMiUbPWU3J/qVAtmPN3XEolWcR +zCSs00Rsca4BIGsDoo8Ytyk6feUWYFN4PMCvFYP3j1IzJL1kk5fui/fbGKhtcbP3 +LBfQdCVp9/5rPJS+TUtBjE7ic9DjkCJzQ83z7+pzzkWKsKZJ/0x9nXGIxHYdkFsd +7v3M9+79YKWxehZx0RbQfBI8bGmX265fOZpwLwU8GUYEmSA20GBuYQa7FkKMcPcw +++DbZqMAAb3mLNqRX6BGi01qnD093QVG/na/oAo85ADmJ7f/hC3euiInlhBx6yLt +398znM/jra6O1I7mT1GvFpLgXPYHDw== +-----END CERTIFICATE----- + +# Issuer: CN=Certum CA O=Unizeto Sp. z o.o. +# Subject: CN=Certum CA O=Unizeto Sp. z o.o. +# Label: "Certum Root CA" +# Serial: 65568 +# MD5 Fingerprint: 2c:8f:9f:66:1d:18:90:b1:47:26:9d:8e:86:82:8c:a9 +# SHA1 Fingerprint: 62:52:dc:40:f7:11:43:a2:2f:de:9e:f7:34:8e:06:42:51:b1:81:18 +# SHA256 Fingerprint: d8:e0:fe:bc:1d:b2:e3:8d:00:94:0f:37:d2:7d:41:34:4d:99:3e:73:4b:99:d5:65:6d:97:78:d4:d8:14:36:24 +-----BEGIN CERTIFICATE----- +MIIDDDCCAfSgAwIBAgIDAQAgMA0GCSqGSIb3DQEBBQUAMD4xCzAJBgNVBAYTAlBM +MRswGQYDVQQKExJVbml6ZXRvIFNwLiB6IG8uby4xEjAQBgNVBAMTCUNlcnR1bSBD +QTAeFw0wMjA2MTExMDQ2MzlaFw0yNzA2MTExMDQ2MzlaMD4xCzAJBgNVBAYTAlBM +MRswGQYDVQQKExJVbml6ZXRvIFNwLiB6IG8uby4xEjAQBgNVBAMTCUNlcnR1bSBD +QTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAM6xwS7TT3zNJc4YPk/E +jG+AanPIW1H4m9LcuwBcsaD8dQPugfCI7iNS6eYVM42sLQnFdvkrOYCJ5JdLkKWo +ePhzQ3ukYbDYWMzhbGZ+nPMJXlVjhNWo7/OxLjBos8Q82KxujZlakE403Daaj4GI +ULdtlkIJ89eVgw1BS7Bqa/j8D35in2fE7SZfECYPCE/wpFcozo+47UX2bu4lXapu +Ob7kky/ZR6By6/qmW6/KUz/iDsaWVhFu9+lmqSbYf5VT7QqFiLpPKaVCjF62/IUg +AKpoC6EahQGcxEZjgoi2IrHu/qpGWX7PNSzVttpd90gzFFS269lvzs2I1qsb2pY7 +HVkCAwEAAaMTMBEwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQUFAAOCAQEA +uI3O7+cUus/usESSbLQ5PqKEbq24IXfS1HeCh+YgQYHu4vgRt2PRFze+GXYkHAQa +TOs9qmdvLdTN/mUxcMUbpgIKumB7bVjCmkn+YzILa+M6wKyrO7Do0wlRjBCDxjTg +xSvgGrZgFCdsMneMvLJymM/NzD+5yCRCFNZX/OYmQ6kd5YCQzgNUKD73P9P4Te1q +CjqTE5s7FCMTY5w/0YcneeVMUeMBrYVdGjux1XMQpNPyvG5k9VpWkKjHDkx0Dy5x +O/fIR/RpbxXyEV6DHpx8Uq79AtoSqFlnGNu8cN2bsWntgM6JQEhqDjXKKWYVIZQs +6GAqm4VKQPNriiTsBhYscw== +-----END CERTIFICATE----- + +# Issuer: CN=AAA Certificate Services O=Comodo CA Limited +# Subject: CN=AAA Certificate Services O=Comodo CA Limited +# Label: "Comodo AAA Services root" +# Serial: 1 +# MD5 Fingerprint: 49:79:04:b0:eb:87:19:ac:47:b0:bc:11:51:9b:74:d0 +# SHA1 Fingerprint: d1:eb:23:a4:6d:17:d6:8f:d9:25:64:c2:f1:f1:60:17:64:d8:e3:49 +# SHA256 Fingerprint: d7:a7:a0:fb:5d:7e:27:31:d7:71:e9:48:4e:bc:de:f7:1d:5f:0c:3e:0a:29:48:78:2b:c8:3e:e0:ea:69:9e:f4 +-----BEGIN CERTIFICATE----- +MIIEMjCCAxqgAwIBAgIBATANBgkqhkiG9w0BAQUFADB7MQswCQYDVQQGEwJHQjEb +MBkGA1UECAwSR3JlYXRlciBNYW5jaGVzdGVyMRAwDgYDVQQHDAdTYWxmb3JkMRow +GAYDVQQKDBFDb21vZG8gQ0EgTGltaXRlZDEhMB8GA1UEAwwYQUFBIENlcnRpZmlj +YXRlIFNlcnZpY2VzMB4XDTA0MDEwMTAwMDAwMFoXDTI4MTIzMTIzNTk1OVowezEL +MAkGA1UEBhMCR0IxGzAZBgNVBAgMEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UE +BwwHU2FsZm9yZDEaMBgGA1UECgwRQ29tb2RvIENBIExpbWl0ZWQxITAfBgNVBAMM +GEFBQSBDZXJ0aWZpY2F0ZSBTZXJ2aWNlczCCASIwDQYJKoZIhvcNAQEBBQADggEP +ADCCAQoCggEBAL5AnfRu4ep2hxxNRUSOvkbIgwadwSr+GB+O5AL686tdUIoWMQua +BtDFcCLNSS1UY8y2bmhGC1Pqy0wkwLxyTurxFa70VJoSCsN6sjNg4tqJVfMiWPPe +3M/vg4aijJRPn2jymJBGhCfHdr/jzDUsi14HZGWCwEiwqJH5YZ92IFCokcdmtet4 +YgNW8IoaE+oxox6gmf049vYnMlhvB/VruPsUK6+3qszWY19zjNoFmag4qMsXeDZR +rOme9Hg6jc8P2ULimAyrL58OAd7vn5lJ8S3frHRNG5i1R8XlKdH5kBjHYpy+g8cm +ez6KJcfA3Z3mNWgQIJ2P2N7Sw4ScDV7oL8kCAwEAAaOBwDCBvTAdBgNVHQ4EFgQU +oBEKIz6W8Qfs4q8p74Klf9AwpLQwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQF +MAMBAf8wewYDVR0fBHQwcjA4oDagNIYyaHR0cDovL2NybC5jb21vZG9jYS5jb20v +QUFBQ2VydGlmaWNhdGVTZXJ2aWNlcy5jcmwwNqA0oDKGMGh0dHA6Ly9jcmwuY29t +b2RvLm5ldC9BQUFDZXJ0aWZpY2F0ZVNlcnZpY2VzLmNybDANBgkqhkiG9w0BAQUF +AAOCAQEACFb8AvCb6P+k+tZ7xkSAzk/ExfYAWMymtrwUSWgEdujm7l3sAg9g1o1Q +GE8mTgHj5rCl7r+8dFRBv/38ErjHT1r0iWAFf2C3BUrz9vHCv8S5dIa2LX1rzNLz +Rt0vxuBqw8M0Ayx9lt1awg6nCpnBBYurDC/zXDrPbDdVCYfeU0BsWO/8tqtlbgT2 +G9w84FoVxp7Z8VlIMCFlA2zs6SFz7JsDoeA3raAVGI/6ugLOpyypEBMs1OUIJqsi +l2D4kF501KKaU73yqWjgom7C12yxow+ev+to51byrvLjKzg6CYG1a4XXvi3tPxq3 +smPi9WIsgtRqAEFQ8TmDn5XpNpaYbg== +-----END CERTIFICATE----- + +# Issuer: CN=Secure Certificate Services O=Comodo CA Limited +# Subject: CN=Secure Certificate Services O=Comodo CA Limited +# Label: "Comodo Secure Services root" +# Serial: 1 +# MD5 Fingerprint: d3:d9:bd:ae:9f:ac:67:24:b3:c8:1b:52:e1:b9:a9:bd +# SHA1 Fingerprint: 4a:65:d5:f4:1d:ef:39:b8:b8:90:4a:4a:d3:64:81:33:cf:c7:a1:d1 +# SHA256 Fingerprint: bd:81:ce:3b:4f:65:91:d1:1a:67:b5:fc:7a:47:fd:ef:25:52:1b:f9:aa:4e:18:b9:e3:df:2e:34:a7:80:3b:e8 +-----BEGIN CERTIFICATE----- +MIIEPzCCAyegAwIBAgIBATANBgkqhkiG9w0BAQUFADB+MQswCQYDVQQGEwJHQjEb +MBkGA1UECAwSR3JlYXRlciBNYW5jaGVzdGVyMRAwDgYDVQQHDAdTYWxmb3JkMRow +GAYDVQQKDBFDb21vZG8gQ0EgTGltaXRlZDEkMCIGA1UEAwwbU2VjdXJlIENlcnRp +ZmljYXRlIFNlcnZpY2VzMB4XDTA0MDEwMTAwMDAwMFoXDTI4MTIzMTIzNTk1OVow +fjELMAkGA1UEBhMCR0IxGzAZBgNVBAgMEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4G +A1UEBwwHU2FsZm9yZDEaMBgGA1UECgwRQ29tb2RvIENBIExpbWl0ZWQxJDAiBgNV +BAMMG1NlY3VyZSBDZXJ0aWZpY2F0ZSBTZXJ2aWNlczCCASIwDQYJKoZIhvcNAQEB +BQADggEPADCCAQoCggEBAMBxM4KK0HDrc4eCQNUd5MvJDkKQ+d40uaG6EfQlhfPM +cm3ye5drswfxdySRXyWP9nQ95IDC+DwN879A6vfIUtFyb+/Iq0G4bi4XKpVpDM3S +HpR7LZQdqnXXs5jLrLxkU0C8j6ysNstcrbvd4JQX7NFc0L/vpZXJkMWwrPsbQ996 +CF23uPJAGysnnlDOXmWCiIxe004MeuoIkbY2qitC++rCoznl2yY4rYsK7hljxxwk +3wN42ubqwUcaCwtGCd0C/N7Lh1/XMGNooa7cMqG6vv5Eq2i2pRcV/b3Vp6ea5EQz +6YiO/O1R65NxTq0B50SOqy3LqP4BSUjwwN3HaNiS/j0CAwEAAaOBxzCBxDAdBgNV +HQ4EFgQUPNiTiMLAggnMAZkGkyDpnnAJY08wDgYDVR0PAQH/BAQDAgEGMA8GA1Ud +EwEB/wQFMAMBAf8wgYEGA1UdHwR6MHgwO6A5oDeGNWh0dHA6Ly9jcmwuY29tb2Rv +Y2EuY29tL1NlY3VyZUNlcnRpZmljYXRlU2VydmljZXMuY3JsMDmgN6A1hjNodHRw +Oi8vY3JsLmNvbW9kby5uZXQvU2VjdXJlQ2VydGlmaWNhdGVTZXJ2aWNlcy5jcmww +DQYJKoZIhvcNAQEFBQADggEBAIcBbSMdflsXfcFhMs+P5/OKlFlm4J4oqF7Tt/Q0 +5qo5spcWxYJvMqTpjOev/e/C6LlLqqP05tqNZSH7uoDrJiiFGv45jN5bBAS0VPmj +Z55B+glSzAVIqMk/IQQezkhr/IXownuvf7fM+F86/TXGDe+X3EyrEeFryzHRbPtI +gKvcnDe4IRRLDXE97IMzbtFuMhbsmMcWi1mmNKsFVy2T96oTy9IT4rcuO81rUBcJ +aD61JlfutuC23bkpgHl9j6PwpCikFcSF9CfUa7/lXORlAnZUtOM3ZiTTGWHIUhDl +izeauan5Hb/qmZJhlv8BzaFfDbxxvA6sCx1HRR3B7Hzs/Sk= +-----END CERTIFICATE----- + +# Issuer: CN=Trusted Certificate Services O=Comodo CA Limited +# Subject: CN=Trusted Certificate Services O=Comodo CA Limited +# Label: "Comodo Trusted Services root" +# Serial: 1 +# MD5 Fingerprint: 91:1b:3f:6e:cd:9e:ab:ee:07:fe:1f:71:d2:b3:61:27 +# SHA1 Fingerprint: e1:9f:e3:0e:8b:84:60:9e:80:9b:17:0d:72:a8:c5:ba:6e:14:09:bd +# SHA256 Fingerprint: 3f:06:e5:56:81:d4:96:f5:be:16:9e:b5:38:9f:9f:2b:8f:f6:1e:17:08:df:68:81:72:48:49:cd:5d:27:cb:69 +-----BEGIN CERTIFICATE----- +MIIEQzCCAyugAwIBAgIBATANBgkqhkiG9w0BAQUFADB/MQswCQYDVQQGEwJHQjEb +MBkGA1UECAwSR3JlYXRlciBNYW5jaGVzdGVyMRAwDgYDVQQHDAdTYWxmb3JkMRow +GAYDVQQKDBFDb21vZG8gQ0EgTGltaXRlZDElMCMGA1UEAwwcVHJ1c3RlZCBDZXJ0 +aWZpY2F0ZSBTZXJ2aWNlczAeFw0wNDAxMDEwMDAwMDBaFw0yODEyMzEyMzU5NTla +MH8xCzAJBgNVBAYTAkdCMRswGQYDVQQIDBJHcmVhdGVyIE1hbmNoZXN0ZXIxEDAO +BgNVBAcMB1NhbGZvcmQxGjAYBgNVBAoMEUNvbW9kbyBDQSBMaW1pdGVkMSUwIwYD +VQQDDBxUcnVzdGVkIENlcnRpZmljYXRlIFNlcnZpY2VzMIIBIjANBgkqhkiG9w0B +AQEFAAOCAQ8AMIIBCgKCAQEA33FvNlhTWvI2VFeAxHQIIO0Yfyod5jWaHiWsnOWW +fnJSoBVC21ndZHoa0Lh73TkVvFVIxO06AOoxEbrycXQaZ7jPM8yoMa+j49d/vzMt +TGo87IvDktJTdyR0nAducPy9C1t2ul/y/9c3S0pgePfw+spwtOpZqqPOSC+pw7IL +fhdyFgymBwwbOM/JYrc/oJOlh0Hyt3BAd9i+FHzjqMB6juljatEPmsbS9Is6FARW +1O24zG71++IsWL1/T2sr92AkWCTOJu80kTrV44HQsvAEAtdbtz6SrGsSivnkBbA7 +kUlcsutT6vifR4buv5XAwAaf0lteERv0xwQ1KdJVXOTt6wIDAQABo4HJMIHGMB0G +A1UdDgQWBBTFe1i97doladL3WRaoszLAeydb9DAOBgNVHQ8BAf8EBAMCAQYwDwYD +VR0TAQH/BAUwAwEB/zCBgwYDVR0fBHwwejA8oDqgOIY2aHR0cDovL2NybC5jb21v +ZG9jYS5jb20vVHJ1c3RlZENlcnRpZmljYXRlU2VydmljZXMuY3JsMDqgOKA2hjRo +dHRwOi8vY3JsLmNvbW9kby5uZXQvVHJ1c3RlZENlcnRpZmljYXRlU2VydmljZXMu +Y3JsMA0GCSqGSIb3DQEBBQUAA4IBAQDIk4E7ibSvuIQSTI3S8NtwuleGFTQQuS9/ +HrCoiWChisJ3DFBKmwCL2Iv0QeLQg4pKHBQGsKNoBXAxMKdTmw7pSqBYaWcOrp32 +pSxBvzwGa+RZzG0Q8ZZvH9/0BAKkn0U+yNj6NkZEUD+Cl5EfKNsYEYwq5GWDVxIS +jBc/lDb+XbDABHcTuPQV1T84zJQ6VdCsmPW6AF/ghhmBeC8owH7TzEIK9a5QoNE+ +xqFx7D+gIIxmOom0jtTYsU0lR+4viMi14QVFwL4Ucd56/Y57fU0IlqUSc/Atyjcn +dBInTMu2l+nZrghtWjlA3QVHdWpaIbOjGM9O9y5Xt5hwXsjEeLBi +-----END CERTIFICATE----- + +# Issuer: CN=QuoVadis Root Certification Authority O=QuoVadis Limited OU=Root Certification Authority +# Subject: CN=QuoVadis Root Certification Authority O=QuoVadis Limited OU=Root Certification Authority +# Label: "QuoVadis Root CA" +# Serial: 985026699 +# MD5 Fingerprint: 27:de:36:fe:72:b7:00:03:00:9d:f4:f0:1e:6c:04:24 +# SHA1 Fingerprint: de:3f:40:bd:50:93:d3:9b:6c:60:f6:da:bc:07:62:01:00:89:76:c9 +# SHA256 Fingerprint: a4:5e:de:3b:bb:f0:9c:8a:e1:5c:72:ef:c0:72:68:d6:93:a2:1c:99:6f:d5:1e:67:ca:07:94:60:fd:6d:88:73 +-----BEGIN CERTIFICATE----- +MIIF0DCCBLigAwIBAgIEOrZQizANBgkqhkiG9w0BAQUFADB/MQswCQYDVQQGEwJC +TTEZMBcGA1UEChMQUXVvVmFkaXMgTGltaXRlZDElMCMGA1UECxMcUm9vdCBDZXJ0 +aWZpY2F0aW9uIEF1dGhvcml0eTEuMCwGA1UEAxMlUXVvVmFkaXMgUm9vdCBDZXJ0 +aWZpY2F0aW9uIEF1dGhvcml0eTAeFw0wMTAzMTkxODMzMzNaFw0yMTAzMTcxODMz +MzNaMH8xCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBMaW1pdGVkMSUw +IwYDVQQLExxSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MS4wLAYDVQQDEyVR +dW9WYWRpcyBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIIBIjANBgkqhkiG +9w0BAQEFAAOCAQ8AMIIBCgKCAQEAv2G1lVO6V/z68mcLOhrfEYBklbTRvM16z/Yp +li4kVEAkOPcahdxYTMukJ0KX0J+DisPkBgNbAKVRHnAEdOLB1Dqr1607BxgFjv2D +rOpm2RgbaIr1VxqYuvXtdj182d6UajtLF8HVj71lODqV0D1VNk7feVcxKh7YWWVJ +WCCYfqtffp/p1k3sg3Spx2zY7ilKhSoGFPlU5tPaZQeLYzcS19Dsw3sgQUSj7cug +F+FxZc4dZjH3dgEZyH0DWLaVSR2mEiboxgx24ONmy+pdpibu5cxfvWenAScOospU +xbF6lR1xHkopigPcakXBpBlebzbNw6Kwt/5cOOJSvPhEQ+aQuwIDAQABo4ICUjCC +Ak4wPQYIKwYBBQUHAQEEMTAvMC0GCCsGAQUFBzABhiFodHRwczovL29jc3AucXVv +dmFkaXNvZmZzaG9yZS5jb20wDwYDVR0TAQH/BAUwAwEB/zCCARoGA1UdIASCAREw +ggENMIIBCQYJKwYBBAG+WAABMIH7MIHUBggrBgEFBQcCAjCBxxqBxFJlbGlhbmNl +IG9uIHRoZSBRdW9WYWRpcyBSb290IENlcnRpZmljYXRlIGJ5IGFueSBwYXJ0eSBh +c3N1bWVzIGFjY2VwdGFuY2Ugb2YgdGhlIHRoZW4gYXBwbGljYWJsZSBzdGFuZGFy +ZCB0ZXJtcyBhbmQgY29uZGl0aW9ucyBvZiB1c2UsIGNlcnRpZmljYXRpb24gcHJh +Y3RpY2VzLCBhbmQgdGhlIFF1b1ZhZGlzIENlcnRpZmljYXRlIFBvbGljeS4wIgYI +KwYBBQUHAgEWFmh0dHA6Ly93d3cucXVvdmFkaXMuYm0wHQYDVR0OBBYEFItLbe3T +KbkGGew5Oanwl4Rqy+/fMIGuBgNVHSMEgaYwgaOAFItLbe3TKbkGGew5Oanwl4Rq +y+/foYGEpIGBMH8xCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBMaW1p +dGVkMSUwIwYDVQQLExxSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MS4wLAYD +VQQDEyVRdW9WYWRpcyBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5ggQ6tlCL +MA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQUFAAOCAQEAitQUtf70mpKnGdSk +fnIYj9lofFIk3WdvOXrEql494liwTXCYhGHoG+NpGA7O+0dQoE7/8CQfvbLO9Sf8 +7C9TqnN7Az10buYWnuulLsS/VidQK2K6vkscPFVcQR0kvoIgR13VRH56FmjffU1R +cHhXHTMe/QKZnAzNCgVPx7uOpHX6Sm2xgI4JVrmcGmD+XcHXetwReNDWXcG31a0y +mQM6isxUJTkxgXsTIlG6Rmyhu576BGxJJnSP0nPrzDCi5upZIof4l/UO/erMkqQW +xFIY6iHOsfHmhIHluqmGKPJDWl0Snawe2ajlCmqnf6CHKc/yiU3U7MXi5nrQNiOK +SnQ2+Q== +-----END CERTIFICATE----- + +# Issuer: CN=QuoVadis Root CA 2 O=QuoVadis Limited +# Subject: CN=QuoVadis Root CA 2 O=QuoVadis Limited +# Label: "QuoVadis Root CA 2" +# Serial: 1289 +# MD5 Fingerprint: 5e:39:7b:dd:f8:ba:ec:82:e9:ac:62:ba:0c:54:00:2b +# SHA1 Fingerprint: ca:3a:fb:cf:12:40:36:4b:44:b2:16:20:88:80:48:39:19:93:7c:f7 +# SHA256 Fingerprint: 85:a0:dd:7d:d7:20:ad:b7:ff:05:f8:3d:54:2b:20:9d:c7:ff:45:28:f7:d6:77:b1:83:89:fe:a5:e5:c4:9e:86 +-----BEGIN CERTIFICATE----- +MIIFtzCCA5+gAwIBAgICBQkwDQYJKoZIhvcNAQEFBQAwRTELMAkGA1UEBhMCQk0x +GTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxGzAZBgNVBAMTElF1b1ZhZGlzIFJv +b3QgQ0EgMjAeFw0wNjExMjQxODI3MDBaFw0zMTExMjQxODIzMzNaMEUxCzAJBgNV +BAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBMaW1pdGVkMRswGQYDVQQDExJRdW9W +YWRpcyBSb290IENBIDIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCa +GMpLlA0ALa8DKYrwD4HIrkwZhR0In6spRIXzL4GtMh6QRr+jhiYaHv5+HBg6XJxg +Fyo6dIMzMH1hVBHL7avg5tKifvVrbxi3Cgst/ek+7wrGsxDp3MJGF/hd/aTa/55J +WpzmM+Yklvc/ulsrHHo1wtZn/qtmUIttKGAr79dgw8eTvI02kfN/+NsRE8Scd3bB +rrcCaoF6qUWD4gXmuVbBlDePSHFjIuwXZQeVikvfj8ZaCuWw419eaxGrDPmF60Tp ++ARz8un+XJiM9XOva7R+zdRcAitMOeGylZUtQofX1bOQQ7dsE/He3fbE+Ik/0XX1 +ksOR1YqI0JDs3G3eicJlcZaLDQP9nL9bFqyS2+r+eXyt66/3FsvbzSUr5R/7mp/i +Ucw6UwxI5g69ybR2BlLmEROFcmMDBOAENisgGQLodKcftslWZvB1JdxnwQ5hYIiz +PtGo/KPaHbDRsSNU30R2be1B2MGyIrZTHN81Hdyhdyox5C315eXbyOD/5YDXC2Og +/zOhD7osFRXql7PSorW+8oyWHhqPHWykYTe5hnMz15eWniN9gqRMgeKh0bpnX5UH +oycR7hYQe7xFSkyyBNKr79X9DFHOUGoIMfmR2gyPZFwDwzqLID9ujWc9Otb+fVuI +yV77zGHcizN300QyNQliBJIWENieJ0f7OyHj+OsdWwIDAQABo4GwMIGtMA8GA1Ud +EwEB/wQFMAMBAf8wCwYDVR0PBAQDAgEGMB0GA1UdDgQWBBQahGK8SEwzJQTU7tD2 +A8QZRtGUazBuBgNVHSMEZzBlgBQahGK8SEwzJQTU7tD2A8QZRtGUa6FJpEcwRTEL +MAkGA1UEBhMCQk0xGTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxGzAZBgNVBAMT +ElF1b1ZhZGlzIFJvb3QgQ0EgMoICBQkwDQYJKoZIhvcNAQEFBQADggIBAD4KFk2f +BluornFdLwUvZ+YTRYPENvbzwCYMDbVHZF34tHLJRqUDGCdViXh9duqWNIAXINzn +g/iN/Ae42l9NLmeyhP3ZRPx3UIHmfLTJDQtyU/h2BwdBR5YM++CCJpNVjP4iH2Bl +fF/nJrP3MpCYUNQ3cVX2kiF495V5+vgtJodmVjB3pjd4M1IQWK4/YY7yarHvGH5K +WWPKjaJW1acvvFYfzznB4vsKqBUsfU16Y8Zsl0Q80m/DShcK+JDSV6IZUaUtl0Ha +B0+pUNqQjZRG4T7wlP0QADj1O+hA4bRuVhogzG9Yje0uRY/W6ZM/57Es3zrWIozc +hLsib9D45MY56QSIPMO661V6bYCZJPVsAfv4l7CUW+v90m/xd2gNNWQjrLhVoQPR +TUIZ3Ph1WVaj+ahJefivDrkRoHy3au000LYmYjgahwz46P0u05B/B5EqHdZ+XIWD +mbA4CD/pXvk1B+TJYm5Xf6dQlfe6yJvmjqIBxdZmv3lh8zwc4bmCXF2gw+nYSL0Z +ohEUGW6yhhtoPkg3Goi3XZZenMfvJ2II4pEZXNLxId26F0KCl3GBUzGpn/Z9Yr9y +4aOTHcyKJloJONDO1w2AFrR4pTqHTI2KpdVGl/IsELm8VCLAAVBpQ570su9t+Oza +8eOx79+Rj1QqCyXBJhnEUhAFZdWCEOrCMc0u +-----END CERTIFICATE----- + +# Issuer: CN=QuoVadis Root CA 3 O=QuoVadis Limited +# Subject: CN=QuoVadis Root CA 3 O=QuoVadis Limited +# Label: "QuoVadis Root CA 3" +# Serial: 1478 +# MD5 Fingerprint: 31:85:3c:62:94:97:63:b9:aa:fd:89:4e:af:6f:e0:cf +# SHA1 Fingerprint: 1f:49:14:f7:d8:74:95:1d:dd:ae:02:c0:be:fd:3a:2d:82:75:51:85 +# SHA256 Fingerprint: 18:f1:fc:7f:20:5d:f8:ad:dd:eb:7f:e0:07:dd:57:e3:af:37:5a:9c:4d:8d:73:54:6b:f4:f1:fe:d1:e1:8d:35 +-----BEGIN CERTIFICATE----- +MIIGnTCCBIWgAwIBAgICBcYwDQYJKoZIhvcNAQEFBQAwRTELMAkGA1UEBhMCQk0x +GTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxGzAZBgNVBAMTElF1b1ZhZGlzIFJv +b3QgQ0EgMzAeFw0wNjExMjQxOTExMjNaFw0zMTExMjQxOTA2NDRaMEUxCzAJBgNV +BAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBMaW1pdGVkMRswGQYDVQQDExJRdW9W +YWRpcyBSb290IENBIDMwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDM +V0IWVJzmmNPTTe7+7cefQzlKZbPoFog02w1ZkXTPkrgEQK0CSzGrvI2RaNggDhoB +4hp7Thdd4oq3P5kazethq8Jlph+3t723j/z9cI8LoGe+AaJZz3HmDyl2/7FWeUUr +H556VOijKTVopAFPD6QuN+8bv+OPEKhyq1hX51SGyMnzW9os2l2ObjyjPtr7guXd +8lyyBTNvijbO0BNO/79KDDRMpsMhvVAEVeuxu537RR5kFd5VAYwCdrXLoT9Cabwv +vWhDFlaJKjdhkf2mrk7AyxRllDdLkgbvBNDInIjbC3uBr7E9KsRlOni27tyAsdLT +mZw67mtaa7ONt9XOnMK+pUsvFrGeaDsGb659n/je7Mwpp5ijJUMv7/FfJuGITfhe +btfZFG4ZM2mnO4SJk8RTVROhUXhA+LjJou57ulJCg54U7QVSWllWp5f8nT8KKdjc +T5EOE7zelaTfi5m+rJsziO+1ga8bxiJTyPbH7pcUsMV8eFLI8M5ud2CEpukqdiDt +WAEXMJPpGovgc2PZapKUSU60rUqFxKMiMPwJ7Wgic6aIDFUhWMXhOp8q3crhkODZ +c6tsgLjoC2SToJyMGf+z0gzskSaHirOi4XCPLArlzW1oUevaPwV/izLmE1xr/l9A +4iLItLRkT9a6fUg+qGkM17uGcclzuD87nSVL2v9A6wIDAQABo4IBlTCCAZEwDwYD +VR0TAQH/BAUwAwEB/zCB4QYDVR0gBIHZMIHWMIHTBgkrBgEEAb5YAAMwgcUwgZMG +CCsGAQUFBwICMIGGGoGDQW55IHVzZSBvZiB0aGlzIENlcnRpZmljYXRlIGNvbnN0 +aXR1dGVzIGFjY2VwdGFuY2Ugb2YgdGhlIFF1b1ZhZGlzIFJvb3QgQ0EgMyBDZXJ0 +aWZpY2F0ZSBQb2xpY3kgLyBDZXJ0aWZpY2F0aW9uIFByYWN0aWNlIFN0YXRlbWVu +dC4wLQYIKwYBBQUHAgEWIWh0dHA6Ly93d3cucXVvdmFkaXNnbG9iYWwuY29tL2Nw +czALBgNVHQ8EBAMCAQYwHQYDVR0OBBYEFPLAE+CCQz777i9nMpY1XNu4ywLQMG4G +A1UdIwRnMGWAFPLAE+CCQz777i9nMpY1XNu4ywLQoUmkRzBFMQswCQYDVQQGEwJC +TTEZMBcGA1UEChMQUXVvVmFkaXMgTGltaXRlZDEbMBkGA1UEAxMSUXVvVmFkaXMg +Um9vdCBDQSAzggIFxjANBgkqhkiG9w0BAQUFAAOCAgEAT62gLEz6wPJv92ZVqyM0 +7ucp2sNbtrCD2dDQ4iH782CnO11gUyeim/YIIirnv6By5ZwkajGxkHon24QRiSem +d1o417+shvzuXYO8BsbRd2sPbSQvS3pspweWyuOEn62Iix2rFo1bZhfZFvSLgNLd ++LJ2w/w4E6oM3kJpK27zPOuAJ9v1pkQNn1pVWQvVDVJIxa6f8i+AxeoyUDUSly7B +4f/xI4hROJ/yZlZ25w9Rl6VSDE1JUZU2Pb+iSwwQHYaZTKrzchGT5Or2m9qoXadN +t54CrnMAyNojA+j56hl0YgCUyyIgvpSnWbWCar6ZeXqp8kokUvd0/bpO5qgdAm6x +DYBEwa7TIzdfu4V8K5Iu6H6li92Z4b8nby1dqnuH/grdS/yO9SbkbnBCbjPsMZ57 +k8HkyWkaPcBrTiJt7qtYTcbQQcEr6k8Sh17rRdhs9ZgC06DYVYoGmRmioHfRMJ6s +zHXug/WwYjnPbFfiTNKRCw51KBuav/0aQ/HKd/s7j2G4aSgWQgRecCocIdiP4b0j +Wy10QJLZYxkNc91pvGJHvOB0K7Lrfb5BG7XARsWhIstfTsEokt4YutUqKLsRixeT +mJlglFwjz1onl14LBQaTNx47aTbrqZ5hHY8y2o4M1nQ+ewkk2gF3R8Q7zTSMmfXK +4SVhM7JZG+Ju1zdXtg2pEto= +-----END CERTIFICATE----- + +# Issuer: O=SECOM Trust.net OU=Security Communication RootCA1 +# Subject: O=SECOM Trust.net OU=Security Communication RootCA1 +# Label: "Security Communication Root CA" +# Serial: 0 +# MD5 Fingerprint: f1:bc:63:6a:54:e0:b5:27:f5:cd:e7:1a:e3:4d:6e:4a +# SHA1 Fingerprint: 36:b1:2b:49:f9:81:9e:d7:4c:9e:bc:38:0f:c6:56:8f:5d:ac:b2:f7 +# SHA256 Fingerprint: e7:5e:72:ed:9f:56:0e:ec:6e:b4:80:00:73:a4:3f:c3:ad:19:19:5a:39:22:82:01:78:95:97:4a:99:02:6b:6c +-----BEGIN CERTIFICATE----- +MIIDWjCCAkKgAwIBAgIBADANBgkqhkiG9w0BAQUFADBQMQswCQYDVQQGEwJKUDEY +MBYGA1UEChMPU0VDT00gVHJ1c3QubmV0MScwJQYDVQQLEx5TZWN1cml0eSBDb21t +dW5pY2F0aW9uIFJvb3RDQTEwHhcNMDMwOTMwMDQyMDQ5WhcNMjMwOTMwMDQyMDQ5 +WjBQMQswCQYDVQQGEwJKUDEYMBYGA1UEChMPU0VDT00gVHJ1c3QubmV0MScwJQYD +VQQLEx5TZWN1cml0eSBDb21tdW5pY2F0aW9uIFJvb3RDQTEwggEiMA0GCSqGSIb3 +DQEBAQUAA4IBDwAwggEKAoIBAQCzs/5/022x7xZ8V6UMbXaKL0u/ZPtM7orw8yl8 +9f/uKuDp6bpbZCKamm8sOiZpUQWZJtzVHGpxxpp9Hp3dfGzGjGdnSj74cbAZJ6kJ +DKaVv0uMDPpVmDvY6CKhS3E4eayXkmmziX7qIWgGmBSWh9JhNrxtJ1aeV+7AwFb9 +Ms+k2Y7CI9eNqPPYJayX5HA49LY6tJ07lyZDo6G8SVlyTCMwhwFY9k6+HGhWZq/N +QV3Is00qVUarH9oe4kA92819uZKAnDfdDJZkndwi92SL32HeFZRSFaB9UslLqCHJ +xrHty8OVYNEP8Ktw+N/LTX7s1vqr2b1/VPKl6Xn62dZ2JChzAgMBAAGjPzA9MB0G +A1UdDgQWBBSgc0mZaNyFW2XjmygvV5+9M7wHSDALBgNVHQ8EBAMCAQYwDwYDVR0T +AQH/BAUwAwEB/zANBgkqhkiG9w0BAQUFAAOCAQEAaECpqLvkT115swW1F7NgE+vG +kl3g0dNq/vu+m22/xwVtWSDEHPC32oRYAmP6SBbvT6UL90qY8j+eG61Ha2POCEfr +Uj94nK9NrvjVT8+amCoQQTlSxN3Zmw7vkwGusi7KaEIkQmywszo+zenaSMQVy+n5 +Bw+SUEmK3TGXX8npN6o7WWWXlDLJs58+OmJYxUmtYg5xpTKqL8aJdkNAExNnPaJU +JRDL8Try2frbSVa7pv6nQTXD4IhhyYjH3zYQIphZ6rBK+1YWc26sTfcioU+tHXot +RSflMMFe8toTyyVCUZVHA4xsIcx0Qu1T/zOLjw9XARYvz6buyXAiFL39vmwLAw== +-----END CERTIFICATE----- + +# Issuer: CN=Sonera Class2 CA O=Sonera +# Subject: CN=Sonera Class2 CA O=Sonera +# Label: "Sonera Class 2 Root CA" +# Serial: 29 +# MD5 Fingerprint: a3:ec:75:0f:2e:88:df:fa:48:01:4e:0b:5c:48:6f:fb +# SHA1 Fingerprint: 37:f7:6d:e6:07:7c:90:c5:b1:3e:93:1a:b7:41:10:b4:f2:e4:9a:27 +# SHA256 Fingerprint: 79:08:b4:03:14:c1:38:10:0b:51:8d:07:35:80:7f:fb:fc:f8:51:8a:00:95:33:71:05:ba:38:6b:15:3d:d9:27 +-----BEGIN CERTIFICATE----- +MIIDIDCCAgigAwIBAgIBHTANBgkqhkiG9w0BAQUFADA5MQswCQYDVQQGEwJGSTEP +MA0GA1UEChMGU29uZXJhMRkwFwYDVQQDExBTb25lcmEgQ2xhc3MyIENBMB4XDTAx +MDQwNjA3Mjk0MFoXDTIxMDQwNjA3Mjk0MFowOTELMAkGA1UEBhMCRkkxDzANBgNV +BAoTBlNvbmVyYTEZMBcGA1UEAxMQU29uZXJhIENsYXNzMiBDQTCCASIwDQYJKoZI +hvcNAQEBBQADggEPADCCAQoCggEBAJAXSjWdyvANlsdE+hY3/Ei9vX+ALTU74W+o +Z6m/AxxNjG8yR9VBaKQTBME1DJqEQ/xcHf+Js+gXGM2RX/uJ4+q/Tl18GybTdXnt +5oTjV+WtKcT0OijnpXuENmmz/V52vaMtmdOQTiMofRhj8VQ7Jp12W5dCsv+u8E7s +3TmVToMGf+dJQMjFAbJUWmYdPfz56TwKnoG4cPABi+QjVHzIrviQHgCWctRUz2Ej +vOr7nQKV0ba5cTppCD8PtOFCx4j1P5iop7oc4HFx71hXgVB6XGt0Rg6DA5jDjqhu +8nYybieDwnPz3BjotJPqdURrBGAgcVeHnfO+oJAjPYok4doh28MCAwEAAaMzMDEw +DwYDVR0TAQH/BAUwAwEB/zARBgNVHQ4ECgQISqCqWITTXjwwCwYDVR0PBAQDAgEG +MA0GCSqGSIb3DQEBBQUAA4IBAQBazof5FnIVV0sd2ZvnoiYw7JNn39Yt0jSv9zil +zqsWuasvfDXLrNAPtEwr/IDva4yRXzZ299uzGxnq9LIR/WFxRL8oszodv7ND6J+/ +3DEIcbCdjdY0RzKQxmUk96BKfARzjzlvF4xytb1LyHr4e4PDKE6cCepnP7JnBBvD +FNr450kkkdAdavphOe9r5yF1BgfYErQhIHBCcYHaPJo2vqZbDWpsmh+Re/n570K6 +Tk6ezAyNlNzZRZxe7EJQY670XcSxEtzKO6gunRRaBXW37Ndj4ro1tgQIkejanZz2 +ZrUYrAqmVCY0M9IbwdR/GjqOC6oybtv8TyWf2TLHllpwrN9M +-----END CERTIFICATE----- + +# Issuer: CN=Staat der Nederlanden Root CA O=Staat der Nederlanden +# Subject: CN=Staat der Nederlanden Root CA O=Staat der Nederlanden +# Label: "Staat der Nederlanden Root CA" +# Serial: 10000010 +# MD5 Fingerprint: 60:84:7c:5a:ce:db:0c:d4:cb:a7:e9:fe:02:c6:a9:c0 +# SHA1 Fingerprint: 10:1d:fa:3f:d5:0b:cb:bb:9b:b5:60:0c:19:55:a4:1a:f4:73:3a:04 +# SHA256 Fingerprint: d4:1d:82:9e:8c:16:59:82:2a:f9:3f:ce:62:bf:fc:de:26:4f:c8:4e:8b:95:0c:5f:f2:75:d0:52:35:46:95:a3 +-----BEGIN CERTIFICATE----- +MIIDujCCAqKgAwIBAgIEAJiWijANBgkqhkiG9w0BAQUFADBVMQswCQYDVQQGEwJO +TDEeMBwGA1UEChMVU3RhYXQgZGVyIE5lZGVybGFuZGVuMSYwJAYDVQQDEx1TdGFh +dCBkZXIgTmVkZXJsYW5kZW4gUm9vdCBDQTAeFw0wMjEyMTcwOTIzNDlaFw0xNTEy +MTYwOTE1MzhaMFUxCzAJBgNVBAYTAk5MMR4wHAYDVQQKExVTdGFhdCBkZXIgTmVk +ZXJsYW5kZW4xJjAkBgNVBAMTHVN0YWF0IGRlciBOZWRlcmxhbmRlbiBSb290IENB +MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAmNK1URF6gaYUmHFtvszn +ExvWJw56s2oYHLZhWtVhCb/ekBPHZ+7d89rFDBKeNVU+LCeIQGv33N0iYfXCxw71 +9tV2U02PjLwYdjeFnejKScfST5gTCaI+Ioicf9byEGW07l8Y1Rfj+MX94p2i71MO +hXeiD+EwR+4A5zN9RGcaC1Hoi6CeUJhoNFIfLm0B8mBF8jHrqTFoKbt6QZ7GGX+U +tFE5A3+y3qcym7RHjm+0Sq7lr7HcsBthvJly3uSJt3omXdozSVtSnA71iq3DuD3o +BmrC1SoLbHuEvVYFy4ZlkuxEK7COudxwC0barbxjiDn622r+I/q85Ej0ZytqERAh +SQIDAQABo4GRMIGOMAwGA1UdEwQFMAMBAf8wTwYDVR0gBEgwRjBEBgRVHSAAMDww +OgYIKwYBBQUHAgEWLmh0dHA6Ly93d3cucGtpb3ZlcmhlaWQubmwvcG9saWNpZXMv +cm9vdC1wb2xpY3kwDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBSofeu8Y6R0E3QA +7Jbg0zTBLL9s+DANBgkqhkiG9w0BAQUFAAOCAQEABYSHVXQ2YcG70dTGFagTtJ+k +/rvuFbQvBgwp8qiSpGEN/KtcCFtREytNwiphyPgJWPwtArI5fZlmgb9uXJVFIGzm +eafR2Bwp/MIgJ1HI8XxdNGdphREwxgDS1/PTfLbwMVcoEoJz6TMvplW0C5GUR5z6 +u3pCMuiufi3IvKwUv9kP2Vv8wfl6leF9fpb8cbDCTMjfRTTJzg3ynGQI0DvDKcWy +7ZAEwbEpkcUwb8GpcjPM/l0WFywRaed+/sWDCN+83CI6LiBpIzlWYGeQiy52OfsR +iJf2fL1LuCAWZwWN4jvBcj+UlTfHXbme2JOhF4//DGYVwSR8MnwDHTuhWEUykw== +-----END CERTIFICATE----- + +# Issuer: CN=UTN - DATACorp SGC O=The USERTRUST Network OU=http://www.usertrust.com +# Subject: CN=UTN - DATACorp SGC O=The USERTRUST Network OU=http://www.usertrust.com +# Label: "UTN DATACorp SGC Root CA" +# Serial: 91374294542884689855167577680241077609 +# MD5 Fingerprint: b3:a5:3e:77:21:6d:ac:4a:c0:c9:fb:d5:41:3d:ca:06 +# SHA1 Fingerprint: 58:11:9f:0e:12:82:87:ea:50:fd:d9:87:45:6f:4f:78:dc:fa:d6:d4 +# SHA256 Fingerprint: 85:fb:2f:91:dd:12:27:5a:01:45:b6:36:53:4f:84:02:4a:d6:8b:69:b8:ee:88:68:4f:f7:11:37:58:05:b3:48 +-----BEGIN CERTIFICATE----- +MIIEXjCCA0agAwIBAgIQRL4Mi1AAIbQR0ypoBqmtaTANBgkqhkiG9w0BAQUFADCB +kzELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAlVUMRcwFQYDVQQHEw5TYWx0IExha2Ug +Q2l0eTEeMBwGA1UEChMVVGhlIFVTRVJUUlVTVCBOZXR3b3JrMSEwHwYDVQQLExho +dHRwOi8vd3d3LnVzZXJ0cnVzdC5jb20xGzAZBgNVBAMTElVUTiAtIERBVEFDb3Jw +IFNHQzAeFw05OTA2MjQxODU3MjFaFw0xOTA2MjQxOTA2MzBaMIGTMQswCQYDVQQG +EwJVUzELMAkGA1UECBMCVVQxFzAVBgNVBAcTDlNhbHQgTGFrZSBDaXR5MR4wHAYD +VQQKExVUaGUgVVNFUlRSVVNUIE5ldHdvcmsxITAfBgNVBAsTGGh0dHA6Ly93d3cu +dXNlcnRydXN0LmNvbTEbMBkGA1UEAxMSVVROIC0gREFUQUNvcnAgU0dDMIIBIjAN +BgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA3+5YEKIrblXEjr8uRgnn4AgPLit6 +E5Qbvfa2gI5lBZMAHryv4g+OGQ0SR+ysraP6LnD43m77VkIVni5c7yPeIbkFdicZ +D0/Ww5y0vpQZY/KmEQrrU0icvvIpOxboGqBMpsn0GFlowHDyUwDAXlCCpVZvNvlK +4ESGoE1O1kduSUrLZ9emxAW5jh70/P/N5zbgnAVssjMiFdC04MwXwLLA9P4yPykq +lXvY8qdOD1R8oQ2AswkDwf9c3V6aPryuvEeKaq5xyh+xKrhfQgUL7EYw0XILyulW +bfXv33i+Ybqypa4ETLyorGkVl73v67SMvzX41MPRKA5cOp9wGDMgd8SirwIDAQAB +o4GrMIGoMAsGA1UdDwQEAwIBxjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBRT +MtGzz3/64PGgXYVOktKeRR20TzA9BgNVHR8ENjA0MDKgMKAuhixodHRwOi8vY3Js +LnVzZXJ0cnVzdC5jb20vVVROLURBVEFDb3JwU0dDLmNybDAqBgNVHSUEIzAhBggr +BgEFBQcDAQYKKwYBBAGCNwoDAwYJYIZIAYb4QgQBMA0GCSqGSIb3DQEBBQUAA4IB +AQAnNZcAiosovcYzMB4p/OL31ZjUQLtgyr+rFywJNn9Q+kHcrpY6CiM+iVnJowft +Gzet/Hy+UUla3joKVAgWRcKZsYfNjGjgaQPpxE6YsjuMFrMOoAyYUJuTqXAJyCyj +j98C5OBxOvG0I3KgqgHf35g+FFCgMSa9KOlaMCZ1+XtgHI3zzVAmbQQnmt/VDUVH +KWss5nbZqSl9Mt3JNjy9rjXxEZ4du5A/EkdOjtd+D2JzHVImOBwYSf0wdJrE5SIv +2MCN7ZF6TACPcn9d2t0bi0Vr591pl6jFVkwPDPafepE39peC4N1xaf92P2BNPM/3 +mfnGV/TJVTl4uix5yaaIK/QI +-----END CERTIFICATE----- + +# Issuer: CN=UTN-USERFirst-Hardware O=The USERTRUST Network OU=http://www.usertrust.com +# Subject: CN=UTN-USERFirst-Hardware O=The USERTRUST Network OU=http://www.usertrust.com +# Label: "UTN USERFirst Hardware Root CA" +# Serial: 91374294542884704022267039221184531197 +# MD5 Fingerprint: 4c:56:41:e5:0d:bb:2b:e8:ca:a3:ed:18:08:ad:43:39 +# SHA1 Fingerprint: 04:83:ed:33:99:ac:36:08:05:87:22:ed:bc:5e:46:00:e3:be:f9:d7 +# SHA256 Fingerprint: 6e:a5:47:41:d0:04:66:7e:ed:1b:48:16:63:4a:a3:a7:9e:6e:4b:96:95:0f:82:79:da:fc:8d:9b:d8:81:21:37 +-----BEGIN CERTIFICATE----- +MIIEdDCCA1ygAwIBAgIQRL4Mi1AAJLQR0zYq/mUK/TANBgkqhkiG9w0BAQUFADCB +lzELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAlVUMRcwFQYDVQQHEw5TYWx0IExha2Ug +Q2l0eTEeMBwGA1UEChMVVGhlIFVTRVJUUlVTVCBOZXR3b3JrMSEwHwYDVQQLExho +dHRwOi8vd3d3LnVzZXJ0cnVzdC5jb20xHzAdBgNVBAMTFlVUTi1VU0VSRmlyc3Qt +SGFyZHdhcmUwHhcNOTkwNzA5MTgxMDQyWhcNMTkwNzA5MTgxOTIyWjCBlzELMAkG +A1UEBhMCVVMxCzAJBgNVBAgTAlVUMRcwFQYDVQQHEw5TYWx0IExha2UgQ2l0eTEe +MBwGA1UEChMVVGhlIFVTRVJUUlVTVCBOZXR3b3JrMSEwHwYDVQQLExhodHRwOi8v +d3d3LnVzZXJ0cnVzdC5jb20xHzAdBgNVBAMTFlVUTi1VU0VSRmlyc3QtSGFyZHdh +cmUwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCx98M4P7Sof885glFn +0G2f0v9Y8+efK+wNiVSZuTiZFvfgIXlIwrthdBKWHTxqctU8EGc6Oe0rE81m65UJ +M6Rsl7HoxuzBdXmcRl6Nq9Bq/bkqVRcQVLMZ8Jr28bFdtqdt++BxF2uiiPsA3/4a +MXcMmgF6sTLjKwEHOG7DpV4jvEWbe1DByTCP2+UretNb+zNAHqDVmBe8i4fDidNd +oI6yqqr2jmmIBsX6iSHzCJ1pLgkzmykNRg+MzEk0sGlRvfkGzWitZky8PqxhvQqI +DsjfPe58BEydCl5rkdbux+0ojatNh4lz0G6k0B4WixThdkQDf2Os5M1JnMWS9Ksy +oUhbAgMBAAGjgbkwgbYwCwYDVR0PBAQDAgHGMA8GA1UdEwEB/wQFMAMBAf8wHQYD +VR0OBBYEFKFyXyYbKJhDlV0HN9WFlp1L0sNFMEQGA1UdHwQ9MDswOaA3oDWGM2h0 +dHA6Ly9jcmwudXNlcnRydXN0LmNvbS9VVE4tVVNFUkZpcnN0LUhhcmR3YXJlLmNy +bDAxBgNVHSUEKjAoBggrBgEFBQcDAQYIKwYBBQUHAwUGCCsGAQUFBwMGBggrBgEF +BQcDBzANBgkqhkiG9w0BAQUFAAOCAQEARxkP3nTGmZev/K0oXnWO6y1n7k57K9cM +//bey1WiCuFMVGWTYGufEpytXoMs61quwOQt9ABjHbjAbPLPSbtNk28Gpgoiskli +CE7/yMgUsogWXecB5BKV5UU0s4tpvc+0hY91UZ59Ojg6FEgSxvunOxqNDYJAB+gE +CJChicsZUN/KHAG8HQQZexB2lzvukJDKxA4fFm517zP4029bHpbj4HR3dHuKom4t +3XbWOTCC8KucUvIqx69JXn7HaOWCgchqJ/kniCrVWFCVH/A7HFe7fRQ5YiuayZSS +KqMiDP+JJn1fIytH1xUdqWqeUQ0qUZ6B+dQ7XnASfxAynB67nfhmqA== +-----END CERTIFICATE----- + +# Issuer: CN=Chambers of Commerce Root O=AC Camerfirma SA CIF A82743287 OU=http://www.chambersign.org +# Subject: CN=Chambers of Commerce Root O=AC Camerfirma SA CIF A82743287 OU=http://www.chambersign.org +# Label: "Camerfirma Chambers of Commerce Root" +# Serial: 0 +# MD5 Fingerprint: b0:01:ee:14:d9:af:29:18:94:76:8e:f1:69:33:2a:84 +# SHA1 Fingerprint: 6e:3a:55:a4:19:0c:19:5c:93:84:3c:c0:db:72:2e:31:30:61:f0:b1 +# SHA256 Fingerprint: 0c:25:8a:12:a5:67:4a:ef:25:f2:8b:a7:dc:fa:ec:ee:a3:48:e5:41:e6:f5:cc:4e:e6:3b:71:b3:61:60:6a:c3 +-----BEGIN CERTIFICATE----- +MIIEvTCCA6WgAwIBAgIBADANBgkqhkiG9w0BAQUFADB/MQswCQYDVQQGEwJFVTEn +MCUGA1UEChMeQUMgQ2FtZXJmaXJtYSBTQSBDSUYgQTgyNzQzMjg3MSMwIQYDVQQL +ExpodHRwOi8vd3d3LmNoYW1iZXJzaWduLm9yZzEiMCAGA1UEAxMZQ2hhbWJlcnMg +b2YgQ29tbWVyY2UgUm9vdDAeFw0wMzA5MzAxNjEzNDNaFw0zNzA5MzAxNjEzNDRa +MH8xCzAJBgNVBAYTAkVVMScwJQYDVQQKEx5BQyBDYW1lcmZpcm1hIFNBIENJRiBB +ODI3NDMyODcxIzAhBgNVBAsTGmh0dHA6Ly93d3cuY2hhbWJlcnNpZ24ub3JnMSIw +IAYDVQQDExlDaGFtYmVycyBvZiBDb21tZXJjZSBSb290MIIBIDANBgkqhkiG9w0B +AQEFAAOCAQ0AMIIBCAKCAQEAtzZV5aVdGDDg2olUkfzIx1L4L1DZ77F1c2VHfRtb +unXF/KGIJPov7coISjlUxFF6tdpg6jg8gbLL8bvZkSM/SAFwdakFKq0fcfPJVD0d +BmpAPrMMhe5cG3nCYsS4No41XQEMIwRHNaqbYE6gZj3LJgqcQKH0XZi/caulAGgq +7YN6D6IUtdQis4CwPAxaUWktWBiP7Zme8a7ileb2R6jWDA+wWFjbw2Y3npuRVDM3 +0pQcakjJyfKl2qUMI/cjDpwyVV5xnIQFUZot/eZOKjRa3spAN2cMVCFVd9oKDMyX +roDclDZK9D7ONhMeU+SsTjoF7Nuucpw4i9A5O4kKPnf+dQIBA6OCAUQwggFAMBIG +A1UdEwEB/wQIMAYBAf8CAQwwPAYDVR0fBDUwMzAxoC+gLYYraHR0cDovL2NybC5j +aGFtYmVyc2lnbi5vcmcvY2hhbWJlcnNyb290LmNybDAdBgNVHQ4EFgQU45T1sU3p +26EpW1eLTXYGduHRooowDgYDVR0PAQH/BAQDAgEGMBEGCWCGSAGG+EIBAQQEAwIA +BzAnBgNVHREEIDAegRxjaGFtYmVyc3Jvb3RAY2hhbWJlcnNpZ24ub3JnMCcGA1Ud +EgQgMB6BHGNoYW1iZXJzcm9vdEBjaGFtYmVyc2lnbi5vcmcwWAYDVR0gBFEwTzBN +BgsrBgEEAYGHLgoDATA+MDwGCCsGAQUFBwIBFjBodHRwOi8vY3BzLmNoYW1iZXJz +aWduLm9yZy9jcHMvY2hhbWJlcnNyb290Lmh0bWwwDQYJKoZIhvcNAQEFBQADggEB +AAxBl8IahsAifJ/7kPMa0QOx7xP5IV8EnNrJpY0nbJaHkb5BkAFyk+cefV/2icZd +p0AJPaxJRUXcLo0waLIJuvvDL8y6C98/d3tGfToSJI6WjzwFCm/SlCgdbQzALogi +1djPHRPH8EjX1wWnz8dHnjs8NMiAT9QUu/wNUPf6s+xCX6ndbcj0dc97wXImsQEc +XCz9ek60AcUFV7nnPKoF2YjpB0ZBzu9Bga5Y34OirsrXdx/nADydb47kMgkdTXg0 +eDQ8lJsm7U9xxhl6vSAiSFr+S30Dt+dYvsYyTnQeaN2oaFuzPu5ifdmA6Ap1erfu +tGWaIZDgqtCYvDi1czyL+Nw= +-----END CERTIFICATE----- + +# Issuer: CN=Global Chambersign Root O=AC Camerfirma SA CIF A82743287 OU=http://www.chambersign.org +# Subject: CN=Global Chambersign Root O=AC Camerfirma SA CIF A82743287 OU=http://www.chambersign.org +# Label: "Camerfirma Global Chambersign Root" +# Serial: 0 +# MD5 Fingerprint: c5:e6:7b:bf:06:d0:4f:43:ed:c4:7a:65:8a:fb:6b:19 +# SHA1 Fingerprint: 33:9b:6b:14:50:24:9b:55:7a:01:87:72:84:d9:e0:2f:c3:d2:d8:e9 +# SHA256 Fingerprint: ef:3c:b4:17:fc:8e:bf:6f:97:87:6c:9e:4e:ce:39:de:1e:a5:fe:64:91:41:d1:02:8b:7d:11:c0:b2:29:8c:ed +-----BEGIN CERTIFICATE----- +MIIExTCCA62gAwIBAgIBADANBgkqhkiG9w0BAQUFADB9MQswCQYDVQQGEwJFVTEn +MCUGA1UEChMeQUMgQ2FtZXJmaXJtYSBTQSBDSUYgQTgyNzQzMjg3MSMwIQYDVQQL +ExpodHRwOi8vd3d3LmNoYW1iZXJzaWduLm9yZzEgMB4GA1UEAxMXR2xvYmFsIENo +YW1iZXJzaWduIFJvb3QwHhcNMDMwOTMwMTYxNDE4WhcNMzcwOTMwMTYxNDE4WjB9 +MQswCQYDVQQGEwJFVTEnMCUGA1UEChMeQUMgQ2FtZXJmaXJtYSBTQSBDSUYgQTgy +NzQzMjg3MSMwIQYDVQQLExpodHRwOi8vd3d3LmNoYW1iZXJzaWduLm9yZzEgMB4G +A1UEAxMXR2xvYmFsIENoYW1iZXJzaWduIFJvb3QwggEgMA0GCSqGSIb3DQEBAQUA +A4IBDQAwggEIAoIBAQCicKLQn0KuWxfH2H3PFIP8T8mhtxOviteePgQKkotgVvq0 +Mi+ITaFgCPS3CU6gSS9J1tPfnZdan5QEcOw/Wdm3zGaLmFIoCQLfxS+EjXqXd7/s +QJ0lcqu1PzKY+7e3/HKE5TWH+VX6ox8Oby4o3Wmg2UIQxvi1RMLQQ3/bvOSiPGpV +eAp3qdjqGTK3L/5cPxvusZjsyq16aUXjlg9V9ubtdepl6DJWk0aJqCWKZQbua795 +B9Dxt6/tLE2Su8CoX6dnfQTyFQhwrJLWfQTSM/tMtgsL+xrJxI0DqX5c8lCrEqWh +z0hQpe/SyBoT+rB/sYIcd2oPX9wLlY/vQ37mRQklAgEDo4IBUDCCAUwwEgYDVR0T +AQH/BAgwBgEB/wIBDDA/BgNVHR8EODA2MDSgMqAwhi5odHRwOi8vY3JsLmNoYW1i +ZXJzaWduLm9yZy9jaGFtYmVyc2lnbnJvb3QuY3JsMB0GA1UdDgQWBBRDnDafsJ4w +TcbOX60Qq+UDpfqpFDAOBgNVHQ8BAf8EBAMCAQYwEQYJYIZIAYb4QgEBBAQDAgAH +MCoGA1UdEQQjMCGBH2NoYW1iZXJzaWducm9vdEBjaGFtYmVyc2lnbi5vcmcwKgYD +VR0SBCMwIYEfY2hhbWJlcnNpZ25yb290QGNoYW1iZXJzaWduLm9yZzBbBgNVHSAE +VDBSMFAGCysGAQQBgYcuCgEBMEEwPwYIKwYBBQUHAgEWM2h0dHA6Ly9jcHMuY2hh +bWJlcnNpZ24ub3JnL2Nwcy9jaGFtYmVyc2lnbnJvb3QuaHRtbDANBgkqhkiG9w0B +AQUFAAOCAQEAPDtwkfkEVCeR4e3t/mh/YV3lQWVPMvEYBZRqHN4fcNs+ezICNLUM +bKGKfKX0j//U2K0X1S0E0T9YgOKBWYi+wONGkyT+kL0mojAt6JcmVzWJdJYY9hXi +ryQZVgICsroPFOrGimbBhkVVi76SvpykBMdJPJ7oKXqJ1/6v/2j1pReQvayZzKWG +VwlnRtvWFsJG8eSpUPWP0ZIV018+xgBJOm5YstHRJw0lyDL4IBHNfTIzSJRUTN3c +ecQwn+uOuFW114hcxWokPbLTBQNRxgfvzBRydD1ucs4YKIxKoHflCStFREest2d/ +AYoFWpO+ocH/+OcOZ6RHSXZddZAa9SaP8A== +-----END CERTIFICATE----- + +# Issuer: CN=NetLock Kozjegyzoi (Class A) Tanusitvanykiado O=NetLock Halozatbiztonsagi Kft. OU=Tanusitvanykiadok +# Subject: CN=NetLock Kozjegyzoi (Class A) Tanusitvanykiado O=NetLock Halozatbiztonsagi Kft. OU=Tanusitvanykiadok +# Label: "NetLock Notary (Class A) Root" +# Serial: 259 +# MD5 Fingerprint: 86:38:6d:5e:49:63:6c:85:5c:db:6d:dc:94:b7:d0:f7 +# SHA1 Fingerprint: ac:ed:5f:65:53:fd:25:ce:01:5f:1f:7a:48:3b:6a:74:9f:61:78:c6 +# SHA256 Fingerprint: 7f:12:cd:5f:7e:5e:29:0e:c7:d8:51:79:d5:b7:2c:20:a5:be:75:08:ff:db:5b:f8:1a:b9:68:4a:7f:c9:f6:67 +-----BEGIN CERTIFICATE----- +MIIGfTCCBWWgAwIBAgICAQMwDQYJKoZIhvcNAQEEBQAwga8xCzAJBgNVBAYTAkhV +MRAwDgYDVQQIEwdIdW5nYXJ5MREwDwYDVQQHEwhCdWRhcGVzdDEnMCUGA1UEChMe +TmV0TG9jayBIYWxvemF0Yml6dG9uc2FnaSBLZnQuMRowGAYDVQQLExFUYW51c2l0 +dmFueWtpYWRvazE2MDQGA1UEAxMtTmV0TG9jayBLb3pqZWd5em9pIChDbGFzcyBB +KSBUYW51c2l0dmFueWtpYWRvMB4XDTk5MDIyNDIzMTQ0N1oXDTE5MDIxOTIzMTQ0 +N1owga8xCzAJBgNVBAYTAkhVMRAwDgYDVQQIEwdIdW5nYXJ5MREwDwYDVQQHEwhC +dWRhcGVzdDEnMCUGA1UEChMeTmV0TG9jayBIYWxvemF0Yml6dG9uc2FnaSBLZnQu +MRowGAYDVQQLExFUYW51c2l0dmFueWtpYWRvazE2MDQGA1UEAxMtTmV0TG9jayBL +b3pqZWd5em9pIChDbGFzcyBBKSBUYW51c2l0dmFueWtpYWRvMIIBIjANBgkqhkiG +9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvHSMD7tM9DceqQWC2ObhbHDqeLVu0ThEDaiD +zl3S1tWBxdRL51uUcCbbO51qTGL3cfNk1mE7PetzozfZz+qMkjvN9wfcZnSX9EUi +3fRc4L9t875lM+QVOr/bmJBVOMTtplVjC7B4BPTjbsE/jvxReB+SnoPC/tmwqcm8 +WgD/qaiYdPv2LD4VOQ22BFWoDpggQrOxJa1+mm9dU7GrDPzr4PN6s6iz/0b2Y6LY +Oph7tqyF/7AlT3Rj5xMHpQqPBffAZG9+pyeAlt7ULoZgx2srXnN7F+eRP2QM2Esi +NCubMvJIH5+hCoR64sKtlz2O1cH5VqNQ6ca0+pii7pXmKgOM3wIDAQABo4ICnzCC +ApswDgYDVR0PAQH/BAQDAgAGMBIGA1UdEwEB/wQIMAYBAf8CAQQwEQYJYIZIAYb4 +QgEBBAQDAgAHMIICYAYJYIZIAYb4QgENBIICURaCAk1GSUdZRUxFTSEgRXplbiB0 +YW51c2l0dmFueSBhIE5ldExvY2sgS2Z0LiBBbHRhbGFub3MgU3pvbGdhbHRhdGFz +aSBGZWx0ZXRlbGVpYmVuIGxlaXJ0IGVsamFyYXNvayBhbGFwamFuIGtlc3p1bHQu +IEEgaGl0ZWxlc2l0ZXMgZm9seWFtYXRhdCBhIE5ldExvY2sgS2Z0LiB0ZXJtZWtm +ZWxlbG9zc2VnLWJpenRvc2l0YXNhIHZlZGkuIEEgZGlnaXRhbGlzIGFsYWlyYXMg +ZWxmb2dhZGFzYW5hayBmZWx0ZXRlbGUgYXogZWxvaXJ0IGVsbGVub3J6ZXNpIGVs +amFyYXMgbWVndGV0ZWxlLiBBeiBlbGphcmFzIGxlaXJhc2EgbWVndGFsYWxoYXRv +IGEgTmV0TG9jayBLZnQuIEludGVybmV0IGhvbmxhcGphbiBhIGh0dHBzOi8vd3d3 +Lm5ldGxvY2submV0L2RvY3MgY2ltZW4gdmFneSBrZXJoZXRvIGF6IGVsbGVub3J6 +ZXNAbmV0bG9jay5uZXQgZS1tYWlsIGNpbWVuLiBJTVBPUlRBTlQhIFRoZSBpc3N1 +YW5jZSBhbmQgdGhlIHVzZSBvZiB0aGlzIGNlcnRpZmljYXRlIGlzIHN1YmplY3Qg +dG8gdGhlIE5ldExvY2sgQ1BTIGF2YWlsYWJsZSBhdCBodHRwczovL3d3dy5uZXRs +b2NrLm5ldC9kb2NzIG9yIGJ5IGUtbWFpbCBhdCBjcHNAbmV0bG9jay5uZXQuMA0G +CSqGSIb3DQEBBAUAA4IBAQBIJEb3ulZv+sgoA0BO5TE5ayZrU3/b39/zcT0mwBQO +xmd7I6gMc90Bu8bKbjc5VdXHjFYgDigKDtIqpLBJUsY4B/6+CgmM0ZjPytoUMaFP +0jn8DxEsQ8Pdq5PHVT5HfBgaANzze9jyf1JsIPQLX2lS9O74silg6+NJMSEN1rUQ +QeJBCWziGppWS3cC9qCbmieH6FUpccKQn0V4GuEVZD3QDtigdp+uxdAu6tYPVuxk +f1qbFFgBJ34TUMdrKuZoPL9coAob4Q566eKAw+np9v1sEZ7Q5SgnK1QyQhSCdeZK +8CtmdWOMovsEPoMOmzbwGOQmIMOM8CgHrTwXZoi1/baI +-----END CERTIFICATE----- + +# Issuer: CN=XRamp Global Certification Authority O=XRamp Security Services Inc OU=www.xrampsecurity.com +# Subject: CN=XRamp Global Certification Authority O=XRamp Security Services Inc OU=www.xrampsecurity.com +# Label: "XRamp Global CA Root" +# Serial: 107108908803651509692980124233745014957 +# MD5 Fingerprint: a1:0b:44:b3:ca:10:d8:00:6e:9d:0f:d8:0f:92:0a:d1 +# SHA1 Fingerprint: b8:01:86:d1:eb:9c:86:a5:41:04:cf:30:54:f3:4c:52:b7:e5:58:c6 +# SHA256 Fingerprint: ce:cd:dc:90:50:99:d8:da:df:c5:b1:d2:09:b7:37:cb:e2:c1:8c:fb:2c:10:c0:ff:0b:cf:0d:32:86:fc:1a:a2 +-----BEGIN CERTIFICATE----- +MIIEMDCCAxigAwIBAgIQUJRs7Bjq1ZxN1ZfvdY+grTANBgkqhkiG9w0BAQUFADCB +gjELMAkGA1UEBhMCVVMxHjAcBgNVBAsTFXd3dy54cmFtcHNlY3VyaXR5LmNvbTEk +MCIGA1UEChMbWFJhbXAgU2VjdXJpdHkgU2VydmljZXMgSW5jMS0wKwYDVQQDEyRY +UmFtcCBHbG9iYWwgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMDQxMTAxMTcx +NDA0WhcNMzUwMTAxMDUzNzE5WjCBgjELMAkGA1UEBhMCVVMxHjAcBgNVBAsTFXd3 +dy54cmFtcHNlY3VyaXR5LmNvbTEkMCIGA1UEChMbWFJhbXAgU2VjdXJpdHkgU2Vy +dmljZXMgSW5jMS0wKwYDVQQDEyRYUmFtcCBHbG9iYWwgQ2VydGlmaWNhdGlvbiBB +dXRob3JpdHkwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCYJB69FbS6 +38eMpSe2OAtp87ZOqCwuIR1cRN8hXX4jdP5efrRKt6atH67gBhbim1vZZ3RrXYCP +KZ2GG9mcDZhtdhAoWORlsH9KmHmf4MMxfoArtYzAQDsRhtDLooY2YKTVMIJt2W7Q +DxIEM5dfT2Fa8OT5kavnHTu86M/0ay00fOJIYRyO82FEzG+gSqmUsE3a56k0enI4 +qEHMPJQRfevIpoy3hsvKMzvZPTeL+3o+hiznc9cKV6xkmxnr9A8ECIqsAxcZZPRa +JSKNNCyy9mgdEm3Tih4U2sSPpuIjhdV6Db1q4Ons7Be7QhtnqiXtRYMh/MHJfNVi +PvryxS3T/dRlAgMBAAGjgZ8wgZwwEwYJKwYBBAGCNxQCBAYeBABDAEEwCwYDVR0P +BAQDAgGGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFMZPoj0GY4QJnM5i5ASs +jVy16bYbMDYGA1UdHwQvMC0wK6ApoCeGJWh0dHA6Ly9jcmwueHJhbXBzZWN1cml0 +eS5jb20vWEdDQS5jcmwwEAYJKwYBBAGCNxUBBAMCAQEwDQYJKoZIhvcNAQEFBQAD +ggEBAJEVOQMBG2f7Shz5CmBbodpNl2L5JFMn14JkTpAuw0kbK5rc/Kh4ZzXxHfAR +vbdI4xD2Dd8/0sm2qlWkSLoC295ZLhVbO50WfUfXN+pfTXYSNrsf16GBBEYgoyxt +qZ4Bfj8pzgCT3/3JknOJiWSe5yvkHJEs0rnOfc5vMZnT5r7SHpDwCRR5XCOrTdLa +IR9NmXmd4c8nnxCbHIgNsIpkQTG4DmyQJKSbXHGPurt+HBvbaoAPIbzp26a3QPSy +i6mx5O+aGtA9aZnuqCij4Tyz8LIRnM98QObd50N9otg6tamN8jSZxNQQ4Qb9CYQQ +O+7ETPTsJ3xCwnR8gooJybQDJbw= +-----END CERTIFICATE----- + +# Issuer: O=The Go Daddy Group, Inc. OU=Go Daddy Class 2 Certification Authority +# Subject: O=The Go Daddy Group, Inc. OU=Go Daddy Class 2 Certification Authority +# Label: "Go Daddy Class 2 CA" +# Serial: 0 +# MD5 Fingerprint: 91:de:06:25:ab:da:fd:32:17:0c:bb:25:17:2a:84:67 +# SHA1 Fingerprint: 27:96:ba:e6:3f:18:01:e2:77:26:1b:a0:d7:77:70:02:8f:20:ee:e4 +# SHA256 Fingerprint: c3:84:6b:f2:4b:9e:93:ca:64:27:4c:0e:c6:7c:1e:cc:5e:02:4f:fc:ac:d2:d7:40:19:35:0e:81:fe:54:6a:e4 +-----BEGIN CERTIFICATE----- +MIIEADCCAuigAwIBAgIBADANBgkqhkiG9w0BAQUFADBjMQswCQYDVQQGEwJVUzEh +MB8GA1UEChMYVGhlIEdvIERhZGR5IEdyb3VwLCBJbmMuMTEwLwYDVQQLEyhHbyBE +YWRkeSBDbGFzcyAyIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTA0MDYyOTE3 +MDYyMFoXDTM0MDYyOTE3MDYyMFowYzELMAkGA1UEBhMCVVMxITAfBgNVBAoTGFRo +ZSBHbyBEYWRkeSBHcm91cCwgSW5jLjExMC8GA1UECxMoR28gRGFkZHkgQ2xhc3Mg +MiBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTCCASAwDQYJKoZIhvcNAQEBBQADggEN +ADCCAQgCggEBAN6d1+pXGEmhW+vXX0iG6r7d/+TvZxz0ZWizV3GgXne77ZtJ6XCA +PVYYYwhv2vLM0D9/AlQiVBDYsoHUwHU9S3/Hd8M+eKsaA7Ugay9qK7HFiH7Eux6w +wdhFJ2+qN1j3hybX2C32qRe3H3I2TqYXP2WYktsqbl2i/ojgC95/5Y0V4evLOtXi +EqITLdiOr18SPaAIBQi2XKVlOARFmR6jYGB0xUGlcmIbYsUfb18aQr4CUWWoriMY +avx4A6lNf4DD+qta/KFApMoZFv6yyO9ecw3ud72a9nmYvLEHZ6IVDd2gWMZEewo+ +YihfukEHU1jPEX44dMX4/7VpkI+EdOqXG68CAQOjgcAwgb0wHQYDVR0OBBYEFNLE +sNKR1EwRcbNhyz2h/t2oatTjMIGNBgNVHSMEgYUwgYKAFNLEsNKR1EwRcbNhyz2h +/t2oatTjoWekZTBjMQswCQYDVQQGEwJVUzEhMB8GA1UEChMYVGhlIEdvIERhZGR5 +IEdyb3VwLCBJbmMuMTEwLwYDVQQLEyhHbyBEYWRkeSBDbGFzcyAyIENlcnRpZmlj +YXRpb24gQXV0aG9yaXR5ggEAMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQAD +ggEBADJL87LKPpH8EsahB4yOd6AzBhRckB4Y9wimPQoZ+YeAEW5p5JYXMP80kWNy +OO7MHAGjHZQopDH2esRU1/blMVgDoszOYtuURXO1v0XJJLXVggKtI3lpjbi2Tc7P +TMozI+gciKqdi0FuFskg5YmezTvacPd+mSYgFFQlq25zheabIZ0KbIIOqPjCDPoQ +HmyW74cNxA9hi63ugyuV+I6ShHI56yDqg+2DzZduCLzrTia2cyvk0/ZM/iZx4mER +dEr/VxqHD3VILs9RaRegAhJhldXRQLIQTO7ErBBDpqWeCtWVYpoNz4iCxTIM5Cuf +ReYNnyicsbkqWletNw+vHX/bvZ8= +-----END CERTIFICATE----- + +# Issuer: O=Starfield Technologies, Inc. OU=Starfield Class 2 Certification Authority +# Subject: O=Starfield Technologies, Inc. OU=Starfield Class 2 Certification Authority +# Label: "Starfield Class 2 CA" +# Serial: 0 +# MD5 Fingerprint: 32:4a:4b:bb:c8:63:69:9b:be:74:9a:c6:dd:1d:46:24 +# SHA1 Fingerprint: ad:7e:1c:28:b0:64:ef:8f:60:03:40:20:14:c3:d0:e3:37:0e:b5:8a +# SHA256 Fingerprint: 14:65:fa:20:53:97:b8:76:fa:a6:f0:a9:95:8e:55:90:e4:0f:cc:7f:aa:4f:b7:c2:c8:67:75:21:fb:5f:b6:58 +-----BEGIN CERTIFICATE----- +MIIEDzCCAvegAwIBAgIBADANBgkqhkiG9w0BAQUFADBoMQswCQYDVQQGEwJVUzEl +MCMGA1UEChMcU3RhcmZpZWxkIFRlY2hub2xvZ2llcywgSW5jLjEyMDAGA1UECxMp +U3RhcmZpZWxkIENsYXNzIDIgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMDQw +NjI5MTczOTE2WhcNMzQwNjI5MTczOTE2WjBoMQswCQYDVQQGEwJVUzElMCMGA1UE +ChMcU3RhcmZpZWxkIFRlY2hub2xvZ2llcywgSW5jLjEyMDAGA1UECxMpU3RhcmZp +ZWxkIENsYXNzIDIgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwggEgMA0GCSqGSIb3 +DQEBAQUAA4IBDQAwggEIAoIBAQC3Msj+6XGmBIWtDBFk385N78gDGIc/oav7PKaf +8MOh2tTYbitTkPskpD6E8J7oX+zlJ0T1KKY/e97gKvDIr1MvnsoFAZMej2YcOadN ++lq2cwQlZut3f+dZxkqZJRRU6ybH838Z1TBwj6+wRir/resp7defqgSHo9T5iaU0 +X9tDkYI22WY8sbi5gv2cOj4QyDvvBmVmepsZGD3/cVE8MC5fvj13c7JdBmzDI1aa +K4UmkhynArPkPw2vCHmCuDY96pzTNbO8acr1zJ3o/WSNF4Azbl5KXZnJHoe0nRrA +1W4TNSNe35tfPe/W93bC6j67eA0cQmdrBNj41tpvi/JEoAGrAgEDo4HFMIHCMB0G +A1UdDgQWBBS/X7fRzt0fhvRbVazc1xDCDqmI5zCBkgYDVR0jBIGKMIGHgBS/X7fR +zt0fhvRbVazc1xDCDqmI56FspGowaDELMAkGA1UEBhMCVVMxJTAjBgNVBAoTHFN0 +YXJmaWVsZCBUZWNobm9sb2dpZXMsIEluYy4xMjAwBgNVBAsTKVN0YXJmaWVsZCBD +bGFzcyAyIENlcnRpZmljYXRpb24gQXV0aG9yaXR5ggEAMAwGA1UdEwQFMAMBAf8w +DQYJKoZIhvcNAQEFBQADggEBAAWdP4id0ckaVaGsafPzWdqbAYcaT1epoXkJKtv3 +L7IezMdeatiDh6GX70k1PncGQVhiv45YuApnP+yz3SFmH8lU+nLMPUxA2IGvd56D +eruix/U0F47ZEUD0/CwqTRV/p2JdLiXTAAsgGh1o+Re49L2L7ShZ3U0WixeDyLJl +xy16paq8U4Zt3VekyvggQQto8PT7dL5WXXp59fkdheMtlb71cZBDzI0fmgAKhynp +VSJYACPq4xJDKVtHCN2MQWplBqjlIapBtJUhlbl90TSrE9atvNziPTnNvT51cKEY +WQPJIrSPnNVeKtelttQKbfi3QBFGmh95DmK/D5fs4C8fF5Q= +-----END CERTIFICATE----- + +# Issuer: CN=StartCom Certification Authority O=StartCom Ltd. OU=Secure Digital Certificate Signing +# Subject: CN=StartCom Certification Authority O=StartCom Ltd. OU=Secure Digital Certificate Signing +# Label: "StartCom Certification Authority" +# Serial: 1 +# MD5 Fingerprint: 22:4d:8f:8a:fc:f7:35:c2:bb:57:34:90:7b:8b:22:16 +# SHA1 Fingerprint: 3e:2b:f7:f2:03:1b:96:f3:8c:e6:c4:d8:a8:5d:3e:2d:58:47:6a:0f +# SHA256 Fingerprint: c7:66:a9:be:f2:d4:07:1c:86:3a:31:aa:49:20:e8:13:b2:d1:98:60:8c:b7:b7:cf:e2:11:43:b8:36:df:09:ea +-----BEGIN CERTIFICATE----- +MIIHyTCCBbGgAwIBAgIBATANBgkqhkiG9w0BAQUFADB9MQswCQYDVQQGEwJJTDEW +MBQGA1UEChMNU3RhcnRDb20gTHRkLjErMCkGA1UECxMiU2VjdXJlIERpZ2l0YWwg +Q2VydGlmaWNhdGUgU2lnbmluZzEpMCcGA1UEAxMgU3RhcnRDb20gQ2VydGlmaWNh +dGlvbiBBdXRob3JpdHkwHhcNMDYwOTE3MTk0NjM2WhcNMzYwOTE3MTk0NjM2WjB9 +MQswCQYDVQQGEwJJTDEWMBQGA1UEChMNU3RhcnRDb20gTHRkLjErMCkGA1UECxMi +U2VjdXJlIERpZ2l0YWwgQ2VydGlmaWNhdGUgU2lnbmluZzEpMCcGA1UEAxMgU3Rh +cnRDb20gQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwggIiMA0GCSqGSIb3DQEBAQUA +A4ICDwAwggIKAoICAQDBiNsJvGxGfHiflXu1M5DycmLWwTYgIiRezul38kMKogZk +pMyONvg45iPwbm2xPN1yo4UcodM9tDMr0y+v/uqwQVlntsQGfQqedIXWeUyAN3rf +OQVSWff0G0ZDpNKFhdLDcfN1YjS6LIp/Ho/u7TTQEceWzVI9ujPW3U3eCztKS5/C +Ji/6tRYccjV3yjxd5srhJosaNnZcAdt0FCX+7bWgiA/deMotHweXMAEtcnn6RtYT +Kqi5pquDSR3l8u/d5AGOGAqPY1MWhWKpDhk6zLVmpsJrdAfkK+F2PrRt2PZE4XNi +HzvEvqBTViVsUQn3qqvKv3b9bZvzndu/PWa8DFaqr5hIlTpL36dYUNk4dalb6kMM +Av+Z6+hsTXBbKWWc3apdzK8BMewM69KN6Oqce+Zu9ydmDBpI125C4z/eIT574Q1w ++2OqqGwaVLRcJXrJosmLFqa7LH4XXgVNWG4SHQHuEhANxjJ/GP/89PrNbpHoNkm+ +Gkhpi8KWTRoSsmkXwQqQ1vp5Iki/untp+HDH+no32NgN0nZPV/+Qt+OR0t3vwmC3 +Zzrd/qqc8NSLf3Iizsafl7b4r4qgEKjZ+xjGtrVcUjyJthkqcwEKDwOzEmDyei+B +26Nu/yYwl/WL3YlXtq09s68rxbd2AvCl1iuahhQqcvbjM4xdCUsT37uMdBNSSwID +AQABo4ICUjCCAk4wDAYDVR0TBAUwAwEB/zALBgNVHQ8EBAMCAa4wHQYDVR0OBBYE +FE4L7xqkQFulF2mHMMo0aEPQQa7yMGQGA1UdHwRdMFswLKAqoCiGJmh0dHA6Ly9j +ZXJ0LnN0YXJ0Y29tLm9yZy9zZnNjYS1jcmwuY3JsMCugKaAnhiVodHRwOi8vY3Js +LnN0YXJ0Y29tLm9yZy9zZnNjYS1jcmwuY3JsMIIBXQYDVR0gBIIBVDCCAVAwggFM +BgsrBgEEAYG1NwEBATCCATswLwYIKwYBBQUHAgEWI2h0dHA6Ly9jZXJ0LnN0YXJ0 +Y29tLm9yZy9wb2xpY3kucGRmMDUGCCsGAQUFBwIBFilodHRwOi8vY2VydC5zdGFy +dGNvbS5vcmcvaW50ZXJtZWRpYXRlLnBkZjCB0AYIKwYBBQUHAgIwgcMwJxYgU3Rh +cnQgQ29tbWVyY2lhbCAoU3RhcnRDb20pIEx0ZC4wAwIBARqBl0xpbWl0ZWQgTGlh +YmlsaXR5LCByZWFkIHRoZSBzZWN0aW9uICpMZWdhbCBMaW1pdGF0aW9ucyogb2Yg +dGhlIFN0YXJ0Q29tIENlcnRpZmljYXRpb24gQXV0aG9yaXR5IFBvbGljeSBhdmFp +bGFibGUgYXQgaHR0cDovL2NlcnQuc3RhcnRjb20ub3JnL3BvbGljeS5wZGYwEQYJ +YIZIAYb4QgEBBAQDAgAHMDgGCWCGSAGG+EIBDQQrFilTdGFydENvbSBGcmVlIFNT +TCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTANBgkqhkiG9w0BAQUFAAOCAgEAFmyZ +9GYMNPXQhV59CuzaEE44HF7fpiUFS5Eyweg78T3dRAlbB0mKKctmArexmvclmAk8 +jhvh3TaHK0u7aNM5Zj2gJsfyOZEdUauCe37Vzlrk4gNXcGmXCPleWKYK34wGmkUW +FjgKXlf2Ysd6AgXmvB618p70qSmD+LIU424oh0TDkBreOKk8rENNZEXO3SipXPJz +ewT4F+irsfMuXGRuczE6Eri8sxHkfY+BUZo7jYn0TZNmezwD7dOaHZrzZVD1oNB1 +ny+v8OqCQ5j4aZyJecRDjkZy42Q2Eq/3JR44iZB3fsNrarnDy0RLrHiQi+fHLB5L +EUTINFInzQpdn4XBidUaePKVEFMy3YCEZnXZtWgo+2EuvoSoOMCZEoalHmdkrQYu +L6lwhceWD3yJZfWOQ1QOq92lgDmUYMA0yZZwLKMS9R9Ie70cfmu3nZD0Ijuu+Pwq +yvqCUqDvr0tVk+vBtfAii6w0TiYiBKGHLHVKt+V9E9e4DGTANtLJL4YSjCMJwRuC +O3NJo2pXh5Tl1njFmUNj403gdy3hZZlyaQQaRwnmDwFWJPsfvw55qVguucQJAX6V +um0ABj6y6koQOdjQK/W/7HW/lwLFCRsI3FU34oH7N4RDYiDK51ZLZer+bMEkkySh +NOsF/5oirpt9P/FlUQqmMGqz9IgcgA38corog14= +-----END CERTIFICATE----- + +# Issuer: O=Government Root Certification Authority +# Subject: O=Government Root Certification Authority +# Label: "Taiwan GRCA" +# Serial: 42023070807708724159991140556527066870 +# MD5 Fingerprint: 37:85:44:53:32:45:1f:20:f0:f3:95:e1:25:c4:43:4e +# SHA1 Fingerprint: f4:8b:11:bf:de:ab:be:94:54:20:71:e6:41:de:6b:be:88:2b:40:b9 +# SHA256 Fingerprint: 76:00:29:5e:ef:e8:5b:9e:1f:d6:24:db:76:06:2a:aa:ae:59:81:8a:54:d2:77:4c:d4:c0:b2:c0:11:31:e1:b3 +-----BEGIN CERTIFICATE----- +MIIFcjCCA1qgAwIBAgIQH51ZWtcvwgZEpYAIaeNe9jANBgkqhkiG9w0BAQUFADA/ +MQswCQYDVQQGEwJUVzEwMC4GA1UECgwnR292ZXJubWVudCBSb290IENlcnRpZmlj +YXRpb24gQXV0aG9yaXR5MB4XDTAyMTIwNTEzMjMzM1oXDTMyMTIwNTEzMjMzM1ow +PzELMAkGA1UEBhMCVFcxMDAuBgNVBAoMJ0dvdmVybm1lbnQgUm9vdCBDZXJ0aWZp +Y2F0aW9uIEF1dGhvcml0eTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIB +AJoluOzMonWoe/fOW1mKydGGEghU7Jzy50b2iPN86aXfTEc2pBsBHH8eV4qNw8XR +IePaJD9IK/ufLqGU5ywck9G/GwGHU5nOp/UKIXZ3/6m3xnOUT0b3EEk3+qhZSV1q +gQdW8or5BtD3cCJNtLdBuTK4sfCxw5w/cP1T3YGq2GN49thTbqGsaoQkclSGxtKy +yhwOeYHWtXBiCAEuTk8O1RGvqa/lmr/czIdtJuTJV6L7lvnM4T9TjGxMfptTCAts +F/tnyMKtsc2AtJfcdgEWFelq16TheEfOhtX7MfP6Mb40qij7cEwdScevLJ1tZqa2 +jWR+tSBqnTuBto9AAGdLiYa4zGX+FVPpBMHWXx1E1wovJ5pGfaENda1UhhXcSTvx +ls4Pm6Dso3pdvtUqdULle96ltqqvKKyskKw4t9VoNSZ63Pc78/1Fm9G7Q3hub/FC +VGqY8A2tl+lSXunVanLeavcbYBT0peS2cWeqH+riTcFCQP5nRhc4L0c/cZyu5SHK +YS1tB6iEfC3uUSXxY5Ce/eFXiGvviiNtsea9P63RPZYLhY3Naye7twWb7LuRqQoH +EgKXTiCQ8P8NHuJBO9NAOueNXdpm5AKwB1KYXA6OM5zCppX7VRluTI6uSw+9wThN +Xo+EHWbNxWCWtFJaBYmOlXqYwZE8lSOyDvR5tMl8wUohAgMBAAGjajBoMB0GA1Ud +DgQWBBTMzO/MKWCkO7GStjz6MmKPrCUVOzAMBgNVHRMEBTADAQH/MDkGBGcqBwAE +MTAvMC0CAQAwCQYFKw4DAhoFADAHBgVnKgMAAAQUA5vwIhP/lSg209yewDL7MTqK +UWUwDQYJKoZIhvcNAQEFBQADggIBAECASvomyc5eMN1PhnR2WPWus4MzeKR6dBcZ +TulStbngCnRiqmjKeKBMmo4sIy7VahIkv9Ro04rQ2JyftB8M3jh+Vzj8jeJPXgyf +qzvS/3WXy6TjZwj/5cAWtUgBfen5Cv8b5Wppv3ghqMKnI6mGq3ZW6A4M9hPdKmaK +ZEk9GhiHkASfQlK3T8v+R0F2Ne//AHY2RTKbxkaFXeIksB7jSJaYV0eUVXoPQbFE +JPPB/hprv4j9wabak2BegUqZIJxIZhm1AHlUD7gsL0u8qV1bYH+Mh6XgUmMqvtg7 +hUAV/h62ZT/FS9p+tXo1KaMuephgIqP0fSdOLeq0dDzpD6QzDxARvBMB1uUO07+1 +EqLhRSPAzAhuYbeJq4PjJB7mXQfnHyA+z2fI56wwbSdLaG5LKlwCCDTb+HbkZ6Mm +nD+iMsJKxYEYMRBWqoTvLQr/uB930r+lWKBi5NdLkXWNiYCYfm3LU05er/ayl4WX +udpVBrkk7tfGOB5jGxI7leFYrPLfhNVfmS8NVVvmONsuP3LpSIXLuykTjx44Vbnz +ssQwmSNOXfJIoRIM3BKQCZBUkQM8R+XVyWXgt0t97EfTsws+rZ7QdAAO671RrcDe +LMDDav7v3Aun+kbfYNucpllQdSNpc5Oy+fwC00fmcc4QAu4njIT/rEUNE1yDMuAl +pYYsfPQS +-----END CERTIFICATE----- + +# Issuer: CN=Swisscom Root CA 1 O=Swisscom OU=Digital Certificate Services +# Subject: CN=Swisscom Root CA 1 O=Swisscom OU=Digital Certificate Services +# Label: "Swisscom Root CA 1" +# Serial: 122348795730808398873664200247279986742 +# MD5 Fingerprint: f8:38:7c:77:88:df:2c:16:68:2e:c2:e2:52:4b:b8:f9 +# SHA1 Fingerprint: 5f:3a:fc:0a:8b:64:f6:86:67:34:74:df:7e:a9:a2:fe:f9:fa:7a:51 +# SHA256 Fingerprint: 21:db:20:12:36:60:bb:2e:d4:18:20:5d:a1:1e:e7:a8:5a:65:e2:bc:6e:55:b5:af:7e:78:99:c8:a2:66:d9:2e +-----BEGIN CERTIFICATE----- +MIIF2TCCA8GgAwIBAgIQXAuFXAvnWUHfV8w/f52oNjANBgkqhkiG9w0BAQUFADBk +MQswCQYDVQQGEwJjaDERMA8GA1UEChMIU3dpc3Njb20xJTAjBgNVBAsTHERpZ2l0 +YWwgQ2VydGlmaWNhdGUgU2VydmljZXMxGzAZBgNVBAMTElN3aXNzY29tIFJvb3Qg +Q0EgMTAeFw0wNTA4MTgxMjA2MjBaFw0yNTA4MTgyMjA2MjBaMGQxCzAJBgNVBAYT +AmNoMREwDwYDVQQKEwhTd2lzc2NvbTElMCMGA1UECxMcRGlnaXRhbCBDZXJ0aWZp +Y2F0ZSBTZXJ2aWNlczEbMBkGA1UEAxMSU3dpc3Njb20gUm9vdCBDQSAxMIICIjAN +BgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA0LmwqAzZuz8h+BvVM5OAFmUgdbI9 +m2BtRsiMMW8Xw/qabFbtPMWRV8PNq5ZJkCoZSx6jbVfd8StiKHVFXqrWW/oLJdih +FvkcxC7mlSpnzNApbjyFNDhhSbEAn9Y6cV9Nbc5fuankiX9qUvrKm/LcqfmdmUc/ +TilftKaNXXsLmREDA/7n29uj/x2lzZAeAR81sH8A25Bvxn570e56eqeqDFdvpG3F +EzuwpdntMhy0XmeLVNxzh+XTF3xmUHJd1BpYwdnP2IkCb6dJtDZd0KTeByy2dbco +kdaXvij1mB7qWybJvbCXc9qukSbraMH5ORXWZ0sKbU/Lz7DkQnGMU3nn7uHbHaBu +HYwadzVcFh4rUx80i9Fs/PJnB3r1re3WmquhsUvhzDdf/X/NTa64H5xD+SpYVUNF +vJbNcA78yeNmuk6NO4HLFWR7uZToXTNShXEuT46iBhFRyePLoW4xCGQMwtI89Tbo +19AOeCMgkckkKmUpWyL3Ic6DXqTz3kvTaI9GdVyDCW4pa8RwjPWd1yAv/0bSKzjC +L3UcPX7ape8eYIVpQtPM+GP+HkM5haa2Y0EQs3MevNP6yn0WR+Kn1dCjigoIlmJW +bjTb2QK5MHXjBNLnj8KwEUAKrNVxAmKLMb7dxiNYMUJDLXT5xp6mig/p/r+D5kNX +JLrvRjSq1xIBOO0CAwEAAaOBhjCBgzAOBgNVHQ8BAf8EBAMCAYYwHQYDVR0hBBYw +FDASBgdghXQBUwABBgdghXQBUwABMBIGA1UdEwEB/wQIMAYBAf8CAQcwHwYDVR0j +BBgwFoAUAyUv3m+CATpcLNwroWm1Z9SM0/0wHQYDVR0OBBYEFAMlL95vggE6XCzc +K6FptWfUjNP9MA0GCSqGSIb3DQEBBQUAA4ICAQA1EMvspgQNDQ/NwNurqPKIlwzf +ky9NfEBWMXrrpA9gzXrzvsMnjgM+pN0S734edAY8PzHyHHuRMSG08NBsl9Tpl7Ik +Vh5WwzW9iAUPWxAaZOHHgjD5Mq2eUCzneAXQMbFamIp1TpBcahQq4FJHgmDmHtqB +sfsUC1rxn9KVuj7QG9YVHaO+htXbD8BJZLsuUBlL0iT43R4HVtA4oJVwIHaM190e +3p9xxCPvgxNcoyQVTSlAPGrEqdi3pkSlDfTgnXceQHAm/NrZNuR55LU/vJtlvrsR +ls/bxig5OgjOR1tTWsWZ/l2p3e9M1MalrQLmjAcSHm8D0W+go/MpvRLHUKKwf4ip +mXeascClOS5cfGniLLDqN2qk4Vrh9VDlg++luyqI54zb/W1elxmofmZ1a3Hqv7HH +b6D0jqTsNFFbjCYDcKF31QESVwA12yPeDooomf2xEG9L/zgtYE4snOtnta1J7ksf +rK/7DZBaZmBwXarNeNQk7shBoJMBkpxqnvy5JMWzFYJ+vq6VK+uxwNrjAWALXmms +hFZhvnEX/h0TD/7Gh0Xp/jKgGg0TpJRVcaUWi7rKibCyx/yP2FS1k2Kdzs9Z+z0Y +zirLNRWCXf9UIltxUvu3yf5gmwBBZPCqKuy2QkPOiWaByIufOVQDJdMWNY6E0F/6 +MBr1mmz0DlP5OlvRHA== +-----END CERTIFICATE----- + +# Issuer: CN=DigiCert Assured ID Root CA O=DigiCert Inc OU=www.digicert.com +# Subject: CN=DigiCert Assured ID Root CA O=DigiCert Inc OU=www.digicert.com +# Label: "DigiCert Assured ID Root CA" +# Serial: 17154717934120587862167794914071425081 +# MD5 Fingerprint: 87:ce:0b:7b:2a:0e:49:00:e1:58:71:9b:37:a8:93:72 +# SHA1 Fingerprint: 05:63:b8:63:0d:62:d7:5a:bb:c8:ab:1e:4b:df:b5:a8:99:b2:4d:43 +# SHA256 Fingerprint: 3e:90:99:b5:01:5e:8f:48:6c:00:bc:ea:9d:11:1e:e7:21:fa:ba:35:5a:89:bc:f1:df:69:56:1e:3d:c6:32:5c +-----BEGIN CERTIFICATE----- +MIIDtzCCAp+gAwIBAgIQDOfg5RfYRv6P5WD8G/AwOTANBgkqhkiG9w0BAQUFADBl +MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 +d3cuZGlnaWNlcnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJv +b3QgQ0EwHhcNMDYxMTEwMDAwMDAwWhcNMzExMTEwMDAwMDAwWjBlMQswCQYDVQQG +EwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNl +cnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3QgQ0EwggEi +MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCtDhXO5EOAXLGH87dg+XESpa7c +JpSIqvTO9SA5KFhgDPiA2qkVlTJhPLWxKISKityfCgyDF3qPkKyK53lTXDGEKvYP +mDI2dsze3Tyoou9q+yHyUmHfnyDXH+Kx2f4YZNISW1/5WBg1vEfNoTb5a3/UsDg+ +wRvDjDPZ2C8Y/igPs6eD1sNuRMBhNZYW/lmci3Zt1/GiSw0r/wty2p5g0I6QNcZ4 +VYcgoc/lbQrISXwxmDNsIumH0DJaoroTghHtORedmTpyoeb6pNnVFzF1roV9Iq4/ +AUaG9ih5yLHa5FcXxH4cDrC0kqZWs72yl+2qp/C3xag/lRbQ/6GW6whfGHdPAgMB +AAGjYzBhMA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQW +BBRF66Kv9JLLgjEtUYunpyGd823IDzAfBgNVHSMEGDAWgBRF66Kv9JLLgjEtUYun +pyGd823IDzANBgkqhkiG9w0BAQUFAAOCAQEAog683+Lt8ONyc3pklL/3cmbYMuRC +dWKuh+vy1dneVrOfzM4UKLkNl2BcEkxY5NM9g0lFWJc1aRqoR+pWxnmrEthngYTf +fwk8lOa4JiwgvT2zKIn3X/8i4peEH+ll74fg38FnSbNd67IJKusm7Xi+fT8r87cm +NW1fiQG2SVufAQWbqz0lwcy2f8Lxb4bG+mRo64EtlOtCt/qMHt1i8b5QZ7dsvfPx +H2sMNgcWfzd8qVttevESRmCD1ycEvkvOl77DZypoEd+A5wwzZr8TDRRu838fYxAe ++o0bJW1sj6W3YQGx0qMmoRBxna3iw/nDmVG3KwcIzi7mULKn+gpFL6Lw8g== +-----END CERTIFICATE----- + +# Issuer: CN=DigiCert Global Root CA O=DigiCert Inc OU=www.digicert.com +# Subject: CN=DigiCert Global Root CA O=DigiCert Inc OU=www.digicert.com +# Label: "DigiCert Global Root CA" +# Serial: 10944719598952040374951832963794454346 +# MD5 Fingerprint: 79:e4:a9:84:0d:7d:3a:96:d7:c0:4f:e2:43:4c:89:2e +# SHA1 Fingerprint: a8:98:5d:3a:65:e5:e5:c4:b2:d7:d6:6d:40:c6:dd:2f:b1:9c:54:36 +# SHA256 Fingerprint: 43:48:a0:e9:44:4c:78:cb:26:5e:05:8d:5e:89:44:b4:d8:4f:96:62:bd:26:db:25:7f:89:34:a4:43:c7:01:61 +-----BEGIN CERTIFICATE----- +MIIDrzCCApegAwIBAgIQCDvgVpBCRrGhdWrJWZHHSjANBgkqhkiG9w0BAQUFADBh +MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 +d3cuZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBD +QTAeFw0wNjExMTAwMDAwMDBaFw0zMTExMTAwMDAwMDBaMGExCzAJBgNVBAYTAlVT +MRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5j +b20xIDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IENBMIIBIjANBgkqhkiG +9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4jvhEXLeqKTTo1eqUKKPC3eQyaKl7hLOllsB +CSDMAZOnTjC3U/dDxGkAV53ijSLdhwZAAIEJzs4bg7/fzTtxRuLWZscFs3YnFo97 +nh6Vfe63SKMI2tavegw5BmV/Sl0fvBf4q77uKNd0f3p4mVmFaG5cIzJLv07A6Fpt +43C/dxC//AH2hdmoRBBYMql1GNXRor5H4idq9Joz+EkIYIvUX7Q6hL+hqkpMfT7P +T19sdl6gSzeRntwi5m3OFBqOasv+zbMUZBfHWymeMr/y7vrTC0LUq7dBMtoM1O/4 +gdW7jVg/tRvoSSiicNoxBN33shbyTApOB6jtSj1etX+jkMOvJwIDAQABo2MwYTAO +BgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUA95QNVbR +TLtm8KPiGxvDl7I90VUwHwYDVR0jBBgwFoAUA95QNVbRTLtm8KPiGxvDl7I90VUw +DQYJKoZIhvcNAQEFBQADggEBAMucN6pIExIK+t1EnE9SsPTfrgT1eXkIoyQY/Esr +hMAtudXH/vTBH1jLuG2cenTnmCmrEbXjcKChzUyImZOMkXDiqw8cvpOp/2PV5Adg +06O/nVsJ8dWO41P0jmP6P6fbtGbfYmbW0W5BjfIttep3Sp+dWOIrWcBAI+0tKIJF +PnlUkiaY4IBIqDfv8NZ5YBberOgOzW6sRBc4L0na4UU+Krk2U886UAb3LujEV0ls +YSEY1QSteDwsOoBrp+uvFRTp2InBuThs4pFsiv9kuXclVzDAGySj4dzp30d8tbQk +CAUw7C29C79Fv1C5qfPrmAESrciIxpg0X40KPMbp1ZWVbd4= +-----END CERTIFICATE----- + +# Issuer: CN=DigiCert High Assurance EV Root CA O=DigiCert Inc OU=www.digicert.com +# Subject: CN=DigiCert High Assurance EV Root CA O=DigiCert Inc OU=www.digicert.com +# Label: "DigiCert High Assurance EV Root CA" +# Serial: 3553400076410547919724730734378100087 +# MD5 Fingerprint: d4:74:de:57:5c:39:b2:d3:9c:85:83:c5:c0:65:49:8a +# SHA1 Fingerprint: 5f:b7:ee:06:33:e2:59:db:ad:0c:4c:9a:e6:d3:8f:1a:61:c7:dc:25 +# SHA256 Fingerprint: 74:31:e5:f4:c3:c1:ce:46:90:77:4f:0b:61:e0:54:40:88:3b:a9:a0:1e:d0:0b:a6:ab:d7:80:6e:d3:b1:18:cf +-----BEGIN CERTIFICATE----- +MIIDxTCCAq2gAwIBAgIQAqxcJmoLQJuPC3nyrkYldzANBgkqhkiG9w0BAQUFADBs +MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 +d3cuZGlnaWNlcnQuY29tMSswKQYDVQQDEyJEaWdpQ2VydCBIaWdoIEFzc3VyYW5j +ZSBFViBSb290IENBMB4XDTA2MTExMDAwMDAwMFoXDTMxMTExMDAwMDAwMFowbDEL +MAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3 +LmRpZ2ljZXJ0LmNvbTErMCkGA1UEAxMiRGlnaUNlcnQgSGlnaCBBc3N1cmFuY2Ug +RVYgUm9vdCBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMbM5XPm ++9S75S0tMqbf5YE/yc0lSbZxKsPVlDRnogocsF9ppkCxxLeyj9CYpKlBWTrT3JTW +PNt0OKRKzE0lgvdKpVMSOO7zSW1xkX5jtqumX8OkhPhPYlG++MXs2ziS4wblCJEM +xChBVfvLWokVfnHoNb9Ncgk9vjo4UFt3MRuNs8ckRZqnrG0AFFoEt7oT61EKmEFB +Ik5lYYeBQVCmeVyJ3hlKV9Uu5l0cUyx+mM0aBhakaHPQNAQTXKFx01p8VdteZOE3 +hzBWBOURtCmAEvF5OYiiAhF8J2a3iLd48soKqDirCmTCv2ZdlYTBoSUeh10aUAsg +EsxBu24LUTi4S8sCAwEAAaNjMGEwDgYDVR0PAQH/BAQDAgGGMA8GA1UdEwEB/wQF +MAMBAf8wHQYDVR0OBBYEFLE+w2kD+L9HAdSYJhoIAu9jZCvDMB8GA1UdIwQYMBaA +FLE+w2kD+L9HAdSYJhoIAu9jZCvDMA0GCSqGSIb3DQEBBQUAA4IBAQAcGgaX3Nec +nzyIZgYIVyHbIUf4KmeqvxgydkAQV8GK83rZEWWONfqe/EW1ntlMMUu4kehDLI6z +eM7b41N5cdblIZQB2lWHmiRk9opmzN6cN82oNLFpmyPInngiK3BD41VHMWEZ71jF +hS9OMPagMRYjyOfiZRYzy78aG6A9+MpeizGLYAiJLQwGXFK3xPkKmNEVX58Svnw2 +Yzi9RKR/5CYrCsSXaQ3pjOLAEFe4yHYSkVXySGnYvCoCWw9E1CAx2/S6cCZdkGCe +vEsXCS+0yx5DaMkHJ8HSXPfqIbloEpw8nL+e/IBcm2PN7EeqJSdnoDfzAIJ9VNep ++OkuE6N36B9K +-----END CERTIFICATE----- + +# Issuer: CN=Class 2 Primary CA O=Certplus +# Subject: CN=Class 2 Primary CA O=Certplus +# Label: "Certplus Class 2 Primary CA" +# Serial: 177770208045934040241468760488327595043 +# MD5 Fingerprint: 88:2c:8c:52:b8:a2:3c:f3:f7:bb:03:ea:ae:ac:42:0b +# SHA1 Fingerprint: 74:20:74:41:72:9c:dd:92:ec:79:31:d8:23:10:8d:c2:81:92:e2:bb +# SHA256 Fingerprint: 0f:99:3c:8a:ef:97:ba:af:56:87:14:0e:d5:9a:d1:82:1b:b4:af:ac:f0:aa:9a:58:b5:d5:7a:33:8a:3a:fb:cb +-----BEGIN CERTIFICATE----- +MIIDkjCCAnqgAwIBAgIRAIW9S/PY2uNp9pTXX8OlRCMwDQYJKoZIhvcNAQEFBQAw +PTELMAkGA1UEBhMCRlIxETAPBgNVBAoTCENlcnRwbHVzMRswGQYDVQQDExJDbGFz +cyAyIFByaW1hcnkgQ0EwHhcNOTkwNzA3MTcwNTAwWhcNMTkwNzA2MjM1OTU5WjA9 +MQswCQYDVQQGEwJGUjERMA8GA1UEChMIQ2VydHBsdXMxGzAZBgNVBAMTEkNsYXNz +IDIgUHJpbWFyeSBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANxQ +ltAS+DXSCHh6tlJw/W/uz7kRy1134ezpfgSN1sxvc0NXYKwzCkTsA18cgCSR5aiR +VhKC9+Ar9NuuYS6JEI1rbLqzAr3VNsVINyPi8Fo3UjMXEuLRYE2+L0ER4/YXJQyL +kcAbmXuZVg2v7tK8R1fjeUl7NIknJITesezpWE7+Tt9avkGtrAjFGA7v0lPubNCd +EgETjdyAYveVqUSISnFOYFWe2yMZeVYHDD9jC1yw4r5+FfyUM1hBOHTE4Y+L3yas +H7WLO7dDWWuwJKZtkIvEcupdM5i3y95ee++U8Rs+yskhwcWYAqqi9lt3m/V+llU0 +HGdpwPFC40es/CgcZlUCAwEAAaOBjDCBiTAPBgNVHRMECDAGAQH/AgEKMAsGA1Ud +DwQEAwIBBjAdBgNVHQ4EFgQU43Mt38sOKAze3bOkynm4jrvoMIkwEQYJYIZIAYb4 +QgEBBAQDAgEGMDcGA1UdHwQwMC4wLKAqoCiGJmh0dHA6Ly93d3cuY2VydHBsdXMu +Y29tL0NSTC9jbGFzczIuY3JsMA0GCSqGSIb3DQEBBQUAA4IBAQCnVM+IRBnL39R/ +AN9WM2K191EBkOvDP9GIROkkXe/nFL0gt5o8AP5tn9uQ3Nf0YtaLcF3n5QRIqWh8 +yfFC82x/xXp8HVGIutIKPidd3i1RTtMTZGnkLuPT55sJmabglZvOGtd/vjzOUrMR +FcEPF80Du5wlFbqidon8BvEY0JNLDnyCt6X09l/+7UCmnYR0ObncHoUW2ikbhiMA +ybuJfm6AiB4vFLQDJKgybwOaRywwvlbGp0ICcBvqQNi6BQNwB6SW//1IMwrh3KWB +kJtN3X3n57LNXMhqlfil9o3EXXgIvnsG1knPGTZQIy4I5p4FTUcY1Rbpsda2ENW7 +l7+ijrRU +-----END CERTIFICATE----- + +# Issuer: CN=DST Root CA X3 O=Digital Signature Trust Co. +# Subject: CN=DST Root CA X3 O=Digital Signature Trust Co. +# Label: "DST Root CA X3" +# Serial: 91299735575339953335919266965803778155 +# MD5 Fingerprint: 41:03:52:dc:0f:f7:50:1b:16:f0:02:8e:ba:6f:45:c5 +# SHA1 Fingerprint: da:c9:02:4f:54:d8:f6:df:94:93:5f:b1:73:26:38:ca:6a:d7:7c:13 +# SHA256 Fingerprint: 06:87:26:03:31:a7:24:03:d9:09:f1:05:e6:9b:cf:0d:32:e1:bd:24:93:ff:c6:d9:20:6d:11:bc:d6:77:07:39 +-----BEGIN CERTIFICATE----- +MIIDSjCCAjKgAwIBAgIQRK+wgNajJ7qJMDmGLvhAazANBgkqhkiG9w0BAQUFADA/ +MSQwIgYDVQQKExtEaWdpdGFsIFNpZ25hdHVyZSBUcnVzdCBDby4xFzAVBgNVBAMT +DkRTVCBSb290IENBIFgzMB4XDTAwMDkzMDIxMTIxOVoXDTIxMDkzMDE0MDExNVow +PzEkMCIGA1UEChMbRGlnaXRhbCBTaWduYXR1cmUgVHJ1c3QgQ28uMRcwFQYDVQQD +Ew5EU1QgUm9vdCBDQSBYMzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB +AN+v6ZdQCINXtMxiZfaQguzH0yxrMMpb7NnDfcdAwRgUi+DoM3ZJKuM/IUmTrE4O +rz5Iy2Xu/NMhD2XSKtkyj4zl93ewEnu1lcCJo6m67XMuegwGMoOifooUMM0RoOEq +OLl5CjH9UL2AZd+3UWODyOKIYepLYYHsUmu5ouJLGiifSKOeDNoJjj4XLh7dIN9b +xiqKqy69cK3FCxolkHRyxXtqqzTWMIn/5WgTe1QLyNau7Fqckh49ZLOMxt+/yUFw +7BZy1SbsOFU5Q9D8/RhcQPGX69Wam40dutolucbY38EVAjqr2m7xPi71XAicPNaD +aeQQmxkqtilX4+U9m5/wAl0CAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNV +HQ8BAf8EBAMCAQYwHQYDVR0OBBYEFMSnsaR7LHH62+FLkHX/xBVghYkQMA0GCSqG +SIb3DQEBBQUAA4IBAQCjGiybFwBcqR7uKGY3Or+Dxz9LwwmglSBd49lZRNI+DT69 +ikugdB/OEIKcdBodfpga3csTS7MgROSR6cz8faXbauX+5v3gTt23ADq1cEmv8uXr +AvHRAosZy5Q6XkjEGB5YGV8eAlrwDPGxrancWYaLbumR9YbK+rlmM6pZW87ipxZz +R8srzJmwN0jP41ZL9c8PDHIyh8bwRLtTcm1D9SZImlJnt1ir/md2cXjbDaJWFBM5 +JDGFoqgCWjBH4d1QB7wCCZAA62RjYJsWvIjJEubSfZGL+T0yjWW06XyxV3bqxbYo +Ob8VZRzI9neWagqNdwvYkQsEjgfbKbYK7p2CNTUQ +-----END CERTIFICATE----- + +# Issuer: CN=DST ACES CA X6 O=Digital Signature Trust OU=DST ACES +# Subject: CN=DST ACES CA X6 O=Digital Signature Trust OU=DST ACES +# Label: "DST ACES CA X6" +# Serial: 17771143917277623872238992636097467865 +# MD5 Fingerprint: 21:d8:4c:82:2b:99:09:33:a2:eb:14:24:8d:8e:5f:e8 +# SHA1 Fingerprint: 40:54:da:6f:1c:3f:40:74:ac:ed:0f:ec:cd:db:79:d1:53:fb:90:1d +# SHA256 Fingerprint: 76:7c:95:5a:76:41:2c:89:af:68:8e:90:a1:c7:0f:55:6c:fd:6b:60:25:db:ea:10:41:6d:7e:b6:83:1f:8c:40 +-----BEGIN CERTIFICATE----- +MIIECTCCAvGgAwIBAgIQDV6ZCtadt3js2AdWO4YV2TANBgkqhkiG9w0BAQUFADBb +MQswCQYDVQQGEwJVUzEgMB4GA1UEChMXRGlnaXRhbCBTaWduYXR1cmUgVHJ1c3Qx +ETAPBgNVBAsTCERTVCBBQ0VTMRcwFQYDVQQDEw5EU1QgQUNFUyBDQSBYNjAeFw0w +MzExMjAyMTE5NThaFw0xNzExMjAyMTE5NThaMFsxCzAJBgNVBAYTAlVTMSAwHgYD +VQQKExdEaWdpdGFsIFNpZ25hdHVyZSBUcnVzdDERMA8GA1UECxMIRFNUIEFDRVMx +FzAVBgNVBAMTDkRTVCBBQ0VTIENBIFg2MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A +MIIBCgKCAQEAuT31LMmU3HWKlV1j6IR3dma5WZFcRt2SPp/5DgO0PWGSvSMmtWPu +ktKe1jzIDZBfZIGxqAgNTNj50wUoUrQBJcWVHAx+PhCEdc/BGZFjz+iokYi5Q1K7 +gLFViYsx+tC3dr5BPTCapCIlF3PoHuLTrCq9Wzgh1SpL11V94zpVvddtawJXa+ZH +fAjIgrrep4c9oW24MFbCswKBXy314powGCi4ZtPLAZZv6opFVdbgnf9nKxcCpk4a +ahELfrd755jWjHZvwTvbUJN+5dCOHze4vbrGn2zpfDPyMjwmR/onJALJfh1biEIT +ajV8fTXpLmaRcpPVMibEdPVTo7NdmvYJywIDAQABo4HIMIHFMA8GA1UdEwEB/wQF +MAMBAf8wDgYDVR0PAQH/BAQDAgHGMB8GA1UdEQQYMBaBFHBraS1vcHNAdHJ1c3Rk +c3QuY29tMGIGA1UdIARbMFkwVwYKYIZIAWUDAgEBATBJMEcGCCsGAQUFBwIBFjto +dHRwOi8vd3d3LnRydXN0ZHN0LmNvbS9jZXJ0aWZpY2F0ZXMvcG9saWN5L0FDRVMt +aW5kZXguaHRtbDAdBgNVHQ4EFgQUCXIGThhDD+XWzMNqizF7eI+og7gwDQYJKoZI +hvcNAQEFBQADggEBAKPYjtay284F5zLNAdMEA+V25FYrnJmQ6AgwbN99Pe7lv7Uk +QIRJ4dEorsTCOlMwiPH1d25Ryvr/ma8kXxug/fKshMrfqfBfBC6tFr8hlxCBPeP/ +h40y3JTlR4peahPJlJU90u7INJXQgNStMgiAVDzgvVJT11J8smk/f3rPanTK+gQq +nExaBqXpIK1FZg9p8d2/6eMyi/rgwYZNcjwu2JN4Cir42NInPRmJX1p7ijvMDNpR +rscL9yuwNwXsvFcj4jjSm2jzVhKIT0J8uDHEtdvkyCE06UgRNe76x5JXxZ805Mf2 +9w4LTJxoeHtxMcfrHuBnQfO3oKfN5XozNmr6mis= +-----END CERTIFICATE----- + +# Issuer: CN=TÜRKTRUST Elektronik Sertifika Hizmet Sağlayıcısı O=(c) 2005 TÜRKTRUST Bilgi İletişim ve Bilişim Güvenliği Hizmetleri A.Ş. +# Subject: CN=TÜRKTRUST Elektronik Sertifika Hizmet Sağlayıcısı O=(c) 2005 TÜRKTRUST Bilgi İletişim ve Bilişim Güvenliği Hizmetleri A.Ş. +# Label: "TURKTRUST Certificate Services Provider Root 1" +# Serial: 1 +# MD5 Fingerprint: f1:6a:22:18:c9:cd:df:ce:82:1d:1d:b7:78:5c:a9:a5 +# SHA1 Fingerprint: 79:98:a3:08:e1:4d:65:85:e6:c2:1e:15:3a:71:9f:ba:5a:d3:4a:d9 +# SHA256 Fingerprint: 44:04:e3:3b:5e:14:0d:cf:99:80:51:fd:fc:80:28:c7:c8:16:15:c5:ee:73:7b:11:1b:58:82:33:a9:b5:35:a0 +-----BEGIN CERTIFICATE----- +MIID+zCCAuOgAwIBAgIBATANBgkqhkiG9w0BAQUFADCBtzE/MD0GA1UEAww2VMOc +UktUUlVTVCBFbGVrdHJvbmlrIFNlcnRpZmlrYSBIaXptZXQgU2HEn2xhecSxY8Sx +c8SxMQswCQYDVQQGDAJUUjEPMA0GA1UEBwwGQU5LQVJBMVYwVAYDVQQKDE0oYykg +MjAwNSBUw5xSS1RSVVNUIEJpbGdpIMSwbGV0acWfaW0gdmUgQmlsacWfaW0gR8O8 +dmVubGnEn2kgSGl6bWV0bGVyaSBBLsWeLjAeFw0wNTA1MTMxMDI3MTdaFw0xNTAz +MjIxMDI3MTdaMIG3MT8wPQYDVQQDDDZUw5xSS1RSVVNUIEVsZWt0cm9uaWsgU2Vy +dGlmaWthIEhpem1ldCBTYcSfbGF5xLFjxLFzxLExCzAJBgNVBAYMAlRSMQ8wDQYD +VQQHDAZBTktBUkExVjBUBgNVBAoMTShjKSAyMDA1IFTDnFJLVFJVU1QgQmlsZ2kg +xLBsZXRpxZ9pbSB2ZSBCaWxpxZ9pbSBHw7x2ZW5sacSfaSBIaXptZXRsZXJpIEEu +xZ4uMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAylIF1mMD2Bxf3dJ7 +XfIMYGFbazt0K3gNfUW9InTojAPBxhEqPZW8qZSwu5GXyGl8hMW0kWxsE2qkVa2k +heiVfrMArwDCBRj1cJ02i67L5BuBf5OI+2pVu32Fks66WJ/bMsW9Xe8iSi9BB35J +YbOG7E6mQW6EvAPs9TscyB/C7qju6hJKjRTP8wrgUDn5CDX4EVmt5yLqS8oUBt5C +urKZ8y1UiBAG6uEaPj1nH/vO+3yC6BFdSsG5FOpU2WabfIl9BJpiyelSPJ6c79L1 +JuTm5Rh8i27fbMx4W09ysstcP4wFjdFMjK2Sx+F4f2VsSQZQLJ4ywtdKxnWKWU51 +b0dewQIDAQABoxAwDjAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBBQUAA4IBAQAV +9VX/N5aAWSGk/KEVTCD21F/aAyT8z5Aa9CEKmu46sWrv7/hg0Uw2ZkUd82YCdAR7 +kjCo3gp2D++Vbr3JN+YaDayJSFvMgzbC9UZcWYJWtNX+I7TYVBxEq8Sn5RTOPEFh +fEPmzcSBCYsk+1Ql1haolgxnB2+zUEfjHCQo3SqYpGH+2+oSN7wBGjSFvW5P55Fy +B0SFHljKVETd96y5y4khctuPwGkplyqjrhgjlxxBKot8KsF8kOipKMDTkcatKIdA +aLX/7KfS0zgYnNN9aV3wxqUeJBujR/xpB2jn5Jq07Q+hh4cCzofSSE7hvP/L8XKS +RGQDJereW26fyfJOrN3H +-----END CERTIFICATE----- + +# Issuer: CN=TÜRKTRUST Elektronik Sertifika Hizmet Sağlayıcısı O=TÜRKTRUST Bilgi İletişim ve Bilişim Güvenliği Hizmetleri A.Ş. (c) Kasım 2005 +# Subject: CN=TÜRKTRUST Elektronik Sertifika Hizmet Sağlayıcısı O=TÜRKTRUST Bilgi İletişim ve Bilişim Güvenliği Hizmetleri A.Ş. (c) Kasım 2005 +# Label: "TURKTRUST Certificate Services Provider Root 2" +# Serial: 1 +# MD5 Fingerprint: 37:a5:6e:d4:b1:25:84:97:b7:fd:56:15:7a:f9:a2:00 +# SHA1 Fingerprint: b4:35:d4:e1:11:9d:1c:66:90:a7:49:eb:b3:94:bd:63:7b:a7:82:b7 +# SHA256 Fingerprint: c4:70:cf:54:7e:23:02:b9:77:fb:29:dd:71:a8:9a:7b:6c:1f:60:77:7b:03:29:f5:60:17:f3:28:bf:4f:6b:e6 +-----BEGIN CERTIFICATE----- +MIIEPDCCAySgAwIBAgIBATANBgkqhkiG9w0BAQUFADCBvjE/MD0GA1UEAww2VMOc +UktUUlVTVCBFbGVrdHJvbmlrIFNlcnRpZmlrYSBIaXptZXQgU2HEn2xhecSxY8Sx +c8SxMQswCQYDVQQGEwJUUjEPMA0GA1UEBwwGQW5rYXJhMV0wWwYDVQQKDFRUw5xS +S1RSVVNUIEJpbGdpIMSwbGV0acWfaW0gdmUgQmlsacWfaW0gR8O8dmVubGnEn2kg +SGl6bWV0bGVyaSBBLsWeLiAoYykgS2FzxLFtIDIwMDUwHhcNMDUxMTA3MTAwNzU3 +WhcNMTUwOTE2MTAwNzU3WjCBvjE/MD0GA1UEAww2VMOcUktUUlVTVCBFbGVrdHJv +bmlrIFNlcnRpZmlrYSBIaXptZXQgU2HEn2xhecSxY8Sxc8SxMQswCQYDVQQGEwJU +UjEPMA0GA1UEBwwGQW5rYXJhMV0wWwYDVQQKDFRUw5xSS1RSVVNUIEJpbGdpIMSw +bGV0acWfaW0gdmUgQmlsacWfaW0gR8O8dmVubGnEn2kgSGl6bWV0bGVyaSBBLsWe +LiAoYykgS2FzxLFtIDIwMDUwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB +AQCpNn7DkUNMwxmYCMjHWHtPFoylzkkBH3MOrHUTpvqeLCDe2JAOCtFp0if7qnef +J1Il4std2NiDUBd9irWCPwSOtNXwSadktx4uXyCcUHVPr+G1QRT0mJKIx+XlZEdh +R3n9wFHxwZnn3M5q+6+1ATDcRhzviuyV79z/rxAc653YsKpqhRgNF8k+v/Gb0AmJ +Qv2gQrSdiVFVKc8bcLyEVK3BEx+Y9C52YItdP5qtygy/p1Zbj3e41Z55SZI/4PGX +JHpsmxcPbe9TmJEr5A++WXkHeLuXlfSfadRYhwqp48y2WBmfJiGxxFmNskF1wK1p +zpwACPI2/z7woQ8arBT9pmAPAgMBAAGjQzBBMB0GA1UdDgQWBBTZN7NOBf3Zz58S +Fq62iS/rJTqIHDAPBgNVHQ8BAf8EBQMDBwYAMA8GA1UdEwEB/wQFMAMBAf8wDQYJ +KoZIhvcNAQEFBQADggEBAHJglrfJ3NgpXiOFX7KzLXb7iNcX/nttRbj2hWyfIvwq +ECLsqrkw9qtY1jkQMZkpAL2JZkH7dN6RwRgLn7Vhy506vvWolKMiVW4XSf/SKfE4 +Jl3vpao6+XF75tpYHdN0wgH6PmlYX63LaL4ULptswLbcoCb6dxriJNoaN+BnrdFz +gw2lGh1uEpJ+hGIAF728JRhX8tepb1mIvDS3LoV4nZbcFMMsilKbloxSZj2GFotH +uFEJjOp9zYhys2AzsfAKRO8P9Qk3iCQOLGsgOqL6EfJANZxEaGM7rDNvY7wsu/LS +y3Z9fYjYHcgFHW68lKlmjHdxx/qR+i9Rnuk5UrbnBEI= +-----END CERTIFICATE----- + +# Issuer: CN=SwissSign Gold CA - G2 O=SwissSign AG +# Subject: CN=SwissSign Gold CA - G2 O=SwissSign AG +# Label: "SwissSign Gold CA - G2" +# Serial: 13492815561806991280 +# MD5 Fingerprint: 24:77:d9:a8:91:d1:3b:fa:88:2d:c2:ff:f8:cd:33:93 +# SHA1 Fingerprint: d8:c5:38:8a:b7:30:1b:1b:6e:d4:7a:e6:45:25:3a:6f:9f:1a:27:61 +# SHA256 Fingerprint: 62:dd:0b:e9:b9:f5:0a:16:3e:a0:f8:e7:5c:05:3b:1e:ca:57:ea:55:c8:68:8f:64:7c:68:81:f2:c8:35:7b:95 +-----BEGIN CERTIFICATE----- +MIIFujCCA6KgAwIBAgIJALtAHEP1Xk+wMA0GCSqGSIb3DQEBBQUAMEUxCzAJBgNV +BAYTAkNIMRUwEwYDVQQKEwxTd2lzc1NpZ24gQUcxHzAdBgNVBAMTFlN3aXNzU2ln +biBHb2xkIENBIC0gRzIwHhcNMDYxMDI1MDgzMDM1WhcNMzYxMDI1MDgzMDM1WjBF +MQswCQYDVQQGEwJDSDEVMBMGA1UEChMMU3dpc3NTaWduIEFHMR8wHQYDVQQDExZT +d2lzc1NpZ24gR29sZCBDQSAtIEcyMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIIC +CgKCAgEAr+TufoskDhJuqVAtFkQ7kpJcyrhdhJJCEyq8ZVeCQD5XJM1QiyUqt2/8 +76LQwB8CJEoTlo8jE+YoWACjR8cGp4QjK7u9lit/VcyLwVcfDmJlD909Vopz2q5+ +bbqBHH5CjCA12UNNhPqE21Is8w4ndwtrvxEvcnifLtg+5hg3Wipy+dpikJKVyh+c +6bM8K8vzARO/Ws/BtQpgvd21mWRTuKCWs2/iJneRjOBiEAKfNA+k1ZIzUd6+jbqE +emA8atufK+ze3gE/bk3lUIbLtK/tREDFylqM2tIrfKjuvqblCqoOpd8FUrdVxyJd +MmqXl2MT28nbeTZ7hTpKxVKJ+STnnXepgv9VHKVxaSvRAiTysybUa9oEVeXBCsdt +MDeQKuSeFDNeFhdVxVu1yzSJkvGdJo+hB9TGsnhQ2wwMC3wLjEHXuendjIj3o02y +MszYF9rNt85mndT9Xv+9lz4pded+p2JYryU0pUHHPbwNUMoDAw8IWh+Vc3hiv69y +FGkOpeUDDniOJihC8AcLYiAQZzlG+qkDzAQ4embvIIO1jEpWjpEA/I5cgt6IoMPi +aG59je883WX0XaxR7ySArqpWl2/5rX3aYT+YdzylkbYcjCbaZaIJbcHiVOO5ykxM +gI93e2CaHt+28kgeDrpOVG2Y4OGiGqJ3UM/EY5LsRxmd6+ZrzsECAwEAAaOBrDCB +qTAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUWyV7 +lqRlUX64OfPAeGZe6Drn8O4wHwYDVR0jBBgwFoAUWyV7lqRlUX64OfPAeGZe6Drn +8O4wRgYDVR0gBD8wPTA7BglghXQBWQECAQEwLjAsBggrBgEFBQcCARYgaHR0cDov +L3JlcG9zaXRvcnkuc3dpc3NzaWduLmNvbS8wDQYJKoZIhvcNAQEFBQADggIBACe6 +45R88a7A3hfm5djV9VSwg/S7zV4Fe0+fdWavPOhWfvxyeDgD2StiGwC5+OlgzczO +UYrHUDFu4Up+GC9pWbY9ZIEr44OE5iKHjn3g7gKZYbge9LgriBIWhMIxkziWMaa5 +O1M/wySTVltpkuzFwbs4AOPsF6m43Md8AYOfMke6UiI0HTJ6CVanfCU2qT1L2sCC +bwq7EsiHSycR+R4tx5M/nttfJmtS2S6K8RTGRI0Vqbe/vd6mGu6uLftIdxf+u+yv +GPUqUfA5hJeVbG4bwyvEdGB5JbAKJ9/fXtI5z0V9QkvfsywexcZdylU6oJxpmo/a +77KwPJ+HbBIrZXAVUjEaJM9vMSNQH4xPjyPDdEFjHFWoFN0+4FFQz/EbMFYOkrCC +hdiDyyJkvC24JdVUorgG6q2SpCSgwYa1ShNqR88uC1aVVMvOmttqtKay20EIhid3 +92qgQmwLOM7XdVAyksLfKzAiSNDVQTglXaTpXZ/GlHXQRf0wl0OPkKsKx4ZzYEpp +Ld6leNcG2mqeSz53OiATIgHQv2ieY2BrNU0LbbqhPcCT4H8js1WtciVORvnSFu+w +ZMEBnunKoGqYDs/YYPIvSbjkQuE4NRb0yG5P94FW6LqjviOvrv1vA+ACOzB2+htt +Qc8Bsem4yWb02ybzOqR08kkkW8mw0FfB+j564ZfJ +-----END CERTIFICATE----- + +# Issuer: CN=SwissSign Silver CA - G2 O=SwissSign AG +# Subject: CN=SwissSign Silver CA - G2 O=SwissSign AG +# Label: "SwissSign Silver CA - G2" +# Serial: 5700383053117599563 +# MD5 Fingerprint: e0:06:a1:c9:7d:cf:c9:fc:0d:c0:56:75:96:d8:62:13 +# SHA1 Fingerprint: 9b:aa:e5:9f:56:ee:21:cb:43:5a:be:25:93:df:a7:f0:40:d1:1d:cb +# SHA256 Fingerprint: be:6c:4d:a2:bb:b9:ba:59:b6:f3:93:97:68:37:42:46:c3:c0:05:99:3f:a9:8f:02:0d:1d:ed:be:d4:8a:81:d5 +-----BEGIN CERTIFICATE----- +MIIFvTCCA6WgAwIBAgIITxvUL1S7L0swDQYJKoZIhvcNAQEFBQAwRzELMAkGA1UE +BhMCQ0gxFTATBgNVBAoTDFN3aXNzU2lnbiBBRzEhMB8GA1UEAxMYU3dpc3NTaWdu +IFNpbHZlciBDQSAtIEcyMB4XDTA2MTAyNTA4MzI0NloXDTM2MTAyNTA4MzI0Nlow +RzELMAkGA1UEBhMCQ0gxFTATBgNVBAoTDFN3aXNzU2lnbiBBRzEhMB8GA1UEAxMY +U3dpc3NTaWduIFNpbHZlciBDQSAtIEcyMIICIjANBgkqhkiG9w0BAQEFAAOCAg8A +MIICCgKCAgEAxPGHf9N4Mfc4yfjDmUO8x/e8N+dOcbpLj6VzHVxumK4DV644N0Mv +Fz0fyM5oEMF4rhkDKxD6LHmD9ui5aLlV8gREpzn5/ASLHvGiTSf5YXu6t+WiE7br +YT7QbNHm+/pe7R20nqA1W6GSy/BJkv6FCgU+5tkL4k+73JU3/JHpMjUi0R86TieF +nbAVlDLaYQ1HTWBCrpJH6INaUFjpiou5XaHc3ZlKHzZnu0jkg7Y360g6rw9njxcH +6ATK72oxh9TAtvmUcXtnZLi2kUpCe2UuMGoM9ZDulebyzYLs2aFK7PayS+VFheZt +eJMELpyCbTapxDFkH4aDCyr0NQp4yVXPQbBH6TCfmb5hqAaEuSh6XzjZG6k4sIN/ +c8HDO0gqgg8hm7jMqDXDhBuDsz6+pJVpATqJAHgE2cn0mRmrVn5bi4Y5FZGkECwJ +MoBgs5PAKrYYC51+jUnyEEp/+dVGLxmSo5mnJqy7jDzmDrxHB9xzUfFwZC8I+bRH +HTBsROopN4WSaGa8gzj+ezku01DwH/teYLappvonQfGbGHLy9YR0SslnxFSuSGTf +jNFusB3hB48IHpmccelM2KX3RxIfdNFRnobzwqIjQAtz20um53MGjMGg6cFZrEb6 +5i/4z3GcRm25xBWNOHkDRUjvxF3XCO6HOSKGsg0PWEP3calILv3q1h8CAwEAAaOB +rDCBqTAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQU +F6DNweRBtjpbO8tFnb0cwpj6hlgwHwYDVR0jBBgwFoAUF6DNweRBtjpbO8tFnb0c +wpj6hlgwRgYDVR0gBD8wPTA7BglghXQBWQEDAQEwLjAsBggrBgEFBQcCARYgaHR0 +cDovL3JlcG9zaXRvcnkuc3dpc3NzaWduLmNvbS8wDQYJKoZIhvcNAQEFBQADggIB +AHPGgeAn0i0P4JUw4ppBf1AsX19iYamGamkYDHRJ1l2E6kFSGG9YrVBWIGrGvShp +WJHckRE1qTodvBqlYJ7YH39FkWnZfrt4csEGDyrOj4VwYaygzQu4OSlWhDJOhrs9 +xCrZ1x9y7v5RoSJBsXECYxqCsGKrXlcSH9/L3XWgwF15kIwb4FDm3jH+mHtwX6WQ +2K34ArZv02DdQEsixT2tOnqfGhpHkXkzuoLcMmkDlm4fS/Bx/uNncqCxv1yL5PqZ +IseEuRuNI5c/7SXgz2W79WEE790eslpBIlqhn10s6FvJbakMDHiqYMZWjwFaDGi8 +aRl5xB9+lwW/xekkUV7U1UtT7dkjWjYDZaPBA61BMPNGG4WQr2W11bHkFlt4dR2X +em1ZqSqPe97Dh4kQmUlzeMg9vVE1dCrV8X5pGyq7O70luJpaPXJhkGaH7gzWTdQR +dAtq/gsD/KNVV4n+SsuuWxcFyPKNIzFTONItaj+CuY0IavdeQXRuwxF+B6wpYJE/ +OMpXEA29MC/HpeZBoNquBYeaoKRlbEwJDIm6uNO5wJOKMPqN5ZprFQFOZ6raYlY+ +hAhm0sQ2fac+EPyI4NSA5QC9qvNOBqN6avlicuMJT+ubDgEj8Z+7fNzcbBGXJbLy +tGMU0gYqZ4yD9c7qB9iaah7s5Aq7KkzrCWA5zspi2C5u +-----END CERTIFICATE----- + +# Issuer: CN=GeoTrust Primary Certification Authority O=GeoTrust Inc. +# Subject: CN=GeoTrust Primary Certification Authority O=GeoTrust Inc. +# Label: "GeoTrust Primary Certification Authority" +# Serial: 32798226551256963324313806436981982369 +# MD5 Fingerprint: 02:26:c3:01:5e:08:30:37:43:a9:d0:7d:cf:37:e6:bf +# SHA1 Fingerprint: 32:3c:11:8e:1b:f7:b8:b6:52:54:e2:e2:10:0d:d6:02:90:37:f0:96 +# SHA256 Fingerprint: 37:d5:10:06:c5:12:ea:ab:62:64:21:f1:ec:8c:92:01:3f:c5:f8:2a:e9:8e:e5:33:eb:46:19:b8:de:b4:d0:6c +-----BEGIN CERTIFICATE----- +MIIDfDCCAmSgAwIBAgIQGKy1av1pthU6Y2yv2vrEoTANBgkqhkiG9w0BAQUFADBY +MQswCQYDVQQGEwJVUzEWMBQGA1UEChMNR2VvVHJ1c3QgSW5jLjExMC8GA1UEAxMo +R2VvVHJ1c3QgUHJpbWFyeSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAeFw0wNjEx +MjcwMDAwMDBaFw0zNjA3MTYyMzU5NTlaMFgxCzAJBgNVBAYTAlVTMRYwFAYDVQQK +Ew1HZW9UcnVzdCBJbmMuMTEwLwYDVQQDEyhHZW9UcnVzdCBQcmltYXJ5IENlcnRp +ZmljYXRpb24gQXV0aG9yaXR5MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC +AQEAvrgVe//UfH1nrYNke8hCUy3f9oQIIGHWAVlqnEQRr+92/ZV+zmEwu3qDXwK9 +AWbK7hWNb6EwnL2hhZ6UOvNWiAAxz9juapYC2e0DjPt1befquFUWBRaa9OBesYjA +ZIVcFU2Ix7e64HXprQU9nceJSOC7KMgD4TCTZF5SwFlwIjVXiIrxlQqD17wxcwE0 +7e9GceBrAqg1cmuXm2bgyxx5X9gaBGgeRwLmnWDiNpcB3841kt++Z8dtd1k7j53W +kBWUvEI0EME5+bEnPn7WinXFsq+W06Lem+SYvn3h6YGttm/81w7a4DSwDRp35+MI +mO9Y+pyEtzavwt+s0vQQBnBxNQIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4G +A1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQULNVQQZcVi/CPNmFbSvtr2ZnJM5IwDQYJ +KoZIhvcNAQEFBQADggEBAFpwfyzdtzRP9YZRqSa+S7iq8XEN3GHHoOo0Hnp3DwQ1 +6CePbJC/kRYkRj5KTs4rFtULUh38H2eiAkUxT87z+gOneZ1TatnaYzr4gNfTmeGl +4b7UVXGYNTq+k+qurUKykG/g/CFNNWMziUnWm07Kx+dOCQD32sfvmWKZd7aVIl6K +oKv0uHiYyjgZmclynnjNS6yvGaBzEi38wkG6gZHaFloxt/m0cYASSJlyc1pZU8Fj +UjPtp8nSOQJw+uCxQmYpqptR7TBUIhRf2asdweSU8Pj1K/fqynhG1riR/aYNKxoU +AT6A8EKglQdebc3MS6RFjasS6LPeWuWgfOgPIh1a6Vk= +-----END CERTIFICATE----- + +# Issuer: CN=thawte Primary Root CA O=thawte, Inc. OU=Certification Services Division/(c) 2006 thawte, Inc. - For authorized use only +# Subject: CN=thawte Primary Root CA O=thawte, Inc. OU=Certification Services Division/(c) 2006 thawte, Inc. - For authorized use only +# Label: "thawte Primary Root CA" +# Serial: 69529181992039203566298953787712940909 +# MD5 Fingerprint: 8c:ca:dc:0b:22:ce:f5:be:72:ac:41:1a:11:a8:d8:12 +# SHA1 Fingerprint: 91:c6:d6:ee:3e:8a:c8:63:84:e5:48:c2:99:29:5c:75:6c:81:7b:81 +# SHA256 Fingerprint: 8d:72:2f:81:a9:c1:13:c0:79:1d:f1:36:a2:96:6d:b2:6c:95:0a:97:1d:b4:6b:41:99:f4:ea:54:b7:8b:fb:9f +-----BEGIN CERTIFICATE----- +MIIEIDCCAwigAwIBAgIQNE7VVyDV7exJ9C/ON9srbTANBgkqhkiG9w0BAQUFADCB +qTELMAkGA1UEBhMCVVMxFTATBgNVBAoTDHRoYXd0ZSwgSW5jLjEoMCYGA1UECxMf +Q2VydGlmaWNhdGlvbiBTZXJ2aWNlcyBEaXZpc2lvbjE4MDYGA1UECxMvKGMpIDIw +MDYgdGhhd3RlLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxHzAdBgNV +BAMTFnRoYXd0ZSBQcmltYXJ5IFJvb3QgQ0EwHhcNMDYxMTE3MDAwMDAwWhcNMzYw +NzE2MjM1OTU5WjCBqTELMAkGA1UEBhMCVVMxFTATBgNVBAoTDHRoYXd0ZSwgSW5j +LjEoMCYGA1UECxMfQ2VydGlmaWNhdGlvbiBTZXJ2aWNlcyBEaXZpc2lvbjE4MDYG +A1UECxMvKGMpIDIwMDYgdGhhd3RlLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNl +IG9ubHkxHzAdBgNVBAMTFnRoYXd0ZSBQcmltYXJ5IFJvb3QgQ0EwggEiMA0GCSqG +SIb3DQEBAQUAA4IBDwAwggEKAoIBAQCsoPD7gFnUnMekz52hWXMJEEUMDSxuaPFs +W0hoSVk3/AszGcJ3f8wQLZU0HObrTQmnHNK4yZc2AreJ1CRfBsDMRJSUjQJib+ta +3RGNKJpchJAQeg29dGYvajig4tVUROsdB58Hum/u6f1OCyn1PoSgAfGcq/gcfomk +6KHYcWUNo1F77rzSImANuVud37r8UVsLr5iy6S7pBOhih94ryNdOwUxkHt3Ph1i6 +Sk/KaAcdHJ1KxtUvkcx8cXIcxcBn6zL9yZJclNqFwJu/U30rCfSMnZEfl2pSy94J +NqR32HuHUETVPm4pafs5SSYeCaWAe0At6+gnhcn+Yf1+5nyXHdWdAgMBAAGjQjBA +MA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBR7W0XP +r87Lev0xkhpqtvNG61dIUDANBgkqhkiG9w0BAQUFAAOCAQEAeRHAS7ORtvzw6WfU +DW5FvlXok9LOAz/t2iWwHVfLHjp2oEzsUHboZHIMpKnxuIvW1oeEuzLlQRHAd9mz +YJ3rG9XRbkREqaYB7FViHXe4XI5ISXycO1cRrK1zN44veFyQaEfZYGDm/Ac9IiAX +xPcW6cTYcvnIc3zfFi8VqT79aie2oetaupgf1eNNZAqdE8hhuvU5HIe6uL17In/2 +/qxAeeWsEG89jxt5dovEN7MhGITlNgDrYyCZuen+MwS7QcjBAvlEYyCegc5C09Y/ +LHbTY5xZ3Y+m4Q6gLkH3LpVHz7z9M/P2C2F+fpErgUfCJzDupxBdN49cOSvkBPB7 +jVaMaA== +-----END CERTIFICATE----- + +# Issuer: CN=VeriSign Class 3 Public Primary Certification Authority - G5 O=VeriSign, Inc. OU=VeriSign Trust Network/(c) 2006 VeriSign, Inc. - For authorized use only +# Subject: CN=VeriSign Class 3 Public Primary Certification Authority - G5 O=VeriSign, Inc. OU=VeriSign Trust Network/(c) 2006 VeriSign, Inc. - For authorized use only +# Label: "VeriSign Class 3 Public Primary Certification Authority - G5" +# Serial: 33037644167568058970164719475676101450 +# MD5 Fingerprint: cb:17:e4:31:67:3e:e2:09:fe:45:57:93:f3:0a:fa:1c +# SHA1 Fingerprint: 4e:b6:d5:78:49:9b:1c:cf:5f:58:1e:ad:56:be:3d:9b:67:44:a5:e5 +# SHA256 Fingerprint: 9a:cf:ab:7e:43:c8:d8:80:d0:6b:26:2a:94:de:ee:e4:b4:65:99:89:c3:d0:ca:f1:9b:af:64:05:e4:1a:b7:df +-----BEGIN CERTIFICATE----- +MIIE0zCCA7ugAwIBAgIQGNrRniZ96LtKIVjNzGs7SjANBgkqhkiG9w0BAQUFADCB +yjELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQL +ExZWZXJpU2lnbiBUcnVzdCBOZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwNiBWZXJp +U2lnbiwgSW5jLiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5MUUwQwYDVQQDEzxW +ZXJpU2lnbiBDbGFzcyAzIFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0 +aG9yaXR5IC0gRzUwHhcNMDYxMTA4MDAwMDAwWhcNMzYwNzE2MjM1OTU5WjCByjEL +MAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQLExZW +ZXJpU2lnbiBUcnVzdCBOZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwNiBWZXJpU2ln +biwgSW5jLiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5MUUwQwYDVQQDEzxWZXJp +U2lnbiBDbGFzcyAzIFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0aG9y +aXR5IC0gRzUwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCvJAgIKXo1 +nmAMqudLO07cfLw8RRy7K+D+KQL5VwijZIUVJ/XxrcgxiV0i6CqqpkKzj/i5Vbex +t0uz/o9+B1fs70PbZmIVYc9gDaTY3vjgw2IIPVQT60nKWVSFJuUrjxuf6/WhkcIz +SdhDY2pSS9KP6HBRTdGJaXvHcPaz3BJ023tdS1bTlr8Vd6Gw9KIl8q8ckmcY5fQG +BO+QueQA5N06tRn/Arr0PO7gi+s3i+z016zy9vA9r911kTMZHRxAy3QkGSGT2RT+ +rCpSx4/VBEnkjWNHiDxpg8v+R70rfk/Fla4OndTRQ8Bnc+MUCH7lP59zuDMKz10/ +NIeWiu5T6CUVAgMBAAGjgbIwga8wDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8E +BAMCAQYwbQYIKwYBBQUHAQwEYTBfoV2gWzBZMFcwVRYJaW1hZ2UvZ2lmMCEwHzAH +BgUrDgMCGgQUj+XTGoasjY5rw8+AatRIGCx7GS4wJRYjaHR0cDovL2xvZ28udmVy +aXNpZ24uY29tL3ZzbG9nby5naWYwHQYDVR0OBBYEFH/TZafC3ey78DAJ80M5+gKv +MzEzMA0GCSqGSIb3DQEBBQUAA4IBAQCTJEowX2LP2BqYLz3q3JktvXf2pXkiOOzE +p6B4Eq1iDkVwZMXnl2YtmAl+X6/WzChl8gGqCBpH3vn5fJJaCGkgDdk+bW48DW7Y +5gaRQBi5+MHt39tBquCWIMnNZBU4gcmU7qKEKQsTb47bDN0lAtukixlE0kF6BWlK +WE9gyn6CagsCqiUXObXbf+eEZSqVir2G3l6BFoMtEMze/aiCKm0oHw0LxOXnGiYZ +4fQRbxC1lfznQgUy286dUV4otp6F01vvpX1FQHKOtw5rDgb7MzVIcbidJ4vEZV8N +hnacRHr2lVz2XTIIM6RUthg/aFzyQkqFOFSDX9HoLPKsEdao7WNq +-----END CERTIFICATE----- + +# Issuer: CN=SecureTrust CA O=SecureTrust Corporation +# Subject: CN=SecureTrust CA O=SecureTrust Corporation +# Label: "SecureTrust CA" +# Serial: 17199774589125277788362757014266862032 +# MD5 Fingerprint: dc:32:c3:a7:6d:25:57:c7:68:09:9d:ea:2d:a9:a2:d1 +# SHA1 Fingerprint: 87:82:c6:c3:04:35:3b:cf:d2:96:92:d2:59:3e:7d:44:d9:34:ff:11 +# SHA256 Fingerprint: f1:c1:b5:0a:e5:a2:0d:d8:03:0e:c9:f6:bc:24:82:3d:d3:67:b5:25:57:59:b4:e7:1b:61:fc:e9:f7:37:5d:73 +-----BEGIN CERTIFICATE----- +MIIDuDCCAqCgAwIBAgIQDPCOXAgWpa1Cf/DrJxhZ0DANBgkqhkiG9w0BAQUFADBI +MQswCQYDVQQGEwJVUzEgMB4GA1UEChMXU2VjdXJlVHJ1c3QgQ29ycG9yYXRpb24x +FzAVBgNVBAMTDlNlY3VyZVRydXN0IENBMB4XDTA2MTEwNzE5MzExOFoXDTI5MTIz +MTE5NDA1NVowSDELMAkGA1UEBhMCVVMxIDAeBgNVBAoTF1NlY3VyZVRydXN0IENv +cnBvcmF0aW9uMRcwFQYDVQQDEw5TZWN1cmVUcnVzdCBDQTCCASIwDQYJKoZIhvcN +AQEBBQADggEPADCCAQoCggEBAKukgeWVzfX2FI7CT8rU4niVWJxB4Q2ZQCQXOZEz +Zum+4YOvYlyJ0fwkW2Gz4BERQRwdbvC4u/jep4G6pkjGnx29vo6pQT64lO0pGtSO +0gMdA+9tDWccV9cGrcrI9f4Or2YlSASWC12juhbDCE/RRvgUXPLIXgGZbf2IzIao +wW8xQmxSPmjL8xk037uHGFaAJsTQ3MBv396gwpEWoGQRS0S8Hvbn+mPeZqx2pHGj +7DaUaHp3pLHnDi+BeuK1cobvomuL8A/b01k/unK8RCSc43Oz969XL0Imnal0ugBS +8kvNU3xHCzaFDmapCJcWNFfBZveA4+1wVMeT4C4oFVmHursCAwEAAaOBnTCBmjAT +BgkrBgEEAYI3FAIEBh4EAEMAQTALBgNVHQ8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB +/zAdBgNVHQ4EFgQUQjK2FvoE/f5dS3rD/fdMQB1aQ68wNAYDVR0fBC0wKzApoCeg +JYYjaHR0cDovL2NybC5zZWN1cmV0cnVzdC5jb20vU1RDQS5jcmwwEAYJKwYBBAGC +NxUBBAMCAQAwDQYJKoZIhvcNAQEFBQADggEBADDtT0rhWDpSclu1pqNlGKa7UTt3 +6Z3q059c4EVlew3KW+JwULKUBRSuSceNQQcSc5R+DCMh/bwQf2AQWnL1mA6s7Ll/ +3XpvXdMc9P+IBWlCqQVxyLesJugutIxq/3HcuLHfmbx8IVQr5Fiiu1cprp6poxkm +D5kuCLDv/WnPmRoJjeOnnyvJNjR7JLN4TJUXpAYmHrZkUjZfYGfZnMUFdAvnZyPS +CPyI6a6Lf+Ew9Dd+/cYy2i2eRDAwbO4H3tI0/NL/QPZL9GZGBlSm8jIKYyYwa5vR +3ItHuuG51WLQoqD0ZwV4KWMabwTW+MZMo5qxN7SN5ShLHZ4swrhovO0C7jE= +-----END CERTIFICATE----- + +# Issuer: CN=Secure Global CA O=SecureTrust Corporation +# Subject: CN=Secure Global CA O=SecureTrust Corporation +# Label: "Secure Global CA" +# Serial: 9751836167731051554232119481456978597 +# MD5 Fingerprint: cf:f4:27:0d:d4:ed:dc:65:16:49:6d:3d:da:bf:6e:de +# SHA1 Fingerprint: 3a:44:73:5a:e5:81:90:1f:24:86:61:46:1e:3b:9c:c4:5f:f5:3a:1b +# SHA256 Fingerprint: 42:00:f5:04:3a:c8:59:0e:bb:52:7d:20:9e:d1:50:30:29:fb:cb:d4:1c:a1:b5:06:ec:27:f1:5a:de:7d:ac:69 +-----BEGIN CERTIFICATE----- +MIIDvDCCAqSgAwIBAgIQB1YipOjUiolN9BPI8PjqpTANBgkqhkiG9w0BAQUFADBK +MQswCQYDVQQGEwJVUzEgMB4GA1UEChMXU2VjdXJlVHJ1c3QgQ29ycG9yYXRpb24x +GTAXBgNVBAMTEFNlY3VyZSBHbG9iYWwgQ0EwHhcNMDYxMTA3MTk0MjI4WhcNMjkx +MjMxMTk1MjA2WjBKMQswCQYDVQQGEwJVUzEgMB4GA1UEChMXU2VjdXJlVHJ1c3Qg +Q29ycG9yYXRpb24xGTAXBgNVBAMTEFNlY3VyZSBHbG9iYWwgQ0EwggEiMA0GCSqG +SIb3DQEBAQUAA4IBDwAwggEKAoIBAQCvNS7YrGxVaQZx5RNoJLNP2MwhR/jxYDiJ +iQPpvepeRlMJ3Fz1Wuj3RSoC6zFh1ykzTM7HfAo3fg+6MpjhHZevj8fcyTiW89sa +/FHtaMbQbqR8JNGuQsiWUGMu4P51/pinX0kuleM5M2SOHqRfkNJnPLLZ/kG5VacJ +jnIFHovdRIWCQtBJwB1g8NEXLJXr9qXBkqPFwqcIYA1gBBCWeZ4WNOaptvolRTnI +HmX5k/Wq8VLcmZg9pYYaDDUz+kulBAYVHDGA76oYa8J719rO+TMg1fW9ajMtgQT7 +sFzUnKPiXB3jqUJ1XnvUd+85VLrJChgbEplJL4hL/VBi0XPnj3pDAgMBAAGjgZ0w +gZowEwYJKwYBBAGCNxQCBAYeBABDAEEwCwYDVR0PBAQDAgGGMA8GA1UdEwEB/wQF +MAMBAf8wHQYDVR0OBBYEFK9EBMJBfkiD2045AuzshHrmzsmkMDQGA1UdHwQtMCsw +KaAnoCWGI2h0dHA6Ly9jcmwuc2VjdXJldHJ1c3QuY29tL1NHQ0EuY3JsMBAGCSsG +AQQBgjcVAQQDAgEAMA0GCSqGSIb3DQEBBQUAA4IBAQBjGghAfaReUw132HquHw0L +URYD7xh8yOOvaliTFGCRsoTciE6+OYo68+aCiV0BN7OrJKQVDpI1WkpEXk5X+nXO +H0jOZvQ8QCaSmGwb7iRGDBezUqXbpZGRzzfTb+cnCDpOGR86p1hcF895P4vkp9Mm +I50mD1hp/Ed+stCNi5O/KU9DaXR2Z0vPB4zmAve14bRDtUstFJ/53CYNv6ZHdAbY +iNE6KTCEztI5gGIbqMdXSbxqVVFnFUq+NQfk1XWYN3kwFNspnWzFacxHVaIw98xc +f8LDmBxrThaA63p4ZUWiABqvDA1VZDRIuJK58bRQKfJPIx/abKwfROHdI3hRW8cW +-----END CERTIFICATE----- + +# Issuer: CN=COMODO Certification Authority O=COMODO CA Limited +# Subject: CN=COMODO Certification Authority O=COMODO CA Limited +# Label: "COMODO Certification Authority" +# Serial: 104350513648249232941998508985834464573 +# MD5 Fingerprint: 5c:48:dc:f7:42:72:ec:56:94:6d:1c:cc:71:35:80:75 +# SHA1 Fingerprint: 66:31:bf:9e:f7:4f:9e:b6:c9:d5:a6:0c:ba:6a:be:d1:f7:bd:ef:7b +# SHA256 Fingerprint: 0c:2c:d6:3d:f7:80:6f:a3:99:ed:e8:09:11:6b:57:5b:f8:79:89:f0:65:18:f9:80:8c:86:05:03:17:8b:af:66 +-----BEGIN CERTIFICATE----- +MIIEHTCCAwWgAwIBAgIQToEtioJl4AsC7j41AkblPTANBgkqhkiG9w0BAQUFADCB +gTELMAkGA1UEBhMCR0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4G +A1UEBxMHU2FsZm9yZDEaMBgGA1UEChMRQ09NT0RPIENBIExpbWl0ZWQxJzAlBgNV +BAMTHkNPTU9ETyBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAeFw0wNjEyMDEwMDAw +MDBaFw0yOTEyMzEyMzU5NTlaMIGBMQswCQYDVQQGEwJHQjEbMBkGA1UECBMSR3Jl +YXRlciBNYW5jaGVzdGVyMRAwDgYDVQQHEwdTYWxmb3JkMRowGAYDVQQKExFDT01P +RE8gQ0EgTGltaXRlZDEnMCUGA1UEAxMeQ09NT0RPIENlcnRpZmljYXRpb24gQXV0 +aG9yaXR5MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA0ECLi3LjkRv3 +UcEbVASY06m/weaKXTuH+7uIzg3jLz8GlvCiKVCZrts7oVewdFFxze1CkU1B/qnI +2GqGd0S7WWaXUF601CxwRM/aN5VCaTwwxHGzUvAhTaHYujl8HJ6jJJ3ygxaYqhZ8 +Q5sVW7euNJH+1GImGEaaP+vB+fGQV+useg2L23IwambV4EajcNxo2f8ESIl33rXp ++2dtQem8Ob0y2WIC8bGoPW43nOIv4tOiJovGuFVDiOEjPqXSJDlqR6sA1KGzqSX+ +DT+nHbrTUcELpNqsOO9VUCQFZUaTNE8tja3G1CEZ0o7KBWFxB3NH5YoZEr0ETc5O +nKVIrLsm9wIDAQABo4GOMIGLMB0GA1UdDgQWBBQLWOWLxkwVN6RAqTCpIb5HNlpW +/zAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zBJBgNVHR8EQjBAMD6g +PKA6hjhodHRwOi8vY3JsLmNvbW9kb2NhLmNvbS9DT01PRE9DZXJ0aWZpY2F0aW9u +QXV0aG9yaXR5LmNybDANBgkqhkiG9w0BAQUFAAOCAQEAPpiem/Yb6dc5t3iuHXIY +SdOH5EOC6z/JqvWote9VfCFSZfnVDeFs9D6Mk3ORLgLETgdxb8CPOGEIqB6BCsAv +IC9Bi5HcSEW88cbeunZrM8gALTFGTO3nnc+IlP8zwFboJIYmuNg4ON8qa90SzMc/ +RxdMosIGlgnW2/4/PEZB31jiVg88O8EckzXZOFKs7sjsLjBOlDW0JB9LeGna8gI4 +zJVSk/BwJVmcIGfE7vmLV2H0knZ9P4SNVbfo5azV8fUZVqZa+5Acr5Pr5RzUZ5dd +BA6+C4OmF4O5MBKgxTMVBbkN+8cFduPYSo38NBejxiEovjBFMR7HeL5YYTisO+IB +ZQ== +-----END CERTIFICATE----- + +# Issuer: CN=Network Solutions Certificate Authority O=Network Solutions L.L.C. +# Subject: CN=Network Solutions Certificate Authority O=Network Solutions L.L.C. +# Label: "Network Solutions Certificate Authority" +# Serial: 116697915152937497490437556386812487904 +# MD5 Fingerprint: d3:f3:a6:16:c0:fa:6b:1d:59:b1:2d:96:4d:0e:11:2e +# SHA1 Fingerprint: 74:f8:a3:c3:ef:e7:b3:90:06:4b:83:90:3c:21:64:60:20:e5:df:ce +# SHA256 Fingerprint: 15:f0:ba:00:a3:ac:7a:f3:ac:88:4c:07:2b:10:11:a0:77:bd:77:c0:97:f4:01:64:b2:f8:59:8a:bd:83:86:0c +-----BEGIN CERTIFICATE----- +MIID5jCCAs6gAwIBAgIQV8szb8JcFuZHFhfjkDFo4DANBgkqhkiG9w0BAQUFADBi +MQswCQYDVQQGEwJVUzEhMB8GA1UEChMYTmV0d29yayBTb2x1dGlvbnMgTC5MLkMu +MTAwLgYDVQQDEydOZXR3b3JrIFNvbHV0aW9ucyBDZXJ0aWZpY2F0ZSBBdXRob3Jp +dHkwHhcNMDYxMjAxMDAwMDAwWhcNMjkxMjMxMjM1OTU5WjBiMQswCQYDVQQGEwJV +UzEhMB8GA1UEChMYTmV0d29yayBTb2x1dGlvbnMgTC5MLkMuMTAwLgYDVQQDEydO +ZXR3b3JrIFNvbHV0aW9ucyBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkwggEiMA0GCSqG +SIb3DQEBAQUAA4IBDwAwggEKAoIBAQDkvH6SMG3G2I4rC7xGzuAnlt7e+foS0zwz +c7MEL7xxjOWftiJgPl9dzgn/ggwbmlFQGiaJ3dVhXRncEg8tCqJDXRfQNJIg6nPP +OCwGJgl6cvf6UDL4wpPTaaIjzkGxzOTVHzbRijr4jGPiFFlp7Q3Tf2vouAPlT2rl +mGNpSAW+Lv8ztumXWWn4Zxmuk2GWRBXTcrA/vGp97Eh/jcOrqnErU2lBUzS1sLnF +BgrEsEX1QV1uiUV7PTsmjHTC5dLRfbIR1PtYMiKagMnc/Qzpf14Dl847ABSHJ3A4 +qY5usyd2mFHgBeMhqxrVhSI8KbWaFsWAqPS7azCPL0YCorEMIuDTAgMBAAGjgZcw +gZQwHQYDVR0OBBYEFCEwyfsA106Y2oeqKtCnLrFAMadMMA4GA1UdDwEB/wQEAwIB +BjAPBgNVHRMBAf8EBTADAQH/MFIGA1UdHwRLMEkwR6BFoEOGQWh0dHA6Ly9jcmwu +bmV0c29sc3NsLmNvbS9OZXR3b3JrU29sdXRpb25zQ2VydGlmaWNhdGVBdXRob3Jp +dHkuY3JsMA0GCSqGSIb3DQEBBQUAA4IBAQC7rkvnt1frf6ott3NHhWrB5KUd5Oc8 +6fRZZXe1eltajSU24HqXLjjAV2CDmAaDn7l2em5Q4LqILPxFzBiwmZVRDuwduIj/ +h1AcgsLj4DKAv6ALR8jDMe+ZZzKATxcheQxpXN5eNK4CtSbqUN9/GGUsyfJj4akH +/nxxH2szJGoeBfcFaMBqEssuXmHLrijTfsK0ZpEmXzwuJF/LWA/rKOyvEZbz3Htv +wKeI8lN3s2Berq4o2jUsbzRF0ybh3uxbTydrFny9RAQYgrOJeRcQcT16ohZO9QHN +pGxlaKFJdlxDydi8NmdspZS11My5vWo1ViHe2MPr+8ukYEywVaCge1ey +-----END CERTIFICATE----- + +# Issuer: CN=WellsSecure Public Root Certificate Authority O=Wells Fargo WellsSecure OU=Wells Fargo Bank NA +# Subject: CN=WellsSecure Public Root Certificate Authority O=Wells Fargo WellsSecure OU=Wells Fargo Bank NA +# Label: "WellsSecure Public Root Certificate Authority" +# Serial: 1 +# MD5 Fingerprint: 15:ac:a5:c2:92:2d:79:bc:e8:7f:cb:67:ed:02:cf:36 +# SHA1 Fingerprint: e7:b4:f6:9d:61:ec:90:69:db:7e:90:a7:40:1a:3c:f4:7d:4f:e8:ee +# SHA256 Fingerprint: a7:12:72:ae:aa:a3:cf:e8:72:7f:7f:b3:9f:0f:b3:d1:e5:42:6e:90:60:b0:6e:e6:f1:3e:9a:3c:58:33:cd:43 +-----BEGIN CERTIFICATE----- +MIIEvTCCA6WgAwIBAgIBATANBgkqhkiG9w0BAQUFADCBhTELMAkGA1UEBhMCVVMx +IDAeBgNVBAoMF1dlbGxzIEZhcmdvIFdlbGxzU2VjdXJlMRwwGgYDVQQLDBNXZWxs +cyBGYXJnbyBCYW5rIE5BMTYwNAYDVQQDDC1XZWxsc1NlY3VyZSBQdWJsaWMgUm9v +dCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkwHhcNMDcxMjEzMTcwNzU0WhcNMjIxMjE0 +MDAwNzU0WjCBhTELMAkGA1UEBhMCVVMxIDAeBgNVBAoMF1dlbGxzIEZhcmdvIFdl +bGxzU2VjdXJlMRwwGgYDVQQLDBNXZWxscyBGYXJnbyBCYW5rIE5BMTYwNAYDVQQD +DC1XZWxsc1NlY3VyZSBQdWJsaWMgUm9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkw +ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDub7S9eeKPCCGeOARBJe+r +WxxTkqxtnt3CxC5FlAM1iGd0V+PfjLindo8796jE2yljDpFoNoqXjopxaAkH5OjU +Dk/41itMpBb570OYj7OeUt9tkTmPOL13i0Nj67eT/DBMHAGTthP796EfvyXhdDcs +HqRePGj4S78NuR4uNuip5Kf4D8uCdXw1LSLWwr8L87T8bJVhHlfXBIEyg1J55oNj +z7fLY4sR4r1e6/aN7ZVyKLSsEmLpSjPmgzKuBXWVvYSV2ypcm44uDLiBK0HmOFaf +SZtsdvqKXfcBeYF8wYNABf5x/Qw/zE5gCQ5lRxAvAcAFP4/4s0HvWkJ+We/Slwxl +AgMBAAGjggE0MIIBMDAPBgNVHRMBAf8EBTADAQH/MDkGA1UdHwQyMDAwLqAsoCqG +KGh0dHA6Ly9jcmwucGtpLndlbGxzZmFyZ28uY29tL3dzcHJjYS5jcmwwDgYDVR0P +AQH/BAQDAgHGMB0GA1UdDgQWBBQmlRkQ2eihl5H/3BnZtQQ+0nMKajCBsgYDVR0j +BIGqMIGngBQmlRkQ2eihl5H/3BnZtQQ+0nMKaqGBi6SBiDCBhTELMAkGA1UEBhMC +VVMxIDAeBgNVBAoMF1dlbGxzIEZhcmdvIFdlbGxzU2VjdXJlMRwwGgYDVQQLDBNX +ZWxscyBGYXJnbyBCYW5rIE5BMTYwNAYDVQQDDC1XZWxsc1NlY3VyZSBQdWJsaWMg +Um9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHmCAQEwDQYJKoZIhvcNAQEFBQADggEB +ALkVsUSRzCPIK0134/iaeycNzXK7mQDKfGYZUMbVmO2rvwNa5U3lHshPcZeG1eMd +/ZDJPHV3V3p9+N701NX3leZ0bh08rnyd2wIDBSxxSyU+B+NemvVmFymIGjifz6pB +A4SXa5M4esowRBskRDPQ5NHcKDj0E0M1NSljqHyita04pO2t/caaH/+Xc/77szWn +k4bGdpEA5qxRFsQnMlzbc9qlk1eOPm01JghZ1edE13YgY+esE2fDbbFwRnzVlhE9 +iW9dqKHrjQrawx0zbKPqZxmamX9LPYNRKh3KL4YMon4QLSvUFpULB6ouFJJJtylv +2G0xffX8oRAHh84vWdw+WNs= +-----END CERTIFICATE----- + +# Issuer: CN=COMODO ECC Certification Authority O=COMODO CA Limited +# Subject: CN=COMODO ECC Certification Authority O=COMODO CA Limited +# Label: "COMODO ECC Certification Authority" +# Serial: 41578283867086692638256921589707938090 +# MD5 Fingerprint: 7c:62:ff:74:9d:31:53:5e:68:4a:d5:78:aa:1e:bf:23 +# SHA1 Fingerprint: 9f:74:4e:9f:2b:4d:ba:ec:0f:31:2c:50:b6:56:3b:8e:2d:93:c3:11 +# SHA256 Fingerprint: 17:93:92:7a:06:14:54:97:89:ad:ce:2f:8f:34:f7:f0:b6:6d:0f:3a:e3:a3:b8:4d:21:ec:15:db:ba:4f:ad:c7 +-----BEGIN CERTIFICATE----- +MIICiTCCAg+gAwIBAgIQH0evqmIAcFBUTAGem2OZKjAKBggqhkjOPQQDAzCBhTEL +MAkGA1UEBhMCR0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UE +BxMHU2FsZm9yZDEaMBgGA1UEChMRQ09NT0RPIENBIExpbWl0ZWQxKzApBgNVBAMT +IkNPTU9ETyBFQ0MgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMDgwMzA2MDAw +MDAwWhcNMzgwMTE4MjM1OTU5WjCBhTELMAkGA1UEBhMCR0IxGzAZBgNVBAgTEkdy +ZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UEBxMHU2FsZm9yZDEaMBgGA1UEChMRQ09N +T0RPIENBIExpbWl0ZWQxKzApBgNVBAMTIkNPTU9ETyBFQ0MgQ2VydGlmaWNhdGlv +biBBdXRob3JpdHkwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAAQDR3svdcmCFYX7deSR +FtSrYpn1PlILBs5BAH+X4QokPB0BBO490o0JlwzgdeT6+3eKKvUDYEs2ixYjFq0J +cfRK9ChQtP6IHG4/bC8vCVlbpVsLM5niwz2J+Wos77LTBumjQjBAMB0GA1UdDgQW +BBR1cacZSBm8nZ3qQUfflMRId5nTeTAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/ +BAUwAwEB/zAKBggqhkjOPQQDAwNoADBlAjEA7wNbeqy3eApyt4jf/7VGFAkK+qDm +fQjGGoe9GKhzvSbKYAydzpmfz1wPMOG+FDHqAjAU9JM8SaczepBGR7NjfRObTrdv +GDeAU/7dIOA1mjbRxwG55tzd8/8dLDoWV9mSOdY= +-----END CERTIFICATE----- + +# Issuer: CN=IGC/A O=PM/SGDN OU=DCSSI +# Subject: CN=IGC/A O=PM/SGDN OU=DCSSI +# Label: "IGC/A" +# Serial: 245102874772 +# MD5 Fingerprint: 0c:7f:dd:6a:f4:2a:b9:c8:9b:bd:20:7e:a9:db:5c:37 +# SHA1 Fingerprint: 60:d6:89:74:b5:c2:65:9e:8a:0f:c1:88:7c:88:d2:46:69:1b:18:2c +# SHA256 Fingerprint: b9:be:a7:86:0a:96:2e:a3:61:1d:ab:97:ab:6d:a3:e2:1c:10:68:b9:7d:55:57:5e:d0:e1:12:79:c1:1c:89:32 +-----BEGIN CERTIFICATE----- +MIIEAjCCAuqgAwIBAgIFORFFEJQwDQYJKoZIhvcNAQEFBQAwgYUxCzAJBgNVBAYT +AkZSMQ8wDQYDVQQIEwZGcmFuY2UxDjAMBgNVBAcTBVBhcmlzMRAwDgYDVQQKEwdQ +TS9TR0ROMQ4wDAYDVQQLEwVEQ1NTSTEOMAwGA1UEAxMFSUdDL0ExIzAhBgkqhkiG +9w0BCQEWFGlnY2FAc2dkbi5wbS5nb3V2LmZyMB4XDTAyMTIxMzE0MjkyM1oXDTIw +MTAxNzE0MjkyMlowgYUxCzAJBgNVBAYTAkZSMQ8wDQYDVQQIEwZGcmFuY2UxDjAM +BgNVBAcTBVBhcmlzMRAwDgYDVQQKEwdQTS9TR0ROMQ4wDAYDVQQLEwVEQ1NTSTEO +MAwGA1UEAxMFSUdDL0ExIzAhBgkqhkiG9w0BCQEWFGlnY2FAc2dkbi5wbS5nb3V2 +LmZyMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsh/R0GLFMzvABIaI +s9z4iPf930Pfeo2aSVz2TqrMHLmh6yeJ8kbpO0px1R2OLc/mratjUMdUC24SyZA2 +xtgv2pGqaMVy/hcKshd+ebUyiHDKcMCWSo7kVc0dJ5S/znIq7Fz5cyD+vfcuiWe4 +u0dzEvfRNWk68gq5rv9GQkaiv6GFGvm/5P9JhfejcIYyHF2fYPepraX/z9E0+X1b +F8bc1g4oa8Ld8fUzaJ1O/Id8NhLWo4DoQw1VYZTqZDdH6nfK0LJYBcNdfrGoRpAx +Vs5wKpayMLh35nnAvSk7/ZR3TL0gzUEl4C7HG7vupARB0l2tEmqKm0f7yd1GQOGd +PDPQtQIDAQABo3cwdTAPBgNVHRMBAf8EBTADAQH/MAsGA1UdDwQEAwIBRjAVBgNV +HSAEDjAMMAoGCCqBegF5AQEBMB0GA1UdDgQWBBSjBS8YYFDCiQrdKyFP/45OqDAx +NjAfBgNVHSMEGDAWgBSjBS8YYFDCiQrdKyFP/45OqDAxNjANBgkqhkiG9w0BAQUF +AAOCAQEABdwm2Pp3FURo/C9mOnTgXeQp/wYHE4RKq89toB9RlPhJy3Q2FLwV3duJ +L92PoF189RLrn544pEfMs5bZvpwlqwN+Mw+VgQ39FuCIvjfwbF3QMZsyK10XZZOY +YLxuj7GoPB7ZHPOpJkL5ZB3C55L29B5aqhlSXa/oovdgoPaN8In1buAKBQGVyYsg +Crpa/JosPL3Dt8ldeCUFP1YUmwza+zpI/pdpXsoQhvdOlgQITeywvl3cO45Pwf2a +NjSaTFR+FwNIlQgRHAdvhQh+XU3Endv7rs6y0bO4g2wdsrN58dhwmX7wEwLOXt1R +0982gaEbeC9xs/FZTEYYKKuF0mBWWg== +-----END CERTIFICATE----- + +# Issuer: O=SECOM Trust Systems CO.,LTD. OU=Security Communication EV RootCA1 +# Subject: O=SECOM Trust Systems CO.,LTD. OU=Security Communication EV RootCA1 +# Label: "Security Communication EV RootCA1" +# Serial: 0 +# MD5 Fingerprint: 22:2d:a6:01:ea:7c:0a:f7:f0:6c:56:43:3f:77:76:d3 +# SHA1 Fingerprint: fe:b8:c4:32:dc:f9:76:9a:ce:ae:3d:d8:90:8f:fd:28:86:65:64:7d +# SHA256 Fingerprint: a2:2d:ba:68:1e:97:37:6e:2d:39:7d:72:8a:ae:3a:9b:62:96:b9:fd:ba:60:bc:2e:11:f6:47:f2:c6:75:fb:37 +-----BEGIN CERTIFICATE----- +MIIDfTCCAmWgAwIBAgIBADANBgkqhkiG9w0BAQUFADBgMQswCQYDVQQGEwJKUDEl +MCMGA1UEChMcU0VDT00gVHJ1c3QgU3lzdGVtcyBDTy4sTFRELjEqMCgGA1UECxMh +U2VjdXJpdHkgQ29tbXVuaWNhdGlvbiBFViBSb290Q0ExMB4XDTA3MDYwNjAyMTIz +MloXDTM3MDYwNjAyMTIzMlowYDELMAkGA1UEBhMCSlAxJTAjBgNVBAoTHFNFQ09N +IFRydXN0IFN5c3RlbXMgQ08uLExURC4xKjAoBgNVBAsTIVNlY3VyaXR5IENvbW11 +bmljYXRpb24gRVYgUm9vdENBMTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC +ggEBALx/7FebJOD+nLpCeamIivqA4PUHKUPqjgo0No0c+qe1OXj/l3X3L+SqawSE +RMqm4miO/VVQYg+kcQ7OBzgtQoVQrTyWb4vVog7P3kmJPdZkLjjlHmy1V4qe70gO +zXppFodEtZDkBp2uoQSXWHnvIEqCa4wiv+wfD+mEce3xDuS4GBPMVjZd0ZoeUWs5 +bmB2iDQL87PRsJ3KYeJkHcFGB7hj3R4zZbOOCVVSPbW9/wfrrWFVGCypaZhKqkDF +MxRldAD5kd6vA0jFQFTcD4SQaCDFkpbcLuUCRarAX1T4bepJz11sS6/vmsJWXMY1 +VkJqMF/Cq/biPT+zyRGPMUzXn0kCAwEAAaNCMEAwHQYDVR0OBBYEFDVK9U2vP9eC +OKyrcWUXdYydVZPmMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MA0G +CSqGSIb3DQEBBQUAA4IBAQCoh+ns+EBnXcPBZsdAS5f8hxOQWsTvoMpfi7ent/HW +tWS3irO4G8za+6xmiEHO6Pzk2x6Ipu0nUBsCMCRGef4Eh3CXQHPRwMFXGZpppSeZ +q51ihPZRwSzJIxXYKLerJRO1RuGGAv8mjMSIkh1W/hln8lXkgKNrnKt34VFxDSDb +EJrbvXZ5B3eZKK2aXtqxT0QsNY6llsf9g/BYxnnWmHyojf6GPgcWkuF75x3sM3Z+ +Qi5KhfmRiWiEA4Glm5q+4zfFVKtWOxgtQaQM+ELbmaDgcm+7XeEWT1MKZPlO9L9O +VL14bIjqv5wTJMJwaaJ/D8g8rQjJsJhAoyrniIPtd490 +-----END CERTIFICATE----- + +# Issuer: CN=OISTE WISeKey Global Root GA CA O=WISeKey OU=Copyright (c) 2005/OISTE Foundation Endorsed +# Subject: CN=OISTE WISeKey Global Root GA CA O=WISeKey OU=Copyright (c) 2005/OISTE Foundation Endorsed +# Label: "OISTE WISeKey Global Root GA CA" +# Serial: 86718877871133159090080555911823548314 +# MD5 Fingerprint: bc:6c:51:33:a7:e9:d3:66:63:54:15:72:1b:21:92:93 +# SHA1 Fingerprint: 59:22:a1:e1:5a:ea:16:35:21:f8:98:39:6a:46:46:b0:44:1b:0f:a9 +# SHA256 Fingerprint: 41:c9:23:86:6a:b4:ca:d6:b7:ad:57:80:81:58:2e:02:07:97:a6:cb:df:4f:ff:78:ce:83:96:b3:89:37:d7:f5 +-----BEGIN CERTIFICATE----- +MIID8TCCAtmgAwIBAgIQQT1yx/RrH4FDffHSKFTfmjANBgkqhkiG9w0BAQUFADCB +ijELMAkGA1UEBhMCQ0gxEDAOBgNVBAoTB1dJU2VLZXkxGzAZBgNVBAsTEkNvcHly +aWdodCAoYykgMjAwNTEiMCAGA1UECxMZT0lTVEUgRm91bmRhdGlvbiBFbmRvcnNl +ZDEoMCYGA1UEAxMfT0lTVEUgV0lTZUtleSBHbG9iYWwgUm9vdCBHQSBDQTAeFw0w +NTEyMTExNjAzNDRaFw0zNzEyMTExNjA5NTFaMIGKMQswCQYDVQQGEwJDSDEQMA4G +A1UEChMHV0lTZUtleTEbMBkGA1UECxMSQ29weXJpZ2h0IChjKSAyMDA1MSIwIAYD +VQQLExlPSVNURSBGb3VuZGF0aW9uIEVuZG9yc2VkMSgwJgYDVQQDEx9PSVNURSBX +SVNlS2V5IEdsb2JhbCBSb290IEdBIENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A +MIIBCgKCAQEAy0+zAJs9Nt350UlqaxBJH+zYK7LG+DKBKUOVTJoZIyEVRd7jyBxR +VVuuk+g3/ytr6dTqvirdqFEr12bDYVxgAsj1znJ7O7jyTmUIms2kahnBAbtzptf2 +w93NvKSLtZlhuAGio9RN1AU9ka34tAhxZK9w8RxrfvbDd50kc3vkDIzh2TbhmYsF +mQvtRTEJysIA2/dyoJaqlYfQjse2YXMNdmaM3Bu0Y6Kff5MTMPGhJ9vZ/yxViJGg +4E8HsChWjBgbl0SOid3gF27nKu+POQoxhILYQBRJLnpB5Kf+42TMwVlxSywhp1t9 +4B3RLoGbw9ho972WG6xwsRYUC9tguSYBBQIDAQABo1EwTzALBgNVHQ8EBAMCAYYw +DwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUswN+rja8sHnR3JQmthG+IbJphpQw +EAYJKwYBBAGCNxUBBAMCAQAwDQYJKoZIhvcNAQEFBQADggEBAEuh/wuHbrP5wUOx +SPMowB0uyQlB+pQAHKSkq0lPjz0e701vvbyk9vImMMkQyh2I+3QZH4VFvbBsUfk2 +ftv1TDI6QU9bR8/oCy22xBmddMVHxjtqD6wU2zz0c5ypBd8A3HR4+vg1YFkCExh8 +vPtNsCBtQ7tgMHpnM1zFmdH4LTlSc/uMqpclXHLZCB6rTjzjgTGfA6b7wP4piFXa +hNVQA7bihKOmNqoROgHhGEvWRGizPflTdISzRpFGlgC3gCy24eMQ4tui5yiPAZZi +Fj4A4xylNoEYokxSdsARo27mHbrjWr42U8U+dY+GaSlYU7Wcu2+fXMUY7N0v4ZjJ +/L7fCg0= +-----END CERTIFICATE----- + +# Issuer: CN=Microsec e-Szigno Root CA O=Microsec Ltd. OU=e-Szigno CA +# Subject: CN=Microsec e-Szigno Root CA O=Microsec Ltd. OU=e-Szigno CA +# Label: "Microsec e-Szigno Root CA" +# Serial: 272122594155480254301341951808045322001 +# MD5 Fingerprint: f0:96:b6:2f:c5:10:d5:67:8e:83:25:32:e8:5e:2e:e5 +# SHA1 Fingerprint: 23:88:c9:d3:71:cc:9e:96:3d:ff:7d:3c:a7:ce:fc:d6:25:ec:19:0d +# SHA256 Fingerprint: 32:7a:3d:76:1a:ba:de:a0:34:eb:99:84:06:27:5c:b1:a4:77:6e:fd:ae:2f:df:6d:01:68:ea:1c:4f:55:67:d0 +-----BEGIN CERTIFICATE----- +MIIHqDCCBpCgAwIBAgIRAMy4579OKRr9otxmpRwsDxEwDQYJKoZIhvcNAQEFBQAw +cjELMAkGA1UEBhMCSFUxETAPBgNVBAcTCEJ1ZGFwZXN0MRYwFAYDVQQKEw1NaWNy +b3NlYyBMdGQuMRQwEgYDVQQLEwtlLVN6aWdubyBDQTEiMCAGA1UEAxMZTWljcm9z +ZWMgZS1Temlnbm8gUm9vdCBDQTAeFw0wNTA0MDYxMjI4NDRaFw0xNzA0MDYxMjI4 +NDRaMHIxCzAJBgNVBAYTAkhVMREwDwYDVQQHEwhCdWRhcGVzdDEWMBQGA1UEChMN +TWljcm9zZWMgTHRkLjEUMBIGA1UECxMLZS1Temlnbm8gQ0ExIjAgBgNVBAMTGU1p +Y3Jvc2VjIGUtU3ppZ25vIFJvb3QgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw +ggEKAoIBAQDtyADVgXvNOABHzNuEwSFpLHSQDCHZU4ftPkNEU6+r+ICbPHiN1I2u +uO/TEdyB5s87lozWbxXGd36hL+BfkrYn13aaHUM86tnsL+4582pnS4uCzyL4ZVX+ +LMsvfUh6PXX5qqAnu3jCBspRwn5mS6/NoqdNAoI/gqyFxuEPkEeZlApxcpMqyabA +vjxWTHOSJ/FrtfX9/DAFYJLG65Z+AZHCabEeHXtTRbjcQR/Ji3HWVBTji1R4P770 +Yjtb9aPs1ZJ04nQw7wHb4dSrmZsqa/i9phyGI0Jf7Enemotb9HI6QMVJPqW+jqpx +62z69Rrkav17fVVA71hu5tnVvCSrwe+3AgMBAAGjggQ3MIIEMzBnBggrBgEFBQcB +AQRbMFkwKAYIKwYBBQUHMAGGHGh0dHBzOi8vcmNhLmUtc3ppZ25vLmh1L29jc3Aw +LQYIKwYBBQUHMAKGIWh0dHA6Ly93d3cuZS1zemlnbm8uaHUvUm9vdENBLmNydDAP +BgNVHRMBAf8EBTADAQH/MIIBcwYDVR0gBIIBajCCAWYwggFiBgwrBgEEAYGoGAIB +AQEwggFQMCgGCCsGAQUFBwIBFhxodHRwOi8vd3d3LmUtc3ppZ25vLmh1L1NaU1ov +MIIBIgYIKwYBBQUHAgIwggEUHoIBEABBACAAdABhAG4A+gBzAO0AdAB2AOEAbgB5 +ACAA6QByAHQAZQBsAG0AZQB6AOkAcwDpAGgAZQB6ACAA6QBzACAAZQBsAGYAbwBn +AGEAZADhAHMA4QBoAG8AegAgAGEAIABTAHoAbwBsAGcA4QBsAHQAYQB0APMAIABT +AHoAbwBsAGcA4QBsAHQAYQB0AOEAcwBpACAAUwB6AGEAYgDhAGwAeQB6AGEAdABh +ACAAcwB6AGUAcgBpAG4AdAAgAGsAZQBsAGwAIABlAGwAagDhAHIAbgBpADoAIABo +AHQAdABwADoALwAvAHcAdwB3AC4AZQAtAHMAegBpAGcAbgBvAC4AaAB1AC8AUwBa +AFMAWgAvMIHIBgNVHR8EgcAwgb0wgbqggbeggbSGIWh0dHA6Ly93d3cuZS1zemln +bm8uaHUvUm9vdENBLmNybIaBjmxkYXA6Ly9sZGFwLmUtc3ppZ25vLmh1L0NOPU1p +Y3Jvc2VjJTIwZS1Temlnbm8lMjBSb290JTIwQ0EsT1U9ZS1Temlnbm8lMjBDQSxP +PU1pY3Jvc2VjJTIwTHRkLixMPUJ1ZGFwZXN0LEM9SFU/Y2VydGlmaWNhdGVSZXZv +Y2F0aW9uTGlzdDtiaW5hcnkwDgYDVR0PAQH/BAQDAgEGMIGWBgNVHREEgY4wgYuB +EGluZm9AZS1zemlnbm8uaHWkdzB1MSMwIQYDVQQDDBpNaWNyb3NlYyBlLVN6aWdu +w7MgUm9vdCBDQTEWMBQGA1UECwwNZS1TemlnbsOzIEhTWjEWMBQGA1UEChMNTWlj +cm9zZWMgS2Z0LjERMA8GA1UEBxMIQnVkYXBlc3QxCzAJBgNVBAYTAkhVMIGsBgNV +HSMEgaQwgaGAFMegSXUWYYTbMUuE0vE3QJDvTtz3oXakdDByMQswCQYDVQQGEwJI +VTERMA8GA1UEBxMIQnVkYXBlc3QxFjAUBgNVBAoTDU1pY3Jvc2VjIEx0ZC4xFDAS +BgNVBAsTC2UtU3ppZ25vIENBMSIwIAYDVQQDExlNaWNyb3NlYyBlLVN6aWdubyBS +b290IENBghEAzLjnv04pGv2i3GalHCwPETAdBgNVHQ4EFgQUx6BJdRZhhNsxS4TS +8TdAkO9O3PcwDQYJKoZIhvcNAQEFBQADggEBANMTnGZjWS7KXHAM/IO8VbH0jgds +ZifOwTsgqRy7RlRw7lrMoHfqaEQn6/Ip3Xep1fvj1KcExJW4C+FEaGAHQzAxQmHl +7tnlJNUb3+FKG6qfx1/4ehHqE5MAyopYse7tDk2016g2JnzgOsHVV4Lxdbb9iV/a +86g4nzUGCM4ilb7N1fy+W955a9x6qWVmvrElWl/tftOsRm1M9DKHtCAE4Gx4sHfR +hUZLphK3dehKyVZs15KrnfVJONJPU+NVkBHbmJbGSfI+9J8b4PeI3CVimUTYc78/ +MPMMNz7UwiiAc7EBt51alhQBS6kRnSlqLtBdgcDPsiBDxwPgN05dCtxZICU= +-----END CERTIFICATE----- + +# Issuer: CN=Certigna O=Dhimyotis +# Subject: CN=Certigna O=Dhimyotis +# Label: "Certigna" +# Serial: 18364802974209362175 +# MD5 Fingerprint: ab:57:a6:5b:7d:42:82:19:b5:d8:58:26:28:5e:fd:ff +# SHA1 Fingerprint: b1:2e:13:63:45:86:a4:6f:1a:b2:60:68:37:58:2d:c4:ac:fd:94:97 +# SHA256 Fingerprint: e3:b6:a2:db:2e:d7:ce:48:84:2f:7a:c5:32:41:c7:b7:1d:54:14:4b:fb:40:c1:1f:3f:1d:0b:42:f5:ee:a1:2d +-----BEGIN CERTIFICATE----- +MIIDqDCCApCgAwIBAgIJAP7c4wEPyUj/MA0GCSqGSIb3DQEBBQUAMDQxCzAJBgNV +BAYTAkZSMRIwEAYDVQQKDAlEaGlteW90aXMxETAPBgNVBAMMCENlcnRpZ25hMB4X +DTA3MDYyOTE1MTMwNVoXDTI3MDYyOTE1MTMwNVowNDELMAkGA1UEBhMCRlIxEjAQ +BgNVBAoMCURoaW15b3RpczERMA8GA1UEAwwIQ2VydGlnbmEwggEiMA0GCSqGSIb3 +DQEBAQUAA4IBDwAwggEKAoIBAQDIaPHJ1tazNHUmgh7stL7qXOEm7RFHYeGifBZ4 +QCHkYJ5ayGPhxLGWkv8YbWkj4Sti993iNi+RB7lIzw7sebYs5zRLcAglozyHGxny +gQcPOJAZ0xH+hrTy0V4eHpbNgGzOOzGTtvKg0KmVEn2lmsxryIRWijOp5yIVUxbw +zBfsV1/pogqYCd7jX5xv3EjjhQsVWqa6n6xI4wmy9/Qy3l40vhx4XUJbzg4ij02Q +130yGLMLLGq/jj8UEYkgDncUtT2UCIf3JR7VsmAA7G8qKCVuKj4YYxclPz5EIBb2 +JsglrgVKtOdjLPOMFlN+XPsRGgjBRmKfIrjxwo1p3Po6WAbfAgMBAAGjgbwwgbkw +DwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUGu3+QTmQtCRZvgHyUtVF9lo53BEw +ZAYDVR0jBF0wW4AUGu3+QTmQtCRZvgHyUtVF9lo53BGhOKQ2MDQxCzAJBgNVBAYT +AkZSMRIwEAYDVQQKDAlEaGlteW90aXMxETAPBgNVBAMMCENlcnRpZ25hggkA/tzj +AQ/JSP8wDgYDVR0PAQH/BAQDAgEGMBEGCWCGSAGG+EIBAQQEAwIABzANBgkqhkiG +9w0BAQUFAAOCAQEAhQMeknH2Qq/ho2Ge6/PAD/Kl1NqV5ta+aDY9fm4fTIrv0Q8h +bV6lUmPOEvjvKtpv6zf+EwLHyzs+ImvaYS5/1HI93TDhHkxAGYwP15zRgzB7mFnc +fca5DClMoTOi62c6ZYTTluLtdkVwj7Ur3vkj1kluPBS1xp81HlDQwY9qcEQCYsuu +HWhBp6pX6FOqB9IG9tUUBguRA3UsbHK1YZWaDYu5Def131TN3ubY1gkIl2PlwS6w +t0QmwCbAr1UwnjvVNioZBPRcHv/PLLf/0P2HQBHVESO7SMAhqaQoLf0V+LBOK/Qw +WyH8EZE0vkHve52Xdf+XlcCWWC/qu0bXu+TZLg== +-----END CERTIFICATE----- + +# Issuer: CN=TC TrustCenter Class 2 CA II O=TC TrustCenter GmbH OU=TC TrustCenter Class 2 CA +# Subject: CN=TC TrustCenter Class 2 CA II O=TC TrustCenter GmbH OU=TC TrustCenter Class 2 CA +# Label: "TC TrustCenter Class 2 CA II" +# Serial: 941389028203453866782103406992443 +# MD5 Fingerprint: ce:78:33:5c:59:78:01:6e:18:ea:b9:36:a0:b9:2e:23 +# SHA1 Fingerprint: ae:50:83:ed:7c:f4:5c:bc:8f:61:c6:21:fe:68:5d:79:42:21:15:6e +# SHA256 Fingerprint: e6:b8:f8:76:64:85:f8:07:ae:7f:8d:ac:16:70:46:1f:07:c0:a1:3e:ef:3a:1f:f7:17:53:8d:7a:ba:d3:91:b4 +-----BEGIN CERTIFICATE----- +MIIEqjCCA5KgAwIBAgIOLmoAAQACH9dSISwRXDswDQYJKoZIhvcNAQEFBQAwdjEL +MAkGA1UEBhMCREUxHDAaBgNVBAoTE1RDIFRydXN0Q2VudGVyIEdtYkgxIjAgBgNV +BAsTGVRDIFRydXN0Q2VudGVyIENsYXNzIDIgQ0ExJTAjBgNVBAMTHFRDIFRydXN0 +Q2VudGVyIENsYXNzIDIgQ0EgSUkwHhcNMDYwMTEyMTQzODQzWhcNMjUxMjMxMjI1 +OTU5WjB2MQswCQYDVQQGEwJERTEcMBoGA1UEChMTVEMgVHJ1c3RDZW50ZXIgR21i +SDEiMCAGA1UECxMZVEMgVHJ1c3RDZW50ZXIgQ2xhc3MgMiBDQTElMCMGA1UEAxMc +VEMgVHJ1c3RDZW50ZXIgQ2xhc3MgMiBDQSBJSTCCASIwDQYJKoZIhvcNAQEBBQAD +ggEPADCCAQoCggEBAKuAh5uO8MN8h9foJIIRszzdQ2Lu+MNF2ujhoF/RKrLqk2jf +tMjWQ+nEdVl//OEd+DFwIxuInie5e/060smp6RQvkL4DUsFJzfb95AhmC1eKokKg +uNV/aVyQMrKXDcpK3EY+AlWJU+MaWss2xgdW94zPEfRMuzBwBJWl9jmM/XOBCH2J +XjIeIqkiRUuwZi4wzJ9l/fzLganx4Duvo4bRierERXlQXa7pIXSSTYtZgo+U4+lK +8edJsBTj9WLL1XK9H7nSn6DNqPoByNkN39r8R52zyFTfSUrxIan+GE7uSNQZu+99 +5OKdy1u2bv/jzVrndIIFuoAlOMvkaZ6vQaoahPUCAwEAAaOCATQwggEwMA8GA1Ud +EwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBTjq1RMgKHbVkO3 +kUrL84J6E1wIqzCB7QYDVR0fBIHlMIHiMIHfoIHcoIHZhjVodHRwOi8vd3d3LnRy +dXN0Y2VudGVyLmRlL2NybC92Mi90Y19jbGFzc18yX2NhX0lJLmNybIaBn2xkYXA6 +Ly93d3cudHJ1c3RjZW50ZXIuZGUvQ049VEMlMjBUcnVzdENlbnRlciUyMENsYXNz +JTIwMiUyMENBJTIwSUksTz1UQyUyMFRydXN0Q2VudGVyJTIwR21iSCxPVT1yb290 +Y2VydHMsREM9dHJ1c3RjZW50ZXIsREM9ZGU/Y2VydGlmaWNhdGVSZXZvY2F0aW9u +TGlzdD9iYXNlPzANBgkqhkiG9w0BAQUFAAOCAQEAjNfffu4bgBCzg/XbEeprS6iS +GNn3Bzn1LL4GdXpoUxUc6krtXvwjshOg0wn/9vYua0Fxec3ibf2uWWuFHbhOIprt +ZjluS5TmVfwLG4t3wVMTZonZKNaL80VKY7f9ewthXbhtvsPcW3nS7Yblok2+XnR8 +au0WOB9/WIFaGusyiC2y8zl3gK9etmF1KdsjTYjKUCjLhdLTEKJZbtOTVAB6okaV +hgWcqRmY5TFyDADiZ9lA4CQze28suVyrZZ0srHbqNZn1l7kPJOzHdiEoZa5X6AeI +dUpWoNIFOqTmjZKILPPy4cHGYdtBxceb9w4aUUXCYWvcZCcXjFq32nQozZfkvQ== +-----END CERTIFICATE----- + +# Issuer: CN=TC TrustCenter Universal CA I O=TC TrustCenter GmbH OU=TC TrustCenter Universal CA +# Subject: CN=TC TrustCenter Universal CA I O=TC TrustCenter GmbH OU=TC TrustCenter Universal CA +# Label: "TC TrustCenter Universal CA I" +# Serial: 601024842042189035295619584734726 +# MD5 Fingerprint: 45:e1:a5:72:c5:a9:36:64:40:9e:f5:e4:58:84:67:8c +# SHA1 Fingerprint: 6b:2f:34:ad:89:58:be:62:fd:b0:6b:5c:ce:bb:9d:d9:4f:4e:39:f3 +# SHA256 Fingerprint: eb:f3:c0:2a:87:89:b1:fb:7d:51:19:95:d6:63:b7:29:06:d9:13:ce:0d:5e:10:56:8a:8a:77:e2:58:61:67:e7 +-----BEGIN CERTIFICATE----- +MIID3TCCAsWgAwIBAgIOHaIAAQAC7LdggHiNtgYwDQYJKoZIhvcNAQEFBQAweTEL +MAkGA1UEBhMCREUxHDAaBgNVBAoTE1RDIFRydXN0Q2VudGVyIEdtYkgxJDAiBgNV +BAsTG1RDIFRydXN0Q2VudGVyIFVuaXZlcnNhbCBDQTEmMCQGA1UEAxMdVEMgVHJ1 +c3RDZW50ZXIgVW5pdmVyc2FsIENBIEkwHhcNMDYwMzIyMTU1NDI4WhcNMjUxMjMx +MjI1OTU5WjB5MQswCQYDVQQGEwJERTEcMBoGA1UEChMTVEMgVHJ1c3RDZW50ZXIg +R21iSDEkMCIGA1UECxMbVEMgVHJ1c3RDZW50ZXIgVW5pdmVyc2FsIENBMSYwJAYD +VQQDEx1UQyBUcnVzdENlbnRlciBVbml2ZXJzYWwgQ0EgSTCCASIwDQYJKoZIhvcN +AQEBBQADggEPADCCAQoCggEBAKR3I5ZEr5D0MacQ9CaHnPM42Q9e3s9B6DGtxnSR +JJZ4Hgmgm5qVSkr1YnwCqMqs+1oEdjneX/H5s7/zA1hV0qq34wQi0fiU2iIIAI3T +fCZdzHd55yx4Oagmcw6iXSVphU9VDprvxrlE4Vc93x9UIuVvZaozhDrzznq+VZeu +jRIPFDPiUHDDSYcTvFHe15gSWu86gzOSBnWLknwSaHtwag+1m7Z3W0hZneTvWq3z +wZ7U10VOylY0Ibw+F1tvdwxIAUMpsN0/lm7mlaoMwCC2/T42J5zjXM9OgdwZu5GQ +fezmlwQek8wiSdeXhrYTCjxDI3d+8NzmzSQfO4ObNDqDNOMCAwEAAaNjMGEwHwYD +VR0jBBgwFoAUkqR1LKSevoFE63n8isWVpesQdXMwDwYDVR0TAQH/BAUwAwEB/zAO +BgNVHQ8BAf8EBAMCAYYwHQYDVR0OBBYEFJKkdSyknr6BROt5/IrFlaXrEHVzMA0G +CSqGSIb3DQEBBQUAA4IBAQAo0uCG1eb4e/CX3CJrO5UUVg8RMKWaTzqwOuAGy2X1 +7caXJ/4l8lfmXpWMPmRgFVp/Lw0BxbFg/UU1z/CyvwbZ71q+s2IhtNerNXxTPqYn +8aEt2hojnczd7Dwtnic0XQ/CNnm8yUpiLe1r2X1BQ3y2qsrtYbE3ghUJGooWMNjs +ydZHcnhLEEYUjl8Or+zHL6sQ17bxbuyGssLoDZJz3KL0Dzq/YSMQiZxIQG5wALPT +ujdEWBF6AmqI8Dc08BnprNRlc/ZpjGSUOnmFKbAWKwyCPwacx/0QK54PLLae4xW/ +2TYcuiUaUj0a7CIMHOCkoj3w6DnPgcB77V0fb8XQC9eY +-----END CERTIFICATE----- + +# Issuer: CN=Deutsche Telekom Root CA 2 O=Deutsche Telekom AG OU=T-TeleSec Trust Center +# Subject: CN=Deutsche Telekom Root CA 2 O=Deutsche Telekom AG OU=T-TeleSec Trust Center +# Label: "Deutsche Telekom Root CA 2" +# Serial: 38 +# MD5 Fingerprint: 74:01:4a:91:b1:08:c4:58:ce:47:cd:f0:dd:11:53:08 +# SHA1 Fingerprint: 85:a4:08:c0:9c:19:3e:5d:51:58:7d:cd:d6:13:30:fd:8c:de:37:bf +# SHA256 Fingerprint: b6:19:1a:50:d0:c3:97:7f:7d:a9:9b:cd:aa:c8:6a:22:7d:ae:b9:67:9e:c7:0b:a3:b0:c9:d9:22:71:c1:70:d3 +-----BEGIN CERTIFICATE----- +MIIDnzCCAoegAwIBAgIBJjANBgkqhkiG9w0BAQUFADBxMQswCQYDVQQGEwJERTEc +MBoGA1UEChMTRGV1dHNjaGUgVGVsZWtvbSBBRzEfMB0GA1UECxMWVC1UZWxlU2Vj +IFRydXN0IENlbnRlcjEjMCEGA1UEAxMaRGV1dHNjaGUgVGVsZWtvbSBSb290IENB +IDIwHhcNOTkwNzA5MTIxMTAwWhcNMTkwNzA5MjM1OTAwWjBxMQswCQYDVQQGEwJE +RTEcMBoGA1UEChMTRGV1dHNjaGUgVGVsZWtvbSBBRzEfMB0GA1UECxMWVC1UZWxl +U2VjIFRydXN0IENlbnRlcjEjMCEGA1UEAxMaRGV1dHNjaGUgVGVsZWtvbSBSb290 +IENBIDIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCrC6M14IspFLEU +ha88EOQ5bzVdSq7d6mGNlUn0b2SjGmBmpKlAIoTZ1KXleJMOaAGtuU1cOs7TuKhC +QN/Po7qCWWqSG6wcmtoIKyUn+WkjR/Hg6yx6m/UTAtB+NHzCnjwAWav12gz1Mjwr +rFDa1sPeg5TKqAyZMg4ISFZbavva4VhYAUlfckE8FQYBjl2tqriTtM2e66foai1S +NNs671x1Udrb8zH57nGYMsRUFUQM+ZtV7a3fGAigo4aKSe5TBY8ZTNXeWHmb0moc +QqvF1afPaA+W5OFhmHZhyJF81j4A4pFQh+GdCuatl9Idxjp9y7zaAzTVjlsB9WoH +txa2bkp/AgMBAAGjQjBAMB0GA1UdDgQWBBQxw3kbuvVT1xfgiXotF2wKsyudMzAP +BgNVHRMECDAGAQH/AgEFMA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQUFAAOC +AQEAlGRZrTlk5ynrE/5aw4sTV8gEJPB0d8Bg42f76Ymmg7+Wgnxu1MM9756Abrsp +tJh6sTtU6zkXR34ajgv8HzFZMQSyzhfzLMdiNlXiItiJVbSYSKpk+tYcNthEeFpa +IzpXl/V6ME+un2pMSyuOoAPjPuCp1NJ70rOo4nI8rZ7/gFnkm0W09juwzTkZmDLl +6iFhkOQxIY40sfcvNUqFENrnijchvllj4PKFiDFT1FQUhXB59C4Gdyd1Lx+4ivn+ +xbrYNuSD7Odlt79jWvNGr4GUN9RBjNYj1h7P9WgbRGOiWrqnNVmh5XAFmw4jV5mU +Cm26OWMohpLzGITY+9HPBVZkVw== +-----END CERTIFICATE----- + +# Issuer: CN=ComSign Secured CA O=ComSign +# Subject: CN=ComSign Secured CA O=ComSign +# Label: "ComSign Secured CA" +# Serial: 264725503855295744117309814499492384489 +# MD5 Fingerprint: 40:01:25:06:8d:21:43:6a:0e:43:00:9c:e7:43:f3:d5 +# SHA1 Fingerprint: f9:cd:0e:2c:da:76:24:c1:8f:bd:f0:f0:ab:b6:45:b8:f7:fe:d5:7a +# SHA256 Fingerprint: 50:79:41:c7:44:60:a0:b4:70:86:22:0d:4e:99:32:57:2a:b5:d1:b5:bb:cb:89:80:ab:1c:b1:76:51:a8:44:d2 +-----BEGIN CERTIFICATE----- +MIIDqzCCApOgAwIBAgIRAMcoRwmzuGxFjB36JPU2TukwDQYJKoZIhvcNAQEFBQAw +PDEbMBkGA1UEAxMSQ29tU2lnbiBTZWN1cmVkIENBMRAwDgYDVQQKEwdDb21TaWdu +MQswCQYDVQQGEwJJTDAeFw0wNDAzMjQxMTM3MjBaFw0yOTAzMTYxNTA0NTZaMDwx +GzAZBgNVBAMTEkNvbVNpZ24gU2VjdXJlZCBDQTEQMA4GA1UEChMHQ29tU2lnbjEL +MAkGA1UEBhMCSUwwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDGtWhf +HZQVw6QIVS3joFd67+l0Kru5fFdJGhFeTymHDEjWaueP1H5XJLkGieQcPOqs49oh +gHMhCu95mGwfCP+hUH3ymBvJVG8+pSjsIQQPRbsHPaHA+iqYHU4Gk/v1iDurX8sW +v+bznkqH7Rnqwp9D5PGBpX8QTz7RSmKtUxvLg/8HZaWSLWapW7ha9B20IZFKF3ue +Mv5WJDmyVIRD9YTC2LxBkMyd1mja6YJQqTtoz7VdApRgFrFD2UNd3V2Hbuq7s8lr +9gOUCXDeFhF6K+h2j0kQmHe5Y1yLM5d19guMsqtb3nQgJT/j8xH5h2iGNXHDHYwt +6+UarA9z1YJZQIDTAgMBAAGjgacwgaQwDAYDVR0TBAUwAwEB/zBEBgNVHR8EPTA7 +MDmgN6A1hjNodHRwOi8vZmVkaXIuY29tc2lnbi5jby5pbC9jcmwvQ29tU2lnblNl +Y3VyZWRDQS5jcmwwDgYDVR0PAQH/BAQDAgGGMB8GA1UdIwQYMBaAFMFL7XC29z58 +ADsAj8c+DkWfHl3sMB0GA1UdDgQWBBTBS+1wtvc+fAA7AI/HPg5Fnx5d7DANBgkq +hkiG9w0BAQUFAAOCAQEAFs/ukhNQq3sUnjO2QiBq1BW9Cav8cujvR3qQrFHBZE7p +iL1DRYHjZiM/EoZNGeQFsOY3wo3aBijJD4mkU6l1P7CW+6tMM1X5eCZGbxs2mPtC +dsGCuY7e+0X5YxtiOzkGynd6qDwJz2w2PQ8KRUtpFhpFfTMDZflScZAmlaxMDPWL +kz/MdXSFmLr/YnpNH4n+rr2UAJm/EaXc4HnFFgt9AmEd6oX5AhVP51qJThRv4zdL +hfXBPGHg/QVBspJ/wx2g0K5SZGBrGMYmnNj1ZOQ2GmKfig8+/21OGVZOIJFsnzQz +OjRXUDpvgV4GxvU+fE6OK85lBi5d0ipTdF7Tbieejw== +-----END CERTIFICATE----- + +# Issuer: CN=Cybertrust Global Root O=Cybertrust, Inc +# Subject: CN=Cybertrust Global Root O=Cybertrust, Inc +# Label: "Cybertrust Global Root" +# Serial: 4835703278459682877484360 +# MD5 Fingerprint: 72:e4:4a:87:e3:69:40:80:77:ea:bc:e3:f4:ff:f0:e1 +# SHA1 Fingerprint: 5f:43:e5:b1:bf:f8:78:8c:ac:1c:c7:ca:4a:9a:c6:22:2b:cc:34:c6 +# SHA256 Fingerprint: 96:0a:df:00:63:e9:63:56:75:0c:29:65:dd:0a:08:67:da:0b:9c:bd:6e:77:71:4a:ea:fb:23:49:ab:39:3d:a3 +-----BEGIN CERTIFICATE----- +MIIDoTCCAomgAwIBAgILBAAAAAABD4WqLUgwDQYJKoZIhvcNAQEFBQAwOzEYMBYG +A1UEChMPQ3liZXJ0cnVzdCwgSW5jMR8wHQYDVQQDExZDeWJlcnRydXN0IEdsb2Jh +bCBSb290MB4XDTA2MTIxNTA4MDAwMFoXDTIxMTIxNTA4MDAwMFowOzEYMBYGA1UE +ChMPQ3liZXJ0cnVzdCwgSW5jMR8wHQYDVQQDExZDeWJlcnRydXN0IEdsb2JhbCBS +b290MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA+Mi8vRRQZhP/8NN5 +7CPytxrHjoXxEnOmGaoQ25yiZXRadz5RfVb23CO21O1fWLE3TdVJDm71aofW0ozS +J8bi/zafmGWgE07GKmSb1ZASzxQG9Dvj1Ci+6A74q05IlG2OlTEQXO2iLb3VOm2y +HLtgwEZLAfVJrn5GitB0jaEMAs7u/OePuGtm839EAL9mJRQr3RAwHQeWP032a7iP +t3sMpTjr3kfb1V05/Iin89cqdPHoWqI7n1C6poxFNcJQZZXcY4Lv3b93TZxiyWNz +FtApD0mpSPCzqrdsxacwOUBdrsTiXSZT8M4cIwhhqJQZugRiQOwfOHB3EgZxpzAY +XSUnpQIDAQABo4GlMIGiMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/ +MB0GA1UdDgQWBBS2CHsNesysIEyGVjJez6tuhS1wVzA/BgNVHR8EODA2MDSgMqAw +hi5odHRwOi8vd3d3Mi5wdWJsaWMtdHJ1c3QuY29tL2NybC9jdC9jdHJvb3QuY3Js +MB8GA1UdIwQYMBaAFLYIew16zKwgTIZWMl7Pq26FLXBXMA0GCSqGSIb3DQEBBQUA +A4IBAQBW7wojoFROlZfJ+InaRcHUowAl9B8Tq7ejhVhpwjCt2BWKLePJzYFa+HMj +Wqd8BfP9IjsO0QbE2zZMcwSO5bAi5MXzLqXZI+O4Tkogp24CJJ8iYGd7ix1yCcUx +XOl5n4BHPa2hCwcUPUf/A2kaDAtE52Mlp3+yybh2hO0j9n0Hq0V+09+zv+mKts2o +omcrUtW3ZfA5TGOgkXmTUg9U3YO7n9GPp1Nzw8v/MOx8BLjYRB+TX3EJIrduPuoc +A06dGiBh+4E37F78CkWr1+cXVdCg6mCbpvbjjFspwgZgFJ0tl0ypkxWdYcQBX0jW +WL1WMRJOEcgh4LMRkWXbtKaIOM5V +-----END CERTIFICATE----- + +# Issuer: O=Chunghwa Telecom Co., Ltd. OU=ePKI Root Certification Authority +# Subject: O=Chunghwa Telecom Co., Ltd. OU=ePKI Root Certification Authority +# Label: "ePKI Root Certification Authority" +# Serial: 28956088682735189655030529057352760477 +# MD5 Fingerprint: 1b:2e:00:ca:26:06:90:3d:ad:fe:6f:15:68:d3:6b:b3 +# SHA1 Fingerprint: 67:65:0d:f1:7e:8e:7e:5b:82:40:a4:f4:56:4b:cf:e2:3d:69:c6:f0 +# SHA256 Fingerprint: c0:a6:f4:dc:63:a2:4b:fd:cf:54:ef:2a:6a:08:2a:0a:72:de:35:80:3e:2f:f5:ff:52:7a:e5:d8:72:06:df:d5 +-----BEGIN CERTIFICATE----- +MIIFsDCCA5igAwIBAgIQFci9ZUdcr7iXAF7kBtK8nTANBgkqhkiG9w0BAQUFADBe +MQswCQYDVQQGEwJUVzEjMCEGA1UECgwaQ2h1bmdod2EgVGVsZWNvbSBDby4sIEx0 +ZC4xKjAoBgNVBAsMIWVQS0kgUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAe +Fw0wNDEyMjAwMjMxMjdaFw0zNDEyMjAwMjMxMjdaMF4xCzAJBgNVBAYTAlRXMSMw +IQYDVQQKDBpDaHVuZ2h3YSBUZWxlY29tIENvLiwgTHRkLjEqMCgGA1UECwwhZVBL +SSBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIICIjANBgkqhkiG9w0BAQEF +AAOCAg8AMIICCgKCAgEA4SUP7o3biDN1Z82tH306Tm2d0y8U82N0ywEhajfqhFAH +SyZbCUNsIZ5qyNUD9WBpj8zwIuQf5/dqIjG3LBXy4P4AakP/h2XGtRrBp0xtInAh +ijHyl3SJCRImHJ7K2RKilTza6We/CKBk49ZCt0Xvl/T29de1ShUCWH2YWEtgvM3X +DZoTM1PRYfl61dd4s5oz9wCGzh1NlDivqOx4UXCKXBCDUSH3ET00hl7lSM2XgYI1 +TBnsZfZrxQWh7kcT1rMhJ5QQCtkkO7q+RBNGMD+XPNjX12ruOzjjK9SXDrkb5wdJ +fzcq+Xd4z1TtW0ado4AOkUPB1ltfFLqfpo0kR0BZv3I4sjZsN/+Z0V0OWQqraffA +sgRFelQArr5T9rXn4fg8ozHSqf4hUmTFpmfwdQcGlBSBVcYn5AGPF8Fqcde+S/uU +WH1+ETOxQvdibBjWzwloPn9s9h6PYq2lY9sJpx8iQkEeb5mKPtf5P0B6ebClAZLS +nT0IFaUQAS2zMnaolQ2zepr7BxB4EW/hj8e6DyUadCrlHJhBmd8hh+iVBmoKs2pH +dmX2Os+PYhcZewoozRrSgx4hxyy/vv9haLdnG7t4TY3OZ+XkwY63I2binZB1NJip +NiuKmpS5nezMirH4JYlcWrYvjB9teSSnUmjDhDXiZo1jDiVN1Rmy5nk3pyKdVDEC +AwEAAaNqMGgwHQYDVR0OBBYEFB4M97Zn8uGSJglFwFU5Lnc/QkqiMAwGA1UdEwQF +MAMBAf8wOQYEZyoHAAQxMC8wLQIBADAJBgUrDgMCGgUAMAcGBWcqAwAABBRFsMLH +ClZ87lt4DJX5GFPBphzYEDANBgkqhkiG9w0BAQUFAAOCAgEACbODU1kBPpVJufGB +uvl2ICO1J2B01GqZNF5sAFPZn/KmsSQHRGoqxqWOeBLoR9lYGxMqXnmbnwoqZ6Yl +PwZpVnPDimZI+ymBV3QGypzqKOg4ZyYr8dW1P2WT+DZdjo2NQCCHGervJ8A9tDkP +JXtoUHRVnAxZfVo9QZQlUgjgRywVMRnVvwdVxrsStZf0X4OFunHB2WyBEXYKCrC/ +gpf36j36+uwtqSiUO1bd0lEursC9CBWMd1I0ltabrNMdjmEPNXubrjlpC2JgQCA2 +j6/7Nu4tCEoduL+bXPjqpRugc6bY+G7gMwRfaKonh+3ZwZCc7b3jajWvY9+rGNm6 +5ulK6lCKD2GTHuItGeIwlDWSXQ62B68ZgI9HkFFLLk3dheLSClIKF5r8GrBQAuUB +o2M3IUxExJtRmREOc5wGj1QupyheRDmHVi03vYVElOEMSyycw5KFNGHLD7ibSkNS +/jQ6fbjpKdx2qcgw+BRxgMYeNkh0IkFch4LoGHGLQYlE535YW6i4jRPpp2zDR+2z +Gp1iro2C6pSe3VkQw63d4k3jMdXH7OjysP6SHhYKGvzZ8/gntsm+HbRsZJB/9OTE +W9c3rkIO3aQab3yIVMUWbuF6aC74Or8NpDyJO3inTmODBCEIZ43ygknQW/2xzQ+D +hNQ+IIX3Sj0rnP0qCglN6oH4EZw= +-----END CERTIFICATE----- + +# Issuer: CN=TÜBİTAK UEKAE Kök Sertifika Hizmet Sağlayıcısı - Sürüm 3 O=Türkiye Bilimsel ve Teknolojik Araştırma Kurumu - TÜBİTAK OU=Ulusal Elektronik ve Kriptoloji Araştırma Enstitüsü - UEKAE/Kamu Sertifikasyon Merkezi +# Subject: CN=TÜBİTAK UEKAE Kök Sertifika Hizmet Sağlayıcısı - Sürüm 3 O=Türkiye Bilimsel ve Teknolojik Araştırma Kurumu - TÜBİTAK OU=Ulusal Elektronik ve Kriptoloji Araştırma Enstitüsü - UEKAE/Kamu Sertifikasyon Merkezi +# Label: "T\xc3\x9c\x42\xC4\xB0TAK UEKAE K\xC3\xB6k Sertifika Hizmet Sa\xC4\x9Flay\xc4\xb1\x63\xc4\xb1s\xc4\xb1 - S\xC3\xBCr\xC3\xBCm 3" +# Serial: 17 +# MD5 Fingerprint: ed:41:f5:8c:50:c5:2b:9c:73:e6:ee:6c:eb:c2:a8:26 +# SHA1 Fingerprint: 1b:4b:39:61:26:27:6b:64:91:a2:68:6d:d7:02:43:21:2d:1f:1d:96 +# SHA256 Fingerprint: e4:c7:34:30:d7:a5:b5:09:25:df:43:37:0a:0d:21:6e:9a:79:b9:d6:db:83:73:a0:c6:9e:b1:cc:31:c7:c5:2a +-----BEGIN CERTIFICATE----- +MIIFFzCCA/+gAwIBAgIBETANBgkqhkiG9w0BAQUFADCCASsxCzAJBgNVBAYTAlRS +MRgwFgYDVQQHDA9HZWJ6ZSAtIEtvY2FlbGkxRzBFBgNVBAoMPlTDvHJraXllIEJp +bGltc2VsIHZlIFRla25vbG9qaWsgQXJhxZ90xLFybWEgS3VydW11IC0gVMOcQsSw +VEFLMUgwRgYDVQQLDD9VbHVzYWwgRWxla3Ryb25payB2ZSBLcmlwdG9sb2ppIEFy +YcWfdMSxcm1hIEVuc3RpdMO8c8O8IC0gVUVLQUUxIzAhBgNVBAsMGkthbXUgU2Vy +dGlmaWthc3lvbiBNZXJrZXppMUowSAYDVQQDDEFUw5xCxLBUQUsgVUVLQUUgS8O2 +ayBTZXJ0aWZpa2EgSGl6bWV0IFNhxJ9sYXnEsWPEsXPEsSAtIFPDvHLDvG0gMzAe +Fw0wNzA4MjQxMTM3MDdaFw0xNzA4MjExMTM3MDdaMIIBKzELMAkGA1UEBhMCVFIx +GDAWBgNVBAcMD0dlYnplIC0gS29jYWVsaTFHMEUGA1UECgw+VMO8cmtpeWUgQmls +aW1zZWwgdmUgVGVrbm9sb2ppayBBcmHFn3TEsXJtYSBLdXJ1bXUgLSBUw5xCxLBU +QUsxSDBGBgNVBAsMP1VsdXNhbCBFbGVrdHJvbmlrIHZlIEtyaXB0b2xvamkgQXJh +xZ90xLFybWEgRW5zdGl0w7xzw7wgLSBVRUtBRTEjMCEGA1UECwwaS2FtdSBTZXJ0 +aWZpa2FzeW9uIE1lcmtlemkxSjBIBgNVBAMMQVTDnELEsFRBSyBVRUtBRSBLw7Zr +IFNlcnRpZmlrYSBIaXptZXQgU2HEn2xhecSxY8Sxc8SxIC0gU8O8csO8bSAzMIIB +IjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAim1L/xCIOsP2fpTo6iBkcK4h +gb46ezzb8R1Sf1n68yJMlaCQvEhOEav7t7WNeoMojCZG2E6VQIdhn8WebYGHV2yK +O7Rm6sxA/OOqbLLLAdsyv9Lrhc+hDVXDWzhXcLh1xnnRFDDtG1hba+818qEhTsXO +fJlfbLm4IpNQp81McGq+agV/E5wrHur+R84EpW+sky58K5+eeROR6Oqeyjh1jmKw +lZMq5d/pXpduIF9fhHpEORlAHLpVK/swsoHvhOPc7Jg4OQOFCKlUAwUp8MmPi+oL +hmUZEdPpCSPeaJMDyTYcIW7OjGbxmTDY17PDHfiBLqi9ggtm/oLL4eAagsNAgQID +AQABo0IwQDAdBgNVHQ4EFgQUvYiHyY/2pAoLquvF/pEjnatKijIwDgYDVR0PAQH/ +BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEBAB18+kmP +NOm3JpIWmgV050vQbTlswyb2zrgxvMTfvCr4N5EY3ATIZJkrGG2AA1nJrvhY0D7t +wyOfaTyGOBye79oneNGEN3GKPEs5z35FBtYt2IpNeBLWrcLTy9LQQfMmNkqblWwM +7uXRQydmwYj3erMgbOqwaSvHIOgMA8RBBZniP+Rr+KCGgceExh/VS4ESshYhLBOh +gLJeDEoTniDYYkCrkOpkSi+sDQESeUWoL4cZaMjihccwsnX5OD+ywJO0a+IDRM5n +oN+J1q2MdqMTw5RhK2vZbMEHCiIHhWyFJEapvj+LeISCfiQMnf2BN+MlqO02TpUs +yZyQ2uypQjyttgI= +-----END CERTIFICATE----- + +# Issuer: CN=Buypass Class 2 CA 1 O=Buypass AS-983163327 +# Subject: CN=Buypass Class 2 CA 1 O=Buypass AS-983163327 +# Label: "Buypass Class 2 CA 1" +# Serial: 1 +# MD5 Fingerprint: b8:08:9a:f0:03:cc:1b:0d:c8:6c:0b:76:a1:75:64:23 +# SHA1 Fingerprint: a0:a1:ab:90:c9:fc:84:7b:3b:12:61:e8:97:7d:5f:d3:22:61:d3:cc +# SHA256 Fingerprint: 0f:4e:9c:dd:26:4b:02:55:50:d1:70:80:63:40:21:4f:e9:44:34:c9:b0:2f:69:7e:c7:10:fc:5f:ea:fb:5e:38 +-----BEGIN CERTIFICATE----- +MIIDUzCCAjugAwIBAgIBATANBgkqhkiG9w0BAQUFADBLMQswCQYDVQQGEwJOTzEd +MBsGA1UECgwUQnV5cGFzcyBBUy05ODMxNjMzMjcxHTAbBgNVBAMMFEJ1eXBhc3Mg +Q2xhc3MgMiBDQSAxMB4XDTA2MTAxMzEwMjUwOVoXDTE2MTAxMzEwMjUwOVowSzEL +MAkGA1UEBhMCTk8xHTAbBgNVBAoMFEJ1eXBhc3MgQVMtOTgzMTYzMzI3MR0wGwYD +VQQDDBRCdXlwYXNzIENsYXNzIDIgQ0EgMTCCASIwDQYJKoZIhvcNAQEBBQADggEP +ADCCAQoCggEBAIs8B0XY9t/mx8q6jUPFR42wWsE425KEHK8T1A9vNkYgxC7McXA0 +ojTTNy7Y3Tp3L8DrKehc0rWpkTSHIln+zNvnma+WwajHQN2lFYxuyHyXA8vmIPLX +l18xoS830r7uvqmtqEyeIWZDO6i88wmjONVZJMHCR3axiFyCO7srpgTXjAePzdVB +HfCuuCkslFJgNJQ72uA40Z0zPhX0kzLFANq1KWYOOngPIVJfAuWSeyXTkh4vFZ2B +5J2O6O+JzhRMVB0cgRJNcKi+EAUXfh/RuFdV7c27UsKwHnjCTTZoy1YmwVLBvXb3 +WNVyfh9EdrsAiR0WnVE1703CVu9r4Iw7DekCAwEAAaNCMEAwDwYDVR0TAQH/BAUw +AwEB/zAdBgNVHQ4EFgQUP42aWYv8e3uco684sDntkHGA1sgwDgYDVR0PAQH/BAQD +AgEGMA0GCSqGSIb3DQEBBQUAA4IBAQAVGn4TirnoB6NLJzKyQJHyIdFkhb5jatLP +gcIV1Xp+DCmsNx4cfHZSldq1fyOhKXdlyTKdqC5Wq2B2zha0jX94wNWZUYN/Xtm+ +DKhQ7SLHrQVMdvvt7h5HZPb3J31cKA9FxVxiXqaakZG3Uxcu3K1gnZZkOb1naLKu +BctN518fV4bVIJwo+28TOPX2EZL2fZleHwzoq0QkKXJAPTZSr4xYkHPB7GEseaHs +h7U/2k3ZIQAw3pDaDtMaSKk+hQsUi4y8QZ5q9w5wwDX3OaJdZtB7WZ+oRxKaJyOk +LY4ng5IgodcVf/EuGO70SH8vf/GhGLWhC5SgYiAynB321O+/TIho +-----END CERTIFICATE----- + +# Issuer: CN=Buypass Class 3 CA 1 O=Buypass AS-983163327 +# Subject: CN=Buypass Class 3 CA 1 O=Buypass AS-983163327 +# Label: "Buypass Class 3 CA 1" +# Serial: 2 +# MD5 Fingerprint: df:3c:73:59:81:e7:39:50:81:04:4c:34:a2:cb:b3:7b +# SHA1 Fingerprint: 61:57:3a:11:df:0e:d8:7e:d5:92:65:22:ea:d0:56:d7:44:b3:23:71 +# SHA256 Fingerprint: b7:b1:2b:17:1f:82:1d:aa:99:0c:d0:fe:50:87:b1:28:44:8b:a8:e5:18:4f:84:c5:1e:02:b5:c8:fb:96:2b:24 +-----BEGIN CERTIFICATE----- +MIIDUzCCAjugAwIBAgIBAjANBgkqhkiG9w0BAQUFADBLMQswCQYDVQQGEwJOTzEd +MBsGA1UECgwUQnV5cGFzcyBBUy05ODMxNjMzMjcxHTAbBgNVBAMMFEJ1eXBhc3Mg +Q2xhc3MgMyBDQSAxMB4XDTA1MDUwOTE0MTMwM1oXDTE1MDUwOTE0MTMwM1owSzEL +MAkGA1UEBhMCTk8xHTAbBgNVBAoMFEJ1eXBhc3MgQVMtOTgzMTYzMzI3MR0wGwYD +VQQDDBRCdXlwYXNzIENsYXNzIDMgQ0EgMTCCASIwDQYJKoZIhvcNAQEBBQADggEP +ADCCAQoCggEBAKSO13TZKWTeXx+HgJHqTjnmGcZEC4DVC69TB4sSveZn8AKxifZg +isRbsELRwCGoy+Gb72RRtqfPFfV0gGgEkKBYouZ0plNTVUhjP5JW3SROjvi6K//z +NIqeKNc0n6wv1g/xpC+9UrJJhW05NfBEMJNGJPO251P7vGGvqaMU+8IXF4Rs4HyI ++MkcVyzwPX6UvCWThOiaAJpFBUJXgPROztmuOfbIUxAMZTpHe2DC1vqRycZxbL2R +hzyRhkmr8w+gbCZ2Xhysm3HljbybIR6c1jh+JIAVMYKWsUnTYjdbiAwKYjT+p0h+ +mbEwi5A3lRyoH6UsjfRVyNvdWQrCrXig9IsCAwEAAaNCMEAwDwYDVR0TAQH/BAUw +AwEB/zAdBgNVHQ4EFgQUOBTmyPCppAP0Tj4io1vy1uCtQHQwDgYDVR0PAQH/BAQD +AgEGMA0GCSqGSIb3DQEBBQUAA4IBAQABZ6OMySU9E2NdFm/soT4JXJEVKirZgCFP +Bdy7pYmrEzMqnji3jG8CcmPHc3ceCQa6Oyh7pEfJYWsICCD8igWKH7y6xsL+z27s +EzNxZy5p+qksP2bAEllNC1QCkoS72xLvg3BweMhT+t/Gxv/ciC8HwEmdMldg0/L2 +mSlf56oBzKwzqBwKu5HEA6BvtjT5htOzdlSY9EqBs1OdTUDs5XcTRa9bqh/YL0yC +e/4qxFi7T/ye/QNlGioOw6UgFpRreaaiErS7GqQjel/wroQk5PMr+4okoyeYZdow +dXb8GZHo2+ubPzK/QJcHJrrM85SFSnonk8+QQtS4Wxam58tAA915 +-----END CERTIFICATE----- + +# Issuer: CN=EBG Elektronik Sertifika Hizmet Sağlayıcısı O=EBG Bilişim Teknolojileri ve Hizmetleri A.Ş. +# Subject: CN=EBG Elektronik Sertifika Hizmet Sağlayıcısı O=EBG Bilişim Teknolojileri ve Hizmetleri A.Ş. +# Label: "EBG Elektronik Sertifika Hizmet Sa\xC4\x9Flay\xc4\xb1\x63\xc4\xb1s\xc4\xb1" +# Serial: 5525761995591021570 +# MD5 Fingerprint: 2c:20:26:9d:cb:1a:4a:00:85:b5:b7:5a:ae:c2:01:37 +# SHA1 Fingerprint: 8c:96:ba:eb:dd:2b:07:07:48:ee:30:32:66:a0:f3:98:6e:7c:ae:58 +# SHA256 Fingerprint: 35:ae:5b:dd:d8:f7:ae:63:5c:ff:ba:56:82:a8:f0:0b:95:f4:84:62:c7:10:8e:e9:a0:e5:29:2b:07:4a:af:b2 +-----BEGIN CERTIFICATE----- +MIIF5zCCA8+gAwIBAgIITK9zQhyOdAIwDQYJKoZIhvcNAQEFBQAwgYAxODA2BgNV +BAMML0VCRyBFbGVrdHJvbmlrIFNlcnRpZmlrYSBIaXptZXQgU2HEn2xhecSxY8Sx +c8SxMTcwNQYDVQQKDC5FQkcgQmlsacWfaW0gVGVrbm9sb2ppbGVyaSB2ZSBIaXpt +ZXRsZXJpIEEuxZ4uMQswCQYDVQQGEwJUUjAeFw0wNjA4MTcwMDIxMDlaFw0xNjA4 +MTQwMDMxMDlaMIGAMTgwNgYDVQQDDC9FQkcgRWxla3Ryb25payBTZXJ0aWZpa2Eg +SGl6bWV0IFNhxJ9sYXnEsWPEsXPEsTE3MDUGA1UECgwuRUJHIEJpbGnFn2ltIFRl +a25vbG9qaWxlcmkgdmUgSGl6bWV0bGVyaSBBLsWeLjELMAkGA1UEBhMCVFIwggIi +MA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDuoIRh0DpqZhAy2DE4f6en5f2h +4fuXd7hxlugTlkaDT7byX3JWbhNgpQGR4lvFzVcfd2NR/y8927k/qqk153nQ9dAk +tiHq6yOU/im/+4mRDGSaBUorzAzu8T2bgmmkTPiab+ci2hC6X5L8GCcKqKpE+i4s +tPtGmggDg3KriORqcsnlZR9uKg+ds+g75AxuetpX/dfreYteIAbTdgtsApWjluTL +dlHRKJ2hGvxEok3MenaoDT2/F08iiFD9rrbskFBKW5+VQarKD7JK/oCZTqNGFav4 +c0JqwmZ2sQomFd2TkuzbqV9UIlKRcF0T6kjsbgNs2d1s/OsNA/+mgxKb8amTD8Um +TDGyY5lhcucqZJnSuOl14nypqZoaqsNW2xCaPINStnuWt6yHd6i58mcLlEOzrz5z ++kI2sSXFCjEmN1ZnuqMLfdb3ic1nobc6HmZP9qBVFCVMLDMNpkGMvQQxahByCp0O +Lna9XvNRiYuoP1Vzv9s6xiQFlpJIqkuNKgPlV5EQ9GooFW5Hd4RcUXSfGenmHmMW +OeMRFeNYGkS9y8RsZteEBt8w9DeiQyJ50hBs37vmExH8nYQKE3vwO9D8owrXieqW +fo1IhR5kX9tUoqzVegJ5a9KK8GfaZXINFHDk6Y54jzJ0fFfy1tb0Nokb+Clsi7n2 +l9GkLqq+CxnCRelwXQIDAJ3Zo2MwYTAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB +/wQEAwIBBjAdBgNVHQ4EFgQU587GT/wWZ5b6SqMHwQSny2re2kcwHwYDVR0jBBgw +FoAU587GT/wWZ5b6SqMHwQSny2re2kcwDQYJKoZIhvcNAQEFBQADggIBAJuYml2+ +8ygjdsZs93/mQJ7ANtyVDR2tFcU22NU57/IeIl6zgrRdu0waypIN30ckHrMk2pGI +6YNw3ZPX6bqz3xZaPt7gyPvT/Wwp+BVGoGgmzJNSroIBk5DKd8pNSe/iWtkqvTDO +TLKBtjDOWU/aWR1qeqRFsIImgYZ29fUQALjuswnoT4cCB64kXPBfrAowzIpAoHME +wfuJJPaaHFy3PApnNgUIMbOv2AFoKuB4j3TeuFGkjGwgPaL7s9QJ/XvCgKqTbCmY +Iai7FvOpEl90tYeY8pUm3zTvilORiF0alKM/fCL414i6poyWqD1SNGKfAB5UVUJn +xk1Gj7sURT0KlhaOEKGXmdXTMIXM3rRyt7yKPBgpaP3ccQfuJDlq+u2lrDgv+R4Q +DgZxGhBM/nV+/x5XOULK1+EVoVZVWRvRo68R2E7DpSvvkL/A7IITW43WciyTTo9q +Kd+FPNMN4KIYEsxVL0e3p5sC/kH2iExt2qkBR4NkJ2IQgtYSe14DHzSpyZH+r11t +hie3I6p1GMog57AP14kOpmciY/SDQSsGS7tY1dHXt7kQY9iJSrSq3RZj9W6+YKH4 +7ejWkE8axsWgKdOnIaj1Wjz3x0miIZpKlVIglnKaZsv30oZDfCK+lvm9AahH3eU7 +QPl1K5srRmSGjR70j/sHd9DqSaIcjVIUpgqT +-----END CERTIFICATE----- + +# Issuer: O=certSIGN OU=certSIGN ROOT CA +# Subject: O=certSIGN OU=certSIGN ROOT CA +# Label: "certSIGN ROOT CA" +# Serial: 35210227249154 +# MD5 Fingerprint: 18:98:c0:d6:e9:3a:fc:f9:b0:f5:0c:f7:4b:01:44:17 +# SHA1 Fingerprint: fa:b7:ee:36:97:26:62:fb:2d:b0:2a:f6:bf:03:fd:e8:7c:4b:2f:9b +# SHA256 Fingerprint: ea:a9:62:c4:fa:4a:6b:af:eb:e4:15:19:6d:35:1c:cd:88:8d:4f:53:f3:fa:8a:e6:d7:c4:66:a9:4e:60:42:bb +-----BEGIN CERTIFICATE----- +MIIDODCCAiCgAwIBAgIGIAYFFnACMA0GCSqGSIb3DQEBBQUAMDsxCzAJBgNVBAYT +AlJPMREwDwYDVQQKEwhjZXJ0U0lHTjEZMBcGA1UECxMQY2VydFNJR04gUk9PVCBD +QTAeFw0wNjA3MDQxNzIwMDRaFw0zMTA3MDQxNzIwMDRaMDsxCzAJBgNVBAYTAlJP +MREwDwYDVQQKEwhjZXJ0U0lHTjEZMBcGA1UECxMQY2VydFNJR04gUk9PVCBDQTCC +ASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALczuX7IJUqOtdu0KBuqV5Do +0SLTZLrTk+jUrIZhQGpgV2hUhE28alQCBf/fm5oqrl0Hj0rDKH/v+yv6efHHrfAQ +UySQi2bJqIirr1qjAOm+ukbuW3N7LBeCgV5iLKECZbO9xSsAfsT8AzNXDe3i+s5d +RdY4zTW2ssHQnIFKquSyAVwdj1+ZxLGt24gh65AIgoDzMKND5pCCrlUoSe1b16kQ +OA7+j0xbm0bqQfWwCHTD0IgztnzXdN/chNFDDnU5oSVAKOp4yw4sLjmdjItuFhwv +JoIQ4uNllAoEwF73XVv4EOLQunpL+943AAAaWyjj0pxzPjKHmKHJUS/X3qwzs08C +AwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAcYwHQYDVR0O +BBYEFOCMm9slSbPxfIbWskKHC9BroNnkMA0GCSqGSIb3DQEBBQUAA4IBAQA+0hyJ +LjX8+HXd5n9liPRyTMks1zJO890ZeUe9jjtbkw9QSSQTaxQGcu8J06Gh40CEyecY +MnQ8SG4Pn0vU9x7Tk4ZkVJdjclDVVc/6IJMCopvDI5NOFlV2oHB5bc0hH88vLbwZ +44gx+FkagQnIl6Z0x2DEW8xXjrJ1/RsCCdtZb3KTafcxQdaIOL+Hsr0Wefmq5L6I +Jd1hJyMctTEHBDa0GpC9oHRxUIltvBTjD4au8as+x6AJzKNI0eDbZOeStc+vckNw +i/nDhDwTqn6Sm1dTk/pwwpEOMfmbZ13pljheX7NzTogVZ96edhBiIL5VaZVDADlN +9u6wWk5JRFRYX0KD +-----END CERTIFICATE----- + +# Issuer: CN=CNNIC ROOT O=CNNIC +# Subject: CN=CNNIC ROOT O=CNNIC +# Label: "CNNIC ROOT" +# Serial: 1228079105 +# MD5 Fingerprint: 21:bc:82:ab:49:c4:13:3b:4b:b2:2b:5c:6b:90:9c:19 +# SHA1 Fingerprint: 8b:af:4c:9b:1d:f0:2a:92:f7:da:12:8e:b9:1b:ac:f4:98:60:4b:6f +# SHA256 Fingerprint: e2:83:93:77:3d:a8:45:a6:79:f2:08:0c:c7:fb:44:a3:b7:a1:c3:79:2c:b7:eb:77:29:fd:cb:6a:8d:99:ae:a7 +-----BEGIN CERTIFICATE----- +MIIDVTCCAj2gAwIBAgIESTMAATANBgkqhkiG9w0BAQUFADAyMQswCQYDVQQGEwJD +TjEOMAwGA1UEChMFQ05OSUMxEzARBgNVBAMTCkNOTklDIFJPT1QwHhcNMDcwNDE2 +MDcwOTE0WhcNMjcwNDE2MDcwOTE0WjAyMQswCQYDVQQGEwJDTjEOMAwGA1UEChMF +Q05OSUMxEzARBgNVBAMTCkNOTklDIFJPT1QwggEiMA0GCSqGSIb3DQEBAQUAA4IB +DwAwggEKAoIBAQDTNfc/c3et6FtzF8LRb+1VvG7q6KR5smzDo+/hn7E7SIX1mlwh +IhAsxYLO2uOabjfhhyzcuQxauohV3/2q2x8x6gHx3zkBwRP9SFIhxFXf2tizVHa6 +dLG3fdfA6PZZxU3Iva0fFNrfWEQlMhkqx35+jq44sDB7R3IJMfAw28Mbdim7aXZO +V/kbZKKTVrdvmW7bCgScEeOAH8tjlBAKqeFkgjH5jCftppkA9nCTGPihNIaj3XrC +GHn2emU1z5DrvTOTn1OrczvmmzQgLx3vqR1jGqCA2wMv+SYahtKNu6m+UjqHZ0gN +v7Sg2Ca+I19zN38m5pIEo3/PIKe38zrKy5nLAgMBAAGjczBxMBEGCWCGSAGG+EIB +AQQEAwIABzAfBgNVHSMEGDAWgBRl8jGtKvf33VKWCscCwQ7vptU7ETAPBgNVHRMB +Af8EBTADAQH/MAsGA1UdDwQEAwIB/jAdBgNVHQ4EFgQUZfIxrSr3991SlgrHAsEO +76bVOxEwDQYJKoZIhvcNAQEFBQADggEBAEs17szkrr/Dbq2flTtLP1se31cpolnK +OOK5Gv+e5m4y3R6u6jW39ZORTtpC4cMXYFDy0VwmuYK36m3knITnA3kXr5g9lNvH +ugDnuL8BV8F3RTIMO/G0HAiw/VGgod2aHRM2mm23xzy54cXZF/qD1T0VoDy7Hgvi +yJA/qIYM/PmLXoXLT1tLYhFHxUV8BS9BsZ4QaRuZluBVeftOhpm4lNqGOGqTo+fL +buXf6iFViZx9fX+Y9QCJ7uOEwFyWtcVG6kbghVW2G8kS1sHNzYDzAgE8yGnLRUhj +2JTQ7IUOO04RZfSCjKY9ri4ilAnIXOo8gV0WKgOXFlUJ24pBgp5mmxE= +-----END CERTIFICATE----- + +# Issuer: O=Japanese Government OU=ApplicationCA +# Subject: O=Japanese Government OU=ApplicationCA +# Label: "ApplicationCA - Japanese Government" +# Serial: 49 +# MD5 Fingerprint: 7e:23:4e:5b:a7:a5:b4:25:e9:00:07:74:11:62:ae:d6 +# SHA1 Fingerprint: 7f:8a:b0:cf:d0:51:87:6a:66:f3:36:0f:47:c8:8d:8c:d3:35:fc:74 +# SHA256 Fingerprint: 2d:47:43:7d:e1:79:51:21:5a:12:f3:c5:8e:51:c7:29:a5:80:26:ef:1f:cc:0a:5f:b3:d9:dc:01:2f:60:0d:19 +-----BEGIN CERTIFICATE----- +MIIDoDCCAoigAwIBAgIBMTANBgkqhkiG9w0BAQUFADBDMQswCQYDVQQGEwJKUDEc +MBoGA1UEChMTSmFwYW5lc2UgR292ZXJubWVudDEWMBQGA1UECxMNQXBwbGljYXRp +b25DQTAeFw0wNzEyMTIxNTAwMDBaFw0xNzEyMTIxNTAwMDBaMEMxCzAJBgNVBAYT +AkpQMRwwGgYDVQQKExNKYXBhbmVzZSBHb3Zlcm5tZW50MRYwFAYDVQQLEw1BcHBs +aWNhdGlvbkNBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAp23gdE6H +j6UG3mii24aZS2QNcfAKBZuOquHMLtJqO8F6tJdhjYq+xpqcBrSGUeQ3DnR4fl+K +f5Sk10cI/VBaVuRorChzoHvpfxiSQE8tnfWuREhzNgaeZCw7NCPbXCbkcXmP1G55 +IrmTwcrNwVbtiGrXoDkhBFcsovW8R0FPXjQilbUfKW1eSvNNcr5BViCH/OlQR9cw +FO5cjFW6WY2H/CPek9AEjP3vbb3QesmlOmpyM8ZKDQUXKi17safY1vC+9D/qDiht +QWEjdnjDuGWk81quzMKq2edY3rZ+nYVunyoKb58DKTCXKB28t89UKU5RMfkntigm +/qJj5kEW8DOYRwIDAQABo4GeMIGbMB0GA1UdDgQWBBRUWssmP3HMlEYNllPqa0jQ +k/5CdTAOBgNVHQ8BAf8EBAMCAQYwWQYDVR0RBFIwUKROMEwxCzAJBgNVBAYTAkpQ +MRgwFgYDVQQKDA/ml6XmnKzlm73mlL/lupwxIzAhBgNVBAsMGuOCouODl+ODquOC +seODvOOCt+ODp+ODs0NBMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEFBQAD +ggEBADlqRHZ3ODrso2dGD/mLBqj7apAxzn7s2tGJfHrrLgy9mTLnsCTWw//1sogJ +hyzjVOGjprIIC8CFqMjSnHH2HZ9g/DgzE+Ge3Atf2hZQKXsvcJEPmbo0NI2VdMV+ +eKlmXb3KIXdCEKxmJj3ekav9FfBv7WxfEPjzFvYDio+nEhEMy/0/ecGc/WLuo89U +DNErXxc+4z6/wCs+CZv+iKZ+tJIX/COUgb1up8WMwusRRdv4QcmWdupwX3kSa+Sj +B1oF7ydJzyGfikwJcGapJsErEU4z0g781mzSDjJkaP+tBXhfAx2o45CsJOAPQKdL +rosot4LKGAfmt1t06SAZf7IbiVQ= +-----END CERTIFICATE----- + +# Issuer: CN=GeoTrust Primary Certification Authority - G3 O=GeoTrust Inc. OU=(c) 2008 GeoTrust Inc. - For authorized use only +# Subject: CN=GeoTrust Primary Certification Authority - G3 O=GeoTrust Inc. OU=(c) 2008 GeoTrust Inc. - For authorized use only +# Label: "GeoTrust Primary Certification Authority - G3" +# Serial: 28809105769928564313984085209975885599 +# MD5 Fingerprint: b5:e8:34:36:c9:10:44:58:48:70:6d:2e:83:d4:b8:05 +# SHA1 Fingerprint: 03:9e:ed:b8:0b:e7:a0:3c:69:53:89:3b:20:d2:d9:32:3a:4c:2a:fd +# SHA256 Fingerprint: b4:78:b8:12:25:0d:f8:78:63:5c:2a:a7:ec:7d:15:5e:aa:62:5e:e8:29:16:e2:cd:29:43:61:88:6c:d1:fb:d4 +-----BEGIN CERTIFICATE----- +MIID/jCCAuagAwIBAgIQFaxulBmyeUtB9iepwxgPHzANBgkqhkiG9w0BAQsFADCB +mDELMAkGA1UEBhMCVVMxFjAUBgNVBAoTDUdlb1RydXN0IEluYy4xOTA3BgNVBAsT +MChjKSAyMDA4IEdlb1RydXN0IEluYy4gLSBGb3IgYXV0aG9yaXplZCB1c2Ugb25s +eTE2MDQGA1UEAxMtR2VvVHJ1c3QgUHJpbWFyeSBDZXJ0aWZpY2F0aW9uIEF1dGhv +cml0eSAtIEczMB4XDTA4MDQwMjAwMDAwMFoXDTM3MTIwMTIzNTk1OVowgZgxCzAJ +BgNVBAYTAlVTMRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMTkwNwYDVQQLEzAoYykg +MjAwOCBHZW9UcnVzdCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxNjA0 +BgNVBAMTLUdlb1RydXN0IFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkg +LSBHMzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANziXmJYHTNXOTIz ++uvLh4yn1ErdBojqZI4xmKU4kB6Yzy5jK/BGvESyiaHAKAxJcCGVn2TAppMSAmUm +hsalifD614SgcK9PGpc/BkTVyetyEH3kMSj7HGHmKAdEc5IiaacDiGydY8hS2pgn +5whMcD60yRLBxWeDXTPzAxHsatBT4tG6NmCUgLthY2xbF37fQJQeqw3CIShwiP/W +JmxsYAQlTlV+fe+/lEjetx3dcI0FX4ilm/LC7urRQEFtYjgdVgbFA0dRIBn8exAL +DmKudlW/X3e+PkkBUz2YJQN2JFodtNuJ6nnltrM7P7pMKEF/BqxqjsHQ9gUdfeZC +huOl1UcCAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYw +HQYDVR0OBBYEFMR5yo6hTgMdHNxr2zFblD4/MH8tMA0GCSqGSIb3DQEBCwUAA4IB +AQAtxRPPVoB7eni9n64smefv2t+UXglpp+duaIy9cr5HqQ6XErhK8WTTOd8lNNTB +zU6B8A8ExCSzNJbGpqow32hhc9f5joWJ7w5elShKKiePEI4ufIbEAp7aDHdlDkQN +kv39sxY2+hENHYwOB4lqKVb3cvTdFZx3NWZXqxNT2I7BQMXXExZacse3aQHEerGD +AWh9jUGhlBjBJVz88P6DAod8DQ3PLghcSkANPuyBYeYk28rgDi0Hsj5W3I31QYUH +SJsMC8tJP33st/3LjWeJGqvtux6jAAgIFyqCXDFdRootD4abdNlF+9RAsXqqaC2G +spki4cErx5z481+oghLrGREt +-----END CERTIFICATE----- + +# Issuer: CN=thawte Primary Root CA - G2 O=thawte, Inc. OU=(c) 2007 thawte, Inc. - For authorized use only +# Subject: CN=thawte Primary Root CA - G2 O=thawte, Inc. OU=(c) 2007 thawte, Inc. - For authorized use only +# Label: "thawte Primary Root CA - G2" +# Serial: 71758320672825410020661621085256472406 +# MD5 Fingerprint: 74:9d:ea:60:24:c4:fd:22:53:3e:cc:3a:72:d9:29:4f +# SHA1 Fingerprint: aa:db:bc:22:23:8f:c4:01:a1:27:bb:38:dd:f4:1d:db:08:9e:f0:12 +# SHA256 Fingerprint: a4:31:0d:50:af:18:a6:44:71:90:37:2a:86:af:af:8b:95:1f:fb:43:1d:83:7f:1e:56:88:b4:59:71:ed:15:57 +-----BEGIN CERTIFICATE----- +MIICiDCCAg2gAwIBAgIQNfwmXNmET8k9Jj1Xm67XVjAKBggqhkjOPQQDAzCBhDEL +MAkGA1UEBhMCVVMxFTATBgNVBAoTDHRoYXd0ZSwgSW5jLjE4MDYGA1UECxMvKGMp +IDIwMDcgdGhhd3RlLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxJDAi +BgNVBAMTG3RoYXd0ZSBQcmltYXJ5IFJvb3QgQ0EgLSBHMjAeFw0wNzExMDUwMDAw +MDBaFw0zODAxMTgyMzU5NTlaMIGEMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMdGhh +d3RlLCBJbmMuMTgwNgYDVQQLEy8oYykgMjAwNyB0aGF3dGUsIEluYy4gLSBGb3Ig +YXV0aG9yaXplZCB1c2Ugb25seTEkMCIGA1UEAxMbdGhhd3RlIFByaW1hcnkgUm9v +dCBDQSAtIEcyMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEotWcgnuVnfFSeIf+iha/ +BebfowJPDQfGAFG6DAJSLSKkQjnE/o/qycG+1E3/n3qe4rF8mq2nhglzh9HnmuN6 +papu+7qzcMBniKI11KOasf2twu8x+qi58/sIxpHR+ymVo0IwQDAPBgNVHRMBAf8E +BTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUmtgAMADna3+FGO6Lts6K +DPgR4bswCgYIKoZIzj0EAwMDaQAwZgIxAN344FdHW6fmCsO99YCKlzUNG4k8VIZ3 +KMqh9HneteY4sPBlcIx/AlTCv//YoT7ZzwIxAMSNlPzcU9LcnXgWHxUzI1NS41ox +XZ3Krr0TKUQNJ1uo52icEvdYPy5yAlejj6EULg== +-----END CERTIFICATE----- + +# Issuer: CN=thawte Primary Root CA - G3 O=thawte, Inc. OU=Certification Services Division/(c) 2008 thawte, Inc. - For authorized use only +# Subject: CN=thawte Primary Root CA - G3 O=thawte, Inc. OU=Certification Services Division/(c) 2008 thawte, Inc. - For authorized use only +# Label: "thawte Primary Root CA - G3" +# Serial: 127614157056681299805556476275995414779 +# MD5 Fingerprint: fb:1b:5d:43:8a:94:cd:44:c6:76:f2:43:4b:47:e7:31 +# SHA1 Fingerprint: f1:8b:53:8d:1b:e9:03:b6:a6:f0:56:43:5b:17:15:89:ca:f3:6b:f2 +# SHA256 Fingerprint: 4b:03:f4:58:07:ad:70:f2:1b:fc:2c:ae:71:c9:fd:e4:60:4c:06:4c:f5:ff:b6:86:ba:e5:db:aa:d7:fd:d3:4c +-----BEGIN CERTIFICATE----- +MIIEKjCCAxKgAwIBAgIQYAGXt0an6rS0mtZLL/eQ+zANBgkqhkiG9w0BAQsFADCB +rjELMAkGA1UEBhMCVVMxFTATBgNVBAoTDHRoYXd0ZSwgSW5jLjEoMCYGA1UECxMf +Q2VydGlmaWNhdGlvbiBTZXJ2aWNlcyBEaXZpc2lvbjE4MDYGA1UECxMvKGMpIDIw +MDggdGhhd3RlLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxJDAiBgNV +BAMTG3RoYXd0ZSBQcmltYXJ5IFJvb3QgQ0EgLSBHMzAeFw0wODA0MDIwMDAwMDBa +Fw0zNzEyMDEyMzU5NTlaMIGuMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMdGhhd3Rl +LCBJbmMuMSgwJgYDVQQLEx9DZXJ0aWZpY2F0aW9uIFNlcnZpY2VzIERpdmlzaW9u +MTgwNgYDVQQLEy8oYykgMjAwOCB0aGF3dGUsIEluYy4gLSBGb3IgYXV0aG9yaXpl +ZCB1c2Ugb25seTEkMCIGA1UEAxMbdGhhd3RlIFByaW1hcnkgUm9vdCBDQSAtIEcz +MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsr8nLPvb2FvdeHsbnndm +gcs+vHyu86YnmjSjaDFxODNi5PNxZnmxqWWjpYvVj2AtP0LMqmsywCPLLEHd5N/8 +YZzic7IilRFDGF/Eth9XbAoFWCLINkw6fKXRz4aviKdEAhN0cXMKQlkC+BsUa0Lf +b1+6a4KinVvnSr0eAXLbS3ToO39/fR8EtCab4LRarEc9VbjXsCZSKAExQGbY2SS9 +9irY7CFJXJv2eul/VTV+lmuNk5Mny5K76qxAwJ/C+IDPXfRa3M50hqY+bAtTyr2S +zhkGcuYMXDhpxwTWvGzOW/b3aJzcJRVIiKHpqfiYnODz1TEoYRFsZ5aNOZnLwkUk +OQIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNV +HQ4EFgQUrWyqlGCc7eT/+j4KdCtjA/e2Wb8wDQYJKoZIhvcNAQELBQADggEBABpA +2JVlrAmSicY59BDlqQ5mU1143vokkbvnRFHfxhY0Cu9qRFHqKweKA3rD6z8KLFIW +oCtDuSWQP3CpMyVtRRooOyfPqsMpQhvfO0zAMzRbQYi/aytlryjvsvXDqmbOe1bu +t8jLZ8HJnBoYuMTDSQPxYA5QzUbF83d597YV4Djbxy8ooAw/dyZ02SUS2jHaGh7c +KUGRIjxpp7sC8rZcJwOJ9Abqm+RyguOhCcHpABnTPtRwa7pxpqpYrvS76Wy274fM +m7v/OeZWYdMKp8RcTGB7BXcmer/YB1IsYvdwY9k5vG8cwnncdimvzsUsZAReiDZu +MdRAGmI0Nj81Aa6sY6A= +-----END CERTIFICATE----- + +# Issuer: CN=GeoTrust Primary Certification Authority - G2 O=GeoTrust Inc. OU=(c) 2007 GeoTrust Inc. - For authorized use only +# Subject: CN=GeoTrust Primary Certification Authority - G2 O=GeoTrust Inc. OU=(c) 2007 GeoTrust Inc. - For authorized use only +# Label: "GeoTrust Primary Certification Authority - G2" +# Serial: 80682863203381065782177908751794619243 +# MD5 Fingerprint: 01:5e:d8:6b:bd:6f:3d:8e:a1:31:f8:12:e0:98:73:6a +# SHA1 Fingerprint: 8d:17:84:d5:37:f3:03:7d:ec:70:fe:57:8b:51:9a:99:e6:10:d7:b0 +# SHA256 Fingerprint: 5e:db:7a:c4:3b:82:a0:6a:87:61:e8:d7:be:49:79:eb:f2:61:1f:7d:d7:9b:f9:1c:1c:6b:56:6a:21:9e:d7:66 +-----BEGIN CERTIFICATE----- +MIICrjCCAjWgAwIBAgIQPLL0SAoA4v7rJDteYD7DazAKBggqhkjOPQQDAzCBmDEL +MAkGA1UEBhMCVVMxFjAUBgNVBAoTDUdlb1RydXN0IEluYy4xOTA3BgNVBAsTMChj +KSAyMDA3IEdlb1RydXN0IEluYy4gLSBGb3IgYXV0aG9yaXplZCB1c2Ugb25seTE2 +MDQGA1UEAxMtR2VvVHJ1c3QgUHJpbWFyeSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0 +eSAtIEcyMB4XDTA3MTEwNTAwMDAwMFoXDTM4MDExODIzNTk1OVowgZgxCzAJBgNV +BAYTAlVTMRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMTkwNwYDVQQLEzAoYykgMjAw +NyBHZW9UcnVzdCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxNjA0BgNV +BAMTLUdlb1RydXN0IFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgLSBH +MjB2MBAGByqGSM49AgEGBSuBBAAiA2IABBWx6P0DFUPlrOuHNxFi79KDNlJ9RVcL +So17VDs6bl8VAsBQps8lL33KSLjHUGMcKiEIfJo22Av+0SbFWDEwKCXzXV2juLal +tJLtbCyf691DiaI8S0iRHVDsJt/WYC69IaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAO +BgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFBVfNVdRVfslsq0DafwBo/q+EVXVMAoG +CCqGSM49BAMDA2cAMGQCMGSWWaboCd6LuvpaiIjwH5HTRqjySkwCY/tsXzjbLkGT +qQ7mndwxHLKgpxgceeHHNgIwOlavmnRs9vuD4DPTCF+hnMJbn0bWtsuRBmOiBucz +rD6ogRLQy7rQkgu2npaqBA+K +-----END CERTIFICATE----- + +# Issuer: CN=VeriSign Universal Root Certification Authority O=VeriSign, Inc. OU=VeriSign Trust Network/(c) 2008 VeriSign, Inc. - For authorized use only +# Subject: CN=VeriSign Universal Root Certification Authority O=VeriSign, Inc. OU=VeriSign Trust Network/(c) 2008 VeriSign, Inc. - For authorized use only +# Label: "VeriSign Universal Root Certification Authority" +# Serial: 85209574734084581917763752644031726877 +# MD5 Fingerprint: 8e:ad:b5:01:aa:4d:81:e4:8c:1d:d1:e1:14:00:95:19 +# SHA1 Fingerprint: 36:79:ca:35:66:87:72:30:4d:30:a5:fb:87:3b:0f:a7:7b:b7:0d:54 +# SHA256 Fingerprint: 23:99:56:11:27:a5:71:25:de:8c:ef:ea:61:0d:df:2f:a0:78:b5:c8:06:7f:4e:82:82:90:bf:b8:60:e8:4b:3c +-----BEGIN CERTIFICATE----- +MIIEuTCCA6GgAwIBAgIQQBrEZCGzEyEDDrvkEhrFHTANBgkqhkiG9w0BAQsFADCB +vTELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQL +ExZWZXJpU2lnbiBUcnVzdCBOZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwOCBWZXJp +U2lnbiwgSW5jLiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5MTgwNgYDVQQDEy9W +ZXJpU2lnbiBVbml2ZXJzYWwgUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAe +Fw0wODA0MDIwMDAwMDBaFw0zNzEyMDEyMzU5NTlaMIG9MQswCQYDVQQGEwJVUzEX +MBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlTaWduIFRydXN0 +IE5ldHdvcmsxOjA4BgNVBAsTMShjKSAyMDA4IFZlcmlTaWduLCBJbmMuIC0gRm9y +IGF1dGhvcml6ZWQgdXNlIG9ubHkxODA2BgNVBAMTL1ZlcmlTaWduIFVuaXZlcnNh +bCBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIIBIjANBgkqhkiG9w0BAQEF +AAOCAQ8AMIIBCgKCAQEAx2E3XrEBNNti1xWb/1hajCMj1mCOkdeQmIN65lgZOIzF +9uVkhbSicfvtvbnazU0AtMgtc6XHaXGVHzk8skQHnOgO+k1KxCHfKWGPMiJhgsWH +H26MfF8WIFFE0XBPV+rjHOPMee5Y2A7Cs0WTwCznmhcrewA3ekEzeOEz4vMQGn+H +LL729fdC4uW/h2KJXwBL38Xd5HVEMkE6HnFuacsLdUYI0crSK5XQz/u5QGtkjFdN +/BMReYTtXlT2NJ8IAfMQJQYXStrxHXpma5hgZqTZ79IugvHw7wnqRMkVauIDbjPT +rJ9VAMf2CGqUuV/c4DPxhGD5WycRtPwW8rtWaoAljQIDAQABo4GyMIGvMA8GA1Ud +EwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMG0GCCsGAQUFBwEMBGEwX6FdoFsw +WTBXMFUWCWltYWdlL2dpZjAhMB8wBwYFKw4DAhoEFI/l0xqGrI2Oa8PPgGrUSBgs +exkuMCUWI2h0dHA6Ly9sb2dvLnZlcmlzaWduLmNvbS92c2xvZ28uZ2lmMB0GA1Ud +DgQWBBS2d/ppSEefUxLVwuoHMnYH0ZcHGTANBgkqhkiG9w0BAQsFAAOCAQEASvj4 +sAPmLGd75JR3Y8xuTPl9Dg3cyLk1uXBPY/ok+myDjEedO2Pzmvl2MpWRsXe8rJq+ +seQxIcaBlVZaDrHC1LGmWazxY8u4TB1ZkErvkBYoH1quEPuBUDgMbMzxPcP1Y+Oz +4yHJJDnp/RVmRvQbEdBNc6N9Rvk97ahfYtTxP/jgdFcrGJ2BtMQo2pSXpXDrrB2+ +BxHw1dvd5Yzw1TKwg+ZX4o+/vqGqvz0dtdQ46tewXDpPaj+PwGZsY6rp2aQW9IHR +lRQOfc2VNNnSj3BzgXucfr2YYdhFh5iQxeuGMMY1v/D/w1WIg0vvBZIGcfK4mJO3 +7M2CYfE45k+XmCpajQ== +-----END CERTIFICATE----- + +# Issuer: CN=VeriSign Class 3 Public Primary Certification Authority - G4 O=VeriSign, Inc. OU=VeriSign Trust Network/(c) 2007 VeriSign, Inc. - For authorized use only +# Subject: CN=VeriSign Class 3 Public Primary Certification Authority - G4 O=VeriSign, Inc. OU=VeriSign Trust Network/(c) 2007 VeriSign, Inc. - For authorized use only +# Label: "VeriSign Class 3 Public Primary Certification Authority - G4" +# Serial: 63143484348153506665311985501458640051 +# MD5 Fingerprint: 3a:52:e1:e7:fd:6f:3a:e3:6f:f3:6f:99:1b:f9:22:41 +# SHA1 Fingerprint: 22:d5:d8:df:8f:02:31:d1:8d:f7:9d:b7:cf:8a:2d:64:c9:3f:6c:3a +# SHA256 Fingerprint: 69:dd:d7:ea:90:bb:57:c9:3e:13:5d:c8:5e:a6:fc:d5:48:0b:60:32:39:bd:c4:54:fc:75:8b:2a:26:cf:7f:79 +-----BEGIN CERTIFICATE----- +MIIDhDCCAwqgAwIBAgIQL4D+I4wOIg9IZxIokYesszAKBggqhkjOPQQDAzCByjEL +MAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQLExZW +ZXJpU2lnbiBUcnVzdCBOZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwNyBWZXJpU2ln +biwgSW5jLiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5MUUwQwYDVQQDEzxWZXJp +U2lnbiBDbGFzcyAzIFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0aG9y +aXR5IC0gRzQwHhcNMDcxMTA1MDAwMDAwWhcNMzgwMTE4MjM1OTU5WjCByjELMAkG +A1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQLExZWZXJp +U2lnbiBUcnVzdCBOZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwNyBWZXJpU2lnbiwg +SW5jLiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5MUUwQwYDVQQDEzxWZXJpU2ln +biBDbGFzcyAzIFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5 +IC0gRzQwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAASnVnp8Utpkmw4tXNherJI9/gHm +GUo9FANL+mAnINmDiWn6VMaaGF5VKmTeBvaNSjutEDxlPZCIBIngMGGzrl0Bp3ve +fLK+ymVhAIau2o970ImtTR1ZmkGxvEeA3J5iw/mjgbIwga8wDwYDVR0TAQH/BAUw +AwEB/zAOBgNVHQ8BAf8EBAMCAQYwbQYIKwYBBQUHAQwEYTBfoV2gWzBZMFcwVRYJ +aW1hZ2UvZ2lmMCEwHzAHBgUrDgMCGgQUj+XTGoasjY5rw8+AatRIGCx7GS4wJRYj +aHR0cDovL2xvZ28udmVyaXNpZ24uY29tL3ZzbG9nby5naWYwHQYDVR0OBBYEFLMW +kf3upm7ktS5Jj4d4gYDs5bG1MAoGCCqGSM49BAMDA2gAMGUCMGYhDBgmYFo4e1ZC +4Kf8NoRRkSAsdk1DPcQdhCPQrNZ8NQbOzWm9kA3bbEhCHQ6qQgIxAJw9SDkjOVga +FRJZap7v1VmyHVIsmXHNxynfGyphe3HR3vPA5Q06Sqotp9iGKt0uEA== +-----END CERTIFICATE----- + +# Issuer: CN=NetLock Arany (Class Gold) Főtanúsítvány O=NetLock Kft. OU=Tanúsítványkiadók (Certification Services) +# Subject: CN=NetLock Arany (Class Gold) Főtanúsítvány O=NetLock Kft. OU=Tanúsítványkiadók (Certification Services) +# Label: "NetLock Arany (Class Gold) Főtanúsítvány" +# Serial: 80544274841616 +# MD5 Fingerprint: c5:a1:b7:ff:73:dd:d6:d7:34:32:18:df:fc:3c:ad:88 +# SHA1 Fingerprint: 06:08:3f:59:3f:15:a1:04:a0:69:a4:6b:a9:03:d0:06:b7:97:09:91 +# SHA256 Fingerprint: 6c:61:da:c3:a2:de:f0:31:50:6b:e0:36:d2:a6:fe:40:19:94:fb:d1:3d:f9:c8:d4:66:59:92:74:c4:46:ec:98 +-----BEGIN CERTIFICATE----- +MIIEFTCCAv2gAwIBAgIGSUEs5AAQMA0GCSqGSIb3DQEBCwUAMIGnMQswCQYDVQQG +EwJIVTERMA8GA1UEBwwIQnVkYXBlc3QxFTATBgNVBAoMDE5ldExvY2sgS2Z0LjE3 +MDUGA1UECwwuVGFuw7pzw610dsOhbnlraWFkw7NrIChDZXJ0aWZpY2F0aW9uIFNl +cnZpY2VzKTE1MDMGA1UEAwwsTmV0TG9jayBBcmFueSAoQ2xhc3MgR29sZCkgRsWR +dGFuw7pzw610dsOhbnkwHhcNMDgxMjExMTUwODIxWhcNMjgxMjA2MTUwODIxWjCB +pzELMAkGA1UEBhMCSFUxETAPBgNVBAcMCEJ1ZGFwZXN0MRUwEwYDVQQKDAxOZXRM +b2NrIEtmdC4xNzA1BgNVBAsMLlRhbsO6c8OtdHbDoW55a2lhZMOzayAoQ2VydGlm +aWNhdGlvbiBTZXJ2aWNlcykxNTAzBgNVBAMMLE5ldExvY2sgQXJhbnkgKENsYXNz +IEdvbGQpIEbFkXRhbsO6c8OtdHbDoW55MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A +MIIBCgKCAQEAxCRec75LbRTDofTjl5Bu0jBFHjzuZ9lk4BqKf8owyoPjIMHj9DrT +lF8afFttvzBPhCf2nx9JvMaZCpDyD/V/Q4Q3Y1GLeqVw/HpYzY6b7cNGbIRwXdrz +AZAj/E4wqX7hJ2Pn7WQ8oLjJM2P+FpD/sLj916jAwJRDC7bVWaaeVtAkH3B5r9s5 +VA1lddkVQZQBr17s9o3x/61k/iCa11zr/qYfCGSji3ZVrR47KGAuhyXoqq8fxmRG +ILdwfzzeSNuWU7c5d+Qa4scWhHaXWy+7GRWF+GmF9ZmnqfI0p6m2pgP8b4Y9VHx2 +BJtr+UBdADTHLpl1neWIA6pN+APSQnbAGwIDAKiLo0UwQzASBgNVHRMBAf8ECDAG +AQH/AgEEMA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUzPpnk/C2uNClwB7zU/2M +U9+D15YwDQYJKoZIhvcNAQELBQADggEBAKt/7hwWqZw8UQCgwBEIBaeZ5m8BiFRh +bvG5GK1Krf6BQCOUL/t1fC8oS2IkgYIL9WHxHG64YTjrgfpioTtaYtOUZcTh5m2C ++C8lcLIhJsFyUR+MLMOEkMNaj7rP9KdlpeuY0fsFskZ1FSNqb4VjMIDw1Z4fKRzC +bLBQWV2QWzuoDTDPv31/zvGdg73JRm4gpvlhUbohL3u+pRVjodSVh/GeufOJ8z2F +uLjbvrW5KfnaNwUASZQDhETnv0Mxz3WLJdH0pmT1kvarBes96aULNmLazAZfNou2 +XjG4Kvte9nHfRCaexOYNkbQudZWAUWpLMKawYqGT8ZvYzsRjdT9ZR7E= +-----END CERTIFICATE----- + +# Issuer: CN=Staat der Nederlanden Root CA - G2 O=Staat der Nederlanden +# Subject: CN=Staat der Nederlanden Root CA - G2 O=Staat der Nederlanden +# Label: "Staat der Nederlanden Root CA - G2" +# Serial: 10000012 +# MD5 Fingerprint: 7c:a5:0f:f8:5b:9a:7d:6d:30:ae:54:5a:e3:42:a2:8a +# SHA1 Fingerprint: 59:af:82:79:91:86:c7:b4:75:07:cb:cf:03:57:46:eb:04:dd:b7:16 +# SHA256 Fingerprint: 66:8c:83:94:7d:a6:3b:72:4b:ec:e1:74:3c:31:a0:e6:ae:d0:db:8e:c5:b3:1b:e3:77:bb:78:4f:91:b6:71:6f +-----BEGIN CERTIFICATE----- +MIIFyjCCA7KgAwIBAgIEAJiWjDANBgkqhkiG9w0BAQsFADBaMQswCQYDVQQGEwJO +TDEeMBwGA1UECgwVU3RhYXQgZGVyIE5lZGVybGFuZGVuMSswKQYDVQQDDCJTdGFh +dCBkZXIgTmVkZXJsYW5kZW4gUm9vdCBDQSAtIEcyMB4XDTA4MDMyNjExMTgxN1oX +DTIwMDMyNTExMDMxMFowWjELMAkGA1UEBhMCTkwxHjAcBgNVBAoMFVN0YWF0IGRl +ciBOZWRlcmxhbmRlbjErMCkGA1UEAwwiU3RhYXQgZGVyIE5lZGVybGFuZGVuIFJv +b3QgQ0EgLSBHMjCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMVZ5291 +qj5LnLW4rJ4L5PnZyqtdj7U5EILXr1HgO+EASGrP2uEGQxGZqhQlEq0i6ABtQ8Sp +uOUfiUtnvWFI7/3S4GCI5bkYYCjDdyutsDeqN95kWSpGV+RLufg3fNU254DBtvPU +Z5uW6M7XxgpT0GtJlvOjCwV3SPcl5XCsMBQgJeN/dVrlSPhOewMHBPqCYYdu8DvE +pMfQ9XQ+pV0aCPKbJdL2rAQmPlU6Yiile7Iwr/g3wtG61jj99O9JMDeZJiFIhQGp +5Rbn3JBV3w/oOM2ZNyFPXfUib2rFEhZgF1XyZWampzCROME4HYYEhLoaJXhena/M +UGDWE4dS7WMfbWV9whUYdMrhfmQpjHLYFhN9C0lK8SgbIHRrxT3dsKpICT0ugpTN +GmXZK4iambwYfp/ufWZ8Pr2UuIHOzZgweMFvZ9C+X+Bo7d7iscksWXiSqt8rYGPy +5V6548r6f1CGPqI0GAwJaCgRHOThuVw+R7oyPxjMW4T182t0xHJ04eOLoEq9jWYv +6q012iDTiIJh8BIitrzQ1aTsr1SIJSQ8p22xcik/Plemf1WvbibG/ufMQFxRRIEK +eN5KzlW/HdXZt1bv8Hb/C3m1r737qWmRRpdogBQ2HbN/uymYNqUg+oJgYjOk7Na6 +B6duxc8UpufWkjTYgfX8HV2qXB72o007uPc5AgMBAAGjgZcwgZQwDwYDVR0TAQH/ +BAUwAwEB/zBSBgNVHSAESzBJMEcGBFUdIAAwPzA9BggrBgEFBQcCARYxaHR0cDov +L3d3dy5wa2lvdmVyaGVpZC5ubC9wb2xpY2llcy9yb290LXBvbGljeS1HMjAOBgNV +HQ8BAf8EBAMCAQYwHQYDVR0OBBYEFJFoMocVHYnitfGsNig0jQt8YojrMA0GCSqG +SIb3DQEBCwUAA4ICAQCoQUpnKpKBglBu4dfYszk78wIVCVBR7y29JHuIhjv5tLyS +CZa59sCrI2AGeYwRTlHSeYAz+51IvuxBQ4EffkdAHOV6CMqqi3WtFMTC6GY8ggen +5ieCWxjmD27ZUD6KQhgpxrRW/FYQoAUXvQwjf/ST7ZwaUb7dRUG/kSS0H4zpX897 +IZmflZ85OkYcbPnNe5yQzSipx6lVu6xiNGI1E0sUOlWDuYaNkqbG9AclVMwWVxJK +gnjIFNkXgiYtXSAfea7+1HAWFpWD2DU5/1JddRwWxRNVz0fMdWVSSt7wsKfkCpYL ++63C4iWEst3kvX5ZbJvw8NjnyvLplzh+ib7M+zkXYT9y2zqR2GUBGR2tUKRXCnxL +vJxxcypFURmFzI79R6d0lR2o0a9OF7FpJsKqeFdbxU2n5Z4FF5TKsl+gSRiNNOkm +bEgeqmiSBeGCc1qb3AdbCG19ndeNIdn8FCCqwkXfP+cAslHkwvgFuXkajDTznlvk +N1trSt8sV4pAWja63XVECDdCcAz+3F4hoKOKwJCcaNpQ5kUQR3i2TtJlycM33+FC +Y7BXN0Ute4qcvwXqZVUz9zkQxSgqIXobisQk+T8VyJoVIPVVYpbtbZNQvOSqeK3Z +ywplh6ZmwcSBo3c6WB4L7oOLnR7SUqTMHW+wmG2UMbX4cQrcufx9MmDm66+KAQ== +-----END CERTIFICATE----- + +# Issuer: CN=CA Disig O=Disig a.s. +# Subject: CN=CA Disig O=Disig a.s. +# Label: "CA Disig" +# Serial: 1 +# MD5 Fingerprint: 3f:45:96:39:e2:50:87:f7:bb:fe:98:0c:3c:20:98:e6 +# SHA1 Fingerprint: 2a:c8:d5:8b:57:ce:bf:2f:49:af:f2:fc:76:8f:51:14:62:90:7a:41 +# SHA256 Fingerprint: 92:bf:51:19:ab:ec:ca:d0:b1:33:2d:c4:e1:d0:5f:ba:75:b5:67:90:44:ee:0c:a2:6e:93:1f:74:4f:2f:33:cf +-----BEGIN CERTIFICATE----- +MIIEDzCCAvegAwIBAgIBATANBgkqhkiG9w0BAQUFADBKMQswCQYDVQQGEwJTSzET +MBEGA1UEBxMKQnJhdGlzbGF2YTETMBEGA1UEChMKRGlzaWcgYS5zLjERMA8GA1UE +AxMIQ0EgRGlzaWcwHhcNMDYwMzIyMDEzOTM0WhcNMTYwMzIyMDEzOTM0WjBKMQsw +CQYDVQQGEwJTSzETMBEGA1UEBxMKQnJhdGlzbGF2YTETMBEGA1UEChMKRGlzaWcg +YS5zLjERMA8GA1UEAxMIQ0EgRGlzaWcwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw +ggEKAoIBAQCS9jHBfYj9mQGp2HvycXXxMcbzdWb6UShGhJd4NLxs/LxFWYgmGErE +Nx+hSkS943EE9UQX4j/8SFhvXJ56CbpRNyIjZkMhsDxkovhqFQ4/61HhVKndBpnX +mjxUizkDPw/Fzsbrg3ICqB9x8y34dQjbYkzo+s7552oftms1grrijxaSfQUMbEYD +XcDtab86wYqg6I7ZuUUohwjstMoVvoLdtUSLLa2GDGhibYVW8qwUYzrG0ZmsNHhW +S8+2rT+MitcE5eN4TPWGqvWP+j1scaMtymfraHtuM6kMgiioTGohQBUgDCZbg8Kp +FhXAJIJdKxatymP2dACw30PEEGBWZ2NFAgMBAAGjgf8wgfwwDwYDVR0TAQH/BAUw +AwEB/zAdBgNVHQ4EFgQUjbJJaJ1yCCW5wCf1UJNWSEZx+Y8wDgYDVR0PAQH/BAQD +AgEGMDYGA1UdEQQvMC2BE2Nhb3BlcmF0b3JAZGlzaWcuc2uGFmh0dHA6Ly93d3cu +ZGlzaWcuc2svY2EwZgYDVR0fBF8wXTAtoCugKYYnaHR0cDovL3d3dy5kaXNpZy5z +ay9jYS9jcmwvY2FfZGlzaWcuY3JsMCygKqAohiZodHRwOi8vY2EuZGlzaWcuc2sv +Y2EvY3JsL2NhX2Rpc2lnLmNybDAaBgNVHSAEEzARMA8GDSuBHpGT5goAAAABAQEw +DQYJKoZIhvcNAQEFBQADggEBAF00dGFMrzvY/59tWDYcPQuBDRIrRhCA/ec8J9B6 +yKm2fnQwM6M6int0wHl5QpNt/7EpFIKrIYwvF/k/Ji/1WcbvgAa3mkkp7M5+cTxq +EEHA9tOasnxakZzArFvITV734VP/Q3f8nktnbNfzg9Gg4H8l37iYC5oyOGwwoPP/ +CBUz91BKez6jPiCp3C9WgArtQVCwyfTssuMmRAAOb54GvCKWU3BlxFAKRmukLyeB +EicTXxChds6KezfqwzlhA5WYOudsiCUI/HloDYd9Yvi0X/vF2Ey9WLw/Q1vUHgFN +PGO+I++MzVpQuGhU+QqZMxEA4Z7CRneC9VkGjCFMhwnN5ag= +-----END CERTIFICATE----- + +# Issuer: CN=Juur-SK O=AS Sertifitseerimiskeskus +# Subject: CN=Juur-SK O=AS Sertifitseerimiskeskus +# Label: "Juur-SK" +# Serial: 999181308 +# MD5 Fingerprint: aa:8e:5d:d9:f8:db:0a:58:b7:8d:26:87:6c:82:35:55 +# SHA1 Fingerprint: 40:9d:4b:d9:17:b5:5c:27:b6:9b:64:cb:98:22:44:0d:cd:09:b8:89 +# SHA256 Fingerprint: ec:c3:e9:c3:40:75:03:be:e0:91:aa:95:2f:41:34:8f:f8:8b:aa:86:3b:22:64:be:fa:c8:07:90:15:74:e9:39 +-----BEGIN CERTIFICATE----- +MIIE5jCCA86gAwIBAgIEO45L/DANBgkqhkiG9w0BAQUFADBdMRgwFgYJKoZIhvcN +AQkBFglwa2lAc2suZWUxCzAJBgNVBAYTAkVFMSIwIAYDVQQKExlBUyBTZXJ0aWZp +dHNlZXJpbWlza2Vza3VzMRAwDgYDVQQDEwdKdXVyLVNLMB4XDTAxMDgzMDE0MjMw +MVoXDTE2MDgyNjE0MjMwMVowXTEYMBYGCSqGSIb3DQEJARYJcGtpQHNrLmVlMQsw +CQYDVQQGEwJFRTEiMCAGA1UEChMZQVMgU2VydGlmaXRzZWVyaW1pc2tlc2t1czEQ +MA4GA1UEAxMHSnV1ci1TSzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB +AIFxNj4zB9bjMI0TfncyRsvPGbJgMUaXhvSYRqTCZUXP00B841oiqBB4M8yIsdOB +SvZiF3tfTQou0M+LI+5PAk676w7KvRhj6IAcjeEcjT3g/1tf6mTll+g/mX8MCgkz +ABpTpyHhOEvWgxutr2TC+Rx6jGZITWYfGAriPrsfB2WThbkasLnE+w0R9vXW+RvH +LCu3GFH+4Hv2qEivbDtPL+/40UceJlfwUR0zlv/vWT3aTdEVNMfqPxZIe5EcgEMP +PbgFPtGzlc3Yyg/CQ2fbt5PgIoIuvvVoKIO5wTtpeyDaTpxt4brNj3pssAki14sL +2xzVWiZbDcDq5WDQn/413z8CAwEAAaOCAawwggGoMA8GA1UdEwEB/wQFMAMBAf8w +ggEWBgNVHSAEggENMIIBCTCCAQUGCisGAQQBzh8BAQEwgfYwgdAGCCsGAQUFBwIC +MIHDHoHAAFMAZQBlACAAcwBlAHIAdABpAGYAaQBrAGEAYQB0ACAAbwBuACAAdgDk +AGwAagBhAHMAdABhAHQAdQBkACAAQQBTAC0AaQBzACAAUwBlAHIAdABpAGYAaQB0 +AHMAZQBlAHIAaQBtAGkAcwBrAGUAcwBrAHUAcwAgAGEAbABhAG0ALQBTAEsAIABz +AGUAcgB0AGkAZgBpAGsAYQBhAHQAaQBkAGUAIABrAGkAbgBuAGkAdABhAG0AaQBz +AGUAawBzMCEGCCsGAQUFBwIBFhVodHRwOi8vd3d3LnNrLmVlL2Nwcy8wKwYDVR0f +BCQwIjAgoB6gHIYaaHR0cDovL3d3dy5zay5lZS9qdXVyL2NybC8wHQYDVR0OBBYE +FASqekej5ImvGs8KQKcYP2/v6X2+MB8GA1UdIwQYMBaAFASqekej5ImvGs8KQKcY +P2/v6X2+MA4GA1UdDwEB/wQEAwIB5jANBgkqhkiG9w0BAQUFAAOCAQEAe8EYlFOi +CfP+JmeaUOTDBS8rNXiRTHyoERF5TElZrMj3hWVcRrs7EKACr81Ptcw2Kuxd/u+g +kcm2k298gFTsxwhwDY77guwqYHhpNjbRxZyLabVAyJRld/JXIWY7zoVAtjNjGr95 +HvxcHdMdkxuLDF2FvZkwMhgJkVLpfKG6/2SSmuz+Ne6ML678IIbsSt4beDI3poHS +na9aEhbKmVv8b20OxaAehsmR0FyYgl9jDIpaq9iVpszLita/ZEuOyoqysOkhMp6q +qIWYNIE5ITuoOlIyPfZrN4YGWhWY3PARZv40ILcD9EEQfTmEeZZyY7aWAuVrua0Z +TbvGRNs2yyqcjg== +-----END CERTIFICATE----- + +# Issuer: CN=Hongkong Post Root CA 1 O=Hongkong Post +# Subject: CN=Hongkong Post Root CA 1 O=Hongkong Post +# Label: "Hongkong Post Root CA 1" +# Serial: 1000 +# MD5 Fingerprint: a8:0d:6f:39:78:b9:43:6d:77:42:6d:98:5a:cc:23:ca +# SHA1 Fingerprint: d6:da:a8:20:8d:09:d2:15:4d:24:b5:2f:cb:34:6e:b2:58:b2:8a:58 +# SHA256 Fingerprint: f9:e6:7d:33:6c:51:00:2a:c0:54:c6:32:02:2d:66:dd:a2:e7:e3:ff:f1:0a:d0:61:ed:31:d8:bb:b4:10:cf:b2 +-----BEGIN CERTIFICATE----- +MIIDMDCCAhigAwIBAgICA+gwDQYJKoZIhvcNAQEFBQAwRzELMAkGA1UEBhMCSEsx +FjAUBgNVBAoTDUhvbmdrb25nIFBvc3QxIDAeBgNVBAMTF0hvbmdrb25nIFBvc3Qg +Um9vdCBDQSAxMB4XDTAzMDUxNTA1MTMxNFoXDTIzMDUxNTA0NTIyOVowRzELMAkG +A1UEBhMCSEsxFjAUBgNVBAoTDUhvbmdrb25nIFBvc3QxIDAeBgNVBAMTF0hvbmdr +b25nIFBvc3QgUm9vdCBDQSAxMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC +AQEArP84tulmAknjorThkPlAj3n54r15/gK97iSSHSL22oVyaf7XPwnU3ZG1ApzQ +jVrhVcNQhrkpJsLj2aDxaQMoIIBFIi1WpztUlVYiWR8o3x8gPW2iNr4joLFutbEn +PzlTCeqrauh0ssJlXI6/fMN4hM2eFvz1Lk8gKgifd/PFHsSaUmYeSF7jEAaPIpjh +ZY4bXSNmO7ilMlHIhqqhqZ5/dpTCpmy3QfDVyAY45tQM4vM7TG1QjMSDJ8EThFk9 +nnV0ttgCXjqQesBCNnLsak3c78QA3xMYV18meMjWCnl3v/evt3a5pQuEF10Q6m/h +q5URX208o1xNg1vysxmKgIsLhwIDAQABoyYwJDASBgNVHRMBAf8ECDAGAQH/AgED +MA4GA1UdDwEB/wQEAwIBxjANBgkqhkiG9w0BAQUFAAOCAQEADkbVPK7ih9legYsC +mEEIjEy82tvuJxuC52pF7BaLT4Wg87JwvVqWuspube5Gi27nKi6Wsxkz67SfqLI3 +7piol7Yutmcn1KZJ/RyTZXaeQi/cImyaT/JaFTmxcdcrUehtHJjA2Sr0oYJ71clB +oiMBdDhViw+5LmeiIAQ32pwL0xch4I+XeTRvhEgCIDMb5jREn5Fw9IBehEPCKdJs +EhTkYY2sEJCehFC78JZvRZ+K88psT/oROhUVRsPNH4NbLUES7VBnQRM9IauUiqpO +fMGx+6fWtScvl6tu4B3i0RwsH0Ti/L6RoZz71ilTc4afU9hDDl3WY4JxHYB0yvbi +AmvZWg== +-----END CERTIFICATE----- + +# Issuer: CN=SecureSign RootCA11 O=Japan Certification Services, Inc. +# Subject: CN=SecureSign RootCA11 O=Japan Certification Services, Inc. +# Label: "SecureSign RootCA11" +# Serial: 1 +# MD5 Fingerprint: b7:52:74:e2:92:b4:80:93:f2:75:e4:cc:d7:f2:ea:26 +# SHA1 Fingerprint: 3b:c4:9f:48:f8:f3:73:a0:9c:1e:bd:f8:5b:b1:c3:65:c7:d8:11:b3 +# SHA256 Fingerprint: bf:0f:ee:fb:9e:3a:58:1a:d5:f9:e9:db:75:89:98:57:43:d2:61:08:5c:4d:31:4f:6f:5d:72:59:aa:42:16:12 +-----BEGIN CERTIFICATE----- +MIIDbTCCAlWgAwIBAgIBATANBgkqhkiG9w0BAQUFADBYMQswCQYDVQQGEwJKUDEr +MCkGA1UEChMiSmFwYW4gQ2VydGlmaWNhdGlvbiBTZXJ2aWNlcywgSW5jLjEcMBoG +A1UEAxMTU2VjdXJlU2lnbiBSb290Q0ExMTAeFw0wOTA0MDgwNDU2NDdaFw0yOTA0 +MDgwNDU2NDdaMFgxCzAJBgNVBAYTAkpQMSswKQYDVQQKEyJKYXBhbiBDZXJ0aWZp +Y2F0aW9uIFNlcnZpY2VzLCBJbmMuMRwwGgYDVQQDExNTZWN1cmVTaWduIFJvb3RD +QTExMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA/XeqpRyQBTvLTJsz +i1oURaTnkBbR31fSIRCkF/3frNYfp+TbfPfs37gD2pRY/V1yfIw/XwFndBWW4wI8 +h9uuywGOwvNmxoVF9ALGOrVisq/6nL+k5tSAMJjzDbaTj6nU2DbysPyKyiyhFTOV +MdrAG/LuYpmGYz+/3ZMqg6h2uRMft85OQoWPIucuGvKVCbIFtUROd6EgvanyTgp9 +UK31BQ1FT0Zx/Sg+U/sE2C3XZR1KG/rPO7AxmjVuyIsG0wCR8pQIZUyxNAYAeoni +8McDWc/V1uinMrPmmECGxc0nEovMe863ETxiYAcjPitAbpSACW22s293bzUIUPsC +h8U+iQIDAQABo0IwQDAdBgNVHQ4EFgQUW/hNT7KlhtQ60vFjmqC+CfZXt94wDgYD +VR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEB +AKChOBZmLqdWHyGcBvod7bkixTgm2E5P7KN/ed5GIaGHd48HCJqypMWvDzKYC3xm +KbabfSVSSUOrTC4rbnpwrxYO4wJs+0LmGJ1F2FXI6Dvd5+H0LgscNFxsWEr7jIhQ +X5Ucv+2rIrVls4W6ng+4reV6G4pQOh29Dbx7VFALuUKvVaAYga1lme++5Jy/xIWr +QbJUb9wlze144o4MjQlJ3WN7WmmWAiGovVJZ6X01y8hSyn+B/tlr0/cR7SXf+Of5 +pPpyl4RTDaXQMhhRdlkUbA/r7F+AjHVDg8OFmP9Mni0N5HeDk061lgeLKBObjBmN +QSdJQO7e5iNEOdyhIta6A/I= +-----END CERTIFICATE----- + +# Issuer: CN=ACEDICOM Root O=EDICOM OU=PKI +# Subject: CN=ACEDICOM Root O=EDICOM OU=PKI +# Label: "ACEDICOM Root" +# Serial: 7029493972724711941 +# MD5 Fingerprint: 42:81:a0:e2:1c:e3:55:10:de:55:89:42:65:96:22:e6 +# SHA1 Fingerprint: e0:b4:32:2e:b2:f6:a5:68:b6:54:53:84:48:18:4a:50:36:87:43:84 +# SHA256 Fingerprint: 03:95:0f:b4:9a:53:1f:3e:19:91:94:23:98:df:a9:e0:ea:32:d7:ba:1c:dd:9b:c8:5d:b5:7e:d9:40:0b:43:4a +-----BEGIN CERTIFICATE----- +MIIFtTCCA52gAwIBAgIIYY3HhjsBggUwDQYJKoZIhvcNAQEFBQAwRDEWMBQGA1UE +AwwNQUNFRElDT00gUm9vdDEMMAoGA1UECwwDUEtJMQ8wDQYDVQQKDAZFRElDT00x +CzAJBgNVBAYTAkVTMB4XDTA4MDQxODE2MjQyMloXDTI4MDQxMzE2MjQyMlowRDEW +MBQGA1UEAwwNQUNFRElDT00gUm9vdDEMMAoGA1UECwwDUEtJMQ8wDQYDVQQKDAZF +RElDT00xCzAJBgNVBAYTAkVTMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKC +AgEA/5KV4WgGdrQsyFhIyv2AVClVYyT/kGWbEHV7w2rbYgIB8hiGtXxaOLHkWLn7 +09gtn70yN78sFW2+tfQh0hOR2QetAQXW8713zl9CgQr5auODAKgrLlUTY4HKRxx7 +XBZXehuDYAQ6PmXDzQHe3qTWDLqO3tkE7hdWIpuPY/1NFgu3e3eM+SW10W2ZEi5P +Grjm6gSSrj0RuVFCPYewMYWveVqc/udOXpJPQ/yrOq2lEiZmueIM15jO1FillUAK +t0SdE3QrwqXrIhWYENiLxQSfHY9g5QYbm8+5eaA9oiM/Qj9r+hwDezCNzmzAv+Yb +X79nuIQZ1RXve8uQNjFiybwCq0Zfm/4aaJQ0PZCOrfbkHQl/Sog4P75n/TSW9R28 +MHTLOO7VbKvU/PQAtwBbhTIWdjPp2KOZnQUAqhbm84F9b32qhm2tFXTTxKJxqvQU +fecyuB+81fFOvW8XAjnXDpVCOscAPukmYxHqC9FK/xidstd7LzrZlvvoHpKuE1XI +2Sf23EgbsCTBheN3nZqk8wwRHQ3ItBTutYJXCb8gWH8vIiPYcMt5bMlL8qkqyPyH +K9caUPgn6C9D4zq92Fdx/c6mUlv53U3t5fZvie27k5x2IXXwkkwp9y+cAS7+UEae +ZAwUswdbxcJzbPEHXEUkFDWug/FqTYl6+rPYLWbwNof1K1MCAwEAAaOBqjCBpzAP +BgNVHRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFKaz4SsrSbbXc6GqlPUB53NlTKxQ +MA4GA1UdDwEB/wQEAwIBhjAdBgNVHQ4EFgQUprPhKytJttdzoaqU9QHnc2VMrFAw +RAYDVR0gBD0wOzA5BgRVHSAAMDEwLwYIKwYBBQUHAgEWI2h0dHA6Ly9hY2VkaWNv +bS5lZGljb21ncm91cC5jb20vZG9jMA0GCSqGSIb3DQEBBQUAA4ICAQDOLAtSUWIm +fQwng4/F9tqgaHtPkl7qpHMyEVNEskTLnewPeUKzEKbHDZ3Ltvo/Onzqv4hTGzz3 +gvoFNTPhNahXwOf9jU8/kzJPeGYDdwdY6ZXIfj7QeQCM8htRM5u8lOk6e25SLTKe +I6RF+7YuE7CLGLHdztUdp0J/Vb77W7tH1PwkzQSulgUV1qzOMPPKC8W64iLgpq0i +5ALudBF/TP94HTXa5gI06xgSYXcGCRZj6hitoocf8seACQl1ThCojz2GuHURwCRi +ipZ7SkXp7FnFvmuD5uHorLUwHv4FB4D54SMNUI8FmP8sX+g7tq3PgbUhh8oIKiMn +MCArz+2UW6yyetLHKKGKC5tNSixthT8Jcjxn4tncB7rrZXtaAWPWkFtPF2Y9fwsZ +o5NjEFIqnxQWWOLcpfShFosOkYuByptZ+thrkQdlVV9SH686+5DdaaVbnG0OLLb6 +zqylfDJKZ0DcMDQj3dcEI2bw/FWAp/tmGYI1Z2JwOV5vx+qQQEQIHriy1tvuWacN +GHk0vFQYXlPKNFHtRQrmjseCNj6nOGOpMCwXEGCSn1WHElkQwg9naRHMTh5+Spqt +r0CodaxWkHS4oJyleW/c6RrIaQXpuvoDs3zk4E7Czp3otkYNbn5XOmeUwssfnHdK +Z05phkOTOPu220+DkdRgfks+KzgHVZhepA== +-----END CERTIFICATE----- + +# Issuer: CN=Microsec e-Szigno Root CA 2009 O=Microsec Ltd. +# Subject: CN=Microsec e-Szigno Root CA 2009 O=Microsec Ltd. +# Label: "Microsec e-Szigno Root CA 2009" +# Serial: 14014712776195784473 +# MD5 Fingerprint: f8:49:f4:03:bc:44:2d:83:be:48:69:7d:29:64:fc:b1 +# SHA1 Fingerprint: 89:df:74:fe:5c:f4:0f:4a:80:f9:e3:37:7d:54:da:91:e1:01:31:8e +# SHA256 Fingerprint: 3c:5f:81:fe:a5:fa:b8:2c:64:bf:a2:ea:ec:af:cd:e8:e0:77:fc:86:20:a7:ca:e5:37:16:3d:f3:6e:db:f3:78 +-----BEGIN CERTIFICATE----- +MIIECjCCAvKgAwIBAgIJAMJ+QwRORz8ZMA0GCSqGSIb3DQEBCwUAMIGCMQswCQYD +VQQGEwJIVTERMA8GA1UEBwwIQnVkYXBlc3QxFjAUBgNVBAoMDU1pY3Jvc2VjIEx0 +ZC4xJzAlBgNVBAMMHk1pY3Jvc2VjIGUtU3ppZ25vIFJvb3QgQ0EgMjAwOTEfMB0G +CSqGSIb3DQEJARYQaW5mb0BlLXN6aWduby5odTAeFw0wOTA2MTYxMTMwMThaFw0y +OTEyMzAxMTMwMThaMIGCMQswCQYDVQQGEwJIVTERMA8GA1UEBwwIQnVkYXBlc3Qx +FjAUBgNVBAoMDU1pY3Jvc2VjIEx0ZC4xJzAlBgNVBAMMHk1pY3Jvc2VjIGUtU3pp +Z25vIFJvb3QgQ0EgMjAwOTEfMB0GCSqGSIb3DQEJARYQaW5mb0BlLXN6aWduby5o +dTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAOn4j/NjrdqG2KfgQvvP +kd6mJviZpWNwrZuuyjNAfW2WbqEORO7hE52UQlKavXWFdCyoDh2Tthi3jCyoz/tc +cbna7P7ofo/kLx2yqHWH2Leh5TvPmUpG0IMZfcChEhyVbUr02MelTTMuhTlAdX4U +fIASmFDHQWe4oIBhVKZsTh/gnQ4H6cm6M+f+wFUoLAKApxn1ntxVUwOXewdI/5n7 +N4okxFnMUBBjjqqpGrCEGob5X7uxUG6k0QrM1XF+H6cbfPVTbiJfyyvm1HxdrtbC +xkzlBQHZ7Vf8wSN5/PrIJIOV87VqUQHQd9bpEqH5GoP7ghu5sJf0dgYzQ0mg/wu1 ++rUCAwEAAaOBgDB+MA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0G +A1UdDgQWBBTLD8bfQkPMPcu1SCOhGnqmKrs0aDAfBgNVHSMEGDAWgBTLD8bfQkPM +Pcu1SCOhGnqmKrs0aDAbBgNVHREEFDASgRBpbmZvQGUtc3ppZ25vLmh1MA0GCSqG +SIb3DQEBCwUAA4IBAQDJ0Q5eLtXMs3w+y/w9/w0olZMEyL/azXm4Q5DwpL7v8u8h +mLzU1F0G9u5C7DBsoKqpyvGvivo/C3NqPuouQH4frlRheesuCDfXI/OMn74dseGk +ddug4lQUsbocKaQY9hK6ohQU4zE1yED/t+AFdlfBHFny+L/k7SViXITwfn4fs775 +tyERzAMBVnCnEJIeGzSBHq2cGsMEPO0CYdYeBvNfOofyK/FFh+U9rNHHV4S9a67c +2Pm2G2JwCz02yULyMtd6YebS2z3PyKnJm9zbWETXbzivf3jTo60adbocwTZ8jx5t +HMN1Rq41Bab2XD0h7lbwyYIiLXpUq3DDfSJlgnCW +-----END CERTIFICATE----- + +# Issuer: CN=GlobalSign O=GlobalSign OU=GlobalSign Root CA - R3 +# Subject: CN=GlobalSign O=GlobalSign OU=GlobalSign Root CA - R3 +# Label: "GlobalSign Root CA - R3" +# Serial: 4835703278459759426209954 +# MD5 Fingerprint: c5:df:b8:49:ca:05:13:55:ee:2d:ba:1a:c3:3e:b0:28 +# SHA1 Fingerprint: d6:9b:56:11:48:f0:1c:77:c5:45:78:c1:09:26:df:5b:85:69:76:ad +# SHA256 Fingerprint: cb:b5:22:d7:b7:f1:27:ad:6a:01:13:86:5b:df:1c:d4:10:2e:7d:07:59:af:63:5a:7c:f4:72:0d:c9:63:c5:3b +-----BEGIN CERTIFICATE----- +MIIDXzCCAkegAwIBAgILBAAAAAABIVhTCKIwDQYJKoZIhvcNAQELBQAwTDEgMB4G +A1UECxMXR2xvYmFsU2lnbiBSb290IENBIC0gUjMxEzARBgNVBAoTCkdsb2JhbFNp +Z24xEzARBgNVBAMTCkdsb2JhbFNpZ24wHhcNMDkwMzE4MTAwMDAwWhcNMjkwMzE4 +MTAwMDAwWjBMMSAwHgYDVQQLExdHbG9iYWxTaWduIFJvb3QgQ0EgLSBSMzETMBEG +A1UEChMKR2xvYmFsU2lnbjETMBEGA1UEAxMKR2xvYmFsU2lnbjCCASIwDQYJKoZI +hvcNAQEBBQADggEPADCCAQoCggEBAMwldpB5BngiFvXAg7aEyiie/QV2EcWtiHL8 +RgJDx7KKnQRfJMsuS+FggkbhUqsMgUdwbN1k0ev1LKMPgj0MK66X17YUhhB5uzsT +gHeMCOFJ0mpiLx9e+pZo34knlTifBtc+ycsmWQ1z3rDI6SYOgxXG71uL0gRgykmm +KPZpO/bLyCiR5Z2KYVc3rHQU3HTgOu5yLy6c+9C7v/U9AOEGM+iCK65TpjoWc4zd +QQ4gOsC0p6Hpsk+QLjJg6VfLuQSSaGjlOCZgdbKfd/+RFO+uIEn8rUAVSNECMWEZ +XriX7613t2Saer9fwRPvm2L7DWzgVGkWqQPabumDk3F2xmmFghcCAwEAAaNCMEAw +DgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFI/wS3+o +LkUkrk1Q+mOai97i3Ru8MA0GCSqGSIb3DQEBCwUAA4IBAQBLQNvAUKr+yAzv95ZU +RUm7lgAJQayzE4aGKAczymvmdLm6AC2upArT9fHxD4q/c2dKg8dEe3jgr25sbwMp +jjM5RcOO5LlXbKr8EpbsU8Yt5CRsuZRj+9xTaGdWPoO4zzUhw8lo/s7awlOqzJCK +6fBdRoyV3XpYKBovHd7NADdBj+1EbddTKJd+82cEHhXXipa0095MJ6RMG3NzdvQX +mcIfeg7jLQitChws/zyrVQ4PkX4268NXSb7hLi18YIvDQVETI53O9zJrlAGomecs +Mx86OyXShkDOOyyGeMlhLxS67ttVb9+E7gUJTb0o2HLO02JQZR7rkpeDMdmztcpH +WD9f +-----END CERTIFICATE----- + +# Issuer: CN=Autoridad de Certificacion Firmaprofesional CIF A62634068 +# Subject: CN=Autoridad de Certificacion Firmaprofesional CIF A62634068 +# Label: "Autoridad de Certificacion Firmaprofesional CIF A62634068" +# Serial: 6047274297262753887 +# MD5 Fingerprint: 73:3a:74:7a:ec:bb:a3:96:a6:c2:e4:e2:c8:9b:c0:c3 +# SHA1 Fingerprint: ae:c5:fb:3f:c8:e1:bf:c4:e5:4f:03:07:5a:9a:e8:00:b7:f7:b6:fa +# SHA256 Fingerprint: 04:04:80:28:bf:1f:28:64:d4:8f:9a:d4:d8:32:94:36:6a:82:88:56:55:3f:3b:14:30:3f:90:14:7f:5d:40:ef +-----BEGIN CERTIFICATE----- +MIIGFDCCA/ygAwIBAgIIU+w77vuySF8wDQYJKoZIhvcNAQEFBQAwUTELMAkGA1UE +BhMCRVMxQjBABgNVBAMMOUF1dG9yaWRhZCBkZSBDZXJ0aWZpY2FjaW9uIEZpcm1h +cHJvZmVzaW9uYWwgQ0lGIEE2MjYzNDA2ODAeFw0wOTA1MjAwODM4MTVaFw0zMDEy +MzEwODM4MTVaMFExCzAJBgNVBAYTAkVTMUIwQAYDVQQDDDlBdXRvcmlkYWQgZGUg +Q2VydGlmaWNhY2lvbiBGaXJtYXByb2Zlc2lvbmFsIENJRiBBNjI2MzQwNjgwggIi +MA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDKlmuO6vj78aI14H9M2uDDUtd9 +thDIAl6zQyrET2qyyhxdKJp4ERppWVevtSBC5IsP5t9bpgOSL/UR5GLXMnE42QQM +cas9UX4PB99jBVzpv5RvwSmCwLTaUbDBPLutN0pcyvFLNg4kq7/DhHf9qFD0sefG +L9ItWY16Ck6WaVICqjaY7Pz6FIMMNx/Jkjd/14Et5cS54D40/mf0PmbR0/RAz15i +NA9wBj4gGFrO93IbJWyTdBSTo3OxDqqHECNZXyAFGUftaI6SEspd/NYrspI8IM/h +X68gvqB2f3bl7BqGYTM+53u0P6APjqK5am+5hyZvQWyIplD9amML9ZMWGxmPsu2b +m8mQ9QEM3xk9Dz44I8kvjwzRAv4bVdZO0I08r0+k8/6vKtMFnXkIoctXMbScyJCy +Z/QYFpM6/EfY0XiWMR+6KwxfXZmtY4laJCB22N/9q06mIqqdXuYnin1oKaPnirja +EbsXLZmdEyRG98Xi2J+Of8ePdG1asuhy9azuJBCtLxTa/y2aRnFHvkLfuwHb9H/T +KI8xWVvTyQKmtFLKbpf7Q8UIJm+K9Lv9nyiqDdVF8xM6HdjAeI9BZzwelGSuewvF +6NkBiDkal4ZkQdU7hwxu+g/GvUgUvzlN1J5Bto+WHWOWk9mVBngxaJ43BjuAiUVh +OSPHG0SjFeUc+JIwuwIDAQABo4HvMIHsMBIGA1UdEwEB/wQIMAYBAf8CAQEwDgYD +VR0PAQH/BAQDAgEGMB0GA1UdDgQWBBRlzeurNR4APn7VdMActHNHDhpkLzCBpgYD +VR0gBIGeMIGbMIGYBgRVHSAAMIGPMC8GCCsGAQUFBwIBFiNodHRwOi8vd3d3LmZp +cm1hcHJvZmVzaW9uYWwuY29tL2NwczBcBggrBgEFBQcCAjBQHk4AUABhAHMAZQBv +ACAAZABlACAAbABhACAAQgBvAG4AYQBuAG8AdgBhACAANAA3ACAAQgBhAHIAYwBl +AGwAbwBuAGEAIAAwADgAMAAxADcwDQYJKoZIhvcNAQEFBQADggIBABd9oPm03cXF +661LJLWhAqvdpYhKsg9VSytXjDvlMd3+xDLx51tkljYyGOylMnfX40S2wBEqgLk9 +am58m9Ot/MPWo+ZkKXzR4Tgegiv/J2Wv+xYVxC5xhOW1//qkR71kMrv2JYSiJ0L1 +ILDCExARzRAVukKQKtJE4ZYm6zFIEv0q2skGz3QeqUvVhyj5eTSSPi5E6PaPT481 +PyWzOdxjKpBrIF/EUhJOlywqrJ2X3kjyo2bbwtKDlaZmp54lD+kLM5FlClrD2VQS +3a/DTg4fJl4N3LON7NWBcN7STyQF82xO9UxJZo3R/9ILJUFI/lGExkKvgATP0H5k +SeTy36LssUzAKh3ntLFlosS88Zj0qnAHY7S42jtM+kAiMFsRpvAFDsYCA0irhpuF +3dvd6qJ2gHN99ZwExEWN57kci57q13XRcrHedUTnQn3iV2t93Jm8PYMo6oCTjcVM +ZcFwgbg4/EMxsvYDNEeyrPsiBsse3RdHHF9mudMaotoRsaS8I8nkvof/uZS2+F0g +StRf571oe2XyFR7SOqkt6dhrJKyXWERHrVkY8SFlcN7ONGCoQPHzPKTDKCOM/icz +Q0CgFzzr6juwcqajuUpLXhZI9LK8yIySxZ2frHI2vDSANGupi5LAuBft7HZT9SQB +jLMi6Et8Vcad+qMUu2WFbm5PEn4KPJ2V +-----END CERTIFICATE----- + +# Issuer: CN=Izenpe.com O=IZENPE S.A. +# Subject: CN=Izenpe.com O=IZENPE S.A. +# Label: "Izenpe.com" +# Serial: 917563065490389241595536686991402621 +# MD5 Fingerprint: a6:b0:cd:85:80:da:5c:50:34:a3:39:90:2f:55:67:73 +# SHA1 Fingerprint: 2f:78:3d:25:52:18:a7:4a:65:39:71:b5:2c:a2:9c:45:15:6f:e9:19 +# SHA256 Fingerprint: 25:30:cc:8e:98:32:15:02:ba:d9:6f:9b:1f:ba:1b:09:9e:2d:29:9e:0f:45:48:bb:91:4f:36:3b:c0:d4:53:1f +-----BEGIN CERTIFICATE----- +MIIF8TCCA9mgAwIBAgIQALC3WhZIX7/hy/WL1xnmfTANBgkqhkiG9w0BAQsFADA4 +MQswCQYDVQQGEwJFUzEUMBIGA1UECgwLSVpFTlBFIFMuQS4xEzARBgNVBAMMCkl6 +ZW5wZS5jb20wHhcNMDcxMjEzMTMwODI4WhcNMzcxMjEzMDgyNzI1WjA4MQswCQYD +VQQGEwJFUzEUMBIGA1UECgwLSVpFTlBFIFMuQS4xEzARBgNVBAMMCkl6ZW5wZS5j +b20wggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDJ03rKDx6sp4boFmVq +scIbRTJxldn+EFvMr+eleQGPicPK8lVx93e+d5TzcqQsRNiekpsUOqHnJJAKClaO +xdgmlOHZSOEtPtoKct2jmRXagaKH9HtuJneJWK3W6wyyQXpzbm3benhB6QiIEn6H +LmYRY2xU+zydcsC8Lv/Ct90NduM61/e0aL6i9eOBbsFGb12N4E3GVFWJGjMxCrFX +uaOKmMPsOzTFlUFpfnXCPCDFYbpRR6AgkJOhkEvzTnyFRVSa0QUmQbC1TR0zvsQD +yCV8wXDbO/QJLVQnSKwv4cSsPsjLkkxTOTcj7NMB+eAJRE1NZMDhDVqHIrytG6P+ +JrUV86f8hBnp7KGItERphIPzidF0BqnMC9bC3ieFUCbKF7jJeodWLBoBHmy+E60Q +rLUk9TiRodZL2vG70t5HtfG8gfZZa88ZU+mNFctKy6lvROUbQc/hhqfK0GqfvEyN +BjNaooXlkDWgYlwWTvDjovoDGrQscbNYLN57C9saD+veIR8GdwYDsMnvmfzAuU8L +hij+0rnq49qlw0dpEuDb8PYZi+17cNcC1u2HGCgsBCRMd+RIihrGO5rUD8r6ddIB +QFqNeb+Lz0vPqhbBleStTIo+F5HUsWLlguWABKQDfo2/2n+iD5dPDNMN+9fR5XJ+ +HMh3/1uaD7euBUbl8agW7EekFwIDAQABo4H2MIHzMIGwBgNVHREEgagwgaWBD2lu +Zm9AaXplbnBlLmNvbaSBkTCBjjFHMEUGA1UECgw+SVpFTlBFIFMuQS4gLSBDSUYg +QTAxMzM3MjYwLVJNZXJjLlZpdG9yaWEtR2FzdGVpeiBUMTA1NSBGNjIgUzgxQzBB +BgNVBAkMOkF2ZGEgZGVsIE1lZGl0ZXJyYW5lbyBFdG9yYmlkZWEgMTQgLSAwMTAx +MCBWaXRvcmlhLUdhc3RlaXowDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMC +AQYwHQYDVR0OBBYEFB0cZQ6o8iV7tJHP5LGx5r1VdGwFMA0GCSqGSIb3DQEBCwUA +A4ICAQB4pgwWSp9MiDrAyw6lFn2fuUhfGI8NYjb2zRlrrKvV9pF9rnHzP7MOeIWb +laQnIUdCSnxIOvVFfLMMjlF4rJUT3sb9fbgakEyrkgPH7UIBzg/YsfqikuFgba56 +awmqxinuaElnMIAkejEWOVt+8Rwu3WwJrfIxwYJOubv5vr8qhT/AQKM6WfxZSzwo +JNu0FXWuDYi6LnPAvViH5ULy617uHjAimcs30cQhbIHsvm0m5hzkQiCeR7Csg1lw +LDXWrzY0tM07+DKo7+N4ifuNRSzanLh+QBxh5z6ikixL8s36mLYp//Pye6kfLqCT +VyvehQP5aTfLnnhqBbTFMXiJ7HqnheG5ezzevh55hM6fcA5ZwjUukCox2eRFekGk +LhObNA5me0mrZJfQRsN5nXJQY6aYWwa9SG3YOYNw6DXwBdGqvOPbyALqfP2C2sJb +UjWumDqtujWTI6cfSN01RpiyEGjkpTHCClguGYEQyVB1/OpaFs4R1+7vUIgtYf8/ +QnMFlEPVjjxOAToZpR9GTnfQXeWBIiGH/pR9hNiTrdZoQ0iy2+tzJOeRf1SktoA+ +naM8THLCV8Sg1Mw4J87VBp6iSNnpn86CcDaTmjvfliHjWbcM2pE38P1ZWrOZyGls +QyYBNWNgVYkDOnXYukrZVP/u3oDYLdE41V4tC5h9Pmzb/CaIxw== +-----END CERTIFICATE----- + +# Issuer: CN=Chambers of Commerce Root - 2008 O=AC Camerfirma S.A. +# Subject: CN=Chambers of Commerce Root - 2008 O=AC Camerfirma S.A. +# Label: "Chambers of Commerce Root - 2008" +# Serial: 11806822484801597146 +# MD5 Fingerprint: 5e:80:9e:84:5a:0e:65:0b:17:02:f3:55:18:2a:3e:d7 +# SHA1 Fingerprint: 78:6a:74:ac:76:ab:14:7f:9c:6a:30:50:ba:9e:a8:7e:fe:9a:ce:3c +# SHA256 Fingerprint: 06:3e:4a:fa:c4:91:df:d3:32:f3:08:9b:85:42:e9:46:17:d8:93:d7:fe:94:4e:10:a7:93:7e:e2:9d:96:93:c0 +-----BEGIN CERTIFICATE----- +MIIHTzCCBTegAwIBAgIJAKPaQn6ksa7aMA0GCSqGSIb3DQEBBQUAMIGuMQswCQYD +VQQGEwJFVTFDMEEGA1UEBxM6TWFkcmlkIChzZWUgY3VycmVudCBhZGRyZXNzIGF0 +IHd3dy5jYW1lcmZpcm1hLmNvbS9hZGRyZXNzKTESMBAGA1UEBRMJQTgyNzQzMjg3 +MRswGQYDVQQKExJBQyBDYW1lcmZpcm1hIFMuQS4xKTAnBgNVBAMTIENoYW1iZXJz +IG9mIENvbW1lcmNlIFJvb3QgLSAyMDA4MB4XDTA4MDgwMTEyMjk1MFoXDTM4MDcz +MTEyMjk1MFowga4xCzAJBgNVBAYTAkVVMUMwQQYDVQQHEzpNYWRyaWQgKHNlZSBj +dXJyZW50IGFkZHJlc3MgYXQgd3d3LmNhbWVyZmlybWEuY29tL2FkZHJlc3MpMRIw +EAYDVQQFEwlBODI3NDMyODcxGzAZBgNVBAoTEkFDIENhbWVyZmlybWEgUy5BLjEp +MCcGA1UEAxMgQ2hhbWJlcnMgb2YgQ29tbWVyY2UgUm9vdCAtIDIwMDgwggIiMA0G +CSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCvAMtwNyuAWko6bHiUfaN/Gh/2NdW9 +28sNRHI+JrKQUrpjOyhYb6WzbZSm891kDFX29ufyIiKAXuFixrYp4YFs8r/lfTJq +VKAyGVn+H4vXPWCGhSRv4xGzdz4gljUha7MI2XAuZPeEklPWDrCQiorjh40G072Q +DuKZoRuGDtqaCrsLYVAGUvGef3bsyw/QHg3PmTA9HMRFEFis1tPo1+XqxQEHd9ZR +5gN/ikilTWh1uem8nk4ZcfUyS5xtYBkL+8ydddy/Js2Pk3g5eXNeJQ7KXOt3EgfL +ZEFHcpOrUMPrCXZkNNI5t3YRCQ12RcSprj1qr7V9ZS+UWBDsXHyvfuK2GNnQm05a +Sd+pZgvMPMZ4fKecHePOjlO+Bd5gD2vlGts/4+EhySnB8esHnFIbAURRPHsl18Tl +UlRdJQfKFiC4reRB7noI/plvg6aRArBsNlVq5331lubKgdaX8ZSD6e2wsWsSaR6s ++12pxZjptFtYer49okQ6Y1nUCyXeG0+95QGezdIp1Z8XGQpvvwyQ0wlf2eOKNcx5 +Wk0ZN5K3xMGtr/R5JJqyAQuxr1yW84Ay+1w9mPGgP0revq+ULtlVmhduYJ1jbLhj +ya6BXBg14JC7vjxPNyK5fuvPnnchpj04gftI2jE9K+OJ9dC1vX7gUMQSibMjmhAx +hduub+84Mxh2EQIDAQABo4IBbDCCAWgwEgYDVR0TAQH/BAgwBgEB/wIBDDAdBgNV +HQ4EFgQU+SSsD7K1+HnA+mCIG8TZTQKeFxkwgeMGA1UdIwSB2zCB2IAU+SSsD7K1 ++HnA+mCIG8TZTQKeFxmhgbSkgbEwga4xCzAJBgNVBAYTAkVVMUMwQQYDVQQHEzpN +YWRyaWQgKHNlZSBjdXJyZW50IGFkZHJlc3MgYXQgd3d3LmNhbWVyZmlybWEuY29t +L2FkZHJlc3MpMRIwEAYDVQQFEwlBODI3NDMyODcxGzAZBgNVBAoTEkFDIENhbWVy +ZmlybWEgUy5BLjEpMCcGA1UEAxMgQ2hhbWJlcnMgb2YgQ29tbWVyY2UgUm9vdCAt +IDIwMDiCCQCj2kJ+pLGu2jAOBgNVHQ8BAf8EBAMCAQYwPQYDVR0gBDYwNDAyBgRV +HSAAMCowKAYIKwYBBQUHAgEWHGh0dHA6Ly9wb2xpY3kuY2FtZXJmaXJtYS5jb20w +DQYJKoZIhvcNAQEFBQADggIBAJASryI1wqM58C7e6bXpeHxIvj99RZJe6dqxGfwW +PJ+0W2aeaufDuV2I6A+tzyMP3iU6XsxPpcG1Lawk0lgH3qLPaYRgM+gQDROpI9CF +5Y57pp49chNyM/WqfcZjHwj0/gF/JM8rLFQJ3uIrbZLGOU8W6jx+ekbURWpGqOt1 +glanq6B8aBMz9p0w8G8nOSQjKpD9kCk18pPfNKXG9/jvjA9iSnyu0/VU+I22mlaH +FoI6M6taIgj3grrqLuBHmrS1RaMFO9ncLkVAO+rcf+g769HsJtg1pDDFOqxXnrN2 +pSB7+R5KBWIBpih1YJeSDW4+TTdDDZIVnBgizVGZoCkaPF+KMjNbMMeJL0eYD6MD +xvbxrN8y8NmBGuScvfaAFPDRLLmF9dijscilIeUcE5fuDr3fKanvNFNb0+RqE4QG +tjICxFKuItLcsiFCGtpA8CnJ7AoMXOLQusxI0zcKzBIKinmwPQN/aUv0NCB9szTq +jktk9T79syNnFQ0EuPAtwQlRPLJsFfClI9eDdOTlLsn+mCdCxqvGnrDQWzilm1De +fhiYtUU79nm06PcaewaD+9CL2rvHvRirCG88gGtAPxkZumWK5r7VXNM21+9AUiRg +OGcEMeyP84LG3rlV8zsxkVrctQgVrXYlCg17LofiDKYGvCYQbTed7N14jHyAxfDZ +d0jQ +-----END CERTIFICATE----- + +# Issuer: CN=Global Chambersign Root - 2008 O=AC Camerfirma S.A. +# Subject: CN=Global Chambersign Root - 2008 O=AC Camerfirma S.A. +# Label: "Global Chambersign Root - 2008" +# Serial: 14541511773111788494 +# MD5 Fingerprint: 9e:80:ff:78:01:0c:2e:c1:36:bd:fe:96:90:6e:08:f3 +# SHA1 Fingerprint: 4a:bd:ee:ec:95:0d:35:9c:89:ae:c7:52:a1:2c:5b:29:f6:d6:aa:0c +# SHA256 Fingerprint: 13:63:35:43:93:34:a7:69:80:16:a0:d3:24:de:72:28:4e:07:9d:7b:52:20:bb:8f:bd:74:78:16:ee:be:ba:ca +-----BEGIN CERTIFICATE----- +MIIHSTCCBTGgAwIBAgIJAMnN0+nVfSPOMA0GCSqGSIb3DQEBBQUAMIGsMQswCQYD +VQQGEwJFVTFDMEEGA1UEBxM6TWFkcmlkIChzZWUgY3VycmVudCBhZGRyZXNzIGF0 +IHd3dy5jYW1lcmZpcm1hLmNvbS9hZGRyZXNzKTESMBAGA1UEBRMJQTgyNzQzMjg3 +MRswGQYDVQQKExJBQyBDYW1lcmZpcm1hIFMuQS4xJzAlBgNVBAMTHkdsb2JhbCBD +aGFtYmVyc2lnbiBSb290IC0gMjAwODAeFw0wODA4MDExMjMxNDBaFw0zODA3MzEx +MjMxNDBaMIGsMQswCQYDVQQGEwJFVTFDMEEGA1UEBxM6TWFkcmlkIChzZWUgY3Vy +cmVudCBhZGRyZXNzIGF0IHd3dy5jYW1lcmZpcm1hLmNvbS9hZGRyZXNzKTESMBAG +A1UEBRMJQTgyNzQzMjg3MRswGQYDVQQKExJBQyBDYW1lcmZpcm1hIFMuQS4xJzAl +BgNVBAMTHkdsb2JhbCBDaGFtYmVyc2lnbiBSb290IC0gMjAwODCCAiIwDQYJKoZI +hvcNAQEBBQADggIPADCCAgoCggIBAMDfVtPkOpt2RbQT2//BthmLN0EYlVJH6xed +KYiONWwGMi5HYvNJBL99RDaxccy9Wglz1dmFRP+RVyXfXjaOcNFccUMd2drvXNL7 +G706tcuto8xEpw2uIRU/uXpbknXYpBI4iRmKt4DS4jJvVpyR1ogQC7N0ZJJ0YPP2 +zxhPYLIj0Mc7zmFLmY/CDNBAspjcDahOo7kKrmCgrUVSY7pmvWjg+b4aqIG7HkF4 +ddPB/gBVsIdU6CeQNR1MM62X/JcumIS/LMmjv9GYERTtY/jKmIhYF5ntRQOXfjyG +HoiMvvKRhI9lNNgATH23MRdaKXoKGCQwoze1eqkBfSbW+Q6OWfH9GzO1KTsXO0G2 +Id3UwD2ln58fQ1DJu7xsepeY7s2MH/ucUa6LcL0nn3HAa6x9kGbo1106DbDVwo3V +yJ2dwW3Q0L9R5OP4wzg2rtandeavhENdk5IMagfeOx2YItaswTXbo6Al/3K1dh3e +beksZixShNBFks4c5eUzHdwHU1SjqoI7mjcv3N2gZOnm3b2u/GSFHTynyQbehP9r +6GsaPMWis0L7iwk+XwhSx2LE1AVxv8Rk5Pihg+g+EpuoHtQ2TS9x9o0o9oOpE9Jh +wZG7SMA0j0GMS0zbaRL/UJScIINZc+18ofLx/d33SdNDWKBWY8o9PeU1VlnpDsog +zCtLkykPAgMBAAGjggFqMIIBZjASBgNVHRMBAf8ECDAGAQH/AgEMMB0GA1UdDgQW +BBS5CcqcHtvTbDprru1U8VuTBjUuXjCB4QYDVR0jBIHZMIHWgBS5CcqcHtvTbDpr +ru1U8VuTBjUuXqGBsqSBrzCBrDELMAkGA1UEBhMCRVUxQzBBBgNVBAcTOk1hZHJp +ZCAoc2VlIGN1cnJlbnQgYWRkcmVzcyBhdCB3d3cuY2FtZXJmaXJtYS5jb20vYWRk +cmVzcykxEjAQBgNVBAUTCUE4Mjc0MzI4NzEbMBkGA1UEChMSQUMgQ2FtZXJmaXJt +YSBTLkEuMScwJQYDVQQDEx5HbG9iYWwgQ2hhbWJlcnNpZ24gUm9vdCAtIDIwMDiC +CQDJzdPp1X0jzjAOBgNVHQ8BAf8EBAMCAQYwPQYDVR0gBDYwNDAyBgRVHSAAMCow +KAYIKwYBBQUHAgEWHGh0dHA6Ly9wb2xpY3kuY2FtZXJmaXJtYS5jb20wDQYJKoZI +hvcNAQEFBQADggIBAICIf3DekijZBZRG/5BXqfEv3xoNa/p8DhxJJHkn2EaqbylZ +UohwEurdPfWbU1Rv4WCiqAm57OtZfMY18dwY6fFn5a+6ReAJ3spED8IXDneRRXoz +X1+WLGiLwUePmJs9wOzL9dWCkoQ10b42OFZyMVtHLaoXpGNR6woBrX/sdZ7LoR/x +fxKxueRkf2fWIyr0uDldmOghp+G9PUIadJpwr2hsUF1Jz//7Dl3mLEfXgTpZALVz +a2Mg9jFFCDkO9HB+QHBaP9BrQql0PSgvAm11cpUJjUhjxsYjV5KTXjXBjfkK9yyd +Yhz2rXzdpjEetrHHfoUm+qRqtdpjMNHvkzeyZi99Bffnt0uYlDXA2TopwZ2yUDMd +SqlapskD7+3056huirRXhOukP9DuqqqHW2Pok+JrqNS4cnhrG+055F3Lm6qH1U9O +AP7Zap88MQ8oAgF9mOinsKJknnn4SPIVqczmyETrP3iZ8ntxPjzxmKfFGBI/5rso +M0LpRQp8bfKGeS/Fghl9CYl8slR2iK7ewfPM4W7bMdaTrpmg7yVqc5iJWzouE4ge +v8CSlDQb4ye3ix5vQv/n6TebUB0tovkC7stYWDpxvGjjqsGvHCgfotwjZT+B6q6Z +09gwzxMNTxXJhLynSC34MCN32EZLeW32jO06f2ARePTpm67VVMB0gNELQp/B +-----END CERTIFICATE----- + +# Issuer: CN=Go Daddy Root Certificate Authority - G2 O=GoDaddy.com, Inc. +# Subject: CN=Go Daddy Root Certificate Authority - G2 O=GoDaddy.com, Inc. +# Label: "Go Daddy Root Certificate Authority - G2" +# Serial: 0 +# MD5 Fingerprint: 80:3a:bc:22:c1:e6:fb:8d:9b:3b:27:4a:32:1b:9a:01 +# SHA1 Fingerprint: 47:be:ab:c9:22:ea:e8:0e:78:78:34:62:a7:9f:45:c2:54:fd:e6:8b +# SHA256 Fingerprint: 45:14:0b:32:47:eb:9c:c8:c5:b4:f0:d7:b5:30:91:f7:32:92:08:9e:6e:5a:63:e2:74:9d:d3:ac:a9:19:8e:da +-----BEGIN CERTIFICATE----- +MIIDxTCCAq2gAwIBAgIBADANBgkqhkiG9w0BAQsFADCBgzELMAkGA1UEBhMCVVMx +EDAOBgNVBAgTB0FyaXpvbmExEzARBgNVBAcTClNjb3R0c2RhbGUxGjAYBgNVBAoT +EUdvRGFkZHkuY29tLCBJbmMuMTEwLwYDVQQDEyhHbyBEYWRkeSBSb290IENlcnRp +ZmljYXRlIEF1dGhvcml0eSAtIEcyMB4XDTA5MDkwMTAwMDAwMFoXDTM3MTIzMTIz +NTk1OVowgYMxCzAJBgNVBAYTAlVTMRAwDgYDVQQIEwdBcml6b25hMRMwEQYDVQQH +EwpTY290dHNkYWxlMRowGAYDVQQKExFHb0RhZGR5LmNvbSwgSW5jLjExMC8GA1UE +AxMoR28gRGFkZHkgUm9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkgLSBHMjCCASIw +DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAL9xYgjx+lk09xvJGKP3gElY6SKD +E6bFIEMBO4Tx5oVJnyfq9oQbTqC023CYxzIBsQU+B07u9PpPL1kwIuerGVZr4oAH +/PMWdYA5UXvl+TW2dE6pjYIT5LY/qQOD+qK+ihVqf94Lw7YZFAXK6sOoBJQ7Rnwy +DfMAZiLIjWltNowRGLfTshxgtDj6AozO091GB94KPutdfMh8+7ArU6SSYmlRJQVh +GkSBjCypQ5Yj36w6gZoOKcUcqeldHraenjAKOc7xiID7S13MMuyFYkMlNAJWJwGR +tDtwKj9useiciAF9n9T521NtYJ2/LOdYq7hfRvzOxBsDPAnrSTFcaUaz4EcCAwEA +AaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYE +FDqahQcQZyi27/a9BUFuIMGU2g/eMA0GCSqGSIb3DQEBCwUAA4IBAQCZ21151fmX +WWcDYfF+OwYxdS2hII5PZYe096acvNjpL9DbWu7PdIxztDhC2gV7+AJ1uP2lsdeu +9tfeE8tTEH6KRtGX+rcuKxGrkLAngPnon1rpN5+r5N9ss4UXnT3ZJE95kTXWXwTr +gIOrmgIttRD02JDHBHNA7XIloKmf7J6raBKZV8aPEjoJpL1E/QYVN8Gb5DKj7Tjo +2GTzLH4U/ALqn83/B2gX2yKQOC16jdFU8WnjXzPKej17CuPKf1855eJ1usV2GDPO +LPAvTK33sefOT6jEm0pUBsV/fdUID+Ic/n4XuKxe9tQWskMJDE32p2u0mYRlynqI +4uJEvlz36hz1 +-----END CERTIFICATE----- + +# Issuer: CN=Starfield Root Certificate Authority - G2 O=Starfield Technologies, Inc. +# Subject: CN=Starfield Root Certificate Authority - G2 O=Starfield Technologies, Inc. +# Label: "Starfield Root Certificate Authority - G2" +# Serial: 0 +# MD5 Fingerprint: d6:39:81:c6:52:7e:96:69:fc:fc:ca:66:ed:05:f2:96 +# SHA1 Fingerprint: b5:1c:06:7c:ee:2b:0c:3d:f8:55:ab:2d:92:f4:fe:39:d4:e7:0f:0e +# SHA256 Fingerprint: 2c:e1:cb:0b:f9:d2:f9:e1:02:99:3f:be:21:51:52:c3:b2:dd:0c:ab:de:1c:68:e5:31:9b:83:91:54:db:b7:f5 +-----BEGIN CERTIFICATE----- +MIID3TCCAsWgAwIBAgIBADANBgkqhkiG9w0BAQsFADCBjzELMAkGA1UEBhMCVVMx +EDAOBgNVBAgTB0FyaXpvbmExEzARBgNVBAcTClNjb3R0c2RhbGUxJTAjBgNVBAoT +HFN0YXJmaWVsZCBUZWNobm9sb2dpZXMsIEluYy4xMjAwBgNVBAMTKVN0YXJmaWVs +ZCBSb290IENlcnRpZmljYXRlIEF1dGhvcml0eSAtIEcyMB4XDTA5MDkwMTAwMDAw +MFoXDTM3MTIzMTIzNTk1OVowgY8xCzAJBgNVBAYTAlVTMRAwDgYDVQQIEwdBcml6 +b25hMRMwEQYDVQQHEwpTY290dHNkYWxlMSUwIwYDVQQKExxTdGFyZmllbGQgVGVj +aG5vbG9naWVzLCBJbmMuMTIwMAYDVQQDEylTdGFyZmllbGQgUm9vdCBDZXJ0aWZp +Y2F0ZSBBdXRob3JpdHkgLSBHMjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC +ggEBAL3twQP89o/8ArFvW59I2Z154qK3A2FWGMNHttfKPTUuiUP3oWmb3ooa/RMg +nLRJdzIpVv257IzdIvpy3Cdhl+72WoTsbhm5iSzchFvVdPtrX8WJpRBSiUZV9Lh1 +HOZ/5FSuS/hVclcCGfgXcVnrHigHdMWdSL5stPSksPNkN3mSwOxGXn/hbVNMYq/N +Hwtjuzqd+/x5AJhhdM8mgkBj87JyahkNmcrUDnXMN/uLicFZ8WJ/X7NfZTD4p7dN +dloedl40wOiWVpmKs/B/pM293DIxfJHP4F8R+GuqSVzRmZTRouNjWwl2tVZi4Ut0 +HZbUJtQIBFnQmA4O5t78w+wfkPECAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAO +BgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFHwMMh+n2TB/xH1oo2Kooc6rB1snMA0G +CSqGSIb3DQEBCwUAA4IBAQARWfolTwNvlJk7mh+ChTnUdgWUXuEok21iXQnCoKjU +sHU48TRqneSfioYmUeYs0cYtbpUgSpIB7LiKZ3sx4mcujJUDJi5DnUox9g61DLu3 +4jd/IroAow57UvtruzvE03lRTs2Q9GcHGcg8RnoNAX3FWOdt5oUwF5okxBDgBPfg +8n/Uqgr/Qh037ZTlZFkSIHc40zI+OIF1lnP6aI+xy84fxez6nH7PfrHxBy22/L/K +pL/QlwVKvOoYKAKQvVR4CSFx09F9HdkWsKlhPdAKACL8x3vLCWRFCztAgfd9fDL1 +mMpYjn0q7pBZc2T5NnReJaH1ZgUufzkVqSr7UIuOhWn0 +-----END CERTIFICATE----- + +# Issuer: CN=Starfield Services Root Certificate Authority - G2 O=Starfield Technologies, Inc. +# Subject: CN=Starfield Services Root Certificate Authority - G2 O=Starfield Technologies, Inc. +# Label: "Starfield Services Root Certificate Authority - G2" +# Serial: 0 +# MD5 Fingerprint: 17:35:74:af:7b:61:1c:eb:f4:f9:3c:e2:ee:40:f9:a2 +# SHA1 Fingerprint: 92:5a:8f:8d:2c:6d:04:e0:66:5f:59:6a:ff:22:d8:63:e8:25:6f:3f +# SHA256 Fingerprint: 56:8d:69:05:a2:c8:87:08:a4:b3:02:51:90:ed:cf:ed:b1:97:4a:60:6a:13:c6:e5:29:0f:cb:2a:e6:3e:da:b5 +-----BEGIN CERTIFICATE----- +MIID7zCCAtegAwIBAgIBADANBgkqhkiG9w0BAQsFADCBmDELMAkGA1UEBhMCVVMx +EDAOBgNVBAgTB0FyaXpvbmExEzARBgNVBAcTClNjb3R0c2RhbGUxJTAjBgNVBAoT +HFN0YXJmaWVsZCBUZWNobm9sb2dpZXMsIEluYy4xOzA5BgNVBAMTMlN0YXJmaWVs +ZCBTZXJ2aWNlcyBSb290IENlcnRpZmljYXRlIEF1dGhvcml0eSAtIEcyMB4XDTA5 +MDkwMTAwMDAwMFoXDTM3MTIzMTIzNTk1OVowgZgxCzAJBgNVBAYTAlVTMRAwDgYD +VQQIEwdBcml6b25hMRMwEQYDVQQHEwpTY290dHNkYWxlMSUwIwYDVQQKExxTdGFy +ZmllbGQgVGVjaG5vbG9naWVzLCBJbmMuMTswOQYDVQQDEzJTdGFyZmllbGQgU2Vy +dmljZXMgUm9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkgLSBHMjCCASIwDQYJKoZI +hvcNAQEBBQADggEPADCCAQoCggEBANUMOsQq+U7i9b4Zl1+OiFOxHz/Lz58gE20p +OsgPfTz3a3Y4Y9k2YKibXlwAgLIvWX/2h/klQ4bnaRtSmpDhcePYLQ1Ob/bISdm2 +8xpWriu2dBTrz/sm4xq6HZYuajtYlIlHVv8loJNwU4PahHQUw2eeBGg6345AWh1K +Ts9DkTvnVtYAcMtS7nt9rjrnvDH5RfbCYM8TWQIrgMw0R9+53pBlbQLPLJGmpufe +hRhJfGZOozptqbXuNC66DQO4M99H67FrjSXZm86B0UVGMpZwh94CDklDhbZsc7tk +6mFBrMnUVN+HL8cisibMn1lUaJ/8viovxFUcdUBgF4UCVTmLfwUCAwEAAaNCMEAw +DwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFJxfAN+q +AdcwKziIorhtSpzyEZGDMA0GCSqGSIb3DQEBCwUAA4IBAQBLNqaEd2ndOxmfZyMI +bw5hyf2E3F/YNoHN2BtBLZ9g3ccaaNnRbobhiCPPE95Dz+I0swSdHynVv/heyNXB +ve6SbzJ08pGCL72CQnqtKrcgfU28elUSwhXqvfdqlS5sdJ/PHLTyxQGjhdByPq1z +qwubdQxtRbeOlKyWN7Wg0I8VRw7j6IPdj/3vQQF3zCepYoUz8jcI73HPdwbeyBkd +iEDPfUYd/x7H4c7/I9vG+o1VTqkC50cRRj70/b17KSa7qWFiNyi2LSr2EIZkyXCn +0q23KXB56jzaYyWf/Wi3MOxw+3WKt21gZ7IeyLnp2KhvAotnDU0mV3HaIPzBSlCN +sSi6 +-----END CERTIFICATE----- + +# Issuer: CN=AffirmTrust Commercial O=AffirmTrust +# Subject: CN=AffirmTrust Commercial O=AffirmTrust +# Label: "AffirmTrust Commercial" +# Serial: 8608355977964138876 +# MD5 Fingerprint: 82:92:ba:5b:ef:cd:8a:6f:a6:3d:55:f9:84:f6:d6:b7 +# SHA1 Fingerprint: f9:b5:b6:32:45:5f:9c:be:ec:57:5f:80:dc:e9:6e:2c:c7:b2:78:b7 +# SHA256 Fingerprint: 03:76:ab:1d:54:c5:f9:80:3c:e4:b2:e2:01:a0:ee:7e:ef:7b:57:b6:36:e8:a9:3c:9b:8d:48:60:c9:6f:5f:a7 +-----BEGIN CERTIFICATE----- +MIIDTDCCAjSgAwIBAgIId3cGJyapsXwwDQYJKoZIhvcNAQELBQAwRDELMAkGA1UE +BhMCVVMxFDASBgNVBAoMC0FmZmlybVRydXN0MR8wHQYDVQQDDBZBZmZpcm1UcnVz +dCBDb21tZXJjaWFsMB4XDTEwMDEyOTE0MDYwNloXDTMwMTIzMTE0MDYwNlowRDEL +MAkGA1UEBhMCVVMxFDASBgNVBAoMC0FmZmlybVRydXN0MR8wHQYDVQQDDBZBZmZp +cm1UcnVzdCBDb21tZXJjaWFsMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC +AQEA9htPZwcroRX1BiLLHwGy43NFBkRJLLtJJRTWzsO3qyxPxkEylFf6EqdbDuKP +Hx6GGaeqtS25Xw2Kwq+FNXkyLbscYjfysVtKPcrNcV/pQr6U6Mje+SJIZMblq8Yr +ba0F8PrVC8+a5fBQpIs7R6UjW3p6+DM/uO+Zl+MgwdYoic+U+7lF7eNAFxHUdPAL +MeIrJmqbTFeurCA+ukV6BfO9m2kVrn1OIGPENXY6BwLJN/3HR+7o8XYdcxXyl6S1 +yHp52UKqK39c/s4mT6NmgTWvRLpUHhwwMmWd5jyTXlBOeuM61G7MGvv50jeuJCqr +VwMiKA1JdX+3KNp1v47j3A55MQIDAQABo0IwQDAdBgNVHQ4EFgQUnZPGU4teyq8/ +nx4P5ZmVvCT2lI8wDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwDQYJ +KoZIhvcNAQELBQADggEBAFis9AQOzcAN/wr91LoWXym9e2iZWEnStB03TX8nfUYG +XUPGhi4+c7ImfU+TqbbEKpqrIZcUsd6M06uJFdhrJNTxFq7YpFzUf1GO7RgBsZNj +vbz4YYCanrHOQnDiqX0GJX0nof5v7LMeJNrjS1UaADs1tDvZ110w/YETifLCBivt +Z8SOyUOyXGsViQK8YvxO8rUzqrJv0wqiUOP2O+guRMLbZjipM1ZI8W0bM40NjD9g +N53Tym1+NH4Nn3J2ixufcv1SNUFFApYvHLKac0khsUlHRUe072o0EclNmsxZt9YC +nlpOZbWUrhvfKbAW8b8Angc6F2S1BLUjIZkKlTuXfO8= +-----END CERTIFICATE----- + +# Issuer: CN=AffirmTrust Networking O=AffirmTrust +# Subject: CN=AffirmTrust Networking O=AffirmTrust +# Label: "AffirmTrust Networking" +# Serial: 8957382827206547757 +# MD5 Fingerprint: 42:65:ca:be:01:9a:9a:4c:a9:8c:41:49:cd:c0:d5:7f +# SHA1 Fingerprint: 29:36:21:02:8b:20:ed:02:f5:66:c5:32:d1:d6:ed:90:9f:45:00:2f +# SHA256 Fingerprint: 0a:81:ec:5a:92:97:77:f1:45:90:4a:f3:8d:5d:50:9f:66:b5:e2:c5:8f:cd:b5:31:05:8b:0e:17:f3:f0:b4:1b +-----BEGIN CERTIFICATE----- +MIIDTDCCAjSgAwIBAgIIfE8EORzUmS0wDQYJKoZIhvcNAQEFBQAwRDELMAkGA1UE +BhMCVVMxFDASBgNVBAoMC0FmZmlybVRydXN0MR8wHQYDVQQDDBZBZmZpcm1UcnVz +dCBOZXR3b3JraW5nMB4XDTEwMDEyOTE0MDgyNFoXDTMwMTIzMTE0MDgyNFowRDEL +MAkGA1UEBhMCVVMxFDASBgNVBAoMC0FmZmlybVRydXN0MR8wHQYDVQQDDBZBZmZp +cm1UcnVzdCBOZXR3b3JraW5nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC +AQEAtITMMxcua5Rsa2FSoOujz3mUTOWUgJnLVWREZY9nZOIG41w3SfYvm4SEHi3y +YJ0wTsyEheIszx6e/jarM3c1RNg1lho9Nuh6DtjVR6FqaYvZ/Ls6rnla1fTWcbua +kCNrmreIdIcMHl+5ni36q1Mr3Lt2PpNMCAiMHqIjHNRqrSK6mQEubWXLviRmVSRL +QESxG9fhwoXA3hA/Pe24/PHxI1Pcv2WXb9n5QHGNfb2V1M6+oF4nI979ptAmDgAp +6zxG8D1gvz9Q0twmQVGeFDdCBKNwV6gbh+0t+nvujArjqWaJGctB+d1ENmHP4ndG +yH329JKBNv3bNPFyfvMMFr20FQIDAQABo0IwQDAdBgNVHQ4EFgQUBx/S55zawm6i +QLSwelAQUHTEyL0wDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwDQYJ +KoZIhvcNAQEFBQADggEBAIlXshZ6qML91tmbmzTCnLQyFE2npN/svqe++EPbkTfO +tDIuUFUaNU52Q3Eg75N3ThVwLofDwR1t3Mu1J9QsVtFSUzpE0nPIxBsFZVpikpzu +QY0x2+c06lkh1QF612S4ZDnNye2v7UsDSKegmQGA3GWjNq5lWUhPgkvIZfFXHeVZ +Lgo/bNjR9eUJtGxUAArgFU2HdW23WJZa3W3SAKD0m0i+wzekujbgfIeFlxoVot4u +olu9rxj5kFDNcFn4J2dHy8egBzp90SxdbBk6ZrV9/ZFvgrG+CJPbFEfxojfHRZ48 +x3evZKiT3/Zpg4Jg8klCNO1aAFSFHBY2kgxc+qatv9s= +-----END CERTIFICATE----- + +# Issuer: CN=AffirmTrust Premium O=AffirmTrust +# Subject: CN=AffirmTrust Premium O=AffirmTrust +# Label: "AffirmTrust Premium" +# Serial: 7893706540734352110 +# MD5 Fingerprint: c4:5d:0e:48:b6:ac:28:30:4e:0a:bc:f9:38:16:87:57 +# SHA1 Fingerprint: d8:a6:33:2c:e0:03:6f:b1:85:f6:63:4f:7d:6a:06:65:26:32:28:27 +# SHA256 Fingerprint: 70:a7:3f:7f:37:6b:60:07:42:48:90:45:34:b1:14:82:d5:bf:0e:69:8e:cc:49:8d:f5:25:77:eb:f2:e9:3b:9a +-----BEGIN CERTIFICATE----- +MIIFRjCCAy6gAwIBAgIIbYwURrGmCu4wDQYJKoZIhvcNAQEMBQAwQTELMAkGA1UE +BhMCVVMxFDASBgNVBAoMC0FmZmlybVRydXN0MRwwGgYDVQQDDBNBZmZpcm1UcnVz +dCBQcmVtaXVtMB4XDTEwMDEyOTE0MTAzNloXDTQwMTIzMTE0MTAzNlowQTELMAkG +A1UEBhMCVVMxFDASBgNVBAoMC0FmZmlybVRydXN0MRwwGgYDVQQDDBNBZmZpcm1U +cnVzdCBQcmVtaXVtMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAxBLf +qV/+Qd3d9Z+K4/as4Tx4mrzY8H96oDMq3I0gW64tb+eT2TZwamjPjlGjhVtnBKAQ +JG9dKILBl1fYSCkTtuG+kU3fhQxTGJoeJKJPj/CihQvL9Cl/0qRY7iZNyaqoe5rZ ++jjeRFcV5fiMyNlI4g0WJx0eyIOFJbe6qlVBzAMiSy2RjYvmia9mx+n/K+k8rNrS +s8PhaJyJ+HoAVt70VZVs+7pk3WKL3wt3MutizCaam7uqYoNMtAZ6MMgpv+0GTZe5 +HMQxK9VfvFMSF5yZVylmd2EhMQcuJUmdGPLu8ytxjLW6OQdJd/zvLpKQBY0tL3d7 +70O/Nbua2Plzpyzy0FfuKE4mX4+QaAkvuPjcBukumj5Rp9EixAqnOEhss/n/fauG +V+O61oV4d7pD6kh/9ti+I20ev9E2bFhc8e6kGVQa9QPSdubhjL08s9NIS+LI+H+S +qHZGnEJlPqQewQcDWkYtuJfzt9WyVSHvutxMAJf7FJUnM7/oQ0dG0giZFmA7mn7S +5u046uwBHjxIVkkJx0w3AJ6IDsBz4W9m6XJHMD4Q5QsDyZpCAGzFlH5hxIrff4Ia +C1nEWTJ3s7xgaVY5/bQGeyzWZDbZvUjthB9+pSKPKrhC9IK31FOQeE4tGv2Bb0TX +OwF0lkLgAOIua+rF7nKsu7/+6qqo+Nz2snmKtmcCAwEAAaNCMEAwHQYDVR0OBBYE +FJ3AZ6YMItkm9UWrpmVSESfYRaxjMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/ +BAQDAgEGMA0GCSqGSIb3DQEBDAUAA4ICAQCzV00QYk465KzquByvMiPIs0laUZx2 +KI15qldGF9X1Uva3ROgIRL8YhNILgM3FEv0AVQVhh0HctSSePMTYyPtwni94loMg +Nt58D2kTiKV1NpgIpsbfrM7jWNa3Pt668+s0QNiigfV4Py/VpfzZotReBA4Xrf5B +8OWycvpEgjNC6C1Y91aMYj+6QrCcDFx+LmUmXFNPALJ4fqENmS2NuB2OosSw/WDQ +MKSOyARiqcTtNd56l+0OOF6SL5Nwpamcb6d9Ex1+xghIsV5n61EIJenmJWtSKZGc +0jlzCFfemQa0W50QBuHCAKi4HEoCChTQwUHK+4w1IX2COPKpVJEZNZOUbWo6xbLQ +u4mGk+ibyQ86p3q4ofB4Rvr8Ny/lioTz3/4E2aFooC8k4gmVBtWVyuEklut89pMF +u+1z6S3RdTnX5yTb2E5fQ4+e0BQ5v1VwSJlXMbSc7kqYA5YwH2AG7hsj/oFgIxpH +YoWlzBk0gG+zrBrjn/B7SK3VAdlntqlyk+otZrWyuOQ9PLLvTIzq6we/qzWaVYa8 +GKa1qF60g2xraUDTn9zxw2lrueFtCfTxqlB2Cnp9ehehVZZCmTEJ3WARjQUwfuaO +RtGdFNrHF+QFlozEJLUbzxQHskD4o55BhrwE0GuWyCqANP2/7waj3VjFhT0+j/6e +KeC2uAloGRwYQw== +-----END CERTIFICATE----- + +# Issuer: CN=AffirmTrust Premium ECC O=AffirmTrust +# Subject: CN=AffirmTrust Premium ECC O=AffirmTrust +# Label: "AffirmTrust Premium ECC" +# Serial: 8401224907861490260 +# MD5 Fingerprint: 64:b0:09:55:cf:b1:d5:99:e2:be:13:ab:a6:5d:ea:4d +# SHA1 Fingerprint: b8:23:6b:00:2f:1d:16:86:53:01:55:6c:11:a4:37:ca:eb:ff:c3:bb +# SHA256 Fingerprint: bd:71:fd:f6:da:97:e4:cf:62:d1:64:7a:dd:25:81:b0:7d:79:ad:f8:39:7e:b4:ec:ba:9c:5e:84:88:82:14:23 +-----BEGIN CERTIFICATE----- +MIIB/jCCAYWgAwIBAgIIdJclisc/elQwCgYIKoZIzj0EAwMwRTELMAkGA1UEBhMC +VVMxFDASBgNVBAoMC0FmZmlybVRydXN0MSAwHgYDVQQDDBdBZmZpcm1UcnVzdCBQ +cmVtaXVtIEVDQzAeFw0xMDAxMjkxNDIwMjRaFw00MDEyMzExNDIwMjRaMEUxCzAJ +BgNVBAYTAlVTMRQwEgYDVQQKDAtBZmZpcm1UcnVzdDEgMB4GA1UEAwwXQWZmaXJt +VHJ1c3QgUHJlbWl1bSBFQ0MwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAAQNMF4bFZ0D +0KF5Nbc6PJJ6yhUczWLznCZcBz3lVPqj1swS6vQUX+iOGasvLkjmrBhDeKzQN8O9 +ss0s5kfiGuZjuD0uL3jET9v0D6RoTFVya5UdThhClXjMNzyR4ptlKymjQjBAMB0G +A1UdDgQWBBSaryl6wBE1NSZRMADDav5A1a7WPDAPBgNVHRMBAf8EBTADAQH/MA4G +A1UdDwEB/wQEAwIBBjAKBggqhkjOPQQDAwNnADBkAjAXCfOHiFBar8jAQr9HX/Vs +aobgxCd05DhT1wV/GzTjxi+zygk8N53X57hG8f2h4nECMEJZh0PUUd+60wkyWs6I +flc9nF9Ca/UHLbXwgpP5WW+uZPpY5Yse42O+tYHNbwKMeQ== +-----END CERTIFICATE----- + +# Issuer: CN=Certum Trusted Network CA O=Unizeto Technologies S.A. OU=Certum Certification Authority +# Subject: CN=Certum Trusted Network CA O=Unizeto Technologies S.A. OU=Certum Certification Authority +# Label: "Certum Trusted Network CA" +# Serial: 279744 +# MD5 Fingerprint: d5:e9:81:40:c5:18:69:fc:46:2c:89:75:62:0f:aa:78 +# SHA1 Fingerprint: 07:e0:32:e0:20:b7:2c:3f:19:2f:06:28:a2:59:3a:19:a7:0f:06:9e +# SHA256 Fingerprint: 5c:58:46:8d:55:f5:8e:49:7e:74:39:82:d2:b5:00:10:b6:d1:65:37:4a:cf:83:a7:d4:a3:2d:b7:68:c4:40:8e +-----BEGIN CERTIFICATE----- +MIIDuzCCAqOgAwIBAgIDBETAMA0GCSqGSIb3DQEBBQUAMH4xCzAJBgNVBAYTAlBM +MSIwIAYDVQQKExlVbml6ZXRvIFRlY2hub2xvZ2llcyBTLkEuMScwJQYDVQQLEx5D +ZXJ0dW0gQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkxIjAgBgNVBAMTGUNlcnR1bSBU +cnVzdGVkIE5ldHdvcmsgQ0EwHhcNMDgxMDIyMTIwNzM3WhcNMjkxMjMxMTIwNzM3 +WjB+MQswCQYDVQQGEwJQTDEiMCAGA1UEChMZVW5pemV0byBUZWNobm9sb2dpZXMg +Uy5BLjEnMCUGA1UECxMeQ2VydHVtIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MSIw +IAYDVQQDExlDZXJ0dW0gVHJ1c3RlZCBOZXR3b3JrIENBMIIBIjANBgkqhkiG9w0B +AQEFAAOCAQ8AMIIBCgKCAQEA4/t9o3K6wvDJFIf1awFO4W5AB7ptJ11/91sts1rH +UV+rpDKmYYe2bg+G0jACl/jXaVehGDldamR5xgFZrDwxSjh80gTSSyjoIF87B6LM +TXPb865Px1bVWqeWifrzq2jUI4ZZJ88JJ7ysbnKDHDBy3+Ci6dLhdHUZvSqeexVU +BBvXQzmtVSjF4hq79MDkrjhJM8x2hZ85RdKknvISjFH4fOQtf/WsX+sWn7Et0brM +kUJ3TCXJkDhv2/DM+44el1k+1WBO5gUo7Ul5E0u6SNsv+XLTOcr+H9g0cvW0QM8x +AcPs3hEtF10fuFDRXhmnad4HMyjKUJX5p1TLVIZQRan5SQIDAQABo0IwQDAPBgNV +HRMBAf8EBTADAQH/MB0GA1UdDgQWBBQIds3LB/8k9sXN7buQvOKEN0Z19zAOBgNV +HQ8BAf8EBAMCAQYwDQYJKoZIhvcNAQEFBQADggEBAKaorSLOAT2mo/9i0Eidi15y +sHhE49wcrwn9I0j6vSrEuVUEtRCjjSfeC4Jj0O7eDDd5QVsisrCaQVymcODU0HfL +I9MA4GxWL+FpDQ3Zqr8hgVDZBqWo/5U30Kr+4rP1mS1FhIrlQgnXdAIv94nYmem8 +J9RHjboNRhx3zxSkHLmkMcScKHQDNP8zGSal6Q10tz6XxnboJ5ajZt3hrvJBW8qY +VoNzcOSGGtIxQbovvi0TWnZvTuhOgQ4/WwMioBK+ZlgRSssDxLQqKi2WF+A5VLxI +03YnnZotBqbJ7DnSq9ufmgsnAjUpsUCV5/nonFWIGUbWtzT1fs45mtk48VH3Tyw= +-----END CERTIFICATE----- + +# Issuer: CN=Certinomis - Autorité Racine O=Certinomis OU=0002 433998903 +# Subject: CN=Certinomis - Autorité Racine O=Certinomis OU=0002 433998903 +# Label: "Certinomis - Autorité Racine" +# Serial: 1 +# MD5 Fingerprint: 7f:30:78:8c:03:e3:ca:c9:0a:e2:c9:ea:1e:aa:55:1a +# SHA1 Fingerprint: 2e:14:da:ec:28:f0:fa:1e:8e:38:9a:4e:ab:eb:26:c0:0a:d3:83:c3 +# SHA256 Fingerprint: fc:bf:e2:88:62:06:f7:2b:27:59:3c:8b:07:02:97:e1:2d:76:9e:d1:0e:d7:93:07:05:a8:09:8e:ff:c1:4d:17 +-----BEGIN CERTIFICATE----- +MIIFnDCCA4SgAwIBAgIBATANBgkqhkiG9w0BAQUFADBjMQswCQYDVQQGEwJGUjET +MBEGA1UEChMKQ2VydGlub21pczEXMBUGA1UECxMOMDAwMiA0MzM5OTg5MDMxJjAk +BgNVBAMMHUNlcnRpbm9taXMgLSBBdXRvcml0w6kgUmFjaW5lMB4XDTA4MDkxNzA4 +Mjg1OVoXDTI4MDkxNzA4Mjg1OVowYzELMAkGA1UEBhMCRlIxEzARBgNVBAoTCkNl +cnRpbm9taXMxFzAVBgNVBAsTDjAwMDIgNDMzOTk4OTAzMSYwJAYDVQQDDB1DZXJ0 +aW5vbWlzIC0gQXV0b3JpdMOpIFJhY2luZTCCAiIwDQYJKoZIhvcNAQEBBQADggIP +ADCCAgoCggIBAJ2Fn4bT46/HsmtuM+Cet0I0VZ35gb5j2CN2DpdUzZlMGvE5x4jY +F1AMnmHawE5V3udauHpOd4cN5bjr+p5eex7Ezyh0x5P1FMYiKAT5kcOrJ3NqDi5N +8y4oH3DfVS9O7cdxbwlyLu3VMpfQ8Vh30WC8Tl7bmoT2R2FFK/ZQpn9qcSdIhDWe +rP5pqZ56XjUl+rSnSTV3lqc2W+HN3yNw2F1MpQiD8aYkOBOo7C+ooWfHpi2GR+6K +/OybDnT0K0kCe5B1jPyZOQE51kqJ5Z52qz6WKDgmi92NjMD2AR5vpTESOH2VwnHu +7XSu5DaiQ3XV8QCb4uTXzEIDS3h65X27uK4uIJPT5GHfceF2Z5c/tt9qc1pkIuVC +28+BA5PY9OMQ4HL2AHCs8MF6DwV/zzRpRbWT5BnbUhYjBYkOjUjkJW+zeL9i9Qf6 +lSTClrLooyPCXQP8w9PlfMl1I9f09bze5N/NgL+RiH2nE7Q5uiy6vdFrzPOlKO1E +nn1So2+WLhl+HPNbxxaOu2B9d2ZHVIIAEWBsMsGoOBvrbpgT1u449fCfDu/+MYHB +0iSVL1N6aaLwD4ZFjliCK0wi1F6g530mJ0jfJUaNSih8hp75mxpZuWW/Bd22Ql09 +5gBIgl4g9xGC3srYn+Y3RyYe63j3YcNBZFgCQfna4NH4+ej9Uji29YnfAgMBAAGj +WzBZMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBQN +jLZh2kS40RR9w759XkjwzspqsDAXBgNVHSAEEDAOMAwGCiqBegFWAgIAAQEwDQYJ +KoZIhvcNAQEFBQADggIBACQ+YAZ+He86PtvqrxyaLAEL9MW12Ukx9F1BjYkMTv9s +ov3/4gbIOZ/xWqndIlgVqIrTseYyCYIDbNc/CMf4uboAbbnW/FIyXaR/pDGUu7ZM +OH8oMDX/nyNTt7buFHAAQCvaR6s0fl6nVjBhK4tDrP22iCj1a7Y+YEq6QpA0Z43q +619FVDsXrIvkxmUP7tCMXWY5zjKn2BCXwH40nJ+U8/aGH88bc62UeYdocMMzpXDn +2NU4lG9jeeu/Cg4I58UvD0KgKxRA/yHgBcUn4YQRE7rWhh1BCxMjidPJC+iKunqj +o3M3NYB9Ergzd0A4wPpeMNLytqOx1qKVl4GbUu1pTP+A5FPbVFsDbVRfsbjvJL1v +nxHDx2TCDyhihWZeGnuyt++uNckZM6i4J9szVb9o4XVIRFb7zdNIu0eJOqxp9YDG +5ERQL1TEqkPFMTFYvZbF6nVsmnWxTfj3l/+WFvKXTej28xH5On2KOG4Ey+HTRRWq +pdEdnV1j6CTmNhTih60bWfVEm/vXd3wfAXBioSAaosUaKPQhA+4u2cGA6rnZgtZb +dsLLO7XSAPCjDuGtbkD326C00EauFddEwk01+dIL8hf2rGbVJLJP0RyZwG71fet0 +BLj5TXcJ17TPBzAJ8bgAVtkXFhYKK4bfjwEZGuW7gmP/vgt2Fl43N+bYdJeimUV5 +-----END CERTIFICATE----- + +# Issuer: CN=Root CA Generalitat Valenciana O=Generalitat Valenciana OU=PKIGVA +# Subject: CN=Root CA Generalitat Valenciana O=Generalitat Valenciana OU=PKIGVA +# Label: "Root CA Generalitat Valenciana" +# Serial: 994436456 +# MD5 Fingerprint: 2c:8c:17:5e:b1:54:ab:93:17:b5:36:5a:db:d1:c6:f2 +# SHA1 Fingerprint: a0:73:e5:c5:bd:43:61:0d:86:4c:21:13:0a:85:58:57:cc:9c:ea:46 +# SHA256 Fingerprint: 8c:4e:df:d0:43:48:f3:22:96:9e:7e:29:a4:cd:4d:ca:00:46:55:06:1c:16:e1:b0:76:42:2e:f3:42:ad:63:0e +-----BEGIN CERTIFICATE----- +MIIGizCCBXOgAwIBAgIEO0XlaDANBgkqhkiG9w0BAQUFADBoMQswCQYDVQQGEwJF +UzEfMB0GA1UEChMWR2VuZXJhbGl0YXQgVmFsZW5jaWFuYTEPMA0GA1UECxMGUEtJ +R1ZBMScwJQYDVQQDEx5Sb290IENBIEdlbmVyYWxpdGF0IFZhbGVuY2lhbmEwHhcN +MDEwNzA2MTYyMjQ3WhcNMjEwNzAxMTUyMjQ3WjBoMQswCQYDVQQGEwJFUzEfMB0G +A1UEChMWR2VuZXJhbGl0YXQgVmFsZW5jaWFuYTEPMA0GA1UECxMGUEtJR1ZBMScw +JQYDVQQDEx5Sb290IENBIEdlbmVyYWxpdGF0IFZhbGVuY2lhbmEwggEiMA0GCSqG +SIb3DQEBAQUAA4IBDwAwggEKAoIBAQDGKqtXETcvIorKA3Qdyu0togu8M1JAJke+ +WmmmO3I2F0zo37i7L3bhQEZ0ZQKQUgi0/6iMweDHiVYQOTPvaLRfX9ptI6GJXiKj +SgbwJ/BXufjpTjJ3Cj9BZPPrZe52/lSqfR0grvPXdMIKX/UIKFIIzFVd0g/bmoGl +u6GzwZTNVOAydTGRGmKy3nXiz0+J2ZGQD0EbtFpKd71ng+CT516nDOeB0/RSrFOy +A8dEJvt55cs0YFAQexvba9dHq198aMpunUEDEO5rmXteJajCq+TA81yc477OMUxk +Hl6AovWDfgzWyoxVjr7gvkkHD6MkQXpYHYTqWBLI4bft75PelAgxAgMBAAGjggM7 +MIIDNzAyBggrBgEFBQcBAQQmMCQwIgYIKwYBBQUHMAGGFmh0dHA6Ly9vY3NwLnBr +aS5ndmEuZXMwEgYDVR0TAQH/BAgwBgEB/wIBAjCCAjQGA1UdIASCAiswggInMIIC +IwYKKwYBBAG/VQIBADCCAhMwggHoBggrBgEFBQcCAjCCAdoeggHWAEEAdQB0AG8A +cgBpAGQAYQBkACAAZABlACAAQwBlAHIAdABpAGYAaQBjAGEAYwBpAPMAbgAgAFIA +YQDtAHoAIABkAGUAIABsAGEAIABHAGUAbgBlAHIAYQBsAGkAdABhAHQAIABWAGEA +bABlAG4AYwBpAGEAbgBhAC4ADQAKAEwAYQAgAEQAZQBjAGwAYQByAGEAYwBpAPMA +bgAgAGQAZQAgAFAAcgDhAGMAdABpAGMAYQBzACAAZABlACAAQwBlAHIAdABpAGYA +aQBjAGEAYwBpAPMAbgAgAHEAdQBlACAAcgBpAGcAZQAgAGUAbAAgAGYAdQBuAGMA +aQBvAG4AYQBtAGkAZQBuAHQAbwAgAGQAZQAgAGwAYQAgAHAAcgBlAHMAZQBuAHQA +ZQAgAEEAdQB0AG8AcgBpAGQAYQBkACAAZABlACAAQwBlAHIAdABpAGYAaQBjAGEA +YwBpAPMAbgAgAHMAZQAgAGUAbgBjAHUAZQBuAHQAcgBhACAAZQBuACAAbABhACAA +ZABpAHIAZQBjAGMAaQDzAG4AIAB3AGUAYgAgAGgAdAB0AHAAOgAvAC8AdwB3AHcA +LgBwAGsAaQAuAGcAdgBhAC4AZQBzAC8AYwBwAHMwJQYIKwYBBQUHAgEWGWh0dHA6 +Ly93d3cucGtpLmd2YS5lcy9jcHMwHQYDVR0OBBYEFHs100DSHHgZZu90ECjcPk+y +eAT8MIGVBgNVHSMEgY0wgYqAFHs100DSHHgZZu90ECjcPk+yeAT8oWykajBoMQsw +CQYDVQQGEwJFUzEfMB0GA1UEChMWR2VuZXJhbGl0YXQgVmFsZW5jaWFuYTEPMA0G +A1UECxMGUEtJR1ZBMScwJQYDVQQDEx5Sb290IENBIEdlbmVyYWxpdGF0IFZhbGVu +Y2lhbmGCBDtF5WgwDQYJKoZIhvcNAQEFBQADggEBACRhTvW1yEICKrNcda3Fbcrn +lD+laJWIwVTAEGmiEi8YPyVQqHxK6sYJ2fR1xkDar1CdPaUWu20xxsdzCkj+IHLt +b8zog2EWRpABlUt9jppSCS/2bxzkoXHPjCpaF3ODR00PNvsETUlR4hTJZGH71BTg +9J63NI8KJr2XXPR5OkowGcytT6CYirQxlyric21+eLj4iIlPsSKRZEv1UN4D2+XF +ducTZnV+ZfsBn5OHiJ35Rld8TWCvmHMTI6QgkYH60GFmuH3Rr9ZvHmw96RH9qfmC +IoaZM3Fa6hlXPZHNqcCjbgcTpsnt+GijnsNacgmHKNHEc8RzGF9QdRYxn7fofMM= +-----END CERTIFICATE----- + +# Issuer: CN=A-Trust-nQual-03 O=A-Trust Ges. f. Sicherheitssysteme im elektr. Datenverkehr GmbH OU=A-Trust-nQual-03 +# Subject: CN=A-Trust-nQual-03 O=A-Trust Ges. f. Sicherheitssysteme im elektr. Datenverkehr GmbH OU=A-Trust-nQual-03 +# Label: "A-Trust-nQual-03" +# Serial: 93214 +# MD5 Fingerprint: 49:63:ae:27:f4:d5:95:3d:d8:db:24:86:b8:9c:07:53 +# SHA1 Fingerprint: d3:c0:63:f2:19:ed:07:3e:34:ad:5d:75:0b:32:76:29:ff:d5:9a:f2 +# SHA256 Fingerprint: 79:3c:bf:45:59:b9:fd:e3:8a:b2:2d:f1:68:69:f6:98:81:ae:14:c4:b0:13:9a:c7:88:a7:8a:1a:fc:ca:02:fb +-----BEGIN CERTIFICATE----- +MIIDzzCCAregAwIBAgIDAWweMA0GCSqGSIb3DQEBBQUAMIGNMQswCQYDVQQGEwJB +VDFIMEYGA1UECgw/QS1UcnVzdCBHZXMuIGYuIFNpY2hlcmhlaXRzc3lzdGVtZSBp +bSBlbGVrdHIuIERhdGVudmVya2VociBHbWJIMRkwFwYDVQQLDBBBLVRydXN0LW5R +dWFsLTAzMRkwFwYDVQQDDBBBLVRydXN0LW5RdWFsLTAzMB4XDTA1MDgxNzIyMDAw +MFoXDTE1MDgxNzIyMDAwMFowgY0xCzAJBgNVBAYTAkFUMUgwRgYDVQQKDD9BLVRy +dXN0IEdlcy4gZi4gU2ljaGVyaGVpdHNzeXN0ZW1lIGltIGVsZWt0ci4gRGF0ZW52 +ZXJrZWhyIEdtYkgxGTAXBgNVBAsMEEEtVHJ1c3QtblF1YWwtMDMxGTAXBgNVBAMM +EEEtVHJ1c3QtblF1YWwtMDMwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB +AQCtPWFuA/OQO8BBC4SAzewqo51ru27CQoT3URThoKgtUaNR8t4j8DRE/5TrzAUj +lUC5B3ilJfYKvUWG6Nm9wASOhURh73+nyfrBJcyFLGM/BWBzSQXgYHiVEEvc+RFZ +znF/QJuKqiTfC0Li21a8StKlDJu3Qz7dg9MmEALP6iPESU7l0+m0iKsMrmKS1GWH +2WrX9IWf5DMiJaXlyDO6w8dB3F/GaswADm0yqLaHNgBid5seHzTLkDx4iHQF63n1 +k3Flyp3HaxgtPVxO59X4PzF9j4fsCiIvI+n+u33J4PTs63zEsMMtYrWacdaxaujs +2e3Vcuy+VwHOBVWf3tFgiBCzAgMBAAGjNjA0MA8GA1UdEwEB/wQFMAMBAf8wEQYD +VR0OBAoECERqlWdVeRFPMA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQUFAAOC +AQEAVdRU0VlIXLOThaq/Yy/kgM40ozRiPvbY7meIMQQDbwvUB/tOdQ/TLtPAF8fG +KOwGDREkDg6lXb+MshOWcdzUzg4NCmgybLlBMRmrsQd7TZjTXLDR8KdCoLXEjq/+ +8T/0709GAHbrAvv5ndJAlseIOrifEXnzgGWovR/TeIGgUUw3tKZdJXDRZslo+S4R +FGjxVJgIrCaSD96JntT6s3kr0qN51OyLrIdTaEJMUVF0HhsnLuP1Hyl0Te2v9+GS +mYHovjrHF1D2t8b8m7CKa9aIA5GPBnc6hQLdmNVDeD/GMBWsm2vLV7eJUYs66MmE +DNuxUCAKGkq6ahq97BvIxYSazQ== +-----END CERTIFICATE----- + +# Issuer: CN=TWCA Root Certification Authority O=TAIWAN-CA OU=Root CA +# Subject: CN=TWCA Root Certification Authority O=TAIWAN-CA OU=Root CA +# Label: "TWCA Root Certification Authority" +# Serial: 1 +# MD5 Fingerprint: aa:08:8f:f6:f9:7b:b7:f2:b1:a7:1e:9b:ea:ea:bd:79 +# SHA1 Fingerprint: cf:9e:87:6d:d3:eb:fc:42:26:97:a3:b5:a3:7a:a0:76:a9:06:23:48 +# SHA256 Fingerprint: bf:d8:8f:e1:10:1c:41:ae:3e:80:1b:f8:be:56:35:0e:e9:ba:d1:a6:b9:bd:51:5e:dc:5c:6d:5b:87:11:ac:44 +-----BEGIN CERTIFICATE----- +MIIDezCCAmOgAwIBAgIBATANBgkqhkiG9w0BAQUFADBfMQswCQYDVQQGEwJUVzES +MBAGA1UECgwJVEFJV0FOLUNBMRAwDgYDVQQLDAdSb290IENBMSowKAYDVQQDDCFU +V0NBIFJvb3QgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMDgwODI4MDcyNDMz +WhcNMzAxMjMxMTU1OTU5WjBfMQswCQYDVQQGEwJUVzESMBAGA1UECgwJVEFJV0FO +LUNBMRAwDgYDVQQLDAdSb290IENBMSowKAYDVQQDDCFUV0NBIFJvb3QgQ2VydGlm +aWNhdGlvbiBBdXRob3JpdHkwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB +AQCwfnK4pAOU5qfeCTiRShFAh6d8WWQUe7UREN3+v9XAu1bihSX0NXIP+FPQQeFE +AcK0HMMxQhZHhTMidrIKbw/lJVBPhYa+v5guEGcevhEFhgWQxFnQfHgQsIBct+HH +K3XLfJ+utdGdIzdjp9xCoi2SBBtQwXu4PhvJVgSLL1KbralW6cH/ralYhzC2gfeX +RfwZVzsrb+RH9JlF/h3x+JejiB03HFyP4HYlmlD4oFT/RJB2I9IyxsOrBr/8+7/z +rX2SYgJbKdM1o5OaQ2RgXbL6Mv87BK9NQGr5x+PvI/1ry+UPizgN7gr8/g+YnzAx +3WxSZfmLgb4i4RxYA7qRG4kHAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNV +HRMBAf8EBTADAQH/MB0GA1UdDgQWBBRqOFsmjd6LWvJPelSDGRjjCDWmujANBgkq +hkiG9w0BAQUFAAOCAQEAPNV3PdrfibqHDAhUaiBQkr6wQT25JmSDCi/oQMCXKCeC +MErJk/9q56YAf4lCmtYR5VPOL8zy2gXE/uJQxDqGfczafhAJO5I1KlOy/usrBdls +XebQ79NqZp4VKIV66IIArB6nCWlWQtNoURi+VJq/REG6Sb4gumlc7rh3zc5sH62D +lhh9DrUUOYTxKOkto557HnpyWoOzeW/vtPzQCqVYT0bf+215WfKEIlKuD8z7fDvn +aspHYcN6+NOSBB+4IIThNlQWx0DeO4pz3N/GCUzf7Nr/1FNCocnyYh0igzyXxfkZ +YiesZSLX0zzG5Y6yU8xJzrww/nsOM5D77dIUkR8Hrw== +-----END CERTIFICATE----- + +# Issuer: O=SECOM Trust Systems CO.,LTD. OU=Security Communication RootCA2 +# Subject: O=SECOM Trust Systems CO.,LTD. OU=Security Communication RootCA2 +# Label: "Security Communication RootCA2" +# Serial: 0 +# MD5 Fingerprint: 6c:39:7d:a4:0e:55:59:b2:3f:d6:41:b1:12:50:de:43 +# SHA1 Fingerprint: 5f:3b:8c:f2:f8:10:b3:7d:78:b4:ce:ec:19:19:c3:73:34:b9:c7:74 +# SHA256 Fingerprint: 51:3b:2c:ec:b8:10:d4:cd:e5:dd:85:39:1a:df:c6:c2:dd:60:d8:7b:b7:36:d2:b5:21:48:4a:a4:7a:0e:be:f6 +-----BEGIN CERTIFICATE----- +MIIDdzCCAl+gAwIBAgIBADANBgkqhkiG9w0BAQsFADBdMQswCQYDVQQGEwJKUDEl +MCMGA1UEChMcU0VDT00gVHJ1c3QgU3lzdGVtcyBDTy4sTFRELjEnMCUGA1UECxMe +U2VjdXJpdHkgQ29tbXVuaWNhdGlvbiBSb290Q0EyMB4XDTA5MDUyOTA1MDAzOVoX +DTI5MDUyOTA1MDAzOVowXTELMAkGA1UEBhMCSlAxJTAjBgNVBAoTHFNFQ09NIFRy +dXN0IFN5c3RlbXMgQ08uLExURC4xJzAlBgNVBAsTHlNlY3VyaXR5IENvbW11bmlj +YXRpb24gUm9vdENBMjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANAV +OVKxUrO6xVmCxF1SrjpDZYBLx/KWvNs2l9amZIyoXvDjChz335c9S672XewhtUGr +zbl+dp+++T42NKA7wfYxEUV0kz1XgMX5iZnK5atq1LXaQZAQwdbWQonCv/Q4EpVM +VAX3NuRFg3sUZdbcDE3R3n4MqzvEFb46VqZab3ZpUql6ucjrappdUtAtCms1FgkQ +hNBqyjoGADdH5H5XTz+L62e4iKrFvlNVspHEfbmwhRkGeC7bYRr6hfVKkaHnFtWO +ojnflLhwHyg/i/xAXmODPIMqGplrz95Zajv8bxbXH/1KEOtOghY6rCcMU/Gt1SSw +awNQwS08Ft1ENCcadfsCAwEAAaNCMEAwHQYDVR0OBBYEFAqFqXdlBZh8QIH4D5cs +OPEK7DzPMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3 +DQEBCwUAA4IBAQBMOqNErLlFsceTfsgLCkLfZOoc7llsCLqJX2rKSpWeeo8HxdpF +coJxDjrSzG+ntKEju/Ykn8sX/oymzsLS28yN/HH8AynBbF0zX2S2ZTuJbxh2ePXc +okgfGT+Ok+vx+hfuzU7jBBJV1uXk3fs+BXziHV7Gp7yXT2g69ekuCkO2r1dcYmh8 +t/2jioSgrGK+KwmHNPBqAbubKVY8/gA3zyNs8U6qtnRGEmyR7jTV7JqR50S+kDFy +1UkC9gLl9B/rfNmWVan/7Ir5mUf/NVoCqgTLiluHcSmRvaS0eg29mvVXIwAHIRc/ +SjnRBUkLp7Y3gaVdjKozXoEofKd9J+sAro03 +-----END CERTIFICATE----- + +# Issuer: CN=Hellenic Academic and Research Institutions RootCA 2011 O=Hellenic Academic and Research Institutions Cert. Authority +# Subject: CN=Hellenic Academic and Research Institutions RootCA 2011 O=Hellenic Academic and Research Institutions Cert. Authority +# Label: "Hellenic Academic and Research Institutions RootCA 2011" +# Serial: 0 +# MD5 Fingerprint: 73:9f:4c:4b:73:5b:79:e9:fa:ba:1c:ef:6e:cb:d5:c9 +# SHA1 Fingerprint: fe:45:65:9b:79:03:5b:98:a1:61:b5:51:2e:ac:da:58:09:48:22:4d +# SHA256 Fingerprint: bc:10:4f:15:a4:8b:e7:09:dc:a5:42:a7:e1:d4:b9:df:6f:05:45:27:e8:02:ea:a9:2d:59:54:44:25:8a:fe:71 +-----BEGIN CERTIFICATE----- +MIIEMTCCAxmgAwIBAgIBADANBgkqhkiG9w0BAQUFADCBlTELMAkGA1UEBhMCR1Ix +RDBCBgNVBAoTO0hlbGxlbmljIEFjYWRlbWljIGFuZCBSZXNlYXJjaCBJbnN0aXR1 +dGlvbnMgQ2VydC4gQXV0aG9yaXR5MUAwPgYDVQQDEzdIZWxsZW5pYyBBY2FkZW1p +YyBhbmQgUmVzZWFyY2ggSW5zdGl0dXRpb25zIFJvb3RDQSAyMDExMB4XDTExMTIw +NjEzNDk1MloXDTMxMTIwMTEzNDk1MlowgZUxCzAJBgNVBAYTAkdSMUQwQgYDVQQK +EztIZWxsZW5pYyBBY2FkZW1pYyBhbmQgUmVzZWFyY2ggSW5zdGl0dXRpb25zIENl +cnQuIEF1dGhvcml0eTFAMD4GA1UEAxM3SGVsbGVuaWMgQWNhZGVtaWMgYW5kIFJl +c2VhcmNoIEluc3RpdHV0aW9ucyBSb290Q0EgMjAxMTCCASIwDQYJKoZIhvcNAQEB +BQADggEPADCCAQoCggEBAKlTAOMupvaO+mDYLZU++CwqVE7NuYRhlFhPjz2L5EPz +dYmNUeTDN9KKiE15HrcS3UN4SoqS5tdI1Q+kOilENbgH9mgdVc04UfCMJDGFr4PJ +fel3r+0ae50X+bOdOFAPplp5kYCvN66m0zH7tSYJnTxa71HFK9+WXesyHgLacEns +bgzImjeN9/E2YEsmLIKe0HjzDQ9jpFEw4fkrJxIH2Oq9GGKYsFk3fb7u8yBRQlqD +75O6aRXxYp2fmTmCobd0LovUxQt7L/DICto9eQqakxylKHJzkUOap9FNhYS5qXSP +FEDH3N6sQWRstBmbAmNtJGSPRLIl6s5ddAxjMlyNh+UCAwEAAaOBiTCBhjAPBgNV +HRMBAf8EBTADAQH/MAsGA1UdDwQEAwIBBjAdBgNVHQ4EFgQUppFC/RNhSiOeCKQp +5dgTBCPuQSUwRwYDVR0eBEAwPqA8MAWCAy5ncjAFggMuZXUwBoIELmVkdTAGggQu +b3JnMAWBAy5ncjAFgQMuZXUwBoEELmVkdTAGgQQub3JnMA0GCSqGSIb3DQEBBQUA +A4IBAQAf73lB4XtuP7KMhjdCSk4cNx6NZrokgclPEg8hwAOXhiVtXdMiKahsog2p +6z0GW5k6x8zDmjR/qw7IThzh+uTczQ2+vyT+bOdrwg3IBp5OjWEopmr95fZi6hg8 +TqBTnbI6nOulnJEWtk2C4AwFSKls9cz4y51JtPACpf1wA+2KIaWuE4ZJwzNzvoc7 +dIsXRSZMFpGD/md9zU1jZ/rzAxKWeAaNsWftjj++n08C9bMJL/NMh98qy5V8Acys +Nnq/onN694/BtZqhFLKPM58N7yLcZnuEvUUXBj08yrl3NI/K6s8/MT7jiOOASSXI +l7WdmplNsDz4SgCbZN2fOUvRJ9e4 +-----END CERTIFICATE----- + +# Issuer: CN=Actalis Authentication Root CA O=Actalis S.p.A./03358520967 +# Subject: CN=Actalis Authentication Root CA O=Actalis S.p.A./03358520967 +# Label: "Actalis Authentication Root CA" +# Serial: 6271844772424770508 +# MD5 Fingerprint: 69:c1:0d:4f:07:a3:1b:c3:fe:56:3d:04:bc:11:f6:a6 +# SHA1 Fingerprint: f3:73:b3:87:06:5a:28:84:8a:f2:f3:4a:ce:19:2b:dd:c7:8e:9c:ac +# SHA256 Fingerprint: 55:92:60:84:ec:96:3a:64:b9:6e:2a:be:01:ce:0b:a8:6a:64:fb:fe:bc:c7:aa:b5:af:c1:55:b3:7f:d7:60:66 +-----BEGIN CERTIFICATE----- +MIIFuzCCA6OgAwIBAgIIVwoRl0LE48wwDQYJKoZIhvcNAQELBQAwazELMAkGA1UE +BhMCSVQxDjAMBgNVBAcMBU1pbGFuMSMwIQYDVQQKDBpBY3RhbGlzIFMucC5BLi8w +MzM1ODUyMDk2NzEnMCUGA1UEAwweQWN0YWxpcyBBdXRoZW50aWNhdGlvbiBSb290 +IENBMB4XDTExMDkyMjExMjIwMloXDTMwMDkyMjExMjIwMlowazELMAkGA1UEBhMC +SVQxDjAMBgNVBAcMBU1pbGFuMSMwIQYDVQQKDBpBY3RhbGlzIFMucC5BLi8wMzM1 +ODUyMDk2NzEnMCUGA1UEAwweQWN0YWxpcyBBdXRoZW50aWNhdGlvbiBSb290IENB +MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAp8bEpSmkLO/lGMWwUKNv +UTufClrJwkg4CsIcoBh/kbWHuUA/3R1oHwiD1S0eiKD4j1aPbZkCkpAW1V8IbInX +4ay8IMKx4INRimlNAJZaby/ARH6jDuSRzVju3PvHHkVH3Se5CAGfpiEd9UEtL0z9 +KK3giq0itFZljoZUj5NDKd45RnijMCO6zfB9E1fAXdKDa0hMxKufgFpbOr3JpyI/ +gCczWw63igxdBzcIy2zSekciRDXFzMwujt0q7bd9Zg1fYVEiVRvjRuPjPdA1Yprb +rxTIW6HMiRvhMCb8oJsfgadHHwTrozmSBp+Z07/T6k9QnBn+locePGX2oxgkg4YQ +51Q+qDp2JE+BIcXjDwL4k5RHILv+1A7TaLndxHqEguNTVHnd25zS8gebLra8Pu2F +be8lEfKXGkJh90qX6IuxEAf6ZYGyojnP9zz/GPvG8VqLWeICrHuS0E4UT1lF9gxe +KF+w6D9Fz8+vm2/7hNN3WpVvrJSEnu68wEqPSpP4RCHiMUVhUE4Q2OM1fEwZtN4F +v6MGn8i1zeQf1xcGDXqVdFUNaBr8EBtiZJ1t4JWgw5QHVw0U5r0F+7if5t+L4sbn +fpb2U8WANFAoWPASUHEXMLrmeGO89LKtmyuy/uE5jF66CyCU3nuDuP/jVo23Eek7 +jPKxwV2dpAtMK9myGPW1n0sCAwEAAaNjMGEwHQYDVR0OBBYEFFLYiDrIn3hm7Ynz +ezhwlMkCAjbQMA8GA1UdEwEB/wQFMAMBAf8wHwYDVR0jBBgwFoAUUtiIOsifeGbt +ifN7OHCUyQICNtAwDgYDVR0PAQH/BAQDAgEGMA0GCSqGSIb3DQEBCwUAA4ICAQAL +e3KHwGCmSUyIWOYdiPcUZEim2FgKDk8TNd81HdTtBjHIgT5q1d07GjLukD0R0i70 +jsNjLiNmsGe+b7bAEzlgqqI0JZN1Ut6nna0Oh4lScWoWPBkdg/iaKWW+9D+a2fDz +WochcYBNy+A4mz+7+uAwTc+G02UQGRjRlwKxK3JCaKygvU5a2hi/a5iB0P2avl4V +SM0RFbnAKVy06Ij3Pjaut2L9HmLecHgQHEhb2rykOLpn7VU+Xlff1ANATIGk0k9j +pwlCCRT8AKnCgHNPLsBA2RF7SOp6AsDT6ygBJlh0wcBzIm2Tlf05fbsq4/aC4yyX +X04fkZT6/iyj2HYauE2yOE+b+h1IYHkm4vP9qdCa6HCPSXrW5b0KDtst842/6+Ok +fcvHlXHo2qN8xcL4dJIEG4aspCJTQLas/kx2z/uUMsA1n3Y/buWQbqCmJqK4LL7R +K4X9p2jIugErsWx0Hbhzlefut8cl8ABMALJ+tguLHPPAUJ4lueAI3jZm/zel0btU +ZCzJJ7VLkn5l/9Mt4blOvH+kQSGQQXemOR/qnuOf0GZvBeyqdn6/axag67XH/JJU +LysRJyU3eExRarDzzFhdFPFqSBX/wge2sY0PjlxQRrM9vwGYT7JZVEc+NHt4bVaT +LnPqZih4zR0Uv6CPLy64Lo7yFIrM6bV8+2ydDKXhlg== +-----END CERTIFICATE----- + +# Issuer: O=Trustis Limited OU=Trustis FPS Root CA +# Subject: O=Trustis Limited OU=Trustis FPS Root CA +# Label: "Trustis FPS Root CA" +# Serial: 36053640375399034304724988975563710553 +# MD5 Fingerprint: 30:c9:e7:1e:6b:e6:14:eb:65:b2:16:69:20:31:67:4d +# SHA1 Fingerprint: 3b:c0:38:0b:33:c3:f6:a6:0c:86:15:22:93:d9:df:f5:4b:81:c0:04 +# SHA256 Fingerprint: c1:b4:82:99:ab:a5:20:8f:e9:63:0a:ce:55:ca:68:a0:3e:da:5a:51:9c:88:02:a0:d3:a6:73:be:8f:8e:55:7d +-----BEGIN CERTIFICATE----- +MIIDZzCCAk+gAwIBAgIQGx+ttiD5JNM2a/fH8YygWTANBgkqhkiG9w0BAQUFADBF +MQswCQYDVQQGEwJHQjEYMBYGA1UEChMPVHJ1c3RpcyBMaW1pdGVkMRwwGgYDVQQL +ExNUcnVzdGlzIEZQUyBSb290IENBMB4XDTAzMTIyMzEyMTQwNloXDTI0MDEyMTEx +MzY1NFowRTELMAkGA1UEBhMCR0IxGDAWBgNVBAoTD1RydXN0aXMgTGltaXRlZDEc +MBoGA1UECxMTVHJ1c3RpcyBGUFMgUm9vdCBDQTCCASIwDQYJKoZIhvcNAQEBBQAD +ggEPADCCAQoCggEBAMVQe547NdDfxIzNjpvto8A2mfRC6qc+gIMPpqdZh8mQRUN+ +AOqGeSoDvT03mYlmt+WKVoaTnGhLaASMk5MCPjDSNzoiYYkchU59j9WvezX2fihH +iTHcDnlkH5nSW7r+f2C/revnPDgpai/lkQtV/+xvWNUtyd5MZnGPDNcE2gfmHhjj +vSkCqPoc4Vu5g6hBSLwacY3nYuUtsuvffM/bq1rKMfFMIvMFE/eC+XN5DL7XSxzA +0RU8k0Fk0ea+IxciAIleH2ulrG6nS4zto3Lmr2NNL4XSFDWaLk6M6jKYKIahkQlB +OrTh4/L68MkKokHdqeMDx4gVOxzUGpTXn2RZEm0CAwEAAaNTMFEwDwYDVR0TAQH/ +BAUwAwEB/zAfBgNVHSMEGDAWgBS6+nEleYtXQSUhhgtx67JkDoshZzAdBgNVHQ4E +FgQUuvpxJXmLV0ElIYYLceuyZA6LIWcwDQYJKoZIhvcNAQEFBQADggEBAH5Y//01 +GX2cGE+esCu8jowU/yyg2kdbw++BLa8F6nRIW/M+TgfHbcWzk88iNVy2P3UnXwmW +zaD+vkAMXBJV+JOCyinpXj9WV4s4NvdFGkwozZ5BuO1WTISkQMi4sKUraXAEasP4 +1BIy+Q7DsdwyhEQsb8tGD+pmQQ9P8Vilpg0ND2HepZ5dfWWhPBfnqFVO76DH7cZE +f1T1o+CP8HxVIo8ptoGj4W1OLBuAZ+ytIJ8MYmHVl/9D7S3B2l0pKoU/rGXuhg8F +jZBf3+6f9L/uHfuY5H+QK4R4EA5sSVPvFVtlRkpdr7r7OnIdzfYliB6XzCGcKQEN +ZetX2fNXlrtIzYE= +-----END CERTIFICATE----- + +# Issuer: CN=StartCom Certification Authority O=StartCom Ltd. OU=Secure Digital Certificate Signing +# Subject: CN=StartCom Certification Authority O=StartCom Ltd. OU=Secure Digital Certificate Signing +# Label: "StartCom Certification Authority" +# Serial: 45 +# MD5 Fingerprint: c9:3b:0d:84:41:fc:a4:76:79:23:08:57:de:10:19:16 +# SHA1 Fingerprint: a3:f1:33:3f:e2:42:bf:cf:c5:d1:4e:8f:39:42:98:40:68:10:d1:a0 +# SHA256 Fingerprint: e1:78:90:ee:09:a3:fb:f4:f4:8b:9c:41:4a:17:d6:37:b7:a5:06:47:e9:bc:75:23:22:72:7f:cc:17:42:a9:11 +-----BEGIN CERTIFICATE----- +MIIHhzCCBW+gAwIBAgIBLTANBgkqhkiG9w0BAQsFADB9MQswCQYDVQQGEwJJTDEW +MBQGA1UEChMNU3RhcnRDb20gTHRkLjErMCkGA1UECxMiU2VjdXJlIERpZ2l0YWwg +Q2VydGlmaWNhdGUgU2lnbmluZzEpMCcGA1UEAxMgU3RhcnRDb20gQ2VydGlmaWNh +dGlvbiBBdXRob3JpdHkwHhcNMDYwOTE3MTk0NjM3WhcNMzYwOTE3MTk0NjM2WjB9 +MQswCQYDVQQGEwJJTDEWMBQGA1UEChMNU3RhcnRDb20gTHRkLjErMCkGA1UECxMi +U2VjdXJlIERpZ2l0YWwgQ2VydGlmaWNhdGUgU2lnbmluZzEpMCcGA1UEAxMgU3Rh +cnRDb20gQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwggIiMA0GCSqGSIb3DQEBAQUA +A4ICDwAwggIKAoICAQDBiNsJvGxGfHiflXu1M5DycmLWwTYgIiRezul38kMKogZk +pMyONvg45iPwbm2xPN1yo4UcodM9tDMr0y+v/uqwQVlntsQGfQqedIXWeUyAN3rf +OQVSWff0G0ZDpNKFhdLDcfN1YjS6LIp/Ho/u7TTQEceWzVI9ujPW3U3eCztKS5/C +Ji/6tRYccjV3yjxd5srhJosaNnZcAdt0FCX+7bWgiA/deMotHweXMAEtcnn6RtYT +Kqi5pquDSR3l8u/d5AGOGAqPY1MWhWKpDhk6zLVmpsJrdAfkK+F2PrRt2PZE4XNi +HzvEvqBTViVsUQn3qqvKv3b9bZvzndu/PWa8DFaqr5hIlTpL36dYUNk4dalb6kMM +Av+Z6+hsTXBbKWWc3apdzK8BMewM69KN6Oqce+Zu9ydmDBpI125C4z/eIT574Q1w ++2OqqGwaVLRcJXrJosmLFqa7LH4XXgVNWG4SHQHuEhANxjJ/GP/89PrNbpHoNkm+ +Gkhpi8KWTRoSsmkXwQqQ1vp5Iki/untp+HDH+no32NgN0nZPV/+Qt+OR0t3vwmC3 +Zzrd/qqc8NSLf3Iizsafl7b4r4qgEKjZ+xjGtrVcUjyJthkqcwEKDwOzEmDyei+B +26Nu/yYwl/WL3YlXtq09s68rxbd2AvCl1iuahhQqcvbjM4xdCUsT37uMdBNSSwID +AQABo4ICEDCCAgwwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYD +VR0OBBYEFE4L7xqkQFulF2mHMMo0aEPQQa7yMB8GA1UdIwQYMBaAFE4L7xqkQFul +F2mHMMo0aEPQQa7yMIIBWgYDVR0gBIIBUTCCAU0wggFJBgsrBgEEAYG1NwEBATCC +ATgwLgYIKwYBBQUHAgEWImh0dHA6Ly93d3cuc3RhcnRzc2wuY29tL3BvbGljeS5w +ZGYwNAYIKwYBBQUHAgEWKGh0dHA6Ly93d3cuc3RhcnRzc2wuY29tL2ludGVybWVk +aWF0ZS5wZGYwgc8GCCsGAQUFBwICMIHCMCcWIFN0YXJ0IENvbW1lcmNpYWwgKFN0 +YXJ0Q29tKSBMdGQuMAMCAQEagZZMaW1pdGVkIExpYWJpbGl0eSwgcmVhZCB0aGUg +c2VjdGlvbiAqTGVnYWwgTGltaXRhdGlvbnMqIG9mIHRoZSBTdGFydENvbSBDZXJ0 +aWZpY2F0aW9uIEF1dGhvcml0eSBQb2xpY3kgYXZhaWxhYmxlIGF0IGh0dHA6Ly93 +d3cuc3RhcnRzc2wuY29tL3BvbGljeS5wZGYwEQYJYIZIAYb4QgEBBAQDAgAHMDgG +CWCGSAGG+EIBDQQrFilTdGFydENvbSBGcmVlIFNTTCBDZXJ0aWZpY2F0aW9uIEF1 +dGhvcml0eTANBgkqhkiG9w0BAQsFAAOCAgEAjo/n3JR5fPGFf59Jb2vKXfuM/gTF +wWLRfUKKvFO3lANmMD+x5wqnUCBVJX92ehQN6wQOQOY+2IirByeDqXWmN3PH/UvS +Ta0XQMhGvjt/UfzDtgUx3M2FIk5xt/JxXrAaxrqTi3iSSoX4eA+D/i+tLPfkpLst +0OcNOrg+zvZ49q5HJMqjNTbOx8aHmNrs++myziebiMMEofYLWWivydsQD032ZGNc +pRJvkrKTlMeIFw6Ttn5ii5B/q06f/ON1FE8qMt9bDeD1e5MNq6HPh+GlBEXoPBKl +CcWw0bdT82AUuoVpaiF8H3VhFyAXe2w7QSlc4axa0c2Mm+tgHRns9+Ww2vl5GKVF +P0lDV9LdJNUso/2RjSe15esUBppMeyG7Oq0wBhjA2MFrLH9ZXF2RsXAiV+uKa0hK +1Q8p7MZAwC+ITGgBF3f0JBlPvfrhsiAhS90a2Cl9qrjeVOwhVYBsHvUwyKMQ5bLm +KhQxw4UtjJixhlpPiVktucf3HMiKf8CdBUrmQk9io20ppB+Fq9vlgcitKj1MXVuE +JnHEhV5xJMqlG2zYYdMa4FTbzrqpMrUi9nNBCV24F10OD5mQ1kfabwo6YigUZ4LZ +8dCAWZvLMdibD4x3TrVoivJs9iQOLWxwxXPR3hTQcY+203sC9uO41Alua551hDnm +fyWl8kgAwKQB2j8= +-----END CERTIFICATE----- + +# Issuer: CN=StartCom Certification Authority G2 O=StartCom Ltd. +# Subject: CN=StartCom Certification Authority G2 O=StartCom Ltd. +# Label: "StartCom Certification Authority G2" +# Serial: 59 +# MD5 Fingerprint: 78:4b:fb:9e:64:82:0a:d3:b8:4c:62:f3:64:f2:90:64 +# SHA1 Fingerprint: 31:f1:fd:68:22:63:20:ee:c6:3b:3f:9d:ea:4a:3e:53:7c:7c:39:17 +# SHA256 Fingerprint: c7:ba:65:67:de:93:a7:98:ae:1f:aa:79:1e:71:2d:37:8f:ae:1f:93:c4:39:7f:ea:44:1b:b7:cb:e6:fd:59:95 +-----BEGIN CERTIFICATE----- +MIIFYzCCA0ugAwIBAgIBOzANBgkqhkiG9w0BAQsFADBTMQswCQYDVQQGEwJJTDEW +MBQGA1UEChMNU3RhcnRDb20gTHRkLjEsMCoGA1UEAxMjU3RhcnRDb20gQ2VydGlm +aWNhdGlvbiBBdXRob3JpdHkgRzIwHhcNMTAwMTAxMDEwMDAxWhcNMzkxMjMxMjM1 +OTAxWjBTMQswCQYDVQQGEwJJTDEWMBQGA1UEChMNU3RhcnRDb20gTHRkLjEsMCoG +A1UEAxMjU3RhcnRDb20gQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgRzIwggIiMA0G +CSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQC2iTZbB7cgNr2Cu+EWIAOVeq8Oo1XJ +JZlKxdBWQYeQTSFgpBSHO839sj60ZwNq7eEPS8CRhXBF4EKe3ikj1AENoBB5uNsD +vfOpL9HG4A/LnooUCri99lZi8cVytjIl2bLzvWXFDSxu1ZJvGIsAQRSCb0AgJnoo +D/Uefyf3lLE3PbfHkffiAez9lInhzG7TNtYKGXmu1zSCZf98Qru23QumNK9LYP5/ +Q0kGi4xDuFby2X8hQxfqp0iVAXV16iulQ5XqFYSdCI0mblWbq9zSOdIxHWDirMxW +RST1HFSr7obdljKF+ExP6JV2tgXdNiNnvP8V4so75qbsO+wmETRIjfaAKxojAuuK +HDp2KntWFhxyKrOq42ClAJ8Em+JvHhRYW6Vsi1g8w7pOOlz34ZYrPu8HvKTlXcxN +nw3h3Kq74W4a7I/htkxNeXJdFzULHdfBR9qWJODQcqhaX2YtENwvKhOuJv4KHBnM +0D4LnMgJLvlblnpHnOl68wVQdJVznjAJ85eCXuaPOQgeWeU1FEIT/wCc976qUM/i +UUjXuG+v+E5+M5iSFGI6dWPPe/regjupuznixL0sAA7IF6wT700ljtizkC+p2il9 +Ha90OrInwMEePnWjFqmveiJdnxMaz6eg6+OGCtP95paV1yPIN93EfKo2rJgaErHg +TuixO/XWb/Ew1wIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQE +AwIBBjAdBgNVHQ4EFgQUS8W0QGutHLOlHGVuRjaJhwUMDrYwDQYJKoZIhvcNAQEL +BQADggIBAHNXPyzVlTJ+N9uWkusZXn5T50HsEbZH77Xe7XRcxfGOSeD8bpkTzZ+K +2s06Ctg6Wgk/XzTQLwPSZh0avZyQN8gMjgdalEVGKua+etqhqaRpEpKwfTbURIfX +UfEpY9Z1zRbkJ4kd+MIySP3bmdCPX1R0zKxnNBFi2QwKN4fRoxdIjtIXHfbX/dtl +6/2o1PXWT6RbdejF0mCy2wl+JYt7ulKSnj7oxXehPOBKc2thz4bcQ///If4jXSRK +9dNtD2IEBVeC2m6kMyV5Sy5UGYvMLD0w6dEG/+gyRr61M3Z3qAFdlsHB1b6uJcDJ +HgoJIIihDsnzb02CVAAgp9KP5DlUFy6NHrgbuxu9mk47EDTcnIhT76IxW1hPkWLI +wpqazRVdOKnWvvgTtZ8SafJQYqz7Fzf07rh1Z2AQ+4NQ+US1dZxAF7L+/XldblhY +XzD8AK6vM8EOTmy6p6ahfzLbOOCxchcKK5HsamMm7YnUeMx0HgX4a/6ManY5Ka5l +IxKVCCIcl85bBu4M4ru8H0ST9tg4RQUh7eStqxK2A6RCLi3ECToDZ2mEmuFZkIoo +hdVddLHRDiBYmxOlsGOm7XtH/UVVMKTumtTm4ofvmMkyghEpIrwACjFeLQ/Ajulr +so8uBtjRkcfGEvRM/TAXw8HaOFvjqermobp573PYtlNXLfbQ4ddI +-----END CERTIFICATE----- + +# Issuer: CN=Buypass Class 2 Root CA O=Buypass AS-983163327 +# Subject: CN=Buypass Class 2 Root CA O=Buypass AS-983163327 +# Label: "Buypass Class 2 Root CA" +# Serial: 2 +# MD5 Fingerprint: 46:a7:d2:fe:45:fb:64:5a:a8:59:90:9b:78:44:9b:29 +# SHA1 Fingerprint: 49:0a:75:74:de:87:0a:47:fe:58:ee:f6:c7:6b:eb:c6:0b:12:40:99 +# SHA256 Fingerprint: 9a:11:40:25:19:7c:5b:b9:5d:94:e6:3d:55:cd:43:79:08:47:b6:46:b2:3c:df:11:ad:a4:a0:0e:ff:15:fb:48 +-----BEGIN CERTIFICATE----- +MIIFWTCCA0GgAwIBAgIBAjANBgkqhkiG9w0BAQsFADBOMQswCQYDVQQGEwJOTzEd +MBsGA1UECgwUQnV5cGFzcyBBUy05ODMxNjMzMjcxIDAeBgNVBAMMF0J1eXBhc3Mg +Q2xhc3MgMiBSb290IENBMB4XDTEwMTAyNjA4MzgwM1oXDTQwMTAyNjA4MzgwM1ow +TjELMAkGA1UEBhMCTk8xHTAbBgNVBAoMFEJ1eXBhc3MgQVMtOTgzMTYzMzI3MSAw +HgYDVQQDDBdCdXlwYXNzIENsYXNzIDIgUm9vdCBDQTCCAiIwDQYJKoZIhvcNAQEB +BQADggIPADCCAgoCggIBANfHXvfBB9R3+0Mh9PT1aeTuMgHbo4Yf5FkNuud1g1Lr +6hxhFUi7HQfKjK6w3Jad6sNgkoaCKHOcVgb/S2TwDCo3SbXlzwx87vFKu3MwZfPV +L4O2fuPn9Z6rYPnT8Z2SdIrkHJasW4DptfQxh6NR/Md+oW+OU3fUl8FVM5I+GC91 +1K2GScuVr1QGbNgGE41b/+EmGVnAJLqBcXmQRFBoJJRfuLMR8SlBYaNByyM21cHx +MlAQTn/0hpPshNOOvEu/XAFOBz3cFIqUCqTqc/sLUegTBxj6DvEr0VQVfTzh97QZ +QmdiXnfgolXsttlpF9U6r0TtSsWe5HonfOV116rLJeffawrbD02TTqigzXsu8lkB +arcNuAeBfos4GzjmCleZPe4h6KP1DBbdi+w0jpwqHAAVF41og9JwnxgIzRFo1clr +Us3ERo/ctfPYV3Me6ZQ5BL/T3jjetFPsaRyifsSP5BtwrfKi+fv3FmRmaZ9JUaLi +FRhnBkp/1Wy1TbMz4GHrXb7pmA8y1x1LPC5aAVKRCfLf6o3YBkBjqhHk/sM3nhRS +P/TizPJhk9H9Z2vXUq6/aKtAQ6BXNVN48FP4YUIHZMbXb5tMOA1jrGKvNouicwoN +9SG9dKpN6nIDSdvHXx1iY8f93ZHsM+71bbRuMGjeyNYmsHVee7QHIJihdjK4TWxP +AgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFMmAd+BikoL1Rpzz +uvdMw964o605MA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQsFAAOCAgEAU18h +9bqwOlI5LJKwbADJ784g7wbylp7ppHR/ehb8t/W2+xUbP6umwHJdELFx7rxP462s +A20ucS6vxOOto70MEae0/0qyexAQH6dXQbLArvQsWdZHEIjzIVEpMMpghq9Gqx3t +OluwlN5E40EIosHsHdb9T7bWR9AUC8rmyrV7d35BH16Dx7aMOZawP5aBQW9gkOLo ++fsicdl9sz1Gv7SEr5AcD48Saq/v7h56rgJKihcrdv6sVIkkLE8/trKnToyokZf7 +KcZ7XC25y2a2t6hbElGFtQl+Ynhw/qlqYLYdDnkM/crqJIByw5c/8nerQyIKx+u2 +DISCLIBrQYoIwOula9+ZEsuK1V6ADJHgJgg2SMX6OBE1/yWDLfJ6v9r9jv6ly0Us +H8SIU653DtmadsWOLB2jutXsMq7Aqqz30XpN69QH4kj3Io6wpJ9qzo6ysmD0oyLQ +I+uUWnpp3Q+/QFesa1lQ2aOZ4W7+jQF5JyMV3pKdewlNWudLSDBaGOYKbeaP4NK7 +5t98biGCwWg5TbSYWGZizEqQXsP6JwSxeRV0mcy+rSDeJmAc61ZRpqPq5KM/p/9h +3PFaTWwyI0PurKju7koSCTxdccK+efrCh2gdC/1cacwG0Jp9VJkqyTkaGa9LKkPz +Y11aWOIv4x3kqdbQCtCev9eBCfHJxyYNrJgWVqA= +-----END CERTIFICATE----- + +# Issuer: CN=Buypass Class 3 Root CA O=Buypass AS-983163327 +# Subject: CN=Buypass Class 3 Root CA O=Buypass AS-983163327 +# Label: "Buypass Class 3 Root CA" +# Serial: 2 +# MD5 Fingerprint: 3d:3b:18:9e:2c:64:5a:e8:d5:88:ce:0e:f9:37:c2:ec +# SHA1 Fingerprint: da:fa:f7:fa:66:84:ec:06:8f:14:50:bd:c7:c2:81:a5:bc:a9:64:57 +# SHA256 Fingerprint: ed:f7:eb:bc:a2:7a:2a:38:4d:38:7b:7d:40:10:c6:66:e2:ed:b4:84:3e:4c:29:b4:ae:1d:5b:93:32:e6:b2:4d +-----BEGIN CERTIFICATE----- +MIIFWTCCA0GgAwIBAgIBAjANBgkqhkiG9w0BAQsFADBOMQswCQYDVQQGEwJOTzEd +MBsGA1UECgwUQnV5cGFzcyBBUy05ODMxNjMzMjcxIDAeBgNVBAMMF0J1eXBhc3Mg +Q2xhc3MgMyBSb290IENBMB4XDTEwMTAyNjA4Mjg1OFoXDTQwMTAyNjA4Mjg1OFow +TjELMAkGA1UEBhMCTk8xHTAbBgNVBAoMFEJ1eXBhc3MgQVMtOTgzMTYzMzI3MSAw +HgYDVQQDDBdCdXlwYXNzIENsYXNzIDMgUm9vdCBDQTCCAiIwDQYJKoZIhvcNAQEB +BQADggIPADCCAgoCggIBAKXaCpUWUOOV8l6ddjEGMnqb8RB2uACatVI2zSRHsJ8Y +ZLya9vrVediQYkwiL944PdbgqOkcLNt4EemOaFEVcsfzM4fkoF0LXOBXByow9c3E +N3coTRiR5r/VUv1xLXA+58bEiuPwKAv0dpihi4dVsjoT/Lc+JzeOIuOoTyrvYLs9 +tznDDgFHmV0ST9tD+leh7fmdvhFHJlsTmKtdFoqwNxxXnUX/iJY2v7vKB3tvh2PX +0DJq1l1sDPGzbjniazEuOQAnFN44wOwZZoYS6J1yFhNkUsepNxz9gjDthBgd9K5c +/3ATAOux9TN6S9ZV+AWNS2mw9bMoNlwUxFFzTWsL8TQH2xc519woe2v1n/MuwU8X +KhDzzMro6/1rqy6any2CbgTUUgGTLT2G/H783+9CHaZr77kgxve9oKeV/afmiSTY +zIw0bOIjL9kSGiG5VZFvC5F5GQytQIgLcOJ60g7YaEi7ghM5EFjp2CoHxhLbWNvS +O1UQRwUVZ2J+GGOmRj8JDlQyXr8NYnon74Do29lLBlo3WiXQCBJ31G8JUJc9yB3D +34xFMFbG02SrZvPAXpacw8Tvw3xrizp5f7NJzz3iiZ+gMEuFuZyUJHmPfWupRWgP +K9Dx2hzLabjKSWJtyNBjYt1gD1iqj6G8BaVmos8bdrKEZLFMOVLAMLrwjEsCsLa3 +AgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFEe4zf/lb+74suwv +Tg75JbCOPGvDMA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQsFAAOCAgEAACAj +QTUEkMJAYmDv4jVM1z+s4jSQuKFvdvoWFqRINyzpkMLyPPgKn9iB5btb2iUspKdV +cSQy9sgL8rxq+JOssgfCX5/bzMiKqr5qb+FJEMwx14C7u8jYog5kV+qi9cKpMRXS +IGrs/CIBKM+GuIAeqcwRpTzyFrNHnfzSgCHEy9BHcEGhyoMZCCxt8l13nIoUE9Q2 +HJLw5QY33KbmkJs4j1xrG0aGQ0JfPgEHU1RdZX33inOhmlRaHylDFCfChQ+1iHsa +O5S3HWCntZznKWlXWpuTekMwGwPXYshApqr8ZORK15FTAaggiG6cX0S5y2CBNOxv +033aSF/rtJC8LakcC6wc1aJoIIAE1vyxjy+7SjENSoYc6+I2KSb12tjE8nVhz36u +dmNKekBlk4f4HoCMhuWG1o8O/FMsYOgWYRqiPkN7zTlgVGr18okmAWiDSKIz6MkE +kbIRNBE+6tBDGR8Dk5AM/1E9V/RBbuHLoL7ryWPNbczk+DaqaJ3tvV2XcEQNtg41 +3OEMXbugUZTLfhbrES+jkkXITHHZvMmZUldGL1DPvTVp9D0VzgalLA8+9oG6lLvD +u79leNKGef9JOxqDDPDeeOzI8k1MGt6CKfjBWtrt7uYnXuhF0J0cUahoq0Tj0Itq +4/g7u9xN12TyUb7mqqta6THuBrxzvxNiCp/HuZc= +-----END CERTIFICATE----- + +# Issuer: CN=T-TeleSec GlobalRoot Class 3 O=T-Systems Enterprise Services GmbH OU=T-Systems Trust Center +# Subject: CN=T-TeleSec GlobalRoot Class 3 O=T-Systems Enterprise Services GmbH OU=T-Systems Trust Center +# Label: "T-TeleSec GlobalRoot Class 3" +# Serial: 1 +# MD5 Fingerprint: ca:fb:40:a8:4e:39:92:8a:1d:fe:8e:2f:c4:27:ea:ef +# SHA1 Fingerprint: 55:a6:72:3e:cb:f2:ec:cd:c3:23:74:70:19:9d:2a:be:11:e3:81:d1 +# SHA256 Fingerprint: fd:73:da:d3:1c:64:4f:f1:b4:3b:ef:0c:cd:da:96:71:0b:9c:d9:87:5e:ca:7e:31:70:7a:f3:e9:6d:52:2b:bd +-----BEGIN CERTIFICATE----- +MIIDwzCCAqugAwIBAgIBATANBgkqhkiG9w0BAQsFADCBgjELMAkGA1UEBhMCREUx +KzApBgNVBAoMIlQtU3lzdGVtcyBFbnRlcnByaXNlIFNlcnZpY2VzIEdtYkgxHzAd +BgNVBAsMFlQtU3lzdGVtcyBUcnVzdCBDZW50ZXIxJTAjBgNVBAMMHFQtVGVsZVNl +YyBHbG9iYWxSb290IENsYXNzIDMwHhcNMDgxMDAxMTAyOTU2WhcNMzMxMDAxMjM1 +OTU5WjCBgjELMAkGA1UEBhMCREUxKzApBgNVBAoMIlQtU3lzdGVtcyBFbnRlcnBy +aXNlIFNlcnZpY2VzIEdtYkgxHzAdBgNVBAsMFlQtU3lzdGVtcyBUcnVzdCBDZW50 +ZXIxJTAjBgNVBAMMHFQtVGVsZVNlYyBHbG9iYWxSb290IENsYXNzIDMwggEiMA0G +CSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC9dZPwYiJvJK7genasfb3ZJNW4t/zN +8ELg63iIVl6bmlQdTQyK9tPPcPRStdiTBONGhnFBSivwKixVA9ZIw+A5OO3yXDw/ +RLyTPWGrTs0NvvAgJ1gORH8EGoel15YUNpDQSXuhdfsaa3Ox+M6pCSzyU9XDFES4 +hqX2iys52qMzVNn6chr3IhUciJFrf2blw2qAsCTz34ZFiP0Zf3WHHx+xGwpzJFu5 +ZeAsVMhg02YXP+HMVDNzkQI6pn97djmiH5a2OK61yJN0HZ65tOVgnS9W0eDrXltM +EnAMbEQgqxHY9Bn20pxSN+f6tsIxO0rUFJmtxxr1XV/6B7h8DR/Wgx6zAgMBAAGj +QjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBS1 +A/d2O2GCahKqGFPrAyGUv/7OyjANBgkqhkiG9w0BAQsFAAOCAQEAVj3vlNW92nOy +WL6ukK2YJ5f+AbGwUgC4TeQbIXQbfsDuXmkqJa9c1h3a0nnJ85cp4IaH3gRZD/FZ +1GSFS5mvJQQeyUapl96Cshtwn5z2r3Ex3XsFpSzTucpH9sry9uetuUg/vBa3wW30 +6gmv7PO15wWeph6KU1HWk4HMdJP2udqmJQV0eVp+QD6CSyYRMG7hP0HHRwA11fXT +91Q+gT3aSWqas+8QPebrb9HIIkfLzM8BMZLZGOMivgkeGj5asuRrDFR6fUNOuIml +e9eiPZaGzPImNC1qkp2aGtAw4l1OBLBfiyB+d8E9lYLRRpo7PHi4b6HQDWSieB4p +TpPDpFQUWw== +-----END CERTIFICATE----- + +# Issuer: CN=EE Certification Centre Root CA O=AS Sertifitseerimiskeskus +# Subject: CN=EE Certification Centre Root CA O=AS Sertifitseerimiskeskus +# Label: "EE Certification Centre Root CA" +# Serial: 112324828676200291871926431888494945866 +# MD5 Fingerprint: 43:5e:88:d4:7d:1a:4a:7e:fd:84:2e:52:eb:01:d4:6f +# SHA1 Fingerprint: c9:a8:b9:e7:55:80:5e:58:e3:53:77:a7:25:eb:af:c3:7b:27:cc:d7 +# SHA256 Fingerprint: 3e:84:ba:43:42:90:85:16:e7:75:73:c0:99:2f:09:79:ca:08:4e:46:85:68:1f:f1:95:cc:ba:8a:22:9b:8a:76 +-----BEGIN CERTIFICATE----- +MIIEAzCCAuugAwIBAgIQVID5oHPtPwBMyonY43HmSjANBgkqhkiG9w0BAQUFADB1 +MQswCQYDVQQGEwJFRTEiMCAGA1UECgwZQVMgU2VydGlmaXRzZWVyaW1pc2tlc2t1 +czEoMCYGA1UEAwwfRUUgQ2VydGlmaWNhdGlvbiBDZW50cmUgUm9vdCBDQTEYMBYG +CSqGSIb3DQEJARYJcGtpQHNrLmVlMCIYDzIwMTAxMDMwMTAxMDMwWhgPMjAzMDEy +MTcyMzU5NTlaMHUxCzAJBgNVBAYTAkVFMSIwIAYDVQQKDBlBUyBTZXJ0aWZpdHNl +ZXJpbWlza2Vza3VzMSgwJgYDVQQDDB9FRSBDZXJ0aWZpY2F0aW9uIENlbnRyZSBS +b290IENBMRgwFgYJKoZIhvcNAQkBFglwa2lAc2suZWUwggEiMA0GCSqGSIb3DQEB +AQUAA4IBDwAwggEKAoIBAQDIIMDs4MVLqwd4lfNE7vsLDP90jmG7sWLqI9iroWUy +euuOF0+W2Ap7kaJjbMeMTC55v6kF/GlclY1i+blw7cNRfdCT5mzrMEvhvH2/UpvO +bntl8jixwKIy72KyaOBhU8E2lf/slLo2rpwcpzIP5Xy0xm90/XsY6KxX7QYgSzIw +WFv9zajmofxwvI6Sc9uXp3whrj3B9UiHbCe9nyV0gVWw93X2PaRka9ZP585ArQ/d +MtO8ihJTmMmJ+xAdTX7Nfh9WDSFwhfYggx/2uh8Ej+p3iDXE/+pOoYtNP2MbRMNE +1CV2yreN1x5KZmTNXMWcg+HCCIia7E6j8T4cLNlsHaFLAgMBAAGjgYowgYcwDwYD +VR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFBLyWj7qVhy/ +zQas8fElyalL1BSZMEUGA1UdJQQ+MDwGCCsGAQUFBwMCBggrBgEFBQcDAQYIKwYB +BQUHAwMGCCsGAQUFBwMEBggrBgEFBQcDCAYIKwYBBQUHAwkwDQYJKoZIhvcNAQEF +BQADggEBAHv25MANqhlHt01Xo/6tu7Fq1Q+e2+RjxY6hUFaTlrg4wCQiZrxTFGGV +v9DHKpY5P30osxBAIWrEr7BSdxjhlthWXePdNl4dp1BUoMUq5KqMlIpPnTX/dqQG +E5Gion0ARD9V04I8GtVbvFZMIi5GQ4okQC3zErg7cBqklrkar4dBGmoYDQZPxz5u +uSlNDUmJEYcyW+ZLBMjkXOZ0c5RdFpgTlf7727FE5TpwrDdr5rMzcijJs1eg9gIW +iAYLtqZLICjU3j2LrTcFU3T+bsy8QxdxXvnFzBqpYe73dgzzcvRyrc9yAjYHR8/v +GVCJYMzpJJUPwssd8m92kMfMdcGWxZ0= +-----END CERTIFICATE----- + +# Issuer: CN=TÜRKTRUST Elektronik Sertifika Hizmet Sağlayıcısı O=TÜRKTRUST Bilgi İletişim ve Bilişim Güvenliği Hizmetleri A.Ş. (c) Aralık 2007 +# Subject: CN=TÜRKTRUST Elektronik Sertifika Hizmet Sağlayıcısı O=TÜRKTRUST Bilgi İletişim ve Bilişim Güvenliği Hizmetleri A.Ş. (c) Aralık 2007 +# Label: "TURKTRUST Certificate Services Provider Root 2007" +# Serial: 1 +# MD5 Fingerprint: 2b:70:20:56:86:82:a0:18:c8:07:53:12:28:70:21:72 +# SHA1 Fingerprint: f1:7f:6f:b6:31:dc:99:e3:a3:c8:7f:fe:1c:f1:81:10:88:d9:60:33 +# SHA256 Fingerprint: 97:8c:d9:66:f2:fa:a0:7b:a7:aa:95:00:d9:c0:2e:9d:77:f2:cd:ad:a6:ad:6b:a7:4a:f4:b9:1c:66:59:3c:50 +-----BEGIN CERTIFICATE----- +MIIEPTCCAyWgAwIBAgIBATANBgkqhkiG9w0BAQUFADCBvzE/MD0GA1UEAww2VMOc +UktUUlVTVCBFbGVrdHJvbmlrIFNlcnRpZmlrYSBIaXptZXQgU2HEn2xhecSxY8Sx +c8SxMQswCQYDVQQGEwJUUjEPMA0GA1UEBwwGQW5rYXJhMV4wXAYDVQQKDFVUw5xS +S1RSVVNUIEJpbGdpIMSwbGV0acWfaW0gdmUgQmlsacWfaW0gR8O8dmVubGnEn2kg +SGl6bWV0bGVyaSBBLsWeLiAoYykgQXJhbMSxayAyMDA3MB4XDTA3MTIyNTE4Mzcx +OVoXDTE3MTIyMjE4MzcxOVowgb8xPzA9BgNVBAMMNlTDnFJLVFJVU1QgRWxla3Ry +b25payBTZXJ0aWZpa2EgSGl6bWV0IFNhxJ9sYXnEsWPEsXPEsTELMAkGA1UEBhMC +VFIxDzANBgNVBAcMBkFua2FyYTFeMFwGA1UECgxVVMOcUktUUlVTVCBCaWxnaSDE +sGxldGnFn2ltIHZlIEJpbGnFn2ltIEfDvHZlbmxpxJ9pIEhpem1ldGxlcmkgQS7F +ni4gKGMpIEFyYWzEsWsgMjAwNzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC +ggEBAKu3PgqMyKVYFeaK7yc9SrToJdPNM8Ig3BnuiD9NYvDdE3ePYakqtdTyuTFY +KTsvP2qcb3N2Je40IIDu6rfwxArNK4aUyeNgsURSsloptJGXg9i3phQvKUmi8wUG ++7RP2qFsmmaf8EMJyupyj+sA1zU511YXRxcw9L6/P8JorzZAwan0qafoEGsIiveG +HtyaKhUG9qPw9ODHFNRRf8+0222vR5YXm3dx2KdxnSQM9pQ/hTEST7ruToK4uT6P +IzdezKKqdfcYbwnTrqdUKDT74eA7YH2gvnmJhsifLfkKS8RQouf9eRbHegsYz85M +733WB2+Y8a+xwXrXgTW4qhe04MsCAwEAAaNCMEAwHQYDVR0OBBYEFCnFkKslrxHk +Yb+j/4hhkeYO/pyBMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MA0G +CSqGSIb3DQEBBQUAA4IBAQAQDdr4Ouwo0RSVgrESLFF6QSU2TJ/sPx+EnWVUXKgW +AkD6bho3hO9ynYYKVZ1WKKxmLNA6VpM0ByWtCLCPyA8JWcqdmBzlVPi5RX9ql2+I +aE1KBiY3iAIOtsbWcpnOa3faYjGkVh+uX4132l32iPwa2Z61gfAyuOOI0JzzaqC5 +mxRZNTZPz/OOXl0XrRWV2N2y1RVuAE6zS89mlOTgzbUF2mNXi+WzqtvALhyQRNsa +XRik7r4EW5nVcV9VZWRi1aKbBFmGyGJ353yCRWo9F7/snXUMrqNvWtMvmDb08PUZ +qxFdyKbjKlhqQgnDvZImZjINXQhVdP+MmNAKpoRq0Tl9 +-----END CERTIFICATE----- + +# Issuer: CN=D-TRUST Root Class 3 CA 2 2009 O=D-Trust GmbH +# Subject: CN=D-TRUST Root Class 3 CA 2 2009 O=D-Trust GmbH +# Label: "D-TRUST Root Class 3 CA 2 2009" +# Serial: 623603 +# MD5 Fingerprint: cd:e0:25:69:8d:47:ac:9c:89:35:90:f7:fd:51:3d:2f +# SHA1 Fingerprint: 58:e8:ab:b0:36:15:33:fb:80:f7:9b:1b:6d:29:d3:ff:8d:5f:00:f0 +# SHA256 Fingerprint: 49:e7:a4:42:ac:f0:ea:62:87:05:00:54:b5:25:64:b6:50:e4:f4:9e:42:e3:48:d6:aa:38:e0:39:e9:57:b1:c1 +-----BEGIN CERTIFICATE----- +MIIEMzCCAxugAwIBAgIDCYPzMA0GCSqGSIb3DQEBCwUAME0xCzAJBgNVBAYTAkRF +MRUwEwYDVQQKDAxELVRydXN0IEdtYkgxJzAlBgNVBAMMHkQtVFJVU1QgUm9vdCBD +bGFzcyAzIENBIDIgMjAwOTAeFw0wOTExMDUwODM1NThaFw0yOTExMDUwODM1NTha +ME0xCzAJBgNVBAYTAkRFMRUwEwYDVQQKDAxELVRydXN0IEdtYkgxJzAlBgNVBAMM +HkQtVFJVU1QgUm9vdCBDbGFzcyAzIENBIDIgMjAwOTCCASIwDQYJKoZIhvcNAQEB +BQADggEPADCCAQoCggEBANOySs96R+91myP6Oi/WUEWJNTrGa9v+2wBoqOADER03 +UAifTUpolDWzU9GUY6cgVq/eUXjsKj3zSEhQPgrfRlWLJ23DEE0NkVJD2IfgXU42 +tSHKXzlABF9bfsyjxiupQB7ZNoTWSPOSHjRGICTBpFGOShrvUD9pXRl/RcPHAY9R +ySPocq60vFYJfxLLHLGvKZAKyVXMD9O0Gu1HNVpK7ZxzBCHQqr0ME7UAyiZsxGsM +lFqVlNpQmvH/pStmMaTJOKDfHR+4CS7zp+hnUquVH+BGPtikw8paxTGA6Eian5Rp +/hnd2HN8gcqW3o7tszIFZYQ05ub9VxC1X3a/L7AQDcUCAwEAAaOCARowggEWMA8G +A1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFP3aFMSfMN4hvR5COfyrYyNJ4PGEMA4G +A1UdDwEB/wQEAwIBBjCB0wYDVR0fBIHLMIHIMIGAoH6gfIZ6bGRhcDovL2RpcmVj +dG9yeS5kLXRydXN0Lm5ldC9DTj1ELVRSVVNUJTIwUm9vdCUyMENsYXNzJTIwMyUy +MENBJTIwMiUyMDIwMDksTz1ELVRydXN0JTIwR21iSCxDPURFP2NlcnRpZmljYXRl +cmV2b2NhdGlvbmxpc3QwQ6BBoD+GPWh0dHA6Ly93d3cuZC10cnVzdC5uZXQvY3Js +L2QtdHJ1c3Rfcm9vdF9jbGFzc18zX2NhXzJfMjAwOS5jcmwwDQYJKoZIhvcNAQEL +BQADggEBAH+X2zDI36ScfSF6gHDOFBJpiBSVYEQBrLLpME+bUMJm2H6NMLVwMeni +acfzcNsgFYbQDfC+rAF1hM5+n02/t2A7nPPKHeJeaNijnZflQGDSNiH+0LS4F9p0 +o3/U37CYAqxva2ssJSRyoWXuJVrl5jLn8t+rSfrzkGkj2wTZ51xY/GXUl77M/C4K +zCUqNQT4YJEVdT1B/yMfGchs64JTBKbkTCJNjYy6zltz7GRUUG3RnFX7acM2w4y8 +PIWmawomDeCTmGCufsYkl4phX5GOZpIJhzbNi5stPvZR1FDUWSi9g/LMKHtThm3Y +Johw1+qRzT65ysCQblrGXnRl11z+o+I= +-----END CERTIFICATE----- + +# Issuer: CN=D-TRUST Root Class 3 CA 2 EV 2009 O=D-Trust GmbH +# Subject: CN=D-TRUST Root Class 3 CA 2 EV 2009 O=D-Trust GmbH +# Label: "D-TRUST Root Class 3 CA 2 EV 2009" +# Serial: 623604 +# MD5 Fingerprint: aa:c6:43:2c:5e:2d:cd:c4:34:c0:50:4f:11:02:4f:b6 +# SHA1 Fingerprint: 96:c9:1b:0b:95:b4:10:98:42:fa:d0:d8:22:79:fe:60:fa:b9:16:83 +# SHA256 Fingerprint: ee:c5:49:6b:98:8c:e9:86:25:b9:34:09:2e:ec:29:08:be:d0:b0:f3:16:c2:d4:73:0c:84:ea:f1:f3:d3:48:81 +-----BEGIN CERTIFICATE----- +MIIEQzCCAyugAwIBAgIDCYP0MA0GCSqGSIb3DQEBCwUAMFAxCzAJBgNVBAYTAkRF +MRUwEwYDVQQKDAxELVRydXN0IEdtYkgxKjAoBgNVBAMMIUQtVFJVU1QgUm9vdCBD +bGFzcyAzIENBIDIgRVYgMjAwOTAeFw0wOTExMDUwODUwNDZaFw0yOTExMDUwODUw +NDZaMFAxCzAJBgNVBAYTAkRFMRUwEwYDVQQKDAxELVRydXN0IEdtYkgxKjAoBgNV +BAMMIUQtVFJVU1QgUm9vdCBDbGFzcyAzIENBIDIgRVYgMjAwOTCCASIwDQYJKoZI +hvcNAQEBBQADggEPADCCAQoCggEBAJnxhDRwui+3MKCOvXwEz75ivJn9gpfSegpn +ljgJ9hBOlSJzmY3aFS3nBfwZcyK3jpgAvDw9rKFs+9Z5JUut8Mxk2og+KbgPCdM0 +3TP1YtHhzRnp7hhPTFiu4h7WDFsVWtg6uMQYZB7jM7K1iXdODL/ZlGsTl28So/6Z +qQTMFexgaDbtCHu39b+T7WYxg4zGcTSHThfqr4uRjRxWQa4iN1438h3Z0S0NL2lR +p75mpoo6Kr3HGrHhFPC+Oh25z1uxav60sUYgovseO3Dvk5h9jHOW8sXvhXCtKSb8 +HgQ+HKDYD8tSg2J87otTlZCpV6LqYQXY+U3EJ/pure3511H3a6UCAwEAAaOCASQw +ggEgMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFNOUikxiEyoZLsyvcop9Ntea +HNxnMA4GA1UdDwEB/wQEAwIBBjCB3QYDVR0fBIHVMIHSMIGHoIGEoIGBhn9sZGFw +Oi8vZGlyZWN0b3J5LmQtdHJ1c3QubmV0L0NOPUQtVFJVU1QlMjBSb290JTIwQ2xh +c3MlMjAzJTIwQ0ElMjAyJTIwRVYlMjAyMDA5LE89RC1UcnVzdCUyMEdtYkgsQz1E +RT9jZXJ0aWZpY2F0ZXJldm9jYXRpb25saXN0MEagRKBChkBodHRwOi8vd3d3LmQt +dHJ1c3QubmV0L2NybC9kLXRydXN0X3Jvb3RfY2xhc3NfM19jYV8yX2V2XzIwMDku +Y3JsMA0GCSqGSIb3DQEBCwUAA4IBAQA07XtaPKSUiO8aEXUHL7P+PPoeUSbrh/Yp +3uDx1MYkCenBz1UbtDDZzhr+BlGmFaQt77JLvyAoJUnRpjZ3NOhk31KxEcdzes05 +nsKtjHEh8lprr988TlWvsoRlFIm5d8sqMb7Po23Pb0iUMkZv53GMoKaEGTcH8gNF +CSuGdXzfX2lXANtu2KZyIktQ1HWYVt+3GP9DQ1CuekR78HlR10M9p9OB0/DJT7na +xpeG0ILD5EJt/rDiZE4OJudANCa1CInXCGNjOCd1HjPqbqjdn5lPdE2BiYBL3ZqX +KVwvvoFBuYz/6n1gBp7N1z3TLqMVvKjmJuVvw9y4AyHqnxbxLFS1 +-----END CERTIFICATE----- + +# Issuer: CN=Autoridad de Certificacion Raiz del Estado Venezolano O=Sistema Nacional de Certificacion Electronica OU=Superintendencia de Servicios de Certificacion Electronica +# Subject: CN=PSCProcert O=Sistema Nacional de Certificacion Electronica OU=Proveedor de Certificados PROCERT +# Label: "PSCProcert" +# Serial: 11 +# MD5 Fingerprint: e6:24:e9:12:01:ae:0c:de:8e:85:c4:ce:a3:12:dd:ec +# SHA1 Fingerprint: 70:c1:8d:74:b4:28:81:0a:e4:fd:a5:75:d7:01:9f:99:b0:3d:50:74 +# SHA256 Fingerprint: 3c:fc:3c:14:d1:f6:84:ff:17:e3:8c:43:ca:44:0c:00:b9:67:ec:93:3e:8b:fe:06:4c:a1:d7:2c:90:f2:ad:b0 +-----BEGIN CERTIFICATE----- +MIIJhjCCB26gAwIBAgIBCzANBgkqhkiG9w0BAQsFADCCAR4xPjA8BgNVBAMTNUF1 +dG9yaWRhZCBkZSBDZXJ0aWZpY2FjaW9uIFJhaXogZGVsIEVzdGFkbyBWZW5lem9s +YW5vMQswCQYDVQQGEwJWRTEQMA4GA1UEBxMHQ2FyYWNhczEZMBcGA1UECBMQRGlz +dHJpdG8gQ2FwaXRhbDE2MDQGA1UEChMtU2lzdGVtYSBOYWNpb25hbCBkZSBDZXJ0 +aWZpY2FjaW9uIEVsZWN0cm9uaWNhMUMwQQYDVQQLEzpTdXBlcmludGVuZGVuY2lh +IGRlIFNlcnZpY2lvcyBkZSBDZXJ0aWZpY2FjaW9uIEVsZWN0cm9uaWNhMSUwIwYJ +KoZIhvcNAQkBFhZhY3JhaXpAc3VzY2VydGUuZ29iLnZlMB4XDTEwMTIyODE2NTEw +MFoXDTIwMTIyNTIzNTk1OVowgdExJjAkBgkqhkiG9w0BCQEWF2NvbnRhY3RvQHBy +b2NlcnQubmV0LnZlMQ8wDQYDVQQHEwZDaGFjYW8xEDAOBgNVBAgTB01pcmFuZGEx +KjAoBgNVBAsTIVByb3ZlZWRvciBkZSBDZXJ0aWZpY2Fkb3MgUFJPQ0VSVDE2MDQG +A1UEChMtU2lzdGVtYSBOYWNpb25hbCBkZSBDZXJ0aWZpY2FjaW9uIEVsZWN0cm9u +aWNhMQswCQYDVQQGEwJWRTETMBEGA1UEAxMKUFNDUHJvY2VydDCCAiIwDQYJKoZI +hvcNAQEBBQADggIPADCCAgoCggIBANW39KOUM6FGqVVhSQ2oh3NekS1wwQYalNo9 +7BVCwfWMrmoX8Yqt/ICV6oNEolt6Vc5Pp6XVurgfoCfAUFM+jbnADrgV3NZs+J74 +BCXfgI8Qhd19L3uA3VcAZCP4bsm+lU/hdezgfl6VzbHvvnpC2Mks0+saGiKLt38G +ieU89RLAu9MLmV+QfI4tL3czkkohRqipCKzx9hEC2ZUWno0vluYC3XXCFCpa1sl9 +JcLB/KpnheLsvtF8PPqv1W7/U0HU9TI4seJfxPmOEO8GqQKJ/+MMbpfg353bIdD0 +PghpbNjU5Db4g7ayNo+c7zo3Fn2/omnXO1ty0K+qP1xmk6wKImG20qCZyFSTXai2 +0b1dCl53lKItwIKOvMoDKjSuc/HUtQy9vmebVOvh+qBa7Dh+PsHMosdEMXXqP+UH +0quhJZb25uSgXTcYOWEAM11G1ADEtMo88aKjPvM6/2kwLkDd9p+cJsmWN63nOaK/ +6mnbVSKVUyqUtd+tFjiBdWbjxywbk5yqjKPK2Ww8F22c3HxT4CAnQzb5EuE8XL1m +v6JpIzi4mWCZDlZTOpx+FIywBm/xhnaQr/2v/pDGj59/i5IjnOcVdo/Vi5QTcmn7 +K2FjiO/mpF7moxdqWEfLcU8UC17IAggmosvpr2uKGcfLFFb14dq12fy/czja+eev +bqQ34gcnAgMBAAGjggMXMIIDEzASBgNVHRMBAf8ECDAGAQH/AgEBMDcGA1UdEgQw +MC6CD3N1c2NlcnRlLmdvYi52ZaAbBgVghl4CAqASDBBSSUYtRy0yMDAwNDAzNi0w +MB0GA1UdDgQWBBRBDxk4qpl/Qguk1yeYVKIXTC1RVDCCAVAGA1UdIwSCAUcwggFD +gBStuyIdxuDSAaj9dlBSk+2YwU2u06GCASakggEiMIIBHjE+MDwGA1UEAxM1QXV0 +b3JpZGFkIGRlIENlcnRpZmljYWNpb24gUmFpeiBkZWwgRXN0YWRvIFZlbmV6b2xh +bm8xCzAJBgNVBAYTAlZFMRAwDgYDVQQHEwdDYXJhY2FzMRkwFwYDVQQIExBEaXN0 +cml0byBDYXBpdGFsMTYwNAYDVQQKEy1TaXN0ZW1hIE5hY2lvbmFsIGRlIENlcnRp +ZmljYWNpb24gRWxlY3Ryb25pY2ExQzBBBgNVBAsTOlN1cGVyaW50ZW5kZW5jaWEg +ZGUgU2VydmljaW9zIGRlIENlcnRpZmljYWNpb24gRWxlY3Ryb25pY2ExJTAjBgkq +hkiG9w0BCQEWFmFjcmFpekBzdXNjZXJ0ZS5nb2IudmWCAQowDgYDVR0PAQH/BAQD +AgEGME0GA1UdEQRGMESCDnByb2NlcnQubmV0LnZloBUGBWCGXgIBoAwMClBTQy0w +MDAwMDKgGwYFYIZeAgKgEgwQUklGLUotMzE2MzUzNzMtNzB2BgNVHR8EbzBtMEag +RKBChkBodHRwOi8vd3d3LnN1c2NlcnRlLmdvYi52ZS9sY3IvQ0VSVElGSUNBRE8t +UkFJWi1TSEEzODRDUkxERVIuY3JsMCOgIaAfhh1sZGFwOi8vYWNyYWl6LnN1c2Nl +cnRlLmdvYi52ZTA3BggrBgEFBQcBAQQrMCkwJwYIKwYBBQUHMAGGG2h0dHA6Ly9v +Y3NwLnN1c2NlcnRlLmdvYi52ZTBBBgNVHSAEOjA4MDYGBmCGXgMBAjAsMCoGCCsG +AQUFBwIBFh5odHRwOi8vd3d3LnN1c2NlcnRlLmdvYi52ZS9kcGMwDQYJKoZIhvcN +AQELBQADggIBACtZ6yKZu4SqT96QxtGGcSOeSwORR3C7wJJg7ODU523G0+1ng3dS +1fLld6c2suNUvtm7CpsR72H0xpkzmfWvADmNg7+mvTV+LFwxNG9s2/NkAZiqlCxB +3RWGymspThbASfzXg0gTB1GEMVKIu4YXx2sviiCtxQuPcD4quxtxj7mkoP3Yldmv +Wb8lK5jpY5MvYB7Eqvh39YtsL+1+LrVPQA3uvFd359m21D+VJzog1eWuq2w1n8Gh +HVnchIHuTQfiSLaeS5UtQbHh6N5+LwUeaO6/u5BlOsju6rEYNxxik6SgMexxbJHm +pHmJWhSnFFAFTKQAVzAswbVhltw+HoSvOULP5dAssSS830DD7X9jSr3hTxJkhpXz +sOfIt+FTvZLm8wyWuevo5pLtp4EJFAv8lXrPj9Y0TzYS3F7RNHXGRoAvlQSMx4bE +qCaJqD8Zm4G7UaRKhqsLEQ+xrmNTbSjq3TNWOByyrYDT13K9mmyZY+gAu0F2Bbdb +mRiKw7gSXFbPVgx96OLP7bx0R/vu0xdOIk9W/1DzLuY5poLWccret9W6aAjtmcz9 +opLLabid+Qqkpj5PkygqYWwHJgD/ll9ohri4zspV4KuxPX+Y1zMOWj3YeMLEYC/H +YvBhkdI4sPaeVdtAgAUSM84dkpvRabP/v/GSCmE1P93+hvS84Bpxs2Km +-----END CERTIFICATE----- + +# Issuer: CN=China Internet Network Information Center EV Certificates Root O=China Internet Network Information Center +# Subject: CN=China Internet Network Information Center EV Certificates Root O=China Internet Network Information Center +# Label: "China Internet Network Information Center EV Certificates Root" +# Serial: 1218379777 +# MD5 Fingerprint: 55:5d:63:00:97:bd:6a:97:f5:67:ab:4b:fb:6e:63:15 +# SHA1 Fingerprint: 4f:99:aa:93:fb:2b:d1:37:26:a1:99:4a:ce:7f:f0:05:f2:93:5d:1e +# SHA256 Fingerprint: 1c:01:c6:f4:db:b2:fe:fc:22:55:8b:2b:ca:32:56:3f:49:84:4a:cf:c3:2b:7b:e4:b0:ff:59:9f:9e:8c:7a:f7 +-----BEGIN CERTIFICATE----- +MIID9zCCAt+gAwIBAgIESJ8AATANBgkqhkiG9w0BAQUFADCBijELMAkGA1UEBhMC +Q04xMjAwBgNVBAoMKUNoaW5hIEludGVybmV0IE5ldHdvcmsgSW5mb3JtYXRpb24g +Q2VudGVyMUcwRQYDVQQDDD5DaGluYSBJbnRlcm5ldCBOZXR3b3JrIEluZm9ybWF0 +aW9uIENlbnRlciBFViBDZXJ0aWZpY2F0ZXMgUm9vdDAeFw0xMDA4MzEwNzExMjVa +Fw0zMDA4MzEwNzExMjVaMIGKMQswCQYDVQQGEwJDTjEyMDAGA1UECgwpQ2hpbmEg +SW50ZXJuZXQgTmV0d29yayBJbmZvcm1hdGlvbiBDZW50ZXIxRzBFBgNVBAMMPkNo +aW5hIEludGVybmV0IE5ldHdvcmsgSW5mb3JtYXRpb24gQ2VudGVyIEVWIENlcnRp +ZmljYXRlcyBSb290MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAm35z +7r07eKpkQ0H1UN+U8i6yjUqORlTSIRLIOTJCBumD1Z9S7eVnAztUwYyZmczpwA// +DdmEEbK40ctb3B75aDFk4Zv6dOtouSCV98YPjUesWgbdYavi7NifFy2cyjw1l1Vx +zUOFsUcW9SxTgHbP0wBkvUCZ3czY28Sf1hNfQYOL+Q2HklY0bBoQCxfVWhyXWIQ8 +hBouXJE0bhlffxdpxWXvayHG1VA6v2G5BY3vbzQ6sm8UY78WO5upKv23KzhmBsUs +4qpnHkWnjQRmQvaPK++IIGmPMowUc9orhpFjIpryp9vOiYurXccUwVswah+xt54u +gQEC7c+WXmPbqOY4twIDAQABo2MwYTAfBgNVHSMEGDAWgBR8cks5x8DbYqVPm6oY +NJKiyoOCWTAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4E +FgQUfHJLOcfA22KlT5uqGDSSosqDglkwDQYJKoZIhvcNAQEFBQADggEBACrDx0M3 +j92tpLIM7twUbY8opJhJywyA6vPtI2Z1fcXTIWd50XPFtQO3WKwMVC/GVhMPMdoG +52U7HW8228gd+f2ABsqjPWYWqJ1MFn3AlUa1UeTiH9fqBk1jjZaM7+czV0I664zB +echNdn3e9rG3geCg+aF4RhcaVpjwTj2rHO3sOdwHSPdj/gauwqRcalsyiMXHM4Ws +ZkJHwlgkmeHlPuV1LI5D1l08eB6olYIpUNHRFrrvwb562bTYzB5MRuF3sTGrvSrI +zo9uoV1/A3U05K2JRVRevq4opbs/eHnrc7MKDf2+yfdWrPa37S+bISnHOLaVxATy +wy39FCqQmbkHzJ8= +-----END CERTIFICATE----- + +# Issuer: CN=Swisscom Root CA 2 O=Swisscom OU=Digital Certificate Services +# Subject: CN=Swisscom Root CA 2 O=Swisscom OU=Digital Certificate Services +# Label: "Swisscom Root CA 2" +# Serial: 40698052477090394928831521023204026294 +# MD5 Fingerprint: 5b:04:69:ec:a5:83:94:63:18:a7:86:d0:e4:f2:6e:19 +# SHA1 Fingerprint: 77:47:4f:c6:30:e4:0f:4c:47:64:3f:84:ba:b8:c6:95:4a:8a:41:ec +# SHA256 Fingerprint: f0:9b:12:2c:71:14:f4:a0:9b:d4:ea:4f:4a:99:d5:58:b4:6e:4c:25:cd:81:14:0d:29:c0:56:13:91:4c:38:41 +-----BEGIN CERTIFICATE----- +MIIF2TCCA8GgAwIBAgIQHp4o6Ejy5e/DfEoeWhhntjANBgkqhkiG9w0BAQsFADBk +MQswCQYDVQQGEwJjaDERMA8GA1UEChMIU3dpc3Njb20xJTAjBgNVBAsTHERpZ2l0 +YWwgQ2VydGlmaWNhdGUgU2VydmljZXMxGzAZBgNVBAMTElN3aXNzY29tIFJvb3Qg +Q0EgMjAeFw0xMTA2MjQwODM4MTRaFw0zMTA2MjUwNzM4MTRaMGQxCzAJBgNVBAYT +AmNoMREwDwYDVQQKEwhTd2lzc2NvbTElMCMGA1UECxMcRGlnaXRhbCBDZXJ0aWZp +Y2F0ZSBTZXJ2aWNlczEbMBkGA1UEAxMSU3dpc3Njb20gUm9vdCBDQSAyMIICIjAN +BgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAlUJOhJ1R5tMJ6HJaI2nbeHCOFvEr +jw0DzpPMLgAIe6szjPTpQOYXTKueuEcUMncy3SgM3hhLX3af+Dk7/E6J2HzFZ++r +0rk0X2s682Q2zsKwzxNoysjL67XiPS4h3+os1OD5cJZM/2pYmLcX5BtS5X4HAB1f +2uY+lQS3aYg5oUFgJWFLlTloYhyxCwWJwDaCFCE/rtuh/bxvHGCGtlOUSbkrRsVP +ACu/obvLP+DHVxxX6NZp+MEkUp2IVd3Chy50I9AU/SpHWrumnf2U5NGKpV+GY3aF +y6//SSj8gO1MedK75MDvAe5QQQg1I3ArqRa0jG6F6bYRzzHdUyYb3y1aSgJA/MTA +tukxGggo5WDDH8SQjhBiYEQN7Aq+VRhxLKX0srwVYv8c474d2h5Xszx+zYIdkeNL +6yxSNLCK/RJOlrDrcH+eOfdmQrGrrFLadkBXeyq96G4DsguAhYidDMfCd7Camlf0 +uPoTXGiTOmekl9AbmbeGMktg2M7v0Ax/lZ9vh0+Hio5fCHyqW/xavqGRn1V9TrAL +acywlKinh/LTSlDcX3KwFnUey7QYYpqwpzmqm59m2I2mbJYV4+by+PGDYmy7Velh +k6M99bFXi08jsJvllGov34zflVEpYKELKeRcVVi3qPyZ7iVNTA6z00yPhOgpD/0Q +VAKFyPnlw4vP5w8CAwEAAaOBhjCBgzAOBgNVHQ8BAf8EBAMCAYYwHQYDVR0hBBYw +FDASBgdghXQBUwIBBgdghXQBUwIBMBIGA1UdEwEB/wQIMAYBAf8CAQcwHQYDVR0O +BBYEFE0mICKJS9PVpAqhb97iEoHF8TwuMB8GA1UdIwQYMBaAFE0mICKJS9PVpAqh +b97iEoHF8TwuMA0GCSqGSIb3DQEBCwUAA4ICAQAyCrKkG8t9voJXiblqf/P0wS4R +fbgZPnm3qKhyN2abGu2sEzsOv2LwnN+ee6FTSA5BesogpxcbtnjsQJHzQq0Qw1zv +/2BZf82Fo4s9SBwlAjxnffUy6S8w5X2lejjQ82YqZh6NM4OKb3xuqFp1mrjX2lhI +REeoTPpMSQpKwhI3qEAMw8jh0FcNlzKVxzqfl9NX+Ave5XLzo9v/tdhZsnPdTSpx +srpJ9csc1fV5yJmz/MFMdOO0vSk3FQQoHt5FRnDsr7p4DooqzgB53MBfGWcsa0vv +aGgLQ+OswWIJ76bdZWGgr4RVSJFSHMYlkSrQwSIjYVmvRRGFHQEkNI/Ps/8XciAT +woCqISxxOQ7Qj1zB09GOInJGTB2Wrk9xseEFKZZZ9LuedT3PDTcNYtsmjGOpI99n +Bjx8Oto0QuFmtEYE3saWmA9LSHokMnWRn6z3aOkquVVlzl1h0ydw2Df+n7mvoC5W +t6NlUe07qxS/TFED6F+KBZvuim6c779o+sjaC+NCydAXFJy3SuCvkychVSa1ZC+N +8f+mQAWFBVzKBxlcCxMoTFh/wqXvRdpg065lYZ1Tg3TCrvJcwhbtkj6EPnNgiLx2 +9CzP0H1907he0ZESEOnN3col49XtmS++dYFLJPlFRpTJKSFTnCZFqhMX5OfNeOI5 +wSsSnqaeG8XmDtkx2Q== +-----END CERTIFICATE----- + +# Issuer: CN=Swisscom Root EV CA 2 O=Swisscom OU=Digital Certificate Services +# Subject: CN=Swisscom Root EV CA 2 O=Swisscom OU=Digital Certificate Services +# Label: "Swisscom Root EV CA 2" +# Serial: 322973295377129385374608406479535262296 +# MD5 Fingerprint: 7b:30:34:9f:dd:0a:4b:6b:35:ca:31:51:28:5d:ae:ec +# SHA1 Fingerprint: e7:a1:90:29:d3:d5:52:dc:0d:0f:c6:92:d3:ea:88:0d:15:2e:1a:6b +# SHA256 Fingerprint: d9:5f:ea:3c:a4:ee:dc:e7:4c:d7:6e:75:fc:6d:1f:f6:2c:44:1f:0f:a8:bc:77:f0:34:b1:9e:5d:b2:58:01:5d +-----BEGIN CERTIFICATE----- +MIIF4DCCA8igAwIBAgIRAPL6ZOJ0Y9ON/RAdBB92ylgwDQYJKoZIhvcNAQELBQAw +ZzELMAkGA1UEBhMCY2gxETAPBgNVBAoTCFN3aXNzY29tMSUwIwYDVQQLExxEaWdp +dGFsIENlcnRpZmljYXRlIFNlcnZpY2VzMR4wHAYDVQQDExVTd2lzc2NvbSBSb290 +IEVWIENBIDIwHhcNMTEwNjI0MDk0NTA4WhcNMzEwNjI1MDg0NTA4WjBnMQswCQYD +VQQGEwJjaDERMA8GA1UEChMIU3dpc3Njb20xJTAjBgNVBAsTHERpZ2l0YWwgQ2Vy +dGlmaWNhdGUgU2VydmljZXMxHjAcBgNVBAMTFVN3aXNzY29tIFJvb3QgRVYgQ0Eg +MjCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMT3HS9X6lds93BdY7Bx +UglgRCgzo3pOCvrY6myLURYaVa5UJsTMRQdBTxB5f3HSek4/OE6zAMaVylvNwSqD +1ycfMQ4jFrclyxy0uYAyXhqdk/HoPGAsp15XGVhRXrwsVgu42O+LgrQ8uMIkqBPH +oCE2G3pXKSinLr9xJZDzRINpUKTk4RtiGZQJo/PDvO/0vezbE53PnUgJUmfANykR +HvvSEaeFGHR55E+FFOtSN+KxRdjMDUN/rhPSays/p8LiqG12W0OfvrSdsyaGOx9/ +5fLoZigWJdBLlzin5M8J0TbDC77aO0RYjb7xnglrPvMyxyuHxuxenPaHZa0zKcQv +idm5y8kDnftslFGXEBuGCxobP/YCfnvUxVFkKJ3106yDgYjTdLRZncHrYTNaRdHL +OdAGalNgHa/2+2m8atwBz735j9m9W8E6X47aD0upm50qKGsaCnw8qyIL5XctcfaC +NYGu+HuB5ur+rPQam3Rc6I8k9l2dRsQs0h4rIWqDJ2dVSqTjyDKXZpBy2uPUZC5f +46Fq9mDU5zXNysRojddxyNMkM3OxbPlq4SjbX8Y96L5V5jcb7STZDxmPX2MYWFCB +UWVv8p9+agTnNCRxunZLWB4ZvRVgRaoMEkABnRDixzgHcgplwLa7JSnaFp6LNYth +7eVxV4O1PHGf40+/fh6Bn0GXAgMBAAGjgYYwgYMwDgYDVR0PAQH/BAQDAgGGMB0G +A1UdIQQWMBQwEgYHYIV0AVMCAgYHYIV0AVMCAjASBgNVHRMBAf8ECDAGAQH/AgED +MB0GA1UdDgQWBBRF2aWBbj2ITY1x0kbBbkUe88SAnTAfBgNVHSMEGDAWgBRF2aWB +bj2ITY1x0kbBbkUe88SAnTANBgkqhkiG9w0BAQsFAAOCAgEAlDpzBp9SSzBc1P6x +XCX5145v9Ydkn+0UjrgEjihLj6p7jjm02Vj2e6E1CqGdivdj5eu9OYLU43otb98T +PLr+flaYC/NUn81ETm484T4VvwYmneTwkLbUwp4wLh/vx3rEUMfqe9pQy3omywC0 +Wqu1kx+AiYQElY2NfwmTv9SoqORjbdlk5LgpWgi/UOGED1V7XwgiG/W9mR4U9s70 +WBCCswo9GcG/W6uqmdjyMb3lOGbcWAXH7WMaLgqXfIeTK7KK4/HsGOV1timH59yL +Gn602MnTihdsfSlEvoqq9X46Lmgxk7lq2prg2+kupYTNHAq4Sgj5nPFhJpiTt3tm +7JFe3VE/23MPrQRYCd0EApUKPtN236YQHoA96M2kZNEzx5LH4k5E4wnJTsJdhw4S +nr8PyQUQ3nqjsTzyP6WqJ3mtMX0f/fwZacXduT98zca0wjAefm6S139hdlqP65VN +vBFuIXxZN5nQBrz5Bm0yFqXZaajh3DyAHmBR3NdUIR7KYndP+tiPsys6DXhyyWhB +WkdKwqPrGtcKqzwyVcgKEZzfdNbwQBUdyLmPtTbFr/giuMod89a2GQ+fYWVq6nTI +fI/DT11lgh/ZDYnadXL77/FHZxOzyNEZiCcmmpl5fx7kLD977vHeTYuWl8PVP3wb +I+2ksx0WckNLIOFZfsLorSa/ovc= +-----END CERTIFICATE----- + +# Issuer: CN=CA Disig Root R1 O=Disig a.s. +# Subject: CN=CA Disig Root R1 O=Disig a.s. +# Label: "CA Disig Root R1" +# Serial: 14052245610670616104 +# MD5 Fingerprint: be:ec:11:93:9a:f5:69:21:bc:d7:c1:c0:67:89:cc:2a +# SHA1 Fingerprint: 8e:1c:74:f8:a6:20:b9:e5:8a:f4:61:fa:ec:2b:47:56:51:1a:52:c6 +# SHA256 Fingerprint: f9:6f:23:f4:c3:e7:9c:07:7a:46:98:8d:5a:f5:90:06:76:a0:f0:39:cb:64:5d:d1:75:49:b2:16:c8:24:40:ce +-----BEGIN CERTIFICATE----- +MIIFaTCCA1GgAwIBAgIJAMMDmu5QkG4oMA0GCSqGSIb3DQEBBQUAMFIxCzAJBgNV +BAYTAlNLMRMwEQYDVQQHEwpCcmF0aXNsYXZhMRMwEQYDVQQKEwpEaXNpZyBhLnMu +MRkwFwYDVQQDExBDQSBEaXNpZyBSb290IFIxMB4XDTEyMDcxOTA5MDY1NloXDTQy +MDcxOTA5MDY1NlowUjELMAkGA1UEBhMCU0sxEzARBgNVBAcTCkJyYXRpc2xhdmEx +EzARBgNVBAoTCkRpc2lnIGEucy4xGTAXBgNVBAMTEENBIERpc2lnIFJvb3QgUjEw +ggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCqw3j33Jijp1pedxiy3QRk +D2P9m5YJgNXoqqXinCaUOuiZc4yd39ffg/N4T0Dhf9Kn0uXKE5Pn7cZ3Xza1lK/o +OI7bm+V8u8yN63Vz4STN5qctGS7Y1oprFOsIYgrY3LMATcMjfF9DCCMyEtztDK3A +fQ+lekLZWnDZv6fXARz2m6uOt0qGeKAeVjGu74IKgEH3G8muqzIm1Cxr7X1r5OJe +IgpFy4QxTaz+29FHuvlglzmxZcfe+5nkCiKxLU3lSCZpq+Kq8/v8kiky6bM+TR8n +oc2OuRf7JT7JbvN32g0S9l3HuzYQ1VTW8+DiR0jm3hTaYVKvJrT1cU/J19IG32PK +/yHoWQbgCNWEFVP3Q+V8xaCJmGtzxmjOZd69fwX3se72V6FglcXM6pM6vpmumwKj +rckWtc7dXpl4fho5frLABaTAgqWjR56M6ly2vGfb5ipN0gTco65F97yLnByn1tUD +3AjLLhbKXEAz6GfDLuemROoRRRw1ZS0eRWEkG4IupZ0zXWX4Qfkuy5Q/H6MMMSRE +7cderVC6xkGbrPAXZcD4XW9boAo0PO7X6oifmPmvTiT6l7Jkdtqr9O3jw2Dv1fkC +yC2fg69naQanMVXVz0tv/wQFx1isXxYb5dKj6zHbHzMVTdDypVP1y+E9Tmgt2BLd +qvLmTZtJ5cUoobqwWsagtQIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1Ud +DwEB/wQEAwIBBjAdBgNVHQ4EFgQUiQq0OJMa5qvum5EY+fU8PjXQ04IwDQYJKoZI +hvcNAQEFBQADggIBADKL9p1Kyb4U5YysOMo6CdQbzoaz3evUuii+Eq5FLAR0rBNR +xVgYZk2C2tXck8An4b58n1KeElb21Zyp9HWc+jcSjxyT7Ff+Bw+r1RL3D65hXlaA +SfX8MPWbTx9BLxyE04nH4toCdu0Jz2zBuByDHBb6lM19oMgY0sidbvW9adRtPTXo +HqJPYNcHKfyyo6SdbhWSVhlMCrDpfNIZTUJG7L399ldb3Zh+pE3McgODWF3vkzpB +emOqfDqo9ayk0d2iLbYq/J8BjuIQscTK5GfbVSUZP/3oNn6z4eGBrxEWi1CXYBmC +AMBrTXO40RMHPuq2MU/wQppt4hF05ZSsjYSVPCGvxdpHyN85YmLLW1AL14FABZyb +7bq2ix4Eb5YgOe2kfSnbSM6C3NQCjR0EMVrHS/BsYVLXtFHCgWzN4funodKSds+x +DzdYpPJScWc/DIh4gInByLUfkmO+p3qKViwaqKactV2zY9ATIKHrkWzQjX2v3wvk +F7mGnjixlAxYjOBVqjtjbZqJYLhkKpLGN/R+Q0O3c+gB53+XD9fyexn9GtePyfqF +a3qdnom2piiZk4hA9z7NUaPK6u95RyG1/jLix8NRb76AdPCkwzryT+lf3xkK8jsT +Q6wxpLPn6/wY1gGp8yqPNg7rtLG8t0zJa7+h89n07eLw4+1knj0vllJPgFOL +-----END CERTIFICATE----- + +# Issuer: CN=CA Disig Root R2 O=Disig a.s. +# Subject: CN=CA Disig Root R2 O=Disig a.s. +# Label: "CA Disig Root R2" +# Serial: 10572350602393338211 +# MD5 Fingerprint: 26:01:fb:d8:27:a7:17:9a:45:54:38:1a:43:01:3b:03 +# SHA1 Fingerprint: b5:61:eb:ea:a4:de:e4:25:4b:69:1a:98:a5:57:47:c2:34:c7:d9:71 +# SHA256 Fingerprint: e2:3d:4a:03:6d:7b:70:e9:f5:95:b1:42:20:79:d2:b9:1e:df:bb:1f:b6:51:a0:63:3e:aa:8a:9d:c5:f8:07:03 +-----BEGIN CERTIFICATE----- +MIIFaTCCA1GgAwIBAgIJAJK4iNuwisFjMA0GCSqGSIb3DQEBCwUAMFIxCzAJBgNV +BAYTAlNLMRMwEQYDVQQHEwpCcmF0aXNsYXZhMRMwEQYDVQQKEwpEaXNpZyBhLnMu +MRkwFwYDVQQDExBDQSBEaXNpZyBSb290IFIyMB4XDTEyMDcxOTA5MTUzMFoXDTQy +MDcxOTA5MTUzMFowUjELMAkGA1UEBhMCU0sxEzARBgNVBAcTCkJyYXRpc2xhdmEx +EzARBgNVBAoTCkRpc2lnIGEucy4xGTAXBgNVBAMTEENBIERpc2lnIFJvb3QgUjIw +ggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCio8QACdaFXS1tFPbCw3Oe +NcJxVX6B+6tGUODBfEl45qt5WDza/3wcn9iXAng+a0EE6UG9vgMsRfYvZNSrXaNH +PWSb6WiaxswbP7q+sos0Ai6YVRn8jG+qX9pMzk0DIaPY0jSTVpbLTAwAFjxfGs3I +x2ymrdMxp7zo5eFm1tL7A7RBZckQrg4FY8aAamkw/dLukO8NJ9+flXP04SXabBbe +QTg06ov80egEFGEtQX6sx3dOy1FU+16SGBsEWmjGycT6txOgmLcRK7fWV8x8nhfR +yyX+hk4kLlYMeE2eARKmK6cBZW58Yh2EhN/qwGu1pSqVg8NTEQxzHQuyRpDRQjrO +QG6Vrf/GlK1ul4SOfW+eioANSW1z4nuSHsPzwfPrLgVv2RvPN3YEyLRa5Beny912 +H9AZdugsBbPWnDTYltxhh5EF5EQIM8HauQhl1K6yNg3ruji6DOWbnuuNZt2Zz9aJ +QfYEkoopKW1rOhzndX0CcQ7zwOe9yxndnWCywmZgtrEE7snmhrmaZkCo5xHtgUUD +i/ZnWejBBhG93c+AAk9lQHhcR1DIm+YfgXvkRKhbhZri3lrVx/k6RGZL5DJUfORs +nLMOPReisjQS1n6yqEm70XooQL6iFh/f5DcfEXP7kAplQ6INfPgGAVUzfbANuPT1 +rqVCV3w2EYx7XsQDnYx5nQIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1Ud +DwEB/wQEAwIBBjAdBgNVHQ4EFgQUtZn4r7CU9eMg1gqtzk5WpC5uQu0wDQYJKoZI +hvcNAQELBQADggIBACYGXnDnZTPIgm7ZnBc6G3pmsgH2eDtpXi/q/075KMOYKmFM +tCQSin1tERT3nLXK5ryeJ45MGcipvXrA1zYObYVybqjGom32+nNjf7xueQgcnYqf +GopTpti72TVVsRHFqQOzVju5hJMiXn7B9hJSi+osZ7z+Nkz1uM/Rs0mSO9MpDpkb +lvdhuDvEK7Z4bLQjb/D907JedR+Zlais9trhxTF7+9FGs9K8Z7RiVLoJ92Owk6Ka ++elSLotgEqv89WBW7xBci8QaQtyDW2QOy7W81k/BfDxujRNt+3vrMNDcTa/F1bal +TFtxyegxvug4BkihGuLq0t4SOVga/4AOgnXmt8kHbA7v/zjxmHHEt38OFdAlab0i +nSvtBfZGR6ztwPDUO+Ls7pZbkBNOHlY667DvlruWIxG68kOGdGSVyCh13x01utI3 +gzhTODY7z2zp+WsO0PsE6E9312UBeIYMej4hYvF/Y3EMyZ9E26gnonW+boE+18Dr +G5gPcFw0sorMwIUY6256s/daoQe/qUKS82Ail+QUoQebTnbAjn39pCXHR+3/H3Os +zMOl6W8KjptlwlCFtaOgUxLMVYdh84GuEEZhvUQhuMI9dM9+JDX6HAcOmz0iyu8x +L4ysEr3vQCj8KWefshNPZiTEUxnpHikV7+ZtsH8tZ/3zbBt1RqPlShfppNcL +-----END CERTIFICATE----- + +# Issuer: CN=ACCVRAIZ1 O=ACCV OU=PKIACCV +# Subject: CN=ACCVRAIZ1 O=ACCV OU=PKIACCV +# Label: "ACCVRAIZ1" +# Serial: 6828503384748696800 +# MD5 Fingerprint: d0:a0:5a:ee:05:b6:09:94:21:a1:7d:f1:b2:29:82:02 +# SHA1 Fingerprint: 93:05:7a:88:15:c6:4f:ce:88:2f:fa:91:16:52:28:78:bc:53:64:17 +# SHA256 Fingerprint: 9a:6e:c0:12:e1:a7:da:9d:be:34:19:4d:47:8a:d7:c0:db:18:22:fb:07:1d:f1:29:81:49:6e:d1:04:38:41:13 +-----BEGIN CERTIFICATE----- +MIIH0zCCBbugAwIBAgIIXsO3pkN/pOAwDQYJKoZIhvcNAQEFBQAwQjESMBAGA1UE +AwwJQUNDVlJBSVoxMRAwDgYDVQQLDAdQS0lBQ0NWMQ0wCwYDVQQKDARBQ0NWMQsw +CQYDVQQGEwJFUzAeFw0xMTA1MDUwOTM3MzdaFw0zMDEyMzEwOTM3MzdaMEIxEjAQ +BgNVBAMMCUFDQ1ZSQUlaMTEQMA4GA1UECwwHUEtJQUNDVjENMAsGA1UECgwEQUND +VjELMAkGA1UEBhMCRVMwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCb +qau/YUqXry+XZpp0X9DZlv3P4uRm7x8fRzPCRKPfmt4ftVTdFXxpNRFvu8gMjmoY +HtiP2Ra8EEg2XPBjs5BaXCQ316PWywlxufEBcoSwfdtNgM3802/J+Nq2DoLSRYWo +G2ioPej0RGy9ocLLA76MPhMAhN9KSMDjIgro6TenGEyxCQ0jVn8ETdkXhBilyNpA +lHPrzg5XPAOBOp0KoVdDaaxXbXmQeOW1tDvYvEyNKKGno6e6Ak4l0Squ7a4DIrhr +IA8wKFSVf+DuzgpmndFALW4ir50awQUZ0m/A8p/4e7MCQvtQqR0tkw8jq8bBD5L/ +0KIV9VMJcRz/RROE5iZe+OCIHAr8Fraocwa48GOEAqDGWuzndN9wrqODJerWx5eH +k6fGioozl2A3ED6XPm4pFdahD9GILBKfb6qkxkLrQaLjlUPTAYVtjrs78yM2x/47 +4KElB0iryYl0/wiPgL/AlmXz7uxLaL2diMMxs0Dx6M/2OLuc5NF/1OVYm3z61PMO +m3WR5LpSLhl+0fXNWhn8ugb2+1KoS5kE3fj5tItQo05iifCHJPqDQsGH+tUtKSpa +cXpkatcnYGMN285J9Y0fkIkyF/hzQ7jSWpOGYdbhdQrqeWZ2iE9x6wQl1gpaepPl +uUsXQA+xtrn13k/c4LOsOxFwYIRKQ26ZIMApcQrAZQIDAQABo4ICyzCCAscwfQYI +KwYBBQUHAQEEcTBvMEwGCCsGAQUFBzAChkBodHRwOi8vd3d3LmFjY3YuZXMvZmls +ZWFkbWluL0FyY2hpdm9zL2NlcnRpZmljYWRvcy9yYWl6YWNjdjEuY3J0MB8GCCsG +AQUFBzABhhNodHRwOi8vb2NzcC5hY2N2LmVzMB0GA1UdDgQWBBTSh7Tj3zcnk1X2 +VuqB5TbMjB4/vTAPBgNVHRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFNKHtOPfNyeT +VfZW6oHlNsyMHj+9MIIBcwYDVR0gBIIBajCCAWYwggFiBgRVHSAAMIIBWDCCASIG +CCsGAQUFBwICMIIBFB6CARAAQQB1AHQAbwByAGkAZABhAGQAIABkAGUAIABDAGUA +cgB0AGkAZgBpAGMAYQBjAGkA8wBuACAAUgBhAO0AegAgAGQAZQAgAGwAYQAgAEEA +QwBDAFYAIAAoAEEAZwBlAG4AYwBpAGEAIABkAGUAIABUAGUAYwBuAG8AbABvAGcA +7QBhACAAeQAgAEMAZQByAHQAaQBmAGkAYwBhAGMAaQDzAG4AIABFAGwAZQBjAHQA +cgDzAG4AaQBjAGEALAAgAEMASQBGACAAUQA0ADYAMAAxADEANQA2AEUAKQAuACAA +QwBQAFMAIABlAG4AIABoAHQAdABwADoALwAvAHcAdwB3AC4AYQBjAGMAdgAuAGUA +czAwBggrBgEFBQcCARYkaHR0cDovL3d3dy5hY2N2LmVzL2xlZ2lzbGFjaW9uX2Mu +aHRtMFUGA1UdHwROMEwwSqBIoEaGRGh0dHA6Ly93d3cuYWNjdi5lcy9maWxlYWRt +aW4vQXJjaGl2b3MvY2VydGlmaWNhZG9zL3JhaXphY2N2MV9kZXIuY3JsMA4GA1Ud +DwEB/wQEAwIBBjAXBgNVHREEEDAOgQxhY2N2QGFjY3YuZXMwDQYJKoZIhvcNAQEF +BQADggIBAJcxAp/n/UNnSEQU5CmH7UwoZtCPNdpNYbdKl02125DgBS4OxnnQ8pdp +D70ER9m+27Up2pvZrqmZ1dM8MJP1jaGo/AaNRPTKFpV8M9xii6g3+CfYCS0b78gU +JyCpZET/LtZ1qmxNYEAZSUNUY9rizLpm5U9EelvZaoErQNV/+QEnWCzI7UiRfD+m +AM/EKXMRNt6GGT6d7hmKG9Ww7Y49nCrADdg9ZuM8Db3VlFzi4qc1GwQA9j9ajepD +vV+JHanBsMyZ4k0ACtrJJ1vnE5Bc5PUzolVt3OAJTS+xJlsndQAJxGJ3KQhfnlms +tn6tn1QwIgPBHnFk/vk4CpYY3QIUrCPLBhwepH2NDd4nQeit2hW3sCPdK6jT2iWH +7ehVRE2I9DZ+hJp4rPcOVkkO1jMl1oRQQmwgEh0q1b688nCBpHBgvgW1m54ERL5h +I6zppSSMEYCUWqKiuUnSwdzRp+0xESyeGabu4VXhwOrPDYTkF7eifKXeVSUG7szA +h1xA2syVP1XgNce4hL60Xc16gwFy7ofmXx2utYXGJt/mwZrpHgJHnyqobalbz+xF +d3+YJ5oyXSrjhO7FmGYvliAd3djDJ9ew+f7Zfc3Qn48LFFhRny+Lwzgt3uiP1o2H +pPVWQxaZLPSkVrQ0uGE3ycJYgBugl6H8WY3pEfbRD0tVNEYqi4Y7 +-----END CERTIFICATE----- + +# Issuer: CN=TWCA Global Root CA O=TAIWAN-CA OU=Root CA +# Subject: CN=TWCA Global Root CA O=TAIWAN-CA OU=Root CA +# Label: "TWCA Global Root CA" +# Serial: 3262 +# MD5 Fingerprint: f9:03:7e:cf:e6:9e:3c:73:7a:2a:90:07:69:ff:2b:96 +# SHA1 Fingerprint: 9c:bb:48:53:f6:a4:f6:d3:52:a4:e8:32:52:55:60:13:f5:ad:af:65 +# SHA256 Fingerprint: 59:76:90:07:f7:68:5d:0f:cd:50:87:2f:9f:95:d5:75:5a:5b:2b:45:7d:81:f3:69:2b:61:0a:98:67:2f:0e:1b +-----BEGIN CERTIFICATE----- +MIIFQTCCAymgAwIBAgICDL4wDQYJKoZIhvcNAQELBQAwUTELMAkGA1UEBhMCVFcx +EjAQBgNVBAoTCVRBSVdBTi1DQTEQMA4GA1UECxMHUm9vdCBDQTEcMBoGA1UEAxMT +VFdDQSBHbG9iYWwgUm9vdCBDQTAeFw0xMjA2MjcwNjI4MzNaFw0zMDEyMzExNTU5 +NTlaMFExCzAJBgNVBAYTAlRXMRIwEAYDVQQKEwlUQUlXQU4tQ0ExEDAOBgNVBAsT +B1Jvb3QgQ0ExHDAaBgNVBAMTE1RXQ0EgR2xvYmFsIFJvb3QgQ0EwggIiMA0GCSqG +SIb3DQEBAQUAA4ICDwAwggIKAoICAQCwBdvI64zEbooh745NnHEKH1Jw7W2CnJfF +10xORUnLQEK1EjRsGcJ0pDFfhQKX7EMzClPSnIyOt7h52yvVavKOZsTuKwEHktSz +0ALfUPZVr2YOy+BHYC8rMjk1Ujoog/h7FsYYuGLWRyWRzvAZEk2tY/XTP3VfKfCh +MBwqoJimFb3u/Rk28OKRQ4/6ytYQJ0lM793B8YVwm8rqqFpD/G2Gb3PpN0Wp8DbH +zIh1HrtsBv+baz4X7GGqcXzGHaL3SekVtTzWoWH1EfcFbx39Eb7QMAfCKbAJTibc +46KokWofwpFFiFzlmLhxpRUZyXx1EcxwdE8tmx2RRP1WKKD+u4ZqyPpcC1jcxkt2 +yKsi2XMPpfRaAok/T54igu6idFMqPVMnaR1sjjIsZAAmY2E2TqNGtz99sy2sbZCi +laLOz9qC5wc0GZbpuCGqKX6mOL6OKUohZnkfs8O1CWfe1tQHRvMq2uYiN2DLgbYP +oA/pyJV/v1WRBXrPPRXAb94JlAGD1zQbzECl8LibZ9WYkTunhHiVJqRaCPgrdLQA +BDzfuBSO6N+pjWxnkjMdwLfS7JLIvgm/LCkFbwJrnu+8vyq8W8BQj0FwcYeyTbcE +qYSjMq+u7msXi7Kx/mzhkIyIqJdIzshNy/MGz19qCkKxHh53L46g5pIOBvwFItIm +4TFRfTLcDwIDAQABoyMwITAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB +/zANBgkqhkiG9w0BAQsFAAOCAgEAXzSBdu+WHdXltdkCY4QWwa6gcFGn90xHNcgL +1yg9iXHZqjNB6hQbbCEAwGxCGX6faVsgQt+i0trEfJdLjbDorMjupWkEmQqSpqsn +LhpNgb+E1HAerUf+/UqdM+DyucRFCCEK2mlpc3INvjT+lIutwx4116KD7+U4x6WF +H6vPNOw/KP4M8VeGTslV9xzU2KV9Bnpv1d8Q34FOIWWxtuEXeZVFBs5fzNxGiWNo +RI2T9GRwoD2dKAXDOXC4Ynsg/eTb6QihuJ49CcdP+yz4k3ZB3lLg4VfSnQO8d57+ +nile98FRYB/e2guyLXW3Q0iT5/Z5xoRdgFlglPx4mI88k1HtQJAH32RjJMtOcQWh +15QaiDLxInQirqWm2BJpTGCjAu4r7NRjkgtevi92a6O2JryPA9gK8kxkRr05YuWW +6zRjESjMlfGt7+/cgFhI6Uu46mWs6fyAtbXIRfmswZ/ZuepiiI7E8UuDEq3mi4TW +nsLrgxifarsbJGAzcMzs9zLzXNl5fe+epP7JI8Mk7hWSsT2RTyaGvWZzJBPqpK5j +wa19hAM8EHiGG3njxPPyBJUgriOCxLM6AGK/5jYk4Ve6xx6QddVfP5VhK8E7zeWz +aGHQRiapIVJpLesux+t3zqY6tQMzT3bR51xUAV3LePTJDL/PEo4XLSNolOer/qmy +KwbQBM0= +-----END CERTIFICATE----- + +# Issuer: CN=TeliaSonera Root CA v1 O=TeliaSonera +# Subject: CN=TeliaSonera Root CA v1 O=TeliaSonera +# Label: "TeliaSonera Root CA v1" +# Serial: 199041966741090107964904287217786801558 +# MD5 Fingerprint: 37:41:49:1b:18:56:9a:26:f5:ad:c2:66:fb:40:a5:4c +# SHA1 Fingerprint: 43:13:bb:96:f1:d5:86:9b:c1:4e:6a:92:f6:cf:f6:34:69:87:82:37 +# SHA256 Fingerprint: dd:69:36:fe:21:f8:f0:77:c1:23:a1:a5:21:c1:22:24:f7:22:55:b7:3e:03:a7:26:06:93:e8:a2:4b:0f:a3:89 +-----BEGIN CERTIFICATE----- +MIIFODCCAyCgAwIBAgIRAJW+FqD3LkbxezmCcvqLzZYwDQYJKoZIhvcNAQEFBQAw +NzEUMBIGA1UECgwLVGVsaWFTb25lcmExHzAdBgNVBAMMFlRlbGlhU29uZXJhIFJv +b3QgQ0EgdjEwHhcNMDcxMDE4MTIwMDUwWhcNMzIxMDE4MTIwMDUwWjA3MRQwEgYD +VQQKDAtUZWxpYVNvbmVyYTEfMB0GA1UEAwwWVGVsaWFTb25lcmEgUm9vdCBDQSB2 +MTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMK+6yfwIaPzaSZVfp3F +VRaRXP3vIb9TgHot0pGMYzHw7CTww6XScnwQbfQ3t+XmfHnqjLWCi65ItqwA3GV1 +7CpNX8GH9SBlK4GoRz6JI5UwFpB/6FcHSOcZrr9FZ7E3GwYq/t75rH2D+1665I+X +Z75Ljo1kB1c4VWk0Nj0TSO9P4tNmHqTPGrdeNjPUtAa9GAH9d4RQAEX1jF3oI7x+ +/jXh7VB7qTCNGdMJjmhnXb88lxhTuylixcpecsHHltTbLaC0H2kD7OriUPEMPPCs +81Mt8Bz17Ww5OXOAFshSsCPN4D7c3TxHoLs1iuKYaIu+5b9y7tL6pe0S7fyYGKkm +dtwoSxAgHNN/Fnct7W+A90m7UwW7XWjH1Mh1Fj+JWov3F0fUTPHSiXk+TT2YqGHe +Oh7S+F4D4MHJHIzTjU3TlTazN19jY5szFPAtJmtTfImMMsJu7D0hADnJoWjiUIMu +sDor8zagrC/kb2HCUQk5PotTubtn2txTuXZZNp1D5SDgPTJghSJRt8czu90VL6R4 +pgd7gUY2BIbdeTXHlSw7sKMXNeVzH7RcWe/a6hBle3rQf5+ztCo3O3CLm1u5K7fs +slESl1MpWtTwEhDcTwK7EpIvYtQ/aUN8Ddb8WHUBiJ1YFkveupD/RwGJBmr2X7KQ +arMCpgKIv7NHfirZ1fpoeDVNAgMBAAGjPzA9MA8GA1UdEwEB/wQFMAMBAf8wCwYD +VR0PBAQDAgEGMB0GA1UdDgQWBBTwj1k4ALP1j5qWDNXr+nuqF+gTEjANBgkqhkiG +9w0BAQUFAAOCAgEAvuRcYk4k9AwI//DTDGjkk0kiP0Qnb7tt3oNmzqjMDfz1mgbl +dxSR651Be5kqhOX//CHBXfDkH1e3damhXwIm/9fH907eT/j3HEbAek9ALCI18Bmx +0GtnLLCo4MBANzX2hFxc469CeP6nyQ1Q6g2EdvZR74NTxnr/DlZJLo961gzmJ1Tj +TQpgcmLNkQfWpb/ImWvtxBnmq0wROMVvMeJuScg/doAmAyYp4Db29iBT4xdwNBed +Y2gea+zDTYa4EzAvXUYNR0PVG6pZDrlcjQZIrXSHX8f8MVRBE+LHIQ6e4B4N4cB7 +Q4WQxYpYxmUKeFfyxiMPAdkgS94P+5KFdSpcc41teyWRyu5FrgZLAMzTsVlQ2jqI +OylDRl6XK1TOU2+NSueW+r9xDkKLfP0ooNBIytrEgUy7onOTJsjrDNYmiLbAJM+7 +vVvrdX3pCI6GMyx5dwlppYn8s3CQh3aP0yK7Qs69cwsgJirQmz1wHiRszYd2qReW +t88NkvuOGKmYSdGe/mBEciG5Ge3C9THxOUiIkCR1VBatzvT4aRRkOfujuLpwQMcn +HL/EVlP6Y2XQ8xwOFvVrhlhNGNTkDY6lnVuR3HYkUD/GKvvZt5y11ubQ2egZixVx +SK236thZiNSQvxaz2emsWWFUyBy6ysHK4bkgTI86k4mloMy/0/Z1pHWWbVY= +-----END CERTIFICATE----- + +# Issuer: CN=E-Tugra Certification Authority O=E-Tuğra EBG Bilişim Teknolojileri ve Hizmetleri A.Ş. OU=E-Tugra Sertifikasyon Merkezi +# Subject: CN=E-Tugra Certification Authority O=E-Tuğra EBG Bilişim Teknolojileri ve Hizmetleri A.Ş. OU=E-Tugra Sertifikasyon Merkezi +# Label: "E-Tugra Certification Authority" +# Serial: 7667447206703254355 +# MD5 Fingerprint: b8:a1:03:63:b0:bd:21:71:70:8a:6f:13:3a:bb:79:49 +# SHA1 Fingerprint: 51:c6:e7:08:49:06:6e:f3:92:d4:5c:a0:0d:6d:a3:62:8f:c3:52:39 +# SHA256 Fingerprint: b0:bf:d5:2b:b0:d7:d9:bd:92:bf:5d:4d:c1:3d:a2:55:c0:2c:54:2f:37:83:65:ea:89:39:11:f5:5e:55:f2:3c +-----BEGIN CERTIFICATE----- +MIIGSzCCBDOgAwIBAgIIamg+nFGby1MwDQYJKoZIhvcNAQELBQAwgbIxCzAJBgNV +BAYTAlRSMQ8wDQYDVQQHDAZBbmthcmExQDA+BgNVBAoMN0UtVHXEn3JhIEVCRyBC +aWxpxZ9pbSBUZWtub2xvamlsZXJpIHZlIEhpem1ldGxlcmkgQS7Fni4xJjAkBgNV +BAsMHUUtVHVncmEgU2VydGlmaWthc3lvbiBNZXJrZXppMSgwJgYDVQQDDB9FLVR1 +Z3JhIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTEzMDMwNTEyMDk0OFoXDTIz +MDMwMzEyMDk0OFowgbIxCzAJBgNVBAYTAlRSMQ8wDQYDVQQHDAZBbmthcmExQDA+ +BgNVBAoMN0UtVHXEn3JhIEVCRyBCaWxpxZ9pbSBUZWtub2xvamlsZXJpIHZlIEhp +em1ldGxlcmkgQS7Fni4xJjAkBgNVBAsMHUUtVHVncmEgU2VydGlmaWthc3lvbiBN +ZXJrZXppMSgwJgYDVQQDDB9FLVR1Z3JhIENlcnRpZmljYXRpb24gQXV0aG9yaXR5 +MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA4vU/kwVRHoViVF56C/UY +B4Oufq9899SKa6VjQzm5S/fDxmSJPZQuVIBSOTkHS0vdhQd2h8y/L5VMzH2nPbxH +D5hw+IyFHnSOkm0bQNGZDbt1bsipa5rAhDGvykPL6ys06I+XawGb1Q5KCKpbknSF +Q9OArqGIW66z6l7LFpp3RMih9lRozt6Plyu6W0ACDGQXwLWTzeHxE2bODHnv0ZEo +q1+gElIwcxmOj+GMB6LDu0rw6h8VqO4lzKRG+Bsi77MOQ7osJLjFLFzUHPhdZL3D +k14opz8n8Y4e0ypQBaNV2cvnOVPAmJ6MVGKLJrD3fY185MaeZkJVgkfnsliNZvcH +fC425lAcP9tDJMW/hkd5s3kc91r0E+xs+D/iWR+V7kI+ua2oMoVJl0b+SzGPWsut +dEcf6ZG33ygEIqDUD13ieU/qbIWGvaimzuT6w+Gzrt48Ue7LE3wBf4QOXVGUnhMM +ti6lTPk5cDZvlsouDERVxcr6XQKj39ZkjFqzAQqptQpHF//vkUAqjqFGOjGY5RH8 +zLtJVor8udBhmm9lbObDyz51Sf6Pp+KJxWfXnUYTTjF2OySznhFlhqt/7x3U+Lzn +rFpct1pHXFXOVbQicVtbC/DP3KBhZOqp12gKY6fgDT+gr9Oq0n7vUaDmUStVkhUX +U8u3Zg5mTPj5dUyQ5xJwx0UCAwEAAaNjMGEwHQYDVR0OBBYEFC7j27JJ0JxUeVz6 +Jyr+zE7S6E5UMA8GA1UdEwEB/wQFMAMBAf8wHwYDVR0jBBgwFoAULuPbsknQnFR5 +XPonKv7MTtLoTlQwDgYDVR0PAQH/BAQDAgEGMA0GCSqGSIb3DQEBCwUAA4ICAQAF +Nzr0TbdF4kV1JI+2d1LoHNgQk2Xz8lkGpD4eKexd0dCrfOAKkEh47U6YA5n+KGCR +HTAduGN8qOY1tfrTYXbm1gdLymmasoR6d5NFFxWfJNCYExL/u6Au/U5Mh/jOXKqY +GwXgAEZKgoClM4so3O0409/lPun++1ndYYRP0lSWE2ETPo+Aab6TR7U1Q9Jauz1c +77NCR807VRMGsAnb/WP2OogKmW9+4c4bU2pEZiNRCHu8W1Ki/QY3OEBhj0qWuJA3 ++GbHeJAAFS6LrVE1Uweoa2iu+U48BybNCAVwzDk/dr2l02cmAYamU9JgO3xDf1WK +vJUawSg5TB9D0pH0clmKuVb8P7Sd2nCcdlqMQ1DujjByTd//SffGqWfZbawCEeI6 +FiWnWAjLb1NBnEg4R2gz0dfHj9R0IdTDBZB6/86WiLEVKV0jq9BgoRJP3vQXzTLl +yb/IQ639Lo7xr+L0mPoSHyDYwKcMhcWQ9DstliaxLL5Mq+ux0orJ23gTDx4JnW2P +AJ8C2sH6H3p6CcRK5ogql5+Ji/03X186zjhZhkuvcQu02PJwT58yE+Owp1fl2tpD +y4Q08ijE6m30Ku/Ba3ba+367hTzSU8JNvnHhRdH9I2cNE3X7z2VnIp2usAnRCf8d +NL/+I5c30jn6PQ0GC7TbO6Orb1wdtn7os4I07QZcJA== +-----END CERTIFICATE----- + +# Issuer: CN=T-TeleSec GlobalRoot Class 2 O=T-Systems Enterprise Services GmbH OU=T-Systems Trust Center +# Subject: CN=T-TeleSec GlobalRoot Class 2 O=T-Systems Enterprise Services GmbH OU=T-Systems Trust Center +# Label: "T-TeleSec GlobalRoot Class 2" +# Serial: 1 +# MD5 Fingerprint: 2b:9b:9e:e4:7b:6c:1f:00:72:1a:cc:c1:77:79:df:6a +# SHA1 Fingerprint: 59:0d:2d:7d:88:4f:40:2e:61:7e:a5:62:32:17:65:cf:17:d8:94:e9 +# SHA256 Fingerprint: 91:e2:f5:78:8d:58:10:eb:a7:ba:58:73:7d:e1:54:8a:8e:ca:cd:01:45:98:bc:0b:14:3e:04:1b:17:05:25:52 +-----BEGIN CERTIFICATE----- +MIIDwzCCAqugAwIBAgIBATANBgkqhkiG9w0BAQsFADCBgjELMAkGA1UEBhMCREUx +KzApBgNVBAoMIlQtU3lzdGVtcyBFbnRlcnByaXNlIFNlcnZpY2VzIEdtYkgxHzAd +BgNVBAsMFlQtU3lzdGVtcyBUcnVzdCBDZW50ZXIxJTAjBgNVBAMMHFQtVGVsZVNl +YyBHbG9iYWxSb290IENsYXNzIDIwHhcNMDgxMDAxMTA0MDE0WhcNMzMxMDAxMjM1 +OTU5WjCBgjELMAkGA1UEBhMCREUxKzApBgNVBAoMIlQtU3lzdGVtcyBFbnRlcnBy +aXNlIFNlcnZpY2VzIEdtYkgxHzAdBgNVBAsMFlQtU3lzdGVtcyBUcnVzdCBDZW50 +ZXIxJTAjBgNVBAMMHFQtVGVsZVNlYyBHbG9iYWxSb290IENsYXNzIDIwggEiMA0G +CSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCqX9obX+hzkeXaXPSi5kfl82hVYAUd +AqSzm1nzHoqvNK38DcLZSBnuaY/JIPwhqgcZ7bBcrGXHX+0CfHt8LRvWurmAwhiC +FoT6ZrAIxlQjgeTNuUk/9k9uN0goOA/FvudocP05l03Sx5iRUKrERLMjfTlH6VJi +1hKTXrcxlkIF+3anHqP1wvzpesVsqXFP6st4vGCvx9702cu+fjOlbpSD8DT6Iavq +jnKgP6TeMFvvhk1qlVtDRKgQFRzlAVfFmPHmBiiRqiDFt1MmUUOyCxGVWOHAD3bZ +wI18gfNycJ5v/hqO2V81xrJvNHy+SE/iWjnX2J14np+GPgNeGYtEotXHAgMBAAGj +QjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBS/ +WSA2AHmgoCJrjNXyYdK4LMuCSjANBgkqhkiG9w0BAQsFAAOCAQEAMQOiYQsfdOhy +NsZt+U2e+iKo4YFWz827n+qrkRk4r6p8FU3ztqONpfSO9kSpp+ghla0+AGIWiPAC +uvxhI+YzmzB6azZie60EI4RYZeLbK4rnJVM3YlNfvNoBYimipidx5joifsFvHZVw +IEoHNN/q/xWA5brXethbdXwFeilHfkCoMRN3zUA7tFFHei4R40cR3p1m0IvVVGb6 +g1XqfMIpiRvpb7PO4gWEyS8+eIVibslfwXhjdFjASBgMmTnrpMwatXlajRWc2BQN +9noHV8cigwUtPJslJj0Ys6lDfMjIq2SPDqO/nBudMNva0Bkuqjzx+zOAduTNrRlP +BSeOE6Fuwg== +-----END CERTIFICATE----- + +# Issuer: CN=Atos TrustedRoot 2011 O=Atos +# Subject: CN=Atos TrustedRoot 2011 O=Atos +# Label: "Atos TrustedRoot 2011" +# Serial: 6643877497813316402 +# MD5 Fingerprint: ae:b9:c4:32:4b:ac:7f:5d:66:cc:77:94:bb:2a:77:56 +# SHA1 Fingerprint: 2b:b1:f5:3e:55:0c:1d:c5:f1:d4:e6:b7:6a:46:4b:55:06:02:ac:21 +# SHA256 Fingerprint: f3:56:be:a2:44:b7:a9:1e:b3:5d:53:ca:9a:d7:86:4a:ce:01:8e:2d:35:d5:f8:f9:6d:df:68:a6:f4:1a:a4:74 +-----BEGIN CERTIFICATE----- +MIIDdzCCAl+gAwIBAgIIXDPLYixfszIwDQYJKoZIhvcNAQELBQAwPDEeMBwGA1UE +AwwVQXRvcyBUcnVzdGVkUm9vdCAyMDExMQ0wCwYDVQQKDARBdG9zMQswCQYDVQQG +EwJERTAeFw0xMTA3MDcxNDU4MzBaFw0zMDEyMzEyMzU5NTlaMDwxHjAcBgNVBAMM +FUF0b3MgVHJ1c3RlZFJvb3QgMjAxMTENMAsGA1UECgwEQXRvczELMAkGA1UEBhMC +REUwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCVhTuXbyo7LjvPpvMp +Nb7PGKw+qtn4TaA+Gke5vJrf8v7MPkfoepbCJI419KkM/IL9bcFyYie96mvr54rM +VD6QUM+A1JX76LWC1BTFtqlVJVfbsVD2sGBkWXppzwO3bw2+yj5vdHLqqjAqc2K+ +SZFhyBH+DgMq92og3AIVDV4VavzjgsG1xZ1kCWyjWZgHJ8cblithdHFsQ/H3NYkQ +4J7sVaE3IqKHBAUsR320HLliKWYoyrfhk/WklAOZuXCFteZI6o1Q/NnezG8HDt0L +cp2AMBYHlT8oDv3FdU9T1nSatCQujgKRz3bFmx5VdJx4IbHwLfELn8LVlhgf8FQi +eowHAgMBAAGjfTB7MB0GA1UdDgQWBBSnpQaxLKYJYO7Rl+lwrrw7GWzbITAPBgNV +HRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFKelBrEspglg7tGX6XCuvDsZbNshMBgG +A1UdIAQRMA8wDQYLKwYBBAGwLQMEAQEwDgYDVR0PAQH/BAQDAgGGMA0GCSqGSIb3 +DQEBCwUAA4IBAQAmdzTblEiGKkGdLD4GkGDEjKwLVLgfuXvTBznk+j57sj1O7Z8j +vZfza1zv7v1Apt+hk6EKhqzvINB5Ab149xnYJDE0BAGmuhWawyfc2E8PzBhj/5kP +DpFrdRbhIfzYJsdHt6bPWHJxfrrhTZVHO8mvbaG0weyJ9rQPOLXiZNwlz6bb65pc +maHFCN795trV1lpFDMS3wrUU77QR/w4VtfX128a961qn8FYiqTxlVMYVqL2Gns2D +lmh6cYGJ4Qvh6hEbaAjMaZ7snkGeRDImeuKHCnE96+RapNLbxc3G3mB/ufNPRJLv +KrcYPqcZ2Qt9sTdBQrC6YB3y/gkRsPCHe6ed +-----END CERTIFICATE----- + +# Issuer: CN=QuoVadis Root CA 1 G3 O=QuoVadis Limited +# Subject: CN=QuoVadis Root CA 1 G3 O=QuoVadis Limited +# Label: "QuoVadis Root CA 1 G3" +# Serial: 687049649626669250736271037606554624078720034195 +# MD5 Fingerprint: a4:bc:5b:3f:fe:37:9a:fa:64:f0:e2:fa:05:3d:0b:ab +# SHA1 Fingerprint: 1b:8e:ea:57:96:29:1a:c9:39:ea:b8:0a:81:1a:73:73:c0:93:79:67 +# SHA256 Fingerprint: 8a:86:6f:d1:b2:76:b5:7e:57:8e:92:1c:65:82:8a:2b:ed:58:e9:f2:f2:88:05:41:34:b7:f1:f4:bf:c9:cc:74 +-----BEGIN CERTIFICATE----- +MIIFYDCCA0igAwIBAgIUeFhfLq0sGUvjNwc1NBMotZbUZZMwDQYJKoZIhvcNAQEL +BQAwSDELMAkGA1UEBhMCQk0xGTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxHjAc +BgNVBAMTFVF1b1ZhZGlzIFJvb3QgQ0EgMSBHMzAeFw0xMjAxMTIxNzI3NDRaFw00 +MjAxMTIxNzI3NDRaMEgxCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBM +aW1pdGVkMR4wHAYDVQQDExVRdW9WYWRpcyBSb290IENBIDEgRzMwggIiMA0GCSqG +SIb3DQEBAQUAA4ICDwAwggIKAoICAQCgvlAQjunybEC0BJyFuTHK3C3kEakEPBtV +wedYMB0ktMPvhd6MLOHBPd+C5k+tR4ds7FtJwUrVu4/sh6x/gpqG7D0DmVIB0jWe +rNrwU8lmPNSsAgHaJNM7qAJGr6Qc4/hzWHa39g6QDbXwz8z6+cZM5cOGMAqNF341 +68Xfuw6cwI2H44g4hWf6Pser4BOcBRiYz5P1sZK0/CPTz9XEJ0ngnjybCKOLXSoh +4Pw5qlPafX7PGglTvF0FBM+hSo+LdoINofjSxxR3W5A2B4GbPgb6Ul5jxaYA/qXp +UhtStZI5cgMJYr2wYBZupt0lwgNm3fME0UDiTouG9G/lg6AnhF4EwfWQvTA9xO+o +abw4m6SkltFi2mnAAZauy8RRNOoMqv8hjlmPSlzkYZqn0ukqeI1RPToV7qJZjqlc +3sX5kCLliEVx3ZGZbHqfPT2YfF72vhZooF6uCyP8Wg+qInYtyaEQHeTTRCOQiJ/G +KubX9ZqzWB4vMIkIG1SitZgj7Ah3HJVdYdHLiZxfokqRmu8hqkkWCKi9YSgxyXSt +hfbZxbGL0eUQMk1fiyA6PEkfM4VZDdvLCXVDaXP7a3F98N/ETH3Goy7IlXnLc6KO +Tk0k+17kBL5yG6YnLUlamXrXXAkgt3+UuU/xDRxeiEIbEbfnkduebPRq34wGmAOt +zCjvpUfzUwIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIB +BjAdBgNVHQ4EFgQUo5fW816iEOGrRZ88F2Q87gFwnMwwDQYJKoZIhvcNAQELBQAD +ggIBABj6W3X8PnrHX3fHyt/PX8MSxEBd1DKquGrX1RUVRpgjpeaQWxiZTOOtQqOC +MTaIzen7xASWSIsBx40Bz1szBpZGZnQdT+3Btrm0DWHMY37XLneMlhwqI2hrhVd2 +cDMT/uFPpiN3GPoajOi9ZcnPP/TJF9zrx7zABC4tRi9pZsMbj/7sPtPKlL92CiUN +qXsCHKnQO18LwIE6PWThv6ctTr1NxNgpxiIY0MWscgKCP6o6ojoilzHdCGPDdRS5 +YCgtW2jgFqlmgiNR9etT2DGbe+m3nUvriBbP+V04ikkwj+3x6xn0dxoxGE1nVGwv +b2X52z3sIexe9PSLymBlVNFxZPT5pqOBMzYzcfCkeF9OrYMh3jRJjehZrJ3ydlo2 +8hP0r+AJx2EqbPfgna67hkooby7utHnNkDPDs3b69fBsnQGQ+p6Q9pxyz0fawx/k +NSBT8lTR32GDpgLiJTjehTItXnOQUl1CxM49S+H5GYQd1aJQzEH7QRTDvdbJWqNj +ZgKAvQU6O0ec7AAmTPWIUb+oI38YB7AL7YsmoWTTYUrrXJ/es69nA7Mf3W1daWhp +q1467HxpvMc7hU6eFbm0FU/DlXpY18ls6Wy58yljXrQs8C097Vpl4KlbQMJImYFt +nh8GKjwStIsPm6Ik8KaN1nrgS7ZklmOVhMJKzRwuJIczYOXD +-----END CERTIFICATE----- + +# Issuer: CN=QuoVadis Root CA 2 G3 O=QuoVadis Limited +# Subject: CN=QuoVadis Root CA 2 G3 O=QuoVadis Limited +# Label: "QuoVadis Root CA 2 G3" +# Serial: 390156079458959257446133169266079962026824725800 +# MD5 Fingerprint: af:0c:86:6e:bf:40:2d:7f:0b:3e:12:50:ba:12:3d:06 +# SHA1 Fingerprint: 09:3c:61:f3:8b:8b:dc:7d:55:df:75:38:02:05:00:e1:25:f5:c8:36 +# SHA256 Fingerprint: 8f:e4:fb:0a:f9:3a:4d:0d:67:db:0b:eb:b2:3e:37:c7:1b:f3:25:dc:bc:dd:24:0e:a0:4d:af:58:b4:7e:18:40 +-----BEGIN CERTIFICATE----- +MIIFYDCCA0igAwIBAgIURFc0JFuBiZs18s64KztbpybwdSgwDQYJKoZIhvcNAQEL +BQAwSDELMAkGA1UEBhMCQk0xGTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxHjAc +BgNVBAMTFVF1b1ZhZGlzIFJvb3QgQ0EgMiBHMzAeFw0xMjAxMTIxODU5MzJaFw00 +MjAxMTIxODU5MzJaMEgxCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBM +aW1pdGVkMR4wHAYDVQQDExVRdW9WYWRpcyBSb290IENBIDIgRzMwggIiMA0GCSqG +SIb3DQEBAQUAA4ICDwAwggIKAoICAQChriWyARjcV4g/Ruv5r+LrI3HimtFhZiFf +qq8nUeVuGxbULX1QsFN3vXg6YOJkApt8hpvWGo6t/x8Vf9WVHhLL5hSEBMHfNrMW +n4rjyduYNM7YMxcoRvynyfDStNVNCXJJ+fKH46nafaF9a7I6JaltUkSs+L5u+9ym +c5GQYaYDFCDy54ejiK2toIz/pgslUiXnFgHVy7g1gQyjO/Dh4fxaXc6AcW34Sas+ +O7q414AB+6XrW7PFXmAqMaCvN+ggOp+oMiwMzAkd056OXbxMmO7FGmh77FOm6RQ1 +o9/NgJ8MSPsc9PG/Srj61YxxSscfrf5BmrODXfKEVu+lV0POKa2Mq1W/xPtbAd0j +IaFYAI7D0GoT7RPjEiuA3GfmlbLNHiJuKvhB1PLKFAeNilUSxmn1uIZoL1NesNKq +IcGY5jDjZ1XHm26sGahVpkUG0CM62+tlXSoREfA7T8pt9DTEceT/AFr2XK4jYIVz +8eQQsSWu1ZK7E8EM4DnatDlXtas1qnIhO4M15zHfeiFuuDIIfR0ykRVKYnLP43eh +vNURG3YBZwjgQQvD6xVu+KQZ2aKrr+InUlYrAoosFCT5v0ICvybIxo/gbjh9Uy3l +7ZizlWNof/k19N+IxWA1ksB8aRxhlRbQ694Lrz4EEEVlWFA4r0jyWbYW8jwNkALG +cC4BrTwV1wIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIB +BjAdBgNVHQ4EFgQU7edvdlq/YOxJW8ald7tyFnGbxD0wDQYJKoZIhvcNAQELBQAD +ggIBAJHfgD9DCX5xwvfrs4iP4VGyvD11+ShdyLyZm3tdquXK4Qr36LLTn91nMX66 +AarHakE7kNQIXLJgapDwyM4DYvmL7ftuKtwGTTwpD4kWilhMSA/ohGHqPHKmd+RC +roijQ1h5fq7KpVMNqT1wvSAZYaRsOPxDMuHBR//47PERIjKWnML2W2mWeyAMQ0Ga +W/ZZGYjeVYg3UQt4XAoeo0L9x52ID8DyeAIkVJOviYeIyUqAHerQbj5hLja7NQ4n +lv1mNDthcnPxFlxHBlRJAHpYErAK74X9sbgzdWqTHBLmYF5vHX/JHyPLhGGfHoJE ++V+tYlUkmlKY7VHnoX6XOuYvHxHaU4AshZ6rNRDbIl9qxV6XU/IyAgkwo1jwDQHV +csaxfGl7w/U2Rcxhbl5MlMVerugOXou/983g7aEOGzPuVBj+D77vfoRrQ+NwmNtd +dbINWQeFFSM51vHfqSYP1kjHs6Yi9TM3WpVHn3u6GBVv/9YUZINJ0gpnIdsPNWNg +KCLjsZWDzYWm3S8P52dSbrsvhXz1SnPnxT7AvSESBT/8twNJAlvIJebiVDj1eYeM +HVOyToV7BjjHLPj4sHKNJeV3UvQDHEimUF+IIDBu8oJDqz2XhOdT+yHBTw8imoa4 +WSr2Rz0ZiC3oheGe7IUIarFsNMkd7EgrO3jtZsSOeWmD3n+M +-----END CERTIFICATE----- + +# Issuer: CN=QuoVadis Root CA 3 G3 O=QuoVadis Limited +# Subject: CN=QuoVadis Root CA 3 G3 O=QuoVadis Limited +# Label: "QuoVadis Root CA 3 G3" +# Serial: 268090761170461462463995952157327242137089239581 +# MD5 Fingerprint: df:7d:b9:ad:54:6f:68:a1:df:89:57:03:97:43:b0:d7 +# SHA1 Fingerprint: 48:12:bd:92:3c:a8:c4:39:06:e7:30:6d:27:96:e6:a4:cf:22:2e:7d +# SHA256 Fingerprint: 88:ef:81:de:20:2e:b0:18:45:2e:43:f8:64:72:5c:ea:5f:bd:1f:c2:d9:d2:05:73:07:09:c5:d8:b8:69:0f:46 +-----BEGIN CERTIFICATE----- +MIIFYDCCA0igAwIBAgIULvWbAiin23r/1aOp7r0DoM8Sah0wDQYJKoZIhvcNAQEL +BQAwSDELMAkGA1UEBhMCQk0xGTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxHjAc +BgNVBAMTFVF1b1ZhZGlzIFJvb3QgQ0EgMyBHMzAeFw0xMjAxMTIyMDI2MzJaFw00 +MjAxMTIyMDI2MzJaMEgxCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBM +aW1pdGVkMR4wHAYDVQQDExVRdW9WYWRpcyBSb290IENBIDMgRzMwggIiMA0GCSqG +SIb3DQEBAQUAA4ICDwAwggIKAoICAQCzyw4QZ47qFJenMioKVjZ/aEzHs286IxSR +/xl/pcqs7rN2nXrpixurazHb+gtTTK/FpRp5PIpM/6zfJd5O2YIyC0TeytuMrKNu +FoM7pmRLMon7FhY4futD4tN0SsJiCnMK3UmzV9KwCoWdcTzeo8vAMvMBOSBDGzXR +U7Ox7sWTaYI+FrUoRqHe6okJ7UO4BUaKhvVZR74bbwEhELn9qdIoyhA5CcoTNs+c +ra1AdHkrAj80//ogaX3T7mH1urPnMNA3I4ZyYUUpSFlob3emLoG+B01vr87ERROR +FHAGjx+f+IdpsQ7vw4kZ6+ocYfx6bIrc1gMLnia6Et3UVDmrJqMz6nWB2i3ND0/k +A9HvFZcba5DFApCTZgIhsUfei5pKgLlVj7WiL8DWM2fafsSntARE60f75li59wzw +eyuxwHApw0BiLTtIadwjPEjrewl5qW3aqDCYz4ByA4imW0aucnl8CAMhZa634Ryl +sSqiMd5mBPfAdOhx3v89WcyWJhKLhZVXGqtrdQtEPREoPHtht+KPZ0/l7DxMYIBp +VzgeAVuNVejH38DMdyM0SXV89pgR6y3e7UEuFAUCf+D+IOs15xGsIs5XPd7JMG0Q +A4XN8f+MFrXBsj6IbGB/kE+V9/YtrQE5BwT6dYB9v0lQ7e/JxHwc64B+27bQ3RP+ +ydOc17KXqQIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIB +BjAdBgNVHQ4EFgQUxhfQvKjqAkPyGwaZXSuQILnXnOQwDQYJKoZIhvcNAQELBQAD +ggIBADRh2Va1EodVTd2jNTFGu6QHcrxfYWLopfsLN7E8trP6KZ1/AvWkyaiTt3px +KGmPc+FSkNrVvjrlt3ZqVoAh313m6Tqe5T72omnHKgqwGEfcIHB9UqM+WXzBusnI +FUBhynLWcKzSt/Ac5IYp8M7vaGPQtSCKFWGafoaYtMnCdvvMujAWzKNhxnQT5Wvv +oxXqA/4Ti2Tk08HS6IT7SdEQTXlm66r99I0xHnAUrdzeZxNMgRVhvLfZkXdxGYFg +u/BYpbWcC/ePIlUnwEsBbTuZDdQdm2NnL9DuDcpmvJRPpq3t/O5jrFc/ZSXPsoaP +0Aj/uHYUbt7lJ+yreLVTubY/6CD50qi+YUbKh4yE8/nxoGibIh6BJpsQBJFxwAYf +3KDTuVan45gtf4Od34wrnDKOMpTwATwiKp9Dwi7DmDkHOHv8XgBCH/MyJnmDhPbl +8MFREsALHgQjDFSlTC9JxUrRtm5gDWv8a4uFJGS3iQ6rJUdbPM9+Sb3H6QrG2vd+ +DhcI00iX0HGS8A85PjRqHH3Y8iKuu2n0M7SmSFXRDw4m6Oy2Cy2nhTXN/VnIn9HN +PlopNLk9hM6xZdRZkZFWdSHBd575euFgndOtBBj0fOtek49TSiIp+EgrPk2GrFt/ +ywaZWWDYWGWVjUTR939+J399roD1B0y2PpxxVJkES/1Y+Zj0 +-----END CERTIFICATE----- + +# Issuer: CN=DigiCert Assured ID Root G2 O=DigiCert Inc OU=www.digicert.com +# Subject: CN=DigiCert Assured ID Root G2 O=DigiCert Inc OU=www.digicert.com +# Label: "DigiCert Assured ID Root G2" +# Serial: 15385348160840213938643033620894905419 +# MD5 Fingerprint: 92:38:b9:f8:63:24:82:65:2c:57:33:e6:fe:81:8f:9d +# SHA1 Fingerprint: a1:4b:48:d9:43:ee:0a:0e:40:90:4f:3c:e0:a4:c0:91:93:51:5d:3f +# SHA256 Fingerprint: 7d:05:eb:b6:82:33:9f:8c:94:51:ee:09:4e:eb:fe:fa:79:53:a1:14:ed:b2:f4:49:49:45:2f:ab:7d:2f:c1:85 +-----BEGIN CERTIFICATE----- +MIIDljCCAn6gAwIBAgIQC5McOtY5Z+pnI7/Dr5r0SzANBgkqhkiG9w0BAQsFADBl +MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 +d3cuZGlnaWNlcnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJv +b3QgRzIwHhcNMTMwODAxMTIwMDAwWhcNMzgwMTE1MTIwMDAwWjBlMQswCQYDVQQG +EwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNl +cnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3QgRzIwggEi +MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDZ5ygvUj82ckmIkzTz+GoeMVSA +n61UQbVH35ao1K+ALbkKz3X9iaV9JPrjIgwrvJUXCzO/GU1BBpAAvQxNEP4Htecc +biJVMWWXvdMX0h5i89vqbFCMP4QMls+3ywPgym2hFEwbid3tALBSfK+RbLE4E9Hp +EgjAALAcKxHad3A2m67OeYfcgnDmCXRwVWmvo2ifv922ebPynXApVfSr/5Vh88lA +bx3RvpO704gqu52/clpWcTs/1PPRCv4o76Pu2ZmvA9OPYLfykqGxvYmJHzDNw6Yu +YjOuFgJ3RFrngQo8p0Quebg/BLxcoIfhG69Rjs3sLPr4/m3wOnyqi+RnlTGNAgMB +AAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgGGMB0GA1UdDgQW +BBTOw0q5mVXyuNtgv6l+vVa1lzan1jANBgkqhkiG9w0BAQsFAAOCAQEAyqVVjOPI +QW5pJ6d1Ee88hjZv0p3GeDgdaZaikmkuOGybfQTUiaWxMTeKySHMq2zNixya1r9I +0jJmwYrA8y8678Dj1JGG0VDjA9tzd29KOVPt3ibHtX2vK0LRdWLjSisCx1BL4Gni +lmwORGYQRI+tBev4eaymG+g3NJ1TyWGqolKvSnAWhsI6yLETcDbYz+70CjTVW0z9 +B5yiutkBclzzTcHdDrEcDcRjvq30FPuJ7KJBDkzMyFdA0G4Dqs0MjomZmWzwPDCv +ON9vvKO+KSAnq3T/EyJ43pdSVR6DtVQgA+6uwE9W3jfMw3+qBCe703e4YtsXfJwo +IhNzbM8m9Yop5w== +-----END CERTIFICATE----- + +# Issuer: CN=DigiCert Assured ID Root G3 O=DigiCert Inc OU=www.digicert.com +# Subject: CN=DigiCert Assured ID Root G3 O=DigiCert Inc OU=www.digicert.com +# Label: "DigiCert Assured ID Root G3" +# Serial: 15459312981008553731928384953135426796 +# MD5 Fingerprint: 7c:7f:65:31:0c:81:df:8d:ba:3e:99:e2:5c:ad:6e:fb +# SHA1 Fingerprint: f5:17:a2:4f:9a:48:c6:c9:f8:a2:00:26:9f:dc:0f:48:2c:ab:30:89 +# SHA256 Fingerprint: 7e:37:cb:8b:4c:47:09:0c:ab:36:55:1b:a6:f4:5d:b8:40:68:0f:ba:16:6a:95:2d:b1:00:71:7f:43:05:3f:c2 +-----BEGIN CERTIFICATE----- +MIICRjCCAc2gAwIBAgIQC6Fa+h3foLVJRK/NJKBs7DAKBggqhkjOPQQDAzBlMQsw +CQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cu +ZGlnaWNlcnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3Qg +RzMwHhcNMTMwODAxMTIwMDAwWhcNMzgwMTE1MTIwMDAwWjBlMQswCQYDVQQGEwJV +UzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQu +Y29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3QgRzMwdjAQBgcq +hkjOPQIBBgUrgQQAIgNiAAQZ57ysRGXtzbg/WPuNsVepRC0FFfLvC/8QdJ+1YlJf +Zn4f5dwbRXkLzMZTCp2NXQLZqVneAlr2lSoOjThKiknGvMYDOAdfVdp+CW7if17Q +RSAPWXYQ1qAk8C3eNvJsKTmjQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/ +BAQDAgGGMB0GA1UdDgQWBBTL0L2p4ZgFUaFNN6KDec6NHSrkhDAKBggqhkjOPQQD +AwNnADBkAjAlpIFFAmsSS3V0T8gj43DydXLefInwz5FyYZ5eEJJZVrmDxxDnOOlY +JjZ91eQ0hjkCMHw2U/Aw5WJjOpnitqM7mzT6HtoQknFekROn3aRukswy1vUhZscv +6pZjamVFkpUBtA== +-----END CERTIFICATE----- + +# Issuer: CN=DigiCert Global Root G2 O=DigiCert Inc OU=www.digicert.com +# Subject: CN=DigiCert Global Root G2 O=DigiCert Inc OU=www.digicert.com +# Label: "DigiCert Global Root G2" +# Serial: 4293743540046975378534879503202253541 +# MD5 Fingerprint: e4:a6:8a:c8:54:ac:52:42:46:0a:fd:72:48:1b:2a:44 +# SHA1 Fingerprint: df:3c:24:f9:bf:d6:66:76:1b:26:80:73:fe:06:d1:cc:8d:4f:82:a4 +# SHA256 Fingerprint: cb:3c:cb:b7:60:31:e5:e0:13:8f:8d:d3:9a:23:f9:de:47:ff:c3:5e:43:c1:14:4c:ea:27:d4:6a:5a:b1:cb:5f +-----BEGIN CERTIFICATE----- +MIIDjjCCAnagAwIBAgIQAzrx5qcRqaC7KGSxHQn65TANBgkqhkiG9w0BAQsFADBh +MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 +d3cuZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBH +MjAeFw0xMzA4MDExMjAwMDBaFw0zODAxMTUxMjAwMDBaMGExCzAJBgNVBAYTAlVT +MRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5j +b20xIDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IEcyMIIBIjANBgkqhkiG +9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuzfNNNx7a8myaJCtSnX/RrohCgiN9RlUyfuI +2/Ou8jqJkTx65qsGGmvPrC3oXgkkRLpimn7Wo6h+4FR1IAWsULecYxpsMNzaHxmx +1x7e/dfgy5SDN67sH0NO3Xss0r0upS/kqbitOtSZpLYl6ZtrAGCSYP9PIUkY92eQ +q2EGnI/yuum06ZIya7XzV+hdG82MHauVBJVJ8zUtluNJbd134/tJS7SsVQepj5Wz +tCO7TG1F8PapspUwtP1MVYwnSlcUfIKdzXOS0xZKBgyMUNGPHgm+F6HmIcr9g+UQ +vIOlCsRnKPZzFBQ9RnbDhxSJITRNrw9FDKZJobq7nMWxM4MphQIDAQABo0IwQDAP +BgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBhjAdBgNVHQ4EFgQUTiJUIBiV +5uNu5g/6+rkS7QYXjzkwDQYJKoZIhvcNAQELBQADggEBAGBnKJRvDkhj6zHd6mcY +1Yl9PMWLSn/pvtsrF9+wX3N3KjITOYFnQoQj8kVnNeyIv/iPsGEMNKSuIEyExtv4 +NeF22d+mQrvHRAiGfzZ0JFrabA0UWTW98kndth/Jsw1HKj2ZL7tcu7XUIOGZX1NG +Fdtom/DzMNU+MeKNhJ7jitralj41E6Vf8PlwUHBHQRFXGU7Aj64GxJUTFy8bJZ91 +8rGOmaFvE7FBcf6IKshPECBV1/MUReXgRPTqh5Uykw7+U0b6LJ3/iyK5S9kJRaTe +pLiaWN0bfVKfjllDiIGknibVb63dDcY3fe0Dkhvld1927jyNxF1WW6LZZm6zNTfl +MrY= +-----END CERTIFICATE----- + +# Issuer: CN=DigiCert Global Root G3 O=DigiCert Inc OU=www.digicert.com +# Subject: CN=DigiCert Global Root G3 O=DigiCert Inc OU=www.digicert.com +# Label: "DigiCert Global Root G3" +# Serial: 7089244469030293291760083333884364146 +# MD5 Fingerprint: f5:5d:a4:50:a5:fb:28:7e:1e:0f:0d:cc:96:57:56:ca +# SHA1 Fingerprint: 7e:04:de:89:6a:3e:66:6d:00:e6:87:d3:3f:fa:d9:3b:e8:3d:34:9e +# SHA256 Fingerprint: 31:ad:66:48:f8:10:41:38:c7:38:f3:9e:a4:32:01:33:39:3e:3a:18:cc:02:29:6e:f9:7c:2a:c9:ef:67:31:d0 +-----BEGIN CERTIFICATE----- +MIICPzCCAcWgAwIBAgIQBVVWvPJepDU1w6QP1atFcjAKBggqhkjOPQQDAzBhMQsw +CQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cu +ZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBHMzAe +Fw0xMzA4MDExMjAwMDBaFw0zODAxMTUxMjAwMDBaMGExCzAJBgNVBAYTAlVTMRUw +EwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20x +IDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IEczMHYwEAYHKoZIzj0CAQYF +K4EEACIDYgAE3afZu4q4C/sLfyHS8L6+c/MzXRq8NOrexpu80JX28MzQC7phW1FG +fp4tn+6OYwwX7Adw9c+ELkCDnOg/QW07rdOkFFk2eJ0DQ+4QE2xy3q6Ip6FrtUPO +Z9wj/wMco+I+o0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBhjAd +BgNVHQ4EFgQUs9tIpPmhxdiuNkHMEWNpYim8S8YwCgYIKoZIzj0EAwMDaAAwZQIx +AK288mw/EkrRLTnDCgmXc/SINoyIJ7vmiI1Qhadj+Z4y3maTD/HMsQmP3Wyr+mt/ +oAIwOWZbwmSNuJ5Q3KjVSaLtx9zRSX8XAbjIho9OjIgrqJqpisXRAL34VOKa5Vt8 +sycX +-----END CERTIFICATE----- + +# Issuer: CN=DigiCert Trusted Root G4 O=DigiCert Inc OU=www.digicert.com +# Subject: CN=DigiCert Trusted Root G4 O=DigiCert Inc OU=www.digicert.com +# Label: "DigiCert Trusted Root G4" +# Serial: 7451500558977370777930084869016614236 +# MD5 Fingerprint: 78:f2:fc:aa:60:1f:2f:b4:eb:c9:37:ba:53:2e:75:49 +# SHA1 Fingerprint: dd:fb:16:cd:49:31:c9:73:a2:03:7d:3f:c8:3a:4d:7d:77:5d:05:e4 +# SHA256 Fingerprint: 55:2f:7b:dc:f1:a7:af:9e:6c:e6:72:01:7f:4f:12:ab:f7:72:40:c7:8e:76:1a:c2:03:d1:d9:d2:0a:c8:99:88 +-----BEGIN CERTIFICATE----- +MIIFkDCCA3igAwIBAgIQBZsbV56OITLiOQe9p3d1XDANBgkqhkiG9w0BAQwFADBi +MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 +d3cuZGlnaWNlcnQuY29tMSEwHwYDVQQDExhEaWdpQ2VydCBUcnVzdGVkIFJvb3Qg +RzQwHhcNMTMwODAxMTIwMDAwWhcNMzgwMTE1MTIwMDAwWjBiMQswCQYDVQQGEwJV +UzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQu +Y29tMSEwHwYDVQQDExhEaWdpQ2VydCBUcnVzdGVkIFJvb3QgRzQwggIiMA0GCSqG +SIb3DQEBAQUAA4ICDwAwggIKAoICAQC/5pBzaN675F1KPDAiMGkz7MKnJS7JIT3y +ithZwuEppz1Yq3aaza57G4QNxDAf8xukOBbrVsaXbR2rsnnyyhHS5F/WBTxSD1If +xp4VpX6+n6lXFllVcq9ok3DCsrp1mWpzMpTREEQQLt+C8weE5nQ7bXHiLQwb7iDV +ySAdYyktzuxeTsiT+CFhmzTrBcZe7FsavOvJz82sNEBfsXpm7nfISKhmV1efVFiO +DCu3T6cw2Vbuyntd463JT17lNecxy9qTXtyOj4DatpGYQJB5w3jHtrHEtWoYOAMQ +jdjUN6QuBX2I9YI+EJFwq1WCQTLX2wRzKm6RAXwhTNS8rhsDdV14Ztk6MUSaM0C/ +CNdaSaTC5qmgZ92kJ7yhTzm1EVgX9yRcRo9k98FpiHaYdj1ZXUJ2h4mXaXpI8OCi +EhtmmnTK3kse5w5jrubU75KSOp493ADkRSWJtppEGSt+wJS00mFt6zPZxd9LBADM +fRyVw4/3IbKyEbe7f/LVjHAsQWCqsWMYRJUadmJ+9oCw++hkpjPRiQfhvbfmQ6QY +uKZ3AeEPlAwhHbJUKSWJbOUOUlFHdL4mrLZBdd56rF+NP8m800ERElvlEFDrMcXK +chYiCd98THU/Y+whX8QgUWtvsauGi0/C1kVfnSD8oR7FwI+isX4KJpn15GkvmB0t +9dmpsh3lGwIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIB +hjAdBgNVHQ4EFgQU7NfjgtJxXWRM3y5nP+e6mK4cD08wDQYJKoZIhvcNAQEMBQAD +ggIBALth2X2pbL4XxJEbw6GiAI3jZGgPVs93rnD5/ZpKmbnJeFwMDF/k5hQpVgs2 +SV1EY+CtnJYYZhsjDT156W1r1lT40jzBQ0CuHVD1UvyQO7uYmWlrx8GnqGikJ9yd ++SeuMIW59mdNOj6PWTkiU0TryF0Dyu1Qen1iIQqAyHNm0aAFYF/opbSnr6j3bTWc +fFqK1qI4mfN4i/RN0iAL3gTujJtHgXINwBQy7zBZLq7gcfJW5GqXb5JQbZaNaHqa +sjYUegbyJLkJEVDXCLG4iXqEI2FCKeWjzaIgQdfRnGTZ6iahixTXTBmyUEFxPT9N +cCOGDErcgdLMMpSEDQgJlxxPwO5rIHQw0uA5NBCFIRUBCOhVMt5xSdkoF1BN5r5N +0XWs0Mr7QbhDparTwwVETyw2m+L64kW4I1NsBm9nVX9GtUw/bihaeSbSpKhil9Ie +4u1Ki7wb/UdKDd9nZn6yW0HQO+T0O/QEY+nvwlQAUaCKKsnOeMzV6ocEGLPOr0mI +r/OSmbaz5mEP0oUA51Aa5BuVnRmhuZyxm7EAHu/QD09CbMkKvO5D+jpxpchNJqU1 +/YldvIViHTLSoCtU7ZpXwdv6EM8Zt4tKG48BtieVU+i2iW1bvGjUI+iLUaJW+fCm +gKDWHrO8Dw9TdSmq6hN35N6MgSGtBxBHEa2HPQfRdbzP82Z+ +-----END CERTIFICATE----- + +# Issuer: CN=Certification Authority of WoSign O=WoSign CA Limited +# Subject: CN=Certification Authority of WoSign O=WoSign CA Limited +# Label: "WoSign" +# Serial: 125491772294754854453622855443212256657 +# MD5 Fingerprint: a1:f2:f9:b5:d2:c8:7a:74:b8:f3:05:f1:d7:e1:84:8d +# SHA1 Fingerprint: b9:42:94:bf:91:ea:8f:b6:4b:e6:10:97:c7:fb:00:13:59:b6:76:cb +# SHA256 Fingerprint: 4b:22:d5:a6:ae:c9:9f:3c:db:79:aa:5e:c0:68:38:47:9c:d5:ec:ba:71:64:f7:f2:2d:c1:d6:5f:63:d8:57:08 +-----BEGIN CERTIFICATE----- +MIIFdjCCA16gAwIBAgIQXmjWEXGUY1BWAGjzPsnFkTANBgkqhkiG9w0BAQUFADBV +MQswCQYDVQQGEwJDTjEaMBgGA1UEChMRV29TaWduIENBIExpbWl0ZWQxKjAoBgNV +BAMTIUNlcnRpZmljYXRpb24gQXV0aG9yaXR5IG9mIFdvU2lnbjAeFw0wOTA4MDgw +MTAwMDFaFw0zOTA4MDgwMTAwMDFaMFUxCzAJBgNVBAYTAkNOMRowGAYDVQQKExFX +b1NpZ24gQ0EgTGltaXRlZDEqMCgGA1UEAxMhQ2VydGlmaWNhdGlvbiBBdXRob3Jp +dHkgb2YgV29TaWduMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAvcqN +rLiRFVaXe2tcesLea9mhsMMQI/qnobLMMfo+2aYpbxY94Gv4uEBf2zmoAHqLoE1U +fcIiePyOCbiohdfMlZdLdNiefvAA5A6JrkkoRBoQmTIPJYhTpA2zDxIIFgsDcScc +f+Hb0v1naMQFXQoOXXDX2JegvFNBmpGN9J42Znp+VsGQX+axaCA2pIwkLCxHC1l2 +ZjC1vt7tj/id07sBMOby8w7gLJKA84X5KIq0VC6a7fd2/BVoFutKbOsuEo/Uz/4M +x1wdC34FMr5esAkqQtXJTpCzWQ27en7N1QhatH/YHGkR+ScPewavVIMYe+HdVHpR +aG53/Ma/UkpmRqGyZxq7o093oL5d//xWC0Nyd5DKnvnyOfUNqfTq1+ezEC8wQjch +zDBwyYaYD8xYTYO7feUapTeNtqwylwA6Y3EkHp43xP901DfA4v6IRmAR3Qg/UDar +uHqklWJqbrDKaiFaafPz+x1wOZXzp26mgYmhiMU7ccqjUu6Du/2gd/Tkb+dC221K +mYo0SLwX3OSACCK28jHAPwQ+658geda4BmRkAjHXqc1S+4RFaQkAKtxVi8QGRkvA +Sh0JWzko/amrzgD5LkhLJuYwTKVYyrREgk/nkR4zw7CT/xH8gdLKH3Ep3XZPkiWv +HYG3Dy+MwwbMLyejSuQOmbp8HkUff6oZRZb9/D0CAwEAAaNCMEAwDgYDVR0PAQH/ +BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFOFmzw7R8bNLtwYgFP6H +EtX2/vs+MA0GCSqGSIb3DQEBBQUAA4ICAQCoy3JAsnbBfnv8rWTjMnvMPLZdRtP1 +LOJwXcgu2AZ9mNELIaCJWSQBnfmvCX0KI4I01fx8cpm5o9dU9OpScA7F9dY74ToJ +MuYhOZO9sxXqT2r09Ys/L3yNWC7F4TmgPsc9SnOeQHrAK2GpZ8nzJLmzbVUsWh2e +JXLOC62qx1ViC777Y7NhRCOjy+EaDveaBk3e1CNOIZZbOVtXHS9dCF4Jef98l7VN +g64N1uajeeAz0JmWAjCnPv/So0M/BVoG6kQC2nz4SNAzqfkHx5Xh9T71XXG68pWp +dIhhWeO/yloTunK0jF02h+mmxTwTv97QRCbut+wucPrXnbes5cVAWubXbHssw1ab +R80LzvobtCHXt2a49CUwi1wNuepnsvRtrtWhnk/Yn+knArAdBtaP4/tIEp9/EaEQ +PkxROpaw0RPxx9gmrjrKkcRpnd8BKWRRb2jaFOwIQZeQjdCygPLPwj2/kWjFgGce +xGATVdVhmVd8upUPYUk6ynW8yQqTP2cOEvIo4jEbwFcW3wh8GcF+Dx+FHgo2fFt+ +J7x6v+Db9NpSvd4MVHAxkUOVyLzwPt0JfjBkUO1/AaQzZ01oT74V77D2AhGiGxMl +OtzCWfHjXEa7ZywCRuoeSKbmW9m1vFGikpbbqsY3Iqb+zCB0oy2pLmvLwIIRIbWT +ee5Ehr7XHuQe+w== +-----END CERTIFICATE----- + +# Issuer: CN=CA 沃通根证书 O=WoSign CA Limited +# Subject: CN=CA 沃通根证书 O=WoSign CA Limited +# Label: "WoSign China" +# Serial: 106921963437422998931660691310149453965 +# MD5 Fingerprint: 78:83:5b:52:16:76:c4:24:3b:83:78:e8:ac:da:9a:93 +# SHA1 Fingerprint: 16:32:47:8d:89:f9:21:3a:92:00:85:63:f5:a4:a7:d3:12:40:8a:d6 +# SHA256 Fingerprint: d6:f0:34:bd:94:aa:23:3f:02:97:ec:a4:24:5b:28:39:73:e4:47:aa:59:0f:31:0c:77:f4:8f:df:83:11:22:54 +-----BEGIN CERTIFICATE----- +MIIFWDCCA0CgAwIBAgIQUHBrzdgT/BtOOzNy0hFIjTANBgkqhkiG9w0BAQsFADBG +MQswCQYDVQQGEwJDTjEaMBgGA1UEChMRV29TaWduIENBIExpbWl0ZWQxGzAZBgNV +BAMMEkNBIOayg+mAmuagueivgeS5pjAeFw0wOTA4MDgwMTAwMDFaFw0zOTA4MDgw +MTAwMDFaMEYxCzAJBgNVBAYTAkNOMRowGAYDVQQKExFXb1NpZ24gQ0EgTGltaXRl +ZDEbMBkGA1UEAwwSQ0Eg5rKD6YCa5qC56K+B5LmmMIICIjANBgkqhkiG9w0BAQEF +AAOCAg8AMIICCgKCAgEA0EkhHiX8h8EqwqzbdoYGTufQdDTc7WU1/FDWiD+k8H/r +D195L4mx/bxjWDeTmzj4t1up+thxx7S8gJeNbEvxUNUqKaqoGXqW5pWOdO2XCld1 +9AXbbQs5uQF/qvbW2mzmBeCkTVL829B0txGMe41P/4eDrv8FAxNXUDf+jJZSEExf +v5RxadmWPgxDT74wwJ85dE8GRV2j1lY5aAfMh09Qd5Nx2UQIsYo06Yms25tO4dnk +UkWMLhQfkWsZHWgpLFbE4h4TV2TwYeO5Ed+w4VegG63XX9Gv2ystP9Bojg/qnw+L +NVgbExz03jWhCl3W6t8Sb8D7aQdGctyB9gQjF+BNdeFyb7Ao65vh4YOhn0pdr8yb ++gIgthhid5E7o9Vlrdx8kHccREGkSovrlXLp9glk3Kgtn3R46MGiCWOc76DbT52V +qyBPt7D3h1ymoOQ3OMdc4zUPLK2jgKLsLl3Az+2LBcLmc272idX10kaO6m1jGx6K +yX2m+Jzr5dVjhU1zZmkR/sgO9MHHZklTfuQZa/HpelmjbX7FF+Ynxu8b22/8DU0G +AbQOXDBGVWCvOGU6yke6rCzMRh+yRpY/8+0mBe53oWprfi1tWFxK1I5nuPHa1UaK +J/kR8slC/k7e3x9cxKSGhxYzoacXGKUN5AXlK8IrC6KVkLn9YDxOiT7nnO4fuwEC +AwEAAaNCMEAwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0O +BBYEFOBNv9ybQV0T6GTwp+kVpOGBwboxMA0GCSqGSIb3DQEBCwUAA4ICAQBqinA4 +WbbaixjIvirTthnVZil6Xc1bL3McJk6jfW+rtylNpumlEYOnOXOvEESS5iVdT2H6 +yAa+Tkvv/vMx/sZ8cApBWNromUuWyXi8mHwCKe0JgOYKOoICKuLJL8hWGSbueBwj +/feTZU7n85iYr83d2Z5AiDEoOqsuC7CsDCT6eiaY8xJhEPRdF/d+4niXVOKM6Cm6 +jBAyvd0zaziGfjk9DgNyp115j0WKWa5bIW4xRtVZjc8VX90xJc/bYNaBRHIpAlf2 +ltTW/+op2znFuCyKGo3Oy+dCMYYFaA6eFN0AkLppRQjbbpCBhqcqBT/mhDn4t/lX +X0ykeVoQDF7Va/81XwVRHmyjdanPUIPTfPRm94KNPQx96N97qA4bLJyuQHCH2u2n +FoJavjVsIE4iYdm8UXrNemHcSxH5/mc0zy4EZmFcV5cjjPOGG0jfKq+nwf/Yjj4D +u9gqsPoUJbJRa4ZDhS4HIxaAjUz7tGM7zMN07RujHv41D198HRaG9Q7DlfEvr10l +O1Hm13ZBONFLAzkopR6RctR9q5czxNM+4Gm2KHmgCY0c0f9BckgG/Jou5yD5m6Le +ie2uPAmvylezkolwQOQvT8Jwg0DXJCxr5wkf09XHwQj02w47HAcLQxGEIYbpgNR1 +2KvxAmLBsX5VYc8T1yaw15zLKYs4SgsOkI26oQ== +-----END CERTIFICATE----- + +# Issuer: CN=COMODO RSA Certification Authority O=COMODO CA Limited +# Subject: CN=COMODO RSA Certification Authority O=COMODO CA Limited +# Label: "COMODO RSA Certification Authority" +# Serial: 101909084537582093308941363524873193117 +# MD5 Fingerprint: 1b:31:b0:71:40:36:cc:14:36:91:ad:c4:3e:fd:ec:18 +# SHA1 Fingerprint: af:e5:d2:44:a8:d1:19:42:30:ff:47:9f:e2:f8:97:bb:cd:7a:8c:b4 +# SHA256 Fingerprint: 52:f0:e1:c4:e5:8e:c6:29:29:1b:60:31:7f:07:46:71:b8:5d:7e:a8:0d:5b:07:27:34:63:53:4b:32:b4:02:34 +-----BEGIN CERTIFICATE----- +MIIF2DCCA8CgAwIBAgIQTKr5yttjb+Af907YWwOGnTANBgkqhkiG9w0BAQwFADCB +hTELMAkGA1UEBhMCR0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4G +A1UEBxMHU2FsZm9yZDEaMBgGA1UEChMRQ09NT0RPIENBIExpbWl0ZWQxKzApBgNV +BAMTIkNPTU9ETyBSU0EgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMTAwMTE5 +MDAwMDAwWhcNMzgwMTE4MjM1OTU5WjCBhTELMAkGA1UEBhMCR0IxGzAZBgNVBAgT +EkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UEBxMHU2FsZm9yZDEaMBgGA1UEChMR +Q09NT0RPIENBIExpbWl0ZWQxKzApBgNVBAMTIkNPTU9ETyBSU0EgQ2VydGlmaWNh +dGlvbiBBdXRob3JpdHkwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCR +6FSS0gpWsawNJN3Fz0RndJkrN6N9I3AAcbxT38T6KhKPS38QVr2fcHK3YX/JSw8X +pz3jsARh7v8Rl8f0hj4K+j5c+ZPmNHrZFGvnnLOFoIJ6dq9xkNfs/Q36nGz637CC +9BR++b7Epi9Pf5l/tfxnQ3K9DADWietrLNPtj5gcFKt+5eNu/Nio5JIk2kNrYrhV +/erBvGy2i/MOjZrkm2xpmfh4SDBF1a3hDTxFYPwyllEnvGfDyi62a+pGx8cgoLEf +Zd5ICLqkTqnyg0Y3hOvozIFIQ2dOciqbXL1MGyiKXCJ7tKuY2e7gUYPDCUZObT6Z ++pUX2nwzV0E8jVHtC7ZcryxjGt9XyD+86V3Em69FmeKjWiS0uqlWPc9vqv9JWL7w +qP/0uK3pN/u6uPQLOvnoQ0IeidiEyxPx2bvhiWC4jChWrBQdnArncevPDt09qZah +SL0896+1DSJMwBGB7FY79tOi4lu3sgQiUpWAk2nojkxl8ZEDLXB0AuqLZxUpaVIC +u9ffUGpVRr+goyhhf3DQw6KqLCGqR84onAZFdr+CGCe01a60y1Dma/RMhnEw6abf +Fobg2P9A3fvQQoh/ozM6LlweQRGBY84YcWsr7KaKtzFcOmpH4MN5WdYgGq/yapiq +crxXStJLnbsQ/LBMQeXtHT1eKJ2czL+zUdqnR+WEUwIDAQABo0IwQDAdBgNVHQ4E +FgQUu69+Aj36pvE8hI6t7jiY7NkyMtQwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB +/wQFMAMBAf8wDQYJKoZIhvcNAQEMBQADggIBAArx1UaEt65Ru2yyTUEUAJNMnMvl +wFTPoCWOAvn9sKIN9SCYPBMtrFaisNZ+EZLpLrqeLppysb0ZRGxhNaKatBYSaVqM +4dc+pBroLwP0rmEdEBsqpIt6xf4FpuHA1sj+nq6PK7o9mfjYcwlYRm6mnPTXJ9OV +2jeDchzTc+CiR5kDOF3VSXkAKRzH7JsgHAckaVd4sjn8OoSgtZx8jb8uk2Intzna +FxiuvTwJaP+EmzzV1gsD41eeFPfR60/IvYcjt7ZJQ3mFXLrrkguhxuhoqEwWsRqZ +CuhTLJK7oQkYdQxlqHvLI7cawiiFwxv/0Cti76R7CZGYZ4wUAc1oBmpjIXUDgIiK +boHGhfKppC3n9KUkEEeDys30jXlYsQab5xoq2Z0B15R97QNKyvDb6KkBPvVWmcke +jkk9u+UJueBPSZI9FoJAzMxZxuY67RIuaTxslbH9qh17f4a+Hg4yRvv7E491f0yL +S0Zj/gA0QHDBw7mh3aZw4gSzQbzpgJHqZJx64SIDqZxubw5lT2yHh17zbqD5daWb +QOhTsiedSrnAdyGN/4fy3ryM7xfft0kL0fJuMAsaDk527RH89elWsn2/x20Kk4yl +0MC2Hb46TpSi125sC8KKfPog88Tk5c0NqMuRkrF8hey1FGlmDoLnzc7ILaZRfyHB +NVOFBkpdn627G190 +-----END CERTIFICATE----- + +# Issuer: CN=USERTrust RSA Certification Authority O=The USERTRUST Network +# Subject: CN=USERTrust RSA Certification Authority O=The USERTRUST Network +# Label: "USERTrust RSA Certification Authority" +# Serial: 2645093764781058787591871645665788717 +# MD5 Fingerprint: 1b:fe:69:d1:91:b7:19:33:a3:72:a8:0f:e1:55:e5:b5 +# SHA1 Fingerprint: 2b:8f:1b:57:33:0d:bb:a2:d0:7a:6c:51:f7:0e:e9:0d:da:b9:ad:8e +# SHA256 Fingerprint: e7:93:c9:b0:2f:d8:aa:13:e2:1c:31:22:8a:cc:b0:81:19:64:3b:74:9c:89:89:64:b1:74:6d:46:c3:d4:cb:d2 +-----BEGIN CERTIFICATE----- +MIIF3jCCA8agAwIBAgIQAf1tMPyjylGoG7xkDjUDLTANBgkqhkiG9w0BAQwFADCB +iDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCk5ldyBKZXJzZXkxFDASBgNVBAcTC0pl +cnNleSBDaXR5MR4wHAYDVQQKExVUaGUgVVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNV +BAMTJVVTRVJUcnVzdCBSU0EgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMTAw +MjAxMDAwMDAwWhcNMzgwMTE4MjM1OTU5WjCBiDELMAkGA1UEBhMCVVMxEzARBgNV +BAgTCk5ldyBKZXJzZXkxFDASBgNVBAcTC0plcnNleSBDaXR5MR4wHAYDVQQKExVU +aGUgVVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNVBAMTJVVTRVJUcnVzdCBSU0EgQ2Vy +dGlmaWNhdGlvbiBBdXRob3JpdHkwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIK +AoICAQCAEmUXNg7D2wiz0KxXDXbtzSfTTK1Qg2HiqiBNCS1kCdzOiZ/MPans9s/B +3PHTsdZ7NygRK0faOca8Ohm0X6a9fZ2jY0K2dvKpOyuR+OJv0OwWIJAJPuLodMkY +tJHUYmTbf6MG8YgYapAiPLz+E/CHFHv25B+O1ORRxhFnRghRy4YUVD+8M/5+bJz/ +Fp0YvVGONaanZshyZ9shZrHUm3gDwFA66Mzw3LyeTP6vBZY1H1dat//O+T23LLb2 +VN3I5xI6Ta5MirdcmrS3ID3KfyI0rn47aGYBROcBTkZTmzNg95S+UzeQc0PzMsNT +79uq/nROacdrjGCT3sTHDN/hMq7MkztReJVni+49Vv4M0GkPGw/zJSZrM233bkf6 +c0Plfg6lZrEpfDKEY1WJxA3Bk1QwGROs0303p+tdOmw1XNtB1xLaqUkL39iAigmT +Yo61Zs8liM2EuLE/pDkP2QKe6xJMlXzzawWpXhaDzLhn4ugTncxbgtNMs+1b/97l +c6wjOy0AvzVVdAlJ2ElYGn+SNuZRkg7zJn0cTRe8yexDJtC/QV9AqURE9JnnV4ee +UB9XVKg+/XRjL7FQZQnmWEIuQxpMtPAlR1n6BB6T1CZGSlCBst6+eLf8ZxXhyVeE +Hg9j1uliutZfVS7qXMYoCAQlObgOK6nyTJccBz8NUvXt7y+CDwIDAQABo0IwQDAd +BgNVHQ4EFgQUU3m/WqorSs9UgOHYm8Cd8rIDZsswDgYDVR0PAQH/BAQDAgEGMA8G +A1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEMBQADggIBAFzUfA3P9wF9QZllDHPF +Up/L+M+ZBn8b2kMVn54CVVeWFPFSPCeHlCjtHzoBN6J2/FNQwISbxmtOuowhT6KO +VWKR82kV2LyI48SqC/3vqOlLVSoGIG1VeCkZ7l8wXEskEVX/JJpuXior7gtNn3/3 +ATiUFJVDBwn7YKnuHKsSjKCaXqeYalltiz8I+8jRRa8YFWSQEg9zKC7F4iRO/Fjs +8PRF/iKz6y+O0tlFYQXBl2+odnKPi4w2r78NBc5xjeambx9spnFixdjQg3IM8WcR +iQycE0xyNN+81XHfqnHd4blsjDwSXWXavVcStkNr/+XeTWYRUc+ZruwXtuhxkYze +Sf7dNXGiFSeUHM9h4ya7b6NnJSFd5t0dCy5oGzuCr+yDZ4XUmFF0sbmZgIn/f3gZ +XHlKYC6SQK5MNyosycdiyA5d9zZbyuAlJQG03RoHnHcAP9Dc1ew91Pq7P8yF1m9/ +qS3fuQL39ZeatTXaw2ewh0qpKJ4jjv9cJ2vhsE/zB+4ALtRZh8tSQZXq9EfX7mRB +VXyNWQKV3WKdwrnuWih0hKWbt5DHDAff9Yk2dDLWKMGwsAvgnEzDHNb842m1R0aB +L6KCq9NjRHDEjf8tM7qtj3u1cIiuPhnPQCjY/MiQu12ZIvVS5ljFH4gxQ+6IHdfG +jjxDah2nGN59PRbxYvnKkKj9 +-----END CERTIFICATE----- + +# Issuer: CN=USERTrust ECC Certification Authority O=The USERTRUST Network +# Subject: CN=USERTrust ECC Certification Authority O=The USERTRUST Network +# Label: "USERTrust ECC Certification Authority" +# Serial: 123013823720199481456569720443997572134 +# MD5 Fingerprint: fa:68:bc:d9:b5:7f:ad:fd:c9:1d:06:83:28:cc:24:c1 +# SHA1 Fingerprint: d1:cb:ca:5d:b2:d5:2a:7f:69:3b:67:4d:e5:f0:5a:1d:0c:95:7d:f0 +# SHA256 Fingerprint: 4f:f4:60:d5:4b:9c:86:da:bf:bc:fc:57:12:e0:40:0d:2b:ed:3f:bc:4d:4f:bd:aa:86:e0:6a:dc:d2:a9:ad:7a +-----BEGIN CERTIFICATE----- +MIICjzCCAhWgAwIBAgIQXIuZxVqUxdJxVt7NiYDMJjAKBggqhkjOPQQDAzCBiDEL +MAkGA1UEBhMCVVMxEzARBgNVBAgTCk5ldyBKZXJzZXkxFDASBgNVBAcTC0plcnNl +eSBDaXR5MR4wHAYDVQQKExVUaGUgVVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNVBAMT +JVVTRVJUcnVzdCBFQ0MgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMTAwMjAx +MDAwMDAwWhcNMzgwMTE4MjM1OTU5WjCBiDELMAkGA1UEBhMCVVMxEzARBgNVBAgT +Ck5ldyBKZXJzZXkxFDASBgNVBAcTC0plcnNleSBDaXR5MR4wHAYDVQQKExVUaGUg +VVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNVBAMTJVVTRVJUcnVzdCBFQ0MgQ2VydGlm +aWNhdGlvbiBBdXRob3JpdHkwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAAQarFRaqflo +I+d61SRvU8Za2EurxtW20eZzca7dnNYMYf3boIkDuAUU7FfO7l0/4iGzzvfUinng +o4N+LZfQYcTxmdwlkWOrfzCjtHDix6EznPO/LlxTsV+zfTJ/ijTjeXmjQjBAMB0G +A1UdDgQWBBQ64QmG1M8ZwpZ2dEl23OA1xmNjmjAOBgNVHQ8BAf8EBAMCAQYwDwYD +VR0TAQH/BAUwAwEB/zAKBggqhkjOPQQDAwNoADBlAjA2Z6EWCNzklwBBHU6+4WMB +zzuqQhFkoJ2UOQIReVx7Hfpkue4WQrO/isIJxOzksU0CMQDpKmFHjFJKS04YcPbW +RNZu9YO6bVi9JNlWSOrvxKJGgYhqOkbRqZtNyWHa0V1Xahg= +-----END CERTIFICATE----- + +# Issuer: CN=GlobalSign O=GlobalSign OU=GlobalSign ECC Root CA - R4 +# Subject: CN=GlobalSign O=GlobalSign OU=GlobalSign ECC Root CA - R4 +# Label: "GlobalSign ECC Root CA - R4" +# Serial: 14367148294922964480859022125800977897474 +# MD5 Fingerprint: 20:f0:27:68:d1:7e:a0:9d:0e:e6:2a:ca:df:5c:89:8e +# SHA1 Fingerprint: 69:69:56:2e:40:80:f4:24:a1:e7:19:9f:14:ba:f3:ee:58:ab:6a:bb +# SHA256 Fingerprint: be:c9:49:11:c2:95:56:76:db:6c:0a:55:09:86:d7:6e:3b:a0:05:66:7c:44:2c:97:62:b4:fb:b7:73:de:22:8c +-----BEGIN CERTIFICATE----- +MIIB4TCCAYegAwIBAgIRKjikHJYKBN5CsiilC+g0mAIwCgYIKoZIzj0EAwIwUDEk +MCIGA1UECxMbR2xvYmFsU2lnbiBFQ0MgUm9vdCBDQSAtIFI0MRMwEQYDVQQKEwpH +bG9iYWxTaWduMRMwEQYDVQQDEwpHbG9iYWxTaWduMB4XDTEyMTExMzAwMDAwMFoX +DTM4MDExOTAzMTQwN1owUDEkMCIGA1UECxMbR2xvYmFsU2lnbiBFQ0MgUm9vdCBD +QSAtIFI0MRMwEQYDVQQKEwpHbG9iYWxTaWduMRMwEQYDVQQDEwpHbG9iYWxTaWdu +MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEuMZ5049sJQ6fLjkZHAOkrprlOQcJ +FspjsbmG+IpXwVfOQvpzofdlQv8ewQCybnMO/8ch5RikqtlxP6jUuc6MHaNCMEAw +DgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFFSwe61F +uOJAf/sKbvu+M8k8o4TVMAoGCCqGSM49BAMCA0gAMEUCIQDckqGgE6bPA7DmxCGX +kPoUVy0D7O48027KqGx2vKLeuwIgJ6iFJzWbVsaj8kfSt24bAgAXqmemFZHe+pTs +ewv4n4Q= +-----END CERTIFICATE----- + +# Issuer: CN=GlobalSign O=GlobalSign OU=GlobalSign ECC Root CA - R5 +# Subject: CN=GlobalSign O=GlobalSign OU=GlobalSign ECC Root CA - R5 +# Label: "GlobalSign ECC Root CA - R5" +# Serial: 32785792099990507226680698011560947931244 +# MD5 Fingerprint: 9f:ad:3b:1c:02:1e:8a:ba:17:74:38:81:0c:a2:bc:08 +# SHA1 Fingerprint: 1f:24:c6:30:cd:a4:18:ef:20:69:ff:ad:4f:dd:5f:46:3a:1b:69:aa +# SHA256 Fingerprint: 17:9f:bc:14:8a:3d:d0:0f:d2:4e:a1:34:58:cc:43:bf:a7:f5:9c:81:82:d7:83:a5:13:f6:eb:ec:10:0c:89:24 +-----BEGIN CERTIFICATE----- +MIICHjCCAaSgAwIBAgIRYFlJ4CYuu1X5CneKcflK2GwwCgYIKoZIzj0EAwMwUDEk +MCIGA1UECxMbR2xvYmFsU2lnbiBFQ0MgUm9vdCBDQSAtIFI1MRMwEQYDVQQKEwpH +bG9iYWxTaWduMRMwEQYDVQQDEwpHbG9iYWxTaWduMB4XDTEyMTExMzAwMDAwMFoX +DTM4MDExOTAzMTQwN1owUDEkMCIGA1UECxMbR2xvYmFsU2lnbiBFQ0MgUm9vdCBD +QSAtIFI1MRMwEQYDVQQKEwpHbG9iYWxTaWduMRMwEQYDVQQDEwpHbG9iYWxTaWdu +MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAER0UOlvt9Xb/pOdEh+J8LttV7HpI6SFkc +8GIxLcB6KP4ap1yztsyX50XUWPrRd21DosCHZTQKH3rd6zwzocWdTaRvQZU4f8ke +hOvRnkmSh5SHDDqFSmafnVmTTZdhBoZKo0IwQDAOBgNVHQ8BAf8EBAMCAQYwDwYD +VR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUPeYpSJvqB8ohREom3m7e0oPQn1kwCgYI +KoZIzj0EAwMDaAAwZQIxAOVpEslu28YxuglB4Zf4+/2a4n0Sye18ZNPLBSWLVtmg +515dTguDnFt2KaAJJiFqYgIwcdK1j1zqO+F4CYWodZI7yFz9SO8NdCKoCOJuxUnO +xwy8p2Fp8fc74SrL+SvzZpA3 +-----END CERTIFICATE----- + +# Issuer: CN=Staat der Nederlanden Root CA - G3 O=Staat der Nederlanden +# Subject: CN=Staat der Nederlanden Root CA - G3 O=Staat der Nederlanden +# Label: "Staat der Nederlanden Root CA - G3" +# Serial: 10003001 +# MD5 Fingerprint: 0b:46:67:07:db:10:2f:19:8c:35:50:60:d1:0b:f4:37 +# SHA1 Fingerprint: d8:eb:6b:41:51:92:59:e0:f3:e7:85:00:c0:3d:b6:88:97:c9:ee:fc +# SHA256 Fingerprint: 3c:4f:b0:b9:5a:b8:b3:00:32:f4:32:b8:6f:53:5f:e1:72:c1:85:d0:fd:39:86:58:37:cf:36:18:7f:a6:f4:28 +-----BEGIN CERTIFICATE----- +MIIFdDCCA1ygAwIBAgIEAJiiOTANBgkqhkiG9w0BAQsFADBaMQswCQYDVQQGEwJO +TDEeMBwGA1UECgwVU3RhYXQgZGVyIE5lZGVybGFuZGVuMSswKQYDVQQDDCJTdGFh +dCBkZXIgTmVkZXJsYW5kZW4gUm9vdCBDQSAtIEczMB4XDTEzMTExNDExMjg0MloX +DTI4MTExMzIzMDAwMFowWjELMAkGA1UEBhMCTkwxHjAcBgNVBAoMFVN0YWF0IGRl +ciBOZWRlcmxhbmRlbjErMCkGA1UEAwwiU3RhYXQgZGVyIE5lZGVybGFuZGVuIFJv +b3QgQ0EgLSBHMzCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAL4yolQP +cPssXFnrbMSkUeiFKrPMSjTysF/zDsccPVMeiAho2G89rcKezIJnByeHaHE6n3WW +IkYFsO2tx1ueKt6c/DrGlaf1F2cY5y9JCAxcz+bMNO14+1Cx3Gsy8KL+tjzk7FqX +xz8ecAgwoNzFs21v0IJyEavSgWhZghe3eJJg+szeP4TrjTgzkApyI/o1zCZxMdFy +KJLZWyNtZrVtB0LrpjPOktvA9mxjeM3KTj215VKb8b475lRgsGYeCasH/lSJEULR +9yS6YHgamPfJEf0WwTUaVHXvQ9Plrk7O53vDxk5hUUurmkVLoR9BvUhTFXFkC4az +5S6+zqQbwSmEorXLCCN2QyIkHxcE1G6cxvx/K2Ya7Irl1s9N9WMJtxU51nus6+N8 +6U78dULI7ViVDAZCopz35HCz33JvWjdAidiFpNfxC95DGdRKWCyMijmev4SH8RY7 +Ngzp07TKbBlBUgmhHbBqv4LvcFEhMtwFdozL92TkA1CvjJFnq8Xy7ljY3r735zHP +bMk7ccHViLVlvMDoFxcHErVc0qsgk7TmgoNwNsXNo42ti+yjwUOH5kPiNL6VizXt +BznaqB16nzaeErAMZRKQFWDZJkBE41ZgpRDUajz9QdwOWke275dhdU/Z/seyHdTt +XUmzqWrLZoQT1Vyg3N9udwbRcXXIV2+vD3dbAgMBAAGjQjBAMA8GA1UdEwEB/wQF +MAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBRUrfrHkleuyjWcLhL75Lpd +INyUVzANBgkqhkiG9w0BAQsFAAOCAgEAMJmdBTLIXg47mAE6iqTnB/d6+Oea31BD +U5cqPco8R5gu4RV78ZLzYdqQJRZlwJ9UXQ4DO1t3ApyEtg2YXzTdO2PCwyiBwpwp +LiniyMMB8jPqKqrMCQj3ZWfGzd/TtiunvczRDnBfuCPRy5FOCvTIeuXZYzbB1N/8 +Ipf3YF3qKS9Ysr1YvY2WTxB1v0h7PVGHoTx0IsL8B3+A3MSs/mrBcDCw6Y5p4ixp +gZQJut3+TcCDjJRYwEYgr5wfAvg1VUkvRtTA8KCWAg8zxXHzniN9lLf9OtMJgwYh +/WA9rjLA0u6NpvDntIJ8CsxwyXmA+P5M9zWEGYox+wrZ13+b8KKaa8MFSu1BYBQw +0aoRQm7TIwIEC8Zl3d1Sd9qBa7Ko+gE4uZbqKmxnl4mUnrzhVNXkanjvSr0rmj1A +fsbAddJu+2gw7OyLnflJNZoaLNmzlTnVHpL3prllL+U9bTpITAjc5CgSKL59NVzq +4BZ+Extq1z7XnvwtdbLBFNUjA9tbbws+eC8N3jONFrdI54OagQ97wUNNVQQXOEpR +1VmiiXTTn74eS9fGbbeIJG9gkaSChVtWQbzQRKtqE77RLFi3EjNYsjdj3BP1lB0/ +QFH1T/U67cjF68IeHRaVesd+QnGTbksVtzDfqu1XhUisHWrdOWnk4Xl4vs4Fv6EM +94B7IWcnMFk= +-----END CERTIFICATE----- + +# Issuer: CN=Staat der Nederlanden EV Root CA O=Staat der Nederlanden +# Subject: CN=Staat der Nederlanden EV Root CA O=Staat der Nederlanden +# Label: "Staat der Nederlanden EV Root CA" +# Serial: 10000013 +# MD5 Fingerprint: fc:06:af:7b:e8:1a:f1:9a:b4:e8:d2:70:1f:c0:f5:ba +# SHA1 Fingerprint: 76:e2:7e:c1:4f:db:82:c1:c0:a6:75:b5:05:be:3d:29:b4:ed:db:bb +# SHA256 Fingerprint: 4d:24:91:41:4c:fe:95:67:46:ec:4c:ef:a6:cf:6f:72:e2:8a:13:29:43:2f:9d:8a:90:7a:c4:cb:5d:ad:c1:5a +-----BEGIN CERTIFICATE----- +MIIFcDCCA1igAwIBAgIEAJiWjTANBgkqhkiG9w0BAQsFADBYMQswCQYDVQQGEwJO +TDEeMBwGA1UECgwVU3RhYXQgZGVyIE5lZGVybGFuZGVuMSkwJwYDVQQDDCBTdGFh +dCBkZXIgTmVkZXJsYW5kZW4gRVYgUm9vdCBDQTAeFw0xMDEyMDgxMTE5MjlaFw0y +MjEyMDgxMTEwMjhaMFgxCzAJBgNVBAYTAk5MMR4wHAYDVQQKDBVTdGFhdCBkZXIg +TmVkZXJsYW5kZW4xKTAnBgNVBAMMIFN0YWF0IGRlciBOZWRlcmxhbmRlbiBFViBS +b290IENBMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA48d+ifkkSzrS +M4M1LGns3Amk41GoJSt5uAg94JG6hIXGhaTK5skuU6TJJB79VWZxXSzFYGgEt9nC +UiY4iKTWO0Cmws0/zZiTs1QUWJZV1VD+hq2kY39ch/aO5ieSZxeSAgMs3NZmdO3d +Z//BYY1jTw+bbRcwJu+r0h8QoPnFfxZpgQNH7R5ojXKhTbImxrpsX23Wr9GxE46p +rfNeaXUmGD5BKyF/7otdBwadQ8QpCiv8Kj6GyzyDOvnJDdrFmeK8eEEzduG/L13l +pJhQDBXd4Pqcfzho0LKmeqfRMb1+ilgnQ7O6M5HTp5gVXJrm0w912fxBmJc+qiXb +j5IusHsMX/FjqTf5m3VpTCgmJdrV8hJwRVXj33NeN/UhbJCONVrJ0yPr08C+eKxC +KFhmpUZtcALXEPlLVPxdhkqHz3/KRawRWrUgUY0viEeXOcDPusBCAUCZSCELa6fS +/ZbV0b5GnUngC6agIk440ME8MLxwjyx1zNDFjFE7PZQIZCZhfbnDZY8UnCHQqv0X +cgOPvZuM5l5Tnrmd74K74bzickFbIZTTRTeU0d8JOV3nI6qaHcptqAqGhYqCvkIH +1vI4gnPah1vlPNOePqc7nvQDs/nxfRN0Av+7oeX6AHkcpmZBiFxgV6YuCcS6/ZrP +px9Aw7vMWgpVSzs4dlG4Y4uElBbmVvMCAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB +/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFP6rAJCYniT8qcwaivsnuL8wbqg7 +MA0GCSqGSIb3DQEBCwUAA4ICAQDPdyxuVr5Os7aEAJSrR8kN0nbHhp8dB9O2tLsI +eK9p0gtJ3jPFrK3CiAJ9Brc1AsFgyb/E6JTe1NOpEyVa/m6irn0F3H3zbPB+po3u +2dfOWBfoqSmuc0iH55vKbimhZF8ZE/euBhD/UcabTVUlT5OZEAFTdfETzsemQUHS +v4ilf0X8rLiltTMMgsT7B/Zq5SWEXwbKwYY5EdtYzXc7LMJMD16a4/CrPmEbUCTC +wPTxGfARKbalGAKb12NMcIxHowNDXLldRqANb/9Zjr7dn3LDWyvfjFvO5QxGbJKy +CqNMVEIYFRIYvdr8unRu/8G2oGTYqV9Vrp9canaW2HNnh/tNf1zuacpzEPuKqf2e +vTY4SUmH9A4U8OmHuD+nT3pajnnUk+S7aFKErGzp85hwVXIy+TSrK0m1zSBi5Dp6 +Z2Orltxtrpfs/J92VoguZs9btsmksNcFuuEnL5O7Jiqik7Ab846+HUCjuTaPPoIa +Gl6I6lD4WeKDRikL40Rc4ZW2aZCaFG+XroHPaO+Zmr615+F/+PoTRxZMzG0IQOeL +eG9QgkRQP2YGiqtDhFZKDyAthg710tvSeopLzaXoTvFeJiUBWSOgftL2fiFX1ye8 +FVdMpEbB4IMeDExNH08GGeL5qPQ6gqGyeUN51q1veieQA6TqJIc/2b3Z6fJfUEkc +7uzXLg== +-----END CERTIFICATE----- + +# Issuer: CN=IdenTrust Commercial Root CA 1 O=IdenTrust +# Subject: CN=IdenTrust Commercial Root CA 1 O=IdenTrust +# Label: "IdenTrust Commercial Root CA 1" +# Serial: 13298821034946342390520003877796839426 +# MD5 Fingerprint: b3:3e:77:73:75:ee:a0:d3:e3:7e:49:63:49:59:bb:c7 +# SHA1 Fingerprint: df:71:7e:aa:4a:d9:4e:c9:55:84:99:60:2d:48:de:5f:bc:f0:3a:25 +# SHA256 Fingerprint: 5d:56:49:9b:e4:d2:e0:8b:cf:ca:d0:8a:3e:38:72:3d:50:50:3b:de:70:69:48:e4:2f:55:60:30:19:e5:28:ae +-----BEGIN CERTIFICATE----- +MIIFYDCCA0igAwIBAgIQCgFCgAAAAUUjyES1AAAAAjANBgkqhkiG9w0BAQsFADBK +MQswCQYDVQQGEwJVUzESMBAGA1UEChMJSWRlblRydXN0MScwJQYDVQQDEx5JZGVu +VHJ1c3QgQ29tbWVyY2lhbCBSb290IENBIDEwHhcNMTQwMTE2MTgxMjIzWhcNMzQw +MTE2MTgxMjIzWjBKMQswCQYDVQQGEwJVUzESMBAGA1UEChMJSWRlblRydXN0MScw +JQYDVQQDEx5JZGVuVHJ1c3QgQ29tbWVyY2lhbCBSb290IENBIDEwggIiMA0GCSqG +SIb3DQEBAQUAA4ICDwAwggIKAoICAQCnUBneP5k91DNG8W9RYYKyqU+PZ4ldhNlT +3Qwo2dfw/66VQ3KZ+bVdfIrBQuExUHTRgQ18zZshq0PirK1ehm7zCYofWjK9ouuU ++ehcCuz/mNKvcbO0U59Oh++SvL3sTzIwiEsXXlfEU8L2ApeN2WIrvyQfYo3fw7gp +S0l4PJNgiCL8mdo2yMKi1CxUAGc1bnO/AljwpN3lsKImesrgNqUZFvX9t++uP0D1 +bVoE/c40yiTcdCMbXTMTEl3EASX2MN0CXZ/g1Ue9tOsbobtJSdifWwLziuQkkORi +T0/Br4sOdBeo0XKIanoBScy0RnnGF7HamB4HWfp1IYVl3ZBWzvurpWCdxJ35UrCL +vYf5jysjCiN2O/cz4ckA82n5S6LgTrx+kzmEB/dEcH7+B1rlsazRGMzyNeVJSQjK +Vsk9+w8YfYs7wRPCTY/JTw436R+hDmrfYi7LNQZReSzIJTj0+kuniVyc0uMNOYZK +dHzVWYfCP04MXFL0PfdSgvHqo6z9STQaKPNBiDoT7uje/5kdX7rL6B7yuVBgwDHT +c+XvvqDtMwt0viAgxGds8AgDelWAf0ZOlqf0Hj7h9tgJ4TNkK2PXMl6f+cB7D3hv +l7yTmvmcEpB4eoCHFddydJxVdHixuuFucAS6T6C6aMN7/zHwcz09lCqxC0EOoP5N +iGVreTO01wIDAQABo0IwQDAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB +/zAdBgNVHQ4EFgQU7UQZwNPwBovupHu+QucmVMiONnYwDQYJKoZIhvcNAQELBQAD +ggIBAA2ukDL2pkt8RHYZYR4nKM1eVO8lvOMIkPkp165oCOGUAFjvLi5+U1KMtlwH +6oi6mYtQlNeCgN9hCQCTrQ0U5s7B8jeUeLBfnLOic7iPBZM4zY0+sLj7wM+x8uwt +LRvM7Kqas6pgghstO8OEPVeKlh6cdbjTMM1gCIOQ045U8U1mwF10A0Cj7oV+wh93 +nAbowacYXVKV7cndJZ5t+qntozo00Fl72u1Q8zW/7esUTTHHYPTa8Yec4kjixsU3 ++wYQ+nVZZjFHKdp2mhzpgq7vmrlR94gjmmmVYjzlVYA211QC//G5Xc7UI2/YRYRK +W2XviQzdFKcgyxilJbQN+QHwotL0AMh0jqEqSI5l2xPE4iUXfeu+h1sXIFRRk0pT +AwvsXcoz7WL9RccvW9xYoIA55vrX/hMUpu09lEpCdNTDd1lzzY9GvlU47/rokTLq +l1gEIt44w8y8bckzOmoKaT+gyOpyj4xjhiO9bTyWnpXgSUyqorkqG5w2gXjtw+hG +4iZZRHUe2XWJUc0QhJ1hYMtd+ZciTY6Y5uN/9lu7rs3KSoFrXgvzUeF0K+l+J6fZ +mUlO+KWA2yUPHGNiiskzZ2s8EIPGrd6ozRaOjfAHN3Gf8qv8QfXBi+wAN10J5U6A +7/qxXDgGpRtK4dw4LTzcqx+QGtVKnO7RcGzM7vRX+Bi6hG6H +-----END CERTIFICATE----- + +# Issuer: CN=IdenTrust Public Sector Root CA 1 O=IdenTrust +# Subject: CN=IdenTrust Public Sector Root CA 1 O=IdenTrust +# Label: "IdenTrust Public Sector Root CA 1" +# Serial: 13298821034946342390521976156843933698 +# MD5 Fingerprint: 37:06:a5:b0:fc:89:9d:ba:f4:6b:8c:1a:64:cd:d5:ba +# SHA1 Fingerprint: ba:29:41:60:77:98:3f:f4:f3:ef:f2:31:05:3b:2e:ea:6d:4d:45:fd +# SHA256 Fingerprint: 30:d0:89:5a:9a:44:8a:26:20:91:63:55:22:d1:f5:20:10:b5:86:7a:ca:e1:2c:78:ef:95:8f:d4:f4:38:9f:2f +-----BEGIN CERTIFICATE----- +MIIFZjCCA06gAwIBAgIQCgFCgAAAAUUjz0Z8AAAAAjANBgkqhkiG9w0BAQsFADBN +MQswCQYDVQQGEwJVUzESMBAGA1UEChMJSWRlblRydXN0MSowKAYDVQQDEyFJZGVu +VHJ1c3QgUHVibGljIFNlY3RvciBSb290IENBIDEwHhcNMTQwMTE2MTc1MzMyWhcN +MzQwMTE2MTc1MzMyWjBNMQswCQYDVQQGEwJVUzESMBAGA1UEChMJSWRlblRydXN0 +MSowKAYDVQQDEyFJZGVuVHJ1c3QgUHVibGljIFNlY3RvciBSb290IENBIDEwggIi +MA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQC2IpT8pEiv6EdrCvsnduTyP4o7 +ekosMSqMjbCpwzFrqHd2hCa2rIFCDQjrVVi7evi8ZX3yoG2LqEfpYnYeEe4IFNGy +RBb06tD6Hi9e28tzQa68ALBKK0CyrOE7S8ItneShm+waOh7wCLPQ5CQ1B5+ctMlS +bdsHyo+1W/CD80/HLaXIrcuVIKQxKFdYWuSNG5qrng0M8gozOSI5Cpcu81N3uURF +/YTLNiCBWS2ab21ISGHKTN9T0a9SvESfqy9rg3LvdYDaBjMbXcjaY8ZNzaxmMc3R +3j6HEDbhuaR672BQssvKplbgN6+rNBM5Jeg5ZuSYeqoSmJxZZoY+rfGwyj4GD3vw +EUs3oERte8uojHH01bWRNszwFcYr3lEXsZdMUD2xlVl8BX0tIdUAvwFnol57plzy +9yLxkA2T26pEUWbMfXYD62qoKjgZl3YNa4ph+bz27nb9cCvdKTz4Ch5bQhyLVi9V +GxyhLrXHFub4qjySjmm2AcG1hp2JDws4lFTo6tyePSW8Uybt1as5qsVATFSrsrTZ +2fjXctscvG29ZV/viDUqZi/u9rNl8DONfJhBaUYPQxxp+pu10GFqzcpL2UyQRqsV +WaFHVCkugyhfHMKiq3IXAAaOReyL4jM9f9oZRORicsPfIsbyVtTdX5Vy7W1f90gD +W/3FKqD2cyOEEBsB5wIDAQABo0IwQDAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/ +BAUwAwEB/zAdBgNVHQ4EFgQU43HgntinQtnbcZFrlJPrw6PRFKMwDQYJKoZIhvcN +AQELBQADggIBAEf63QqwEZE4rU1d9+UOl1QZgkiHVIyqZJnYWv6IAcVYpZmxI1Qj +t2odIFflAWJBF9MJ23XLblSQdf4an4EKwt3X9wnQW3IV5B4Jaj0z8yGa5hV+rVHV +DRDtfULAj+7AmgjVQdZcDiFpboBhDhXAuM/FSRJSzL46zNQuOAXeNf0fb7iAaJg9 +TaDKQGXSc3z1i9kKlT/YPyNtGtEqJBnZhbMX73huqVjRI9PHE+1yJX9dsXNw0H8G +lwmEKYBhHfpe/3OsoOOJuBxxFcbeMX8S3OFtm6/n6J91eEyrRjuazr8FGF1NFTwW +mhlQBJqymm9li1JfPFgEKCXAZmExfrngdbkaqIHWchezxQMxNRF4eKLg6TCMf4Df +WN88uieW4oA0beOY02QnrEh+KHdcxiVhJfiFDGX6xDIvpZgF5PgLZxYWxoK4Mhn5 ++bl53B/N66+rDt0b20XkeucC4pVd/GnwU2lhlXV5C15V5jgclKlZM57IcXR5f1GJ +tshquDDIajjDbp7hNxbqBWJMWxJH7ae0s1hWx0nzfxJoCTFx8G34Tkf71oXuxVhA +GaQdp/lLQzfcaFpPz+vCZHTetBXZ9FRUGi8c15dxVJCO2SCdUyt/q4/i6jC8UDfv +8Ue1fXwsBOxonbRJRBD0ckscZOf85muQ3Wl9af0AVqW3rLatt8o+Ae+c +-----END CERTIFICATE----- + +# Issuer: CN=Entrust Root Certification Authority - G2 O=Entrust, Inc. OU=See www.entrust.net/legal-terms/(c) 2009 Entrust, Inc. - for authorized use only +# Subject: CN=Entrust Root Certification Authority - G2 O=Entrust, Inc. OU=See www.entrust.net/legal-terms/(c) 2009 Entrust, Inc. - for authorized use only +# Label: "Entrust Root Certification Authority - G2" +# Serial: 1246989352 +# MD5 Fingerprint: 4b:e2:c9:91:96:65:0c:f4:0e:5a:93:92:a0:0a:fe:b2 +# SHA1 Fingerprint: 8c:f4:27:fd:79:0c:3a:d1:66:06:8d:e8:1e:57:ef:bb:93:22:72:d4 +# SHA256 Fingerprint: 43:df:57:74:b0:3e:7f:ef:5f:e4:0d:93:1a:7b:ed:f1:bb:2e:6b:42:73:8c:4e:6d:38:41:10:3d:3a:a7:f3:39 +-----BEGIN CERTIFICATE----- +MIIEPjCCAyagAwIBAgIESlOMKDANBgkqhkiG9w0BAQsFADCBvjELMAkGA1UEBhMC +VVMxFjAUBgNVBAoTDUVudHJ1c3QsIEluYy4xKDAmBgNVBAsTH1NlZSB3d3cuZW50 +cnVzdC5uZXQvbGVnYWwtdGVybXMxOTA3BgNVBAsTMChjKSAyMDA5IEVudHJ1c3Qs +IEluYy4gLSBmb3IgYXV0aG9yaXplZCB1c2Ugb25seTEyMDAGA1UEAxMpRW50cnVz +dCBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IC0gRzIwHhcNMDkwNzA3MTcy +NTU0WhcNMzAxMjA3MTc1NTU0WjCBvjELMAkGA1UEBhMCVVMxFjAUBgNVBAoTDUVu +dHJ1c3QsIEluYy4xKDAmBgNVBAsTH1NlZSB3d3cuZW50cnVzdC5uZXQvbGVnYWwt +dGVybXMxOTA3BgNVBAsTMChjKSAyMDA5IEVudHJ1c3QsIEluYy4gLSBmb3IgYXV0 +aG9yaXplZCB1c2Ugb25seTEyMDAGA1UEAxMpRW50cnVzdCBSb290IENlcnRpZmlj +YXRpb24gQXV0aG9yaXR5IC0gRzIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK +AoIBAQC6hLZy254Ma+KZ6TABp3bqMriVQRrJ2mFOWHLP/vaCeb9zYQYKpSfYs1/T +RU4cctZOMvJyig/3gxnQaoCAAEUesMfnmr8SVycco2gvCoe9amsOXmXzHHfV1IWN +cCG0szLni6LVhjkCsbjSR87kyUnEO6fe+1R9V77w6G7CebI6C1XiUJgWMhNcL3hW +wcKUs/Ja5CeanyTXxuzQmyWC48zCxEXFjJd6BmsqEZ+pCm5IO2/b1BEZQvePB7/1 +U1+cPvQXLOZprE4yTGJ36rfo5bs0vBmLrpxR57d+tVOxMyLlbc9wPBr64ptntoP0 +jaWvYkxN4FisZDQSA/i2jZRjJKRxAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAP +BgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBRqciZ60B7vfec7aVHUbI2fkBJmqzAN +BgkqhkiG9w0BAQsFAAOCAQEAeZ8dlsa2eT8ijYfThwMEYGprmi5ZiXMRrEPR9RP/ +jTkrwPK9T3CMqS/qF8QLVJ7UG5aYMzyorWKiAHarWWluBh1+xLlEjZivEtRh2woZ +Rkfz6/djwUAFQKXSt/S1mja/qYh2iARVBCuch38aNzx+LaUa2NSJXsq9rD1s2G2v +1fN2D807iDginWyTmsQ9v4IbZT+mD12q/OWyFcq1rca8PdCE6OoGcrBNOTJ4vz4R +nAuknZoh8/CbCzB428Hch0P+vGOaysXCHMnHjf87ElgI5rY97HosTvuDls4MPGmH +VHOkc8KT/1EQrBVUAdj8BbGJoX90g5pJ19xOe4pIb4tF9g== +-----END CERTIFICATE----- + +# Issuer: CN=Entrust Root Certification Authority - EC1 O=Entrust, Inc. OU=See www.entrust.net/legal-terms/(c) 2012 Entrust, Inc. - for authorized use only +# Subject: CN=Entrust Root Certification Authority - EC1 O=Entrust, Inc. OU=See www.entrust.net/legal-terms/(c) 2012 Entrust, Inc. - for authorized use only +# Label: "Entrust Root Certification Authority - EC1" +# Serial: 51543124481930649114116133369 +# MD5 Fingerprint: b6:7e:1d:f0:58:c5:49:6c:24:3b:3d:ed:98:18:ed:bc +# SHA1 Fingerprint: 20:d8:06:40:df:9b:25:f5:12:25:3a:11:ea:f7:59:8a:eb:14:b5:47 +# SHA256 Fingerprint: 02:ed:0e:b2:8c:14:da:45:16:5c:56:67:91:70:0d:64:51:d7:fb:56:f0:b2:ab:1d:3b:8e:b0:70:e5:6e:df:f5 +-----BEGIN CERTIFICATE----- +MIIC+TCCAoCgAwIBAgINAKaLeSkAAAAAUNCR+TAKBggqhkjOPQQDAzCBvzELMAkG +A1UEBhMCVVMxFjAUBgNVBAoTDUVudHJ1c3QsIEluYy4xKDAmBgNVBAsTH1NlZSB3 +d3cuZW50cnVzdC5uZXQvbGVnYWwtdGVybXMxOTA3BgNVBAsTMChjKSAyMDEyIEVu +dHJ1c3QsIEluYy4gLSBmb3IgYXV0aG9yaXplZCB1c2Ugb25seTEzMDEGA1UEAxMq +RW50cnVzdCBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IC0gRUMxMB4XDTEy +MTIxODE1MjUzNloXDTM3MTIxODE1NTUzNlowgb8xCzAJBgNVBAYTAlVTMRYwFAYD +VQQKEw1FbnRydXN0LCBJbmMuMSgwJgYDVQQLEx9TZWUgd3d3LmVudHJ1c3QubmV0 +L2xlZ2FsLXRlcm1zMTkwNwYDVQQLEzAoYykgMjAxMiBFbnRydXN0LCBJbmMuIC0g +Zm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxMzAxBgNVBAMTKkVudHJ1c3QgUm9vdCBD +ZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAtIEVDMTB2MBAGByqGSM49AgEGBSuBBAAi +A2IABIQTydC6bUF74mzQ61VfZgIaJPRbiWlH47jCffHyAsWfoPZb1YsGGYZPUxBt +ByQnoaD41UcZYUx9ypMn6nQM72+WCf5j7HBdNq1nd67JnXxVRDqiY1Ef9eNi1KlH +Bz7MIKNCMEAwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0O +BBYEFLdj5xrdjekIplWDpOBqUEFlEUJJMAoGCCqGSM49BAMDA2cAMGQCMGF52OVC +R98crlOZF7ZvHH3hvxGU0QOIdeSNiaSKd0bebWHvAvX7td/M/k7//qnmpwIwW5nX +hTcGtXsI/esni0qU+eH6p44mCOh8kmhtc9hvJqwhAriZtyZBWyVgrtBIGu4G +-----END CERTIFICATE----- + +# Issuer: CN=CFCA EV ROOT O=China Financial Certification Authority +# Subject: CN=CFCA EV ROOT O=China Financial Certification Authority +# Label: "CFCA EV ROOT" +# Serial: 407555286 +# MD5 Fingerprint: 74:e1:b6:ed:26:7a:7a:44:30:33:94:ab:7b:27:81:30 +# SHA1 Fingerprint: e2:b8:29:4b:55:84:ab:6b:58:c2:90:46:6c:ac:3f:b8:39:8f:84:83 +# SHA256 Fingerprint: 5c:c3:d7:8e:4e:1d:5e:45:54:7a:04:e6:87:3e:64:f9:0c:f9:53:6d:1c:cc:2e:f8:00:f3:55:c4:c5:fd:70:fd +-----BEGIN CERTIFICATE----- +MIIFjTCCA3WgAwIBAgIEGErM1jANBgkqhkiG9w0BAQsFADBWMQswCQYDVQQGEwJD +TjEwMC4GA1UECgwnQ2hpbmEgRmluYW5jaWFsIENlcnRpZmljYXRpb24gQXV0aG9y +aXR5MRUwEwYDVQQDDAxDRkNBIEVWIFJPT1QwHhcNMTIwODA4MDMwNzAxWhcNMjkx +MjMxMDMwNzAxWjBWMQswCQYDVQQGEwJDTjEwMC4GA1UECgwnQ2hpbmEgRmluYW5j +aWFsIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MRUwEwYDVQQDDAxDRkNBIEVWIFJP +T1QwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDXXWvNED8fBVnVBU03 +sQ7smCuOFR36k0sXgiFxEFLXUWRwFsJVaU2OFW2fvwwbwuCjZ9YMrM8irq93VCpL +TIpTUnrD7i7es3ElweldPe6hL6P3KjzJIx1qqx2hp/Hz7KDVRM8Vz3IvHWOX6Jn5 +/ZOkVIBMUtRSqy5J35DNuF++P96hyk0g1CXohClTt7GIH//62pCfCqktQT+x8Rgp +7hZZLDRJGqgG16iI0gNyejLi6mhNbiyWZXvKWfry4t3uMCz7zEasxGPrb382KzRz +EpR/38wmnvFyXVBlWY9ps4deMm/DGIq1lY+wejfeWkU7xzbh72fROdOXW3NiGUgt +hxwG+3SYIElz8AXSG7Ggo7cbcNOIabla1jj0Ytwli3i/+Oh+uFzJlU9fpy25IGvP +a931DfSCt/SyZi4QKPaXWnuWFo8BGS1sbn85WAZkgwGDg8NNkt0yxoekN+kWzqot +aK8KgWU6cMGbrU1tVMoqLUuFG7OA5nBFDWteNfB/O7ic5ARwiRIlk9oKmSJgamNg +TnYGmE69g60dWIolhdLHZR4tjsbftsbhf4oEIRUpdPA+nJCdDC7xij5aqgwJHsfV +PKPtl8MeNPo4+QgO48BdK4PRVmrJtqhUUy54Mmc9gn900PvhtgVguXDbjgv5E1hv +cWAQUhC5wUEJ73IfZzF4/5YFjQIDAQABo2MwYTAfBgNVHSMEGDAWgBTj/i39KNAL +tbq2osS/BqoFjJP7LzAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAd +BgNVHQ4EFgQU4/4t/SjQC7W6tqLEvwaqBYyT+y8wDQYJKoZIhvcNAQELBQADggIB +ACXGumvrh8vegjmWPfBEp2uEcwPenStPuiB/vHiyz5ewG5zz13ku9Ui20vsXiObT +ej/tUxPQ4i9qecsAIyjmHjdXNYmEwnZPNDatZ8POQQaIxffu2Bq41gt/UP+TqhdL +jOztUmCypAbqTuv0axn96/Ua4CUqmtzHQTb3yHQFhDmVOdYLO6Qn+gjYXB74BGBS +ESgoA//vU2YApUo0FmZ8/Qmkrp5nGm9BC2sGE5uPhnEFtC+NiWYzKXZUmhH4J/qy +P5Hgzg0b8zAarb8iXRvTvyUFTeGSGn+ZnzxEk8rUQElsgIfXBDrDMlI1Dlb4pd19 +xIsNER9Tyx6yF7Zod1rg1MvIB671Oi6ON7fQAUtDKXeMOZePglr4UeWJoBjnaH9d +Ci77o0cOPaYjesYBx4/IXr9tgFa+iiS6M+qf4TIRnvHST4D2G0CvOJ4RUHlzEhLN +5mydLIhyPDCBBpEi6lmt2hkuIsKNuYyH4Ga8cyNfIWRjgEj1oDwYPZTISEEdQLpe +/v5WOaHIz16eGWRGENoXkbcFgKyLmZJ956LYBws2J+dIeWCKw9cTXPhyQN9Ky8+Z +AAoACxGV2lZFA4gKn2fQ1XmxqI1AbQ3CekD6819kR5LLU7m7Wc5P/dAVUwHY3+vZ +5nbv0CO7O6l5s9UCKc2Jo5YPSjXnTkLAdc0Hz+Ys63su +-----END CERTIFICATE----- +# Issuer: CN=Entrust.net Secure Server Certification Authority O=Entrust.net OU=www.entrust.net/CPS incorp. by ref. (limits liab.)/(c) 1999 Entrust.net Limited +# Subject: CN=Entrust.net Secure Server Certification Authority O=Entrust.net OU=www.entrust.net/CPS incorp. by ref. (limits liab.)/(c) 1999 Entrust.net Limited +# Label: "Entrust.net Secure Server CA" +# Serial: 927650371 +# MD5 Fingerprint: df:f2:80:73:cc:f1:e6:61:73:fc:f5:42:e9:c5:7c:ee +# SHA1 Fingerprint: 99:a6:9b:e6:1a:fe:88:6b:4d:2b:82:00:7c:b8:54:fc:31:7e:15:39 +# SHA256 Fingerprint: 62:f2:40:27:8c:56:4c:4d:d8:bf:7d:9d:4f:6f:36:6e:a8:94:d2:2f:5f:34:d9:89:a9:83:ac:ec:2f:ff:ed:50 +-----BEGIN CERTIFICATE----- +MIIE2DCCBEGgAwIBAgIEN0rSQzANBgkqhkiG9w0BAQUFADCBwzELMAkGA1UEBhMC +VVMxFDASBgNVBAoTC0VudHJ1c3QubmV0MTswOQYDVQQLEzJ3d3cuZW50cnVzdC5u +ZXQvQ1BTIGluY29ycC4gYnkgcmVmLiAobGltaXRzIGxpYWIuKTElMCMGA1UECxMc +KGMpIDE5OTkgRW50cnVzdC5uZXQgTGltaXRlZDE6MDgGA1UEAxMxRW50cnVzdC5u +ZXQgU2VjdXJlIFNlcnZlciBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAeFw05OTA1 +MjUxNjA5NDBaFw0xOTA1MjUxNjM5NDBaMIHDMQswCQYDVQQGEwJVUzEUMBIGA1UE +ChMLRW50cnVzdC5uZXQxOzA5BgNVBAsTMnd3dy5lbnRydXN0Lm5ldC9DUFMgaW5j +b3JwLiBieSByZWYuIChsaW1pdHMgbGlhYi4pMSUwIwYDVQQLExwoYykgMTk5OSBF +bnRydXN0Lm5ldCBMaW1pdGVkMTowOAYDVQQDEzFFbnRydXN0Lm5ldCBTZWN1cmUg +U2VydmVyIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIGdMA0GCSqGSIb3DQEBAQUA +A4GLADCBhwKBgQDNKIM0VBuJ8w+vN5Ex/68xYMmo6LIQaO2f55M28Qpku0f1BBc/ +I0dNxScZgSYMVHINiC3ZH5oSn7yzcdOAGT9HZnuMNSjSuQrfJNqc1lB5gXpa0zf3 +wkrYKZImZNHkmGw6AIr1NJtl+O3jEP/9uElY3KDegjlrgbEWGWG5VLbmQwIBA6OC +AdcwggHTMBEGCWCGSAGG+EIBAQQEAwIABzCCARkGA1UdHwSCARAwggEMMIHeoIHb +oIHYpIHVMIHSMQswCQYDVQQGEwJVUzEUMBIGA1UEChMLRW50cnVzdC5uZXQxOzA5 +BgNVBAsTMnd3dy5lbnRydXN0Lm5ldC9DUFMgaW5jb3JwLiBieSByZWYuIChsaW1p +dHMgbGlhYi4pMSUwIwYDVQQLExwoYykgMTk5OSBFbnRydXN0Lm5ldCBMaW1pdGVk +MTowOAYDVQQDEzFFbnRydXN0Lm5ldCBTZWN1cmUgU2VydmVyIENlcnRpZmljYXRp +b24gQXV0aG9yaXR5MQ0wCwYDVQQDEwRDUkwxMCmgJ6AlhiNodHRwOi8vd3d3LmVu +dHJ1c3QubmV0L0NSTC9uZXQxLmNybDArBgNVHRAEJDAigA8xOTk5MDUyNTE2MDk0 +MFqBDzIwMTkwNTI1MTYwOTQwWjALBgNVHQ8EBAMCAQYwHwYDVR0jBBgwFoAU8Bdi +E1U9s/8KAGv7UISX8+1i0BowHQYDVR0OBBYEFPAXYhNVPbP/CgBr+1CEl/PtYtAa +MAwGA1UdEwQFMAMBAf8wGQYJKoZIhvZ9B0EABAwwChsEVjQuMAMCBJAwDQYJKoZI +hvcNAQEFBQADgYEAkNwwAvpkdMKnCqV8IY00F6j7Rw7/JXyNEwr75Ji174z4xRAN +95K+8cPV1ZVqBLssziY2ZcgxxufuP+NXdYR6Ee9GTxj005i7qIcyunL2POI9n9cd +2cNgQ4xYDiKWL2KjLB+6rQXvqzJ4h6BUcxm1XAX5Uj5tLUUL9wqT6u0G+bI= +-----END CERTIFICATE----- + +# Issuer: CN=http://www.valicert.com/ O=ValiCert, Inc. OU=ValiCert Class 2 Policy Validation Authority +# Subject: CN=http://www.valicert.com/ O=ValiCert, Inc. OU=ValiCert Class 2 Policy Validation Authority +# Label: "ValiCert Class 2 VA" +# Serial: 1 +# MD5 Fingerprint: a9:23:75:9b:ba:49:36:6e:31:c2:db:f2:e7:66:ba:87 +# SHA1 Fingerprint: 31:7a:2a:d0:7f:2b:33:5e:f5:a1:c3:4e:4b:57:e8:b7:d8:f1:fc:a6 +# SHA256 Fingerprint: 58:d0:17:27:9c:d4:dc:63:ab:dd:b1:96:a6:c9:90:6c:30:c4:e0:87:83:ea:e8:c1:60:99:54:d6:93:55:59:6b +-----BEGIN CERTIFICATE----- +MIIC5zCCAlACAQEwDQYJKoZIhvcNAQEFBQAwgbsxJDAiBgNVBAcTG1ZhbGlDZXJ0 +IFZhbGlkYXRpb24gTmV0d29yazEXMBUGA1UEChMOVmFsaUNlcnQsIEluYy4xNTAz +BgNVBAsTLFZhbGlDZXJ0IENsYXNzIDIgUG9saWN5IFZhbGlkYXRpb24gQXV0aG9y +aXR5MSEwHwYDVQQDExhodHRwOi8vd3d3LnZhbGljZXJ0LmNvbS8xIDAeBgkqhkiG +9w0BCQEWEWluZm9AdmFsaWNlcnQuY29tMB4XDTk5MDYyNjAwMTk1NFoXDTE5MDYy +NjAwMTk1NFowgbsxJDAiBgNVBAcTG1ZhbGlDZXJ0IFZhbGlkYXRpb24gTmV0d29y +azEXMBUGA1UEChMOVmFsaUNlcnQsIEluYy4xNTAzBgNVBAsTLFZhbGlDZXJ0IENs +YXNzIDIgUG9saWN5IFZhbGlkYXRpb24gQXV0aG9yaXR5MSEwHwYDVQQDExhodHRw +Oi8vd3d3LnZhbGljZXJ0LmNvbS8xIDAeBgkqhkiG9w0BCQEWEWluZm9AdmFsaWNl +cnQuY29tMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDOOnHK5avIWZJV16vY +dA757tn2VUdZZUcOBVXc65g2PFxTXdMwzzjsvUGJ7SVCCSRrCl6zfN1SLUzm1NZ9 +WlmpZdRJEy0kTRxQb7XBhVQ7/nHk01xC+YDgkRoKWzk2Z/M/VXwbP7RfZHM047QS +v4dk+NoS/zcnwbNDu+97bi5p9wIDAQABMA0GCSqGSIb3DQEBBQUAA4GBADt/UG9v +UJSZSWI4OB9L+KXIPqeCgfYrx+jFzug6EILLGACOTb2oWH+heQC1u+mNr0HZDzTu +IYEZoDJJKPTEjlbVUjP9UNV+mWwD5MlM/Mtsq2azSiGM5bUMMj4QssxsodyamEwC +W/POuZ6lcg5Ktz885hZo+L7tdEy8W9ViH0Pd +-----END CERTIFICATE----- + +# Issuer: CN=NetLock Expressz (Class C) Tanusitvanykiado O=NetLock Halozatbiztonsagi Kft. OU=Tanusitvanykiadok +# Subject: CN=NetLock Expressz (Class C) Tanusitvanykiado O=NetLock Halozatbiztonsagi Kft. OU=Tanusitvanykiadok +# Label: "NetLock Express (Class C) Root" +# Serial: 104 +# MD5 Fingerprint: 4f:eb:f1:f0:70:c2:80:63:5d:58:9f:da:12:3c:a9:c4 +# SHA1 Fingerprint: e3:92:51:2f:0a:cf:f5:05:df:f6:de:06:7f:75:37:e1:65:ea:57:4b +# SHA256 Fingerprint: 0b:5e:ed:4e:84:64:03:cf:55:e0:65:84:84:40:ed:2a:82:75:8b:f5:b9:aa:1f:25:3d:46:13:cf:a0:80:ff:3f +-----BEGIN CERTIFICATE----- +MIIFTzCCBLigAwIBAgIBaDANBgkqhkiG9w0BAQQFADCBmzELMAkGA1UEBhMCSFUx +ETAPBgNVBAcTCEJ1ZGFwZXN0MScwJQYDVQQKEx5OZXRMb2NrIEhhbG96YXRiaXp0 +b25zYWdpIEtmdC4xGjAYBgNVBAsTEVRhbnVzaXR2YW55a2lhZG9rMTQwMgYDVQQD +EytOZXRMb2NrIEV4cHJlc3N6IChDbGFzcyBDKSBUYW51c2l0dmFueWtpYWRvMB4X +DTk5MDIyNTE0MDgxMVoXDTE5MDIyMDE0MDgxMVowgZsxCzAJBgNVBAYTAkhVMREw +DwYDVQQHEwhCdWRhcGVzdDEnMCUGA1UEChMeTmV0TG9jayBIYWxvemF0Yml6dG9u +c2FnaSBLZnQuMRowGAYDVQQLExFUYW51c2l0dmFueWtpYWRvazE0MDIGA1UEAxMr +TmV0TG9jayBFeHByZXNzeiAoQ2xhc3MgQykgVGFudXNpdHZhbnlraWFkbzCBnzAN +BgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA6+ywbGGKIyWvYCDj2Z/8kwvbXY2wobNA +OoLO/XXgeDIDhlqGlZHtU/qdQPzm6N3ZW3oDvV3zOwzDUXmbrVWg6dADEK8KuhRC +2VImESLH0iDMgqSaqf64gXadarfSNnU+sYYJ9m5tfk63euyucYT2BDMIJTLrdKwW +RMbkQJMdf60CAwEAAaOCAp8wggKbMBIGA1UdEwEB/wQIMAYBAf8CAQQwDgYDVR0P +AQH/BAQDAgAGMBEGCWCGSAGG+EIBAQQEAwIABzCCAmAGCWCGSAGG+EIBDQSCAlEW +ggJNRklHWUVMRU0hIEV6ZW4gdGFudXNpdHZhbnkgYSBOZXRMb2NrIEtmdC4gQWx0 +YWxhbm9zIFN6b2xnYWx0YXRhc2kgRmVsdGV0ZWxlaWJlbiBsZWlydCBlbGphcmFz +b2sgYWxhcGphbiBrZXN6dWx0LiBBIGhpdGVsZXNpdGVzIGZvbHlhbWF0YXQgYSBO +ZXRMb2NrIEtmdC4gdGVybWVrZmVsZWxvc3NlZy1iaXp0b3NpdGFzYSB2ZWRpLiBB +IGRpZ2l0YWxpcyBhbGFpcmFzIGVsZm9nYWRhc2FuYWsgZmVsdGV0ZWxlIGF6IGVs +b2lydCBlbGxlbm9yemVzaSBlbGphcmFzIG1lZ3RldGVsZS4gQXogZWxqYXJhcyBs +ZWlyYXNhIG1lZ3RhbGFsaGF0byBhIE5ldExvY2sgS2Z0LiBJbnRlcm5ldCBob25s +YXBqYW4gYSBodHRwczovL3d3dy5uZXRsb2NrLm5ldC9kb2NzIGNpbWVuIHZhZ3kg +a2VyaGV0byBheiBlbGxlbm9yemVzQG5ldGxvY2submV0IGUtbWFpbCBjaW1lbi4g +SU1QT1JUQU5UISBUaGUgaXNzdWFuY2UgYW5kIHRoZSB1c2Ugb2YgdGhpcyBjZXJ0 +aWZpY2F0ZSBpcyBzdWJqZWN0IHRvIHRoZSBOZXRMb2NrIENQUyBhdmFpbGFibGUg +YXQgaHR0cHM6Ly93d3cubmV0bG9jay5uZXQvZG9jcyBvciBieSBlLW1haWwgYXQg +Y3BzQG5ldGxvY2submV0LjANBgkqhkiG9w0BAQQFAAOBgQAQrX/XDDKACtiG8XmY +ta3UzbM2xJZIwVzNmtkFLp++UOv0JhQQLdRmF/iewSf98e3ke0ugbLWrmldwpu2g +pO0u9f38vf5NNwgMvOOWgyL1SRt/Syu0VMGAfJlOHdCM7tCs5ZL6dVb+ZKATj7i4 +Fp1hBWeAyNDYpQcCNJgEjTME1A== +-----END CERTIFICATE----- + +# Issuer: CN=NetLock Uzleti (Class B) Tanusitvanykiado O=NetLock Halozatbiztonsagi Kft. OU=Tanusitvanykiadok +# Subject: CN=NetLock Uzleti (Class B) Tanusitvanykiado O=NetLock Halozatbiztonsagi Kft. OU=Tanusitvanykiadok +# Label: "NetLock Business (Class B) Root" +# Serial: 105 +# MD5 Fingerprint: 39:16:aa:b9:6a:41:e1:14:69:df:9e:6c:3b:72:dc:b6 +# SHA1 Fingerprint: 87:9f:4b:ee:05:df:98:58:3b:e3:60:d6:33:e7:0d:3f:fe:98:71:af +# SHA256 Fingerprint: 39:df:7b:68:2b:7b:93:8f:84:71:54:81:cc:de:8d:60:d8:f2:2e:c5:98:87:7d:0a:aa:c1:2b:59:18:2b:03:12 +-----BEGIN CERTIFICATE----- +MIIFSzCCBLSgAwIBAgIBaTANBgkqhkiG9w0BAQQFADCBmTELMAkGA1UEBhMCSFUx +ETAPBgNVBAcTCEJ1ZGFwZXN0MScwJQYDVQQKEx5OZXRMb2NrIEhhbG96YXRiaXp0 +b25zYWdpIEtmdC4xGjAYBgNVBAsTEVRhbnVzaXR2YW55a2lhZG9rMTIwMAYDVQQD +EylOZXRMb2NrIFV6bGV0aSAoQ2xhc3MgQikgVGFudXNpdHZhbnlraWFkbzAeFw05 +OTAyMjUxNDEwMjJaFw0xOTAyMjAxNDEwMjJaMIGZMQswCQYDVQQGEwJIVTERMA8G +A1UEBxMIQnVkYXBlc3QxJzAlBgNVBAoTHk5ldExvY2sgSGFsb3phdGJpenRvbnNh +Z2kgS2Z0LjEaMBgGA1UECxMRVGFudXNpdHZhbnlraWFkb2sxMjAwBgNVBAMTKU5l +dExvY2sgVXpsZXRpIChDbGFzcyBCKSBUYW51c2l0dmFueWtpYWRvMIGfMA0GCSqG +SIb3DQEBAQUAA4GNADCBiQKBgQCx6gTsIKAjwo84YM/HRrPVG/77uZmeBNwcf4xK +gZjupNTKihe5In+DCnVMm8Bp2GQ5o+2So/1bXHQawEfKOml2mrriRBf8TKPV/riX +iK+IA4kfpPIEPsgHC+b5sy96YhQJRhTKZPWLgLViqNhr1nGTLbO/CVRY7QbrqHvc +Q7GhaQIDAQABo4ICnzCCApswEgYDVR0TAQH/BAgwBgEB/wIBBDAOBgNVHQ8BAf8E +BAMCAAYwEQYJYIZIAYb4QgEBBAQDAgAHMIICYAYJYIZIAYb4QgENBIICURaCAk1G +SUdZRUxFTSEgRXplbiB0YW51c2l0dmFueSBhIE5ldExvY2sgS2Z0LiBBbHRhbGFu +b3MgU3pvbGdhbHRhdGFzaSBGZWx0ZXRlbGVpYmVuIGxlaXJ0IGVsamFyYXNvayBh +bGFwamFuIGtlc3p1bHQuIEEgaGl0ZWxlc2l0ZXMgZm9seWFtYXRhdCBhIE5ldExv +Y2sgS2Z0LiB0ZXJtZWtmZWxlbG9zc2VnLWJpenRvc2l0YXNhIHZlZGkuIEEgZGln +aXRhbGlzIGFsYWlyYXMgZWxmb2dhZGFzYW5hayBmZWx0ZXRlbGUgYXogZWxvaXJ0 +IGVsbGVub3J6ZXNpIGVsamFyYXMgbWVndGV0ZWxlLiBBeiBlbGphcmFzIGxlaXJh +c2EgbWVndGFsYWxoYXRvIGEgTmV0TG9jayBLZnQuIEludGVybmV0IGhvbmxhcGph +biBhIGh0dHBzOi8vd3d3Lm5ldGxvY2submV0L2RvY3MgY2ltZW4gdmFneSBrZXJo +ZXRvIGF6IGVsbGVub3J6ZXNAbmV0bG9jay5uZXQgZS1tYWlsIGNpbWVuLiBJTVBP +UlRBTlQhIFRoZSBpc3N1YW5jZSBhbmQgdGhlIHVzZSBvZiB0aGlzIGNlcnRpZmlj +YXRlIGlzIHN1YmplY3QgdG8gdGhlIE5ldExvY2sgQ1BTIGF2YWlsYWJsZSBhdCBo +dHRwczovL3d3dy5uZXRsb2NrLm5ldC9kb2NzIG9yIGJ5IGUtbWFpbCBhdCBjcHNA +bmV0bG9jay5uZXQuMA0GCSqGSIb3DQEBBAUAA4GBAATbrowXr/gOkDFOzT4JwG06 +sPgzTEdM43WIEJessDgVkcYplswhwG08pXTP2IKlOcNl40JwuyKQ433bNXbhoLXa +n3BukxowOR0w2y7jfLKRstE3Kfq51hdcR0/jHTjrn9V7lagonhVK0dHQKwCXoOKS +NitjrFgBazMpUIaD8QFI +-----END CERTIFICATE----- + +# Issuer: CN=http://www.valicert.com/ O=ValiCert, Inc. OU=ValiCert Class 3 Policy Validation Authority +# Subject: CN=http://www.valicert.com/ O=ValiCert, Inc. OU=ValiCert Class 3 Policy Validation Authority +# Label: "RSA Root Certificate 1" +# Serial: 1 +# MD5 Fingerprint: a2:6f:53:b7:ee:40:db:4a:68:e7:fa:18:d9:10:4b:72 +# SHA1 Fingerprint: 69:bd:8c:f4:9c:d3:00:fb:59:2e:17:93:ca:55:6a:f3:ec:aa:35:fb +# SHA256 Fingerprint: bc:23:f9:8a:31:3c:b9:2d:e3:bb:fc:3a:5a:9f:44:61:ac:39:49:4c:4a:e1:5a:9e:9d:f1:31:e9:9b:73:01:9a +-----BEGIN CERTIFICATE----- +MIIC5zCCAlACAQEwDQYJKoZIhvcNAQEFBQAwgbsxJDAiBgNVBAcTG1ZhbGlDZXJ0 +IFZhbGlkYXRpb24gTmV0d29yazEXMBUGA1UEChMOVmFsaUNlcnQsIEluYy4xNTAz +BgNVBAsTLFZhbGlDZXJ0IENsYXNzIDMgUG9saWN5IFZhbGlkYXRpb24gQXV0aG9y +aXR5MSEwHwYDVQQDExhodHRwOi8vd3d3LnZhbGljZXJ0LmNvbS8xIDAeBgkqhkiG +9w0BCQEWEWluZm9AdmFsaWNlcnQuY29tMB4XDTk5MDYyNjAwMjIzM1oXDTE5MDYy +NjAwMjIzM1owgbsxJDAiBgNVBAcTG1ZhbGlDZXJ0IFZhbGlkYXRpb24gTmV0d29y +azEXMBUGA1UEChMOVmFsaUNlcnQsIEluYy4xNTAzBgNVBAsTLFZhbGlDZXJ0IENs +YXNzIDMgUG9saWN5IFZhbGlkYXRpb24gQXV0aG9yaXR5MSEwHwYDVQQDExhodHRw +Oi8vd3d3LnZhbGljZXJ0LmNvbS8xIDAeBgkqhkiG9w0BCQEWEWluZm9AdmFsaWNl +cnQuY29tMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDjmFGWHOjVsQaBalfD +cnWTq8+epvzzFlLWLU2fNUSoLgRNB0mKOCn1dzfnt6td3zZxFJmP3MKS8edgkpfs +2Ejcv8ECIMYkpChMMFp2bbFc893enhBxoYjHW5tBbcqwuI4V7q0zK89HBFx1cQqY +JJgpp0lZpd34t0NiYfPT4tBVPwIDAQABMA0GCSqGSIb3DQEBBQUAA4GBAFa7AliE +Zwgs3x/be0kz9dNnnfS0ChCzycUs4pJqcXgn8nCDQtM+z6lU9PHYkhaM0QTLS6vJ +n0WuPIqpsHEzXcjFV9+vqDWzf4mH6eglkrh/hXqu1rweN1gqZ8mRzyqBPu3GOd/A +PhmcGcwTTYJBtYze4D1gCCAPRX5ron+jjBXu +-----END CERTIFICATE----- + +# Issuer: CN=http://www.valicert.com/ O=ValiCert, Inc. OU=ValiCert Class 1 Policy Validation Authority +# Subject: CN=http://www.valicert.com/ O=ValiCert, Inc. OU=ValiCert Class 1 Policy Validation Authority +# Label: "ValiCert Class 1 VA" +# Serial: 1 +# MD5 Fingerprint: 65:58:ab:15:ad:57:6c:1e:a8:a7:b5:69:ac:bf:ff:eb +# SHA1 Fingerprint: e5:df:74:3c:b6:01:c4:9b:98:43:dc:ab:8c:e8:6a:81:10:9f:e4:8e +# SHA256 Fingerprint: f4:c1:49:55:1a:30:13:a3:5b:c7:bf:fe:17:a7:f3:44:9b:c1:ab:5b:5a:0a:e7:4b:06:c2:3b:90:00:4c:01:04 +-----BEGIN CERTIFICATE----- +MIIC5zCCAlACAQEwDQYJKoZIhvcNAQEFBQAwgbsxJDAiBgNVBAcTG1ZhbGlDZXJ0 +IFZhbGlkYXRpb24gTmV0d29yazEXMBUGA1UEChMOVmFsaUNlcnQsIEluYy4xNTAz +BgNVBAsTLFZhbGlDZXJ0IENsYXNzIDEgUG9saWN5IFZhbGlkYXRpb24gQXV0aG9y +aXR5MSEwHwYDVQQDExhodHRwOi8vd3d3LnZhbGljZXJ0LmNvbS8xIDAeBgkqhkiG +9w0BCQEWEWluZm9AdmFsaWNlcnQuY29tMB4XDTk5MDYyNTIyMjM0OFoXDTE5MDYy +NTIyMjM0OFowgbsxJDAiBgNVBAcTG1ZhbGlDZXJ0IFZhbGlkYXRpb24gTmV0d29y +azEXMBUGA1UEChMOVmFsaUNlcnQsIEluYy4xNTAzBgNVBAsTLFZhbGlDZXJ0IENs +YXNzIDEgUG9saWN5IFZhbGlkYXRpb24gQXV0aG9yaXR5MSEwHwYDVQQDExhodHRw +Oi8vd3d3LnZhbGljZXJ0LmNvbS8xIDAeBgkqhkiG9w0BCQEWEWluZm9AdmFsaWNl +cnQuY29tMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDYWYJ6ibiWuqYvaG9Y +LqdUHAZu9OqNSLwxlBfw8068srg1knaw0KWlAdcAAxIiGQj4/xEjm84H9b9pGib+ +TunRf50sQB1ZaG6m+FiwnRqP0z/x3BkGgagO4DrdyFNFCQbmD3DD+kCmDuJWBQ8Y +TfwggtFzVXSNdnKgHZ0dwN0/cQIDAQABMA0GCSqGSIb3DQEBBQUAA4GBAFBoPUn0 +LBwGlN+VYH+Wexf+T3GtZMjdd9LvWVXoP+iOBSoh8gfStadS/pyxtuJbdxdA6nLW +I8sogTLDAHkY7FkXicnGah5xyf23dKUlRWnFSKsZ4UWKJWsZ7uW7EvV/96aNUcPw +nXS3qT6gpf+2SQMT2iLM7XGCK5nPOrf1LXLI +-----END CERTIFICATE----- + +# Issuer: CN=Equifax Secure eBusiness CA-1 O=Equifax Secure Inc. +# Subject: CN=Equifax Secure eBusiness CA-1 O=Equifax Secure Inc. +# Label: "Equifax Secure eBusiness CA 1" +# Serial: 4 +# MD5 Fingerprint: 64:9c:ef:2e:44:fc:c6:8f:52:07:d0:51:73:8f:cb:3d +# SHA1 Fingerprint: da:40:18:8b:91:89:a3:ed:ee:ae:da:97:fe:2f:9d:f5:b7:d1:8a:41 +# SHA256 Fingerprint: cf:56:ff:46:a4:a1:86:10:9d:d9:65:84:b5:ee:b5:8a:51:0c:42:75:b0:e5:f9:4f:40:bb:ae:86:5e:19:f6:73 +-----BEGIN CERTIFICATE----- +MIICgjCCAeugAwIBAgIBBDANBgkqhkiG9w0BAQQFADBTMQswCQYDVQQGEwJVUzEc +MBoGA1UEChMTRXF1aWZheCBTZWN1cmUgSW5jLjEmMCQGA1UEAxMdRXF1aWZheCBT +ZWN1cmUgZUJ1c2luZXNzIENBLTEwHhcNOTkwNjIxMDQwMDAwWhcNMjAwNjIxMDQw +MDAwWjBTMQswCQYDVQQGEwJVUzEcMBoGA1UEChMTRXF1aWZheCBTZWN1cmUgSW5j +LjEmMCQGA1UEAxMdRXF1aWZheCBTZWN1cmUgZUJ1c2luZXNzIENBLTEwgZ8wDQYJ +KoZIhvcNAQEBBQADgY0AMIGJAoGBAM4vGbwXt3fek6lfWg0XTzQaDJj0ItlZ1MRo +RvC0NcWFAyDGr0WlIVFFQesWWDYyb+JQYmT5/VGcqiTZ9J2DKocKIdMSODRsjQBu +WqDZQu4aIZX5UkxVWsUPOE9G+m34LjXWHXzr4vCwdYDIqROsvojvOm6rXyo4YgKw +Env+j6YDAgMBAAGjZjBkMBEGCWCGSAGG+EIBAQQEAwIABzAPBgNVHRMBAf8EBTAD +AQH/MB8GA1UdIwQYMBaAFEp4MlIR21kWNl7fwRQ2QGpHfEyhMB0GA1UdDgQWBBRK +eDJSEdtZFjZe38EUNkBqR3xMoTANBgkqhkiG9w0BAQQFAAOBgQB1W6ibAxHm6VZM +zfmpTMANmvPMZWnmJXbMWbfWVMMdzZmsGd20hdXgPfxiIKeES1hl8eL5lSE/9dR+ +WB5Hh1Q+WKG1tfgq73HnvMP2sUlG4tega+VWeponmHxGYhTnyfxuAxJ5gDgdSIKN +/Bf+KpYrtWKmpj29f5JZzVoqgrI3eQ== +-----END CERTIFICATE----- + +# Issuer: CN=Equifax Secure Global eBusiness CA-1 O=Equifax Secure Inc. +# Subject: CN=Equifax Secure Global eBusiness CA-1 O=Equifax Secure Inc. +# Label: "Equifax Secure Global eBusiness CA" +# Serial: 1 +# MD5 Fingerprint: 8f:5d:77:06:27:c4:98:3c:5b:93:78:e7:d7:7d:9b:cc +# SHA1 Fingerprint: 7e:78:4a:10:1c:82:65:cc:2d:e1:f1:6d:47:b4:40:ca:d9:0a:19:45 +# SHA256 Fingerprint: 5f:0b:62:ea:b5:e3:53:ea:65:21:65:16:58:fb:b6:53:59:f4:43:28:0a:4a:fb:d1:04:d7:7d:10:f9:f0:4c:07 +-----BEGIN CERTIFICATE----- +MIICkDCCAfmgAwIBAgIBATANBgkqhkiG9w0BAQQFADBaMQswCQYDVQQGEwJVUzEc +MBoGA1UEChMTRXF1aWZheCBTZWN1cmUgSW5jLjEtMCsGA1UEAxMkRXF1aWZheCBT +ZWN1cmUgR2xvYmFsIGVCdXNpbmVzcyBDQS0xMB4XDTk5MDYyMTA0MDAwMFoXDTIw +MDYyMTA0MDAwMFowWjELMAkGA1UEBhMCVVMxHDAaBgNVBAoTE0VxdWlmYXggU2Vj +dXJlIEluYy4xLTArBgNVBAMTJEVxdWlmYXggU2VjdXJlIEdsb2JhbCBlQnVzaW5l +c3MgQ0EtMTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAuucXkAJlsTRVPEnC +UdXfp9E3j9HngXNBUmCbnaEXJnitx7HoJpQytd4zjTov2/KaelpzmKNc6fuKcxtc +58O/gGzNqfTWK8D3+ZmqY6KxRwIP1ORROhI8bIpaVIRw28HFkM9yRcuoWcDNM50/ +o5brhTMhHD4ePmBudpxnhcXIw2ECAwEAAaNmMGQwEQYJYIZIAYb4QgEBBAQDAgAH +MA8GA1UdEwEB/wQFMAMBAf8wHwYDVR0jBBgwFoAUvqigdHJQa0S3ySPY+6j/s1dr +aGwwHQYDVR0OBBYEFL6ooHRyUGtEt8kj2Puo/7NXa2hsMA0GCSqGSIb3DQEBBAUA +A4GBADDiAVGqx+pf2rnQZQ8w1j7aDRRJbpGTJxQx78T3LUX47Me/okENI7SS+RkA +Z70Br83gcfxaz2TE4JaY0KNA4gGK7ycH8WUBikQtBmV1UsCGECAhX2xrD2yuCRyv +8qIYNMR1pHMc8Y3c7635s3a0kr/clRAevsvIO1qEYBlWlKlV +-----END CERTIFICATE----- + +# Issuer: CN=Thawte Premium Server CA O=Thawte Consulting cc OU=Certification Services Division +# Subject: CN=Thawte Premium Server CA O=Thawte Consulting cc OU=Certification Services Division +# Label: "Thawte Premium Server CA" +# Serial: 1 +# MD5 Fingerprint: 06:9f:69:79:16:66:90:02:1b:8c:8c:a2:c3:07:6f:3a +# SHA1 Fingerprint: 62:7f:8d:78:27:65:63:99:d2:7d:7f:90:44:c9:fe:b3:f3:3e:fa:9a +# SHA256 Fingerprint: ab:70:36:36:5c:71:54:aa:29:c2:c2:9f:5d:41:91:16:3b:16:2a:22:25:01:13:57:d5:6d:07:ff:a7:bc:1f:72 +-----BEGIN CERTIFICATE----- +MIIDJzCCApCgAwIBAgIBATANBgkqhkiG9w0BAQQFADCBzjELMAkGA1UEBhMCWkEx +FTATBgNVBAgTDFdlc3Rlcm4gQ2FwZTESMBAGA1UEBxMJQ2FwZSBUb3duMR0wGwYD +VQQKExRUaGF3dGUgQ29uc3VsdGluZyBjYzEoMCYGA1UECxMfQ2VydGlmaWNhdGlv +biBTZXJ2aWNlcyBEaXZpc2lvbjEhMB8GA1UEAxMYVGhhd3RlIFByZW1pdW0gU2Vy +dmVyIENBMSgwJgYJKoZIhvcNAQkBFhlwcmVtaXVtLXNlcnZlckB0aGF3dGUuY29t +MB4XDTk2MDgwMTAwMDAwMFoXDTIwMTIzMTIzNTk1OVowgc4xCzAJBgNVBAYTAlpB +MRUwEwYDVQQIEwxXZXN0ZXJuIENhcGUxEjAQBgNVBAcTCUNhcGUgVG93bjEdMBsG +A1UEChMUVGhhd3RlIENvbnN1bHRpbmcgY2MxKDAmBgNVBAsTH0NlcnRpZmljYXRp +b24gU2VydmljZXMgRGl2aXNpb24xITAfBgNVBAMTGFRoYXd0ZSBQcmVtaXVtIFNl +cnZlciBDQTEoMCYGCSqGSIb3DQEJARYZcHJlbWl1bS1zZXJ2ZXJAdGhhd3RlLmNv +bTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA0jY2aovXwlue2oFBYo847kkE +VdbQ7xwblRZH7xhINTpS9CtqBo87L+pW46+GjZ4X9560ZXUCTe/LCaIhUdib0GfQ +ug2SBhRz1JPLlyoAnFxODLz6FVL88kRu2hFKbgifLy3j+ao6hnO2RlNYyIkFvYMR +uHM/qgeN9EJN50CdHDcCAwEAAaMTMBEwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG +9w0BAQQFAAOBgQAmSCwWwlj66BZ0DKqqX1Q/8tfJeGBeXm43YyJ3Nn6yF8Q0ufUI +hfzJATj/Tb7yFkJD57taRvvBxhEf8UqwKEbJw8RCfbz6q1lu1bdRiBHjpIUZa4JM +pAwSremkrj/xw0llmozFyD4lt5SZu5IycQfwhl7tUCemDaYj+bvLpgcUQg== +-----END CERTIFICATE----- + +# Issuer: CN=Thawte Server CA O=Thawte Consulting cc OU=Certification Services Division +# Subject: CN=Thawte Server CA O=Thawte Consulting cc OU=Certification Services Division +# Label: "Thawte Server CA" +# Serial: 1 +# MD5 Fingerprint: c5:70:c4:a2:ed:53:78:0c:c8:10:53:81:64:cb:d0:1d +# SHA1 Fingerprint: 23:e5:94:94:51:95:f2:41:48:03:b4:d5:64:d2:a3:a3:f5:d8:8b:8c +# SHA256 Fingerprint: b4:41:0b:73:e2:e6:ea:ca:47:fb:c4:2f:8f:a4:01:8a:f4:38:1d:c5:4c:fa:a8:44:50:46:1e:ed:09:45:4d:e9 +-----BEGIN CERTIFICATE----- +MIIDEzCCAnygAwIBAgIBATANBgkqhkiG9w0BAQQFADCBxDELMAkGA1UEBhMCWkEx +FTATBgNVBAgTDFdlc3Rlcm4gQ2FwZTESMBAGA1UEBxMJQ2FwZSBUb3duMR0wGwYD +VQQKExRUaGF3dGUgQ29uc3VsdGluZyBjYzEoMCYGA1UECxMfQ2VydGlmaWNhdGlv +biBTZXJ2aWNlcyBEaXZpc2lvbjEZMBcGA1UEAxMQVGhhd3RlIFNlcnZlciBDQTEm +MCQGCSqGSIb3DQEJARYXc2VydmVyLWNlcnRzQHRoYXd0ZS5jb20wHhcNOTYwODAx +MDAwMDAwWhcNMjAxMjMxMjM1OTU5WjCBxDELMAkGA1UEBhMCWkExFTATBgNVBAgT +DFdlc3Rlcm4gQ2FwZTESMBAGA1UEBxMJQ2FwZSBUb3duMR0wGwYDVQQKExRUaGF3 +dGUgQ29uc3VsdGluZyBjYzEoMCYGA1UECxMfQ2VydGlmaWNhdGlvbiBTZXJ2aWNl +cyBEaXZpc2lvbjEZMBcGA1UEAxMQVGhhd3RlIFNlcnZlciBDQTEmMCQGCSqGSIb3 +DQEJARYXc2VydmVyLWNlcnRzQHRoYXd0ZS5jb20wgZ8wDQYJKoZIhvcNAQEBBQAD +gY0AMIGJAoGBANOkUG7I/1Zr5s9dtuoMaHVHoqrC2oQl/Kj0R1HahbUgdJSGHg91 +yekIYfUGbTBuFRkC6VLAYttNmZ7iagxEOM3+vuNkCXDF/rFrKbYvScg71CcEJRCX +L+eQbcAoQpnXTEPew/UhbVSfXcNY4cDk2VuwuNy0e982OsK1ZiIS1ocNAgMBAAGj +EzARMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEEBQADgYEAB/pMaVz7lcxG +7oWDTSEwjsrZqG9JGubaUeNgcGyEYRGhGshIPllDfU+VPaGLtwtimHp1it2ITk6e +QNuozDJ0uW8NxuOzRAvZim+aKZuZGCg70eNAKJpaPNW15yAbi8qkq43pUdniTCxZ +qdq5snUb9kLy78fyGPmJvKP/iiMucEc= +-----END CERTIFICATE----- + +# Issuer: O=VeriSign, Inc. OU=Class 3 Public Primary Certification Authority +# Subject: O=VeriSign, Inc. OU=Class 3 Public Primary Certification Authority +# Label: "Verisign Class 3 Public Primary Certification Authority" +# Serial: 149843929435818692848040365716851702463 +# MD5 Fingerprint: 10:fc:63:5d:f6:26:3e:0d:f3:25:be:5f:79:cd:67:67 +# SHA1 Fingerprint: 74:2c:31:92:e6:07:e4:24:eb:45:49:54:2b:e1:bb:c5:3e:61:74:e2 +# SHA256 Fingerprint: e7:68:56:34:ef:ac:f6:9a:ce:93:9a:6b:25:5b:7b:4f:ab:ef:42:93:5b:50:a2:65:ac:b5:cb:60:27:e4:4e:70 +-----BEGIN CERTIFICATE----- +MIICPDCCAaUCEHC65B0Q2Sk0tjjKewPMur8wDQYJKoZIhvcNAQECBQAwXzELMAkG +A1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMTcwNQYDVQQLEy5DbGFz +cyAzIFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTk2 +MDEyOTAwMDAwMFoXDTI4MDgwMTIzNTk1OVowXzELMAkGA1UEBhMCVVMxFzAVBgNV +BAoTDlZlcmlTaWduLCBJbmMuMTcwNQYDVQQLEy5DbGFzcyAzIFB1YmxpYyBQcmlt +YXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIGfMA0GCSqGSIb3DQEBAQUAA4GN +ADCBiQKBgQDJXFme8huKARS0EN8EQNvjV69qRUCPhAwL0TPZ2RHP7gJYHyX3KqhE +BarsAx94f56TuZoAqiN91qyFomNFx3InzPRMxnVx0jnvT0Lwdd8KkMaOIG+YD/is +I19wKTakyYbnsZogy1Olhec9vn2a/iRFM9x2Fe0PonFkTGUugWhFpwIDAQABMA0G +CSqGSIb3DQEBAgUAA4GBALtMEivPLCYATxQT3ab7/AoRhIzzKBxnki98tsX63/Do +lbwdj2wsqFHMc9ikwFPwTtYmwHYBV4GSXiHx0bH/59AhWM1pF+NEHJwZRDmJXNyc +AA9WjQKZ7aKQRUzkuxCkPfAyAw7xzvjoyVGM5mKf5p/AfbdynMk2OmufTqj/ZA1k +-----END CERTIFICATE----- + +# Issuer: O=VeriSign, Inc. OU=Class 3 Public Primary Certification Authority +# Subject: O=VeriSign, Inc. OU=Class 3 Public Primary Certification Authority +# Label: "Verisign Class 3 Public Primary Certification Authority" +# Serial: 80507572722862485515306429940691309246 +# MD5 Fingerprint: ef:5a:f1:33:ef:f1:cd:bb:51:02:ee:12:14:4b:96:c4 +# SHA1 Fingerprint: a1:db:63:93:91:6f:17:e4:18:55:09:40:04:15:c7:02:40:b0:ae:6b +# SHA256 Fingerprint: a4:b6:b3:99:6f:c2:f3:06:b3:fd:86:81:bd:63:41:3d:8c:50:09:cc:4f:a3:29:c2:cc:f0:e2:fa:1b:14:03:05 +-----BEGIN CERTIFICATE----- +MIICPDCCAaUCEDyRMcsf9tAbDpq40ES/Er4wDQYJKoZIhvcNAQEFBQAwXzELMAkG +A1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMTcwNQYDVQQLEy5DbGFz +cyAzIFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTk2 +MDEyOTAwMDAwMFoXDTI4MDgwMjIzNTk1OVowXzELMAkGA1UEBhMCVVMxFzAVBgNV +BAoTDlZlcmlTaWduLCBJbmMuMTcwNQYDVQQLEy5DbGFzcyAzIFB1YmxpYyBQcmlt +YXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIGfMA0GCSqGSIb3DQEBAQUAA4GN +ADCBiQKBgQDJXFme8huKARS0EN8EQNvjV69qRUCPhAwL0TPZ2RHP7gJYHyX3KqhE +BarsAx94f56TuZoAqiN91qyFomNFx3InzPRMxnVx0jnvT0Lwdd8KkMaOIG+YD/is +I19wKTakyYbnsZogy1Olhec9vn2a/iRFM9x2Fe0PonFkTGUugWhFpwIDAQABMA0G +CSqGSIb3DQEBBQUAA4GBABByUqkFFBkyCEHwxWsKzH4PIRnN5GfcX6kb5sroc50i +2JhucwNhkcV8sEVAbkSdjbCxlnRhLQ2pRdKkkirWmnWXbj9T/UWZYB2oK0z5XqcJ +2HUw19JlYD1n1khVdWk/kfVIC0dpImmClr7JyDiGSnoscxlIaU5rfGW/D/xwzoiQ +-----END CERTIFICATE----- + +# Issuer: O=VeriSign, Inc. OU=Class 3 Public Primary Certification Authority - G2/(c) 1998 VeriSign, Inc. - For authorized use only/VeriSign Trust Network +# Subject: O=VeriSign, Inc. OU=Class 3 Public Primary Certification Authority - G2/(c) 1998 VeriSign, Inc. - For authorized use only/VeriSign Trust Network +# Label: "Verisign Class 3 Public Primary Certification Authority - G2" +# Serial: 167285380242319648451154478808036881606 +# MD5 Fingerprint: a2:33:9b:4c:74:78:73:d4:6c:e7:c1:f3:8d:cb:5c:e9 +# SHA1 Fingerprint: 85:37:1c:a6:e5:50:14:3d:ce:28:03:47:1b:de:3a:09:e8:f8:77:0f +# SHA256 Fingerprint: 83:ce:3c:12:29:68:8a:59:3d:48:5f:81:97:3c:0f:91:95:43:1e:da:37:cc:5e:36:43:0e:79:c7:a8:88:63:8b +-----BEGIN CERTIFICATE----- +MIIDAjCCAmsCEH3Z/gfPqB63EHln+6eJNMYwDQYJKoZIhvcNAQEFBQAwgcExCzAJ +BgNVBAYTAlVTMRcwFQYDVQQKEw5WZXJpU2lnbiwgSW5jLjE8MDoGA1UECxMzQ2xh +c3MgMyBQdWJsaWMgUHJpbWFyeSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAtIEcy +MTowOAYDVQQLEzEoYykgMTk5OCBWZXJpU2lnbiwgSW5jLiAtIEZvciBhdXRob3Jp +emVkIHVzZSBvbmx5MR8wHQYDVQQLExZWZXJpU2lnbiBUcnVzdCBOZXR3b3JrMB4X +DTk4MDUxODAwMDAwMFoXDTI4MDgwMTIzNTk1OVowgcExCzAJBgNVBAYTAlVTMRcw +FQYDVQQKEw5WZXJpU2lnbiwgSW5jLjE8MDoGA1UECxMzQ2xhc3MgMyBQdWJsaWMg +UHJpbWFyeSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAtIEcyMTowOAYDVQQLEzEo +YykgMTk5OCBWZXJpU2lnbiwgSW5jLiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5 +MR8wHQYDVQQLExZWZXJpU2lnbiBUcnVzdCBOZXR3b3JrMIGfMA0GCSqGSIb3DQEB +AQUAA4GNADCBiQKBgQDMXtERXVxp0KvTuWpMmR9ZmDCOFoUgRm1HP9SFIIThbbP4 +pO0M8RcPO/mn+SXXwc+EY/J8Y8+iR/LGWzOOZEAEaMGAuWQcRXfH2G71lSk8UOg0 +13gfqLptQ5GVj0VXXn7F+8qkBOvqlzdUMG+7AUcyM83cV5tkaWH4mx0ciU9cZwID +AQABMA0GCSqGSIb3DQEBBQUAA4GBAFFNzb5cy5gZnBWyATl4Lk0PZ3BwmcYQWpSk +U01UbSuvDV1Ai2TT1+7eVmGSX6bEHRBhNtMsJzzoKQm5EWR0zLVznxxIqbxhAe7i +F6YM40AIOw7n60RzKprxaZLvcRTDOaxxp5EJb+RxBrO6WVcmeQD2+A2iMzAo1KpY +oJ2daZH9 +-----END CERTIFICATE----- + +# Issuer: CN=GTE CyberTrust Global Root O=GTE Corporation OU=GTE CyberTrust Solutions, Inc. +# Subject: CN=GTE CyberTrust Global Root O=GTE Corporation OU=GTE CyberTrust Solutions, Inc. +# Label: "GTE CyberTrust Global Root" +# Serial: 421 +# MD5 Fingerprint: ca:3d:d3:68:f1:03:5c:d0:32:fa:b8:2b:59:e8:5a:db +# SHA1 Fingerprint: 97:81:79:50:d8:1c:96:70:cc:34:d8:09:cf:79:44:31:36:7e:f4:74 +# SHA256 Fingerprint: a5:31:25:18:8d:21:10:aa:96:4b:02:c7:b7:c6:da:32:03:17:08:94:e5:fb:71:ff:fb:66:67:d5:e6:81:0a:36 +-----BEGIN CERTIFICATE----- +MIICWjCCAcMCAgGlMA0GCSqGSIb3DQEBBAUAMHUxCzAJBgNVBAYTAlVTMRgwFgYD +VQQKEw9HVEUgQ29ycG9yYXRpb24xJzAlBgNVBAsTHkdURSBDeWJlclRydXN0IFNv +bHV0aW9ucywgSW5jLjEjMCEGA1UEAxMaR1RFIEN5YmVyVHJ1c3QgR2xvYmFsIFJv +b3QwHhcNOTgwODEzMDAyOTAwWhcNMTgwODEzMjM1OTAwWjB1MQswCQYDVQQGEwJV +UzEYMBYGA1UEChMPR1RFIENvcnBvcmF0aW9uMScwJQYDVQQLEx5HVEUgQ3liZXJU +cnVzdCBTb2x1dGlvbnMsIEluYy4xIzAhBgNVBAMTGkdURSBDeWJlclRydXN0IEds +b2JhbCBSb290MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCVD6C28FCc6HrH +iM3dFw4usJTQGz0O9pTAipTHBsiQl8i4ZBp6fmw8U+E3KHNgf7KXUwefU/ltWJTS +r41tiGeA5u2ylc9yMcqlHHK6XALnZELn+aks1joNrI1CqiQBOeacPwGFVw1Yh0X4 +04Wqk2kmhXBIgD8SFcd5tB8FLztimQIDAQABMA0GCSqGSIb3DQEBBAUAA4GBAG3r +GwnpXtlR22ciYaQqPEh346B8pt5zohQDhT37qw4wxYMWM4ETCJ57NE7fQMh017l9 +3PR2VX2bY1QY6fDq81yx2YtCHrnAlU66+tXifPVoYb+O7AWXX1uw16OFNMQkpw0P +lZPvy5TYnh+dXIVtx6quTx8itc2VrbqnzPmrC3p/ +-----END CERTIFICATE----- diff --git a/eos/__init__.py b/eos/__init__.py index f6e146b5f..83ab57f41 100644 --- a/eos/__init__.py +++ b/eos/__init__.py @@ -1,8 +1,2 @@ -version = "0.2.3" -tag = "git" - - -def test(): - import tests.runTests - import unittest - unittest.main(defaultTest="discover", testLoader=tests.runTests.loader) +version = "0.2.3" +tag = "git" diff --git a/eos/config.py b/eos/config.py index 63bf7695b..01d65cfe0 100644 --- a/eos/config.py +++ b/eos/config.py @@ -4,10 +4,12 @@ from os.path import realpath, join, dirname, abspath debug = False gamedataCache = True saveddataCache = True +gamedata_version = "" gamedata_connectionstring = 'sqlite:///' + unicode(realpath(join(dirname(abspath(__file__)), "..", "eve.db")), sys.getfilesystemencoding()) saveddata_connectionstring = 'sqlite:///' + unicode( realpath(join(dirname(abspath(__file__)), "..", "saveddata", "saveddata.db")), sys.getfilesystemencoding()) + # Autodetect path, only change if the autodetection bugs out. path = dirname(unicode(__file__, sys.getfilesystemencoding())) diff --git a/eos/db/__init__.py b/eos/db/__init__.py index e0dd4d135..680c67e1e 100644 --- a/eos/db/__init__.py +++ b/eos/db/__init__.py @@ -20,13 +20,13 @@ import threading from sqlalchemy import MetaData, create_engine -from sqlalchemy.orm import sessionmaker, scoped_session -from sqlalchemy.ext.declarative import declarative_base -from sqlalchemy import pool - +from sqlalchemy.orm import sessionmaker import migration from eos import config +from logbook import Logger + +pyfalog = Logger(__name__) class ReadOnlyException(Exception): @@ -49,7 +49,9 @@ try: config.gamedata_version = gamedata_session.execute( "SELECT `field_value` FROM `metadata` WHERE `field_name` LIKE 'client_build'" ).fetchone()[0] -except: +except Exception as e: + pyfalog.warning("Missing gamedata version.") + pyfalog.critical(e) config.gamedata_version = None saveddata_connectionstring = config.saveddata_connectionstring @@ -62,16 +64,22 @@ if saveddata_connectionstring is not None: saveddata_meta = MetaData() saveddata_meta.bind = saveddata_engine saveddata_session = sessionmaker(bind=saveddata_engine, autoflush=False, expire_on_commit=False)() +else: + saveddata_meta = None # Lock controlling any changes introduced to session sd_lock = threading.Lock() # Import all the definitions for all our database stuff -from eos.db.gamedata import * -from eos.db.saveddata import * +# noinspection PyPep8 +from eos.db.gamedata import alphaClones, attribute, category, effect, group, icon, item, marketGroup, metaData, metaGroup, queries, traits, unit +# noinspection PyPep8 +from eos.db.saveddata import booster, cargo, character, crest, damagePattern, databaseRepair, drone, fighter, fit, implant, implantSet, loadDefaultDatabaseValues, miscData, module, override, price, queries, skill, targetResists, user # Import queries +# noinspection PyPep8 from eos.db.gamedata.queries import * +# noinspection PyPep8 from eos.db.saveddata.queries import * # If using in memory saveddata, you'll want to reflect it so the data structure is good. diff --git a/eos/db/gamedata/__init__.py b/eos/db/gamedata/__init__.py index 83fe03c97..eabfd7f1b 100644 --- a/eos/db/gamedata/__init__.py +++ b/eos/db/gamedata/__init__.py @@ -1,2 +1,2 @@ __all__ = ["attribute", "category", "effect", "group", "metaData", - "icon", "item", "marketGroup", "metaGroup", "unit"] + "icon", "item", "marketGroup", "metaGroup", "unit", "alphaClones"] diff --git a/eos/db/gamedata/alphaClones.py b/eos/db/gamedata/alphaClones.py new file mode 100644 index 000000000..d18130358 --- /dev/null +++ b/eos/db/gamedata/alphaClones.py @@ -0,0 +1,50 @@ +# =============================================================================== +# Copyright (C) 2010 Diego Duclos +# +# This file is part of eos. +# +# eos is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation, either version 2 of the License, or +# (at your option) any later version. +# +# eos is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with eos. If not, see . +# =============================================================================== + +from sqlalchemy import Column, String, Integer, Table, ForeignKey +from sqlalchemy.orm import relation, mapper, synonym + +from eos.db import gamedata_meta +from eos.gamedata import AlphaClone, AlphaCloneSkill + +alphaclones_table = Table( + "alphaClones", + gamedata_meta, + Column("alphaCloneID", Integer, primary_key=True), + Column("alphaCloneName", String), +) + +alphacloneskskills_table = Table( + "alphaCloneSkills", + gamedata_meta, + Column("alphaCloneID", Integer, ForeignKey("alphaClones.alphaCloneID"), primary_key=True), + Column("typeID", Integer, primary_key=True), + Column("level", Integer), +) + +mapper(AlphaClone, alphaclones_table, + properties={ + "ID": synonym("alphaCloneID"), + "skills": relation( + AlphaCloneSkill, + cascade="all,delete-orphan", + backref="clone") + }) + +mapper(AlphaCloneSkill, alphacloneskskills_table) diff --git a/eos/db/gamedata/attribute.py b/eos/db/gamedata/attribute.py index e09e0865b..ac2f73978 100644 --- a/eos/db/gamedata/attribute.py +++ b/eos/db/gamedata/attribute.py @@ -22,7 +22,7 @@ from sqlalchemy.ext.associationproxy import association_proxy from sqlalchemy.orm import relation, mapper, synonym, deferred from eos.db import gamedata_meta -from eos.types import Attribute, Icon, AttributeInfo, Unit +from eos.gamedata import Attribute, AttributeInfo, Unit, Icon typeattributes_table = Table("dgmtypeattribs", gamedata_meta, Column("value", Float), diff --git a/eos/db/gamedata/category.py b/eos/db/gamedata/category.py index d33cce130..884f93666 100644 --- a/eos/db/gamedata/category.py +++ b/eos/db/gamedata/category.py @@ -21,7 +21,7 @@ from sqlalchemy import Column, String, Integer, ForeignKey, Boolean, Table from sqlalchemy.orm import relation, mapper, synonym, deferred from eos.db import gamedata_meta -from eos.types import Category, Icon +from eos.gamedata import Category, Icon categories_table = Table("invcategories", gamedata_meta, Column("categoryID", Integer, primary_key=True), diff --git a/eos/db/gamedata/effect.py b/eos/db/gamedata/effect.py index 001d05162..d766b29eb 100644 --- a/eos/db/gamedata/effect.py +++ b/eos/db/gamedata/effect.py @@ -22,7 +22,7 @@ from sqlalchemy.ext.associationproxy import association_proxy from sqlalchemy.orm import mapper, synonym, relation, deferred from eos.db import gamedata_meta -from eos.types import Effect, EffectInfo +from eos.gamedata import Effect, EffectInfo typeeffects_table = Table("dgmtypeeffects", gamedata_meta, Column("typeID", Integer, ForeignKey("invtypes.typeID"), primary_key=True, index=True), diff --git a/eos/db/gamedata/group.py b/eos/db/gamedata/group.py index cbf0a34d8..cd6763923 100644 --- a/eos/db/gamedata/group.py +++ b/eos/db/gamedata/group.py @@ -21,7 +21,7 @@ from sqlalchemy import Column, String, Integer, Boolean, ForeignKey, Table from sqlalchemy.orm import relation, mapper, synonym, deferred from eos.db import gamedata_meta -from eos.types import Group, Icon, Category +from eos.gamedata import Category, Group, Icon groups_table = Table("invgroups", gamedata_meta, Column("groupID", Integer, primary_key=True), diff --git a/eos/db/gamedata/icon.py b/eos/db/gamedata/icon.py index d0315b28f..396c5c582 100644 --- a/eos/db/gamedata/icon.py +++ b/eos/db/gamedata/icon.py @@ -21,7 +21,7 @@ from sqlalchemy import Column, String, Integer, Table from sqlalchemy.orm import mapper, synonym, deferred from eos.db import gamedata_meta -from eos.types import Icon +from eos.gamedata import Icon icons_table = Table("icons", gamedata_meta, Column("iconID", Integer, primary_key=True), diff --git a/eos/db/gamedata/item.py b/eos/db/gamedata/item.py index a23c3a786..da43f9ef2 100644 --- a/eos/db/gamedata/item.py +++ b/eos/db/gamedata/item.py @@ -23,7 +23,7 @@ from sqlalchemy.orm import relation, mapper, synonym, deferred from sqlalchemy.orm.collections import attribute_mapped_collection from eos.db import gamedata_meta -from eos.types import Icon, Attribute, Item, Effect, MetaType, Group, Traits +from eos.gamedata import Attribute, Effect, Group, Icon, Item, MetaType, Traits items_table = Table("invtypes", gamedata_meta, Column("typeID", Integer, primary_key=True), @@ -39,8 +39,8 @@ items_table = Table("invtypes", gamedata_meta, Column("iconID", Integer, ForeignKey("icons.iconID")), Column("groupID", Integer, ForeignKey("invgroups.groupID"), index=True)) -from .metaGroup import metatypes_table -from .traits import traits_table +from .metaGroup import metatypes_table # noqa +from .traits import traits_table # noqa mapper(Item, items_table, properties={"group": relation(Group, backref="items"), diff --git a/eos/db/gamedata/marketGroup.py b/eos/db/gamedata/marketGroup.py index b5b631ea3..c107c486d 100644 --- a/eos/db/gamedata/marketGroup.py +++ b/eos/db/gamedata/marketGroup.py @@ -21,7 +21,7 @@ from sqlalchemy import Column, String, Integer, Boolean, ForeignKey, Table from sqlalchemy.orm import relation, mapper, synonym, deferred from eos.db import gamedata_meta -from eos.types import Item, MarketGroup, Icon +from eos.gamedata import Icon, Item, MarketGroup marketgroups_table = Table("invmarketgroups", gamedata_meta, Column("marketGroupID", Integer, primary_key=True), diff --git a/eos/db/gamedata/metaData.py b/eos/db/gamedata/metaData.py index 6bf500a72..d42f46a6f 100644 --- a/eos/db/gamedata/metaData.py +++ b/eos/db/gamedata/metaData.py @@ -1,30 +1,30 @@ -# =============================================================================== -# Copyright (C) 2010 Diego Duclos -# -# This file is part of eos. -# -# eos is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser General Public License as published by -# the Free Software Foundation, either version 2 of the License, or -# (at your option) any later version. -# -# eos is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public License -# along with eos. If not, see . -# =============================================================================== - -from sqlalchemy import Column, Table, String -from sqlalchemy.orm import mapper - -from eos.db import gamedata_meta -from eos.types import MetaData - -metadata_table = Table("metadata", gamedata_meta, - Column("field_name", String, primary_key=True), - Column("field_value", String)) - -mapper(MetaData, metadata_table) +# =============================================================================== +# Copyright (C) 2010 Diego Duclos +# +# This file is part of eos. +# +# eos is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation, either version 2 of the License, or +# (at your option) any later version. +# +# eos is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with eos. If not, see . +# =============================================================================== + +from sqlalchemy import Column, Table, String +from sqlalchemy.orm import mapper + +from eos.db import gamedata_meta +from eos.gamedata import MetaData + +metadata_table = Table("metadata", gamedata_meta, + Column("field_name", String, primary_key=True), + Column("field_value", String)) + +mapper(MetaData, metadata_table) diff --git a/eos/db/gamedata/metaGroup.py b/eos/db/gamedata/metaGroup.py index e5ed5c399..713e8a2c4 100644 --- a/eos/db/gamedata/metaGroup.py +++ b/eos/db/gamedata/metaGroup.py @@ -23,7 +23,7 @@ from sqlalchemy.orm import relation, mapper, synonym from eos.db import gamedata_meta from eos.db.gamedata.item import items_table -from eos.types import MetaGroup, Item, MetaType +from eos.gamedata import Item, MetaGroup, MetaType metagroups_table = Table("invmetagroups", gamedata_meta, Column("metaGroupID", Integer, primary_key=True), diff --git a/eos/db/gamedata/queries.py b/eos/db/gamedata/queries.py index 31a2b914d..5f6cca32e 100644 --- a/eos/db/gamedata/queries.py +++ b/eos/db/gamedata/queries.py @@ -25,7 +25,7 @@ from eos.db import gamedata_session from eos.db.gamedata.metaGroup import metatypes_table, items_table from eos.db.gamedata.group import groups_table from eos.db.util import processEager, processWhere -from eos.types import Item, Category, Group, MarketGroup, AttributeInfo, MetaData, MetaGroup +from eos.gamedata import AlphaClone, Attribute, Category, Group, Item, MarketGroup, MetaGroup, AttributeInfo, MetaData configVal = getattr(eos.config, "gamedataCache", None) if configVal is True: @@ -98,6 +98,24 @@ def getItem(lookfor, eager=None): return item +@cachedQuery(1, "lookfor") +def getAlphaClone(lookfor, eager=None): + if isinstance(lookfor, int): + if eager is None: + item = gamedata_session.query(AlphaClone).get(lookfor) + else: + item = gamedata_session.query(AlphaClone).options(*processEager(eager)).filter(AlphaClone.ID == lookfor).first() + else: + raise TypeError("Need integer as argument") + return item + + +def getAlphaCloneList(eager=None): + eager = processEager(eager) + clones = gamedata_session.query(AlphaClone).options(*eager).all() + return clones + + groupNameMap = {} @@ -292,9 +310,9 @@ def directAttributeRequest(itemIDs, attrIDs): if not isinstance(itemID, int): raise TypeError("All itemIDs must be integer") - q = select((eos.types.Item.typeID, eos.types.Attribute.attributeID, eos.types.Attribute.value), - and_(eos.types.Attribute.attributeID.in_(attrIDs), eos.types.Item.typeID.in_(itemIDs)), - from_obj=[join(eos.types.Attribute, eos.types.Item)]) + q = select((Item.typeID, Attribute.attributeID, Attribute.value), + and_(Attribute.attributeID.in_(attrIDs), Item.typeID.in_(itemIDs)), + from_obj=[join(Attribute, Item)]) result = gamedata_session.execute(q).fetchall() return result diff --git a/eos/db/gamedata/traits.py b/eos/db/gamedata/traits.py index 1dbc4d43d..d70c6ca58 100644 --- a/eos/db/gamedata/traits.py +++ b/eos/db/gamedata/traits.py @@ -2,7 +2,7 @@ from sqlalchemy import Column, Table, Integer, String, ForeignKey from sqlalchemy.orm import mapper from eos.db import gamedata_meta -from eos.types import Traits +from eos.gamedata import Traits traits_table = Table("invtraits", gamedata_meta, Column("typeID", Integer, ForeignKey("invtypes.typeID"), primary_key=True), diff --git a/eos/db/gamedata/unit.py b/eos/db/gamedata/unit.py index 55594eb90..5c6556ef1 100644 --- a/eos/db/gamedata/unit.py +++ b/eos/db/gamedata/unit.py @@ -21,7 +21,7 @@ from sqlalchemy import Column, Table, Integer, String from sqlalchemy.orm import mapper, synonym from eos.db import gamedata_meta -from eos.types import Unit +from eos.gamedata import Unit groups_table = Table("dgmunits", gamedata_meta, Column("unitID", Integer, primary_key=True), diff --git a/eos/db/migration.py b/eos/db/migration.py index 6dc59565c..3d8921eb2 100644 --- a/eos/db/migration.py +++ b/eos/db/migration.py @@ -1,11 +1,11 @@ -import logging +from logbook import Logger import shutil import time import config import migrations -logger = logging.getLogger(__name__) +pyfalog = Logger(__name__) def getVersion(db): @@ -37,7 +37,7 @@ def update(saveddata_engine): for version in xrange(dbVersion, appVersion): func = migrations.updates[version + 1] if func: - logger.info("Applying database update: %d", version + 1) + pyfalog.info("Applying database update: {0}", version + 1) func(saveddata_engine) # when all is said and done, set version to current diff --git a/eos/db/migrations/upgrade1.py b/eos/db/migrations/upgrade1.py index fc2148734..5eb3478c6 100644 --- a/eos/db/migrations/upgrade1.py +++ b/eos/db/migrations/upgrade1.py @@ -45,7 +45,7 @@ CONVERSIONS = { 8746, # Quantum Co-Processor 8745, # Photonic CPU Enhancer 15425, # Naiyon's Modified Co-Processor (never existed but convert - # anyway as some fits may include it) + # anyway as some fits may include it) ], 8748: [ # Upgraded Co-Processor 8747, # Nanomechanical CPU Enhancer I @@ -70,7 +70,7 @@ CONVERSIONS = { 16543, # Micro 'Vigor' Core Augmentation ], 8089: [ # Compact Light Missile Launcher - 8093, # Prototype 'Arbalest' Light Missile Launcher + 8093, # Prototype 'Arbalest' Light Missile Launcher ], 8091: [ # Ample Light Missile Launcher 7993, # Experimental TE-2100 Light Missile Launcher @@ -82,6 +82,7 @@ CONVERSIONS = { ] } + def upgrade(saveddata_engine): # Update fits schema to include target resists attribute try: @@ -92,6 +93,7 @@ def upgrade(saveddata_engine): # Convert modules for replacement_item, list in CONVERSIONS.iteritems(): for retired_item in list: - saveddata_engine.execute('UPDATE "modules" SET "itemID" = ? WHERE "itemID" = ?', (replacement_item, retired_item)) - saveddata_engine.execute('UPDATE "cargo" SET "itemID" = ? WHERE "itemID" = ?', (replacement_item, retired_item)) - + saveddata_engine.execute('UPDATE "modules" SET "itemID" = ? WHERE "itemID" = ?', + (replacement_item, retired_item)) + saveddata_engine.execute('UPDATE "cargo" SET "itemID" = ? WHERE "itemID" = ?', + (replacement_item, retired_item)) diff --git a/eos/db/migrations/upgrade10.py b/eos/db/migrations/upgrade10.py index 0bfb0f0ee..1ceeb59e8 100644 --- a/eos/db/migrations/upgrade10.py +++ b/eos/db/migrations/upgrade10.py @@ -6,6 +6,7 @@ Migration 10 import sqlalchemy + def upgrade(saveddata_engine): # Update projectedFits schema to include active attribute try: diff --git a/eos/db/migrations/upgrade11.py b/eos/db/migrations/upgrade11.py index 7265e064a..475537b01 100644 --- a/eos/db/migrations/upgrade11.py +++ b/eos/db/migrations/upgrade11.py @@ -7,7 +7,6 @@ Migration 11 modules with their new replacements """ - CONVERSIONS = { 16467: ( # Medium Gremlin Compact Energy Neutralizer 16471, # Medium Unstable Power Fluctuator I @@ -106,11 +105,12 @@ CONVERSIONS = { ), } -def upgrade(saveddata_engine): +def upgrade(saveddata_engine): # Convert modules for replacement_item, list in CONVERSIONS.iteritems(): for retired_item in list: - saveddata_engine.execute('UPDATE "modules" SET "itemID" = ? WHERE "itemID" = ?', (replacement_item, retired_item)) - saveddata_engine.execute('UPDATE "cargo" SET "itemID" = ? WHERE "itemID" = ?', (replacement_item, retired_item)) - + saveddata_engine.execute('UPDATE "modules" SET "itemID" = ? WHERE "itemID" = ?', + (replacement_item, retired_item)) + saveddata_engine.execute('UPDATE "cargo" SET "itemID" = ? WHERE "itemID" = ?', + (replacement_item, retired_item)) diff --git a/eos/db/migrations/upgrade12.py b/eos/db/migrations/upgrade12.py index dc0eee511..6e3a1d73b 100644 --- a/eos/db/migrations/upgrade12.py +++ b/eos/db/migrations/upgrade12.py @@ -7,7 +7,6 @@ Migration 12 modules with their new replacements """ - CONVERSIONS = { 16457: ( # Crosslink Compact Ballistic Control System 16459, # Muon Coil Bolt Array I @@ -330,11 +329,12 @@ CONVERSIONS = { ), } -def upgrade(saveddata_engine): +def upgrade(saveddata_engine): # Convert modules for replacement_item, list in CONVERSIONS.iteritems(): for retired_item in list: - saveddata_engine.execute('UPDATE "modules" SET "itemID" = ? WHERE "itemID" = ?', (replacement_item, retired_item)) - saveddata_engine.execute('UPDATE "cargo" SET "itemID" = ? WHERE "itemID" = ?', (replacement_item, retired_item)) - + saveddata_engine.execute('UPDATE "modules" SET "itemID" = ? WHERE "itemID" = ?', + (replacement_item, retired_item)) + saveddata_engine.execute('UPDATE "cargo" SET "itemID" = ? WHERE "itemID" = ?', + (replacement_item, retired_item)) diff --git a/eos/db/migrations/upgrade13.py b/eos/db/migrations/upgrade13.py index 047dc7129..b8af898e8 100644 --- a/eos/db/migrations/upgrade13.py +++ b/eos/db/migrations/upgrade13.py @@ -6,10 +6,11 @@ Migration 13 import sqlalchemy + def upgrade(saveddata_engine): # Update fits schema to include implant location attribute try: saveddata_engine.execute("SELECT implantLocation FROM fits LIMIT 1") except sqlalchemy.exc.DatabaseError: saveddata_engine.execute("ALTER TABLE fits ADD COLUMN implantLocation INTEGER;") - saveddata_engine.execute("UPDATE fits SET implantLocation = 0") \ No newline at end of file + saveddata_engine.execute("UPDATE fits SET implantLocation = 0") diff --git a/eos/db/migrations/upgrade14.py b/eos/db/migrations/upgrade14.py index 8b39947b2..c62afa6d1 100644 --- a/eos/db/migrations/upgrade14.py +++ b/eos/db/migrations/upgrade14.py @@ -6,8 +6,10 @@ Migration 14 import sqlalchemy + def upgrade(saveddata_engine): - if saveddata_engine.execute("SELECT name FROM sqlite_master WHERE type='table' AND name='fighters'").scalar() == 'fighters': + if saveddata_engine.execute( + "SELECT name FROM sqlite_master WHERE type='table' AND name='fighters'").scalar() == 'fighters': # Fighters table exists try: saveddata_engine.execute("SELECT active FROM fighters LIMIT 1") @@ -16,4 +18,4 @@ def upgrade(saveddata_engine): # (they will be recreated) saveddata_engine.execute("DROP TABLE fighters") - saveddata_engine.execute("DROP TABLE fightersAbilities") \ No newline at end of file + saveddata_engine.execute("DROP TABLE fightersAbilities") diff --git a/eos/db/migrations/upgrade15.py b/eos/db/migrations/upgrade15.py index d3c57a957..feb824dad 100644 --- a/eos/db/migrations/upgrade15.py +++ b/eos/db/migrations/upgrade15.py @@ -4,10 +4,8 @@ Migration 15 - Delete projected modules on citadels """ -import sqlalchemy def upgrade(saveddata_engine): - sql = """ DELETE FROM modules WHERE ID IN ( diff --git a/eos/db/migrations/upgrade16.py b/eos/db/migrations/upgrade16.py index 7dfaac97c..15f9b8786 100644 --- a/eos/db/migrations/upgrade16.py +++ b/eos/db/migrations/upgrade16.py @@ -6,6 +6,7 @@ Migration 16 import sqlalchemy + def upgrade(saveddata_engine): # Update fits schema to include notes attribute try: diff --git a/eos/db/migrations/upgrade17.py b/eos/db/migrations/upgrade17.py index c3d996899..90ed6e646 100644 --- a/eos/db/migrations/upgrade17.py +++ b/eos/db/migrations/upgrade17.py @@ -4,8 +4,6 @@ Migration 17 - Moves all fleet boosters to the new schema """ -import sqlalchemy - def upgrade(saveddata_engine): from eos.db import saveddata_session @@ -33,7 +31,8 @@ def upgrade(saveddata_engine): inserts.append({"boosterID": value, "boostedID": boosted, "active": 1}) try: - saveddata_session.execute(commandFits_table.insert(), {"boosterID": value, "boostedID": boosted, "active": 1}) - except Exception, e: + saveddata_session.execute(commandFits_table.insert(), + {"boosterID": value, "boostedID": boosted, "active": 1}) + except Exception: pass saveddata_session.commit() diff --git a/eos/db/migrations/upgrade18.py b/eos/db/migrations/upgrade18.py index fadc65134..4a13b7d57 100644 --- a/eos/db/migrations/upgrade18.py +++ b/eos/db/migrations/upgrade18.py @@ -4,27 +4,26 @@ Migration 8 - Converts modules from old Warfare Links to Command Modules """ - CONVERSIONS = { 42526: ( # Armor Command Burst I - 20069, # Armored Warfare Link - Damage Control I - 20409, # Armored Warfare Link - Passive Defense I - 22227, # Armored Warfare Link - Rapid Repair I + 20069, # Armored Warfare Link - Damage Control I + 20409, # Armored Warfare Link - Passive Defense I + 22227, # Armored Warfare Link - Rapid Repair I ), 43552: ( # Armor Command Burst II - 4264, # Armored Warfare Link - Damage Control II - 4266, # Armored Warfare Link - Passive Defense II - 4266, # Armored Warfare Link - Rapid Repair II + 4264, # Armored Warfare Link - Damage Control II + 4266, # Armored Warfare Link - Passive Defense II + 4266, # Armored Warfare Link - Rapid Repair II ), 42527: ( # Information Command Burst I - 11052, # Information Warfare Link - Sensor Integrity I - 20405, # Information Warfare Link - Recon Operation I - 20406, # Information Warfare Link - Electronic Superiority I + 11052, # Information Warfare Link - Sensor Integrity I + 20405, # Information Warfare Link - Recon Operation I + 20406, # Information Warfare Link - Electronic Superiority I ), 43554: ( # Information Command Burst II - 4268, # Information Warfare Link - Electronic Superiority II - 4270, # Information Warfare Link - Recon Operation II - 4272, # Information Warfare Link - Sensor Integrity II + 4268, # Information Warfare Link - Electronic Superiority II + 4270, # Information Warfare Link - Recon Operation II + 4272, # Information Warfare Link - Sensor Integrity II ), 42529: ( # Shield Command Burst I 20124, # Siege Warfare Link - Active Shielding I @@ -34,17 +33,17 @@ CONVERSIONS = { 43555: ( # Shield Command Burst II 4280, # Siege Warfare Link - Active Shielding II 4282, # Siege Warfare Link - Shield Efficiency II - 4284 # Siege Warfare Link - Shield Harmonizing II + 4284 # Siege Warfare Link - Shield Harmonizing II ), 42530: ( # Skirmish Command Burst I - 11017, # Skirmish Warfare Link - Interdiction Maneuvers I - 20070, # Skirmish Warfare Link - Evasive Maneuvers I - 20408, # Skirmish Warfare Link - Rapid Deployment I + 11017, # Skirmish Warfare Link - Interdiction Maneuvers I + 20070, # Skirmish Warfare Link - Evasive Maneuvers I + 20408, # Skirmish Warfare Link - Rapid Deployment I ), 43556: ( # Skirmish Command Burst II 4286, # Skirmish Warfare Link - Evasive Maneuvers II 4288, # Skirmish Warfare Link - Interdiction Maneuvers II - 4290 # Skirmish Warfare Link - Rapid Deployment II + 4290 # Skirmish Warfare Link - Rapid Deployment II ), 42528: ( # Mining Foreman Burst I 22553, # Mining Foreman Link - Harvester Capacitor Efficiency I @@ -54,15 +53,16 @@ CONVERSIONS = { 43551: ( # Mining Foreman Burst II 4274, # Mining Foreman Link - Harvester Capacitor Efficiency II 4276, # Mining Foreman Link - Laser Optimization II - 4278 # Mining Foreman Link - Mining Laser Field Enhancement II + 4278 # Mining Foreman Link - Mining Laser Field Enhancement II ), } -def upgrade(saveddata_engine): +def upgrade(saveddata_engine): # Convert modules for replacement_item, list in CONVERSIONS.iteritems(): for retired_item in list: - saveddata_engine.execute('UPDATE "modules" SET "itemID" = ? WHERE "itemID" = ?', (replacement_item, retired_item)) - saveddata_engine.execute('UPDATE "cargo" SET "itemID" = ? WHERE "itemID" = ?', (replacement_item, retired_item)) - + saveddata_engine.execute('UPDATE "modules" SET "itemID" = ? WHERE "itemID" = ?', + (replacement_item, retired_item)) + saveddata_engine.execute('UPDATE "cargo" SET "itemID" = ? WHERE "itemID" = ?', + (replacement_item, retired_item)) diff --git a/eos/db/migrations/upgrade19.py b/eos/db/migrations/upgrade19.py index 4d93edc7c..d10836e31 100644 --- a/eos/db/migrations/upgrade19.py +++ b/eos/db/migrations/upgrade19.py @@ -4,17 +4,15 @@ Migration 19 - Deletes broken references to fits from the commandFits table (see GH issue #844) """ -import sqlalchemy - def upgrade(saveddata_engine): from eos.db import saveddata_session sql = """ - DELETE FROM commandFits - WHERE boosterID NOT IN (select ID from fits) - OR boostedID NOT IN (select ID from fits) - """ + DELETE FROM commandFits + WHERE boosterID NOT IN (select ID from fits) + OR boostedID NOT IN (select ID from fits) + """ saveddata_session.execute(sql) saveddata_session.commit() diff --git a/eos/db/migrations/upgrade2.py b/eos/db/migrations/upgrade2.py index 917a5218e..526177896 100644 --- a/eos/db/migrations/upgrade2.py +++ b/eos/db/migrations/upgrade2.py @@ -6,6 +6,7 @@ Migration 2 import sqlalchemy + def upgrade(saveddata_engine): # Update characters schema to include default chars try: diff --git a/eos/db/migrations/upgrade20.py b/eos/db/migrations/upgrade20.py new file mode 100644 index 000000000..20da4331e --- /dev/null +++ b/eos/db/migrations/upgrade20.py @@ -0,0 +1,15 @@ +""" +Migration 20 + +- Adds support for alpha clones to the characters table +""" + +import sqlalchemy + + +def upgrade(saveddata_engine): + # Update characters schema to include alphaCloneID + try: + saveddata_engine.execute("SELECT alphaCloneID FROM characters LIMIT 1") + except sqlalchemy.exc.DatabaseError: + saveddata_engine.execute("ALTER TABLE characters ADD COLUMN alphaCloneID INTEGER;") diff --git a/eos/db/migrations/upgrade21.py b/eos/db/migrations/upgrade21.py new file mode 100644 index 000000000..e6101b3ad --- /dev/null +++ b/eos/db/migrations/upgrade21.py @@ -0,0 +1,10 @@ +""" +Migration 21 + +- Fixes discrepancy in drone table where we may have an amount active that is not equal to the amount in the stack + (we don't support activating only 2/5 drones). See GH issue #728 +""" + + +def upgrade(saveddata_engine): + saveddata_engine.execute("UPDATE drones SET amountActive = amount where amountActive > 0 AND amountActive <> amount;") diff --git a/eos/db/migrations/upgrade3.py b/eos/db/migrations/upgrade3.py index 0350ded72..ccc481047 100644 --- a/eos/db/migrations/upgrade3.py +++ b/eos/db/migrations/upgrade3.py @@ -6,6 +6,7 @@ Migration 3 import sqlalchemy + def upgrade(saveddata_engine): try: saveddata_engine.execute("SELECT modeID FROM fits LIMIT 1") diff --git a/eos/db/migrations/upgrade4.py b/eos/db/migrations/upgrade4.py index 87906cffc..f8c670684 100644 --- a/eos/db/migrations/upgrade4.py +++ b/eos/db/migrations/upgrade4.py @@ -10,7 +10,6 @@ Migration 4 and output of itemDiff.py """ - CONVERSIONS = { 506: ( # 'Basic' Capacitor Power Relay 8205, # Alpha Reactor Control: Capacitor Power Relay @@ -131,11 +130,12 @@ CONVERSIONS = { ), } -def upgrade(saveddata_engine): +def upgrade(saveddata_engine): # Convert modules for replacement_item, list in CONVERSIONS.iteritems(): for retired_item in list: - saveddata_engine.execute('UPDATE "modules" SET "itemID" = ? WHERE "itemID" = ?', (replacement_item, retired_item)) - saveddata_engine.execute('UPDATE "cargo" SET "itemID" = ? WHERE "itemID" = ?', (replacement_item, retired_item)) - + saveddata_engine.execute('UPDATE "modules" SET "itemID" = ? WHERE "itemID" = ?', + (replacement_item, retired_item)) + saveddata_engine.execute('UPDATE "cargo" SET "itemID" = ? WHERE "itemID" = ?', + (replacement_item, retired_item)) diff --git a/eos/db/migrations/upgrade5.py b/eos/db/migrations/upgrade5.py index cf6a3385d..1f5201cf5 100644 --- a/eos/db/migrations/upgrade5.py +++ b/eos/db/migrations/upgrade5.py @@ -4,5 +4,6 @@ Migration 5 Simply deletes damage profiles with a blank name. See GH issue #256 """ + def upgrade(saveddata_engine): saveddata_engine.execute('DELETE FROM damagePatterns WHERE name LIKE ?', ("",)) diff --git a/eos/db/migrations/upgrade6.py b/eos/db/migrations/upgrade6.py index ee8a091e6..724a94008 100644 --- a/eos/db/migrations/upgrade6.py +++ b/eos/db/migrations/upgrade6.py @@ -4,6 +4,8 @@ Migration 6 Overwrites damage profile 0 to reset bad uniform values (bad values set with bug) """ + def upgrade(saveddata_engine): saveddata_engine.execute('DELETE FROM damagePatterns WHERE name LIKE ? OR ID LIKE ?', ("Uniform", "1")) - saveddata_engine.execute('INSERT INTO damagePatterns VALUES (?, ?, ?, ?, ?, ?, ?)', (1, "Uniform", 25, 25, 25, 25, None)) + saveddata_engine.execute('INSERT INTO damagePatterns VALUES (?, ?, ?, ?, ?, ?, ?)', + (1, "Uniform", 25, 25, 25, 25, None)) diff --git a/eos/db/migrations/upgrade7.py b/eos/db/migrations/upgrade7.py index 226f84b3a..fbb74f910 100644 --- a/eos/db/migrations/upgrade7.py +++ b/eos/db/migrations/upgrade7.py @@ -8,17 +8,16 @@ Migration 7 Pyfa. """ - CONVERSIONS = { 640: ( # Scorpion 4005, # Scorpion Ishukone Watch ) } -def upgrade(saveddata_engine): +def upgrade(saveddata_engine): # Convert ships for replacement_item, list in CONVERSIONS.iteritems(): for retired_item in list: - saveddata_engine.execute('UPDATE "fits" SET "shipID" = ? WHERE "shipID" = ?', (replacement_item, retired_item)) - + saveddata_engine.execute('UPDATE "fits" SET "shipID" = ? WHERE "shipID" = ?', + (replacement_item, retired_item)) diff --git a/eos/db/migrations/upgrade8.py b/eos/db/migrations/upgrade8.py index 9d2c04321..19185443b 100644 --- a/eos/db/migrations/upgrade8.py +++ b/eos/db/migrations/upgrade8.py @@ -7,7 +7,6 @@ Migration 8 modules with their new replacements """ - CONVERSIONS = { 8529: ( # Large F-S9 Regolith Compact Shield Extender 8409, # Large Subordinate Screen Stabilizer I @@ -71,15 +70,16 @@ CONVERSIONS = { 11321, # 800mm Reinforced Nanofiber Plates I ), 11317: ( # 800mm Rolled Tungsten Compact Plates - 11315, # 800mm Reinforced Titanium Plates I + 11315, # 800mm Reinforced Titanium Plates I ), } -def upgrade(saveddata_engine): +def upgrade(saveddata_engine): # Convert modules for replacement_item, list in CONVERSIONS.iteritems(): for retired_item in list: - saveddata_engine.execute('UPDATE "modules" SET "itemID" = ? WHERE "itemID" = ?', (replacement_item, retired_item)) - saveddata_engine.execute('UPDATE "cargo" SET "itemID" = ? WHERE "itemID" = ?', (replacement_item, retired_item)) - + saveddata_engine.execute('UPDATE "modules" SET "itemID" = ? WHERE "itemID" = ?', + (replacement_item, retired_item)) + saveddata_engine.execute('UPDATE "cargo" SET "itemID" = ? WHERE "itemID" = ?', + (replacement_item, retired_item)) diff --git a/eos/db/migrations/upgrade9.py b/eos/db/migrations/upgrade9.py index ae7b66ad5..a2f5b6148 100644 --- a/eos/db/migrations/upgrade9.py +++ b/eos/db/migrations/upgrade9.py @@ -16,8 +16,10 @@ CREATE TABLE boostersTemp ( ) """ + def upgrade(saveddata_engine): saveddata_engine.execute(tmpTable) - saveddata_engine.execute("INSERT INTO boostersTemp (ID, itemID, fitID, active) SELECT ID, itemID, fitID, active FROM boosters") + saveddata_engine.execute( + "INSERT INTO boostersTemp (ID, itemID, fitID, active) SELECT ID, itemID, fitID, active FROM boosters") saveddata_engine.execute("DROP TABLE boosters") saveddata_engine.execute("ALTER TABLE boostersTemp RENAME TO boosters") diff --git a/eos/db/saveddata/booster.py b/eos/db/saveddata/booster.py index 9b8272540..11ef16506 100644 --- a/eos/db/saveddata/booster.py +++ b/eos/db/saveddata/booster.py @@ -22,7 +22,7 @@ from sqlalchemy.ext.associationproxy import association_proxy from sqlalchemy.orm import mapper, relation from eos.db import saveddata_meta -from eos.types import Booster +from eos.saveddata.booster import Booster boosters_table = Table("boosters", saveddata_meta, Column("ID", Integer, primary_key=True), diff --git a/eos/db/saveddata/cargo.py b/eos/db/saveddata/cargo.py index d4b829777..a0222ac3b 100644 --- a/eos/db/saveddata/cargo.py +++ b/eos/db/saveddata/cargo.py @@ -21,7 +21,7 @@ from sqlalchemy import Table, Column, Integer, ForeignKey from sqlalchemy.orm import mapper from eos.db import saveddata_meta -from eos.types import Cargo +from eos.saveddata.cargo import Cargo cargo_table = Table("cargo", saveddata_meta, Column("ID", Integer, primary_key=True), diff --git a/eos/db/saveddata/character.py b/eos/db/saveddata/character.py index fad8c579d..aaa8a3829 100644 --- a/eos/db/saveddata/character.py +++ b/eos/db/saveddata/character.py @@ -23,7 +23,9 @@ from sqlalchemy.orm import relation, mapper from eos.db import saveddata_meta from eos.db.saveddata.implant import charImplants_table from eos.effectHandlerHelpers import HandledImplantBoosterList -from eos.types import Character, User, Skill, Implant +from eos.saveddata.implant import Implant +from eos.saveddata.user import User +from eos.saveddata.character import Character, Skill characters_table = Table("characters", saveddata_meta, Column("ID", Integer, primary_key=True), @@ -33,10 +35,12 @@ characters_table = Table("characters", saveddata_meta, Column("defaultChar", Integer), Column("chars", String, nullable=True), Column("defaultLevel", Integer, nullable=True), + Column("alphaCloneID", Integer, nullable=True), Column("ownerID", ForeignKey("users.ID"), nullable=True)) mapper(Character, characters_table, properties={ + "_Character__alphaCloneID": characters_table.c.alphaCloneID, "savedName": characters_table.c.name, "_Character__owner": relation( User, diff --git a/eos/db/saveddata/crest.py b/eos/db/saveddata/crest.py index 2cbeeb44a..c5cbcbb05 100644 --- a/eos/db/saveddata/crest.py +++ b/eos/db/saveddata/crest.py @@ -21,7 +21,7 @@ from sqlalchemy import Table, Column, Integer, String from sqlalchemy.orm import mapper from eos.db import saveddata_meta -from eos.types import CrestChar +from eos.saveddata.crestchar import CrestChar crest_table = Table("crest", saveddata_meta, Column("ID", Integer, primary_key=True), diff --git a/eos/db/saveddata/damagePattern.py b/eos/db/saveddata/damagePattern.py index a9e59f228..d31c66ea6 100644 --- a/eos/db/saveddata/damagePattern.py +++ b/eos/db/saveddata/damagePattern.py @@ -21,7 +21,7 @@ from sqlalchemy import Table, Column, Integer, ForeignKey, String from sqlalchemy.orm import mapper from eos.db import saveddata_meta -from eos.types import DamagePattern +from eos.saveddata.damagePattern import DamagePattern damagePatterns_table = Table("damagePatterns", saveddata_meta, Column("ID", Integer, primary_key=True), diff --git a/eos/db/saveddata/databaseRepair.py b/eos/db/saveddata/databaseRepair.py index ede0f2fde..0d133d5e6 100644 --- a/eos/db/saveddata/databaseRepair.py +++ b/eos/db/saveddata/databaseRepair.py @@ -17,88 +17,223 @@ # along with pyfa. If not, see . # =============================================================================== -import sqlalchemy -import logging +from sqlalchemy.exc import DatabaseError +from logbook import Logger -logger = logging.getLogger(__name__) +pyfalog = Logger(__name__) -class DatabaseCleanup: +class DatabaseCleanup(object): def __init__(self): pass @staticmethod - def OrphanedCharacterSkills(saveddata_engine): - # Finds and fixes database corruption issues. - logger.debug("Start databsae validation and cleanup.") + def ExecuteSQLQuery(saveddata_engine, query): + try: + results = saveddata_engine.execute(query) + return results + except DatabaseError: + pyfalog.error("Failed to connect to database or error executing query:\n{0}", query) + return None + @staticmethod + def OrphanedCharacterSkills(saveddata_engine): # Find orphaned character skills. # This solves an issue where the character doesn't exist, but skills for that character do. # See issue #917 - try: - logger.debug("Running database cleanup for character skills.") - results = saveddata_engine.execute("SELECT COUNT(*) AS num FROM characterSkills " - "WHERE characterID NOT IN (SELECT ID from characters)") - row = results.first() + pyfalog.debug("Running database cleanup for character skills.") + query = "SELECT COUNT(*) AS num FROM characterSkills WHERE characterID NOT IN (SELECT ID from characters)" + results = DatabaseCleanup.ExecuteSQLQuery(saveddata_engine, query) - if row and row['num']: - delete = saveddata_engine.execute("DELETE FROM characterSkills WHERE characterID NOT IN (SELECT ID from characters)") - logger.error("Database corruption found. Cleaning up %d records.", delete.rowcount) + if results is None: + return - except sqlalchemy.exc.DatabaseError: - logger.error("Failed to connect to database.") + row = results.first() + + if row and row['num']: + query = "DELETE FROM characterSkills WHERE characterID NOT IN (SELECT ID from characters)" + delete = DatabaseCleanup.ExecuteSQLQuery(saveddata_engine, query) + pyfalog.error("Database corruption found. Cleaning up {0} records.", delete.rowcount) @staticmethod def OrphanedFitDamagePatterns(saveddata_engine): # Find orphaned damage patterns. # This solves an issue where the damage pattern doesn't exist, but fits reference the pattern. # See issue #777 - try: - logger.debug("Running database cleanup for orphaned damage patterns attached to fits.") + pyfalog.debug("Running database cleanup for orphaned damage patterns attached to fits.") + query = "SELECT COUNT(*) AS num FROM fits WHERE damagePatternID NOT IN (SELECT ID FROM damagePatterns) OR damagePatternID IS NULL" + results = DatabaseCleanup.ExecuteSQLQuery(saveddata_engine, query) - results = saveddata_engine.execute("SELECT COUNT(*) AS num FROM fits WHERE damagePatternID NOT IN (SELECT ID FROM damagePatterns) OR damagePatternID IS NULL") - row = results.first() + if results is None: + return - if row and row['num']: - # Get Uniform damage pattern ID - query = saveddata_engine.execute("SELECT ID FROM damagePatterns WHERE name = 'Uniform'") - rows = query.fetchall() + row = results.first() - if len(rows) == 0: - logger.error("Missing uniform damage pattern.") - elif len(rows) > 1: - logger.error("More than one uniform damage pattern found.") - else: - uniform_damage_pattern_id = rows[0]['ID'] - update = saveddata_engine.execute("UPDATE 'fits' SET 'damagePatternID' = ? " - "WHERE damagePatternID NOT IN (SELECT ID FROM damagePatterns) OR damagePatternID IS NULL", - uniform_damage_pattern_id) - logger.error("Database corruption found. Cleaning up %d records.", update.rowcount) - except sqlalchemy.exc.DatabaseError: - logger.error("Failed to connect to database.") + if row and row['num']: + # Get Uniform damage pattern ID + uniform_query = "SELECT ID FROM damagePatterns WHERE name = 'Uniform'" + uniform_results = DatabaseCleanup.ExecuteSQLQuery(saveddata_engine, uniform_query) + + if uniform_results is None: + return + + rows = uniform_results.fetchall() + + if len(rows) == 0: + pyfalog.error("Missing uniform damage pattern.") + elif len(rows) > 1: + pyfalog.error("More than one uniform damage pattern found.") + else: + uniform_damage_pattern_id = rows[0]['ID'] + update_query = "UPDATE 'fits' SET 'damagePatternID' = {} " \ + "WHERE damagePatternID NOT IN (SELECT ID FROM damagePatterns) OR damagePatternID IS NULL".format(uniform_damage_pattern_id) + update_results = DatabaseCleanup.ExecuteSQLQuery(saveddata_engine, update_query) + pyfalog.error("Database corruption found. Cleaning up {0} records.", update_results.rowcount) @staticmethod def OrphanedFitCharacterIDs(saveddata_engine): # Find orphaned character IDs. This solves an issue where the character doesn't exist, but fits reference the pattern. - try: - logger.debug("Running database cleanup for orphaned characters attached to fits.") - results = saveddata_engine.execute("SELECT COUNT(*) AS num FROM fits WHERE characterID NOT IN (SELECT ID FROM characters) OR characterID IS NULL") + pyfalog.debug("Running database cleanup for orphaned characters attached to fits.") + query = "SELECT COUNT(*) AS num FROM fits WHERE characterID NOT IN (SELECT ID FROM characters) OR characterID IS NULL" + results = DatabaseCleanup.ExecuteSQLQuery(saveddata_engine, query) + + if results is None: + return + + row = results.first() + + if row and row['num']: + # Get All 5 character ID + all5_query = "SELECT ID FROM characters WHERE name = 'All 5'" + all5_results = DatabaseCleanup.ExecuteSQLQuery(saveddata_engine, all5_query) + + if all5_results is None: + return + + rows = all5_results.fetchall() + + if len(rows) == 0: + pyfalog.error("Missing 'All 5' character.") + elif len(rows) > 1: + pyfalog.error("More than one 'All 5' character found.") + else: + all5_id = rows[0]['ID'] + update_query = "UPDATE 'fits' SET 'characterID' = " + str(all5_id) + \ + " WHERE characterID not in (select ID from characters) OR characterID IS NULL" + update_results = DatabaseCleanup.ExecuteSQLQuery(saveddata_engine, update_query) + pyfalog.error("Database corruption found. Cleaning up {0} records.", update_results.rowcount) + + @staticmethod + def NullDamagePatternNames(saveddata_engine): + # Find damage patterns that are missing the name. + # This solves an issue where the damage pattern ends up with a name that is null. + # See issue #949 + pyfalog.debug("Running database cleanup for missing damage pattern names.") + query = "SELECT COUNT(*) AS num FROM damagePatterns WHERE name IS NULL OR name = ''" + results = DatabaseCleanup.ExecuteSQLQuery(saveddata_engine, query) + + if results is None: + return + + row = results.first() + + if row and row['num']: + query = "DELETE FROM damagePatterns WHERE name IS NULL OR name = ''" + delete = DatabaseCleanup.ExecuteSQLQuery(saveddata_engine, query) + pyfalog.error("Database corruption found. Cleaning up {0} records.", delete.rowcount) + + @staticmethod + def NullTargetResistNames(saveddata_engine): + # Find target resists that are missing the name. + # This solves an issue where the target resist ends up with a name that is null. + # See issue #949 + pyfalog.debug("Running database cleanup for missing target resist names.") + query = "SELECT COUNT(*) AS num FROM targetResists WHERE name IS NULL OR name = ''" + results = DatabaseCleanup.ExecuteSQLQuery(saveddata_engine, query) + + if results is None: + return + + row = results.first() + + if row and row['num']: + query = "DELETE FROM targetResists WHERE name IS NULL OR name = ''" + delete = DatabaseCleanup.ExecuteSQLQuery(saveddata_engine, query) + pyfalog.error("Database corruption found. Cleaning up {0} records.", delete.rowcount) + + @staticmethod + def OrphanedFitIDItemID(saveddata_engine): + # Orphaned items that are missing the fit ID or item ID. + # See issue #954 + for table in ['drones', 'cargo', 'fighters']: + pyfalog.debug("Running database cleanup for orphaned {0} items.", table) + query = "SELECT COUNT(*) AS num FROM {} WHERE itemID IS NULL OR itemID = '' or itemID = '0' or fitID IS NULL OR fitID = '' or fitID = '0'".format( + table) + results = DatabaseCleanup.ExecuteSQLQuery(saveddata_engine, query) + + if results is None: + return + row = results.first() - if row['num']: - # Get All 5 character ID - query = saveddata_engine.execute("SELECT ID FROM characters WHERE name = 'All 5'") - rows = query.fetchall() + if row and row['num']: + query = "DELETE FROM {} WHERE itemID IS NULL OR itemID = '' or itemID = '0' or fitID IS NULL OR fitID = '' or fitID = '0'".format( + table) + delete = DatabaseCleanup.ExecuteSQLQuery(saveddata_engine, query) + pyfalog.error("Database corruption found. Cleaning up {0} records.", delete.rowcount) - if len(rows) == 0: - logger.error("Missing 'All 5' character.") - elif len(rows) > 1: - logger.error("More than one 'All 5' character found.") - else: - all5_id = rows[0]['ID'] - update = saveddata_engine.execute("UPDATE 'fits' SET 'characterID' = ? " - "WHERE characterID not in (select ID from characters) OR characterID IS NULL", - all5_id) - logger.error("Database corruption found. Cleaning up %d records.", update.rowcount) - except sqlalchemy.exc.DatabaseError: - logger.error("Failed to connect to database.") + for table in ['modules']: + pyfalog.debug("Running database cleanup for orphaned {0} items.", table) + query = "SELECT COUNT(*) AS num FROM {} WHERE itemID = '0' or fitID IS NULL OR fitID = '' or fitID = '0'".format( + table) + results = DatabaseCleanup.ExecuteSQLQuery(saveddata_engine, query) + + if results is None: + return + + row = results.first() + + if row and row['num']: + query = "DELETE FROM {} WHERE itemID = '0' or fitID IS NULL OR fitID = '' or fitID = '0'".format(table) + delete = DatabaseCleanup.ExecuteSQLQuery(saveddata_engine, query) + pyfalog.error("Database corruption found. Cleaning up {0} records.", delete.rowcount) + + @staticmethod + def NullDamageTargetPatternValues(saveddata_engine): + # Find patterns that have null values + # See issue #954 + for profileType in ['damagePatterns', 'targetResists']: + for damageType in ['em', 'thermal', 'kinetic', 'explosive']: + pyfalog.debug("Running database cleanup for null {0} values. ({1})", profileType, damageType) + query = "SELECT COUNT(*) AS num FROM {0} WHERE {1}Amount IS NULL OR {1}Amount = ''".format(profileType, + damageType) + results = DatabaseCleanup.ExecuteSQLQuery(saveddata_engine, query) + + if results is None: + return + + row = results.first() + + if row and row['num']: + query = "UPDATE '{0}' SET '{1}Amount' = '0' WHERE {1}Amount IS NULL OR Amount = ''".format(profileType, + damageType) + delete = DatabaseCleanup.ExecuteSQLQuery(saveddata_engine, query) + pyfalog.error("Database corruption found. Cleaning up {0} records.", delete.rowcount) + + @staticmethod + def DuplicateSelectedAmmoName(saveddata_engine): + # Orphaned items that are missing the fit ID or item ID. + # See issue #954 + pyfalog.debug("Running database cleanup for duplicated selected ammo profiles.") + query = "SELECT COUNT(*) AS num FROM damagePatterns WHERE name = 'Selected Ammo'" + results = DatabaseCleanup.ExecuteSQLQuery(saveddata_engine, query) + + if results is None: + return + + row = results.first() + + if row and row['num'] > 1: + query = "DELETE FROM damagePatterns WHERE name = 'Selected Ammo'" + delete = DatabaseCleanup.ExecuteSQLQuery(saveddata_engine, query) + pyfalog.error("Database corruption found. Cleaning up {0} records.", delete.rowcount) diff --git a/eos/db/saveddata/drone.py b/eos/db/saveddata/drone.py index 7163c06ff..6a5ffa8bb 100644 --- a/eos/db/saveddata/drone.py +++ b/eos/db/saveddata/drone.py @@ -21,7 +21,7 @@ from sqlalchemy import Table, Column, Integer, ForeignKey, Boolean from sqlalchemy.orm import mapper from eos.db import saveddata_meta -from eos.types import Drone +from eos.saveddata.drone import Drone drones_table = Table("drones", saveddata_meta, Column("groupID", Integer, primary_key=True), diff --git a/eos/db/saveddata/fighter.py b/eos/db/saveddata/fighter.py index 7074369da..bd7d8fd54 100644 --- a/eos/db/saveddata/fighter.py +++ b/eos/db/saveddata/fighter.py @@ -18,11 +18,12 @@ # =============================================================================== from sqlalchemy import Table, Column, Integer, ForeignKey, Boolean -from sqlalchemy.orm import * +from sqlalchemy.orm import mapper, relation from eos.db import saveddata_meta -from eos.types import Fighter, Fit -from eos.types import FighterAbility +from eos.saveddata.fighterAbility import FighterAbility +from eos.saveddata.fighter import Fighter +from eos.saveddata.fit import Fit fighters_table = Table("fighters", saveddata_meta, Column("groupID", Integer, primary_key=True), diff --git a/eos/db/saveddata/fit.py b/eos/db/saveddata/fit.py index 46a9ff5f5..8cbdb4d6f 100644 --- a/eos/db/saveddata/fit.py +++ b/eos/db/saveddata/fit.py @@ -17,21 +17,32 @@ # along with eos. If not, see . # =============================================================================== -from sqlalchemy import * from sqlalchemy.ext.associationproxy import association_proxy -from sqlalchemy.orm import * from sqlalchemy.orm.collections import attribute_mapped_collection from sqlalchemy.sql import and_ +from sqlalchemy.orm import relation, reconstructor, mapper, relationship +from sqlalchemy import ForeignKey, Column, Integer, String, Table, Boolean from eos.db import saveddata_meta +from eos.db import saveddata_session from eos.db.saveddata.cargo import cargo_table from eos.db.saveddata.drone import drones_table from eos.db.saveddata.fighter import fighters_table from eos.db.saveddata.implant import fitImplants_table from eos.db.saveddata.module import modules_table -from eos.effectHandlerHelpers import * -from eos.types import Fit, Module, User, Booster, Drone, Fighter, Cargo, Implant, Character, DamagePattern, \ - TargetResists, ImplantLocation +from eos.effectHandlerHelpers import HandledModuleList, HandledImplantBoosterList, HandledProjectedModList, \ + HandledDroneCargoList, HandledProjectedDroneList +from eos.saveddata.implant import Implant +from eos.saveddata.character import Character +from eos.saveddata.user import User +from eos.saveddata.fighter import Fighter +from eos.saveddata.fit import Fit as es_Fit, ImplantLocation +from eos.saveddata.drone import Drone +from eos.saveddata.booster import Booster +from eos.saveddata.module import Module +from eos.saveddata.cargo import Cargo +from eos.saveddata.damagePattern import DamagePattern +from eos.saveddata.targetResists import TargetResists fits_table = Table("fits", saveddata_meta, Column("ID", Integer, primary_key=True), @@ -45,7 +56,7 @@ fits_table = Table("fits", saveddata_meta, Column("targetResistsID", ForeignKey("targetResists.ID"), nullable=True), Column("modeID", Integer, nullable=True), Column("implantLocation", Integer, nullable=False, default=ImplantLocation.FIT), - Column("notes", String, nullable = True), + Column("notes", String, nullable=True), ) projectedFits_table = Table("projectedFits", saveddata_meta, @@ -61,6 +72,7 @@ commandFits_table = Table("commandFits", saveddata_meta, Column("active", Boolean, nullable=False, default=1) ) + class ProjectedFit(object): def __init__(self, sourceID, source_fit, amount=1, active=True): self.sourceID = sourceID @@ -72,9 +84,9 @@ class ProjectedFit(object): def init(self): if self.source_fit.isInvalid: # Very rare for this to happen, but be prepared for it - eos.db.saveddata_session.delete(self.source_fit) - eos.db.saveddata_session.flush() - eos.db.saveddata_session.refresh(self.victim_fit) + saveddata_session.delete(self.source_fit) + saveddata_session.flush() + saveddata_session.refresh(self.victim_fit) # We have a series of setters and getters here just in case someone # downgrades and screws up the table with NULL values @@ -91,6 +103,7 @@ class ProjectedFit(object): self.sourceID, self.victimID, self.amount, self.active, hex(id(self)) ) + class CommandFit(object): def __init__(self, boosterID, booster_fit, active=True): self.boosterID = boosterID @@ -101,125 +114,126 @@ class CommandFit(object): def init(self): if self.booster_fit.isInvalid: # Very rare for this to happen, but be prepared for it - eos.db.saveddata_session.delete(self.booster_fit) - eos.db.saveddata_session.flush() - eos.db.saveddata_session.refresh(self.boosted_fit) + saveddata_session.delete(self.booster_fit) + saveddata_session.flush() + saveddata_session.refresh(self.boosted_fit) def __repr__(self): return "CommandFit(boosterID={}, boostedID={}, active={}) at {}".format( self.boosterID, self.boostedID, self.active, hex(id(self)) ) -Fit._Fit__projectedFits = association_proxy( + +es_Fit._Fit__projectedFits = association_proxy( "victimOf", # look at the victimOf association... "source_fit", # .. and return the source fits creator=lambda sourceID, source_fit: ProjectedFit(sourceID, source_fit) ) -Fit._Fit__commandFits = association_proxy( +es_Fit._Fit__commandFits = association_proxy( "boostedOf", # look at the boostedOf association... "booster_fit", # .. and return the booster fit creator=lambda boosterID, booster_fit: CommandFit(boosterID, booster_fit) ) -mapper(Fit, fits_table, - properties={ - "_Fit__modules": relation( - Module, - collection_class=HandledModuleList, - primaryjoin=and_(modules_table.c.fitID == fits_table.c.ID, modules_table.c.projected == False), - order_by=modules_table.c.position, - cascade='all, delete, delete-orphan'), - "_Fit__projectedModules": relation( - Module, - collection_class=HandledProjectedModList, - cascade='all, delete, delete-orphan', - single_parent=True, - primaryjoin=and_(modules_table.c.fitID == fits_table.c.ID, modules_table.c.projected == True)), - "owner": relation( - User, - backref="fits"), - "itemID": fits_table.c.shipID, - "shipID": fits_table.c.shipID, - "_Fit__boosters": relation( - Booster, - collection_class=HandledImplantBoosterList, - cascade='all, delete, delete-orphan', - single_parent=True), - "_Fit__drones": relation( - Drone, - collection_class=HandledDroneCargoList, - cascade='all, delete, delete-orphan', - single_parent=True, - primaryjoin=and_(drones_table.c.fitID == fits_table.c.ID, drones_table.c.projected == False)), - "_Fit__fighters": relation( - Fighter, - collection_class=HandledDroneCargoList, - cascade='all, delete, delete-orphan', - single_parent=True, - primaryjoin=and_(fighters_table.c.fitID == fits_table.c.ID, fighters_table.c.projected == False)), - "_Fit__cargo": relation( - Cargo, - collection_class=HandledDroneCargoList, - cascade='all, delete, delete-orphan', - single_parent=True, - primaryjoin=and_(cargo_table.c.fitID == fits_table.c.ID)), - "_Fit__projectedDrones": relation( - Drone, - collection_class=HandledProjectedDroneList, - cascade='all, delete, delete-orphan', - single_parent=True, - primaryjoin=and_(drones_table.c.fitID == fits_table.c.ID, drones_table.c.projected == True)), - "_Fit__projectedFighters": relation( - Fighter, - collection_class=HandledProjectedDroneList, - cascade='all, delete, delete-orphan', - single_parent=True, - primaryjoin=and_(fighters_table.c.fitID == fits_table.c.ID, fighters_table.c.projected == True)), - "_Fit__implants": relation( - Implant, - collection_class=HandledImplantBoosterList, - cascade='all, delete, delete-orphan', - backref='fit', - single_parent=True, - primaryjoin=fitImplants_table.c.fitID == fits_table.c.ID, - secondaryjoin=fitImplants_table.c.implantID == Implant.ID, - secondary=fitImplants_table), - "_Fit__character": relation( - Character, - backref="fits"), - "_Fit__damagePattern": relation(DamagePattern), - "_Fit__targetResists": relation(TargetResists), - "projectedOnto": relationship( - ProjectedFit, - primaryjoin=projectedFits_table.c.sourceID == fits_table.c.ID, - backref='source_fit', - collection_class=attribute_mapped_collection('victimID'), - cascade='all, delete, delete-orphan'), - "victimOf": relationship( - ProjectedFit, - primaryjoin=fits_table.c.ID == projectedFits_table.c.victimID, - backref='victim_fit', - collection_class=attribute_mapped_collection('sourceID'), - cascade='all, delete, delete-orphan'), - "boostedOnto": relationship( - CommandFit, - primaryjoin=commandFits_table.c.boosterID == fits_table.c.ID, - backref='booster_fit', - collection_class=attribute_mapped_collection('boostedID'), - cascade='all, delete, delete-orphan'), - "boostedOf": relationship( - CommandFit, - primaryjoin=fits_table.c.ID == commandFits_table.c.boostedID, - backref='boosted_fit', - collection_class=attribute_mapped_collection('boosterID'), - cascade='all, delete, delete-orphan'), - } -) +mapper(es_Fit, fits_table, + properties={ + "_Fit__modules": relation( + Module, + collection_class=HandledModuleList, + primaryjoin=and_(modules_table.c.fitID == fits_table.c.ID, modules_table.c.projected == False), # noqa + order_by=modules_table.c.position, + cascade='all, delete, delete-orphan'), + "_Fit__projectedModules": relation( + Module, + collection_class=HandledProjectedModList, + cascade='all, delete, delete-orphan', + single_parent=True, + primaryjoin=and_(modules_table.c.fitID == fits_table.c.ID, modules_table.c.projected == True)), # noqa + "owner": relation( + User, + backref="fits"), + "itemID": fits_table.c.shipID, + "shipID": fits_table.c.shipID, + "_Fit__boosters": relation( + Booster, + collection_class=HandledImplantBoosterList, + cascade='all, delete, delete-orphan', + single_parent=True), + "_Fit__drones": relation( + Drone, + collection_class=HandledDroneCargoList, + cascade='all, delete, delete-orphan', + single_parent=True, + primaryjoin=and_(drones_table.c.fitID == fits_table.c.ID, drones_table.c.projected == False)), # noqa + "_Fit__fighters": relation( + Fighter, + collection_class=HandledDroneCargoList, + cascade='all, delete, delete-orphan', + single_parent=True, + primaryjoin=and_(fighters_table.c.fitID == fits_table.c.ID, fighters_table.c.projected == False)), # noqa + "_Fit__cargo": relation( + Cargo, + collection_class=HandledDroneCargoList, + cascade='all, delete, delete-orphan', + single_parent=True, + primaryjoin=and_(cargo_table.c.fitID == fits_table.c.ID)), + "_Fit__projectedDrones": relation( + Drone, + collection_class=HandledProjectedDroneList, + cascade='all, delete, delete-orphan', + single_parent=True, + primaryjoin=and_(drones_table.c.fitID == fits_table.c.ID, drones_table.c.projected == True)), # noqa + "_Fit__projectedFighters": relation( + Fighter, + collection_class=HandledProjectedDroneList, + cascade='all, delete, delete-orphan', + single_parent=True, + primaryjoin=and_(fighters_table.c.fitID == fits_table.c.ID, fighters_table.c.projected == True)), # noqa + "_Fit__implants": relation( + Implant, + collection_class=HandledImplantBoosterList, + cascade='all, delete, delete-orphan', + backref='fit', + single_parent=True, + primaryjoin=fitImplants_table.c.fitID == fits_table.c.ID, + secondaryjoin=fitImplants_table.c.implantID == Implant.ID, + secondary=fitImplants_table), + "_Fit__character": relation( + Character, + backref="fits"), + "_Fit__damagePattern": relation(DamagePattern), + "_Fit__targetResists": relation(TargetResists), + "projectedOnto": relationship( + ProjectedFit, + primaryjoin=projectedFits_table.c.sourceID == fits_table.c.ID, + backref='source_fit', + collection_class=attribute_mapped_collection('victimID'), + cascade='all, delete, delete-orphan'), + "victimOf": relationship( + ProjectedFit, + primaryjoin=fits_table.c.ID == projectedFits_table.c.victimID, + backref='victim_fit', + collection_class=attribute_mapped_collection('sourceID'), + cascade='all, delete, delete-orphan'), + "boostedOnto": relationship( + CommandFit, + primaryjoin=commandFits_table.c.boosterID == fits_table.c.ID, + backref='booster_fit', + collection_class=attribute_mapped_collection('boostedID'), + cascade='all, delete, delete-orphan'), + "boostedOf": relationship( + CommandFit, + primaryjoin=fits_table.c.ID == commandFits_table.c.boostedID, + backref='boosted_fit', + collection_class=attribute_mapped_collection('boosterID'), + cascade='all, delete, delete-orphan'), + } + ) mapper(ProjectedFit, projectedFits_table, - properties={ - "_ProjectedFit__amount": projectedFits_table.c.amount, - } -) + properties={ + "_ProjectedFit__amount": projectedFits_table.c.amount, + } + ) -mapper(CommandFit, commandFits_table) \ No newline at end of file +mapper(CommandFit, commandFits_table) diff --git a/eos/db/saveddata/implant.py b/eos/db/saveddata/implant.py index 21145698f..c5bfc4cc5 100644 --- a/eos/db/saveddata/implant.py +++ b/eos/db/saveddata/implant.py @@ -21,7 +21,7 @@ from sqlalchemy import Table, Column, Integer, ForeignKey, Boolean from sqlalchemy.orm import mapper from eos.db import saveddata_meta -from eos.types import Implant +from eos.saveddata.implant import Implant implants_table = Table("implants", saveddata_meta, Column("ID", Integer, primary_key=True), diff --git a/eos/db/saveddata/implantSet.py b/eos/db/saveddata/implantSet.py index 042e986f6..955accc07 100644 --- a/eos/db/saveddata/implantSet.py +++ b/eos/db/saveddata/implantSet.py @@ -23,7 +23,8 @@ from sqlalchemy.orm import relation, mapper from eos.db import saveddata_meta from eos.db.saveddata.implant import implantsSetMap_table from eos.effectHandlerHelpers import HandledImplantBoosterList -from eos.types import Implant, ImplantSet +from eos.saveddata.implant import Implant +from eos.saveddata.implantSet import ImplantSet implant_set_table = Table("implantSets", saveddata_meta, Column("ID", Integer, primary_key=True), diff --git a/eos/db/saveddata/loadDefaultDatabaseValues.py b/eos/db/saveddata/loadDefaultDatabaseValues.py index 3dcccca68..befaad44b 100644 --- a/eos/db/saveddata/loadDefaultDatabaseValues.py +++ b/eos/db/saveddata/loadDefaultDatabaseValues.py @@ -18,14 +18,15 @@ # =============================================================================== import eos.db -import eos.types +from eos.saveddata.damagePattern import DamagePattern as es_DamagePattern +from eos.saveddata.targetResists import TargetResists as es_TargetResists class ImportError(Exception): pass -class DefaultDatabaseValues(): +class DefaultDatabaseValues(object): def __init__(self): pass @@ -118,7 +119,7 @@ class DefaultDatabaseValues(): name, em, therm, kin, exp = damageProfileRow damageProfile = eos.db.getDamagePattern(name) if damageProfile is None: - damageProfile = eos.types.DamagePattern(em, therm, kin, exp) + damageProfile = es_DamagePattern(em, therm, kin, exp) damageProfile.name = name eos.db.save(damageProfile) @@ -180,7 +181,7 @@ class DefaultDatabaseValues(): name, em, therm, kin, exp = targetResistProfileRow resistsProfile = eos.db.eos.db.getTargetResists(name) if resistsProfile is None: - resistsProfile = eos.types.TargetResists(em, therm, kin, exp) + resistsProfile = es_TargetResists(em, therm, kin, exp) resistsProfile.name = name eos.db.save(resistsProfile) @@ -192,6 +193,6 @@ class DefaultDatabaseValues(): name, em, therm, kin, exp = damageProfileRow damageProfile = eos.db.getDamagePattern(name) if damageProfile is None: - damageProfile = eos.types.DamagePattern(em, therm, kin, exp) + damageProfile = es_DamagePattern(em, therm, kin, exp) damageProfile.name = name eos.db.save(damageProfile) diff --git a/eos/db/saveddata/miscData.py b/eos/db/saveddata/miscData.py index ac4c85b2d..2f6ceedeb 100644 --- a/eos/db/saveddata/miscData.py +++ b/eos/db/saveddata/miscData.py @@ -21,7 +21,7 @@ from sqlalchemy import Column, Table, String from sqlalchemy.orm import mapper from eos.db import saveddata_meta -from eos.types import MiscData +from eos.saveddata.miscData import MiscData miscdata_table = Table("miscdata", saveddata_meta, Column("fieldName", String, primary_key=True), diff --git a/eos/db/saveddata/module.py b/eos/db/saveddata/module.py index d6c2b068c..c5533d4ed 100644 --- a/eos/db/saveddata/module.py +++ b/eos/db/saveddata/module.py @@ -21,7 +21,8 @@ from sqlalchemy import Table, Column, Integer, ForeignKey, CheckConstraint, Bool from sqlalchemy.orm import relation, mapper from eos.db import saveddata_meta -from eos.types import Module, Fit +from eos.saveddata.module import Module +from eos.saveddata.fit import Fit modules_table = Table("modules", saveddata_meta, Column("ID", Integer, primary_key=True), diff --git a/eos/db/saveddata/override.py b/eos/db/saveddata/override.py index e260cd3df..2ca622966 100644 --- a/eos/db/saveddata/override.py +++ b/eos/db/saveddata/override.py @@ -21,7 +21,7 @@ from sqlalchemy import Table, Column, Integer, Float from sqlalchemy.orm import mapper from eos.db import saveddata_meta -from eos.types import Override +from eos.saveddata.override import Override overrides_table = Table("overrides", saveddata_meta, Column("itemID", Integer, primary_key=True, index=True), diff --git a/eos/db/saveddata/price.py b/eos/db/saveddata/price.py index 021d341e8..2135d20cf 100644 --- a/eos/db/saveddata/price.py +++ b/eos/db/saveddata/price.py @@ -21,7 +21,7 @@ from sqlalchemy import Table, Column, Float, Integer from sqlalchemy.orm import mapper from eos.db import saveddata_meta -from eos.types import Price +from eos.saveddata.price import Price prices_table = Table("prices", saveddata_meta, Column("typeID", Integer, primary_key=True), diff --git a/eos/db/saveddata/queries.py b/eos/db/saveddata/queries.py index cb3033cc1..16f6a3bf9 100644 --- a/eos/db/saveddata/queries.py +++ b/eos/db/saveddata/queries.py @@ -1,4 +1,4 @@ -#=============================================================================== +# =============================================================================== # Copyright (C) 2010 Diego Duclos # # This file is part of eos. @@ -15,26 +15,40 @@ # # You should have received a copy of the GNU Lesser General Public License # along with eos. If not, see . -#=============================================================================== +# =============================================================================== -from eos.db.util import processEager, processWhere -from eos.db import saveddata_session, sd_lock - -from eos.types import * -from eos.db.saveddata.fit import projectedFits_table from sqlalchemy.sql import and_ + +from eos.db import saveddata_session, sd_lock +from eos.db.saveddata.fit import projectedFits_table +from eos.db.util import processEager, processWhere +from eos.saveddata.price import Price +from eos.saveddata.user import User +from eos.saveddata.crestchar import CrestChar +from eos.saveddata.damagePattern import DamagePattern +from eos.saveddata.targetResists import TargetResists +from eos.saveddata.character import Character +from eos.saveddata.implantSet import ImplantSet +from eos.saveddata.fit import Fit +from eos.saveddata.miscData import MiscData +from eos.saveddata.override import Override + import eos.config configVal = getattr(eos.config, "saveddataCache", None) if configVal is True: import weakref + itemCache = {} queryCache = {} + def cachedQuery(type, amount, *keywords): itemCache[type] = localItemCache = weakref.WeakValueDictionary() queryCache[type] = typeQueryCache = {} + def deco(function): localQueryCache = typeQueryCache[function] = {} + def setCache(cacheKey, args, kwargs): items = function(*args, **kwargs) IDs = set() @@ -43,7 +57,7 @@ if configVal is True: for item in stuff: ID = getattr(item, "ID", None) if ID is None: - #Some uncachable data, don't cache this query + # Some uncachable data, don't cache this query del localQueryCache[cacheKey] break localItemCache[ID] = item @@ -54,6 +68,7 @@ if configVal is True: def checkAndReturn(*args, **kwargs): useCache = kwargs.pop("useCache", True) cacheKey = [] + items = None cacheKey.extend(args) for keyword in keywords: cacheKey.append(kwargs.get(keyword)) @@ -69,7 +84,7 @@ if configVal is True: for ID in IDs: data = localItemCache.get(ID) if data is None: - #Fuck, some of our stuff isn't cached it seems. + # Fuck, some of our stuff isn't cached it seems. items = setCache(cacheKey, args, kwargs) break items.append(data) @@ -81,11 +96,13 @@ if configVal is True: break return items + return checkAndReturn + return deco def removeCachedEntry(type, ID): - if not type in queryCache: + if type not in queryCache: return functionCache = queryCache[type] for _, localCache in functionCache.iteritems(): @@ -110,11 +127,13 @@ else: return function(*args, **kwargs) return checkAndReturn + return deco def removeCachedEntry(*args, **kwargs): return + def sqlizeString(line): # Escape backslashes first, as they will be as escape symbol in queries # Then escape percent and underscore signs @@ -122,6 +141,7 @@ def sqlizeString(line): line = line.replace("\\", "\\\\").replace("%", "\\%").replace("_", "\\_").replace("*", "%") return line + @cachedQuery(User, 1, "lookfor") def getUser(lookfor, eager=None): if isinstance(lookfor, int): @@ -140,6 +160,7 @@ def getUser(lookfor, eager=None): raise TypeError("Need integer or string as argument") return user + @cachedQuery(Character, 1, "lookfor") def getCharacter(lookfor, eager=None): if isinstance(lookfor, int): @@ -153,17 +174,20 @@ def getCharacter(lookfor, eager=None): elif isinstance(lookfor, basestring): eager = processEager(eager) with sd_lock: - character = saveddata_session.query(Character).options(*eager).filter(Character.savedName == lookfor).first() + character = saveddata_session.query(Character).options(*eager).filter( + Character.savedName == lookfor).first() else: raise TypeError("Need integer or string as argument") return character + def getCharacterList(eager=None): eager = processEager(eager) with sd_lock: characters = saveddata_session.query(Character).options(*eager).all() return characters + def getCharactersForUser(lookfor, eager=None): if isinstance(lookfor, int): eager = processEager(eager) @@ -173,6 +197,7 @@ def getCharactersForUser(lookfor, eager=None): raise TypeError("Need integer as argument") return characters + @cachedQuery(Fit, 1, "lookfor") def getFit(lookfor, eager=None): if isinstance(lookfor, int): @@ -193,6 +218,7 @@ def getFit(lookfor, eager=None): return fit + def getFitsWithShip(shipID, ownerID=None, where=None, eager=None): """ Get all the fits using a certain ship. @@ -214,6 +240,7 @@ def getFitsWithShip(shipID, ownerID=None, where=None, eager=None): return fits + def getBoosterFits(ownerID=None, where=None, eager=None): """ Get all the fits that are flagged as a boosting ship @@ -233,31 +260,41 @@ def getBoosterFits(ownerID=None, where=None, eager=None): return fits + def countAllFits(): with sd_lock: count = saveddata_session.query(Fit).count() return count -def countFitsWithShip(shipID, ownerID=None, where=None, eager=None): + +def countFitsWithShip(lookfor, ownerID=None, where=None, eager=None): """ Get all the fits using a certain ship. If no user is passed, do this for all users. """ - if isinstance(shipID, int): - if ownerID is not None and not isinstance(ownerID, int): - raise TypeError("OwnerID must be integer") - filter = Fit.shipID == shipID - if ownerID is not None: - filter = and_(filter, Fit.ownerID == ownerID) + if ownerID is not None and not isinstance(ownerID, int): + raise TypeError("OwnerID must be integer") - filter = processWhere(filter, where) - eager = processEager(eager) - with sd_lock: - count = saveddata_session.query(Fit).options(*eager).filter(filter).count() + if isinstance(lookfor, int): + filter = Fit.shipID == lookfor + elif isinstance(lookfor, list): + if len(lookfor) == 0: + return 0 + filter = Fit.shipID.in_(lookfor) else: - raise TypeError("ShipID must be integer") + raise TypeError("You must supply either an integer or ShipID must be integer") + + if ownerID is not None: + filter = and_(filter, Fit.ownerID == ownerID) + + filter = processWhere(filter, where) + eager = processEager(eager) + with sd_lock: + count = saveddata_session.query(Fit).options(*eager).filter(filter).count() + return count + def getFitList(eager=None): eager = processEager(eager) with sd_lock: @@ -265,6 +302,7 @@ def getFitList(eager=None): return fits + @cachedQuery(Price, 1, "typeID") def getPrice(typeID): if isinstance(typeID, int): @@ -274,12 +312,14 @@ def getPrice(typeID): raise TypeError("Need integer as argument") return price + def clearPrices(): with sd_lock: deleted_rows = saveddata_session.query(Price).delete() commit() return deleted_rows + def getMiscData(field): if isinstance(field, basestring): with sd_lock: @@ -288,24 +328,28 @@ def getMiscData(field): raise TypeError("Need string as argument") return data + def getDamagePatternList(eager=None): eager = processEager(eager) with sd_lock: patterns = saveddata_session.query(DamagePattern).options(*eager).all() return patterns + def getTargetResistsList(eager=None): eager = processEager(eager) with sd_lock: patterns = saveddata_session.query(TargetResists).options(*eager).all() return patterns + def getImplantSetList(eager=None): eager = processEager(eager) with sd_lock: sets = saveddata_session.query(ImplantSet).options(*eager).all() return sets + @cachedQuery(DamagePattern, 1, "lookfor") def getDamagePattern(lookfor, eager=None): if isinstance(lookfor, int): @@ -315,15 +359,18 @@ def getDamagePattern(lookfor, eager=None): else: eager = processEager(eager) with sd_lock: - pattern = saveddata_session.query(DamagePattern).options(*eager).filter(DamagePattern.ID == lookfor).first() + pattern = saveddata_session.query(DamagePattern).options(*eager).filter( + DamagePattern.ID == lookfor).first() elif isinstance(lookfor, basestring): eager = processEager(eager) with sd_lock: - pattern = saveddata_session.query(DamagePattern).options(*eager).filter(DamagePattern.name == lookfor).first() + pattern = saveddata_session.query(DamagePattern).options(*eager).filter( + DamagePattern.name == lookfor).first() else: raise TypeError("Need integer or string as argument") return pattern + @cachedQuery(TargetResists, 1, "lookfor") def getTargetResists(lookfor, eager=None): if isinstance(lookfor, int): @@ -333,15 +380,18 @@ def getTargetResists(lookfor, eager=None): else: eager = processEager(eager) with sd_lock: - pattern = saveddata_session.query(TargetResists).options(*eager).filter(TargetResists.ID == lookfor).first() + pattern = saveddata_session.query(TargetResists).options(*eager).filter( + TargetResists.ID == lookfor).first() elif isinstance(lookfor, basestring): eager = processEager(eager) with sd_lock: - pattern = saveddata_session.query(TargetResists).options(*eager).filter(TargetResists.name == lookfor).first() + pattern = saveddata_session.query(TargetResists).options(*eager).filter( + TargetResists.name == lookfor).first() else: raise TypeError("Need integer or string as argument") return pattern + @cachedQuery(ImplantSet, 1, "lookfor") def getImplantSet(lookfor, eager=None): if isinstance(lookfor, int): @@ -351,7 +401,8 @@ def getImplantSet(lookfor, eager=None): else: eager = processEager(eager) with sd_lock: - pattern = saveddata_session.query(ImplantSet).options(*eager).filter(TargetResists.ID == lookfor).first() + pattern = saveddata_session.query(ImplantSet).options(*eager).filter( + TargetResists.ID == lookfor).first() elif isinstance(lookfor, basestring): eager = processEager(eager) with sd_lock: @@ -360,13 +411,14 @@ def getImplantSet(lookfor, eager=None): raise TypeError("Improper argument") return pattern + def searchFits(nameLike, where=None, eager=None): if not isinstance(nameLike, basestring): raise TypeError("Need string as argument") # Prepare our string for request nameLike = u"%{0}%".format(sqlizeString(nameLike)) - #Add any extra components to the search to our where clause + # Add any extra components to the search to our where clause filter = processWhere(Fit.name.like(nameLike, escape="\\"), where) eager = processEager(eager) with sd_lock: @@ -374,6 +426,7 @@ def searchFits(nameLike, where=None, eager=None): return fits + def getProjectedFits(fitID): if isinstance(fitID, int): with sd_lock: @@ -383,12 +436,14 @@ def getProjectedFits(fitID): else: raise TypeError("Need integer as argument") + def getCrestCharacters(eager=None): eager = processEager(eager) with sd_lock: characters = saveddata_session.query(CrestChar).options(*eager).all() return characters + @cachedQuery(CrestChar, 1, "lookfor") def getCrestCharacter(lookfor, eager=None): if isinstance(lookfor, int): @@ -407,21 +462,25 @@ def getCrestCharacter(lookfor, eager=None): raise TypeError("Need integer or string as argument") return character + def getOverrides(itemID, eager=None): if isinstance(itemID, int): return saveddata_session.query(Override).filter(Override.itemID == itemID).all() else: raise TypeError("Need integer as argument") + def clearOverrides(): with sd_lock: deleted_rows = saveddata_session.query(Override).delete() commit() return deleted_rows + def getAllOverrides(eager=None): return saveddata_session.query(Override).all() + def removeInvalid(fits): invalids = [f for f in fits if f.isInvalid] @@ -432,14 +491,17 @@ def removeInvalid(fits): return fits + def add(stuff): with sd_lock: saveddata_session.add(stuff) + def save(stuff): add(stuff) commit() + def remove(stuff): removeCachedEntry(type(stuff), stuff.ID) with sd_lock: diff --git a/eos/db/saveddata/skill.py b/eos/db/saveddata/skill.py index 1c488a2cd..5b279e0d3 100644 --- a/eos/db/saveddata/skill.py +++ b/eos/db/saveddata/skill.py @@ -21,7 +21,7 @@ from sqlalchemy import Table, Column, Integer, ForeignKey from sqlalchemy.orm import mapper from eos.db import saveddata_meta -from eos.types import Skill +from eos.saveddata.character import Skill skills_table = Table("characterSkills", saveddata_meta, Column("characterID", ForeignKey("characters.ID"), primary_key=True, index=True), diff --git a/eos/db/saveddata/targetResists.py b/eos/db/saveddata/targetResists.py index 88c1ca104..e8f125c5c 100644 --- a/eos/db/saveddata/targetResists.py +++ b/eos/db/saveddata/targetResists.py @@ -21,7 +21,7 @@ from sqlalchemy import Table, Column, Integer, Float, ForeignKey, String from sqlalchemy.orm import mapper from eos.db import saveddata_meta -from eos.types import TargetResists +from eos.saveddata.targetResists import TargetResists targetResists_table = Table("targetResists", saveddata_meta, Column("ID", Integer, primary_key=True), diff --git a/eos/db/saveddata/user.py b/eos/db/saveddata/user.py index f7ec10629..b4d20fa7f 100644 --- a/eos/db/saveddata/user.py +++ b/eos/db/saveddata/user.py @@ -21,7 +21,7 @@ from sqlalchemy import Table, Column, Integer, String, Boolean from sqlalchemy.orm import mapper from eos.db import saveddata_meta -from eos.types import User +from eos.saveddata.user import User users_table = Table("users", saveddata_meta, Column("ID", Integer, primary_key=True), diff --git a/eos/effectHandlerHelpers.py b/eos/effectHandlerHelpers.py index 2e42ef69f..0b0b5e795 100644 --- a/eos/effectHandlerHelpers.py +++ b/eos/effectHandlerHelpers.py @@ -17,13 +17,9 @@ # along with eos. If not, see . # =============================================================================== -# from sqlalchemy.orm.attributes import flag_modified -import logging +from logbook import Logger -import eos.db -import eos.types - -logger = logging.getLogger(__name__) +pyfalog = Logger(__name__) class HandledList(list): @@ -140,10 +136,6 @@ class HandledModuleList(HandledList): self.remove(mod) return - # fix for #529, where a module may be in incorrect state after CCP changes mechanics of module - if not mod.isValidState(mod.state): - mod.state = eos.types.State.ONLINE - def insert(self, index, mod): mod.position = index i = index @@ -163,7 +155,7 @@ class HandledModuleList(HandledList): def toDummy(self, index): mod = self[index] if not mod.isEmpty: - dummy = eos.types.Module.buildEmpty(mod.slot) + dummy = mod.buildEmpty(mod.slot) dummy.position = index self[index] = dummy @@ -172,10 +164,11 @@ class HandledModuleList(HandledList): self[index] = mod def freeSlot(self, slot): - for i in range(len(self) - 1, -1, -1): + for i in range(len(self)): mod = self[i] if mod.getModifiedItemAttr("subSystemSlot") == slot: - del self[i] + self.toDummy(i) + break class HandledDroneCargoList(HandledList): @@ -205,7 +198,7 @@ class HandledImplantBoosterList(HandledList): # if needed, remove booster that was occupying slot oldObj = next((m for m in self if m.slot == thing.slot), None) if oldObj: - logging.info("Slot %d occupied with %s, replacing with %s", thing.slot, oldObj.item.name, thing.item.name) + pyfalog.info("Slot {0} occupied with {1}, replacing with {2}", thing.slot, oldObj.item.name, thing.item.name) oldObj.itemID = 0 # hack to remove from DB. See GH issue #324 self.remove(oldObj) @@ -229,7 +222,7 @@ class HandledProjectedModList(HandledList): oldEffect = next((m for m in self if m.item.group.name == "Effect Beacon"), None) if oldEffect: - logging.info("System effect occupied with %s, replacing with %s", oldEffect.item.name, proj.item.name) + pyfalog.info("System effect occupied with {0}, replacing with {1}", oldEffect.item.name, proj.item.name) self.remove(oldEffect) HandledList.append(self, proj) diff --git a/eos/effects/adaptivearmorhardener.py b/eos/effects/adaptivearmorhardener.py index bf20478c2..8d017715d 100644 --- a/eos/effects/adaptivearmorhardener.py +++ b/eos/effects/adaptivearmorhardener.py @@ -2,19 +2,19 @@ # # Used by: # Module: Reactive Armor Hardener -import logging +from logbook import Logger -logger = logging.getLogger(__name__) +pyfalog = Logger(__name__) runTime = "late" type = "active" + + def handler(fit, module, context): damagePattern = fit.damagePattern # Skip if there is no damage pattern. Example: projected ships or fleet boosters if damagePattern: - #logger.debug("Damage Pattern: %f/%f/%f/%f", damagePattern.emAmount, damagePattern.thermalAmount, damagePattern.kineticAmount, damagePattern.explosiveAmount) - #logger.debug("Original Armor Resists: %f/%f/%f/%f", fit.ship.getModifiedItemAttr('armorEmDamageResonance'), fit.ship.getModifiedItemAttr('armorThermalDamageResonance'), fit.ship.getModifiedItemAttr('armorKineticDamageResonance'), fit.ship.getModifiedItemAttr('armorExplosiveDamageResonance')) # Populate a tuple with the damage profile modified by current armor resists. baseDamageTaken = ( @@ -23,35 +23,35 @@ def handler(fit, module, context): damagePattern.kineticAmount * fit.ship.getModifiedItemAttr('armorKineticDamageResonance'), damagePattern.explosiveAmount * fit.ship.getModifiedItemAttr('armorExplosiveDamageResonance'), ) - #logger.debug("Damage Adjusted for Armor Resists: %f/%f/%f/%f", baseDamageTaken[0], baseDamageTaken[1], baseDamageTaken[2], baseDamageTaken[3]) + # pyfalog.debug("Damage Adjusted for Armor Resists: %f/%f/%f/%f", baseDamageTaken[0], baseDamageTaken[1], baseDamageTaken[2], baseDamageTaken[3]) - resistanceShiftAmount = module.getModifiedItemAttr('resistanceShiftAmount') / 100 # The attribute is in percent and we want a fraction + resistanceShiftAmount = module.getModifiedItemAttr( + 'resistanceShiftAmount') / 100 # The attribute is in percent and we want a fraction RAHResistance = [ - module.getModifiedItemAttr('armorEmDamageResonance'), - module.getModifiedItemAttr('armorThermalDamageResonance'), - module.getModifiedItemAttr('armorKineticDamageResonance'), + module.getModifiedItemAttr('armorEmDamageResonance'), + module.getModifiedItemAttr('armorThermalDamageResonance'), + module.getModifiedItemAttr('armorKineticDamageResonance'), module.getModifiedItemAttr('armorExplosiveDamageResonance'), ] - + # Simulate RAH cycles until the RAH either stops changing or enters a loop. # The number of iterations is limited to prevent an infinite loop if something goes wrong. cycleList = [] loopStart = -20 for num in range(50): - #logger.debug("Starting cycle %d.", num) + # pyfalog.debug("Starting cycle %d.", num) # The strange order is to emulate the ingame sorting when different types have taken the same amount of damage. # This doesn't take into account stacking penalties. In a few cases fitting a Damage Control causes an inaccurate result. damagePattern_tuples = [ (0, baseDamageTaken[0] * RAHResistance[0], RAHResistance[0]), - (3, baseDamageTaken[3] * RAHResistance[3], RAHResistance[3]), + (3, baseDamageTaken[3] * RAHResistance[3], RAHResistance[3]), (2, baseDamageTaken[2] * RAHResistance[2], RAHResistance[2]), (1, baseDamageTaken[1] * RAHResistance[1], RAHResistance[1]), ] - #logger.debug("Damage taken this cycle: %f/%f/%f/%f", damagePattern_tuples[0][1], damagePattern_tuples[3][1], damagePattern_tuples[2][1], damagePattern_tuples[1][1]) - + # Sort the tuple to drop the highest damage value to the bottom sortedDamagePattern_tuples = sorted(damagePattern_tuples, key=lambda damagePattern: damagePattern[1]) - + if sortedDamagePattern_tuples[2][1] == 0: # One damage type: the top damage type takes from the other three # Since the resistances not taking damage will end up going to the type taking damage we just do the whole thing at once. @@ -72,41 +72,47 @@ def handler(fit, module, context): change1 = min(resistanceShiftAmount, 1 - sortedDamagePattern_tuples[1][2]) change2 = -(change0 + change1) / 2 change3 = -(change0 + change1) / 2 - + RAHResistance[sortedDamagePattern_tuples[0][0]] = sortedDamagePattern_tuples[0][2] + change0 RAHResistance[sortedDamagePattern_tuples[1][0]] = sortedDamagePattern_tuples[1][2] + change1 RAHResistance[sortedDamagePattern_tuples[2][0]] = sortedDamagePattern_tuples[2][2] + change2 RAHResistance[sortedDamagePattern_tuples[3][0]] = sortedDamagePattern_tuples[3][2] + change3 - #logger.debug("Resistances shifted to %f/%f/%f/%f", RAHResistance[0], RAHResistance[1], RAHResistance[2], RAHResistance[3]) - + # pyfalog.debug("Resistances shifted to %f/%f/%f/%f", RAHResistance[0], RAHResistance[1], RAHResistance[2], RAHResistance[3]) + # See if the current RAH profile has been encountered before, indicating a loop. for i, val in enumerate(cycleList): - tolerance = 1e-06 - if abs(RAHResistance[0] - val[0]) <= tolerance and abs(RAHResistance[1] - val[1]) <= tolerance and abs(RAHResistance[2] - val[2]) <= tolerance and abs(RAHResistance[3] - val[3]) <= tolerance: + tolerance = 1e-06 + if abs(RAHResistance[0] - val[0]) <= tolerance and \ + abs(RAHResistance[1] - val[1]) <= tolerance and \ + abs(RAHResistance[2] - val[2]) <= tolerance and \ + abs(RAHResistance[3] - val[3]) <= tolerance: loopStart = i - #logger.debug("Loop found: %d-%d", loopStart, num) + # pyfalog.debug("Loop found: %d-%d", loopStart, num) break - if loopStart >= 0: break - + if loopStart >= 0: + break + cycleList.append(list(RAHResistance)) - + if loopStart < 0: - logger.error("Reactive Armor Hardener failed to find equilibrium. Damage profile after armor: %f/%f/%f/%f", baseDamageTaken[0], baseDamageTaken[1], baseDamageTaken[2], baseDamageTaken[3]) - - # Average the profiles in the RAH loop, or the last 20 if it didn't find a loop. + pyfalog.error("Reactive Armor Hardener failed to find equilibrium. Damage profile after armor: {0}/{1}/{2}/{3}", + baseDamageTaken[0], baseDamageTaken[1], baseDamageTaken[2], baseDamageTaken[3]) + + # Average the profiles in the RAH loop, or the last 20 if it didn't find a loop. loopCycles = cycleList[loopStart:] numCycles = len(loopCycles) average = [0, 0, 0, 0] for cycle in loopCycles: for i in range(4): average[i] += cycle[i] - + for i in range(4): average[i] = round(average[i] / numCycles, 3) - + # Set the new resistances - #logger.debug("Setting new resist profile: %f/%f/%f/%f", average[0], average[1], average[2],average[3]) - for i, attr in enumerate(('armorEmDamageResonance', 'armorThermalDamageResonance', 'armorKineticDamageResonance', 'armorExplosiveDamageResonance')): + # pyfalog.debug("Setting new resist profile: %f/%f/%f/%f", average[0], average[1], average[2],average[3]) + for i, attr in enumerate(( + 'armorEmDamageResonance', 'armorThermalDamageResonance', 'armorKineticDamageResonance', + 'armorExplosiveDamageResonance')): module.increaseItemAttr(attr, average[i] - module.getModifiedItemAttr(attr)) fit.ship.multiplyItemAttr(attr, average[i], stackingPenalties=True, penaltyGroup="preMul") - \ No newline at end of file diff --git a/eos/effects/ammoinfluencecapneed.py b/eos/effects/ammoinfluencecapneed.py index 3b696d552..c30a91bd1 100644 --- a/eos/effects/ammoinfluencecapneed.py +++ b/eos/effects/ammoinfluencecapneed.py @@ -1,7 +1,7 @@ # ammoInfluenceCapNeed # # Used by: -# Items from category: Charge (465 of 899) +# Items from category: Charge (465 of 912) # Charges from group: Frequency Crystal (185 of 185) # Charges from group: Hybrid Charge (209 of 209) type = "passive" diff --git a/eos/effects/ammoinfluencerange.py b/eos/effects/ammoinfluencerange.py index 5569a3dfb..ea6860bbe 100644 --- a/eos/effects/ammoinfluencerange.py +++ b/eos/effects/ammoinfluencerange.py @@ -1,7 +1,7 @@ # ammoInfluenceRange # # Used by: -# Items from category: Charge (571 of 899) +# Items from category: Charge (571 of 912) type = "passive" diff --git a/eos/effects/ammospeedmultiplier.py b/eos/effects/ammospeedmultiplier.py index 14f4298f5..afef8b267 100644 --- a/eos/effects/ammospeedmultiplier.py +++ b/eos/effects/ammospeedmultiplier.py @@ -1,7 +1,7 @@ # ammoSpeedMultiplier # # Used by: -# Charges from group: Festival Charges (9 of 9) +# Charges from group: Festival Charges (22 of 22) # Charges from group: Interdiction Probe (2 of 2) # Charges from group: Survey Probe (3 of 3) type = "passive" diff --git a/eos/effects/angelsetbonus.py b/eos/effects/angelsetbonus.py index 9057b5876..1591e9be1 100644 --- a/eos/effects/angelsetbonus.py +++ b/eos/effects/angelsetbonus.py @@ -8,7 +8,7 @@ type = "passive" def handler(fit, implant, context): fit.appliedImplants.filteredItemMultiply( - lambda - implant: "signatureRadiusBonus" in implant.itemModifiedAttributes and "implantSetAngel" in implant.itemModifiedAttributes, + lambda implant: "signatureRadiusBonus" in implant.itemModifiedAttributes and + "implantSetAngel" in implant.itemModifiedAttributes, "signatureRadiusBonus", implant.getModifiedItemAttr("implantSetAngel")) diff --git a/eos/effects/armoredcommanddurationbonus.py b/eos/effects/armoredcommanddurationbonus.py index 031c6d0ef..171dfbbb2 100644 --- a/eos/effects/armoredcommanddurationbonus.py +++ b/eos/effects/armoredcommanddurationbonus.py @@ -3,6 +3,9 @@ # Used by: # Skill: Armored Command type = "passive" + + def handler(fit, src, context): lvl = src.level - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Armored Command"), "buffDuration", src.getModifiedItemAttr("durationBonus") * lvl) + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Armored Command"), "buffDuration", + src.getModifiedItemAttr("durationBonus") * lvl) diff --git a/eos/effects/armoredcommandmindlink.py b/eos/effects/armoredcommandmindlink.py index 832bc2678..2e36ed6f0 100644 --- a/eos/effects/armoredcommandmindlink.py +++ b/eos/effects/armoredcommandmindlink.py @@ -5,9 +5,16 @@ # Implant: Federation Navy Command Mindlink # Implant: Imperial Navy Command Mindlink type = "passive" + + def handler(fit, src, context): - fit.modules.filteredChargeBoost(lambda mod: mod.item.requiresSkill("Armored Command"), "warfareBuff2Multiplier", src.getModifiedItemAttr("mindlinkBonus")) - fit.modules.filteredChargeBoost(lambda mod: mod.item.requiresSkill("Armored Command"), "warfareBuff1Multiplier", src.getModifiedItemAttr("mindlinkBonus")) - fit.modules.filteredChargeBoost(lambda mod: mod.item.requiresSkill("Armored Command"), "warfareBuff4Multiplier", src.getModifiedItemAttr("mindlinkBonus")) - fit.modules.filteredChargeBoost(lambda mod: mod.item.requiresSkill("Armored Command"), "warfareBuff3Multiplier", src.getModifiedItemAttr("mindlinkBonus")) - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Armored Command"), "buffDuration", src.getModifiedItemAttr("mindlinkBonus")) + fit.modules.filteredChargeBoost(lambda mod: mod.item.requiresSkill("Armored Command"), "warfareBuff2Multiplier", + src.getModifiedItemAttr("mindlinkBonus")) + fit.modules.filteredChargeBoost(lambda mod: mod.item.requiresSkill("Armored Command"), "warfareBuff1Multiplier", + src.getModifiedItemAttr("mindlinkBonus")) + fit.modules.filteredChargeBoost(lambda mod: mod.item.requiresSkill("Armored Command"), "warfareBuff4Multiplier", + src.getModifiedItemAttr("mindlinkBonus")) + fit.modules.filteredChargeBoost(lambda mod: mod.item.requiresSkill("Armored Command"), "warfareBuff3Multiplier", + src.getModifiedItemAttr("mindlinkBonus")) + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Armored Command"), "buffDuration", + src.getModifiedItemAttr("mindlinkBonus")) diff --git a/eos/effects/armoredcommandstrengthbonus.py b/eos/effects/armoredcommandstrengthbonus.py index 363c4f1a8..6bc5e365e 100644 --- a/eos/effects/armoredcommandstrengthbonus.py +++ b/eos/effects/armoredcommandstrengthbonus.py @@ -3,9 +3,15 @@ # Used by: # Skill: Armored Command Specialist type = "passive" + + def handler(fit, src, context): lvl = src.level - fit.modules.filteredChargeBoost(lambda mod: mod.item.requiresSkill("Armored Command"), "warfareBuff1Multiplier", src.getModifiedItemAttr("commandStrengthBonus") * lvl) - fit.modules.filteredChargeBoost(lambda mod: mod.item.requiresSkill("Armored Command"), "warfareBuff2Multiplier", src.getModifiedItemAttr("commandStrengthBonus") * lvl) - fit.modules.filteredChargeBoost(lambda mod: mod.item.requiresSkill("Armored Command"), "warfareBuff4Multiplier", src.getModifiedItemAttr("commandStrengthBonus") * lvl) - fit.modules.filteredChargeBoost(lambda mod: mod.item.requiresSkill("Armored Command"), "warfareBuff3Multiplier", src.getModifiedItemAttr("commandStrengthBonus") * lvl) + fit.modules.filteredChargeBoost(lambda mod: mod.item.requiresSkill("Armored Command"), "warfareBuff1Multiplier", + src.getModifiedItemAttr("commandStrengthBonus") * lvl) + fit.modules.filteredChargeBoost(lambda mod: mod.item.requiresSkill("Armored Command"), "warfareBuff2Multiplier", + src.getModifiedItemAttr("commandStrengthBonus") * lvl) + fit.modules.filteredChargeBoost(lambda mod: mod.item.requiresSkill("Armored Command"), "warfareBuff4Multiplier", + src.getModifiedItemAttr("commandStrengthBonus") * lvl) + fit.modules.filteredChargeBoost(lambda mod: mod.item.requiresSkill("Armored Command"), "warfareBuff3Multiplier", + src.getModifiedItemAttr("commandStrengthBonus") * lvl) diff --git a/eos/effects/armorhpmultiply.py b/eos/effects/armorhpmultiply.py index d9326eb73..25039f35f 100644 --- a/eos/effects/armorhpmultiply.py +++ b/eos/effects/armorhpmultiply.py @@ -3,7 +3,6 @@ # Used by: # Modules from group: Armor Coating (202 of 202) # Modules from group: Armor Plating Energized (187 of 187) -# Modules named like: QA Multiship Module Players (4 of 4) type = "passive" diff --git a/eos/effects/armorrepairamountbonussubcap.py b/eos/effects/armorrepairamountbonussubcap.py index e50b74ab9..725c43f33 100644 --- a/eos/effects/armorrepairamountbonussubcap.py +++ b/eos/effects/armorrepairamountbonussubcap.py @@ -1,7 +1,7 @@ # armorRepairAmountBonusSubcap # # Used by: -# Implants named like: Grade Asklepian (15 of 16) +# Implants named like: grade Asklepian (15 of 18) type = "passive" diff --git a/eos/effects/caldarishipewstrengthcb.py b/eos/effects/caldarishipewstrengthcb.py index b362382d1..48bdccb60 100644 --- a/eos/effects/caldarishipewstrengthcb.py +++ b/eos/effects/caldarishipewstrengthcb.py @@ -7,5 +7,7 @@ type = "passive" def handler(fit, ship, context): for sensorType in ("Gravimetric", "Ladar", "Magnetometric", "Radar"): - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Electronic Warfare"), "scan{0}StrengthBonus".format(sensorType), - ship.getModifiedItemAttr("shipBonusCB"), stackingPenalties=True, skill="Caldari Battleship") + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Electronic Warfare"), + "scan{0}StrengthBonus".format(sensorType), + ship.getModifiedItemAttr("shipBonusCB"), stackingPenalties=True, + skill="Caldari Battleship") diff --git a/eos/effects/chargebonuswarfarecharge.py b/eos/effects/chargebonuswarfarecharge.py index 6b3c5f3f2..600379c44 100644 --- a/eos/effects/chargebonuswarfarecharge.py +++ b/eos/effects/chargebonuswarfarecharge.py @@ -4,7 +4,8 @@ # Items from market group: Ammunition & Charges > Command Burst Charges (15 of 15) type = "active" + def handler(fit, module, context): for x in xrange(1, 4): value = module.getModifiedChargeAttr("warfareBuff{}Multiplier".format(x)) - module.multiplyItemAttr("warfareBuff{}Value".format(x), value) \ No newline at end of file + module.multiplyItemAttr("warfareBuff{}Value".format(x), value) diff --git a/eos/effects/commandburstaoebonus.py b/eos/effects/commandburstaoebonus.py index 8b7bbfce3..7965a6877 100644 --- a/eos/effects/commandburstaoebonus.py +++ b/eos/effects/commandburstaoebonus.py @@ -5,6 +5,8 @@ # Skill: Leadership # Skill: Wing Command type = "passive" + + def handler(fit, src, context): fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Leadership"), "maxRange", diff --git a/eos/effects/commandburstaoerolebonus.py b/eos/effects/commandburstaoerolebonus.py index eddf8713e..63de8ca87 100644 --- a/eos/effects/commandburstaoerolebonus.py +++ b/eos/effects/commandburstaoerolebonus.py @@ -11,5 +11,8 @@ # Ship: Orca # Ship: Rorqual type = "passive" + + def handler(fit, src, context): - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Leadership"), "maxRange", src.getModifiedItemAttr("roleBonusCommandBurstAoERange")) + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Leadership"), "maxRange", + src.getModifiedItemAttr("roleBonusCommandBurstAoERange")) diff --git a/eos/effects/commandburstreloadtimebonus.py b/eos/effects/commandburstreloadtimebonus.py index 1af92ee57..c0207bef4 100644 --- a/eos/effects/commandburstreloadtimebonus.py +++ b/eos/effects/commandburstreloadtimebonus.py @@ -3,6 +3,8 @@ # Used by: # Skill: Command Burst Specialist type = "passive" + + def handler(fit, src, context): lvl = src.level fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Leadership"), diff --git a/eos/effects/commandprocessoreffect.py b/eos/effects/commandprocessoreffect.py index 03030ed85..887644ece 100644 --- a/eos/effects/commandprocessoreffect.py +++ b/eos/effects/commandprocessoreffect.py @@ -3,6 +3,10 @@ # Used by: # Modules named like: Command Processor I (4 of 4) type = "passive" + + def handler(fit, src, context): - fit.modules.filteredItemIncrease(lambda mod: mod.item.requiresSkill("Leadership"), "maxGroupActive", src.getModifiedItemAttr("maxGangModules")) - fit.modules.filteredItemIncrease(lambda mod: mod.item.requiresSkill("Leadership"), "maxGroupOnline", src.getModifiedItemAttr("maxGangModules")) + fit.modules.filteredItemIncrease(lambda mod: mod.item.requiresSkill("Leadership"), "maxGroupActive", + src.getModifiedItemAttr("maxGangModules")) + fit.modules.filteredItemIncrease(lambda mod: mod.item.requiresSkill("Leadership"), "maxGroupOnline", + src.getModifiedItemAttr("maxGangModules")) diff --git a/eos/effects/commandshipmultirelayeffect.py b/eos/effects/commandshipmultirelayeffect.py index de57a5160..9a3b30089 100644 --- a/eos/effects/commandshipmultirelayeffect.py +++ b/eos/effects/commandshipmultirelayeffect.py @@ -6,6 +6,9 @@ # Ship: Rorqual type = "passive" + def handler(fit, src, context): - fit.modules.filteredItemIncrease(lambda mod: mod.item.requiresSkill("Leadership"), "maxGroupActive", src.getModifiedItemAttr("maxGangModules")) - fit.modules.filteredItemIncrease(lambda mod: mod.item.requiresSkill("Leadership"), "maxGroupOnline", src.getModifiedItemAttr("maxGangModules")) \ No newline at end of file + fit.modules.filteredItemIncrease(lambda mod: mod.item.requiresSkill("Leadership"), "maxGroupActive", + src.getModifiedItemAttr("maxGangModules")) + fit.modules.filteredItemIncrease(lambda mod: mod.item.requiresSkill("Leadership"), "maxGroupOnline", + src.getModifiedItemAttr("maxGangModules")) diff --git a/eos/effects/dohacking.py b/eos/effects/dohacking.py index b41b91069..13412daba 100644 --- a/eos/effects/dohacking.py +++ b/eos/effects/dohacking.py @@ -2,7 +2,6 @@ # # Used by: # Modules from group: Data Miners (15 of 16) -# Module: QA Cross Protocol Analyzer type = "active" diff --git a/eos/effects/electronicattributemodifyonline.py b/eos/effects/electronicattributemodifyonline.py index 28197f971..9e1f52524 100644 --- a/eos/effects/electronicattributemodifyonline.py +++ b/eos/effects/electronicattributemodifyonline.py @@ -2,7 +2,6 @@ # # Used by: # Modules from group: Automated Targeting System (6 of 6) -# Module: QA Damage Module type = "passive" diff --git a/eos/effects/elitebonuscommanddestroyerarmored1.py b/eos/effects/elitebonuscommanddestroyerarmored1.py index 4b5442b0d..83df03351 100644 --- a/eos/effects/elitebonuscommanddestroyerarmored1.py +++ b/eos/effects/elitebonuscommanddestroyerarmored1.py @@ -4,9 +4,16 @@ # Ship: Magus # Ship: Pontifex type = "passive" + + def handler(fit, src, context): - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Armored Command"), "warfareBuff2Value", src.getModifiedItemAttr("eliteBonusCommandDestroyer1"), skill="Command Destroyers") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Armored Command"), "warfareBuff3Value", src.getModifiedItemAttr("eliteBonusCommandDestroyer1"), skill="Command Destroyers") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Armored Command"), "buffDuration", src.getModifiedItemAttr("eliteBonusCommandDestroyer1"), skill="Command Destroyers") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Armored Command"), "warfareBuff4Value", src.getModifiedItemAttr("eliteBonusCommandDestroyer1"), skill="Command Destroyers") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Armored Command"), "warfareBuff1Value", src.getModifiedItemAttr("eliteBonusCommandDestroyer1"), skill="Command Destroyers") + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Armored Command"), "warfareBuff2Value", + src.getModifiedItemAttr("eliteBonusCommandDestroyer1"), skill="Command Destroyers") + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Armored Command"), "warfareBuff3Value", + src.getModifiedItemAttr("eliteBonusCommandDestroyer1"), skill="Command Destroyers") + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Armored Command"), "buffDuration", + src.getModifiedItemAttr("eliteBonusCommandDestroyer1"), skill="Command Destroyers") + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Armored Command"), "warfareBuff4Value", + src.getModifiedItemAttr("eliteBonusCommandDestroyer1"), skill="Command Destroyers") + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Armored Command"), "warfareBuff1Value", + src.getModifiedItemAttr("eliteBonusCommandDestroyer1"), skill="Command Destroyers") diff --git a/eos/effects/elitebonuscommanddestroyerinfo1.py b/eos/effects/elitebonuscommanddestroyerinfo1.py index 9198bc1c7..2f17453b2 100644 --- a/eos/effects/elitebonuscommanddestroyerinfo1.py +++ b/eos/effects/elitebonuscommanddestroyerinfo1.py @@ -4,9 +4,16 @@ # Ship: Pontifex # Ship: Stork type = "passive" + + def handler(fit, src, context): - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Information Command"), "warfareBuff1Value", src.getModifiedItemAttr("eliteBonusCommandDestroyer1"), skill="Command Destroyers") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Information Command"), "warfareBuff3Value", src.getModifiedItemAttr("eliteBonusCommandDestroyer1"), skill="Command Destroyers") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Information Command"), "warfareBuff2Value", src.getModifiedItemAttr("eliteBonusCommandDestroyer1"), skill="Command Destroyers") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Information Command"), "buffDuration", src.getModifiedItemAttr("eliteBonusCommandDestroyer1"), skill="Command Destroyers") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Information Command"), "warfareBuff4Value", src.getModifiedItemAttr("eliteBonusCommandDestroyer1"), skill="Command Destroyers") + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Information Command"), "warfareBuff1Value", + src.getModifiedItemAttr("eliteBonusCommandDestroyer1"), skill="Command Destroyers") + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Information Command"), "warfareBuff3Value", + src.getModifiedItemAttr("eliteBonusCommandDestroyer1"), skill="Command Destroyers") + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Information Command"), "warfareBuff2Value", + src.getModifiedItemAttr("eliteBonusCommandDestroyer1"), skill="Command Destroyers") + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Information Command"), "buffDuration", + src.getModifiedItemAttr("eliteBonusCommandDestroyer1"), skill="Command Destroyers") + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Information Command"), "warfareBuff4Value", + src.getModifiedItemAttr("eliteBonusCommandDestroyer1"), skill="Command Destroyers") diff --git a/eos/effects/elitebonuscommanddestroyerinfohidden1.py b/eos/effects/elitebonuscommanddestroyerinfohidden1.py index 4ffc352e0..cf31b2b1b 100644 --- a/eos/effects/elitebonuscommanddestroyerinfohidden1.py +++ b/eos/effects/elitebonuscommanddestroyerinfohidden1.py @@ -3,4 +3,7 @@ type = "passive" def handler(fit, src, context): - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Information Command Specialist"), "commandBonusHidden", src.getModifiedItemAttr("eliteBonusCommandDestroyer1"), skill="Command Destroyers") + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Information Command Specialist"), + "commandBonusHidden", + src.getModifiedItemAttr("eliteBonusCommandDestroyer1"), + skill="Command Destroyers") diff --git a/eos/effects/elitebonuscommanddestroyershield1.py b/eos/effects/elitebonuscommanddestroyershield1.py index ef5d75c29..03c2e2b8e 100644 --- a/eos/effects/elitebonuscommanddestroyershield1.py +++ b/eos/effects/elitebonuscommanddestroyershield1.py @@ -4,9 +4,16 @@ # Ship: Bifrost # Ship: Stork type = "passive" + + def handler(fit, src, context): - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Shield Command"), "warfareBuff3Value", src.getModifiedItemAttr("eliteBonusCommandDestroyer1"), skill="Command Destroyers") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Shield Command"), "buffDuration", src.getModifiedItemAttr("eliteBonusCommandDestroyer1"), skill="Command Destroyers") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Shield Command"), "warfareBuff1Value", src.getModifiedItemAttr("eliteBonusCommandDestroyer1"), skill="Command Destroyers") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Shield Command"), "warfareBuff4Value", src.getModifiedItemAttr("eliteBonusCommandDestroyer1"), skill="Command Destroyers") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Shield Command"), "warfareBuff2Value", src.getModifiedItemAttr("eliteBonusCommandDestroyer1"), skill="Command Destroyers") + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Shield Command"), "warfareBuff3Value", + src.getModifiedItemAttr("eliteBonusCommandDestroyer1"), skill="Command Destroyers") + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Shield Command"), "buffDuration", + src.getModifiedItemAttr("eliteBonusCommandDestroyer1"), skill="Command Destroyers") + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Shield Command"), "warfareBuff1Value", + src.getModifiedItemAttr("eliteBonusCommandDestroyer1"), skill="Command Destroyers") + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Shield Command"), "warfareBuff4Value", + src.getModifiedItemAttr("eliteBonusCommandDestroyer1"), skill="Command Destroyers") + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Shield Command"), "warfareBuff2Value", + src.getModifiedItemAttr("eliteBonusCommandDestroyer1"), skill="Command Destroyers") diff --git a/eos/effects/elitebonuscommanddestroyerskirmish1.py b/eos/effects/elitebonuscommanddestroyerskirmish1.py index aa600f865..b33bdfa02 100644 --- a/eos/effects/elitebonuscommanddestroyerskirmish1.py +++ b/eos/effects/elitebonuscommanddestroyerskirmish1.py @@ -4,9 +4,16 @@ # Ship: Bifrost # Ship: Magus type = "passive" + + def handler(fit, src, context): - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Skirmish Command"), "warfareBuff3Value", src.getModifiedItemAttr("eliteBonusCommandDestroyer1"), skill="Command Destroyers") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Skirmish Command"), "buffDuration", src.getModifiedItemAttr("eliteBonusCommandDestroyer1"), skill="Command Destroyers") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Skirmish Command"), "warfareBuff1Value", src.getModifiedItemAttr("eliteBonusCommandDestroyer1"), skill="Command Destroyers") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Skirmish Command"), "warfareBuff4Value", src.getModifiedItemAttr("eliteBonusCommandDestroyer1"), skill="Command Destroyers") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Skirmish Command"), "warfareBuff2Value", src.getModifiedItemAttr("eliteBonusCommandDestroyer1"), skill="Command Destroyers") + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Skirmish Command"), "warfareBuff3Value", + src.getModifiedItemAttr("eliteBonusCommandDestroyer1"), skill="Command Destroyers") + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Skirmish Command"), "buffDuration", + src.getModifiedItemAttr("eliteBonusCommandDestroyer1"), skill="Command Destroyers") + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Skirmish Command"), "warfareBuff1Value", + src.getModifiedItemAttr("eliteBonusCommandDestroyer1"), skill="Command Destroyers") + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Skirmish Command"), "warfareBuff4Value", + src.getModifiedItemAttr("eliteBonusCommandDestroyer1"), skill="Command Destroyers") + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Skirmish Command"), "warfareBuff2Value", + src.getModifiedItemAttr("eliteBonusCommandDestroyer1"), skill="Command Destroyers") diff --git a/eos/effects/elitebonuscommandshiparmoredcs3.py b/eos/effects/elitebonuscommandshiparmoredcs3.py index 4832b2904..d7bc3fc46 100644 --- a/eos/effects/elitebonuscommandshiparmoredcs3.py +++ b/eos/effects/elitebonuscommandshiparmoredcs3.py @@ -3,9 +3,16 @@ # Used by: # Ships from group: Command Ship (4 of 8) type = "passive" + + def handler(fit, src, context): - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Armored Command"), "warfareBuff3Value", src.getModifiedItemAttr("eliteBonusCommandShips3"), skill="Command Ships") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Armored Command"), "warfareBuff1Value", src.getModifiedItemAttr("eliteBonusCommandShips3"), skill="Command Ships") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Armored Command"), "warfareBuff2Value", src.getModifiedItemAttr("eliteBonusCommandShips3"), skill="Command Ships") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Armored Command"), "buffDuration", src.getModifiedItemAttr("eliteBonusCommandShips3"), skill="Command Ships") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Armored Command"), "warfareBuff4Value", src.getModifiedItemAttr("eliteBonusCommandShips3"), skill="Command Ships") + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Armored Command"), "warfareBuff3Value", + src.getModifiedItemAttr("eliteBonusCommandShips3"), skill="Command Ships") + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Armored Command"), "warfareBuff1Value", + src.getModifiedItemAttr("eliteBonusCommandShips3"), skill="Command Ships") + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Armored Command"), "warfareBuff2Value", + src.getModifiedItemAttr("eliteBonusCommandShips3"), skill="Command Ships") + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Armored Command"), "buffDuration", + src.getModifiedItemAttr("eliteBonusCommandShips3"), skill="Command Ships") + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Armored Command"), "warfareBuff4Value", + src.getModifiedItemAttr("eliteBonusCommandShips3"), skill="Command Ships") diff --git a/eos/effects/elitebonuscommandshipinformationcs3.py b/eos/effects/elitebonuscommandshipinformationcs3.py index ffa02ba05..283cc76d6 100644 --- a/eos/effects/elitebonuscommandshipinformationcs3.py +++ b/eos/effects/elitebonuscommandshipinformationcs3.py @@ -3,9 +3,16 @@ # Used by: # Ships from group: Command Ship (4 of 8) type = "passive" + + def handler(fit, src, context): - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Information Command"), "buffDuration", src.getModifiedItemAttr("eliteBonusCommandShips3"), skill="Command Ships") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Information Command"), "warfareBuff3Value", src.getModifiedItemAttr("eliteBonusCommandShips3"), skill="Command Ships") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Information Command"), "warfareBuff2Value", src.getModifiedItemAttr("eliteBonusCommandShips3"), skill="Command Ships") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Information Command"), "warfareBuff1Value", src.getModifiedItemAttr("eliteBonusCommandShips3"), skill="Command Ships") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Information Command"), "warfareBuff4Value", src.getModifiedItemAttr("eliteBonusCommandShips3"), skill="Command Ships") + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Information Command"), "buffDuration", + src.getModifiedItemAttr("eliteBonusCommandShips3"), skill="Command Ships") + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Information Command"), "warfareBuff3Value", + src.getModifiedItemAttr("eliteBonusCommandShips3"), skill="Command Ships") + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Information Command"), "warfareBuff2Value", + src.getModifiedItemAttr("eliteBonusCommandShips3"), skill="Command Ships") + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Information Command"), "warfareBuff1Value", + src.getModifiedItemAttr("eliteBonusCommandShips3"), skill="Command Ships") + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Information Command"), "warfareBuff4Value", + src.getModifiedItemAttr("eliteBonusCommandShips3"), skill="Command Ships") diff --git a/eos/effects/elitebonuscommandshipinformationhiddencs3.py b/eos/effects/elitebonuscommandshipinformationhiddencs3.py index 27148ebec..eeee692ed 100644 --- a/eos/effects/elitebonuscommandshipinformationhiddencs3.py +++ b/eos/effects/elitebonuscommandshipinformationhiddencs3.py @@ -1,5 +1,7 @@ # Not used by any item type = "passive" + + def handler(fit, module, context): fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Information Command Specialist"), "commandBonusHidden", module.getModifiedItemAttr("eliteBonusCommandShips3"), skill="Command Ships") diff --git a/eos/effects/elitebonuscommandshipsiegecs3.py b/eos/effects/elitebonuscommandshipsiegecs3.py index 4e7395d21..a17eb44dc 100644 --- a/eos/effects/elitebonuscommandshipsiegecs3.py +++ b/eos/effects/elitebonuscommandshipsiegecs3.py @@ -3,9 +3,16 @@ # Used by: # Ships from group: Command Ship (4 of 8) type = "passive" + + def handler(fit, src, context): - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Shield Command"), "warfareBuff1Value", src.getModifiedItemAttr("eliteBonusCommandShips3"), skill="Command Ships") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Shield Command"), "warfareBuff4Value", src.getModifiedItemAttr("eliteBonusCommandShips3"), skill="Command Ships") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Shield Command"), "warfareBuff2Value", src.getModifiedItemAttr("eliteBonusCommandShips3"), skill="Command Ships") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Shield Command"), "buffDuration", src.getModifiedItemAttr("eliteBonusCommandShips3"), skill="Command Ships") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Shield Command"), "warfareBuff3Value", src.getModifiedItemAttr("eliteBonusCommandShips3"), skill="Command Ships") + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Shield Command"), "warfareBuff1Value", + src.getModifiedItemAttr("eliteBonusCommandShips3"), skill="Command Ships") + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Shield Command"), "warfareBuff4Value", + src.getModifiedItemAttr("eliteBonusCommandShips3"), skill="Command Ships") + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Shield Command"), "warfareBuff2Value", + src.getModifiedItemAttr("eliteBonusCommandShips3"), skill="Command Ships") + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Shield Command"), "buffDuration", + src.getModifiedItemAttr("eliteBonusCommandShips3"), skill="Command Ships") + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Shield Command"), "warfareBuff3Value", + src.getModifiedItemAttr("eliteBonusCommandShips3"), skill="Command Ships") diff --git a/eos/effects/elitebonuscommandshipskirmishcs3.py b/eos/effects/elitebonuscommandshipskirmishcs3.py index 2c6d03add..345191164 100644 --- a/eos/effects/elitebonuscommandshipskirmishcs3.py +++ b/eos/effects/elitebonuscommandshipskirmishcs3.py @@ -3,9 +3,16 @@ # Used by: # Ships from group: Command Ship (4 of 8) type = "passive" + + def handler(fit, src, context): - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Skirmish Command"), "warfareBuff2Value", src.getModifiedItemAttr("eliteBonusCommandShips3"), skill="Command Ships") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Skirmish Command"), "warfareBuff1Value", src.getModifiedItemAttr("eliteBonusCommandShips3"), skill="Command Ships") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Skirmish Command"), "warfareBuff3Value", src.getModifiedItemAttr("eliteBonusCommandShips3"), skill="Command Ships") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Skirmish Command"), "warfareBuff4Value", src.getModifiedItemAttr("eliteBonusCommandShips3"), skill="Command Ships") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Skirmish Command"), "buffDuration", src.getModifiedItemAttr("eliteBonusCommandShips3"), skill="Command Ships") + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Skirmish Command"), "warfareBuff2Value", + src.getModifiedItemAttr("eliteBonusCommandShips3"), skill="Command Ships") + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Skirmish Command"), "warfareBuff1Value", + src.getModifiedItemAttr("eliteBonusCommandShips3"), skill="Command Ships") + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Skirmish Command"), "warfareBuff3Value", + src.getModifiedItemAttr("eliteBonusCommandShips3"), skill="Command Ships") + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Skirmish Command"), "warfareBuff4Value", + src.getModifiedItemAttr("eliteBonusCommandShips3"), skill="Command Ships") + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Skirmish Command"), "buffDuration", + src.getModifiedItemAttr("eliteBonusCommandShips3"), skill="Command Ships") diff --git a/eos/effects/elitebonuscoveropsnosneutfalloff1.py b/eos/effects/elitebonuscoveropsnosneutfalloff1.py index df0711cac..aa5b5b558 100644 --- a/eos/effects/elitebonuscoveropsnosneutfalloff1.py +++ b/eos/effects/elitebonuscoveropsnosneutfalloff1.py @@ -3,7 +3,9 @@ # Used by: # Ship: Caedes type = "passive" + + def handler(fit, src, context): fit.modules.filteredItemBoost(lambda mod: mod.item.group.name in ("Energy Nosferatu", "Energy Neutralizer"), - "falloffEffectiveness", src.getModifiedItemAttr("eliteBonusCoverOps1"), stackingPenalties=True, skill="Covert Ops") - + "falloffEffectiveness", src.getModifiedItemAttr("eliteBonusCoverOps1"), + stackingPenalties=True, skill="Covert Ops") diff --git a/eos/effects/elitebonuslogisticremotearmorrepairduration3.py b/eos/effects/elitebonuslogisticremotearmorrepairduration3.py index d26ef3525..5c9b24c1c 100644 --- a/eos/effects/elitebonuslogisticremotearmorrepairduration3.py +++ b/eos/effects/elitebonuslogisticremotearmorrepairduration3.py @@ -3,5 +3,8 @@ # Used by: # Ship: Rabisu type = "passive" + + def handler(fit, src, context): - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Remote Armor Repair Systems"), "duration", src.getModifiedItemAttr("eliteBonusLogistics3"), skill="Logistics Cruisers") + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Remote Armor Repair Systems"), "duration", + src.getModifiedItemAttr("eliteBonusLogistics3"), skill="Logistics Cruisers") diff --git a/eos/effects/elitebonuslogisticremotearmorrepairoptimalfalloff1.py b/eos/effects/elitebonuslogisticremotearmorrepairoptimalfalloff1.py index d9180ebd7..cacbd4478 100644 --- a/eos/effects/elitebonuslogisticremotearmorrepairoptimalfalloff1.py +++ b/eos/effects/elitebonuslogisticremotearmorrepairoptimalfalloff1.py @@ -3,6 +3,14 @@ # Used by: # Ship: Rabisu type = "passive" + + def handler(fit, src, context): - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Remote Armor Repair Systems"), "falloffEffectiveness", src.getModifiedItemAttr("eliteBonusLogistics1"), stackingPenalties=True, skill="Logistics Cruisers") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Remote Armor Repair Systems"), "maxRange", src.getModifiedItemAttr("eliteBonusLogistics1"), stackingPenalties=True, skill="Logistics Cruisers") + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Remote Armor Repair Systems"), + "falloffEffectiveness", + src.getModifiedItemAttr("eliteBonusLogistics1"), + skill="Logistics Cruisers") + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Remote Armor Repair Systems"), + "maxRange", + src.getModifiedItemAttr("eliteBonusLogistics1"), + skill="Logistics Cruisers") diff --git a/eos/effects/energydestabilizationnew.py b/eos/effects/energydestabilizationnew.py index 6cf82afa5..7a45fe8b7 100644 --- a/eos/effects/energydestabilizationnew.py +++ b/eos/effects/energydestabilizationnew.py @@ -1,12 +1,11 @@ # Not used by any item -from eos.types import State +from eos.saveddata.module import State type = "active", "projected" def handler(fit, src, context): - if "projected" in context and ( - (hasattr(src, "state") and src.state >= State.ACTIVE) or hasattr(src, "amountActive")): + if "projected" in context and ((hasattr(src, "state") and src.state >= State.ACTIVE) or hasattr(src, "amountActive")): multiplier = src.amountActive if hasattr(src, "amountActive") else 1 amount = src.getModifiedItemAttr("energyNeutralizerAmount") time = src.getModifiedItemAttr("duration") diff --git a/eos/effects/energyneutralizerfalloff.py b/eos/effects/energyneutralizerfalloff.py index 212f05ffa..bdaaa2d3a 100644 --- a/eos/effects/energyneutralizerfalloff.py +++ b/eos/effects/energyneutralizerfalloff.py @@ -2,14 +2,14 @@ # # Used by: # Modules from group: Energy Neutralizer (51 of 51) -from eos.types import State +from eos.saveddata.module import State type = "active", "projected" def handler(fit, src, context): - if "projected" in context and ( - (hasattr(src, "state") and src.state >= State.ACTIVE) or hasattr(src, "amountActive")): + if "projected" in context and ((hasattr(src, "state") and src.state >= State.ACTIVE) or + hasattr(src, "amountActive")): amount = src.getModifiedItemAttr("energyNeutralizerAmount") time = src.getModifiedItemAttr("duration") diff --git a/eos/effects/energyweapondamagemultiply.py b/eos/effects/energyweapondamagemultiply.py index c1abce625..0eb10ef75 100644 --- a/eos/effects/energyweapondamagemultiply.py +++ b/eos/effects/energyweapondamagemultiply.py @@ -2,8 +2,6 @@ # # Used by: # Modules from group: Heat Sink (18 of 18) -# Modules named like: QA Multiship Module Players (4 of 4) -# Module: QA Damage Module type = "passive" diff --git a/eos/effects/entityenergyneutralizerfalloff.py b/eos/effects/entityenergyneutralizerfalloff.py index 0783295cf..f5aa5ead1 100644 --- a/eos/effects/entityenergyneutralizerfalloff.py +++ b/eos/effects/entityenergyneutralizerfalloff.py @@ -2,14 +2,14 @@ # # Used by: # Drones from group: Energy Neutralizer Drone (3 of 3) -from eos.types import State +from eos.saveddata.module import State type = "active", "projected" def handler(fit, src, context): - if "projected" in context and ( - (hasattr(src, "state") and src.state >= State.ACTIVE) or hasattr(src, "amountActive")): + if "projected" in context and ((hasattr(src, "state") and src.state >= State.ACTIVE) or + hasattr(src, "amountActive")): amount = src.getModifiedItemAttr("energyNeutralizerAmount") time = src.getModifiedItemAttr("energyNeutralizerDuration") diff --git a/eos/effects/gunneryfalloffbonusonline.py b/eos/effects/gunneryfalloffbonusonline.py index 553f9d428..00532b254 100644 --- a/eos/effects/gunneryfalloffbonusonline.py +++ b/eos/effects/gunneryfalloffbonusonline.py @@ -2,7 +2,6 @@ # # Used by: # Modules from group: Tracking Enhancer (10 of 10) -# Module: QA Damage Module type = "passive" diff --git a/eos/effects/gunnerymaxrangebonusonline.py b/eos/effects/gunnerymaxrangebonusonline.py index 6f1bc78ed..284d5b3ca 100644 --- a/eos/effects/gunnerymaxrangebonusonline.py +++ b/eos/effects/gunnerymaxrangebonusonline.py @@ -2,7 +2,6 @@ # # Used by: # Modules from group: Tracking Enhancer (10 of 10) -# Module: QA Damage Module type = "passive" diff --git a/eos/effects/gunnerytrackingspeedbonusonline.py b/eos/effects/gunnerytrackingspeedbonusonline.py index f7bed1ecd..bec1111d7 100644 --- a/eos/effects/gunnerytrackingspeedbonusonline.py +++ b/eos/effects/gunnerytrackingspeedbonusonline.py @@ -2,7 +2,6 @@ # # Used by: # Modules from group: Tracking Enhancer (10 of 10) -# Module: QA Damage Module type = "passive" diff --git a/eos/effects/hybridweapondamagemultiply.py b/eos/effects/hybridweapondamagemultiply.py index 94f6149e3..0fe01369c 100644 --- a/eos/effects/hybridweapondamagemultiply.py +++ b/eos/effects/hybridweapondamagemultiply.py @@ -2,8 +2,6 @@ # # Used by: # Modules from group: Magnetic Field Stabilizer (14 of 14) -# Modules named like: QA Multiship Module Players (4 of 4) -# Module: QA Damage Module type = "passive" diff --git a/eos/effects/iceharvestingdroneoperationdurationbonus.py b/eos/effects/iceharvestingdroneoperationdurationbonus.py index 970c8c0d1..9cd10d66a 100644 --- a/eos/effects/iceharvestingdroneoperationdurationbonus.py +++ b/eos/effects/iceharvestingdroneoperationdurationbonus.py @@ -4,6 +4,8 @@ # Modules named like: Drone Mining Augmentor (8 of 8) # Skill: Ice Harvesting Drone Operation type = "passive" + + def handler(fit, src, context): lvl = src.level if "skill" in context else 1 fit.drones.filteredItemBoost(lambda mod: mod.item.requiresSkill("Ice Harvesting Drone Operation"), "duration", src.getModifiedItemAttr("rofBonus") * lvl) diff --git a/eos/effects/iceharvestingdronespecbonus.py b/eos/effects/iceharvestingdronespecbonus.py index 6fee3f685..18af9f3b1 100644 --- a/eos/effects/iceharvestingdronespecbonus.py +++ b/eos/effects/iceharvestingdronespecbonus.py @@ -3,7 +3,11 @@ # Used by: # Skill: Ice Harvesting Drone Specialization type = "passive" + + def handler(fit, src, context): lvl = src.level - fit.drones.filteredItemBoost(lambda mod: mod.item.requiresSkill("Ice Harvesting Drone Specialization"), "duration", src.getModifiedItemAttr("rofBonus") * lvl) - fit.drones.filteredItemBoost(lambda mod: mod.item.requiresSkill("Ice Harvesting Drone Specialization"), "maxVelocity", src.getModifiedItemAttr("maxVelocityBonus") * lvl) + fit.drones.filteredItemBoost(lambda mod: mod.item.requiresSkill("Ice Harvesting Drone Specialization"), "duration", + src.getModifiedItemAttr("rofBonus") * lvl) + fit.drones.filteredItemBoost(lambda mod: mod.item.requiresSkill("Ice Harvesting Drone Specialization"), + "maxVelocity", src.getModifiedItemAttr("maxVelocityBonus") * lvl) diff --git a/eos/effects/industrialbonusdronedamage.py b/eos/effects/industrialbonusdronedamage.py index db3fba01d..f6c5137bd 100644 --- a/eos/effects/industrialbonusdronedamage.py +++ b/eos/effects/industrialbonusdronedamage.py @@ -11,8 +11,8 @@ # Ship: Rorqual type = "passive" + def handler(fit, src, context): fit.drones.filteredItemBoost(lambda drone: drone.item.requiresSkill("Drones"), "damageMultiplier", src.getModifiedItemAttr("industrialBonusDroneDamage")) - diff --git a/eos/effects/informationcommanddurationbonus.py b/eos/effects/informationcommanddurationbonus.py index 2235cde74..f5a7dc351 100644 --- a/eos/effects/informationcommanddurationbonus.py +++ b/eos/effects/informationcommanddurationbonus.py @@ -3,6 +3,9 @@ # Used by: # Skill: Information Command type = "passive" + + def handler(fit, src, context): lvl = src.level - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Information Command"), "buffDuration", src.getModifiedItemAttr("durationBonus") * lvl) + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Information Command"), "buffDuration", + src.getModifiedItemAttr("durationBonus") * lvl) diff --git a/eos/effects/informationcommandmindlink.py b/eos/effects/informationcommandmindlink.py index a4551304d..f545ab629 100644 --- a/eos/effects/informationcommandmindlink.py +++ b/eos/effects/informationcommandmindlink.py @@ -5,9 +5,16 @@ # Implant: Imperial Navy Command Mindlink # Implant: Information Command Mindlink type = "passive" + + def handler(fit, src, context): - fit.modules.filteredChargeBoost(lambda mod: mod.item.requiresSkill("Information Command"), "warfareBuff4Multiplier", src.getModifiedItemAttr("mindlinkBonus")) - fit.modules.filteredChargeBoost(lambda mod: mod.item.requiresSkill("Information Command"), "warfareBuff3Multiplier", src.getModifiedItemAttr("mindlinkBonus")) - fit.modules.filteredChargeBoost(lambda mod: mod.item.requiresSkill("Information Command"), "warfareBuff1Multiplier", src.getModifiedItemAttr("mindlinkBonus")) - fit.modules.filteredChargeBoost(lambda mod: mod.item.requiresSkill("Information Command"), "warfareBuff2Multiplier", src.getModifiedItemAttr("mindlinkBonus")) - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Information Command"), "buffDuration", src.getModifiedItemAttr("mindlinkBonus")) + fit.modules.filteredChargeBoost(lambda mod: mod.item.requiresSkill("Information Command"), "warfareBuff4Multiplier", + src.getModifiedItemAttr("mindlinkBonus")) + fit.modules.filteredChargeBoost(lambda mod: mod.item.requiresSkill("Information Command"), "warfareBuff3Multiplier", + src.getModifiedItemAttr("mindlinkBonus")) + fit.modules.filteredChargeBoost(lambda mod: mod.item.requiresSkill("Information Command"), "warfareBuff1Multiplier", + src.getModifiedItemAttr("mindlinkBonus")) + fit.modules.filteredChargeBoost(lambda mod: mod.item.requiresSkill("Information Command"), "warfareBuff2Multiplier", + src.getModifiedItemAttr("mindlinkBonus")) + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Information Command"), "buffDuration", + src.getModifiedItemAttr("mindlinkBonus")) diff --git a/eos/effects/informationcommandstrengthbonus.py b/eos/effects/informationcommandstrengthbonus.py index 04f17d646..e4431bee0 100644 --- a/eos/effects/informationcommandstrengthbonus.py +++ b/eos/effects/informationcommandstrengthbonus.py @@ -3,9 +3,15 @@ # Used by: # Skill: Information Command Specialist type = "passive" + + def handler(fit, src, context): lvl = src.level - fit.modules.filteredChargeBoost(lambda mod: mod.item.requiresSkill("Information Command"), "warfareBuff2Multiplier", src.getModifiedItemAttr("commandStrengthBonus") * lvl) - fit.modules.filteredChargeBoost(lambda mod: mod.item.requiresSkill("Information Command"), "warfareBuff1Multiplier", src.getModifiedItemAttr("commandStrengthBonus") * lvl) - fit.modules.filteredChargeBoost(lambda mod: mod.item.requiresSkill("Information Command"), "warfareBuff3Multiplier", src.getModifiedItemAttr("commandStrengthBonus") * lvl) - fit.modules.filteredChargeBoost(lambda mod: mod.item.requiresSkill("Information Command"), "warfareBuff4Multiplier", src.getModifiedItemAttr("commandStrengthBonus") * lvl) + fit.modules.filteredChargeBoost(lambda mod: mod.item.requiresSkill("Information Command"), "warfareBuff2Multiplier", + src.getModifiedItemAttr("commandStrengthBonus") * lvl) + fit.modules.filteredChargeBoost(lambda mod: mod.item.requiresSkill("Information Command"), "warfareBuff1Multiplier", + src.getModifiedItemAttr("commandStrengthBonus") * lvl) + fit.modules.filteredChargeBoost(lambda mod: mod.item.requiresSkill("Information Command"), "warfareBuff3Multiplier", + src.getModifiedItemAttr("commandStrengthBonus") * lvl) + fit.modules.filteredChargeBoost(lambda mod: mod.item.requiresSkill("Information Command"), "warfareBuff4Multiplier", + src.getModifiedItemAttr("commandStrengthBonus") * lvl) diff --git a/eos/effects/invulnerabilitycoredurationbonus.py b/eos/effects/invulnerabilitycoredurationbonus.py index 8d4165859..b318f11cb 100644 --- a/eos/effects/invulnerabilitycoredurationbonus.py +++ b/eos/effects/invulnerabilitycoredurationbonus.py @@ -3,7 +3,11 @@ # Used by: # Skill: Invulnerability Core Operation type = "passive" + + def handler(fit, src, context): lvl = src.level - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Invulnerability Core Operation"), "buffDuration", src.getModifiedItemAttr("durationBonus") * lvl) - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Invulnerability Core Operation"), "duration", src.getModifiedItemAttr("durationBonus") * lvl) + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Invulnerability Core Operation"), "buffDuration", + src.getModifiedItemAttr("durationBonus") * lvl) + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Invulnerability Core Operation"), "duration", + src.getModifiedItemAttr("durationBonus") * lvl) diff --git a/eos/effects/miningdronespecbonus.py b/eos/effects/miningdronespecbonus.py index 17c36723c..fe8cdff5a 100644 --- a/eos/effects/miningdronespecbonus.py +++ b/eos/effects/miningdronespecbonus.py @@ -3,7 +3,11 @@ # Used by: # Skill: Mining Drone Specialization type = "passive" + + def handler(fit, src, context): lvl = src.level - fit.drones.filteredItemBoost(lambda mod: mod.item.requiresSkill("Mining Drone Specialization"), "miningAmount", src.getModifiedItemAttr("miningAmountBonus") * lvl) - fit.drones.filteredItemBoost(lambda mod: mod.item.requiresSkill("Mining Drone Specialization"), "maxVelocity", src.getModifiedItemAttr("maxVelocityBonus") * lvl) + fit.drones.filteredItemBoost(lambda mod: mod.item.requiresSkill("Mining Drone Specialization"), "miningAmount", + src.getModifiedItemAttr("miningAmountBonus") * lvl) + fit.drones.filteredItemBoost(lambda mod: mod.item.requiresSkill("Mining Drone Specialization"), "maxVelocity", + src.getModifiedItemAttr("maxVelocityBonus") * lvl) diff --git a/eos/effects/miningforemanburstbonusics2.py b/eos/effects/miningforemanburstbonusics2.py index a2664b025..0e1140e0e 100644 --- a/eos/effects/miningforemanburstbonusics2.py +++ b/eos/effects/miningforemanburstbonusics2.py @@ -3,9 +3,16 @@ # Used by: # Ships from group: Industrial Command Ship (2 of 2) type = "passive" + + def handler(fit, src, context): - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Mining Foreman"), "warfareBuff4Multiplier", src.getModifiedItemAttr("shipBonusICS2"), skill="Industrial Command Ships") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Mining Foreman"), "warfareBuff1Multiplier", src.getModifiedItemAttr("shipBonusICS2"), skill="Industrial Command Ships") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Mining Foreman"), "buffDuration", src.getModifiedItemAttr("shipBonusICS2"), skill="Industrial Command Ships") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Mining Foreman"), "warfareBuff3Multiplier", src.getModifiedItemAttr("shipBonusICS2"), skill="Industrial Command Ships") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Mining Foreman"), "warfareBuff2Multiplier", src.getModifiedItemAttr("shipBonusICS2"), skill="Industrial Command Ships") + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Mining Foreman"), "warfareBuff4Value", + src.getModifiedItemAttr("shipBonusICS2"), skill="Industrial Command Ships") + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Mining Foreman"), "warfareBuff1Value", + src.getModifiedItemAttr("shipBonusICS2"), skill="Industrial Command Ships") + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Mining Foreman"), "buffDuration", + src.getModifiedItemAttr("shipBonusICS2"), skill="Industrial Command Ships") + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Mining Foreman"), "warfareBuff3Value", + src.getModifiedItemAttr("shipBonusICS2"), skill="Industrial Command Ships") + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Mining Foreman"), "warfareBuff2Value", + src.getModifiedItemAttr("shipBonusICS2"), skill="Industrial Command Ships") diff --git a/eos/effects/miningforemanburstbonusorecapital2.py b/eos/effects/miningforemanburstbonusorecapital2.py index e66243732..b1e7f4b57 100644 --- a/eos/effects/miningforemanburstbonusorecapital2.py +++ b/eos/effects/miningforemanburstbonusorecapital2.py @@ -3,9 +3,16 @@ # Used by: # Ship: Rorqual type = "passive" + + def handler(fit, src, context): - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Mining Foreman"), "warfareBuff1Multiplier", src.getModifiedItemAttr("shipBonusORECapital2"), skill="Capital Industrial Ships") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Mining Foreman"), "warfareBuff2Multiplier", src.getModifiedItemAttr("shipBonusORECapital2"), skill="Capital Industrial Ships") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Mining Foreman"), "warfareBuff4Multiplier", src.getModifiedItemAttr("shipBonusORECapital2"), skill="Capital Industrial Ships") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Mining Foreman"), "warfareBuff3Multiplier", src.getModifiedItemAttr("shipBonusORECapital2"), skill="Capital Industrial Ships") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Mining Foreman"), "buffDuration", src.getModifiedItemAttr("shipBonusORECapital2"), skill="Capital Industrial Ships") + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Mining Foreman"), "warfareBuff1Multiplier", + src.getModifiedItemAttr("shipBonusORECapital2"), skill="Capital Industrial Ships") + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Mining Foreman"), "warfareBuff2Multiplier", + src.getModifiedItemAttr("shipBonusORECapital2"), skill="Capital Industrial Ships") + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Mining Foreman"), "warfareBuff4Multiplier", + src.getModifiedItemAttr("shipBonusORECapital2"), skill="Capital Industrial Ships") + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Mining Foreman"), "warfareBuff3Multiplier", + src.getModifiedItemAttr("shipBonusORECapital2"), skill="Capital Industrial Ships") + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Mining Foreman"), "buffDuration", + src.getModifiedItemAttr("shipBonusORECapital2"), skill="Capital Industrial Ships") diff --git a/eos/effects/miningforemandurationbonus.py b/eos/effects/miningforemandurationbonus.py index 88f33e00a..14a290d2e 100644 --- a/eos/effects/miningforemandurationbonus.py +++ b/eos/effects/miningforemandurationbonus.py @@ -3,6 +3,9 @@ # Used by: # Skill: Mining Foreman type = "passive" + + def handler(fit, src, context): lvl = src.level - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Mining Foreman"), "buffDuration", src.getModifiedItemAttr("durationBonus") * lvl) + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Mining Foreman"), "buffDuration", + src.getModifiedItemAttr("durationBonus") * lvl) diff --git a/eos/effects/miningforemanmindlink.py b/eos/effects/miningforemanmindlink.py index e8d89bdd7..ad4339f67 100644 --- a/eos/effects/miningforemanmindlink.py +++ b/eos/effects/miningforemanmindlink.py @@ -4,9 +4,16 @@ # Implant: Mining Foreman Mindlink # Implant: ORE Mining Director Mindlink type = "passive" + + def handler(fit, src, context): - fit.modules.filteredChargeBoost(lambda mod: mod.item.requiresSkill("Mining Foreman"), "warfareBuff4Multiplier", src.getModifiedItemAttr("mindlinkBonus")) - fit.modules.filteredChargeBoost(lambda mod: mod.item.requiresSkill("Mining Foreman"), "warfareBuff2Multiplier", src.getModifiedItemAttr("mindlinkBonus")) - fit.modules.filteredChargeBoost(lambda mod: mod.item.requiresSkill("Mining Foreman"), "warfareBuff1Multiplier", src.getModifiedItemAttr("mindlinkBonus")) - fit.modules.filteredChargeBoost(lambda mod: mod.item.requiresSkill("Mining Foreman"), "warfareBuff3Multiplier", src.getModifiedItemAttr("mindlinkBonus")) - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Mining Foreman"), "buffDuration", src.getModifiedItemAttr("mindlinkBonus")) + fit.modules.filteredChargeBoost(lambda mod: mod.item.requiresSkill("Mining Foreman"), "warfareBuff4Multiplier", + src.getModifiedItemAttr("mindlinkBonus")) + fit.modules.filteredChargeBoost(lambda mod: mod.item.requiresSkill("Mining Foreman"), "warfareBuff2Multiplier", + src.getModifiedItemAttr("mindlinkBonus")) + fit.modules.filteredChargeBoost(lambda mod: mod.item.requiresSkill("Mining Foreman"), "warfareBuff1Multiplier", + src.getModifiedItemAttr("mindlinkBonus")) + fit.modules.filteredChargeBoost(lambda mod: mod.item.requiresSkill("Mining Foreman"), "warfareBuff3Multiplier", + src.getModifiedItemAttr("mindlinkBonus")) + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Mining Foreman"), "buffDuration", + src.getModifiedItemAttr("mindlinkBonus")) diff --git a/eos/effects/miningforemanstrengthbonus.py b/eos/effects/miningforemanstrengthbonus.py index ac280eb87..e7af57839 100644 --- a/eos/effects/miningforemanstrengthbonus.py +++ b/eos/effects/miningforemanstrengthbonus.py @@ -3,9 +3,11 @@ # Used by: # Skill: Mining Director type = "passive" + + def handler(fit, src, context): lvl = src.level fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Mining Foreman"), "warfareBuff4Value", src.getModifiedItemAttr("commandStrengthBonus") * lvl) fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Mining Foreman"), "warfareBuff3Value", src.getModifiedItemAttr("commandStrengthBonus") * lvl) fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Mining Foreman"), "warfareBuff2Value", src.getModifiedItemAttr("commandStrengthBonus") * lvl) - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Mining Foreman"), "warfareBuff1Value", src.getModifiedItemAttr("commandStrengthBonus") * lvl) \ No newline at end of file + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Mining Foreman"), "warfareBuff1Value", src.getModifiedItemAttr("commandStrengthBonus") * lvl) diff --git a/eos/effects/mininginfomultiplier.py b/eos/effects/mininginfomultiplier.py index d0ca12180..6cb722d76 100644 --- a/eos/effects/mininginfomultiplier.py +++ b/eos/effects/mininginfomultiplier.py @@ -7,5 +7,6 @@ type = "passive" def handler(fit, module, context): - module.multiplyItemAttr("specialtyMiningAmount", module.getModifiedChargeAttr("specialisationAsteroidYieldMultiplier")) - #module.multiplyItemAttr("miningAmount", module.getModifiedChargeAttr("specialisationAsteroidYieldMultiplier")) + module.multiplyItemAttr("specialtyMiningAmount", + module.getModifiedChargeAttr("specialisationAsteroidYieldMultiplier")) + # module.multiplyItemAttr("miningAmount", module.getModifiedChargeAttr("specialisationAsteroidYieldMultiplier")) diff --git a/eos/effects/missiledmgbonus.py b/eos/effects/missiledmgbonus.py index d18ce2e30..48a845345 100644 --- a/eos/effects/missiledmgbonus.py +++ b/eos/effects/missiledmgbonus.py @@ -2,7 +2,6 @@ # # Used by: # Modules from group: Ballistic Control system (17 of 17) -# Modules named like: QA Multiship Module Players (4 of 4) type = "passive" diff --git a/eos/effects/modedamptdresistspostdiv.py b/eos/effects/modedamptdresistspostdiv.py index db8580229..891b84a82 100644 --- a/eos/effects/modedamptdresistspostdiv.py +++ b/eos/effects/modedamptdresistspostdiv.py @@ -4,6 +4,7 @@ # Modules named like: Sharpshooter Mode (4 of 4) type = "passive" + def handler(fit, module, context): fit.ship.multiplyItemAttr("weaponDisruptionResistance", 1 / module.getModifiedItemAttr("modeEwarResistancePostDiv")) fit.ship.multiplyItemAttr("sensorDampenerResistance", 1 / module.getModifiedItemAttr("modeEwarResistancePostDiv")) diff --git a/eos/effects/modifyactivearmorresonancepostpercent.py b/eos/effects/modifyactivearmorresonancepostpercent.py index be8b613a8..6a6a9e571 100644 --- a/eos/effects/modifyactivearmorresonancepostpercent.py +++ b/eos/effects/modifyactivearmorresonancepostpercent.py @@ -1,13 +1,13 @@ -# modifyActiveArmorResonancePostPercent -# -# Used by: -# Modules from group: Armor Hardener (156 of 156) -# Modules from group: Flex Armor Hardener (4 of 4) -type = "active" - - -def handler(fit, module, context): - for damageType in ("kinetic", "thermal", "explosive", "em"): - fit.ship.boostItemAttr("armor%sDamageResonance" % damageType.capitalize(), - module.getModifiedItemAttr("%sDamageResistanceBonus" % damageType), - stackingPenalties=True) +# modifyActiveArmorResonancePostPercent +# +# Used by: +# Modules from group: Armor Hardener (156 of 156) +# Modules from group: Flex Armor Hardener (4 of 4) +type = "active" + + +def handler(fit, module, context): + for damageType in ("kinetic", "thermal", "explosive", "em"): + fit.ship.boostItemAttr("armor%sDamageResonance" % damageType.capitalize(), + module.getModifiedItemAttr("%sDamageResistanceBonus" % damageType), + stackingPenalties=True) diff --git a/eos/effects/modifyactiveshieldresonancepostpercent.py b/eos/effects/modifyactiveshieldresonancepostpercent.py index 41e70a776..04c2edac3 100644 --- a/eos/effects/modifyactiveshieldresonancepostpercent.py +++ b/eos/effects/modifyactiveshieldresonancepostpercent.py @@ -1,13 +1,13 @@ -# modifyActiveShieldResonancePostPercent -# -# Used by: -# Modules from group: Flex Shield Hardener (5 of 5) -# Modules from group: Shield Hardener (97 of 97) -type = "active" - - -def handler(fit, module, context): - for damageType in ("kinetic", "thermal", "explosive", "em"): - fit.ship.boostItemAttr("shield" + damageType.capitalize() + "DamageResonance", - module.getModifiedItemAttr(damageType + "DamageResistanceBonus"), - stackingPenalties=True) +# modifyActiveShieldResonancePostPercent +# +# Used by: +# Modules from group: Flex Shield Hardener (5 of 5) +# Modules from group: Shield Hardener (97 of 97) +type = "active" + + +def handler(fit, module, context): + for damageType in ("kinetic", "thermal", "explosive", "em"): + fit.ship.boostItemAttr("shield" + damageType.capitalize() + "DamageResonance", + module.getModifiedItemAttr(damageType + "DamageResistanceBonus"), + stackingPenalties=True) diff --git a/eos/effects/modifyshieldrechargerate.py b/eos/effects/modifyshieldrechargerate.py index 2a1c209ad..bebf517a7 100644 --- a/eos/effects/modifyshieldrechargerate.py +++ b/eos/effects/modifyshieldrechargerate.py @@ -6,7 +6,6 @@ # Modules from group: Reactor Control Unit (22 of 22) # Modules from group: Shield Recharger (4 of 4) # Modules named like: Flux Coil (12 of 12) -# Modules named like: QA Multiship Module Players (4 of 4) type = "passive" diff --git a/eos/effects/modulebonusafterburner.py b/eos/effects/modulebonusafterburner.py index e48bdb437..c26412bb0 100644 --- a/eos/effects/modulebonusafterburner.py +++ b/eos/effects/modulebonusafterburner.py @@ -11,4 +11,4 @@ def handler(fit, module, context): speedBoost = module.getModifiedItemAttr("speedFactor") mass = fit.ship.getModifiedItemAttr("mass") thrust = module.getModifiedItemAttr("speedBoostFactor") - fit.ship.boostItemAttr("maxVelocity", speedBoost * thrust / mass) \ No newline at end of file + fit.ship.boostItemAttr("maxVelocity", speedBoost * thrust / mass) diff --git a/eos/effects/modulebonusancillaryremotearmorrepairer.py b/eos/effects/modulebonusancillaryremotearmorrepairer.py index c3c157b81..78a6ef855 100644 --- a/eos/effects/modulebonusancillaryremotearmorrepairer.py +++ b/eos/effects/modulebonusancillaryremotearmorrepairer.py @@ -7,7 +7,8 @@ type = "projected", "active" def handler(fit, module, context): - if "projected" not in context: return + if "projected" not in context: + return if module.charge and module.charge.name == "Nanite Repair Paste": multiplier = 3 diff --git a/eos/effects/modulebonusancillaryremoteshieldbooster.py b/eos/effects/modulebonusancillaryremoteshieldbooster.py index 74443b269..99b1a444c 100644 --- a/eos/effects/modulebonusancillaryremoteshieldbooster.py +++ b/eos/effects/modulebonusancillaryremoteshieldbooster.py @@ -7,7 +7,8 @@ type = "projected", "active" def handler(fit, module, context): - if "projected" not in context: return + if "projected" not in context: + return amount = module.getModifiedItemAttr("shieldBonus") speed = module.getModifiedItemAttr("duration") / 1000.0 fit.extraAttributes.increase("shieldRepair", amount / speed) diff --git a/eos/effects/modulebonusmicrowarpdrive.py b/eos/effects/modulebonusmicrowarpdrive.py index 2c4f6cd91..75df915ce 100644 --- a/eos/effects/modulebonusmicrowarpdrive.py +++ b/eos/effects/modulebonusmicrowarpdrive.py @@ -12,4 +12,5 @@ def handler(fit, module, context): mass = fit.ship.getModifiedItemAttr("mass") thrust = module.getModifiedItemAttr("speedBoostFactor") fit.ship.boostItemAttr("maxVelocity", speedBoost * thrust / mass) - fit.ship.boostItemAttr("signatureRadius", module.getModifiedItemAttr("signatureRadiusBonus"), stackingPenalties = True) + fit.ship.boostItemAttr("signatureRadius", module.getModifiedItemAttr("signatureRadiusBonus"), + stackingPenalties=True) diff --git a/eos/effects/modulebonuswarfarelinkarmor.py b/eos/effects/modulebonuswarfarelinkarmor.py index f39ae8a1b..2c792da83 100644 --- a/eos/effects/modulebonuswarfarelinkarmor.py +++ b/eos/effects/modulebonuswarfarelinkarmor.py @@ -13,6 +13,8 @@ which warfareBuffID to run (shouldn't need this right now, but better safe than ''' type = "active", "gang" + + def handler(fit, module, context, **kwargs): for x in xrange(1, 5): if module.getModifiedChargeAttr("warfareBuff{}ID".format(x)): @@ -21,5 +23,3 @@ def handler(fit, module, context, **kwargs): if id: fit.addCommandBonus(id, value, module, kwargs['effect']) - - diff --git a/eos/effects/modulebonuswarfarelinkinfo.py b/eos/effects/modulebonuswarfarelinkinfo.py index 2951cc9db..491d824ad 100644 --- a/eos/effects/modulebonuswarfarelinkinfo.py +++ b/eos/effects/modulebonuswarfarelinkinfo.py @@ -4,6 +4,8 @@ # Variations of module: Information Command Burst I (2 of 2) type = "active", "gang" + + def handler(fit, module, context, **kwargs): for x in xrange(1, 5): if module.getModifiedChargeAttr("warfareBuff{}ID".format(x)): @@ -11,4 +13,4 @@ def handler(fit, module, context, **kwargs): id = module.getModifiedChargeAttr("warfareBuff{}ID".format(x)) if id: - fit.addCommandBonus(id, value, module, kwargs['effect']) \ No newline at end of file + fit.addCommandBonus(id, value, module, kwargs['effect']) diff --git a/eos/effects/modulebonuswarfarelinkmining.py b/eos/effects/modulebonuswarfarelinkmining.py index 494495873..fa28fcf4d 100644 --- a/eos/effects/modulebonuswarfarelinkmining.py +++ b/eos/effects/modulebonuswarfarelinkmining.py @@ -4,6 +4,8 @@ # Variations of module: Mining Foreman Burst I (2 of 2) type = "active", "gang" + + def handler(fit, module, context, **kwargs): for x in xrange(1, 5): if module.getModifiedChargeAttr("warfareBuff{}ID".format(x)): @@ -11,4 +13,4 @@ def handler(fit, module, context, **kwargs): id = module.getModifiedChargeAttr("warfareBuff{}ID".format(x)) if id: - fit.addCommandBonus(id, value, module, kwargs['effect']) \ No newline at end of file + fit.addCommandBonus(id, value, module, kwargs['effect']) diff --git a/eos/effects/modulebonuswarfarelinkshield.py b/eos/effects/modulebonuswarfarelinkshield.py index 08b19b3a3..f8d3130e0 100644 --- a/eos/effects/modulebonuswarfarelinkshield.py +++ b/eos/effects/modulebonuswarfarelinkshield.py @@ -4,6 +4,8 @@ # Variations of module: Shield Command Burst I (2 of 2) type = "active", "gang" + + def handler(fit, module, context, **kwargs): for x in xrange(1, 5): if module.getModifiedChargeAttr("warfareBuff{}ID".format(x)): @@ -11,4 +13,4 @@ def handler(fit, module, context, **kwargs): id = module.getModifiedChargeAttr("warfareBuff{}ID".format(x)) if id: - fit.addCommandBonus(id, value, module, kwargs['effect']) \ No newline at end of file + fit.addCommandBonus(id, value, module, kwargs['effect']) diff --git a/eos/effects/modulebonuswarfarelinkskirmish.py b/eos/effects/modulebonuswarfarelinkskirmish.py index 53f1d0a06..84b0cea3e 100644 --- a/eos/effects/modulebonuswarfarelinkskirmish.py +++ b/eos/effects/modulebonuswarfarelinkskirmish.py @@ -4,6 +4,8 @@ # Variations of module: Skirmish Command Burst I (2 of 2) type = "active", "gang" + + def handler(fit, module, context, **kwargs): for x in xrange(1, 5): if module.getModifiedChargeAttr("warfareBuff{}ID".format(x)): @@ -11,4 +13,4 @@ def handler(fit, module, context, **kwargs): id = module.getModifiedChargeAttr("warfareBuff{}ID".format(x)) if id: - fit.addCommandBonus(id, value, module, kwargs['effect']) \ No newline at end of file + fit.addCommandBonus(id, value, module, kwargs['effect']) diff --git a/eos/effects/moduletitaneffectgenerator.py b/eos/effects/moduletitaneffectgenerator.py index 64e21857a..901a0e89e 100644 --- a/eos/effects/moduletitaneffectgenerator.py +++ b/eos/effects/moduletitaneffectgenerator.py @@ -3,6 +3,8 @@ # Used by: # Modules from group: Titan Phenomena Generator (4 of 4) type = "active", "gang" + + def handler(fit, module, context, **kwargs): for x in xrange(1, 5): if module.getModifiedItemAttr("warfareBuff{}ID".format(x)): @@ -11,7 +13,3 @@ def handler(fit, module, context, **kwargs): if id: fit.addCommandBonus(id, value, module, kwargs['effect']) - - - - diff --git a/eos/effects/noscpuneedbonuseffect.py b/eos/effects/noscpuneedbonuseffect.py index 9f92b373c..d54b6e023 100644 --- a/eos/effects/noscpuneedbonuseffect.py +++ b/eos/effects/noscpuneedbonuseffect.py @@ -3,5 +3,8 @@ # Used by: # Ship: Rabisu type = "passive" + + def handler(fit, src, context): - fit.modules.filteredItemBoost(lambda mod: mod.item.group.name == "Energy Nosferatu", "cpu", src.getModifiedItemAttr("nosferatuCpuNeedBonus")) + fit.modules.filteredItemBoost(lambda mod: mod.item.group.name == "Energy Nosferatu", "cpu", + src.getModifiedItemAttr("nosferatuCpuNeedBonus")) diff --git a/eos/effects/remoteweapondisruptentity.py b/eos/effects/npcentityweapondisruptor.py similarity index 97% rename from eos/effects/remoteweapondisruptentity.py rename to eos/effects/npcentityweapondisruptor.py index 486f67f58..e2fb7bf4a 100644 --- a/eos/effects/remoteweapondisruptentity.py +++ b/eos/effects/npcentityweapondisruptor.py @@ -1,4 +1,4 @@ -# remoteWeaponDisruptEntity +# npcEntityWeaponDisruptor # # Used by: # Drones named like: TD (3 of 3) diff --git a/eos/effects/overloadselfdurationbonus.py b/eos/effects/overloadselfdurationbonus.py index 757ba6d76..356eab98a 100644 --- a/eos/effects/overloadselfdurationbonus.py +++ b/eos/effects/overloadselfdurationbonus.py @@ -11,8 +11,6 @@ # Modules from group: Smart Bomb (118 of 118) # Modules from group: Warp Disrupt Field Generator (7 of 7) # Modules named like: Ancillary Remote (8 of 8) -# Module: QA Remote Armor Repair System - 5 Players -# Module: QA Shield Transporter - 5 Players # Module: Reactive Armor Hardener # Module: Target Spectrum Breaker type = "overheat" diff --git a/eos/effects/powerbooster.py b/eos/effects/powerbooster.py index 6fdafc9a4..42dcaca89 100644 --- a/eos/effects/powerbooster.py +++ b/eos/effects/powerbooster.py @@ -11,6 +11,7 @@ def handler(fit, module, context): # Make so that reloads are always taken into account during clculations module.forceReload = True - if module.charge is None: return + if module.charge is None: + return capAmount = module.getModifiedChargeAttr("capacitorBonus") or 0 module.itemModifiedAttributes["capacitorNeed"] = -capAmount diff --git a/eos/effects/projectileweapondamagemultiply.py b/eos/effects/projectileweapondamagemultiply.py index 7037897d8..711075efb 100644 --- a/eos/effects/projectileweapondamagemultiply.py +++ b/eos/effects/projectileweapondamagemultiply.py @@ -2,8 +2,6 @@ # # Used by: # Modules from group: Gyrostabilizer (13 of 13) -# Modules named like: QA Multiship Module Players (4 of 4) -# Module: QA Damage Module type = "passive" diff --git a/eos/effects/remotehullrepair.py b/eos/effects/remotehullrepair.py index 099e0c2ac..508b5684e 100644 --- a/eos/effects/remotehullrepair.py +++ b/eos/effects/remotehullrepair.py @@ -4,7 +4,8 @@ runTime = "late" def handler(fit, module, context): - if "projected" not in context: return + if "projected" not in context: + return bonus = module.getModifiedItemAttr("structureDamageAmount") duration = module.getModifiedItemAttr("duration") / 1000.0 fit.extraAttributes.increase("hullRepair", bonus / duration) diff --git a/eos/effects/remotehullrepairentity.py b/eos/effects/remotehullrepairentity.py index db8de5fe4..367d08bf1 100644 --- a/eos/effects/remotehullrepairentity.py +++ b/eos/effects/remotehullrepairentity.py @@ -7,7 +7,8 @@ runTime = "late" def handler(fit, module, context): - if "projected" not in context: return + if "projected" not in context: + return bonus = module.getModifiedItemAttr("structureDamageAmount") duration = module.getModifiedItemAttr("duration") / 1000.0 fit.extraAttributes.increase("hullRepair", bonus / duration) diff --git a/eos/effects/remotehullrepairfalloff.py b/eos/effects/remotehullrepairfalloff.py index d7344b646..eaecf6bf9 100644 --- a/eos/effects/remotehullrepairfalloff.py +++ b/eos/effects/remotehullrepairfalloff.py @@ -7,7 +7,8 @@ runTime = "late" def handler(fit, module, context): - if "projected" not in context: return + if "projected" not in context: + return bonus = module.getModifiedItemAttr("structureDamageAmount") duration = module.getModifiedItemAttr("duration") / 1000.0 fit.extraAttributes.increase("hullRepair", bonus / duration) diff --git a/eos/effects/remotewebifierentity.py b/eos/effects/remotewebifierentity.py index 38028881a..cf9905405 100644 --- a/eos/effects/remotewebifierentity.py +++ b/eos/effects/remotewebifierentity.py @@ -6,6 +6,7 @@ type = "active", "projected" def handler(fit, module, context): - if "projected" not in context: return + if "projected" not in context: + return fit.ship.boostItemAttr("maxVelocity", module.getModifiedItemAttr("speedFactor"), stackingPenalties=True, remoteResists=True) diff --git a/eos/effects/remotewebifierfalloff.py b/eos/effects/remotewebifierfalloff.py index 67f6d4dc5..1db5eb858 100644 --- a/eos/effects/remotewebifierfalloff.py +++ b/eos/effects/remotewebifierfalloff.py @@ -7,6 +7,7 @@ type = "active", "projected" def handler(fit, module, context): - if "projected" not in context: return + if "projected" not in context: + return fit.ship.boostItemAttr("maxVelocity", module.getModifiedItemAttr("speedFactor"), stackingPenalties=True, remoteResists=True) diff --git a/eos/effects/rolebonusremotearmorrepairoptimalfalloff.py b/eos/effects/rolebonusremotearmorrepairoptimalfalloff.py index c28744a06..fae8acfac 100644 --- a/eos/effects/rolebonusremotearmorrepairoptimalfalloff.py +++ b/eos/effects/rolebonusremotearmorrepairoptimalfalloff.py @@ -3,6 +3,12 @@ # Used by: # Ship: Rabisu type = "passive" + + def handler(fit, src, context): - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Remote Armor Repair Systems"), "falloffEffectiveness", src.getModifiedItemAttr("roleBonusRepairRange"), stackingPenalties=True) - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Remote Armor Repair Systems"), "maxRange", src.getModifiedItemAttr("roleBonusRepairRange"), stackingPenalties=True) + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Remote Armor Repair Systems"), + "falloffEffectiveness", + src.getModifiedItemAttr("roleBonusRepairRange")) + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Remote Armor Repair Systems"), + "maxRange", + src.getModifiedItemAttr("roleBonusRepairRange")) diff --git a/eos/effects/scanstrengthbonuspercentactivate.py b/eos/effects/scanstrengthbonuspercentactivate.py index 2ce6a627a..10b66e3c4 100644 --- a/eos/effects/scanstrengthbonuspercentactivate.py +++ b/eos/effects/scanstrengthbonuspercentactivate.py @@ -1,7 +1,4 @@ -# scanStrengthBonusPercentActivate -# -# Used by: -# Module: QA ECCM +# Not used by any item type = "active" diff --git a/eos/effects/scanstrengthbonuspercentpassive.py b/eos/effects/scanstrengthbonuspercentpassive.py index 7e0f43c1d..d00f4e2b6 100644 --- a/eos/effects/scanstrengthbonuspercentpassive.py +++ b/eos/effects/scanstrengthbonuspercentpassive.py @@ -1,7 +1,7 @@ # scanStrengthBonusPercentPassive # # Used by: -# Implants named like: High grade (20 of 61) +# Implants named like: High grade (20 of 66) type = "passive" diff --git a/eos/effects/setbonusasklepian.py b/eos/effects/setbonusasklepian.py index 4bd989f13..853626c7d 100644 --- a/eos/effects/setbonusasklepian.py +++ b/eos/effects/setbonusasklepian.py @@ -1,8 +1,7 @@ # setBonusAsklepian # # Used by: -# Implants named like: Asklepian Omega (3 of 3) -# Implants named like: Grade Asklepian (16 of 16) +# Implants named like: grade Asklepian (18 of 18) runTime = "early" type = "passive" diff --git a/eos/effects/shieldcommandburstbonusics3.py b/eos/effects/shieldcommandburstbonusics3.py index 9c6ee1541..1a61286b7 100644 --- a/eos/effects/shieldcommandburstbonusics3.py +++ b/eos/effects/shieldcommandburstbonusics3.py @@ -3,9 +3,16 @@ # Used by: # Ship: Orca type = "passive" + + def handler(fit, src, context): - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Shield Command"), "warfareBuff4Multiplier", src.getModifiedItemAttr("shipBonusICS3"), skill="Industrial Command Ships") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Shield Command"), "warfareBuff1Multiplier", src.getModifiedItemAttr("shipBonusICS3"), skill="Industrial Command Ships") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Shield Command"), "warfareBuff2Multiplier", src.getModifiedItemAttr("shipBonusICS3"), skill="Industrial Command Ships") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Shield Command"), "warfareBuff3Multiplier", src.getModifiedItemAttr("shipBonusICS3"), skill="Industrial Command Ships") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Shield Command"), "buffDuration", src.getModifiedItemAttr("shipBonusICS3"), skill="Industrial Command Ships") + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Shield Command"), "warfareBuff4Multiplier", + src.getModifiedItemAttr("shipBonusICS3"), skill="Industrial Command Ships") + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Shield Command"), "warfareBuff1Multiplier", + src.getModifiedItemAttr("shipBonusICS3"), skill="Industrial Command Ships") + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Shield Command"), "warfareBuff2Multiplier", + src.getModifiedItemAttr("shipBonusICS3"), skill="Industrial Command Ships") + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Shield Command"), "warfareBuff3Multiplier", + src.getModifiedItemAttr("shipBonusICS3"), skill="Industrial Command Ships") + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Shield Command"), "buffDuration", + src.getModifiedItemAttr("shipBonusICS3"), skill="Industrial Command Ships") diff --git a/eos/effects/shieldcommandburstbonusorecapital3.py b/eos/effects/shieldcommandburstbonusorecapital3.py index 595d7afae..5972f2044 100644 --- a/eos/effects/shieldcommandburstbonusorecapital3.py +++ b/eos/effects/shieldcommandburstbonusorecapital3.py @@ -3,9 +3,16 @@ # Used by: # Ship: Rorqual type = "passive" + + def handler(fit, src, context): - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Shield Command"), "warfareBuff4Multiplier", src.getModifiedItemAttr("shipBonusORECapital3"), skill="Capital Industrial Ships") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Shield Command"), "buffDuration", src.getModifiedItemAttr("shipBonusORECapital3"), skill="Capital Industrial Ships") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Shield Command"), "warfareBuff1Multiplier", src.getModifiedItemAttr("shipBonusORECapital3"), skill="Capital Industrial Ships") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Shield Command"), "warfareBuff3Multiplier", src.getModifiedItemAttr("shipBonusORECapital3"), skill="Capital Industrial Ships") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Shield Command"), "warfareBuff2Multiplier", src.getModifiedItemAttr("shipBonusORECapital3"), skill="Capital Industrial Ships") + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Shield Command"), "warfareBuff4Multiplier", + src.getModifiedItemAttr("shipBonusORECapital3"), skill="Capital Industrial Ships") + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Shield Command"), "buffDuration", + src.getModifiedItemAttr("shipBonusORECapital3"), skill="Capital Industrial Ships") + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Shield Command"), "warfareBuff1Multiplier", + src.getModifiedItemAttr("shipBonusORECapital3"), skill="Capital Industrial Ships") + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Shield Command"), "warfareBuff3Multiplier", + src.getModifiedItemAttr("shipBonusORECapital3"), skill="Capital Industrial Ships") + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Shield Command"), "warfareBuff2Multiplier", + src.getModifiedItemAttr("shipBonusORECapital3"), skill="Capital Industrial Ships") diff --git a/eos/effects/shieldcommanddurationbonus.py b/eos/effects/shieldcommanddurationbonus.py index 8e7c78ea5..a7425a974 100644 --- a/eos/effects/shieldcommanddurationbonus.py +++ b/eos/effects/shieldcommanddurationbonus.py @@ -3,6 +3,9 @@ # Used by: # Skill: Shield Command type = "passive" + + def handler(fit, src, context): lvl = src.level - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Shield Command"), "buffDuration", src.getModifiedItemAttr("durationBonus") * lvl) + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Shield Command"), "buffDuration", + src.getModifiedItemAttr("durationBonus") * lvl) diff --git a/eos/effects/shieldcommandmindlink.py b/eos/effects/shieldcommandmindlink.py index a350e5c29..fac7a7720 100644 --- a/eos/effects/shieldcommandmindlink.py +++ b/eos/effects/shieldcommandmindlink.py @@ -3,9 +3,16 @@ # Used by: # Implants from group: Cyber Leadership (4 of 10) type = "passive" + + def handler(fit, src, context): - fit.modules.filteredChargeBoost(lambda mod: mod.item.requiresSkill("Shield Command"), "warfareBuff4Multiplier", src.getModifiedItemAttr("mindlinkBonus")) - fit.modules.filteredChargeBoost(lambda mod: mod.item.requiresSkill("Shield Command"), "warfareBuff3Multiplier", src.getModifiedItemAttr("mindlinkBonus")) - fit.modules.filteredChargeBoost(lambda mod: mod.item.requiresSkill("Shield Command"), "warfareBuff2Multiplier", src.getModifiedItemAttr("mindlinkBonus")) - fit.modules.filteredChargeBoost(lambda mod: mod.item.requiresSkill("Shield Command"), "warfareBuff1Multiplier", src.getModifiedItemAttr("mindlinkBonus")) - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Shield Command"), "buffDuration", src.getModifiedItemAttr("mindlinkBonus")) + fit.modules.filteredChargeBoost(lambda mod: mod.item.requiresSkill("Shield Command"), "warfareBuff4Multiplier", + src.getModifiedItemAttr("mindlinkBonus")) + fit.modules.filteredChargeBoost(lambda mod: mod.item.requiresSkill("Shield Command"), "warfareBuff3Multiplier", + src.getModifiedItemAttr("mindlinkBonus")) + fit.modules.filteredChargeBoost(lambda mod: mod.item.requiresSkill("Shield Command"), "warfareBuff2Multiplier", + src.getModifiedItemAttr("mindlinkBonus")) + fit.modules.filteredChargeBoost(lambda mod: mod.item.requiresSkill("Shield Command"), "warfareBuff1Multiplier", + src.getModifiedItemAttr("mindlinkBonus")) + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Shield Command"), "buffDuration", + src.getModifiedItemAttr("mindlinkBonus")) diff --git a/eos/effects/shieldcommandstrengthbonus.py b/eos/effects/shieldcommandstrengthbonus.py index 775e54d62..b95fe66df 100644 --- a/eos/effects/shieldcommandstrengthbonus.py +++ b/eos/effects/shieldcommandstrengthbonus.py @@ -3,9 +3,15 @@ # Used by: # Skill: Shield Command Specialist type = "passive" + + def handler(fit, src, context): lvl = src.level - fit.modules.filteredChargeBoost(lambda mod: mod.item.requiresSkill("Shield Command"), "warfareBuff3Multiplier", src.getModifiedItemAttr("commandStrengthBonus") * lvl) - fit.modules.filteredChargeBoost(lambda mod: mod.item.requiresSkill("Shield Command"), "warfareBuff1Multiplier", src.getModifiedItemAttr("commandStrengthBonus") * lvl) - fit.modules.filteredChargeBoost(lambda mod: mod.item.requiresSkill("Shield Command"), "warfareBuff2Multiplier", src.getModifiedItemAttr("commandStrengthBonus") * lvl) - fit.modules.filteredChargeBoost(lambda mod: mod.item.requiresSkill("Shield Command"), "warfareBuff4Multiplier", src.getModifiedItemAttr("commandStrengthBonus") * lvl) + fit.modules.filteredChargeBoost(lambda mod: mod.item.requiresSkill("Shield Command"), "warfareBuff3Multiplier", + src.getModifiedItemAttr("commandStrengthBonus") * lvl) + fit.modules.filteredChargeBoost(lambda mod: mod.item.requiresSkill("Shield Command"), "warfareBuff1Multiplier", + src.getModifiedItemAttr("commandStrengthBonus") * lvl) + fit.modules.filteredChargeBoost(lambda mod: mod.item.requiresSkill("Shield Command"), "warfareBuff2Multiplier", + src.getModifiedItemAttr("commandStrengthBonus") * lvl) + fit.modules.filteredChargeBoost(lambda mod: mod.item.requiresSkill("Shield Command"), "warfareBuff4Multiplier", + src.getModifiedItemAttr("commandStrengthBonus") * lvl) diff --git a/eos/effects/shieldtransfer.py b/eos/effects/shieldtransfer.py index b55165a32..54bbc1112 100644 --- a/eos/effects/shieldtransfer.py +++ b/eos/effects/shieldtransfer.py @@ -1,7 +1,4 @@ -# shieldTransfer -# -# Used by: -# Module: QA Shield Transporter - 5 Players +# Not used by any item type = "projected", "active" diff --git a/eos/effects/shipbonuscarriera4warfarelinksbonus.py b/eos/effects/shipbonuscarriera4warfarelinksbonus.py index 43a053e04..31981bba6 100644 --- a/eos/effects/shipbonuscarriera4warfarelinksbonus.py +++ b/eos/effects/shipbonuscarriera4warfarelinksbonus.py @@ -3,9 +3,21 @@ # Used by: # Ship: Archon type = "passive" + + def handler(fit, src, context): - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Armored Command") or mod.item.requiresSkill("Information Command"), "buffDuration", src.getModifiedItemAttr("shipBonusCarrierA4"), skill="Amarr Carrier") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Armored Command") or mod.item.requiresSkill("Information Command"), "warfareBuff3Value", src.getModifiedItemAttr("shipBonusCarrierA4"), skill="Amarr Carrier") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Armored Command") or mod.item.requiresSkill("Information Command"), "warfareBuff1Value", src.getModifiedItemAttr("shipBonusCarrierA4"), skill="Amarr Carrier") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Armored Command") or mod.item.requiresSkill("Information Command"), "warfareBuff4Value", src.getModifiedItemAttr("shipBonusCarrierA4"), skill="Amarr Carrier") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Armored Command") or mod.item.requiresSkill("Information Command"), "warfareBuff2Value", src.getModifiedItemAttr("shipBonusCarrierA4"), skill="Amarr Carrier") + fit.modules.filteredItemBoost( + lambda mod: mod.item.requiresSkill("Armored Command") or mod.item.requiresSkill("Information Command"), + "buffDuration", src.getModifiedItemAttr("shipBonusCarrierA4"), skill="Amarr Carrier") + fit.modules.filteredItemBoost( + lambda mod: mod.item.requiresSkill("Armored Command") or mod.item.requiresSkill("Information Command"), + "warfareBuff3Value", src.getModifiedItemAttr("shipBonusCarrierA4"), skill="Amarr Carrier") + fit.modules.filteredItemBoost( + lambda mod: mod.item.requiresSkill("Armored Command") or mod.item.requiresSkill("Information Command"), + "warfareBuff1Value", src.getModifiedItemAttr("shipBonusCarrierA4"), skill="Amarr Carrier") + fit.modules.filteredItemBoost( + lambda mod: mod.item.requiresSkill("Armored Command") or mod.item.requiresSkill("Information Command"), + "warfareBuff4Value", src.getModifiedItemAttr("shipBonusCarrierA4"), skill="Amarr Carrier") + fit.modules.filteredItemBoost( + lambda mod: mod.item.requiresSkill("Armored Command") or mod.item.requiresSkill("Information Command"), + "warfareBuff2Value", src.getModifiedItemAttr("shipBonusCarrierA4"), skill="Amarr Carrier") diff --git a/eos/effects/shipbonuscarrierc4warfarelinksbonus.py b/eos/effects/shipbonuscarrierc4warfarelinksbonus.py index ecaf23399..4e19f6916 100644 --- a/eos/effects/shipbonuscarrierc4warfarelinksbonus.py +++ b/eos/effects/shipbonuscarrierc4warfarelinksbonus.py @@ -3,9 +3,21 @@ # Used by: # Ship: Chimera type = "passive" + + def handler(fit, src, context): - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Shield Command") or mod.item.requiresSkill("Information Command"), "warfareBuff2Value", src.getModifiedItemAttr("shipBonusCarrierC4"), skill="Caldari Carrier") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Shield Command") or mod.item.requiresSkill("Information Command"), "buffDuration", src.getModifiedItemAttr("shipBonusCarrierC4"), skill="Caldari Carrier") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Shield Command") or mod.item.requiresSkill("Information Command"), "warfareBuff3Value", src.getModifiedItemAttr("shipBonusCarrierC4"), skill="Caldari Carrier") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Shield Command") or mod.item.requiresSkill("Information Command"), "warfareBuff4Value", src.getModifiedItemAttr("shipBonusCarrierC4"), skill="Caldari Carrier") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Shield Command") or mod.item.requiresSkill("Information Command"), "warfareBuff1Value", src.getModifiedItemAttr("shipBonusCarrierC4"), skill="Caldari Carrier") + fit.modules.filteredItemBoost( + lambda mod: mod.item.requiresSkill("Shield Command") or mod.item.requiresSkill("Information Command"), + "warfareBuff2Value", src.getModifiedItemAttr("shipBonusCarrierC4"), skill="Caldari Carrier") + fit.modules.filteredItemBoost( + lambda mod: mod.item.requiresSkill("Shield Command") or mod.item.requiresSkill("Information Command"), + "buffDuration", src.getModifiedItemAttr("shipBonusCarrierC4"), skill="Caldari Carrier") + fit.modules.filteredItemBoost( + lambda mod: mod.item.requiresSkill("Shield Command") or mod.item.requiresSkill("Information Command"), + "warfareBuff3Value", src.getModifiedItemAttr("shipBonusCarrierC4"), skill="Caldari Carrier") + fit.modules.filteredItemBoost( + lambda mod: mod.item.requiresSkill("Shield Command") or mod.item.requiresSkill("Information Command"), + "warfareBuff4Value", src.getModifiedItemAttr("shipBonusCarrierC4"), skill="Caldari Carrier") + fit.modules.filteredItemBoost( + lambda mod: mod.item.requiresSkill("Shield Command") or mod.item.requiresSkill("Information Command"), + "warfareBuff1Value", src.getModifiedItemAttr("shipBonusCarrierC4"), skill="Caldari Carrier") diff --git a/eos/effects/shipbonuscarrierg4warfarelinksbonus.py b/eos/effects/shipbonuscarrierg4warfarelinksbonus.py index 673807782..05eb85c60 100644 --- a/eos/effects/shipbonuscarrierg4warfarelinksbonus.py +++ b/eos/effects/shipbonuscarrierg4warfarelinksbonus.py @@ -3,9 +3,21 @@ # Used by: # Ship: Thanatos type = "passive" + + def handler(fit, src, context): - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Skirmish Command") or mod.item.requiresSkill("Armored Command"), "warfareBuff2Value", src.getModifiedItemAttr("shipBonusCarrierG4"), skill="Gallente Carrier") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Skirmish Command") or mod.item.requiresSkill("Armored Command"), "warfareBuff3Value", src.getModifiedItemAttr("shipBonusCarrierG4"), skill="Gallente Carrier") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Skirmish Command") or mod.item.requiresSkill("Armored Command"), "warfareBuff4Value", src.getModifiedItemAttr("shipBonusCarrierG4"), skill="Gallente Carrier") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Skirmish Command") or mod.item.requiresSkill("Armored Command"), "buffDuration", src.getModifiedItemAttr("shipBonusCarrierG4"), skill="Gallente Carrier") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Skirmish Command") or mod.item.requiresSkill("Armored Command"), "warfareBuff1Value", src.getModifiedItemAttr("shipBonusCarrierG4"), skill="Gallente Carrier") + fit.modules.filteredItemBoost( + lambda mod: mod.item.requiresSkill("Skirmish Command") or mod.item.requiresSkill("Armored Command"), + "warfareBuff2Value", src.getModifiedItemAttr("shipBonusCarrierG4"), skill="Gallente Carrier") + fit.modules.filteredItemBoost( + lambda mod: mod.item.requiresSkill("Skirmish Command") or mod.item.requiresSkill("Armored Command"), + "warfareBuff3Value", src.getModifiedItemAttr("shipBonusCarrierG4"), skill="Gallente Carrier") + fit.modules.filteredItemBoost( + lambda mod: mod.item.requiresSkill("Skirmish Command") or mod.item.requiresSkill("Armored Command"), + "warfareBuff4Value", src.getModifiedItemAttr("shipBonusCarrierG4"), skill="Gallente Carrier") + fit.modules.filteredItemBoost( + lambda mod: mod.item.requiresSkill("Skirmish Command") or mod.item.requiresSkill("Armored Command"), + "buffDuration", src.getModifiedItemAttr("shipBonusCarrierG4"), skill="Gallente Carrier") + fit.modules.filteredItemBoost( + lambda mod: mod.item.requiresSkill("Skirmish Command") or mod.item.requiresSkill("Armored Command"), + "warfareBuff1Value", src.getModifiedItemAttr("shipBonusCarrierG4"), skill="Gallente Carrier") diff --git a/eos/effects/shipbonuscarrierm4warfarelinksbonus.py b/eos/effects/shipbonuscarrierm4warfarelinksbonus.py index 36fbbfa01..11592c9df 100644 --- a/eos/effects/shipbonuscarrierm4warfarelinksbonus.py +++ b/eos/effects/shipbonuscarrierm4warfarelinksbonus.py @@ -3,9 +3,21 @@ # Used by: # Ship: Nidhoggur type = "passive" + + def handler(fit, src, context): - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Skirmish Command") or mod.item.requiresSkill("Shield Command"), "warfareBuff4Value", src.getModifiedItemAttr("shipBonusCarrierM4"), skill="Minmatar Carrier") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Skirmish Command") or mod.item.requiresSkill("Shield Command"), "warfareBuff2Value", src.getModifiedItemAttr("shipBonusCarrierM4"), skill="Minmatar Carrier") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Skirmish Command") or mod.item.requiresSkill("Shield Command"), "warfareBuff3Value", src.getModifiedItemAttr("shipBonusCarrierM4"), skill="Minmatar Carrier") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Skirmish Command") or mod.item.requiresSkill("Shield Command"), "warfareBuff1Value", src.getModifiedItemAttr("shipBonusCarrierM4"), skill="Minmatar Carrier") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Skirmish Command") or mod.item.requiresSkill("Shield Command"), "buffDuration", src.getModifiedItemAttr("shipBonusCarrierM4"), skill="Minmatar Carrier") + fit.modules.filteredItemBoost( + lambda mod: mod.item.requiresSkill("Skirmish Command") or mod.item.requiresSkill("Shield Command"), + "warfareBuff4Value", src.getModifiedItemAttr("shipBonusCarrierM4"), skill="Minmatar Carrier") + fit.modules.filteredItemBoost( + lambda mod: mod.item.requiresSkill("Skirmish Command") or mod.item.requiresSkill("Shield Command"), + "warfareBuff2Value", src.getModifiedItemAttr("shipBonusCarrierM4"), skill="Minmatar Carrier") + fit.modules.filteredItemBoost( + lambda mod: mod.item.requiresSkill("Skirmish Command") or mod.item.requiresSkill("Shield Command"), + "warfareBuff3Value", src.getModifiedItemAttr("shipBonusCarrierM4"), skill="Minmatar Carrier") + fit.modules.filteredItemBoost( + lambda mod: mod.item.requiresSkill("Skirmish Command") or mod.item.requiresSkill("Shield Command"), + "warfareBuff1Value", src.getModifiedItemAttr("shipBonusCarrierM4"), skill="Minmatar Carrier") + fit.modules.filteredItemBoost( + lambda mod: mod.item.requiresSkill("Skirmish Command") or mod.item.requiresSkill("Shield Command"), + "buffDuration", src.getModifiedItemAttr("shipBonusCarrierM4"), skill="Minmatar Carrier") diff --git a/eos/effects/shipbonuscloakcpumc2.py b/eos/effects/shipbonuscloakcpumc2.py index 0f8c90e35..ac83921f0 100644 --- a/eos/effects/shipbonuscloakcpumc2.py +++ b/eos/effects/shipbonuscloakcpumc2.py @@ -3,5 +3,8 @@ # Used by: # Ship: Rabisu type = "passive" + + def handler(fit, src, context): - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Cloaking"), "cpu", src.getModifiedItemAttr("shipBonusMC2"), skill="Minmatar Cruiser") + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Cloaking"), "cpu", + src.getModifiedItemAttr("shipBonusMC2"), skill="Minmatar Cruiser") diff --git a/eos/effects/shipbonuscloakcpumf1.py b/eos/effects/shipbonuscloakcpumf1.py index 62a6d0aa7..fe201e1df 100644 --- a/eos/effects/shipbonuscloakcpumf1.py +++ b/eos/effects/shipbonuscloakcpumf1.py @@ -3,5 +3,8 @@ # Used by: # Ship: Caedes type = "passive" + + def handler(fit, src, context): - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Cloaking"), "cpu", src.getModifiedItemAttr("shipBonusMF"), skill="Minmatar Frigate") + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Cloaking"), "cpu", + src.getModifiedItemAttr("shipBonusMF"), skill="Minmatar Frigate") diff --git a/eos/effects/shipbonusdronehpdamageminingics4.py b/eos/effects/shipbonusdronehpdamageminingics4.py index c8d49571f..8b06c6dec 100644 --- a/eos/effects/shipbonusdronehpdamageminingics4.py +++ b/eos/effects/shipbonusdronehpdamageminingics4.py @@ -35,4 +35,3 @@ def handler(fit, src, context): src.getModifiedItemAttr("shipBonusICS4"), skill="Industrial Command Ships" ) - diff --git a/eos/effects/shipbonusdronehpdamageminingorecapital4.py b/eos/effects/shipbonusdronehpdamageminingorecapital4.py index 1b5ed13f5..8cd53cd81 100644 --- a/eos/effects/shipbonusdronehpdamageminingorecapital4.py +++ b/eos/effects/shipbonusdronehpdamageminingorecapital4.py @@ -35,4 +35,3 @@ def handler(fit, src, context): src.getModifiedItemAttr("shipBonusORECapital4"), skill="Capital Industrial Ships" ) - diff --git a/eos/effects/shipbonusdroneiceharvestingrole.py b/eos/effects/shipbonusdroneiceharvestingrole.py index 010fbea1b..b45241f9f 100644 --- a/eos/effects/shipbonusdroneiceharvestingrole.py +++ b/eos/effects/shipbonusdroneiceharvestingrole.py @@ -3,5 +3,8 @@ # Used by: # Ship: Orca type = "passive" + + def handler(fit, src, context): - fit.drones.filteredItemBoost(lambda mod: mod.item.requiresSkill("Ice Harvesting Drone Operation"), "duration", src.getModifiedItemAttr("roleBonusDroneIceHarvestingSpeed")) + fit.drones.filteredItemBoost(lambda mod: mod.item.requiresSkill("Ice Harvesting Drone Operation"), "duration", + src.getModifiedItemAttr("roleBonusDroneIceHarvestingSpeed")) diff --git a/eos/effects/shipbonusdronerepairmc1.py b/eos/effects/shipbonusdronerepairmc1.py index baaff71f5..77cc9a69d 100644 --- a/eos/effects/shipbonusdronerepairmc1.py +++ b/eos/effects/shipbonusdronerepairmc1.py @@ -3,7 +3,12 @@ # Used by: # Ship: Rabisu type = "passive" + + def handler(fit, src, context): - fit.drones.filteredItemBoost(lambda mod: mod.item.requiresSkill("Drones"), "shieldBonus", src.getModifiedItemAttr("shipBonusMC"), skill="Minmatar Cruiser") - fit.drones.filteredItemBoost(lambda mod: mod.item.requiresSkill("Drones"), "structureDamageAmount", src.getModifiedItemAttr("shipBonusMC"), skill="Minmatar Cruiser") - fit.drones.filteredItemBoost(lambda mod: mod.item.requiresSkill("Drones"), "armorDamageAmount", src.getModifiedItemAttr("shipBonusMC"), skill="Minmatar Cruiser") + fit.drones.filteredItemBoost(lambda mod: mod.item.requiresSkill("Drones"), "shieldBonus", + src.getModifiedItemAttr("shipBonusMC"), skill="Minmatar Cruiser") + fit.drones.filteredItemBoost(lambda mod: mod.item.requiresSkill("Drones"), "structureDamageAmount", + src.getModifiedItemAttr("shipBonusMC"), skill="Minmatar Cruiser") + fit.drones.filteredItemBoost(lambda mod: mod.item.requiresSkill("Drones"), "armorDamageAmount", + src.getModifiedItemAttr("shipBonusMC"), skill="Minmatar Cruiser") diff --git a/eos/effects/shipbonusforceauxiliarya4warfarelinksbonus.py b/eos/effects/shipbonusforceauxiliarya4warfarelinksbonus.py index 363c6bd96..824f57069 100644 --- a/eos/effects/shipbonusforceauxiliarya4warfarelinksbonus.py +++ b/eos/effects/shipbonusforceauxiliarya4warfarelinksbonus.py @@ -3,9 +3,21 @@ # Used by: # Ship: Apostle type = "passive" + + def handler(fit, src, context): - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Armored Command") or mod.item.requiresSkill("Information Command"), "warfareBuff4Value", src.getModifiedItemAttr("shipBonusForceAuxiliaryA4"), skill="Amarr Carrier") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Armored Command") or mod.item.requiresSkill("Information Command"), "warfareBuff3Value", src.getModifiedItemAttr("shipBonusForceAuxiliaryA4"), skill="Amarr Carrier") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Armored Command") or mod.item.requiresSkill("Information Command"), "warfareBuff1Value", src.getModifiedItemAttr("shipBonusForceAuxiliaryA4"), skill="Amarr Carrier") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Armored Command") or mod.item.requiresSkill("Information Command"), "buffDuration", src.getModifiedItemAttr("shipBonusForceAuxiliaryA4"), skill="Amarr Carrier") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Armored Command") or mod.item.requiresSkill("Information Command"), "warfareBuff2Value", src.getModifiedItemAttr("shipBonusForceAuxiliaryA4"), skill="Amarr Carrier") + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Armored Command") or + mod.item.requiresSkill("Information Command"), + "warfareBuff4Value", src.getModifiedItemAttr("shipBonusForceAuxiliaryA4"), skill="Amarr Carrier") + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Armored Command") or + mod.item.requiresSkill("Information Command"), + "warfareBuff3Value", src.getModifiedItemAttr("shipBonusForceAuxiliaryA4"), skill="Amarr Carrier") + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Armored Command") or + mod.item.requiresSkill("Information Command"), + "warfareBuff1Value", src.getModifiedItemAttr("shipBonusForceAuxiliaryA4"), skill="Amarr Carrier") + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Armored Command") or + mod.item.requiresSkill("Information Command"), + "buffDuration", src.getModifiedItemAttr("shipBonusForceAuxiliaryA4"), skill="Amarr Carrier") + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Armored Command") or + mod.item.requiresSkill("Information Command"), + "warfareBuff2Value", src.getModifiedItemAttr("shipBonusForceAuxiliaryA4"), skill="Amarr Carrier") diff --git a/eos/effects/shipbonusforceauxiliaryc4warfarelinksbonus.py b/eos/effects/shipbonusforceauxiliaryc4warfarelinksbonus.py index e2e6cd483..fda300de2 100644 --- a/eos/effects/shipbonusforceauxiliaryc4warfarelinksbonus.py +++ b/eos/effects/shipbonusforceauxiliaryc4warfarelinksbonus.py @@ -3,9 +3,21 @@ # Used by: # Ship: Minokawa type = "passive" + + def handler(fit, src, context): - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Shield Command") or mod.item.requiresSkill("Information Command"), "warfareBuff3Value", src.getModifiedItemAttr("shipBonusForceAuxiliaryC4"), skill="Caldari Carrier") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Shield Command") or mod.item.requiresSkill("Information Command"), "warfareBuff4Value", src.getModifiedItemAttr("shipBonusForceAuxiliaryC4"), skill="Caldari Carrier") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Shield Command") or mod.item.requiresSkill("Information Command"), "warfareBuff2Value", src.getModifiedItemAttr("shipBonusForceAuxiliaryC4"), skill="Caldari Carrier") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Shield Command") or mod.item.requiresSkill("Information Command"), "buffDuration", src.getModifiedItemAttr("shipBonusForceAuxiliaryC4"), skill="Caldari Carrier") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Shield Command") or mod.item.requiresSkill("Information Command"), "warfareBuff1Value", src.getModifiedItemAttr("shipBonusForceAuxiliaryC4"), skill="Caldari Carrier") + fit.modules.filteredItemBoost( + lambda mod: mod.item.requiresSkill("Shield Command") or mod.item.requiresSkill("Information Command"), + "warfareBuff3Value", src.getModifiedItemAttr("shipBonusForceAuxiliaryC4"), skill="Caldari Carrier") + fit.modules.filteredItemBoost( + lambda mod: mod.item.requiresSkill("Shield Command") or mod.item.requiresSkill("Information Command"), + "warfareBuff4Value", src.getModifiedItemAttr("shipBonusForceAuxiliaryC4"), skill="Caldari Carrier") + fit.modules.filteredItemBoost( + lambda mod: mod.item.requiresSkill("Shield Command") or mod.item.requiresSkill("Information Command"), + "warfareBuff2Value", src.getModifiedItemAttr("shipBonusForceAuxiliaryC4"), skill="Caldari Carrier") + fit.modules.filteredItemBoost( + lambda mod: mod.item.requiresSkill("Shield Command") or mod.item.requiresSkill("Information Command"), + "buffDuration", src.getModifiedItemAttr("shipBonusForceAuxiliaryC4"), skill="Caldari Carrier") + fit.modules.filteredItemBoost( + lambda mod: mod.item.requiresSkill("Shield Command") or mod.item.requiresSkill("Information Command"), + "warfareBuff1Value", src.getModifiedItemAttr("shipBonusForceAuxiliaryC4"), skill="Caldari Carrier") diff --git a/eos/effects/shipbonusforceauxiliaryg4warfarelinksbonus.py b/eos/effects/shipbonusforceauxiliaryg4warfarelinksbonus.py index 147ae8df0..63efca50e 100644 --- a/eos/effects/shipbonusforceauxiliaryg4warfarelinksbonus.py +++ b/eos/effects/shipbonusforceauxiliaryg4warfarelinksbonus.py @@ -3,9 +3,21 @@ # Used by: # Ship: Ninazu type = "passive" + + def handler(fit, src, context): - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Skirmish Command") or mod.item.requiresSkill("Armored Command"), "warfareBuff3Value", src.getModifiedItemAttr("shipBonusForceAuxiliaryG4"), skill="Gallente Carrier") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Skirmish Command") or mod.item.requiresSkill("Armored Command"), "warfareBuff4Value", src.getModifiedItemAttr("shipBonusForceAuxiliaryG4"), skill="Gallente Carrier") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Skirmish Command") or mod.item.requiresSkill("Armored Command"), "buffDuration", src.getModifiedItemAttr("shipBonusForceAuxiliaryG4"), skill="Gallente Carrier") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Skirmish Command") or mod.item.requiresSkill("Armored Command"), "warfareBuff2Value", src.getModifiedItemAttr("shipBonusForceAuxiliaryG4"), skill="Gallente Carrier") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Skirmish Command") or mod.item.requiresSkill("Armored Command"), "warfareBuff1Value", src.getModifiedItemAttr("shipBonusForceAuxiliaryG4"), skill="Gallente Carrier") + fit.modules.filteredItemBoost( + lambda mod: mod.item.requiresSkill("Skirmish Command") or mod.item.requiresSkill("Armored Command"), + "warfareBuff3Value", src.getModifiedItemAttr("shipBonusForceAuxiliaryG4"), skill="Gallente Carrier") + fit.modules.filteredItemBoost( + lambda mod: mod.item.requiresSkill("Skirmish Command") or mod.item.requiresSkill("Armored Command"), + "warfareBuff4Value", src.getModifiedItemAttr("shipBonusForceAuxiliaryG4"), skill="Gallente Carrier") + fit.modules.filteredItemBoost( + lambda mod: mod.item.requiresSkill("Skirmish Command") or mod.item.requiresSkill("Armored Command"), + "buffDuration", src.getModifiedItemAttr("shipBonusForceAuxiliaryG4"), skill="Gallente Carrier") + fit.modules.filteredItemBoost( + lambda mod: mod.item.requiresSkill("Skirmish Command") or mod.item.requiresSkill("Armored Command"), + "warfareBuff2Value", src.getModifiedItemAttr("shipBonusForceAuxiliaryG4"), skill="Gallente Carrier") + fit.modules.filteredItemBoost( + lambda mod: mod.item.requiresSkill("Skirmish Command") or mod.item.requiresSkill("Armored Command"), + "warfareBuff1Value", src.getModifiedItemAttr("shipBonusForceAuxiliaryG4"), skill="Gallente Carrier") diff --git a/eos/effects/shipbonusforceauxiliarym4warfarelinksbonus.py b/eos/effects/shipbonusforceauxiliarym4warfarelinksbonus.py index 0c476942a..df5b5181b 100644 --- a/eos/effects/shipbonusforceauxiliarym4warfarelinksbonus.py +++ b/eos/effects/shipbonusforceauxiliarym4warfarelinksbonus.py @@ -3,9 +3,21 @@ # Used by: # Ship: Lif type = "passive" + + def handler(fit, src, context): - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Skirmish Command") or mod.item.requiresSkill("Shield Command"), "warfareBuff3Value", src.getModifiedItemAttr("shipBonusForceAuxiliaryM4"), skill="Minmatar Carrier") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Skirmish Command") or mod.item.requiresSkill("Shield Command"), "warfareBuff2Value", src.getModifiedItemAttr("shipBonusForceAuxiliaryM4"), skill="Minmatar Carrier") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Skirmish Command") or mod.item.requiresSkill("Shield Command"), "warfareBuff1Value", src.getModifiedItemAttr("shipBonusForceAuxiliaryM4"), skill="Minmatar Carrier") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Skirmish Command") or mod.item.requiresSkill("Shield Command"), "buffDuration", src.getModifiedItemAttr("shipBonusForceAuxiliaryM4"), skill="Minmatar Carrier") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Skirmish Command") or mod.item.requiresSkill("Shield Command"), "warfareBuff4Value", src.getModifiedItemAttr("shipBonusForceAuxiliaryM4"), skill="Minmatar Carrier") + fit.modules.filteredItemBoost( + lambda mod: mod.item.requiresSkill("Skirmish Command") or mod.item.requiresSkill("Shield Command"), + "warfareBuff3Value", src.getModifiedItemAttr("shipBonusForceAuxiliaryM4"), skill="Minmatar Carrier") + fit.modules.filteredItemBoost( + lambda mod: mod.item.requiresSkill("Skirmish Command") or mod.item.requiresSkill("Shield Command"), + "warfareBuff2Value", src.getModifiedItemAttr("shipBonusForceAuxiliaryM4"), skill="Minmatar Carrier") + fit.modules.filteredItemBoost( + lambda mod: mod.item.requiresSkill("Skirmish Command") or mod.item.requiresSkill("Shield Command"), + "warfareBuff1Value", src.getModifiedItemAttr("shipBonusForceAuxiliaryM4"), skill="Minmatar Carrier") + fit.modules.filteredItemBoost( + lambda mod: mod.item.requiresSkill("Skirmish Command") or mod.item.requiresSkill("Shield Command"), + "buffDuration", src.getModifiedItemAttr("shipBonusForceAuxiliaryM4"), skill="Minmatar Carrier") + fit.modules.filteredItemBoost( + lambda mod: mod.item.requiresSkill("Skirmish Command") or mod.item.requiresSkill("Shield Command"), + "warfareBuff4Value", src.getModifiedItemAttr("shipBonusForceAuxiliaryM4"), skill="Minmatar Carrier") diff --git a/eos/effects/shipbonusminingdroneamountpercentrookie.py b/eos/effects/shipbonusminingdroneamountpercentrookie.py index ddffa24e9..bffbe6b81 100644 --- a/eos/effects/shipbonusminingdroneamountpercentrookie.py +++ b/eos/effects/shipbonusminingdroneamountpercentrookie.py @@ -8,6 +8,5 @@ type = "passive" def handler(fit, container, context): - level = container.level if "skill" in context else 1 fit.drones.filteredItemBoost(lambda drone: drone.item.group.name == "Mining Drone", "miningAmount", container.getModifiedItemAttr("rookieDroneBonus")) diff --git a/eos/effects/shipbonusnosoptimalfalloffac2.py b/eos/effects/shipbonusnosoptimalfalloffac2.py index f741681b5..355b6e22c 100644 --- a/eos/effects/shipbonusnosoptimalfalloffac2.py +++ b/eos/effects/shipbonusnosoptimalfalloffac2.py @@ -3,6 +3,10 @@ # Used by: # Ship: Rabisu type = "passive" + + def handler(fit, src, context): - fit.modules.filteredItemBoost(lambda mod: mod.item.group.name == "Energy Nosferatu", "falloffEffectiveness", src.getModifiedItemAttr("shipBonusAC2"), skill="Amarr Cruiser") - fit.modules.filteredItemBoost(lambda mod: mod.item.group.name == "Energy Nosferatu", "maxRange", src.getModifiedItemAttr("shipBonusAC2"), skill="Amarr Cruiser") + fit.modules.filteredItemBoost(lambda mod: mod.item.group.name == "Energy Nosferatu", "falloffEffectiveness", + src.getModifiedItemAttr("shipBonusAC2"), skill="Amarr Cruiser") + fit.modules.filteredItemBoost(lambda mod: mod.item.group.name == "Energy Nosferatu", "maxRange", + src.getModifiedItemAttr("shipBonusAC2"), skill="Amarr Cruiser") diff --git a/eos/effects/shipbonusrepairsystemsbonusatc2.py b/eos/effects/shipbonusrepairsystemsbonusatc2.py index b2f7bd1aa..c761ad921 100644 --- a/eos/effects/shipbonusrepairsystemsbonusatc2.py +++ b/eos/effects/shipbonusrepairsystemsbonusatc2.py @@ -1,10 +1,10 @@ -# shipBonusRepairSystemsBonusATC2 -# -# Used by: -# Ship: Vangel -type = "passive" - - -def handler(fit, ship, context): - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Repair Systems"), - "armorDamageAmount", ship.getModifiedItemAttr("shipBonusATC2")) +# shipBonusRepairSystemsBonusATC2 +# +# Used by: +# Ship: Vangel +type = "passive" + + +def handler(fit, ship, context): + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Repair Systems"), + "armorDamageAmount", ship.getModifiedItemAttr("shipBonusATC2")) diff --git a/eos/effects/shipbonussetfalloffaf2.py b/eos/effects/shipbonussetfalloffaf2.py index cf22eedbd..9c6f67d13 100644 --- a/eos/effects/shipbonussetfalloffaf2.py +++ b/eos/effects/shipbonussetfalloffaf2.py @@ -3,5 +3,8 @@ # Used by: # Ship: Caedes type = "passive" + + def handler(fit, src, context): - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Small Energy Turret"), "falloff", src.getModifiedItemAttr("shipBonus2AF"), skill="Amarr Frigate") + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Small Energy Turret"), "falloff", + src.getModifiedItemAttr("shipBonus2AF"), skill="Amarr Frigate") diff --git a/eos/effects/shipbonussupercarriera5warfarelinksbonus.py b/eos/effects/shipbonussupercarriera5warfarelinksbonus.py index e1d000525..fdc2f24bc 100644 --- a/eos/effects/shipbonussupercarriera5warfarelinksbonus.py +++ b/eos/effects/shipbonussupercarriera5warfarelinksbonus.py @@ -3,9 +3,21 @@ # Used by: # Ship: Aeon type = "passive" + + def handler(fit, src, context): - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Armored Command") or mod.item.requiresSkill("Information Command"), "warfareBuff4Value", src.getModifiedItemAttr("shipBonusSupercarrierA5"), skill="Amarr Carrier") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Armored Command") or mod.item.requiresSkill("Information Command"), "warfareBuff3Value", src.getModifiedItemAttr("shipBonusSupercarrierA5"), skill="Amarr Carrier") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Armored Command") or mod.item.requiresSkill("Information Command"), "buffDuration", src.getModifiedItemAttr("shipBonusSupercarrierA5"), skill="Amarr Carrier") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Armored Command") or mod.item.requiresSkill("Information Command"), "warfareBuff2Value", src.getModifiedItemAttr("shipBonusSupercarrierA5"), skill="Amarr Carrier") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Armored Command") or mod.item.requiresSkill("Information Command"), "warfareBuff1Value", src.getModifiedItemAttr("shipBonusSupercarrierA5"), skill="Amarr Carrier") + fit.modules.filteredItemBoost( + lambda mod: mod.item.requiresSkill("Armored Command") or mod.item.requiresSkill("Information Command"), + "warfareBuff4Value", src.getModifiedItemAttr("shipBonusSupercarrierA5"), skill="Amarr Carrier") + fit.modules.filteredItemBoost( + lambda mod: mod.item.requiresSkill("Armored Command") or mod.item.requiresSkill("Information Command"), + "warfareBuff3Value", src.getModifiedItemAttr("shipBonusSupercarrierA5"), skill="Amarr Carrier") + fit.modules.filteredItemBoost( + lambda mod: mod.item.requiresSkill("Armored Command") or mod.item.requiresSkill("Information Command"), + "buffDuration", src.getModifiedItemAttr("shipBonusSupercarrierA5"), skill="Amarr Carrier") + fit.modules.filteredItemBoost( + lambda mod: mod.item.requiresSkill("Armored Command") or mod.item.requiresSkill("Information Command"), + "warfareBuff2Value", src.getModifiedItemAttr("shipBonusSupercarrierA5"), skill="Amarr Carrier") + fit.modules.filteredItemBoost( + lambda mod: mod.item.requiresSkill("Armored Command") or mod.item.requiresSkill("Information Command"), + "warfareBuff1Value", src.getModifiedItemAttr("shipBonusSupercarrierA5"), skill="Amarr Carrier") diff --git a/eos/effects/shipbonussupercarrierc5warfarelinksbonus.py b/eos/effects/shipbonussupercarrierc5warfarelinksbonus.py index 768818b2d..5ff00b1ca 100644 --- a/eos/effects/shipbonussupercarrierc5warfarelinksbonus.py +++ b/eos/effects/shipbonussupercarrierc5warfarelinksbonus.py @@ -3,9 +3,21 @@ # Used by: # Ship: Wyvern type = "passive" + + def handler(fit, src, context): - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Shield Command") or mod.item.requiresSkill("Information Command"), "buffDuration", src.getModifiedItemAttr("shipBonusSupercarrierC5"), skill="Caldari Carrier") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Shield Command") or mod.item.requiresSkill("Information Command"), "warfareBuff2Value", src.getModifiedItemAttr("shipBonusSupercarrierC5"), skill="Caldari Carrier") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Shield Command") or mod.item.requiresSkill("Information Command"), "warfareBuff1Value", src.getModifiedItemAttr("shipBonusSupercarrierC5"), skill="Caldari Carrier") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Shield Command") or mod.item.requiresSkill("Information Command"), "warfareBuff4Value", src.getModifiedItemAttr("shipBonusSupercarrierC5"), skill="Caldari Carrier") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Shield Command") or mod.item.requiresSkill("Information Command"), "warfareBuff3Value", src.getModifiedItemAttr("shipBonusSupercarrierC5"), skill="Caldari Carrier") + fit.modules.filteredItemBoost( + lambda mod: mod.item.requiresSkill("Shield Command") or mod.item.requiresSkill("Information Command"), + "buffDuration", src.getModifiedItemAttr("shipBonusSupercarrierC5"), skill="Caldari Carrier") + fit.modules.filteredItemBoost( + lambda mod: mod.item.requiresSkill("Shield Command") or mod.item.requiresSkill("Information Command"), + "warfareBuff2Value", src.getModifiedItemAttr("shipBonusSupercarrierC5"), skill="Caldari Carrier") + fit.modules.filteredItemBoost( + lambda mod: mod.item.requiresSkill("Shield Command") or mod.item.requiresSkill("Information Command"), + "warfareBuff1Value", src.getModifiedItemAttr("shipBonusSupercarrierC5"), skill="Caldari Carrier") + fit.modules.filteredItemBoost( + lambda mod: mod.item.requiresSkill("Shield Command") or mod.item.requiresSkill("Information Command"), + "warfareBuff4Value", src.getModifiedItemAttr("shipBonusSupercarrierC5"), skill="Caldari Carrier") + fit.modules.filteredItemBoost( + lambda mod: mod.item.requiresSkill("Shield Command") or mod.item.requiresSkill("Information Command"), + "warfareBuff3Value", src.getModifiedItemAttr("shipBonusSupercarrierC5"), skill="Caldari Carrier") diff --git a/eos/effects/shipbonussupercarrierg5warfarelinksbonus.py b/eos/effects/shipbonussupercarrierg5warfarelinksbonus.py index 28dd6ab20..aa6d4d1e2 100644 --- a/eos/effects/shipbonussupercarrierg5warfarelinksbonus.py +++ b/eos/effects/shipbonussupercarrierg5warfarelinksbonus.py @@ -3,9 +3,21 @@ # Used by: # Ship: Nyx type = "passive" + + def handler(fit, src, context): - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Skirmish Command") or mod.item.requiresSkill("Armored Command"), "warfareBuff2Value", src.getModifiedItemAttr("shipBonusSupercarrierG5"), skill="Gallente Carrier") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Skirmish Command") or mod.item.requiresSkill("Armored Command"), "buffDuration", src.getModifiedItemAttr("shipBonusSupercarrierG5"), skill="Gallente Carrier") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Skirmish Command") or mod.item.requiresSkill("Armored Command"), "warfareBuff1Value", src.getModifiedItemAttr("shipBonusSupercarrierG5"), skill="Gallente Carrier") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Skirmish Command") or mod.item.requiresSkill("Armored Command"), "warfareBuff3Value", src.getModifiedItemAttr("shipBonusSupercarrierG5"), skill="Gallente Carrier") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Skirmish Command") or mod.item.requiresSkill("Armored Command"), "warfareBuff4Value", src.getModifiedItemAttr("shipBonusSupercarrierG5"), skill="Gallente Carrier") + fit.modules.filteredItemBoost( + lambda mod: mod.item.requiresSkill("Skirmish Command") or mod.item.requiresSkill("Armored Command"), + "warfareBuff2Value", src.getModifiedItemAttr("shipBonusSupercarrierG5"), skill="Gallente Carrier") + fit.modules.filteredItemBoost( + lambda mod: mod.item.requiresSkill("Skirmish Command") or mod.item.requiresSkill("Armored Command"), + "buffDuration", src.getModifiedItemAttr("shipBonusSupercarrierG5"), skill="Gallente Carrier") + fit.modules.filteredItemBoost( + lambda mod: mod.item.requiresSkill("Skirmish Command") or mod.item.requiresSkill("Armored Command"), + "warfareBuff1Value", src.getModifiedItemAttr("shipBonusSupercarrierG5"), skill="Gallente Carrier") + fit.modules.filteredItemBoost( + lambda mod: mod.item.requiresSkill("Skirmish Command") or mod.item.requiresSkill("Armored Command"), + "warfareBuff3Value", src.getModifiedItemAttr("shipBonusSupercarrierG5"), skill="Gallente Carrier") + fit.modules.filteredItemBoost( + lambda mod: mod.item.requiresSkill("Skirmish Command") or mod.item.requiresSkill("Armored Command"), + "warfareBuff4Value", src.getModifiedItemAttr("shipBonusSupercarrierG5"), skill="Gallente Carrier") diff --git a/eos/effects/shipbonussupercarrierm5warfarelinksbonus.py b/eos/effects/shipbonussupercarrierm5warfarelinksbonus.py index 53a6fafec..403b6c6a7 100644 --- a/eos/effects/shipbonussupercarrierm5warfarelinksbonus.py +++ b/eos/effects/shipbonussupercarrierm5warfarelinksbonus.py @@ -3,9 +3,21 @@ # Used by: # Ship: Hel type = "passive" + + def handler(fit, src, context): - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Skirmish Command") or mod.item.requiresSkill("Shield Command"), "warfareBuff4Value", src.getModifiedItemAttr("shipBonusSupercarrierM5"), skill="Minmatar Carrier") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Skirmish Command") or mod.item.requiresSkill("Shield Command"), "warfareBuff1Value", src.getModifiedItemAttr("shipBonusSupercarrierM5"), skill="Minmatar Carrier") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Skirmish Command") or mod.item.requiresSkill("Shield Command"), "warfareBuff3Value", src.getModifiedItemAttr("shipBonusSupercarrierM5"), skill="Minmatar Carrier") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Skirmish Command") or mod.item.requiresSkill("Shield Command"), "buffDuration", src.getModifiedItemAttr("shipBonusSupercarrierM5"), skill="Minmatar Carrier") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Skirmish Command") or mod.item.requiresSkill("Shield Command"), "warfareBuff2Value", src.getModifiedItemAttr("shipBonusSupercarrierM5"), skill="Minmatar Carrier") + fit.modules.filteredItemBoost( + lambda mod: mod.item.requiresSkill("Skirmish Command") or mod.item.requiresSkill("Shield Command"), + "warfareBuff4Value", src.getModifiedItemAttr("shipBonusSupercarrierM5"), skill="Minmatar Carrier") + fit.modules.filteredItemBoost( + lambda mod: mod.item.requiresSkill("Skirmish Command") or mod.item.requiresSkill("Shield Command"), + "warfareBuff1Value", src.getModifiedItemAttr("shipBonusSupercarrierM5"), skill="Minmatar Carrier") + fit.modules.filteredItemBoost( + lambda mod: mod.item.requiresSkill("Skirmish Command") or mod.item.requiresSkill("Shield Command"), + "warfareBuff3Value", src.getModifiedItemAttr("shipBonusSupercarrierM5"), skill="Minmatar Carrier") + fit.modules.filteredItemBoost( + lambda mod: mod.item.requiresSkill("Skirmish Command") or mod.item.requiresSkill("Shield Command"), + "buffDuration", src.getModifiedItemAttr("shipBonusSupercarrierM5"), skill="Minmatar Carrier") + fit.modules.filteredItemBoost( + lambda mod: mod.item.requiresSkill("Skirmish Command") or mod.item.requiresSkill("Shield Command"), + "warfareBuff2Value", src.getModifiedItemAttr("shipBonusSupercarrierM5"), skill="Minmatar Carrier") diff --git a/eos/effects/shipenergyneutralizertransferamountbonusab.py b/eos/effects/shipenergyneutralizertransferamountbonusab.py index c814b51b5..925a33c92 100644 --- a/eos/effects/shipenergyneutralizertransferamountbonusab.py +++ b/eos/effects/shipenergyneutralizertransferamountbonusab.py @@ -1,11 +1,11 @@ -# shipEnergyNeutralizerTransferAmountBonusAB -# -# Used by: -# Ship: Bhaalgorn -type = "passive" - - -def handler(fit, ship, context): - fit.modules.filteredItemBoost(lambda mod: mod.item.group.name == "Energy Neutralizer", - "energyNeutralizerAmount", ship.getModifiedItemAttr("shipBonusAB"), - skill="Amarr Battleship") +# shipEnergyNeutralizerTransferAmountBonusAB +# +# Used by: +# Ship: Bhaalgorn +type = "passive" + + +def handler(fit, ship, context): + fit.modules.filteredItemBoost(lambda mod: mod.item.group.name == "Energy Neutralizer", + "energyNeutralizerAmount", ship.getModifiedItemAttr("shipBonusAB"), + skill="Amarr Battleship") diff --git a/eos/effects/shipmodesmallmissiledamagepostdiv.py b/eos/effects/shipmodesmallmissiledamagepostdiv.py index 4db9c2f16..31df33c3a 100644 --- a/eos/effects/shipmodesmallmissiledamagepostdiv.py +++ b/eos/effects/shipmodesmallmissiledamagepostdiv.py @@ -4,11 +4,12 @@ # Module: Jackdaw Sharpshooter Mode type = "passive" + def handler(fit, module, context): types = ("thermal", "em", "explosive", "kinetic") for type in types: fit.modules.filteredChargeMultiply(lambda mod: mod.charge.requiresSkill("Rockets") or mod.charge.requiresSkill("Light Missiles"), "{}Damage".format(type), 1 / module.getModifiedItemAttr("modeDamageBonusPostDiv"), - stackingPenalties = True, + stackingPenalties=True, penaltyGroup="postDiv") diff --git a/eos/effects/remoteguidancedisruptfalloff.py b/eos/effects/shipmoduleguidancedisruptor.py similarity index 95% rename from eos/effects/remoteguidancedisruptfalloff.py rename to eos/effects/shipmoduleguidancedisruptor.py index 5dfbeba41..1588d52d7 100644 --- a/eos/effects/remoteguidancedisruptfalloff.py +++ b/eos/effects/shipmoduleguidancedisruptor.py @@ -1,4 +1,4 @@ -# remoteGuidanceDisruptFalloff +# shipModuleGuidanceDisruptor # # Used by: # Variations of module: Guidance Disruptor I (6 of 6) diff --git a/eos/effects/remotetrackingassistfalloff.py b/eos/effects/shipmoduleremotetrackingcomputer.py similarity index 96% rename from eos/effects/remotetrackingassistfalloff.py rename to eos/effects/shipmoduleremotetrackingcomputer.py index 411d4fc05..83a63b97f 100644 --- a/eos/effects/remotetrackingassistfalloff.py +++ b/eos/effects/shipmoduleremotetrackingcomputer.py @@ -1,4 +1,4 @@ -# remoteTrackingAssistFalloff +# shipModuleRemoteTrackingComputer # # Used by: # Modules from group: Remote Tracking Computer (8 of 8) diff --git a/eos/effects/remotetrackingdisruptfalloff.py b/eos/effects/shipmoduletrackingdisruptor.py similarity index 96% rename from eos/effects/remotetrackingdisruptfalloff.py rename to eos/effects/shipmoduletrackingdisruptor.py index 18fd3239e..e970dae3c 100644 --- a/eos/effects/remotetrackingdisruptfalloff.py +++ b/eos/effects/shipmoduletrackingdisruptor.py @@ -1,4 +1,4 @@ -# remoteTrackingDisruptFalloff +# shipModuleTrackingDisruptor # # Used by: # Variations of module: Tracking Disruptor I (6 of 6) diff --git a/eos/effects/shipscanresolutionbonusonline.py b/eos/effects/shipscanresolutionbonusonline.py index 1de730658..041b72b75 100644 --- a/eos/effects/shipscanresolutionbonusonline.py +++ b/eos/effects/shipscanresolutionbonusonline.py @@ -2,7 +2,6 @@ # # Used by: # Modules from group: Signal Amplifier (7 of 7) -# Module: QA Damage Module type = "passive" diff --git a/eos/effects/skillreactivearmorhardenercapneedbonus.py b/eos/effects/skillreactivearmorhardenercapneedbonus.py index 22b9f94c4..a8baab0f4 100644 --- a/eos/effects/skillreactivearmorhardenercapneedbonus.py +++ b/eos/effects/skillreactivearmorhardenercapneedbonus.py @@ -1,13 +1,13 @@ -# skillReactiveArmorHardenerCapNeedBonus -# -# Used by: -# Skill: Resistance Phasing -type = "passive" - - -def handler(fit, src, context): - lvl = src.level - fit.modules.filteredItemBoost(lambda mod: mod.item.group.name == "Armor Resistance Shift Hardener", "capacitorNeed", - src.getModifiedItemAttr("capNeedBonus") * lvl) - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Resistance Phasing"), "capacitorNeed", - src.getModifiedItemAttr("capNeedBonus") * lvl) +# skillReactiveArmorHardenerCapNeedBonus +# +# Used by: +# Skill: Resistance Phasing +type = "passive" + + +def handler(fit, src, context): + lvl = src.level + fit.modules.filteredItemBoost(lambda mod: mod.item.group.name == "Armor Resistance Shift Hardener", "capacitorNeed", + src.getModifiedItemAttr("capNeedBonus") * lvl) + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Resistance Phasing"), "capacitorNeed", + src.getModifiedItemAttr("capNeedBonus") * lvl) diff --git a/eos/effects/skillreactivearmorhardenerdurationbonus.py b/eos/effects/skillreactivearmorhardenerdurationbonus.py index 9ecabd1ca..00ced0d4f 100644 --- a/eos/effects/skillreactivearmorhardenerdurationbonus.py +++ b/eos/effects/skillreactivearmorhardenerdurationbonus.py @@ -1,13 +1,13 @@ -# skillReactiveArmorHardenerDurationBonus -# -# Used by: -# Skill: Resistance Phasing -type = "passive" - - -def handler(fit, src, context): - lvl = src.level - fit.modules.filteredItemBoost(lambda mod: mod.item.group.name == "Armor Resistance Shift Hardener", "duration", - src.getModifiedItemAttr("durationBonus") * lvl) - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Resistance Phasing"), "duration", - src.getModifiedItemAttr("durationBonus") * lvl) +# skillReactiveArmorHardenerDurationBonus +# +# Used by: +# Skill: Resistance Phasing +type = "passive" + + +def handler(fit, src, context): + lvl = src.level + fit.modules.filteredItemBoost(lambda mod: mod.item.group.name == "Armor Resistance Shift Hardener", "duration", + src.getModifiedItemAttr("durationBonus") * lvl) + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Resistance Phasing"), "duration", + src.getModifiedItemAttr("durationBonus") * lvl) diff --git a/eos/effects/skillremoteecmdurationbonus.py b/eos/effects/skillremoteecmdurationbonus.py index d2b13bdad..77c687f34 100644 --- a/eos/effects/skillremoteecmdurationbonus.py +++ b/eos/effects/skillremoteecmdurationbonus.py @@ -6,5 +6,28 @@ type = "passive" def handler(fit, skill, context): - fit.modules.filteredItemBoost(lambda mod: mod.item.group.name == "Burst Projectors", - "duration", skill.getModifiedItemAttr("projECMDurationBonus") * skill.level) + # We need to make sure that the attribute exists, otherwise we add attributes that don't belong. See #927 + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Burst Projector Operation") and + mod.item.getAttribute("duration"), + "duration", + skill.getModifiedItemAttr("projECMDurationBonus") * skill.level) + + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Burst Projector Operation") and + mod.item.getAttribute("durationECMJammerBurstProjector"), + "durationECMJammerBurstProjector", + skill.getModifiedItemAttr("projECMDurationBonus") * skill.level) + + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Burst Projector Operation") and + mod.item.getAttribute("durationTargetIlluminationBurstProjector"), + "durationTargetIlluminationBurstProjector", + skill.getModifiedItemAttr("projECMDurationBonus") * skill.level) + + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Burst Projector Operation") and + mod.item.getAttribute("durationSensorDampeningBurstProjector"), + "durationSensorDampeningBurstProjector", + skill.getModifiedItemAttr("projECMDurationBonus") * skill.level) + + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Burst Projector Operation") and + mod.item.getAttribute("durationWeaponDisruptionBurstProjector"), + "durationWeaponDisruptionBurstProjector", + skill.getModifiedItemAttr("projECMDurationBonus") * skill.level) diff --git a/eos/effects/skirmishcommanddurationbonus.py b/eos/effects/skirmishcommanddurationbonus.py index 5ab212a25..7b42437e1 100644 --- a/eos/effects/skirmishcommanddurationbonus.py +++ b/eos/effects/skirmishcommanddurationbonus.py @@ -3,6 +3,9 @@ # Used by: # Skill: Skirmish Command type = "passive" + + def handler(fit, src, context): lvl = src.level - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Skirmish Command"), "buffDuration", src.getModifiedItemAttr("durationBonus") * lvl) + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Skirmish Command"), "buffDuration", + src.getModifiedItemAttr("durationBonus") * lvl) diff --git a/eos/effects/skirmishcommandmindlink.py b/eos/effects/skirmishcommandmindlink.py index 4b5d09cad..9999d7f56 100644 --- a/eos/effects/skirmishcommandmindlink.py +++ b/eos/effects/skirmishcommandmindlink.py @@ -5,9 +5,16 @@ # Implant: Republic Fleet Command Mindlink # Implant: Skirmish Command Mindlink type = "passive" + + def handler(fit, src, context): - fit.modules.filteredChargeBoost(lambda mod: mod.item.requiresSkill("Skirmish Command"), "warfareBuff3Multiplier", src.getModifiedItemAttr("mindlinkBonus")) - fit.modules.filteredChargeBoost(lambda mod: mod.item.requiresSkill("Skirmish Command"), "warfareBuff4Multiplier", src.getModifiedItemAttr("mindlinkBonus")) - fit.modules.filteredChargeBoost(lambda mod: mod.item.requiresSkill("Skirmish Command"), "warfareBuff2Multiplier", src.getModifiedItemAttr("mindlinkBonus")) - fit.modules.filteredChargeBoost(lambda mod: mod.item.requiresSkill("Skirmish Command"), "warfareBuff1Multiplier", src.getModifiedItemAttr("mindlinkBonus")) - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Skirmish Command"), "buffDuration", src.getModifiedItemAttr("mindlinkBonus")) + fit.modules.filteredChargeBoost(lambda mod: mod.item.requiresSkill("Skirmish Command"), "warfareBuff3Multiplier", + src.getModifiedItemAttr("mindlinkBonus")) + fit.modules.filteredChargeBoost(lambda mod: mod.item.requiresSkill("Skirmish Command"), "warfareBuff4Multiplier", + src.getModifiedItemAttr("mindlinkBonus")) + fit.modules.filteredChargeBoost(lambda mod: mod.item.requiresSkill("Skirmish Command"), "warfareBuff2Multiplier", + src.getModifiedItemAttr("mindlinkBonus")) + fit.modules.filteredChargeBoost(lambda mod: mod.item.requiresSkill("Skirmish Command"), "warfareBuff1Multiplier", + src.getModifiedItemAttr("mindlinkBonus")) + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Skirmish Command"), "buffDuration", + src.getModifiedItemAttr("mindlinkBonus")) diff --git a/eos/effects/skirmishcommandstrengthbonus.py b/eos/effects/skirmishcommandstrengthbonus.py index f0af86f77..7963755df 100644 --- a/eos/effects/skirmishcommandstrengthbonus.py +++ b/eos/effects/skirmishcommandstrengthbonus.py @@ -3,9 +3,15 @@ # Used by: # Skill: Skirmish Command Specialist type = "passive" + + def handler(fit, src, context): lvl = src.level - fit.modules.filteredChargeBoost(lambda mod: mod.item.requiresSkill("Skirmish Command"), "warfareBuff3Multiplier", src.getModifiedItemAttr("commandStrengthBonus") * lvl) - fit.modules.filteredChargeBoost(lambda mod: mod.item.requiresSkill("Skirmish Command"), "warfareBuff4Multiplier", src.getModifiedItemAttr("commandStrengthBonus") * lvl) - fit.modules.filteredChargeBoost(lambda mod: mod.item.requiresSkill("Skirmish Command"), "warfareBuff1Multiplier", src.getModifiedItemAttr("commandStrengthBonus") * lvl) - fit.modules.filteredChargeBoost(lambda mod: mod.item.requiresSkill("Skirmish Command"), "warfareBuff2Multiplier", src.getModifiedItemAttr("commandStrengthBonus") * lvl) + fit.modules.filteredChargeBoost(lambda mod: mod.item.requiresSkill("Skirmish Command"), "warfareBuff3Multiplier", + src.getModifiedItemAttr("commandStrengthBonus") * lvl) + fit.modules.filteredChargeBoost(lambda mod: mod.item.requiresSkill("Skirmish Command"), "warfareBuff4Multiplier", + src.getModifiedItemAttr("commandStrengthBonus") * lvl) + fit.modules.filteredChargeBoost(lambda mod: mod.item.requiresSkill("Skirmish Command"), "warfareBuff1Multiplier", + src.getModifiedItemAttr("commandStrengthBonus") * lvl) + fit.modules.filteredChargeBoost(lambda mod: mod.item.requiresSkill("Skirmish Command"), "warfareBuff2Multiplier", + src.getModifiedItemAttr("commandStrengthBonus") * lvl) diff --git a/eos/effects/spatialphenomenagenerationdurationbonus.py b/eos/effects/spatialphenomenagenerationdurationbonus.py index 846fb6040..b127ae30d 100644 --- a/eos/effects/spatialphenomenagenerationdurationbonus.py +++ b/eos/effects/spatialphenomenagenerationdurationbonus.py @@ -3,6 +3,9 @@ # Used by: # Skill: Spatial Phenomena Generation type = "passive" + + def handler(fit, src, context): lvl = src.level - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Spatial Phenomena Generation"), "buffDuration", src.getModifiedItemAttr("durationBonus") * lvl) + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Spatial Phenomena Generation"), "buffDuration", + src.getModifiedItemAttr("durationBonus") * lvl) diff --git a/eos/effects/standardmissilesskillboostmissilevelocitybonus.py b/eos/effects/standardmissilesskillboostmissilevelocitybonus.py deleted file mode 100644 index f65a5bc34..000000000 --- a/eos/effects/standardmissilesskillboostmissilevelocitybonus.py +++ /dev/null @@ -1,10 +0,0 @@ -# standardMissilesSkillBoostMissileVelocityBonus -# -# Used by: -# Skill: Defender Missiles -type = "passive" - - -def handler(fit, skill, context): - fit.modules.filteredChargeBoost(lambda mod: mod.charge.requiresSkill("Defender Missiles"), - "maxVelocity", skill.getModifiedItemAttr("missileVelocityBonus") * skill.level) diff --git a/eos/effects/structuralanalysiseffect.py b/eos/effects/structuralanalysiseffect.py index de26cb71b..b94832ca0 100644 --- a/eos/effects/structuralanalysiseffect.py +++ b/eos/effects/structuralanalysiseffect.py @@ -3,7 +3,6 @@ # Used by: # Implants named like: Inherent Implants 'Noble' Repair Proficiency RP (6 of 6) # Modules named like: Auxiliary Nano Pump (8 of 8) -# Modules named like: QA Multiship Module Players (4 of 4) # Implant: Imperial Navy Modified 'Noble' Implant type = "passive" diff --git a/eos/effects/structureenergyneutralizerfalloff.py b/eos/effects/structureenergyneutralizerfalloff.py index 673630a6b..02ee9b4f8 100644 --- a/eos/effects/structureenergyneutralizerfalloff.py +++ b/eos/effects/structureenergyneutralizerfalloff.py @@ -1,13 +1,13 @@ # Not used by any item -from eos.types import State +from eos.saveddata.module import State type = "active", "projected" def handler(fit, container, context): amount = 0 - if "projected" in context and ((hasattr(container, "state") - and container.state >= State.ACTIVE) or hasattr(container, "amountActive")): - amount = container.getModifiedItemAttr("energyNeutralizerAmount") - time = container.getModifiedItemAttr("duration") - fit.addDrain(container, time, amount, 0) + if "projected" in context: + if (hasattr(container, "state") and container.state >= State.ACTIVE) or hasattr(container, "amountActive"): + amount = container.getModifiedItemAttr("energyNeutralizerAmount") + time = container.getModifiedItemAttr("duration") + fit.addDrain(container, time, amount, 0) diff --git a/eos/effects/structurehpmultiply.py b/eos/effects/structurehpmultiply.py index 33fc44bf6..0b7618e97 100644 --- a/eos/effects/structurehpmultiply.py +++ b/eos/effects/structurehpmultiply.py @@ -3,7 +3,6 @@ # Used by: # Modules from group: Nanofiber Internal Structure (7 of 7) # Modules from group: Reinforced Bulkhead (8 of 8) -# Modules named like: QA Multiship Module Players (4 of 4) type = "passive" diff --git a/eos/effects/structuremoduleeffectstasiswebifier.py b/eos/effects/structuremoduleeffectstasiswebifier.py index dc4b6f046..0aa907fc6 100644 --- a/eos/effects/structuremoduleeffectstasiswebifier.py +++ b/eos/effects/structuremoduleeffectstasiswebifier.py @@ -3,6 +3,7 @@ type = "active", "projected" def handler(fit, module, context): - if "projected" not in context: return + if "projected" not in context: + return fit.ship.boostItemAttr("maxVelocity", module.getModifiedItemAttr("speedFactor"), stackingPenalties=True, remoteResists=True) diff --git a/eos/effects/structurerigmaxtargets.py b/eos/effects/structurerigmaxtargets.py index d7b5ecfe5..47e2ce5d2 100644 --- a/eos/effects/structurerigmaxtargets.py +++ b/eos/effects/structurerigmaxtargets.py @@ -3,4 +3,4 @@ type = "passive" def handler(fit, src, context): - fit.ship.increaseItemAttr("maxLockedTargets", src.getModifiedItemAttr("structureRigMaxTargetBonus")) + fit.extraAttributes.increase("maxTargetsLockedFromSkills", src.getModifiedItemAttr("structureRigMaxTargetBonus")) diff --git a/eos/effects/structurewarpscrambleblockmwdwithnpceffect.py b/eos/effects/structurewarpscrambleblockmwdwithnpceffect.py index 4bf33ce41..4103b3f42 100644 --- a/eos/effects/structurewarpscrambleblockmwdwithnpceffect.py +++ b/eos/effects/structurewarpscrambleblockmwdwithnpceffect.py @@ -1,5 +1,5 @@ # Not used by any item -from eos.types import State +from eos.saveddata.module import State # Not used by any item runTime = "early" diff --git a/eos/effects/subsystembonusamarrdefensive2remotearmorrepairamount.py b/eos/effects/subsystembonusamarrdefensive2remotearmorrepairamount.py index 9ff4f9d42..5805bbd60 100644 --- a/eos/effects/subsystembonusamarrdefensive2remotearmorrepairamount.py +++ b/eos/effects/subsystembonusamarrdefensive2remotearmorrepairamount.py @@ -4,6 +4,8 @@ # Subsystem: Legion Defensive - Adaptive Augmenter type = "passive" runTime = "early" + + def handler(fit, module, context): fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Remote Armor Repair Systems"), "armorDamageAmount", module.getModifiedItemAttr("subsystemBonusAmarrDefensive2"), diff --git a/eos/effects/subsystembonusamarrdefensivearmoredwarfare.py b/eos/effects/subsystembonusamarrdefensivearmoredwarfare.py index 4a045cd49..75bcf296d 100644 --- a/eos/effects/subsystembonusamarrdefensivearmoredwarfare.py +++ b/eos/effects/subsystembonusamarrdefensivearmoredwarfare.py @@ -3,9 +3,21 @@ # Used by: # Subsystem: Legion Defensive - Warfare Processor type = "passive" + + def handler(fit, src, context): - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Armored Command"), "warfareBuff3Value", src.getModifiedItemAttr("subsystemBonusAmarrDefensive"), skill="Amarr Defensive Systems") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Armored Command"), "warfareBuff2Value", src.getModifiedItemAttr("subsystemBonusAmarrDefensive"), skill="Amarr Defensive Systems") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Armored Command"), "warfareBuff4Value", src.getModifiedItemAttr("subsystemBonusAmarrDefensive"), skill="Amarr Defensive Systems") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Armored Command"), "buffDuration", src.getModifiedItemAttr("subsystemBonusAmarrDefensive"), skill="Amarr Defensive Systems") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Armored Command"), "warfareBuff1Value", src.getModifiedItemAttr("subsystemBonusAmarrDefensive"), skill="Amarr Defensive Systems") + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Armored Command"), "warfareBuff3Value", + src.getModifiedItemAttr("subsystemBonusAmarrDefensive"), + skill="Amarr Defensive Systems") + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Armored Command"), "warfareBuff2Value", + src.getModifiedItemAttr("subsystemBonusAmarrDefensive"), + skill="Amarr Defensive Systems") + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Armored Command"), "warfareBuff4Value", + src.getModifiedItemAttr("subsystemBonusAmarrDefensive"), + skill="Amarr Defensive Systems") + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Armored Command"), "buffDuration", + src.getModifiedItemAttr("subsystemBonusAmarrDefensive"), + skill="Amarr Defensive Systems") + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Armored Command"), "warfareBuff1Value", + src.getModifiedItemAttr("subsystemBonusAmarrDefensive"), + skill="Amarr Defensive Systems") diff --git a/eos/effects/subsystembonusamarrdefensivearmorrepairamount.py b/eos/effects/subsystembonusamarrdefensivearmorrepairamount.py index 27bb5924b..4928f9906 100644 --- a/eos/effects/subsystembonusamarrdefensivearmorrepairamount.py +++ b/eos/effects/subsystembonusamarrdefensivearmorrepairamount.py @@ -1,11 +1,11 @@ -# subsystemBonusAmarrDefensiveArmorRepairAmount -# -# Used by: -# Subsystem: Legion Defensive - Nanobot Injector -type = "passive" - - -def handler(fit, module, context): - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Repair Systems"), - "armorDamageAmount", module.getModifiedItemAttr("subsystemBonusAmarrDefensive"), - skill="Amarr Defensive Systems") +# subsystemBonusAmarrDefensiveArmorRepairAmount +# +# Used by: +# Subsystem: Legion Defensive - Nanobot Injector +type = "passive" + + +def handler(fit, module, context): + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Repair Systems"), + "armorDamageAmount", module.getModifiedItemAttr("subsystemBonusAmarrDefensive"), + skill="Amarr Defensive Systems") diff --git a/eos/effects/subsystembonusamarrdefensiveinformationwarfare.py b/eos/effects/subsystembonusamarrdefensiveinformationwarfare.py index 2f3b96474..1d5b476b7 100644 --- a/eos/effects/subsystembonusamarrdefensiveinformationwarfare.py +++ b/eos/effects/subsystembonusamarrdefensiveinformationwarfare.py @@ -3,9 +3,21 @@ # Used by: # Subsystem: Legion Defensive - Warfare Processor type = "passive" + + def handler(fit, src, context): - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Information Command"), "warfareBuff1Value", src.getModifiedItemAttr("subsystemBonusAmarrDefensive"), skill="Amarr Defensive Systems") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Information Command"), "warfareBuff4Value", src.getModifiedItemAttr("subsystemBonusAmarrDefensive"), skill="Amarr Defensive Systems") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Information Command"), "warfareBuff3Value", src.getModifiedItemAttr("subsystemBonusAmarrDefensive"), skill="Amarr Defensive Systems") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Information Command"), "warfareBuff2Value", src.getModifiedItemAttr("subsystemBonusAmarrDefensive"), skill="Amarr Defensive Systems") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Information Command"), "buffDuration", src.getModifiedItemAttr("subsystemBonusAmarrDefensive"), skill="Amarr Defensive Systems") + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Information Command"), "warfareBuff1Value", + src.getModifiedItemAttr("subsystemBonusAmarrDefensive"), + skill="Amarr Defensive Systems") + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Information Command"), "warfareBuff4Value", + src.getModifiedItemAttr("subsystemBonusAmarrDefensive"), + skill="Amarr Defensive Systems") + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Information Command"), "warfareBuff3Value", + src.getModifiedItemAttr("subsystemBonusAmarrDefensive"), + skill="Amarr Defensive Systems") + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Information Command"), "warfareBuff2Value", + src.getModifiedItemAttr("subsystemBonusAmarrDefensive"), + skill="Amarr Defensive Systems") + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Information Command"), "buffDuration", + src.getModifiedItemAttr("subsystemBonusAmarrDefensive"), + skill="Amarr Defensive Systems") diff --git a/eos/effects/subsystembonusamarrdefensiveinformationwarfarehidden.py b/eos/effects/subsystembonusamarrdefensiveinformationwarfarehidden.py index 9dfb9ad19..7dd70fefa 100644 --- a/eos/effects/subsystembonusamarrdefensiveinformationwarfarehidden.py +++ b/eos/effects/subsystembonusamarrdefensiveinformationwarfarehidden.py @@ -1,5 +1,7 @@ # Not used by any item type = "passive" + + def handler(fit, module, context): fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Information Command Specialist"), "commandBonusHidden", module.getModifiedItemAttr("subsystemBonusAmarrDefensive"), skill="Amarr Defensive Systems") diff --git a/eos/effects/subsystembonusamarrdefensiveskirmishwarfare.py b/eos/effects/subsystembonusamarrdefensiveskirmishwarfare.py index c767405fb..141f65cef 100644 --- a/eos/effects/subsystembonusamarrdefensiveskirmishwarfare.py +++ b/eos/effects/subsystembonusamarrdefensiveskirmishwarfare.py @@ -3,9 +3,21 @@ # Used by: # Subsystem: Legion Defensive - Warfare Processor type = "passive" + + def handler(fit, src, context): - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Skirmish Command"), "warfareBuff4Value", src.getModifiedItemAttr("subsystemBonusAmarrDefensive"), skill="Amarr Defensive Systems") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Skirmish Command"), "warfareBuff1Value", src.getModifiedItemAttr("subsystemBonusAmarrDefensive"), skill="Amarr Defensive Systems") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Skirmish Command"), "buffDuration", src.getModifiedItemAttr("subsystemBonusAmarrDefensive"), skill="Amarr Defensive Systems") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Skirmish Command"), "warfareBuff2Value", src.getModifiedItemAttr("subsystemBonusAmarrDefensive"), skill="Amarr Defensive Systems") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Skirmish Command"), "warfareBuff3Value", src.getModifiedItemAttr("subsystemBonusAmarrDefensive"), skill="Amarr Defensive Systems") + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Skirmish Command"), "warfareBuff4Value", + src.getModifiedItemAttr("subsystemBonusAmarrDefensive"), + skill="Amarr Defensive Systems") + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Skirmish Command"), "warfareBuff1Value", + src.getModifiedItemAttr("subsystemBonusAmarrDefensive"), + skill="Amarr Defensive Systems") + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Skirmish Command"), "buffDuration", + src.getModifiedItemAttr("subsystemBonusAmarrDefensive"), + skill="Amarr Defensive Systems") + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Skirmish Command"), "warfareBuff2Value", + src.getModifiedItemAttr("subsystemBonusAmarrDefensive"), + skill="Amarr Defensive Systems") + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Skirmish Command"), "warfareBuff3Value", + src.getModifiedItemAttr("subsystemBonusAmarrDefensive"), + skill="Amarr Defensive Systems") diff --git a/eos/effects/subsystembonuscaldaridefensive2remoteshieldtransporteramount.py b/eos/effects/subsystembonuscaldaridefensive2remoteshieldtransporteramount.py index 1f3b20c03..4478365ef 100644 --- a/eos/effects/subsystembonuscaldaridefensive2remoteshieldtransporteramount.py +++ b/eos/effects/subsystembonuscaldaridefensive2remoteshieldtransporteramount.py @@ -4,6 +4,8 @@ # Subsystem: Tengu Defensive - Adaptive Shielding type = "passive" runTime = "early" + + def handler(fit, module, context): fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Shield Emission Systems"), "shieldBonus", module.getModifiedItemAttr("subsystemBonusCaldariDefensive2"), diff --git a/eos/effects/subsystembonuscaldaridefensiveinformationwarfare.py b/eos/effects/subsystembonuscaldaridefensiveinformationwarfare.py index c06646196..aefcd0b2d 100644 --- a/eos/effects/subsystembonuscaldaridefensiveinformationwarfare.py +++ b/eos/effects/subsystembonuscaldaridefensiveinformationwarfare.py @@ -3,9 +3,21 @@ # Used by: # Subsystem: Tengu Defensive - Warfare Processor type = "passive" + + def handler(fit, src, context): - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Information Command"), "buffDuration", src.getModifiedItemAttr("subsystemBonusCaldariDefensive"), skill="Caldari Defensive Systems") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Information Command"), "warfareBuff3Value", src.getModifiedItemAttr("subsystemBonusCaldariDefensive"), skill="Caldari Defensive Systems") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Information Command"), "warfareBuff1Value", src.getModifiedItemAttr("subsystemBonusCaldariDefensive"), skill="Caldari Defensive Systems") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Information Command"), "warfareBuff2Value", src.getModifiedItemAttr("subsystemBonusCaldariDefensive"), skill="Caldari Defensive Systems") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Information Command"), "warfareBuff4Value", src.getModifiedItemAttr("subsystemBonusCaldariDefensive"), skill="Caldari Defensive Systems") + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Information Command"), "buffDuration", + src.getModifiedItemAttr("subsystemBonusCaldariDefensive"), + skill="Caldari Defensive Systems") + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Information Command"), "warfareBuff3Value", + src.getModifiedItemAttr("subsystemBonusCaldariDefensive"), + skill="Caldari Defensive Systems") + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Information Command"), "warfareBuff1Value", + src.getModifiedItemAttr("subsystemBonusCaldariDefensive"), + skill="Caldari Defensive Systems") + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Information Command"), "warfareBuff2Value", + src.getModifiedItemAttr("subsystemBonusCaldariDefensive"), + skill="Caldari Defensive Systems") + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Information Command"), "warfareBuff4Value", + src.getModifiedItemAttr("subsystemBonusCaldariDefensive"), + skill="Caldari Defensive Systems") diff --git a/eos/effects/subsystembonuscaldaridefensiveinformationwarfarehidden.py b/eos/effects/subsystembonuscaldaridefensiveinformationwarfarehidden.py index f01b7efb7..0cf7a3ef8 100644 --- a/eos/effects/subsystembonuscaldaridefensiveinformationwarfarehidden.py +++ b/eos/effects/subsystembonuscaldaridefensiveinformationwarfarehidden.py @@ -1,5 +1,7 @@ # Not used by any item type = "passive" + + def handler(fit, module, context): fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Information Command Specialist"), "commandBonusHidden", module.getModifiedItemAttr("subsystemBonusCaldariDefensive"), skill="Caldari Defensive Systems") diff --git a/eos/effects/subsystembonuscaldaridefensivesiegewarfare.py b/eos/effects/subsystembonuscaldaridefensivesiegewarfare.py index 9a3de25c4..2869a8227 100644 --- a/eos/effects/subsystembonuscaldaridefensivesiegewarfare.py +++ b/eos/effects/subsystembonuscaldaridefensivesiegewarfare.py @@ -3,9 +3,21 @@ # Used by: # Subsystem: Tengu Defensive - Warfare Processor type = "passive" + + def handler(fit, src, context): - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Shield Command"), "warfareBuff2Value", src.getModifiedItemAttr("subsystemBonusCaldariDefensive"), skill="Caldari Defensive Systems") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Shield Command"), "warfareBuff1Value", src.getModifiedItemAttr("subsystemBonusCaldariDefensive"), skill="Caldari Defensive Systems") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Shield Command"), "warfareBuff3Value", src.getModifiedItemAttr("subsystemBonusCaldariDefensive"), skill="Caldari Defensive Systems") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Shield Command"), "buffDuration", src.getModifiedItemAttr("subsystemBonusCaldariDefensive"), skill="Caldari Defensive Systems") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Shield Command"), "warfareBuff4Value", src.getModifiedItemAttr("subsystemBonusCaldariDefensive"), skill="Caldari Defensive Systems") + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Shield Command"), "warfareBuff2Value", + src.getModifiedItemAttr("subsystemBonusCaldariDefensive"), + skill="Caldari Defensive Systems") + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Shield Command"), "warfareBuff1Value", + src.getModifiedItemAttr("subsystemBonusCaldariDefensive"), + skill="Caldari Defensive Systems") + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Shield Command"), "warfareBuff3Value", + src.getModifiedItemAttr("subsystemBonusCaldariDefensive"), + skill="Caldari Defensive Systems") + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Shield Command"), "buffDuration", + src.getModifiedItemAttr("subsystemBonusCaldariDefensive"), + skill="Caldari Defensive Systems") + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Shield Command"), "warfareBuff4Value", + src.getModifiedItemAttr("subsystemBonusCaldariDefensive"), + skill="Caldari Defensive Systems") diff --git a/eos/effects/subsystembonuscaldaridefensiveskirmishwarfare.py b/eos/effects/subsystembonuscaldaridefensiveskirmishwarfare.py index 21a1a2ad5..54778136c 100644 --- a/eos/effects/subsystembonuscaldaridefensiveskirmishwarfare.py +++ b/eos/effects/subsystembonuscaldaridefensiveskirmishwarfare.py @@ -3,9 +3,21 @@ # Used by: # Subsystem: Tengu Defensive - Warfare Processor type = "passive" + + def handler(fit, src, context): - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Skirmish Command"), "buffDuration", src.getModifiedItemAttr("subsystemBonusCaldariDefensive"), skill="Caldari Defensive Systems") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Skirmish Command"), "warfareBuff1Value", src.getModifiedItemAttr("subsystemBonusCaldariDefensive"), skill="Caldari Defensive Systems") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Skirmish Command"), "warfareBuff4Value", src.getModifiedItemAttr("subsystemBonusCaldariDefensive"), skill="Caldari Defensive Systems") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Skirmish Command"), "warfareBuff2Value", src.getModifiedItemAttr("subsystemBonusCaldariDefensive"), skill="Caldari Defensive Systems") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Skirmish Command"), "warfareBuff3Value", src.getModifiedItemAttr("subsystemBonusCaldariDefensive"), skill="Caldari Defensive Systems") + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Skirmish Command"), "buffDuration", + src.getModifiedItemAttr("subsystemBonusCaldariDefensive"), + skill="Caldari Defensive Systems") + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Skirmish Command"), "warfareBuff1Value", + src.getModifiedItemAttr("subsystemBonusCaldariDefensive"), + skill="Caldari Defensive Systems") + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Skirmish Command"), "warfareBuff4Value", + src.getModifiedItemAttr("subsystemBonusCaldariDefensive"), + skill="Caldari Defensive Systems") + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Skirmish Command"), "warfareBuff2Value", + src.getModifiedItemAttr("subsystemBonusCaldariDefensive"), + skill="Caldari Defensive Systems") + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Skirmish Command"), "warfareBuff3Value", + src.getModifiedItemAttr("subsystemBonusCaldariDefensive"), + skill="Caldari Defensive Systems") diff --git a/eos/effects/subsystembonusgallentedefensive2remotearmorrepairamount.py b/eos/effects/subsystembonusgallentedefensive2remotearmorrepairamount.py index f18b60266..f9e7e5039 100644 --- a/eos/effects/subsystembonusgallentedefensive2remotearmorrepairamount.py +++ b/eos/effects/subsystembonusgallentedefensive2remotearmorrepairamount.py @@ -4,6 +4,8 @@ # Subsystem: Proteus Defensive - Adaptive Augmenter type = "passive" runTime = "early" + + def handler(fit, module, context): fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Remote Armor Repair Systems"), "armorDamageAmount", module.getModifiedItemAttr("subsystemBonusGallenteDefensive2"), diff --git a/eos/effects/subsystembonusgallentedefensivearmoredwarfare.py b/eos/effects/subsystembonusgallentedefensivearmoredwarfare.py index 5d9369857..d66f4fc32 100644 --- a/eos/effects/subsystembonusgallentedefensivearmoredwarfare.py +++ b/eos/effects/subsystembonusgallentedefensivearmoredwarfare.py @@ -3,9 +3,21 @@ # Used by: # Subsystem: Proteus Defensive - Warfare Processor type = "passive" + + def handler(fit, src, context): - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Armored Command"), "buffDuration", src.getModifiedItemAttr("subsystemBonusGallenteDefensive"), skill="Gallente Defensive Systems") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Armored Command"), "warfareBuff3Value", src.getModifiedItemAttr("subsystemBonusGallenteDefensive"), skill="Gallente Defensive Systems") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Armored Command"), "warfareBuff4Value", src.getModifiedItemAttr("subsystemBonusGallenteDefensive"), skill="Gallente Defensive Systems") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Armored Command"), "warfareBuff2Value", src.getModifiedItemAttr("subsystemBonusGallenteDefensive"), skill="Gallente Defensive Systems") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Armored Command"), "warfareBuff1Value", src.getModifiedItemAttr("subsystemBonusGallenteDefensive"), skill="Gallente Defensive Systems") + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Armored Command"), "buffDuration", + src.getModifiedItemAttr("subsystemBonusGallenteDefensive"), + skill="Gallente Defensive Systems") + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Armored Command"), "warfareBuff3Value", + src.getModifiedItemAttr("subsystemBonusGallenteDefensive"), + skill="Gallente Defensive Systems") + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Armored Command"), "warfareBuff4Value", + src.getModifiedItemAttr("subsystemBonusGallenteDefensive"), + skill="Gallente Defensive Systems") + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Armored Command"), "warfareBuff2Value", + src.getModifiedItemAttr("subsystemBonusGallenteDefensive"), + skill="Gallente Defensive Systems") + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Armored Command"), "warfareBuff1Value", + src.getModifiedItemAttr("subsystemBonusGallenteDefensive"), + skill="Gallente Defensive Systems") diff --git a/eos/effects/subsystembonusgallentedefensivearmorrepairamount.py b/eos/effects/subsystembonusgallentedefensivearmorrepairamount.py index 5b17e2461..32dc79789 100644 --- a/eos/effects/subsystembonusgallentedefensivearmorrepairamount.py +++ b/eos/effects/subsystembonusgallentedefensivearmorrepairamount.py @@ -1,11 +1,11 @@ -# subsystemBonusGallenteDefensiveArmorRepairAmount -# -# Used by: -# Subsystem: Proteus Defensive - Nanobot Injector -type = "passive" - - -def handler(fit, module, context): - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Repair Systems"), - "armorDamageAmount", module.getModifiedItemAttr("subsystemBonusGallenteDefensive"), - skill="Gallente Defensive Systems") +# subsystemBonusGallenteDefensiveArmorRepairAmount +# +# Used by: +# Subsystem: Proteus Defensive - Nanobot Injector +type = "passive" + + +def handler(fit, module, context): + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Repair Systems"), + "armorDamageAmount", module.getModifiedItemAttr("subsystemBonusGallenteDefensive"), + skill="Gallente Defensive Systems") diff --git a/eos/effects/subsystembonusgallentedefensiveinformationwarfare.py b/eos/effects/subsystembonusgallentedefensiveinformationwarfare.py index 83a9943ee..90e90eac6 100644 --- a/eos/effects/subsystembonusgallentedefensiveinformationwarfare.py +++ b/eos/effects/subsystembonusgallentedefensiveinformationwarfare.py @@ -3,9 +3,21 @@ # Used by: # Subsystem: Proteus Defensive - Warfare Processor type = "passive" + + def handler(fit, src, context): - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Information Command"), "warfareBuff4Value", src.getModifiedItemAttr("subsystemBonusGallenteDefensive"), skill="Gallente Defensive Systems") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Information Command"), "warfareBuff2Value", src.getModifiedItemAttr("subsystemBonusGallenteDefensive"), skill="Gallente Defensive Systems") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Information Command"), "warfareBuff3Value", src.getModifiedItemAttr("subsystemBonusGallenteDefensive"), skill="Gallente Defensive Systems") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Information Command"), "buffDuration", src.getModifiedItemAttr("subsystemBonusGallenteDefensive"), skill="Gallente Defensive Systems") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Information Command"), "warfareBuff1Value", src.getModifiedItemAttr("subsystemBonusGallenteDefensive"), skill="Gallente Defensive Systems") + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Information Command"), "warfareBuff4Value", + src.getModifiedItemAttr("subsystemBonusGallenteDefensive"), + skill="Gallente Defensive Systems") + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Information Command"), "warfareBuff2Value", + src.getModifiedItemAttr("subsystemBonusGallenteDefensive"), + skill="Gallente Defensive Systems") + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Information Command"), "warfareBuff3Value", + src.getModifiedItemAttr("subsystemBonusGallenteDefensive"), + skill="Gallente Defensive Systems") + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Information Command"), "buffDuration", + src.getModifiedItemAttr("subsystemBonusGallenteDefensive"), + skill="Gallente Defensive Systems") + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Information Command"), "warfareBuff1Value", + src.getModifiedItemAttr("subsystemBonusGallenteDefensive"), + skill="Gallente Defensive Systems") diff --git a/eos/effects/subsystembonusgallentedefensiveinformationwarfarehidden.py b/eos/effects/subsystembonusgallentedefensiveinformationwarfarehidden.py index 7a3f8205f..19b0cf02e 100644 --- a/eos/effects/subsystembonusgallentedefensiveinformationwarfarehidden.py +++ b/eos/effects/subsystembonusgallentedefensiveinformationwarfarehidden.py @@ -1,5 +1,7 @@ # Not used by any item type = "passive" + + def handler(fit, module, context): fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Information Command Specialist"), "commandBonusHidden", module.getModifiedItemAttr("subsystemBonusGallenteDefensive"), skill="Gallente Defensive Systems") diff --git a/eos/effects/subsystembonusgallentedefensiveskirmishwarfare.py b/eos/effects/subsystembonusgallentedefensiveskirmishwarfare.py index 6aae7922b..b4ed4e9e2 100644 --- a/eos/effects/subsystembonusgallentedefensiveskirmishwarfare.py +++ b/eos/effects/subsystembonusgallentedefensiveskirmishwarfare.py @@ -3,9 +3,21 @@ # Used by: # Subsystem: Proteus Defensive - Warfare Processor type = "passive" + + def handler(fit, src, context): - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Skirmish Command"), "warfareBuff4Value", src.getModifiedItemAttr("subsystemBonusGallenteDefensive"), skill="Gallente Defensive Systems") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Skirmish Command"), "warfareBuff2Value", src.getModifiedItemAttr("subsystemBonusGallenteDefensive"), skill="Gallente Defensive Systems") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Skirmish Command"), "warfareBuff1Value", src.getModifiedItemAttr("subsystemBonusGallenteDefensive"), skill="Gallente Defensive Systems") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Skirmish Command"), "warfareBuff3Value", src.getModifiedItemAttr("subsystemBonusGallenteDefensive"), skill="Gallente Defensive Systems") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Skirmish Command"), "buffDuration", src.getModifiedItemAttr("subsystemBonusGallenteDefensive"), skill="Gallente Defensive Systems") + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Skirmish Command"), "warfareBuff4Value", + src.getModifiedItemAttr("subsystemBonusGallenteDefensive"), + skill="Gallente Defensive Systems") + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Skirmish Command"), "warfareBuff2Value", + src.getModifiedItemAttr("subsystemBonusGallenteDefensive"), + skill="Gallente Defensive Systems") + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Skirmish Command"), "warfareBuff1Value", + src.getModifiedItemAttr("subsystemBonusGallenteDefensive"), + skill="Gallente Defensive Systems") + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Skirmish Command"), "warfareBuff3Value", + src.getModifiedItemAttr("subsystemBonusGallenteDefensive"), + skill="Gallente Defensive Systems") + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Skirmish Command"), "buffDuration", + src.getModifiedItemAttr("subsystemBonusGallenteDefensive"), + skill="Gallente Defensive Systems") diff --git a/eos/effects/subsystembonusminmatardefensive2remoteshieldtransporteramount.py b/eos/effects/subsystembonusminmatardefensive2remoteshieldtransporteramount.py index 784c7e7e8..2586c28ec 100644 --- a/eos/effects/subsystembonusminmatardefensive2remoteshieldtransporteramount.py +++ b/eos/effects/subsystembonusminmatardefensive2remoteshieldtransporteramount.py @@ -4,6 +4,8 @@ # Subsystem: Loki Defensive - Adaptive Shielding type = "passive" runTime = "early" + + def handler(fit, module, context): fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Shield Emission Systems"), "shieldBonus", module.getModifiedItemAttr("subsystemBonusMinmatarDefensive2"), diff --git a/eos/effects/subsystembonusminmatardefensivearmoredwarfare.py b/eos/effects/subsystembonusminmatardefensivearmoredwarfare.py index f38d8af9c..7921b5713 100644 --- a/eos/effects/subsystembonusminmatardefensivearmoredwarfare.py +++ b/eos/effects/subsystembonusminmatardefensivearmoredwarfare.py @@ -3,9 +3,21 @@ # Used by: # Subsystem: Loki Defensive - Warfare Processor type = "passive" + + def handler(fit, src, context): - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Armored Command"), "buffDuration", src.getModifiedItemAttr("subsystemBonusMinmatarDefensive"), skill="Minmatar Defensive Systems") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Armored Command"), "warfareBuff2Value", src.getModifiedItemAttr("subsystemBonusMinmatarDefensive"), skill="Minmatar Defensive Systems") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Armored Command"), "warfareBuff4Value", src.getModifiedItemAttr("subsystemBonusMinmatarDefensive"), skill="Minmatar Defensive Systems") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Armored Command"), "warfareBuff1Value", src.getModifiedItemAttr("subsystemBonusMinmatarDefensive"), skill="Minmatar Defensive Systems") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Armored Command"), "warfareBuff3Value", src.getModifiedItemAttr("subsystemBonusMinmatarDefensive"), skill="Minmatar Defensive Systems") + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Armored Command"), "buffDuration", + src.getModifiedItemAttr("subsystemBonusMinmatarDefensive"), + skill="Minmatar Defensive Systems") + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Armored Command"), "warfareBuff2Value", + src.getModifiedItemAttr("subsystemBonusMinmatarDefensive"), + skill="Minmatar Defensive Systems") + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Armored Command"), "warfareBuff4Value", + src.getModifiedItemAttr("subsystemBonusMinmatarDefensive"), + skill="Minmatar Defensive Systems") + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Armored Command"), "warfareBuff1Value", + src.getModifiedItemAttr("subsystemBonusMinmatarDefensive"), + skill="Minmatar Defensive Systems") + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Armored Command"), "warfareBuff3Value", + src.getModifiedItemAttr("subsystemBonusMinmatarDefensive"), + skill="Minmatar Defensive Systems") diff --git a/eos/effects/subsystembonusminmatardefensivesiegewarfare.py b/eos/effects/subsystembonusminmatardefensivesiegewarfare.py index 80547e83e..de019c309 100644 --- a/eos/effects/subsystembonusminmatardefensivesiegewarfare.py +++ b/eos/effects/subsystembonusminmatardefensivesiegewarfare.py @@ -3,9 +3,21 @@ # Used by: # Subsystem: Loki Defensive - Warfare Processor type = "passive" + + def handler(fit, src, context): - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Shield Command"), "warfareBuff1Value", src.getModifiedItemAttr("subsystemBonusMinmatarDefensive"), skill="Minmatar Defensive Systems") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Shield Command"), "buffDuration", src.getModifiedItemAttr("subsystemBonusMinmatarDefensive"), skill="Minmatar Defensive Systems") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Shield Command"), "warfareBuff4Value", src.getModifiedItemAttr("subsystemBonusMinmatarDefensive"), skill="Minmatar Defensive Systems") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Shield Command"), "warfareBuff3Value", src.getModifiedItemAttr("subsystemBonusMinmatarDefensive"), skill="Minmatar Defensive Systems") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Shield Command"), "warfareBuff2Value", src.getModifiedItemAttr("subsystemBonusMinmatarDefensive"), skill="Minmatar Defensive Systems") + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Shield Command"), "warfareBuff1Value", + src.getModifiedItemAttr("subsystemBonusMinmatarDefensive"), + skill="Minmatar Defensive Systems") + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Shield Command"), "buffDuration", + src.getModifiedItemAttr("subsystemBonusMinmatarDefensive"), + skill="Minmatar Defensive Systems") + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Shield Command"), "warfareBuff4Value", + src.getModifiedItemAttr("subsystemBonusMinmatarDefensive"), + skill="Minmatar Defensive Systems") + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Shield Command"), "warfareBuff3Value", + src.getModifiedItemAttr("subsystemBonusMinmatarDefensive"), + skill="Minmatar Defensive Systems") + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Shield Command"), "warfareBuff2Value", + src.getModifiedItemAttr("subsystemBonusMinmatarDefensive"), + skill="Minmatar Defensive Systems") diff --git a/eos/effects/subsystembonusminmatardefensiveskirmishwarfare.py b/eos/effects/subsystembonusminmatardefensiveskirmishwarfare.py index 75ecd4da2..645257326 100644 --- a/eos/effects/subsystembonusminmatardefensiveskirmishwarfare.py +++ b/eos/effects/subsystembonusminmatardefensiveskirmishwarfare.py @@ -3,9 +3,21 @@ # Used by: # Subsystem: Loki Defensive - Warfare Processor type = "passive" + + def handler(fit, src, context): - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Skirmish Command"), "warfareBuff1Value", src.getModifiedItemAttr("subsystemBonusMinmatarDefensive"), skill="Minmatar Defensive Systems") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Skirmish Command"), "warfareBuff3Value", src.getModifiedItemAttr("subsystemBonusMinmatarDefensive"), skill="Minmatar Defensive Systems") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Skirmish Command"), "warfareBuff4Value", src.getModifiedItemAttr("subsystemBonusMinmatarDefensive"), skill="Minmatar Defensive Systems") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Skirmish Command"), "warfareBuff2Value", src.getModifiedItemAttr("subsystemBonusMinmatarDefensive"), skill="Minmatar Defensive Systems") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Skirmish Command"), "buffDuration", src.getModifiedItemAttr("subsystemBonusMinmatarDefensive"), skill="Minmatar Defensive Systems") + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Skirmish Command"), "warfareBuff1Value", + src.getModifiedItemAttr("subsystemBonusMinmatarDefensive"), + skill="Minmatar Defensive Systems") + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Skirmish Command"), "warfareBuff3Value", + src.getModifiedItemAttr("subsystemBonusMinmatarDefensive"), + skill="Minmatar Defensive Systems") + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Skirmish Command"), "warfareBuff4Value", + src.getModifiedItemAttr("subsystemBonusMinmatarDefensive"), + skill="Minmatar Defensive Systems") + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Skirmish Command"), "warfareBuff2Value", + src.getModifiedItemAttr("subsystemBonusMinmatarDefensive"), + skill="Minmatar Defensive Systems") + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Skirmish Command"), "buffDuration", + src.getModifiedItemAttr("subsystemBonusMinmatarDefensive"), + skill="Minmatar Defensive Systems") diff --git a/eos/effects/systemarmorrepairamount.py b/eos/effects/systemarmorrepairamount.py index 1b28168f1..da585583d 100644 --- a/eos/effects/systemarmorrepairamount.py +++ b/eos/effects/systemarmorrepairamount.py @@ -7,7 +7,7 @@ type = ("projected", "passive") def handler(fit, module, context): - fit.modules.filteredItemMultiply(lambda mod: mod.item.requiresSkill("Repair Systems") - or mod.item.requiresSkill("Capital Repair Systems"), + fit.modules.filteredItemMultiply(lambda mod: mod.item.requiresSkill("Repair Systems") or + mod.item.requiresSkill("Capital Repair Systems"), "armorDamageAmount", module.getModifiedItemAttr("armorDamageAmountMultiplier"), stackingPenalties=True, penaltyGroup="postMul") diff --git a/eos/effects/systemshieldrepairamountshieldskills.py b/eos/effects/systemshieldrepairamountshieldskills.py index bde28cd04..707e2dbbb 100644 --- a/eos/effects/systemshieldrepairamountshieldskills.py +++ b/eos/effects/systemshieldrepairamountshieldskills.py @@ -7,7 +7,7 @@ type = ("projected", "passive") def handler(fit, module, context): - fit.modules.filteredItemMultiply(lambda mod: mod.item.requiresSkill("Shield Operation") - or mod.item.requiresSkill("Capital Shield Operation"), + fit.modules.filteredItemMultiply(lambda mod: mod.item.requiresSkill("Shield Operation") or + mod.item.requiresSkill("Capital Shield Operation"), "shieldBonus", module.getModifiedItemAttr("shieldBonusMultiplier"), stackingPenalties=True, penaltyGroup="postMul") diff --git a/eos/effects/targetarmorrepair.py b/eos/effects/targetarmorrepair.py index 358ca2c58..0a4c0c154 100644 --- a/eos/effects/targetarmorrepair.py +++ b/eos/effects/targetarmorrepair.py @@ -1,7 +1,4 @@ -# targetArmorRepair -# -# Used by: -# Module: QA Remote Armor Repair System - 5 Players +# Not used by any item type = "projected", "active" diff --git a/eos/effects/techtwocommandburstbonus.py b/eos/effects/techtwocommandburstbonus.py index a85f78ab9..757513cef 100644 --- a/eos/effects/techtwocommandburstbonus.py +++ b/eos/effects/techtwocommandburstbonus.py @@ -2,6 +2,7 @@ type = "passive" runTime = "late" + def handler(fit, module, context): for x in xrange(1, 4): module.boostChargeAttr("warfareBuff{}Multiplier".format(x), module.getModifiedItemAttr("commandBurstStrengthBonus")) diff --git a/eos/effects/turretweaponrangefallofftrackingspeedmultiplytargethostile.py b/eos/effects/turretweaponrangefallofftrackingspeedmultiplytargethostile.py deleted file mode 100644 index 12d535bb2..000000000 --- a/eos/effects/turretweaponrangefallofftrackingspeedmultiplytargethostile.py +++ /dev/null @@ -1,15 +0,0 @@ -# Not used by any item -type = "projected", "active" - - -def handler(fit, container, context): - if "projected" in context: - fit.modules.filteredItemMultiply(lambda mod: mod.item.requiresSkill("Gunnery"), - "trackingSpeed", container.getModifiedItemAttr("trackingSpeedMultiplier"), - stackingPenalties=True, penaltyGroup="postMul") - fit.modules.filteredItemMultiply(lambda mod: mod.item.requiresSkill("Gunnery"), - "maxRange", container.getModifiedItemAttr("maxRangeMultiplier"), - stackingPenalties=True, penaltyGroup="postMul") - fit.modules.filteredItemMultiply(lambda mod: mod.item.requiresSkill("Gunnery"), - "falloff", container.getModifiedItemAttr("fallofMultiplier"), - stackingPenalties=True, penaltyGroup="postMul") diff --git a/eos/effects/usemissiles.py b/eos/effects/usemissiles.py index 49ef3c5a0..1971dbe46 100644 --- a/eos/effects/usemissiles.py +++ b/eos/effects/usemissiles.py @@ -3,7 +3,7 @@ # Used by: # Modules from group: Missile Launcher Heavy (12 of 12) # Modules from group: Missile Launcher Rocket (15 of 15) -# Modules named like: Launcher (151 of 151) +# Modules named like: Launcher (153 of 153) type = 'active', "projected" diff --git a/eos/effects/warpscrambleblockmwdwithnpceffect.py b/eos/effects/warpscrambleblockmwdwithnpceffect.py index c220fb4fd..1e417b710 100644 --- a/eos/effects/warpscrambleblockmwdwithnpceffect.py +++ b/eos/effects/warpscrambleblockmwdwithnpceffect.py @@ -3,7 +3,7 @@ # Used by: # Modules named like: Warp Scrambler (26 of 26) -from eos.types import State +from eos.saveddata.module import State runTime = "early" type = "projected", "active" diff --git a/eos/effects/zcolinorcasurveyscannerbonus.py b/eos/effects/zcolinorcasurveyscannerbonus.py index feed0cc0c..a1eb01fea 100644 --- a/eos/effects/zcolinorcasurveyscannerbonus.py +++ b/eos/effects/zcolinorcasurveyscannerbonus.py @@ -3,5 +3,8 @@ # Used by: # Ships from group: Industrial Command Ship (2 of 2) type = "passive" + + def handler(fit, src, context): - fit.modules.filteredItemBoost(lambda mod: mod.item.group.name == "Survey Scanner", "surveyScanRange", src.getModifiedItemAttr("roleBonusSurveyScannerRange")) + fit.modules.filteredItemBoost(lambda mod: mod.item.group.name == "Survey Scanner", "surveyScanRange", + src.getModifiedItemAttr("roleBonusSurveyScannerRange")) diff --git a/eos/effects/zcolinorcatractorrangebonus.py b/eos/effects/zcolinorcatractorrangebonus.py index b2262a1e9..9c4a20c03 100644 --- a/eos/effects/zcolinorcatractorrangebonus.py +++ b/eos/effects/zcolinorcatractorrangebonus.py @@ -3,5 +3,8 @@ # Used by: # Ships from group: Industrial Command Ship (2 of 2) type = "passive" + + def handler(fit, src, context): - fit.modules.filteredItemBoost(lambda mod: mod.item.group.name == "Tractor Beam", "maxRange", src.getModifiedItemAttr("roleBonusTractorBeamRange")) + fit.modules.filteredItemBoost(lambda mod: mod.item.group.name == "Tractor Beam", "maxRange", + src.getModifiedItemAttr("roleBonusTractorBeamRange")) diff --git a/eos/effects/zcolinorcatractorvelocitybonus.py b/eos/effects/zcolinorcatractorvelocitybonus.py index 95219da80..736643324 100644 --- a/eos/effects/zcolinorcatractorvelocitybonus.py +++ b/eos/effects/zcolinorcatractorvelocitybonus.py @@ -6,4 +6,5 @@ type = "passive" def handler(fit, ship, context): - fit.modules.filteredItemBoost(lambda mod: mod.item.group.name == "Tractor Beam", "maxTractorVelocity", ship.getModifiedItemAttr("roleBonusTractorBeamVelocity")) + fit.modules.filteredItemBoost(lambda mod: mod.item.group.name == "Tractor Beam", "maxTractorVelocity", + ship.getModifiedItemAttr("roleBonusTractorBeamVelocity")) diff --git a/eos/enum.py b/eos/enum.py index e9fb20a0b..12e8e09e1 100644 --- a/eos/enum.py +++ b/eos/enum.py @@ -1,23 +1,23 @@ -class Enum(): - def __init__(self): - pass - - @classmethod - def getTypes(cls): - for stuff in cls.__dict__: - if stuff.upper() == stuff: - yield stuff - - @classmethod - def getName(cls, v): - map = getattr(cls, "_map", None) - if map is None: - map = cls._map = {} - for type in cls.getTypes(): - map[cls.getValue(type)] = type - - return map.get(v) - - @classmethod - def getValue(cls, type): - return cls.__dict__[type] +class Enum(object): + def __init__(self): + pass + + @classmethod + def getTypes(cls): + for stuff in cls.__dict__: + if stuff.upper() == stuff: + yield stuff + + @classmethod + def getName(cls, v): + map = getattr(cls, "_map", None) + if map is None: + map = cls._map = {} + for type in cls.getTypes(): + map[cls.getValue(type)] = type + + return map.get(v) + + @classmethod + def getValue(cls, type): + return cls.__dict__[type] diff --git a/eos/eqBase.py b/eos/eqBase.py index a400aec33..73462c5ae 100644 --- a/eos/eqBase.py +++ b/eos/eqBase.py @@ -1,29 +1,31 @@ -# =============================================================================== -# Copyright (C) 2010 Diego Duclos -# -# This file is part of eos. -# -# eos is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser General Public License as published by -# the Free Software Foundation, either version 2 of the License, or -# (at your option) any later version. -# -# eos is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public License -# along with eos. If not, see . -# =============================================================================== - - -class EqBase(object): - def __eq__(self, other): - return type(self) == type(other) and self.ID == other.ID - - def __ne__(self, other): - return type(self) != type(other) or self.ID != other.ID - - def __hash__(self): - return id(type(self)) + self.ID +# =============================================================================== +# Copyright (C) 2010 Diego Duclos +# +# This file is part of eos. +# +# eos is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation, either version 2 of the License, or +# (at your option) any later version. +# +# eos is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with eos. If not, see . +# =============================================================================== + + +class EqBase(object): + ID = None + + def __eq__(self, other): + return type(self) == type(other) and self.ID == other.ID + + def __ne__(self, other): + return type(self) != type(other) or self.ID != other.ID + + def __hash__(self): + return id(type(self)) + self.ID diff --git a/eos/gamedata.py b/eos/gamedata.py index c4b58b235..c5a035b25 100644 --- a/eos/gamedata.py +++ b/eos/gamedata.py @@ -18,7 +18,6 @@ # =============================================================================== import re -import traceback from sqlalchemy.orm import reconstructor @@ -30,6 +29,9 @@ try: except ImportError: from utils.compat import OrderedDict +from logbook import Logger +pyfalog = Logger(__name__) + class Effect(EqBase): """ @@ -64,6 +66,8 @@ class Effect(EqBase): if not self.__generated: self.__generateHandler() + pyfalog.debug("Generating effect: {0} ({1}) [runTime: {2}]", self.name, self.effectID, self.runTime) + return self.__handler @property @@ -138,7 +142,7 @@ class Effect(EqBase): @property def isImplemented(self): """ - Wether this effect is implemented in code or not, + Whether this effect is implemented in code or not, unimplemented effects simply do nothing at all when run """ return self.handler != effectDummy @@ -184,8 +188,11 @@ class Effect(EqBase): self.__runTime = "normal" self.__activeByDefault = True self.__type = None + pyfalog.debug("ImportError or AttributeError generating handler:") + pyfalog.debug(e) except Exception as e: - traceback.print_exc(e) + pyfalog.critical("Exception generating handler:") + pyfalog.critical(e) self.__generated = True @@ -243,9 +250,11 @@ class Item(EqBase): return self.__attributes - def getAttribute(self, key): + def getAttribute(self, key, default=None): if key in self.attributes: return self.attributes[key].value + else: + return default def isType(self, type): for effect in self.effects.itervalues(): @@ -444,6 +453,26 @@ class Category(EqBase): pass +class AlphaClone(EqBase): + + @reconstructor + def init(self): + self.skillCache = {} + + for x in self.skills: + self.skillCache[x.typeID] = x + + def getSkillLevel(self, skill): + if skill.item.ID in self.skillCache: + return self.skillCache[skill.item.ID].level + else: + return None + + +class AlphaCloneSkill(EqBase): + pass + + class Group(EqBase): pass @@ -453,6 +482,7 @@ class Icon(EqBase): class MarketGroup(EqBase): + def __repr__(self): return u"MarketGroup(ID={}, name={}, parent={}) at {}".format( self.ID, self.name, getattr(self.parent, "name", None), self.name, hex(id(self)) diff --git a/eos/graph/__init__.py b/eos/graph/__init__.py index 882ac35d1..44802171f 100644 --- a/eos/graph/__init__.py +++ b/eos/graph/__init__.py @@ -1,118 +1,120 @@ -# =============================================================================== -# Copyright (C) 2010 Diego Duclos -# -# This file is part of eos. -# -# eos is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser General Public License as published by -# the Free Software Foundation, either version 2 of the License, or -# (at your option) any later version. -# -# eos is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public License -# along with eos. If not, see . -# =============================================================================== - -import itertools - - -class Graph(object): - def __init__(self, fit, function, data=None): - self.fit = fit - self.data = {} - if data is not None: - for name, d in data.iteritems(): - self.setData(Data(name, d)) - - self.function = function - - def clearData(self): - self.data.clear() - - def setData(self, data): - self.data[data.name] = data - - def getIterator(self): - pointNames = [] - pointIterators = [] - for data in self.data.itervalues(): - pointNames.append(data.name) - pointIterators.append(data) - - return self._iterator(pointNames, pointIterators) - - def _iterator(self, pointNames, pointIterators): - for pointValues in itertools.product(*pointIterators): - point = {} - for i in xrange(len(pointValues)): - point[pointNames[i]] = pointValues[i] - - yield point, self.function(point) - - -class Data(object): - def __init__(self, name, dataString, step=None): - self.name = name - self.step = step - self.data = self.parseString(dataString) - - def parseString(self, dataString): - if not isinstance(dataString, basestring): - return (Constant(dataString),) - - dataList = [] - for data in dataString.split(";"): - if isinstance(data, basestring) and "-" in data: - # Dealing with a range - dataList.append(Range(data, self.step)) - else: - dataList.append(Constant(data)) - - return dataList - - def __iter__(self): - for data in self.data: - for value in data: - yield value - - def isConstant(self): - return len(self.data) == 1 and self.data[0].isConstant() - - -class Constant(object): - def __init__(self, const): - if isinstance(const, basestring): - self.value = None if const == "" else float(const) - else: - self.value = const - - def __iter__(self): - yield self.value - - def isConstant(self): - return True - - -class Range(object): - def __init__(self, string, step): - start, end = string.split("-") - self.start = float(start) - self.end = float(end) - self.step = step - - def __iter__(self): - current = start = self.start - end = self.end - step = self.step or (end - start) / 50.0 - i = 1 - while current < end: - current = start + i * step - i += 1 - yield current - - def isConstant(self): - return False +# =============================================================================== +# Copyright (C) 2010 Diego Duclos +# +# This file is part of eos. +# +# eos is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation, either version 2 of the License, or +# (at your option) any later version. +# +# eos is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with eos. If not, see . +# =============================================================================== + +import itertools + + +class Graph(object): + def __init__(self, fit, function, data=None): + self.fit = fit + self.data = {} + if data is not None: + for name, d in data.iteritems(): + self.setData(Data(name, d)) + + self.function = function + + def clearData(self): + self.data.clear() + + def setData(self, data): + self.data[data.name] = data + + def getIterator(self): + pointNames = [] + pointIterators = [] + for data in self.data.itervalues(): + pointNames.append(data.name) + pointIterators.append(data) + + return self._iterator(pointNames, pointIterators) + + def _iterator(self, pointNames, pointIterators): + for pointValues in itertools.product(*pointIterators): + point = {} + for i in xrange(len(pointValues)): + point[pointNames[i]] = pointValues[i] + + yield point, self.function(point) + + +class Data(object): + def __init__(self, name, dataString, step=None): + self.name = name + self.step = step + self.data = self.parseString(dataString) + + def parseString(self, dataString): + if not isinstance(dataString, basestring): + return Constant(dataString), + + dataList = [] + for data in dataString.split(";"): + if isinstance(data, basestring) and "-" in data: + # Dealing with a range + dataList.append(Range(data, self.step)) + else: + dataList.append(Constant(data)) + + return dataList + + def __iter__(self): + for data in self.data: + for value in data: + yield value + + def isConstant(self): + return len(self.data) == 1 and self.data[0].isConstant() + + +class Constant(object): + def __init__(self, const): + if isinstance(const, basestring): + self.value = None if const == "" else float(const) + else: + self.value = const + + def __iter__(self): + yield self.value + + @staticmethod + def isConstant(): + return True + + +class Range(object): + def __init__(self, string, step): + start, end = string.split("-") + self.start = float(start) + self.end = float(end) + self.step = step + + def __iter__(self): + current = start = self.start + end = self.end + step = self.step or (end - start) / 50.0 + i = 1 + while current < end: + current = start + i * step + i += 1 + yield current + + @staticmethod + def isConstant(): + return False diff --git a/eos/graph/fitDps.py b/eos/graph/fitDps.py index cfc9ec5f4..f09a2c34b 100644 --- a/eos/graph/fitDps.py +++ b/eos/graph/fitDps.py @@ -1,183 +1,190 @@ -# =============================================================================== -# Copyright (C) 2010 Diego Duclos -# -# This file is part of eos. -# -# eos is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser General Public License as published by -# the Free Software Foundation, either version 2 of the License, or -# (at your option) any later version. -# -# eos is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public License -# along with eos. If not, see . -# =============================================================================== - -from math import log, sin, radians, exp - -from eos.graph import Graph -from eos.types import Hardpoint, State - - -class FitDpsGraph(Graph): - defaults = {"angle": 0, - "distance": 0, - "signatureRadius": None, - "velocity": 0} - - def __init__(self, fit, data=None): - Graph.__init__(self, fit, self.calcDps, data if data is not None else self.defaults) - self.fit = fit - - def calcDps(self, data): - ew = {'signatureRadius': [], 'velocity': []} - fit = self.fit - total = 0 - distance = data["distance"] * 1000 - abssort = lambda val: -abs(val - 1) - - for mod in fit.modules: - if not mod.isEmpty and mod.state >= State.ACTIVE: - if "remoteTargetPaintFalloff" in mod.item.effects: - ew['signatureRadius'].append( - 1 + (mod.getModifiedItemAttr("signatureRadiusBonus") / 100) * self.calculateModuleMultiplier( - mod, data)) - if "remoteWebifierFalloff" in mod.item.effects: - if distance <= mod.getModifiedItemAttr("maxRange"): - ew['velocity'].append(1 + (mod.getModifiedItemAttr("speedFactor") / 100)) - elif mod.getModifiedItemAttr("falloffEffectiveness") > 0: - # I am affected by falloff - ew['velocity'].append( - 1 + (mod.getModifiedItemAttr("speedFactor") / 100) * self.calculateModuleMultiplier(mod, - data)) - - ew['signatureRadius'].sort(key=abssort) - ew['velocity'].sort(key=abssort) - - for attr, values in ew.iteritems(): - val = data[attr] - try: - for i in xrange(len(values)): - bonus = values[i] - val *= 1 + (bonus - 1) * exp(- i ** 2 / 7.1289) - data[attr] = val - except: - pass - - for mod in fit.modules: - dps, _ = mod.damageStats(fit.targetResists) - if mod.hardpoint == Hardpoint.TURRET: - if mod.state >= State.ACTIVE: - total += dps * self.calculateTurretMultiplier(mod, data) - - elif mod.hardpoint == Hardpoint.MISSILE: - if mod.state >= State.ACTIVE and mod.maxRange >= distance: - total += dps * self.calculateMissileMultiplier(mod, data) - - if distance <= fit.extraAttributes["droneControlRange"]: - for drone in fit.drones: - multiplier = 1 if drone.getModifiedItemAttr("maxVelocity") > 1 else self.calculateTurretMultiplier( - drone, data) - dps, _ = drone.damageStats(fit.targetResists) - total += dps * multiplier - - # this is janky as fuck - for fighter in fit.fighters: - for ability in fighter.abilities: - if ability.dealsDamage and ability.active: - multiplier = self.calculateFighterMissileMultiplier(ability, data) - dps, _ = ability.damageStats(fit.targetResists) - total += dps * multiplier - - return total - - def calculateMissileMultiplier(self, mod, data): - targetSigRad = data["signatureRadius"] - targetVelocity = data["velocity"] - explosionRadius = mod.getModifiedChargeAttr("aoeCloudSize") - targetSigRad = explosionRadius if targetSigRad is None else targetSigRad - explosionVelocity = mod.getModifiedChargeAttr("aoeVelocity") - damageReductionFactor = mod.getModifiedChargeAttr("aoeDamageReductionFactor") - - sigRadiusFactor = targetSigRad / explosionRadius - if targetVelocity: - velocityFactor = ( - explosionVelocity / explosionRadius * targetSigRad / targetVelocity) ** damageReductionFactor - else: - velocityFactor = 1 - - return min(sigRadiusFactor, velocityFactor, 1) - - def calculateTurretMultiplier(self, mod, data): - # Source for most of turret calculation info: http://wiki.eveonline.com/en/wiki/Falloff - chanceToHit = self.calculateTurretChanceToHit(mod, data) - if chanceToHit > 0.01: - # AvgDPS = Base Damage * [ ( ChanceToHit^2 + ChanceToHit + 0.0499 ) / 2 ] - multiplier = (chanceToHit ** 2 + chanceToHit + 0.0499) / 2 - else: - # All hits are wreckings - multiplier = chanceToHit * 3 - dmgScaling = mod.getModifiedItemAttr("turretDamageScalingRadius") - if dmgScaling: - targetSigRad = data["signatureRadius"] - multiplier = min(1, (float(targetSigRad) / dmgScaling) ** 2) - return multiplier - - def calculateFighterMissileMultiplier(self, ability, data): - prefix = ability.attrPrefix - - targetSigRad = data["signatureRadius"] - targetVelocity = data["velocity"] - explosionRadius = ability.fighter.getModifiedItemAttr("{}ExplosionRadius".format(prefix)) - explosionVelocity = ability.fighter.getModifiedItemAttr("{}ExplosionVelocity".format(prefix)) - damageReductionFactor = ability.fighter.getModifiedItemAttr("{}ReductionFactor".format(prefix)) - - # the following conditionals are because CCP can't keep a decent naming convention, as if fighter implementation - # wasn't already fucked. - if damageReductionFactor is None: - damageReductionFactor = ability.fighter.getModifiedItemAttr("{}DamageReductionFactor".format(prefix)) - - damageReductionSensitivity = ability.fighter.getModifiedItemAttr("{}ReductionSensitivity".format(prefix)) - if damageReductionSensitivity is None: - damageReductionSensitivity = ability.fighter.getModifiedItemAttr( - "{}DamageReductionSensitivity".format(prefix)) - - targetSigRad = explosionRadius if targetSigRad is None else targetSigRad - sigRadiusFactor = targetSigRad / explosionRadius - - if targetVelocity: - velocityFactor = (explosionVelocity / explosionRadius * targetSigRad / targetVelocity) ** ( - log(damageReductionFactor) / log(damageReductionSensitivity)) - else: - velocityFactor = 1 - - return min(sigRadiusFactor, velocityFactor, 1) - - def calculateTurretChanceToHit(self, mod, data): - distance = data["distance"] * 1000 - tracking = mod.getModifiedItemAttr("trackingSpeed") - turretOptimal = mod.maxRange - turretFalloff = mod.falloff - turretSigRes = mod.getModifiedItemAttr("optimalSigRadius") - targetSigRad = data["signatureRadius"] - targetSigRad = turretSigRes if targetSigRad is None else targetSigRad - transversal = sin(radians(data["angle"])) * data["velocity"] - trackingEq = (((transversal / (distance * tracking)) * - (turretSigRes / targetSigRad)) ** 2) - rangeEq = ((max(0, distance - turretOptimal)) / turretFalloff) ** 2 - - return 0.5 ** (trackingEq + rangeEq) - - def calculateModuleMultiplier(self, mod, data): - # Simplified formula, we make some assumptions about the module - # This is basically the calculateTurretChanceToHit without tracking values - distance = data["distance"] * 1000 - turretOptimal = mod.maxRange - turretFalloff = mod.falloff - rangeEq = ((max(0, distance - turretOptimal)) / turretFalloff) ** 2 - - return 0.5 ** (rangeEq) +# =============================================================================== +# Copyright (C) 2010 Diego Duclos +# +# This file is part of eos. +# +# eos is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation, either version 2 of the License, or +# (at your option) any later version. +# +# eos is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with eos. If not, see . +# =============================================================================== + +from math import log, sin, radians, exp + +from eos.graph import Graph +from eos.saveddata.module import State, Hardpoint +from logbook import Logger + +pyfalog = Logger(__name__) + + +class FitDpsGraph(Graph): + defaults = {"angle": 0, + "distance": 0, + "signatureRadius": None, + "velocity": 0} + + def __init__(self, fit, data=None): + Graph.__init__(self, fit, self.calcDps, data if data is not None else self.defaults) + self.fit = fit + + def calcDps(self, data): + ew = {'signatureRadius': [], 'velocity': []} + fit = self.fit + total = 0 + distance = data["distance"] * 1000 + abssort = lambda _val: -abs(_val - 1) + + for mod in fit.modules: + if not mod.isEmpty and mod.state >= State.ACTIVE: + if "remoteTargetPaintFalloff" in mod.item.effects: + ew['signatureRadius'].append( + 1 + (mod.getModifiedItemAttr("signatureRadiusBonus") / 100) * self.calculateModuleMultiplier( + mod, data)) + if "remoteWebifierFalloff" in mod.item.effects: + if distance <= mod.getModifiedItemAttr("maxRange"): + ew['velocity'].append(1 + (mod.getModifiedItemAttr("speedFactor") / 100)) + elif mod.getModifiedItemAttr("falloffEffectiveness") > 0: + # I am affected by falloff + ew['velocity'].append( + 1 + (mod.getModifiedItemAttr("speedFactor") / 100) * self.calculateModuleMultiplier(mod, + data)) + + ew['signatureRadius'].sort(key=abssort) + ew['velocity'].sort(key=abssort) + + for attr, values in ew.iteritems(): + val = data[attr] + try: + for i in xrange(len(values)): + bonus = values[i] + val *= 1 + (bonus - 1) * exp(- i ** 2 / 7.1289) + data[attr] = val + except Exception as e: + pyfalog.critical("Caught exception in calcDPS.") + pyfalog.critical(e) + + for mod in fit.modules: + dps, _ = mod.damageStats(fit.targetResists) + if mod.hardpoint == Hardpoint.TURRET: + if mod.state >= State.ACTIVE: + total += dps * self.calculateTurretMultiplier(mod, data) + + elif mod.hardpoint == Hardpoint.MISSILE: + if mod.state >= State.ACTIVE and mod.maxRange >= distance: + total += dps * self.calculateMissileMultiplier(mod, data) + + if distance <= fit.extraAttributes["droneControlRange"]: + for drone in fit.drones: + multiplier = 1 if drone.getModifiedItemAttr("maxVelocity") > 1 else self.calculateTurretMultiplier( + drone, data) + dps, _ = drone.damageStats(fit.targetResists) + total += dps * multiplier + + # this is janky as fuck + for fighter in fit.fighters: + for ability in fighter.abilities: + if ability.dealsDamage and ability.active: + multiplier = self.calculateFighterMissileMultiplier(ability, data) + dps, _ = ability.damageStats(fit.targetResists) + total += dps * multiplier + + return total + + @staticmethod + def calculateMissileMultiplier(mod, data): + targetSigRad = data["signatureRadius"] + targetVelocity = data["velocity"] + explosionRadius = mod.getModifiedChargeAttr("aoeCloudSize") + targetSigRad = explosionRadius if targetSigRad is None else targetSigRad + explosionVelocity = mod.getModifiedChargeAttr("aoeVelocity") + damageReductionFactor = mod.getModifiedChargeAttr("aoeDamageReductionFactor") + + sigRadiusFactor = targetSigRad / explosionRadius + if targetVelocity: + velocityFactor = (explosionVelocity / explosionRadius * targetSigRad / targetVelocity) ** damageReductionFactor + else: + velocityFactor = 1 + + return min(sigRadiusFactor, velocityFactor, 1) + + def calculateTurretMultiplier(self, mod, data): + # Source for most of turret calculation info: http://wiki.eveonline.com/en/wiki/Falloff + chanceToHit = self.calculateTurretChanceToHit(mod, data) + if chanceToHit > 0.01: + # AvgDPS = Base Damage * [ ( ChanceToHit^2 + ChanceToHit + 0.0499 ) / 2 ] + multiplier = (chanceToHit ** 2 + chanceToHit + 0.0499) / 2 + else: + # All hits are wreckings + multiplier = chanceToHit * 3 + dmgScaling = mod.getModifiedItemAttr("turretDamageScalingRadius") + if dmgScaling: + targetSigRad = data["signatureRadius"] + multiplier = min(1, (float(targetSigRad) / dmgScaling) ** 2) + return multiplier + + @staticmethod + def calculateFighterMissileMultiplier(ability, data): + prefix = ability.attrPrefix + + targetSigRad = data["signatureRadius"] + targetVelocity = data["velocity"] + explosionRadius = ability.fighter.getModifiedItemAttr("{}ExplosionRadius".format(prefix)) + explosionVelocity = ability.fighter.getModifiedItemAttr("{}ExplosionVelocity".format(prefix)) + damageReductionFactor = ability.fighter.getModifiedItemAttr("{}ReductionFactor".format(prefix)) + + # the following conditionals are because CCP can't keep a decent naming convention, as if fighter implementation + # wasn't already fucked. + if damageReductionFactor is None: + damageReductionFactor = ability.fighter.getModifiedItemAttr("{}DamageReductionFactor".format(prefix)) + + damageReductionSensitivity = ability.fighter.getModifiedItemAttr("{}ReductionSensitivity".format(prefix)) + if damageReductionSensitivity is None: + damageReductionSensitivity = ability.fighter.getModifiedItemAttr( + "{}DamageReductionSensitivity".format(prefix)) + + targetSigRad = explosionRadius if targetSigRad is None else targetSigRad + sigRadiusFactor = targetSigRad / explosionRadius + + if targetVelocity: + velocityFactor = (explosionVelocity / explosionRadius * targetSigRad / targetVelocity) ** ( + log(damageReductionFactor) / log(damageReductionSensitivity)) + else: + velocityFactor = 1 + + return min(sigRadiusFactor, velocityFactor, 1) + + @staticmethod + def calculateTurretChanceToHit(mod, data): + distance = data["distance"] * 1000 + tracking = mod.getModifiedItemAttr("trackingSpeed") + turretOptimal = mod.maxRange + turretFalloff = mod.falloff + turretSigRes = mod.getModifiedItemAttr("optimalSigRadius") + targetSigRad = data["signatureRadius"] + targetSigRad = turretSigRes if targetSigRad is None else targetSigRad + transversal = sin(radians(data["angle"])) * data["velocity"] + trackingEq = (((transversal / (distance * tracking)) * + (turretSigRes / targetSigRad)) ** 2) + rangeEq = ((max(0, distance - turretOptimal)) / turretFalloff) ** 2 + + return 0.5 ** (trackingEq + rangeEq) + + @staticmethod + def calculateModuleMultiplier(mod, data): + # Simplified formula, we make some assumptions about the module + # This is basically the calculateTurretChanceToHit without tracking values + distance = data["distance"] * 1000 + turretOptimal = mod.maxRange + turretFalloff = mod.falloff + rangeEq = ((max(0, distance - turretOptimal)) / turretFalloff) ** 2 + + return 0.5 ** rangeEq diff --git a/eos/mathUtils.py b/eos/mathUtils.py index 5de12c60f..845bb4844 100644 --- a/eos/mathUtils.py +++ b/eos/mathUtils.py @@ -17,15 +17,9 @@ # along with eos. If not, see . # =============================================================================== -import decimal +from math import floor def floorFloat(value): - """Round float down to integer""" - # We have to convert float to str to keep compatibility with - # decimal module in python 2.6 - value = str(value) - # Do the conversions for proper rounding down, avoiding float - # representation errors - result = int(decimal.Decimal(value).to_integral_value(rounding=decimal.ROUND_DOWN)) + result = int(floor(value)) return result diff --git a/eos/modifiedAttributeDict.py b/eos/modifiedAttributeDict.py index c0f778263..757d8a3d1 100644 --- a/eos/modifiedAttributeDict.py +++ b/eos/modifiedAttributeDict.py @@ -25,25 +25,25 @@ cappingAttrKeyCache = {} class ItemAttrShortcut(object): - def getModifiedItemAttr(self, key): + def getModifiedItemAttr(self, key, default=None): if key in self.itemModifiedAttributes: return self.itemModifiedAttributes[key] else: - return None + return default class ChargeAttrShortcut(object): - def getModifiedChargeAttr(self, key): + def getModifiedChargeAttr(self, key, default=None): if key in self.chargeModifiedAttributes: return self.chargeModifiedAttributes[key] else: - return None + return default class ModifiedAttributeDict(collections.MutableMapping): OVERRIDES = False - class CalculationPlaceholder(): + class CalculationPlaceholder(object): def __init__(self): pass @@ -132,8 +132,8 @@ class ModifiedAttributeDict(collections.MutableMapping): return (key for key in all) def __contains__(self, key): - return ( - self.__original is not None and key in self.__original) or key in self.__modified or key in self.__intermediary + return (self.__original is not None and key in self.__original) or \ + key in self.__modified or key in self.__intermediary def __placehold(self, key): """Create calculation placeholder in item's modified attribute dict""" @@ -212,11 +212,11 @@ class ModifiedAttributeDict(collections.MutableMapping): for penalizedMultipliers in penalizedMultiplierGroups.itervalues(): # A quick explanation of how this works: # 1: Bonuses and penalties are calculated seperately, so we'll have to filter each of them - l1 = filter(lambda val: val > 1, penalizedMultipliers) - l2 = filter(lambda val: val < 1, penalizedMultipliers) + l1 = filter(lambda _val: _val > 1, penalizedMultipliers) + l2 = filter(lambda _val: _val < 1, penalizedMultipliers) # 2: The most significant bonuses take the smallest penalty, # This means we'll have to sort - abssort = lambda val: -abs(val - 1) + abssort = lambda _val: -abs(_val - 1) l1.sort(key=abssort) l2.sort(key=abssort) # 3: The first module doesn't get penalized at all @@ -359,7 +359,7 @@ class ModifiedAttributeDict(collections.MutableMapping): self.__afflict(attributeName, u"\u2263", value) -class Affliction(): +class Affliction(object): def __init__(self, type, amount): self.type = type self.amount = amount diff --git a/eos/saveddata/booster.py b/eos/saveddata/booster.py index 3be3845d0..5ef2d78f6 100644 --- a/eos/saveddata/booster.py +++ b/eos/saveddata/booster.py @@ -17,7 +17,7 @@ # along with eos. If not, see . # =============================================================================== -import logging +from logbook import Logger from sqlalchemy.orm import reconstructor, validates @@ -25,7 +25,7 @@ import eos.db from eos.effectHandlerHelpers import HandledItem from eos.modifiedAttributeDict import ModifiedAttributeDict, ItemAttrShortcut -logger = logging.getLogger(__name__) +pyfalog = Logger(__name__) class Booster(HandledItem, ItemAttrShortcut): @@ -47,11 +47,11 @@ class Booster(HandledItem, ItemAttrShortcut): if self.itemID: self.__item = eos.db.getItem(self.itemID) if self.__item is None: - logger.error("Item (id: %d) does not exist", self.itemID) + pyfalog.error("Item (id: {0}) does not exist", self.itemID) return if self.isInvalid: - logger.error("Item (id: %d) is not a Booser", self.itemID) + pyfalog.error("Item (id: {0}) is not a Booster", self.itemID) return self.build() @@ -103,7 +103,8 @@ class Booster(HandledItem, ItemAttrShortcut): def item(self): return self.__item - def __calculateSlot(self, item): + @staticmethod + def __calculateSlot(item): if "boosterness" not in item.attributes: raise ValueError("Passed item is not a booster") @@ -132,11 +133,11 @@ class Booster(HandledItem, ItemAttrShortcut): @validates("ID", "itemID", "ammoID", "active") def validator(self, key, val): - map = {"ID": lambda val: isinstance(val, int), - "itemID": lambda val: isinstance(val, int), - "ammoID": lambda val: isinstance(val, int), - "active": lambda val: isinstance(val, bool), - "slot": lambda val: isinstance(val, int) and 1 <= val <= 3} + map = {"ID": lambda _val: isinstance(_val, int), + "itemID": lambda _val: isinstance(_val, int), + "ammoID": lambda _val: isinstance(_val, int), + "active": lambda _val: isinstance(_val, bool), + "slot": lambda _val: isinstance(_val, int) and 1 <= _val <= 3} if not map[key](val): raise ValueError(str(val) + " is not a valid value for " + key) @@ -159,6 +160,7 @@ class Booster(HandledItem, ItemAttrShortcut): return copy + # Legacy booster side effect code, disabling as not currently implemented ''' class SideEffect(object): @@ -194,4 +196,4 @@ class Booster(HandledItem, ItemAttrShortcut): raise TypeError("Need an effect with a handler") self.__effect = effect -''' \ No newline at end of file +''' diff --git a/eos/saveddata/cargo.py b/eos/saveddata/cargo.py index 34ab42db3..57da22e79 100644 --- a/eos/saveddata/cargo.py +++ b/eos/saveddata/cargo.py @@ -17,7 +17,8 @@ # along with eos. If not, see . # =============================================================================== -import logging +import sys +from logbook import Logger from sqlalchemy.orm import validates, reconstructor @@ -25,7 +26,7 @@ import eos.db from eos.effectHandlerHelpers import HandledItem from eos.modifiedAttributeDict import ModifiedAttributeDict, ItemAttrShortcut -logger = logging.getLogger(__name__) +pyfalog = Logger(__name__) class Cargo(HandledItem, ItemAttrShortcut): @@ -46,7 +47,7 @@ class Cargo(HandledItem, ItemAttrShortcut): if self.itemID: self.__item = eos.db.getItem(self.itemID) if self.__item is None: - logger.error("Item (id: %d) does not exist", self.itemID) + pyfalog.error("Item (id: {0}) does not exist", self.itemID) return self.__itemModifiedAttributes = ModifiedAttributeDict() @@ -68,10 +69,14 @@ class Cargo(HandledItem, ItemAttrShortcut): def clear(self): self.itemModifiedAttributes.clear() - @validates("fitID", "itemID") + @validates("fitID", "itemID", "amount") def validator(self, key, val): - map = {"fitID": lambda val: isinstance(val, int), - "itemID": lambda val: isinstance(val, int)} + map = {"fitID": lambda _val: isinstance(_val, int), + "itemID": lambda _val: isinstance(_val, int), + "amount": lambda _val: isinstance(_val, int)} + + if key == "amount" and val > sys.maxint: + val = sys.maxint if not map[key](val): raise ValueError(str(val) + " is not a valid value for " + key) diff --git a/eos/saveddata/character.py b/eos/saveddata/character.py index 9657c7d04..067b9f98d 100644 --- a/eos/saveddata/character.py +++ b/eos/saveddata/character.py @@ -18,17 +18,16 @@ # =============================================================================== -import logging +from logbook import Logger from itertools import chain from sqlalchemy.orm import validates, reconstructor import eos import eos.db -import eos.types from eos.effectHandlerHelpers import HandledItem, HandledImplantBoosterList -logger = logging.getLogger(__name__) +pyfalog = Logger(__name__) class Character(object): @@ -99,6 +98,7 @@ class Character(object): self.__skills = [] self.__skillIdMap = {} self.dirtySkills = set() + self.alphaClone = None if initSkills: for item in self.getSkillList(): @@ -109,11 +109,17 @@ class Character(object): @reconstructor def init(self): + self.__skillIdMap = {} for skill in self.__skills: self.__skillIdMap[skill.itemID] = skill self.dirtySkills = set() + self.alphaClone = None + + if self.alphaCloneID: + self.alphaClone = eos.db.getAlphaClone(self.alphaCloneID) + def apiUpdateCharSheet(self, skills): del self.__skills[:] self.__skillIdMap.clear() @@ -134,12 +140,31 @@ class Character(object): @property def name(self): - return self.savedName if not self.isDirty else "{} *".format(self.savedName) + name = self.savedName + + if self.isDirty: + name += " *" + + if self.alphaCloneID: + clone = eos.db.getAlphaClone(self.alphaCloneID) + type = clone.alphaCloneName.split()[1] + name += u' (\u03B1{})'.format(type[0].upper()) + + return name @name.setter def name(self, name): self.savedName = name + @property + def alphaCloneID(self): + return self.__alphaCloneID + + @alphaCloneID.setter + def alphaCloneID(self, cloneID): + self.__alphaCloneID = cloneID + self.alphaClone = eos.db.getAlphaClone(cloneID) if cloneID is not None else None + @property def skills(self): return self.__skills @@ -241,10 +266,10 @@ class Character(object): @validates("ID", "name", "apiKey", "ownerID") def validator(self, key, val): - map = {"ID": lambda val: isinstance(val, int), - "name": lambda val: True, - "apiKey": lambda val: val is None or (isinstance(val, basestring) and len(val) > 0), - "ownerID": lambda val: isinstance(val, int) or val is None} + map = {"ID": lambda _val: isinstance(_val, int), + "name": lambda _val: True, + "apiKey": lambda _val: _val is None or (isinstance(_val, basestring) and len(_val) > 0), + "ownerID": lambda _val: isinstance(_val, int) or _val is None} if not map[key](val): raise ValueError(str(val) + " is not a valid value for " + key) @@ -294,6 +319,9 @@ class Skill(HandledItem): @property def level(self): + if self.character.alphaClone: + return min(self.activeLevel, self.character.alphaClone.getSkillLevel(self)) or 0 + return self.activeLevel or 0 @level.setter @@ -359,8 +387,8 @@ class Skill(HandledItem): if hasattr(self, "_Skill__ro") and self.__ro is True and key != "characterID": raise ReadOnlyException() - map = {"characterID": lambda val: isinstance(val, int), - "skillID": lambda val: isinstance(val, int)} + map = {"characterID": lambda _val: isinstance(_val, int), + "skillID": lambda _val: isinstance(_val, int)} if not map[key](val): raise ValueError(str(val) + " is not a valid value for " + key) diff --git a/eos/saveddata/citadel.py b/eos/saveddata/citadel.py index 1c3c22884..90d7ab425 100644 --- a/eos/saveddata/citadel.py +++ b/eos/saveddata/citadel.py @@ -17,16 +17,17 @@ # along with eos. If not, see . # =============================================================================== -import logging +from logbook import Logger -from eos.types import Ship +from eos.saveddata.ship import Ship -logger = logging.getLogger(__name__) +pyfalog = Logger(__name__) class Citadel(Ship): def validate(self, item): if item.category.name != "Structure": + pyfalog.error("Passed item '{0}' (category: {1}) is not under Structure category", item.name, item.category.name) raise ValueError( 'Passed item "%s" (category: (%s)) is not under Structure category' % (item.name, item.category.name)) diff --git a/eos/saveddata/drone.py b/eos/saveddata/drone.py index d0e8511f5..647e0a99c 100644 --- a/eos/saveddata/drone.py +++ b/eos/saveddata/drone.py @@ -17,7 +17,7 @@ # along with eos. If not, see . # =============================================================================== -import logging +from logbook import Logger from sqlalchemy.orm import validates, reconstructor @@ -25,7 +25,7 @@ import eos.db from eos.effectHandlerHelpers import HandledItem, HandledCharge from eos.modifiedAttributeDict import ModifiedAttributeDict, ItemAttrShortcut, ChargeAttrShortcut -logger = logging.getLogger(__name__) +pyfalog = Logger(__name__) class Drone(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut): @@ -53,11 +53,11 @@ class Drone(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut): if self.itemID: self.__item = eos.db.getItem(self.itemID) if self.__item is None: - logger.error("Item (id: %d) does not exist", self.itemID) + pyfalog.error("Item (id: {0}) does not exist", self.itemID) return if self.isInvalid: - logger.error("Item (id: %d) is not a Drone", self.itemID) + pyfalog.error("Item (id: {0}) is not a Drone", self.itemID) return self.build() @@ -186,11 +186,11 @@ class Drone(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut): @validates("ID", "itemID", "chargeID", "amount", "amountActive") def validator(self, key, val): - map = {"ID": lambda val: isinstance(val, int), - "itemID": lambda val: isinstance(val, int), - "chargeID": lambda val: isinstance(val, int), - "amount": lambda val: isinstance(val, int) and val >= 0, - "amountActive": lambda val: isinstance(val, int) and self.amount >= val >= 0} + map = {"ID": lambda _val: isinstance(_val, int), + "itemID": lambda _val: isinstance(_val, int), + "chargeID": lambda _val: isinstance(_val, int), + "amount": lambda _val: isinstance(_val, int) and _val >= 0, + "amountActive": lambda _val: isinstance(_val, int) and self.amount >= _val >= 0} if not map[key](val): raise ValueError(str(val) + " is not a valid value for " + key) diff --git a/eos/saveddata/fighter.py b/eos/saveddata/fighter.py index 66dd9af89..e10b5b3c5 100644 --- a/eos/saveddata/fighter.py +++ b/eos/saveddata/fighter.py @@ -17,16 +17,17 @@ # along with eos. If not, see . # =============================================================================== -import logging +from logbook import Logger from sqlalchemy.orm import validates, reconstructor import eos.db from eos.effectHandlerHelpers import HandledItem, HandledCharge from eos.modifiedAttributeDict import ModifiedAttributeDict, ItemAttrShortcut, ChargeAttrShortcut -from eos.types import FighterAbility, Slot +from eos.saveddata.fighterAbility import FighterAbility +from eos.saveddata.module import Slot -logger = logging.getLogger(__name__) +pyfalog = Logger(__name__) class Fighter(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut): @@ -60,11 +61,11 @@ class Fighter(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut): if self.itemID: self.__item = eos.db.getItem(self.itemID) if self.__item is None: - logger.error("Item (id: %d) does not exist", self.itemID) + pyfalog.error("Item (id: {0}) does not exist", self.itemID) return if self.isInvalid: - logger.error("Item (id: %d) is not a Fighter", self.itemID) + pyfalog.error("Item (id: {0}) is not a Fighter", self.itemID) return self.build() @@ -148,10 +149,6 @@ class Fighter(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut): def item(self): return self.__item - @property - def charge(self): - return self.__charge - @property def hasAmmo(self): return self.charge is not None @@ -222,10 +219,10 @@ class Fighter(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut): @validates("ID", "itemID", "chargeID", "amount", "amountActive") def validator(self, key, val): - map = {"ID": lambda val: isinstance(val, int), - "itemID": lambda val: isinstance(val, int), - "chargeID": lambda val: isinstance(val, int), - "amount": lambda val: isinstance(val, int) and val >= -1, + map = {"ID": lambda _val: isinstance(_val, int), + "itemID": lambda _val: isinstance(_val, int), + "chargeID": lambda _val: isinstance(_val, int), + "amount": lambda _val: isinstance(_val, int) and _val >= -1, } if not map[key](val): @@ -273,8 +270,7 @@ class Fighter(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut): for ability in self.abilities: if ability.active: effect = ability.effect - if effect.runTime == runTime and \ - effect.activeByDefault and \ + if effect.runTime == runTime and effect.activeByDefault and \ ((projected and effect.isType("projected")) or not projected): if ability.grouped: effect.handler(fit, self, context) diff --git a/eos/saveddata/fighterAbility.py b/eos/saveddata/fighterAbility.py index 38a21a27e..efe429c41 100644 --- a/eos/saveddata/fighterAbility.py +++ b/eos/saveddata/fighterAbility.py @@ -17,11 +17,11 @@ # along with eos. If not, see . # =============================================================================== -import logging +from logbook import Logger from sqlalchemy.orm import reconstructor -logger = logging.getLogger(__name__) +pyfalog = Logger(__name__) class FighterAbility(object): @@ -59,7 +59,7 @@ class FighterAbility(object): if self.effectID: self.__effect = next((x for x in self.fighter.item.effects.itervalues() if x.ID == self.effectID), None) if self.__effect is None: - logger.error("Effect (id: %d) does not exist", self.effectID) + pyfalog.error("Effect (id: {0}) does not exist", self.effectID) return self.build() @@ -95,14 +95,12 @@ class FighterAbility(object): @property def reloadTime(self): - return self.fighter.getModifiedItemAttr("fighterRefuelingTime") + \ - (self.REARM_TIME_MAPPING[self.fighter.getModifiedItemAttr( - "fighterSquadronRole")] or 0 if self.hasCharges else 0) * self.numShots + rearm_time = (self.REARM_TIME_MAPPING[self.fighter.getModifiedItemAttr("fighterSquadronRole")] or 0 if self.hasCharges else 0) + return self.fighter.getModifiedItemAttr("fighterRefuelingTime") + rearm_time * self.numShots @property def numShots(self): - return self.NUM_SHOTS_MAPPING[ - self.fighter.getModifiedItemAttr("fighterSquadronRole")] or 0 if self.hasCharges else 0 + return self.NUM_SHOTS_MAPPING[self.fighter.getModifiedItemAttr("fighterSquadronRole")] or 0 if self.hasCharges else 0 @property def cycleTime(self): diff --git a/eos/saveddata/fit.py b/eos/saveddata/fit.py index b25831ff7..e332bc14f 100644 --- a/eos/saveddata/fit.py +++ b/eos/saveddata/fit.py @@ -17,7 +17,6 @@ # along with eos. If not, see . # =============================================================================== -import copy import time from copy import deepcopy from itertools import chain @@ -27,24 +26,19 @@ from sqlalchemy.orm import validates, reconstructor import eos.db from eos import capSim -from eos.effectHandlerHelpers import * +from eos.effectHandlerHelpers import HandledModuleList, HandledDroneCargoList, HandledImplantBoosterList, HandledProjectedDroneList, HandledProjectedModList from eos.enum import Enum -from eos.saveddata.module import State, Hardpoint -from eos.types import Ship, Character, Slot, Module, Citadel +from eos.saveddata.ship import Ship +from eos.saveddata.character import Character +from eos.saveddata.citadel import Citadel +from eos.saveddata.module import Module, State, Slot, Hardpoint from utils.timer import Timer +from logbook import Logger -logger = logging.getLogger(__name__) - -try: - from collections import OrderedDict -except ImportError: - from utils.compat import OrderedDict +pyfalog = Logger(__name__) class ImplantLocation(Enum): - def __init__(self): - pass - FIT = 0 CHARACTER = 1 @@ -90,7 +84,7 @@ class Fit(object): if self.shipID: item = eos.db.getItem(self.shipID) if item is None: - logger.error("Item (id: %d) does not exist", self.shipID) + pyfalog.error("Item (id: {0}) does not exist", self.shipID) return try: @@ -103,7 +97,7 @@ class Fit(object): # change all instances in source). Remove this at some point self.extraAttributes = self.__ship.itemModifiedAttributes except ValueError: - logger.error("Item (id: %d) is not a Ship", self.shipID) + pyfalog.error("Item (id: {0}) is not a Ship", self.shipID) return if self.modeID and self.__ship: @@ -370,9 +364,9 @@ class Fit(object): @validates("ID", "ownerID", "shipID") def validator(self, key, val): - map = {"ID": lambda val: isinstance(val, int), - "ownerID": lambda val: isinstance(val, int) or val is None, - "shipID": lambda val: isinstance(val, int) or val is None} + map = {"ID": lambda _val: isinstance(_val, int), + "ownerID": lambda _val: isinstance(_val, int) or _val is None, + "shipID": lambda _val: isinstance(_val, int) or _val is None} if not map[key](val): raise ValueError(str(val) + " is not a valid value for " + key) @@ -434,9 +428,11 @@ class Fit(object): self.__modifier = currModifier self.__origin = origin if hasattr(currModifier, "itemModifiedAttributes"): - currModifier.itemModifiedAttributes.fit = origin or self + if hasattr(currModifier.itemModifiedAttributes, "fit"): + currModifier.itemModifiedAttributes.fit = origin or self if hasattr(currModifier, "chargeModifiedAttributes"): - currModifier.chargeModifiedAttributes.fit = origin or self + if hasattr(currModifier.itemModifiedAttributes, "fit"): + currModifier.chargeModifiedAttributes.fit = origin or self def getModifier(self): return self.__modifier @@ -452,7 +448,7 @@ class Fit(object): self.commandBonuses[warfareBuffID] = (runTime, value, module, effect) def __runCommandBoosts(self, runTime="normal"): - logger.debug("Applying gang boosts for %r", self) + pyfalog.debug("Applying gang boosts for {0}", self) for warfareBuffID in self.commandBonuses.keys(): # Unpack all data required to run effect properly effect_runTime, value, thing, effect = self.commandBonuses[warfareBuffID] @@ -485,12 +481,12 @@ class Fit(object): self.ship.boostItemAttr("armor%sDamageResonance" % damageType, value) if warfareBuffID == 14: # Armor Burst: Rapid Repair: Repair Duration/Capacitor - self.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill( - "Remote Armor Repair Systems") or mod.item.requiresSkill("Repair Systems"), - "capacitorNeed", value) - self.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill( - "Remote Armor Repair Systems") or mod.item.requiresSkill("Repair Systems"), "duration", - value) + self.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Remote Armor Repair Systems") or + mod.item.requiresSkill("Repair Systems"), + "capacitorNeed", value) + self.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Remote Armor Repair Systems") or + mod.item.requiresSkill("Repair Systems"), + "duration", value) if warfareBuffID == 15: # Armor Burst: Armor Reinforcement: Armor HP self.ship.boostItemAttr("armorHP", value, stackingPenalties=True) @@ -510,9 +506,8 @@ class Fit(object): "scan%sStrengthBonus" % scanType, value, stackingPenalties=True) - for attr in ( - "missileVelocityBonus", "explosionDelayBonus", "aoeVelocityBonus", "falloffBonus", - "maxRangeBonus", "aoeCloudSizeBonus", "trackingSpeedBonus"): + for attr in ("missileVelocityBonus", "explosionDelayBonus", "aoeVelocityBonus", "falloffBonus", + "maxRangeBonus", "aoeCloudSizeBonus", "trackingSpeedBonus"): self.modules.filteredItemBoost(lambda mod: mod.item.group.name == "Weapon Disruptor", attr, value) @@ -520,7 +515,7 @@ class Fit(object): self.modules.filteredItemBoost(lambda mod: mod.item.group.name == "Sensor Dampener", attr, value) - self.modules.filteredItemBoost(lambda mod: mod.item.gorup.name == "Target Painter", + self.modules.filteredItemBoost(lambda mod: mod.item.group.name == "Target Painter", "signatureRadiusBonus", value, stackingPenalties=True) if warfareBuffID == 18: # Information Burst: Electronic Hardening: Scan Strength @@ -625,38 +620,31 @@ class Fit(object): self.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Shield Emission Systems"), "shieldBonus", value, stackingPenalties=True) if warfareBuffID == 53: # Leviathan Effect Generator : Armor RR penalty - self.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Remote Armor Repair Systems"), "armorDamageAmount", value, stackingPenalties=True) + self.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Remote Armor Repair Systems"), + "armorDamageAmount", value, stackingPenalties=True) if warfareBuffID == 54: # Ragnarok Effect Generator : Laser and Hybrid Optimal penalty groups = ("Energy Weapon", "Hybrid Weapon") self.modules.filteredItemBoost(lambda mod: mod.item.group.name in groups, "maxRange", value, stackingPenalties=True) - else: - # Run effect, and get proper bonuses applied - try: - self.register(thing) - effect.handler(self, thing, context) - except: - pass - del self.commandBonuses[warfareBuffID] def calculateModifiedAttributes(self, targetFit=None, withBoosters=False, dirtyStorage=None): - timer = Timer(u'Fit: {}, {}'.format(self.ID, self.name), logger) - logger.debug("Starting fit calculation on: %r, withBoosters: %s", self, withBoosters) + timer = Timer(u'Fit: {}, {}'.format(self.ID, self.name), pyfalog) + pyfalog.debug("Starting fit calculation on: {0}, withBoosters: {1}", self, withBoosters) shadow = False if targetFit and not withBoosters: - logger.debug("Applying projections to target: %r", targetFit) + pyfalog.debug("Applying projections to target: {0}", targetFit) projectionInfo = self.getProjectionInfo(targetFit.ID) - logger.debug("ProjectionInfo: %s", projectionInfo) + pyfalog.debug("ProjectionInfo: {0}", projectionInfo) if self == targetFit: copied = self # original fit shadow = True # Don't inspect this, we genuinely want to reassign self # noinspection PyMethodFirstArgAssignment - self = copy.deepcopy(self) - logger.debug("Handling self projection - making shadow copy of fit. %r => %r", copied, self) + self = deepcopy(self) + pyfalog.debug("Handling self projection - making shadow copy of fit. {0} => {1}", copied, self) # we delete the fit because when we copy a fit, flush() is # called to properly handle projection updates. However, we do # not want to save this fit to the database, so simply remove it @@ -691,7 +679,7 @@ class Fit(object): # projection have modifying stuff applied, such as gang boosts and other # local modules that may help if self.__calculated and not projected and not withBoosters: - logger.debug("Fit has already been calculated and is not projected, returning: %r", self) + pyfalog.debug("Fit has already been calculated and is not projected, returning: {0}", self) return for runTime in ("early", "normal", "late"): @@ -731,7 +719,7 @@ class Fit(object): self.register(item) item.calculateModifiedAttributes(self, runTime, False) - if projected is True and item not in chain.from_iterable(r): + if projected is True and projectionInfo and item not in chain.from_iterable(r): # apply effects onto target fit for _ in xrange(projectionInfo.amount): targetFit.register(item, origin=self) @@ -742,19 +730,20 @@ class Fit(object): # targetFit.register(item, origin=self) item.calculateModifiedAttributes(targetFit, runTime, False, True) - print "Command: " - print self.commandBonuses + if len(self.commandBonuses) > 0: + pyfalog.info("Command bonuses applied.") + pyfalog.debug(self.commandBonuses) if not withBoosters and self.commandBonuses: self.__runCommandBoosts(runTime) - timer.checkpoint('Done with runtime: %s'%runTime) + timer.checkpoint('Done with runtime: %s' % runTime) # Mark fit as calculated self.__calculated = True # Only apply projected fits if fit it not projected itself. - if not projected: + if not projected and not withBoosters: for fit in self.projectedFits: if fit.getProjectionInfo(self.ID).active: fit.calculateModifiedAttributes(self, withBoosters=withBoosters, dirtyStorage=dirtyStorage) @@ -762,7 +751,7 @@ class Fit(object): timer.checkpoint('Done with fit calculation') if shadow: - logger.debug("Delete shadow fit object") + pyfalog.debug("Delete shadow fit object") del self def fill(self): @@ -807,7 +796,8 @@ class Fit(object): x += 1 return x - def getItemAttrSum(self, dict, attr): + @staticmethod + def getItemAttrSum(dict, attr): amount = 0 for mod in dict: add = mod.getModifiedItemAttr(attr) @@ -816,7 +806,8 @@ class Fit(object): return amount - def getItemAttrOnlineSum(self, dict, attr): + @staticmethod + def getItemAttrOnlineSum(dict, attr): amount = 0 for mod in dict: add = mod.getModifiedItemAttr(attr) if mod.state >= State.ONLINE else None @@ -1051,8 +1042,8 @@ class Fit(object): repairers.append(mod) # Sort repairers by efficiency. We want to use the most efficient repairers first - repairers.sort(key=lambda mod: mod.getModifiedItemAttr( - groupAttrMap[mod.item.group.name]) / mod.getModifiedItemAttr("capacitorNeed"), reverse=True) + repairers.sort(key=lambda _mod: _mod.getModifiedItemAttr( + groupAttrMap[_mod.item.group.name]) / _mod.getModifiedItemAttr("capacitorNeed"), reverse=True) # Loop through every module until we're above peak recharge # Most efficient first, as we sorted earlier. @@ -1267,16 +1258,16 @@ class Fit(object): return True - def __deepcopy__(self, memo): - copy = Fit() + def __deepcopy__(self, memo=None): + copy_ship = Fit() # Character and owner are not copied - copy.character = self.__character - copy.owner = self.owner - copy.ship = deepcopy(self.ship, memo) - copy.name = "%s copy" % self.name - copy.damagePattern = self.damagePattern - copy.targetResists = self.targetResists - copy.notes = self.notes + copy_ship.character = self.__character + copy_ship.owner = self.owner + copy_ship.ship = deepcopy(self.ship) + copy_ship.name = "%s copy" % self.name + copy_ship.damagePattern = self.damagePattern + copy_ship.targetResists = self.targetResists + copy_ship.notes = self.notes toCopy = ( "modules", @@ -1290,17 +1281,17 @@ class Fit(object): "projectedFighters") for name in toCopy: orig = getattr(self, name) - c = getattr(copy, name) + c = getattr(copy_ship, name) for i in orig: - c.append(deepcopy(i, memo)) + c.append(deepcopy(i)) for fit in self.projectedFits: - copy.__projectedFits[fit.ID] = fit + copy_ship.__projectedFits[fit.ID] = fit # this bit is required -- see GH issue # 83 eos.db.saveddata_session.flush() eos.db.saveddata_session.refresh(fit) - return copy + return copy_ship def __repr__(self): return u"Fit(ID={}, ship={}, name={}) at {}".format( diff --git a/eos/saveddata/implant.py b/eos/saveddata/implant.py index a77190bc4..240a51b0f 100644 --- a/eos/saveddata/implant.py +++ b/eos/saveddata/implant.py @@ -17,7 +17,7 @@ # along with eos. If not, see . # =============================================================================== -import logging +from logbook import Logger from sqlalchemy.orm import validates, reconstructor @@ -25,7 +25,7 @@ import eos.db from eos.effectHandlerHelpers import HandledItem from eos.modifiedAttributeDict import ModifiedAttributeDict, ItemAttrShortcut -logger = logging.getLogger(__name__) +pyfalog = Logger(__name__) class Implant(HandledItem, ItemAttrShortcut): @@ -46,11 +46,11 @@ class Implant(HandledItem, ItemAttrShortcut): if self.itemID: self.__item = eos.db.getItem(self.itemID) if self.__item is None: - logger.error("Item (id: %d) does not exist", self.itemID) + pyfalog.error("Item (id: {0}) does not exist", self.itemID) return if self.isInvalid: - logger.error("Item (id: %d) is not an Implant", self.itemID) + pyfalog.error("Item (id: {0}) is not an Implant", self.itemID) return self.build() @@ -78,7 +78,8 @@ class Implant(HandledItem, ItemAttrShortcut): def item(self): return self.__item - def __calculateSlot(self, item): + @staticmethod + def __calculateSlot(item): if "implantness" not in item.attributes: raise ValueError("Passed item is not an implant") @@ -98,9 +99,9 @@ class Implant(HandledItem, ItemAttrShortcut): @validates("fitID", "itemID", "active") def validator(self, key, val): - map = {"fitID": lambda val: isinstance(val, int), - "itemID": lambda val: isinstance(val, int), - "active": lambda val: isinstance(val, bool)} + map = {"fitID": lambda _val: isinstance(_val, int), + "itemID": lambda _val: isinstance(_val, int), + "active": lambda _val: isinstance(_val, bool)} if not map[key](val): raise ValueError(str(val) + " is not a valid value for " + key) diff --git a/eos/saveddata/implantSet.py b/eos/saveddata/implantSet.py index 2daaaa3eb..ca0334fff 100644 --- a/eos/saveddata/implantSet.py +++ b/eos/saveddata/implantSet.py @@ -48,13 +48,13 @@ class ImplantSet(object): return out.strip() - def __deepcopy__(self, memo): + def __deepcopy__(self): copy = ImplantSet(self.name) copy.name = "%s copy" % self.name orig = getattr(self, 'implants') c = getattr(copy, 'implants') for i in orig: - c.append(deepcopy(i, memo)) + c.append(deepcopy(i)) return copy diff --git a/eos/saveddata/module.py b/eos/saveddata/module.py index c3f84ef15..9ea524067 100644 --- a/eos/saveddata/module.py +++ b/eos/saveddata/module.py @@ -1,729 +1,728 @@ -# =============================================================================== -# Copyright (C) 2010 Diego Duclos -# -# This file is part of eos. -# -# eos is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser General Public License as published by -# the Free Software Foundation, either version 2 of the License, or -# (at your option) any later version. -# -# eos is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public License -# along with eos. If not, see . -# =============================================================================== - -import logging - -from sqlalchemy.orm import validates, reconstructor - -import eos.db -from eos.effectHandlerHelpers import HandledItem, HandledCharge -from eos.enum import Enum -from eos.mathUtils import floorFloat -from eos.modifiedAttributeDict import ModifiedAttributeDict, ItemAttrShortcut, ChargeAttrShortcut -from eos.types import Citadel - -logger = logging.getLogger(__name__) - - -class State(Enum): - def __init__(self): - pass - - OFFLINE = -1 - ONLINE = 0 - ACTIVE = 1 - OVERHEATED = 2 - - -class Slot(Enum): - def __init__(self): - pass - - # These are self-explanatory - LOW = 1 - MED = 2 - HIGH = 3 - RIG = 4 - SUBSYSTEM = 5 - # not a real slot, need for pyfa display rack separation - MODE = 6 - # system effects. They are projected "modules" and pyfa assumes all modules - # have a slot. In this case, make one up. - SYSTEM = 7 - # used for citadel services - SERVICE = 8 - # fighter 'slots'. Just easier to put them here... - F_LIGHT = 10 - F_SUPPORT = 11 - F_HEAVY = 12 - - -class Hardpoint(Enum): - def __init__(self): - pass - - NONE = 0 - MISSILE = 1 - TURRET = 2 - - -class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut): - """An instance of this class represents a module together with its charge and modified attributes""" - DAMAGE_TYPES = ("em", "thermal", "kinetic", "explosive") - MINING_ATTRIBUTES = ("miningAmount",) - - def __init__(self, item): - """Initialize a module from the program""" - self.__item = item - - if item is not None and self.isInvalid: - raise ValueError("Passed item is not a Module") - - self.__charge = None - self.itemID = item.ID if item is not None else None - self.projected = False - self.state = State.ONLINE - self.build() - - @reconstructor - def init(self): - """Initialize a module from the database and validate""" - self.__item = None - self.__charge = None - - # we need this early if module is invalid and returns early - self.__slot = self.dummySlot - - if self.itemID: - self.__item = eos.db.getItem(self.itemID) - if self.__item is None: - logger.error("Item (id: %d) does not exist", self.itemID) - return - - if self.isInvalid: - logger.error("Item (id: %d) is not a Module", self.itemID) - return - - if self.chargeID: - self.__charge = eos.db.getItem(self.chargeID) - - self.build() - - def build(self): - """ Builds internal module variables from both init's """ - - if self.__charge and self.__charge.category.name != "Charge": - self.__charge = None - - self.__dps = None - self.__miningyield = None - self.__volley = None - self.__reloadTime = None - self.__reloadForce = None - self.__chargeCycles = None - self.__hardpoint = Hardpoint.NONE - self.__itemModifiedAttributes = ModifiedAttributeDict(parent=self) - self.__chargeModifiedAttributes = ModifiedAttributeDict(parent=self) - self.__slot = self.dummySlot # defaults to None - - if self.__item: - self.__itemModifiedAttributes.original = self.__item.attributes - self.__itemModifiedAttributes.overrides = self.__item.overrides - self.__hardpoint = self.__calculateHardpoint(self.__item) - self.__slot = self.__calculateSlot(self.__item) - if self.__charge: - self.__chargeModifiedAttributes.original = self.__charge.attributes - self.__chargeModifiedAttributes.overrides = self.__charge.overrides - - @classmethod - def buildEmpty(cls, slot): - empty = Module(None) - empty.__slot = slot - empty.dummySlot = slot - return empty - - @classmethod - def buildRack(cls, slot): - empty = Rack(None) - empty.__slot = slot - empty.dummySlot = slot - return empty - - @property - def isEmpty(self): - return self.dummySlot is not None - - @property - def hardpoint(self): - return self.__hardpoint - - @property - def isInvalid(self): - if self.isEmpty: - return False - return self.__item is None or (self.__item.category.name not in ("Module", "Subsystem", "Structure Module") and self.__item.group.name != "Effect Beacon") - - @property - def numCharges(self): - if self.charge is None: - charges = 0 - else: - chargeVolume = self.charge.volume - containerCapacity = self.item.capacity - if chargeVolume is None or containerCapacity is None: - charges = 0 - else: - charges = floorFloat(float(containerCapacity) / chargeVolume) - return charges - - @property - def numShots(self): - if self.charge is None: - return None - if self.__chargeCycles is None and self.charge: - numCharges = self.numCharges - # Usual ammo like projectiles and missiles - if numCharges > 0 and "chargeRate" in self.itemModifiedAttributes: - self.__chargeCycles = self.__calculateAmmoShots() - # Frequency crystals (combat and mining lasers) - elif numCharges > 0 and "crystalsGetDamaged" in self.chargeModifiedAttributes: - self.__chargeCycles = self.__calculateCrystalShots() - # Scripts and stuff - else: - self.__chargeCycles = 0 - return self.__chargeCycles - else: - return self.__chargeCycles - - @property - def hpBeforeReload(self): - """ - If item is some kind of repairer with charges, calculate - HP it reps before going into reload. - """ - cycles = self.numShots - armorRep = self.getModifiedItemAttr("armorDamageAmount") or 0 - shieldRep = self.getModifiedItemAttr("shieldBonus") or 0 - if not cycles or (not armorRep and not shieldRep): - return None - hp = round((armorRep + shieldRep) * cycles) - return hp - - def __calculateAmmoShots(self): - if self.charge is not None: - # Set number of cycles before reload is needed - chargeRate = self.getModifiedItemAttr("chargeRate") - numCharges = self.numCharges - numShots = floorFloat(float(numCharges) / chargeRate) - else: - numShots = None - return numShots - - def __calculateCrystalShots(self): - if self.charge is not None: - if self.getModifiedChargeAttr("crystalsGetDamaged") == 1: - # For depletable crystals, calculate average amount of shots before it's destroyed - hp = self.getModifiedChargeAttr("hp") - chance = self.getModifiedChargeAttr("crystalVolatilityChance") - damage = self.getModifiedChargeAttr("crystalVolatilityDamage") - crystals = self.numCharges - numShots = floorFloat(float(crystals * hp) / (damage * chance)) - else: - # Set 0 (infinite) for permanent crystals like t1 laser crystals - numShots = 0 - else: - numShots = None - return numShots - - @property - def maxRange(self): - attrs = ("maxRange", "shieldTransferRange", "powerTransferRange", - "energyDestabilizationRange", "empFieldRange", - "ecmBurstRange", "warpScrambleRange", "cargoScanRange", - "shipScanRange", "surveyScanRange") - for attr in attrs: - maxRange = self.getModifiedItemAttr(attr) - if maxRange is not None: - return maxRange - if self.charge is not None: - try: - chargeName = self.charge.group.name - except AttributeError: - pass - else: - if chargeName in ("Scanner Probe", "Survey Probe"): - return None - # Source: http://www.eveonline.com/ingameboard.asp?a=topic&threadID=1307419&page=1#15 - # D_m = V_m * (T_m + T_0*[exp(- T_m/T_0)-1]) - maxVelocity = self.getModifiedChargeAttr("maxVelocity") - flightTime = self.getModifiedChargeAttr("explosionDelay") / 1000.0 - mass = self.getModifiedChargeAttr("mass") - agility = self.getModifiedChargeAttr("agility") - if maxVelocity and (flightTime or mass or agility): - accelTime = min(flightTime, mass * agility / 1000000) - # Average distance done during acceleration - duringAcceleration = maxVelocity / 2 * accelTime - # Distance done after being at full speed - fullSpeed = maxVelocity * (flightTime - accelTime) - return duringAcceleration + fullSpeed - - @property - def falloff(self): - attrs = ("falloffEffectiveness", "falloff", "shipScanFalloff") - for attr in attrs: - falloff = self.getModifiedItemAttr(attr) - if falloff is not None: - return falloff - - @property - def slot(self): - return self.__slot - - @property - def itemModifiedAttributes(self): - return self.__itemModifiedAttributes - - @property - def chargeModifiedAttributes(self): - return self.__chargeModifiedAttributes - - @property - def item(self): - return self.__item if self.__item != 0 else None - - @property - def charge(self): - return self.__charge if self.__charge != 0 else None - - @charge.setter - def charge(self, charge): - self.__charge = charge - if charge is not None: - self.chargeID = charge.ID - self.__chargeModifiedAttributes.original = charge.attributes - self.__chargeModifiedAttributes.overrides = charge.overrides - else: - self.chargeID = None - self.__chargeModifiedAttributes.original = None - self.__chargeModifiedAttributes.overrides = {} - - self.__itemModifiedAttributes.clear() - - def damageStats(self, targetResists): - if self.__dps is None: - self.__dps = 0 - self.__volley = 0 - - if not self.isEmpty and self.state >= State.ACTIVE: - if self.charge: - func = self.getModifiedChargeAttr - else: - func = self.getModifiedItemAttr - - volley = sum(map( - lambda attr: (func("%sDamage" % attr) or 0) * (1 - getattr(targetResists, "%sAmount" % attr, 0)), - self.DAMAGE_TYPES)) - volley *= self.getModifiedItemAttr("damageMultiplier") or 1 - if volley: - cycleTime = self.cycleTime - self.__volley = volley - self.__dps = volley / (cycleTime / 1000.0) - - return self.__dps, self.__volley - - @property - def miningStats(self): - if self.__miningyield is None: - if self.isEmpty: - self.__miningyield = 0 - else: - if self.state >= State.ACTIVE: - volley = self.getModifiedItemAttr("specialtyMiningAmount") or self.getModifiedItemAttr( - "miningAmount") or 0 - if volley: - cycleTime = self.cycleTime - self.__miningyield = volley / (cycleTime / 1000.0) - else: - self.__miningyield = 0 - else: - self.__miningyield = 0 - - return self.__miningyield - - @property - def dps(self): - return self.damageStats(None)[0] - - @property - def volley(self): - return self.damageStats(None)[1] - - @property - def reloadTime(self): - # Get reload time from attrs first, then use - # custom value specified otherwise (e.g. in effects) - moduleReloadTime = self.getModifiedItemAttr("reloadTime") - if moduleReloadTime is None: - moduleReloadTime = self.__reloadTime - return moduleReloadTime - - @reloadTime.setter - def reloadTime(self, milliseconds): - self.__reloadTime = milliseconds - - @property - def forceReload(self): - return self.__reloadForce - - @forceReload.setter - def forceReload(self, type): - self.__reloadForce = type - - def fits(self, fit, hardpointLimit=True): - slot = self.slot - if fit.getSlotsFree(slot) <= (0 if self.owner != fit else -1): - return False - - # Check ship type restrictions - fitsOnType = set() - fitsOnGroup = set() - - shipType = self.getModifiedItemAttr("fitsToShipType") - if shipType is not None: - fitsOnType.add(shipType) - - for attr in self.itemModifiedAttributes.keys(): - if attr.startswith("canFitShipType"): - shipType = self.getModifiedItemAttr(attr) - if shipType is not None: - fitsOnType.add(shipType) - - for attr in self.itemModifiedAttributes.keys(): - if attr.startswith("canFitShipGroup"): - shipGroup = self.getModifiedItemAttr(attr) - if shipGroup is not None: - fitsOnGroup.add(shipGroup) - - if (len(fitsOnGroup) > 0 or len( - fitsOnType) > 0) and fit.ship.item.group.ID not in fitsOnGroup and fit.ship.item.ID not in fitsOnType: - return False - - # AFAIK Citadel modules will always be restricted based on canFitShipType/Group. If we are fitting to a Citadel - # and the module does not have these properties, return false to prevent regular ship modules from being used - if isinstance(fit.ship, Citadel) and len(fitsOnGroup) == 0 and len(fitsOnType) == 0: - return False - - # If the mod is a subsystem, don't let two subs in the same slot fit - if self.slot == Slot.SUBSYSTEM: - subSlot = self.getModifiedItemAttr("subSystemSlot") - for mod in fit.modules: - if mod.getModifiedItemAttr("subSystemSlot") == subSlot: - return False - - # Check rig sizes - if self.slot == Slot.RIG: - if self.getModifiedItemAttr("rigSize") != fit.ship.getModifiedItemAttr("rigSize"): - return False - - # Check max group fitted - max = self.getModifiedItemAttr("maxGroupFitted") - if max is not None: - current = 0 if self.owner != fit else -1 - for mod in fit.modules: - if mod.item and mod.item.groupID == self.item.groupID: - current += 1 - - if current >= max: - return False - - # Check this only if we're told to do so - if hardpointLimit: - if self.hardpoint == Hardpoint.TURRET: - if (fit.ship.getModifiedItemAttr('turretSlotsLeft') or 0) - fit.getHardpointsUsed(Hardpoint.TURRET) < 1: - return False - elif self.hardpoint == Hardpoint.MISSILE: - if (fit.ship.getModifiedItemAttr('launcherSlotsLeft') or 0) - fit.getHardpointsUsed( - Hardpoint.MISSILE) < 1: - return False - - return True - - def isValidState(self, state): - """ - Check if the state is valid for this module, without considering other modules at all - """ - # Check if we're within bounds - if state < -1 or state > 2: - return False - elif state >= State.ACTIVE and not self.item.isType("active"): - return False - elif state == State.OVERHEATED and not self.item.isType("overheat"): - return False - else: - return True - - def canHaveState(self, state=None, projectedOnto=None): - """ - Check with other modules if there are restrictions that might not allow this module to be activated - """ - # If we're going to set module to offline or online for local modules or offline for projected, - # it should be fine for all cases - item = self.item - if (state <= State.ONLINE and projectedOnto is None) or (state <= State.OFFLINE): - return True - - # Check if the local module is over it's max limit; if it's not, we're fine - maxGroupActive = self.getModifiedItemAttr("maxGroupActive") - if maxGroupActive is None and projectedOnto is None: - return True - - # Following is applicable only to local modules, we do not want to limit projected - if projectedOnto is None: - currActive = 0 - group = item.group.name - for mod in self.owner.modules: - currItem = getattr(mod, "item", None) - if mod.state >= State.ACTIVE and currItem is not None and currItem.group.name == group: - currActive += 1 - if currActive > maxGroupActive: - break - return currActive <= maxGroupActive - # For projected, we're checking if ship is vulnerable to given item - else: - # Do not allow to apply offensive modules on ship with offensive module immunite, with few exceptions - # (all effects which apply instant modification are exception, generally speaking) - if item.offensive and projectedOnto.ship.getModifiedItemAttr("disallowOffensiveModifiers") == 1: - offensiveNonModifiers = {"energyDestabilizationNew", - "leech", - "energyNosferatuFalloff", - "energyNeutralizerFalloff"} - if not offensiveNonModifiers.intersection(set(item.effects)): - return False - # If assistive modules are not allowed, do not let to apply these altogether - if item.assistive and projectedOnto.ship.getModifiedItemAttr("disallowAssistance") == 1: - return False - return True - - def isValidCharge(self, charge): - # Check sizes, if 'charge size > module volume' it won't fit - if charge is None: - return True - chargeVolume = charge.volume - moduleCapacity = self.item.capacity - if chargeVolume is not None and moduleCapacity is not None and chargeVolume > moduleCapacity: - return False - - itemChargeSize = self.getModifiedItemAttr("chargeSize") - if itemChargeSize > 0: - chargeSize = charge.getAttribute('chargeSize') - if itemChargeSize != chargeSize: - return False - - chargeGroup = charge.groupID - for i in range(5): - itemChargeGroup = self.getModifiedItemAttr('chargeGroup' + str(i)) - if itemChargeGroup is None: - continue - if itemChargeGroup == chargeGroup: - return True - - return False - - def getValidCharges(self): - validCharges = set() - for i in range(5): - itemChargeGroup = self.getModifiedItemAttr('chargeGroup' + str(i)) - if itemChargeGroup is not None: - g = eos.db.getGroup(int(itemChargeGroup), eager=("items.icon", "items.attributes")) - if g is None: - continue - for singleItem in g.items: - if singleItem.published and self.isValidCharge(singleItem): - validCharges.add(singleItem) - - return validCharges - - def __calculateHardpoint(self, item): - effectHardpointMap = {"turretFitted": Hardpoint.TURRET, - "launcherFitted": Hardpoint.MISSILE} - - if item is None: - return Hardpoint.NONE - - for effectName, slot in effectHardpointMap.iteritems(): - if effectName in item.effects: - return slot - - return Hardpoint.NONE - - def __calculateSlot(self, item): - effectSlotMap = {"rigSlot": Slot.RIG, - "loPower": Slot.LOW, - "medPower": Slot.MED, - "hiPower": Slot.HIGH, - "subSystem": Slot.SUBSYSTEM, - "serviceSlot": Slot.SERVICE} - if item is None: - return None - for effectName, slot in effectSlotMap.iteritems(): - if effectName in item.effects: - return slot - if item.group.name == "Effect Beacon": - return Slot.SYSTEM - - raise ValueError("Passed item does not fit in any known slot") - - @validates("ID", "itemID", "ammoID") - def validator(self, key, val): - map = {"ID": lambda val: isinstance(val, int), - "itemID": lambda val: val is None or isinstance(val, int), - "ammoID": lambda val: isinstance(val, int)} - - if not map[key](val): - raise ValueError(str(val) + " is not a valid value for " + key) - else: - return val - - def clear(self): - self.__dps = None - self.__miningyield = None - self.__volley = None - self.__reloadTime = None - self.__reloadForce = None - self.__chargeCycles = None - self.itemModifiedAttributes.clear() - self.chargeModifiedAttributes.clear() - - def calculateModifiedAttributes(self, fit, runTime, forceProjected = False, gang = False): - #We will run the effect when two conditions are met: - #1: It makes sense to run the effect - # The effect is either offline - # or the effect is passive and the module is in the online state (or higher) - - # or the effect is active and the module is in the active state (or higher) - # or the effect is overheat and the module is in the overheated state (or higher) - # 2: the runtimes match - - if self.projected or forceProjected: - context = "projected", "module" - projected = True - else: - context = ("module",) - projected = False - - # if gang: - # context += ("commandRun",) - - if self.charge is not None: - # fix for #82 and it's regression #106 - if not projected or (self.projected and not forceProjected) or gang: - for effect in self.charge.effects.itervalues(): - if effect.runTime == runTime and \ - effect.activeByDefault and \ - (effect.isType("offline") or - (effect.isType("passive") and self.state >= State.ONLINE) or - (effect.isType("active") and self.state >= State.ACTIVE)) and \ - (not gang or (gang and effect.isType("gang"))): - - chargeContext = ("moduleCharge",) - # For gang effects, we pass in the effect itself as an argument. However, to avoid going through - # all the effect files and defining this argument, do a simple try/catch here and be done with it. - # @todo: possibly fix this - try: - effect.handler(fit, self, chargeContext, effect=effect) - except: - effect.handler(fit, self, chargeContext) - - if self.item: - if self.state >= State.OVERHEATED: - for effect in self.item.effects.itervalues(): - if effect.runTime == runTime and \ - effect.isType("overheat") \ - and not forceProjected \ - and effect.activeByDefault \ - and ((gang and effect.isType("gang")) or not gang): - effect.handler(fit, self, context) - - for effect in self.item.effects.itervalues(): - if effect.runTime == runTime and \ - effect.activeByDefault and \ - (effect.isType("offline") or - (effect.isType("passive") and self.state >= State.ONLINE) or - - (effect.isType("active") and self.state >= State.ACTIVE))\ - and ((projected and effect.isType("projected")) or not projected)\ - and ((gang and effect.isType("gang")) or not gang): - try: - effect.handler(fit, self, context, effect=effect) - except: - effect.handler(fit, self, context) - - @property - def cycleTime(self): - reactivation = (self.getModifiedItemAttr("moduleReactivationDelay") or 0) - # Reactivation time starts counting after end of module cycle - speed = self.rawCycleTime + reactivation - if self.charge: - reload = self.reloadTime - else: - reload = 0.0 - # Determine if we'll take into account reload time or not - factorReload = self.owner.factorReload if self.forceReload is None else self.forceReload - # If reactivation is longer than 10 seconds then module can be reloaded - # during reactivation time, thus we may ignore reload - if factorReload and reactivation < reload: - numShots = self.numShots - # Time it takes to reload module after end of reactivation time, - # given that we started when module cycle has just over - additionalReloadTime = (reload - reactivation) - # Speed here already takes into consideration reactivation time - speed = (speed * numShots + additionalReloadTime) / numShots if numShots > 0 else speed - - return speed - - @property - def rawCycleTime(self): - speed = self.getModifiedItemAttr("speed") or self.getModifiedItemAttr("duration") - return speed - - @property - def capUse(self): - capNeed = self.getModifiedItemAttr("capacitorNeed") - if capNeed and self.state >= State.ACTIVE: - cycleTime = self.cycleTime - capUsed = capNeed / (cycleTime / 1000.0) - return capUsed - else: - return 0 - - def __deepcopy__(self, memo): - item = self.item - if item is None: - copy = Module.buildEmpty(self.slot) - else: - copy = Module(self.item) - copy.charge = self.charge - copy.state = self.state - return copy - - def __repr__(self): - if self.item: - return "Module(ID={}, name={}) at {}".format( - self.item.ID, self.item.name, hex(id(self)) - ) - else: - return "EmptyModule() at {}".format(hex(id(self))) - - -class Rack(Module): - """ - This is simply the Module class named something else to differentiate - it for app logic. This class does not do anything special - """ - pass +# =============================================================================== +# Copyright (C) 2010 Diego Duclos +# +# This file is part of eos. +# +# eos is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation, either version 2 of the License, or +# (at your option) any later version. +# +# eos is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with eos. If not, see . +# =============================================================================== + +from logbook import Logger + +from sqlalchemy.orm import validates, reconstructor + +import eos.db +from eos.effectHandlerHelpers import HandledItem, HandledCharge +from eos.enum import Enum +from eos.mathUtils import floorFloat +from eos.modifiedAttributeDict import ModifiedAttributeDict, ItemAttrShortcut, ChargeAttrShortcut +from eos.saveddata.citadel import Citadel + +pyfalog = Logger(__name__) + + +class State(Enum): + OFFLINE = -1 + ONLINE = 0 + ACTIVE = 1 + OVERHEATED = 2 + + +class Slot(Enum): + # These are self-explanatory + LOW = 1 + MED = 2 + HIGH = 3 + RIG = 4 + SUBSYSTEM = 5 + # not a real slot, need for pyfa display rack separation + MODE = 6 + # system effects. They are projected "modules" and pyfa assumes all modules + # have a slot. In this case, make one up. + SYSTEM = 7 + # used for citadel services + SERVICE = 8 + # fighter 'slots'. Just easier to put them here... + F_LIGHT = 10 + F_SUPPORT = 11 + F_HEAVY = 12 + + +class Hardpoint(Enum): + NONE = 0 + MISSILE = 1 + TURRET = 2 + + +class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut): + """An instance of this class represents a module together with its charge and modified attributes""" + DAMAGE_TYPES = ("em", "thermal", "kinetic", "explosive") + MINING_ATTRIBUTES = ("miningAmount",) + + def __init__(self, item): + """Initialize a module from the program""" + self.__item = item + + if item is not None and self.isInvalid: + raise ValueError("Passed item is not a Module") + + self.__charge = None + self.itemID = item.ID if item is not None else None + self.projected = False + self.state = State.ONLINE + self.build() + + @reconstructor + def init(self): + """Initialize a module from the database and validate""" + self.__item = None + self.__charge = None + + # we need this early if module is invalid and returns early + self.__slot = self.dummySlot + + if self.itemID: + self.__item = eos.db.getItem(self.itemID) + if self.__item is None: + pyfalog.error("Item (id: {0}) does not exist", self.itemID) + return + + if self.isInvalid: + pyfalog.error("Item (id: {0}) is not a Module", self.itemID) + return + + if self.chargeID: + self.__charge = eos.db.getItem(self.chargeID) + + self.build() + + def build(self): + """ Builds internal module variables from both init's """ + + if self.__charge and self.__charge.category.name != "Charge": + self.__charge = None + + self.__dps = None + self.__miningyield = None + self.__volley = None + self.__reloadTime = None + self.__reloadForce = None + self.__chargeCycles = None + self.__hardpoint = Hardpoint.NONE + self.__itemModifiedAttributes = ModifiedAttributeDict(parent=self) + self.__chargeModifiedAttributes = ModifiedAttributeDict(parent=self) + self.__slot = self.dummySlot # defaults to None + + if self.__item: + self.__itemModifiedAttributes.original = self.__item.attributes + self.__itemModifiedAttributes.overrides = self.__item.overrides + self.__hardpoint = self.__calculateHardpoint(self.__item) + self.__slot = self.__calculateSlot(self.__item) + if self.__charge: + self.__chargeModifiedAttributes.original = self.__charge.attributes + self.__chargeModifiedAttributes.overrides = self.__charge.overrides + + @classmethod + def buildEmpty(cls, slot): + empty = Module(None) + empty.__slot = slot + empty.dummySlot = slot + return empty + + @classmethod + def buildRack(cls, slot): + empty = Rack(None) + empty.__slot = slot + empty.dummySlot = slot + return empty + + @property + def isEmpty(self): + return self.dummySlot is not None + + @property + def hardpoint(self): + return self.__hardpoint + + @property + def isInvalid(self): + if self.isEmpty: + return False + return self.__item is None or \ + (self.__item.category.name not in ("Module", "Subsystem", "Structure Module") and + self.__item.group.name != "Effect Beacon") + + @property + def numCharges(self): + if self.charge is None: + charges = 0 + else: + chargeVolume = self.charge.volume + containerCapacity = self.item.capacity + if chargeVolume is None or containerCapacity is None: + charges = 0 + else: + charges = floorFloat(float(containerCapacity) / chargeVolume) + return charges + + @property + def numShots(self): + if self.charge is None: + return None + if self.__chargeCycles is None and self.charge: + numCharges = self.numCharges + # Usual ammo like projectiles and missiles + if numCharges > 0 and "chargeRate" in self.itemModifiedAttributes: + self.__chargeCycles = self.__calculateAmmoShots() + # Frequency crystals (combat and mining lasers) + elif numCharges > 0 and "crystalsGetDamaged" in self.chargeModifiedAttributes: + self.__chargeCycles = self.__calculateCrystalShots() + # Scripts and stuff + else: + self.__chargeCycles = 0 + return self.__chargeCycles + else: + return self.__chargeCycles + + @property + def modPosition(self): + if self.owner: + return self.owner.modules.index(self) + + @property + def hpBeforeReload(self): + """ + If item is some kind of repairer with charges, calculate + HP it reps before going into reload. + """ + cycles = self.numShots + armorRep = self.getModifiedItemAttr("armorDamageAmount") or 0 + shieldRep = self.getModifiedItemAttr("shieldBonus") or 0 + if not cycles or (not armorRep and not shieldRep): + return None + hp = round((armorRep + shieldRep) * cycles) + return hp + + def __calculateAmmoShots(self): + if self.charge is not None: + # Set number of cycles before reload is needed + chargeRate = self.getModifiedItemAttr("chargeRate") + numCharges = self.numCharges + numShots = floorFloat(float(numCharges) / chargeRate) + else: + numShots = None + return numShots + + def __calculateCrystalShots(self): + if self.charge is not None: + if self.getModifiedChargeAttr("crystalsGetDamaged") == 1: + # For depletable crystals, calculate average amount of shots before it's destroyed + hp = self.getModifiedChargeAttr("hp") + chance = self.getModifiedChargeAttr("crystalVolatilityChance") + damage = self.getModifiedChargeAttr("crystalVolatilityDamage") + crystals = self.numCharges + numShots = floorFloat(float(crystals * hp) / (damage * chance)) + else: + # Set 0 (infinite) for permanent crystals like t1 laser crystals + numShots = 0 + else: + numShots = None + return numShots + + @property + def maxRange(self): + attrs = ("maxRange", "shieldTransferRange", "powerTransferRange", + "energyDestabilizationRange", "empFieldRange", + "ecmBurstRange", "warpScrambleRange", "cargoScanRange", + "shipScanRange", "surveyScanRange") + for attr in attrs: + maxRange = self.getModifiedItemAttr(attr) + if maxRange is not None: + return maxRange + if self.charge is not None: + try: + chargeName = self.charge.group.name + except AttributeError: + pass + else: + if chargeName in ("Scanner Probe", "Survey Probe"): + return None + # Source: http://www.eveonline.com/ingameboard.asp?a=topic&threadID=1307419&page=1#15 + # D_m = V_m * (T_m + T_0*[exp(- T_m/T_0)-1]) + maxVelocity = self.getModifiedChargeAttr("maxVelocity") + flightTime = self.getModifiedChargeAttr("explosionDelay") / 1000.0 + mass = self.getModifiedChargeAttr("mass") + agility = self.getModifiedChargeAttr("agility") + if maxVelocity and (flightTime or mass or agility): + accelTime = min(flightTime, mass * agility / 1000000) + # Average distance done during acceleration + duringAcceleration = maxVelocity / 2 * accelTime + # Distance done after being at full speed + fullSpeed = maxVelocity * (flightTime - accelTime) + return duringAcceleration + fullSpeed + + @property + def falloff(self): + attrs = ("falloffEffectiveness", "falloff", "shipScanFalloff") + for attr in attrs: + falloff = self.getModifiedItemAttr(attr) + if falloff is not None: + return falloff + + @property + def slot(self): + return self.__slot + + @property + def itemModifiedAttributes(self): + return self.__itemModifiedAttributes + + @property + def chargeModifiedAttributes(self): + return self.__chargeModifiedAttributes + + @property + def item(self): + return self.__item if self.__item != 0 else None + + @property + def charge(self): + return self.__charge if self.__charge != 0 else None + + @charge.setter + def charge(self, charge): + self.__charge = charge + if charge is not None: + self.chargeID = charge.ID + self.__chargeModifiedAttributes.original = charge.attributes + self.__chargeModifiedAttributes.overrides = charge.overrides + else: + self.chargeID = None + self.__chargeModifiedAttributes.original = None + self.__chargeModifiedAttributes.overrides = {} + + self.__itemModifiedAttributes.clear() + + def damageStats(self, targetResists): + if self.__dps is None: + self.__dps = 0 + self.__volley = 0 + + if not self.isEmpty and self.state >= State.ACTIVE: + if self.charge: + func = self.getModifiedChargeAttr + else: + func = self.getModifiedItemAttr + + volley = sum(map( + lambda attr: (func("%sDamage" % attr) or 0) * (1 - getattr(targetResists, "%sAmount" % attr, 0)), + self.DAMAGE_TYPES)) + volley *= self.getModifiedItemAttr("damageMultiplier") or 1 + if volley: + cycleTime = self.cycleTime + self.__volley = volley + self.__dps = volley / (cycleTime / 1000.0) + + return self.__dps, self.__volley + + @property + def miningStats(self): + if self.__miningyield is None: + if self.isEmpty: + self.__miningyield = 0 + else: + if self.state >= State.ACTIVE: + volley = self.getModifiedItemAttr("specialtyMiningAmount") or self.getModifiedItemAttr( + "miningAmount") or 0 + if volley: + cycleTime = self.cycleTime + self.__miningyield = volley / (cycleTime / 1000.0) + else: + self.__miningyield = 0 + else: + self.__miningyield = 0 + + return self.__miningyield + + @property + def dps(self): + return self.damageStats(None)[0] + + @property + def volley(self): + return self.damageStats(None)[1] + + @property + def reloadTime(self): + # Get reload time from attrs first, then use + # custom value specified otherwise (e.g. in effects) + moduleReloadTime = self.getModifiedItemAttr("reloadTime") + if moduleReloadTime is None: + moduleReloadTime = self.__reloadTime + return moduleReloadTime + + @reloadTime.setter + def reloadTime(self, milliseconds): + self.__reloadTime = milliseconds + + @property + def forceReload(self): + return self.__reloadForce + + @forceReload.setter + def forceReload(self, type): + self.__reloadForce = type + + def fits(self, fit, hardpointLimit=True): + slot = self.slot + if fit.getSlotsFree(slot) <= (0 if self.owner != fit else -1): + return False + + # Check ship type restrictions + fitsOnType = set() + fitsOnGroup = set() + + shipType = self.getModifiedItemAttr("fitsToShipType") + if shipType is not None: + fitsOnType.add(shipType) + + for attr in self.itemModifiedAttributes.keys(): + if attr.startswith("canFitShipType"): + shipType = self.getModifiedItemAttr(attr) + if shipType is not None: + fitsOnType.add(shipType) + + for attr in self.itemModifiedAttributes.keys(): + if attr.startswith("canFitShipGroup"): + shipGroup = self.getModifiedItemAttr(attr) + if shipGroup is not None: + fitsOnGroup.add(shipGroup) + + if (len(fitsOnGroup) > 0 or len( + fitsOnType) > 0) and fit.ship.item.group.ID not in fitsOnGroup and fit.ship.item.ID not in fitsOnType: + return False + + # AFAIK Citadel modules will always be restricted based on canFitShipType/Group. If we are fitting to a Citadel + # and the module does not have these properties, return false to prevent regular ship modules from being used + if isinstance(fit.ship, Citadel) and len(fitsOnGroup) == 0 and len(fitsOnType) == 0: + return False + + # If the mod is a subsystem, don't let two subs in the same slot fit + if self.slot == Slot.SUBSYSTEM: + subSlot = self.getModifiedItemAttr("subSystemSlot") + for mod in fit.modules: + if mod.getModifiedItemAttr("subSystemSlot") == subSlot: + return False + + # Check rig sizes + if self.slot == Slot.RIG: + if self.getModifiedItemAttr("rigSize") != fit.ship.getModifiedItemAttr("rigSize"): + return False + + # Check max group fitted + max = self.getModifiedItemAttr("maxGroupFitted") + if max is not None: + current = 0 if self.owner != fit else -1 + for mod in fit.modules: + if mod.item and mod.item.groupID == self.item.groupID: + current += 1 + + if current >= max: + return False + + # Check this only if we're told to do so + if hardpointLimit: + if self.hardpoint == Hardpoint.TURRET: + if (fit.ship.getModifiedItemAttr('turretSlotsLeft') or 0) - fit.getHardpointsUsed(Hardpoint.TURRET) < 1: + return False + elif self.hardpoint == Hardpoint.MISSILE: + if (fit.ship.getModifiedItemAttr('launcherSlotsLeft') or 0) - fit.getHardpointsUsed( + Hardpoint.MISSILE) < 1: + return False + + return True + + def isValidState(self, state): + """ + Check if the state is valid for this module, without considering other modules at all + """ + # Check if we're within bounds + if state < -1 or state > 2: + return False + elif state >= State.ACTIVE and not self.item.isType("active"): + return False + elif state == State.OVERHEATED and not self.item.isType("overheat"): + return False + else: + return True + + def canHaveState(self, state=None, projectedOnto=None): + """ + Check with other modules if there are restrictions that might not allow this module to be activated + """ + # If we're going to set module to offline or online for local modules or offline for projected, + # it should be fine for all cases + item = self.item + if (state <= State.ONLINE and projectedOnto is None) or (state <= State.OFFLINE): + return True + + # Check if the local module is over it's max limit; if it's not, we're fine + maxGroupActive = self.getModifiedItemAttr("maxGroupActive") + if maxGroupActive is None and projectedOnto is None: + return True + + # Following is applicable only to local modules, we do not want to limit projected + if projectedOnto is None: + currActive = 0 + group = item.group.name + for mod in self.owner.modules: + currItem = getattr(mod, "item", None) + if mod.state >= State.ACTIVE and currItem is not None and currItem.group.name == group: + currActive += 1 + if currActive > maxGroupActive: + break + return currActive <= maxGroupActive + # For projected, we're checking if ship is vulnerable to given item + else: + # Do not allow to apply offensive modules on ship with offensive module immunite, with few exceptions + # (all effects which apply instant modification are exception, generally speaking) + if item.offensive and projectedOnto.ship.getModifiedItemAttr("disallowOffensiveModifiers") == 1: + offensiveNonModifiers = {"energyDestabilizationNew", + "leech", + "energyNosferatuFalloff", + "energyNeutralizerFalloff"} + if not offensiveNonModifiers.intersection(set(item.effects)): + return False + # If assistive modules are not allowed, do not let to apply these altogether + if item.assistive and projectedOnto.ship.getModifiedItemAttr("disallowAssistance") == 1: + return False + return True + + def isValidCharge(self, charge): + # Check sizes, if 'charge size > module volume' it won't fit + if charge is None: + return True + chargeVolume = charge.volume + moduleCapacity = self.item.capacity + if chargeVolume is not None and moduleCapacity is not None and chargeVolume > moduleCapacity: + return False + + itemChargeSize = self.getModifiedItemAttr("chargeSize") + if itemChargeSize > 0: + chargeSize = charge.getAttribute('chargeSize') + if itemChargeSize != chargeSize: + return False + + chargeGroup = charge.groupID + for i in range(5): + itemChargeGroup = self.getModifiedItemAttr('chargeGroup' + str(i)) + if itemChargeGroup is None: + continue + if itemChargeGroup == chargeGroup: + return True + + return False + + def getValidCharges(self): + validCharges = set() + for i in range(5): + itemChargeGroup = self.getModifiedItemAttr('chargeGroup' + str(i)) + if itemChargeGroup is not None: + g = eos.db.getGroup(int(itemChargeGroup), eager=("items.icon", "items.attributes")) + if g is None: + continue + for singleItem in g.items: + if singleItem.published and self.isValidCharge(singleItem): + validCharges.add(singleItem) + + return validCharges + + @staticmethod + def __calculateHardpoint(item): + effectHardpointMap = {"turretFitted": Hardpoint.TURRET, + "launcherFitted": Hardpoint.MISSILE} + + if item is None: + return Hardpoint.NONE + + for effectName, slot in effectHardpointMap.iteritems(): + if effectName in item.effects: + return slot + + return Hardpoint.NONE + + @staticmethod + def __calculateSlot(item): + effectSlotMap = {"rigSlot": Slot.RIG, + "loPower": Slot.LOW, + "medPower": Slot.MED, + "hiPower": Slot.HIGH, + "subSystem": Slot.SUBSYSTEM, + "serviceSlot": Slot.SERVICE} + if item is None: + return None + for effectName, slot in effectSlotMap.iteritems(): + if effectName in item.effects: + return slot + if item.group.name == "Effect Beacon": + return Slot.SYSTEM + + raise ValueError("Passed item does not fit in any known slot") + + @validates("ID", "itemID", "ammoID") + def validator(self, key, val): + map = {"ID": lambda _val: isinstance(_val, int), + "itemID": lambda _val: _val is None or isinstance(_val, int), + "ammoID": lambda _val: isinstance(_val, int)} + + if not map[key](val): + raise ValueError(str(val) + " is not a valid value for " + key) + else: + return val + + def clear(self): + self.__dps = None + self.__miningyield = None + self.__volley = None + self.__reloadTime = None + self.__reloadForce = None + self.__chargeCycles = None + self.itemModifiedAttributes.clear() + self.chargeModifiedAttributes.clear() + + def calculateModifiedAttributes(self, fit, runTime, forceProjected=False, gang=False): + # We will run the effect when two conditions are met: + # 1: It makes sense to run the effect + # The effect is either offline + # or the effect is passive and the module is in the online state (or higher) + + # or the effect is active and the module is in the active state (or higher) + # or the effect is overheat and the module is in the overheated state (or higher) + # 2: the runtimes match + + if self.projected or forceProjected: + context = "projected", "module" + projected = True + else: + context = ("module",) + projected = False + + # if gang: + # context += ("commandRun",) + + if self.charge is not None: + # fix for #82 and it's regression #106 + if not projected or (self.projected and not forceProjected) or gang: + for effect in self.charge.effects.itervalues(): + if effect.runTime == runTime and \ + effect.activeByDefault and \ + (effect.isType("offline") or + (effect.isType("passive") and self.state >= State.ONLINE) or + (effect.isType("active") and self.state >= State.ACTIVE)) and \ + (not gang or (gang and effect.isType("gang"))): + + chargeContext = ("moduleCharge",) + # For gang effects, we pass in the effect itself as an argument. However, to avoid going through + # all the effect files and defining this argument, do a simple try/catch here and be done with it. + # @todo: possibly fix this + try: + effect.handler(fit, self, chargeContext, effect=effect) + except: + effect.handler(fit, self, chargeContext) + + if self.item: + if self.state >= State.OVERHEATED: + for effect in self.item.effects.itervalues(): + if effect.runTime == runTime and \ + effect.isType("overheat") \ + and not forceProjected \ + and effect.activeByDefault \ + and ((gang and effect.isType("gang")) or not gang): + effect.handler(fit, self, context) + + for effect in self.item.effects.itervalues(): + if effect.runTime == runTime and \ + effect.activeByDefault and \ + (effect.isType("offline") or + (effect.isType("passive") and self.state >= State.ONLINE) or + (effect.isType("active") and self.state >= State.ACTIVE)) \ + and ((projected and effect.isType("projected")) or not projected) \ + and ((gang and effect.isType("gang")) or not gang): + try: + effect.handler(fit, self, context, effect=effect) + except: + effect.handler(fit, self, context) + + @property + def cycleTime(self): + reactivation = (self.getModifiedItemAttr("moduleReactivationDelay") or 0) + # Reactivation time starts counting after end of module cycle + speed = self.rawCycleTime + reactivation + if self.charge: + reload = self.reloadTime + else: + reload = 0.0 + # Determine if we'll take into account reload time or not + factorReload = self.owner.factorReload if self.forceReload is None else self.forceReload + # If reactivation is longer than 10 seconds then module can be reloaded + # during reactivation time, thus we may ignore reload + if factorReload and reactivation < reload: + numShots = self.numShots + # Time it takes to reload module after end of reactivation time, + # given that we started when module cycle has just over + additionalReloadTime = (reload - reactivation) + # Speed here already takes into consideration reactivation time + speed = (speed * numShots + additionalReloadTime) / numShots if numShots > 0 else speed + + return speed + + @property + def rawCycleTime(self): + speed = self.getModifiedItemAttr("speed") or self.getModifiedItemAttr("duration") + return speed + + @property + def capUse(self): + capNeed = self.getModifiedItemAttr("capacitorNeed") + if capNeed and self.state >= State.ACTIVE: + cycleTime = self.cycleTime + capUsed = capNeed / (cycleTime / 1000.0) + return capUsed + else: + return 0 + + def __deepcopy__(self, memo): + item = self.item + if item is None: + copy = Module.buildEmpty(self.slot) + else: + copy = Module(self.item) + copy.charge = self.charge + copy.state = self.state + return copy + + def __repr__(self): + if self.item: + return "Module(ID={}, name={}) at {}".format( + self.item.ID, self.item.name, hex(id(self)) + ) + else: + return "EmptyModule() at {}".format(hex(id(self))) + + +class Rack(Module): + """ + This is simply the Module class named something else to differentiate + it for app logic. This class does not do anything special + """ + pass diff --git a/eos/saveddata/override.py b/eos/saveddata/override.py index c4f8a1e02..cc7fb9342 100644 --- a/eos/saveddata/override.py +++ b/eos/saveddata/override.py @@ -17,14 +17,14 @@ # along with eos. If not, see . # =============================================================================== -import logging +from logbook import Logger from sqlalchemy.orm import reconstructor import eos.db from eos.eqBase import EqBase -logger = logging.getLogger(__name__) +pyfalog = Logger(__name__) class Override(EqBase): @@ -43,13 +43,13 @@ class Override(EqBase): if self.attrID: self.__attr = eos.db.getAttributeInfo(self.attrID) if self.__attr is None: - logger.error("Attribute (id: %d) does not exist", self.attrID) + pyfalog.error("Attribute (id: {0}) does not exist", self.attrID) return if self.itemID: self.__item = eos.db.getItem(self.itemID) if self.__item is None: - logger.error("Item (id: %d) does not exist", self.itemID) + pyfalog.error("Item (id: {0}) does not exist", self.itemID) return @property diff --git a/eos/saveddata/ship.py b/eos/saveddata/ship.py index 4e4020cc7..1e3fd41f0 100644 --- a/eos/saveddata/ship.py +++ b/eos/saveddata/ship.py @@ -17,14 +17,14 @@ # along with eos. If not, see . # =============================================================================== -import logging +from logbook import Logger import eos.db from eos.effectHandlerHelpers import HandledItem from eos.modifiedAttributeDict import ModifiedAttributeDict, ItemAttrShortcut, cappingAttrKeyCache from eos.saveddata.mode import Mode -logger = logging.getLogger(__name__) +pyfalog = Logger(__name__) class Ship(ItemAttrShortcut, HandledItem): @@ -63,6 +63,7 @@ class Ship(ItemAttrShortcut, HandledItem): def validate(self, item): if item.category.name != "Ship": + pyfalog.error("Passed item '{0}' (category: {1}) is not under Ship category", item.name, item.category.name) raise ValueError( 'Passed item "%s" (category: (%s)) is not under Ship category' % (item.name, item.category.name)) diff --git a/eos/saveddata/targetResists.py b/eos/saveddata/targetResists.py index 8b286b3f6..e50c13791 100644 --- a/eos/saveddata/targetResists.py +++ b/eos/saveddata/targetResists.py @@ -18,6 +18,9 @@ # =============================================================================== import re +from logbook import Logger + +pyfalog = Logger(__name__) class TargetResists(object): @@ -43,7 +46,7 @@ class TargetResists(object): type, data = line.rsplit('=', 1) type, data = type.strip(), data.split(',') except: - # Data isn't in correct format, continue to next line + pyfalog.warning("Data isn't in correct format, continue to next line.") continue if type != "TargetResists": @@ -59,6 +62,7 @@ class TargetResists(object): assert 0 <= val <= 100 fields["%sAmount" % cls.DAMAGE_TYPES[index]] = val / 100 except: + pyfalog.warning("Caught unhandled exception in import patterns.") continue if len(fields) == 4: # Avoid possible blank lines @@ -77,7 +81,12 @@ class TargetResists(object): out += "# TargetResists = [name],[EM %],[Thermal %],[Kinetic %],[Explosive %]\n\n" for dp in patterns: out += cls.EXPORT_FORMAT % ( - dp.name, dp.emAmount * 100, dp.thermalAmount * 100, dp.kineticAmount * 100, dp.explosiveAmount * 100) + dp.name, + dp.emAmount * 100, + dp.thermalAmount * 100, + dp.kineticAmount * 100, + dp.explosiveAmount * 100 + ) return out.strip() diff --git a/eos/saveddata/user.py b/eos/saveddata/user.py index 8c3905a0d..baf09d9f3 100644 --- a/eos/saveddata/user.py +++ b/eos/saveddata/user.py @@ -49,10 +49,10 @@ class User(object): @validates("ID", "username", "password", "admin") def validator(self, key, val): - map = {"ID": lambda val: isinstance(val, int), - "username": lambda val: isinstance(val, basestring), - "password": lambda val: isinstance(val, basestring) and len(val) == 96, - "admin": lambda val: isinstance(val, bool)} + map = {"ID": lambda _val: isinstance(_val, int), + "username": lambda _val: isinstance(_val, basestring), + "password": lambda _val: isinstance(_val, basestring) and len(_val) == 96, + "admin": lambda _val: isinstance(_val, bool)} if not map[key](val): raise ValueError(str(val) + " is not a valid value for " + key) diff --git a/eos/types.py b/eos/types.py deleted file mode 100644 index 1fbf2676b..000000000 --- a/eos/types.py +++ /dev/null @@ -1,45 +0,0 @@ -# =============================================================================== -# Copyright (C) 2010 Diego Duclos -# -# This file is part of eos. -# -# eos is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser General Public License as published by -# the Free Software Foundation, either version 2 of the License, or -# (at your option) any later version. -# -# eos is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public License -# along with eos. If not, see . -# =============================================================================== - -from eos.gamedata import Attribute, Category, Effect, Group, Icon, Item, MarketGroup, \ - MetaGroup, AttributeInfo, Unit, EffectInfo, MetaType, MetaData, Traits -from eos.saveddata.price import Price -from eos.saveddata.user import User -from eos.saveddata.crestchar import CrestChar -from eos.saveddata.damagePattern import DamagePattern -from eos.saveddata.targetResists import TargetResists -from eos.saveddata.character import Character, Skill -from eos.saveddata.ship import Ship -from eos.saveddata.citadel import Citadel -from eos.saveddata.module import Module, State, Slot, Hardpoint, Rack -from eos.saveddata.drone import Drone -from eos.saveddata.fighterAbility import FighterAbility -from eos.saveddata.fighter import Fighter -from eos.saveddata.cargo import Cargo -from eos.saveddata.implant import Implant -from eos.saveddata.implantSet import ImplantSet -# Legacy booster side effect code, disabling as not currently implemented -# from eos.saveddata.booster import SideEffect -from eos.saveddata.booster import Booster -from eos.saveddata.fit import Fit, ImplantLocation -from eos.saveddata.mode import Mode -from eos.saveddata.miscData import MiscData -from eos.saveddata.override import Override - -import eos.db diff --git a/eve.db b/eve.db index a5168b4df..1c5c07a91 100644 Binary files a/eve.db and b/eve.db differ diff --git a/fun.py b/fun.py deleted file mode 100644 index 5f14c34e6..000000000 --- a/fun.py +++ /dev/null @@ -1,5 +0,0 @@ -problem 15 - -number of moves: XxY - -each block added introduces two possible moves diff --git a/gui/PFListPane.py b/gui/PFListPane.py index 9385dbc75..e6689ce8d 100644 --- a/gui/PFListPane.py +++ b/gui/PFListPane.py @@ -1,4 +1,4 @@ -#=============================================================================== +# ============================================================================= # Copyright (C) 2010 Diego Duclos # # This file is part of pyfa. @@ -15,13 +15,16 @@ # # You should have received a copy of the GNU General Public License # along with pyfa. If not, see . -#=============================================================================== +# ============================================================================= +# noinspection PyPackageRequirements import wx + class PFListPane(wx.ScrolledWindow): def __init__(self, parent): - wx.ScrolledWindow.__init__ (self, parent, id=wx.ID_ANY, pos=wx.DefaultPosition, size=wx.Size(1, 1), style=wx.TAB_TRAVERSAL) + wx.ScrolledWindow.__init__(self, parent, id=wx.ID_ANY, pos=wx.DefaultPosition, size=wx.Size(1, 1), + style=wx.TAB_TRAVERSAL) self._wList = [] self._wCount = 0 @@ -29,19 +32,18 @@ class PFListPane(wx.ScrolledWindow): self.SetBackgroundColour(wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOW)) - self.SetVirtualSize((1, 1)) self.SetScrollRate(0, 1) self.Bind(wx.EVT_SCROLLWIN_LINEUP, self.MScrollUp) self.Bind(wx.EVT_SCROLLWIN_LINEDOWN, self.MScrollDown) -# self.Bind(wx.EVT_CHILD_FOCUS, self.OnChildFocus) -# self.Bind(wx.EVT_LEFT_DOWN, self.ForceFocus) + # self.Bind(wx.EVT_CHILD_FOCUS, self.OnChildFocus) + # self.Bind(wx.EVT_LEFT_DOWN, self.ForceFocus) self.SetFocus() -# self.Bind(wx.EVT_MOUSE_CAPTURE_CHANGED, self.ForceFocus) + # self.Bind(wx.EVT_MOUSE_CAPTURE_CHANGED, self.ForceFocus) self.Bind(wx.EVT_SCROLLWIN_THUMBRELEASE, self.ForceFocus) - def ForceFocus(self,event): + def ForceFocus(self, event): if self.FindFocus() and self.FindFocus().Parent != self: self.SetFocus() event.Skip() @@ -67,23 +69,22 @@ class PFListPane(wx.ScrolledWindow): event.Skip() - def ScrollChildIntoView(self, child): """ Scrolls the panel such that the specified child window is in view. """ sppu_x, sppu_y = self.GetScrollPixelsPerUnit() - vs_x, vs_y = self.GetViewStart() + vs_x, vs_y = self.GetViewStart() cr = child.GetRect() clntsz = self.GetSize() new_vs_x, new_vs_y = -1, -1 # is it before the left edge? - if cr.x < 0 and sppu_x > 0: + if cr.x < 0 < sppu_x: new_vs_x = vs_x + (cr.x / sppu_x) # is it above the top? - if cr.y < 0 and sppu_y > 0: + if cr.y < 0 < sppu_y: new_vs_y = vs_y + (cr.y / sppu_y) # For the right and bottom edges, scroll enough to show the @@ -110,8 +111,6 @@ class PFListPane(wx.ScrolledWindow): if new_vs_x != -1 or new_vs_y != -1: self.Scroll(new_vs_x, new_vs_y) - - def AddWidget(self, widget): widget.Reparent(self) self._wList.append(widget) @@ -124,13 +123,11 @@ class PFListPane(wx.ScrolledWindow): def IsWidgetSelectedByContext(self, widget): return False - def RefreshList(self, doRefresh = False, doFocus = False): - ypos = 0 + def RefreshList(self, doRefresh=False, doFocus=False): maxy = 0 - scrollTo = 0 selected = None - for i in xrange( len(self._wList) ): + for i in xrange(len(self._wList)): iwidth, iheight = self._wList[i].GetSize() xa, ya = self.CalcScrolledPosition((0, maxy)) self._wList[i].SetPosition((xa, ya)) @@ -143,16 +140,14 @@ class PFListPane(wx.ScrolledWindow): if selected: self.ScrollChildIntoView(selected) - #selected.SetFocus() + # selected.SetFocus() elif doFocus: self.SetFocus() - clientW,clientH = self.GetSize() - for i in xrange( len(self._wList) ): + for i in xrange(len(self._wList)): iwidth, iheight = self._wList[i].GetSize() - itemX,itemY = self._wList[i].GetPosition() self._wList[i].SetSize((cwidth, iheight)) - if doRefresh == True: + if doRefresh is True: self._wList[i].Refresh() self.itemsHeight = max(self.itemsHeight, iheight - 1) @@ -160,7 +155,6 @@ class PFListPane(wx.ScrolledWindow): child.Destroy() self._wList.remove(child) - def RemoveAllChildren(self): for widget in self._wList: widget.Destroy() diff --git a/gui/PFSearchBox.py b/gui/PFSearchBox.py index 7fff91e9b..f5f8d8c6d 100644 --- a/gui/PFSearchBox.py +++ b/gui/PFSearchBox.py @@ -1,17 +1,17 @@ +# noinspection PyPackageRequirements import wx import gui.utils.colorUtils as colorUtils import gui.utils.drawUtils as drawUtils -from gui.bitmapLoader import BitmapLoader - SearchButton, EVT_SEARCH_BTN = wx.lib.newevent.NewEvent() CancelButton, EVT_CANCEL_BTN = wx.lib.newevent.NewEvent() TextEnter, EVT_TEXT_ENTER = wx.lib.newevent.NewEvent() TextTyped, EVT_TEXT = wx.lib.newevent.NewEvent() + class PFSearchBox(wx.Window): - def __init__(self, parent, id = wx.ID_ANY, value = "", pos = wx.DefaultPosition, size = wx.Size(-1,24), style = 0): - wx.Window.__init__(self, parent, id, pos, size, style = style) + def __init__(self, parent, id=wx.ID_ANY, value="", pos=wx.DefaultPosition, size=wx.Size(-1, 24), style=0): + wx.Window.__init__(self, parent, id, pos, size, style=style) self.isSearchButtonVisible = False self.isCancelButtonVisible = False @@ -35,13 +35,14 @@ class PFSearchBox(wx.Window): self.editX = 0 self.editY = 0 - self.padding = 4 self._hl = False - w,h = size - self.EditBox = wx.TextCtrl(self, wx.ID_ANY, "", wx.DefaultPosition, (-1, h - 2 if 'wxGTK' in wx.PlatformInfo else -1 ), wx.TE_PROCESS_ENTER | (wx.BORDER_NONE if 'wxGTK' in wx.PlatformInfo else 0)) + w, h = size + self.EditBox = wx.TextCtrl(self, wx.ID_ANY, "", wx.DefaultPosition, + (-1, h - 2 if 'wxGTK' in wx.PlatformInfo else -1), + wx.TE_PROCESS_ENTER | (wx.BORDER_NONE if 'wxGTK' in wx.PlatformInfo else 0)) self.Bind(wx.EVT_PAINT, self.OnPaint) self.Bind(wx.EVT_ERASE_BACKGROUND, self.OnEraseBk) @@ -50,7 +51,7 @@ class PFSearchBox(wx.Window): self.Bind(wx.EVT_LEFT_DOWN, self.OnLeftDown) self.Bind(wx.EVT_LEFT_UP, self.OnLeftUp) -# self.EditBox.ChangeValue(self.descriptiveText) + # self.EditBox.ChangeValue(self.descriptiveText) self.EditBox.Bind(wx.EVT_SET_FOCUS, self.OnEditSetFocus) self.EditBox.Bind(wx.EVT_KILL_FOCUS, self.OnEditKillFocus) @@ -68,11 +69,11 @@ class PFSearchBox(wx.Window): wx.PostEvent(self, TextEnter()) event.Skip() - - def OnEditSetFocus(self, event): -# value = self.EditBox.GetValue() -# if value == self.descriptiveText: -# self.EditBox.ChangeValue("") + @staticmethod + def OnEditSetFocus(event): + # value = self.EditBox.GetValue() + # if value == self.descriptiveText: + # self.EditBox.ChangeValue("") event.Skip() def OnEditKillFocus(self, event): @@ -80,10 +81,9 @@ class PFSearchBox(wx.Window): self.Clear() event.Skip() - def Clear(self): self.EditBox.Clear() -# self.EditBox.ChangeValue(self.descriptiveText) + # self.EditBox.ChangeValue(self.descriptiveText) def Focus(self): self.EditBox.SetFocus() @@ -104,14 +104,15 @@ class PFSearchBox(wx.Window): x, y = target px, py = position aX, aY = area - if (px > x and px < x + aX) and (py > y and py < y + aY): + if (x < px < x + aX) and (y < py < y + aY): return True return False def GetButtonsPos(self): - btnpos = [] - btnpos.append( (self.searchButtonX, self.searchButtonY) ) - btnpos.append( (self.cancelButtonX, self.cancelButtonY) ) + btnpos = [ + (self.searchButtonX, self.searchButtonY), + (self.cancelButtonX, self.cancelButtonY) + ] return btnpos def GetButtonsSize(self): @@ -131,8 +132,8 @@ class PFSearchBox(wx.Window): cw = 0 ch = 0 - btnsize.append( (sw,sh)) - btnsize.append( (cw,ch)) + btnsize.append((sw, sh)) + btnsize.append((cw, ch)) return btnsize def OnLeftDown(self, event): @@ -140,7 +141,7 @@ class PFSearchBox(wx.Window): btnsize = self.GetButtonsSize() self.CaptureMouse() - for btn in xrange(2): + for btn in range(2): if self.HitTest(btnpos[btn], event.GetPosition(), btnsize[btn]): if btn == 0: if not self.searchButtonPressed: @@ -158,7 +159,7 @@ class PFSearchBox(wx.Window): if self.HasCapture(): self.ReleaseMouse() - for btn in xrange(2): + for btn in range(2): if self.HitTest(btnpos[btn], event.GetPosition(), btnsize[btn]): if btn == 0: if self.searchButtonPressed: @@ -218,9 +219,9 @@ class PFSearchBox(wx.Window): editWidth, editHeight = self.EditBox.GetSize() - self.editY = (cheight - editHeight)/2 + self.editY = (cheight - editHeight) / 2 self.EditBox.SetPosition((self.editX, self.editY)) - self.EditBox.SetSize( (self.cancelButtonX - self.padding - self.editX, -1)) + self.EditBox.SetSize((self.cancelButtonX - self.padding - self.editX, -1)) def OnPaint(self, event): dc = wx.BufferedPaintDC(self) @@ -246,7 +247,6 @@ class PFSearchBox(wx.Window): dc.DrawBitmap(self.searchBitmapShadow, self.searchButtonX + 1, self.searchButtonY + 1) dc.DrawBitmap(self.searchBitmap, self.searchButtonX + spad, self.searchButtonY + spad) - if self.isCancelButtonVisible: if self.cancelBitmap: if self.cancelButtonPressed: @@ -256,8 +256,8 @@ class PFSearchBox(wx.Window): dc.DrawBitmap(self.cancelBitmapShadow, self.cancelButtonX + 1, self.cancelButtonY + 1) dc.DrawBitmap(self.cancelBitmap, self.cancelButtonX + cpad, self.cancelButtonY + cpad) - dc.SetPen(wx.Pen(sepColor,1)) - dc.DrawLine(0,rect.height - 1, rect.width, rect.height - 1) + dc.SetPen(wx.Pen(sepColor, 1)) + dc.DrawLine(0, rect.height - 1, rect.width, rect.height - 1) def SetSearchBitmap(self, bitmap): self.searchBitmap = bitmap @@ -273,10 +273,10 @@ class PFSearchBox(wx.Window): def IsCancelButtonVisible(self): return self.isCancelButtonVisible - def ShowSearchButton(self, show = True): + def ShowSearchButton(self, show=True): self.isSearchButtonVisible = show - def ShowCancelButton(self, show = True): + def ShowCancelButton(self, show=True): self.isCancelButtonVisible = show def SetDescriptiveText(self, text): @@ -284,4 +284,3 @@ class PFSearchBox(wx.Window): def GetDescriptiveText(self): return self.descriptiveText - diff --git a/gui/aboutData.py b/gui/aboutData.py index be29b6298..6e8ce07bc 100644 --- a/gui/aboutData.py +++ b/gui/aboutData.py @@ -1,4 +1,4 @@ -#=============================================================================== +# ============================================================================= # Copyright (C) 2010 Diego Duclos # # This file is part of pyfa. @@ -15,7 +15,8 @@ # # You should have received a copy of the GNU General Public License # along with pyfa. If not, see . -#=============================================================================== +# ============================================================================= + import config versionString = "{0} {1} - {2} {3}".format(config.version, config.tag, config.expansionName, config.expansionVersion) @@ -27,7 +28,7 @@ licenses = ( ) developers = ( "blitzmann \tSable Blitzmann (maintainer)", - "cncfanatics \tSakari Orisi (retired)" , + "cncfanatics \tSakari Orisi (retired)", "DarkPhoenix \tKadesh Priestess (retired)", "Darriele \t\tDarriele (retired)", "Ebag333 \t\tEbag Trescientas" diff --git a/gui/additionsPane.py b/gui/additionsPane.py index e67963ee1..503fc4135 100644 --- a/gui/additionsPane.py +++ b/gui/additionsPane.py @@ -1,4 +1,4 @@ -#=============================================================================== +# ============================================================================= # Copyright (C) 2010 Diego Duclos # # This file is part of pyfa. @@ -15,10 +15,10 @@ # # You should have received a copy of the GNU General Public License # along with pyfa. If not, see . -#=============================================================================== +# ============================================================================= +# noinspection PyPackageRequirements import wx -import gui.mainFrame from gui.boosterView import BoosterView from gui.droneView import DroneView from gui.fighterView import FighterView @@ -31,13 +31,13 @@ from gui.notesView import NotesView from gui.pyfatogglepanel import TogglePanel from gui.bitmapLoader import BitmapLoader -import gui.chromeTabs +from gui.chromeTabs import PFNotebook + class AdditionsPane(TogglePanel): - def __init__(self, parent): - TogglePanel.__init__(self, parent, forceLayout = 1) + TogglePanel.__init__(self, parent, forceLayout=1) self.SetLabel("Additions") pane = self.GetContentPane() @@ -45,9 +45,7 @@ class AdditionsPane(TogglePanel): baseSizer = wx.BoxSizer(wx.HORIZONTAL) pane.SetSizer(baseSizer) - self.mainFrame = gui.mainFrame.MainFrame.getInstance() - - self.notebook = gui.chromeTabs.PFNotebook(pane, False) + self.notebook = PFNotebook(pane, False) self.notebook.SetMinSize((-1, 1000)) baseSizer.Add(self.notebook, 1, wx.EXPAND) @@ -62,28 +60,28 @@ class AdditionsPane(TogglePanel): notesImg = BitmapLoader.getImage("skill_small", "gui") self.drone = DroneView(self.notebook) - self.notebook.AddPage(self.drone, "Drones", tabImage = droneImg, showClose = False) + self.notebook.AddPage(self.drone, "Drones", tabImage=droneImg, showClose=False) self.fighter = FighterView(self.notebook) - self.notebook.AddPage(self.fighter, "Fighters", tabImage = fighterImg, showClose = False) + self.notebook.AddPage(self.fighter, "Fighters", tabImage=fighterImg, showClose=False) self.cargo = CargoView(self.notebook) - self.notebook.AddPage(self.cargo, "Cargo", tabImage = cargoImg, showClose = False) + self.notebook.AddPage(self.cargo, "Cargo", tabImage=cargoImg, showClose=False) self.implant = ImplantView(self.notebook) - self.notebook.AddPage(self.implant, "Implants", tabImage = implantImg, showClose = False) + self.notebook.AddPage(self.implant, "Implants", tabImage=implantImg, showClose=False) self.booster = BoosterView(self.notebook) - self.notebook.AddPage(self.booster, "Boosters", tabImage = boosterImg, showClose = False) + self.notebook.AddPage(self.booster, "Boosters", tabImage=boosterImg, showClose=False) self.projectedPage = ProjectedView(self.notebook) - self.notebook.AddPage(self.projectedPage, "Projected", tabImage = projectedImg, showClose = False) + self.notebook.AddPage(self.projectedPage, "Projected", tabImage=projectedImg, showClose=False) self.gangPage = CommandView(self.notebook) - self.notebook.AddPage(self.gangPage, "Command", tabImage = gangImg, showClose = False) + self.notebook.AddPage(self.gangPage, "Command", tabImage=gangImg, showClose=False) self.notes = NotesView(self.notebook) - self.notebook.AddPage(self.notes, "Notes", tabImage = notesImg, showClose = False) + self.notebook.AddPage(self.notes, "Notes", tabImage=notesImg, showClose=False) self.notebook.SetSelection(0) @@ -92,20 +90,17 @@ class AdditionsPane(TogglePanel): def select(self, name): self.notebook.SetSelection(self.PANES.index(name)) - def toggleBoosters(self, event): - self.notebook.ToggleShown(self.booster) - def getName(self, idx): return self.PANES[idx] def toggleContent(self, event): TogglePanel.toggleContent(self, event) - h = self.headerPanel.GetSize()[1]+4 + h = self.headerPanel.GetSize()[1] + 4 if self.IsCollapsed(): self.old_pos = self.parent.GetSashPosition() self.parent.SetMinimumPaneSize(h) - self.parent.SetSashPosition(h*-1, True) + self.parent.SetSashPosition(h * -1, True) # only available in >= wx2.9 if getattr(self.parent, "SetSashInvisible", None): self.parent.SetSashInvisible(True) @@ -114,4 +109,3 @@ class AdditionsPane(TogglePanel): self.parent.SetSashInvisible(False) self.parent.SetMinimumPaneSize(200) self.parent.SetSashPosition(self.old_pos, True) - diff --git a/gui/bitmapLoader.py b/gui/bitmapLoader.py index 45026bedb..eb53b1f1d 100644 --- a/gui/bitmapLoader.py +++ b/gui/bitmapLoader.py @@ -1,4 +1,4 @@ -#=============================================================================== +# ============================================================================= # Copyright (C) 2010 Diego Duclos # # This file is part of pyfa. @@ -15,24 +15,32 @@ # # You should have received a copy of the GNU General Public License # along with pyfa. If not, see . -#=============================================================================== +# ============================================================================= -import os.path -import config -import wx -import zipfile import cStringIO +import os.path +import zipfile + +# noinspection PyPackageRequirements +import wx + +import config + +from logbook import Logger +logging = Logger(__name__) try: from collections import OrderedDict except ImportError: from utils.compat import OrderedDict -class BitmapLoader(): +class BitmapLoader(object): try: archive = zipfile.ZipFile(os.path.join(config.pyfaPath, 'imgs.zip'), 'r') + logging.info("Using zipped image files.") except IOError: + logging.info("Using local image files.") archive = None cachedBitmaps = OrderedDict() @@ -42,7 +50,7 @@ class BitmapLoader(): @classmethod def getStaticBitmap(cls, name, parent, location): static = wx.StaticBitmap(parent) - static.SetBitmap(cls.getBitmap(name,location)) + static.SetBitmap(cls.getBitmap(name, location)) return static @classmethod @@ -83,11 +91,11 @@ class BitmapLoader(): sbuf = cStringIO.StringIO(img_data) return wx.ImageFromStream(sbuf) except KeyError: - print "Missing icon file from zip: {0}".format(path) + print("Missing icon file from zip: {0}".format(path)) else: - path = os.path.join(config.pyfaPath, 'imgs', location, filename) + path = os.path.join(config.pyfaPath, 'imgs' + os.sep + location + os.sep + filename) if os.path.exists(path): return wx.Image(path) else: - print "Missing icon file: {0}".format(path) + print("Missing icon file: {0}".format(path)) diff --git a/gui/boosterView.py b/gui/boosterView.py index 5c8dca993..fe0d802d3 100644 --- a/gui/boosterView.py +++ b/gui/boosterView.py @@ -1,4 +1,4 @@ -#=============================================================================== +# ============================================================================= # Copyright (C) 2010 Diego Duclos # # This file is part of pyfa. @@ -15,29 +15,32 @@ # # You should have received a copy of the GNU General Public License # along with pyfa. If not, see . -#=============================================================================== +# ============================================================================= +# noinspection PyPackageRequirements import wx -import service import gui.display as d import gui.globalEvents as GE -import gui.marketBrowser as mb +import gui.marketBrowser as marketBrowser from gui.builtinViewColumns.state import State from gui.contextMenu import ContextMenu +from service.fit import Fit + class BoosterViewDrop(wx.PyDropTarget): - def __init__(self, dropFn): - wx.PyDropTarget.__init__(self) - self.dropFn = dropFn - # this is really transferring an EVE itemID - self.dropData = wx.PyTextDataObject() - self.SetDataObject(self.dropData) + def __init__(self, dropFn, *args, **kwargs): + super(BoosterViewDrop, self).__init__(*args, **kwargs) + self.dropFn = dropFn + # this is really transferring an EVE itemID + self.dropData = wx.PyTextDataObject() + self.SetDataObject(self.dropData) + + def OnData(self, x, y, t): + if self.GetData(): + data = self.dropData.GetText().split(':') + self.dropFn(x, y, data) + return t - def OnData(self, x, y, t): - if self.GetData(): - data = self.dropData.GetText().split(':') - self.dropFn(x, y, data) - return t class BoosterView(d.Display): DEFAULT_COLS = ["State", @@ -50,7 +53,7 @@ class BoosterView(d.Display): self.lastFitId = None self.mainFrame.Bind(GE.FIT_CHANGED, self.fitChanged) - self.mainFrame.Bind(mb.ITEM_SELECTED, self.addItem) + self.mainFrame.Bind(marketBrowser.ITEM_SELECTED, self.addItem) self.Bind(wx.EVT_LEFT_DCLICK, self.removeItem) self.Bind(wx.EVT_LEFT_DOWN, self.click) @@ -58,24 +61,24 @@ class BoosterView(d.Display): self.SetDropTarget(BoosterViewDrop(self.handleListDrag)) - if "__WXGTK__" in wx.PlatformInfo: + if "__WXGTK__" in wx.PlatformInfo: self.Bind(wx.EVT_RIGHT_UP, self.scheduleMenu) else: self.Bind(wx.EVT_RIGHT_DOWN, self.scheduleMenu) def handleListDrag(self, x, y, data): - ''' + """ Handles dragging of items from various pyfa displays which support it data is list with two indices: data[0] is hard-coded str of originating source data[1] is typeID or index of data we want to manipulate - ''' + """ if data[0] == "market": - wx.PostEvent(self.mainFrame, mb.ItemSelected(itemID=int(data[1]))) + wx.PostEvent(self.mainFrame, marketBrowser.ItemSelected(itemID=int(data[1]))) - def kbEvent(self,event): + def kbEvent(self, event): keycode = event.GetKeyCode() if keycode == wx.WXK_DELETE or keycode == wx.WXK_NUMPAD_DELETE: row = self.GetFirstSelected() @@ -85,12 +88,12 @@ class BoosterView(d.Display): event.Skip() def fitChanged(self, event): - sFit = service.Fit.getInstance() + sFit = Fit.getInstance() fit = sFit.getFit(event.fitID) self.Parent.Parent.DisablePage(self, not fit or fit.isStructure) - #Clear list and get out if current fitId is None + # Clear list and get out if current fitId is None if event.fitID is None and self.lastFitId is not None: self.DeleteAllItems() self.lastFitId = None @@ -115,7 +118,7 @@ class BoosterView(d.Display): event.Skip() def addItem(self, event): - sFit = service.Fit.getInstance() + sFit = Fit.getInstance() fitID = self.mainFrame.getActiveFit() fit = sFit.getFit(fitID) @@ -139,7 +142,7 @@ class BoosterView(d.Display): def removeBooster(self, booster): fitID = self.mainFrame.getActiveFit() - sFit = service.Fit.getInstance() + sFit = Fit.getInstance() sFit.removeBooster(fitID, self.origional.index(booster)) wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=fitID)) @@ -150,11 +153,10 @@ class BoosterView(d.Display): col = self.getColumn(event.Position) if col == self.getColIndex(State): fitID = self.mainFrame.getActiveFit() - sFit = service.Fit.getInstance() + sFit = Fit.getInstance() sFit.toggleBooster(fitID, row) wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=fitID)) - def scheduleMenu(self, event): event.Skip() if self.getColumn(event.Position) != self.getColIndex(State): @@ -163,7 +165,7 @@ class BoosterView(d.Display): def spawnMenu(self): sel = self.GetFirstSelected() if sel != -1: - sFit = service.Fit.getInstance() + sFit = Fit.getInstance() fit = sFit.getFit(self.mainFrame.getActiveFit()) item = fit.boosters[sel] diff --git a/gui/builtinContextMenus/__init__.py b/gui/builtinContextMenus/__init__.py index 10e44a72b..bb8631623 100644 --- a/gui/builtinContextMenus/__init__.py +++ b/gui/builtinContextMenus/__init__.py @@ -1,6 +1,6 @@ __all__ = [ "openFit", - #"moduleGlobalAmmoPicker", + # "moduleGlobalAmmoPicker", "moduleAmmoPicker", "itemStats", "damagePattern", diff --git a/gui/builtinContextMenus/ammoPattern.py b/gui/builtinContextMenus/ammoPattern.py index d8d789b68..e131bb1c6 100644 --- a/gui/builtinContextMenus/ammoPattern.py +++ b/gui/builtinContextMenus/ammoPattern.py @@ -1,35 +1,38 @@ -from gui.contextMenu import ContextMenu -import gui.mainFrame -import service -import wx -import gui.globalEvents as GE - -class AmmoPattern(ContextMenu): - def __init__(self): - self.mainFrame = gui.mainFrame.MainFrame.getInstance() - - def display(self, srcContext, selection): - if srcContext not in ("marketItemGroup", "marketItemMisc") or self.mainFrame.getActiveFit() is None: - return False - - item = selection[0] - for attr in ("emDamage", "thermalDamage", "explosiveDamage", "kineticDamage"): - if item.getAttribute(attr) is not None: - return True - - return False - - def getText(self, itmContext, selection): - return "Set {0} as Damage Pattern".format(itmContext if itmContext is not None else "Item") - - def activate(self, fullContext, selection, i): - item = selection[0] - fit = self.mainFrame.getActiveFit() - sFit = service.Fit.getInstance() - sFit.setAsPattern(fit, item) - wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=fit)) - - def getBitmap(self, context, selection): - return None - -AmmoPattern.register() +from gui.contextMenu import ContextMenu +import gui.mainFrame +# noinspection PyPackageRequirements +import wx +import gui.globalEvents as GE +from service.fit import Fit + + +class AmmoPattern(ContextMenu): + def __init__(self): + self.mainFrame = gui.mainFrame.MainFrame.getInstance() + + def display(self, srcContext, selection): + if srcContext not in ("marketItemGroup", "marketItemMisc") or self.mainFrame.getActiveFit() is None: + return False + + item = selection[0] + for attr in ("emDamage", "thermalDamage", "explosiveDamage", "kineticDamage"): + if item.getAttribute(attr) is not None: + return True + + return False + + def getText(self, itmContext, selection): + return "Set {0} as Damage Pattern".format(itmContext if itmContext is not None else "Item") + + def activate(self, fullContext, selection, i): + item = selection[0] + fit = self.mainFrame.getActiveFit() + sFit = Fit.getInstance() + sFit.setAsPattern(fit, item) + wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=fit)) + + def getBitmap(self, context, selection): + return None + + +AmmoPattern.register() diff --git a/gui/builtinContextMenus/amount.py b/gui/builtinContextMenus/amount.py index 99acdf247..32eaf2026 100644 --- a/gui/builtinContextMenus/amount.py +++ b/gui/builtinContextMenus/amount.py @@ -1,17 +1,20 @@ from gui.contextMenu import ContextMenu -from gui.itemStats import ItemStatsDialog -import eos.types +from eos.saveddata.fit import Fit as es_Fit import gui.mainFrame -import service import gui.globalEvents as GE +# noinspection PyPackageRequirements import wx +from service.fit import Fit +from eos.saveddata.cargo import Cargo as es_Cargo +from eos.saveddata.fighter import Fighter as es_Fighter + class ChangeAmount(ContextMenu): def __init__(self): self.mainFrame = gui.mainFrame.MainFrame.getInstance() def display(self, srcContext, selection): - return srcContext in ("cargoItem","projectedFit","fighterItem","projectedFighter") + return srcContext in ("cargoItem", "projectedFit", "fighterItem", "projectedFighter") def getText(self, itmContext, selection): return "Change {0} Quantity".format(itmContext) @@ -21,10 +24,11 @@ class ChangeAmount(ContextMenu): dlg = AmountChanger(self.mainFrame, selection[0], srcContext) dlg.ShowModal() + ChangeAmount.register() -class AmountChanger(wx.Dialog): +class AmountChanger(wx.Dialog): def __init__(self, parent, thing, context): wx.Dialog.__init__(self, parent, title="Select Amount", size=wx.Size(220, 60)) self.thing = thing @@ -46,31 +50,36 @@ class AmountChanger(wx.Dialog): self.button.Bind(wx.EVT_BUTTON, self.change) def change(self, event): - sFit = service.Fit.getInstance() + if self.input.GetLineText(0).strip() == '': + event.Skip() + self.Close() + return + + sFit = Fit.getInstance() mainFrame = gui.mainFrame.MainFrame.getInstance() fitID = mainFrame.getActiveFit() - if isinstance(self.thing, eos.types.Cargo): - sFit.addCargo(fitID, self.thing.item.ID, int(self.input.GetLineText(0)), replace=True) - elif isinstance(self.thing, eos.types.Fit): - sFit.changeAmount(fitID, self.thing, int(self.input.GetLineText(0))) - elif isinstance(self.thing, eos.types.Fighter): - sFit.changeActiveFighters(fitID, self.thing, int(self.input.GetLineText(0))) + if isinstance(self.thing, es_Cargo): + sFit.addCargo(fitID, self.thing.item.ID, int(float(self.input.GetLineText(0))), replace=True) + elif isinstance(self.thing, es_Fit): + sFit.changeAmount(fitID, self.thing, int(float(self.input.GetLineText(0)))) + elif isinstance(self.thing, es_Fighter): + sFit.changeActiveFighters(fitID, self.thing, int(float(self.input.GetLineText(0)))) wx.PostEvent(mainFrame, GE.FitChanged(fitID=fitID)) event.Skip() self.Close() - ## checks to make sure it's valid number - def onChar(self, event): + # checks to make sure it's valid number + @staticmethod + def onChar(event): key = event.GetKeyCode() acceptable_characters = "1234567890" - acceptable_keycode = [3, 22, 13, 8, 127] # modifiers like delete, copy, paste + acceptable_keycode = [3, 22, 13, 8, 127] # modifiers like delete, copy, paste if key in acceptable_keycode or key >= 255 or (key < 255 and chr(key) in acceptable_characters): event.Skip() return else: return False - diff --git a/gui/builtinContextMenus/cargo.py b/gui/builtinContextMenus/cargo.py index 0b4c031b4..97ea7b8ab 100644 --- a/gui/builtinContextMenus/cargo.py +++ b/gui/builtinContextMenus/cargo.py @@ -1,17 +1,17 @@ from gui.contextMenu import ContextMenu -from gui.itemStats import ItemStatsDialog -import eos.types import gui.mainFrame -import service import gui.globalEvents as GE +# noinspection PyPackageRequirements import wx +from service.fit import Fit + class Cargo(ContextMenu): def __init__(self): self.mainFrame = gui.mainFrame.MainFrame.getInstance() def display(self, srcContext, selection): - sFit = service.Fit.getInstance() + sFit = Fit.getInstance() fitID = self.mainFrame.getActiveFit() fit = sFit.getFit(fitID) @@ -24,7 +24,7 @@ class Cargo(ContextMenu): return "Add {0} to Cargo".format(itmContext) def activate(self, fullContext, selection, i): - sFit = service.Fit.getInstance() + sFit = Fit.getInstance() fitID = self.mainFrame.getActiveFit() typeID = int(selection[0].ID) @@ -32,4 +32,5 @@ class Cargo(ContextMenu): self.mainFrame.additionsPane.select("Cargo") wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=fitID)) + Cargo.register() diff --git a/gui/builtinContextMenus/changeAffectingSkills.py b/gui/builtinContextMenus/changeAffectingSkills.py index 5111a8d27..6c02d07b9 100644 --- a/gui/builtinContextMenus/changeAffectingSkills.py +++ b/gui/builtinContextMenus/changeAffectingSkills.py @@ -1,11 +1,14 @@ # -*- coding: utf-8 -*- from gui.contextMenu import ContextMenu import gui.mainFrame -import service +# noinspection PyPackageRequirements import wx from gui.bitmapLoader import BitmapLoader -from eos.types import Skill +from eos.saveddata.character import Skill import gui.globalEvents as GE +from service.fit import Fit +from service.character import Character + class ChangeAffectingSkills(ContextMenu): def __init__(self): @@ -15,18 +18,18 @@ class ChangeAffectingSkills(ContextMenu): if self.mainFrame.getActiveFit() is None or srcContext not in ("fittingModule", "fittingCharge", "fittingShip"): return False - self.sChar = service.Character.getInstance() - self.sFit = service.Fit.getInstance() + self.sChar = Character.getInstance() + self.sFit = Fit.getInstance() fit = self.sFit.getFit(self.mainFrame.getActiveFit()) self.charID = fit.character.ID - #if self.sChar.getCharName(self.charID) in ("All 0", "All 5"): + # if self.sChar.getCharName(self.charID) in ("All 0", "All 5"): # return False if srcContext == "fittingShip": fitID = self.mainFrame.getActiveFit() - sFit = service.Fit.getInstance() + sFit = Fit.getInstance() self.stuff = sFit.getFit(fitID).ship cont = sFit.getFit(fitID).ship.itemModifiedAttributes elif srcContext == "fittingCharge": @@ -99,4 +102,5 @@ class ChangeAffectingSkills(ContextMenu): wx.PostEvent(self.mainFrame, GE.CharListUpdated()) wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=fitID)) + ChangeAffectingSkills.register() diff --git a/gui/builtinContextMenus/damagePattern.py b/gui/builtinContextMenus/damagePattern.py index 8d9b6be32..20e1b4343 100644 --- a/gui/builtinContextMenus/damagePattern.py +++ b/gui/builtinContextMenus/damagePattern.py @@ -1,14 +1,17 @@ from gui.contextMenu import ContextMenu import gui.mainFrame -import service import gui.globalEvents as GE +# noinspection PyPackageRequirements import wx from gui.bitmapLoader import BitmapLoader +from service.fit import Fit +from service.damagePattern import DamagePattern as import_DamagePattern try: from collections import OrderedDict except ImportError: - from gui.utils.compat import OrderedDict + from utils.compat import OrderedDict + class DamagePattern(ContextMenu): def __init__(self): @@ -18,8 +21,8 @@ class DamagePattern(ContextMenu): return srcContext == "resistancesViewFull" and self.mainFrame.getActiveFit() is not None def getText(self, itmContext, selection): - sDP = service.DamagePattern.getInstance() - sFit = service.Fit.getInstance() + sDP = import_DamagePattern.getInstance() + sFit = Fit.getInstance() fitID = self.mainFrame.getActiveFit() self.fit = sFit.getFit(fitID) @@ -34,9 +37,9 @@ class DamagePattern(ContextMenu): for pattern in self.patterns: start, end = pattern.name.find('['), pattern.name.find(']') if start is not -1 and end is not -1: - currBase = pattern.name[start+1:end] + currBase = pattern.name[start + 1:end] # set helper attr - setattr(pattern, "_name", pattern.name[end+1:].strip()) + setattr(pattern, "_name", pattern.name[end + 1:].strip()) if currBase not in self.subMenus: self.subMenus[currBase] = [] self.subMenus[currBase].append(pattern) @@ -59,7 +62,7 @@ class DamagePattern(ContextMenu): menuItem.pattern = pattern # determine active pattern - sFit = service.Fit.getInstance() + sFit = Fit.getInstance() fitID = self.mainFrame.getActiveFit() f = sFit.getFit(fitID) dp = f.damagePattern @@ -98,10 +101,11 @@ class DamagePattern(ContextMenu): event.Skip() return - sFit = service.Fit.getInstance() + sFit = Fit.getInstance() fitID = self.mainFrame.getActiveFit() sFit.setDamagePattern(fitID, pattern) - setattr(self.mainFrame,"_activeDmgPattern", pattern) + setattr(self.mainFrame, "_activeDmgPattern", pattern) wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=fitID)) + DamagePattern.register() diff --git a/gui/builtinContextMenus/droneRemoveStack.py b/gui/builtinContextMenus/droneRemoveStack.py index f5c94853b..b8e4b820b 100644 --- a/gui/builtinContextMenus/droneRemoveStack.py +++ b/gui/builtinContextMenus/droneRemoveStack.py @@ -1,8 +1,10 @@ from gui.contextMenu import ContextMenu import gui.mainFrame -import service +# noinspection PyPackageRequirements import wx import gui.globalEvents as GE +from service.fit import Fit + class ItemRemove(ContextMenu): def __init__(self): @@ -15,7 +17,7 @@ class ItemRemove(ContextMenu): return "Remove {0} Stack".format(itmContext) def activate(self, fullContext, selection, i): - sFit = service.Fit.getInstance() + sFit = Fit.getInstance() fitID = self.mainFrame.getActiveFit() fit = sFit.getFit(fitID) @@ -24,4 +26,5 @@ class ItemRemove(ContextMenu): wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=fitID)) + ItemRemove.register() diff --git a/gui/builtinContextMenus/droneSplit.py b/gui/builtinContextMenus/droneSplit.py index f4d2b5a44..77ba87e8a 100644 --- a/gui/builtinContextMenus/droneSplit.py +++ b/gui/builtinContextMenus/droneSplit.py @@ -1,59 +1,60 @@ -from gui.contextMenu import ContextMenu -from gui.itemStats import ItemStatsDialog -import gui.mainFrame -import gui.globalEvents as GE -import service -import wx - -class DroneSplit(ContextMenu): - def __init__(self): - self.mainFrame = gui.mainFrame.MainFrame.getInstance() - - def display(self, srcContext, selection): - return srcContext in ("droneItem", "projectedDrone") and selection[0].amount > 1 - - def getText(self, itmContext, selection): - return "Split {0} Stack".format(itmContext) - - def activate(self, fullContext, selection, i): - srcContext = fullContext[0] - dlg = DroneSpinner(self.mainFrame, selection[0], srcContext) - dlg.ShowModal() - dlg.Destroy() - -DroneSplit.register() - - -class DroneSpinner(wx.Dialog): - - def __init__(self, parent, drone, context): - wx.Dialog.__init__(self, parent, title="Select Amount", size=wx.Size(220, 60)) - self.drone = drone - self.context = context - - bSizer1 = wx.BoxSizer(wx.HORIZONTAL) - - self.spinner = wx.SpinCtrl(self) - self.spinner.SetRange(1, drone.amount - 1) - self.spinner.SetValue(1) - - bSizer1.Add(self.spinner, 0, wx.ALL, 5) - - self.button = wx.Button(self, wx.ID_OK, u"Split") - bSizer1.Add(self.button, 0, wx.ALL, 5) - - self.SetSizer(bSizer1) - self.Layout() - self.Centre(wx.BOTH) - self.button.Bind(wx.EVT_BUTTON, self.split) - - def split(self, event): - sFit = service.Fit.getInstance() - mainFrame = gui.mainFrame.MainFrame.getInstance() - fitID = mainFrame.getActiveFit() - if self.context == "droneItem": - sFit.splitDroneStack(fitID, self.drone, self.spinner.GetValue()) - else: - sFit.splitProjectedDroneStack(fitID, self.drone, self.spinner.GetValue()) - wx.PostEvent(mainFrame, GE.FitChanged(fitID=fitID)) - event.Skip() +from gui.contextMenu import ContextMenu +import gui.mainFrame +import gui.globalEvents as GE +from service.fit import Fit +# noinspection PyPackageRequirements +import wx + + +class DroneSplit(ContextMenu): + def __init__(self): + self.mainFrame = gui.mainFrame.MainFrame.getInstance() + + def display(self, srcContext, selection): + return srcContext in ("droneItem", "projectedDrone") and selection[0].amount > 1 + + def getText(self, itmContext, selection): + return "Split {0} Stack".format(itmContext) + + def activate(self, fullContext, selection, i): + srcContext = fullContext[0] + dlg = DroneSpinner(self.mainFrame, selection[0], srcContext) + dlg.ShowModal() + dlg.Destroy() + + +DroneSplit.register() + + +class DroneSpinner(wx.Dialog): + def __init__(self, parent, drone, context): + wx.Dialog.__init__(self, parent, title="Select Amount", size=wx.Size(220, 60)) + self.drone = drone + self.context = context + + bSizer1 = wx.BoxSizer(wx.HORIZONTAL) + + self.spinner = wx.SpinCtrl(self) + self.spinner.SetRange(1, drone.amount - 1) + self.spinner.SetValue(1) + + bSizer1.Add(self.spinner, 0, wx.ALL, 5) + + self.button = wx.Button(self, wx.ID_OK, u"Split") + bSizer1.Add(self.button, 0, wx.ALL, 5) + + self.SetSizer(bSizer1) + self.Layout() + self.Centre(wx.BOTH) + self.button.Bind(wx.EVT_BUTTON, self.split) + + def split(self, event): + sFit = Fit.getInstance() + mainFrame = gui.mainFrame.MainFrame.getInstance() + fitID = mainFrame.getActiveFit() + if self.context == "droneItem": + sFit.splitDroneStack(fitID, self.drone, self.spinner.GetValue()) + else: + sFit.splitProjectedDroneStack(fitID, self.drone, self.spinner.GetValue()) + wx.PostEvent(mainFrame, GE.FitChanged(fitID=fitID)) + event.Skip() diff --git a/gui/builtinContextMenus/factorReload.py b/gui/builtinContextMenus/factorReload.py index c258240ea..86553c73b 100644 --- a/gui/builtinContextMenus/factorReload.py +++ b/gui/builtinContextMenus/factorReload.py @@ -1,35 +1,37 @@ -from gui.contextMenu import ContextMenu -import gui.mainFrame -import service -import gui.globalEvents as GE -import wx -from gui.bitmapLoader import BitmapLoader - -class FactorReload(ContextMenu): - def __init__(self): - self.mainFrame = gui.mainFrame.MainFrame.getInstance() - - def display(self, srcContext, selection): - return srcContext == "firepowerViewFull" and self.mainFrame.getActiveFit() is not None - - def getText(self, itmContext, selection): - return "Factor in Reload Time" - - def activate(self, fullContext, selection, i): - sFit = service.Fit.getInstance() - sFit.serviceFittingOptions["useGlobalForceReload"] = not sFit.serviceFittingOptions["useGlobalForceReload"] - fitID = self.mainFrame.getActiveFit() - sFit.refreshFit(fitID) - wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=fitID)) - - def getBitmap(self, context, selection): - sFit = service.Fit.getInstance() - fitID = self.mainFrame.getActiveFit() - fit = sFit.getFit(fitID) - if fit.factorReload: - return BitmapLoader.getBitmap("state_active_small", "gui") - else: - return None - - -FactorReload.register() +from gui.contextMenu import ContextMenu +import gui.mainFrame +import gui.globalEvents as GE +# noinspection PyPackageRequirements +import wx +from gui.bitmapLoader import BitmapLoader +from service.fit import Fit + + +class FactorReload(ContextMenu): + def __init__(self): + self.mainFrame = gui.mainFrame.MainFrame.getInstance() + + def display(self, srcContext, selection): + return srcContext == "firepowerViewFull" and self.mainFrame.getActiveFit() is not None + + def getText(self, itmContext, selection): + return "Factor in Reload Time" + + def activate(self, fullContext, selection, i): + sFit = Fit.getInstance() + sFit.serviceFittingOptions["useGlobalForceReload"] = not sFit.serviceFittingOptions["useGlobalForceReload"] + fitID = self.mainFrame.getActiveFit() + sFit.refreshFit(fitID) + wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=fitID)) + + def getBitmap(self, context, selection): + sFit = Fit.getInstance() + fitID = self.mainFrame.getActiveFit() + fit = sFit.getFit(fitID) + if fit.factorReload: + return BitmapLoader.getBitmap("state_active_small", "gui") + else: + return None + + +FactorReload.register() diff --git a/gui/builtinContextMenus/fighterAbilities.py b/gui/builtinContextMenus/fighterAbilities.py index 9a86c23b1..8c26a9ffe 100644 --- a/gui/builtinContextMenus/fighterAbilities.py +++ b/gui/builtinContextMenus/fighterAbilities.py @@ -1,8 +1,10 @@ +# noinspection PyPackageRequirements import wx from gui.contextMenu import ContextMenu import gui.mainFrame -import service import gui.globalEvents as GE +from service.fit import Fit + class FighterAbility(ContextMenu): def __init__(self): @@ -48,9 +50,10 @@ class FighterAbility(ContextMenu): event.Skip() return - sFit = service.Fit.getInstance() + sFit = Fit.getInstance() fitID = self.mainFrame.getActiveFit() sFit.toggleFighterAbility(fitID, ability) wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=fitID)) + FighterAbility.register() diff --git a/gui/builtinContextMenus/implantSets.py b/gui/builtinContextMenus/implantSets.py index 8dcf6dd1f..994c0d607 100644 --- a/gui/builtinContextMenus/implantSets.py +++ b/gui/builtinContextMenus/implantSets.py @@ -1,8 +1,12 @@ from gui.contextMenu import ContextMenu import gui.mainFrame -import service import gui.globalEvents as GE +# noinspection PyPackageRequirements import wx +from service.implantSet import ImplantSets as s_ImplantSets +from service.character import Character +from service.fit import Fit + class ImplantSets(ContextMenu): def __init__(self): @@ -32,7 +36,7 @@ class ImplantSets(ContextMenu): m = wx.Menu() bindmenu = rootMenu if "wxMSW" in wx.PlatformInfo else m - sIS = service.ImplantSets.getInstance() + sIS = s_ImplantSets.getInstance() implantSets = sIS.getImplantSetList() self.context = context @@ -59,7 +63,7 @@ class ImplantSets(ContextMenu): if self.context == "implantEditor": # we are calling from character editor, the implant source is different - sChar = service.Character.getInstance() + sChar = Character.getInstance() charID = self.selection.getActiveCharacter() for implant in set.implants: @@ -67,7 +71,7 @@ class ImplantSets(ContextMenu): wx.PostEvent(self.selection, GE.CharChanged()) else: - sFit = service.Fit.getInstance() + sFit = Fit.getInstance() fitID = self.mainFrame.getActiveFit() for implant in set.implants: sFit.addImplant(fitID, implant.item.ID, recalc=implant == set.implants[-1]) diff --git a/gui/builtinContextMenus/itemRemove.py b/gui/builtinContextMenus/itemRemove.py index c355d98b5..fb8a70679 100644 --- a/gui/builtinContextMenus/itemRemove.py +++ b/gui/builtinContextMenus/itemRemove.py @@ -1,8 +1,10 @@ from gui.contextMenu import ContextMenu import gui.mainFrame -import service +# noinspection PyPackageRequirements import wx import gui.globalEvents as GE +from service.fit import Fit + class ItemRemove(ContextMenu): def __init__(self): @@ -21,15 +23,15 @@ class ItemRemove(ContextMenu): def activate(self, fullContext, selection, i): srcContext = fullContext[0] - sFit = service.Fit.getInstance() + sFit = Fit.getInstance() fitID = self.mainFrame.getActiveFit() fit = sFit.getFit(fitID) if srcContext == "fittingModule": for module in selection: if module is not None: - sFit.removeModule(fitID,fit.modules.index(module)) - elif srcContext in ("fittingCharge" , "projectedCharge"): + sFit.removeModule(fitID, fit.modules.index(module)) + elif srcContext in ("fittingCharge", "projectedCharge"): sFit.setAmmo(fitID, None, selection) elif srcContext == "droneItem": sFit.removeDrone(fitID, fit.drones.index(selection[0])) @@ -47,6 +49,4 @@ class ItemRemove(ContextMenu): wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=fitID)) - - ItemRemove.register() diff --git a/gui/builtinContextMenus/itemStats.py b/gui/builtinContextMenus/itemStats.py index df059e782..1b923ecf5 100644 --- a/gui/builtinContextMenus/itemStats.py +++ b/gui/builtinContextMenus/itemStats.py @@ -1,65 +1,68 @@ -from gui.contextMenu import ContextMenu -from gui.itemStats import ItemStatsDialog -import gui.mainFrame -import service -import wx - -class ItemStats(ContextMenu): - def __init__(self): - self.mainFrame = gui.mainFrame.MainFrame.getInstance() - - def display(self, srcContext, selection): - - return srcContext in ("marketItemGroup", "marketItemMisc", - "fittingModule", "fittingCharge", - "fittingShip", "baseShip", - "cargoItem", "droneItem", - "implantItem", "boosterItem", - "skillItem", "projectedModule", - "projectedDrone", "projectedCharge", - "itemStats", "fighterItem", - "implantItemChar", "projectedFighter", - "fittingMode") - - def getText(self, itmContext, selection): - return "{0} Stats".format(itmContext if itmContext is not None else "Item") - - def activate(self, fullContext, selection, i): - srcContext = fullContext[0] - if srcContext == "fittingShip": - fitID = self.mainFrame.getActiveFit() - sFit = service.Fit.getInstance() - stuff = sFit.getFit(fitID).ship - elif srcContext == "fittingMode": - stuff = selection[0].item - else: - stuff = selection[0] - - if srcContext == "fittingModule" and stuff.isEmpty: - return - - mstate = wx.GetMouseState() - reuse = False - - if mstate.CmdDown(): - reuse = True - - if self.mainFrame.GetActiveStatsWindow() is None and reuse: - ItemStatsDialog(stuff, fullContext) - - elif reuse: - lastWnd = self.mainFrame.GetActiveStatsWindow() - pos = lastWnd.GetPosition() - maximized = lastWnd.IsMaximized() - if not maximized: - size = lastWnd.GetSize() - else: - size = wx.DefaultSize - pos = wx.DefaultPosition - ItemStatsDialog(stuff, fullContext, pos, size, maximized) - lastWnd.closeEvent(None) - - else: - ItemStatsDialog(stuff, fullContext) - -ItemStats.register() +from gui.contextMenu import ContextMenu +from gui.itemStats import ItemStatsDialog +import gui.mainFrame +# noinspection PyPackageRequirements +import wx +from service.fit import Fit + + +class ItemStats(ContextMenu): + def __init__(self): + self.mainFrame = gui.mainFrame.MainFrame.getInstance() + + def display(self, srcContext, selection): + + return srcContext in ("marketItemGroup", "marketItemMisc", + "fittingModule", "fittingCharge", + "fittingShip", "baseShip", + "cargoItem", "droneItem", + "implantItem", "boosterItem", + "skillItem", "projectedModule", + "projectedDrone", "projectedCharge", + "itemStats", "fighterItem", + "implantItemChar", "projectedFighter", + "fittingMode") + + def getText(self, itmContext, selection): + return "{0} Stats".format(itmContext if itmContext is not None else "Item") + + def activate(self, fullContext, selection, i): + srcContext = fullContext[0] + if srcContext == "fittingShip": + fitID = self.mainFrame.getActiveFit() + sFit = Fit.getInstance() + stuff = sFit.getFit(fitID).ship + elif srcContext == "fittingMode": + stuff = selection[0].item + else: + stuff = selection[0] + + if srcContext == "fittingModule" and stuff.isEmpty: + return + + mstate = wx.GetMouseState() + reuse = False + + if mstate.CmdDown(): + reuse = True + + if self.mainFrame.GetActiveStatsWindow() is None and reuse: + ItemStatsDialog(stuff, fullContext) + + elif reuse: + lastWnd = self.mainFrame.GetActiveStatsWindow() + pos = lastWnd.GetPosition() + maximized = lastWnd.IsMaximized() + if not maximized: + size = lastWnd.GetSize() + else: + size = wx.DefaultSize + pos = wx.DefaultPosition + ItemStatsDialog(stuff, fullContext, pos, size, maximized) + lastWnd.closeEvent(None) + + else: + ItemStatsDialog(stuff, fullContext) + + +ItemStats.register() diff --git a/gui/builtinContextMenus/marketJump.py b/gui/builtinContextMenus/marketJump.py index 75884576d..e96292b41 100644 --- a/gui/builtinContextMenus/marketJump.py +++ b/gui/builtinContextMenus/marketJump.py @@ -1,48 +1,49 @@ -from gui.contextMenu import ContextMenu -from gui.itemStats import ItemStatsDialog -import gui.mainFrame -import service - -class MarketJump(ContextMenu): - def __init__(self): - self.mainFrame = gui.mainFrame.MainFrame.getInstance() - - def display(self, srcContext, selection): - validContexts = ("marketItemMisc", "fittingModule", - "fittingCharge", "droneItem", - "implantItem", "boosterItem", - "projectedModule", "projectedDrone", - "projectedCharge", "cargoItem", - "implantItemChar", "fighterItem", - "projectedDrone") - - if not srcContext in validContexts or selection is None or len(selection) < 1: - return False - - sMkt = service.Market.getInstance() - item = getattr(selection[0], "item", selection[0]) - mktGrp = sMkt.getMarketGroupByItem(item) - - # 1663 is Special Edition Festival Assets, we don't have root group for it - if mktGrp is None or mktGrp.ID == 1663: - return False - - doit = not selection[0].isEmpty if srcContext == "fittingModule" else True - return doit - - def getText(self, itmContext, selection): - return "{0} Market Group".format(itmContext if itmContext is not None else "Item") - - def activate(self, fullContext, selection, i): - srcContext = fullContext[0] - if srcContext in ("fittingCharge", "projectedCharge"): - item = selection[0].charge - elif hasattr(selection[0], "item"): - item = selection[0].item - else: - item = selection[0] - - self.mainFrame.notebookBrowsers.SetSelection(0) - self.mainFrame.marketBrowser.jump(item) - -MarketJump.register() +from gui.contextMenu import ContextMenu +import gui.mainFrame +from service.market import Market + + +class MarketJump(ContextMenu): + def __init__(self): + self.mainFrame = gui.mainFrame.MainFrame.getInstance() + + def display(self, srcContext, selection): + validContexts = ("marketItemMisc", "fittingModule", + "fittingCharge", "droneItem", + "implantItem", "boosterItem", + "projectedModule", "projectedDrone", + "projectedCharge", "cargoItem", + "implantItemChar", "fighterItem", + "projectedDrone") + + if srcContext not in validContexts or selection is None or len(selection) < 1: + return False + + sMkt = Market.getInstance() + item = getattr(selection[0], "item", selection[0]) + mktGrp = sMkt.getMarketGroupByItem(item) + + # 1663 is Special Edition Festival Assets, we don't have root group for it + if mktGrp is None or mktGrp.ID == 1663: + return False + + doit = not selection[0].isEmpty if srcContext == "fittingModule" else True + return doit + + def getText(self, itmContext, selection): + return "{0} Market Group".format(itmContext if itmContext is not None else "Item") + + def activate(self, fullContext, selection, i): + srcContext = fullContext[0] + if srcContext in ("fittingCharge", "projectedCharge"): + item = selection[0].charge + elif hasattr(selection[0], "item"): + item = selection[0].item + else: + item = selection[0] + + self.mainFrame.notebookBrowsers.SetSelection(0) + self.mainFrame.marketBrowser.jump(item) + + +MarketJump.register() diff --git a/gui/builtinContextMenus/metaSwap.py b/gui/builtinContextMenus/metaSwap.py index ff40ef8a0..7e2f8edf7 100644 --- a/gui/builtinContextMenus/metaSwap.py +++ b/gui/builtinContextMenus/metaSwap.py @@ -1,10 +1,14 @@ -# -*- coding: utf-8 -*- -from gui.contextMenu import ContextMenu -from gui.itemStats import ItemStatsDialog -import gui.mainFrame -import service +# coding: utf-8 + +# noinspection PyPackageRequirements import wx + +from service.fit import Fit +from service.market import Market +import gui.mainFrame import gui.globalEvents as GE +from gui.contextMenu import ContextMenu + class MetaSwap(ContextMenu): def __init__(self): @@ -23,7 +27,7 @@ class MetaSwap(ContextMenu): # Check if list of variations is same for all of selection # If not - don't show the menu - mkt = service.Market.getInstance() + mkt = Market.getInstance() self.variations = None for i in selection: variations = mkt.getVariationsByItems([i.item]) @@ -47,7 +51,8 @@ class MetaSwap(ContextMenu): self.moduleLookup = {} def get_metalevel(x): - if "metaLevel" not in x.attributes: return 0 + if "metaLevel" not in x.attributes: + return 0 return x.attributes["metaLevel"].value def get_metagroup(x): @@ -94,10 +99,10 @@ class MetaSwap(ContextMenu): event.Skip() return - sFit = service.Fit.getInstance() + sFit = Fit.getInstance() fitID = self.mainFrame.getActiveFit() fit = sFit.getFit(fitID) - + for selected_item in self.selection: if type(selected_item).__name__== 'Module': pos = fit.modules.index(selected_item) @@ -153,4 +158,5 @@ class MetaSwap(ContextMenu): wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=fitID)) + MetaSwap.register() diff --git a/gui/builtinContextMenus/moduleAmmoPicker.py b/gui/builtinContextMenus/moduleAmmoPicker.py index 3e7ee02cd..b6791662d 100644 --- a/gui/builtinContextMenus/moduleAmmoPicker.py +++ b/gui/builtinContextMenus/moduleAmmoPicker.py @@ -1,223 +1,233 @@ -# -*- coding: utf-8 -*- -from gui.contextMenu import ContextMenu -import gui.mainFrame -import service -import wx -from gui.bitmapLoader import BitmapLoader -from eos.types import Hardpoint -import gui.globalEvents as GE - -class ModuleAmmoPicker(ContextMenu): - DAMAGE_TYPES = ("em", "explosive", "kinetic", "thermal") - MISSILE_ORDER = ("em", "thermal", "kinetic", "explosive", "mixed") - - def __init__(self): - self.mainFrame = gui.mainFrame.MainFrame.getInstance() - - def display(self, srcContext, selection): - if self.mainFrame.getActiveFit() is None or srcContext not in ("fittingModule", "projectedModule"): - return False - - modules = selection if srcContext == "fittingModule" else (selection[0],) - - validCharges = None - checkedTypes = set() - - for mod in modules: - # loop through modules and gather list of valid charges - if mod.item.ID in checkedTypes: - continue - checkedTypes.add(mod.item.ID) - currCharges = mod.getValidCharges() - if len(currCharges) > 0: - if validCharges is not None and validCharges != currCharges: - return False - - validCharges = currCharges - self.module = mod - - if validCharges is None: - return False - - self.modules = modules - self.charges = list(filter(lambda charge: service.Market.getInstance().getPublicityByItem(charge), validCharges)) - return len(self.charges) > 0 - - def getText(self, itmContext, selection): - return "Charge" - - def turretSorter(self, charge): - damage = 0 - range = (self.module.getModifiedItemAttr("maxRange") or 0) * (charge.getAttribute("weaponRangeMultiplier") or 1) - falloff = (self.module.getModifiedItemAttr("falloff") or 0) * (charge.getAttribute("fallofMultiplier") or 1) - for type in self.DAMAGE_TYPES: - d = charge.getAttribute("%sDamage" % type) - if d > 0: - damage += d - - # Take optimal and half falloff as range factor - rangeFactor = range + falloff / 2 - - return - rangeFactor, charge.name.rsplit()[-2:], damage, charge.name - - def missileSorter(self, charge): - # Get charge damage type and total damage - chargeDamageType, totalDamage = self.damageInfo(charge) - # Find its position in sort list - position = self.MISSILE_ORDER.index(chargeDamageType) - return position, totalDamage, charge.name - - def damageInfo(self, charge): - # Set up data storage for missile damage stuff - damageMap = {} - totalDamage = 0 - # Fill them with the data about charge - for damageType in self.DAMAGE_TYPES: - currentDamage = charge.getAttribute("{0}Damage".format(damageType)) or 0 - damageMap[damageType] = currentDamage - totalDamage += currentDamage - # Detect type of ammo - chargeDamageType = None - for damageType in damageMap: - # If all damage belongs to certain type purely, set appropriate - # ammoType - if damageMap[damageType] == totalDamage: - chargeDamageType = damageType - break - # Else consider ammo as mixed damage - if chargeDamageType is None: - chargeDamageType = "mixed" - - return chargeDamageType, totalDamage - - def numericConverter(self, string): - return int(string) if string.isdigit() else string - - def nameSorter(self, charge): - parts = charge.name.split(" ") - return map(self.numericConverter, parts) - - def addCharge(self, menu, charge): - id = ContextMenu.nextID() - name = charge.name if charge is not None else "Empty" - self.chargeIds[id] = charge - item = wx.MenuItem(menu, id, name) - menu.Bind(wx.EVT_MENU, self.handleAmmoSwitch, item) - item.charge = charge - if charge is not None and charge.icon is not None: - bitmap = BitmapLoader.getBitmap(charge.icon.iconFile, "icons") - if bitmap is not None: - item.SetBitmap(bitmap) - - return item - - def addSeperator(self, m, text): - id = ContextMenu.nextID() - m.Append(id, u'─ %s ─' % text) - m.Enable(id, False) - - def getSubMenu(self, context, selection, rootMenu, i, pitem): - msw = True if "wxMSW" in wx.PlatformInfo else False - m = wx.Menu() - self.chargeIds = {} - hardpoint = self.module.hardpoint - moduleName = self.module.item.name - # Make sure we do not consider mining turrets as combat turrets - if hardpoint == Hardpoint.TURRET and self.module.getModifiedItemAttr("miningAmount") is None: - self.addSeperator(m, "Long Range") - items = [] - range = None - nameBase = None - sub = None - self.charges.sort(key=self.turretSorter) - for charge in self.charges: - # fix issue 71 - will probably have to change if CCP adds more Orbital ammo - if "Orbital" in charge.name: - # uncomment if we ever want to include Oribital ammo in ammo picker - see issue #71 - # This allows us to hide the ammo, but it's still loadable from the market - #item = self.addCharge(m, charge) - #items.append(item) - continue - currBase = charge.name.rsplit()[-2:] - currRange = charge.getAttribute("weaponRangeMultiplier") - if nameBase is None or range != currRange or nameBase != currBase: - if sub is not None: - self.addSeperator(sub, "More Damage") - - sub = None - base = charge - nameBase = currBase - range = currRange - item = self.addCharge(rootMenu if msw else m, charge) - items.append(item) - else: - if sub is None: - sub = wx.Menu() - sub.Bind(wx.EVT_MENU, self.handleAmmoSwitch) - self.addSeperator(sub, "Less Damage") - item.SetSubMenu(sub) - sub.AppendItem(self.addCharge(rootMenu if msw else sub, base)) - - sub.AppendItem(self.addCharge(rootMenu if msw else sub, charge)) - - if sub is not None: - self.addSeperator(sub, "More Damage") - - for item in items: - m.AppendItem(item) - - self.addSeperator(m, "Short Range") - elif hardpoint == Hardpoint.MISSILE and moduleName != 'Festival Launcher': - self.charges.sort(key=self.missileSorter) - type = None - sub = None - defender = None - for charge in self.charges: - currType = self.damageInfo(charge)[0] - - if currType != type or type is None: - if sub is not None: - self.addSeperator(sub, "More Damage") - - type = currType - item = wx.MenuItem(m, wx.ID_ANY, type.capitalize()) - bitmap = BitmapLoader.getBitmap("%s_small" % type, "gui") - if bitmap is not None: - item.SetBitmap(bitmap) - - sub = wx.Menu() - sub.Bind(wx.EVT_MENU, self.handleAmmoSwitch) - self.addSeperator(sub, "Less Damage") - item.SetSubMenu(sub) - m.AppendItem(item) - - if charge.name not in ("Light Defender Missile I", "Heavy Defender Missile I"): - sub.AppendItem(self.addCharge(rootMenu if msw else sub, charge)) - else: - defender = charge - - if defender is not None: - m.AppendItem(self.addCharge(rootMenu if msw else m, defender)) - if sub is not None: - self.addSeperator(sub, "More Damage") - else: - self.charges.sort(key=self.nameSorter) - for charge in self.charges: - m.AppendItem(self.addCharge(rootMenu if msw else m, charge)) - - m.AppendItem(self.addCharge(rootMenu if msw else m, None)) - return m - - def handleAmmoSwitch(self, event): - charge = self.chargeIds.get(event.Id, False) - if charge is False: - event.Skip() - return - - sFit = service.Fit.getInstance() - fitID = self.mainFrame.getActiveFit() - - sFit.setAmmo(fitID, charge.ID if charge is not None else None, self.modules) - wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=fitID)) - -ModuleAmmoPicker.register() +# coding: utf-8 + +# noinspection PyPackageRequirements +import wx + +from service.fit import Fit +from service.market import Market +from eos.saveddata.module import Hardpoint +import gui.mainFrame +import gui.globalEvents as GE +from gui.contextMenu import ContextMenu +from gui.bitmapLoader import BitmapLoader + + +class ModuleAmmoPicker(ContextMenu): + DAMAGE_TYPES = ("em", "explosive", "kinetic", "thermal") + MISSILE_ORDER = ("em", "thermal", "kinetic", "explosive", "mixed") + + def __init__(self): + self.mainFrame = gui.mainFrame.MainFrame.getInstance() + + def display(self, srcContext, selection): + if self.mainFrame.getActiveFit() is None or srcContext not in ("fittingModule", "projectedModule"): + return False + + modules = selection if srcContext == "fittingModule" else (selection[0],) + + validCharges = None + checkedTypes = set() + + for mod in modules: + # loop through modules and gather list of valid charges + if mod.item.ID in checkedTypes: + continue + checkedTypes.add(mod.item.ID) + currCharges = mod.getValidCharges() + if len(currCharges) > 0: + if validCharges is not None and validCharges != currCharges: + return False + + validCharges = currCharges + self.module = mod + + if validCharges is None: + return False + + self.modules = modules + self.charges = list(filter(lambda charge: Market.getInstance().getPublicityByItem(charge), validCharges)) + return len(self.charges) > 0 + + def getText(self, itmContext, selection): + return "Charge" + + def turretSorter(self, charge): + damage = 0 + range_ = (self.module.getModifiedItemAttr("maxRange") or 0) * \ + (charge.getAttribute("weaponRangeMultiplier") or 1) + falloff = (self.module.getModifiedItemAttr("falloff") or 0) * \ + (charge.getAttribute("fallofMultiplier") or 1) + for type_ in self.DAMAGE_TYPES: + d = charge.getAttribute("%sDamage" % type_) + if d > 0: + damage += d + + # Take optimal and half falloff as range factor + rangeFactor = range_ + falloff / 2 + + return - rangeFactor, charge.name.rsplit()[-2:], damage, charge.name + + def missileSorter(self, charge): + # Get charge damage type and total damage + chargeDamageType, totalDamage = self.damageInfo(charge) + # Find its position in sort list + position = self.MISSILE_ORDER.index(chargeDamageType) + return position, totalDamage, charge.name + + def damageInfo(self, charge): + # Set up data storage for missile damage stuff + damageMap = {} + totalDamage = 0 + # Fill them with the data about charge + for damageType in self.DAMAGE_TYPES: + currentDamage = charge.getAttribute("{0}Damage".format(damageType)) or 0 + damageMap[damageType] = currentDamage + totalDamage += currentDamage + # Detect type of ammo + chargeDamageType = None + for damageType in damageMap: + # If all damage belongs to certain type purely, set appropriate + # ammoType + if damageMap[damageType] == totalDamage: + chargeDamageType = damageType + break + # Else consider ammo as mixed damage + if chargeDamageType is None: + chargeDamageType = "mixed" + + return chargeDamageType, totalDamage + + @staticmethod + def numericConverter(string): + return int(string) if string.isdigit() else string + + def nameSorter(self, charge): + parts = charge.name.split(" ") + return map(self.numericConverter, parts) + + def addCharge(self, menu, charge): + id_ = ContextMenu.nextID() + name = charge.name if charge is not None else "Empty" + self.chargeIds[id_] = charge + item = wx.MenuItem(menu, id_, name) + menu.Bind(wx.EVT_MENU, self.handleAmmoSwitch, item) + item.charge = charge + if charge is not None and charge.icon is not None: + bitmap = BitmapLoader.getBitmap(charge.icon.iconFile, "icons") + if bitmap is not None: + item.SetBitmap(bitmap) + + return item + + @staticmethod + def addSeperator(m, text): + id_ = ContextMenu.nextID() + m.Append(id_, u'─ %s ─' % text) + m.Enable(id_, False) + + def getSubMenu(self, context, selection, rootMenu, i, pitem): + msw = True if "wxMSW" in wx.PlatformInfo else False + m = wx.Menu() + self.chargeIds = {} + hardpoint = self.module.hardpoint + moduleName = self.module.item.name + # Make sure we do not consider mining turrets as combat turrets + if hardpoint == Hardpoint.TURRET and self.module.getModifiedItemAttr("miningAmount") is None: + self.addSeperator(m, "Long Range") + items = [] + range_ = None + nameBase = None + sub = None + self.charges.sort(key=self.turretSorter) + for charge in self.charges: + # fix issue 71 - will probably have to change if CCP adds more Orbital ammo + if "Orbital" in charge.name: + # uncomment if we ever want to include Oribital ammo in ammo picker - see issue #71 + # This allows us to hide the ammo, but it's still loadable from the market + # item = self.addCharge(m, charge) + # items.append(item) + continue + currBase = charge.name.rsplit()[-2:] + currRange = charge.getAttribute("weaponRangeMultiplier") + if nameBase is None or range_ != currRange or nameBase != currBase: + if sub is not None: + self.addSeperator(sub, "More Damage") + + sub = None + base = charge + nameBase = currBase + range_ = currRange + item = self.addCharge(rootMenu if msw else m, charge) + items.append(item) + else: + if sub is None and item and base: + sub = wx.Menu() + sub.Bind(wx.EVT_MENU, self.handleAmmoSwitch) + self.addSeperator(sub, "Less Damage") + item.SetSubMenu(sub) + sub.AppendItem(self.addCharge(rootMenu if msw else sub, base)) + + sub.AppendItem(self.addCharge(rootMenu if msw else sub, charge)) + + if sub is not None: + self.addSeperator(sub, "More Damage") + + for item in items: + m.AppendItem(item) + + self.addSeperator(m, "Short Range") + elif hardpoint == Hardpoint.MISSILE and moduleName != 'Festival Launcher': + self.charges.sort(key=self.missileSorter) + type_ = None + sub = None + defender = None + for charge in self.charges: + currType = self.damageInfo(charge)[0] + + if currType != type_ or type_ is None: + if sub is not None: + self.addSeperator(sub, "More Damage") + + type_ = currType + item = wx.MenuItem(m, wx.ID_ANY, type_.capitalize()) + bitmap = BitmapLoader.getBitmap("%s_small" % type, "gui") + if bitmap is not None: + item.SetBitmap(bitmap) + + sub = wx.Menu() + sub.Bind(wx.EVT_MENU, self.handleAmmoSwitch) + self.addSeperator(sub, "Less Damage") + item.SetSubMenu(sub) + m.AppendItem(item) + + if charge.name not in ("Light Defender Missile I", "Heavy Defender Missile I"): + sub.AppendItem(self.addCharge(rootMenu if msw else sub, charge)) + else: + defender = charge + + if defender is not None: + m.AppendItem(self.addCharge(rootMenu if msw else m, defender)) + if sub is not None: + self.addSeperator(sub, "More Damage") + else: + self.charges.sort(key=self.nameSorter) + for charge in self.charges: + m.AppendItem(self.addCharge(rootMenu if msw else m, charge)) + + m.AppendItem(self.addCharge(rootMenu if msw else m, None)) + return m + + def handleAmmoSwitch(self, event): + charge = self.chargeIds.get(event.Id, False) + if charge is False: + event.Skip() + return + + sFit = Fit.getInstance() + fitID = self.mainFrame.getActiveFit() + + sFit.setAmmo(fitID, charge.ID if charge is not None else None, self.modules) + wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=fitID)) + + +ModuleAmmoPicker.register() diff --git a/gui/builtinContextMenus/moduleGlobalAmmoPicker.py b/gui/builtinContextMenus/moduleGlobalAmmoPicker.py index 41f969b35..849896111 100644 --- a/gui/builtinContextMenus/moduleGlobalAmmoPicker.py +++ b/gui/builtinContextMenus/moduleGlobalAmmoPicker.py @@ -1,16 +1,16 @@ # -*- coding: utf-8 -*- -from gui.contextMenu import ContextMenu import gui.mainFrame -import service +# noinspection PyPackageRequirements import wx -from gui.bitmapLoader import BitmapLoader -from eos.types import Hardpoint import gui.globalEvents as GE from gui.builtinContextMenus.moduleAmmoPicker import ModuleAmmoPicker -import eos.db +from eos.db.saveddata.queries import getFit as db_getFit +from service.fit import Fit + class ModuleGlobalAmmoPicker(ModuleAmmoPicker): def __init__(self): + super(ModuleGlobalAmmoPicker, self).__init__() self.mainFrame = gui.mainFrame.MainFrame.getInstance() def getText(self, itmContext, selection): @@ -26,9 +26,9 @@ class ModuleGlobalAmmoPicker(ModuleAmmoPicker): event.Skip() return - sFit = service.Fit.getInstance() + sFit = Fit.getInstance() fitID = self.mainFrame.getActiveFit() - fit = eos.db.getFit(fitID) + fit = db_getFit(fitID) selectedModule = self.modules[0] allModules = [] diff --git a/gui/builtinContextMenus/openFit.py b/gui/builtinContextMenus/openFit.py index eaf38832e..5a0a760f7 100644 --- a/gui/builtinContextMenus/openFit.py +++ b/gui/builtinContextMenus/openFit.py @@ -1,8 +1,10 @@ from gui.contextMenu import ContextMenu import gui.mainFrame +# noinspection PyPackageRequirements import wx from gui.shipBrowser import FitSelected + class OpenFit(ContextMenu): def __init__(self): self.mainFrame = gui.mainFrame.MainFrame.getInstance() @@ -17,4 +19,5 @@ class OpenFit(ContextMenu): fit = selection[0] wx.PostEvent(self.mainFrame, FitSelected(fitID=fit.ID, startup=2)) + OpenFit.register() diff --git a/gui/builtinContextMenus/priceClear.py b/gui/builtinContextMenus/priceClear.py index da33bfc21..8cb1a5175 100644 --- a/gui/builtinContextMenus/priceClear.py +++ b/gui/builtinContextMenus/priceClear.py @@ -1,8 +1,10 @@ from gui.contextMenu import ContextMenu import gui.mainFrame +# noinspection PyPackageRequirements import wx import gui.globalEvents as GE -import service +from service.market import Market + class PriceClear(ContextMenu): def __init__(self): @@ -15,8 +17,9 @@ class PriceClear(ContextMenu): return "Reset Price Cache" def activate(self, fullContext, selection, i): - sMkt = service.Market.getInstance() + sMkt = Market.getInstance() sMkt.clearPriceCache() wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.mainFrame.getActiveFit())) + PriceClear.register() diff --git a/gui/builtinContextMenus/project.py b/gui/builtinContextMenus/project.py index 4299f67b6..838b4e4fc 100644 --- a/gui/builtinContextMenus/project.py +++ b/gui/builtinContextMenus/project.py @@ -1,37 +1,39 @@ -from gui.contextMenu import ContextMenu -import gui.mainFrame -import service -import gui.globalEvents as GE -import wx -import eos.db - -class Project(ContextMenu): - def __init__(self): - self.mainFrame = gui.mainFrame.MainFrame.getInstance() - - def display(self, srcContext, selection): - if srcContext not in ("marketItemGroup", "marketItemMisc") or self.mainFrame.getActiveFit() is None: - return False - - sFit = service.Fit.getInstance() - fitID = self.mainFrame.getActiveFit() - fit = sFit.getFit(fitID) - - if fit.isStructure: - return False - - item = selection[0] - return item.isType("projected") - - def getText(self, itmContext, selection): - return "Project {0} onto Fit".format(itmContext) - - def activate(self, fullContext, selection, i): - sFit = service.Fit.getInstance() - fitID = self.mainFrame.getActiveFit() - trigger = sFit.project(fitID, selection[0]) - if trigger: - wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=fitID)) - self.mainFrame.additionsPane.select("Projected") - -Project.register() +from gui.contextMenu import ContextMenu +import gui.mainFrame +import gui.globalEvents as GE +# noinspection PyPackageRequirements +import wx +from service.fit import Fit + + +class Project(ContextMenu): + def __init__(self): + self.mainFrame = gui.mainFrame.MainFrame.getInstance() + + def display(self, srcContext, selection): + if srcContext not in ("marketItemGroup", "marketItemMisc") or self.mainFrame.getActiveFit() is None: + return False + + sFit = Fit.getInstance() + fitID = self.mainFrame.getActiveFit() + fit = sFit.getFit(fitID) + + if fit.isStructure: + return False + + item = selection[0] + return item.isType("projected") + + def getText(self, itmContext, selection): + return "Project {0} onto Fit".format(itmContext) + + def activate(self, fullContext, selection, i): + sFit = Fit.getInstance() + fitID = self.mainFrame.getActiveFit() + trigger = sFit.project(fitID, selection[0]) + if trigger: + wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=fitID)) + self.mainFrame.additionsPane.select("Projected") + + +Project.register() diff --git a/gui/builtinContextMenus/shipJump.py b/gui/builtinContextMenus/shipJump.py index bc332e4a4..40dda42c8 100644 --- a/gui/builtinContextMenus/shipJump.py +++ b/gui/builtinContextMenus/shipJump.py @@ -1,8 +1,10 @@ +# noinspection PyPackageRequirements import wx from gui.contextMenu import ContextMenu import gui.mainFrame -import service from gui.shipBrowser import Stage3Selected +from service.fit import Fit + class ShipJump(ContextMenu): def __init__(self): @@ -16,11 +18,12 @@ class ShipJump(ContextMenu): def activate(self, fullContext, selection, i): fitID = self.mainFrame.getActiveFit() - sFit = service.Fit.getInstance() + sFit = Fit.getInstance() stuff = sFit.getFit(fitID).ship groupID = stuff.item.group.ID self.mainFrame.notebookBrowsers.SetSelection(1) - wx.PostEvent(self.mainFrame.shipBrowser,Stage3Selected(shipID=stuff.item.ID, back=groupID)) + wx.PostEvent(self.mainFrame.shipBrowser, Stage3Selected(shipID=stuff.item.ID, back=groupID)) + ShipJump.register() diff --git a/gui/builtinContextMenus/tacticalMode.py b/gui/builtinContextMenus/tacticalMode.py index 3b5cc5043..679be45f3 100644 --- a/gui/builtinContextMenus/tacticalMode.py +++ b/gui/builtinContextMenus/tacticalMode.py @@ -1,8 +1,11 @@ +# noinspection PyPackageRequirements import wx from gui.contextMenu import ContextMenu import gui.mainFrame -import service + import gui.globalEvents as GE +from service.fit import Fit + class TacticalMode(ContextMenu): def __init__(self): @@ -12,7 +15,7 @@ class TacticalMode(ContextMenu): if self.mainFrame.getActiveFit() is None or srcContext != "fittingShip": return False - sFit = service.Fit.getInstance() + sFit = Fit.getInstance() fitID = self.mainFrame.getActiveFit() fit = sFit.getFit(fitID) @@ -52,9 +55,10 @@ class TacticalMode(ContextMenu): event.Skip() return - sFit = service.Fit.getInstance() + sFit = Fit.getInstance() fitID = self.mainFrame.getActiveFit() sFit.setMode(fitID, self.modeIds[event.Id]) wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=fitID)) + TacticalMode.register() diff --git a/gui/builtinContextMenus/targetResists.py b/gui/builtinContextMenus/targetResists.py index c0f9bf00c..8ebd4076d 100644 --- a/gui/builtinContextMenus/targetResists.py +++ b/gui/builtinContextMenus/targetResists.py @@ -1,14 +1,17 @@ from gui.contextMenu import ContextMenu import gui.mainFrame -import service import gui.globalEvents as GE +# noinspection PyPackageRequirements import wx from gui.bitmapLoader import BitmapLoader +from service.targetResists import TargetResists as svc_TargetResists +from service.fit import Fit try: from collections import OrderedDict except ImportError: - from gui.utils.compat import OrderedDict + from utils.compat import OrderedDict + class TargetResists(ContextMenu): def __init__(self): @@ -18,7 +21,7 @@ class TargetResists(ContextMenu): if self.mainFrame.getActiveFit() is None or srcContext != "firepowerViewFull": return False - sTR = service.TargetResists.getInstance() + sTR = svc_TargetResists.getInstance() self.patterns = sTR.getTargetResistsList() self.patterns.sort(key=lambda p: (p.name in ["None"], p.name)) @@ -33,7 +36,7 @@ class TargetResists(ContextMenu): event.Skip() return - sFit = service.Fit.getInstance() + sFit = Fit.getInstance() fitID = self.mainFrame.getActiveFit() sFit.setTargetResists(fitID, pattern) wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=fitID)) @@ -50,7 +53,7 @@ class TargetResists(ContextMenu): item.pattern = pattern # determine active pattern - sFit = service.Fit.getInstance() + sFit = Fit.getInstance() fitID = self.mainFrame.getActiveFit() f = sFit.getFit(fitID) tr = f.targetResists @@ -64,15 +67,15 @@ class TargetResists(ContextMenu): msw = True if "wxMSW" in wx.PlatformInfo else False self.patternIds = {} self.subMenus = OrderedDict() - self.singles = [] + self.singles = [] sub = wx.Menu() for pattern in self.patterns: start, end = pattern.name.find('['), pattern.name.find(']') if start is not -1 and end is not -1: - currBase = pattern.name[start+1:end] + currBase = pattern.name[start + 1:end] # set helper attr - setattr(pattern, "_name", pattern.name[end+1:].strip()) + setattr(pattern, "_name", pattern.name[end + 1:].strip()) if currBase not in self.subMenus: self.subMenus[currBase] = [] self.subMenus[currBase].append(pattern) @@ -93,7 +96,7 @@ class TargetResists(ContextMenu): # Create menu for child items grandSub = wx.Menu() - #sub.Bind(wx.EVT_MENU, self.handleResistSwitch) + # sub.Bind(wx.EVT_MENU, self.handleResistSwitch) # Apply child menu to parent item item.SetSubMenu(grandSub) @@ -101,8 +104,9 @@ class TargetResists(ContextMenu): # Append child items to child menu for pattern in patterns: grandSub.AppendItem(self.addPattern(rootMenu if msw else grandSub, pattern)) - sub.AppendItem(item) #finally, append parent item to root menu + sub.AppendItem(item) # finally, append parent item to root menu return sub + TargetResists.register() diff --git a/gui/builtinContextMenus/whProjector.py b/gui/builtinContextMenus/whProjector.py index ba0677e20..d5883541f 100644 --- a/gui/builtinContextMenus/whProjector.py +++ b/gui/builtinContextMenus/whProjector.py @@ -1,8 +1,11 @@ from gui.contextMenu import ContextMenu import gui.mainFrame import gui.globalEvents as GE -import service +# noinspection PyPackageRequirements import wx +from service.market import Market +from service.fit import Fit + class WhProjector(ContextMenu): def __init__(self): @@ -16,7 +19,7 @@ class WhProjector(ContextMenu): def getSubMenu(self, context, selection, rootMenu, i, pitem): msw = True if "wxMSW" in wx.PlatformInfo else False - sMkt = service.Market.getInstance() + sMkt = Market.getInstance() effdata = sMkt.getSystemWideEffects() self.idmap = {} @@ -41,16 +44,17 @@ class WhProjector(ContextMenu): return sub def handleSelection(self, event): - #Skip events ids that aren't mapped + # Skip events ids that aren't mapped swObj, swName = self.idmap.get(event.Id, (False, False)) if not swObj and not swName: event.Skip() return - sFit = service.Fit.getInstance() + sFit = Fit.getInstance() fitID = self.mainFrame.getActiveFit() sFit.project(fitID, swObj) wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=fitID)) + WhProjector.register() diff --git a/gui/builtinGraphs/__init__.py b/gui/builtinGraphs/__init__.py index ec25b90b2..31eaaaf9e 100644 --- a/gui/builtinGraphs/__init__.py +++ b/gui/builtinGraphs/__init__.py @@ -1 +1 @@ -__all__ = ["fitDps"] +__all__ = ["fitDps"] diff --git a/gui/builtinGraphs/fitDps.py b/gui/builtinGraphs/fitDps.py index ef2782674..ae03c180b 100644 --- a/gui/builtinGraphs/fitDps.py +++ b/gui/builtinGraphs/fitDps.py @@ -1,94 +1,95 @@ -#=============================================================================== -# Copyright (C) 2010 Diego Duclos -# -# This file is part of pyfa. -# -# pyfa is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# pyfa is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with pyfa. If not, see . -#=============================================================================== - -from gui.graph import Graph -import service -from gui.bitmapLoader import BitmapLoader -from eos.graph.fitDps import FitDpsGraph as FitDps -from eos.graph import Data -import gui.mainFrame -import service - -class FitDpsGraph(Graph): - propertyAttributeMap = {"angle": "maxVelocity", - "distance": "maxRange", - "signatureRadius": "signatureRadius", - "velocity": "maxVelocity"} - - propertyLabelMap = {"angle": "Target Angle (degrees)", - "distance": "Distance to Target (km)", - "signatureRadius": "Target Signature Radius (m)", - "velocity": "Target Velocity (m/s)"} - - defaults = FitDps.defaults.copy() - - def __init__(self): - Graph.__init__(self) - self.defaults["distance"] = "0-20" - self.name = "DPS" - self.fitDps = None - self.mainFrame = gui.mainFrame.MainFrame.getInstance() - - def getFields(self): - return self.defaults - - def getLabels(self): - return self.propertyLabelMap - - def getIcons(self): - icons = {} - sAttr = service.Attribute.getInstance() - for key, attrName in self.propertyAttributeMap.iteritems(): - iconFile = sAttr.getAttributeInfo(attrName).icon.iconFile - bitmap = BitmapLoader.getBitmap(iconFile, "icons") - if bitmap: - icons[key] = bitmap - - return icons - - def getPoints(self, fit, fields): - fitDps = getattr(self, "fitDps", None) - if fitDps is None or fitDps.fit != fit: - fitDps = self.fitDps = FitDps(fit) - - fitDps.clearData() - variable = None - for fieldName, value in fields.iteritems(): - d = Data(fieldName, value) - if not d.isConstant(): - if variable is None: - variable = fieldName - else: - #We can't handle more then one variable atm, OOPS FUCK OUT - return False, "Can only handle 1 variable" - - fitDps.setData(d) - - if variable is None: - return False, "No variable" - - x = [] - y = [] - for point, val in fitDps.getIterator(): - x.append(point[variable]) - y.append(val) - - return x, y - -FitDpsGraph.register() +# ============================================================================= +# Copyright (C) 2010 Diego Duclos +# +# This file is part of pyfa. +# +# pyfa is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# pyfa is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with pyfa. If not, see . +# ============================================================================= + +from gui.graph import Graph +from gui.bitmapLoader import BitmapLoader +from eos.graph.fitDps import FitDpsGraph as FitDps +from eos.graph import Data +import gui.mainFrame +from service.attribute import Attribute + + +class FitDpsGraph(Graph): + propertyAttributeMap = {"angle": "maxVelocity", + "distance": "maxRange", + "signatureRadius": "signatureRadius", + "velocity": "maxVelocity"} + + propertyLabelMap = {"angle": "Target Angle (degrees)", + "distance": "Distance to Target (km)", + "signatureRadius": "Target Signature Radius (m)", + "velocity": "Target Velocity (m/s)"} + + defaults = FitDps.defaults.copy() + + def __init__(self): + Graph.__init__(self) + self.defaults["distance"] = "0-20" + self.name = "DPS" + self.fitDps = None + self.mainFrame = gui.mainFrame.MainFrame.getInstance() + + def getFields(self): + return self.defaults + + def getLabels(self): + return self.propertyLabelMap + + def getIcons(self): + icons = {} + sAttr = Attribute.getInstance() + for key, attrName in self.propertyAttributeMap.iteritems(): + iconFile = sAttr.getAttributeInfo(attrName).icon.iconFile + bitmap = BitmapLoader.getBitmap(iconFile, "icons") + if bitmap: + icons[key] = bitmap + + return icons + + def getPoints(self, fit, fields): + fitDps = getattr(self, "fitDps", None) + if fitDps is None or fitDps.fit != fit: + fitDps = self.fitDps = FitDps(fit) + + fitDps.clearData() + variable = None + for fieldName, value in fields.iteritems(): + d = Data(fieldName, value) + if not d.isConstant(): + if variable is None: + variable = fieldName + else: + # We can't handle more then one variable atm, OOPS FUCK OUT + return False, "Can only handle 1 variable" + + fitDps.setData(d) + + if variable is None: + return False, "No variable" + + x = [] + y = [] + for point, val in fitDps.getIterator(): + x.append(point[variable]) + y.append(val) + + return x, y + + +FitDpsGraph.register() diff --git a/gui/builtinPreferenceViews/__init__.py b/gui/builtinPreferenceViews/__init__.py index 9d29c75ff..38beb0d0f 100644 --- a/gui/builtinPreferenceViews/__init__.py +++ b/gui/builtinPreferenceViews/__init__.py @@ -1,6 +1,8 @@ -__all__ = ["pyfaGeneralPreferences","pyfaHTMLExportPreferences","pyfaUpdatePreferences","pyfaNetworkPreferences"] - +# noinspection PyPackageRequirements import wx -if not 'wxMac' in wx.PlatformInfo or ('wxMac' in wx.PlatformInfo and wx.VERSION >= (3,0)): +__all__ = ["pyfaGeneralPreferences", "pyfaHTMLExportPreferences", "pyfaUpdatePreferences", + "pyfaNetworkPreferences"] # noqa + +if 'wxMac' not in wx.PlatformInfo or ('wxMac' in wx.PlatformInfo and wx.VERSION >= (3, 0)): __all__.append("pyfaCrestPreferences") diff --git a/gui/builtinPreferenceViews/dummyView.py b/gui/builtinPreferenceViews/dummyView.py index 5b8822d1c..5bded0d05 100644 --- a/gui/builtinPreferenceViews/dummyView.py +++ b/gui/builtinPreferenceViews/dummyView.py @@ -1,94 +1,95 @@ -#=============================================================================== -# Copyright (C) 2010 Diego Duclos -# -# This file is part of pyfa. -# -# pyfa is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# pyfa is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with pyfa. If not, see . -#=============================================================================== - -import wx -from gui.preferenceView import PreferenceView -from gui.bitmapLoader import BitmapLoader -class DummyView(PreferenceView): - title = "Dummy" - - def populatePanel(self, panel): - - mainSizer = wx.BoxSizer( wx.VERTICAL ) - - headerSizer = self.initHeader(panel) - mainSizer.Add( headerSizer, 0, wx.EXPAND, 5 ) - - self.stline1 = wx.StaticLine( panel, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LI_HORIZONTAL ) - mainSizer.Add( self.stline1, 0, wx.EXPAND, 5 ) - - contentSizer = self.initContent(panel) - mainSizer.Add( contentSizer, 1, wx.EXPAND|wx.TOP|wx.BOTTOM|wx.LEFT, 10 ) - - self.stline2 = wx.StaticLine( panel, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LI_HORIZONTAL ) - mainSizer.Add( self.stline2, 0, wx.EXPAND, 5 ) - - - footerSizer = self.initFooter(panel) - mainSizer.Add( footerSizer, 0, wx.EXPAND, 5 ) - panel.SetSizer( mainSizer ) - panel.Layout() - - def refreshPanel(self, fit): - pass - - def initHeader(self, panel): - headerSizer = wx.BoxSizer( wx.VERTICAL ) - self.stTitle = wx.StaticText( panel, wx.ID_ANY, u"Dummy", wx.DefaultPosition, wx.DefaultSize, 0 ) - self.stTitle.Wrap( -1 ) - self.stTitle.SetFont( wx.Font( 14, 70, 90, 90, False, wx.EmptyString ) ) - headerSizer.Add( self.stTitle, 0, wx.ALL, 5) - - return headerSizer - - def initContent(self, panel): - contentSizer = wx.BoxSizer( wx.VERTICAL ) - - self.m_checkBox2 = wx.CheckBox( panel, wx.ID_ANY, u"Check Me!", wx.DefaultPosition, wx.DefaultSize, 0 ) - contentSizer.Add( self.m_checkBox2, 0, wx.ALL, 5 ) - - self.m_radioBtn2 = wx.RadioButton( panel, wx.ID_ANY, u"RadioBtn", wx.DefaultPosition, wx.DefaultSize, 0 ) - contentSizer.Add( self.m_radioBtn2, 0, wx.ALL, 5 ) - - self.m_slider2 = wx.Slider( panel, wx.ID_ANY, 50, 0, 100, wx.DefaultPosition, wx.DefaultSize, wx.SL_HORIZONTAL ) - contentSizer.Add( self.m_slider2, 0, wx.ALL, 5 ) - - self.m_gauge1 = wx.Gauge( panel, wx.ID_ANY, 100, wx.DefaultPosition, wx.DefaultSize, wx.GA_HORIZONTAL ) - contentSizer.Add( self.m_gauge1, 0, wx.ALL, 5 ) - - self.m_textCtrl2 = wx.TextCtrl( panel, wx.ID_ANY, wx.EmptyString, wx.DefaultPosition, wx.DefaultSize, 0 ) - contentSizer.Add( self.m_textCtrl2, 0, wx.ALL, 5 ) - - return contentSizer - - def initFooter(self, panel): - footerSizer = wx.BoxSizer( wx.HORIZONTAL ) - - - footerSizer.AddSpacer( ( 0, 0), 1, wx.EXPAND, 5 ) - - self.btnRestore = wx.Button( panel, wx.ID_ANY, u"Restore", wx.DefaultPosition, wx.DefaultSize, 0 ) - self.btnRestore.Enable( False ) - - footerSizer.Add( self.btnRestore, 0, wx.ALL, 5 ) - - self.btnApply = wx.Button( panel, wx.ID_ANY, u"Apply", wx.DefaultPosition, wx.DefaultSize, 0 ) - footerSizer.Add( self.btnApply, 0, wx.ALL, 5 ) - return footerSizer -DummyView.register() +# ============================================================================= +# Copyright (C) 2010 Diego Duclos +# +# This file is part of pyfa. +# +# pyfa is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# pyfa is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with pyfa. If not, see . +# ============================================================================= + +# noinspection PyPackageRequirements +import wx +from gui.preferenceView import PreferenceView + + +class DummyView(PreferenceView): + title = "Dummy" + + def populatePanel(self, panel): + mainSizer = wx.BoxSizer(wx.VERTICAL) + + headerSizer = self.initHeader(panel) + mainSizer.Add(headerSizer, 0, wx.EXPAND, 5) + + self.stline1 = wx.StaticLine(panel, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LI_HORIZONTAL) + mainSizer.Add(self.stline1, 0, wx.EXPAND, 5) + + contentSizer = self.initContent(panel) + mainSizer.Add(contentSizer, 1, wx.EXPAND | wx.TOP | wx.BOTTOM | wx.LEFT, 10) + + self.stline2 = wx.StaticLine(panel, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LI_HORIZONTAL) + mainSizer.Add(self.stline2, 0, wx.EXPAND, 5) + + footerSizer = self.initFooter(panel) + mainSizer.Add(footerSizer, 0, wx.EXPAND, 5) + panel.SetSizer(mainSizer) + panel.Layout() + + def refreshPanel(self, fit): + pass + + def initHeader(self, panel): + headerSizer = wx.BoxSizer(wx.VERTICAL) + self.stTitle = wx.StaticText(panel, wx.ID_ANY, u"Dummy", wx.DefaultPosition, wx.DefaultSize, 0) + self.stTitle.Wrap(-1) + self.stTitle.SetFont(wx.Font(14, 70, 90, 90, False, wx.EmptyString)) + headerSizer.Add(self.stTitle, 0, wx.ALL, 5) + + return headerSizer + + def initContent(self, panel): + contentSizer = wx.BoxSizer(wx.VERTICAL) + + self.m_checkBox2 = wx.CheckBox(panel, wx.ID_ANY, u"Check Me!", wx.DefaultPosition, wx.DefaultSize, 0) + contentSizer.Add(self.m_checkBox2, 0, wx.ALL, 5) + + self.m_radioBtn2 = wx.RadioButton(panel, wx.ID_ANY, u"RadioBtn", wx.DefaultPosition, wx.DefaultSize, 0) + contentSizer.Add(self.m_radioBtn2, 0, wx.ALL, 5) + + self.m_slider2 = wx.Slider(panel, wx.ID_ANY, 50, 0, 100, wx.DefaultPosition, wx.DefaultSize, wx.SL_HORIZONTAL) + contentSizer.Add(self.m_slider2, 0, wx.ALL, 5) + + self.m_gauge1 = wx.Gauge(panel, wx.ID_ANY, 100, wx.DefaultPosition, wx.DefaultSize, wx.GA_HORIZONTAL) + contentSizer.Add(self.m_gauge1, 0, wx.ALL, 5) + + self.m_textCtrl2 = wx.TextCtrl(panel, wx.ID_ANY, wx.EmptyString, wx.DefaultPosition, wx.DefaultSize, 0) + contentSizer.Add(self.m_textCtrl2, 0, wx.ALL, 5) + + return contentSizer + + def initFooter(self, panel): + footerSizer = wx.BoxSizer(wx.HORIZONTAL) + + footerSizer.AddSpacer((0, 0), 1, wx.EXPAND, 5) + + self.btnRestore = wx.Button(panel, wx.ID_ANY, u"Restore", wx.DefaultPosition, wx.DefaultSize, 0) + self.btnRestore.Enable(False) + + footerSizer.Add(self.btnRestore, 0, wx.ALL, 5) + + self.btnApply = wx.Button(panel, wx.ID_ANY, u"Apply", wx.DefaultPosition, wx.DefaultSize, 0) + footerSizer.Add(self.btnApply, 0, wx.ALL, 5) + return footerSizer + + +DummyView.register() diff --git a/gui/builtinPreferenceViews/pyfaCrestPreferences.py b/gui/builtinPreferenceViews/pyfaCrestPreferences.py index 081ccf871..4a9208d0a 100644 --- a/gui/builtinPreferenceViews/pyfaCrestPreferences.py +++ b/gui/builtinPreferenceViews/pyfaCrestPreferences.py @@ -1,104 +1,117 @@ +# noinspection PyPackageRequirements import wx from gui.preferenceView import PreferenceView from gui.bitmapLoader import BitmapLoader import gui.mainFrame -import service -from service.crest import CrestModes +from service.settings import CRESTSettings + +# noinspection PyPackageRequirements from wx.lib.intctrl import IntCtrl -class PFCrestPref ( PreferenceView): +if 'wxMac' not in wx.PlatformInfo or ('wxMac' in wx.PlatformInfo and wx.VERSION >= (3, 0)): + from service.crest import Crest + + +class PFCrestPref(PreferenceView): title = "CREST" - def populatePanel( self, panel ): + def populatePanel(self, panel): self.mainFrame = gui.mainFrame.MainFrame.getInstance() - self.settings = service.settings.CRESTSettings.getInstance() + self.settings = CRESTSettings.getInstance() self.dirtySettings = False dlgWidth = panel.GetParent().GetParent().ClientSize.width - mainSizer = wx.BoxSizer( wx.VERTICAL ) + mainSizer = wx.BoxSizer(wx.VERTICAL) - self.stTitle = wx.StaticText( panel, wx.ID_ANY, self.title, wx.DefaultPosition, wx.DefaultSize, 0 ) - self.stTitle.Wrap( -1 ) - self.stTitle.SetFont( wx.Font( 12, 70, 90, 90, False, wx.EmptyString ) ) + self.stTitle = wx.StaticText(panel, wx.ID_ANY, self.title, wx.DefaultPosition, wx.DefaultSize, 0) + self.stTitle.Wrap(-1) + self.stTitle.SetFont(wx.Font(12, 70, 90, 90, False, wx.EmptyString)) - mainSizer.Add( self.stTitle, 0, wx.ALL, 5 ) + mainSizer.Add(self.stTitle, 0, wx.ALL, 5) - self.m_staticline1 = wx.StaticLine( panel, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LI_HORIZONTAL ) - mainSizer.Add( self.m_staticline1, 0, wx.EXPAND|wx.TOP|wx.BOTTOM, 5 ) + self.m_staticline1 = wx.StaticLine(panel, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LI_HORIZONTAL) + mainSizer.Add(self.m_staticline1, 0, wx.EXPAND | wx.TOP | wx.BOTTOM, 5) - self.stInfo = wx.StaticText( panel, wx.ID_ANY, u"Please see the pyfa wiki on GitHub for information regarding these options.", wx.DefaultPosition, wx.DefaultSize, 0 ) + self.stInfo = wx.StaticText(panel, wx.ID_ANY, + u"Please see the pyfa wiki on GitHub for information regarding these options.", + wx.DefaultPosition, wx.DefaultSize, 0) self.stInfo.Wrap(dlgWidth - 50) - mainSizer.Add( self.stInfo, 0, wx.EXPAND|wx.TOP|wx.BOTTOM, 5 ) + mainSizer.Add(self.stInfo, 0, wx.EXPAND | wx.TOP | wx.BOTTOM, 5) rbSizer = wx.BoxSizer(wx.HORIZONTAL) - self.rbMode = wx.RadioBox(panel, -1, "Mode", wx.DefaultPosition, wx.DefaultSize, ['Implicit', 'User-supplied details'], 1, wx.RA_SPECIFY_COLS) - self.rbServer = wx.RadioBox(panel, -1, "Server", wx.DefaultPosition, wx.DefaultSize, ['Tranquility', 'Singularity'], 1, wx.RA_SPECIFY_COLS) + self.rbMode = wx.RadioBox(panel, -1, "Mode", wx.DefaultPosition, wx.DefaultSize, + ['Implicit', 'User-supplied details'], 1, wx.RA_SPECIFY_COLS) + self.rbServer = wx.RadioBox(panel, -1, "Server", wx.DefaultPosition, wx.DefaultSize, + ['Tranquility', 'Singularity'], 1, wx.RA_SPECIFY_COLS) self.rbMode.SetSelection(self.settings.get('mode')) self.rbServer.SetSelection(self.settings.get('server')) - rbSizer.Add(self.rbMode, 1, wx.TOP | wx.RIGHT, 5 ) - rbSizer.Add(self.rbServer, 1, wx.ALL, 5 ) + rbSizer.Add(self.rbMode, 1, wx.TOP | wx.RIGHT, 5) + rbSizer.Add(self.rbServer, 1, wx.ALL, 5) self.rbMode.Bind(wx.EVT_RADIOBOX, self.OnModeChange) self.rbServer.Bind(wx.EVT_RADIOBOX, self.OnServerChange) - mainSizer.Add(rbSizer, 1, wx.ALL|wx.EXPAND, 0) + mainSizer.Add(rbSizer, 1, wx.ALL | wx.EXPAND, 0) timeoutSizer = wx.BoxSizer(wx.HORIZONTAL) - self.stTimout = wx.StaticText( panel, wx.ID_ANY, u"Timeout (seconds):", wx.DefaultPosition, wx.DefaultSize, 0 ) - self.stTimout.Wrap( -1 ) + self.stTimout = wx.StaticText(panel, wx.ID_ANY, u"Timeout (seconds):", wx.DefaultPosition, wx.DefaultSize, 0) + self.stTimout.Wrap(-1) - timeoutSizer.Add( self.stTimout, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 5 ) + timeoutSizer.Add(self.stTimout, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 5) self.intTimeout = IntCtrl(panel, max=300000, limited=True, value=self.settings.get('timeout')) - timeoutSizer.Add(self.intTimeout, 0, wx.ALL, 5 ) + timeoutSizer.Add(self.intTimeout, 0, wx.ALL, 5) self.intTimeout.Bind(wx.lib.intctrl.EVT_INT, self.OnTimeoutChange) - mainSizer.Add(timeoutSizer, 0, wx.ALL|wx.EXPAND, 0) + mainSizer.Add(timeoutSizer, 0, wx.ALL | wx.EXPAND, 0) - detailsTitle = wx.StaticText( panel, wx.ID_ANY, "CREST client details", wx.DefaultPosition, wx.DefaultSize, 0 ) - detailsTitle.Wrap( -1 ) - detailsTitle.SetFont( wx.Font( 12, 70, 90, 90, False, wx.EmptyString ) ) + detailsTitle = wx.StaticText(panel, wx.ID_ANY, "CREST client details", wx.DefaultPosition, wx.DefaultSize, 0) + detailsTitle.Wrap(-1) + detailsTitle.SetFont(wx.Font(12, 70, 90, 90, False, wx.EmptyString)) - mainSizer.Add( detailsTitle, 0, wx.ALL, 5 ) - mainSizer.Add( wx.StaticLine( panel, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LI_HORIZONTAL ), 0, wx.EXPAND, 5 ) + mainSizer.Add(detailsTitle, 0, wx.ALL, 5) + mainSizer.Add(wx.StaticLine(panel, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LI_HORIZONTAL), 0, + wx.EXPAND, 5) - fgAddrSizer = wx.FlexGridSizer( 2, 2, 0, 0 ) - fgAddrSizer.AddGrowableCol( 1 ) - fgAddrSizer.SetFlexibleDirection( wx.BOTH ) - fgAddrSizer.SetNonFlexibleGrowMode( wx.FLEX_GROWMODE_SPECIFIED ) + fgAddrSizer = wx.FlexGridSizer(2, 2, 0, 0) + fgAddrSizer.AddGrowableCol(1) + fgAddrSizer.SetFlexibleDirection(wx.BOTH) + fgAddrSizer.SetNonFlexibleGrowMode(wx.FLEX_GROWMODE_SPECIFIED) - self.stSetID = wx.StaticText( panel, wx.ID_ANY, u"Client ID:", wx.DefaultPosition, wx.DefaultSize, 0 ) - self.stSetID.Wrap( -1 ) - fgAddrSizer.Add( self.stSetID, 0, wx.ALL|wx.ALIGN_CENTER_VERTICAL, 5 ) + self.stSetID = wx.StaticText(panel, wx.ID_ANY, u"Client ID:", wx.DefaultPosition, wx.DefaultSize, 0) + self.stSetID.Wrap(-1) + fgAddrSizer.Add(self.stSetID, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 5) - self.inputClientID = wx.TextCtrl( panel, wx.ID_ANY, self.settings.get('clientID'), wx.DefaultPosition, wx.DefaultSize, 0 ) + self.inputClientID = wx.TextCtrl(panel, wx.ID_ANY, self.settings.get('clientID'), wx.DefaultPosition, + wx.DefaultSize, 0) - fgAddrSizer.Add( self.inputClientID, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL | wx.EXPAND, 5 ) + fgAddrSizer.Add(self.inputClientID, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL | wx.EXPAND, 5) - self.stSetSecret = wx.StaticText( panel, wx.ID_ANY, u"Client Secret:", wx.DefaultPosition, wx.DefaultSize, 0 ) - self.stSetSecret.Wrap( -1 ) + self.stSetSecret = wx.StaticText(panel, wx.ID_ANY, u"Client Secret:", wx.DefaultPosition, wx.DefaultSize, 0) + self.stSetSecret.Wrap(-1) - fgAddrSizer.Add( self.stSetSecret, 0, wx.ALL|wx.ALIGN_CENTER_VERTICAL, 5 ) + fgAddrSizer.Add(self.stSetSecret, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 5) - self.inputClientSecret = wx.TextCtrl( panel, wx.ID_ANY, self.settings.get('clientSecret'), wx.DefaultPosition, wx.DefaultSize, 0 ) + self.inputClientSecret = wx.TextCtrl(panel, wx.ID_ANY, self.settings.get('clientSecret'), wx.DefaultPosition, + wx.DefaultSize, 0) - fgAddrSizer.Add( self.inputClientSecret, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL | wx.EXPAND, 5 ) + fgAddrSizer.Add(self.inputClientSecret, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL | wx.EXPAND, 5) - self.btnApply = wx.Button( panel, wx.ID_ANY, u"Save Client Settings", wx.DefaultPosition, wx.DefaultSize, 0 ) + self.btnApply = wx.Button(panel, wx.ID_ANY, u"Save Client Settings", wx.DefaultPosition, wx.DefaultSize, 0) self.btnApply.Bind(wx.EVT_BUTTON, self.OnBtnApply) - mainSizer.Add( fgAddrSizer, 0, wx.EXPAND, 5) - mainSizer.Add( self.btnApply, 0, wx.ALIGN_RIGHT, 5) + mainSizer.Add(fgAddrSizer, 0, wx.EXPAND, 5) + mainSizer.Add(self.btnApply, 0, wx.ALIGN_RIGHT, 5) self.ToggleProxySettings(self.settings.get('mode')) - panel.SetSizer( mainSizer ) + panel.SetSizer(mainSizer) panel.Layout() def OnTimeoutChange(self, event): @@ -107,16 +120,16 @@ class PFCrestPref ( PreferenceView): def OnModeChange(self, event): self.settings.set('mode', event.GetInt()) self.ToggleProxySettings(self.settings.get('mode')) - service.Crest.restartService() + Crest.restartService() def OnServerChange(self, event): self.settings.set('server', event.GetInt()) - service.Crest.restartService() + Crest.restartService() def OnBtnApply(self, event): self.settings.set('clientID', self.inputClientID.GetValue().strip()) self.settings.set('clientSecret', self.inputClientSecret.GetValue().strip()) - sCrest = service.Crest.getInstance() + sCrest = Crest.getInstance() sCrest.delAllCharacters() def ToggleProxySettings(self, mode): @@ -134,4 +147,5 @@ class PFCrestPref ( PreferenceView): def getImage(self): return BitmapLoader.getBitmap("eve", "gui") + PFCrestPref.register() diff --git a/gui/builtinPreferenceViews/pyfaGaugePreferences.py b/gui/builtinPreferenceViews/pyfaGaugePreferences.py index 4163eeaf4..5f6509dc0 100644 --- a/gui/builtinPreferenceViews/pyfaGaugePreferences.py +++ b/gui/builtinPreferenceViews/pyfaGaugePreferences.py @@ -2,6 +2,7 @@ # -*- coding: utf-8 -*- +# noinspection PyPackageRequirements import wx import copy @@ -10,13 +11,15 @@ from gui.bitmapLoader import BitmapLoader from gui.utils import colorUtils import gui.utils.drawUtils as drawUtils + ########################################################################### -## Class PFGaugePref +# Class PFGaugePref ########################################################################### + class PFGaugePreview(wx.Window): - def __init__ (self, parent, id = wx.ID_ANY, value = 0, pos = wx.DefaultPosition, size = wx.DefaultSize, style = 0): - wx.Window.__init__(self, parent, id, pos = pos, size = size, style = style) + def __init__(self, parent, id=wx.ID_ANY, value=0, pos=wx.DefaultPosition, size=wx.DefaultSize, style=0): + wx.Window.__init__(self, parent, id, pos=pos, size=size, style=style) self.value = float(value) self.oldValue = self.value @@ -28,14 +31,14 @@ class PFGaugePreview(wx.Window): self.animDir = 1 self._fractionDigits = 2 - self.colorS = wx.Colour(0,0,0,255) - self.colorE = wx.Colour(0,0,0,255) + self.colorS = wx.Colour(0, 0, 0, 255) + self.colorE = wx.Colour(0, 0, 0, 255) self.gradientStart = 0 - self.bkColor = wx.Colour(0,0,0,255) - self.SetMinSize((100,-1)) + self.bkColor = wx.Colour(0, 0, 0, 255) + self.SetMinSize((100, -1)) - self.font = wx.FontFromPixelSize((0,13),wx.SWISS, wx.NORMAL, wx.NORMAL, False) + self.font = wx.FontFromPixelSize((0, 13), wx.SWISS, wx.NORMAL, wx.NORMAL, False) self.timerID = wx.NewId() self.timer = wx.Timer(self, self.timerID) @@ -56,7 +59,7 @@ class PFGaugePreview(wx.Window): if self.value > 100: self.value = 100 self.animDir = -1 - if self.value <0: + if self.value < 0: self.value = 0 self.animDir = 1 self.Refresh() @@ -79,7 +82,7 @@ class PFGaugePreview(wx.Window): self.Refresh() event.Skip() - def CanAnimate(self, anim = True): + def CanAnimate(self, anim=True): self.animate = anim if self.timer.IsRunning(): self.timer.Stop() @@ -97,7 +100,7 @@ class PFGaugePreview(wx.Window): self.Refresh() def SetValue(self, value): - self.value = min(max(value,0),100) + self.value = min(max(value, 0), 100) self.Refresh() def SetPercentages(self, start, end): @@ -119,198 +122,210 @@ class PFGaugePreview(wx.Window): r = copy.copy(rect) r.width = w - color = colorUtils.CalculateTransitionColor(self.colorS, self.colorE, float(value)/100) + color = colorUtils.CalculateTransitionColor(self.colorS, self.colorE, float(value) / 100) if self.gradientStart > 0: - gcolor = colorUtils.BrightenColor(color, float(self.gradientStart) / 100) - gMid = colorUtils.BrightenColor(color, float(self.gradientStart/2) / 100) + gcolor = colorUtils.BrightenColor(color, float(self.gradientStart) / 100) + gMid = colorUtils.BrightenColor(color, float(self.gradientStart / 2) / 100) else: - gcolor = colorUtils.DarkenColor(color, float(-self.gradientStart) / 100) - gMid = colorUtils.DarkenColor(color, float(-self.gradientStart/2) / 100) + gcolor = colorUtils.DarkenColor(color, float(-self.gradientStart) / 100) + gMid = colorUtils.DarkenColor(color, float(-self.gradientStart / 2) / 100) gBmp = drawUtils.DrawGradientBar(r.width, r.height, gMid, color, gcolor) - dc.DrawBitmap(gBmp,0,0) + dc.DrawBitmap(gBmp, 0, 0) dc.SetFont(self.font) r = copy.copy(rect) - r.left +=1 - r.top +=1 - + r.left += 1 + r.top += 1 formatStr = "{0:." + str(self._fractionDigits) + "f}%" value = (self.percE - self.percS) * value / (self.percE - self.percS) value = self.percS + (self.percE - self.percS) * value / 100 - dc.SetTextForeground(wx.Colour(80,80,80)) + dc.SetTextForeground(wx.Colour(80, 80, 80)) dc.DrawLabel(formatStr.format(value), r, wx.ALIGN_CENTER) - dc.SetTextForeground(wx.Colour(255,255,255)) + dc.SetTextForeground(wx.Colour(255, 255, 255)) dc.DrawLabel(formatStr.format(value), rect, wx.ALIGN_CENTER) -class PFGaugePref ( PreferenceView): +class PFGaugePref(PreferenceView): title = "Pyfa Gauge Theme" - def populatePanel( self, panel ): + + def populatePanel(self, panel): self.InitDefaultColours() - mainSizer = wx.BoxSizer( wx.VERTICAL ) + mainSizer = wx.BoxSizer(wx.VERTICAL) - gSizer1 = wx.BoxSizer( wx.HORIZONTAL ) + gSizer1 = wx.BoxSizer(wx.HORIZONTAL) - self.st0100 = wx.StaticText( panel, wx.ID_ANY, u"0 - 100", wx.DefaultPosition, wx.DefaultSize, wx.ALIGN_RIGHT ) - self.st0100.Wrap( -1 ) - gSizer1.Add( self.st0100, 1, wx.ALL|wx.ALIGN_CENTER_VERTICAL, 5 ) + self.st0100 = wx.StaticText(panel, wx.ID_ANY, u"0 - 100", wx.DefaultPosition, wx.DefaultSize, wx.ALIGN_RIGHT) + self.st0100.Wrap(-1) + gSizer1.Add(self.st0100, 1, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 5) - self.cp0100S = wx.ColourPickerCtrl( panel, wx.ID_ANY, wx.BLACK, wx.DefaultPosition, wx.DefaultSize, wx.CLRP_DEFAULT_STYLE | wx.CLRP_SHOW_LABEL ) - gSizer1.Add( self.cp0100S, 0, wx.ALL|wx.ALIGN_CENTER_VERTICAL, 5 ) + self.cp0100S = wx.ColourPickerCtrl(panel, wx.ID_ANY, wx.BLACK, wx.DefaultPosition, wx.DefaultSize, + wx.CLRP_DEFAULT_STYLE | wx.CLRP_SHOW_LABEL) + gSizer1.Add(self.cp0100S, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 5) - self.cp0100E = wx.ColourPickerCtrl( panel, wx.ID_ANY, wx.BLACK, wx.DefaultPosition, wx.DefaultSize, wx.CLRP_DEFAULT_STYLE | wx.CLRP_SHOW_LABEL ) - gSizer1.Add( self.cp0100E, 0, wx.ALL|wx.ALIGN_CENTER_VERTICAL, 5 ) + self.cp0100E = wx.ColourPickerCtrl(panel, wx.ID_ANY, wx.BLACK, wx.DefaultPosition, wx.DefaultSize, + wx.CLRP_DEFAULT_STYLE | wx.CLRP_SHOW_LABEL) + gSizer1.Add(self.cp0100E, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 5) - self.gauge0100S = PFGaugePreview( panel, wx.ID_ANY, 33, wx.DefaultPosition, wx.DefaultSize, wx.SIMPLE_BORDER ) - gSizer1.Add( self.gauge0100S, 0, wx.ALIGN_CENTER_VERTICAL|wx.LEFT, 5 ) + self.gauge0100S = PFGaugePreview(panel, wx.ID_ANY, 33, wx.DefaultPosition, wx.DefaultSize, wx.SIMPLE_BORDER) + gSizer1.Add(self.gauge0100S, 0, wx.ALIGN_CENTER_VERTICAL | wx.LEFT, 5) - self.gauge0100M = PFGaugePreview( panel, wx.ID_ANY, 66, wx.DefaultPosition, wx.DefaultSize, wx.SIMPLE_BORDER ) - gSizer1.Add( self.gauge0100M, 0, wx.ALIGN_CENTER_VERTICAL|wx.LEFT, 5 ) + self.gauge0100M = PFGaugePreview(panel, wx.ID_ANY, 66, wx.DefaultPosition, wx.DefaultSize, wx.SIMPLE_BORDER) + gSizer1.Add(self.gauge0100M, 0, wx.ALIGN_CENTER_VERTICAL | wx.LEFT, 5) - self.gauge0100E = PFGaugePreview( panel, wx.ID_ANY, 100, wx.DefaultPosition, wx.DefaultSize, wx.SIMPLE_BORDER ) - gSizer1.Add( self.gauge0100E, 0, wx.ALIGN_CENTER_VERTICAL|wx.RIGHT|wx.LEFT, 5 ) + self.gauge0100E = PFGaugePreview(panel, wx.ID_ANY, 100, wx.DefaultPosition, wx.DefaultSize, wx.SIMPLE_BORDER) + gSizer1.Add(self.gauge0100E, 0, wx.ALIGN_CENTER_VERTICAL | wx.RIGHT | wx.LEFT, 5) - mainSizer.Add( gSizer1, 0, wx.EXPAND, 5 ) + mainSizer.Add(gSizer1, 0, wx.EXPAND, 5) - gSizer2 = wx.BoxSizer( wx.HORIZONTAL ) + gSizer2 = wx.BoxSizer(wx.HORIZONTAL) - self.st100101 = wx.StaticText( panel, wx.ID_ANY, u"100 - 101", wx.DefaultPosition, wx.DefaultSize, wx.ALIGN_RIGHT ) - self.st100101.Wrap( -1 ) - gSizer2.Add( self.st100101, 1, wx.ALL|wx.ALIGN_CENTER_VERTICAL, 5 ) + self.st100101 = wx.StaticText(panel, wx.ID_ANY, u"100 - 101", wx.DefaultPosition, wx.DefaultSize, + wx.ALIGN_RIGHT) + self.st100101.Wrap(-1) + gSizer2.Add(self.st100101, 1, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 5) - self.cp100101S = wx.ColourPickerCtrl( panel, wx.ID_ANY, wx.BLACK, wx.DefaultPosition, wx.DefaultSize, wx.CLRP_DEFAULT_STYLE | wx.CLRP_SHOW_LABEL ) - gSizer2.Add( self.cp100101S, 0, wx.ALL|wx.ALIGN_CENTER_VERTICAL, 5 ) + self.cp100101S = wx.ColourPickerCtrl(panel, wx.ID_ANY, wx.BLACK, wx.DefaultPosition, wx.DefaultSize, + wx.CLRP_DEFAULT_STYLE | wx.CLRP_SHOW_LABEL) + gSizer2.Add(self.cp100101S, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 5) - self.cp100101E = wx.ColourPickerCtrl( panel, wx.ID_ANY, wx.BLACK, wx.DefaultPosition, wx.DefaultSize, wx.CLRP_DEFAULT_STYLE | wx.CLRP_SHOW_LABEL ) - gSizer2.Add( self.cp100101E, 0, wx.ALL|wx.ALIGN_CENTER_VERTICAL, 5 ) + self.cp100101E = wx.ColourPickerCtrl(panel, wx.ID_ANY, wx.BLACK, wx.DefaultPosition, wx.DefaultSize, + wx.CLRP_DEFAULT_STYLE | wx.CLRP_SHOW_LABEL) + gSizer2.Add(self.cp100101E, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 5) - self.gauge100101S = PFGaugePreview( panel, wx.ID_ANY, 33, wx.DefaultPosition, wx.DefaultSize, wx.SIMPLE_BORDER ) - gSizer2.Add( self.gauge100101S, 0, wx.ALIGN_CENTER_VERTICAL|wx.LEFT, 5 ) + self.gauge100101S = PFGaugePreview(panel, wx.ID_ANY, 33, wx.DefaultPosition, wx.DefaultSize, wx.SIMPLE_BORDER) + gSizer2.Add(self.gauge100101S, 0, wx.ALIGN_CENTER_VERTICAL | wx.LEFT, 5) - self.gauge100101M = PFGaugePreview( panel, wx.ID_ANY, 66, wx.DefaultPosition, wx.DefaultSize, wx.SIMPLE_BORDER ) - gSizer2.Add( self.gauge100101M, 0, wx.ALIGN_CENTER_VERTICAL|wx.LEFT, 5 ) + self.gauge100101M = PFGaugePreview(panel, wx.ID_ANY, 66, wx.DefaultPosition, wx.DefaultSize, wx.SIMPLE_BORDER) + gSizer2.Add(self.gauge100101M, 0, wx.ALIGN_CENTER_VERTICAL | wx.LEFT, 5) - self.gauge100101E = PFGaugePreview( panel, wx.ID_ANY, 100, wx.DefaultPosition, wx.DefaultSize, wx.SIMPLE_BORDER ) - gSizer2.Add( self.gauge100101E, 0, wx.ALIGN_CENTER_VERTICAL|wx.RIGHT|wx.LEFT, 5 ) + self.gauge100101E = PFGaugePreview(panel, wx.ID_ANY, 100, wx.DefaultPosition, wx.DefaultSize, wx.SIMPLE_BORDER) + gSizer2.Add(self.gauge100101E, 0, wx.ALIGN_CENTER_VERTICAL | wx.RIGHT | wx.LEFT, 5) - mainSizer.Add( gSizer2, 0, wx.EXPAND, 5 ) + mainSizer.Add(gSizer2, 0, wx.EXPAND, 5) - gSizer3 = wx.BoxSizer( wx.HORIZONTAL ) + gSizer3 = wx.BoxSizer(wx.HORIZONTAL) - self.st101103 = wx.StaticText( panel, wx.ID_ANY, u"101 - 103", wx.DefaultPosition, wx.DefaultSize, wx.ALIGN_RIGHT ) - self.st101103.Wrap( -1 ) - gSizer3.Add( self.st101103, 1, wx.ALL|wx.ALIGN_CENTER_VERTICAL, 5 ) + self.st101103 = wx.StaticText(panel, wx.ID_ANY, u"101 - 103", wx.DefaultPosition, wx.DefaultSize, + wx.ALIGN_RIGHT) + self.st101103.Wrap(-1) + gSizer3.Add(self.st101103, 1, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 5) - self.cp101103S = wx.ColourPickerCtrl( panel, wx.ID_ANY, wx.BLACK, wx.DefaultPosition, wx.DefaultSize, wx.CLRP_DEFAULT_STYLE | wx.CLRP_SHOW_LABEL ) - gSizer3.Add( self.cp101103S, 0, wx.ALL|wx.ALIGN_CENTER_VERTICAL, 5 ) + self.cp101103S = wx.ColourPickerCtrl(panel, wx.ID_ANY, wx.BLACK, wx.DefaultPosition, wx.DefaultSize, + wx.CLRP_DEFAULT_STYLE | wx.CLRP_SHOW_LABEL) + gSizer3.Add(self.cp101103S, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 5) - self.cp101103E = wx.ColourPickerCtrl( panel, wx.ID_ANY, wx.BLACK, wx.DefaultPosition, wx.DefaultSize, wx.CLRP_DEFAULT_STYLE | wx.CLRP_SHOW_LABEL ) - gSizer3.Add( self.cp101103E, 0, wx.ALL|wx.ALIGN_CENTER_VERTICAL, 5 ) + self.cp101103E = wx.ColourPickerCtrl(panel, wx.ID_ANY, wx.BLACK, wx.DefaultPosition, wx.DefaultSize, + wx.CLRP_DEFAULT_STYLE | wx.CLRP_SHOW_LABEL) + gSizer3.Add(self.cp101103E, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 5) - self.gauge101103S = PFGaugePreview( panel, wx.ID_ANY, 33, wx.DefaultPosition, wx.DefaultSize, wx.SIMPLE_BORDER ) - gSizer3.Add( self.gauge101103S, 0, wx.ALIGN_CENTER_VERTICAL|wx.LEFT, 5 ) + self.gauge101103S = PFGaugePreview(panel, wx.ID_ANY, 33, wx.DefaultPosition, wx.DefaultSize, wx.SIMPLE_BORDER) + gSizer3.Add(self.gauge101103S, 0, wx.ALIGN_CENTER_VERTICAL | wx.LEFT, 5) - self.gauge101103M = PFGaugePreview( panel, wx.ID_ANY, 66, wx.DefaultPosition, wx.DefaultSize, wx.SIMPLE_BORDER ) - gSizer3.Add( self.gauge101103M, 0, wx.ALIGN_CENTER_VERTICAL|wx.LEFT, 5 ) + self.gauge101103M = PFGaugePreview(panel, wx.ID_ANY, 66, wx.DefaultPosition, wx.DefaultSize, wx.SIMPLE_BORDER) + gSizer3.Add(self.gauge101103M, 0, wx.ALIGN_CENTER_VERTICAL | wx.LEFT, 5) - self.gauge101103E = PFGaugePreview( panel, wx.ID_ANY, 100, wx.DefaultPosition, wx.DefaultSize, wx.SIMPLE_BORDER ) - gSizer3.Add( self.gauge101103E, 0, wx.ALIGN_CENTER_VERTICAL|wx.RIGHT|wx.LEFT, 5 ) + self.gauge101103E = PFGaugePreview(panel, wx.ID_ANY, 100, wx.DefaultPosition, wx.DefaultSize, wx.SIMPLE_BORDER) + gSizer3.Add(self.gauge101103E, 0, wx.ALIGN_CENTER_VERTICAL | wx.RIGHT | wx.LEFT, 5) - mainSizer.Add( gSizer3, 0, wx.EXPAND, 5 ) + mainSizer.Add(gSizer3, 0, wx.EXPAND, 5) - gSizer4 = wx.BoxSizer( wx.HORIZONTAL ) + gSizer4 = wx.BoxSizer(wx.HORIZONTAL) - self.st103105 = wx.StaticText( panel, wx.ID_ANY, u"103 - 105", wx.DefaultPosition, wx.DefaultSize, wx.ALIGN_RIGHT ) - self.st103105.Wrap( -1 ) - gSizer4.Add( self.st103105, 1, wx.ALL|wx.ALIGN_CENTER_VERTICAL, 5 ) + self.st103105 = wx.StaticText(panel, wx.ID_ANY, u"103 - 105", wx.DefaultPosition, wx.DefaultSize, + wx.ALIGN_RIGHT) + self.st103105.Wrap(-1) + gSizer4.Add(self.st103105, 1, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 5) - self.cp103105S = wx.ColourPickerCtrl( panel, wx.ID_ANY, wx.BLACK, wx.DefaultPosition, wx.DefaultSize, wx.CLRP_DEFAULT_STYLE | wx.CLRP_SHOW_LABEL ) - gSizer4.Add( self.cp103105S, 0, wx.ALL|wx.ALIGN_CENTER_VERTICAL, 5 ) + self.cp103105S = wx.ColourPickerCtrl(panel, wx.ID_ANY, wx.BLACK, wx.DefaultPosition, wx.DefaultSize, + wx.CLRP_DEFAULT_STYLE | wx.CLRP_SHOW_LABEL) + gSizer4.Add(self.cp103105S, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 5) - self.cp103105E = wx.ColourPickerCtrl( panel, wx.ID_ANY, wx.BLACK, wx.DefaultPosition, wx.DefaultSize, wx.CLRP_DEFAULT_STYLE | wx.CLRP_SHOW_LABEL ) - gSizer4.Add( self.cp103105E, 0, wx.ALL|wx.ALIGN_CENTER_VERTICAL, 5 ) + self.cp103105E = wx.ColourPickerCtrl(panel, wx.ID_ANY, wx.BLACK, wx.DefaultPosition, wx.DefaultSize, + wx.CLRP_DEFAULT_STYLE | wx.CLRP_SHOW_LABEL) + gSizer4.Add(self.cp103105E, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 5) - self.gauge103105S = PFGaugePreview( panel, wx.ID_ANY, 33, wx.DefaultPosition, wx.DefaultSize, wx.SIMPLE_BORDER ) - gSizer4.Add( self.gauge103105S, 0, wx.ALIGN_CENTER_VERTICAL|wx.LEFT, 5 ) + self.gauge103105S = PFGaugePreview(panel, wx.ID_ANY, 33, wx.DefaultPosition, wx.DefaultSize, wx.SIMPLE_BORDER) + gSizer4.Add(self.gauge103105S, 0, wx.ALIGN_CENTER_VERTICAL | wx.LEFT, 5) - self.gauge103105M = PFGaugePreview( panel, wx.ID_ANY, 66, wx.DefaultPosition, wx.DefaultSize, wx.SIMPLE_BORDER ) - gSizer4.Add( self.gauge103105M, 0, wx.ALIGN_CENTER_VERTICAL|wx.LEFT, 5 ) + self.gauge103105M = PFGaugePreview(panel, wx.ID_ANY, 66, wx.DefaultPosition, wx.DefaultSize, wx.SIMPLE_BORDER) + gSizer4.Add(self.gauge103105M, 0, wx.ALIGN_CENTER_VERTICAL | wx.LEFT, 5) - self.gauge103105E = PFGaugePreview( panel, wx.ID_ANY, 100, wx.DefaultPosition, wx.DefaultSize, wx.SIMPLE_BORDER ) - gSizer4.Add( self.gauge103105E, 0, wx.ALIGN_CENTER_VERTICAL|wx.RIGHT|wx.LEFT, 5 ) + self.gauge103105E = PFGaugePreview(panel, wx.ID_ANY, 100, wx.DefaultPosition, wx.DefaultSize, wx.SIMPLE_BORDER) + gSizer4.Add(self.gauge103105E, 0, wx.ALIGN_CENTER_VERTICAL | wx.RIGHT | wx.LEFT, 5) - mainSizer.Add( gSizer4, 0, wx.EXPAND, 5 ) + mainSizer.Add(gSizer4, 0, wx.EXPAND, 5) - footerSizer = wx.BoxSizer( wx.VERTICAL ) + footerSizer = wx.BoxSizer(wx.VERTICAL) - self.sl1 = wx.StaticLine( panel, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LI_HORIZONTAL ) - footerSizer.Add( self.sl1, 0, wx.EXPAND |wx.ALL, 5 ) + self.sl1 = wx.StaticLine(panel, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LI_HORIZONTAL) + footerSizer.Add(self.sl1, 0, wx.EXPAND | wx.ALL, 5) - previewSizer = wx.BoxSizer( wx.HORIZONTAL ) + previewSizer = wx.BoxSizer(wx.HORIZONTAL) - self.wndPreview0100 = PFGaugePreview( panel, wx.ID_ANY, 100, wx.DefaultPosition, wx.DefaultSize, 0) - previewSizer.Add( self.wndPreview0100, 1, wx.ALIGN_CENTER_VERTICAL, 5 ) + self.wndPreview0100 = PFGaugePreview(panel, wx.ID_ANY, 100, wx.DefaultPosition, wx.DefaultSize, 0) + previewSizer.Add(self.wndPreview0100, 1, wx.ALIGN_CENTER_VERTICAL, 5) - self.wndPreview100101 = PFGaugePreview( panel, wx.ID_ANY, 100, wx.DefaultPosition, wx.DefaultSize, 0) - previewSizer.Add( self.wndPreview100101, 1, wx.ALIGN_CENTER_VERTICAL, 5 ) + self.wndPreview100101 = PFGaugePreview(panel, wx.ID_ANY, 100, wx.DefaultPosition, wx.DefaultSize, 0) + previewSizer.Add(self.wndPreview100101, 1, wx.ALIGN_CENTER_VERTICAL, 5) - self.wndPreview101103 = PFGaugePreview( panel, wx.ID_ANY, 100, wx.DefaultPosition, wx.DefaultSize, 0) - previewSizer.Add( self.wndPreview101103, 1, wx.ALIGN_CENTER_VERTICAL, 5 ) + self.wndPreview101103 = PFGaugePreview(panel, wx.ID_ANY, 100, wx.DefaultPosition, wx.DefaultSize, 0) + previewSizer.Add(self.wndPreview101103, 1, wx.ALIGN_CENTER_VERTICAL, 5) - self.wndPreview103105 = PFGaugePreview( panel, wx.ID_ANY, 100, wx.DefaultPosition, wx.DefaultSize, 0) - previewSizer.Add( self.wndPreview103105, 1, wx.ALIGN_CENTER_VERTICAL, 5 ) + self.wndPreview103105 = PFGaugePreview(panel, wx.ID_ANY, 100, wx.DefaultPosition, wx.DefaultSize, 0) + previewSizer.Add(self.wndPreview103105, 1, wx.ALIGN_CENTER_VERTICAL, 5) - footerSizer.Add( previewSizer, 1, wx.EXPAND | wx.ALL, 5 ) + footerSizer.Add(previewSizer, 1, wx.EXPAND | wx.ALL, 5) - buttonsSizer = wx.BoxSizer( wx.HORIZONTAL ) + buttonsSizer = wx.BoxSizer(wx.HORIZONTAL) - self.cbLink = wx.CheckBox( panel, wx.ID_ANY, u"Link Colors", wx.DefaultPosition, wx.DefaultSize, 0 ) - buttonsSizer.Add( self.cbLink, 0, wx.ALIGN_CENTER_VERTICAL | wx.RIGHT | wx.LEFT, 5 ) + self.cbLink = wx.CheckBox(panel, wx.ID_ANY, u"Link Colors", wx.DefaultPosition, wx.DefaultSize, 0) + buttonsSizer.Add(self.cbLink, 0, wx.ALIGN_CENTER_VERTICAL | wx.RIGHT | wx.LEFT, 5) - self.sliderGradientStart = wx.Slider( panel, wx.ID_ANY, self.gradientStart, -100, 100, wx.DefaultPosition, (127,-1), wx.SL_HORIZONTAL|wx.SL_LABELS ) - buttonsSizer.Add( self.sliderGradientStart, 1, wx.EXPAND | wx.ALL | wx.ALIGN_CENTER_VERTICAL, 5 ) + self.sliderGradientStart = wx.Slider(panel, wx.ID_ANY, self.gradientStart, -100, 100, wx.DefaultPosition, + (127, -1), wx.SL_HORIZONTAL | wx.SL_LABELS) + buttonsSizer.Add(self.sliderGradientStart, 1, wx.EXPAND | wx.ALL | wx.ALIGN_CENTER_VERTICAL, 5) - self.btnRestore = wx.Button( panel, wx.ID_ANY, u"Restore Defaults", wx.DefaultPosition, wx.DefaultSize, 0 ) - buttonsSizer.Add( self.btnRestore, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 5 ) + self.btnRestore = wx.Button(panel, wx.ID_ANY, u"Restore Defaults", wx.DefaultPosition, wx.DefaultSize, 0) + buttonsSizer.Add(self.btnRestore, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 5) - self.btnDump = wx.Button( panel, wx.ID_ANY, u"Dump Colors", wx.DefaultPosition, wx.DefaultSize, 0 ) - buttonsSizer.Add( self.btnDump, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 5 ) + self.btnDump = wx.Button(panel, wx.ID_ANY, u"Dump Colors", wx.DefaultPosition, wx.DefaultSize, 0) + buttonsSizer.Add(self.btnDump, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 5) - self.btnOk = wx.Button( panel, wx.ID_ANY, u"Apply", wx.DefaultPosition, wx.DefaultSize, 0 ) - buttonsSizer.Add( self.btnOk, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 5 ) + self.btnOk = wx.Button(panel, wx.ID_ANY, u"Apply", wx.DefaultPosition, wx.DefaultSize, 0) + buttonsSizer.Add(self.btnOk, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 5) - footerSizer.Add( buttonsSizer, 1, wx.ALIGN_RIGHT, 5 ) - mainSizer.Add( footerSizer, 0, wx.EXPAND, 5 ) + footerSizer.Add(buttonsSizer, 1, wx.ALIGN_RIGHT, 5) + mainSizer.Add(footerSizer, 0, wx.EXPAND, 5) - panel.SetSizer( mainSizer ) + panel.SetSizer(mainSizer) self.SetColours() -# self.Fit() -# self.Layout() + # self.Fit() + # self.Layout() self.sliderGradientStart.Bind(wx.EVT_SCROLL, self.OnGradientStartScroll) self.btnRestore.Bind(wx.EVT_BUTTON, self.RestoreDefaults) self.btnDump.Bind(wx.EVT_BUTTON, self.DumpColours) self.btnOk.Bind(wx.EVT_BUTTON, self.OnOk) - self.cp0100S.Bind( wx.EVT_COLOURPICKER_CHANGED, self.OnColourChanged ) - self.cp0100E.Bind( wx.EVT_COLOURPICKER_CHANGED, self.OnColourChanged ) + self.cp0100S.Bind(wx.EVT_COLOURPICKER_CHANGED, self.OnColourChanged) + self.cp0100E.Bind(wx.EVT_COLOURPICKER_CHANGED, self.OnColourChanged) - self.cp100101S.Bind( wx.EVT_COLOURPICKER_CHANGED, self.OnColourChanged ) - self.cp100101E.Bind( wx.EVT_COLOURPICKER_CHANGED, self.OnColourChanged ) + self.cp100101S.Bind(wx.EVT_COLOURPICKER_CHANGED, self.OnColourChanged) + self.cp100101E.Bind(wx.EVT_COLOURPICKER_CHANGED, self.OnColourChanged) - self.cp101103S.Bind( wx.EVT_COLOURPICKER_CHANGED, self.OnColourChanged ) - self.cp101103E.Bind( wx.EVT_COLOURPICKER_CHANGED, self.OnColourChanged ) + self.cp101103S.Bind(wx.EVT_COLOURPICKER_CHANGED, self.OnColourChanged) + self.cp101103E.Bind(wx.EVT_COLOURPICKER_CHANGED, self.OnColourChanged) - self.cp103105S.Bind( wx.EVT_COLOURPICKER_CHANGED, self.OnColourChanged ) - self.cp103105E.Bind( wx.EVT_COLOURPICKER_CHANGED, self.OnColourChanged ) + self.cp103105S.Bind(wx.EVT_COLOURPICKER_CHANGED, self.OnColourChanged) + self.cp103105E.Bind(wx.EVT_COLOURPICKER_CHANGED, self.OnColourChanged) def getImage(self): return BitmapLoader.getBitmap("pref-gauges_big", "gui") @@ -344,7 +359,6 @@ class PFGaugePref ( PreferenceView): self.gauge0100M.SetGradientStart(self.sliderGradientStart.GetValue()) self.gauge0100E.SetGradientStart(self.sliderGradientStart.GetValue()) - self.cp100101S.SetColour(self.c100101S) self.cp100101E.SetColour(self.c100101E) self.gauge100101S.SetColour(self.c100101S, self.c100101E) @@ -363,7 +377,6 @@ class PFGaugePref ( PreferenceView): self.gauge100101M.SetGradientStart(self.sliderGradientStart.GetValue()) self.gauge100101E.SetGradientStart(self.sliderGradientStart.GetValue()) - self.cp101103S.SetColour(self.c101103S) self.cp101103E.SetColour(self.c101103E) self.gauge101103S.SetColour(self.c101103S, self.c101103E) @@ -378,7 +391,6 @@ class PFGaugePref ( PreferenceView): self.gauge101103M.SetGradientStart(self.sliderGradientStart.GetValue()) self.gauge101103E.SetGradientStart(self.sliderGradientStart.GetValue()) - self.cp103105S.SetColour(self.c103105S) self.cp103105E.SetColour(self.c103105E) self.gauge103105S.SetColour(self.c103105S, self.c103105E) @@ -406,7 +418,7 @@ class PFGaugePref ( PreferenceView): self.wndPreview101103.SetGradientStart(self.sliderGradientStart.GetValue()) self.wndPreview103105.SetColour(self.c103105S, self.c103105E) - self.wndPreview103105.SetPercentages(103,104.99) + self.wndPreview103105.SetPercentages(103, 104.99) self.wndPreview103105.SetGradientStart(self.sliderGradientStart.GetValue()) def OnGradientStartScroll(self, event): @@ -414,16 +426,17 @@ class PFGaugePref ( PreferenceView): self.SetColours() event.Skip() - def OnOk(self, event): - #Apply New Settings + @staticmethod + def OnOk(event): + # Apply New Settings event.Skip() def DumpColours(self, event): - print "Gradient start: %d" % self.sliderGradientStart.GetValue() - print " 0 <-> 100 Start: ", self.c0100S, " End: ", self.c0100E - print "100 <-> 101 Start: ", self.c100101S, " End: ", self.c100101E - print "101 <-> 103 Start: ", self.c101103S, " End: ", self.c101103E - print "103 <-> 105 Start: ", self.c103105S, " End: ", self.c103105E + print("Gradient start: %d" % self.sliderGradientStart.GetValue()) + print(" 0 <-> 100 Start: ", self.c0100S, " End: ", self.c0100E) + print("100 <-> 101 Start: ", self.c100101S, " End: ", self.c100101E) + print("101 <-> 103 Start: ", self.c101103S, " End: ", self.c101103E) + print("103 <-> 105 Start: ", self.c103105S, " End: ", self.c103105E) event.Skip() @@ -478,7 +491,8 @@ class PFGaugePref ( PreferenceView): self.SetColours() event.Skip() - def __del__( self ): + def __del__(self): pass + PFGaugePref.register() diff --git a/gui/builtinPreferenceViews/pyfaGeneralPreferences.py b/gui/builtinPreferenceViews/pyfaGeneralPreferences.py index 2d8104bdd..2e69466bb 100644 --- a/gui/builtinPreferenceViews/pyfaGeneralPreferences.py +++ b/gui/builtinPreferenceViews/pyfaGeneralPreferences.py @@ -1,76 +1,99 @@ +# noinspection PyPackageRequirements import wx from gui.preferenceView import PreferenceView from gui.bitmapLoader import BitmapLoader import gui.mainFrame -import service import gui.globalEvents as GE +from service.settings import SettingsProvider +from service.fit import Fit +from service.price import Price +from service.market import Market -class PFGeneralPref ( PreferenceView): +class PFGeneralPref(PreferenceView): title = "General" - def populatePanel( self, panel ): + def populatePanel(self, panel): self.mainFrame = gui.mainFrame.MainFrame.getInstance() self.dirtySettings = False - self.openFitsSettings = service.SettingsProvider.getInstance().getSettings("pyfaPrevOpenFits", {"enabled": False, "pyfaOpenFits": []}) + self.openFitsSettings = SettingsProvider.getInstance().getSettings("pyfaPrevOpenFits", + {"enabled": False, "pyfaOpenFits": []}) - mainSizer = wx.BoxSizer( wx.VERTICAL ) + mainSizer = wx.BoxSizer(wx.VERTICAL) - self.stTitle = wx.StaticText( panel, wx.ID_ANY, self.title, wx.DefaultPosition, wx.DefaultSize, 0 ) - self.stTitle.Wrap( -1 ) - self.stTitle.SetFont( wx.Font( 12, 70, 90, 90, False, wx.EmptyString ) ) + self.stTitle = wx.StaticText(panel, wx.ID_ANY, self.title, wx.DefaultPosition, wx.DefaultSize, 0) + self.stTitle.Wrap(-1) + self.stTitle.SetFont(wx.Font(12, 70, 90, 90, False, wx.EmptyString)) - mainSizer.Add( self.stTitle, 0, wx.ALL, 5 ) + mainSizer.Add(self.stTitle, 0, wx.ALL, 5) - self.m_staticline1 = wx.StaticLine( panel, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LI_HORIZONTAL ) - mainSizer.Add( self.m_staticline1, 0, wx.EXPAND|wx.TOP|wx.BOTTOM, 5 ) + self.m_staticline1 = wx.StaticLine(panel, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LI_HORIZONTAL) + mainSizer.Add(self.m_staticline1, 0, wx.EXPAND | wx.TOP | wx.BOTTOM, 5) - self.cbGlobalChar = wx.CheckBox( panel, wx.ID_ANY, u"Use global character", wx.DefaultPosition, wx.DefaultSize, 0 ) - mainSizer.Add( self.cbGlobalChar, 0, wx.ALL|wx.EXPAND, 5 ) + self.cbGlobalChar = wx.CheckBox(panel, wx.ID_ANY, u"Use global character", wx.DefaultPosition, wx.DefaultSize, + 0) + mainSizer.Add(self.cbGlobalChar, 0, wx.ALL | wx.EXPAND, 5) - self.cbGlobalDmgPattern = wx.CheckBox( panel, wx.ID_ANY, u"Use global damage pattern", wx.DefaultPosition, wx.DefaultSize, 0 ) - mainSizer.Add( self.cbGlobalDmgPattern, 0, wx.ALL|wx.EXPAND, 5 ) + self.cbGlobalDmgPattern = wx.CheckBox(panel, wx.ID_ANY, u"Use global damage pattern", wx.DefaultPosition, + wx.DefaultSize, 0) + mainSizer.Add(self.cbGlobalDmgPattern, 0, wx.ALL | wx.EXPAND, 5) - self.cbGlobalForceReload = wx.CheckBox( panel, wx.ID_ANY, u"Factor in reload time", wx.DefaultPosition, wx.DefaultSize, 0 ) - mainSizer.Add( self.cbGlobalForceReload, 0, wx.ALL|wx.EXPAND, 5 ) + self.cbGlobalForceReload = wx.CheckBox(panel, wx.ID_ANY, u"Factor in reload time", wx.DefaultPosition, + wx.DefaultSize, 0) + mainSizer.Add(self.cbGlobalForceReload, 0, wx.ALL | wx.EXPAND, 5) - self.cbCompactSkills = wx.CheckBox( panel, wx.ID_ANY, u"Compact skills needed tooltip", wx.DefaultPosition, wx.DefaultSize, 0 ) - mainSizer.Add( self.cbCompactSkills, 0, wx.ALL|wx.EXPAND, 5 ) + self.cbCompactSkills = wx.CheckBox(panel, wx.ID_ANY, u"Compact skills needed tooltip", wx.DefaultPosition, + wx.DefaultSize, 0) + mainSizer.Add(self.cbCompactSkills, 0, wx.ALL | wx.EXPAND, 5) - self.cbFitColorSlots = wx.CheckBox( panel, wx.ID_ANY, u"Color fitting view by slot", wx.DefaultPosition, wx.DefaultSize, 0 ) - mainSizer.Add( self.cbFitColorSlots, 0, wx.ALL|wx.EXPAND, 5 ) + self.cbFitColorSlots = wx.CheckBox(panel, wx.ID_ANY, u"Color fitting view by slot", wx.DefaultPosition, + wx.DefaultSize, 0) + mainSizer.Add(self.cbFitColorSlots, 0, wx.ALL | wx.EXPAND, 5) - self.cbReopenFits = wx.CheckBox( panel, wx.ID_ANY, u"Reopen previous fits on startup", wx.DefaultPosition, wx.DefaultSize, 0 ) - mainSizer.Add( self.cbReopenFits, 0, wx.ALL|wx.EXPAND, 5 ) + self.cbReopenFits = wx.CheckBox(panel, wx.ID_ANY, u"Reopen previous fits on startup", wx.DefaultPosition, + wx.DefaultSize, 0) + mainSizer.Add(self.cbReopenFits, 0, wx.ALL | wx.EXPAND, 5) - self.cbRackSlots = wx.CheckBox( panel, wx.ID_ANY, u"Separate Racks", wx.DefaultPosition, wx.DefaultSize, 0 ) - mainSizer.Add( self.cbRackSlots, 0, wx.ALL|wx.EXPAND, 5 ) + self.cbRackSlots = wx.CheckBox(panel, wx.ID_ANY, u"Separate Racks", wx.DefaultPosition, wx.DefaultSize, 0) + mainSizer.Add(self.cbRackSlots, 0, wx.ALL | wx.EXPAND, 5) - labelSizer = wx.BoxSizer( wx.VERTICAL ) - self.cbRackLabels = wx.CheckBox( panel, wx.ID_ANY, u"Show Rack Labels", wx.DefaultPosition, wx.DefaultSize, 0 ) - labelSizer.Add( self.cbRackLabels, 0, wx.ALL|wx.EXPAND, 5 ) - mainSizer.Add( labelSizer, 0, wx.LEFT|wx.EXPAND, 30 ) + labelSizer = wx.BoxSizer(wx.VERTICAL) + self.cbRackLabels = wx.CheckBox(panel, wx.ID_ANY, u"Show Rack Labels", wx.DefaultPosition, wx.DefaultSize, 0) + labelSizer.Add(self.cbRackLabels, 0, wx.ALL | wx.EXPAND, 5) + mainSizer.Add(labelSizer, 0, wx.LEFT | wx.EXPAND, 30) - self.cbShowTooltip = wx.CheckBox( panel, wx.ID_ANY, u"Show tab tooltips", wx.DefaultPosition, wx.DefaultSize, 0 ) - mainSizer.Add( self.cbShowTooltip, 0, wx.ALL|wx.EXPAND, 5 ) + self.cbShowTooltip = wx.CheckBox(panel, wx.ID_ANY, u"Show tab tooltips", wx.DefaultPosition, wx.DefaultSize, 0) + mainSizer.Add(self.cbShowTooltip, 0, wx.ALL | wx.EXPAND, 5) - self.cbMarketShortcuts = wx.CheckBox( panel, wx.ID_ANY, u"Show market shortcuts", wx.DefaultPosition, wx.DefaultSize, 0 ) - mainSizer.Add( self.cbMarketShortcuts, 0, wx.ALL|wx.EXPAND, 5 ) + self.cbMarketShortcuts = wx.CheckBox(panel, wx.ID_ANY, u"Show market shortcuts", wx.DefaultPosition, + wx.DefaultSize, 0) + mainSizer.Add(self.cbMarketShortcuts, 0, wx.ALL | wx.EXPAND, 5) - self.cbGaugeAnimation = wx.CheckBox( panel, wx.ID_ANY, u"Animate gauges", wx.DefaultPosition, wx.DefaultSize, 0 ) - mainSizer.Add( self.cbGaugeAnimation, 0, wx.ALL|wx.EXPAND, 5 ) + self.cbGaugeAnimation = wx.CheckBox(panel, wx.ID_ANY, u"Animate gauges", wx.DefaultPosition, wx.DefaultSize, 0) + mainSizer.Add(self.cbGaugeAnimation, 0, wx.ALL | wx.EXPAND, 5) - self.cbExportCharges = wx.CheckBox( panel, wx.ID_ANY, u"Export loaded charges", wx.DefaultPosition, wx.DefaultSize, 0 ) - mainSizer.Add( self.cbExportCharges, 0, wx.ALL|wx.EXPAND, 5 ) - - self.cbOpenFitInNew = wx.CheckBox( panel, wx.ID_ANY, u"Open fittings in a new page by default", wx.DefaultPosition, wx.DefaultSize, 0 ) - mainSizer.Add( self.cbOpenFitInNew, 0, wx.ALL|wx.EXPAND, 5 ) + self.cbExportCharges = wx.CheckBox(panel, wx.ID_ANY, u"Export loaded charges", wx.DefaultPosition, + wx.DefaultSize, 0) + mainSizer.Add(self.cbExportCharges, 0, wx.ALL | wx.EXPAND, 5) - defCharSizer = wx.BoxSizer( wx.HORIZONTAL ) + self.cbOpenFitInNew = wx.CheckBox(panel, wx.ID_ANY, u"Open fittings in a new page by default", + wx.DefaultPosition, wx.DefaultSize, 0) + mainSizer.Add(self.cbOpenFitInNew, 0, wx.ALL | wx.EXPAND, 5) - self.sFit = service.Fit.getInstance() + priceSizer = wx.BoxSizer(wx.HORIZONTAL) + + self.stDefaultSystem = wx.StaticText(panel, wx.ID_ANY, u"Default Market Prices:", wx.DefaultPosition, wx.DefaultSize, 0) + self.stDefaultSystem.Wrap(-1) + priceSizer.Add(self.stDefaultSystem, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 5) + + self.chPriceSystem = wx.Choice(panel, choices=Price.systemsList.keys()) + priceSizer.Add(self.chPriceSystem, 1, wx.ALL | wx.EXPAND, 5) + + mainSizer.Add(priceSizer, 0, wx.ALL | wx.EXPAND, 0) + + self.sFit = Fit.getInstance() self.cbGlobalChar.SetValue(self.sFit.serviceFittingOptions["useGlobalCharacter"]) self.cbGlobalDmgPattern.SetValue(self.sFit.serviceFittingOptions["useGlobalDamagePattern"]) @@ -85,6 +108,7 @@ class PFGeneralPref ( PreferenceView): self.cbGaugeAnimation.SetValue(self.sFit.serviceFittingOptions["enableGaugeAnimation"]) self.cbExportCharges.SetValue(self.sFit.serviceFittingOptions["exportCharges"]) self.cbOpenFitInNew.SetValue(self.sFit.serviceFittingOptions["openFitInNew"]) + self.chPriceSystem.SetStringSelection(self.sFit.serviceFittingOptions["priceSystem"]) self.cbGlobalChar.Bind(wx.EVT_CHECKBOX, self.OnCBGlobalCharStateChange) self.cbGlobalDmgPattern.Bind(wx.EVT_CHECKBOX, self.OnCBGlobalDmgPatternStateChange) @@ -99,10 +123,11 @@ class PFGeneralPref ( PreferenceView): self.cbGaugeAnimation.Bind(wx.EVT_CHECKBOX, self.onCBGaugeAnimation) self.cbExportCharges.Bind(wx.EVT_CHECKBOX, self.onCBExportCharges) self.cbOpenFitInNew.Bind(wx.EVT_CHECKBOX, self.onCBOpenFitInNew) + self.chPriceSystem.Bind(wx.EVT_CHOICE, self.onPriceSelection) self.cbRackLabels.Enable(self.sFit.serviceFittingOptions["rackSlots"] or False) - panel.SetSizer( mainSizer ) + panel.SetSizer(mainSizer) panel.Layout() def onCBGlobalColorBySlot(self, event): @@ -163,11 +188,25 @@ class PFGeneralPref ( PreferenceView): def onCBExportCharges(self, event): self.sFit.serviceFittingOptions["exportCharges"] = self.cbExportCharges.GetValue() - + def onCBOpenFitInNew(self, event): self.sFit.serviceFittingOptions["openFitInNew"] = self.cbOpenFitInNew.GetValue() def getImage(self): return BitmapLoader.getBitmap("prefs_settings", "gui") -PFGeneralPref.register() \ No newline at end of file + def onPriceSelection(self, event): + system = self.chPriceSystem.GetString(self.chPriceSystem.GetSelection()) + self.sFit.serviceFittingOptions["priceSystem"] = system + + fitID = self.mainFrame.getActiveFit() + + sMkt = Market.getInstance() + sMkt.clearPriceCache() + + self.sFit.refreshFit(fitID) + wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=fitID)) + event.Skip() + + +PFGeneralPref.register() diff --git a/gui/builtinPreferenceViews/pyfaHTMLExportPreferences.py b/gui/builtinPreferenceViews/pyfaHTMLExportPreferences.py index c80a7a33e..01f00409d 100644 --- a/gui/builtinPreferenceViews/pyfaHTMLExportPreferences.py +++ b/gui/builtinPreferenceViews/pyfaHTMLExportPreferences.py @@ -1,3 +1,4 @@ +# noinspection PyPackageRequirements import wx import os @@ -5,78 +6,82 @@ from gui.preferenceView import PreferenceView from gui.bitmapLoader import BitmapLoader import gui.mainFrame -import service -import gui.globalEvents as GE + +from service.settings import HTMLExportSettings -class PFHTMLExportPref ( PreferenceView): +class PFHTMLExportPref(PreferenceView): title = "HTML Export" - desc = "HTML Export (File > Export HTML) allows you to export your entire fitting "+\ - "database into an HTML file at the specified location. This file can be "+\ - "used in the in-game browser to easily open and import your fits, or used "+\ - "in a regular web browser to open them at NULL-SEC.com or Osmium." - desc2 = "Enabling automatic exporting will update the HTML file after any change "+\ - "to a fit is made. Under certain circumstance, this may cause performance issues." - desc4 = "Export Fittings in a minmal HTML Version, just containing the Fittingslinks " +\ - "without any visual styling or javscript features" + desc = ("HTML Export (File > Export HTML) allows you to export your entire fitting " + "database into an HTML file at the specified location. This file can be " + "used in the in-game browser to easily open and import your fits, or used " + "in a regular web browser to open them at NULL-SEC.com or Osmium.") + desc2 = ("Enabling automatic exporting will update the HTML file after any change " + "to a fit is made. Under certain circumstance, this may cause performance issues.") + desc4 = ("Export Fittings in a minmal HTML Version, just containing the Fittingslinks " + "without any visual styling or javscript features") - def populatePanel( self, panel ): + def populatePanel(self, panel): self.mainFrame = gui.mainFrame.MainFrame.getInstance() - self.HTMLExportSettings = service.settings.HTMLExportSettings.getInstance() + self.HTMLExportSettings = HTMLExportSettings.getInstance() self.dirtySettings = False dlgWidth = panel.GetParent().GetParent().ClientSize.width - mainSizer = wx.BoxSizer( wx.VERTICAL ) + mainSizer = wx.BoxSizer(wx.VERTICAL) - self.stTitle = wx.StaticText( panel, wx.ID_ANY, self.title, wx.DefaultPosition, wx.DefaultSize, 0 ) - self.stTitle.Wrap( -1 ) - self.stTitle.SetFont( wx.Font( 12, 70, 90, 90, False, wx.EmptyString ) ) - mainSizer.Add( self.stTitle, 0, wx.ALL, 5 ) + self.stTitle = wx.StaticText(panel, wx.ID_ANY, self.title, wx.DefaultPosition, wx.DefaultSize, 0) + self.stTitle.Wrap(-1) + self.stTitle.SetFont(wx.Font(12, 70, 90, 90, False, wx.EmptyString)) + mainSizer.Add(self.stTitle, 0, wx.ALL, 5) - self.m_staticline1 = wx.StaticLine( panel, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LI_HORIZONTAL ) - mainSizer.Add( self.m_staticline1, 0, wx.EXPAND|wx.TOP|wx.BOTTOM, 5 ) + self.m_staticline1 = wx.StaticLine(panel, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LI_HORIZONTAL) + mainSizer.Add(self.m_staticline1, 0, wx.EXPAND | wx.TOP | wx.BOTTOM, 5) - self.stDesc = wx.StaticText( panel, wx.ID_ANY, self.desc, wx.DefaultPosition, wx.DefaultSize, 0 ) + self.stDesc = wx.StaticText(panel, wx.ID_ANY, self.desc, wx.DefaultPosition, wx.DefaultSize, 0) self.stDesc.Wrap(dlgWidth - 50) - mainSizer.Add( self.stDesc, 0, wx.ALL, 5 ) + mainSizer.Add(self.stDesc, 0, wx.ALL, 5) - self.PathLinkCtrl = wx.HyperlinkCtrl( panel, wx.ID_ANY, self.HTMLExportSettings.getPath(), u'file:///{}'.format(self.HTMLExportSettings.getPath()), wx.DefaultPosition, wx.DefaultSize, wx.HL_ALIGN_LEFT|wx.NO_BORDER|wx.HL_CONTEXTMENU ) - mainSizer.Add( self.PathLinkCtrl, 0, wx.ALL|wx.EXPAND, 5) + self.PathLinkCtrl = wx.HyperlinkCtrl(panel, wx.ID_ANY, self.HTMLExportSettings.getPath(), + u'file:///{}'.format(self.HTMLExportSettings.getPath()), + wx.DefaultPosition, wx.DefaultSize, + wx.HL_ALIGN_LEFT | wx.NO_BORDER | wx.HL_CONTEXTMENU) + mainSizer.Add(self.PathLinkCtrl, 0, wx.ALL | wx.EXPAND, 5) - self.fileSelectDialog = wx.FileDialog(None, "Save Fitting As...", wildcard = "EVE IGB HTML fitting file (*.html)|*.html", style = wx.FD_SAVE) + self.fileSelectDialog = wx.FileDialog(None, "Save Fitting As...", + wildcard="EVE IGB HTML fitting file (*.html)|*.html", style=wx.FD_SAVE) self.fileSelectDialog.SetPath(self.HTMLExportSettings.getPath()) - self.fileSelectDialog.SetFilename(os.path.basename(self.HTMLExportSettings.getPath())); + self.fileSelectDialog.SetFilename(os.path.basename(self.HTMLExportSettings.getPath())) - self.fileSelectButton = wx.Button(panel, -1, "Set export destination", pos=(0,0)) + self.fileSelectButton = wx.Button(panel, -1, "Set export destination", pos=(0, 0)) self.fileSelectButton.Bind(wx.EVT_BUTTON, self.selectHTMLExportFilePath) - mainSizer.Add( self.fileSelectButton, 0, wx.ALL|wx.ALIGN_CENTER_VERTICAL, 5) + mainSizer.Add(self.fileSelectButton, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 5) - self.stDesc2 = wx.StaticText( panel, wx.ID_ANY, self.desc2, wx.DefaultPosition, wx.DefaultSize, 0 ) + self.stDesc2 = wx.StaticText(panel, wx.ID_ANY, self.desc2, wx.DefaultPosition, wx.DefaultSize, 0) self.stDesc2.Wrap(dlgWidth - 50) - mainSizer.Add( self.stDesc2, 0, wx.ALL, 5 ) + mainSizer.Add(self.stDesc2, 0, wx.ALL, 5) - self.exportEnabled = wx.CheckBox( panel, wx.ID_ANY, u"Enable automatic HTML export", wx.DefaultPosition, wx.DefaultSize, 0 ) + self.exportEnabled = wx.CheckBox(panel, wx.ID_ANY, u"Enable automatic HTML export", wx.DefaultPosition, + wx.DefaultSize, 0) self.exportEnabled.SetValue(self.HTMLExportSettings.getEnabled()) self.exportEnabled.Bind(wx.EVT_CHECKBOX, self.OnExportEnabledChange) - mainSizer.Add( self.exportEnabled, 0, wx.ALL|wx.EXPAND, 5 ) + mainSizer.Add(self.exportEnabled, 0, wx.ALL | wx.EXPAND, 5) - - - self.stDesc4 = wx.StaticText( panel, wx.ID_ANY, self.desc4, wx.DefaultPosition, wx.DefaultSize, 0 ) + self.stDesc4 = wx.StaticText(panel, wx.ID_ANY, self.desc4, wx.DefaultPosition, wx.DefaultSize, 0) self.stDesc4.Wrap(dlgWidth - 50) - mainSizer.Add( self.stDesc4, 0, wx.ALL, 5 ) + mainSizer.Add(self.stDesc4, 0, wx.ALL, 5) - self.exportMinimal = wx.CheckBox( panel, wx.ID_ANY, u"Enable minimal export Format", wx.DefaultPosition, wx.DefaultSize, 0 ) + self.exportMinimal = wx.CheckBox(panel, wx.ID_ANY, u"Enable minimal export Format", wx.DefaultPosition, + wx.DefaultSize, 0) self.exportMinimal.SetValue(self.HTMLExportSettings.getMinimalEnabled()) self.exportMinimal.Bind(wx.EVT_CHECKBOX, self.OnMinimalEnabledChange) - mainSizer.Add( self.exportMinimal, 0, wx.ALL|wx.EXPAND, 5 ) + mainSizer.Add(self.exportMinimal, 0, wx.ALL | wx.EXPAND, 5) - panel.SetSizer( mainSizer ) + panel.SetSizer(mainSizer) panel.Layout() def setPathLinkCtrlValues(self, path): self.PathLinkCtrl.SetLabel(self.HTMLExportSettings.getPath()) self.PathLinkCtrl.SetURL(u'file:///{}'.format(self.HTMLExportSettings.getPath())) - self.PathLinkCtrl.SetSize(wx.DefaultSize); + self.PathLinkCtrl.SetSize(wx.DefaultSize) self.PathLinkCtrl.Refresh() def selectHTMLExportFilePath(self, event): @@ -94,4 +99,5 @@ class PFHTMLExportPref ( PreferenceView): def getImage(self): return BitmapLoader.getBitmap("prefs_html", "gui") + PFHTMLExportPref.register() diff --git a/gui/builtinPreferenceViews/pyfaNetworkPreferences.py b/gui/builtinPreferenceViews/pyfaNetworkPreferences.py index bd79dd88c..e20f8a01d 100644 --- a/gui/builtinPreferenceViews/pyfaNetworkPreferences.py +++ b/gui/builtinPreferenceViews/pyfaNetworkPreferences.py @@ -1,53 +1,58 @@ +# noinspection PyPackageRequirements import wx from gui.preferenceView import PreferenceView from gui.bitmapLoader import BitmapLoader import gui.mainFrame -import service +from service.settings import NetworkSettings +from service.network import Network -class PFNetworkPref ( PreferenceView): + +class PFNetworkPref(PreferenceView): title = "Network" - def populatePanel( self, panel ): + def populatePanel(self, panel): self.mainFrame = gui.mainFrame.MainFrame.getInstance() - self.settings = service.settings.NetworkSettings.getInstance() - self.network = service.Network.getInstance() + self.settings = NetworkSettings.getInstance() + self.network = Network.getInstance() self.dirtySettings = False - mainSizer = wx.BoxSizer( wx.VERTICAL ) + mainSizer = wx.BoxSizer(wx.VERTICAL) - self.stTitle = wx.StaticText( panel, wx.ID_ANY, self.title, wx.DefaultPosition, wx.DefaultSize, 0 ) - self.stTitle.Wrap( -1 ) - self.stTitle.SetFont( wx.Font( 12, 70, 90, 90, False, wx.EmptyString ) ) + self.stTitle = wx.StaticText(panel, wx.ID_ANY, self.title, wx.DefaultPosition, wx.DefaultSize, 0) + self.stTitle.Wrap(-1) + self.stTitle.SetFont(wx.Font(12, 70, 90, 90, False, wx.EmptyString)) - mainSizer.Add( self.stTitle, 0, wx.ALL, 5 ) + mainSizer.Add(self.stTitle, 0, wx.ALL, 5) - self.m_staticline1 = wx.StaticLine( panel, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LI_HORIZONTAL ) - mainSizer.Add( self.m_staticline1, 0, wx.EXPAND|wx.TOP|wx.BOTTOM, 5 ) + self.m_staticline1 = wx.StaticLine(panel, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LI_HORIZONTAL) + mainSizer.Add(self.m_staticline1, 0, wx.EXPAND | wx.TOP | wx.BOTTOM, 5) - self.cbEnableNetwork = wx.CheckBox( panel, wx.ID_ANY, u"Enable Network", wx.DefaultPosition, wx.DefaultSize, 0 ) - mainSizer.Add( self.cbEnableNetwork, 0, wx.ALL|wx.EXPAND, 5 ) + self.cbEnableNetwork = wx.CheckBox(panel, wx.ID_ANY, u"Enable Network", wx.DefaultPosition, wx.DefaultSize, 0) + mainSizer.Add(self.cbEnableNetwork, 0, wx.ALL | wx.EXPAND, 5) - subSizer = wx.BoxSizer( wx.VERTICAL ) - self.cbEve = wx.CheckBox( panel, wx.ID_ANY, u"EVE Servers (API && CREST import)", wx.DefaultPosition, wx.DefaultSize, 0 ) - subSizer.Add( self.cbEve, 0, wx.ALL|wx.EXPAND, 5 ) + subSizer = wx.BoxSizer(wx.VERTICAL) + self.cbEve = wx.CheckBox(panel, wx.ID_ANY, u"EVE Servers (API && CREST import)", wx.DefaultPosition, + wx.DefaultSize, 0) + subSizer.Add(self.cbEve, 0, wx.ALL | wx.EXPAND, 5) - self.cbPricing = wx.CheckBox( panel, wx.ID_ANY, u"Pricing updates", wx.DefaultPosition, wx.DefaultSize, 0 ) - subSizer.Add( self.cbPricing, 0, wx.ALL|wx.EXPAND, 5 ) + self.cbPricing = wx.CheckBox(panel, wx.ID_ANY, u"Pricing updates", wx.DefaultPosition, wx.DefaultSize, 0) + subSizer.Add(self.cbPricing, 0, wx.ALL | wx.EXPAND, 5) - self.cbPyfaUpdate = wx.CheckBox( panel, wx.ID_ANY, u"Pyfa Update checks", wx.DefaultPosition, wx.DefaultSize, 0 ) - subSizer.Add( self.cbPyfaUpdate, 0, wx.ALL|wx.EXPAND, 5 ) + self.cbPyfaUpdate = wx.CheckBox(panel, wx.ID_ANY, u"Pyfa Update checks", wx.DefaultPosition, wx.DefaultSize, 0) + subSizer.Add(self.cbPyfaUpdate, 0, wx.ALL | wx.EXPAND, 5) - mainSizer.Add( subSizer, 0, wx.LEFT|wx.EXPAND, 30 ) + mainSizer.Add(subSizer, 0, wx.LEFT | wx.EXPAND, 30) - proxyTitle = wx.StaticText( panel, wx.ID_ANY, "Proxy settings", wx.DefaultPosition, wx.DefaultSize, 0 ) - proxyTitle.Wrap( -1 ) - proxyTitle.SetFont( wx.Font( 12, 70, 90, 90, False, wx.EmptyString ) ) + proxyTitle = wx.StaticText(panel, wx.ID_ANY, "Proxy settings", wx.DefaultPosition, wx.DefaultSize, 0) + proxyTitle.Wrap(-1) + proxyTitle.SetFont(wx.Font(12, 70, 90, 90, False, wx.EmptyString)) - mainSizer.Add( proxyTitle, 0, wx.ALL, 5 ) - mainSizer.Add( wx.StaticLine( panel, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LI_HORIZONTAL ), 0, wx.EXPAND, 5 ) + mainSizer.Add(proxyTitle, 0, wx.ALL, 5) + mainSizer.Add(wx.StaticLine(panel, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LI_HORIZONTAL), 0, + wx.EXPAND, 5) self.cbEnableNetwork.SetValue(self.settings.isEnabled(self.network.ENABLED)) self.cbEve.SetValue(self.settings.isEnabled(self.network.EVE)) @@ -61,9 +66,9 @@ class PFNetworkPref ( PreferenceView): self.toggleNetworks(self.cbEnableNetwork.GetValue()) - #--------------- + # --------------- # Proxy - #--------------- + # --------------- self.nMode = self.settings.getMode() self.nAddr = self.settings.getAddress() @@ -73,51 +78,50 @@ class PFNetworkPref ( PreferenceView): if self.nAuth is None: self.nAuth = ("", "") # we don't want None here, it should be a tuple - ptypeSizer = wx.BoxSizer( wx.HORIZONTAL ) + ptypeSizer = wx.BoxSizer(wx.HORIZONTAL) - self.stPType = wx.StaticText( panel, wx.ID_ANY, u"Mode:", wx.DefaultPosition, wx.DefaultSize, 0 ) - self.stPType.Wrap( -1 ) - ptypeSizer.Add( self.stPType, 0, wx.ALL|wx.ALIGN_CENTER_VERTICAL, 5 ) + self.stPType = wx.StaticText(panel, wx.ID_ANY, u"Mode:", wx.DefaultPosition, wx.DefaultSize, 0) + self.stPType.Wrap(-1) + ptypeSizer.Add(self.stPType, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 5) - self.chProxyTypeChoices = [ u"No proxy", u"Auto-detected proxy settings", u"Manual proxy settings" ] - self.chProxyType = wx.Choice( panel, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, self.chProxyTypeChoices, 0 ) + self.chProxyTypeChoices = [u"No proxy", u"Auto-detected proxy settings", u"Manual proxy settings"] + self.chProxyType = wx.Choice(panel, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, self.chProxyTypeChoices, 0) + self.chProxyType.SetSelection(self.nMode) - self.chProxyType.SetSelection( self.nMode ) + ptypeSizer.Add(self.chProxyType, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 5) - ptypeSizer.Add( self.chProxyType, 0, wx.ALL|wx.ALIGN_CENTER_VERTICAL, 5 ) + mainSizer.Add(ptypeSizer, 0, wx.EXPAND, 5) - mainSizer.Add( ptypeSizer, 0, wx.EXPAND, 5 ) + fgAddrSizer = wx.FlexGridSizer(2, 2, 0, 0) + fgAddrSizer.AddGrowableCol(1) + fgAddrSizer.SetFlexibleDirection(wx.BOTH) + fgAddrSizer.SetNonFlexibleGrowMode(wx.FLEX_GROWMODE_SPECIFIED) - fgAddrSizer = wx.FlexGridSizer( 2, 2, 0, 0 ) - fgAddrSizer.AddGrowableCol( 1 ) - fgAddrSizer.SetFlexibleDirection( wx.BOTH ) - fgAddrSizer.SetNonFlexibleGrowMode( wx.FLEX_GROWMODE_SPECIFIED ) + self.stPSetAddr = wx.StaticText(panel, wx.ID_ANY, u"Addr:", wx.DefaultPosition, wx.DefaultSize, 0) + self.stPSetAddr.Wrap(-1) + fgAddrSizer.Add(self.stPSetAddr, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 5) + self.editProxySettingsAddr = wx.TextCtrl(panel, wx.ID_ANY, self.nAddr, wx.DefaultPosition, wx.DefaultSize, 0) - self.stPSetAddr = wx.StaticText( panel, wx.ID_ANY, u"Addr:", wx.DefaultPosition, wx.DefaultSize, 0 ) - self.stPSetAddr.Wrap( -1 ) - fgAddrSizer.Add( self.stPSetAddr, 0, wx.ALL|wx.ALIGN_CENTER_VERTICAL, 5 ) + fgAddrSizer.Add(self.editProxySettingsAddr, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL | wx.EXPAND, 5) - self.editProxySettingsAddr = wx.TextCtrl( panel, wx.ID_ANY, self.nAddr, wx.DefaultPosition, wx.DefaultSize, 0 ) + self.stPSetPort = wx.StaticText(panel, wx.ID_ANY, u"Port:", wx.DefaultPosition, wx.DefaultSize, 0) + self.stPSetPort.Wrap(-1) - fgAddrSizer.Add( self.editProxySettingsAddr, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL | wx.EXPAND, 5 ) + fgAddrSizer.Add(self.stPSetPort, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 5) - self.stPSetPort = wx.StaticText( panel, wx.ID_ANY, u"Port:", wx.DefaultPosition, wx.DefaultSize, 0 ) - self.stPSetPort.Wrap( -1 ) + self.editProxySettingsPort = wx.TextCtrl(panel, wx.ID_ANY, self.nPort, wx.DefaultPosition, wx.DefaultSize, 0) - fgAddrSizer.Add( self.stPSetPort, 0, wx.ALL|wx.ALIGN_CENTER_VERTICAL, 5 ) + fgAddrSizer.Add(self.editProxySettingsPort, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL | wx.EXPAND, 5) - self.editProxySettingsPort = wx.TextCtrl( panel, wx.ID_ANY, self.nPort, wx.DefaultPosition, wx.DefaultSize, 0 ) - - fgAddrSizer.Add( self.editProxySettingsPort, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL | wx.EXPAND, 5 ) - - mainSizer.Add( fgAddrSizer, 0, wx.EXPAND, 5) + mainSizer.Add(fgAddrSizer, 0, wx.EXPAND, 5) # proxy auth information: login and pass self.stPSetLogin = wx.StaticText(panel, wx.ID_ANY, u"Username:", wx.DefaultPosition, wx.DefaultSize, 0) self.stPSetLogin.Wrap(-1) - self.editProxySettingsLogin = wx.TextCtrl(panel, wx.ID_ANY, self.nAuth[0], wx.DefaultPosition, wx.DefaultSize, 0) + self.editProxySettingsLogin = wx.TextCtrl(panel, wx.ID_ANY, self.nAuth[0], wx.DefaultPosition, wx.DefaultSize, + 0) self.stPSetPassword = wx.StaticText(panel, wx.ID_ANY, u"Password:", wx.DefaultPosition, wx.DefaultSize, 0) self.stPSetPassword.Wrap(-1) self.editProxySettingsPassword = wx.TextCtrl(panel, wx.ID_ANY, self.nAuth[1], wx.DefaultPosition, @@ -129,23 +133,24 @@ class PFNetworkPref ( PreferenceView): pAuthSizer.Add(self.editProxySettingsPassword, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 5) mainSizer.Add(pAuthSizer, 0, wx.EXPAND, 5) - self.stPSAutoDetected = wx.StaticText( panel, wx.ID_ANY, u"Auto-detected: ", wx.DefaultPosition, wx.DefaultSize, 0 ) - self.stPSAutoDetected.Wrap( -1 ) - mainSizer.Add( self.stPSAutoDetected, 0, wx.ALL|wx.ALIGN_CENTER_VERTICAL, 5 ) + self.stPSAutoDetected = wx.StaticText(panel, wx.ID_ANY, u"Auto-detected: ", wx.DefaultPosition, wx.DefaultSize, + 0) + self.stPSAutoDetected.Wrap(-1) + mainSizer.Add(self.stPSAutoDetected, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 5) - btnSizer = wx.BoxSizer( wx.HORIZONTAL ) - btnSizer.AddSpacer( ( 0, 0), 1, wx.EXPAND, 5 ) + btnSizer = wx.BoxSizer(wx.HORIZONTAL) + btnSizer.AddSpacer((0, 0), 1, wx.EXPAND, 5) - self.btnApply = wx.Button( panel, wx.ID_ANY, u"Apply Proxy Settings", wx.DefaultPosition, wx.DefaultSize, 0 ) + self.btnApply = wx.Button(panel, wx.ID_ANY, u"Apply Proxy Settings", wx.DefaultPosition, wx.DefaultSize, 0) - btnSizer.Add( self.btnApply, 0, wx.ALL, 5 ) + btnSizer.Add(self.btnApply, 0, wx.ALL, 5) - mainSizer.Add(btnSizer, 0, wx.EXPAND,5) + mainSizer.Add(btnSizer, 0, wx.EXPAND, 5) proxy = self.settings.autodetect() if proxy is not None: - addr,port = proxy + addr, port = proxy txt = addr + ":" + str(port) else: txt = "None" @@ -159,17 +164,16 @@ class PFNetworkPref ( PreferenceView): self.editProxySettingsLogin.Bind(wx.EVT_TEXT, self.OnEditPSLoginText) self.editProxySettingsPassword.Bind(wx.EVT_TEXT, self.OnEditPSPasswordText) - self.btnApply.Bind(wx.EVT_BUTTON, self.OnBtnApply) self.UpdateApplyButtonState() - if self.nMode is not service.settings.NetworkSettings.PROXY_MODE_MANUAL: # == 2 + if self.nMode is not NetworkSettings.PROXY_MODE_MANUAL: # == 2 self.ToggleProxySettings(False) else: self.ToggleProxySettings(True) - panel.SetSizer( mainSizer ) + panel.SetSizer(mainSizer) panel.Layout() def toggleNetworks(self, toggle): @@ -236,7 +240,7 @@ class PFNetworkPref ( PreferenceView): self.UpdateApplyButtonState() - if choice is not service.settings.NetworkSettings.PROXY_MODE_MANUAL: + if choice is not NetworkSettings.PROXY_MODE_MANUAL: self.ToggleProxySettings(False) else: self.ToggleProxySettings(True) @@ -264,4 +268,5 @@ class PFNetworkPref ( PreferenceView): def getImage(self): return BitmapLoader.getBitmap("prefs_proxy", "gui") + PFNetworkPref.register() diff --git a/gui/builtinPreferenceViews/pyfaUpdatePreferences.py b/gui/builtinPreferenceViews/pyfaUpdatePreferences.py index c49bd0c94..4e3519e2f 100644 --- a/gui/builtinPreferenceViews/pyfaUpdatePreferences.py +++ b/gui/builtinPreferenceViews/pyfaUpdatePreferences.py @@ -1,84 +1,83 @@ +# noinspection PyPackageRequirements import wx -import service -import os from gui.preferenceView import PreferenceView from gui.bitmapLoader import BitmapLoader - -import service -import gui.globalEvents as GE +from service.settings import UpdateSettings -class PFUpdatePref (PreferenceView): +class PFUpdatePref(PreferenceView): title = "Updates" - desc = "Pyfa can automatically check and notify you of new releases. "+\ - "This feature is toggled in the Network settings. "+\ - "Here, you may allow pre-release notifications and view "+\ - "suppressed release notifications, if any." + desc = ("Pyfa can automatically check and notify you of new releases. " + "This feature is toggled in the Network settings. " + "Here, you may allow pre-release notifications and view " + "suppressed release notifications, if any.") - def populatePanel( self, panel ): - self.UpdateSettings = service.settings.UpdateSettings.getInstance() + def populatePanel(self, panel): + self.UpdateSettings = UpdateSettings.getInstance() self.dirtySettings = False dlgWidth = panel.GetParent().GetParent().ClientSize.width - mainSizer = wx.BoxSizer( wx.VERTICAL ) + mainSizer = wx.BoxSizer(wx.VERTICAL) - self.stTitle = wx.StaticText( panel, wx.ID_ANY, self.title, wx.DefaultPosition, wx.DefaultSize, 0 ) - self.stTitle.Wrap( -1 ) - self.stTitle.SetFont( wx.Font( 12, 70, 90, 90, False, wx.EmptyString ) ) - mainSizer.Add( self.stTitle, 0, wx.ALL, 5 ) + self.stTitle = wx.StaticText(panel, wx.ID_ANY, self.title, wx.DefaultPosition, wx.DefaultSize, 0) + self.stTitle.Wrap(-1) + self.stTitle.SetFont(wx.Font(12, 70, 90, 90, False, wx.EmptyString)) + mainSizer.Add(self.stTitle, 0, wx.ALL, 5) - self.m_staticline1 = wx.StaticLine( panel, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LI_HORIZONTAL ) - mainSizer.Add( self.m_staticline1, 0, wx.EXPAND|wx.TOP|wx.BOTTOM, 5 ) + self.m_staticline1 = wx.StaticLine(panel, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LI_HORIZONTAL) + mainSizer.Add(self.m_staticline1, 0, wx.EXPAND | wx.TOP | wx.BOTTOM, 5) - self.stDesc = wx.StaticText( panel, wx.ID_ANY, self.desc, wx.DefaultPosition, wx.DefaultSize, 0 ) + self.stDesc = wx.StaticText(panel, wx.ID_ANY, self.desc, wx.DefaultPosition, wx.DefaultSize, 0) self.stDesc.Wrap(dlgWidth - 50) - mainSizer.Add( self.stDesc, 0, wx.ALL, 5 ) + mainSizer.Add(self.stDesc, 0, wx.ALL, 5) - self.suppressPrerelease = wx.CheckBox( panel, wx.ID_ANY, u"Allow pre-release notifications", wx.DefaultPosition, wx.DefaultSize, 0 ) + self.suppressPrerelease = wx.CheckBox(panel, wx.ID_ANY, u"Allow pre-release notifications", wx.DefaultPosition, + wx.DefaultSize, 0) self.suppressPrerelease.Bind(wx.EVT_CHECKBOX, self.OnPrereleaseStateChange) self.suppressPrerelease.SetValue(not self.UpdateSettings.get('prerelease')) - mainSizer.Add( self.suppressPrerelease, 0, wx.ALL|wx.EXPAND, 5 ) + mainSizer.Add(self.suppressPrerelease, 0, wx.ALL | wx.EXPAND, 5) - if (self.UpdateSettings.get('version')): - self.versionSizer = wx.BoxSizer( wx.VERTICAL ) + if self.UpdateSettings.get('version'): + self.versionSizer = wx.BoxSizer(wx.VERTICAL) + self.versionTitle = wx.StaticText(panel, wx.ID_ANY, "Suppressing {0} Notifications".format( + self.UpdateSettings.get('version')), wx.DefaultPosition, wx.DefaultSize, 0) + self.versionTitle.Wrap(-1) + self.versionTitle.SetFont(wx.Font(12, 70, 90, 90, False, wx.EmptyString)) - self.versionTitle = wx.StaticText( panel, wx.ID_ANY, "Suppressing "+self.UpdateSettings.get('version')+" Notifications", wx.DefaultPosition, wx.DefaultSize, 0 ) - self.versionTitle.Wrap( -1 ) - self.versionTitle.SetFont( wx.Font( 12, 70, 90, 90, False, wx.EmptyString ) ) + self.versionInfo = ("There is a release available which you have chosen to suppress. " + "You can choose to reset notification suppression for this release, " + "or download the new release from GitHub.") - self.versionInfo = "There is a release available which you have chosen to suppress. "+\ - "You can choose to reset notification suppression for this release, "+\ - "or download the new release from GitHub." + self.versionSizer.AddSpacer((5, 5), 0, wx.EXPAND, 5) - self.versionSizer.AddSpacer( ( 5, 5), 0, wx.EXPAND, 5 ) + self.versionSizer.Add(wx.StaticLine(panel, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LI_HORIZONTAL), + 0, wx.EXPAND, 5) + self.versionSizer.AddSpacer((5, 5), 0, wx.EXPAND, 5) - self.versionSizer.Add( wx.StaticLine( panel, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LI_HORIZONTAL ), 0, wx.EXPAND, 5 ) - self.versionSizer.AddSpacer( ( 5, 5), 0, wx.EXPAND, 5 ) - - self.versionSizer.Add( self.versionTitle, 0, wx.EXPAND, 5 ) - self.versionDesc = wx.StaticText( panel, wx.ID_ANY, self.versionInfo, wx.DefaultPosition, wx.DefaultSize, 0 ) + self.versionSizer.Add(self.versionTitle, 0, wx.EXPAND, 5) + self.versionDesc = wx.StaticText(panel, wx.ID_ANY, self.versionInfo, wx.DefaultPosition, wx.DefaultSize, 0) self.versionDesc.Wrap(dlgWidth - 50) - self.versionSizer.Add( self.versionDesc, 0, wx.ALL, 5 ) + self.versionSizer.Add(self.versionDesc, 0, wx.ALL, 5) - actionSizer = wx.BoxSizer( wx.HORIZONTAL ) - resetSizer = wx.BoxSizer( wx.VERTICAL ) + actionSizer = wx.BoxSizer(wx.HORIZONTAL) + resetSizer = wx.BoxSizer(wx.VERTICAL) - self.downloadButton = wx.Button( panel, wx.ID_ANY, "Download", wx.DefaultPosition, wx.DefaultSize, 0 ) + self.downloadButton = wx.Button(panel, wx.ID_ANY, "Download", wx.DefaultPosition, wx.DefaultSize, 0) self.downloadButton.Bind(wx.EVT_BUTTON, self.OnDownload) - resetSizer.Add( self.downloadButton, 0, wx.ALL, 5 ) - actionSizer.Add( resetSizer, 1, wx.EXPAND, 5 ) + resetSizer.Add(self.downloadButton, 0, wx.ALL, 5) + actionSizer.Add(resetSizer, 1, wx.EXPAND, 5) - self.resetButton = wx.Button( panel, wx.ID_ANY, "Reset Suppression", wx.DefaultPosition, wx.DefaultSize, 0 ) + self.resetButton = wx.Button(panel, wx.ID_ANY, "Reset Suppression", wx.DefaultPosition, wx.DefaultSize, 0) self.resetButton.Bind(wx.EVT_BUTTON, self.ResetSuppression) - actionSizer.Add( self.resetButton, 0, wx.ALL, 5 ) - self.versionSizer.Add( actionSizer, 0, wx.EXPAND, 5 ) - mainSizer.Add( self.versionSizer, 0, wx.EXPAND, 5 ) + actionSizer.Add(self.resetButton, 0, wx.ALL, 5) + self.versionSizer.Add(actionSizer, 0, wx.EXPAND, 5) + mainSizer.Add(self.versionSizer, 0, wx.EXPAND, 5) - panel.SetSizer( mainSizer ) + panel.SetSizer(mainSizer) panel.Layout() def OnPrereleaseStateChange(self, event): @@ -96,9 +95,10 @@ class PFUpdatePref (PreferenceView): self.resetButton.Hide() def OnDownload(self, event): - wx.LaunchDefaultBrowser('https://github.com/pyfa-org/Pyfa/releases/tag/'+self.UpdateSettings.get('version')) + wx.LaunchDefaultBrowser('https://github.com/pyfa-org/Pyfa/releases/tag/' + self.UpdateSettings.get('version')) def getImage(self): return BitmapLoader.getBitmap("prefs_update", "gui") + PFUpdatePref.register() diff --git a/gui/builtinStatsViews/capacitorViewFull.py b/gui/builtinStatsViews/capacitorViewFull.py index c07045199..f85b35e4c 100644 --- a/gui/builtinStatsViews/capacitorViewFull.py +++ b/gui/builtinStatsViews/capacitorViewFull.py @@ -1,4 +1,4 @@ -#=============================================================================== +# ============================================================================= # Copyright (C) 2010 Diego Duclos # # This file is part of pyfa. @@ -15,24 +15,27 @@ # # You should have received a copy of the GNU General Public License # along with pyfa. If not, see . -#=============================================================================== +# ============================================================================= +# noinspection PyPackageRequirements import wx from gui.statsView import StatsView -from gui import builtinStatsViews from gui.bitmapLoader import BitmapLoader from gui.utils.numberFormatter import formatAmount + class CapacitorViewFull(StatsView): name = "capacitorViewFull" + def __init__(self, parent): StatsView.__init__(self) self.parent = parent + def getHeaderText(self, fit): return "Capacitor" def getTextExtentW(self, text): - width, height = self.parent.GetTextExtent( text ) + width, height = self.parent.GetTextExtent(text) return width def populatePanel(self, contentPanel, headerPanel): @@ -104,12 +107,13 @@ class CapacitorViewFull(StatsView): chargeSizer.Add(lbl, 0, wx.ALIGN_CENTER) chargeSizer.Add(wx.StaticText(parent, wx.ID_ANY, " GJ/s"), 0, wx.ALIGN_CENTER) - def refreshPanel(self, fit): - #If we did anything intresting, we'd update our labels to reflect the new fit's stats here - stats= (("label%sCapacitorCapacity", lambda: fit.ship.getModifiedItemAttr("capacitorCapacity"), 3, 0, 9), - ("label%sCapacitorRecharge", lambda: fit.capRecharge, 3, 0, 0), - ("label%sCapacitorDischarge", lambda: fit.capUsed, 3, 0, 0)) + # If we did anything intresting, we'd update our labels to reflect the new fit's stats here + stats = ( + ("label%sCapacitorCapacity", lambda: fit.ship.getModifiedItemAttr("capacitorCapacity"), 3, 0, 9), + ("label%sCapacitorRecharge", lambda: fit.capRecharge, 3, 0, 0), + ("label%sCapacitorDischarge", lambda: fit.capUsed, 3, 0, 0), + ) panel = "Full" for labelName, value, prec, lowest, highest in stats: @@ -127,8 +131,8 @@ class CapacitorViewFull(StatsView): capStable = fit.capStable if fit is not None else False lblNameTime = "label%sCapacitorTime" lblNameState = "label%sCapacitorState" - if isinstance(capState, tuple): - t = "%.1f%%-%.1f%%" % capState + if isinstance(capState, tuple) and len(capState) >= 2: + t = ("{0}%-{1}%", capState[0], capState[1]) s = "" else: if capStable: @@ -147,4 +151,5 @@ class CapacitorViewFull(StatsView): self.panel.Layout() self.headerPanel.Layout() + CapacitorViewFull.register() diff --git a/gui/builtinStatsViews/firepowerViewFull.py b/gui/builtinStatsViews/firepowerViewFull.py index 0e8d1c070..618ca52de 100644 --- a/gui/builtinStatsViews/firepowerViewFull.py +++ b/gui/builtinStatsViews/firepowerViewFull.py @@ -1,4 +1,4 @@ -#=============================================================================== +# ============================================================================= # Copyright (C) 2010 Diego Duclos # # This file is part of pyfa. @@ -15,17 +15,20 @@ # # You should have received a copy of the GNU General Public License # along with pyfa. If not, see . -#=============================================================================== +# ============================================================================= +# noinspection PyPackageRequirements import wx -import service import gui.mainFrame from gui.statsView import StatsView from gui.bitmapLoader import BitmapLoader from gui.utils.numberFormatter import formatAmount +from service.fit import Fit + class FirepowerViewFull(StatsView): name = "firepowerViewFull" + def __init__(self, parent): StatsView.__init__(self) self.parent = parent @@ -35,7 +38,7 @@ class FirepowerViewFull(StatsView): return "Firepower" def getTextExtentW(self, text): - width, height = self.parent.GetTextExtent( text ) + width, height = self.parent.GetTextExtent(text) return width def populatePanel(self, contentPanel, headerPanel): @@ -84,7 +87,7 @@ class FirepowerViewFull(StatsView): baseBox.Add(BitmapLoader.getStaticBitmap("volley_big", parent, "gui"), 0, wx.ALIGN_CENTER) - gridS = wx.GridSizer(2,2,0,0) + gridS = wx.GridSizer(2, 2, 0, 0) baseBox.Add(gridS, 0) @@ -114,7 +117,7 @@ class FirepowerViewFull(StatsView): def switchToMiningYieldView(self, event): # Getting the active fit mainFrame = gui.mainFrame.MainFrame.getInstance() - sFit = service.Fit.getInstance() + sFit = Fit.getInstance() fit = sFit.getFit(mainFrame.getActiveFit()) # Remove ourselves from statsPane's view list self.parent.views.remove(self) @@ -139,20 +142,20 @@ class FirepowerViewFull(StatsView): view.refreshPanel(fit) def refreshPanel(self, fit): - #If we did anything intresting, we'd update our labels to reflect the new fit's stats here + # If we did anything intresting, we'd update our labels to reflect the new fit's stats here if fit is not None and fit.targetResists is not None: self.stEff.Show() else: self.stEff.Hide() - stats = (("labelFullDpsWeapon", lambda: fit.weaponDPS, 3, 0, 0, "%s DPS",None), + stats = (("labelFullDpsWeapon", lambda: fit.weaponDPS, 3, 0, 0, "%s DPS", None), ("labelFullDpsDrone", lambda: fit.droneDPS, 3, 0, 0, "%s DPS", None), ("labelFullVolleyTotal", lambda: fit.totalVolley, 3, 0, 0, "%s", "Volley: %.1f"), ("labelFullDpsTotal", lambda: fit.totalDPS, 3, 0, 0, "%s", None)) # See GH issue # - #if fit is not None and fit.totalYield > 0: + # if fit is not None and fit.totalYield > 0: # self.miningyield.Show() - #else: + # else: # self.miningyield.Hide() counter = 0 @@ -166,9 +169,10 @@ class FirepowerViewFull(StatsView): tipStr = valueFormat % valueStr if altFormat is None else altFormat % value label.SetToolTip(wx.ToolTip(tipStr)) self._cachedValues[counter] = value - counter +=1 + counter += 1 self.panel.Layout() self.headerPanel.Layout() + FirepowerViewFull.register() diff --git a/gui/builtinStatsViews/miningyieldViewFull.py b/gui/builtinStatsViews/miningyieldViewFull.py index 65ce9ae96..be69be316 100644 --- a/gui/builtinStatsViews/miningyieldViewFull.py +++ b/gui/builtinStatsViews/miningyieldViewFull.py @@ -1,4 +1,4 @@ -#=============================================================================== +# ============================================================================= # Copyright (C) 2014 Alexandros Kosiaris # # This file is part of pyfa. @@ -15,26 +15,30 @@ # # You should have received a copy of the GNU General Public License # along with pyfa. If not, see . -#=============================================================================== +# ============================================================================= +# noinspection PyPackageRequirements import wx -import service import gui.mainFrame from gui.statsView import StatsView from gui.bitmapLoader import BitmapLoader from gui.utils.numberFormatter import formatAmount +from service.fit import Fit + class MiningYieldViewFull(StatsView): name = "miningyieldViewFull" + def __init__(self, parent): StatsView.__init__(self) self.parent = parent self._cachedValues = [] + def getHeaderText(self, fit): return "Mining Yield" def getTextExtentW(self, text): - width, height = self.parent.GetTextExtent( text ) + width, height = self.parent.GetTextExtent(text) return width def populatePanel(self, contentPanel, headerPanel): @@ -47,11 +51,11 @@ class MiningYieldViewFull(StatsView): sizerMiningYield = wx.FlexGridSizer(1, 4) sizerMiningYield.AddGrowableCol(1) - contentSizer.Add( sizerMiningYield, 0, wx.EXPAND, 0) + contentSizer.Add(sizerMiningYield, 0, wx.EXPAND, 0) counter = 0 - for miningType, image in (("miner", "mining") , ("drone", "drones")): + for miningType, image in (("miner", "mining"), ("drone", "drones")): baseBox = wx.BoxSizer(wx.HORIZONTAL) sizerMiningYield.Add(baseBox, 1, wx.ALIGN_LEFT if counter == 0 else wx.ALIGN_CENTER_HORIZONTAL) @@ -66,7 +70,7 @@ class MiningYieldViewFull(StatsView): box.Add(hbox, 1, wx.ALIGN_CENTER) lbl = wx.StaticText(parent, wx.ID_ANY, u"0.0 m\u00B3/s") - setattr(self, "label%sminingyield%s" % (panel.capitalize() ,miningType.capitalize()), lbl) + setattr(self, "label%sminingyield%s" % (panel.capitalize(), miningType.capitalize()), lbl) hbox.Add(lbl, 0, wx.ALIGN_CENTER) self._cachedValues.append(0) @@ -103,7 +107,7 @@ class MiningYieldViewFull(StatsView): def switchToFirepowerView(self, event): # Getting the active fit mainFrame = gui.mainFrame.MainFrame.getInstance() - sFit = service.Fit.getInstance() + sFit = Fit.getInstance() fit = sFit.getFit(mainFrame.getActiveFit()) # Remove ourselves from statsPane's view list self.parent.views.remove(self) @@ -122,11 +126,11 @@ class MiningYieldViewFull(StatsView): view.refreshPanel(fit) def refreshPanel(self, fit): - #If we did anything intresting, we'd update our labels to reflect the new fit's stats here + # If we did anything intresting, we'd update our labels to reflect the new fit's stats here - stats = (("labelFullminingyieldMiner", lambda: fit.minerYield * 3600, 3, 0, 0, u"%s m\u00B3/h",None), - ("labelFullminingyieldDrone", lambda: fit.droneYield * 3600, 3, 0, 0, u"%s m\u00B3/h", None), - ("labelFullminingyieldTotal", lambda: fit.totalYield * 3600, 3, 0, 0, u"%s m\u00B3/h", None)) + stats = (("labelFullminingyieldMiner", lambda: fit.minerYield, 3, 0, 0, u"%s m\u00B3/s", None), + ("labelFullminingyieldDrone", lambda: fit.droneYield, 3, 0, 0, u"%s m\u00B3/s", None), + ("labelFullminingyieldTotal", lambda: fit.totalYield, 3, 0, 0, u"%s m\u00B3/s", None)) counter = 0 for labelName, value, prec, lowest, highest, valueFormat, altFormat in stats: @@ -136,11 +140,12 @@ class MiningYieldViewFull(StatsView): if self._cachedValues[counter] != value: valueStr = formatAmount(value, prec, lowest, highest) label.SetLabel(valueFormat % valueStr) - tipStr = valueFormat % valueStr if altFormat is None else altFormat % value + tipStr = "Mining Yield per second ({0} per hour)".format(formatAmount(value * 3600, 3, 0, 3)) label.SetToolTip(wx.ToolTip(tipStr)) self._cachedValues[counter] = value - counter +=1 + counter += 1 self.panel.Layout() self.headerPanel.Layout() + MiningYieldViewFull.register() diff --git a/gui/builtinStatsViews/priceViewFull.py b/gui/builtinStatsViews/priceViewFull.py index 16935a136..0fbd24ff0 100644 --- a/gui/builtinStatsViews/priceViewFull.py +++ b/gui/builtinStatsViews/priceViewFull.py @@ -1,4 +1,4 @@ -#=============================================================================== +# ============================================================================= # Copyright (C) 2010 Diego Duclos # # This file is part of pyfa. @@ -15,17 +15,20 @@ # # You should have received a copy of the GNU General Public License # along with pyfa. If not, see . -#=============================================================================== +# ============================================================================= +# noinspection PyPackageRequirements import wx from gui.statsView import StatsView -from gui import builtinStatsViews from gui.bitmapLoader import BitmapLoader from gui.utils.numberFormatter import formatAmount -import service +from service.market import Market +from service.price import Price + class PriceViewFull(StatsView): name = "priceViewFull" + def __init__(self, parent): StatsView.__init__(self) self.parent = parent @@ -72,27 +75,10 @@ class PriceViewFull(StatsView): def refreshPanel(self, fit): if fit is not None: self.fit = fit - # Compose a list of all the data we need & request it - typeIDs = [] - typeIDs.append(fit.ship.item.ID) - for mod in fit.modules: - if not mod.isEmpty: - typeIDs.append(mod.itemID) + typeIDs = Price.fitItemsList(fit) - for drone in fit.drones: - for _ in xrange(drone.amount): - typeIDs.append(drone.itemID) - - for fighter in fit.fighters: - for _ in xrange(fighter.amountActive): - typeIDs.append(fighter.itemID) - - for cargo in fit.cargo: - for _ in xrange(cargo.amount): - typeIDs.append(cargo.itemID) - - sMkt = service.Market.getInstance() + sMkt = Market.getInstance() sMkt.getPrices(typeIDs, self.processPrices) self.labelEMStatus.SetLabel("Updating prices...") else: @@ -117,10 +103,11 @@ class PriceViewFull(StatsView): self.labelPriceFittings.SetLabel("%s ISK" % formatAmount(modPrice, 3, 3, 9, currency=True)) self.labelPriceFittings.SetToolTip(wx.ToolTip('{:,.2f}'.format(modPrice))) self._cachedFittings = modPrice - if self._cachedTotal != (shipPrice+modPrice): + if self._cachedTotal != (shipPrice + modPrice): self.labelPriceTotal.SetLabel("%s ISK" % formatAmount(shipPrice + modPrice, 3, 3, 9, currency=True)) self.labelPriceTotal.SetToolTip(wx.ToolTip('{:,.2f}'.format(shipPrice + modPrice))) self._cachedTotal = shipPrice + modPrice self.panel.Layout() + PriceViewFull.register() diff --git a/gui/builtinStatsViews/rechargeViewFull.py b/gui/builtinStatsViews/rechargeViewFull.py index c73aaf0b4..21399602e 100644 --- a/gui/builtinStatsViews/rechargeViewFull.py +++ b/gui/builtinStatsViews/rechargeViewFull.py @@ -1,4 +1,4 @@ -#=============================================================================== +# ============================================================================= # Copyright (C) 2010 Diego Duclos # # This file is part of pyfa. @@ -15,35 +15,38 @@ # # You should have received a copy of the GNU General Public License # along with pyfa. If not, see . -#=============================================================================== +# ============================================================================= +# noinspection PyPackageRequirements import wx from gui.statsView import StatsView from gui.bitmapLoader import BitmapLoader from gui.utils.numberFormatter import formatAmount import gui.mainFrame -import gui.builtinStatsViews.resistancesViewFull as rvf -import service +from gui.builtinStatsViews.resistancesViewFull import EFFECTIVE_HP_TOGGLED +from service.fit import Fit + class RechargeViewFull(StatsView): name = "rechargeViewFull" + def __init__(self, parent): StatsView.__init__(self) self.parent = parent self.mainFrame = gui.mainFrame.MainFrame.getInstance() - self.mainFrame.Bind(rvf.EFFECTIVE_HP_TOGGLED, self.toggleEffective) + self.mainFrame.Bind(EFFECTIVE_HP_TOGGLED, self.toggleEffective) self.effective = True def getHeaderText(self, fit): return "Recharge rates" def getTextExtentW(self, text): - width, height = self.parent.GetTextExtent( text ) + width, height = self.parent.GetTextExtent(text) return width def toggleEffective(self, event): self.effective = event.effective - sFit = service.Fit.getInstance() + sFit = Fit.getInstance() self.refreshPanel(sFit.getFit(self.mainFrame.getActiveFit())) event.Skip() @@ -53,21 +56,22 @@ class RechargeViewFull(StatsView): self.panel = contentPanel self.headerPanel = headerPanel sizerTankStats = wx.FlexGridSizer(3, 5) - for i in xrange(4): + for i in range(4): sizerTankStats.AddGrowableCol(i + 1) contentSizer.Add(sizerTankStats, 0, wx.EXPAND, 0) - #Add an empty label first for correct alignment. + # Add an empty label first for correct alignment. sizerTankStats.Add(wx.StaticText(contentPanel, wx.ID_ANY, ""), 0) - toolTipText = {"shieldPassive" : "Passive shield recharge", "shieldActive" : "Active shield boost", "armorActive" : "Armor repair amount", "hullActive" : "Hull repair amount"} + toolTipText = {"shieldPassive": "Passive shield recharge", "shieldActive": "Active shield boost", + "armorActive": "Armor repair amount", "hullActive": "Hull repair amount"} for tankType in ("shieldPassive", "shieldActive", "armorActive", "hullActive"): bitmap = BitmapLoader.getStaticBitmap("%s_big" % tankType, contentPanel, "gui") tooltip = wx.ToolTip(toolTipText[tankType]) bitmap.SetToolTip(tooltip) sizerTankStats.Add(bitmap, 0, wx.ALIGN_CENTER) - toolTipText = {"reinforced" : "Reinforced", "sustained" : "Sustained"} + toolTipText = {"reinforced": "Reinforced", "sustained": "Sustained"} for stability in ("reinforced", "sustained"): bitmap = BitmapLoader.getStaticBitmap("regen%s_big" % stability.capitalize(), contentPanel, "gui") tooltip = wx.ToolTip(toolTipText[stability]) @@ -79,7 +83,7 @@ class RechargeViewFull(StatsView): continue tankTypeCap = tankType[0].capitalize() + tankType[1:] - lbl = wx.StaticText(contentPanel, wx.ID_ANY, "0.0", style = wx.ALIGN_RIGHT) + lbl = wx.StaticText(contentPanel, wx.ID_ANY, "0.0", style=wx.ALIGN_RIGHT) setattr(self, "labelTank%s%s" % (stability.capitalize(), tankTypeCap), lbl) box = wx.BoxSizer(wx.HORIZONTAL) @@ -91,12 +95,12 @@ class RechargeViewFull(StatsView): contentPanel.Layout() def refreshPanel(self, fit): - #If we did anything intresting, we'd update our labels to reflect the new fit's stats here + # If we did anything intresting, we'd update our labels to reflect the new fit's stats here for stability in ("reinforced", "sustained"): - if stability == "reinforced" and fit != None: + if stability == "reinforced" and fit is not None: tank = fit.effectiveTank if self.effective else fit.tank - elif stability == "sustained" and fit != None: + elif stability == "sustained" and fit is not None: tank = fit.effectiveSustainableTank if self.effective else fit.sustainableTank else: tank = None @@ -122,4 +126,5 @@ class RechargeViewFull(StatsView): self.panel.Layout() self.headerPanel.Layout() + RechargeViewFull.register() diff --git a/gui/builtinStatsViews/resistancesViewFull.py b/gui/builtinStatsViews/resistancesViewFull.py index 55daa813d..9946310ec 100644 --- a/gui/builtinStatsViews/resistancesViewFull.py +++ b/gui/builtinStatsViews/resistancesViewFull.py @@ -1,4 +1,4 @@ -#=============================================================================== +# ============================================================================= # Copyright (C) 2010 Diego Duclos # # This file is part of pyfa. @@ -15,23 +15,23 @@ # # You should have received a copy of the GNU General Public License # along with pyfa. If not, see . -#=============================================================================== +# ============================================================================= +# noinspection PyPackageRequirements import wx from gui.statsView import StatsView -from gui import builtinStatsViews from gui.bitmapLoader import BitmapLoader -from gui import pygauge as PG +from gui.pygauge import PyGauge from gui.utils.numberFormatter import formatAmount -import service import gui.mainFrame -import gui.builtinViews.fittingView as fv import gui.globalEvents as GE EffectiveHpToggled, EFFECTIVE_HP_TOGGLED = wx.lib.newevent.NewEvent() + class ResistancesViewFull(StatsView): name = "resistancesViewFull" + def __init__(self, parent): StatsView.__init__(self) self.parent = parent @@ -45,7 +45,7 @@ class ResistancesViewFull(StatsView): return "Resistances" def getTextExtentW(self, text): - width, height = self.parent.GetTextExtent( text ) + width, height = self.parent.GetTextExtent(text) return width def populatePanel(self, contentPanel, headerPanel): @@ -56,7 +56,7 @@ class ResistancesViewFull(StatsView): # Custom header EHP headerContentSizer = wx.BoxSizer(wx.HORIZONTAL) hsizer = headerPanel.GetSizer() - hsizer.Add(headerContentSizer,0,0,0) + hsizer.Add(headerContentSizer, 0, 0, 0) self.stEff = wx.StaticText(headerPanel, wx.ID_ANY, "( Effective HP: ") headerContentSizer.Add(self.stEff) headerPanel.GetParent().AddToggleItem(self.stEff) @@ -67,81 +67,83 @@ class ResistancesViewFull(StatsView): stCls = wx.StaticText(headerPanel, wx.ID_ANY, " )") - headerPanel.GetParent().AddToggleItem( stCls ) - headerContentSizer.Add( stCls ) -# headerContentSizer.Add(wx.StaticLine(headerPanel, wx.ID_ANY), 1, wx.ALIGN_CENTER) + headerPanel.GetParent().AddToggleItem(stCls) + headerContentSizer.Add(stCls) + # headerContentSizer.Add(wx.StaticLine(headerPanel, wx.ID_ANY), 1, wx.ALIGN_CENTER) # Display table col = 0 row = 0 sizerResistances = wx.GridBagSizer() - contentSizer.Add( sizerResistances, 0, wx.EXPAND , 0) + contentSizer.Add(sizerResistances, 0, wx.EXPAND, 0) # Add an empty label, then the rest. - sizerResistances.Add(wx.StaticText(contentPanel, wx.ID_ANY), wx.GBPosition( row, col ), wx.GBSpan( 1, 1 )) - col+=1 - toolTipText = {"em" : "Electromagnetic resistance", "thermal" : "Thermal resistance", "kinetic" : "Kinetic resistance", "explosive" : "Explosive resistance"} + sizerResistances.Add(wx.StaticText(contentPanel, wx.ID_ANY), wx.GBPosition(row, col), wx.GBSpan(1, 1)) + col += 1 + toolTipText = {"em": "Electromagnetic resistance", "thermal": "Thermal resistance", + "kinetic": "Kinetic resistance", "explosive": "Explosive resistance"} for damageType in ("em", "thermal", "kinetic", "explosive"): bitmap = BitmapLoader.getStaticBitmap("%s_big" % damageType, contentPanel, "gui") tooltip = wx.ToolTip(toolTipText[damageType]) bitmap.SetToolTip(tooltip) - sizerResistances.Add(bitmap, wx.GBPosition( row, col ), wx.GBSpan( 1, 1 ), wx.ALIGN_CENTER) - col+=1 - self.stEHPs = wx.Button(contentPanel, style = wx.BU_EXACTFIT, label = "EHP") + sizerResistances.Add(bitmap, wx.GBPosition(row, col), wx.GBSpan(1, 1), wx.ALIGN_CENTER) + col += 1 + self.stEHPs = wx.Button(contentPanel, style=wx.BU_EXACTFIT, label="EHP") self.stEHPs.SetToolTip(wx.ToolTip("Click to toggle between effective HP and raw HP")) self.stEHPs.Bind(wx.EVT_BUTTON, self.toggleEHP) for i in xrange(4): - sizerResistances.AddGrowableCol(i+1) + sizerResistances.AddGrowableCol(i + 1) - sizerResistances.Add(self.stEHPs, wx.GBPosition( row, col ), wx.GBSpan( 1, 1 ), wx.ALIGN_CENTER) - col=0 - row+=1 + sizerResistances.Add(self.stEHPs, wx.GBPosition(row, col), wx.GBSpan(1, 1), wx.ALIGN_CENTER) + col = 0 + row += 1 - gaugeColours=( ((38,133,198),(52,86,98)), ((198,38,38),(83,65,67)), ((163,163,163),(74,90,93)), ((198,133,38),(81,83,67)) ) + gaugeColours = (((38, 133, 198), (52, 86, 98)), ((198, 38, 38), (83, 65, 67)), ((163, 163, 163), (74, 90, 93)), + ((198, 133, 38), (81, 83, 67))) - toolTipText = {"shield" : "Shield resistance", "armor" : "Armor resistance", "hull" : "Hull resistance", "damagePattern" : "Incoming damage pattern"} + toolTipText = {"shield": "Shield resistance", "armor": "Armor resistance", "hull": "Hull resistance", + "damagePattern": "Incoming damage pattern"} for tankType in ("shield", "armor", "hull", "separator", "damagePattern"): if tankType != "separator": bitmap = BitmapLoader.getStaticBitmap("%s_big" % tankType, contentPanel, "gui") tooltip = wx.ToolTip(toolTipText[tankType]) bitmap.SetToolTip(tooltip) - sizerResistances.Add(bitmap, wx.GBPosition( row, col ), wx.GBSpan( 1, 1 ), wx.ALIGN_CENTER) - col+=1 + sizerResistances.Add(bitmap, wx.GBPosition(row, col), wx.GBSpan(1, 1), wx.ALIGN_CENTER) + col += 1 else: - sizerResistances.Add(wx.StaticLine(contentPanel, wx.ID_ANY), wx.GBPosition( row, col ), wx.GBSpan( 1, 6 ), wx.EXPAND|wx.ALIGN_CENTER) - row+=1 - col=0 + sizerResistances.Add(wx.StaticLine(contentPanel, wx.ID_ANY), wx.GBPosition(row, col), wx.GBSpan(1, 6), + wx.EXPAND | wx.ALIGN_CENTER) + row += 1 + col = 0 continue - currGColour=0 + currGColour = 0 for damageType in ("em", "thermal", "kinetic", "explosive"): - box = wx.BoxSizer(wx.HORIZONTAL) - sizerResistances.Add(box, wx.GBPosition( row, col ), wx.GBSpan( 1, 1 ), wx.ALIGN_CENTER) + sizerResistances.Add(box, wx.GBPosition(row, col), wx.GBSpan(1, 1), wx.ALIGN_CENTER) + # Fancy gauges addon - #Fancy gauges addon - - pgColour= gaugeColours[currGColour] + pgColour = gaugeColours[currGColour] fc = pgColour[0] bc = pgColour[1] - currGColour+=1 + currGColour += 1 - lbl = PG.PyGauge(contentPanel, wx.ID_ANY, 100) + lbl = PyGauge(contentPanel, wx.ID_ANY, 100) lbl.SetMinSize((48, 16)) - lbl.SetBackgroundColour(wx.Colour(bc[0],bc[1],bc[2])) - lbl.SetBarColour(wx.Colour(fc[0],fc[1],fc[2])) + lbl.SetBackgroundColour(wx.Colour(bc[0], bc[1], bc[2])) + lbl.SetBarColour(wx.Colour(fc[0], fc[1], fc[2])) lbl.SetBarGradient() lbl.SetFractionDigits(1) setattr(self, "gaugeResistance%s%s" % (tankType.capitalize(), damageType.capitalize()), lbl) box.Add(lbl, 0, wx.ALIGN_CENTER) - col+=1 + col += 1 box = wx.BoxSizer(wx.VERTICAL) box.SetMinSize(wx.Size(self.getTextExtentW("WWWWk"), -1)) @@ -149,9 +151,9 @@ class ResistancesViewFull(StatsView): box.Add(lbl, 0, wx.ALIGN_CENTER) setattr(self, "labelResistance%sEhp" % tankType.capitalize(), lbl) - sizerResistances.Add(box, wx.GBPosition( row, col ), wx.GBSpan( 1, 1 ), wx.ALIGN_CENTER) - row+=1 - col=0 + sizerResistances.Add(box, wx.GBPosition(row, col), wx.GBSpan(1, 1), wx.ALIGN_CENTER) + row += 1 + col = 0 self.stEHPs.SetToolTip(wx.ToolTip("Click to toggle between effective HP and raw HP")) @@ -163,7 +165,7 @@ class ResistancesViewFull(StatsView): wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.mainFrame.getActiveFit())) def refreshPanel(self, fit): - #If we did anything intresting, we'd update our labels to reflect the new fit's stats here + # If we did anything intresting, we'd update our labels to reflect the new fit's stats here if fit is None and not self.showEffective: self.showEffective = True wx.PostEvent(self.mainFrame, EffectiveHpToggled(effective=True)) @@ -194,11 +196,11 @@ class ResistancesViewFull(StatsView): total += ehp[tankType] rrFactor = fit.ehp[tankType] / fit.hp[tankType] lbl.SetLabel(formatAmount(ehp[tankType], 3, 0, 9)) - lbl.SetToolTip(wx.ToolTip("%s: %d\nResist Multiplier: x%.2f" % (tankType.capitalize(), ehp[tankType], rrFactor))) + lbl.SetToolTip( + wx.ToolTip("%s: %d\nResist Multiplier: x%.2f" % (tankType.capitalize(), ehp[tankType], rrFactor))) else: lbl.SetLabel("0") - self.labelEhp.SetLabel("%s" % formatAmount(total, 3, 0, 9)) if self.showEffective: self.stEff.SetLabel("( Effective HP: ") @@ -207,10 +209,9 @@ class ResistancesViewFull(StatsView): self.stEff.SetLabel("( Raw HP: ") self.labelEhp.SetToolTip(wx.ToolTip("Raw: %d HP" % total)) - - damagePattern = fit.damagePattern if fit is not None and self.showEffective else None + damagePattern = fit.damagePattern if fit is not None and self.showEffective else None total = sum((damagePattern.emAmount, damagePattern.thermalAmount, - damagePattern.kineticAmount, damagePattern.explosiveAmount)) if damagePattern is not None else 0 + damagePattern.kineticAmount, damagePattern.explosiveAmount)) if damagePattern is not None else 0 for damageType in ("em", "thermal", "kinetic", "explosive"): lbl = getattr(self, "gaugeResistanceDamagepattern%s" % damageType.capitalize()) @@ -223,5 +224,5 @@ class ResistancesViewFull(StatsView): self.panel.Layout() self.headerPanel.Layout() -ResistancesViewFull.register() +ResistancesViewFull.register() diff --git a/gui/builtinStatsViews/resourcesViewFull.py b/gui/builtinStatsViews/resourcesViewFull.py index e0ac0da52..3171aaa4b 100644 --- a/gui/builtinStatsViews/resourcesViewFull.py +++ b/gui/builtinStatsViews/resourcesViewFull.py @@ -1,4 +1,4 @@ -#=============================================================================== +# ============================================================================= # Copyright (C) 2010 Diego Duclos # # This file is part of pyfa. @@ -15,20 +15,21 @@ # # You should have received a copy of the GNU General Public License # along with pyfa. If not, see . -#=============================================================================== +# ============================================================================= +# noinspection PyPackageRequirements import wx from gui.statsView import StatsView -from gui import builtinStatsViews from gui.bitmapLoader import BitmapLoader -from gui import pygauge as PG +from gui.pygauge import PyGauge import gui.mainFrame -import gui.chromeTabs +from gui.chromeTabs import EVT_NOTEBOOK_PAGE_CHANGED -from eos.types import Hardpoint +from eos.saveddata.module import Hardpoint from gui.utils.numberFormatter import formatAmount + class ResourcesViewFull(StatsView): name = "resourcesViewFull" contexts = ["drone", "fighter", "cargo"] @@ -37,7 +38,7 @@ class ResourcesViewFull(StatsView): StatsView.__init__(self) self.parent = parent self.mainFrame = gui.mainFrame.MainFrame.getInstance() - self.mainFrame.additionsPane.notebook.Bind(gui.chromeTabs.EVT_NOTEBOOK_PAGE_CHANGED, self.pageChanged) + self.mainFrame.additionsPane.notebook.Bind(EVT_NOTEBOOK_PAGE_CHANGED, self.pageChanged) def pageChanged(self, event): page = self.mainFrame.additionsPane.getName(event.GetSelection()) @@ -79,7 +80,7 @@ class ResourcesViewFull(StatsView): return "Resources" def getTextExtentW(self, text): - width, height = self.parent.GetTextExtent( text ) + width, height = self.parent.GetTextExtent(text) return width def populatePanel(self, contentPanel, headerPanel): @@ -99,54 +100,56 @@ class ResourcesViewFull(StatsView): self.headerPanel = headerPanel panel = "full" - base = sizerResources sizer.AddSpacer((0, 0), 1, wx.EXPAND, 5) - #Turrets & launcher hardslots display - tooltipText = {"turret":"Turret hardpoints", "launcher":"Launcher hardpoints", "drones":"Drones active", "fighter": "Fighter squadrons active", "calibration":"Calibration"} - for type in ("turret", "launcher", "drones", "fighter", "calibration"): + # Turrets & launcher hardslots display + tooltipText = {"turret": "Turret hardpoints", "launcher": "Launcher hardpoints", "drones": "Drones active", + "fighter": "Fighter squadrons active", "calibration": "Calibration"} + for type_ in ("turret", "launcher", "drones", "fighter", "calibration"): box = wx.BoxSizer(wx.HORIZONTAL) - bitmap = BitmapLoader.getStaticBitmap("%s_big" % type, parent, "gui") - tooltip = wx.ToolTip(tooltipText[type]) + bitmap = BitmapLoader.getStaticBitmap("%s_big" % type_, parent, "gui") + tooltip = wx.ToolTip(tooltipText[type_]) bitmap.SetToolTip(tooltip) box.Add(bitmap, 0, wx.ALIGN_CENTER) sizer.Add(box, 0, wx.ALIGN_CENTER) - suffix = {'turret':'Hardpoints', 'launcher':'Hardpoints', 'drones':'Active', 'fighter':'Tubes', 'calibration':'Points'} + suffix = {'turret': 'Hardpoints', 'launcher': 'Hardpoints', 'drones': 'Active', 'fighter': 'Tubes', + 'calibration': 'Points'} lbl = wx.StaticText(parent, wx.ID_ANY, "0") - setattr(self, "label%sUsed%s%s" % (panel.capitalize(), type.capitalize(), suffix[type].capitalize()), lbl) + setattr(self, "label%sUsed%s%s" % (panel.capitalize(), type_.capitalize(), suffix[type_].capitalize()), lbl) box.Add(lbl, 0, wx.ALIGN_CENTER | wx.LEFT, 5) box.Add(wx.StaticText(parent, wx.ID_ANY, "/"), 0, wx.ALIGN_CENTER) lbl = wx.StaticText(parent, wx.ID_ANY, "0") - setattr(self, "label%sTotal%s%s" % (panel.capitalize(), type.capitalize(), suffix[type].capitalize()), lbl) + setattr(self, "label%sTotal%s%s" % (panel.capitalize(), type_.capitalize(), suffix[type_].capitalize()), + lbl) box.Add(lbl, 0, wx.ALIGN_CENTER) - setattr(self, "boxSizer{}".format(type.capitalize()), box) + setattr(self, "boxSizer{}".format(type_.capitalize()), box) # Hack - We add a spacer after each thing, but we are always hiding something. The spacer is stil there. # This way, we only have one space after the drones/fighters - if type != "drones": + if type_ != "drones": sizer.AddSpacer((0, 0), 1, wx.EXPAND, 5) - - #PG, Cpu & drone stuff - tooltipText = {"cpu":"CPU", "pg":"PowerGrid", "droneBay":"Drone bay", "fighterBay": "Fighter bay", "droneBandwidth":"Drone bandwidth", "cargoBay":"Cargo bay"} + # PG, Cpu & drone stuff + tooltipText = {"cpu": "CPU", "pg": "PowerGrid", "droneBay": "Drone bay", "fighterBay": "Fighter bay", + "droneBandwidth": "Drone bandwidth", "cargoBay": "Cargo bay"} for i, group in enumerate((("cpu", "pg"), ("cargoBay", "droneBay", "fighterBay", "droneBandwidth"))): main = wx.BoxSizer(wx.VERTICAL) - base.Add(main, 1 , wx.ALIGN_CENTER) + base.Add(main, 1, wx.ALIGN_CENTER) - for type in group: - capitalizedType = type[0].capitalize() + type[1:] - bitmap = BitmapLoader.getStaticBitmap(type + "_big", parent, "gui") - tooltip = wx.ToolTip(tooltipText[type]) + for type_ in group: + capitalizedType = type_[0].capitalize() + type_[1:] + bitmap = BitmapLoader.getStaticBitmap(type_ + "_big", parent, "gui") + tooltip = wx.ToolTip(tooltipText[type_]) bitmap.SetToolTip(tooltip) stats = wx.BoxSizer(wx.VERTICAL) - absolute = wx.BoxSizer(wx.HORIZONTAL) + absolute = wx.BoxSizer(wx.HORIZONTAL) stats.Add(absolute, 0, wx.EXPAND) b = wx.BoxSizer(wx.HORIZONTAL) @@ -166,18 +169,19 @@ class ResourcesViewFull(StatsView): setattr(self, "label%sTotal%s" % (panel.capitalize(), capitalizedType), lbl) absolute.Add(lbl, 0, wx.ALIGN_LEFT) - units = {"cpu":" tf", "pg":" MW", "droneBandwidth":" mbit/s", "droneBay":u" m\u00B3", "fighterBay":u" m\u00B3", "cargoBay":u" m\u00B3"} - lbl = wx.StaticText(parent, wx.ID_ANY, "%s" % units[type]) + units = {"cpu": " tf", "pg": " MW", "droneBandwidth": " mbit/s", "droneBay": u" m\u00B3", + "fighterBay": u" m\u00B3", "cargoBay": u" m\u00B3"} + lbl = wx.StaticText(parent, wx.ID_ANY, "%s" % units[type_]) absolute.Add(lbl, 0, wx.ALIGN_LEFT) # Gauges modif. - Darriele - gauge = PG.PyGauge(parent, wx.ID_ANY, 1) + gauge = PyGauge(parent, wx.ID_ANY, 1) gauge.SetValueRange(0, 0) gauge.SetMinSize((self.getTextExtentW("1.999M/1.99M MW"), 23)) gauge.SetFractionDigits(2) - setattr(self, "gauge%s%s" % (panel.capitalize(),capitalizedType), gauge) + setattr(self, "gauge%s%s" % (panel.capitalize(), capitalizedType), gauge) stats.Add(gauge, 0, wx.ALIGN_CENTER) setattr(self, "base%s%s" % (panel.capitalize(), capitalizedType), b) @@ -186,77 +190,88 @@ class ResourcesViewFull(StatsView): self.toggleContext("drone") def refreshPanel(self, fit): - #If we did anything intresting, we'd update our labels to reflect the new fit's stats here + # If we did anything intresting, we'd update our labels to reflect the new fit's stats here - stats = (("label%sUsedTurretHardpoints", lambda: fit.getHardpointsUsed(Hardpoint.TURRET), 0, 0, 0), - ("label%sTotalTurretHardpoints", lambda: fit.ship.getModifiedItemAttr('turretSlotsLeft'), 0, 0, 0), - ("label%sUsedLauncherHardpoints", lambda: fit.getHardpointsUsed(Hardpoint.MISSILE), 0, 0, 0), - ("label%sTotalLauncherHardpoints", lambda: fit.ship.getModifiedItemAttr('launcherSlotsLeft'), 0, 0, 0), - ("label%sUsedDronesActive", lambda: fit.activeDrones, 0, 0, 0), - ("label%sTotalDronesActive", lambda: fit.extraAttributes["maxActiveDrones"], 0, 0, 0), - ("label%sUsedFighterTubes", lambda: fit.fighterTubesUsed, 3, 0, 9), - ("label%sTotalFighterTubes", lambda: fit.ship.getModifiedItemAttr("fighterTubes"), 3, 0, 9), - ("label%sUsedCalibrationPoints", lambda: fit.calibrationUsed, 0, 0, 0), - ("label%sTotalCalibrationPoints", lambda: fit.ship.getModifiedItemAttr('upgradeCapacity'), 0, 0, 0), - ("label%sUsedPg", lambda: fit.pgUsed, 4, 0, 9), - ("label%sUsedCpu", lambda: fit.cpuUsed, 4, 0, 9), - ("label%sTotalPg", lambda: fit.ship.getModifiedItemAttr("powerOutput"), 4, 0, 9), - ("label%sTotalCpu", lambda: fit.ship.getModifiedItemAttr("cpuOutput"), 4, 0, 9), - ("label%sUsedDroneBay", lambda: fit.droneBayUsed, 3, 0, 9), - ("label%sUsedFighterBay", lambda: fit.fighterBayUsed, 3, 0, 9), - ("label%sUsedDroneBandwidth", lambda: fit.droneBandwidthUsed, 3, 0, 9), - ("label%sTotalDroneBay", lambda: fit.ship.getModifiedItemAttr("droneCapacity"), 3, 0, 9), - ("label%sTotalDroneBandwidth", lambda: fit.ship.getModifiedItemAttr("droneBandwidth"), 3, 0, 9), - ("label%sTotalFighterBay", lambda: fit.ship.getModifiedItemAttr("fighterCapacity"), 3, 0, 9), - ("label%sUsedCargoBay", lambda: fit.cargoBayUsed, 3, 0, 9), - ("label%sTotalCargoBay", lambda: fit.ship.getModifiedItemAttr("capacity"), 3, 0, 9)) + stats = ( + ("label%sUsedTurretHardpoints", lambda: fit.getHardpointsUsed(Hardpoint.TURRET), 0, 0, 0), + ("label%sTotalTurretHardpoints", lambda: fit.ship.getModifiedItemAttr('turretSlotsLeft'), 0, 0, 0), + ("label%sUsedLauncherHardpoints", lambda: fit.getHardpointsUsed(Hardpoint.MISSILE), 0, 0, 0), + ("label%sTotalLauncherHardpoints", lambda: fit.ship.getModifiedItemAttr('launcherSlotsLeft'), 0, 0, 0), + ("label%sUsedDronesActive", lambda: fit.activeDrones, 0, 0, 0), + ("label%sTotalDronesActive", lambda: fit.extraAttributes["maxActiveDrones"], 0, 0, 0), + ("label%sUsedFighterTubes", lambda: fit.fighterTubesUsed, 3, 0, 9), + ("label%sTotalFighterTubes", lambda: fit.ship.getModifiedItemAttr("fighterTubes"), 3, 0, 9), + ("label%sUsedCalibrationPoints", lambda: fit.calibrationUsed, 0, 0, 0), + ("label%sTotalCalibrationPoints", lambda: fit.ship.getModifiedItemAttr('upgradeCapacity'), 0, 0, 0), + ("label%sUsedPg", lambda: fit.pgUsed, 4, 0, 9), + ("label%sUsedCpu", lambda: fit.cpuUsed, 4, 0, 9), + ("label%sTotalPg", lambda: fit.ship.getModifiedItemAttr("powerOutput"), 4, 0, 9), + ("label%sTotalCpu", lambda: fit.ship.getModifiedItemAttr("cpuOutput"), 4, 0, 9), + ("label%sUsedDroneBay", lambda: fit.droneBayUsed, 3, 0, 9), + ("label%sUsedFighterBay", lambda: fit.fighterBayUsed, 3, 0, 9), + ("label%sUsedDroneBandwidth", lambda: fit.droneBandwidthUsed, 3, 0, 9), + ("label%sTotalDroneBay", lambda: fit.ship.getModifiedItemAttr("droneCapacity"), 3, 0, 9), + ("label%sTotalDroneBandwidth", lambda: fit.ship.getModifiedItemAttr("droneBandwidth"), 3, 0, 9), + ("label%sTotalFighterBay", lambda: fit.ship.getModifiedItemAttr("fighterCapacity"), 3, 0, 9), + ("label%sUsedCargoBay", lambda: fit.cargoBayUsed, 3, 0, 9), + ("label%sTotalCargoBay", lambda: fit.ship.getModifiedItemAttr("capacity"), 3, 0, 9), + ) panel = "Full" + usedTurretHardpoints = 0 + labelUTH = "" totalTurretHardpoints = 0 + labelTTH = "" usedLauncherHardpoints = 0 + labelULH = "" totalLauncherHardPoints = 0 + labelTLH = "" + usedDronesActive = 0 + labelUDA = "" + totalDronesActive = 0 + labelTDA = "" + usedFighterTubes = 0 + labelUFT = "" + totalFighterTubes = 0 + labelTFT = "" + usedCalibrationPoints = 0 + labelUCP = "" + totalCalibrationPoints = 0 + labelTCP = "" for labelName, value, prec, lowest, highest in stats: label = getattr(self, labelName % panel) value = value() if fit is not None else 0 value = value if value is not None else 0 + if labelName % panel == "label%sUsedTurretHardpoints" % panel: usedTurretHardpoints = value labelUTH = label - - if labelName % panel == "label%sTotalTurretHardpoints" % panel: + elif labelName % panel == "label%sTotalTurretHardpoints" % panel: totalTurretHardpoints = value labelTTH = label - - if labelName % panel == "label%sUsedLauncherHardpoints" % panel: + elif labelName % panel == "label%sUsedLauncherHardpoints" % panel: usedLauncherHardpoints = value labelULH = label - - if labelName % panel == "label%sTotalLauncherHardpoints" % panel: + elif labelName % panel == "label%sTotalLauncherHardpoints" % panel: totalLauncherHardPoints = value labelTLH = label - - if labelName % panel == "label%sUsedDronesActive" % panel: + elif labelName % panel == "label%sUsedDronesActive" % panel: usedDronesActive = value labelUDA = label - - if labelName % panel == "label%sTotalDronesActive" % panel: + elif labelName % panel == "label%sTotalDronesActive" % panel: totalDronesActive = value labelTDA = label - - if labelName % panel == "label%sUsedFighterTubes" % panel: + elif labelName % panel == "label%sUsedFighterTubes" % panel: usedFighterTubes = value labelUFT = label - - if labelName % panel == "label%sTotalFighterTubes" % panel: + elif labelName % panel == "label%sTotalFighterTubes" % panel: totalFighterTubes = value labelTFT = label - - if labelName % panel == "label%sUsedCalibrationPoints" % panel: + elif labelName % panel == "label%sUsedCalibrationPoints" % panel: usedCalibrationPoints = value labelUCP = label - - if labelName % panel == "label%sTotalCalibrationPoints" % panel: + elif labelName % panel == "label%sTotalCalibrationPoints" % panel: totalCalibrationPoints = value labelTCP = label @@ -303,12 +318,16 @@ class ResourcesViewFull(StatsView): labelTCP.SetForegroundColour(colorC) if fit is not None: - resMax = (lambda: fit.ship.getModifiedItemAttr("cpuOutput"), - lambda: fit.ship.getModifiedItemAttr("powerOutput"), - lambda: fit.ship.getModifiedItemAttr("droneCapacity"), - lambda: fit.ship.getModifiedItemAttr("fighterCapacity"), - lambda: fit.ship.getModifiedItemAttr("droneBandwidth"), - lambda: fit.ship.getModifiedItemAttr("capacity")) + resMax = ( + lambda: fit.ship.getModifiedItemAttr("cpuOutput"), + lambda: fit.ship.getModifiedItemAttr("powerOutput"), + lambda: fit.ship.getModifiedItemAttr("droneCapacity"), + lambda: fit.ship.getModifiedItemAttr("fighterCapacity"), + lambda: fit.ship.getModifiedItemAttr("droneBandwidth"), + lambda: fit.ship.getModifiedItemAttr("capacity"), + ) + else: + resMax = None i = 0 for resourceType in ("cpu", "pg", "droneBay", "fighterBay", "droneBandwidth", "cargoBay"): @@ -316,11 +335,11 @@ class ResourcesViewFull(StatsView): capitalizedType = resourceType[0].capitalize() + resourceType[1:] gauge = getattr(self, "gauge%s%s" % (panel, capitalizedType)) - resUsed = getattr(fit,"%sUsed" % resourceType) + resUsed = getattr(fit, "%sUsed" % resourceType) gauge.SetValueRange(resUsed or 0, resMax[i]() or 0) - i+=1 + i += 1 else: capitalizedType = resourceType[0].capitalize() + resourceType[1:] @@ -328,9 +347,10 @@ class ResourcesViewFull(StatsView): gauge.SetValueRange(0, 0) - i+=1 + i += 1 self.panel.Layout() self.headerPanel.Layout() + ResourcesViewFull.register() diff --git a/gui/builtinStatsViews/targetingMiscViewFull.py b/gui/builtinStatsViews/targetingMiscViewFull.py index 207fe46c5..27e83fea8 100644 --- a/gui/builtinStatsViews/targetingMiscViewFull.py +++ b/gui/builtinStatsViews/targetingMiscViewFull.py @@ -1,4 +1,4 @@ -#=============================================================================== +# ============================================================================= # Copyright (C) 2010 Diego Duclos # # This file is part of pyfa. @@ -15,29 +15,32 @@ # # You should have received a copy of the GNU General Public License # along with pyfa. If not, see . -#=============================================================================== +# ============================================================================= +# noinspection PyPackageRequirements import wx from gui.statsView import StatsView -from gui import builtinStatsViews from gui.utils.numberFormatter import formatAmount -import locale + try: from collections import OrderedDict except ImportError: from utils.compat import OrderedDict + class TargetingMiscViewFull(StatsView): name = "targetingmiscViewFull" + def __init__(self, parent): StatsView.__init__(self) self.parent = parent self._cachedValues = [] + def getHeaderText(self, fit): return "Targeting && Misc" def getTextExtentW(self, text): - width, height = self.parent.GetTextExtent( text ) + width, height = self.parent.GetTextExtent(text) return width def populatePanel(self, contentPanel, headerPanel): @@ -46,7 +49,7 @@ class TargetingMiscViewFull(StatsView): self.panel = contentPanel self.headerPanel = headerPanel gridTargetingMisc = wx.FlexGridSizer(1, 3) - contentSizer.Add( gridTargetingMisc, 0, wx.EXPAND | wx.ALL, 0) + contentSizer.Add(gridTargetingMisc, 0, wx.EXPAND | wx.ALL, 0) gridTargetingMisc.AddGrowableCol(0) gridTargetingMisc.AddGrowableCol(2) # Targeting @@ -68,17 +71,17 @@ class TargetingMiscViewFull(StatsView): box = wx.BoxSizer(wx.HORIZONTAL) gridTargeting.Add(box, 0, wx.ALIGN_LEFT) - lbl = wx.StaticText(contentPanel, wx.ID_ANY, "0 %s" %unit) + lbl = wx.StaticText(contentPanel, wx.ID_ANY, "0 %s" % unit) setattr(self, "label%s" % labelShort, lbl) box.Add(lbl, 0, wx.ALIGN_LEFT) self._cachedValues.append({"main": 0}) # Misc - gridTargetingMisc.Add( wx.StaticLine( contentPanel, wx.ID_ANY, style = wx.VERTICAL),0, wx.EXPAND, 3 ) + gridTargetingMisc.Add(wx.StaticLine(contentPanel, wx.ID_ANY, style=wx.VERTICAL), 0, wx.EXPAND, 3) gridMisc = wx.FlexGridSizer(5, 2) gridMisc.AddGrowableCol(1) - gridTargetingMisc.Add(gridMisc,0 , wx.ALIGN_LEFT | wx.ALL, 5) + gridTargetingMisc.Add(gridMisc, 0, wx.ALIGN_LEFT | wx.ALL, 5) labels = (("Speed", "Speed", "m/s"), ("Align time", "AlignTime", "s"), @@ -98,9 +101,8 @@ class TargetingMiscViewFull(StatsView): self._cachedValues.append({"main": 0}) - def refreshPanel(self, fit): - #If we did anything interesting, we'd update our labels to reflect the new fit's stats here + # If we did anything interesting, we'd update our labels to reflect the new fit's stats here cargoNamesOrder = OrderedDict(( ("fleetHangarCapacity", "Fleet hangar"), @@ -132,14 +134,17 @@ class TargetingMiscViewFull(StatsView): "specialSmallShipHoldCapacity": lambda: fit.ship.getModifiedItemAttr("specialSmallShipHoldCapacity"), "specialMediumShipHoldCapacity": lambda: fit.ship.getModifiedItemAttr("specialMediumShipHoldCapacity"), "specialLargeShipHoldCapacity": lambda: fit.ship.getModifiedItemAttr("specialLargeShipHoldCapacity"), - "specialIndustrialShipHoldCapacity": lambda: fit.ship.getModifiedItemAttr("specialIndustrialShipHoldCapacity"), + "specialIndustrialShipHoldCapacity": lambda: fit.ship.getModifiedItemAttr( + "specialIndustrialShipHoldCapacity"), "specialOreHoldCapacity": lambda: fit.ship.getModifiedItemAttr("specialOreHoldCapacity"), "specialMineralHoldCapacity": lambda: fit.ship.getModifiedItemAttr("specialMineralHoldCapacity"), "specialMaterialBayCapacity": lambda: fit.ship.getModifiedItemAttr("specialMaterialBayCapacity"), "specialGasHoldCapacity": lambda: fit.ship.getModifiedItemAttr("specialGasHoldCapacity"), "specialSalvageHoldCapacity": lambda: fit.ship.getModifiedItemAttr("specialSalvageHoldCapacity"), - "specialCommandCenterHoldCapacity": lambda: fit.ship.getModifiedItemAttr("specialCommandCenterHoldCapacity"), - "specialPlanetaryCommoditiesHoldCapacity": lambda: fit.ship.getModifiedItemAttr("specialPlanetaryCommoditiesHoldCapacity"), + "specialCommandCenterHoldCapacity": lambda: fit.ship.getModifiedItemAttr( + "specialCommandCenterHoldCapacity"), + "specialPlanetaryCommoditiesHoldCapacity": lambda: fit.ship.getModifiedItemAttr( + "specialPlanetaryCommoditiesHoldCapacity"), "specialQuafeHoldCapacity": lambda: fit.ship.getModifiedItemAttr("specialQuafeHoldCapacity") } @@ -155,9 +160,9 @@ class TargetingMiscViewFull(StatsView): ("labelFullCargo", cargoValues, 4, 0, 9, u"m\u00B3")) counter = 0 - RADII = [("Pod",25), ("Interceptor",33), ("Frigate",38), + RADII = [("Pod", 25), ("Interceptor", 33), ("Frigate", 38), ("Destroyer", 83), ("Cruiser", 130), - ("Battlecruiser", 265), ("Battleship",420), + ("Battlecruiser", 265), ("Battleship", 420), ("Carrier", 3000)] for labelName, valueDict, prec, lowest, highest, unit in stats: label = getattr(self, labelName) @@ -173,13 +178,13 @@ class TargetingMiscViewFull(StatsView): # Get sum of all cargoholds except for maintenance bay additionalCargo = sum(otherValues.values()) if additionalCargo > 0: - label.SetLabel("%s+%s %s" %(formatAmount(mainValue, prec, lowest, highest), - formatAmount(additionalCargo, prec, lowest, highest), - unit)) + label.SetLabel("%s+%s %s" % (formatAmount(mainValue, prec, lowest, highest), + formatAmount(additionalCargo, prec, lowest, highest), + unit)) else: - label.SetLabel("%s %s" %(formatAmount(mainValue, prec, lowest, highest), unit)) + label.SetLabel("%s %s" % (formatAmount(mainValue, prec, lowest, highest), unit)) else: - label.SetLabel("%s %s" %(formatAmount(mainValue, prec, lowest, highest), unit)) + label.SetLabel("%s %s" % (formatAmount(mainValue, prec, lowest, highest), unit)) # Tooltip stuff if fit: if labelName == "labelScanRes": @@ -187,25 +192,25 @@ class TargetingMiscViewFull(StatsView): for size, radius in RADII: left = "%.1fs" % fit.calculateLockTime(radius) right = "%s [%d]" % (size, radius) - lockTime += "%5s\t%s\n" % (left,right) + lockTime += "%5s\t%s\n" % (left, right) label.SetToolTip(wx.ToolTip(lockTime)) elif labelName == "labelFullSigRadius": - label.SetToolTip(wx.ToolTip("Probe Size: %.3f" % (fit.probeSize or 0) )) + label.SetToolTip(wx.ToolTip("Probe Size: %.3f" % (fit.probeSize or 0))) elif labelName == "labelFullWarpSpeed": label.SetToolTip(wx.ToolTip("Max Warp Distance: %.1f AU" % fit.maxWarpDistance)) elif labelName == "labelSensorStr": if fit.jamChance > 0: - label.SetToolTip(wx.ToolTip("Type: %s\n%.1f%% Chance of Jam" % (fit.scanType, fit.jamChance))) + label.SetToolTip( + wx.ToolTip("Type: %s\n%.1f%% Chance of Jam" % (fit.scanType, fit.jamChance))) else: - label.SetToolTip(wx.ToolTip("Type: %s" % (fit.scanType))) + label.SetToolTip(wx.ToolTip("Type: %s" % fit.scanType)) elif labelName == "labelFullAlignTime": - alignTime = "Align:\t%.3fs"%mainValue + alignTime = "Align:\t%.3fs" % mainValue mass = 'Mass:\t{:,.0f}kg'.format(fit.ship.getModifiedItemAttr("mass")) - agility = "Agility:\t%.3fx"%(fit.ship.getModifiedItemAttr("agility") or 0) + agility = "Agility:\t%.3fx" % (fit.ship.getModifiedItemAttr("agility") or 0) label.SetToolTip(wx.ToolTip("%s\n%s\n%s" % (alignTime, mass, agility))) elif labelName == "labelFullCargo": - tipLines = [] - tipLines.append(u"Cargohold: {:,.2f}m\u00B3 / {:,.2f}m\u00B3".format(fit.cargoBayUsed, newValues["main"])) + tipLines = [u"Cargohold: {:,.2f}m\u00B3 / {:,.2f}m\u00B3".format(fit.cargoBayUsed, newValues["main"])] for attrName, tipAlias in cargoNamesOrder.items(): if newValues[attrName] > 0: tipLines.append(u"{}: {:,.2f}m\u00B3".format(tipAlias, newValues[attrName])) @@ -225,7 +230,7 @@ class TargetingMiscViewFull(StatsView): if fit.jamChance > 0: label.SetToolTip(wx.ToolTip("Type: %s\n%.1f%% Chance of Jam" % (fit.scanType, fit.jamChance))) else: - label.SetToolTip(wx.ToolTip("Type: %s" % (fit.scanType))) + label.SetToolTip(wx.ToolTip("Type: %s" % fit.scanType)) else: label.SetToolTip(wx.ToolTip("")) elif labelName == "labelFullCargo": @@ -233,8 +238,7 @@ class TargetingMiscViewFull(StatsView): cachedCargo = self._cachedValues[counter] # if you add stuff to cargo, the capacity doesn't change and thus it is still cached # This assures us that we force refresh of cargo tooltip - tipLines = [] - tipLines.append(u"Cargohold: {:,.2f}m\u00B3 / {:,.2f}m\u00B3".format(fit.cargoBayUsed, cachedCargo["main"])) + tipLines = [u"Cargohold: {:,.2f}m\u00B3 / {:,.2f}m\u00B3".format(fit.cargoBayUsed, cachedCargo["main"])] for attrName, tipAlias in cargoNamesOrder.items(): if cachedCargo[attrName] > 0: tipLines.append(u"{}: {:,.2f}m\u00B3".format(tipAlias, cachedCargo[attrName])) @@ -247,4 +251,5 @@ class TargetingMiscViewFull(StatsView): self.panel.Layout() self.headerPanel.Layout() + TargetingMiscViewFull.register() diff --git a/gui/builtinViewColumns/__init__.py b/gui/builtinViewColumns/__init__.py index d66f423bf..cff8bc614 100644 --- a/gui/builtinViewColumns/__init__.py +++ b/gui/builtinViewColumns/__init__.py @@ -1,2 +1,2 @@ -__all__ = ["ammo", "ammoIcon", "attributeDisplay", "baseIcon", "baseName", - "capacitorUse", "maxRange", "price", "propertyDisplay", "state", "misc", "abilities"] +__all__ = ["ammo", "ammoIcon", "attributeDisplay", "baseIcon", "baseName", + "capacitorUse", "maxRange", "price", "propertyDisplay", "state", "misc", "abilities"] diff --git a/gui/builtinViewColumns/abilities.py b/gui/builtinViewColumns/abilities.py index f61557446..69b7ece97 100644 --- a/gui/builtinViewColumns/abilities.py +++ b/gui/builtinViewColumns/abilities.py @@ -1,5 +1,4 @@ -# -*- coding: utf-8 -*- -#=============================================================================== +# ============================================================================= # Copyright (C) 2010 Diego Duclos # # This file is part of pyfa. @@ -16,17 +15,19 @@ # # You should have received a copy of the GNU General Public License # along with pyfa. If not, see . -#=============================================================================== +# ============================================================================= + +# noinspection PyPackageRequirements +import wx +from eos.saveddata.fighter import Fighter from gui.viewColumn import ViewColumn import gui.mainFrame -import wx -from eos.types import Fighter - class Abilities(ViewColumn): name = "Fighter Abilities" + def __init__(self, fittingView, params): ViewColumn.__init__(self, fittingView) @@ -41,4 +42,5 @@ class Abilities(ViewColumn): return "None" return ", ".join(active) + Abilities.register() diff --git a/gui/builtinViewColumns/ammo.py b/gui/builtinViewColumns/ammo.py index 698e8595b..069328a32 100644 --- a/gui/builtinViewColumns/ammo.py +++ b/gui/builtinViewColumns/ammo.py @@ -1,58 +1,59 @@ -#=============================================================================== -# Copyright (C) 2010 Diego Duclos -# -# This file is part of pyfa. -# -# pyfa is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# pyfa is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with pyfa. If not, see . -#=============================================================================== - -from gui import builtinViewColumns -from gui.viewColumn import ViewColumn -from gui.bitmapLoader import BitmapLoader -import wx -from eos.types import Fighter - - -class Ammo(ViewColumn): - name = "Ammo" - def __init__(self, fittingView, params): - ViewColumn.__init__(self, fittingView) - self.mask = wx.LIST_MASK_IMAGE - self.imageId = fittingView.imageList.GetImageIndex("damagePattern_small", "gui") - self.bitmap = BitmapLoader.getBitmap("damagePattern_small", "gui") - - def getText(self, stuff): - if isinstance(stuff, Fighter): - # this is an experiment, not sure I like it. But it saves us from duplicating code. - col = self.columns['Fighter Abilities'](self.fittingView, {}) - text = col.getText(stuff) - del col - return text - if getattr(stuff, "charge", None) is not None: - charges = stuff.numCharges - if charges > 0: - cycles = stuff.numShots - if cycles !=0 and charges != cycles: - return "%s (%d, %d cycles)" % (stuff.charge.name, charges, cycles) - else: - return "%s (%d)" % (stuff.charge.name, charges) - else: - return stuff.charge.name - return "" - - def getImageId(self, mod): - return -1 - -Ammo.register() - +# ============================================================================= +# Copyright (C) 2010 Diego Duclos +# +# This file is part of pyfa. +# +# pyfa is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# pyfa is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with pyfa. If not, see . +# ============================================================================= + +# noinspection PyPackageRequirements +import wx +from eos.saveddata.fighter import Fighter +from gui.viewColumn import ViewColumn +from gui.bitmapLoader import BitmapLoader + + +class Ammo(ViewColumn): + name = "Ammo" + + def __init__(self, fittingView, params): + ViewColumn.__init__(self, fittingView) + self.mask = wx.LIST_MASK_IMAGE + self.imageId = fittingView.imageList.GetImageIndex("damagePattern_small", "gui") + self.bitmap = BitmapLoader.getBitmap("damagePattern_small", "gui") + + def getText(self, stuff): + if isinstance(stuff, Fighter): + # this is an experiment, not sure I like it. But it saves us from duplicating code. + col = self.columns['Fighter Abilities'](self.fittingView, {}) + text = col.getText(stuff) + del col + return text + if getattr(stuff, "charge", None) is not None: + charges = stuff.numCharges + if charges > 0: + cycles = stuff.numShots + if cycles != 0 and charges != cycles: + return "%s (%d, %d cycles)" % (stuff.charge.name, charges, cycles) + else: + return "%s (%d)" % (stuff.charge.name, charges) + else: + return stuff.charge.name + return "" + + def getImageId(self, mod): + return -1 + + +Ammo.register() diff --git a/gui/builtinViewColumns/ammoIcon.py b/gui/builtinViewColumns/ammoIcon.py index 2403077bd..7459db6a4 100644 --- a/gui/builtinViewColumns/ammoIcon.py +++ b/gui/builtinViewColumns/ammoIcon.py @@ -1,55 +1,57 @@ -#=============================================================================== -# Copyright (C) 2010 Diego Duclos -# -# This file is part of pyfa. -# -# pyfa is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# pyfa is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with pyfa. If not, see . -#=============================================================================== - -from gui import builtinViewColumns -from gui.viewColumn import ViewColumn -from gui.bitmapLoader import BitmapLoader -import wx -from eos.types import Module - -class AmmoIcon(ViewColumn): - name = "Ammo Icon" - def __init__(self, fittingView, params): - ViewColumn.__init__(self, fittingView) - self.size = 24 - self.maxsize = self.size - self.mask = wx.LIST_MASK_IMAGE - self.columnText = "" - - def getText(self, mod): - return "" - - def getImageId(self, stuff): - if not isinstance(stuff, Module): - return -1 - - if stuff.charge is None: - return -1 - else: - iconFile = stuff.charge.icon.iconFile if stuff.charge.icon else "" - if iconFile: - return self.fittingView.imageList.GetImageIndex(iconFile, "icons") - else: - return -1 - - def getToolTip(self, mod): - if isinstance(mod, Module) and mod.charge is not None: - return mod.charge.name - -AmmoIcon.register() +# ============================================================================= +# Copyright (C) 2010 Diego Duclos +# +# This file is part of pyfa. +# +# pyfa is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# pyfa is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with pyfa. If not, see . +# ============================================================================= + +from gui.viewColumn import ViewColumn +# noinspection PyPackageRequirements +import wx +from eos.saveddata.module import Module + + +class AmmoIcon(ViewColumn): + name = "Ammo Icon" + + def __init__(self, fittingView, params): + ViewColumn.__init__(self, fittingView) + self.size = 24 + self.maxsize = self.size + self.mask = wx.LIST_MASK_IMAGE + self.columnText = "" + + def getText(self, mod): + return "" + + def getImageId(self, stuff): + if not isinstance(stuff, Module): + return -1 + + if stuff.charge is None: + return -1 + else: + iconFile = stuff.charge.icon.iconFile if stuff.charge.icon else "" + if iconFile: + return self.fittingView.imageList.GetImageIndex(iconFile, "icons") + else: + return -1 + + def getToolTip(self, mod): + if isinstance(mod, Module) and mod.charge is not None: + return mod.charge.name + + +AmmoIcon.register() diff --git a/gui/builtinViewColumns/attributeDisplay.py b/gui/builtinViewColumns/attributeDisplay.py index 47d9aa043..855bfe2a1 100644 --- a/gui/builtinViewColumns/attributeDisplay.py +++ b/gui/builtinViewColumns/attributeDisplay.py @@ -1,4 +1,4 @@ -#=============================================================================== +# ============================================================================= # Copyright (C) 2010 Diego Duclos # # This file is part of pyfa. @@ -15,21 +15,25 @@ # # You should have received a copy of the GNU General Public License # along with pyfa. If not, see . -#=============================================================================== +# ============================================================================= + +# noinspection PyPackageRequirements +import wx -from gui import builtinViewColumns from gui.viewColumn import ViewColumn from gui.bitmapLoader import BitmapLoader from gui.utils.numberFormatter import formatAmount -import service -import wx +from service.attribute import Attribute +from service.market import Market + class AttributeDisplay(ViewColumn): name = "attr" + def __init__(self, fittingView, params): ViewColumn.__init__(self, fittingView) - sAttr = service.Attribute.getInstance() + sAttr = Attribute.getInstance() info = sAttr.getAttributeInfo(params["attribute"]) self.info = info if params["showIcon"]: @@ -57,9 +61,10 @@ class AttributeDisplay(ViewColumn): self.direct = True self.view = fittingView originalRefresh = fittingView.refresh - sMkt = service.Market.getInstance() - #Hack into our master view and add a callback for ourselves to know when to query + sMkt = Market.getInstance() + def refresh(stuff): + # Hack into our master view and add a callback for ourselves to know when to query self.directInfo = sMkt.directAttrRequest(stuff, info) if stuff else None originalRefresh(stuff) @@ -76,10 +81,10 @@ class AttributeDisplay(ViewColumn): attr = mod.getAttribute(self.info.name) if self.info.name == "volume": - str = (formatAmount(attr, 3, 0, 3)) + str_ = (formatAmount(attr, 3, 0, 3)) if hasattr(mod, "amount"): - str = str + u"m\u00B3 (%s m\u00B3)"%(formatAmount(attr*mod.amount, 3, 0, 3)) - attr = str + str_ += u"m\u00B3 (%s m\u00B3)" % (formatAmount(attr * mod.amount, 3, 0, 3)) + attr = str_ if isinstance(attr, (float, int)): attr = (formatAmount(attr, 3, 0, 3)) @@ -102,4 +107,5 @@ class AttributeDisplay(ViewColumn): ("showIcon", bool, True), ("direct", bool, False)) + AttributeDisplay.register() diff --git a/gui/builtinViewColumns/baseIcon.py b/gui/builtinViewColumns/baseIcon.py index 868c73a21..8c09a9fa3 100644 --- a/gui/builtinViewColumns/baseIcon.py +++ b/gui/builtinViewColumns/baseIcon.py @@ -1,44 +1,50 @@ -from gui import builtinViewColumns -from gui.viewColumn import ViewColumn -from gui.bitmapLoader import BitmapLoader -import wx -from eos.types import Drone, Fit, Module, Slot, Rack, Implant - -class BaseIcon(ViewColumn): - name = "Base Icon" - def __init__(self, fittingView, params): - ViewColumn.__init__(self, fittingView) - self.size = 24 - self.maxsize = self.size - self.mask = wx.LIST_MASK_IMAGE - self.columnText = "" - self.shipImage = fittingView.imageList.GetImageIndex("ship_small", "gui") - - def getImageId(self, stuff): - if isinstance(stuff, Drone): - return -1 - if isinstance(stuff, Fit): - return self.shipImage - if isinstance(stuff, Rack): - return -1 - if isinstance(stuff, Implant): - if stuff.character: # if it has a character as it's parent - return self.fittingView.imageList.GetImageIndex("character_small", "gui") - else: - return self.shipImage - if isinstance(stuff, Module): - if stuff.isEmpty: - return self.fittingView.imageList.GetImageIndex("slot_%s_small" % Slot.getName(stuff.slot).lower(), "gui") - else: - return self.loadIconFile(stuff.item.icon.iconFile if stuff.item.icon else "") - - item = getattr(stuff, "item", stuff) - return self.loadIconFile(item.icon.iconFile if item.icon else "") - - def loadIconFile(self, iconFile): - if iconFile: - return self.fittingView.imageList.GetImageIndex(iconFile, "icons") - else: - return -1 - -BaseIcon.register() +# noinspection PyPackageRequirements +import wx +from eos.saveddata.implant import Implant +from eos.saveddata.drone import Drone +from eos.saveddata.module import Module, Slot, Rack +from eos.saveddata.fit import Fit +from gui.viewColumn import ViewColumn + + +class BaseIcon(ViewColumn): + name = "Base Icon" + + def __init__(self, fittingView, params): + ViewColumn.__init__(self, fittingView) + self.size = 24 + self.maxsize = self.size + self.mask = wx.LIST_MASK_IMAGE + self.columnText = "" + self.shipImage = fittingView.imageList.GetImageIndex("ship_small", "gui") + + def getImageId(self, stuff): + if isinstance(stuff, Drone): + return -1 + elif isinstance(stuff, Fit): + return self.shipImage + elif isinstance(stuff, Rack): + return -1 + elif isinstance(stuff, Implant): + if stuff.character: # if it has a character as it's parent + return self.fittingView.imageList.GetImageIndex("character_small", "gui") + else: + return self.shipImage + elif isinstance(stuff, Module): + if stuff.isEmpty: + return self.fittingView.imageList.GetImageIndex("slot_%s_small" % Slot.getName(stuff.slot).lower(), + "gui") + else: + return self.loadIconFile(stuff.item.icon.iconFile if stuff.item.icon else "") + + item = getattr(stuff, "item", stuff) + return self.loadIconFile(item.icon.iconFile if item.icon else "") + + def loadIconFile(self, iconFile): + if iconFile: + return self.fittingView.imageList.GetImageIndex(iconFile, "icons") + else: + return -1 + + +BaseIcon.register() diff --git a/gui/builtinViewColumns/baseName.py b/gui/builtinViewColumns/baseName.py index 77b4698bd..70d0c65a7 100644 --- a/gui/builtinViewColumns/baseName.py +++ b/gui/builtinViewColumns/baseName.py @@ -1,5 +1,5 @@ -# -*- coding: utf-8 -*- -#=============================================================================== +# coding: utf-8 +# ============================================================================= # Copyright (C) 2010 Diego Duclos # # This file is part of pyfa. @@ -16,17 +16,24 @@ # # You should have received a copy of the GNU General Public License # along with pyfa. If not, see . -#=============================================================================== +# ============================================================================= +# noinspection PyPackageRequirements +import wx +from eos.saveddata.cargo import Cargo +from eos.saveddata.implant import Implant +from eos.saveddata.drone import Drone +from eos.saveddata.fighter import Fighter +from eos.saveddata.module import Module, Slot, Rack +from eos.saveddata.fit import Fit +from service.fit import Fit as FitSvc from gui.viewColumn import ViewColumn import gui.mainFrame -import wx -from eos.types import Drone, Cargo, Fit, Module, Slot, Rack, Implant, Fighter -import service class BaseName(ViewColumn): name = "Base Name" + def __init__(self, fittingView, params): ViewColumn.__init__(self, fittingView) @@ -39,8 +46,9 @@ class BaseName(ViewColumn): def getText(self, stuff): if isinstance(stuff, Drone): return "%dx %s" % (stuff.amount, stuff.item.name) - if isinstance(stuff, Fighter): - return "%d/%d %s" % (stuff.amountActive, stuff.getModifiedItemAttr("fighterSquadronMaxSize"), stuff.item.name) + elif isinstance(stuff, Fighter): + return "%d/%d %s" % \ + (stuff.amountActive, stuff.getModifiedItemAttr("fighterSquadronMaxSize"), stuff.item.name) elif isinstance(stuff, Cargo): return "%dx %s" % (stuff.amount, stuff.item.name) elif isinstance(stuff, Fit): @@ -51,7 +59,7 @@ class BaseName(ViewColumn): else: return "%s (%s)" % (stuff.name, stuff.ship.item.name) elif isinstance(stuff, Rack): - if service.Fit.getInstance().serviceFittingOptions["rackLabels"]: + if FitSvc.getInstance().serviceFittingOptions["rackLabels"]: if stuff.slot == Slot.MODE: return u'─ Tactical Mode ─' else: @@ -68,15 +76,16 @@ class BaseName(ViewColumn): else: item = getattr(stuff, "item", stuff) - if service.Fit.getInstance().serviceFittingOptions["showMarketShortcuts"]: + if FitSvc.getInstance().serviceFittingOptions["showMarketShortcuts"]: marketShortcut = getattr(item, "marketShortcut", None) if marketShortcut: # use unicode subscript to display shortcut value - shortcut = unichr(marketShortcut+8320)+u" " + shortcut = unichr(marketShortcut + 8320) + u" " del item.marketShortcut - return shortcut+item.name + return shortcut + item.name return item.name + BaseName.register() diff --git a/gui/builtinViewColumns/capacitorUse.py b/gui/builtinViewColumns/capacitorUse.py index 4a3407ab9..5d0287375 100644 --- a/gui/builtinViewColumns/capacitorUse.py +++ b/gui/builtinViewColumns/capacitorUse.py @@ -1,56 +1,59 @@ -#=============================================================================== -# Copyright (C) 2010 Diego Duclos -# -# This file is part of pyfa. -# -# pyfa is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# pyfa is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with pyfa. If not, see . -#=============================================================================== - -import wx -import service - -from gui.utils.numberFormatter import formatAmount -from gui.viewColumn import ViewColumn -from gui.bitmapLoader import BitmapLoader -from eos.types import Mode - -class CapacitorUse(ViewColumn): - name = "Capacitor Usage" - def __init__(self, fittingView, params): - ViewColumn.__init__(self, fittingView) - - self.mask = wx.LIST_MASK_IMAGE - - sAttr = service.Attribute.getInstance() - info = sAttr.getAttributeInfo("capacitorNeed") - self.imageId = fittingView.imageList.GetImageIndex("capacitorRecharge_small", "gui") - self.bitmap = BitmapLoader.getBitmap("capacitorRecharge_small", "gui") - - def getText(self, mod): - if isinstance(mod, Mode): - return "" - - capUse = mod.capUse - if capUse: - return "%s%s" % ("+" if capUse < 0 else "", (formatAmount(-capUse, 3, 0, 3))) - else: - return "" - - def getImageId(self, mod): - return -1 - - def getToolTip(self, mod): - return self.name - -CapacitorUse.register() +# ============================================================================= +# Copyright (C) 2010 Diego Duclos +# +# This file is part of pyfa. +# +# pyfa is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# pyfa is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with pyfa. If not, see . +# ============================================================================= + +# noinspection PyPackageRequirements +import wx + +from eos.saveddata.mode import Mode +from service.attribute import Attribute +from gui.utils.numberFormatter import formatAmount +from gui.viewColumn import ViewColumn +from gui.bitmapLoader import BitmapLoader + + +class CapacitorUse(ViewColumn): + name = "Capacitor Usage" + + def __init__(self, fittingView, params): + ViewColumn.__init__(self, fittingView) + + self.mask = wx.LIST_MASK_IMAGE + + Attribute.getInstance().getAttributeInfo("capacitorNeed") + self.imageId = fittingView.imageList.GetImageIndex("capacitorRecharge_small", "gui") + self.bitmap = BitmapLoader.getBitmap("capacitorRecharge_small", "gui") + + def getText(self, mod): + if isinstance(mod, Mode): + return "" + + capUse = mod.capUse + if capUse: + return "%s%s" % ("+" if capUse < 0 else "", (formatAmount(-capUse, 3, 0, 3))) + else: + return "" + + def getImageId(self, mod): + return -1 + + def getToolTip(self, mod): + return self.name + + +CapacitorUse.register() diff --git a/gui/builtinViewColumns/maxRange.py b/gui/builtinViewColumns/maxRange.py index eb8f8d693..d4b3367da 100644 --- a/gui/builtinViewColumns/maxRange.py +++ b/gui/builtinViewColumns/maxRange.py @@ -1,4 +1,4 @@ -#=============================================================================== +# ============================================================================= # Copyright (C) 2010 Diego Duclos # # This file is part of pyfa. @@ -15,25 +15,28 @@ # # You should have received a copy of the GNU General Public License # along with pyfa. If not, see . -#=============================================================================== +# ============================================================================= -from gui import builtinViewColumns +# noinspection PyPackageRequirements +import wx + +from eos.saveddata.mode import Mode +from service.attribute import Attribute from gui.viewColumn import ViewColumn from gui.bitmapLoader import BitmapLoader -import service from gui.utils.numberFormatter import formatAmount -import wx -from eos.types import Mode + class MaxRange(ViewColumn): name = "Max Range" - def __init__(self, fittingView, params = None): - if params == None: - params = {"showIcon": True, - "displayName": False} + + def __init__(self, fittingView, params=None): + if params is None: + params = {"showIcon": True, "displayName": False} + ViewColumn.__init__(self, fittingView) - sAttr = service.Attribute.getInstance() + sAttr = Attribute.getInstance() info = sAttr.getAttributeInfo("maxRange") self.info = info if params["showIcon"]: @@ -71,10 +74,10 @@ class MaxRange(ViewColumn): return -1 def getParameters(self): - return (("displayName", bool, False), - ("showIcon", bool, True)) + return ("displayName", bool, False), ("showIcon", bool, True) def getToolTip(self, mod): return "Optimal + Falloff" + MaxRange.register() diff --git a/gui/builtinViewColumns/misc.py b/gui/builtinViewColumns/misc.py index 69e7beebb..c1e12f29c 100644 --- a/gui/builtinViewColumns/misc.py +++ b/gui/builtinViewColumns/misc.py @@ -1,543 +1,589 @@ -#=============================================================================== -# Copyright (C) 2010 Diego Duclos -# -# This file is part of pyfa. -# -# pyfa is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# pyfa is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with pyfa. If not, see . -#=============================================================================== - - -import gui.mainFrame -from gui.viewColumn import ViewColumn -from gui.bitmapLoader import BitmapLoader -from gui.utils.numberFormatter import formatAmount -from gui.utils.listFormatter import formatList -from service.fit import Fit, Market - -import wx - -class Miscellanea(ViewColumn): - name = "Miscellanea" - def __init__(self, fittingView, params = None): - if params == None: - params = {"showIcon": True, - "displayName": False} - ViewColumn.__init__(self, fittingView) - if params["showIcon"]: - self.imageId = fittingView.imageList.GetImageIndex("column_misc", "gui") - self.bitmap = BitmapLoader.getBitmap("column_misc", "gui") - self.mask = wx.LIST_MASK_IMAGE - else: - self.imageId = -1 - - if params["displayName"] or self.imageId == -1: - self.columnText = "Misc data" - self.mask |= wx.LIST_MASK_TEXT - self.mainFrame = gui.mainFrame.MainFrame.getInstance() - - def getText(self, stuff): - text = self.__getData(stuff)[0] - return text - - def getToolTip(self, mod): - text = self.__getData(mod)[1] - return text - - def getImageId(self, mod): - return -1 - - def getParameters(self): - return (("displayName", bool, False), - ("showIcon", bool, True)) - - def __getData(self, stuff): - item = stuff.item - if item is None: - return "", None - itemGroup = item.group.name - itemCategory = item.category.name - - if itemGroup == "Ship Modifiers": - return "", None - elif itemGroup in ("Energy Weapon", "Hybrid Weapon", "Projectile Weapon", "Combat Drone", "Fighter Drone"): - trackingSpeed = stuff.getModifiedItemAttr("trackingSpeed") - if not trackingSpeed: - return "", None - text = "{0}".format(formatAmount(trackingSpeed, 3, 0, 3)) - tooltip = "Tracking speed" - return text, tooltip - elif itemCategory == "Subsystem": - slots = ("hi", "med", "low") - info = [] - for slot in slots: - n = int(stuff.getModifiedItemAttr("%sSlotModifier"%slot)) - if n > 0: - info.append("{0}{1}".format(n, slot[0].upper())) - return "+ "+", ".join(info), "Slot Modifiers" - elif itemGroup == "Energy Neutralizer": - neutAmount = stuff.getModifiedItemAttr("energyNeutralizerAmount") - cycleTime = stuff.cycleTime - if not neutAmount or not cycleTime: - return "", None - capPerSec = float(-neutAmount) * 1000 / cycleTime - text = "{0}/s".format(formatAmount(capPerSec, 3, 0, 3)) - tooltip = "Energy neutralization per second" - return text, tooltip - elif itemGroup == "Energy Nosferatu": - neutAmount = stuff.getModifiedItemAttr("powerTransferAmount") - cycleTime = stuff.cycleTime - if not neutAmount or not cycleTime: - return "", None - capPerSec = float(-neutAmount) * 1000 / cycleTime - text = "{0}/s".format(formatAmount(capPerSec, 3, 0, 3)) - tooltip = "Energy neutralization per second" - return text, tooltip - elif itemGroup == "Salvager": - chance = stuff.getModifiedItemAttr("accessDifficultyBonus") - if not chance: - return "", None - text = "{0}%".format(formatAmount(chance, 3, 0, 3)) - tooltip = "Item retrieval chance" - return text, tooltip - elif itemGroup == "Data Miners": - strength = stuff.getModifiedItemAttr("virusStrength") - coherence = stuff.getModifiedItemAttr("virusCoherence") - if not strength or not coherence: - return "", None - text = "{0} | {1}".format(formatAmount(strength, 3, 0, 3), formatAmount(coherence, 3, 0, 3)) - tooltip = "Virus strength and coherence" - return text, tooltip - elif itemGroup in ("Warp Scrambler", "Warp Core Stabilizer"): - scramStr = stuff.getModifiedItemAttr("warpScrambleStrength") - if not scramStr: - return "", None - text = "{0}".format(formatAmount(-scramStr, 3, 0, 3, forceSign=True)) - tooltip = "Warp core strength modification" - return text, tooltip - elif itemGroup in ("Stasis Web", "Stasis Webifying Drone"): - speedFactor = stuff.getModifiedItemAttr("speedFactor") - if not speedFactor: - return "", None - text = "{0}%".format(formatAmount(speedFactor, 3, 0, 3)) - tooltip = "Speed reduction" - return text, tooltip - elif itemGroup == "Target Painter": - sigRadBonus = stuff.getModifiedItemAttr("signatureRadiusBonus") - if not sigRadBonus: - return "", None - text = "{0}%".format(formatAmount(sigRadBonus, 3, 0, 3, forceSign=True)) - tooltip = "Signature radius increase" - return text, tooltip - elif itemGroup == "Sensor Dampener": - lockRangeBonus = stuff.getModifiedItemAttr("maxTargetRangeBonus") - scanResBonus = stuff.getModifiedItemAttr("scanResolutionBonus") - if lockRangeBonus is None or scanResBonus is None: - return "", None - display = 0 - for bonus in (lockRangeBonus, scanResBonus): - if abs(bonus) > abs(display): - display = bonus - if not display: - return "", None - text = "{0}%".format(formatAmount(display, 3, 0, 3, forceSign=True)) - ttEntries = [] - if display == lockRangeBonus: - ttEntries.append("lock range") - if display == scanResBonus: - ttEntries.append("scan resolution") - tooltip = "{0} dampening".format(formatList(ttEntries)).capitalize() - return text, tooltip - elif itemGroup == "Weapon Disruptor": - # Weapon disruption now covers both tracking and guidance (missile) disruptors - # First get the attributes for tracking disruptors - optimalRangeBonus = stuff.getModifiedItemAttr("maxRangeBonus") - falloffRangeBonus = stuff.getModifiedItemAttr("falloffBonus") - trackingSpeedBonus = stuff.getModifiedItemAttr("trackingSpeedBonus") - - trackingDisruptorAttributes = { - "optimal range": optimalRangeBonus, - "falloff range": falloffRangeBonus, - "tracking speed": trackingSpeedBonus} - - isTrackingDisruptor = any(map(lambda x: x is not None and x != 0, trackingDisruptorAttributes.values())) - - # Then get the attributes for guidance disruptors - explosionVelocityBonus = stuff.getModifiedItemAttr("aoeVelocityBonus") - explosionRadiusBonus = stuff.getModifiedItemAttr("aoeCloudSizeBonus") - - flightTimeBonus = stuff.getModifiedItemAttr("explosionDelayBonus") - missileVelocityBonus = stuff.getModifiedItemAttr("missileVelocityBonus") - - guidanceDisruptorAttributes = { - "explosion velocity": explosionVelocityBonus, - "explosion radius": explosionRadiusBonus, - "flight time": flightTimeBonus, - "missile velocity": missileVelocityBonus} - - isGuidanceDisruptor = any(map(lambda x: x is not None and x != 0, guidanceDisruptorAttributes.values())) - - if isTrackingDisruptor: - attributes = trackingDisruptorAttributes - elif isGuidanceDisruptor: - attributes = guidanceDisruptorAttributes - else: - return "", None - - display = max(attributes.values(), key=lambda x: abs(x)) - - text = "{0}%".format(formatAmount(display, 3, 0, 3, forceSign=True)) - - ttEntries = [] - for attributeName, attributeValue in attributes.items(): - if attributeValue == display: - ttEntries.append(attributeName) - - tooltip = "{0} disruption".format(formatList(ttEntries)).capitalize() - return text, tooltip - elif itemGroup in ("ECM", "Burst Jammer", "Burst Projectors"): - grav = stuff.getModifiedItemAttr("scanGravimetricStrengthBonus") - ladar = stuff.getModifiedItemAttr("scanLadarStrengthBonus") - radar = stuff.getModifiedItemAttr("scanRadarStrengthBonus") - magnet = stuff.getModifiedItemAttr("scanMagnetometricStrengthBonus") - displayMax = max(grav, ladar, radar, magnet) - displayMin = min(grav, ladar, radar, magnet) - if grav is None or ladar is None or radar is None or magnet is None or displayMax is None: - return "", None - - if displayMax == displayMin or displayMin is None: - text = "{0}".format( - formatAmount(displayMax, 3, 0, 3), - ) - else: - text = "{0} | {1}".format( - formatAmount(displayMax, 3, 0, 3), - formatAmount(displayMin, 3, 0, 3), - ) - tooltip = "ECM Jammer Strength:\n{0} Gravimetric | {1} Ladar | {2} Magnetometric | {3} Radar".format( - formatAmount(grav, 3, 0, 3), - formatAmount(ladar, 3, 0, 3), - formatAmount(magnet, 3, 0, 3), - formatAmount(radar, 3, 0, 3), - ) - return text, tooltip - elif itemGroup in ("Remote Sensor Booster", "Sensor Booster", "Signal Amplifier"): - scanResBonus = stuff.getModifiedItemAttr("scanResolutionBonus") - lockRangeBonus = stuff.getModifiedItemAttr("maxTargetRangeBonus") - gravBonus = stuff.getModifiedItemAttr("scanGravimetricStrengthPercent") - if scanResBonus is None or lockRangeBonus is None or gravBonus is None: - return "", None - - text = "{0}% | {1}% | {2}%".format( - formatAmount(scanResBonus, 3, 0, 3), - formatAmount(lockRangeBonus, 3, 0, 3), - formatAmount(gravBonus, 3, 0, 3), - ) - tooltip = "Applied bonuses:\n{0}% scan resolution | {1}% lock range | {2}% sensor strength".format( - formatAmount(scanResBonus, 3, 0, 3), - formatAmount(lockRangeBonus, 3, 0, 3), - formatAmount(gravBonus, 3, 0, 3), - ) - return text, tooltip - elif itemGroup in ("Projected ECCM", "ECCM", "Sensor Backup Array"): - grav = stuff.getModifiedItemAttr("scanGravimetricStrengthPercent") - ladar = stuff.getModifiedItemAttr("scanLadarStrengthPercent") - radar = stuff.getModifiedItemAttr("scanRadarStrengthPercent") - magnet = stuff.getModifiedItemAttr("scanMagnetometricStrengthPercent") - if grav is None or ladar is None or radar is None or magnet is None: - return "", None - display = max(grav, ladar, radar, magnet) - if not display: - return "", None - text = "{0}%".format(formatAmount(display, 3, 0, 3, forceSign=True)) - ttEntries = [] - if display == grav: - ttEntries.append("gravimetric") - if display == ladar: - ttEntries.append("ladar") - if display == magnet: - ttEntries.append("magnetometric") - if display == radar: - ttEntries.append("radar") - plu = "" if len(ttEntries) == 1 else "s" - tooltip = "{0} strength{1} bonus".format(formatList(ttEntries), plu).capitalize() - return text, tooltip - elif itemGroup == "Cloaking Device": - recalibration = stuff.getModifiedItemAttr("cloakingTargetingDelay") - if recalibration is None: - return "", None - text = "{0}s".format(formatAmount(float(recalibration)/1000, 3, 0, 3)) - tooltip = "Sensor recalibration time" - return text, tooltip - elif itemGroup == "Remote Armor Repairer": - repAmount = stuff.getModifiedItemAttr("armorDamageAmount") - cycleTime = stuff.getModifiedItemAttr("duration") - if not repAmount or not cycleTime: - return "", None - repPerSec = float(repAmount) * 1000 / cycleTime - text = "{0}/s".format(formatAmount(repPerSec, 3, 0, 3, forceSign=True)) - tooltip = "Armor repaired per second" - return text, tooltip - elif itemGroup == "Remote Shield Booster": - repAmount = stuff.getModifiedItemAttr("shieldBonus") - cycleTime = stuff.cycleTime - if not repAmount or not cycleTime: - return "", None - repPerSec = float(repAmount) * 1000 / cycleTime - text = "{0}/s".format(formatAmount(repPerSec, 3, 0, 3, forceSign=True)) - tooltip = "Shield transferred per second" - return text, tooltip - elif itemGroup == "Remote Capacitor Transmitter": - repAmount = stuff.getModifiedItemAttr("powerTransferAmount") - cycleTime = stuff.cycleTime - if not repAmount or not cycleTime: - return "", None - repPerSec = float(repAmount) * 1000 / cycleTime - text = "{0}/s".format(formatAmount(repPerSec, 3, 0, 3, forceSign=True)) - tooltip = "Energy transferred per second" - return text, tooltip - elif itemGroup == "Remote Hull Repairer": - repAmount = stuff.getModifiedItemAttr("structureDamageAmount") - cycleTime = stuff.cycleTime - if not repAmount or not cycleTime: - return "", None - repPerSec = float(repAmount) * 1000 / cycleTime - text = "{0}/s".format(formatAmount(repPerSec, 3, 0, 3, forceSign=True)) - tooltip = "Structure repaired per second" - return text, tooltip - elif itemGroup == "Gang Coordinator": - command = stuff.getModifiedItemAttr("commandBonus") or stuff.getModifiedItemAttr("commandBonusHidden") - if not command: - return "", None - text = "{0}%".format(formatAmount(command, 3, 0, 3, forceSign=True)) - tooltip = "Gang bonus strength" - return text, tooltip - elif itemGroup == "Electronic Warfare Drone": - sigRadBonus = stuff.getModifiedItemAttr("signatureRadiusBonus") - lockRangeMult = stuff.getModifiedItemAttr("maxTargetRangeMultiplier") - scanResMult = stuff.getModifiedItemAttr("scanResolutionMultiplier") - falloffRangeMult = stuff.getModifiedItemAttr("fallofMultiplier") - optimalRangeMult = stuff.getModifiedItemAttr("maxRangeMultiplier") - trackingSpeedMult = stuff.getModifiedItemAttr("trackingSpeedMultiplier") - grav = stuff.getModifiedItemAttr("scanGravimetricStrengthBonus") - ladar = stuff.getModifiedItemAttr("scanLadarStrengthBonus") - radar = stuff.getModifiedItemAttr("scanRadarStrengthBonus") - magnet = stuff.getModifiedItemAttr("scanMagnetometricStrengthBonus") - if sigRadBonus: - text = "{0}%".format(formatAmount(sigRadBonus, 3, 0, 3, forceSign=True)) - tooltip = "Signature radius increase" - return text, tooltip - if lockRangeMult is not None and scanResMult is not None: - lockRangeBonus = (lockRangeMult - 1) * 100 - scanResBonus = (scanResMult - 1) * 100 - display = 0 - for bonus in (lockRangeBonus, scanResBonus): - if abs(bonus) > abs(display): - display = bonus - if not display: - return "", None - text = "{0}%".format(formatAmount(display, 3, 0, 3, forceSign=True)) - ttEntries = [] - if display == lockRangeBonus: - ttEntries.append("lock range") - if display == scanResBonus: - ttEntries.append("scan resolution") - tooltip = "{0} dampening".format(formatList(ttEntries)).capitalize() - return text, tooltip - if falloffRangeMult is not None and optimalRangeMult is not None and trackingSpeedMult is not None: - falloffRangeBonus = (falloffRangeMult - 1) * 100 - optimalRangeBonus = (optimalRangeMult - 1) * 100 - trackingSpeedBonus = (trackingSpeedMult - 1) * 100 - display = 0 - for bonus in (falloffRangeBonus, optimalRangeBonus, trackingSpeedBonus): - if abs(bonus) > abs(display): - display = bonus - if not display: - return "", None - text = "{0}%".format(formatAmount(display, 3, 0, 3), forceSign=True) - ttEntries = [] - if display == optimalRangeBonus: - ttEntries.append("optimal range") - if display == falloffRangeBonus: - ttEntries.append("falloff range") - if display == trackingSpeedBonus: - ttEntries.append("tracking speed") - tooltip = "{0} disruption".format(formatList(ttEntries)).capitalize() - return text, tooltip - if grav is not None and ladar is not None and radar is not None and magnet is not None: - display = max(grav, ladar, radar, magnet) - if not display: - return "", None - text = "{0}".format(formatAmount(display, 3, 0, 3)) - ttEntries = [] - if display == grav: - ttEntries.append("gravimetric") - if display == ladar: - ttEntries.append("ladar") - if display == magnet: - ttEntries.append("magnetometric") - if display == radar: - ttEntries.append("radar") - plu = "" if len(ttEntries) == 1 else "s" - tooltip = "{0} strength{1}".format(formatList(ttEntries), plu).capitalize() - return text, tooltip - else: - return "", None - elif itemGroup == "Fighter Bomber": - optimalSig = stuff.getModifiedItemAttr("optimalSigRadius") - if not optimalSig: - return "", None - text = "{0}m".format(formatAmount(optimalSig, 3, 0, 3)) - tooltip = "Optimal signature radius" - return text, tooltip - elif itemGroup in ("Frequency Mining Laser", "Strip Miner", "Mining Laser", "Gas Cloud Harvester"): - miningAmount = stuff.getModifiedItemAttr("specialtyMiningAmount") or stuff.getModifiedItemAttr("miningAmount") - cycleTime = stuff.cycleTime - if not miningAmount or not cycleTime: - return "", None - minePerHour = (float(miningAmount) * 1000 / cycleTime) * 3600 - text = "{0}/h".format(formatAmount(minePerHour, 3, 0, 3)) - tooltip = "Mining Yield per hour" - return text, tooltip - elif itemGroup == "Logistic Drone": - armorAmount = stuff.getModifiedItemAttr("armorDamageAmount") - shieldAmount = stuff.getModifiedItemAttr("shieldBonus") - hullAmount = stuff.getModifiedItemAttr("structureDamageAmount") - repAmount = armorAmount or shieldAmount or hullAmount - cycleTime = stuff.getModifiedItemAttr("duration") - if not repAmount or not cycleTime: - return "", None - repPerSec = float(repAmount) * 1000 / cycleTime - text = "{0}/s".format(formatAmount(repPerSec, 3, 0, 3)) - ttEntries = [] - if hullAmount is not None and repAmount == hullAmount: - ttEntries.append("structure") - if armorAmount is not None and repAmount == armorAmount: - ttEntries.append("armor") - if shieldAmount is not None and repAmount == shieldAmount: - ttEntries.append("shield") - tooltip = "{0} repaired per second".format(formatList(ttEntries)).capitalize() - return text, tooltip - elif itemGroup == "Energy Neutralizer Drone": - neutAmount = stuff.getModifiedItemAttr("energyNeutralizerAmount") - cycleTime = stuff.getModifiedItemAttr("energyNeutralizerDuration") - if not neutAmount or not cycleTime: - return "", None - capPerSec = float(-neutAmount) * 1000 / cycleTime - text = "{0}/s".format(formatAmount(capPerSec, 3, 0, 3)) - tooltip = "Energy neutralization per second" - return text, tooltip - elif itemGroup == "Mining Drone": - miningAmount = stuff.getModifiedItemAttr("miningAmount") - cycleTime = stuff.getModifiedItemAttr("duration") - if not miningAmount or not cycleTime: - return "", None - minePerHour = (float(miningAmount) * 1000 / cycleTime) * 3600 - text = "{0}/h".format(formatAmount(minePerHour, 3, 0, 3)) - tooltip = "Mining Yield per hour" - return text, tooltip - elif itemGroup == "Micro Jump Drive": - cycleTime = stuff.getModifiedItemAttr("duration") / 1000 - text = "{0}s".format(cycleTime) - tooltip = "Spoolup time" - return text, tooltip - elif itemGroup in ("Siege Module", "Cynosural Field"): - amt = stuff.getModifiedItemAttr("consumptionQuantity") - if amt: - typeID = stuff.getModifiedItemAttr("consumptionType") - item = Market.getInstance().getItem(typeID) - text = "{0} units".format(formatAmount(amt, 3, 0, 3)) - return text, item.name - else: - return "", None - elif itemGroup in ("Ancillary Armor Repairer", "Ancillary Shield Booster"): - hp = stuff.hpBeforeReload - cycles = stuff.numShots - cycleTime = stuff.rawCycleTime - if not hp or not cycleTime or not cycles: - return "", None - fit = Fit.getInstance().getFit(self.mainFrame.getActiveFit()) - ehpTotal = fit.ehp - hpTotal = fit.hp - useEhp = self.mainFrame.statsPane.nameViewMap["resistancesViewFull"].showEffective - tooltip = "HP restored over duration using charges" - if useEhp: - if itemGroup == "Ancillary Armor Repairer": - hpRatio = ehpTotal["armor"] / hpTotal["armor"] - else: - hpRatio = ehpTotal["shield"] / hpTotal["shield"] - tooltip = "E{0}".format(tooltip) - else: - hpRatio = 1 - if itemGroup == "Ancillary Armor Repairer": - hpRatio *= 3 - ehp = hp * hpRatio - duration = cycles * cycleTime / 1000 - text = "{0} / {1}s".format(formatAmount(ehp, 3, 0, 9), formatAmount(duration, 3, 0, 3)) - - return text, tooltip - elif itemGroup == "Armor Resistance Shift Hardener": - itemArmorResistanceShiftHardenerEM = (1-stuff.getModifiedItemAttr("armorEmDamageResonance"))*100 - itemArmorResistanceShiftHardenerTherm = (1-stuff.getModifiedItemAttr("armorThermalDamageResonance")) * 100 - itemArmorResistanceShiftHardenerKin = (1-stuff.getModifiedItemAttr("armorKineticDamageResonance")) * 100 - itemArmorResistanceShiftHardenerExp = (1-stuff.getModifiedItemAttr("armorExplosiveDamageResonance")) * 100 - - text = "{0}% | {1}% | {2}% | {3}%".format( - formatAmount(itemArmorResistanceShiftHardenerEM, 3, 0, 3), - formatAmount(itemArmorResistanceShiftHardenerTherm, 3, 0, 3), - formatAmount(itemArmorResistanceShiftHardenerKin, 3, 0, 3), - formatAmount(itemArmorResistanceShiftHardenerExp, 3, 0, 3), - ) - tooltip = "Resistances Shifted to Damage Profile:\n{0}% EM | {1}% Therm | {2}% Kin | {3}% Exp".format( - formatAmount(itemArmorResistanceShiftHardenerEM, 3, 0, 3), - formatAmount(itemArmorResistanceShiftHardenerTherm, 3, 0, 3), - formatAmount(itemArmorResistanceShiftHardenerKin, 3, 0, 3), - formatAmount(itemArmorResistanceShiftHardenerExp, 3, 0, 3), - ) - return text, tooltip - elif stuff.charge is not None: - chargeGroup = stuff.charge.group.name - if chargeGroup in ("Rocket", "Advanced Rocket", "Light Missile", "Advanced Light Missile", "FoF Light Missile", - "Heavy Assault Missile", "Advanced Heavy Assault Missile", "Heavy Missile", "Advanced Heavy Missile", "FoF Heavy Missile", - "Torpedo", "Advanced Torpedo", "Cruise Missile", "Advanced Cruise Missile", "FoF Cruise Missile", - "XL Torpedo", "XL Cruise Missile"): - cloudSize = stuff.getModifiedChargeAttr("aoeCloudSize") - aoeVelocity = stuff.getModifiedChargeAttr("aoeVelocity") - if not cloudSize or not aoeVelocity: - return "", None - text = "{0}{1} | {2}{3}".format(formatAmount(cloudSize, 3, 0, 3), "m", - formatAmount(aoeVelocity, 3, 0, 3), "m/s") - tooltip = "Explosion radius and explosion velocity" - return text, tooltip - elif chargeGroup == "Bomb": - cloudSize = stuff.getModifiedChargeAttr("aoeCloudSize") - if not cloudSize: - return "", None - text = "{0}{1}".format(formatAmount(cloudSize, 3, 0, 3), "m") - tooltip = "Explosion radius" - return text, tooltip - elif chargeGroup in ("Scanner Probe",): - scanStr = stuff.getModifiedChargeAttr("baseSensorStrength") - baseRange = stuff.getModifiedChargeAttr("baseScanRange") - if not scanStr or not baseRange: - return "", None - strTwoAu = scanStr / (2.0 / baseRange) - text = "{0}".format(formatAmount(strTwoAu, 3, 0, 3)) - tooltip = "Scan strength with 2 AU scan range" - return text, tooltip - else: - return "", None - else: - return "", None - -Miscellanea.register() +# ============================================================================= +# Copyright (C) 2010 Diego Duclos +# +# This file is part of pyfa. +# +# pyfa is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# pyfa is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with pyfa. If not, see . +# ============================================================================= + +# noinspection PyPackageRequirements +import wx + +from service.fit import Fit +from service.market import Market +import gui.mainFrame +from gui.viewColumn import ViewColumn +from gui.bitmapLoader import BitmapLoader +from gui.utils.numberFormatter import formatAmount +from gui.utils.listFormatter import formatList + + +class Miscellanea(ViewColumn): + name = "Miscellanea" + + def __init__(self, fittingView, params=None): + if params is None: + params = {"showIcon": True, "displayName": False} + + ViewColumn.__init__(self, fittingView) + if params["showIcon"]: + self.imageId = fittingView.imageList.GetImageIndex("column_misc", "gui") + self.bitmap = BitmapLoader.getBitmap("column_misc", "gui") + self.mask = wx.LIST_MASK_IMAGE + else: + self.imageId = -1 + + if params["displayName"] or self.imageId == -1: + self.columnText = "Misc data" + self.mask |= wx.LIST_MASK_TEXT + self.mainFrame = gui.mainFrame.MainFrame.getInstance() + + def getText(self, stuff): + return self.__getData(stuff)[0] + + def getToolTip(self, mod): + return self.__getData(mod)[1] + + def getImageId(self, mod): + return -1 + + def getParameters(self): + return ("displayName", bool, False), ("showIcon", bool, True) + + def __getData(self, stuff): + item = stuff.item + if item is None: + return "", None + itemGroup = item.group.name + itemCategory = item.category.name + + if itemGroup == "Ship Modifiers": + return "", None + elif itemGroup in ("Energy Weapon", "Hybrid Weapon", "Projectile Weapon", "Combat Drone", "Fighter Drone"): + trackingSpeed = stuff.getModifiedItemAttr("trackingSpeed") + if not trackingSpeed: + return "", None + text = "{0}".format(formatAmount(trackingSpeed, 3, 0, 3)) + tooltip = "Tracking speed" + return text, tooltip + elif itemCategory == "Subsystem": + slots = ("hi", "med", "low") + info = [] + for slot in slots: + n = int(stuff.getModifiedItemAttr("%sSlotModifier" % slot)) + if n > 0: + info.append("{0}{1}".format(n, slot[0].upper())) + return "+ " + ", ".join(info), "Slot Modifiers" + elif itemGroup == "Energy Neutralizer": + neutAmount = stuff.getModifiedItemAttr("energyNeutralizerAmount") + cycleTime = stuff.cycleTime + if not neutAmount or not cycleTime: + return "", None + capPerSec = float(-neutAmount) * 1000 / cycleTime + text = "{0}/s".format(formatAmount(capPerSec, 3, 0, 3)) + tooltip = "Energy neutralization per second" + return text, tooltip + elif itemGroup == "Energy Nosferatu": + neutAmount = stuff.getModifiedItemAttr("powerTransferAmount") + cycleTime = stuff.cycleTime + if not neutAmount or not cycleTime: + return "", None + capPerSec = float(-neutAmount) * 1000 / cycleTime + text = "{0}/s".format(formatAmount(capPerSec, 3, 0, 3)) + tooltip = "Energy neutralization per second" + return text, tooltip + elif itemGroup == "Salvager": + chance = stuff.getModifiedItemAttr("accessDifficultyBonus") + if not chance: + return "", None + text = "{0}%".format(formatAmount(chance, 3, 0, 3)) + tooltip = "Item retrieval chance" + return text, tooltip + elif itemGroup == "Data Miners": + strength = stuff.getModifiedItemAttr("virusStrength") + coherence = stuff.getModifiedItemAttr("virusCoherence") + if not strength or not coherence: + return "", None + text = "{0} | {1}".format(formatAmount(strength, 3, 0, 3), formatAmount(coherence, 3, 0, 3)) + tooltip = "Virus strength and coherence" + return text, tooltip + elif itemGroup in ("Warp Scrambler", "Warp Core Stabilizer"): + scramStr = stuff.getModifiedItemAttr("warpScrambleStrength") + if not scramStr: + return "", None + text = "{0}".format(formatAmount(-scramStr, 3, 0, 3, forceSign=True)) + tooltip = "Warp core strength modification" + return text, tooltip + elif itemGroup in ("Stasis Web", "Stasis Webifying Drone"): + speedFactor = stuff.getModifiedItemAttr("speedFactor") + if not speedFactor: + return "", None + text = "{0}%".format(formatAmount(speedFactor, 3, 0, 3)) + tooltip = "Speed reduction" + return text, tooltip + elif itemGroup == "Target Painter": + sigRadBonus = stuff.getModifiedItemAttr("signatureRadiusBonus") + if not sigRadBonus: + return "", None + text = "{0}%".format(formatAmount(sigRadBonus, 3, 0, 3, forceSign=True)) + tooltip = "Signature radius increase" + return text, tooltip + elif itemGroup == "Sensor Dampener": + lockRangeBonus = stuff.getModifiedItemAttr("maxTargetRangeBonus") + scanResBonus = stuff.getModifiedItemAttr("scanResolutionBonus") + if lockRangeBonus is None or scanResBonus is None: + return "", None + display = 0 + for bonus in (lockRangeBonus, scanResBonus): + if abs(bonus) > abs(display): + display = bonus + if not display: + return "", None + text = "{0}%".format(formatAmount(display, 3, 0, 3, forceSign=True)) + ttEntries = [] + if display == lockRangeBonus: + ttEntries.append("lock range") + if display == scanResBonus: + ttEntries.append("scan resolution") + tooltip = "{0} dampening".format(formatList(ttEntries)).capitalize() + return text, tooltip + elif itemGroup == "Weapon Disruptor": + # Weapon disruption now covers both tracking and guidance (missile) disruptors + # First get the attributes for tracking disruptors + optimalRangeBonus = stuff.getModifiedItemAttr("maxRangeBonus") + falloffRangeBonus = stuff.getModifiedItemAttr("falloffBonus") + trackingSpeedBonus = stuff.getModifiedItemAttr("trackingSpeedBonus") + + trackingDisruptorAttributes = { + "optimal range": optimalRangeBonus, + "falloff range": falloffRangeBonus, + "tracking speed": trackingSpeedBonus} + + isTrackingDisruptor = any(map(lambda x: x is not None and x != 0, trackingDisruptorAttributes.values())) + + # Then get the attributes for guidance disruptors + explosionVelocityBonus = stuff.getModifiedItemAttr("aoeVelocityBonus") + explosionRadiusBonus = stuff.getModifiedItemAttr("aoeCloudSizeBonus") + + flightTimeBonus = stuff.getModifiedItemAttr("explosionDelayBonus") + missileVelocityBonus = stuff.getModifiedItemAttr("missileVelocityBonus") + + guidanceDisruptorAttributes = { + "explosion velocity": explosionVelocityBonus, + "explosion radius": explosionRadiusBonus, + "flight time": flightTimeBonus, + "missile velocity": missileVelocityBonus} + + isGuidanceDisruptor = any(map(lambda x: x is not None and x != 0, guidanceDisruptorAttributes.values())) + + if isTrackingDisruptor: + attributes = trackingDisruptorAttributes + elif isGuidanceDisruptor: + attributes = guidanceDisruptorAttributes + else: + return "", None + + display = max(attributes.values(), key=lambda x: abs(x)) + + text = "{0}%".format(formatAmount(display, 3, 0, 3, forceSign=True)) + + ttEntries = [] + for attributeName, attributeValue in attributes.items(): + if attributeValue == display: + ttEntries.append(attributeName) + + tooltip = "{0} disruption".format(formatList(ttEntries)).capitalize() + return text, tooltip + elif itemGroup in ("ECM", "Burst Jammer", "Burst Projectors"): + grav = stuff.getModifiedItemAttr("scanGravimetricStrengthBonus") + ladar = stuff.getModifiedItemAttr("scanLadarStrengthBonus") + radar = stuff.getModifiedItemAttr("scanRadarStrengthBonus") + magnet = stuff.getModifiedItemAttr("scanMagnetometricStrengthBonus") + displayMax = max(grav, ladar, radar, magnet) + displayMin = min(grav, ladar, radar, magnet) + if grav is None or ladar is None or radar is None or magnet is None or displayMax is None: + return "", None + + if displayMax == displayMin or displayMin is None: + text = "{0}".format( + formatAmount(displayMax, 3, 0, 3), + ) + else: + text = "{0} | {1}".format( + formatAmount(displayMax, 3, 0, 3), + formatAmount(displayMin, 3, 0, 3), + ) + tooltip = "ECM Jammer Strength:\n{0} Gravimetric | {1} Ladar | {2} Magnetometric | {3} Radar".format( + formatAmount(grav, 3, 0, 3), + formatAmount(ladar, 3, 0, 3), + formatAmount(magnet, 3, 0, 3), + formatAmount(radar, 3, 0, 3), + ) + return text, tooltip + elif itemGroup in ("Remote Sensor Booster", "Sensor Booster", "Signal Amplifier"): + scanResBonus = stuff.getModifiedItemAttr("scanResolutionBonus") + lockRangeBonus = stuff.getModifiedItemAttr("maxTargetRangeBonus") + gravBonus = stuff.getModifiedItemAttr("scanGravimetricStrengthPercent") + if scanResBonus is None or lockRangeBonus is None or gravBonus is None: + return "", None + + text = "{0}% | {1}% | {2}%".format( + formatAmount(scanResBonus, 3, 0, 3), + formatAmount(lockRangeBonus, 3, 0, 3), + formatAmount(gravBonus, 3, 0, 3), + ) + tooltip = "Applied bonuses:\n{0}% scan resolution | {1}% lock range | {2}% sensor strength".format( + formatAmount(scanResBonus, 3, 0, 3), + formatAmount(lockRangeBonus, 3, 0, 3), + formatAmount(gravBonus, 3, 0, 3), + ) + return text, tooltip + elif itemGroup in ("Projected ECCM", "ECCM", "Sensor Backup Array"): + grav = stuff.getModifiedItemAttr("scanGravimetricStrengthPercent") + ladar = stuff.getModifiedItemAttr("scanLadarStrengthPercent") + radar = stuff.getModifiedItemAttr("scanRadarStrengthPercent") + magnet = stuff.getModifiedItemAttr("scanMagnetometricStrengthPercent") + if grav is None or ladar is None or radar is None or magnet is None: + return "", None + display = max(grav, ladar, radar, magnet) + if not display: + return "", None + text = "{0}%".format(formatAmount(display, 3, 0, 3, forceSign=True)) + ttEntries = [] + if display == grav: + ttEntries.append("gravimetric") + if display == ladar: + ttEntries.append("ladar") + if display == magnet: + ttEntries.append("magnetometric") + if display == radar: + ttEntries.append("radar") + plu = "" if len(ttEntries) == 1 else "s" + tooltip = "{0} strength{1} bonus".format(formatList(ttEntries), plu).capitalize() + return text, tooltip + elif itemGroup == "Cloaking Device": + recalibration = stuff.getModifiedItemAttr("cloakingTargetingDelay") + if recalibration is None: + return "", None + text = "{0}s".format(formatAmount(float(recalibration) / 1000, 3, 0, 3)) + tooltip = "Sensor recalibration time" + return text, tooltip + elif itemGroup == "Remote Armor Repairer": + repAmount = stuff.getModifiedItemAttr("armorDamageAmount") + cycleTime = stuff.getModifiedItemAttr("duration") + if not repAmount or not cycleTime: + return "", None + repPerSec = float(repAmount) * 1000 / cycleTime + text = "{0}/s".format(formatAmount(repPerSec, 3, 0, 3, forceSign=True)) + tooltip = "Armor repaired per second" + return text, tooltip + elif itemGroup == "Remote Shield Booster": + repAmount = stuff.getModifiedItemAttr("shieldBonus") + cycleTime = stuff.cycleTime + if not repAmount or not cycleTime: + return "", None + repPerSec = float(repAmount) * 1000 / cycleTime + text = "{0}/s".format(formatAmount(repPerSec, 3, 0, 3, forceSign=True)) + tooltip = "Shield transferred per second" + return text, tooltip + elif itemGroup == "Remote Capacitor Transmitter": + repAmount = stuff.getModifiedItemAttr("powerTransferAmount") + cycleTime = stuff.cycleTime + if not repAmount or not cycleTime: + return "", None + repPerSec = float(repAmount) * 1000 / cycleTime + text = "{0}/s".format(formatAmount(repPerSec, 3, 0, 3, forceSign=True)) + tooltip = "Energy transferred per second" + return text, tooltip + elif itemGroup == "Remote Hull Repairer": + repAmount = stuff.getModifiedItemAttr("structureDamageAmount") + cycleTime = stuff.cycleTime + if not repAmount or not cycleTime: + return "", None + repPerSec = float(repAmount) * 1000 / cycleTime + text = "{0}/s".format(formatAmount(repPerSec, 3, 0, 3, forceSign=True)) + tooltip = "Structure repaired per second" + return text, tooltip + elif itemGroup == "Gang Coordinator": + command = stuff.getModifiedItemAttr("commandBonus") or stuff.getModifiedItemAttr("commandBonusHidden") + if not command: + return "", None + text = "{0}%".format(formatAmount(command, 3, 0, 3, forceSign=True)) + tooltip = "Gang bonus strength" + return text, tooltip + elif itemGroup == "Electronic Warfare Drone": + sigRadBonus = stuff.getModifiedItemAttr("signatureRadiusBonus") + lockRangeMult = stuff.getModifiedItemAttr("maxTargetRangeMultiplier") + scanResMult = stuff.getModifiedItemAttr("scanResolutionMultiplier") + falloffRangeMult = stuff.getModifiedItemAttr("fallofMultiplier") + optimalRangeMult = stuff.getModifiedItemAttr("maxRangeMultiplier") + trackingSpeedMult = stuff.getModifiedItemAttr("trackingSpeedMultiplier") + grav = stuff.getModifiedItemAttr("scanGravimetricStrengthBonus") + ladar = stuff.getModifiedItemAttr("scanLadarStrengthBonus") + radar = stuff.getModifiedItemAttr("scanRadarStrengthBonus") + magnet = stuff.getModifiedItemAttr("scanMagnetometricStrengthBonus") + if sigRadBonus: + text = "{0}%".format(formatAmount(sigRadBonus, 3, 0, 3, forceSign=True)) + tooltip = "Signature radius increase" + return text, tooltip + if lockRangeMult is not None and scanResMult is not None: + lockRangeBonus = (lockRangeMult - 1) * 100 + scanResBonus = (scanResMult - 1) * 100 + display = 0 + for bonus in (lockRangeBonus, scanResBonus): + if abs(bonus) > abs(display): + display = bonus + if not display: + return "", None + text = "{0}%".format(formatAmount(display, 3, 0, 3, forceSign=True)) + ttEntries = [] + if display == lockRangeBonus: + ttEntries.append("lock range") + if display == scanResBonus: + ttEntries.append("scan resolution") + tooltip = "{0} dampening".format(formatList(ttEntries)).capitalize() + return text, tooltip + if falloffRangeMult is not None and optimalRangeMult is not None and trackingSpeedMult is not None: + falloffRangeBonus = (falloffRangeMult - 1) * 100 + optimalRangeBonus = (optimalRangeMult - 1) * 100 + trackingSpeedBonus = (trackingSpeedMult - 1) * 100 + display = 0 + for bonus in (falloffRangeBonus, optimalRangeBonus, trackingSpeedBonus): + if abs(bonus) > abs(display): + display = bonus + if not display: + return "", None + text = "{0}%".format(formatAmount(display, 3, 0, 3), forceSign=True) + ttEntries = [] + if display == optimalRangeBonus: + ttEntries.append("optimal range") + if display == falloffRangeBonus: + ttEntries.append("falloff range") + if display == trackingSpeedBonus: + ttEntries.append("tracking speed") + tooltip = "{0} disruption".format(formatList(ttEntries)).capitalize() + return text, tooltip + if grav is not None and ladar is not None and radar is not None and magnet is not None: + display = max(grav, ladar, radar, magnet) + if not display: + return "", None + text = "{0}".format(formatAmount(display, 3, 0, 3)) + ttEntries = [] + if display == grav: + ttEntries.append("gravimetric") + if display == ladar: + ttEntries.append("ladar") + if display == magnet: + ttEntries.append("magnetometric") + if display == radar: + ttEntries.append("radar") + plu = "" if len(ttEntries) == 1 else "s" + tooltip = "{0} strength{1}".format(formatList(ttEntries), plu).capitalize() + return text, tooltip + else: + return "", None + elif itemGroup == "Fighter Bomber": + optimalSig = stuff.getModifiedItemAttr("optimalSigRadius") + if not optimalSig: + return "", None + text = "{0}m".format(formatAmount(optimalSig, 3, 0, 3)) + tooltip = "Optimal signature radius" + return text, tooltip + elif itemGroup in ("Frequency Mining Laser", "Strip Miner", "Mining Laser", "Gas Cloud Harvester", "Mining Drone"): + miningAmount = stuff.getModifiedItemAttr("specialtyMiningAmount") or stuff.getModifiedItemAttr("miningAmount") + cycleTime = getattr(stuff, 'cycleTime', stuff.getModifiedItemAttr("duration")) + if not miningAmount or not cycleTime: + return "", None + minePerSec = (float(miningAmount) * 1000 / cycleTime) + text = "{0} m3/s".format(formatAmount(minePerSec, 3, 0, 3)) + tooltip = "Mining Yield per second ({0} per hour)".format(formatAmount(minePerSec * 3600, 3, 0, 3)) + return text, tooltip + elif itemGroup == "Logistic Drone": + armorAmount = stuff.getModifiedItemAttr("armorDamageAmount") + shieldAmount = stuff.getModifiedItemAttr("shieldBonus") + hullAmount = stuff.getModifiedItemAttr("structureDamageAmount") + repAmount = armorAmount or shieldAmount or hullAmount + cycleTime = stuff.getModifiedItemAttr("duration") + if not repAmount or not cycleTime: + return "", None + repPerSec = float(repAmount) * 1000 / cycleTime + text = "{0}/s".format(formatAmount(repPerSec, 3, 0, 3)) + ttEntries = [] + if hullAmount is not None and repAmount == hullAmount: + ttEntries.append("structure") + if armorAmount is not None and repAmount == armorAmount: + ttEntries.append("armor") + if shieldAmount is not None and repAmount == shieldAmount: + ttEntries.append("shield") + tooltip = "{0} repaired per second".format(formatList(ttEntries)).capitalize() + return text, tooltip + elif itemGroup == "Energy Neutralizer Drone": + neutAmount = stuff.getModifiedItemAttr("energyNeutralizerAmount") + cycleTime = stuff.getModifiedItemAttr("energyNeutralizerDuration") + if not neutAmount or not cycleTime: + return "", None + capPerSec = float(-neutAmount) * 1000 / cycleTime + text = "{0}/s".format(formatAmount(capPerSec, 3, 0, 3)) + tooltip = "Energy neutralization per second" + return text, tooltip + elif itemGroup == "Micro Jump Drive": + cycleTime = stuff.getModifiedItemAttr("duration") / 1000 + text = "{0}s".format(cycleTime) + tooltip = "Spoolup time" + return text, tooltip + elif itemGroup in ("Siege Module", "Cynosural Field"): + amt = stuff.getModifiedItemAttr("consumptionQuantity") + if amt: + typeID = stuff.getModifiedItemAttr("consumptionType") + item = Market.getInstance().getItem(typeID) + text = "{0} units".format(formatAmount(amt, 3, 0, 3)) + return text, item.name + else: + return "", None + elif itemGroup in ( + "Ancillary Armor Repairer", + "Ancillary Shield Booster", + "Capacitor Booster", + "Ancillary Remote Armor Repairer", + "Ancillary Remote Shield Booster", + ): + if "Armor" in itemGroup or "Shield" in itemGroup: + boosted_attribute = "HP" + reload_time = item.getAttribute("reloadTime", 0) / 1000 + elif "Capacitor" in itemGroup: + boosted_attribute = "Cap" + reload_time = 10 + else: + boosted_attribute = "" + reload_time = 0 + + cycles = max(stuff.numShots, 0) + cycleTime = max(stuff.rawCycleTime, 0) + + # Get HP or boosted amount + stuff_hp = max(stuff.hpBeforeReload, 0) + armor_hp = stuff.getModifiedItemAttr("armorDamageAmount", 0) + capacitor_hp = stuff.getModifiedChargeAttr("capacitorBonus", 0) + shield_hp = stuff.getModifiedItemAttr("shieldBonus", 0) + hp = max(stuff_hp, armor_hp * cycles, capacitor_hp * cycles, shield_hp * cycles, 0) + + if not hp or not cycleTime or not cycles: + return "", None + + fit = Fit.getInstance().getFit(self.mainFrame.getActiveFit()) + ehpTotal = fit.ehp + hpTotal = fit.hp + useEhp = self.mainFrame.statsPane.nameViewMap["resistancesViewFull"].showEffective + tooltip = "{0} restored over duration using charges (plus reload)".format(boosted_attribute) + + if useEhp and boosted_attribute == "HP" and "Remote" not in itemGroup: + if "Ancillary Armor Repairer" in itemGroup: + hpRatio = ehpTotal["armor"] / hpTotal["armor"] + else: + hpRatio = ehpTotal["shield"] / hpTotal["shield"] + tooltip = "E{0}".format(tooltip) + else: + hpRatio = 1 + + if "Ancillary" in itemGroup and "Armor" in itemGroup: + hpRatio *= stuff.getModifiedItemAttr("chargedArmorDamageMultiplier", 1) + + ehp = hp * hpRatio + + duration = cycles * cycleTime / 1000 + for number_of_cycles in {5, 10, 25}: + tooltip = "{0}\n{1} charges lasts {2} seconds ({3} cycles)".format( + tooltip, + formatAmount(number_of_cycles * cycles, 3, 0, 3), + formatAmount((duration + reload_time) * number_of_cycles, 3, 0, 3), + formatAmount(number_of_cycles, 3, 0, 3) + ) + text = "{0} / {1}s (+{2}s)".format( + formatAmount(ehp, 3, 0, 9), + formatAmount(duration, 3, 0, 3), + formatAmount(reload_time, 3, 0, 3) + ) + + return text, tooltip + elif itemGroup == "Armor Resistance Shift Hardener": + itemArmorResistanceShiftHardenerEM = (1 - stuff.getModifiedItemAttr("armorEmDamageResonance")) * 100 + itemArmorResistanceShiftHardenerTherm = (1 - stuff.getModifiedItemAttr("armorThermalDamageResonance")) * 100 + itemArmorResistanceShiftHardenerKin = (1 - stuff.getModifiedItemAttr("armorKineticDamageResonance")) * 100 + itemArmorResistanceShiftHardenerExp = (1 - stuff.getModifiedItemAttr("armorExplosiveDamageResonance")) * 100 + + text = "{0}% | {1}% | {2}% | {3}%".format( + formatAmount(itemArmorResistanceShiftHardenerEM, 3, 0, 3), + formatAmount(itemArmorResistanceShiftHardenerTherm, 3, 0, 3), + formatAmount(itemArmorResistanceShiftHardenerKin, 3, 0, 3), + formatAmount(itemArmorResistanceShiftHardenerExp, 3, 0, 3), + ) + tooltip = "Resistances Shifted to Damage Profile:\n{0}% EM | {1}% Therm | {2}% Kin | {3}% Exp".format( + formatAmount(itemArmorResistanceShiftHardenerEM, 3, 0, 3), + formatAmount(itemArmorResistanceShiftHardenerTherm, 3, 0, 3), + formatAmount(itemArmorResistanceShiftHardenerKin, 3, 0, 3), + formatAmount(itemArmorResistanceShiftHardenerExp, 3, 0, 3), + ) + return text, tooltip + elif stuff.charge is not None: + chargeGroup = stuff.charge.group.name + if chargeGroup in ( + "Rocket", + "Advanced Rocket", + "Light Missile", + "Advanced Light Missile", + "FoF Light Missile", + "Heavy Assault Missile", + "Advanced Heavy Assault Missile", + "Heavy Missile", + "Advanced Heavy Missile", + "FoF Heavy Missile", + "Torpedo", + "Advanced Torpedo", + "Cruise Missile", + "Advanced Cruise Missile", + "FoF Cruise Missile", + "XL Torpedo", + "XL Cruise Missile" + ): + cloudSize = stuff.getModifiedChargeAttr("aoeCloudSize") + aoeVelocity = stuff.getModifiedChargeAttr("aoeVelocity") + if not cloudSize or not aoeVelocity: + return "", None + text = "{0}{1} | {2}{3}".format(formatAmount(cloudSize, 3, 0, 3), "m", + formatAmount(aoeVelocity, 3, 0, 3), "m/s") + tooltip = "Explosion radius and explosion velocity" + return text, tooltip + elif chargeGroup == "Bomb": + cloudSize = stuff.getModifiedChargeAttr("aoeCloudSize") + if not cloudSize: + return "", None + text = "{0}{1}".format(formatAmount(cloudSize, 3, 0, 3), "m") + tooltip = "Explosion radius" + return text, tooltip + elif chargeGroup in ("Scanner Probe",): + scanStr = stuff.getModifiedChargeAttr("baseSensorStrength") + baseRange = stuff.getModifiedChargeAttr("baseScanRange") + if not scanStr or not baseRange: + return "", None + strTwoAu = scanStr / (2.0 / baseRange) + text = "{0}".format(formatAmount(strTwoAu, 3, 0, 3)) + tooltip = "Scan strength with 2 AU scan range" + return text, tooltip + else: + return "", None + else: + return "", None + + +Miscellanea.register() diff --git a/gui/builtinViewColumns/price.py b/gui/builtinViewColumns/price.py index 74d2fc890..194aa594d 100644 --- a/gui/builtinViewColumns/price.py +++ b/gui/builtinViewColumns/price.py @@ -1,4 +1,4 @@ -#=============================================================================== +# ============================================================================= # Copyright (C) 2010 Diego Duclos, Lucas Thode # # This file is part of pyfa. @@ -15,17 +15,22 @@ # # You should have received a copy of the GNU General Public License # along with pyfa. If not, see . -#=============================================================================== +# ============================================================================= +# noinspection PyPackageRequirements +import wx + +from eos.saveddata.cargo import Cargo +from eos.saveddata.drone import Drone +from service.market import Market from gui.viewColumn import ViewColumn from gui.bitmapLoader import BitmapLoader from gui.utils.numberFormatter import formatAmount -from eos.types import Drone, Cargo -import wx -import service + class Price(ViewColumn): name = "Price" + def __init__(self, fittingView, params): ViewColumn.__init__(self, fittingView) self.mask = wx.LIST_MASK_IMAGE @@ -36,7 +41,7 @@ class Price(ViewColumn): if stuff.item is None or stuff.item.group.name == "Ship Modifiers": return "" - sMkt = service.Market.getInstance() + sMkt = Market.getInstance() price = sMkt.getPriceNow(stuff.item.ID) if not price or not price.price or not price.isValid: @@ -45,21 +50,22 @@ class Price(ViewColumn): price = price.price # Set new price variable with what we need if isinstance(stuff, Drone) or isinstance(stuff, Cargo): - price *= stuff.amount + price *= stuff.amount return formatAmount(price, 3, 3, 9, currency=True) def delayedText(self, mod, display, colItem): - sMkt = service.Market.getInstance() + sMkt = Market.getInstance() + def callback(item): price = sMkt.getPriceNow(item.ID) text = formatAmount(price.price, 3, 3, 9, currency=True) if price.price else "" - if price.failed: text += " (!)" + if price.failed: + text += " (!)" colItem.SetText(text) display.SetItem(colItem) - sMkt.waitForPrice(mod.item, callback) def getImageId(self, mod): @@ -68,4 +74,5 @@ class Price(ViewColumn): def getToolTip(self, mod): return self.name + Price.register() diff --git a/gui/builtinViewColumns/propertyDisplay.py b/gui/builtinViewColumns/propertyDisplay.py index 0bfee5899..b4faa177a 100644 --- a/gui/builtinViewColumns/propertyDisplay.py +++ b/gui/builtinViewColumns/propertyDisplay.py @@ -1,70 +1,71 @@ -#=============================================================================== -# Copyright (C) 2010 Diego Duclos -# -# This file is part of pyfa. -# -# pyfa is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# pyfa is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with pyfa. If not, see . -#=============================================================================== - -from gui.viewColumn import ViewColumn -from gui.bitmapLoader import BitmapLoader -from gui.utils.numberFormatter import formatAmount -import wx -import service - -class PropertyDisplay(ViewColumn): - name = "prop" - def __init__(self, fittingView, params): - ViewColumn.__init__(self, fittingView) - sAttr = service.Attribute.getInstance() - attributeSlave = params["attributeSlave"] or params["property"] - # This function can throw an exception if the database isn't sane - # We need to do a sanity check before this runs - info = sAttr.getAttributeInfo(attributeSlave) - - self.mask = 0 - self.propertyName = params["property"] - self.info = info - if params["showIcon"]: - if info.name == "power": - iconFile = "pg_small" - iconType = "gui" - else: - iconFile = info.icon.iconFile if info.icon else None - iconType = "icons" - if iconFile: - self.imageId = fittingView.imageList.GetImageIndex(iconFile, iconType) - else: - self.imageId = -1 - else: - self.imageId = -1 - - if params["displayName"] or self.imageId == -1: - self.columnText = info.displayName if info.displayName != "" else info.name - - def getText(self, stuff): - attr = getattr(stuff, self.propertyName, None) - if attr: - return (formatAmount(attr, 3, 0, 3)) - else: - return "" - - @staticmethod - def getParameters(): - return (("property", str, None), - ("attributeSlave", str, None), - ("displayName", bool, False), - ("showIcon", bool, True)) - -PropertyDisplay.register() +# ============================================================================= +# Copyright (C) 2010 Diego Duclos +# +# This file is part of pyfa. +# +# pyfa is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# pyfa is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with pyfa. If not, see . +# ============================================================================= + +from gui.viewColumn import ViewColumn +from gui.utils.numberFormatter import formatAmount +from service.attribute import Attribute + + +class PropertyDisplay(ViewColumn): + name = "prop" + + def __init__(self, fittingView, params): + ViewColumn.__init__(self, fittingView) + sAttr = Attribute.getInstance() + attributeSlave = params["attributeSlave"] or params["property"] + # This function can throw an exception if the database isn't sane + # We need to do a sanity check before this runs + info = sAttr.getAttributeInfo(attributeSlave) + + self.mask = 0 + self.propertyName = params["property"] + self.info = info + if params["showIcon"]: + if info.name == "power": + iconFile = "pg_small" + iconType = "gui" + else: + iconFile = info.icon.iconFile if info.icon else None + iconType = "icons" + if iconFile: + self.imageId = fittingView.imageList.GetImageIndex(iconFile, iconType) + else: + self.imageId = -1 + else: + self.imageId = -1 + + if params["displayName"] or self.imageId == -1: + self.columnText = info.displayName if info.displayName != "" else info.name + + def getText(self, stuff): + attr = getattr(stuff, self.propertyName, None) + if attr: + return formatAmount(attr, 3, 0, 3) + else: + return "" + + @staticmethod + def getParameters(): + return (("property", str, None), + ("attributeSlave", str, None), + ("displayName", bool, False), + ("showIcon", bool, True)) + + +PropertyDisplay.register() diff --git a/gui/builtinViewColumns/state.py b/gui/builtinViewColumns/state.py index cc3dee59b..aa3f79ebe 100644 --- a/gui/builtinViewColumns/state.py +++ b/gui/builtinViewColumns/state.py @@ -1,4 +1,4 @@ -#=============================================================================== +# ============================================================================= # Copyright (C) 2010 Diego Duclos # # This file is part of pyfa. @@ -15,18 +15,24 @@ # # You should have received a copy of the GNU General Public License # along with pyfa. If not, see . -#=============================================================================== +# ============================================================================= + +# noinspection PyPackageRequirements +import wx + +from eos.saveddata.fit import Fit +from eos.saveddata.implant import Implant +from eos.saveddata.drone import Drone +from eos.saveddata.module import Module, State as State_, Rack from gui.viewColumn import ViewColumn -from gui.bitmapLoader import BitmapLoader + import gui.mainFrame -import wx -from eos.types import Drone, Module, Rack, Fit, Implant -from eos.types import State as State_ class State(ViewColumn): name = "State" + def __init__(self, fittingView, params): ViewColumn.__init__(self, fittingView) self.mainFrame = gui.mainFrame.MainFrame.getInstance() @@ -44,7 +50,8 @@ class State(ViewColumn): def getImageId(self, stuff): generic_active = self.fittingView.imageList.GetImageIndex("state_%s_small" % State_.getName(1).lower(), "gui") - generic_inactive = self.fittingView.imageList.GetImageIndex("state_%s_small" % State_.getName(-1).lower(), "gui") + generic_inactive = self.fittingView.imageList.GetImageIndex("state_%s_small" % State_.getName(-1).lower(), + "gui") if isinstance(stuff, Drone): if stuff.amountActive > 0: @@ -57,7 +64,8 @@ class State(ViewColumn): if stuff.isEmpty: return -1 else: - return self.fittingView.imageList.GetImageIndex("state_%s_small" % State_.getName(stuff.state).lower(), "gui") + return self.fittingView.imageList.GetImageIndex("state_%s_small" % State_.getName(stuff.state).lower(), + "gui") elif isinstance(stuff, Fit): fitID = self.mainFrame.getActiveFit() projectionInfo = stuff.getProjectionInfo(fitID) @@ -78,4 +86,5 @@ class State(ViewColumn): return generic_active return generic_inactive + State.register() diff --git a/gui/builtinViews/__init__.py b/gui/builtinViews/__init__.py index d51fdbad6..0537a1e16 100644 --- a/gui/builtinViews/__init__.py +++ b/gui/builtinViews/__init__.py @@ -1 +1 @@ -__all__ = ["fittingView", "implantEditor"] +__all__ = ["fittingView", "implantEditor"] diff --git a/gui/builtinViews/emptyView.py b/gui/builtinViews/emptyView.py index 2ea3dbeb1..ebf667726 100644 --- a/gui/builtinViews/emptyView.py +++ b/gui/builtinViews/emptyView.py @@ -1,8 +1,9 @@ +# noinspection PyPackageRequirements import wx import gui.globalEvents as GE -import gui.chromeTabs +from gui.chromeTabs import EVT_NOTEBOOK_PAGE_CHANGED import gui.mainFrame -import service + class BlankPage(wx.Panel): def __init__(self, parent): @@ -11,20 +12,20 @@ class BlankPage(wx.Panel): self.mainFrame = gui.mainFrame.MainFrame.getInstance() self.parent = parent - self.parent.Bind(gui.chromeTabs.EVT_NOTEBOOK_PAGE_CHANGED, self.pageChanged) + self.parent.Bind(EVT_NOTEBOOK_PAGE_CHANGED, self.pageChanged) self.SetBackgroundColour(wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOW)) wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=None)) def Destroy(self): - self.parent.Unbind(gui.chromeTabs.EVT_NOTEBOOK_PAGE_CHANGED, handler=self.pageChanged) + self.parent.Unbind(EVT_NOTEBOOK_PAGE_CHANGED, handler=self.pageChanged) wx.Panel.Destroy(self) def pageChanged(self, event): if self.parent.IsActive(self): fitID = None -# sFit = service.Fit.getInstance() -# sFit.switchFit(fitID) + # sFit = Fit.getInstance() + # sFit.switchFit(fitID) wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=fitID)) - event.Skip() \ No newline at end of file + event.Skip() diff --git a/gui/builtinViews/entityEditor.py b/gui/builtinViews/entityEditor.py index b62060357..ecbe065c6 100644 --- a/gui/builtinViews/entityEditor.py +++ b/gui/builtinViews/entityEditor.py @@ -1,6 +1,7 @@ +# noinspection PyPackageRequirements import wx from gui.bitmapLoader import BitmapLoader -import service + class BaseValidator(wx.PyValidator): def __init__(self): @@ -15,6 +16,7 @@ class BaseValidator(wx.PyValidator): def TransferFromWindow(self): return True + class TextEntryValidatedDialog(wx.TextEntryDialog): def __init__(self, parent, validator=None, *args, **kargs): wx.TextEntryDialog.__init__(self, parent, *args, **kargs) @@ -24,7 +26,8 @@ class TextEntryValidatedDialog(wx.TextEntryDialog): if validator: self.txtctrl.SetValidator(validator()) -class EntityEditor (wx.Panel): + +class EntityEditor(wx.Panel): """ Entity Editor is a panel that takes some sort of list as a source and populates a drop down with options to add/ rename/clone/delete an entity. Comes with dialogs that take user input. Classes that derive this class must override @@ -52,7 +55,7 @@ class EntityEditor (wx.Panel): bitmap = wx.ArtProvider.GetBitmap(art, wx.ART_BUTTON) if name != "rename" else art btn = wx.BitmapButton(self, wx.ID_ANY, bitmap) if size is None: - size = btn.GetSize() + size = btn.GetSize() btn.SetMinSize(size) btn.SetMaxSize(size) @@ -135,8 +138,9 @@ class EntityEditor (wx.Panel): def OnDelete(self, event): dlg = wx.MessageDialog(self, - "Do you really want to delete the {} {}?".format(self.getActiveEntity().name, self.entityName), - "Confirm Delete", wx.YES | wx.NO | wx.ICON_QUESTION) + "Do you really want to delete the {} {}?".format(self.getActiveEntity().name, + self.entityName), + "Confirm Delete", wx.YES | wx.NO | wx.ICON_QUESTION) dlg.CenterOnParent() if dlg.ShowModal() == wx.ID_YES: @@ -171,4 +175,4 @@ class EntityEditor (wx.Panel): return False self.Parent.Show() - return True \ No newline at end of file + return True diff --git a/gui/builtinViews/fittingView.py b/gui/builtinViews/fittingView.py index 597687d2d..6555c6ce5 100644 --- a/gui/builtinViews/fittingView.py +++ b/gui/builtinViews/fittingView.py @@ -1,4 +1,4 @@ -#=============================================================================== +# ============================================================================= # Copyright (C) 2010 Diego Duclos # # This file is part of pyfa. @@ -15,29 +15,36 @@ # # You should have received a copy of the GNU General Public License # along with pyfa. If not, see . -#=============================================================================== +# ============================================================================= +# noinspection PyPackageRequirements import wx +# noinspection PyPackageRequirements import wx.lib.newevent -import service import gui.mainFrame import gui.marketBrowser import gui.display as d from gui.contextMenu import ContextMenu import gui.shipBrowser import gui.multiSwitch -from eos.types import Slot, Rack, Module, Mode +from eos.saveddata.mode import Mode +from eos.saveddata.module import Module, Slot, Rack from gui.builtinViewColumns.state import State from gui.bitmapLoader import BitmapLoader import gui.builtinViews.emptyView from gui.utils.exportHtml import exportHtml -from logging import getLogger, Formatter +from logbook import Logger +from gui.chromeTabs import EVT_NOTEBOOK_PAGE_CHANGED + +from service.fit import Fit +from service.market import Market import gui.globalEvents as GE -logger = getLogger(__name__) +pyfalog = Logger(__name__) -#Tab spawning handler + +# Tab spawning handler class FitSpawner(gui.multiSwitch.TabSpawner): def __init__(self, multiSwitch): self.multiSwitch = multiSwitch @@ -54,11 +61,12 @@ class FitSpawner(gui.multiSwitch.TabSpawner): self.multiSwitch.SetSelection(index) wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=event.fitID)) break - except: - pass + except Exception as e: + pyfalog.critical("Caught exception in fitSelected") + pyfalog.critical(e) if count < 0: startup = getattr(event, "startup", False) # see OpenFitsThread in gui.mainFrame - sFit = service.Fit.getInstance() + sFit = Fit.getInstance() openFitInNew = sFit.serviceFittingOptions["openFitInNew"] mstate = wx.GetMouseState() @@ -87,22 +95,25 @@ class FitSpawner(gui.multiSwitch.TabSpawner): self.multiSwitch.AddPage(view) view.handleDrag(type, fitID) + FitSpawner.register() -#Drag'n'drop handler -class FittingViewDrop(wx.PyDropTarget): - def __init__(self, dropFn): - wx.PyDropTarget.__init__(self) - self.dropFn = dropFn - # this is really transferring an EVE itemID - self.dropData = wx.PyTextDataObject() - self.SetDataObject(self.dropData) - def OnData(self, x, y, t): - if self.GetData(): - data = self.dropData.GetText().split(':') - self.dropFn(x, y, data) - return t +# Drag'n'drop handler +class FittingViewDrop(wx.PyDropTarget): + def __init__(self, dropFn, *args, **kwargs): + super(FittingViewDrop, self).__init__(*args, **kwargs) + self.dropFn = dropFn + # this is really transferring an EVE itemID + self.dropData = wx.PyTextDataObject() + self.SetDataObject(self.dropData) + + def OnData(self, x, y, t): + if self.GetData(): + data = self.dropData.GetText().split(':') + self.dropFn(x, y, data) + return t + class FittingView(d.Display): DEFAULT_COLS = ["State", @@ -119,7 +130,7 @@ class FittingView(d.Display): ] def __init__(self, parent): - d.Display.__init__(self, parent, size = (0,0), style = wx.BORDER_NONE) + d.Display.__init__(self, parent, size=(0, 0), style=wx.BORDER_NONE) self.Show(False) self.parent = parent self.mainFrame.Bind(GE.FIT_CHANGED, self.fitChanged) @@ -129,7 +140,7 @@ class FittingView(d.Display): self.Bind(wx.EVT_LEFT_DCLICK, self.removeItem) self.Bind(wx.EVT_LIST_BEGIN_DRAG, self.startDrag) - if "__WXGTK__" in wx.PlatformInfo: + if "__WXGTK__" in wx.PlatformInfo: self.Bind(wx.EVT_RIGHT_UP, self.scheduleMenu) else: self.Bind(wx.EVT_RIGHT_DOWN, self.scheduleMenu) @@ -150,7 +161,7 @@ class FittingView(d.Display): self.Bind(wx.EVT_SHOW, self.OnShow) self.Bind(wx.EVT_MOTION, self.OnMouseMove) self.Bind(wx.EVT_LEAVE_WINDOW, self.OnLeaveWindow) - self.parent.Bind(gui.chromeTabs.EVT_NOTEBOOK_PAGE_CHANGED, self.pageChanged) + self.parent.Bind(EVT_NOTEBOOK_PAGE_CHANGED, self.pageChanged) def OnLeaveWindow(self, event): self.SetToolTip(None) @@ -178,13 +189,13 @@ class FittingView(d.Display): event.Skip() def handleListDrag(self, x, y, data): - ''' + """ Handles dragging of items from various pyfa displays which support it data is list with two items: data[0] is hard-coded str of originating source data[1] is typeID or index of data we want to manipulate - ''' + """ if data[0] == "fitting": self.swapItems(x, y, int(data[1])) @@ -194,12 +205,12 @@ class FittingView(d.Display): self.addModule(x, y, int(data[1])) def handleDrag(self, type, fitID): - #Those are drags coming from pyfa sources, NOT builtin wx drags + # Those are drags coming from pyfa sources, NOT builtin wx drags if type == "fit": wx.PostEvent(self.mainFrame, gui.shipBrowser.FitSelected(fitID=fitID)) def Destroy(self): - self.parent.Unbind(gui.chromeTabs.EVT_NOTEBOOK_PAGE_CHANGED, handler=self.pageChanged) + self.parent.Unbind(EVT_NOTEBOOK_PAGE_CHANGED, handler=self.pageChanged) self.mainFrame.Unbind(GE.FIT_CHANGED, handler=self.fitChanged) self.mainFrame.Unbind(gui.shipBrowser.EVT_FIT_RENAMED, handler=self.fitRenamed) self.mainFrame.Unbind(gui.shipBrowser.EVT_FIT_REMOVED, handler=self.fitRemoved) @@ -210,7 +221,7 @@ class FittingView(d.Display): def pageChanged(self, event): if self.parent.IsActive(self): fitID = self.getActiveFit() - sFit = service.Fit.getInstance() + sFit = Fit.getInstance() sFit.switchFit(fitID) wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=fitID)) @@ -224,11 +235,11 @@ class FittingView(d.Display): if row != -1 and row not in self.blanks: data = wx.PyTextDataObject() - data.SetText("fitting:"+str(self.mods[row].position)) + data.SetText("fitting:" + str(self.mods[row].modPosition)) dropSource = wx.DropSource(self) dropSource.SetData(data) - res = dropSource.DoDragDrop() + dropSource.DoDragDrop() def getSelectedMods(self): sel = [] @@ -239,25 +250,24 @@ class FittingView(d.Display): return sel - def kbEvent(self,event): + def kbEvent(self, event): keycode = event.GetKeyCode() if keycode == wx.WXK_DELETE or keycode == wx.WXK_NUMPAD_DELETE: row = self.GetFirstSelected() - firstSel = row while row != -1: if row not in self.blanks: self.removeModule(self.mods[row]) - self.Select(row,0) + self.Select(row, 0) row = self.GetNextSelected(row) event.Skip() def fitRemoved(self, event): - ''' + """ If fit is removed and active, the page is deleted. We also refresh the fit of the new current page in case delete fit caused change in stats (projected) - ''' + """ fitID = event.fitID if fitID == self.getActiveFit(): @@ -265,10 +275,11 @@ class FittingView(d.Display): try: # Sometimes there is no active page after deletion, hence the try block - sFit = service.Fit.getInstance() + sFit = Fit.getInstance() sFit.refreshFit(self.getActiveFit()) wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.activeFitID)) except wx._core.PyDeadObjectError: + pyfalog.warning("Caught dead object") pass event.Skip() @@ -285,7 +296,7 @@ class FittingView(d.Display): fitID = event.fitID startup = getattr(event, "startup", False) self.activeFitID = fitID - sFit = service.Fit.getInstance() + sFit = Fit.getInstance() self.updateTab() if not startup or startup == 2: # see OpenFitsThread in gui.mainFrame self.Show(fitID is not None) @@ -296,7 +307,7 @@ class FittingView(d.Display): event.Skip() def updateTab(self): - sFit = service.Fit.getInstance() + sFit = Fit.getInstance() fit = sFit.getFit(self.getActiveFit(), basic=True) bitmap = BitmapLoader.getImage("race_%s_small" % fit.ship.item.race, "gui") @@ -310,8 +321,8 @@ class FittingView(d.Display): if self.parent.IsActive(self): itemID = event.itemID fitID = self.activeFitID - if fitID != None: - sFit = service.Fit.getInstance() + if fitID is not None: + sFit = Fit.getInstance() if sFit.isAmmo(itemID): modules = [] sel = self.GetFirstSelected() @@ -340,7 +351,7 @@ class FittingView(d.Display): self.click(event) def removeModule(self, module): - sFit = service.Fit.getInstance() + sFit = Fit.getInstance() fit = sFit.getFit(self.activeFitID) populate = sFit.removeModule(self.activeFitID, fit.modules.index(module)) @@ -349,36 +360,36 @@ class FittingView(d.Display): wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.activeFitID)) def addModule(self, x, y, srcIdx): - '''Add a module from the market browser''' - mstate = wx.GetMouseState() + """Add a module from the market browser""" dstRow, _ = self.HitTest((x, y)) if dstRow != -1 and dstRow not in self.blanks: - sFit = service.Fit.getInstance() + sFit = Fit.getInstance() fitID = self.mainFrame.getActiveFit() - moduleChanged = sFit.changeModule(fitID, self.mods[dstRow].position, srcIdx) + moduleChanged = sFit.changeModule(fitID, self.mods[dstRow].modPosition, srcIdx) if moduleChanged is None: # the new module doesn't fit in specified slot, try to simply append it wx.PostEvent(self.mainFrame, gui.marketBrowser.ItemSelected(itemID=srcIdx)) wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.mainFrame.getActiveFit())) def swapCargo(self, x, y, srcIdx): - '''Swap a module from cargo to fitting window''' + """Swap a module from cargo to fitting window""" mstate = wx.GetMouseState() dstRow, _ = self.HitTest((x, y)) if dstRow != -1 and dstRow not in self.blanks: module = self.mods[dstRow] - sFit = service.Fit.getInstance() - sFit.moveCargoToModule(self.mainFrame.getActiveFit(), module.position, srcIdx, mstate.CmdDown() and module.isEmpty) + sFit = Fit.getInstance() + sFit.moveCargoToModule(self.mainFrame.getActiveFit(), module.modPosition, srcIdx, + mstate.CmdDown() and module.isEmpty) wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.mainFrame.getActiveFit())) def swapItems(self, x, y, srcIdx): - '''Swap two modules in fitting window''' + """Swap two modules in fitting window""" mstate = wx.GetMouseState() - sFit = service.Fit.getInstance() + sFit = Fit.getInstance() fit = sFit.getFit(self.activeFitID) if mstate.CmdDown(): @@ -389,6 +400,7 @@ class FittingView(d.Display): dstRow, _ = self.HitTest((x, y)) if dstRow != -1 and dstRow not in self.blanks: + mod1 = fit.modules[srcIdx] mod2 = self.mods[dstRow] @@ -396,35 +408,38 @@ class FittingView(d.Display): if mod1.slot != mod2.slot: return - if clone and mod2.isEmpty: - sFit.cloneModule(self.mainFrame.getActiveFit(), mod1.position, mod2.position) - else: - sFit.swapModules(self.mainFrame.getActiveFit(), mod1.position, mod2.position) + if getattr(mod2, "modPosition"): + if clone and mod2.isEmpty: + sFit.cloneModule(self.mainFrame.getActiveFit(), srcIdx, mod2.modPosition) + else: + sFit.swapModules(self.mainFrame.getActiveFit(), srcIdx, mod2.modPosition) - wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.mainFrame.getActiveFit())) + wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.mainFrame.getActiveFit())) + else: + pyfalog.error("Missing module position for: {0}", str(getattr(mod2, "ID", "Unknown"))) def generateMods(self): - ''' + """ Generate module list. This also injects dummy modules to visually separate racks. These modules are only known to the display, and not the backend, so it's safe. - ''' + """ - sFit = service.Fit.getInstance() + sFit = Fit.getInstance() fit = sFit.getFit(self.activeFitID) slotOrder = [Slot.SUBSYSTEM, Slot.HIGH, Slot.MED, Slot.LOW, Slot.RIG, Slot.SERVICE] if fit is not None: self.mods = fit.modules[:] - self.mods.sort(key=lambda mod: (slotOrder.index(mod.slot), mod.position)) + self.mods.sort(key=lambda _mod: (slotOrder.index(_mod.slot), _mod.position)) # Blanks is a list of indexes that mark non-module positions (such # as Racks and tactical Modes. This allows us to skip over common # module operations such as swapping, removing, copying, etc. that # would otherwise cause complications - self.blanks = [] # preliminary markers where blanks will be inserted + self.blanks = [] # preliminary markers where blanks will be inserted if sFit.serviceFittingOptions["rackSlots"]: # flag to know when to add blanks, based on previous slot @@ -434,12 +449,12 @@ class FittingView(d.Display): for i, mod in enumerate(self.mods): if mod.slot != slotDivider: slotDivider = mod.slot - self.blanks.append((i, slotDivider)) # where and what + self.blanks.append((i, slotDivider)) # where and what # second loop modifies self.mods, rewrites self.blanks to represent actual index of blanks for i, (x, slot) in enumerate(self.blanks): - self.blanks[i] = x+i # modify blanks with actual index - self.mods.insert(x+i, Rack.buildRack(slot)) + self.blanks[i] = x + i # modify blanks with actual index + self.mods.insert(x + i, Rack.buildRack(slot)) if fit.mode: # Modes are special snowflakes and need a little manual loving @@ -470,7 +485,7 @@ class FittingView(d.Display): self.Show(self.activeFitID is not None and self.activeFitID == event.fitID) except wx._core.PyDeadObjectError: - pass + pyfalog.warning("Caught dead object") finally: event.Skip() @@ -483,7 +498,7 @@ class FittingView(d.Display): if self.activeFitID is None: return - sMkt = service.Market.getInstance() + sMkt = Market.getInstance() selection = [] sel = self.GetFirstSelected() contexts = [] @@ -497,7 +512,7 @@ class FittingView(d.Display): itemContext = "Tactical Mode" fullContext = (srcContext, itemContext) - if not srcContext in tuple(fCtxt[0] for fCtxt in contexts): + if srcContext not in tuple(fCtxt[0] for fCtxt in contexts): contexts.append(fullContext) selection.append(mod) @@ -506,22 +521,21 @@ class FittingView(d.Display): srcContext = "fittingModule" itemContext = sMkt.getCategoryByItem(mod.item).name fullContext = (srcContext, itemContext) - if not srcContext in tuple(fCtxt[0] for fCtxt in contexts): + if srcContext not in tuple(fCtxt[0] for fCtxt in contexts): contexts.append(fullContext) - if mod.charge is not None: srcContext = "fittingCharge" itemContext = sMkt.getCategoryByItem(mod.charge).name fullContext = (srcContext, itemContext) - if not srcContext in tuple(fCtxt[0] for fCtxt in contexts): + if srcContext not in tuple(fCtxt[0] for fCtxt in contexts): contexts.append(fullContext) selection.append(mod) sel = self.GetNextSelected(sel) - sFit = service.Fit.getInstance() + sFit = Fit.getInstance() fit = sFit.getFit(self.activeFitID) contexts.append(("fittingShip", "Ship" if not fit.isStructure else "Citadel")) @@ -530,13 +544,13 @@ class FittingView(d.Display): self.PopupMenu(menu) def click(self, event): - ''' + """ Handle click event on modules. This is only useful for the State column. If multiple items are selected, and we have clicked the State column, iterate through the selections and change State - ''' + """ row, _, col = self.HitTestSubItem(event.Position) # only do State column and ignore invalid rows @@ -544,7 +558,7 @@ class FittingView(d.Display): sel = [] curr = self.GetFirstSelected() - while curr != -1 and row not in self.blanks : + while curr != -1 and row not in self.blanks: sel.append(curr) curr = self.GetNextSelected(curr) @@ -553,7 +567,7 @@ class FittingView(d.Display): else: mods = self.getSelectedMods() - sFit = service.Fit.getInstance() + sFit = Fit.getInstance() fitID = self.mainFrame.getActiveFit() ctrl = wx.GetMouseState().CmdDown() or wx.GetMouseState().MiddleDown() click = "ctrl" if ctrl is True else "right" if event.GetButton() == 3 else "left" @@ -578,16 +592,16 @@ class FittingView(d.Display): return self.slotColourMap.get(slot) or self.GetBackgroundColour() def refresh(self, stuff): - ''' + """ Displays fitting Sends data to d.Display.refresh where the rows and columns are set up, then does a bit of post-processing (colors) - ''' + """ self.Freeze() d.Display.refresh(self, stuff) - sFit = service.Fit.getInstance() + sFit = Fit.getInstance() fit = sFit.getFit(self.activeFitID) slotMap = {} @@ -624,40 +638,44 @@ class FittingView(d.Display): if 'wxMac' in wx.PlatformInfo: try: self.MakeSnapshot() - except: - pass + except Exception as e: + pyfalog.critical("Failed to make snapshot") + pyfalog.critical(e) def OnShow(self, event): if event.GetShow(): try: self.MakeSnapshot() - except: - pass + except Exception as e: + pyfalog.critical("Failed to make snapshot") + pyfalog.critical(e) event.Skip() def Snapshot(self): return self.FVsnapshot - def MakeSnapshot(self, maxColumns = 1337): + # noinspection PyPropertyAccess + def MakeSnapshot(self, maxColumns=1337): if self.FVsnapshot: del self.FVsnapshot - tbmp = wx.EmptyBitmap(16,16) + tbmp = wx.EmptyBitmap(16, 16) tdc = wx.MemoryDC() tdc.SelectObject(tbmp) font = wx.SystemSettings_GetFont(wx.SYS_DEFAULT_GUI_FONT) tdc.SetFont(font) columnsWidths = [] - for i in xrange(len(self.DEFAULT_COLS)): + for i in range(len(self.DEFAULT_COLS)): columnsWidths.append(0) - sFit = service.Fit.getInstance() + sFit = Fit.getInstance() try: fit = sFit.getFit(self.activeFitID) - except: - return + except Exception as e: + pyfalog.critical("Failed to get fit") + pyfalog.critical(e) if fit is None: return @@ -671,28 +689,27 @@ class FittingView(d.Display): isize = 16 headerSize = max(isize, tdc.GetTextExtent("W")[0]) + padding * 2 - maxWidth = 0 maxRowHeight = isize rows = 0 - for id,st in enumerate(self.mods): + for st in self.mods: for i, col in enumerate(self.activeColumns): - if i>maxColumns: + if i > maxColumns: break name = col.getText(st) if not isinstance(name, basestring): name = "" - nx,ny = tdc.GetTextExtent(name) + nx, ny = tdc.GetTextExtent(name) imgId = col.getImageId(st) cw = 0 if imgId != -1: cw += isize + padding if name != "": - cw += nx + 4*padding + cw += nx + 4 * padding if imgId == -1 and name == "": - cw += isize +padding + cw += isize + padding maxRowHeight = max(ny, maxRowHeight) columnsWidths[i] = max(columnsWidths[i], cw) @@ -701,7 +718,7 @@ class FittingView(d.Display): render = wx.RendererNative.Get() - #Fix column widths (use biggest between header or items) + # Fix column widths (use biggest between header or items) for i, col in enumerate(self.activeColumns): if i > maxColumns: @@ -719,26 +736,23 @@ class FittingView(d.Display): opts.m_labelText = name if imgId != -1: - opts.m_labelBitmap = wx.EmptyBitmap(isize,isize) + opts.m_labelBitmap = wx.EmptyBitmap(isize, isize) - width = render.DrawHeaderButton(self, tdc, (0, 0, 16, 16), - sortArrow = wx.HDR_SORT_ICON_NONE, params = opts) + width = render.DrawHeaderButton(self, tdc, (0, 0, 16, 16), sortArrow=wx.HDR_SORT_ICON_NONE, params=opts) columnsWidths[i] = max(columnsWidths[i], width) tdc.SelectObject(wx.NullBitmap) - maxWidth = padding * 2 - for i in xrange(len(self.DEFAULT_COLS)): + for i in range(len(self.DEFAULT_COLS)): if i > maxColumns: break maxWidth += columnsWidths[i] - mdc = wx.MemoryDC() - mbmp = wx.EmptyBitmap(maxWidth, (maxRowHeight) * rows + padding*4 + headerSize) + mbmp = wx.EmptyBitmap(maxWidth, maxRowHeight * rows + padding * 4 + headerSize) mdc.SelectObject(mbmp) @@ -768,8 +782,7 @@ class FittingView(d.Display): bmp = col.bitmap opts.m_labelBitmap = bmp - width = render.DrawHeaderButton (self, mdc, (cx, padding, columnsWidths[i], headerSize), wx.CONTROL_CURRENT, - sortArrow = wx.HDR_SORT_ICON_NONE, params = opts) + render.DrawHeaderButton(self, mdc, (cx, padding, columnsWidths[i], headerSize), wx.CONTROL_CURRENT, sortArrow=wx.HDR_SORT_ICON_NONE, params=opts) cx += columnsWidths[i] @@ -779,15 +792,15 @@ class FittingView(d.Display): mdc.SetPen(pen) mdc.SetBrush(brush) - cy = padding*2 + headerSize - for id,st in enumerate(self.mods): + cy = padding * 2 + headerSize + for st in self.mods: cx = padding if slotMap[st.slot]: - mdc.DrawRectangle(cx,cy,maxWidth - cx,maxRowHeight) + mdc.DrawRectangle(cx, cy, maxWidth - cx, maxRowHeight) for i, col in enumerate(self.activeColumns): - if i>maxColumns: + if i > maxColumns: break name = col.getText(st) @@ -798,14 +811,14 @@ class FittingView(d.Display): tcx = cx if imgId != -1: - self.imageList.Draw(imgId,mdc,cx,cy,wx.IMAGELIST_DRAW_TRANSPARENT,False) + self.imageList.Draw(imgId, mdc, cx, cy, wx.IMAGELIST_DRAW_TRANSPARENT, False) tcx += isize + padding if name != "": - nx,ny = mdc.GetTextExtent(name) + nx, ny = mdc.GetTextExtent(name) rect = wx.Rect() rect.top = cy - rect.left = cx + 2*padding + rect.left = cx + 2 * padding rect.width = nx rect.height = maxRowHeight + padding mdc.DrawLabel(name, rect, wx.ALIGN_CENTER_VERTICAL) @@ -817,4 +830,4 @@ class FittingView(d.Display): mdc.SelectObject(wx.NullBitmap) - self.FVsnapshot = mbmp \ No newline at end of file + self.FVsnapshot = mbmp diff --git a/gui/builtinViews/implantEditor.py b/gui/builtinViews/implantEditor.py index bcdb96c7b..bb9c125e7 100644 --- a/gui/builtinViews/implantEditor.py +++ b/gui/builtinViews/implantEditor.py @@ -1,12 +1,16 @@ +# noinspection PyPackageRequirements import wx -import service -import gui.display as d -from gui.bitmapLoader import BitmapLoader -import gui.PFSearchBox as SBox -from gui.marketBrowser import SearchBox +# noinspection PyPackageRequirements from wx.lib.buttons import GenBitmapButton -class BaseImplantEditorView (wx.Panel): +from service.market import Market +import gui.display as d +import gui.PFSearchBox as SBox +from gui.bitmapLoader import BitmapLoader +from gui.marketBrowser import SearchBox + + +class BaseImplantEditorView(wx.Panel): def addMarketViewImage(self, iconFile): if iconFile is None: return -1 @@ -17,7 +21,8 @@ class BaseImplantEditorView (wx.Panel): return self.availableImplantsImageList.Add(bitmap) def __init__(self, parent): - wx.Panel.__init__ (self, parent, id=wx.ID_ANY, pos=wx.DefaultPosition, size=wx.DefaultSize, style=wx.TAB_TRAVERSAL) + wx.Panel.__init__(self, parent, id=wx.ID_ANY, pos=wx.DefaultPosition, size=wx.DefaultSize, + style=wx.TAB_TRAVERSAL) self.SetBackgroundColour(wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOW)) pmainSizer = wx.BoxSizer(wx.HORIZONTAL) @@ -46,32 +51,32 @@ class BaseImplantEditorView (wx.Panel): availableSizer.Add(self.availableImplantsTree, 1, wx.EXPAND) - pmainSizer.Add(availableSizer, 1, wx.ALL | wx.EXPAND, 5) - buttonSizer = wx.BoxSizer(wx.VERTICAL) - buttonSizer.AddSpacer(( 0, 0), 1) + buttonSizer.AddSpacer((0, 0), 1) - self.btnAdd = GenBitmapButton(self, wx.ID_ADD, BitmapLoader.getBitmap("fit_add_small", "gui"), style = wx.BORDER_NONE) + self.btnAdd = GenBitmapButton(self, wx.ID_ADD, BitmapLoader.getBitmap("fit_add_small", "gui"), + style=wx.BORDER_NONE) buttonSizer.Add(self.btnAdd, 0) - self.btnRemove = GenBitmapButton(self, wx.ID_REMOVE, BitmapLoader.getBitmap("fit_delete_small", "gui"), style = wx.BORDER_NONE) + self.btnRemove = GenBitmapButton(self, wx.ID_REMOVE, BitmapLoader.getBitmap("fit_delete_small", "gui"), + style=wx.BORDER_NONE) buttonSizer.Add(self.btnRemove, 0) - buttonSizer.AddSpacer(( 0, 0), 1) + buttonSizer.AddSpacer((0, 0), 1) pmainSizer.Add(buttonSizer, 0, wx.EXPAND, 0) characterImplantSizer = wx.BoxSizer(wx.VERTICAL) self.pluggedImplantsTree = AvailableImplantsView(self) - characterImplantSizer.Add(self.pluggedImplantsTree, 1, wx.ALL|wx.EXPAND, 5) + characterImplantSizer.Add(self.pluggedImplantsTree, 1, wx.ALL | wx.EXPAND, 5) pmainSizer.Add(characterImplantSizer, 1, wx.EXPAND, 5) self.SetSizer(pmainSizer) # Populate the market tree - sMkt = service.Market.getInstance() + sMkt = Market.getInstance() for mktGrp in sMkt.getImplantTree(): iconId = self.addMarketViewImage(sMkt.getIconByMarketGroup(mktGrp)) childId = self.availableImplantsTree.AppendItem(root, mktGrp.name, iconId, data=wx.TreeItemData(mktGrp.ID)) @@ -80,13 +85,13 @@ class BaseImplantEditorView (wx.Panel): self.availableImplantsTree.SortChildren(self.availableRoot) - #Bind the event to replace dummies by real data + # Bind the event to replace dummies by real data self.availableImplantsTree.Bind(wx.EVT_TREE_ITEM_EXPANDING, self.expandLookup) self.availableImplantsTree.Bind(wx.EVT_TREE_ITEM_ACTIVATED, self.itemSelected) self.itemView.Bind(wx.EVT_LIST_ITEM_ACTIVATED, self.itemSelected) - #Bind add & remove buttons + # Bind add & remove buttons self.btnAdd.Bind(wx.EVT_BUTTON, self.itemSelected) self.btnRemove.Bind(wx.EVT_BUTTON, self.removeItem) @@ -126,7 +131,7 @@ class BaseImplantEditorView (wx.Panel): def expandLookup(self, event): tree = self.availableImplantsTree - sMkt = service.Market.getInstance() + sMkt = Market.getInstance() parent = event.Item child, _ = tree.GetFirstChild(parent) text = tree.GetItemText(child) @@ -136,7 +141,7 @@ class BaseImplantEditorView (wx.Panel): # if the dummy item is a market group, replace with actual market groups if text == "dummy": - #Add 'real stoof!' instead + # Add 'real stoof!' instead currentMktGrp = sMkt.getMarketGroup(tree.GetPyData(parent), eager="children") for childMktGrp in sMkt.getMarketGroupChildren(currentMktGrp): iconId = self.addMarketViewImage(sMkt.getIconByMarketGroup(childMktGrp)) @@ -194,6 +199,7 @@ class BaseImplantEditorView (wx.Panel): self.removeImplantFromContext(self.implants[pos]) self.update() + class AvailableImplantsView(d.Display): DEFAULT_COLS = ["attr:implantness", "Base Icon", @@ -203,6 +209,7 @@ class AvailableImplantsView(d.Display): d.Display.__init__(self, parent, style=wx.LC_SINGLE_SEL) self.Bind(wx.EVT_LEFT_DCLICK, parent.removeItem) + class ItemView(d.Display): DEFAULT_COLS = ["Base Icon", "Base Name", @@ -235,7 +242,7 @@ class ItemView(d.Display): self.update(self.items) def scheduleSearch(self, event=None): - sMkt = service.Market.getInstance() + sMkt = Market.getInstance() search = self.searchBox.GetLineText(0) # Make sure we do not count wildcard as search symbol @@ -255,4 +262,4 @@ class ItemView(d.Display): self.items = sorted(list(items), key=lambda i: i.name) - self.update(self.items) \ No newline at end of file + self.update(self.items) diff --git a/gui/cachingImageList.py b/gui/cachingImageList.py index 4ec0b2f69..4bbef1e23 100644 --- a/gui/cachingImageList.py +++ b/gui/cachingImageList.py @@ -1,35 +1,38 @@ -#=============================================================================== -# Copyright (C) 2010 Diego Duclos -# -# This file is part of pyfa. -# -# pyfa is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# pyfa is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with pyfa. If not, see . -#=============================================================================== - -import wx -from gui.bitmapLoader import BitmapLoader - -class CachingImageList(wx.ImageList): - def __init__(self, width, height): - wx.ImageList.__init__(self, width, height) - self.map = {} - - def GetImageIndex(self, *loaderArgs): - id = self.map.get(loaderArgs) - if id is None: - bitmap = BitmapLoader.getBitmap(*loaderArgs) - if bitmap is None: - return -1 - id = self.map[loaderArgs] = wx.ImageList.Add(self,bitmap) - return id +# ============================================================================= +# Copyright (C) 2010 Diego Duclos +# +# This file is part of pyfa. +# +# pyfa is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# pyfa is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with pyfa. If not, see . +# ============================================================================= + + +# noinspection PyPackageRequirements +import wx +from gui.bitmapLoader import BitmapLoader + + +class CachingImageList(wx.ImageList): + def __init__(self, width, height): + wx.ImageList.__init__(self, width, height) + self.map = {} + + def GetImageIndex(self, *loaderArgs): + id_ = self.map.get(loaderArgs) + if id_ is None: + bitmap = BitmapLoader.getBitmap(*loaderArgs) + if bitmap is None: + return -1 + id_ = self.map[loaderArgs] = wx.ImageList.Add(self, bitmap) + return id_ diff --git a/gui/cargoView.py b/gui/cargoView.py index 91bfb2b5f..d997afa79 100644 --- a/gui/cargoView.py +++ b/gui/cargoView.py @@ -1,4 +1,4 @@ -#=============================================================================== +# ============================================================================= # Copyright (C) 2010 Diego Duclos # # This file is part of pyfa. @@ -15,31 +15,34 @@ # # You should have received a copy of the GNU General Public License # along with pyfa. If not, see . -#=============================================================================== +# ============================================================================= +# noinspection PyPackageRequirements import wx -import service import gui.display as d -import gui.marketBrowser as mb from gui.builtinViewColumns.state import State from gui.contextMenu import ContextMenu import globalEvents as GE +from service.fit import Fit +from service.market import Market + class CargoViewDrop(wx.PyDropTarget): - def __init__(self, dropFn): - wx.PyDropTarget.__init__(self) - self.dropFn = dropFn - # this is really transferring an EVE itemID - self.dropData = wx.PyTextDataObject() - self.SetDataObject(self.dropData) + def __init__(self, dropFn, *args, **kwargs): + super(CargoViewDrop, self).__init__(*args, **kwargs) + self.dropFn = dropFn + # this is really transferring an EVE itemID + self.dropData = wx.PyTextDataObject() + self.SetDataObject(self.dropData) - def OnData(self, x, y, t): - if self.GetData(): - data = self.dropData.GetText().split(':') - self.dropFn(x, y, data) - return t + def OnData(self, x, y, t): + if self.GetData(): + data = self.dropData.GetText().split(':') + self.dropFn(x, y, data) + return t -# @todo: Was copied form another class and modified. Look through entire file, refine + +# @todo: Was copied form another class and modified. Look through entire file, refine class CargoView(d.Display): DEFAULT_COLS = ["Base Icon", "Base Name", @@ -58,24 +61,24 @@ class CargoView(d.Display): self.SetDropTarget(CargoViewDrop(self.handleListDrag)) self.Bind(wx.EVT_LIST_BEGIN_DRAG, self.startDrag) - if "__WXGTK__" in wx.PlatformInfo: + if "__WXGTK__" in wx.PlatformInfo: self.Bind(wx.EVT_RIGHT_UP, self.scheduleMenu) else: self.Bind(wx.EVT_RIGHT_DOWN, self.scheduleMenu) def handleListDrag(self, x, y, data): - ''' + """ Handles dragging of items from various pyfa displays which support it data is list with two indices: data[0] is hard-coded str of originating source data[1] is typeID or index of data we want to manipulate - ''' + """ if data[0] == "fitting": self.swapModule(x, y, int(data[1])) elif data[0] == "market": - sFit = service.Fit.getInstance() + sFit = Fit.getInstance() sFit.addCargo(self.mainFrame.getActiveFit(), int(data[1]), 1) wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.mainFrame.getActiveFit())) @@ -84,17 +87,17 @@ class CargoView(d.Display): if row != -1: data = wx.PyTextDataObject() - data.SetText("cargo:"+str(row)) + data.SetText("cargo:" + str(row)) dropSource = wx.DropSource(self) dropSource.SetData(data) - res = dropSource.DoDragDrop() + dropSource.DoDragDrop() - def kbEvent(self,event): + def kbEvent(self, event): keycode = event.GetKeyCode() if keycode == wx.WXK_DELETE or keycode == wx.WXK_NUMPAD_DELETE: fitID = self.mainFrame.getActiveFit() - sFit = service.Fit.getInstance() + sFit = Fit.getInstance() row = self.GetFirstSelected() if row != -1: sFit.removeCargo(fitID, self.GetItemData(row)) @@ -102,8 +105,8 @@ class CargoView(d.Display): event.Skip() def swapModule(self, x, y, modIdx): - '''Swap a module from fitting window with cargo''' - sFit = service.Fit.getInstance() + """Swap a module from fitting window with cargo""" + sFit = Fit.getInstance() fit = sFit.getFit(self.mainFrame.getActiveFit()) dstRow, _ = self.HitTest((x, y)) mstate = wx.GetMouseState() @@ -111,26 +114,26 @@ class CargoView(d.Display): # Gather module information to get position module = fit.modules[modIdx] - if dstRow != -1: # we're swapping with cargo - if mstate.CmdDown(): # if copying, append to cargo + if dstRow != -1: # we're swapping with cargo + if mstate.CmdDown(): # if copying, append to cargo sFit.addCargo(self.mainFrame.getActiveFit(), module.item.ID) - else: # else, move / swap + else: # else, move / swap sFit.moveCargoToModule(self.mainFrame.getActiveFit(), module.position, dstRow) - else: # dragging to blank spot, append + else: # dragging to blank spot, append sFit.addCargo(self.mainFrame.getActiveFit(), module.item.ID) - if not mstate.CmdDown(): # if not copying, remove module - sFit.removeModule(self.mainFrame.getActiveFit(), module.position) + if not mstate.CmdDown(): # if not copying, remove module + sFit.removeModule(self.mainFrame.getActiveFit(), module.position) wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.mainFrame.getActiveFit())) def fitChanged(self, event): - sFit = service.Fit.getInstance() + sFit = Fit.getInstance() fit = sFit.getFit(event.fitID) self.Parent.Parent.DisablePage(self, not fit or fit.isStructure) - #Clear list and get out if current fitId is None + # Clear list and get out if current fitId is None if event.fitID is None and self.lastFitId is not None: self.DeleteAllItems() self.lastFitId = None @@ -139,7 +142,8 @@ class CargoView(d.Display): self.original = fit.cargo if fit is not None else None self.cargo = stuff = fit.cargo if fit is not None else None - if stuff is not None: stuff.sort(key=lambda cargo: cargo.itemID) + if stuff is not None: + stuff.sort(key=lambda cargo: cargo.itemID) if event.fitID != self.lastFitId: self.lastFitId = event.fitID @@ -161,7 +165,7 @@ class CargoView(d.Display): col = self.getColumn(event.Position) if col != self.getColIndex(State): fitID = self.mainFrame.getActiveFit() - sFit = service.Fit.getInstance() + sFit = Fit.getInstance() cargo = self.cargo[self.GetItemData(row)] sFit.removeCargo(fitID, self.original.index(cargo)) wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=fitID)) @@ -174,11 +178,11 @@ class CargoView(d.Display): def spawnMenu(self): sel = self.GetFirstSelected() if sel != -1: - sFit = service.Fit.getInstance() + sFit = Fit.getInstance() fit = sFit.getFit(self.mainFrame.getActiveFit()) cargo = fit.cargo[sel] - sMkt = service.Market.getInstance() + sMkt = Market.getInstance() sourceContext = "cargoItem" itemContext = sMkt.getCategoryByItem(cargo.item).name diff --git a/gui/characterEditor.py b/gui/characterEditor.py index bf7db18bf..8006fa56a 100644 --- a/gui/characterEditor.py +++ b/gui/characterEditor.py @@ -1,4 +1,4 @@ -#=============================================================================== +# ============================================================================= # Copyright (C) 2010 Diego Duclos # # This file is part of pyfa. @@ -15,18 +15,26 @@ # # You should have received a copy of the GNU General Public License # along with pyfa. If not, see . -#=============================================================================== +# ============================================================================= +# noinspection PyPackageRequirements import wx +# noinspection PyPackageRequirements import wx.lib.newevent +# noinspection PyPackageRequirements import wx.gizmos from gui.bitmapLoader import BitmapLoader -import service from gui.contextMenu import ContextMenu import gui.globalEvents as GE from gui.builtinViews.implantEditor import BaseImplantEditorView from gui.builtinViews.entityEditor import EntityEditor, BaseValidator +from service.fit import Fit +from service.character import Character +from service.network import AuthenticationError, TimeoutError +from service.market import Market +from logbook import Logger +pyfalog = Logger(__name__) class CharacterTextValidor(BaseValidator): @@ -49,6 +57,7 @@ class CharacterTextValidor(BaseValidator): return True except ValueError, e: + pyfalog.error(e) wx.MessageBox(u"{}".format(e), "Error") textCtrl.SetFocus() return False @@ -60,7 +69,7 @@ class CharacterEntityEditor(EntityEditor): self.SetEditorValidator(CharacterTextValidor) def getEntitiesFromContext(self): - sChar = service.Character.getInstance() + sChar = Character.getInstance() charList = sorted(sChar.getCharacterList(), key=lambda c: c.name) # Do some processing to ensure that we have All 0 and All 5 at the top @@ -76,35 +85,35 @@ class CharacterEntityEditor(EntityEditor): return charList def DoNew(self, name): - sChar = service.Character.getInstance() + sChar = Character.getInstance() return sChar.new(name) def DoRename(self, entity, name): - sChar = service.Character.getInstance() + sChar = Character.getInstance() sChar.rename(entity, name) def DoCopy(self, entity, name): - sChar = service.Character.getInstance() + sChar = Character.getInstance() copy = sChar.copy(entity) sChar.rename(copy, name) return copy def DoDelete(self, entity): - sChar = service.Character.getInstance() + sChar = Character.getInstance() sChar.delete(entity) class CharacterEditor(wx.Frame): def __init__(self, parent): - wx.Frame.__init__ (self, parent, id=wx.ID_ANY, title=u"pyfa: Character Editor", pos=wx.DefaultPosition, - size=wx.Size(640, 600), style=wx.DEFAULT_FRAME_STYLE ^ wx.RESIZE_BORDER) + wx.Frame.__init__(self, parent, id=wx.ID_ANY, title=u"pyfa: Character Editor", pos=wx.DefaultPosition, + size=wx.Size(640, 600), style=wx.DEFAULT_FRAME_STYLE ^ wx.RESIZE_BORDER) i = wx.IconFromBitmap(BitmapLoader.getBitmap("character_small", "gui")) self.SetIcon(i) self.mainFrame = parent - #self.disableWin = wx.WindowDisabler(self) - sFit = service.Fit.getInstance() + # self.disableWin = wx.WindowDisabler(self) + sFit = Fit.getInstance() self.SetBackgroundColour(wx.SystemSettings.GetColour(wx.SYS_COLOUR_BTNFACE)) @@ -158,6 +167,8 @@ class CharacterEditor(wx.Frame): self.Bind(GE.CHAR_LIST_UPDATED, self.refreshCharacterList) self.entityEditor.Bind(wx.EVT_CHOICE, self.charChanged) + self.charChanged(None) + def btnRestrict(self): char = self.entityEditor.getActiveEntity() @@ -176,12 +187,12 @@ class CharacterEditor(wx.Frame): event.Skip() def editingFinished(self, event): - #del self.disableWin + # del self.disableWin wx.PostEvent(self.mainFrame, GE.CharListUpdated()) self.Destroy() def saveChar(self, event): - sChr = service.Character.getInstance() + sChr = Character.getInstance() char = self.entityEditor.getActiveEntity() sChr.saveCharacter(char.ID) wx.PostEvent(self, GE.CharListUpdated()) @@ -192,13 +203,13 @@ class CharacterEditor(wx.Frame): dlg.ShowModal() def revertChar(self, event): - sChr = service.Character.getInstance() + sChr = Character.getInstance() char = self.entityEditor.getActiveEntity() sChr.revertCharacter(char.ID) wx.PostEvent(self, GE.CharListUpdated()) def closeEvent(self, event): - #del self.disableWin + # del self.disableWin wx.PostEvent(self.mainFrame, GE.CharListUpdated()) self.Destroy() @@ -223,7 +234,7 @@ class CharacterEditor(wx.Frame): event.Skip() def Destroy(self): - sFit = service.Fit.getInstance() + sFit = Fit.getInstance() fitID = self.mainFrame.getActiveFit() if fitID is not None: sFit.clearFit(fitID) @@ -231,18 +242,35 @@ class CharacterEditor(wx.Frame): wx.Frame.Destroy(self) -class SkillTreeView (wx.Panel): + +class SkillTreeView(wx.Panel): def __init__(self, parent): - wx.Panel.__init__ (self, parent, id=wx.ID_ANY, pos=wx.DefaultPosition, size=wx.DefaultSize, style=wx.TAB_TRAVERSAL) + wx.Panel.__init__(self, parent, id=wx.ID_ANY, pos=wx.DefaultPosition, size=wx.DefaultSize, + style=wx.TAB_TRAVERSAL) self.charEditor = self.Parent.Parent # first parent is Notebook, second is Character Editor self.SetBackgroundColour(wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOW)) pmainSizer = wx.BoxSizer(wx.VERTICAL) + self.clonesChoice = wx.Choice(self, wx.ID_ANY, style=0) + i = self.clonesChoice.Append("Omega Clone", None) + self.clonesChoice.SetSelection(i) + pmainSizer.Add(self.clonesChoice, 0, wx.ALL | wx.EXPAND, 5) + + sChar = Character.getInstance() + self.alphaClones = sChar.getAlphaCloneList() + char = self.charEditor.entityEditor.getActiveEntity() + + for clone in self.alphaClones: + i = self.clonesChoice.Append(clone.alphaCloneName, clone.ID) + if clone.ID == char.alphaCloneID: + self.clonesChoice.SetSelection(i) + + self.clonesChoice.Bind(wx.EVT_CHOICE, self.cloneChanged) + tree = self.skillTreeListCtrl = wx.gizmos.TreeListCtrl(self, wx.ID_ANY, style=wx.TR_DEFAULT_STYLE | wx.TR_HIDE_ROOT) pmainSizer.Add(tree, 1, wx.EXPAND | wx.ALL, 5) - self.imageList = wx.ImageList(16, 16) tree.SetImageList(self.imageList) self.skillBookImageId = self.imageList.Add(BitmapLoader.getBitmap("skill_small", "gui")) @@ -262,7 +290,7 @@ class SkillTreeView (wx.Panel): tree.Bind(wx.EVT_TREE_ITEM_RIGHT_CLICK, self.scheduleMenu) # bind the Character selection event - self.charEditor.entityEditor.Bind(wx.EVT_CHOICE, self.populateSkillTree) + self.charEditor.entityEditor.Bind(wx.EVT_CHOICE, self.charChanged) self.charEditor.Bind(GE.CHAR_LIST_UPDATED, self.populateSkillTree) srcContext = "skillItem" @@ -286,7 +314,6 @@ class SkillTreeView (wx.Panel): self.revertID = wx.NewId() self.levelChangeMenu.Append(self.revertID, "Revert") - self.saveID = wx.NewId() self.levelChangeMenu.Append(self.saveID, "Save") @@ -295,11 +322,30 @@ class SkillTreeView (wx.Panel): self.Layout() + def cloneChanged(self, event): + sChar = Character.getInstance() + sChar.setAlphaClone(self.charEditor.entityEditor.getActiveEntity(), event.ClientData) + self.populateSkillTree() + + def charChanged(self, event=None): + char = self.charEditor.entityEditor.getActiveEntity() + for i in range(self.clonesChoice.GetCount()): + cloneID = self.clonesChoice.GetClientData(i) + if char.alphaCloneID == cloneID: + self.clonesChoice.SetSelection(i) + + self.populateSkillTree(event) + def populateSkillTree(self, event=None): - sChar = service.Character.getInstance() + sChar = Character.getInstance() char = self.charEditor.entityEditor.getActiveEntity() dirtyGroups = set([skill.item.group.ID for skill in char.dirtySkills]) + if char.name in ("All 0", "All 5"): + self.clonesChoice.Disable() + else: + self.clonesChoice.Enable() + groups = sChar.getSkillGroups() imageId = self.skillBookImageId root = self.root @@ -325,8 +371,8 @@ class SkillTreeView (wx.Panel): if tree.GetItemText(child) == "dummy": tree.Delete(child) - #Get the real intrestin' stuff - sChar = service.Character.getInstance() + # Get the real intrestin' stuff + sChar = Character.getInstance() char = self.charEditor.entityEditor.getActiveEntity() for id, name in sChar.getSkills(tree.GetPyData(root)): iconId = self.skillBookImageId @@ -348,7 +394,7 @@ class SkillTreeView (wx.Panel): return char = self.charEditor.entityEditor.getActiveEntity() - sMkt = service.Market.getInstance() + sMkt = Market.getInstance() if char.name not in ("All 0", "All 5"): self.levelChangeMenu.selection = sMkt.getItem(self.skillTreeListCtrl.GetPyData(item)) self.PopupMenu(self.levelChangeMenu) @@ -359,7 +405,7 @@ class SkillTreeView (wx.Panel): def changeLevel(self, event): level = self.levelIds.get(event.Id) - sChar = service.Character.getInstance() + sChar = Character.getInstance() char = self.charEditor.entityEditor.getActiveEntity() selection = self.skillTreeListCtrl.GetSelection() skillID = self.skillTreeListCtrl.GetPyData(selection) @@ -388,7 +434,7 @@ class SkillTreeView (wx.Panel): class ImplantEditorView(BaseImplantEditorView): def __init__(self, parent): - BaseImplantEditorView.__init__ (self, parent) + BaseImplantEditorView.__init__(self, parent) self.determineEnabled() @@ -405,19 +451,19 @@ class ImplantEditorView(BaseImplantEditorView): self.determineEnabled() def getImplantsFromContext(self): - sChar = service.Character.getInstance() + sChar = Character.getInstance() char = self.Parent.Parent.entityEditor.getActiveEntity() return sChar.getImplants(char.ID) def addImplantToContext(self, item): - sChar = service.Character.getInstance() + sChar = Character.getInstance() char = self.Parent.Parent.entityEditor.getActiveEntity() sChar.addImplant(char.ID, item.ID) def removeImplantFromContext(self, implant): - sChar = service.Character.getInstance() + sChar = Character.getInstance() char = self.Parent.Parent.entityEditor.getActiveEntity() sChar.removeImplant(char.ID, implant) @@ -442,9 +488,10 @@ class ImplantEditorView(BaseImplantEditorView): self.Enable() -class APIView (wx.Panel): +class APIView(wx.Panel): def __init__(self, parent): - wx.Panel.__init__ (self, parent, id=wx.ID_ANY, pos=wx.DefaultPosition, size=wx.Size(500, 300), style=wx.TAB_TRAVERSAL) + wx.Panel.__init__(self, parent, id=wx.ID_ANY, pos=wx.DefaultPosition, size=wx.Size(500, 300), + style=wx.TAB_TRAVERSAL) self.charEditor = self.Parent.Parent # first parent is Notebook, second is Character Editor self.SetBackgroundColour(wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOW)) @@ -453,17 +500,17 @@ class APIView (wx.Panel): pmainSizer = wx.BoxSizer(wx.VERTICAL) - hintSizer = wx.BoxSizer( wx.HORIZONTAL ) + hintSizer = wx.BoxSizer(wx.HORIZONTAL) hintSizer.AddStretchSpacer() - self.stDisabledTip = wx.StaticText( self, wx.ID_ANY, u"You cannot add API Details for All 0 and All 5 characters.\n" - u"Please select another character or make a new one.", style=wx.ALIGN_CENTER ) - self.stDisabledTip.Wrap( -1 ) - hintSizer.Add( self.stDisabledTip, 0, wx.TOP | wx.BOTTOM, 10 ) + self.stDisabledTip = wx.StaticText(self, wx.ID_ANY, + u"You cannot add API Details for All 0 and All 5 characters.\n" + u"Please select another character or make a new one.", style=wx.ALIGN_CENTER) + self.stDisabledTip.Wrap(-1) + hintSizer.Add(self.stDisabledTip, 0, wx.TOP | wx.BOTTOM, 10) self.stDisabledTip.Hide() hintSizer.AddStretchSpacer() pmainSizer.Add(hintSizer, 0, wx.EXPAND, 5) - fgSizerInput = wx.FlexGridSizer(3, 2, 0, 0) fgSizerInput.AddGrowableCol(1) fgSizerInput.SetFlexibleDirection(wx.BOTH) @@ -495,39 +542,44 @@ class APIView (wx.Panel): pmainSizer.Add(fgSizerInput, 0, wx.EXPAND, 5) - btnSizer = wx.BoxSizer( wx.HORIZONTAL ) + btnSizer = wx.BoxSizer(wx.HORIZONTAL) btnSizer.AddStretchSpacer() self.btnFetchCharList = wx.Button(self, wx.ID_ANY, u"Get Characters") btnSizer.Add(self.btnFetchCharList, 0, wx.ALL, 2) self.btnFetchCharList.Bind(wx.EVT_BUTTON, self.fetchCharList) - self.btnFetchSkills = wx.Button(self, wx.ID_ANY, u"Fetch Skills") - btnSizer.Add(self.btnFetchSkills, 0, wx.ALL, 2) + self.btnFetchSkills = wx.Button(self, wx.ID_ANY, u"Fetch Skills") + btnSizer.Add(self.btnFetchSkills, 0, wx.ALL, 2) self.btnFetchSkills.Bind(wx.EVT_BUTTON, self.fetchSkills) self.btnFetchSkills.Enable(False) btnSizer.AddStretchSpacer() pmainSizer.Add(btnSizer, 0, wx.EXPAND, 5) - self.stStatus = wx.StaticText(self, wx.ID_ANY, wx.EmptyString) + self.stStatus = wx.StaticText(self, wx.ID_ANY, wx.EmptyString) pmainSizer.Add(self.stStatus, 0, wx.ALL, 5) pmainSizer.AddStretchSpacer() - self.stAPITip = wx.StaticText( self, wx.ID_ANY, u"You can create a pre-defined key here (only CharacterSheet is required):", wx.DefaultPosition, wx.DefaultSize, 0 ) - self.stAPITip.Wrap( -1 ) + self.stAPITip = wx.StaticText(self, wx.ID_ANY, + u"You can create a pre-defined key here (only CharacterSheet is required):", + wx.DefaultPosition, wx.DefaultSize, 0) + self.stAPITip.Wrap(-1) - pmainSizer.Add( self.stAPITip, 0, wx.ALL, 2 ) + pmainSizer.Add(self.stAPITip, 0, wx.ALL, 2) - self.hlEveAPI = wx.HyperlinkCtrl( self, wx.ID_ANY, self.apiUrlCreatePredefined, self.apiUrlCreatePredefined, wx.DefaultPosition, wx.DefaultSize, wx.HL_DEFAULT_STYLE ) - pmainSizer.Add( self.hlEveAPI, 0, wx.ALL, 2 ) + self.hlEveAPI = wx.HyperlinkCtrl(self, wx.ID_ANY, self.apiUrlCreatePredefined, self.apiUrlCreatePredefined, + wx.DefaultPosition, wx.DefaultSize, wx.HL_DEFAULT_STYLE) + pmainSizer.Add(self.hlEveAPI, 0, wx.ALL, 2) - self.stAPITip2 = wx.StaticText( self, wx.ID_ANY, u"Or, you can choose an existing key from:", wx.DefaultPosition, wx.DefaultSize, 0 ) - self.stAPITip2.Wrap( -1 ) - pmainSizer.Add( self.stAPITip2, 0, wx.ALL, 2 ) + self.stAPITip2 = wx.StaticText(self, wx.ID_ANY, u"Or, you can choose an existing key from:", wx.DefaultPosition, + wx.DefaultSize, 0) + self.stAPITip2.Wrap(-1) + pmainSizer.Add(self.stAPITip2, 0, wx.ALL, 2) - self.hlEveAPI2 = wx.HyperlinkCtrl( self, wx.ID_ANY, self.apiUrlKeyList, self.apiUrlKeyList, wx.DefaultPosition, wx.DefaultSize, wx.HL_DEFAULT_STYLE ) - pmainSizer.Add( self.hlEveAPI2, 0, wx.ALL, 2 ) + self.hlEveAPI2 = wx.HyperlinkCtrl(self, wx.ID_ANY, self.apiUrlKeyList, self.apiUrlKeyList, wx.DefaultPosition, + wx.DefaultSize, wx.HL_DEFAULT_STYLE) + pmainSizer.Add(self.hlEveAPI2, 0, wx.ALL, 2) self.charEditor.entityEditor.Bind(wx.EVT_CHOICE, self.charChanged) @@ -536,7 +588,7 @@ class APIView (wx.Panel): self.charChanged(None) def charChanged(self, event): - sChar = service.Character.getInstance() + sChar = Character.getInstance() activeChar = self.charEditor.entityEditor.getActiveEntity() ID, key, char, chars = sChar.getApiDetails(activeChar.ID) @@ -547,7 +599,7 @@ class APIView (wx.Panel): if chars: for charName in chars: - i = self.charChoice.Append(charName) + self.charChoice.Append(charName) self.charChoice.SetStringSelection(char) self.charChoice.Enable(True) self.btnFetchSkills.Enable(True) @@ -575,20 +627,25 @@ class APIView (wx.Panel): self.stStatus.SetLabel("Invalid keyID or vCode!") return - sChar = service.Character.getInstance() + sChar = Character.getInstance() try: activeChar = self.charEditor.entityEditor.getActiveEntity() list = sChar.apiCharList(activeChar.ID, self.inputID.GetLineText(0), self.inputKey.GetLineText(0)) - except service.network.AuthenticationError, e: - self.stStatus.SetLabel("Authentication failure. Please check keyID and vCode combination.") - except service.network.TimeoutError, e: - self.stStatus.SetLabel("Request timed out. Please check network connectivity and/or proxy settings.") + except AuthenticationError, e: + msg = "Authentication failure. Please check keyID and vCode combination." + pyfalog.info(msg) + self.stStatus.SetLabel(msg) + except TimeoutError, e: + msg = "Request timed out. Please check network connectivity and/or proxy settings." + pyfalog.info(msg) + self.stStatus.SetLabel(msg) except Exception, e: - self.stStatus.SetLabel("Error:\n%s"%e.message) + pyfalog.error(e) + self.stStatus.SetLabel("Error:\n%s" % e.message) else: self.charChoice.Clear() for charName in list: - i = self.charChoice.Append(charName) + self.charChoice.Append(charName) self.btnFetchSkills.Enable(True) self.charChoice.Enable(True) @@ -601,20 +658,21 @@ class APIView (wx.Panel): charName = self.charChoice.GetString(self.charChoice.GetSelection()) if charName: try: - sChar = service.Character.getInstance() + sChar = Character.getInstance() activeChar = self.charEditor.entityEditor.getActiveEntity() sChar.apiFetch(activeChar.ID, charName) self.stStatus.SetLabel("Successfully fetched %s\'s skills from EVE API." % charName) except Exception, e: + pyfalog.error("Unable to retrieve {0}\'s skills. Error message:\n{1}", charName, e) self.stStatus.SetLabel("Unable to retrieve %s\'s skills. Error message:\n%s" % (charName, e)) -class SaveCharacterAs(wx.Dialog): +class SaveCharacterAs(wx.Dialog): def __init__(self, parent, charID): wx.Dialog.__init__(self, parent, title="Save Character As...", size=wx.Size(300, 60)) self.charID = charID self.parent = parent - sChar = service.Character.getInstance() + sChar = Character.getInstance() name = sChar.getCharName(charID) bSizer1 = wx.BoxSizer(wx.HORIZONTAL) @@ -631,10 +689,9 @@ class SaveCharacterAs(wx.Dialog): self.button.Bind(wx.EVT_BUTTON, self.change) def change(self, event): - sChar = service.Character.getInstance() + sChar = Character.getInstance() sChar.saveCharacterAs(self.charID, self.input.GetLineText(0)) wx.PostEvent(self.parent, GE.CharListUpdated()) event.Skip() self.Close() - \ No newline at end of file diff --git a/gui/characterSelection.py b/gui/characterSelection.py index 47df7e018..3ee531d8f 100644 --- a/gui/characterSelection.py +++ b/gui/characterSelection.py @@ -1,4 +1,4 @@ -#=============================================================================== +# ============================================================================= # Copyright (C) 2010 Diego Duclos # # This file is part of pyfa. @@ -15,13 +15,18 @@ # # You should have received a copy of the GNU General Public License # along with pyfa. If not, see . -#=============================================================================== +# ============================================================================= +# noinspection PyPackageRequirements import wx -import service from gui.bitmapLoader import BitmapLoader import gui.globalEvents as GE import gui.mainFrame +from service.character import Character +from service.fit import Fit +from logbook import Logger +pyfalog = Logger(__name__) + class CharacterSelection(wx.Panel): def __init__(self, parent): @@ -42,9 +47,9 @@ class CharacterSelection(wx.Panel): self.refreshCharacterList() self.cleanSkills = BitmapLoader.getBitmap("skill_big", "gui") - self.redSkills = BitmapLoader.getBitmap("skillRed_big", "gui") + self.redSkills = BitmapLoader.getBitmap("skillRed_big", "gui") self.greenSkills = BitmapLoader.getBitmap("skillGreen_big", "gui") - self.refresh = BitmapLoader.getBitmap("refresh", "gui") + self.refresh = BitmapLoader.getBitmap("refresh", "gui") self.btnRefresh = wx.BitmapButton(self, wx.ID_ANY, self.refresh) size = self.btnRefresh.GetSize() @@ -66,7 +71,7 @@ class CharacterSelection(wx.Panel): self.mainFrame.Bind(GE.CHAR_LIST_UPDATED, self.refreshCharacterList) self.mainFrame.Bind(GE.FIT_CHANGED, self.fitChanged) - self.SetMinSize(wx.Size(25,-1)) + self.SetMinSize(wx.Size(25, -1)) self.charChoice.Enable(False) @@ -76,7 +81,7 @@ class CharacterSelection(wx.Panel): def refreshCharacterList(self, event=None): choice = self.charChoice - sChar = service.Character.getInstance() + sChar = Character.getInstance() activeChar = self.getActiveCharacter() choice.Clear() @@ -94,7 +99,7 @@ class CharacterSelection(wx.Panel): charID = sChar.all5ID() self.selectChar(charID) fitID = self.mainFrame.getActiveFit() - sFit = service.Fit.getInstance() + sFit = Fit.getInstance() sFit.changeChar(fitID, charID) choice.Append(u"\u2015 Open Character Editor \u2015", -1) @@ -104,20 +109,21 @@ class CharacterSelection(wx.Panel): event.Skip() def refreshApi(self, event): - sChar = service.Character.getInstance() + sChar = Character.getInstance() ID, key, charName, chars = sChar.getApiDetails(self.getActiveCharacter()) if charName: try: sChar.apiFetch(self.getActiveCharacter(), charName) - except: + except Exception as e: # can we do a popup, notifying user of API error? - pass + pyfalog.error("API fetch error") + pyfalog.error(e) self.refreshCharacterList() def charChanged(self, event): fitID = self.mainFrame.getActiveFit() charID = self.getActiveCharacter() - sChar = service.Character.getInstance() + sChar = Character.getInstance() if charID == -1: # revert to previous character @@ -129,7 +135,7 @@ class CharacterSelection(wx.Panel): else: self.btnRefresh.Enable(False) - sFit = service.Fit.getInstance() + sFit = Fit.getInstance() sFit.changeChar(fitID, charID) self.charCache = self.charChoice.GetCurrentSelection() wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=fitID)) @@ -137,18 +143,18 @@ class CharacterSelection(wx.Panel): def selectChar(self, charID): choice = self.charChoice numItems = len(choice.GetItems()) - for i in xrange(numItems): - id = choice.GetClientData(i) - if id == charID: + for i in range(numItems): + id_ = choice.GetClientData(i) + if id_ == charID: choice.SetSelection(i) return True return False def fitChanged(self, event): - self.charChoice.Enable(event.fitID != None) + self.charChoice.Enable(event.fitID is not None) choice = self.charChoice - sFit = service.Fit.getInstance() + sFit = Fit.getInstance() currCharID = choice.GetClientData(choice.GetCurrentSelection()) fit = sFit.getFit(event.fitID) newCharID = fit.character.ID if fit is not None else None @@ -156,38 +162,37 @@ class CharacterSelection(wx.Panel): self.skillReqsStaticBitmap.SetBitmap(self.cleanSkills) self.skillReqsStaticBitmap.SetToolTipString("No active fit") else: - sCharacter = service.Character.getInstance() + sCharacter = Character.getInstance() reqs = sCharacter.checkRequirements(fit) - sCharacter.skillReqsDict = {'charname':fit.character.name, 'skills':[]} + sCharacter.skillReqsDict = {'charname': fit.character.name, 'skills': []} if len(reqs) == 0: tip = "All skill prerequisites have been met" self.skillReqsStaticBitmap.SetBitmap(self.greenSkills) else: - tip = "Skills required:\n" + tip = "Skills required:\n" condensed = sFit.serviceFittingOptions["compactSkills"] if condensed: - dict = self._buildSkillsTooltipCondensed(reqs, skillsMap = {}) - for key in sorted(dict): - tip += "%s: %d\n" % (key, dict[key]) + dict_ = self._buildSkillsTooltipCondensed(reqs, skillsMap={}) + for key in sorted(dict_): + tip += "%s: %d\n" % (key, dict_[key]) else: tip += self._buildSkillsTooltip(reqs) self.skillReqsStaticBitmap.SetBitmap(self.redSkills) self.skillReqsStaticBitmap.SetToolTipString(tip.strip()) - if newCharID == None: - sChar = service.Character.getInstance() + if newCharID is None: + sChar = Character.getInstance() self.selectChar(sChar.all5ID()) elif currCharID != newCharID: self.selectChar(newCharID) self.charChanged(None) - event.Skip() - def _buildSkillsTooltip(self, reqs, currItem = "", tabulationLevel = 0): + def _buildSkillsTooltip(self, reqs, currItem="", tabulationLevel=0): tip = "" - sCharacter = service.Character.getInstance() + sCharacter = Character.getInstance() if tabulationLevel == 0: for item, subReqs in reqs.iteritems(): @@ -197,11 +202,11 @@ class CharacterSelection(wx.Panel): for name, info in reqs.iteritems(): level, ID, more = info sCharacter.skillReqsDict['skills'].append({ - 'item' : currItem, - 'skillID' : ID, - 'skill' : name, - 'level' : level, - 'indent' : tabulationLevel + 'item': currItem, + 'skillID': ID, + 'skill': name, + 'level': level, + 'indent': tabulationLevel, }) tip += "%s%s: %d\n" % (" " * tabulationLevel, name, level) @@ -209,8 +214,11 @@ class CharacterSelection(wx.Panel): return tip - def _buildSkillsTooltipCondensed(self, reqs, currItem = "", tabulationLevel = 0, skillsMap = {}): - sCharacter = service.Character.getInstance() + def _buildSkillsTooltipCondensed(self, reqs, currItem="", tabulationLevel=0, skillsMap=None): + if skillsMap is None: + skillsMap = {} + + sCharacter = Character.getInstance() if tabulationLevel == 0: for item, subReqs in reqs.iteritems(): @@ -220,11 +228,11 @@ class CharacterSelection(wx.Panel): for name, info in reqs.iteritems(): level, ID, more = info sCharacter.skillReqsDict['skills'].append({ - 'item' : currItem, - 'skillID' : ID, - 'skill' : name, - 'level' : level, - 'indent' : tabulationLevel + 'item': currItem, + 'skillID': ID, + 'skill': name, + 'level': level, + 'indent': tabulationLevel, }) if name not in skillsMap: @@ -232,6 +240,6 @@ class CharacterSelection(wx.Panel): elif skillsMap[name] < level: skillsMap[name] = level - skillMap = self._buildSkillsTooltipCondensed(more, currItem, tabulationLevel + 1, skillsMap) + skillsMap = self._buildSkillsTooltipCondensed(more, currItem, tabulationLevel + 1, skillsMap) return skillsMap diff --git a/gui/chromeTabs.py b/gui/chromeTabs.py index bfc3adeb4..89ad9327b 100644 --- a/gui/chromeTabs.py +++ b/gui/chromeTabs.py @@ -1,4 +1,4 @@ -#=============================================================================== +# ============================================================================= # Copyright (C) 2010 Darriele # # This file is part of pyfa. @@ -15,17 +15,20 @@ # # You should have received a copy of the GNU General Public License # along with pyfa. If not, see . -#=============================================================================== +# ============================================================================= +# noinspection PyPackageRequirements import wx +# noinspection PyPackageRequirements import wx.lib.newevent import gui.utils.colorUtils as colorUtils import gui.utils.drawUtils as drawUtils import gui.utils.fonts as fonts from gui.bitmapLoader import BitmapLoader -import gui.utils.fonts as fonts +from logbook import Logger +from service.fit import Fit -import service +pyfalog = Logger(__name__) _PageChanging, EVT_NOTEBOOK_PAGE_CHANGING = wx.lib.newevent.NewEvent() _PageChanged, EVT_NOTEBOOK_PAGE_CHANGED = wx.lib.newevent.NewEvent() @@ -34,7 +37,8 @@ _PageClosing, EVT_NOTEBOOK_PAGE_CLOSING = wx.lib.newevent.NewEvent() PageAdded, EVT_NOTEBOOK_PAGE_ADDED = wx.lib.newevent.NewEvent() PageClosed, EVT_NOTEBOOK_PAGE_CLOSED = wx.lib.newevent.NewEvent() -class VetoAble(): + +class VetoAble(object): def __init__(self): self.__vetoed = False @@ -45,7 +49,7 @@ class VetoAble(): return self.__vetoed -class NotebookTabChangeEvent(): +class NotebookTabChangeEvent(object): def __init__(self, old, new): self.__old = old self.__new = new @@ -91,7 +95,6 @@ class PageAdding(_PageAdding, VetoAble): class PFNotebook(wx.Panel): - def __init__(self, parent, canAdd=True): """ Instance of Pyfa Notebook. Initializes general layout, includes methods @@ -100,8 +103,8 @@ class PFNotebook(wx.Panel): parent - wx parent element canAdd - True if tabs be deleted and added, passed directly to PFTabsContainer - """ + wx.Panel.__init__(self, parent, wx.ID_ANY, size=(-1, -1)) self.pages = [] @@ -147,7 +150,8 @@ class PFNotebook(wx.Panel): if self.activePage == page: self.ShowActive() - def GetBorders(self): + @staticmethod + def GetBorders(): """Gets border widths to better determine page size in ShowActive()""" bx = wx.SystemSettings_GetMetric(wx.SYS_BORDER_X) @@ -234,7 +238,7 @@ class PFNotebook(wx.Panel): try: # Set page to the first non-disabled page self.SetSelection(next(i for i, _ in enumerate(self.pages) if not self.tabsContainer.tabs[i].disabled)) - except StopIteration, ex: + except StopIteration: self.SetSelection(0) self.tabsContainer.DisableTab(idx, toggle) @@ -336,8 +340,8 @@ class PFNotebook(wx.Panel): event.Skip() -class PFTabRenderer: - def __init__(self, size=(36, 24), text=wx.EmptyString, img=None, inclination=6 , closeButton=True): +class PFTabRenderer(object): + def __init__(self, size=(36, 24), text=wx.EmptyString, img=None, inclination=6, closeButton=True): """ Renders a new tab @@ -432,14 +436,15 @@ class PFTabRenderer: mdc.SelectObject(ebmp) mdc.SetFont(self.font) textSizeX, textSizeY = mdc.GetTextExtent(self.text) - totalSize = self.leftWidth + self.rightWidth + textSizeX + self.closeBtnWidth/2 + 16 + self.padding*2 + totalSize = self.leftWidth + self.rightWidth + textSizeX + self.closeBtnWidth / 2 + 16 + self.padding * 2 mdc.SelectObject(wx.NullBitmap) return totalSize, self.tabHeight def SetTabImage(self, img): self.tabImg = img - def CopyRegion(self, region): + @staticmethod + def CopyRegion(region): rect = region.GetBox() newRegion = wx.Region(rect.X, rect.Y, rect.Width, rect.Height) @@ -497,7 +502,7 @@ class PFTabRenderer: mdc = wx.MemoryDC() mdc.SelectObject(bkbmp) - #mdc.SetBackground(wx.Brush((0x12, 0x23, 0x32))) + # mdc.SetBackground(wx.Brush((0x12, 0x23, 0x32))) mdc.Clear() mdc.DrawBitmap(self.ctabLeftBmp, 0, 0) # set the left bitmap @@ -506,14 +511,14 @@ class PFTabRenderer: cm = self.ctabMiddleBmp.ConvertToImage() mimg = cm.Scale(self.contentWidth, self.ctabMiddle.GetHeight(), wx.IMAGE_QUALITY_NORMAL) mbmp = wx.BitmapFromImage(mimg) - mdc.DrawBitmap(mbmp, self.leftWidth, 0 ) # set middle bitmap, offset by left + mdc.DrawBitmap(mbmp, self.leftWidth, 0) # set middle bitmap, offset by left # set right bitmap offset by left + middle mdc.DrawBitmap(self.ctabRightBmp, self.contentWidth + self.leftWidth, 0) mdc.SelectObject(wx.NullBitmap) - #bkbmp.SetMaskColour((0x12, 0x23, 0x32)) + # bkbmp.SetMaskColour((0x12, 0x23, 0x32)) if self.tabBackBitmap: del self.tabBackBitmap @@ -527,7 +532,10 @@ class PFTabRenderer: """ self.tabRegion = wx.RegionFromBitmap(self.tabBackBitmap) self.closeBtnRegion = wx.RegionFromBitmap(self.ctabCloseBmp) - self.closeBtnRegion.Offset(self.contentWidth + self.leftWidth - self.ctabCloseBmp.GetWidth()/2, (self.tabHeight - self.ctabCloseBmp.GetHeight())/2) + self.closeBtnRegion.Offset( + self.contentWidth + self.leftWidth - self.ctabCloseBmp.GetWidth() / 2, + (self.tabHeight - self.ctabCloseBmp.GetHeight()) / 2 + ) def InitColors(self): """Determines colors used for tab, based on system settings""" @@ -545,33 +553,33 @@ class PFTabRenderer: height = self.tabHeight - #rect = wx.Rect(0, 0, self.tabWidth, self.tabHeight) + # rect = wx.Rect(0, 0, self.tabWidth, self.tabHeight) canvas = wx.EmptyBitmap(self.tabWidth, self.tabHeight, 24) mdc = wx.MemoryDC() mdc.SelectObject(canvas) - #mdc.SetBackground(wx.Brush ((0x12,0x23,0x32))) + # mdc.SetBackground(wx.Brush ((0x12,0x23,0x32))) mdc.Clear() - #r = copy.copy(rect) - #r.top = r.left = 0 - #r.height = height + # r = copy.copy(rect) + # r.top = r.left = 0 + # r.height = height mdc.DrawBitmap(self.tabBackBitmap, 0, 0, True) if self.tabImg: bmp = wx.BitmapFromImage(self.tabImg.ConvertToGreyscale() if self.disabled else self.tabImg) if self.contentWidth > 16: # @todo: is this conditional relevant anymore? # Draw tab icon - mdc.DrawBitmap(bmp, self.leftWidth + self.padding - bmp.GetWidth()/2, (height - bmp.GetHeight())/2) - textStart = self.leftWidth + self.padding + bmp.GetWidth()/2 + mdc.DrawBitmap(bmp, self.leftWidth + self.padding - bmp.GetWidth() / 2, (height - bmp.GetHeight()) / 2) + textStart = self.leftWidth + self.padding + bmp.GetWidth() / 2 else: textStart = self.leftWidth mdc.SetFont(self.font) - maxsize = self.tabWidth - textStart - self.rightWidth - self.padding*4 + maxsize = self.tabWidth - textStart - self.rightWidth - self.padding * 4 color = self.selectedColor if self.selected else self.inactiveColor mdc.SetTextForeground(colorUtils.GetSuitableColor(color, 1)) @@ -589,9 +597,10 @@ class PFTabRenderer: cbmp = wx.BitmapFromImage(cimg) mdc.DrawBitmap( - cbmp, - self.contentWidth + self.leftWidth - self.ctabCloseBmp.GetWidth()/2, - (height - self.ctabCloseBmp.GetHeight())/2) + cbmp, + self.contentWidth + self.leftWidth - self.ctabCloseBmp.GetWidth() / 2, + (height - self.ctabCloseBmp.GetHeight()) / 2, + ) mdc.SelectObject(wx.NullBitmap) @@ -606,10 +615,11 @@ class PFTabRenderer: def __repr__(self): return "PFTabRenderer(text={}, disabled={}) at {}".format( - self.text, self.disabled, hex(id(self)) + self.text, self.disabled, hex(id(self)) ) -class PFAddRenderer: + +class PFAddRenderer(object): def __init__(self): """Renders the add tab button""" self.addImg = BitmapLoader.getImage("ctabadd", "gui") @@ -628,7 +638,7 @@ class PFAddRenderer: def GetPosition(self): return self.position - def SetPosition(self,pos): + def SetPosition(self, pos): self.position = pos def GetSize(self): @@ -648,7 +658,8 @@ class PFAddRenderer: region = wx.RegionFromBitmap(self.tbmp) return region - def CopyRegion(self, region): + @staticmethod + def CopyRegion(region): rect = region.GetBox() newRegion = wx.Region(rect.X, rect.Y, rect.Width, rect.Height) @@ -689,7 +700,7 @@ class PFTabsContainer(wx.Panel): """ wx.Panel.__init__(self, parent, id, pos, size) - if wx.VERSION >= (3,0): + if wx.VERSION >= (3, 0): self.SetBackgroundStyle(wx.BG_STYLE_PAINT) self.tabs = [] @@ -699,7 +710,7 @@ class PFTabsContainer(wx.Panel): self.containerHeight = height self.startDrag = False self.dragging = False - self.sFit = service.Fit.getInstance() + self.sFit = Fit.getInstance() self.inclination = 7 if canAdd: @@ -865,7 +876,8 @@ class PFTabsContainer(wx.Panel): return True if self.TabHitTest(tab, x, y): - if tab.disabled: return + if tab.disabled: + return tab.SetSelected(True) oldSelTab.SetSelected(False) @@ -942,7 +954,7 @@ class PFTabsContainer(wx.Panel): closeBtnReg = tab.GetCloseButtonRegion() tabPos = tab.GetPosition() tabPosX, tabPosY = tabPos - closeBtnReg.Offset(tabPosX,tabPosY) + closeBtnReg.Offset(tabPosX, tabPosY) if closeBtnReg.Contains(x, y): if not tab.GetCloseButtonHoverStatus(): @@ -968,7 +980,8 @@ class PFTabsContainer(wx.Panel): return tab return None - def TabHitTest(self, tab, x, y): + @staticmethod + def TabHitTest(tab, x, y): tabRegion = tab.GetTabRegion() tabPos = tab.GetPosition() tabPosX, tabPosY = tabPos @@ -985,7 +998,6 @@ class PFTabsContainer(wx.Panel): def GetTabAtRight(self, tabIndex): return self.tabs[tabIndex + 1] if tabIndex < self.GetTabsCount() - 1 else None - def SwitchTabs(self, src, dest, draggedTab=None): self.tabs[src], self.tabs[dest] = self.tabs[dest], self.tabs[src] self.UpdateTabsPosition(draggedTab) @@ -1083,8 +1095,9 @@ class PFTabsContainer(wx.Panel): self.previewTab = tab self.previewTimer.Start(500, True) break - except: - pass + except Exception as e: + pyfalog.critical("Exception caught in CheckTabPreview.") + pyfalog.critical(e) def CheckAddHighlighted(self, x, y): """ @@ -1110,25 +1123,23 @@ class PFTabsContainer(wx.Panel): def OnPaint(self, event): if "wxGTK" in wx.PlatformInfo: mdc = wx.AutoBufferedPaintDC(self) - else: - rect = self.GetRect() mdc = wx.BufferedPaintDC(self) - selected = 0 - - if 'wxMac' in wx.PlatformInfo and wx.VERSION < (3,0): + if 'wxMac' in wx.PlatformInfo and wx.VERSION < (3, 0): color = wx.Colour(0, 0, 0) brush = wx.Brush(color) + # noinspection PyPackageRequirements,PyUnresolvedReferences,PyUnresolvedReferences,PyUnresolvedReferences from Carbon.Appearance import kThemeBrushDialogBackgroundActive + # noinspection PyUnresolvedReferences brush.MacSetTheme(kThemeBrushDialogBackgroundActive) else: color = wx.SystemSettings_GetColour(wx.SYS_COLOUR_3DFACE) brush = wx.Brush(color) if "wxGTK" not in wx.PlatformInfo: - mdc.SetBackground (brush) + mdc.SetBackground(brush) mdc.Clear() selected = None @@ -1136,21 +1147,18 @@ class PFTabsContainer(wx.Panel): tabsWidth = 0 for tab in self.tabs: - tabsWidth += tab.tabWidth - self.inclination*2 - - pos = tabsWidth + tabsWidth += tab.tabWidth - self.inclination * 2 if self.showAddButton: - ax,ay = self.addButton.GetPosition() + ax, ay = self.addButton.GetPosition() mdc.DrawBitmap(self.addButton.Render(), ax, ay, True) - for i in xrange(len(self.tabs) - 1, -1, -1): + for i in range(len(self.tabs) - 1, -1, -1): tab = self.tabs[i] - width = tab.tabWidth - 6 posx, posy = tab.GetPosition() if not tab.IsSelected(): - mdc.DrawBitmap(self.efxBmp, posx, posy, True ) + mdc.DrawBitmap(self.efxBmp, posx, posy, True) bmp = tab.Render() img = bmp.ConvertToImage() img = img.AdjustChannels(1, 1, 1, 0.85) @@ -1254,7 +1262,7 @@ class PFTabsContainer(wx.Panel): if self.tabMinWidth < 1: self.tabMinWidth = 1 for tab in self.tabs: - w, h = tab.GetSize() + tab.GetSize() tab.SetSize((self.tabMinWidth, self.height)) if self.GetTabsCount() > 0: @@ -1265,13 +1273,14 @@ class PFTabsContainer(wx.Panel): def UpdateTabsPosition(self, skipTab=None): tabsWidth = 0 for tab in self.tabs: - tabsWidth += tab.tabWidth - self.inclination*2 + tabsWidth += tab.tabWidth - self.inclination * 2 pos = tabsWidth selected = None - for i in xrange(len(self.tabs) - 1, -1, -1): + selpos = None + for i in range(len(self.tabs) - 1, -1, -1): tab = self.tabs[i] - width = tab.tabWidth - self.inclination*2 + width = tab.tabWidth - self.inclination * 2 pos -= width if not tab.IsSelected(): tab.SetPosition((pos, self.containerHeight - self.height)) @@ -1280,7 +1289,12 @@ class PFTabsContainer(wx.Panel): selpos = pos if selected is not skipTab: selected.SetPosition((selpos, self.containerHeight - self.height)) - self.addButton.SetPosition((round(tabsWidth) + self.inclination*2, self.containerHeight - self.height/2 - self.addButton.GetHeight()/3)) + self.addButton.SetPosition( + ( + round(tabsWidth) + self.inclination * 2, + self.containerHeight - self.height / 2 - self.addButton.GetHeight() / 3 + ) + ) def OnLeaveWindow(self, event): @@ -1305,17 +1319,28 @@ class PFTabsContainer(wx.Panel): if not self.previewTab.GetSelected(): page = self.Parent.GetPage(self.GetTabIndex(self.previewTab)) if page.Snapshot(): - self.previewWnd = PFNotebookPagePreview(self, (mposx+3, mposy+3), page.Snapshot(), self.previewTab.text) + self.previewWnd = PFNotebookPagePreview( + self, + (mposx + 3, mposy + 3), + page.Snapshot(), + self.previewTab.text + ) self.previewWnd.Show() event.Skip() + class PFNotebookPagePreview(wx.Frame): - def __init__ (self,parent, pos, bitmap, title): - wx.Frame.__init__(self, parent, id=wx.ID_ANY, title=wx.EmptyString, pos=pos, size=wx.DefaultSize, style= - wx.NO_BORDER - | wx.FRAME_NO_TASKBAR - | wx.STAY_ON_TOP) + def __init__(self, parent, pos, bitmap, title): + wx.Frame.__init__( + self, + parent, + id=wx.ID_ANY, + title=wx.EmptyString, + pos=pos, + size=wx.DefaultSize, + style=wx.NO_BORDER | wx.FRAME_NO_TASKBAR | wx.STAY_ON_TOP + ) self.title = title self.bitmap = bitmap @@ -1342,13 +1367,13 @@ class PFNotebookPagePreview(wx.Frame): else: width = bitmap.GetWidth() - self.SetSize((width, bitmap.GetHeight()+16)) + self.SetSize((width, bitmap.GetHeight() + 16)) self.SetTransparent(0) self.Refresh() def OnTimer(self, event): - self.transp += 20*self.direction + self.transp += 20 * self.direction if self.transp > 220: self.transp = 220 @@ -1357,7 +1382,7 @@ class PFNotebookPagePreview(wx.Frame): if self.transp < 0: self.transp = 0 self.timer.Stop() - wx.Frame.Show(self,False) + wx.Frame.Show(self, False) self.Destroy() return self.SetTransparent(self.transp) @@ -1381,8 +1406,7 @@ class PFNotebookPagePreview(wx.Frame): self.direction = -1 self.timer.Start(10) - - def OnWindowEraseBk(self,event): + def OnWindowEraseBk(self, event): pass def OnWindowPaint(self, event): @@ -1394,17 +1418,17 @@ class PFNotebookPagePreview(wx.Frame): mdc.SetBackground(wx.Brush(color)) mdc.Clear() - font = wx.Font(fonts.NORMAL, wx.SWISS, wx.NORMAL,wx.NORMAL, False) + font = wx.Font(fonts.NORMAL, wx.SWISS, wx.NORMAL, wx.NORMAL, False) mdc.SetFont(font) - x,y = mdc.GetTextExtent(self.title) + x, y = mdc.GetTextExtent(self.title) mdc.SetBrush(wx.Brush(wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOWTEXT))) mdc.DrawRectangle(0, 0, rect.width, 16) mdc.SetTextForeground(wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOW)) - mdc.DrawText(self.title, (rect.width - x)/2, (16 - y)/2) + mdc.DrawText(self.title, (rect.width - x) / 2, (16 - y) / 2) mdc.DrawBitmap(self.bitmap, 0, 16) diff --git a/gui/commandView.py b/gui/commandView.py index 5971e537c..38dec46c9 100644 --- a/gui/commandView.py +++ b/gui/commandView.py @@ -1,4 +1,4 @@ -#=============================================================================== +# ============================================================================= # Copyright (C) 2010 Diego Duclos # # This file is part of pyfa. @@ -15,46 +15,51 @@ # # You should have received a copy of the GNU General Public License # along with pyfa. If not, see . -#=============================================================================== +# ============================================================================= +# noinspection PyPackageRequirements import wx import gui.display as d import gui.globalEvents as GE -import service + import gui.droneView from gui.builtinViewColumns.state import State from gui.contextMenu import ContextMenu -import eos.types +from service.fit import Fit +from eos.saveddata.drone import Drone as es_Drone -class DummyItem: +class DummyItem(object): def __init__(self, txt): self.name = txt self.icon = None -class DummyEntry: + +class DummyEntry(object): def __init__(self, txt): self.item = DummyItem(txt) -class CommandViewDrop(wx.PyDropTarget): - def __init__(self, dropFn): - wx.PyDropTarget.__init__(self) - self.dropFn = dropFn - # this is really transferring an EVE itemID - self.dropData = wx.PyTextDataObject() - self.SetDataObject(self.dropData) - def OnData(self, x, y, t): - if self.GetData(): - data = self.dropData.GetText().split(':') - self.dropFn(x, y, data) - return t +class CommandViewDrop(wx.PyDropTarget): + def __init__(self, dropFn, *args, **kwargs): + super(CommandViewDrop, self).__init__(*args, **kwargs) + self.dropFn = dropFn + # this is really transferring an EVE itemID + self.dropData = wx.PyTextDataObject() + self.SetDataObject(self.dropData) + + def OnData(self, x, y, t): + if self.GetData(): + data = self.dropData.GetText().split(':') + self.dropFn(x, y, data) + return t + class CommandView(d.Display): - DEFAULT_COLS = ["Base Name",] + DEFAULT_COLS = ["Base Name"] def __init__(self, parent): - d.Display.__init__(self, parent, style = wx.LC_SINGLE_SEL | wx.BORDER_NONE) + d.Display.__init__(self, parent, style=wx.LC_SINGLE_SEL | wx.BORDER_NONE) self.lastFitId = None @@ -66,7 +71,7 @@ class CommandView(d.Display): self.droneView = gui.droneView.DroneView - if "__WXGTK__" in wx.PlatformInfo: + if "__WXGTK__" in wx.PlatformInfo: self.Bind(wx.EVT_RIGHT_UP, self.scheduleMenu) else: self.Bind(wx.EVT_RIGHT_DOWN, self.scheduleMenu) @@ -74,56 +79,58 @@ class CommandView(d.Display): self.Bind(wx.EVT_LIST_BEGIN_DRAG, self.startDrag) self.SetDropTarget(CommandViewDrop(self.handleListDrag)) - def handleListDrag(self, x, y, data): - ''' + @staticmethod + def handleListDrag(x, y, data): + """ Handles dragging of items from various pyfa displays which support it data is list with two indices: data[0] is hard-coded str of originating source data[1] is typeID or index of data we want to manipulate - ''' + """ pass - def kbEvent(self,event): + def kbEvent(self, event): keycode = event.GetKeyCode() if keycode == wx.WXK_DELETE or keycode == wx.WXK_NUMPAD_DELETE: fitID = self.mainFrame.getActiveFit() - sFit = service.Fit.getInstance() + sFit = Fit.getInstance() row = self.GetFirstSelected() if row != -1: sFit.removeCommand(fitID, self.get(row)) wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=fitID)) def handleDrag(self, type, fitID): - #Those are drags coming from pyfa sources, NOT builtin wx drags + # Those are drags coming from pyfa sources, NOT builtin wx drags if type == "fit": activeFit = self.mainFrame.getActiveFit() if activeFit: - sFit = service.Fit.getInstance() + sFit = Fit.getInstance() draggedFit = sFit.getFit(fitID) sFit.addCommandFit(activeFit, draggedFit) wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=activeFit)) def startDrag(self, event): row = event.GetIndex() - if row != -1 and isinstance(self.get(row), eos.types.Drone): + if row != -1 and isinstance(self.get(row), es_Drone): data = wx.PyTextDataObject() - data.SetText("command:"+str(self.GetItemData(row))) + data.SetText("command:" + str(self.GetItemData(row))) dropSource = wx.DropSource(self) dropSource.SetData(data) dropSource.DoDragDrop() - def fitSort(self, fit): + @staticmethod + def fitSort(fit): return fit.name def fitChanged(self, event): - sFit = service.Fit.getInstance() + sFit = Fit.getInstance() fit = sFit.getFit(event.fitID) self.Parent.Parent.DisablePage(self, not fit or fit.isStructure) - #Clear list and get out if current fitId is None + # Clear list and get out if current fitId is None if event.fitID is None and self.lastFitId is not None: self.DeleteAllItems() self.lastFitId = None @@ -147,7 +154,7 @@ class CommandView(d.Display): self.deselectItems() # todo: verify - if stuff == []: + if not stuff: stuff = [DummyEntry("Drag a fit to this area")] self.update(stuff) @@ -155,7 +162,7 @@ class CommandView(d.Display): def get(self, row): numFits = len(self.fits) - if (numFits) == 0: + if numFits == 0: return None return self.fits[row] @@ -168,7 +175,7 @@ class CommandView(d.Display): col = self.getColumn(event.Position) if col == self.getColIndex(State): fitID = self.mainFrame.getActiveFit() - sFit = service.Fit.getInstance() + sFit = Fit.getInstance() sFit.toggleCommandFit(fitID, item) wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=fitID)) @@ -182,12 +189,12 @@ class CommandView(d.Display): menu = None if sel != -1: item = self.get(sel) - if item is None: return - sMkt = service.Market.getInstance() + if item is None: + return fitSrcContext = "commandFit" fitItemContext = item.name - context = ((fitSrcContext,fitItemContext),) - context = context + (("command",),) + context = ((fitSrcContext, fitItemContext),) + context += ("command",), menu = ContextMenu.getMenu((item,), *context) elif sel == -1: fitID = self.mainFrame.getActiveFit() @@ -204,6 +211,6 @@ class CommandView(d.Display): col = self.getColumn(event.Position) if col != self.getColIndex(State): fitID = self.mainFrame.getActiveFit() - sFit = service.Fit.getInstance() + sFit = Fit.getInstance() sFit.removeCommand(fitID, self.get(row)) wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=fitID)) diff --git a/gui/contextMenu.py b/gui/contextMenu.py index f1e364383..243a4a743 100644 --- a/gui/contextMenu.py +++ b/gui/contextMenu.py @@ -1,179 +1,206 @@ -#=============================================================================== -# Copyright (C) 2010 Diego Duclos -# -# This file is part of pyfa. -# -# pyfa is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# pyfa is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with pyfa. If not, see . -#=============================================================================== - -import wx -import logging - -logger = logging.getLogger(__name__) - -class ContextMenu(object): - menus = [] - _ids = [] #[wx.NewId() for x in xrange(200)] # init with decent amount - _idxid = -1 - - @classmethod - def register(cls): - ContextMenu.menus.append(cls) - - @classmethod - def getMenu(cls, selection, *fullContexts): - """ - getMenu returns a menu that is used with wx.PopupMenu. - - selection: provides a list of what was selected. If only 1 item was - selected, it's is a 1-item list or tuple. Can also be None for - contexts without selection, such as statsPane or projected view - fullContexts: a number of tuples of the following tuple: - srcContext - context were menu was spawned, eg: projectedFit, - cargoItem, etc - itemContext - usually the name of the item's category - - eg: - (('fittingModule', 'Module'), ('fittingShip', 'Ship')) - (('marketItemGroup', 'Implant'),) - (('fittingShip', 'Ship'),) - """ - cls._idxid = -1 - debug_start = len(cls._ids) - - rootMenu = wx.Menu() - rootMenu.info = {} - rootMenu.selection = (selection,) if not hasattr(selection, "__iter__") else selection - empty = True - for i, fullContext in enumerate(fullContexts): - amount = 0 - srcContext = fullContext[0] - try: - itemContext = fullContext[1] - except IndexError: - itemContext = None - for menuHandler in cls.menus: - # loop through registered menus - m = menuHandler() - if m.display(srcContext, selection): - amount += 1 - texts = m.getText(itemContext, selection) - - if isinstance(texts, basestring): - texts = (texts,) - - bitmap = m.getBitmap(srcContext, selection) - multiple = not isinstance(bitmap, wx.Bitmap) - for it, text in enumerate(texts): - id = cls.nextID() - rootItem = wx.MenuItem(rootMenu, id, text) - rootMenu.info[id] = (m, fullContext, it) - - sub = m.getSubMenu(srcContext, selection, rootMenu, it, rootItem) - - if sub is None: - # if there is no sub menu, bind the handler to the rootItem - rootMenu.Bind(wx.EVT_MENU, cls.handler, rootItem) - elif sub: - # If sub exists and is not False, set submenu. - # Sub might return False when we have a mix of - # single menu items and submenus (see: damage profile - # context menu) - # - # If there is a submenu, it is expected that the sub - # logic take care of it's own bindings, including for - # any single root items. No binding is done here - # - # It is important to remember that when binding sub - # menu items, the menu to bind to depends on platform. - # Windows should bind to rootMenu, and all other - # platforms should bind to sub menu. See existing - # implementations for examples. - rootItem.SetSubMenu(sub) - - if bitmap is not None: - if multiple: - bp = bitmap[it] - if bp: - rootItem.SetBitmap(bp) - else: - rootItem.SetBitmap(bitmap) - - rootMenu.AppendItem(rootItem) - - empty = False - - if amount > 0 and i != len(fullContexts) - 1: - rootMenu.AppendSeparator() - - debug_end = len(cls._ids) - if (debug_end - debug_start): - logger.debug("%d new IDs created for this menu" % (debug_end - debug_start)) - - return rootMenu if empty is False else None - - @classmethod - def handler(cls, event): - menu = event.EventObject - stuff = menu.info.get(event.Id) - if stuff is not None: - menuHandler, context, i = stuff - selection = menu.selection - if not hasattr(selection, "__iter__"): - selection = (selection,) - - menuHandler.activate(context, selection, i) - else: - event.Skip() - - def display(self, context, selection): - raise NotImplementedError() - - def activate(self, fullContext, selection, i): - return None - - def getSubMenu(self, context, selection, rootMenu, i, pitem): - return None - - @classmethod - def nextID(cls): - """ - Fetches an ID from the pool of IDs allocated to Context Menu. - If we don't have enough ID's to fulfill request, create new - ID and add it to the pool. - - See GH Issue #589 - """ - cls._idxid += 1 - - if cls._idxid >= len(cls._ids): # We don't ahve an ID for this index, create one - cls._ids.append(wx.NewId()) - - return cls._ids[cls._idxid] - - def getText(self, context, selection): - """ - getText should be implemented in child classes, and should return either - a string that will make up a menu item label or a list of strings which - will make numerous menu items. - - These menu items will be added to the root menu - """ - raise NotImplementedError() - - def getBitmap(self, context, selection): - return None - - -from gui.builtinContextMenus import * +# ============================================================================= +# Copyright (C) 2010 Diego Duclos +# +# This file is part of pyfa. +# +# pyfa is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# pyfa is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with pyfa. If not, see . +# ============================================================================= + +# noinspection PyPackageRequirements +import wx +from logbook import Logger + +pyfalog = Logger(__name__) + + +class ContextMenu(object): + menus = [] + _ids = [] # [wx.NewId() for x in xrange(200)] # init with decent amount + _idxid = -1 + + @classmethod + def register(cls): + ContextMenu.menus.append(cls) + + @classmethod + def getMenu(cls, selection, *fullContexts): + """ + getMenu returns a menu that is used with wx.PopupMenu. + + selection: provides a list of what was selected. If only 1 item was + selected, it's is a 1-item list or tuple. Can also be None for + contexts without selection, such as statsPane or projected view + fullContexts: a number of tuples of the following tuple: + srcContext - context were menu was spawned, eg: projectedFit, + cargoItem, etc + itemContext - usually the name of the item's category + + eg: + (('fittingModule', 'Module'), ('fittingShip', 'Ship')) + (('marketItemGroup', 'Implant'),) + (('fittingShip', 'Ship'),) + """ + cls._idxid = -1 + debug_start = len(cls._ids) + + rootMenu = wx.Menu() + rootMenu.info = {} + rootMenu.selection = (selection,) if not hasattr(selection, "__iter__") else selection + empty = True + for i, fullContext in enumerate(fullContexts): + display_amount = 0 + srcContext = fullContext[0] + try: + itemContext = fullContext[1] + except IndexError: + itemContext = None + for menuHandler in cls.menus: + # loop through registered menus + m = menuHandler() + if m.display(srcContext, selection): + display_amount += 1 + texts = m.getText(itemContext, selection) + + if isinstance(texts, basestring): + texts = (texts,) + + bitmap = m.getBitmap(srcContext, selection) + multiple = not isinstance(bitmap, wx.Bitmap) + for it, text in enumerate(texts): + id = cls.nextID() + rootItem = wx.MenuItem(rootMenu, id, text) + rootMenu.info[id] = (m, fullContext, it) + + sub = m.getSubMenu(srcContext, selection, rootMenu, it, rootItem) + + if sub is None: + # if there is no sub menu, bind the handler to the rootItem + rootMenu.Bind(wx.EVT_MENU, cls.handler, rootItem) + elif sub: + # If sub exists and is not False, set submenu. + # Sub might return False when we have a mix of + # single menu items and submenus (see: damage profile + # context menu) + # + # If there is a submenu, it is expected that the sub + # logic take care of it's own bindings, including for + # any single root items. No binding is done here + # + # It is important to remember that when binding sub + # menu items, the menu to bind to depends on platform. + # Windows should bind to rootMenu, and all other + # platforms should bind to sub menu. See existing + # implementations for examples. + rootItem.SetSubMenu(sub) + + if bitmap is not None: + if multiple: + bp = bitmap[it] + if bp: + rootItem.SetBitmap(bp) + else: + rootItem.SetBitmap(bitmap) + + rootMenu.AppendItem(rootItem) + + empty = False + + if display_amount > 0 and i != len(fullContexts) - 1: + rootMenu.AppendSeparator() + + debug_end = len(cls._ids) + if debug_end - debug_start: + pyfalog.debug("{0} new IDs created for this menu", (debug_end - debug_start)) + + return rootMenu if empty is False else None + + @classmethod + def handler(cls, event): + menu = event.EventObject + stuff = menu.info.get(event.Id) + if stuff is not None: + menuHandler, context, i = stuff + selection = menu.selection + if not hasattr(selection, "__iter__"): + selection = (selection,) + + menuHandler.activate(context, selection, i) + else: + event.Skip() + + def display(self, context, selection): + raise NotImplementedError() + + def activate(self, fullContext, selection, i): + return None + + def getSubMenu(self, context, selection, rootMenu, i, pitem): + return None + + @classmethod + def nextID(cls): + """ + Fetches an ID from the pool of IDs allocated to Context Menu. + If we don't have enough ID's to fulfill request, create new + ID and add it to the pool. + + See GH Issue #589 + """ + cls._idxid += 1 + + if cls._idxid >= len(cls._ids): # We don't ahve an ID for this index, create one + cls._ids.append(wx.NewId()) + + return cls._ids[cls._idxid] + + def getText(self, context, selection): + """ + getText should be implemented in child classes, and should return either + a string that will make up a menu item label or a list of strings which + will make numerous menu items. + + These menu items will be added to the root menu + """ + raise NotImplementedError() + + def getBitmap(self, context, selection): + return None + + +# noinspection PyUnresolvedReferences +from gui.builtinContextMenus import ( # noqa: E402,F401 + openFit, + # moduleGlobalAmmoPicker, + moduleAmmoPicker, + itemStats, + damagePattern, + marketJump, + droneSplit, + itemRemove, + droneRemoveStack, + ammoPattern, + project, + factorReload, + whProjector, + cargo, + shipJump, + changeAffectingSkills, + tacticalMode, + targetResists, + priceClear, + amount, + metaSwap, + implantSets, + fighterAbilities, +) diff --git a/gui/copySelectDialog.py b/gui/copySelectDialog.py index ea2e44620..57ce48f47 100644 --- a/gui/copySelectDialog.py +++ b/gui/copySelectDialog.py @@ -1,4 +1,4 @@ -#=============================================================================== +# ============================================================================= # Copyright (C) 2010 Lucas Thode # # This file is part of pyfa. @@ -15,11 +15,13 @@ # # You should have received a copy of the GNU General Public License # along with pyfa. If not, see . -#=============================================================================== +# ============================================================================= +# noinspection PyPackageRequirements import wx + class CopySelectDialog(wx.Dialog): copyFormatEft = 0 copyFormatEftImps = 1 @@ -29,7 +31,8 @@ class CopySelectDialog(wx.Dialog): copyFormatMultiBuy = 5 def __init__(self, parent): - wx.Dialog.__init__(self, parent, id = wx.ID_ANY, title = u"Select a format", size = (-1,-1), style = wx.DEFAULT_DIALOG_STYLE) + wx.Dialog.__init__(self, parent, id=wx.ID_ANY, title=u"Select a format", size=(-1, -1), + style=wx.DEFAULT_DIALOG_STYLE) mainSizer = wx.BoxSizer(wx.VERTICAL) copyFormats = [u"EFT", u"EFT (Implants)", u"XML", u"DNA", u"CREST", u"MultiBuy"] @@ -39,7 +42,8 @@ class CopySelectDialog(wx.Dialog): CopySelectDialog.copyFormatDna: u"A one-line text format", CopySelectDialog.copyFormatCrest: u"A JSON format used for EVE CREST", CopySelectDialog.copyFormatMultiBuy: u"MultiBuy text format"} - selector = wx.RadioBox(self, wx.ID_ANY, label = u"Copy to the clipboard using:", choices = copyFormats, style = wx.RA_SPECIFY_ROWS) + selector = wx.RadioBox(self, wx.ID_ANY, label=u"Copy to the clipboard using:", choices=copyFormats, + style=wx.RA_SPECIFY_ROWS) selector.Bind(wx.EVT_RADIOBOX, self.Selected) for format, tooltip in copyFormatTooltips.iteritems(): selector.SetItemToolTip(format, tooltip) @@ -47,21 +51,18 @@ class CopySelectDialog(wx.Dialog): self.copyFormat = CopySelectDialog.copyFormatEft selector.SetSelection(self.copyFormat) - mainSizer.Add(selector,0,wx.EXPAND | wx.ALL, 5) + mainSizer.Add(selector, 0, wx.EXPAND | wx.ALL, 5) buttonSizer = self.CreateButtonSizer(wx.OK | wx.CANCEL) - if (buttonSizer): - mainSizer.Add(buttonSizer,0, wx.EXPAND | wx.ALL, 5) + if buttonSizer: + mainSizer.Add(buttonSizer, 0, wx.EXPAND | wx.ALL, 5) self.SetSizer(mainSizer) self.Fit() self.Center() - def Selected(self, event): self.copyFormat = event.GetSelection() def GetSelected(self): return self.copyFormat - - diff --git a/gui/crestFittings.py b/gui/crestFittings.py index 0b2cf54cf..0f1911206 100644 --- a/gui/crestFittings.py +++ b/gui/crestFittings.py @@ -1,66 +1,74 @@ import time import webbrowser import json +# noinspection PyPackageRequirements import wx import requests -import service -from service.crest import CrestModes +from service.port import Port +from service.fit import Fit -from eos.types import Cargo +from eos.saveddata.cargo import Cargo from eos.db import getItem -import gui.display as d +from gui.display import Display import gui.globalEvents as GE +from logbook import Logger +pyfalog = Logger(__name__) + +if 'wxMac' not in wx.PlatformInfo or ('wxMac' in wx.PlatformInfo and wx.VERSION >= (3, 0)): + from service.crest import Crest, CrestModes + class CrestFittings(wx.Frame): - def __init__(self, parent): - wx.Frame.__init__(self, parent, id=wx.ID_ANY, title="Browse EVE Fittings", pos=wx.DefaultPosition, size=wx.Size( 550,450 ), style=wx.DEFAULT_FRAME_STYLE | wx.TAB_TRAVERSAL) + wx.Frame.__init__(self, parent, id=wx.ID_ANY, title="Browse EVE Fittings", pos=wx.DefaultPosition, + size=wx.Size(550, 450), style=wx.DEFAULT_FRAME_STYLE | wx.TAB_TRAVERSAL) self.SetBackgroundColour(wx.SystemSettings.GetColour(wx.SYS_COLOUR_BTNFACE)) self.mainFrame = parent mainSizer = wx.BoxSizer(wx.VERTICAL) - sCrest = service.Crest.getInstance() + sCrest = Crest.getInstance() - characterSelectSizer = wx.BoxSizer( wx.HORIZONTAL ) + characterSelectSizer = wx.BoxSizer(wx.HORIZONTAL) if sCrest.settings.get('mode') == CrestModes.IMPLICIT: - self.stLogged = wx.StaticText(self, wx.ID_ANY, "Currently logged in as %s"%sCrest.implicitCharacter.name, wx.DefaultPosition, wx.DefaultSize) - self.stLogged.Wrap( -1 ) + self.stLogged = wx.StaticText(self, wx.ID_ANY, "Currently logged in as %s" % sCrest.implicitCharacter.name, + wx.DefaultPosition, wx.DefaultSize) + self.stLogged.Wrap(-1) - characterSelectSizer.Add( self.stLogged, 1, wx.ALIGN_CENTER_VERTICAL|wx.ALL, 5 ) + characterSelectSizer.Add(self.stLogged, 1, wx.ALIGN_CENTER_VERTICAL | wx.ALL, 5) else: self.charChoice = wx.Choice(self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, []) - characterSelectSizer.Add( self.charChoice, 1, wx.ALIGN_CENTER_VERTICAL|wx.ALL, 5 ) + characterSelectSizer.Add(self.charChoice, 1, wx.ALIGN_CENTER_VERTICAL | wx.ALL, 5) self.updateCharList() - self.fetchBtn = wx.Button( self, wx.ID_ANY, u"Fetch Fits", wx.DefaultPosition, wx.DefaultSize, 5 ) - characterSelectSizer.Add( self.fetchBtn, 0, wx.ALL, 5 ) - mainSizer.Add( characterSelectSizer, 0, wx.EXPAND, 5 ) + self.fetchBtn = wx.Button(self, wx.ID_ANY, u"Fetch Fits", wx.DefaultPosition, wx.DefaultSize, 5) + characterSelectSizer.Add(self.fetchBtn, 0, wx.ALL, 5) + mainSizer.Add(characterSelectSizer, 0, wx.EXPAND, 5) - self.sl = wx.StaticLine( self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LI_HORIZONTAL ) - mainSizer.Add( self.sl, 0, wx.EXPAND |wx.ALL, 5 ) + self.sl = wx.StaticLine(self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LI_HORIZONTAL) + mainSizer.Add(self.sl, 0, wx.EXPAND | wx.ALL, 5) - contentSizer = wx.BoxSizer( wx.HORIZONTAL ) - browserSizer = wx.BoxSizer( wx.VERTICAL ) + contentSizer = wx.BoxSizer(wx.HORIZONTAL) + browserSizer = wx.BoxSizer(wx.VERTICAL) self.fitTree = FittingsTreeView(self) - browserSizer.Add( self.fitTree, 1, wx.ALL|wx.EXPAND, 5 ) - contentSizer.Add( browserSizer, 1, wx.EXPAND, 0 ) - fitSizer = wx.BoxSizer( wx.VERTICAL ) + browserSizer.Add(self.fitTree, 1, wx.ALL | wx.EXPAND, 5) + contentSizer.Add(browserSizer, 1, wx.EXPAND, 0) + fitSizer = wx.BoxSizer(wx.VERTICAL) self.fitView = FitView(self) - fitSizer.Add( self.fitView, 1, wx.ALL|wx.EXPAND, 5 ) + fitSizer.Add(self.fitView, 1, wx.ALL | wx.EXPAND, 5) - btnSizer = wx.BoxSizer( wx.HORIZONTAL ) - self.importBtn = wx.Button( self, wx.ID_ANY, u"Import to pyfa", wx.DefaultPosition, wx.DefaultSize, 5 ) - self.deleteBtn = wx.Button( self, wx.ID_ANY, u"Delete from EVE", wx.DefaultPosition, wx.DefaultSize, 5 ) - btnSizer.Add( self.importBtn, 1, wx.ALL, 5 ) - btnSizer.Add( self.deleteBtn, 1, wx.ALL, 5 ) - fitSizer.Add( btnSizer, 0, wx.EXPAND ) + btnSizer = wx.BoxSizer(wx.HORIZONTAL) + self.importBtn = wx.Button(self, wx.ID_ANY, u"Import to pyfa", wx.DefaultPosition, wx.DefaultSize, 5) + self.deleteBtn = wx.Button(self, wx.ID_ANY, u"Delete from EVE", wx.DefaultPosition, wx.DefaultSize, 5) + btnSizer.Add(self.importBtn, 1, wx.ALL, 5) + btnSizer.Add(self.deleteBtn, 1, wx.ALL, 5) + fitSizer.Add(btnSizer, 0, wx.EXPAND) contentSizer.Add(fitSizer, 1, wx.EXPAND, 0) mainSizer.Add(contentSizer, 1, wx.EXPAND, 5) @@ -90,7 +98,7 @@ class CrestFittings(wx.Frame): event.Skip() def updateCharList(self): - sCrest = service.Crest.getInstance() + sCrest = Crest.getInstance() chars = sCrest.getCrestCharacters() if len(chars) == 0: @@ -103,12 +111,12 @@ class CrestFittings(wx.Frame): self.charChoice.SetSelection(0) def updateCacheStatus(self, event): - t = time.gmtime(self.cacheTime-time.time()) + t = time.gmtime(self.cacheTime - time.time()) if t < 0: self.cacheTimer.Stop() else: sTime = time.strftime("%H:%M:%S", t) - self.statusbar.SetStatusText("Cached for %s"%sTime, 0) + self.statusbar.SetStatusText("Cached for %s" % sTime, 0) def ssoLogout(self, event): if event.type == CrestModes.IMPLICIT: @@ -123,7 +131,7 @@ class CrestFittings(wx.Frame): event.Skip() def getActiveCharacter(self): - sCrest = service.Crest.getInstance() + sCrest = Crest.getInstance() if sCrest.settings.get('mode') == CrestModes.IMPLICIT: return sCrest.implicitCharacter.ID @@ -132,7 +140,7 @@ class CrestFittings(wx.Frame): return self.charChoice.GetClientData(selection) if selection is not None else None def fetchFittings(self, event): - sCrest = service.Crest.getInstance() + sCrest = Crest.getInstance() try: waitDialog = wx.BusyInfo("Fetching fits, please wait...", parent=self) fittings = sCrest.getFittings(self.getActiveCharacter()) @@ -140,65 +148,69 @@ class CrestFittings(wx.Frame): self.updateCacheStatus(None) self.cacheTimer.Start(1000) self.fitTree.populateSkillTree(fittings) - except requests.exceptions.ConnectionError: - self.statusbar.SetStatusText("Connection error, please check your internet connection") - finally: del waitDialog + except requests.exceptions.ConnectionError: + msg = "Connection error, please check your internet connection" + pyfalog.error(msg) + self.statusbar.SetStatusText(msg) def importFitting(self, event): selection = self.fitView.fitSelection if not selection: return data = self.fitTree.fittingsTreeCtrl.GetPyData(selection) - sFit = service.Fit.getInstance() - fits = sFit.importFitFromBuffer(data) + sPort = Port.getInstance() + fits = sPort.importFitFromBuffer(data) self.mainFrame._openAfterImport(fits) def deleteFitting(self, event): - sCrest = service.Crest.getInstance() + sCrest = Crest.getInstance() selection = self.fitView.fitSelection if not selection: return data = json.loads(self.fitTree.fittingsTreeCtrl.GetPyData(selection)) dlg = wx.MessageDialog(self, - "Do you really want to delete %s (%s) from EVE?"%(data['name'], data['ship']['name']), - "Confirm Delete", wx.YES | wx.NO | wx.ICON_QUESTION) + "Do you really want to delete %s (%s) from EVE?" % (data['name'], data['ship']['name']), + "Confirm Delete", wx.YES | wx.NO | wx.ICON_QUESTION) if dlg.ShowModal() == wx.ID_YES: try: sCrest.delFitting(self.getActiveCharacter(), data['fittingID']) except requests.exceptions.ConnectionError: - self.statusbar.SetStatusText("Connection error, please check your internet connection") + msg = "Connection error, please check your internet connection" + pyfalog.error(msg) + self.statusbar.SetStatusText(msg) class ExportToEve(wx.Frame): - def __init__(self, parent): - wx.Frame.__init__(self, parent, id=wx.ID_ANY, title="Export fit to EVE", pos=wx.DefaultPosition, size=(wx.Size(350,100)), style=wx.DEFAULT_FRAME_STYLE | wx.TAB_TRAVERSAL) + wx.Frame.__init__(self, parent, id=wx.ID_ANY, title="Export fit to EVE", pos=wx.DefaultPosition, + size=(wx.Size(350, 100)), style=wx.DEFAULT_FRAME_STYLE | wx.TAB_TRAVERSAL) self.mainFrame = parent self.SetBackgroundColour(wx.SystemSettings.GetColour(wx.SYS_COLOUR_BTNFACE)) - sCrest = service.Crest.getInstance() + sCrest = Crest.getInstance() mainSizer = wx.BoxSizer(wx.VERTICAL) hSizer = wx.BoxSizer(wx.HORIZONTAL) if sCrest.settings.get('mode') == CrestModes.IMPLICIT: - self.stLogged = wx.StaticText(self, wx.ID_ANY, "Currently logged in as %s"%sCrest.implicitCharacter.name, wx.DefaultPosition, wx.DefaultSize) - self.stLogged.Wrap( -1 ) + self.stLogged = wx.StaticText(self, wx.ID_ANY, "Currently logged in as %s" % sCrest.implicitCharacter.name, + wx.DefaultPosition, wx.DefaultSize) + self.stLogged.Wrap(-1) - hSizer.Add( self.stLogged, 1, wx.ALIGN_CENTER_VERTICAL|wx.ALL, 5 ) + hSizer.Add(self.stLogged, 1, wx.ALIGN_CENTER_VERTICAL | wx.ALL, 5) else: self.charChoice = wx.Choice(self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, []) - hSizer.Add( self.charChoice, 1, wx.ALIGN_CENTER_VERTICAL|wx.ALL, 5 ) + hSizer.Add(self.charChoice, 1, wx.ALIGN_CENTER_VERTICAL | wx.ALL, 5) self.updateCharList() self.charChoice.SetSelection(0) - self.exportBtn = wx.Button( self, wx.ID_ANY, u"Export Fit", wx.DefaultPosition, wx.DefaultSize, 5 ) - hSizer.Add( self.exportBtn, 0, wx.ALL, 5 ) + self.exportBtn = wx.Button(self, wx.ID_ANY, u"Export Fit", wx.DefaultPosition, wx.DefaultSize, 5) + hSizer.Add(self.exportBtn, 0, wx.ALL, 5) - mainSizer.Add( hSizer, 0, wx.EXPAND, 5 ) + mainSizer.Add(hSizer, 0, wx.EXPAND, 5) self.exportBtn.Bind(wx.EVT_BUTTON, self.exportFitting) @@ -217,7 +229,7 @@ class ExportToEve(wx.Frame): self.Centre(wx.BOTH) def updateCharList(self): - sCrest = service.Crest.getInstance() + sCrest = Crest.getInstance() chars = sCrest.getCrestCharacters() if len(chars) == 0: @@ -245,7 +257,7 @@ class ExportToEve(wx.Frame): event.Skip() def getActiveCharacter(self): - sCrest = service.Crest.getInstance() + sCrest = Crest.getInstance() if sCrest.settings.get('mode') == CrestModes.IMPLICIT: return sCrest.implicitCharacter.ID @@ -254,7 +266,7 @@ class ExportToEve(wx.Frame): return self.charChoice.GetClientData(selection) if selection is not None else None def exportFitting(self, event): - sFit = service.Fit.getInstance() + sPort = Port.getInstance() fitID = self.mainFrame.getActiveFit() self.statusbar.SetStatusText("", 0) @@ -264,64 +276,68 @@ class ExportToEve(wx.Frame): return self.statusbar.SetStatusText("Sending request and awaiting response", 1) - sCrest = service.Crest.getInstance() + sCrest = Crest.getInstance() try: - data = sFit.exportCrest(fitID) + sFit = Fit.getInstance() + data = sPort.exportCrest(sFit.getFit(fitID)) res = sCrest.postFitting(self.getActiveCharacter(), data) - self.statusbar.SetStatusText("%d: %s"%(res.status_code, res.reason), 0) + self.statusbar.SetStatusText("%d: %s" % (res.status_code, res.reason), 0) try: text = json.loads(res.text) self.statusbar.SetStatusText(text['message'], 1) except ValueError: + pyfalog.warning("Value error on loading JSON.") self.statusbar.SetStatusText("", 1) except requests.exceptions.ConnectionError: - self.statusbar.SetStatusText("Connection error, please check your internet connection", 1) + msg = "Connection error, please check your internet connection" + pyfalog.error(msg) + self.statusbar.SetStatusText(msg) class CrestMgmt(wx.Dialog): - - def __init__( self, parent ): - wx.Dialog.__init__ ( self, parent, id = wx.ID_ANY, title = "CREST Character Management", pos = wx.DefaultPosition, size = wx.Size( 550,250 ), style = wx.DEFAULT_DIALOG_STYLE ) + def __init__(self, parent): + wx.Dialog.__init__(self, parent, id=wx.ID_ANY, title="CREST Character Management", pos=wx.DefaultPosition, + size=wx.Size(550, 250), style=wx.DEFAULT_DIALOG_STYLE) self.mainFrame = parent - mainSizer = wx.BoxSizer( wx.HORIZONTAL ) + mainSizer = wx.BoxSizer(wx.HORIZONTAL) - self.lcCharacters = wx.ListCtrl( self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LC_REPORT) + self.lcCharacters = wx.ListCtrl(self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LC_REPORT) self.lcCharacters.InsertColumn(0, heading='Character') self.lcCharacters.InsertColumn(1, heading='Refresh Token') self.popCharList() - mainSizer.Add( self.lcCharacters, 1, wx.ALL|wx.EXPAND, 5 ) + mainSizer.Add(self.lcCharacters, 1, wx.ALL | wx.EXPAND, 5) - btnSizer = wx.BoxSizer( wx.VERTICAL ) + btnSizer = wx.BoxSizer(wx.VERTICAL) - self.addBtn = wx.Button( self, wx.ID_ANY, u"Add Character", wx.DefaultPosition, wx.DefaultSize, 0 ) - btnSizer.Add( self.addBtn, 0, wx.ALL | wx.EXPAND, 5 ) + self.addBtn = wx.Button(self, wx.ID_ANY, u"Add Character", wx.DefaultPosition, wx.DefaultSize, 0) + btnSizer.Add(self.addBtn, 0, wx.ALL | wx.EXPAND, 5) - self.deleteBtn = wx.Button( self, wx.ID_ANY, u"Revoke Character", wx.DefaultPosition, wx.DefaultSize, 0 ) - btnSizer.Add( self.deleteBtn, 0, wx.ALL | wx.EXPAND, 5 ) + self.deleteBtn = wx.Button(self, wx.ID_ANY, u"Revoke Character", wx.DefaultPosition, wx.DefaultSize, 0) + btnSizer.Add(self.deleteBtn, 0, wx.ALL | wx.EXPAND, 5) - mainSizer.Add( btnSizer, 0, wx.EXPAND, 5 ) + mainSizer.Add(btnSizer, 0, wx.EXPAND, 5) self.addBtn.Bind(wx.EVT_BUTTON, self.addChar) self.deleteBtn.Bind(wx.EVT_BUTTON, self.delChar) self.mainFrame.Bind(GE.EVT_SSO_LOGIN, self.ssoLogin) - self.SetSizer( mainSizer ) + self.SetSizer(mainSizer) self.Layout() - self.Centre( wx.BOTH ) + self.Centre(wx.BOTH) def ssoLogin(self, event): self.popCharList() event.Skip() def popCharList(self): - sCrest = service.Crest.getInstance() + sCrest = Crest.getInstance() chars = sCrest.getCrestCharacters() self.lcCharacters.DeleteAllItems() @@ -334,8 +350,9 @@ class CrestMgmt(wx.Dialog): self.lcCharacters.SetColumnWidth(0, wx.LIST_AUTOSIZE) self.lcCharacters.SetColumnWidth(1, wx.LIST_AUTOSIZE) - def addChar(self, event): - sCrest = service.Crest.getInstance() + @staticmethod + def addChar(event): + sCrest = Crest.getInstance() uri = sCrest.startServer() webbrowser.open(uri) @@ -343,7 +360,7 @@ class CrestMgmt(wx.Dialog): item = self.lcCharacters.GetFirstSelected() if item > -1: charID = self.lcCharacters.GetItemData(item) - sCrest = service.Crest.getInstance() + sCrest = Crest.getInstance() sCrest.delCrestCharacter(charID) self.popCharList() @@ -398,17 +415,18 @@ class FittingsTreeView(wx.Panel): cargo = Cargo(getItem(item['type']['id'])) cargo.amount = item['quantity'] list.append(cargo) - except: - pass + except Exception as e: + pyfalog.critical("Exception caught in displayFit") + pyfalog.critical(e) self.parent.fitView.fitSelection = selection self.parent.fitView.update(list) -class FitView(d.Display): +class FitView(Display): DEFAULT_COLS = ["Base Icon", "Base Name"] def __init__(self, parent): - d.Display.__init__(self, parent, style=wx.LC_SINGLE_SEL) + Display.__init__(self, parent, style=wx.LC_SINGLE_SEL) self.fitSelection = None diff --git a/gui/display.py b/gui/display.py index 707623581..2ae83aa44 100644 --- a/gui/display.py +++ b/gui/display.py @@ -1,4 +1,4 @@ -#=============================================================================== +# ============================================================================= # Copyright (C) 2010 Diego Duclos # # This file is part of pyfa. @@ -15,19 +15,22 @@ # # You should have received a copy of the GNU General Public License # along with pyfa. If not, see . -#=============================================================================== +# ============================================================================= import sys +# noinspection PyPackageRequirements import wx import gui.mainFrame - from gui.viewColumn import ViewColumn from gui.cachingImageList import CachingImageList -class Display(wx.ListCtrl): - def __init__(self, parent, size = wx.DefaultSize, style = 0): - wx.ListCtrl.__init__(self, parent,size = size, style=wx.LC_REPORT | style ) +class Display(wx.ListCtrl): + DEFAULT_COLS = None + + def __init__(self, parent, size=wx.DefaultSize, style=0): + + wx.ListCtrl.__init__(self, parent, size=size, style=wx.LC_REPORT | style) self.imageList = CachingImageList(16, 16) self.SetImageList(self.imageList, wx.IMAGE_LIST_SMALL) self.activeColumns = [] @@ -64,15 +67,15 @@ class Display(wx.ListCtrl): i += 1 info = wx.ListItem() + # noinspection PyPropertyAccess info.m_mask = wx.LIST_MASK_WIDTH self.InsertColumnInfo(i, info) self.SetColumnWidth(i, 0) self.imageListBase = self.imageList.ImageCount - -# Override native HitTestSubItem (doesn't work as it should on GTK) -# Source: ObjectListView + # Override native HitTestSubItem (doesn't work as it should on GTK) + # Source: ObjectListView def HitTestSubItem(self, pt): """ @@ -89,11 +92,11 @@ class Display(wx.ListCtrl): # Did the point hit any item? if (flags & wx.LIST_HITTEST_ONITEM) == 0: - return (-1, 0, -1) + return -1, 0, -1 # If it did hit an item and we are not in report mode, it must be the primary cell if not self.InReportView(): - return (rowIndex, wx.LIST_HITTEST_ONITEM, 0) + return rowIndex, wx.LIST_HITTEST_ONITEM, 0 # Find which subitem is hit right = 0 @@ -106,19 +109,18 @@ class Display(wx.ListCtrl): flag = wx.LIST_HITTEST_ONITEMICON else: flag = wx.LIST_HITTEST_ONITEMLABEL - return (rowIndex, flag, i) + return rowIndex, flag, i - return (rowIndex, 0, -1) + return rowIndex, 0, -1 - - def OnEraseBk(self,event): - if self.GetItemCount() >0: + def OnEraseBk(self, event): + if self.GetItemCount() > 0: width, height = self.GetClientSize() dc = event.GetDC() dc.DestroyClippingRegion() dc.SetClippingRegion(0, 0, width, height) - x,y,w,h = dc.GetClippingBox() + x, y, w, h = dc.GetClippingBox() topItem = self.GetTopItem() bottomItem = topItem + self.GetCountPerPage() @@ -129,10 +131,9 @@ class Display(wx.ListCtrl): topRect = self.GetItemRect(topItem, wx.LIST_RECT_LABEL) bottomRect = self.GetItemRect(bottomItem, wx.LIST_RECT_BOUNDS) + items_rect = wx.Rect(topRect.left, 0, bottomRect.right - topRect.left, bottomRect.bottom) - items_rect = wx.Rect(topRect.left, 0, bottomRect.right - topRect.left, bottomRect.bottom ) - - updateRegion = wx.Region(x,y,w,h) + updateRegion = wx.Region(x, y, w, h) updateRegion.SubtractRect(items_rect) dc.DestroyClippingRegion() @@ -146,6 +147,7 @@ class Display(wx.ListCtrl): else: event.Skip() + # noinspection PyPropertyAccess def addColumn(self, i, col): self.activeColumns.append(col) info = wx.ListItem() @@ -171,41 +173,37 @@ class Display(wx.ListCtrl): # we veto header cell resize by default till we find a way # to assure a minimal size for the resized header cell column = event.GetColumn() - wx.CallAfter(self.checkColumnSize,column) + wx.CallAfter(self.checkColumnSize, column) event.Skip() def resizeSkip(self, event): column = event.GetColumn() - if column > len (self.activeColumns)-1: + if column > len(self.activeColumns) - 1: self.SetColumnWidth(column, 0) event.Veto() return - colItem = self.activeColumns[column] + # colItem = self.activeColumns[column] if self.activeColumns[column].maxsize != -1: event.Veto() else: event.Skip() - def checkColumnSize(self,column): + def checkColumnSize(self, column): colItem = self.activeColumns[column] if self.GetColumnWidth(column) < self.columnsMinWidth[column]: - self.SetColumnWidth(column,self.columnsMinWidth[column]) + self.SetColumnWidth(column, self.columnsMinWidth[column]) colItem.resized = True - def getLastItem( self, state = wx.LIST_STATE_DONTCARE): - lastFound = -1 - while True: - index = self.GetNextItem( - lastFound, - wx.LIST_NEXT_ALL, - state, - ) - if index == -1: - break - else: - lastFound = index + def getLastItem(self, state=wx.LIST_STATE_DONTCARE): + lastFound = -1 + while True: + index = self.GetNextItem(lastFound, wx.LIST_NEXT_ALL, state) + if index == -1: + break + else: + lastFound = index - return lastFound + return lastFound def deselectItems(self): sel = self.GetFirstSelected() @@ -220,26 +218,25 @@ class Display(wx.ListCtrl): stuffItemCount = len(stuff) if listItemCount < stuffItemCount: - for i in xrange(stuffItemCount - listItemCount): - index = self.InsertStringItem(sys.maxint, "") + for i in range(stuffItemCount - listItemCount): + self.InsertStringItem(sys.maxint, "") if listItemCount > stuffItemCount: - if listItemCount - stuffItemCount > 20 and stuffItemCount < 20: + if listItemCount - stuffItemCount > 20 > stuffItemCount: self.DeleteAllItems() - for i in xrange(stuffItemCount): - index = self.InsertStringItem(sys.maxint, "") + for i in range(stuffItemCount): + self.InsertStringItem(sys.maxint, "") else: - for i in xrange(listItemCount - stuffItemCount): + for i in range(listItemCount - stuffItemCount): self.DeleteItem(self.getLastItem()) self.Refresh() - def refresh(self, stuff): - if stuff == None: + if stuff is None: return item = -1 - for id, st in enumerate(stuff): + for id_, st in enumerate(stuff): item = self.GetNextItem(item) @@ -270,11 +267,11 @@ class Display(wx.ListCtrl): colItem.SetMask(mask) self.SetItem(colItem) - self.SetItemData(item, id) + self.SetItemData(item, id_) -# self.Freeze() + # self.Freeze() if 'wxMSW' in wx.PlatformInfo: - for i,col in enumerate(self.activeColumns): + for i, col in enumerate(self.activeColumns): if not col.resized: self.SetColumnWidth(i, col.size) else: @@ -289,9 +286,7 @@ class Display(wx.ListCtrl): self.SetColumnWidth(i, headerWidth) else: self.SetColumnWidth(i, col.size) -# self.Thaw() - - + # self.Thaw() def update(self, stuff): self.populate(stuff) diff --git a/gui/droneView.py b/gui/droneView.py index 531c28d05..e506c278d 100644 --- a/gui/droneView.py +++ b/gui/droneView.py @@ -1,4 +1,4 @@ -#=============================================================================== +# ============================================================================= # Copyright (C) 2010 Diego Duclos # # This file is part of pyfa. @@ -15,43 +15,49 @@ # # You should have received a copy of the GNU General Public License # along with pyfa. If not, see . -#=============================================================================== +# ============================================================================= +# noinspection PyPackageRequirements import wx -import service import gui.globalEvents as GE -import gui.marketBrowser as mb -import gui.display as d +from gui.marketBrowser import ITEM_SELECTED, ItemSelected +from gui.display import Display from gui.builtinViewColumns.state import State from gui.contextMenu import ContextMenu +from service.fit import Fit +from service.market import Market + class DroneViewDrop(wx.PyDropTarget): - def __init__(self, dropFn): - wx.PyDropTarget.__init__(self) - self.dropFn = dropFn - # this is really transferring an EVE itemID - self.dropData = wx.PyTextDataObject() - self.SetDataObject(self.dropData) + def __init__(self, dropFn, *args, **kwargs): + super(DroneViewDrop, self).__init__(*args, **kwargs) + self.dropFn = dropFn + # this is really transferring an EVE itemID + self.dropData = wx.PyTextDataObject() + self.SetDataObject(self.dropData) - def OnData(self, x, y, t): - if self.GetData(): - data = self.dropData.GetText().split(':') - self.dropFn(x, y, data) - return t + def OnData(self, x, y, t): + if self.GetData(): + data = self.dropData.GetText().split(':') + self.dropFn(x, y, data) + return t -class DroneView(d.Display): - DEFAULT_COLS = ["State", - #"Base Icon", - "Base Name", - # "prop:droneDps,droneBandwidth", - "Max Range", - "Miscellanea", - "attr:maxVelocity", - "Price",] + +class DroneView(Display): + DEFAULT_COLS = [ + "State", + # "Base Icon", + "Base Name", + # "prop:droneDps,droneBandwidth", + "Max Range", + "Miscellanea", + "attr:maxVelocity", + "Price", + ] def __init__(self, parent): - d.Display.__init__(self, parent, style=wx.LC_SINGLE_SEL | wx.BORDER_NONE) + Display.__init__(self, parent, style=wx.LC_SINGLE_SEL | wx.BORDER_NONE) self.lastFitId = None @@ -59,19 +65,18 @@ class DroneView(d.Display): self.hoveredColumn = None self.mainFrame.Bind(GE.FIT_CHANGED, self.fitChanged) - self.mainFrame.Bind(mb.ITEM_SELECTED, self.addItem) + self.mainFrame.Bind(ITEM_SELECTED, self.addItem) self.Bind(wx.EVT_LEFT_DCLICK, self.removeItem) self.Bind(wx.EVT_LEFT_DOWN, self.click) self.Bind(wx.EVT_KEY_UP, self.kbEvent) self.Bind(wx.EVT_MOTION, self.OnMouseMove) self.Bind(wx.EVT_LEAVE_WINDOW, self.OnLeaveWindow) - if "__WXGTK__" in wx.PlatformInfo: + if "__WXGTK__" in wx.PlatformInfo: self.Bind(wx.EVT_RIGHT_UP, self.scheduleMenu) else: self.Bind(wx.EVT_RIGHT_DOWN, self.scheduleMenu) - self.Bind(wx.EVT_LIST_BEGIN_DRAG, self.startDrag) self.SetDropTarget(DroneViewDrop(self.handleDragDrop)) @@ -107,7 +112,6 @@ class DroneView(d.Display): keycode = event.GetKeyCode() if keycode == wx.WXK_DELETE or keycode == wx.WXK_NUMPAD_DELETE: row = self.GetFirstSelected() - firstSel = row if row != -1: drone = self.drones[self.GetItemData(row)] self.removeDrone(drone) @@ -118,31 +122,32 @@ class DroneView(d.Display): row = event.GetIndex() if row != -1: data = wx.PyTextDataObject() - data.SetText("drone:"+str(row)) + data.SetText("drone:" + str(row)) dropSource = wx.DropSource(self) dropSource.SetData(data) - res = dropSource.DoDragDrop() + dropSource.DoDragDrop() def handleDragDrop(self, x, y, data): - ''' + """ Handles dragging of items from various pyfa displays which support it data is list with two indices: data[0] is hard-coded str of originating source data[1] is typeID or index of data we want to manipulate - ''' + """ if data[0] == "drone": # we want to merge drones srcRow = int(data[1]) dstRow, _ = self.HitTest((x, y)) if srcRow != -1 and dstRow != -1: self._merge(srcRow, dstRow) elif data[0] == "market": - wx.PostEvent(self.mainFrame, mb.ItemSelected(itemID=int(data[1]))) + wx.PostEvent(self.mainFrame, ItemSelected(itemID=int(data[1]))) def _merge(self, src, dst): - sFit = service.Fit.getInstance() + sFit = Fit.getInstance() fitID = self.mainFrame.getActiveFit() + if sFit.mergeDrones(fitID, self.drones[src], self.drones[dst]): wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=fitID)) @@ -150,8 +155,9 @@ class DroneView(d.Display): 'Heavy Attack Drones', 'Sentry Drones', 'Fighters', 'Fighter Bombers', 'Combat Utility Drones', 'Electronic Warfare Drones', 'Logistic Drones', 'Mining Drones', 'Salvage Drones') + def droneKey(self, drone): - sMkt = service.Market.getInstance() + sMkt = Market.getInstance() groupName = sMkt.getMarketGroupByItem(drone.item).name @@ -159,12 +165,12 @@ class DroneView(d.Display): drone.item.name) def fitChanged(self, event): - sFit = service.Fit.getInstance() + sFit = Fit.getInstance() fit = sFit.getFit(event.fitID) self.Parent.Parent.DisablePage(self, not fit or fit.isStructure) - #Clear list and get out if current fitId is None + # Clear list and get out if current fitId is None if event.fitID is None and self.lastFitId is not None: self.DeleteAllItems() self.lastFitId = None @@ -177,7 +183,6 @@ class DroneView(d.Display): if stuff is not None: stuff.sort(key=self.droneKey) - if event.fitID != self.lastFitId: self.lastFitId = event.fitID @@ -191,9 +196,8 @@ class DroneView(d.Display): self.update(stuff) event.Skip() - def addItem(self, event): - sFit = service.Fit.getInstance() + sFit = Fit.getInstance() fitID = self.mainFrame.getActiveFit() fit = sFit.getFit(fitID) @@ -218,7 +222,7 @@ class DroneView(d.Display): def removeDrone(self, drone): fitID = self.mainFrame.getActiveFit() - sFit = service.Fit.getInstance() + sFit = Fit.getInstance() sFit.removeDrone(fitID, self.original.index(drone)) wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=fitID)) @@ -229,7 +233,7 @@ class DroneView(d.Display): col = self.getColumn(event.Position) if col == self.getColIndex(State): fitID = self.mainFrame.getActiveFit() - sFit = service.Fit.getInstance() + sFit = Fit.getInstance() drone = self.drones[row] sFit.toggleDrone(fitID, self.original.index(drone)) wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=fitID)) @@ -244,7 +248,7 @@ class DroneView(d.Display): if sel != -1: drone = self.drones[sel] - sMkt = service.Market.getInstance() + sMkt = Market.getInstance() sourceContext = "droneItem" itemContext = sMkt.getCategoryByItem(drone.item).name menu = ContextMenu.getMenu((drone,), (sourceContext, itemContext)) diff --git a/gui/errorDialog.py b/gui/errorDialog.py new file mode 100644 index 000000000..469eb9e63 --- /dev/null +++ b/gui/errorDialog.py @@ -0,0 +1,94 @@ +# =============================================================================== +# Copyright (C) 2010 Diego Duclos +# +# This file is part of pyfa. +# +# pyfa is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# pyfa is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with pyfa. If not, see . +# =============================================================================== + +import wx +import sys +import gui.utils.fonts as fonts +import config + + +class ErrorFrame(wx.Frame): + + def __init__(self, exception, tb): + wx.Frame.__init__(self, None, id=wx.ID_ANY, title="pyfa error", pos=wx.DefaultPosition, size=wx.Size(500, 400), + style=wx.DEFAULT_FRAME_STYLE ^ wx.RESIZE_BORDER | wx.STAY_ON_TOP) + + desc = "pyfa has experienced an unexpected error. Below is the " \ + "Traceback that contains crucial information about how this " \ + "error was triggered. Please contact the developers with " \ + "the information provided through the EVE Online forums " \ + "or file a GitHub issue." + + self.SetSizeHintsSz(wx.DefaultSize, wx.DefaultSize) + + if 'wxMSW' in wx.PlatformInfo: + self.SetBackgroundColour(wx.SystemSettings.GetColour(wx.SYS_COLOUR_BTNFACE)) + + mainSizer = wx.BoxSizer(wx.VERTICAL) + headSizer = wx.BoxSizer(wx.HORIZONTAL) + + self.headingText = wx.StaticText(self, wx.ID_ANY, "Error!", wx.DefaultPosition, wx.DefaultSize, wx.ALIGN_CENTRE) + self.headingText.SetFont(wx.Font(14, 74, 90, 92, False)) + + headSizer.Add(self.headingText, 1, wx.ALL, 5) + mainSizer.Add(headSizer, 0, wx.EXPAND, 5) + + mainSizer.Add(wx.StaticLine(self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LI_HORIZONTAL), 0, wx.EXPAND | wx.ALL, 5) + + descSizer = wx.BoxSizer(wx.HORIZONTAL) + self.descText = wx.TextCtrl(self, wx.ID_ANY, desc, wx.DefaultPosition, wx.DefaultSize, + wx.TE_AUTO_URL | wx.TE_MULTILINE | wx.TE_READONLY | wx.BORDER_NONE | wx.TRANSPARENT_WINDOW) + self.descText.SetFont(wx.Font(fonts.BIG, wx.SWISS, wx.NORMAL, wx.NORMAL)) + descSizer.Add(self.descText, 1, wx.ALL, 5) + mainSizer.Add(descSizer, 1, wx.EXPAND, 5) + + self.eveForums = wx.HyperlinkCtrl(self, wx.ID_ANY, "EVE Forums Thread", "https://forums.eveonline.com/default.aspx?g=posts&t=466425", + wx.DefaultPosition, wx.DefaultSize, wx.HL_DEFAULT_STYLE) + + mainSizer.Add(self.eveForums, 0, wx.ALL, 2) + + self.eveForums = wx.HyperlinkCtrl(self, wx.ID_ANY, "Github Issues", "https://github.com/pyfa-org/Pyfa/issues", + wx.DefaultPosition, wx.DefaultSize, wx.HL_DEFAULT_STYLE) + + mainSizer.Add(self.eveForums, 0, wx.ALL, 2) + + # mainSizer.AddSpacer((0, 5), 0, wx.EXPAND, 5) + + self.errorTextCtrl = wx.TextCtrl(self, wx.ID_ANY, "", wx.DefaultPosition, wx.DefaultSize, wx.TE_MULTILINE | wx.TE_READONLY | wx.TE_RICH2 | wx.TE_DONTWRAP) + self.errorTextCtrl.SetFont(wx.Font(8, wx.FONTFAMILY_TELETYPE, wx.NORMAL, wx.NORMAL)) + mainSizer.Add(self.errorTextCtrl, 0, wx.EXPAND | wx.LEFT | wx.RIGHT, 5) + + self.errorTextCtrl.AppendText("pyfa root: ") + self.errorTextCtrl.AppendText(config.pyfaPath or "Unknown") + self.errorTextCtrl.AppendText('\n') + self.errorTextCtrl.AppendText("save path: ") + self.errorTextCtrl.AppendText(config.savePath or "Unknown") + self.errorTextCtrl.AppendText('\n') + self.errorTextCtrl.AppendText("fs encoding: ") + self.errorTextCtrl.AppendText(sys.getfilesystemencoding()) + self.errorTextCtrl.AppendText('\n\n') + self.errorTextCtrl.AppendText(tb) + + self.SetSizer(mainSizer) + mainSizer.Layout() + self.Layout() + + self.Centre(wx.BOTH) + + self.Show() diff --git a/gui/fighterView.py b/gui/fighterView.py index b4468c94f..6332817fe 100644 --- a/gui/fighterView.py +++ b/gui/fighterView.py @@ -1,4 +1,4 @@ -#=============================================================================== +# ============================================================================= # Copyright (C) 2010 Diego Duclos # # This file is part of pyfa. @@ -15,37 +15,40 @@ # # You should have received a copy of the GNU General Public License # along with pyfa. If not, see . -#=============================================================================== +# ============================================================================= +# noinspection PyPackageRequirements import wx -import service import gui.globalEvents as GE -import gui.marketBrowser as mb +import gui.marketBrowser as marketBrowser import gui.mainFrame import gui.display as d from gui.builtinViewColumns.state import State -from eos.types import Slot +from eos.saveddata.module import Slot from gui.contextMenu import ContextMenu +from service.fit import Fit +from service.market import Market + class FighterViewDrop(wx.PyDropTarget): - def __init__(self, dropFn): - wx.PyDropTarget.__init__(self) - self.dropFn = dropFn - # this is really transferring an EVE itemID - self.dropData = wx.PyTextDataObject() - self.SetDataObject(self.dropData) + def __init__(self, dropFn, *args, **kwargs): + super(FighterViewDrop, self).__init__(*args, **kwargs) + self.dropFn = dropFn + # this is really transferring an EVE itemID + self.dropData = wx.PyTextDataObject() + self.SetDataObject(self.dropData) - def OnData(self, x, y, t): - if self.GetData(): - data = self.dropData.GetText().split(':') - self.dropFn(x, y, data) - return t + def OnData(self, x, y, t): + if self.GetData(): + data = self.dropData.GetText().split(':') + self.dropFn(x, y, data) + return t class FighterView(wx.Panel): def __init__(self, parent): - wx.Panel.__init__(self, parent, id=wx.ID_ANY, pos=wx.DefaultPosition, style=wx.TAB_TRAVERSAL ) + wx.Panel.__init__(self, parent, id=wx.ID_ANY, pos=wx.DefaultPosition, style=wx.TAB_TRAVERSAL) self.mainFrame = gui.mainFrame.MainFrame.getInstance() self.labels = ["Light", "Heavy", "Support"] @@ -55,7 +58,7 @@ class FighterView(wx.Panel): mainSizer.Add(self.fighterDisplay, 1, wx.EXPAND, 0) textSizer = wx.BoxSizer(wx.HORIZONTAL) - textSizer.AddSpacer(( 0, 0), 1, wx.EXPAND, 5) + textSizer.AddSpacer((0, 0), 1, wx.EXPAND, 5) for x in self.labels: lbl = wx.StaticText(self, wx.ID_ANY, x.capitalize()) @@ -74,14 +77,13 @@ class FighterView(wx.Panel): mainSizer.Add(textSizer, 0, wx.EXPAND, 5) - self.SetSizer( mainSizer ) + self.SetSizer(mainSizer) self.SetAutoLayout(True) - self.mainFrame.Bind(GE.FIT_CHANGED, self.fitChanged) def fitChanged(self, event): - sFit = service.Fit.getInstance() + sFit = Fit.getInstance() activeFitID = self.mainFrame.getActiveFit() fit = sFit.getFit(activeFitID) @@ -90,7 +92,8 @@ class FighterView(wx.Panel): slot = getattr(Slot, "F_{}".format(x.upper())) used = fit.getSlotsUsed(slot) total = fit.getNumSlots(slot) - color = wx.Colour(204, 51, 51) if used > total else wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOWTEXT) + color = wx.Colour(204, 51, 51) if used > total else wx.SystemSettings_GetColour( + wx.SYS_COLOUR_WINDOWTEXT) lbl = getattr(self, "label%sUsed" % x.capitalize()) lbl.SetLabel(str(int(used))) @@ -105,15 +108,15 @@ class FighterView(wx.Panel): class FighterDisplay(d.Display): DEFAULT_COLS = ["State", - #"Base Icon", + # "Base Icon", "Base Name", # "prop:droneDps,droneBandwidth", - #"Max Range", - #"Miscellanea", + # "Max Range", + # "Miscellanea", "attr:maxVelocity", "Fighter Abilities" - #"Price", - ] + # "Price", + ] def __init__(self, parent): d.Display.__init__(self, parent, style=wx.LC_SINGLE_SEL | wx.BORDER_NONE) @@ -124,19 +127,18 @@ class FighterDisplay(d.Display): self.hoveredColumn = None self.mainFrame.Bind(GE.FIT_CHANGED, self.fitChanged) - self.mainFrame.Bind(mb.ITEM_SELECTED, self.addItem) + self.mainFrame.Bind(marketBrowser.ITEM_SELECTED, self.addItem) self.Bind(wx.EVT_LEFT_DCLICK, self.removeItem) self.Bind(wx.EVT_LEFT_DOWN, self.click) self.Bind(wx.EVT_KEY_UP, self.kbEvent) self.Bind(wx.EVT_MOTION, self.OnMouseMove) self.Bind(wx.EVT_LEAVE_WINDOW, self.OnLeaveWindow) - if "__WXGTK__" in wx.PlatformInfo: + if "__WXGTK__" in wx.PlatformInfo: self.Bind(wx.EVT_RIGHT_UP, self.scheduleMenu) else: self.Bind(wx.EVT_RIGHT_DOWN, self.scheduleMenu) - self.Bind(wx.EVT_LIST_BEGIN_DRAG, self.startDrag) self.SetDropTarget(FighterViewDrop(self.handleDragDrop)) @@ -172,7 +174,6 @@ class FighterDisplay(d.Display): keycode = event.GetKeyCode() if keycode == wx.WXK_DELETE or keycode == wx.WXK_NUMPAD_DELETE: row = self.GetFirstSelected() - firstSel = row if row != -1: fighter = self.fighters[self.GetItemData(row)] self.removeFighter(fighter) @@ -183,29 +184,30 @@ class FighterDisplay(d.Display): row = event.GetIndex() if row != -1: data = wx.PyTextDataObject() - data.SetText("fighter:"+str(row)) + data.SetText("fighter:" + str(row)) dropSource = wx.DropSource(self) dropSource.SetData(data) - res = dropSource.DoDragDrop() + dropSource.DoDragDrop() def handleDragDrop(self, x, y, data): - ''' + """ Handles dragging of items from various pyfa displays which support it data is list with two indices: data[0] is hard-coded str of originating source data[1] is typeID or index of data we want to manipulate - ''' + """ if data[0] == "fighter": # we want to merge fighters srcRow = int(data[1]) dstRow, _ = self.HitTest((x, y)) if srcRow != -1 and dstRow != -1: self._merge(srcRow, dstRow) elif data[0] == "market": - wx.PostEvent(self.mainFrame, mb.ItemSelected(itemID=int(data[1]))) + wx.PostEvent(self.mainFrame, marketBrowser.ItemSelected(itemID=int(data[1]))) - def _merge(self, src, dst): + @staticmethod + def _merge(src, dst): return ''' @@ -215,7 +217,7 @@ class FighterDisplay(d.Display): 'Electronic Warfare Drones', 'Logistic Drones', 'Mining Drones', 'Salvage Drones', 'Light Fighters', 'Heavy Fighters', 'Support Fighters') def droneKey(self, drone): - sMkt = service.Market.getInstance() + sMkt = Market.getInstance() groupName = sMkt.getMarketGroupByItem(drone.item).name print groupName @@ -224,12 +226,12 @@ class FighterDisplay(d.Display): ''' def fitChanged(self, event): - sFit = service.Fit.getInstance() + sFit = Fit.getInstance() fit = sFit.getFit(event.fitID) self.Parent.Parent.Parent.DisablePage(self.Parent, not fit) - #Clear list and get out if current fitId is None + # Clear list and get out if current fitId is None if event.fitID is None and self.lastFitId is not None: self.DeleteAllItems() self.lastFitId = None @@ -257,9 +259,8 @@ class FighterDisplay(d.Display): self.update(stuff) event.Skip() - def addItem(self, event): - sFit = service.Fit.getInstance() + sFit = Fit.getInstance() fitID = self.mainFrame.getActiveFit() trigger = sFit.addFighter(fitID, event.itemID) if trigger: @@ -278,7 +279,7 @@ class FighterDisplay(d.Display): def removeFighter(self, fighter): fitID = self.mainFrame.getActiveFit() - sFit = service.Fit.getInstance() + sFit = Fit.getInstance() sFit.removeFighter(fitID, self.original.index(fighter)) wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=fitID)) @@ -289,7 +290,7 @@ class FighterDisplay(d.Display): col = self.getColumn(event.Position) if col == self.getColIndex(State): fitID = self.mainFrame.getActiveFit() - sFit = service.Fit.getInstance() + sFit = Fit.getInstance() fighter = self.fighters[row] sFit.toggleFighter(fitID, self.original.index(fighter)) wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=fitID)) @@ -304,7 +305,7 @@ class FighterDisplay(d.Display): if sel != -1: fighter = self.fighters[sel] - sMkt = service.Market.getInstance() + sMkt = Market.getInstance() sourceContext = "fighterItem" itemContext = sMkt.getCategoryByItem(fighter.item).name menu = ContextMenu.getMenu((fighter,), (sourceContext, itemContext)) diff --git a/gui/globalEvents.py b/gui/globalEvents.py index 18f91d348..1c1cc7e38 100644 --- a/gui/globalEvents.py +++ b/gui/globalEvents.py @@ -1,3 +1,4 @@ +# noinspection PyPackageRequirements import wx.lib.newevent FitChanged, FIT_CHANGED = wx.lib.newevent.NewEvent() diff --git a/gui/graph.py b/gui/graph.py index 53d66f6a5..a2b2239de 100644 --- a/gui/graph.py +++ b/gui/graph.py @@ -1,36 +1,39 @@ -#=============================================================================== -# Copyright (C) 2010 Diego Duclos -# -# This file is part of pyfa. -# -# pyfa is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# pyfa is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with pyfa. If not, see . -#=============================================================================== - -class Graph(object): - views = [] - - @classmethod - def register(cls): - Graph.views.append(cls) - - def __init__(self): - self.name = "" - - def getFields(self, fit, fields): - raise NotImplementedError() - - def getIcons(self): - return None - -from gui.builtinGraphs import * +# ============================================================================= +# Copyright (C) 2010 Diego Duclos +# +# This file is part of pyfa. +# +# pyfa is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# pyfa is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with pyfa. If not, see . +# ============================================================================= + + +class Graph(object): + views = [] + + @classmethod + def register(cls): + Graph.views.append(cls) + + def __init__(self): + self.name = "" + + def getFields(self, fit, fields): + raise NotImplementedError() + + def getIcons(self): + return None + + +# noinspection PyUnresolvedReferences +from gui.builtinGraphs import fitDps # noqa: E402, F401 diff --git a/gui/graphFrame.py b/gui/graphFrame.py index 2cedc84d1..7a0aa3387 100644 --- a/gui/graphFrame.py +++ b/gui/graphFrame.py @@ -1,278 +1,349 @@ -#=============================================================================== -# Copyright (C) 2010 Diego Duclos -# -# This file is part of pyfa. -# -# pyfa is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# pyfa is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with pyfa. If not, see . -#=============================================================================== - -import wx -import os -from gui.bitmapLoader import BitmapLoader -import gui.display -import gui.globalEvents as GE - -from gui.graph import Graph -import service -import gui.mainFrame - -enabled = True -mplImported = False - -class GraphFrame(wx.Frame): - def __init__(self, parent, style=wx.DEFAULT_FRAME_STYLE | wx.NO_FULL_REPAINT_ON_RESIZE | wx.FRAME_FLOAT_ON_PARENT): - - global enabled - global mplImported - - self.legendFix = False - if not enabled: - return - - try: - import matplotlib as mpl - - try: - cache_dir = mpl._get_cachedir() - except: - cache_dir = unicode(os.path.expanduser(os.path.join("~", ".matplotlib"))) - - cache_file = os.path.join(cache_dir, 'fontList.cache') - if os.access(cache_dir, os.W_OK | os.X_OK) and os.path.isfile(cache_file): - # remove matplotlib font cache, see #234 - os.remove(cache_file) - if not mplImported: - mpl.use('wxagg') - from matplotlib.backends.backend_wxagg import FigureCanvasWxAgg as Canvas - from matplotlib.figure import Figure - enabled = True - if mpl.__version__[0] != "1": - print "pyfa: Found matplotlib version ",mpl.__version__, " - activating OVER9000 workarounds" - print "pyfa: Recommended minimum matplotlib version is 1.0.0" - self.legendFix = True - except: - print "Problems importing matplotlib; continuing without graphs" - enabled = False - return - - mplImported = True - - wx.Frame.__init__(self, parent, title=u"pyfa: Graph Generator", style=style, size=(520, 390)) - - i = wx.IconFromBitmap(BitmapLoader.getBitmap("graphs_small", "gui")) - self.SetIcon(i) - self.mainFrame = gui.mainFrame.MainFrame.getInstance() - self.CreateStatusBar() - - self.mainSizer = wx.BoxSizer(wx.VERTICAL) - self.SetSizer(self.mainSizer) - - sFit = service.Fit.getInstance() - fit = sFit.getFit(self.mainFrame.getActiveFit()) - self.fits = [fit] if fit is not None else [] - self.fitList = FitList(self) - self.fitList.SetMinSize((270, -1)) - - self.fitList.fitList.update(self.fits) - - self.graphSelection = wx.Choice(self, wx.ID_ANY, style=0) - self.mainSizer.Add(self.graphSelection, 0, wx.EXPAND) - - self.figure = Figure(figsize=(4, 3)) - - rgbtuple = wx.SystemSettings.GetColour( wx.SYS_COLOUR_BTNFACE ).Get() - clr = [c/255. for c in rgbtuple] - self.figure.set_facecolor( clr ) - self.figure.set_edgecolor( clr ) - - self.canvas = Canvas(self, -1, self.figure) - self.canvas.SetBackgroundColour( wx.Colour( *rgbtuple ) ) - - self.subplot = self.figure.add_subplot(111) - self.subplot.grid(True) - - self.mainSizer.Add(self.canvas, 1, wx.EXPAND) - self.mainSizer.Add(wx.StaticLine( self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LI_HORIZONTAL ), 0 , wx.EXPAND) - - self.gridPanel = wx.Panel(self) - self.mainSizer.Add(self.gridPanel, 0, wx.EXPAND) - - dummyBox = wx.BoxSizer(wx.VERTICAL) - self.gridPanel.SetSizer(dummyBox) - - self.gridSizer = wx.FlexGridSizer(0, 4) - self.gridSizer.AddGrowableCol(1) - dummyBox.Add(self.gridSizer, 0, wx.EXPAND) - - for view in Graph.views: - view = view() - self.graphSelection.Append(view.name, view) - - self.graphSelection.SetSelection(0) - self.fields = {} - self.select(0) - self.sl1 = wx.StaticLine( self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LI_HORIZONTAL ) - self.mainSizer.Add(self.sl1,0, wx.EXPAND) - self.mainSizer.Add(self.fitList, 0, wx.EXPAND) - - self.fitList.fitList.Bind(wx.EVT_LEFT_DCLICK, self.removeItem) - self.mainFrame.Bind(GE.FIT_CHANGED, self.draw) - self.Bind(wx.EVT_CLOSE, self.close) - - self.Fit() - self.SetMinSize(self.GetSize()) - - def handleDrag(self, type, fitID): - if type == "fit": - self.AppendFitToList(fitID) - - def close(self, event): - self.fitList.fitList.Unbind(wx.EVT_LEFT_DCLICK, handler=self.removeItem) - self.mainFrame.Unbind(GE.FIT_CHANGED, handler=self.draw) - event.Skip() - - def getView(self): - return self.graphSelection.GetClientData(self.graphSelection.GetSelection()) - - def getValues(self): - values = {} - for fieldName, field in self.fields.iteritems(): - values[fieldName] = field.GetValue() - - return values - - def select(self, index): - view = self.getView() - icons = view.getIcons() - labels = view.getLabels() - sizer = self.gridSizer - self.gridPanel.DestroyChildren() - self.fields.clear() - - #Setup textboxes - for field, defaultVal in view.getFields().iteritems(): - - textBox = wx.TextCtrl(self.gridPanel, wx.ID_ANY, style=0) - self.fields[field] = textBox - textBox.Bind(wx.EVT_TEXT, self.onFieldChanged) - sizer.Add(textBox, 1, wx.EXPAND | wx.ALIGN_CENTER_VERTICAL | wx.ALL, 3) - if defaultVal is not None: - if not isinstance(defaultVal, basestring): - defaultVal = ("%f" % defaultVal).rstrip("0") - if defaultVal[-1:] == ".": - defaultVal = defaultVal + "0" - - textBox.ChangeValue(defaultVal) - - imgLabelSizer = wx.BoxSizer(wx.HORIZONTAL) - if icons: - icon = icons.get(field) - if icon is not None: - static = wx.StaticBitmap(self.gridPanel) - static.SetBitmap(icon) - imgLabelSizer.Add(static, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 1) - - if labels: - label = labels.get(field) - label = label if label is not None else field - else: - label = field - - imgLabelSizer.Add(wx.StaticText(self.gridPanel, wx.ID_ANY, label), 0, wx.LEFT | wx.RIGHT | wx.ALIGN_CENTER_VERTICAL, 3) - sizer.Add(imgLabelSizer, 0, wx.ALIGN_CENTER_VERTICAL) - self.draw() - - def draw(self, event=None): - values = self.getValues() - view = self.getView() - self.subplot.clear() - self.subplot.grid(True) - legend = [] - - for fit in self.fits: - try: - success, status = view.getPoints(fit, values) - if not success: - #TODO: Add a pwetty statys bar to report errors with - self.SetStatusText(status) - return - - x, y = success, status - - self.subplot.plot(x, y) - legend.append(fit.name) - except: - self.SetStatusText("Invalid values in '%s'" % fit.name) - self.canvas.draw() - return - - if self.legendFix and len(legend) > 0: - leg = self.subplot.legend(tuple(legend), "upper right" , shadow = False) - for t in leg.get_texts(): - t.set_fontsize('small') - - for l in leg.get_lines(): - l.set_linewidth(1) - - elif not self.legendFix and len(legend) >0: - leg = self.subplot.legend(tuple(legend), "upper right" , shadow = False, frameon = False) - for t in leg.get_texts(): - t.set_fontsize('small') - - for l in leg.get_lines(): - l.set_linewidth(1) - - self.canvas.draw() - self.SetStatusText("") - if event is not None: - event.Skip() - - def onFieldChanged(self, event): - self.draw() - - def AppendFitToList(self, fitID): - sFit = service.Fit.getInstance() - fit = sFit.getFit(fitID) - if fit not in self.fits: - self.fits.append(fit) - - self.fitList.fitList.update(self.fits) - self.draw() - - def removeItem(self, event): - row, _ = self.fitList.fitList.HitTest(event.Position) - if row != -1: - del self.fits[row] - self.fitList.fitList.update(self.fits) - self.draw() - - -class FitList(wx.Panel): - def __init__(self, parent): - wx.Panel.__init__(self, parent) - self.mainSizer = wx.BoxSizer(wx.VERTICAL) - self.SetSizer(self.mainSizer) - - self.fitList = FitDisplay(self) - self.mainSizer.Add(self.fitList, 1, wx.EXPAND) - fitToolTip = wx.ToolTip("Drag a fit into this list to graph it") - self.fitList.SetToolTip(fitToolTip) - -class FitDisplay(gui.display.Display): - DEFAULT_COLS = ["Base Icon", - "Base Name"] - - def __init__(self, parent): - gui.display.Display.__init__(self, parent) - +# ============================================================================= +# Copyright (C) 2010 Diego Duclos +# +# This file is part of pyfa. +# +# pyfa is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# pyfa is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with pyfa. If not, see . +# ============================================================================= + +import os +from logbook import Logger + +# noinspection PyPackageRequirements +import wx + +from service.fit import Fit +import gui.display +import gui.mainFrame +import gui.globalEvents as GE +from gui.graph import Graph +from gui.bitmapLoader import BitmapLoader + +pyfalog = Logger(__name__) + +try: + import matplotlib as mpl + + mpl_version = int(mpl.__version__[0]) + if mpl_version >= 2: + mpl.use('wxagg') + mplImported = True + else: + mplImported = False + from matplotlib.patches import Patch + + from matplotlib.backends.backend_wxagg import FigureCanvasWxAgg as Canvas + from matplotlib.figure import Figure + + graphFrame_enabled = True + mplImported = True +except ImportError: + Patch = mpl = Canvas = Figure = None + graphFrame_enabled = False + mplImported = False + +pyfalog = Logger(__name__) + + +class GraphFrame(wx.Frame): + def __init__(self, parent, style=wx.DEFAULT_FRAME_STYLE | wx.NO_FULL_REPAINT_ON_RESIZE | wx.FRAME_FLOAT_ON_PARENT): + + global graphFrame_enabled + global mplImported + + self.Patch = None + self.mpl_version = -1 + + try: + import matplotlib as mpl + self.mpl_version = int(mpl.__version__[0]) + if self.mpl_version >= 2: + mpl.use('wxagg') + mplImported = True + else: + mplImported = False + from matplotlib.patches import Patch + self.Patch = Patch + from matplotlib.backends.backend_wxagg import FigureCanvasWxAgg as Canvas + from matplotlib.figure import Figure + graphFrame_enabled = True + except ImportError: + Patch = mpl = Canvas = Figure = None + graphFrame_enabled = False + + self.legendFix = False + if not graphFrame_enabled: + pyfalog.info("Problems importing matplotlib; continuing without graphs") + return + + try: + cache_dir = mpl._get_cachedir() + except: + cache_dir = os.path.expanduser(os.path.join("~", ".matplotlib")) + + cache_file = os.path.join(cache_dir, 'fontList.cache') + + if os.access(cache_dir, os.W_OK | os.X_OK) and os.path.isfile(cache_file): + # remove matplotlib font cache, see #234 + os.remove(cache_file) + if not mplImported: + mpl.use('wxagg') + + graphFrame_enabled = True + if int(mpl.__version__[0]) < 1: + print("pyfa: Found matplotlib version ", mpl.__version__, " - activating OVER9000 workarounds") + print("pyfa: Recommended minimum matplotlib version is 1.0.0") + self.legendFix = True + + mplImported = True + + wx.Frame.__init__(self, parent, title=u"pyfa: Graph Generator", style=style, size=(520, 390)) + + i = wx.IconFromBitmap(BitmapLoader.getBitmap("graphs_small", "gui")) + self.SetIcon(i) + self.mainFrame = gui.mainFrame.MainFrame.getInstance() + self.CreateStatusBar() + + self.mainSizer = wx.BoxSizer(wx.VERTICAL) + self.SetSizer(self.mainSizer) + + sFit = Fit.getInstance() + fit = sFit.getFit(self.mainFrame.getActiveFit()) + self.fits = [fit] if fit is not None else [] + self.fitList = FitList(self) + self.fitList.SetMinSize((270, -1)) + + self.fitList.fitList.update(self.fits) + + self.graphSelection = wx.Choice(self, wx.ID_ANY, style=0) + self.mainSizer.Add(self.graphSelection, 0, wx.EXPAND) + + self.figure = Figure(figsize=(4, 3)) + + rgbtuple = wx.SystemSettings.GetColour(wx.SYS_COLOUR_BTNFACE).Get() + clr = [c / 255. for c in rgbtuple] + self.figure.set_facecolor(clr) + self.figure.set_edgecolor(clr) + + self.canvas = Canvas(self, -1, self.figure) + self.canvas.SetBackgroundColour(wx.Colour(*rgbtuple)) + + self.subplot = self.figure.add_subplot(111) + self.subplot.grid(True) + + self.mainSizer.Add(self.canvas, 1, wx.EXPAND) + self.mainSizer.Add(wx.StaticLine(self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LI_HORIZONTAL), 0, + wx.EXPAND) + + self.gridPanel = wx.Panel(self) + self.mainSizer.Add(self.gridPanel, 0, wx.EXPAND) + + dummyBox = wx.BoxSizer(wx.VERTICAL) + self.gridPanel.SetSizer(dummyBox) + + self.gridSizer = wx.FlexGridSizer(0, 4) + self.gridSizer.AddGrowableCol(1) + dummyBox.Add(self.gridSizer, 0, wx.EXPAND) + + for view in Graph.views: + view = view() + self.graphSelection.Append(view.name, view) + + self.graphSelection.SetSelection(0) + self.fields = {} + self.select(0) + self.sl1 = wx.StaticLine(self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LI_HORIZONTAL) + self.mainSizer.Add(self.sl1, 0, wx.EXPAND) + self.mainSizer.Add(self.fitList, 0, wx.EXPAND) + + self.fitList.fitList.Bind(wx.EVT_LEFT_DCLICK, self.removeItem) + self.mainFrame.Bind(GE.FIT_CHANGED, self.draw) + self.Bind(wx.EVT_CLOSE, self.close) + + self.Fit() + self.SetMinSize(self.GetSize()) + + def handleDrag(self, type, fitID): + if type == "fit": + self.AppendFitToList(fitID) + + def close(self, event): + self.fitList.fitList.Unbind(wx.EVT_LEFT_DCLICK, handler=self.removeItem) + self.mainFrame.Unbind(GE.FIT_CHANGED, handler=self.draw) + event.Skip() + + def getView(self): + return self.graphSelection.GetClientData(self.graphSelection.GetSelection()) + + def getValues(self): + values = {} + for fieldName, field in self.fields.iteritems(): + values[fieldName] = field.GetValue() + + return values + + def select(self, index): + view = self.getView() + icons = view.getIcons() + labels = view.getLabels() + sizer = self.gridSizer + self.gridPanel.DestroyChildren() + self.fields.clear() + + # Setup textboxes + for field, defaultVal in view.getFields().iteritems(): + + textBox = wx.TextCtrl(self.gridPanel, wx.ID_ANY, style=0) + self.fields[field] = textBox + textBox.Bind(wx.EVT_TEXT, self.onFieldChanged) + sizer.Add(textBox, 1, wx.EXPAND | wx.ALIGN_CENTER_VERTICAL | wx.ALL, 3) + if defaultVal is not None: + if not isinstance(defaultVal, basestring): + defaultVal = ("%f" % defaultVal).rstrip("0") + if defaultVal[-1:] == ".": + defaultVal += "0" + + textBox.ChangeValue(defaultVal) + + imgLabelSizer = wx.BoxSizer(wx.HORIZONTAL) + if icons: + icon = icons.get(field) + if icon is not None: + static = wx.StaticBitmap(self.gridPanel) + static.SetBitmap(icon) + imgLabelSizer.Add(static, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 1) + + if labels: + label = labels.get(field) + label = label if label is not None else field + else: + label = field + + imgLabelSizer.Add(wx.StaticText(self.gridPanel, wx.ID_ANY, label), 0, + wx.LEFT | wx.RIGHT | wx.ALIGN_CENTER_VERTICAL, 3) + sizer.Add(imgLabelSizer, 0, wx.ALIGN_CENTER_VERTICAL) + self.draw() + + def draw(self, event=None): + values = self.getValues() + view = self.getView() + self.subplot.clear() + self.subplot.grid(True) + legend = [] + + for fit in self.fits: + try: + success, status = view.getPoints(fit, values) + if not success: + # TODO: Add a pwetty statys bar to report errors with + self.SetStatusText(status) + return + + x, y = success, status + + self.subplot.plot(x, y) + legend.append(fit.name) + except: + pyfalog.warning("Invalid values in '{0}'", fit.name) + self.SetStatusText("Invalid values in '%s'" % fit.name) + self.canvas.draw() + return + + if self.mpl_version < 2: + if self.legendFix and len(legend) > 0: + leg = self.subplot.legend(tuple(legend), "upper right", shadow=False) + for t in leg.get_texts(): + t.set_fontsize('small') + + for l in leg.get_lines(): + l.set_linewidth(1) + + elif not self.legendFix and len(legend) > 0: + leg = self.subplot.legend(tuple(legend), "upper right", shadow=False, frameon=False) + for t in leg.get_texts(): + t.set_fontsize('small') + + for l in leg.get_lines(): + l.set_linewidth(1) + elif self.mpl_version >= 2: + legend2 = [] + legend_colors = { + 0: "blue", + 1: "orange", + 2: "green", + 3: "red", + 4: "purple", + 5: "brown", + 6: "pink", + 7: "grey", + } + + for i, i_name in enumerate(legend): + try: + selected_color = legend_colors[i] + except: + selected_color = None + legend2.append(Patch(color=selected_color, label=i_name), ) + + if len(legend2) > 0: + leg = self.subplot.legend(handles=legend2) + for t in leg.get_texts(): + t.set_fontsize('small') + + for l in leg.get_lines(): + l.set_linewidth(1) + + self.canvas.draw() + self.SetStatusText("") + if event is not None: + event.Skip() + + def onFieldChanged(self, event): + self.draw() + + def AppendFitToList(self, fitID): + sFit = Fit.getInstance() + fit = sFit.getFit(fitID) + if fit not in self.fits: + self.fits.append(fit) + + self.fitList.fitList.update(self.fits) + self.draw() + + def removeItem(self, event): + row, _ = self.fitList.fitList.HitTest(event.Position) + if row != -1: + del self.fits[row] + self.fitList.fitList.update(self.fits) + self.draw() + + +class FitList(wx.Panel): + def __init__(self, parent): + wx.Panel.__init__(self, parent) + self.mainSizer = wx.BoxSizer(wx.VERTICAL) + self.SetSizer(self.mainSizer) + + self.fitList = FitDisplay(self) + self.mainSizer.Add(self.fitList, 1, wx.EXPAND) + fitToolTip = wx.ToolTip("Drag a fit into this list to graph it") + self.fitList.SetToolTip(fitToolTip) + + +class FitDisplay(gui.display.Display): + DEFAULT_COLS = ["Base Icon", + "Base Name"] + + def __init__(self, parent): + gui.display.Display.__init__(self, parent) diff --git a/gui/implantView.py b/gui/implantView.py index 15da4d097..86b9afd96 100644 --- a/gui/implantView.py +++ b/gui/implantView.py @@ -1,4 +1,4 @@ -#=============================================================================== +# ============================================================================= # Copyright (C) 2010 Diego Duclos # # This file is part of pyfa. @@ -15,31 +15,33 @@ # # You should have received a copy of the GNU General Public License # along with pyfa. If not, see . -#=============================================================================== +# ============================================================================= +# noinspection PyPackageRequirements import wx -import service import gui.display as d -import gui.marketBrowser as mb +import gui.marketBrowser as marketBrowser import gui.mainFrame from gui.builtinViewColumns.state import State from gui.contextMenu import ContextMenu import globalEvents as GE -from eos.types import ImplantLocation +from eos.saveddata.fit import ImplantLocation +from service.fit import Fit +from service.market import Market class ImplantView(wx.Panel): def __init__(self, parent): - wx.Panel.__init__(self, parent, id=wx.ID_ANY, pos=wx.DefaultPosition, style=wx.TAB_TRAVERSAL ) + wx.Panel.__init__(self, parent, id=wx.ID_ANY, pos=wx.DefaultPosition, style=wx.TAB_TRAVERSAL) self.mainFrame = gui.mainFrame.MainFrame.getInstance() mainSizer = wx.BoxSizer(wx.VERTICAL) self.implantDisplay = ImplantDisplay(self) - mainSizer.Add(self.implantDisplay, 1, wx.EXPAND, 0 ) + mainSizer.Add(self.implantDisplay, 1, wx.EXPAND, 0) radioSizer = wx.BoxSizer(wx.HORIZONTAL) - radioSizer.AddSpacer(( 0, 0), 1, wx.EXPAND, 5) + radioSizer.AddSpacer((0, 0), 1, wx.EXPAND, 5) self.rbFit = wx.RadioButton(self, id=wx.ID_ANY, label="Use Fit-specific Implants", style=wx.RB_GROUP) self.rbChar = wx.RadioButton(self, id=wx.ID_ANY, label="Use Character Implants") radioSizer.Add(self.rbFit, 0, wx.ALL, 5) @@ -48,7 +50,7 @@ class ImplantView(wx.Panel): mainSizer.Add(radioSizer, 0, wx.EXPAND, 5) - self.SetSizer( mainSizer ) + self.SetSizer(mainSizer) self.SetAutoLayout(True) self.Bind(wx.EVT_RADIOBUTTON, self.OnRadioSelect, self.rbFit) @@ -56,7 +58,7 @@ class ImplantView(wx.Panel): self.mainFrame.Bind(GE.FIT_CHANGED, self.fitChanged) def fitChanged(self, event): - sFit = service.Fit.getInstance() + sFit = Fit.getInstance() activeFitID = self.mainFrame.getActiveFit() fit = sFit.getFit(activeFitID) if fit: @@ -70,7 +72,7 @@ class ImplantView(wx.Panel): def OnRadioSelect(self, event): fitID = self.mainFrame.getActiveFit() - sFit = service.Fit.getInstance() + sFit = Fit.getInstance() sFit.toggleImplantSource(fitID, ImplantLocation.FIT if self.rbFit.GetValue() else ImplantLocation.CHARACTER) wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=fitID)) @@ -88,17 +90,17 @@ class ImplantDisplay(d.Display): self.lastFitId = None self.mainFrame.Bind(GE.FIT_CHANGED, self.fitChanged) - self.mainFrame.Bind(mb.ITEM_SELECTED, self.addItem) + self.mainFrame.Bind(marketBrowser.ITEM_SELECTED, self.addItem) self.Bind(wx.EVT_LEFT_DCLICK, self.removeItem) self.Bind(wx.EVT_LEFT_DOWN, self.click) self.Bind(wx.EVT_KEY_UP, self.kbEvent) - if "__WXGTK__" in wx.PlatformInfo: + if "__WXGTK__" in wx.PlatformInfo: self.Bind(wx.EVT_RIGHT_UP, self.scheduleMenu) else: self.Bind(wx.EVT_RIGHT_DOWN, self.scheduleMenu) - def kbEvent(self,event): + def kbEvent(self, event): keycode = event.GetKeyCode() if keycode == wx.WXK_DELETE or keycode == wx.WXK_NUMPAD_DELETE: row = self.GetFirstSelected() @@ -107,12 +109,12 @@ class ImplantDisplay(d.Display): event.Skip() def fitChanged(self, event): - sFit = service.Fit.getInstance() + sFit = Fit.getInstance() fit = sFit.getFit(event.fitID) self.Parent.Parent.Parent.DisablePage(self.Parent, not fit or fit.isStructure) - #Clear list and get out if current fitId is None + # Clear list and get out if current fitId is None if event.fitID is None and self.lastFitId is not None: self.DeleteAllItems() self.lastFitId = None @@ -121,7 +123,8 @@ class ImplantDisplay(d.Display): self.original = fit.implants if fit is not None else None self.implants = stuff = fit.appliedImplants if fit is not None else None - if stuff is not None: stuff.sort(key=lambda implant: implant.slot) + if stuff is not None: + stuff.sort(key=lambda implant: implant.slot) if event.fitID != self.lastFitId: self.lastFitId = event.fitID @@ -137,7 +140,7 @@ class ImplantDisplay(d.Display): event.Skip() def addItem(self, event): - sFit = service.Fit.getInstance() + sFit = Fit.getInstance() fitID = self.mainFrame.getActiveFit() fit = sFit.getFit(fitID) @@ -161,7 +164,7 @@ class ImplantDisplay(d.Display): def removeImplant(self, implant): fitID = self.mainFrame.getActiveFit() - sFit = service.Fit.getInstance() + sFit = Fit.getInstance() sFit.removeImplant(fitID, self.original.index(implant)) wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=fitID)) @@ -172,7 +175,7 @@ class ImplantDisplay(d.Display): col = self.getColumn(event.Position) if col == self.getColIndex(State): fitID = self.mainFrame.getActiveFit() - sFit = service.Fit.getInstance() + sFit = Fit.getInstance() sFit.toggleImplant(fitID, row) wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=fitID)) @@ -185,7 +188,7 @@ class ImplantDisplay(d.Display): sel = self.GetFirstSelected() menu = None - sFit = service.Fit.getInstance() + sFit = Fit.getInstance() fit = sFit.getFit(self.mainFrame.getActiveFit()) if not fit: @@ -194,7 +197,7 @@ class ImplantDisplay(d.Display): if sel != -1: implant = fit.appliedImplants[sel] - sMkt = service.Market.getInstance() + sMkt = Market.getInstance() sourceContext = "implantItem" if fit.implantSource == ImplantLocation.FIT else "implantItemChar" itemContext = sMkt.getCategoryByItem(implant.item).name diff --git a/gui/itemStats.py b/gui/itemStats.py index 35fb48355..325793e86 100644 --- a/gui/itemStats.py +++ b/gui/itemStats.py @@ -1,4 +1,4 @@ -#=============================================================================== +# ============================================================================= # Copyright (C) 2010 Diego Duclos # # This file is part of pyfa. @@ -15,47 +15,60 @@ # # You should have received a copy of the GNU General Public License # along with pyfa. If not, see . -#=============================================================================== +# ============================================================================= -import wx import re +import os +import csv +import sys +import subprocess + +# noinspection PyPackageRequirements +import wx +# noinspection PyPackageRequirements +import wx.html +# noinspection PyPackageRequirements +import wx.lib.mixins.listctrl as listmix + +import config +from eos.saveddata.mode import Mode +from eos.saveddata.character import Skill +from eos.saveddata.implant import Implant +from eos.saveddata.booster import Booster +from eos.saveddata.drone import Drone +from eos.saveddata.fighter import Fighter +from eos.saveddata.module import Module +from eos.saveddata.ship import Ship +from eos.saveddata.citadel import Citadel +from eos.saveddata.fit import Fit +from service.market import Market +from service.attribute import Attribute import gui.mainFrame from gui.bitmapLoader import BitmapLoader -import sys -import wx.lib.mixins.listctrl as listmix -import wx.html -from eos.types import Fit, Ship, Citadel, Module, Skill, Booster, Implant, Drone, Mode, Fighter from gui.utils.numberFormatter import formatAmount -import service -import config from gui.contextMenu import ContextMenu -from gui.utils.numberFormatter import formatAmount -import csv -try: - from collections import OrderedDict -except ImportError: - from utils.compat import OrderedDict class ItemStatsDialog(wx.Dialog): counter = 0 + def __init__( self, victim, fullContext=None, pos=wx.DefaultPosition, size=wx.DefaultSize, - maximized = False - ): + maximized=False + ): wx.Dialog.__init__( - self, - gui.mainFrame.MainFrame.getInstance(), - wx.ID_ANY, - title="Item stats", - pos=pos, - size=size, - style=wx.CAPTION | wx.CLOSE_BOX | wx.MINIMIZE_BOX | wx.MAXIMIZE_BOX | wx.RESIZE_BORDER| wx.SYSTEM_MENU + self, + gui.mainFrame.MainFrame.getInstance(), + wx.ID_ANY, + title="Item stats", + pos=pos, + size=size, + style=wx.CAPTION | wx.CLOSE_BOX | wx.MINIMIZE_BOX | wx.MAXIMIZE_BOX | wx.RESIZE_BORDER | wx.SYSTEM_MENU ) empty = getattr(victim, "isEmpty", False) @@ -70,33 +83,37 @@ class ItemStatsDialog(wx.Dialog): itmContext = fullContext[1] except IndexError: itmContext = None - item = getattr(victim, "item", None) if srcContext.lower() not in ("projectedcharge", "fittingcharge") else getattr(victim, "charge", None) + item = getattr(victim, "item", None) if srcContext.lower() not in ( + "projectedcharge", + "fittingcharge" + ) else getattr(victim, "charge", None) if item is None: - sMkt = service.Market.getInstance() + sMkt = Market.getInstance() item = sMkt.getItem(victim.ID) victim = None self.context = itmContext if item.icon is not None: - before,sep,after = item.icon.iconFile.rpartition("_") - iconFile = "%s%s%s" % (before,sep,"0%s" % after if len(after) < 2 else after) + before, sep, after = item.icon.iconFile.rpartition("_") + iconFile = "%s%s%s" % (before, sep, "0%s" % after if len(after) < 2 else after) itemImg = BitmapLoader.getBitmap(iconFile, "icons") if itemImg is not None: self.SetIcon(wx.IconFromBitmap(itemImg)) - self.SetTitle("%s: %s%s" % ("%s Stats" % itmContext if itmContext is not None else "Stats", item.name, " (%d)"%item.ID if config.debug else "")) + self.SetTitle("%s: %s%s" % ("%s Stats" % itmContext if itmContext is not None else "Stats", item.name, + " (%d)" % item.ID if config.debug else "")) self.SetMinSize((300, 200)) if "wxGTK" in wx.PlatformInfo: # GTK has huge tab widgets, give it a bit more room self.SetSize((580, 500)) else: self.SetSize((550, 500)) - #self.SetMaxSize((500, -1)) + # self.SetMaxSize((500, -1)) self.mainSizer = wx.BoxSizer(wx.VERTICAL) self.container = ItemStatsContainer(self, victim, item, itmContext) self.mainSizer.Add(self.container, 1, wx.EXPAND) if "wxGTK" in wx.PlatformInfo: - self.closeBtn = wx.Button( self, wx.ID_ANY, u"Close", wx.DefaultPosition, wx.DefaultSize, 0 ) - self.mainSizer.Add( self.closeBtn, 0, wx.ALL|wx.ALIGN_RIGHT, 5 ) + self.closeBtn = wx.Button(self, wx.ID_ANY, u"Close", wx.DefaultPosition, wx.DefaultSize, 0) + self.mainSizer.Add(self.closeBtn, 0, wx.ALL | wx.ALIGN_RIGHT, 5) self.closeBtn.Bind(wx.EVT_BUTTON, self.closeEvent) self.SetSizer(self.mainSizer) @@ -112,13 +129,13 @@ class ItemStatsDialog(wx.Dialog): counter = ItemStatsDialog.counter dlgStep = 30 - if counter * dlgStep > ppos.x+psize.width-dlgsize.x or counter * dlgStep > ppos.y+psize.height-dlgsize.y: + if counter * dlgStep > ppos.x + psize.width - dlgsize.x or counter * dlgStep > ppos.y + psize.height - dlgsize.y: ItemStatsDialog.counter = 1 dlgx = ppos.x + counter * dlgStep dlgy = ppos.y + counter * dlgStep if pos == wx.DefaultPosition: - self.SetPosition((dlgx,dlgy)) + self.SetPosition((dlgx, dlgy)) else: self.SetPosition(pos) if maximized: @@ -138,26 +155,22 @@ class ItemStatsDialog(wx.Dialog): def closeEvent(self, event): - if self.dlgOrder==ItemStatsDialog.counter: + if self.dlgOrder == ItemStatsDialog.counter: ItemStatsDialog.counter -= 1 self.parentWnd.UnregisterStatsWindow(self) self.Destroy() -########################################################################### -## Class ItemStatsContainer -########################################################################### -class ItemStatsContainer ( wx.Panel ): +class ItemStatsContainer(wx.Panel): + def __init__(self, parent, stuff, item, context=None): + wx.Panel.__init__(self, parent) + sMkt = Market.getInstance() - def __init__( self, parent, stuff, item, context = None): - wx.Panel.__init__ ( self, parent ) - sMkt = service.Market.getInstance() + mainSizer = wx.BoxSizer(wx.VERTICAL) - mainSizer = wx.BoxSizer( wx.VERTICAL ) - - self.nbContainer = wx.Notebook( self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, 0 ) - mainSizer.Add( self.nbContainer, 1, wx.EXPAND |wx.ALL, 2 ) + self.nbContainer = wx.Notebook(self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, 0) + mainSizer.Add(self.nbContainer, 1, wx.EXPAND | wx.ALL, 2) if item.traits is not None: self.traits = ItemTraits(self.nbContainer, stuff, item) @@ -184,11 +197,15 @@ class ItemStatsContainer ( wx.Panel ): self.affectedby = ItemAffectedBy(self.nbContainer, stuff, item) self.nbContainer.AddPage(self.affectedby, "Affected by") + if config.debug: + self.properties = ItemProperties(self.nbContainer, stuff, item, context) + self.nbContainer.AddPage(self.properties, "Properties") + self.nbContainer.Bind(wx.EVT_LEFT_DOWN, self.mouseHit) self.SetSizer(mainSizer) self.Layout() - def __del__( self ): + def __del__(self): pass def mouseHit(self, event): @@ -196,54 +213,36 @@ class ItemStatsContainer ( wx.Panel ): if tab != -1: self.nbContainer.SetSelection(tab) -########################################################################### -## Class AutoListCtrl -########################################################################### class AutoListCtrl(wx.ListCtrl, listmix.ListCtrlAutoWidthMixin, listmix.ListRowHighlighter): - - def __init__(self, parent, ID, pos=wx.DefaultPosition, - size=wx.DefaultSize, style=0): + def __init__(self, parent, ID, pos=wx.DefaultPosition, size=wx.DefaultSize, style=0): wx.ListCtrl.__init__(self, parent, ID, pos, size, style) listmix.ListCtrlAutoWidthMixin.__init__(self) listmix.ListRowHighlighter.__init__(self) -########################################################################### -## Class AutoListCtrl -########################################################################### class AutoListCtrlNoHighlight(wx.ListCtrl, listmix.ListCtrlAutoWidthMixin, listmix.ListRowHighlighter): - - def __init__(self, parent, ID, pos=wx.DefaultPosition, - size=wx.DefaultSize, style=0): + def __init__(self, parent, ID, pos=wx.DefaultPosition, size=wx.DefaultSize, style=0): wx.ListCtrl.__init__(self, parent, ID, pos, size, style) listmix.ListCtrlAutoWidthMixin.__init__(self) -########################################################################### -## Class ItemTraits -########################################################################### - -class ItemTraits ( wx.Panel ): +class ItemTraits(wx.Panel): def __init__(self, parent, stuff, item): - wx.Panel.__init__ (self, parent) + wx.Panel.__init__(self, parent) mainSizer = wx.BoxSizer(wx.VERTICAL) self.SetSizer(mainSizer) self.traits = wx.html.HtmlWindow(self) self.traits.SetPage(item.traits.traitText) - mainSizer.Add(self.traits, 1, wx.ALL|wx.EXPAND, 0) + mainSizer.Add(self.traits, 1, wx.ALL | wx.EXPAND, 0) self.Layout() -########################################################################### -## Class ItemDescription -########################################################################### - -class ItemDescription ( wx.Panel ): +class ItemDescription(wx.Panel): def __init__(self, parent, stuff, item): - wx.Panel.__init__ (self, parent) + wx.Panel.__init__(self, parent) mainSizer = wx.BoxSizer(wx.VERTICAL) self.SetSizer(mainSizer) @@ -260,28 +259,24 @@ class ItemDescription ( wx.Panel ): desc = re.sub("<( *)font( *)color( *)=(.*?)>(?P.*?)<( *)/( *)font( *)>", "\g", desc) # Strip URLs desc = re.sub("<( *)a(.*?)>(?P.*?)<( *)/( *)a( *)>", "\g", desc) - desc = "" + desc + "" + desc = "" + desc + "" self.description.SetPage(desc) - mainSizer.Add(self.description, 1, wx.ALL|wx.EXPAND, 0) + mainSizer.Add(self.description, 1, wx.ALL | wx.EXPAND, 0) self.Layout() -########################################################################### -## Class ItemParams -########################################################################### -class ItemParams (wx.Panel): - def __init__(self, parent, stuff, item, context = None): - wx.Panel.__init__ (self, parent) - mainSizer = wx.BoxSizer( wx.VERTICAL ) +class ItemParams(wx.Panel): + def __init__(self, parent, stuff, item, context=None): + wx.Panel.__init__(self, parent) + mainSizer = wx.BoxSizer(wx.VERTICAL) self.paramList = AutoListCtrl(self, wx.ID_ANY, - style = #wx.LC_HRULES | - #wx.LC_NO_HEADER | - wx.LC_REPORT |wx.LC_SINGLE_SEL |wx.LC_VRULES |wx.NO_BORDER) - mainSizer.Add( self.paramList, 1, wx.ALL|wx.EXPAND, 0 ) - self.SetSizer( mainSizer ) + style=wx.LC_REPORT | wx.LC_SINGLE_SEL | wx.LC_VRULES | wx.NO_BORDER) + mainSizer.Add(self.paramList, 1, wx.ALL | wx.EXPAND, 0) + self.SetSizer(mainSizer) self.toggleView = 1 self.stuff = stuff @@ -290,29 +285,31 @@ class ItemParams (wx.Panel): self.attrValues = {} self._fetchValues() - self.m_staticline = wx.StaticLine( self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LI_HORIZONTAL ) - mainSizer.Add( self.m_staticline, 0, wx.EXPAND) - bSizer = wx.BoxSizer( wx.HORIZONTAL ) + self.m_staticline = wx.StaticLine(self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LI_HORIZONTAL) + mainSizer.Add(self.m_staticline, 0, wx.EXPAND) + bSizer = wx.BoxSizer(wx.HORIZONTAL) - self.totalAttrsLabel = wx.StaticText( self, wx.ID_ANY, u" ", wx.DefaultPosition, wx.DefaultSize, 0 ) - bSizer.Add( self.totalAttrsLabel, 0, wx.ALIGN_CENTER_VERTICAL|wx.RIGHT) + self.totalAttrsLabel = wx.StaticText(self, wx.ID_ANY, u" ", wx.DefaultPosition, wx.DefaultSize, 0) + bSizer.Add(self.totalAttrsLabel, 0, wx.ALIGN_CENTER_VERTICAL | wx.RIGHT) - self.toggleViewBtn = wx.ToggleButton( self, wx.ID_ANY, u"Toggle view mode", wx.DefaultPosition, wx.DefaultSize, 0 ) - bSizer.Add( self.toggleViewBtn, 0, wx.ALIGN_CENTER_VERTICAL) + self.toggleViewBtn = wx.ToggleButton(self, wx.ID_ANY, u"Toggle view mode", wx.DefaultPosition, wx.DefaultSize, + 0) + bSizer.Add(self.toggleViewBtn, 0, wx.ALIGN_CENTER_VERTICAL) - self.exportStatsBtn = wx.ToggleButton( self, wx.ID_ANY, u"Export Item Stats", wx.DefaultPosition, wx.DefaultSize, 0 ) - bSizer.Add( self.exportStatsBtn, 0, wx.ALIGN_CENTER_VERTICAL) + self.exportStatsBtn = wx.ToggleButton(self, wx.ID_ANY, u"Export Item Stats", wx.DefaultPosition, wx.DefaultSize, + 0) + bSizer.Add(self.exportStatsBtn, 0, wx.ALIGN_CENTER_VERTICAL) if stuff is not None: - self.refreshBtn = wx.Button( self, wx.ID_ANY, u"Refresh", wx.DefaultPosition, wx.DefaultSize, wx.BU_EXACTFIT ) - bSizer.Add( self.refreshBtn, 0, wx.ALIGN_CENTER_VERTICAL) - self.refreshBtn.Bind( wx.EVT_BUTTON, self.RefreshValues ) + self.refreshBtn = wx.Button(self, wx.ID_ANY, u"Refresh", wx.DefaultPosition, wx.DefaultSize, wx.BU_EXACTFIT) + bSizer.Add(self.refreshBtn, 0, wx.ALIGN_CENTER_VERTICAL) + self.refreshBtn.Bind(wx.EVT_BUTTON, self.RefreshValues) - mainSizer.Add( bSizer, 0, wx.ALIGN_RIGHT) + mainSizer.Add(bSizer, 0, wx.ALIGN_RIGHT) self.PopulateList() - self.toggleViewBtn.Bind(wx.EVT_TOGGLEBUTTON,self.ToggleViewMode) + self.toggleViewBtn.Bind(wx.EVT_TOGGLEBUTTON, self.ToggleViewMode) self.exportStatsBtn.Bind(wx.EVT_TOGGLEBUTTON, self.ExportItemStats) def _fetchValues(self): @@ -348,7 +345,7 @@ class ItemParams (wx.Panel): event.Skip() def ToggleViewMode(self, event): - self.toggleView *=-1 + self.toggleView *= -1 self.UpdateList() event.Skip() @@ -365,13 +362,13 @@ class ItemParams (wx.Panel): writer = csv.writer(exportFile, delimiter=',') writer.writerow( - [ - "ID", - "Internal Name", - "Friendly Name", - "Modified Value", - "Base Value", - ] + [ + "ID", + "Internal Name", + "Friendly Name", + "Modified Value", + "Base Value", + ] ) for attribute in self.attrValues: @@ -402,27 +399,27 @@ class ItemParams (wx.Panel): attribute_modified_value = self.attrValues[attribute] writer.writerow( - [ - attribute_id, - attribute_name, - attribute_displayname, - attribute_modified_value, - attribute_value, - ] + [ + attribute_id, + attribute_name, + attribute_displayname, + attribute_modified_value, + attribute_value, + ] ) def PopulateList(self): - self.paramList.InsertColumn(0,"Attribute") - self.paramList.InsertColumn(1,"Current Value") + self.paramList.InsertColumn(0, "Attribute") + self.paramList.InsertColumn(1, "Current Value") if self.stuff is not None: - self.paramList.InsertColumn(2,"Base Value") - self.paramList.SetColumnWidth(0,110) - self.paramList.SetColumnWidth(1,90) + self.paramList.InsertColumn(2, "Base Value") + self.paramList.SetColumnWidth(0, 110) + self.paramList.SetColumnWidth(1, 90) if self.stuff is not None: - self.paramList.SetColumnWidth(2,90) + self.paramList.SetColumnWidth(2, 90) self.paramList.setResizeColumn(0) self.imageList = wx.ImageList(16, 16) - self.paramList.SetImageList(self.imageList,wx.IMAGE_LIST_SMALL) + self.paramList.SetImageList(self.imageList, wx.IMAGE_LIST_SMALL) names = list(self.attrValues.iterkeys()) names.sort() @@ -461,7 +458,6 @@ class ItemParams (wx.Panel): else: attrIcon = self.imageList.Add(BitmapLoader.getBitmap("7_15", "icons")) - index = self.paramList.InsertImageStringItem(sys.maxint, attrName, attrIcon) idNameMap[idCount] = attrName self.paramList.SetItemData(index, idCount) @@ -485,51 +481,52 @@ class ItemParams (wx.Panel): if self.stuff is not None: self.paramList.SetStringItem(index, 2, valueUnitDefault) - - self.paramList.SortItems(lambda id1, id2: cmp(idNameMap[id1], idNameMap[id2])) self.paramList.RefreshRows() - self.totalAttrsLabel.SetLabel("%d attributes. " %idCount) + self.totalAttrsLabel.SetLabel("%d attributes. " % idCount) self.Layout() - def TranslateValueUnit(self, value, unitName, unitDisplayName): + @staticmethod + def TranslateValueUnit(value, unitName, unitDisplayName): def itemIDCallback(): - item = service.Market.getInstance().getItem(value) + item = Market.getInstance().getItem(value) return "%s (%d)" % (item.name, value) if item is not None else str(value) def groupIDCallback(): - group = service.Market.getInstance().getGroup(value) + group = Market.getInstance().getGroup(value) return "%s (%d)" % (group.name, value) if group is not None else str(value) def attributeIDCallback(): - attribute = service.Attribute.getInstance().getAttributeInfo(value) + attribute = Attribute.getInstance().getAttributeInfo(value) return "%s (%d)" % (attribute.name.capitalize(), value) - trans = {"Inverse Absolute Percent": (lambda: (1-value)*100, unitName), - "Inversed Modifier Percent": (lambda: (1-value) * 100, unitName), - "Modifier Percent": (lambda: ("%+.2f" if ((value - 1) * 100) % 1 else "%+d") % ((value - 1) * 100), unitName), - "Volume": (lambda: value, u"m\u00B3"), - "Sizeclass": (lambda: value, ""), - "Absolute Percent": (lambda: (value * 100) , unitName), - "Milliseconds": (lambda: value / 1000.0, unitName), - "typeID": (itemIDCallback, ""), - "groupID": (groupIDCallback,""), - "attributeID": (attributeIDCallback, "")} + trans = { + "Inverse Absolute Percent" : (lambda: (1 - value) * 100, unitName), + "Inversed Modifier Percent": (lambda: (1 - value) * 100, unitName), + "Modifier Percent" : ( + lambda: ("%+.2f" if ((value - 1) * 100) % 1 else "%+d") % ((value - 1) * 100), unitName), + "Volume" : (lambda: value, u"m\u00B3"), + "Sizeclass" : (lambda: value, ""), + "Absolute Percent" : (lambda: (value * 100), unitName), + "Milliseconds" : (lambda: value / 1000.0, unitName), + "typeID" : (itemIDCallback, ""), + "groupID" : (groupIDCallback, ""), + "attributeID" : (attributeIDCallback, "") + } override = trans.get(unitDisplayName) if override is not None: - - if type(override[0]()) == type(str()): - fvalue = override[0]() + v = override[0]() + if isinstance(v, str): + fvalue = v + elif isinstance(v, (int, float, long)): + fvalue = formatAmount(v, 3, 0, 0) else: - v = override[0]() - if isinstance(v, (int, float, long)): - fvalue = formatAmount(v, 3, 0, 0) - else: - fvalue = v - return "%s %s" % (fvalue , override[1]) + fvalue = v + return "%s %s" % (fvalue, override[1]) else: - return "%s %s" % (formatAmount(value, 3, 0),unitName) + return "%s %s" % (formatAmount(value, 3, 0), unitName) + class ItemCompare(wx.Panel): def __init__(self, parent, stuff, item, items, context=None): @@ -537,9 +534,7 @@ class ItemCompare(wx.Panel): mainSizer = wx.BoxSizer(wx.VERTICAL) self.paramList = AutoListCtrl(self, wx.ID_ANY, - style= # wx.LC_HRULES | - # wx.LC_NO_HEADER | - wx.LC_REPORT | wx.LC_SINGLE_SEL | wx.LC_VRULES | wx.NO_BORDER) + style=wx.LC_REPORT | wx.LC_SINGLE_SEL | wx.LC_VRULES | wx.NO_BORDER) mainSizer.Add(self.paramList, 1, wx.ALL | wx.EXPAND, 0) self.SetSizer(mainSizer) @@ -548,7 +543,8 @@ class ItemCompare(wx.Panel): self.currentSort = None self.sortReverse = False self.item = item - self.items = sorted(items, key=lambda x: x.attributes['metaLevel'].value if 'metaLevel' in x.attributes else None) + self.items = sorted(items, + key=lambda x: x.attributes['metaLevel'].value if 'metaLevel' in x.attributes else None) self.attrs = {} # get a dict of attrName: attrInfo of all unique attributes across all items @@ -603,7 +599,7 @@ class ItemCompare(wx.Panel): self.toggleViewBtn.Bind(wx.EVT_TOGGLEBUTTON, self.ToggleViewMode) self.Bind(wx.EVT_LIST_COL_CLICK, self.SortCompareCols) - def SortCompareCols(self,event): + def SortCompareCols(self, event): self.Freeze() self.paramList.ClearAll() self.PopulateList(event.Column) @@ -627,7 +623,7 @@ class ItemCompare(wx.Panel): def processPrices(self, prices): for i, price in enumerate(prices): - self.paramList.SetStringItem(i, len(self.attrs)+1, formatAmount(price.price, 3, 3, 9, currency=True)) + self.paramList.SetStringItem(i, len(self.attrs) + 1, formatAmount(price.price, 3, 3, 9, currency=True)) def PopulateList(self, sort=None): @@ -639,17 +635,17 @@ class ItemCompare(wx.Panel): if sort is not None: if sort == 0: # Name sort - func = lambda x: x.name + func = lambda _val: _val.name else: try: # Remember to reduce by 1, because the attrs array # starts at 0 while the list has the item name as column 0. attr = str(self.attrs.keys()[sort - 1]) - func = lambda x: x.attributes[attr].value if attr in x.attributes else None + func = lambda _val: _val.attributes[attr].value if attr in _val.attributes else None except IndexError: # Clicked on a column that's not part of our array (price most likely) self.sortReverse = False - func = lambda x: x.attributes['metaLevel'].value if 'metaLevel' in x.attributes else None + func = lambda _val: _val.attributes['metaLevel'].value if 'metaLevel' in _val.attributes else None self.items = sorted(self.items, key=func, reverse=self.sortReverse) @@ -658,13 +654,13 @@ class ItemCompare(wx.Panel): for i, attr in enumerate(self.attrs.keys()): name = self.attrs[attr].displayName if self.attrs[attr].displayName else attr - self.paramList.InsertColumn(i+1, name) - self.paramList.SetColumnWidth(i+1, 120) + self.paramList.InsertColumn(i + 1, name) + self.paramList.SetColumnWidth(i + 1, 120) - self.paramList.InsertColumn(len(self.attrs)+1, "Price") - self.paramList.SetColumnWidth(len(self.attrs)+1, 60) + self.paramList.InsertColumn(len(self.attrs) + 1, "Price") + self.paramList.SetColumnWidth(len(self.attrs) + 1, 60) - sMkt = service.Market.getInstance() + sMkt = Market.getInstance() sMkt.getPrices([x.ID for x in self.items], self.processPrices) for item in self.items: @@ -675,74 +671,69 @@ class ItemCompare(wx.Panel): value = item.attributes[attr].value if self.toggleView != 1: valueUnit = str(value) - if info and info.unit: + elif info and info.unit and self.toggleView == 1: valueUnit = self.TranslateValueUnit(value, info.unit.displayName, info.unit.name) else: valueUnit = formatAmount(value, 3, 0, 0) - self.paramList.SetStringItem(i, x+1, valueUnit) + self.paramList.SetStringItem(i, x + 1, valueUnit) self.paramList.RefreshRows() self.Layout() - def TranslateValueUnit(self, value, unitName, unitDisplayName): + @staticmethod + def TranslateValueUnit(value, unitName, unitDisplayName): def itemIDCallback(): - item = service.Market.getInstance().getItem(value) + item = Market.getInstance().getItem(value) return "%s (%d)" % (item.name, value) if item is not None else str(value) def groupIDCallback(): - group = service.Market.getInstance().getGroup(value) + group = Market.getInstance().getGroup(value) return "%s (%d)" % (group.name, value) if group is not None else str(value) def attributeIDCallback(): - attribute = service.Attribute.getInstance().getAttributeInfo(value) + attribute = Attribute.getInstance().getAttributeInfo(value) return "%s (%d)" % (attribute.name.capitalize(), value) - trans = {"Inverse Absolute Percent": (lambda: (1 - value) * 100, unitName), - "Inversed Modifier Percent": (lambda: (1 - value) * 100, unitName), - "Modifier Percent": ( - lambda: ("%+.2f" if ((value - 1) * 100) % 1 else "%+d") % ((value - 1) * 100), unitName), - "Volume": (lambda: value, u"m\u00B3"), - "Sizeclass": (lambda: value, ""), - "Absolute Percent": (lambda: (value * 100), unitName), - "Milliseconds": (lambda: value / 1000.0, unitName), - "typeID": (itemIDCallback, ""), - "groupID": (groupIDCallback, ""), - "attributeID": (attributeIDCallback, "")} + trans = { + "Inverse Absolute Percent" : (lambda: (1 - value) * 100, unitName), + "Inversed Modifier Percent": (lambda: (1 - value) * 100, unitName), + "Modifier Percent" : (lambda: ("%+.2f" if ((value - 1) * 100) % 1 else "%+d") % ((value - 1) * 100), unitName), + "Volume" : (lambda: value, u"m\u00B3"), + "Sizeclass" : (lambda: value, ""), + "Absolute Percent" : (lambda: (value * 100), unitName), + "Milliseconds" : (lambda: value / 1000.0, unitName), + "typeID" : (itemIDCallback, ""), + "groupID" : (groupIDCallback, ""), + "attributeID" : (attributeIDCallback, "") + } override = trans.get(unitDisplayName) if override is not None: - - if type(override[0]()) == type(str()): - fvalue = override[0]() + v = override[0]() + if isinstance(v, str): + fvalue = v + elif isinstance(v, (int, float, long)): + fvalue = formatAmount(v, 3, 0, 0) else: - v = override[0]() - if isinstance(v, (int, float, long)): - fvalue = formatAmount(v, 3, 0, 0) - else: - fvalue = v + fvalue = v return "%s %s" % (fvalue, override[1]) else: return "%s %s" % (formatAmount(value, 3, 0), unitName) -########################################################################### -## Class ItemRequirements -########################################################################### - -class ItemRequirements ( wx.Panel ): - +class ItemRequirements(wx.Panel): def __init__(self, parent, stuff, item): - wx.Panel.__init__ (self, parent, style = wx.TAB_TRAVERSAL) + wx.Panel.__init__(self, parent, style=wx.TAB_TRAVERSAL) - #itemId is set by the parent. - self.romanNb = ["0","I","II","III","IV","V","VI","VII","VIII","IX","X"] - self.skillIdHistory=[] - mainSizer = wx.BoxSizer( wx.VERTICAL ) + # itemId is set by the parent. + self.romanNb = ["0", "I", "II", "III", "IV", "V", "VI", "VII", "VIII", "IX", "X"] + self.skillIdHistory = [] + mainSizer = wx.BoxSizer(wx.VERTICAL) - self.reqTree = wx.TreeCtrl(self, style = wx.TR_DEFAULT_STYLE | wx.TR_HIDE_ROOT | wx.NO_BORDER) + self.reqTree = wx.TreeCtrl(self, style=wx.TR_DEFAULT_STYLE | wx.TR_HIDE_ROOT | wx.NO_BORDER) - mainSizer.Add(self.reqTree, 1, wx.ALL|wx.EXPAND, 0) + mainSizer.Add(self.reqTree, 1, wx.ALL | wx.EXPAND, 0) self.SetSizer(mainSizer) self.root = self.reqTree.AddRoot("WINRARZOR") @@ -752,25 +743,21 @@ class ItemRequirements ( wx.Panel ): self.reqTree.SetImageList(self.imageList) skillBookId = self.imageList.Add(BitmapLoader.getBitmap("skill_small", "gui")) - self.getFullSkillTree(item,self.root,skillBookId) + self.getFullSkillTree(item, self.root, skillBookId) self.reqTree.ExpandAll() self.Layout() - def getFullSkillTree(self,parentSkill,parent,sbIconId): + def getFullSkillTree(self, parentSkill, parent, sbIconId): for skill, level in parentSkill.requiredSkills.iteritems(): - child = self.reqTree.AppendItem(parent,"%s %s" %(skill.name,self.romanNb[int(level)]), sbIconId) + child = self.reqTree.AppendItem(parent, "%s %s" % (skill.name, self.romanNb[int(level)]), sbIconId) if skill.ID not in self.skillIdHistory: - self.getFullSkillTree(skill,child,sbIconId) + self.getFullSkillTree(skill, child, sbIconId) self.skillIdHistory.append(skill.ID) -########################################################################### -## Class ItemEffects -########################################################################### - -class ItemEffects (wx.Panel): +class ItemEffects(wx.Panel): def __init__(self, parent, stuff, item): wx.Panel.__init__(self, parent) self.item = item @@ -778,10 +765,7 @@ class ItemEffects (wx.Panel): mainSizer = wx.BoxSizer(wx.VERTICAL) self.effectList = AutoListCtrl(self, wx.ID_ANY, - style= - # wx.LC_HRULES | - # wx.LC_NO_HEADER | - wx.LC_REPORT | wx.LC_SINGLE_SEL | wx.LC_VRULES | wx.NO_BORDER) + style=wx.LC_REPORT | wx.LC_SINGLE_SEL | wx.LC_VRULES | wx.NO_BORDER) mainSizer.Add(self.effectList, 1, wx.ALL | wx.EXPAND, 0) self.SetSizer(mainSizer) @@ -793,17 +777,17 @@ class ItemEffects (wx.Panel): def PopulateList(self): - self.effectList.InsertColumn(0,"Name") - self.effectList.InsertColumn(1,"Active") + self.effectList.InsertColumn(0, "Name") + self.effectList.InsertColumn(1, "Active") self.effectList.InsertColumn(2, "Type") if config.debug: self.effectList.InsertColumn(3, "Run Time") - self.effectList.InsertColumn(4,"ID") + self.effectList.InsertColumn(4, "ID") - #self.effectList.SetColumnWidth(0,385) + # self.effectList.SetColumnWidth(0,385) self.effectList.setResizeColumn(0) - self.effectList.SetColumnWidth(1,50) + self.effectList.SetColumnWidth(1, 50) self.effectList.SetColumnWidth(2, 80) if config.debug: self.effectList.SetColumnWidth(3, 65) @@ -865,25 +849,24 @@ class ItemEffects (wx.Panel): self.RefreshValues(event) - def OnRightClick(self, event): + @staticmethod + def OnRightClick(event): """ Debug use: open effect file with default application. If effect file does not exist, create it """ - import os - file = os.path.join(config.pyfaPath, "eos", "effects", "%s.py"%event.GetText().lower()) + file_ = os.path.join(config.pyfaPath, "eos", "effects", "%s.py" % event.GetText().lower()) - if not os.path.isfile(file): - open(file, 'a').close() + if not os.path.isfile(file_): + open(file_, 'a').close() if 'wxMSW' in wx.PlatformInfo: - os.startfile(file) + os.startfile(file_) elif 'wxMac' in wx.PlatformInfo: - os.system("open "+file) + os.system("open " + file_) else: - import subprocess - subprocess.call(["xdg-open", file]) + subprocess.call(["xdg-open", file_]) def RefreshValues(self, event): self.Freeze() @@ -894,13 +877,10 @@ class ItemEffects (wx.Panel): self.Thaw() event.Skip() -########################################################################### -## Class ItemAffectedBy -########################################################################### - -class ItemAffectedBy (wx.Panel): +class ItemAffectedBy(wx.Panel): ORDER = [Fit, Ship, Citadel, Mode, Module, Drone, Fighter, Implant, Booster, Skill] + def __init__(self, parent, stuff, item): wx.Panel.__init__(self, parent) self.stuff = stuff @@ -916,33 +896,33 @@ class ItemAffectedBy (wx.Panel): mainSizer = wx.BoxSizer(wx.VERTICAL) - self.affectedBy = wx.TreeCtrl(self, style = wx.TR_DEFAULT_STYLE | wx.TR_HIDE_ROOT | wx.NO_BORDER) - mainSizer.Add(self.affectedBy, 1, wx.ALL|wx.EXPAND, 0) + self.affectedBy = wx.TreeCtrl(self, style=wx.TR_DEFAULT_STYLE | wx.TR_HIDE_ROOT | wx.NO_BORDER) + mainSizer.Add(self.affectedBy, 1, wx.ALL | wx.EXPAND, 0) - self.m_staticline = wx.StaticLine( self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LI_HORIZONTAL ) + self.m_staticline = wx.StaticLine(self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LI_HORIZONTAL) - mainSizer.Add( self.m_staticline, 0, wx.EXPAND) - bSizer = wx.BoxSizer( wx.HORIZONTAL ) + mainSizer.Add(self.m_staticline, 0, wx.EXPAND) + bSizer = wx.BoxSizer(wx.HORIZONTAL) - self.toggleExpandBtn = wx.ToggleButton( self, wx.ID_ANY, u"Expand All", wx.DefaultPosition, wx.DefaultSize, 0 ) - bSizer.Add( self.toggleExpandBtn, 0, wx.ALIGN_CENTER_VERTICAL) + self.toggleExpandBtn = wx.ToggleButton(self, wx.ID_ANY, u"Expand All", wx.DefaultPosition, wx.DefaultSize, 0) + bSizer.Add(self.toggleExpandBtn, 0, wx.ALIGN_CENTER_VERTICAL) - self.toggleNameBtn = wx.ToggleButton( self, wx.ID_ANY, u"Toggle Names", wx.DefaultPosition, wx.DefaultSize, 0 ) - bSizer.Add( self.toggleNameBtn, 0, wx.ALIGN_CENTER_VERTICAL) + self.toggleNameBtn = wx.ToggleButton(self, wx.ID_ANY, u"Toggle Names", wx.DefaultPosition, wx.DefaultSize, 0) + bSizer.Add(self.toggleNameBtn, 0, wx.ALIGN_CENTER_VERTICAL) - self.toggleViewBtn = wx.ToggleButton( self, wx.ID_ANY, u"Toggle View", wx.DefaultPosition, wx.DefaultSize, 0 ) - bSizer.Add( self.toggleViewBtn, 0, wx.ALIGN_CENTER_VERTICAL) + self.toggleViewBtn = wx.ToggleButton(self, wx.ID_ANY, u"Toggle View", wx.DefaultPosition, wx.DefaultSize, 0) + bSizer.Add(self.toggleViewBtn, 0, wx.ALIGN_CENTER_VERTICAL) if stuff is not None: - self.refreshBtn = wx.Button( self, wx.ID_ANY, u"Refresh", wx.DefaultPosition, wx.DefaultSize, wx.BU_EXACTFIT ) - bSizer.Add( self.refreshBtn, 0, wx.ALIGN_CENTER_VERTICAL) - self.refreshBtn.Bind( wx.EVT_BUTTON, self.RefreshTree ) + self.refreshBtn = wx.Button(self, wx.ID_ANY, u"Refresh", wx.DefaultPosition, wx.DefaultSize, wx.BU_EXACTFIT) + bSizer.Add(self.refreshBtn, 0, wx.ALIGN_CENTER_VERTICAL) + self.refreshBtn.Bind(wx.EVT_BUTTON, self.RefreshTree) - self.toggleNameBtn.Bind(wx.EVT_TOGGLEBUTTON,self.ToggleNameMode) - self.toggleExpandBtn.Bind(wx.EVT_TOGGLEBUTTON,self.ToggleExpand) - self.toggleViewBtn.Bind(wx.EVT_TOGGLEBUTTON,self.ToggleViewMode) + self.toggleNameBtn.Bind(wx.EVT_TOGGLEBUTTON, self.ToggleNameMode) + self.toggleExpandBtn.Bind(wx.EVT_TOGGLEBUTTON, self.ToggleExpand) + self.toggleViewBtn.Bind(wx.EVT_TOGGLEBUTTON, self.ToggleViewMode) - mainSizer.Add( bSizer, 0, wx.ALIGN_RIGHT) + mainSizer.Add(bSizer, 0, wx.ALIGN_RIGHT) self.SetSizer(mainSizer) self.PopulateTree() self.Layout() @@ -964,9 +944,9 @@ class ItemAffectedBy (wx.Panel): # Skills are different in that they don't have itemModifiedAttributes, # which is needed if we send the container to itemStats dialog. So # instead, we send the item. - type = stuff.__class__.__name__ - contexts.append(("itemStats", type)) - menu = ContextMenu.getMenu(stuff if type != "Skill" else stuff.item, *contexts) + type_ = stuff.__class__.__name__ + contexts.append(("itemStats", type_)) + menu = ContextMenu.getMenu(stuff if type_ != "Skill" else stuff.item, *contexts) self.PopupMenu(menu) def ExpandCollapseTree(self): @@ -982,7 +962,7 @@ class ItemAffectedBy (wx.Panel): self.Thaw() - def ToggleExpand(self,event): + def ToggleExpand(self, event): self.expand *= -1 self.ExpandCollapseTree() @@ -1042,20 +1022,22 @@ class ItemAffectedBy (wx.Panel): return attr def buildAttributeView(self, root): - # We first build a usable dictionary of items. The key is either a fit - # if the afflictions stem from a projected fit, or self.stuff if they - # are local afflictions (everything else, even gang boosts at this time) - # The value of this is yet another dictionary in the following format: - # - # "attribute name": { - # "Module Name": [ - # class of affliction, - # affliction item (required due to GH issue #335) - # modifier type - # amount of modification - # whether this affliction was projected - # ] - # } + """ + We first build a usable dictionary of items. The key is either a fit + if the afflictions stem from a projected fit, or self.stuff if they + are local afflictions (everything else, even gang boosts at this time) + The value of this is yet another dictionary in the following format: + + "attribute name": { + "Module Name": [ + class of affliction, + affliction item (required due to GH issue #335) + modifier type + amount of modification + whether this affliction was projected + ] + } + """ attributes = self.stuff.itemModifiedAttributes if self.item == self.stuff.item else self.stuff.chargeModifiedAttributes container = {} @@ -1091,7 +1073,8 @@ class ItemAffectedBy (wx.Panel): else: item = afflictor.item - items[attrName].append((type(afflictor), afflictor, item, modifier, amount, getattr(afflictor, "projected", False))) + items[attrName].append( + (type(afflictor), afflictor, item, modifier, amount, getattr(afflictor, "projected", False))) # Make sure projected fits are on top rootOrder = container.keys() @@ -1167,20 +1150,21 @@ class ItemAffectedBy (wx.Panel): treeItem = self.affectedBy.AppendItem(child, display, itemIcon) self.affectedBy.SetPyData(treeItem, afflictor) - def buildModuleView(self, root): - # We first build a usable dictionary of items. The key is either a fit - # if the afflictions stem from a projected fit, or self.stuff if they - # are local afflictions (everything else, even gang boosts at this time) - # The value of this is yet another dictionary in the following format: - # - # "Module Name": [ - # class of affliction, - # set of afflictors (such as 2 of the same module), - # info on affliction (attribute name, modifier, and modification amount), - # item that will be used to determine icon (required due to GH issue #335) - # whether this affliction is actually used (unlearned skills are not used) - # ] + """ + We first build a usable dictionary of items. The key is either a fit + if the afflictions stem from a projected fit, or self.stuff if they + are local afflictions (everything else, even gang boosts at this time) + The value of this is yet another dictionary in the following format: + + "Module Name": [ + class of affliction, + set of afflictors (such as 2 of the same module), + info on affliction (attribute name, modifier, and modification amount), + item that will be used to determine icon (required due to GH issue #335) + whether this affliction is actually used (unlearned skills are not used) + ] + """ attributes = self.stuff.itemModifiedAttributes if self.item == self.stuff.item else self.stuff.chargeModifiedAttributes container = {} @@ -1291,19 +1275,124 @@ class ItemAffectedBy (wx.Panel): else: penalized = "" - attributes.append((attrName, (displayName if displayName != "" else attrName), attrModifier, attrAmount, penalized, attrIcon)) + attributes.append((attrName, (displayName if displayName != "" else attrName), attrModifier, + attrAmount, penalized, attrIcon)) - attrSorted = sorted(attributes, key = lambda attribName: attribName[0]) + attrSorted = sorted(attributes, key=lambda attribName: attribName[0]) for attr in attrSorted: attrName, displayName, attrModifier, attrAmount, penalized, attrIcon = attr if self.showRealNames: display = "%s %s %.2f %s" % (attrName, attrModifier, attrAmount, penalized) - saved = "%s %s %.2f %s" % ((displayName if displayName != "" else attrName), attrModifier, attrAmount, penalized) + saved = "%s %s %.2f %s" % ( + displayName if displayName != "" else attrName, + attrModifier, + attrAmount, + penalized + ) else: - display = "%s %s %.2f %s" % ((displayName if displayName != "" else attrName), attrModifier, attrAmount, penalized) + display = "%s %s %.2f %s" % ( + displayName if displayName != "" else attrName, + attrModifier, + attrAmount, + penalized + ) saved = "%s %s %.2f %s" % (attrName, attrModifier, attrAmount, penalized) treeitem = self.affectedBy.AppendItem(child, display, attrIcon) self.affectedBy.SetPyData(treeitem, saved) self.treeItems.append(treeitem) + + +class ItemProperties(wx.Panel): + def __init__(self, parent, stuff, item, context=None): + wx.Panel.__init__(self, parent) + mainSizer = wx.BoxSizer(wx.VERTICAL) + + self.paramList = AutoListCtrl(self, wx.ID_ANY, + style=wx.LC_REPORT | wx.LC_SINGLE_SEL | wx.LC_VRULES | wx.NO_BORDER) + mainSizer.Add(self.paramList, 1, wx.ALL | wx.EXPAND, 0) + self.SetSizer(mainSizer) + + self.toggleView = 1 + self.stuff = stuff + self.item = item + self.attrInfo = {} + self.attrValues = {} + self._fetchValues() + + self.m_staticline = wx.StaticLine(self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LI_HORIZONTAL) + mainSizer.Add(self.m_staticline, 0, wx.EXPAND) + bSizer = wx.BoxSizer(wx.HORIZONTAL) + + self.totalAttrsLabel = wx.StaticText(self, wx.ID_ANY, u" ", wx.DefaultPosition, wx.DefaultSize, 0) + bSizer.Add(self.totalAttrsLabel, 0, wx.ALIGN_CENTER_VERTICAL | wx.RIGHT) + + mainSizer.Add(bSizer, 0, wx.ALIGN_RIGHT) + + self.PopulateList() + + def _fetchValues(self): + if self.stuff is None: + self.attrInfo.clear() + self.attrValues.clear() + self.attrInfo.update(self.item.attributes) + self.attrValues.update(self.item.attributes) + elif self.stuff.item == self.item: + self.attrInfo.clear() + self.attrValues.clear() + self.attrInfo.update(self.stuff.item.attributes) + self.attrValues.update(self.stuff.itemModifiedAttributes) + elif self.stuff.charge == self.item: + self.attrInfo.clear() + self.attrValues.clear() + self.attrInfo.update(self.stuff.charge.attributes) + self.attrValues.update(self.stuff.chargeModifiedAttributes) + # When item for stats window no longer exists, don't change anything + else: + return + + def PopulateList(self): + self.paramList.InsertColumn(0, "Attribute") + self.paramList.InsertColumn(1, "Current Value") + self.paramList.SetColumnWidth(0, 110) + self.paramList.SetColumnWidth(1, 1500) + self.paramList.setResizeColumn(0) + + if self.stuff: + names = dir(self.stuff) + else: + names = dir(self.item) + + names = [a for a in names if not (a.startswith('__') and a.endswith('__'))] + + idNameMap = {} + idCount = 0 + for name in names: + try: + if self.stuff: + attrName = name.title() + value = getattr(self.stuff, name) + else: + attrName = name.title() + value = getattr(self.item, name) + except Exception as e: + # TODO: Add logging to this. + # We couldn't get a property for some reason. Skip it for now. + print(e) + continue + + index = self.paramList.InsertStringItem(sys.maxint, attrName) + # index = self.paramList.InsertImageStringItem(sys.maxint, attrName) + idNameMap[idCount] = attrName + self.paramList.SetItemData(index, idCount) + idCount += 1 + + valueUnit = str(value) + + self.paramList.SetStringItem(index, 1, valueUnit) + + self.paramList.SortItems(lambda id1, id2: cmp(idNameMap[id1], idNameMap[id2])) + self.paramList.RefreshRows() + self.totalAttrsLabel.SetLabel("%d attributes. " % idCount) + self.Layout() diff --git a/gui/mainFrame.py b/gui/mainFrame.py index 4c2b350a5..7af37d875 100644 --- a/gui/mainFrame.py +++ b/gui/mainFrame.py @@ -1,4 +1,4 @@ -#=============================================================================== +# ============================================================================= # Copyright (C) 2010 Diego Duclos # # This file is part of pyfa. @@ -15,28 +15,31 @@ # # You should have received a copy of the GNU General Public License # along with pyfa. If not, see . -#=============================================================================== +# ============================================================================= import sys import os.path +from logbook import Logger import sqlalchemy +# noinspection PyPackageRequirements import wx +# noinspection PyPackageRequirements +from wx._core import PyDeadObjectError +# noinspection PyPackageRequirements +from wx.lib.wordwrap import wordwrap +# noinspection PyPackageRequirements +from wx.lib.inspection import InspectionTool import time from codecs import open -from wx._core import PyDeadObjectError -from wx.lib.wordwrap import wordwrap - -import service import config -import threading -import webbrowser + +from eos.config import gamedata_version import gui.aboutData -import gui.chromeTabs -import gui.utils.animUtils as animUtils +from gui.chromeTabs import PFNotebook import gui.globalEvents as GE from gui.bitmapLoader import BitmapLoader @@ -56,40 +59,62 @@ from gui.graphFrame import GraphFrame from gui.copySelectDialog import CopySelectDialog from gui.utils.clipboard import toClipboard, fromClipboard from gui.updateDialog import UpdateDialog -from gui.builtinViews import * +# noinspection PyUnresolvedReferences +from gui.builtinViews import emptyView, entityEditor, fittingView, implantEditor # noqa: F401 +from gui import graphFrame + +from service.settings import SettingsProvider +from service.fit import Fit +from service.character import Character +from service.update import Update # import this to access override setting from eos.modifiedAttributeDict import ModifiedAttributeDict from eos.db.saveddata.loadDefaultDatabaseValues import DefaultDatabaseValues +from eos.db.saveddata.queries import getFit as db_getFit +from service.port import Port +from service.settings import HTMLExportSettings from time import gmtime, strftime -if not 'wxMac' in wx.PlatformInfo or ('wxMac' in wx.PlatformInfo and wx.VERSION >= (3,0)): +import threading +import webbrowser + +if 'wxMac' not in wx.PlatformInfo or ('wxMac' in wx.PlatformInfo and wx.VERSION >= (3, 0)): + from service.crest import Crest from service.crest import CrestModes from gui.crestFittings import CrestFittings, ExportToEve, CrestMgmt - try: - from gui.propertyEditor import AttributeEditor - disableOverrideEditor = False - except ImportError, e: - print "Error loading Attribute Editor: %s.\nAccess to Attribute Editor is disabled."%e.message - disableOverrideEditor = True +disableOverrideEditor = False -#dummy panel(no paint no erasebk) +try: + from gui.propertyEditor import AttributeEditor +except ImportError as e: + AttributeEditor = None + print("Error loading Attribute Editor: %s.\nAccess to Attribute Editor is disabled." % e.message) + disableOverrideEditor = True + +pyfalog = Logger(__name__) + + +# dummy panel(no paint no erasebk) class PFPanel(wx.Panel): - def __init__(self,parent): - wx.Panel.__init__(self,parent) + def __init__(self, parent): + wx.Panel.__init__(self, parent) self.Bind(wx.EVT_PAINT, self.OnPaint) self.Bind(wx.EVT_ERASE_BACKGROUND, self.OnBkErase) def OnPaint(self, event): event.Skip() + def OnBkErase(self, event): pass + 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 @@ -111,51 +136,56 @@ class OpenFitsThread(threading.Thread): wx.PostEvent(self.mainFrame, FitSelected(fitID=self.fits[-1], startup=2)) wx.CallAfter(self.callback) + class MainFrame(wx.Frame): __instance = None + @classmethod def getInstance(cls): return cls.__instance if cls.__instance is not None else MainFrame() - def __init__(self, title): - self.title=title + def __init__(self, title="pyfa"): + pyfalog.debug("Initialize MainFrame") + self.title = title wx.Frame.__init__(self, None, wx.ID_ANY, self.title) MainFrame.__instance = self - #Load stored settings (width/height/maximized..) + # Load stored settings (width/height/maximized..) self.LoadMainFrameAttribs() - #Fix for msw (have the frame background color match panel color - if 'wxMSW' in wx.PlatformInfo: - self.SetBackgroundColour( wx.SystemSettings.GetColour( wx.SYS_COLOUR_BTNFACE ) ) + self.disableOverrideEditor = disableOverrideEditor - #Load and set the icon for pyfa main window + # Fix for msw (have the frame background color match panel color + if 'wxMSW' in wx.PlatformInfo: + self.SetBackgroundColour(wx.SystemSettings.GetColour(wx.SYS_COLOUR_BTNFACE)) + + # Load and set the icon for pyfa main window i = wx.IconFromBitmap(BitmapLoader.getBitmap("pyfa", "gui")) self.SetIcon(i) - #Create the layout and windows + # Create the layout and windows mainSizer = wx.BoxSizer(wx.HORIZONTAL) - self.browser_fitting_split = wx.SplitterWindow(self, style = wx.SP_LIVE_UPDATE) - self.fitting_additions_split = wx.SplitterWindow(self.browser_fitting_split, style = wx.SP_LIVE_UPDATE) + self.browser_fitting_split = wx.SplitterWindow(self, style=wx.SP_LIVE_UPDATE) + self.fitting_additions_split = wx.SplitterWindow(self.browser_fitting_split, style=wx.SP_LIVE_UPDATE) mainSizer.Add(self.browser_fitting_split, 1, wx.EXPAND | wx.LEFT, 2) self.fitMultiSwitch = MultiSwitch(self.fitting_additions_split) self.additionsPane = AdditionsPane(self.fitting_additions_split) - self.notebookBrowsers = gui.chromeTabs.PFNotebook(self.browser_fitting_split, False) + self.notebookBrowsers = PFNotebook(self.browser_fitting_split, False) marketImg = BitmapLoader.getImage("market_small", "gui") shipBrowserImg = BitmapLoader.getImage("ship_small", "gui") self.marketBrowser = MarketBrowser(self.notebookBrowsers) - self.notebookBrowsers.AddPage(self.marketBrowser, "Market", tabImage = marketImg, showClose = False) + self.notebookBrowsers.AddPage(self.marketBrowser, "Market", tabImage=marketImg, showClose=False) self.marketBrowser.splitter.SetSashPosition(self.marketHeight) self.shipBrowser = ShipBrowser(self.notebookBrowsers) - self.notebookBrowsers.AddPage(self.shipBrowser, "Fittings", tabImage = shipBrowserImg, showClose = False) + self.notebookBrowsers.AddPage(self.shipBrowser, "Fittings", tabImage=shipBrowserImg, showClose=False) self.notebookBrowsers.SetSelection(1) @@ -180,31 +210,31 @@ class MainFrame(wx.Frame): self.SetSizer(mainSizer) - #Add menu + # Add menu self.addPageId = wx.NewId() self.closePageId = wx.NewId() self.widgetInspectMenuID = wx.NewId() - self.SetMenuBar(MainMenuBar()) + self.SetMenuBar(MainMenuBar(self)) self.registerMenu() - #Internal vars to keep track of other windows (graphing/stats) + # Internal vars to keep track of other windows (graphing/stats) self.graphFrame = None self.statsWnds = [] self.activeStatsWnd = None self.Bind(wx.EVT_CLOSE, self.OnClose) - #Show ourselves + # Show ourselves self.Show() self.LoadPreviousOpenFits() - #Check for updates - self.sUpdate = service.Update.getInstance() + # Check for updates + self.sUpdate = Update.getInstance() self.sUpdate.CheckUpdate(self.ShowUpdateBox) - if not 'wxMac' in wx.PlatformInfo or ('wxMac' in wx.PlatformInfo and wx.VERSION >= (3,0)): + if 'wxMac' not in wx.PlatformInfo or ('wxMac' in wx.PlatformInfo and wx.VERSION >= (3, 0)): self.Bind(GE.EVT_SSO_LOGIN, self.onSSOLogin) self.Bind(GE.EVT_SSO_LOGOUT, self.onSSOLogout) @@ -216,9 +246,10 @@ class MainFrame(wx.Frame): dlg.ShowModal() def LoadPreviousOpenFits(self): - sFit = service.Fit.getInstance() + sFit = Fit.getInstance() - self.prevOpenFits = service.SettingsProvider.getInstance().getSettings("pyfaPrevOpenFits", {"enabled": False, "pyfaOpenFits": []}) + self.prevOpenFits = SettingsProvider.getInstance().getSettings("pyfaPrevOpenFits", + {"enabled": False, "pyfaOpenFits": []}) fits = self.prevOpenFits['pyfaOpenFits'] # Remove any fits that cause exception when fetching (non-existent fits) @@ -237,8 +268,10 @@ class MainFrame(wx.Frame): OpenFitsThread(fits, self.closeWaitDialog) def LoadMainFrameAttribs(self): - mainFrameDefaultAttribs = {"wnd_width": 1000, "wnd_height": 700, "wnd_maximized": False, "browser_width": 300, "market_height": 0, "fitting_height": -200} - self.mainFrameAttribs = service.SettingsProvider.getInstance().getSettings("pyfaMainWindowAttribs", mainFrameDefaultAttribs) + mainFrameDefaultAttribs = {"wnd_width": 1000, "wnd_height": 700, "wnd_maximized": False, "browser_width": 300, + "market_height": 0, "fitting_height": -200} + self.mainFrameAttribs = SettingsProvider.getInstance().getSettings("pyfaMainWindowAttribs", + mainFrameDefaultAttribs) if self.mainFrameAttribs["wnd_maximized"]: width = mainFrameDefaultAttribs["wnd_width"] @@ -258,7 +291,7 @@ class MainFrame(wx.Frame): def UpdateMainFrameAttribs(self): if self.IsIconized(): return - width,height = self.GetSize() + width, height = self.GetSize() self.mainFrameAttribs["wnd_width"] = width self.mainFrameAttribs["wnd_height"] = height @@ -292,7 +325,7 @@ class MainFrame(wx.Frame): return m() if m is not None else None def getActiveView(self): - sel = self.fitMultiSwitch.GetSelectedPage() + self.fitMultiSwitch.GetSelectedPage() def CloseCurrentPage(self, evt): ms = self.fitMultiSwitch @@ -305,14 +338,14 @@ class MainFrame(wx.Frame): self.UpdateMainFrameAttribs() # save open fits - self.prevOpenFits['pyfaOpenFits'] = [] # clear old list + self.prevOpenFits['pyfaOpenFits'] = [] # clear old list for page in self.fitMultiSwitch.pages: m = getattr(page, "getActiveFit", None) if m is not None: - self.prevOpenFits['pyfaOpenFits'].append(m()) + self.prevOpenFits['pyfaOpenFits'].append(m()) # save all teh settingz - service.SettingsProvider.getInstance().saveAll() + SettingsProvider.getInstance().saveAll() event.Skip() def ExitApp(self, event): @@ -320,86 +353,107 @@ class MainFrame(wx.Frame): event.Skip() def ShowAboutBox(self, evt): - import eos.config v = sys.version_info info = wx.AboutDialogInfo() info.Name = "pyfa" info.Version = gui.aboutData.versionString + + try: + import matplotlib + matplotlib_version = matplotlib.__version__ + except: + matplotlib_version = None + info.Description = wordwrap(gui.aboutData.description + "\n\nDevelopers:\n\t" + - "\n\t".join(gui.aboutData.developers) + - "\n\nAdditional credits:\n\t" + - "\n\t".join(gui.aboutData.credits) + - "\n\nLicenses:\n\t" + - "\n\t".join(gui.aboutData.licenses) + - "\n\nEVE Data: \t" + eos.config.gamedata_version + - "\nPython: \t\t" + '{}.{}.{}'.format(v.major, v.minor, v.micro) + - "\nwxPython: \t" + wx.__version__ + - "\nSQLAlchemy: \t" + sqlalchemy.__version__, - 500, wx.ClientDC(self)) - if "__WXGTK__" in wx.PlatformInfo: + "\n\t".join(gui.aboutData.developers) + + "\n\nAdditional credits:\n\t" + + "\n\t".join(gui.aboutData.credits) + + "\n\nLicenses:\n\t" + + "\n\t".join(gui.aboutData.licenses) + + "\n\nEVE Data: \t" + gamedata_version + + "\nPython: \t\t" + '{}.{}.{}'.format(v.major, v.minor, v.micro) + + "\nwxPython: \t" + wx.__version__ + + "\nSQLAlchemy: \t" + sqlalchemy.__version__ + + "\nmatplotlib: \t {}".format(matplotlib_version if matplotlib_version else "Not Installed"), + 500, wx.ClientDC(self)) + if "__WXGTK__" in wx.PlatformInfo: forumUrl = "http://forums.eveonline.com/default.aspx?g=posts&t=466425" else: forumUrl = "http://forums.eveonline.com/default.aspx?g=posts&t=466425" info.WebSite = (forumUrl, "pyfa thread at EVE Online forum") wx.AboutBox(info) - def showCharacterEditor(self, event): - dlg=CharacterEditor(self) + dlg = CharacterEditor(self) dlg.Show() def showAttrEditor(self, event): - dlg=AttributeEditor(self) + dlg = AttributeEditor(self) dlg.Show() def showTargetResistsEditor(self, event): ResistsEditorDlg(self) def showDamagePatternEditor(self, event): - dlg=DmgPatternEditorDlg(self) + dlg = DmgPatternEditorDlg(self) dlg.ShowModal() - dlg.Destroy() + try: + dlg.Destroy() + except PyDeadObjectError: + pyfalog.error("Tried to destroy an object that doesn't exist in .") def showImplantSetEditor(self, event): ImplantSetEditorDlg(self) def showExportDialog(self, event): """ Export active fit """ - sFit = service.Fit.getInstance() + sFit = Fit.getInstance() fit = sFit.getFit(self.getActiveFit()) - defaultFile = "%s - %s.xml"%(fit.ship.item.name, fit.name) if fit else None + defaultFile = "%s - %s.xml" % (fit.ship.item.name, fit.name) if fit else None dlg = wx.FileDialog(self, "Save Fitting As...", - wildcard = "EVE XML fitting files (*.xml)|*.xml", - style = wx.FD_SAVE, + wildcard="EVE XML fitting files (*.xml)|*.xml", + style=wx.FD_SAVE, defaultFile=defaultFile) if dlg.ShowModal() == wx.ID_OK: - format = dlg.GetFilterIndex() + format_ = dlg.GetFilterIndex() path = dlg.GetPath() - if format == 0: - output = sFit.exportXml(None, self.getActiveFit()) + if format_ == 0: + sPort = Port.getInstance() + output = sPort.exportXml(None, fit) if '.' not in os.path.basename(path): path += ".xml" else: - print "oops, invalid fit format %d" % format - dlg.Destroy() + print("oops, invalid fit format %d" % format_) + try: + dlg.Destroy() + except PyDeadObjectError: + pyfalog.error("Tried to destroy an object that doesn't exist in .") return - file = open(path, "w", encoding="utf-8") - file.write(output) - file.close() - dlg.Destroy() + + with open(path, "w", encoding="utf-8") as openfile: + openfile.write(output) + openfile.close() + + try: + dlg.Destroy() + except PyDeadObjectError: + pyfalog.error("Tried to destroy an object that doesn't exist in .") def showPreferenceDialog(self, event): dlg = PreferenceDialog(self) dlg.ShowModal() - def goWiki(self, event): + @staticmethod + def goWiki(event): webbrowser.open('https://github.com/pyfa-org/Pyfa/wiki') - def goForums(self, event): + @staticmethod + def goForums(event): webbrowser.open('https://forums.eveonline.com/default.aspx?g=posts&t=466425') - def loadDatabaseDefaults(self, event): + @staticmethod + def loadDatabaseDefaults(event): # Import values that must exist otherwise Pyfa breaks DefaultDatabaseValues.importRequiredDefaults() # Import default values for damage profiles @@ -415,7 +469,7 @@ class MainFrame(wx.Frame): self.Bind(wx.EVT_MENU, self.loadDatabaseDefaults, id=menuBar.importDatabaseDefaultsId) # Widgets Inspector if config.debug: - self.Bind(wx.EVT_MENU, self.openWXInspectTool, id = self.widgetInspectMenuID) + self.Bind(wx.EVT_MENU, self.openWXInspectTool, id=self.widgetInspectMenuID) # About self.Bind(wx.EVT_MENU, self.ShowAboutBox, id=wx.ID_ABOUT) # Char editor @@ -443,32 +497,32 @@ class MainFrame(wx.Frame): # Preference dialog self.Bind(wx.EVT_MENU, self.showPreferenceDialog, id=wx.ID_PREFERENCES) # User guide - self.Bind(wx.EVT_MENU, self.goWiki, id = menuBar.wikiId) + self.Bind(wx.EVT_MENU, self.goWiki, id=menuBar.wikiId) # EVE Forums - self.Bind(wx.EVT_MENU, self.goForums, id = menuBar.forumId) + self.Bind(wx.EVT_MENU, self.goForums, id=menuBar.forumId) # Save current character - self.Bind(wx.EVT_MENU, self.saveChar, id = menuBar.saveCharId) + self.Bind(wx.EVT_MENU, self.saveChar, id=menuBar.saveCharId) # Save current character as another character - self.Bind(wx.EVT_MENU, self.saveCharAs, id = menuBar.saveCharAsId) + self.Bind(wx.EVT_MENU, self.saveCharAs, id=menuBar.saveCharAsId) # Save current character - self.Bind(wx.EVT_MENU, self.revertChar, id = menuBar.revertCharId) + self.Bind(wx.EVT_MENU, self.revertChar, id=menuBar.revertCharId) # Browse fittings - self.Bind(wx.EVT_MENU, self.eveFittings, id = menuBar.eveFittingsId) + self.Bind(wx.EVT_MENU, self.eveFittings, id=menuBar.eveFittingsId) # Export to EVE - self.Bind(wx.EVT_MENU, self.exportToEve, id = menuBar.exportToEveId) + self.Bind(wx.EVT_MENU, self.exportToEve, id=menuBar.exportToEveId) # Handle SSO event (login/logout/manage characters, depending on mode and current state) - self.Bind(wx.EVT_MENU, self.ssoHandler, id = menuBar.ssoLoginId) + self.Bind(wx.EVT_MENU, self.ssoHandler, id=menuBar.ssoLoginId) # Open attribute editor - self.Bind(wx.EVT_MENU, self.showAttrEditor, id = menuBar.attrEditorId) + self.Bind(wx.EVT_MENU, self.showAttrEditor, id=menuBar.attrEditorId) # Toggle Overrides - self.Bind(wx.EVT_MENU, self.toggleOverrides, id = menuBar.toggleOverridesId) + self.Bind(wx.EVT_MENU, self.toggleOverrides, id=menuBar.toggleOverridesId) - #Clipboard exports + # Clipboard exports self.Bind(wx.EVT_MENU, self.exportToClipboard, id=wx.ID_COPY) - #Graphs + # Graphs self.Bind(wx.EVT_MENU, self.openGraphFrame, id=menuBar.graphFrameId) toggleSearchBoxId = wx.NewId() @@ -478,11 +532,11 @@ class MainFrame(wx.Frame): # Close Page self.Bind(wx.EVT_MENU, self.CloseCurrentPage, id=self.closePageId) - self.Bind(wx.EVT_MENU, self.HAddPage, id = self.addPageId) - self.Bind(wx.EVT_MENU, self.toggleSearchBox, id = toggleSearchBoxId) - self.Bind(wx.EVT_MENU, self.toggleShipMarket, id = toggleShipMarketId) - self.Bind(wx.EVT_MENU, self.CTabNext, id = ctabnext) - self.Bind(wx.EVT_MENU, self.CTabPrev, id = ctabprev) + self.Bind(wx.EVT_MENU, self.HAddPage, id=self.addPageId) + self.Bind(wx.EVT_MENU, self.toggleSearchBox, id=toggleSearchBoxId) + self.Bind(wx.EVT_MENU, self.toggleShipMarket, id=toggleShipMarketId) + self.Bind(wx.EVT_MENU, self.CTabNext, id=ctabnext) + self.Bind(wx.EVT_MENU, self.CTabPrev, id=ctabprev) actb = [(wx.ACCEL_CTRL, ord('T'), self.addPageId), (wx.ACCEL_CMD, ord('T'), self.addPageId), @@ -515,30 +569,30 @@ class MainFrame(wx.Frame): for i in range(0, self.additionsPane.notebook.GetPageCount()): self.additionsSelect.append(wx.NewId()) self.Bind(wx.EVT_MENU, self.AdditionsTabSelect, id=self.additionsSelect[i]) - actb.append((wx.ACCEL_CMD, i+49, self.additionsSelect[i])) - actb.append((wx.ACCEL_CTRL, i+49, self.additionsSelect[i])) + actb.append((wx.ACCEL_CMD, i + 49, self.additionsSelect[i])) + actb.append((wx.ACCEL_CTRL, i + 49, self.additionsSelect[i])) # Alt+1-9 for market item selection self.itemSelect = [] for i in range(0, 9): self.itemSelect.append(wx.NewId()) - self.Bind(wx.EVT_MENU, self.ItemSelect, id = self.itemSelect[i]) + self.Bind(wx.EVT_MENU, self.ItemSelect, id=self.itemSelect[i]) actb.append((wx.ACCEL_ALT, i + 49, self.itemSelect[i])) atable = wx.AcceleratorTable(actb) self.SetAcceleratorTable(atable) def eveFittings(self, event): - dlg=CrestFittings(self) + dlg = CrestFittings(self) dlg.Show() def updateTitle(self, event): - sCrest = service.Crest.getInstance() + sCrest = Crest.getInstance() char = sCrest.implicitCharacter if char: - t = time.gmtime(char.eve.expires-time.time()) + t = time.gmtime(char.eve.expires - time.time()) sTime = time.strftime("%H:%M:%S", t if t >= 0 else 0) - newTitle = "%s | %s - %s"%(self.title, char.name, sTime) + newTitle = "%s | %s - %s" % (self.title, char.name, sTime) self.SetTitle(newTitle) def onSSOLogin(self, event): @@ -568,7 +622,7 @@ class MainFrame(wx.Frame): self.SetTitle(self.title) menu = self.GetMenuBar() - sCrest = service.Crest.getInstance() + sCrest = Crest.getInstance() if type == CrestModes.IMPLICIT: menu.SetLabel(menu.ssoLoginId, "Login to EVE") @@ -581,7 +635,7 @@ class MainFrame(wx.Frame): menu.Enable(menu.exportToEveId, not enable) def ssoHandler(self, event): - sCrest = service.Crest.getInstance() + sCrest = Crest.getInstance() if sCrest.settings.get('mode') == CrestModes.IMPLICIT: if sCrest.implicitCharacter is not None: sCrest.logout() @@ -589,21 +643,22 @@ class MainFrame(wx.Frame): uri = sCrest.startServer() webbrowser.open(uri) else: - dlg=CrestMgmt(self) + dlg = CrestMgmt(self) dlg.Show() def exportToEve(self, event): - dlg=ExportToEve(self) + dlg = ExportToEve(self) dlg.Show() def toggleOverrides(self, event): ModifiedAttributeDict.OVERRIDES = not ModifiedAttributeDict.OVERRIDES wx.PostEvent(self, GE.FitChanged(fitID=self.getActiveFit())) menu = self.GetMenuBar() - menu.SetLabel(menu.toggleOverridesId, "Turn Overrides Off" if ModifiedAttributeDict.OVERRIDES else "Turn Overrides On") + menu.SetLabel(menu.toggleOverridesId, + "Turn Overrides Off" if ModifiedAttributeDict.OVERRIDES else "Turn Overrides On") def saveChar(self, event): - sChr = service.Character.getInstance() + sChr = Character.getInstance() charID = self.charSelection.getActiveCharacter() sChr.saveCharacter(charID) wx.PostEvent(self, GE.CharListUpdated()) @@ -614,7 +669,7 @@ class MainFrame(wx.Frame): dlg.ShowModal() def revertChar(self, event): - sChr = service.Character.getInstance() + sChr = Character.getInstance() charID = self.charSelection.getActiveCharacter() sChr.revertCharacter(charID) wx.PostEvent(self, GE.CharListUpdated()) @@ -637,7 +692,7 @@ class MainFrame(wx.Frame): def CTabPrev(self, event): self.fitMultiSwitch.PrevPage() - def HAddPage(self,event): + def HAddPage(self, event): self.fitMultiSwitch.AddPage() def toggleShipMarket(self, event): @@ -652,35 +707,35 @@ class MainFrame(wx.Frame): self.marketBrowser.search.Focus() def clipboardEft(self): - sFit = service.Fit.getInstance() - toClipboard(sFit.exportFit(self.getActiveFit())) + fit = db_getFit(self.getActiveFit()) + toClipboard(Port.exportEft(fit)) def clipboardEftImps(self): - sFit = service.Fit.getInstance() - toClipboard(sFit.exportEftImps(self.getActiveFit())) + fit = db_getFit(self.getActiveFit()) + toClipboard(Port.exportEftImps(fit)) def clipboardDna(self): - sFit = service.Fit.getInstance() - toClipboard(sFit.exportDna(self.getActiveFit())) + fit = db_getFit(self.getActiveFit()) + toClipboard(Port.exportDna(fit)) def clipboardCrest(self): - sFit = service.Fit.getInstance() - toClipboard(sFit.exportCrest(self.getActiveFit())) + fit = db_getFit(self.getActiveFit()) + toClipboard(Port.exportCrest(fit)) def clipboardXml(self): - sFit = service.Fit.getInstance() - toClipboard(sFit.exportXml(None, self.getActiveFit())) + fit = db_getFit(self.getActiveFit()) + toClipboard(Port.exportXml(None, fit)) def clipboardMultiBuy(self): - sFit = service.Fit.getInstance() - toClipboard(sFit.exportMultiBuy(self.getActiveFit())) + fit = db_getFit(self.getActiveFit()) + toClipboard(Port.exportMultiBuy(fit)) def importFromClipboard(self, event): - sFit = service.Fit.getInstance() + clipboard = fromClipboard() try: - fits = sFit.importFitFromBuffer(fromClipboard(), self.getActiveFit()) + fits = Port().importFitFromBuffer(clipboard, self.getActiveFit()) except: - pass + pyfalog.error("Attempt to import failed:\n{0}", clipboard) else: self._openAfterImport(fits) @@ -697,17 +752,22 @@ class MainFrame(wx.Frame): CopySelectDict[selected]() - - dlg.Destroy() + try: + dlg.Destroy() + except PyDeadObjectError: + pyfalog.error("Tried to destroy an object that doesn't exist in .") def exportSkillsNeeded(self, event): """ Exports skills needed for active fit and active character """ - sCharacter = service.Character.getInstance() - saveDialog = wx.FileDialog(self, "Export Skills Needed As...", - wildcard = "EVEMon skills training file (*.emp)|*.emp|" \ - "EVEMon skills training XML file (*.xml)|*.xml|" \ - "Text skills training file (*.txt)|*.txt", - style=wx.FD_SAVE | wx.FD_OVERWRITE_PROMPT) + sCharacter = Character.getInstance() + saveDialog = wx.FileDialog( + self, + "Export Skills Needed As...", + wildcard=("EVEMon skills training file (*.emp)|*.emp|" + "EVEMon skills training XML file (*.xml)|*.xml|" + "Text skills training file (*.txt)|*.txt"), + style=wx.FD_SAVE | wx.FD_OVERWRITE_PROMPT, + ) if saveDialog.ShowModal() == wx.ID_OK: saveFmtInt = saveDialog.GetFilterIndex() @@ -730,66 +790,85 @@ class MainFrame(wx.Frame): def fileImportDialog(self, event): """Handles importing single/multiple EVE XML / EFT cfg fit files""" - sFit = service.Fit.getInstance() - dlg = wx.FileDialog(self, "Open One Or More Fitting Files", - wildcard = "EVE XML fitting files (*.xml)|*.xml|" \ - "EFT text fitting files (*.cfg)|*.cfg|" \ - "All Files (*)|*", - style = wx.FD_OPEN | wx.FD_FILE_MUST_EXIST | wx.FD_MULTIPLE) - if (dlg.ShowModal() == wx.ID_OK): + sPort = Port.getInstance() + dlg = wx.FileDialog( + self, + "Open One Or More Fitting Files", + wildcard=("EVE XML fitting files (*.xml)|*.xml|" + "EFT text fitting files (*.cfg)|*.cfg|" + "All Files (*)|*"), + style=wx.FD_OPEN | wx.FD_FILE_MUST_EXIST | wx.FD_MULTIPLE + ) + if dlg.ShowModal() == wx.ID_OK: self.progressDialog = wx.ProgressDialog( - "Importing fits", - " "*100, # set some arbitrary spacing to create width in window - parent=self, style = wx.PD_APP_MODAL | wx.PD_ELAPSED_TIME) + "Importing fits", + " " * 100, # set some arbitrary spacing to create width in window + parent=self, + style=wx.PD_APP_MODAL | wx.PD_ELAPSED_TIME + ) self.progressDialog.message = None - sFit.importFitsThreaded(dlg.GetPaths(), self.fileImportCallback) + sPort.importFitsThreaded(dlg.GetPaths(), self.fileImportCallback) self.progressDialog.ShowModal() - dlg.Destroy() + try: + dlg.Destroy() + except PyDeadObjectError: + pyfalog.error("Tried to destroy an object that doesn't exist in .") def backupToXml(self, event): """ Back up all fits to EVE XML file """ - defaultFile = "pyfa-fits-%s.xml"%strftime("%Y%m%d_%H%M%S", gmtime()) + defaultFile = "pyfa-fits-%s.xml" % strftime("%Y%m%d_%H%M%S", gmtime()) - saveDialog = wx.FileDialog(self, "Save Backup As...", - wildcard = "EVE XML fitting file (*.xml)|*.xml", - style = wx.FD_SAVE | wx.FD_OVERWRITE_PROMPT, - defaultFile=defaultFile) + saveDialog = wx.FileDialog( + self, + "Save Backup As...", + wildcard="EVE XML fitting file (*.xml)|*.xml", + style=wx.FD_SAVE | wx.FD_OVERWRITE_PROMPT, + defaultFile=defaultFile, + ) if saveDialog.ShowModal() == wx.ID_OK: filePath = saveDialog.GetPath() if '.' not in os.path.basename(filePath): filePath += ".xml" - sFit = service.Fit.getInstance() - max = sFit.countAllFits() + sFit = Fit.getInstance() + max_ = sFit.countAllFits() - self.progressDialog = wx.ProgressDialog("Backup fits", - "Backing up %d fits to: %s"%(max, filePath), - maximum=max, parent=self, - style=wx.PD_APP_MODAL | wx.PD_ELAPSED_TIME) - sFit.backupFits(filePath, self.backupCallback) + self.progressDialog = wx.ProgressDialog( + "Backup fits", + "Backing up %d fits to: %s" % (max_, filePath), + maximum=max_, + parent=self, + style=wx.PD_APP_MODAL | wx.PD_ELAPSED_TIME, + ) + Port().backupFits(filePath, self.backupCallback) self.progressDialog.ShowModal() def exportHtml(self, event): from gui.utils.exportHtml import exportHtml - sFit = service.Fit.getInstance() - settings = service.settings.HTMLExportSettings.getInstance() + sFit = Fit.getInstance() + settings = HTMLExportSettings.getInstance() - max = sFit.countAllFits() + max_ = sFit.countAllFits() path = settings.getPath() if not os.path.isdir(os.path.dirname(path)): - dlg = wx.MessageDialog(self, - "Invalid Path\n\nThe following path is invalid or does not exist: \n%s\n\nPlease verify path location pyfa's preferences."%path, - "Error", wx.OK | wx.ICON_ERROR) + dlg = wx.MessageDialog( + self, + "Invalid Path\n\nThe following path is invalid or does not exist: \n%s\n\nPlease verify path location pyfa's preferences." % path, + "Error", + wx.OK | wx.ICON_ERROR + ) if dlg.ShowModal() == wx.ID_OK: return - self.progressDialog = wx.ProgressDialog("Backup fits", - "Generating HTML file at: %s"%path, - maximum=max, parent=self, - style=wx.PD_APP_MODAL | wx.PD_ELAPSED_TIME) + self.progressDialog = wx.ProgressDialog( + "Backup fits", + "Generating HTML file at: %s" % path, + maximum=max_, parent=self, + style=wx.PD_APP_MODAL | wx.PD_ELAPSED_TIME + ) exportHtml.getInstance().refreshFittingHtml(True, self.backupCallback) self.progressDialog.ShowModal() @@ -826,7 +905,7 @@ class MainFrame(wx.Frame): self._openAfterImport(data) elif action == -2: dlg = wx.MessageDialog(self, - "The following error was generated\n\n%s\n\nBe aware that already processed fits were not saved"%data, + "The following error was generated\n\n%s\n\nBe aware that already processed fits were not saved" % data, "Import Error", wx.OK | wx.ICON_ERROR) if dlg.ShowModal() == wx.ID_OK: return @@ -851,14 +930,16 @@ class MainFrame(wx.Frame): def importCharacter(self, event): """ Imports character XML file from EVE API """ - dlg = wx.FileDialog(self, "Open One Or More Character Files", - wildcard="EVE API XML character files (*.xml)|*.xml|" \ - "All Files (*)|*", - style=wx.FD_OPEN | wx.FD_FILE_MUST_EXIST | wx.FD_MULTIPLE) + dlg = wx.FileDialog( + self, + "Open One Or More Character Files", + wildcard="EVE API XML character files (*.xml)|*.xml|All Files (*)|*", + style=wx.FD_OPEN | wx.FD_FILE_MUST_EXIST | wx.FD_MULTIPLE + ) if dlg.ShowModal() == wx.ID_OK: self.waitDialog = wx.BusyInfo("Importing Character...") - sCharacter = service.Character.getInstance() + sCharacter = Character.getInstance() sCharacter.importCharacter(dlg.GetPaths(), self.importCharacterCallback) def importCharacterCallback(self): @@ -871,13 +952,13 @@ class MainFrame(wx.Frame): def openGraphFrame(self, event): if not self.graphFrame: self.graphFrame = GraphFrame(self) - if gui.graphFrame.enabled: + + if graphFrame.graphFrame_enabled: self.graphFrame.Show() - else: + elif graphFrame.graphFrame_enabled: self.graphFrame.SetFocus() def openWXInspectTool(self, event): - from wx.lib.inspection import InspectionTool if not InspectionTool().initialized: InspectionTool().Init() @@ -887,4 +968,3 @@ class MainFrame(wx.Frame): if not wnd: wnd = self InspectionTool().Show(wnd, True) - diff --git a/gui/mainMenuBar.py b/gui/mainMenuBar.py index 287866909..00402a7a3 100644 --- a/gui/mainMenuBar.py +++ b/gui/mainMenuBar.py @@ -1,4 +1,4 @@ -#=============================================================================== +# ============================================================================= # Copyright (C) 2010 Diego Duclos # # This file is part of pyfa. @@ -15,21 +15,28 @@ # # You should have received a copy of the GNU General Public License # along with pyfa. If not, see . -#=============================================================================== +# ============================================================================= +# noinspection PyPackageRequirements import wx + import config -from gui.bitmapLoader import BitmapLoader -import gui.mainFrame +from service.character import Character import gui.graphFrame import gui.globalEvents as GE -import service +from gui.bitmapLoader import BitmapLoader -if not 'wxMac' in wx.PlatformInfo or ('wxMac' in wx.PlatformInfo and wx.VERSION >= (3,0)): +from logbook import Logger +pyfalog = Logger(__name__) + +if 'wxMac' not in wx.PlatformInfo or ('wxMac' in wx.PlatformInfo and wx.VERSION >= (3, 0)): + from service.crest import Crest from service.crest import CrestModes + class MainMenuBar(wx.MenuBar): - def __init__(self): + def __init__(self, mainFrame): + pyfalog.debug("Initialize MainMenuBar") self.characterEditorId = wx.NewId() self.damagePatternEditorId = wx.NewId() self.targetResistsEditorId = wx.NewId() @@ -51,11 +58,11 @@ class MainMenuBar(wx.MenuBar): self.toggleOverridesId = wx.NewId() self.importDatabaseDefaultsId = wx.NewId() - if 'wxMac' in wx.PlatformInfo and wx.VERSION >= (3,0): + if 'wxMac' in wx.PlatformInfo and wx.VERSION >= (3, 0): wx.ID_COPY = wx.NewId() wx.ID_PASTE = wx.NewId() - self.mainFrame = gui.mainFrame.MainFrame.getInstance() + self.mainFrame = mainFrame wx.MenuBar.__init__(self) # File menu @@ -80,8 +87,8 @@ class MainMenuBar(wx.MenuBar): editMenu = wx.Menu() self.Append(editMenu, "&Edit") - #editMenu.Append(wx.ID_UNDO) - #editMenu.Append(wx.ID_REDO) + # editMenu.Append(wx.ID_UNDO) + # editMenu.Append(wx.ID_REDO) editMenu.Append(wx.ID_COPY, "To Clipboard\tCTRL+C", "Export a fit to the clipboard") editMenu.Append(wx.ID_PASTE, "From Clipboard\tCTRL+V", "Import a fit from the clipboard") @@ -114,13 +121,16 @@ class MainMenuBar(wx.MenuBar): graphFrameItem.SetBitmap(BitmapLoader.getBitmap("graphs_small", "gui")) windowMenu.AppendItem(graphFrameItem) + if not gui.graphFrame.graphFrame_enabled: + self.Enable(self.graphFrameId, False) + preferencesShortCut = "CTRL+," if 'wxMac' in wx.PlatformInfo else "CTRL+P" - preferencesItem = wx.MenuItem(windowMenu, wx.ID_PREFERENCES, "Preferences\t"+preferencesShortCut) + preferencesItem = wx.MenuItem(windowMenu, wx.ID_PREFERENCES, "Preferences\t" + preferencesShortCut) preferencesItem.SetBitmap(BitmapLoader.getBitmap("preferences_small", "gui")) windowMenu.AppendItem(preferencesItem) - if not 'wxMac' in wx.PlatformInfo or ('wxMac' in wx.PlatformInfo and wx.VERSION >= (3,0)): - self.sCrest = service.Crest.getInstance() + if 'wxMac' not in wx.PlatformInfo or ('wxMac' in wx.PlatformInfo and wx.VERSION >= (3, 0)): + self.sCrest = Crest.getInstance() # CREST Menu crestMenu = wx.Menu() @@ -136,7 +146,7 @@ class MainMenuBar(wx.MenuBar): self.Enable(self.eveFittingsId, False) self.Enable(self.exportToEveId, False) - if not gui.mainFrame.disableOverrideEditor: + if not self.mainFrame.disableOverrideEditor: windowMenu.AppendSeparator() attrItem = wx.MenuItem(windowMenu, self.attrEditorId, "Attribute Overrides\tCTRL+B") attrItem.SetBitmap(BitmapLoader.getBitmap("fit_rename_small", "gui")) @@ -154,17 +164,19 @@ class MainMenuBar(wx.MenuBar): helpMenu.Append(wx.ID_ABOUT) if config.debug: - helpMenu.Append( self.mainFrame.widgetInspectMenuID, "Open Widgets Inspect tool", "Open Widgets Inspect tool") + helpMenu.Append(self.mainFrame.widgetInspectMenuID, "Open Widgets Inspect tool", + "Open Widgets Inspect tool") self.mainFrame.Bind(GE.FIT_CHANGED, self.fitChanged) def fitChanged(self, event): + pyfalog.debug("fitChanged triggered") enable = event.fitID is not None self.Enable(wx.ID_SAVEAS, enable) self.Enable(wx.ID_COPY, enable) self.Enable(self.exportSkillsNeededId, enable) - sChar = service.Character.getInstance() + sChar = Character.getInstance() charID = self.mainFrame.charSelection.getActiveCharacter() char = sChar.getCharacter(charID) @@ -174,5 +186,3 @@ class MainMenuBar(wx.MenuBar): self.Enable(self.revertCharId, char.isDirty) event.Skip() - - diff --git a/gui/marketBrowser.py b/gui/marketBrowser.py index 6ad10597e..4047507da 100644 --- a/gui/marketBrowser.py +++ b/gui/marketBrowser.py @@ -1,456 +1,471 @@ -#=============================================================================== -# Copyright (C) 2010 Diego Duclos -# -# This file is part of pyfa. -# -# pyfa is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# pyfa is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with pyfa. If not, see . -#=============================================================================== - -import wx -import service -import gui.display as d -from gui.cachingImageList import CachingImageList -from gui.contextMenu import ContextMenu -import gui.PFSearchBox as SBox - -from gui.bitmapLoader import BitmapLoader - -ItemSelected, ITEM_SELECTED = wx.lib.newevent.NewEvent() - -RECENTLY_USED_MODULES = -2 -MAX_RECENTLY_USED_MODULES = 20 - -class MetaButton(wx.ToggleButton): - def __init__(self, *args, **kwargs): - super(MetaButton, self).__init__(*args, **kwargs) - self.setUserSelection(True) - - def setUserSelection(self, isSelected): - self.userSelected = isSelected - self.SetValue(isSelected) - - def setMetaAvailable(self, isAvailable): - self.Enable(isAvailable) - # need to also SetValue(False) for windows because Enabled=False AND SetValue(True) looks enabled. - if not isAvailable: - self.SetValue(False) - - def reset(self): - self.Enable(True) - self.SetValue(self.userSelected) - -class MarketBrowser(wx.Panel): - def __init__(self, parent): - wx.Panel.__init__(self, parent) - vbox = wx.BoxSizer(wx.VERTICAL) - self.SetSizer(vbox) - - # Add a search box on top - self.search = SearchBox(self) - vbox.Add(self.search, 0, wx.EXPAND) - - self.splitter = wx.SplitterWindow(self, style = wx.SP_LIVE_UPDATE) - vbox.Add(self.splitter, 1, wx.EXPAND) - - # Grab market service instance and create child objects - self.sMkt = service.Market.getInstance() - self.searchMode = False - self.marketView = MarketTree(self.splitter, self) - self.itemView = ItemView(self.splitter, self) - - self.splitter.SplitHorizontally(self.marketView, self.itemView) - self.splitter.SetMinimumPaneSize(250) - - # Setup our buttons for metaGroup selection - # Same fix as for search box on macs, - # need some pixels of extra space or everything clips and is ugly - p = wx.Panel(self) - box = wx.BoxSizer(wx.HORIZONTAL) - p.SetSizer(box) - vbox.Add(p, 0, wx.EXPAND) - self.metaButtons = [] - for name in self.sMkt.META_MAP.keys(): - btn = MetaButton(p, wx.ID_ANY, name.capitalize(), style=wx.BU_EXACTFIT) - setattr(self, name, btn) - box.Add(btn, 1, wx.ALIGN_CENTER) - btn.Bind(wx.EVT_TOGGLEBUTTON, self.toggleMetaButton) - btn.metaName = name - self.metaButtons.append(btn) - # Make itemview to set toggles according to list contents - self.itemView.setToggles() - - p.SetMinSize((wx.SIZE_AUTO_WIDTH, btn.GetSize()[1] + 5)) - - def toggleMetaButton(self, event): - """Process clicks on toggle buttons""" - appendMeta = wx.GetMouseState().CmdDown() - clickedBtn = event.EventObject - - if appendMeta: - activeBtns = [btn for btn in self.metaButtons if btn.GetValue()] - if activeBtns: - clickedBtn.setUserSelection(clickedBtn.GetValue()) - self.itemView.filterItemStore() - else: - # Do 'nothing' if we're trying to turn last active button off - # Keep button in the same state - clickedBtn.setUserSelection(True) - else: - for btn in self.metaButtons: - btn.setUserSelection(btn == clickedBtn) - - self.itemView.filterItemStore() - - def jump(self, item): - self.marketView.jump(item) - -class SearchBox(SBox.PFSearchBox): - def __init__(self, parent, **kwargs): - SBox.PFSearchBox.__init__(self, parent, **kwargs) - cancelBitmap = BitmapLoader.getBitmap("fit_delete_small","gui") - searchBitmap = BitmapLoader.getBitmap("fsearch_small","gui") - self.SetSearchBitmap(searchBitmap) - self.SetCancelBitmap(cancelBitmap) - self.ShowSearchButton() - self.ShowCancelButton() - -class MarketTree(wx.TreeCtrl): - def __init__(self, parent, marketBrowser): - wx.TreeCtrl.__init__(self, parent, style=wx.TR_DEFAULT_STYLE | wx.TR_HIDE_ROOT) - self.root = self.AddRoot("root") - - self.imageList = CachingImageList(16, 16) - self.SetImageList(self.imageList) - - self.sMkt = marketBrowser.sMkt - self.marketBrowser = marketBrowser - - # Form market tree root - sMkt = self.sMkt - for mktGrp in sMkt.getMarketRoot(): - iconId = self.addImage(sMkt.getIconByMarketGroup(mktGrp)) - childId = self.AppendItem(self.root, mktGrp.name, iconId, data=wx.TreeItemData(mktGrp.ID)) - # All market groups which were never expanded are dummies, here we assume - # that all root market groups are expandable - self.AppendItem(childId, "dummy") - self.SortChildren(self.root) - - # Add recently used modules node - rumIconId = self.addImage("market_small", "gui") - self.AppendItem(self.root, "Recently Used Modules", rumIconId, data = wx.TreeItemData(RECENTLY_USED_MODULES)) - - # Bind our lookup method to when the tree gets expanded - self.Bind(wx.EVT_TREE_ITEM_EXPANDING, self.expandLookup) - - def addImage(self, iconFile, location="icons"): - if iconFile is None: - return -1 - return self.imageList.GetImageIndex(iconFile, location) - - def expandLookup(self, event): - """Process market tree expands""" - root = event.Item - child = self.GetFirstChild(root)[0] - # If child of given market group is a dummy - if self.GetItemText(child) == "dummy": - # Delete it - self.Delete(child) - # And add real market group contents - sMkt = self.sMkt - currentMktGrp = sMkt.getMarketGroup(self.GetPyData(root), eager="children") - for childMktGrp in sMkt.getMarketGroupChildren(currentMktGrp): - # If market should have items but it doesn't, do not show it - if sMkt.marketGroupValidityCheck(childMktGrp) is False: - continue - iconId = self.addImage(sMkt.getIconByMarketGroup(childMktGrp)) - try: - childId = self.AppendItem(root, childMktGrp.name, iconId, data=wx.TreeItemData(childMktGrp.ID)) - except: - continue - if sMkt.marketGroupHasTypesCheck(childMktGrp) is False: - self.AppendItem(childId, "dummy") - - self.SortChildren(root) - - def jump(self, item): - """Open market group and meta tab of given item""" - self.marketBrowser.searchMode = False - sMkt = self.sMkt - mg = sMkt.getMarketGroupByItem(item) - - jumpList = [] - while mg is not None: - jumpList.append(mg.ID) - mg = mg.parent - - for id in sMkt.ROOT_MARKET_GROUPS: - if id in jumpList: - jumpList = jumpList[:jumpList.index(id)+1] - - item = self.root - for i in range(len(jumpList) -1, -1, -1): - target = jumpList[i] - child, cookie = self.GetFirstChild(item) - while self.GetItemPyData(child) != target: - child, cookie = self.GetNextChild(item, cookie) - - item = child - self.Expand(item) - - self.SelectItem(item) - self.marketBrowser.itemView.selectionMade() - -class ItemView(d.Display): - DEFAULT_COLS = ["Base Icon", - "Base Name", - "attr:power,,,True", - "attr:cpu,,,True"] - - def __init__(self, parent, marketBrowser): - d.Display.__init__(self, parent) - marketBrowser.Bind(wx.EVT_TREE_SEL_CHANGED, self.selectionMade) - - self.unfilteredStore = set() - self.filteredStore = set() - self.recentlyUsedModules = set() - self.sMkt = marketBrowser.sMkt - self.searchMode = marketBrowser.searchMode - - self.marketBrowser = marketBrowser - self.marketView = marketBrowser.marketView - - # Make sure our search actually does interesting stuff - self.marketBrowser.search.Bind(SBox.EVT_TEXT_ENTER, self.scheduleSearch) - self.marketBrowser.search.Bind(SBox.EVT_SEARCH_BTN, self.scheduleSearch) - self.marketBrowser.search.Bind(SBox.EVT_CANCEL_BTN, self.clearSearch) - self.marketBrowser.search.Bind(SBox.EVT_TEXT, self.scheduleSearch) - - # Make sure WE do interesting stuff too - self.Bind(wx.EVT_CONTEXT_MENU, self.contextMenu) - 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.metaMap = self.makeReverseMetaMap() - - # Fill up recently used modules set - for itemID in self.sMkt.serviceMarketRecentlyUsedModules["pyfaMarketRecentlyUsedModules"]: - self.recentlyUsedModules.add(self.sMkt.getItem(itemID)) - - def startDrag(self, event): - row = self.GetFirstSelected() - - if row != -1: - data = wx.PyTextDataObject() - data.SetText("market:"+str(self.active[row].ID)) - - dropSource = wx.DropSource(self) - dropSource.SetData(data) - res = dropSource.DoDragDrop() - - - def itemActivated(self, event=None): - # Check if something is selected, if so, spawn the menu for it - sel = self.GetFirstSelected() - if sel == -1: - return - - if self.mainFrame.getActiveFit(): - - self.storeRecentlyUsedMarketItem(self.active[sel].ID) - self.recentlyUsedModules = set() - for itemID in self.sMkt.serviceMarketRecentlyUsedModules["pyfaMarketRecentlyUsedModules"]: - self.recentlyUsedModules.add(self.sMkt.getItem(itemID)) - - wx.PostEvent(self.mainFrame, ItemSelected(itemID=self.active[sel].ID)) - - def storeRecentlyUsedMarketItem(self, itemID): - if len(self.sMkt.serviceMarketRecentlyUsedModules["pyfaMarketRecentlyUsedModules"]) > MAX_RECENTLY_USED_MODULES: - self.sMkt.serviceMarketRecentlyUsedModules["pyfaMarketRecentlyUsedModules"].pop(0) - - self.sMkt.serviceMarketRecentlyUsedModules["pyfaMarketRecentlyUsedModules"].append(itemID) - - def selectionMade(self, event=None): - self.marketBrowser.searchMode = False - # Grab the threeview selection and check if it's fine - sel = self.marketView.GetSelection() - if sel.IsOk(): - # Get data field of the selected item (which is a marketGroup ID if anything was selected) - seldata = self.marketView.GetPyData(sel) - if seldata is not None and seldata != RECENTLY_USED_MODULES: - # If market group treeview item doesn't have children (other market groups or dummies), - # then it should have items in it and we want to request them - if self.marketView.ItemHasChildren(sel) is False: - sMkt = self.sMkt - # Get current market group - mg = sMkt.getMarketGroup(seldata, eager=("items", "items.metaGroup")) - # Get all its items - items = sMkt.getItemsByMarketGroup(mg) - else: - items = set() - else: - # If method was called but selection wasn't actually made or we have a hit on recently used modules - if seldata == RECENTLY_USED_MODULES: - items = self.recentlyUsedModules - else: - items = set() - - # Fill store - self.updateItemStore(items) - - # Set toggle buttons / use search mode flag if recently used modules category is selected (in order to have all modules listed and not filtered) - if seldata is not RECENTLY_USED_MODULES: - self.setToggles() - else: - self.marketBrowser.searchMode = True - self.setToggles() - - # Update filtered items - self.filterItemStore() - - def updateItemStore(self, items): - self.unfilteredStore = items - - def filterItemStore(self): - sMkt = self.sMkt - selectedMetas = set() - for btn in self.marketBrowser.metaButtons: - if btn.GetValue(): - selectedMetas.update(sMkt.META_MAP[btn.metaName]) - self.filteredStore = sMkt.filterItemsByMeta(self.unfilteredStore, selectedMetas) - self.update(list(self.filteredStore)) - - def setToggles(self): - metaIDs = set() - sMkt = self.sMkt - for item in self.unfilteredStore: - metaIDs.add(sMkt.getMetaGroupIdByItem(item)) - - for btn in self.marketBrowser.metaButtons: - btn.reset() - btnMetas = sMkt.META_MAP[btn.metaName] - if len(metaIDs.intersection(btnMetas)) > 0: - btn.setMetaAvailable(True) - else: - btn.setMetaAvailable(False) - - def scheduleSearch(self, event=None): - search = self.marketBrowser.search.GetLineText(0) - # Make sure we do not count wildcard as search symbol - realsearch = search.replace("*", "") - # Re-select market group if search query has zero length - if len(realsearch) == 0: - self.selectionMade() - return - # Show nothing if query is too short - elif len(realsearch) < 3: - self.clearSearch() - return - - self.marketBrowser.searchMode = True - self.sMkt.searchItems(search, self.populateSearch) - - def clearSearch(self, event=None): - # Wipe item store and update everything to accomodate with it - # If clearSearch was generated by SearchCtrl's Cancel button, clear the content also - - if event: - self.marketBrowser.search.Clear() - - self.marketBrowser.searchMode = False - self.updateItemStore(set()) - self.setToggles() - self.filterItemStore() - - def populateSearch(self, items): - # If we're no longer searching, dump the results - if self.marketBrowser.searchMode is False: - return - self.updateItemStore(items) - self.setToggles() - self.filterItemStore() - - def itemSort(self, item): - sMkt = self.sMkt - catname = sMkt.getCategoryByItem(item).name - try: - mktgrpid = sMkt.getMarketGroupByItem(item).ID - except AttributeError: - mktgrpid = None - print "unable to find market group for", item.name - parentname = sMkt.getParentItemByItem(item).name - # Get position of market group - metagrpid = sMkt.getMetaGroupIdByItem(item) - metatab = self.metaMap.get(metagrpid) - metalvl = self.metalvls.get(item.ID, 0) - return (catname, mktgrpid, parentname, metatab, metalvl, item.name) - - def contextMenu(self, event): - # Check if something is selected, if so, spawn the menu for it - sel = self.GetFirstSelected() - if sel == -1: - return - - item = self.active[sel] - - sMkt = self.sMkt - sourceContext = "marketItemGroup" if self.marketBrowser.searchMode is False else "marketItemMisc" - itemContext = sMkt.getCategoryByItem(item).name - - menu = ContextMenu.getMenu((item,), (sourceContext, itemContext)) - self.PopupMenu(menu) - - def populate(self, items): - if len(items) > 0: - # Get dictionary with meta level attribute - sAttr = service.Attribute.getInstance() - attrs = sAttr.getAttributeInfo("metaLevel") - sMkt = self.sMkt - self.metalvls = sMkt.directAttrRequest(items, attrs) - # Clear selection - self.deselectItems() - # Perform sorting, using item's meta levels besides other stuff - items.sort(key=self.itemSort) - # Mark current item list as active - self.active = items - # Show them - d.Display.populate(self, items) - - def refresh(self, items): - if len(items) > 1: - # Get dictionary with meta level attribute - sAttr = service.Attribute.getInstance() - attrs = sAttr.getAttributeInfo("metaLevel") - sMkt = self.sMkt - self.metalvls = sMkt.directAttrRequest(items, attrs) - # Re-sort stuff - items.sort(key=self.itemSort) - - for i, item in enumerate(items[:9]): - # set shortcut info for first 9 modules - item.marketShortcut = i+1 - - d.Display.refresh(self, items) - - def makeReverseMetaMap(self): - """ - Form map which tells in which tab items of given metagroup are located - """ - revmap = {} - i = 0 - for mgids in self.sMkt.META_MAP.itervalues(): - for mgid in mgids: - revmap[mgid] = i - i += 1 - return revmap +# ============================================================================= +# Copyright (C) 2010 Diego Duclos +# +# This file is part of pyfa. +# +# pyfa is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# pyfa is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with pyfa. If not, see . +# ============================================================================= + +# noinspection PyPackageRequirements +import wx +from service.market import Market +from service.attribute import Attribute +from gui.display import Display +import gui.PFSearchBox as SBox +from gui.cachingImageList import CachingImageList +from gui.contextMenu import ContextMenu +from gui.bitmapLoader import BitmapLoader +from logbook import Logger + +pyfalog = Logger(__name__) + +ItemSelected, ITEM_SELECTED = wx.lib.newevent.NewEvent() + +RECENTLY_USED_MODULES = -2 +MAX_RECENTLY_USED_MODULES = 20 + + +class MetaButton(wx.ToggleButton): + def __init__(self, *args, **kwargs): + super(MetaButton, self).__init__(*args, **kwargs) + self.setUserSelection(True) + + def setUserSelection(self, isSelected): + self.userSelected = isSelected + self.SetValue(isSelected) + + def setMetaAvailable(self, isAvailable): + self.Enable(isAvailable) + # need to also SetValue(False) for windows because Enabled=False AND SetValue(True) looks enabled. + if not isAvailable: + self.SetValue(False) + + def reset(self): + self.Enable(True) + self.SetValue(self.userSelected) + + +class MarketBrowser(wx.Panel): + def __init__(self, parent): + wx.Panel.__init__(self, parent) + pyfalog.debug("Initialize marketBrowser") + vbox = wx.BoxSizer(wx.VERTICAL) + self.SetSizer(vbox) + + # Add a search box on top + self.search = SearchBox(self) + vbox.Add(self.search, 0, wx.EXPAND) + + self.splitter = wx.SplitterWindow(self, style=wx.SP_LIVE_UPDATE) + vbox.Add(self.splitter, 1, wx.EXPAND) + + # Grab market service instance and create child objects + self.sMkt = Market.getInstance() + self.searchMode = False + self.marketView = MarketTree(self.splitter, self) + self.itemView = ItemView(self.splitter, self) + + self.splitter.SplitHorizontally(self.marketView, self.itemView) + self.splitter.SetMinimumPaneSize(250) + + # Setup our buttons for metaGroup selection + # Same fix as for search box on macs, + # need some pixels of extra space or everything clips and is ugly + p = wx.Panel(self) + box = wx.BoxSizer(wx.HORIZONTAL) + p.SetSizer(box) + vbox.Add(p, 0, wx.EXPAND) + self.metaButtons = [] + btn = None + for name in self.sMkt.META_MAP.keys(): + btn = MetaButton(p, wx.ID_ANY, name.capitalize(), style=wx.BU_EXACTFIT) + setattr(self, name, btn) + box.Add(btn, 1, wx.ALIGN_CENTER) + btn.Bind(wx.EVT_TOGGLEBUTTON, self.toggleMetaButton) + btn.metaName = name + self.metaButtons.append(btn) + # Make itemview to set toggles according to list contents + self.itemView.setToggles() + + p.SetMinSize((wx.SIZE_AUTO_WIDTH, btn.GetSize()[1] + 5)) + + def toggleMetaButton(self, event): + """Process clicks on toggle buttons""" + appendMeta = wx.GetMouseState().CmdDown() + clickedBtn = event.EventObject + + if appendMeta: + activeBtns = [btn for btn in self.metaButtons if btn.GetValue()] + if activeBtns: + clickedBtn.setUserSelection(clickedBtn.GetValue()) + self.itemView.filterItemStore() + else: + # Do 'nothing' if we're trying to turn last active button off + # Keep button in the same state + clickedBtn.setUserSelection(True) + else: + for btn in self.metaButtons: + btn.setUserSelection(btn == clickedBtn) + + self.itemView.filterItemStore() + + def jump(self, item): + self.marketView.jump(item) + + +class SearchBox(SBox.PFSearchBox): + def __init__(self, parent, **kwargs): + SBox.PFSearchBox.__init__(self, parent, **kwargs) + cancelBitmap = BitmapLoader.getBitmap("fit_delete_small", "gui") + searchBitmap = BitmapLoader.getBitmap("fsearch_small", "gui") + self.SetSearchBitmap(searchBitmap) + self.SetCancelBitmap(cancelBitmap) + self.ShowSearchButton() + self.ShowCancelButton() + + +class MarketTree(wx.TreeCtrl): + def __init__(self, parent, marketBrowser): + wx.TreeCtrl.__init__(self, parent, style=wx.TR_DEFAULT_STYLE | wx.TR_HIDE_ROOT) + pyfalog.debug("Initialize marketTree") + self.root = self.AddRoot("root") + + self.imageList = CachingImageList(16, 16) + self.SetImageList(self.imageList) + + self.sMkt = marketBrowser.sMkt + self.marketBrowser = marketBrowser + + # Form market tree root + sMkt = self.sMkt + for mktGrp in sMkt.getMarketRoot(): + iconId = self.addImage(sMkt.getIconByMarketGroup(mktGrp)) + childId = self.AppendItem(self.root, mktGrp.name, iconId, data=wx.TreeItemData(mktGrp.ID)) + # All market groups which were never expanded are dummies, here we assume + # that all root market groups are expandable + self.AppendItem(childId, "dummy") + self.SortChildren(self.root) + + # Add recently used modules node + rumIconId = self.addImage("market_small", "gui") + self.AppendItem(self.root, "Recently Used Modules", rumIconId, data=wx.TreeItemData(RECENTLY_USED_MODULES)) + + # Bind our lookup method to when the tree gets expanded + self.Bind(wx.EVT_TREE_ITEM_EXPANDING, self.expandLookup) + + def addImage(self, iconFile, location="icons"): + if iconFile is None: + return -1 + return self.imageList.GetImageIndex(iconFile, location) + + def expandLookup(self, event): + """Process market tree expands""" + root = event.Item + child = self.GetFirstChild(root)[0] + # If child of given market group is a dummy + if self.GetItemText(child) == "dummy": + # Delete it + self.Delete(child) + # And add real market group contents + sMkt = self.sMkt + currentMktGrp = sMkt.getMarketGroup(self.GetPyData(root), eager="children") + for childMktGrp in sMkt.getMarketGroupChildren(currentMktGrp): + # If market should have items but it doesn't, do not show it + if sMkt.marketGroupValidityCheck(childMktGrp) is False: + continue + iconId = self.addImage(sMkt.getIconByMarketGroup(childMktGrp)) + try: + childId = self.AppendItem(root, childMktGrp.name, iconId, data=wx.TreeItemData(childMktGrp.ID)) + except Exception as e: + pyfalog.debug("Error appending item.") + pyfalog.debug(e) + continue + if sMkt.marketGroupHasTypesCheck(childMktGrp) is False: + self.AppendItem(childId, "dummy") + + self.SortChildren(root) + + def jump(self, item): + """Open market group and meta tab of given item""" + self.marketBrowser.searchMode = False + sMkt = self.sMkt + mg = sMkt.getMarketGroupByItem(item) + + jumpList = [] + while mg is not None: + jumpList.append(mg.ID) + mg = mg.parent + + for id in sMkt.ROOT_MARKET_GROUPS: + if id in jumpList: + jumpList = jumpList[:jumpList.index(id) + 1] + + item = self.root + for i in range(len(jumpList) - 1, -1, -1): + target = jumpList[i] + child, cookie = self.GetFirstChild(item) + while self.GetItemPyData(child) != target: + child, cookie = self.GetNextChild(item, cookie) + + item = child + self.Expand(item) + + self.SelectItem(item) + self.marketBrowser.itemView.selectionMade() + + +class ItemView(Display): + DEFAULT_COLS = ["Base Icon", + "Base Name", + "attr:power,,,True", + "attr:cpu,,,True"] + + def __init__(self, parent, marketBrowser): + Display.__init__(self, parent) + pyfalog.debug("Initialize ItemView") + marketBrowser.Bind(wx.EVT_TREE_SEL_CHANGED, self.selectionMade) + + self.unfilteredStore = set() + self.filteredStore = set() + self.recentlyUsedModules = set() + self.sMkt = marketBrowser.sMkt + self.searchMode = marketBrowser.searchMode + + self.marketBrowser = marketBrowser + self.marketView = marketBrowser.marketView + + # Make sure our search actually does interesting stuff + self.marketBrowser.search.Bind(SBox.EVT_TEXT_ENTER, self.scheduleSearch) + self.marketBrowser.search.Bind(SBox.EVT_SEARCH_BTN, self.scheduleSearch) + self.marketBrowser.search.Bind(SBox.EVT_CANCEL_BTN, self.clearSearch) + self.marketBrowser.search.Bind(SBox.EVT_TEXT, self.scheduleSearch) + + # Make sure WE do interesting stuff too + self.Bind(wx.EVT_CONTEXT_MENU, self.contextMenu) + 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.metaMap = self.makeReverseMetaMap() + + # Fill up recently used modules set + pyfalog.debug("Fill up recently used modules set") + for itemID in self.sMkt.serviceMarketRecentlyUsedModules["pyfaMarketRecentlyUsedModules"]: + self.recentlyUsedModules.add(self.sMkt.getItem(itemID)) + + def startDrag(self, event): + row = self.GetFirstSelected() + + if row != -1: + data = wx.PyTextDataObject() + data.SetText("market:" + str(self.active[row].ID)) + + dropSource = wx.DropSource(self) + dropSource.SetData(data) + dropSource.DoDragDrop() + + def itemActivated(self, event=None): + # Check if something is selected, if so, spawn the menu for it + sel = self.GetFirstSelected() + if sel == -1: + return + + if self.mainFrame.getActiveFit(): + + self.storeRecentlyUsedMarketItem(self.active[sel].ID) + self.recentlyUsedModules = set() + for itemID in self.sMkt.serviceMarketRecentlyUsedModules["pyfaMarketRecentlyUsedModules"]: + self.recentlyUsedModules.add(self.sMkt.getItem(itemID)) + + wx.PostEvent(self.mainFrame, ItemSelected(itemID=self.active[sel].ID)) + + def storeRecentlyUsedMarketItem(self, itemID): + if len(self.sMkt.serviceMarketRecentlyUsedModules["pyfaMarketRecentlyUsedModules"]) > MAX_RECENTLY_USED_MODULES: + self.sMkt.serviceMarketRecentlyUsedModules["pyfaMarketRecentlyUsedModules"].pop(0) + + self.sMkt.serviceMarketRecentlyUsedModules["pyfaMarketRecentlyUsedModules"].append(itemID) + + def selectionMade(self, event=None): + self.marketBrowser.searchMode = False + # Grab the threeview selection and check if it's fine + sel = self.marketView.GetSelection() + if sel.IsOk(): + # Get data field of the selected item (which is a marketGroup ID if anything was selected) + seldata = self.marketView.GetPyData(sel) + if seldata is not None and seldata != RECENTLY_USED_MODULES: + # If market group treeview item doesn't have children (other market groups or dummies), + # then it should have items in it and we want to request them + if self.marketView.ItemHasChildren(sel) is False: + sMkt = self.sMkt + # Get current market group + mg = sMkt.getMarketGroup(seldata, eager=("items", "items.metaGroup")) + # Get all its items + items = sMkt.getItemsByMarketGroup(mg) + else: + items = set() + else: + # If method was called but selection wasn't actually made or we have a hit on recently used modules + if seldata == RECENTLY_USED_MODULES: + items = self.recentlyUsedModules + else: + items = set() + + # Fill store + self.updateItemStore(items) + + # Set toggle buttons / use search mode flag if recently used modules category is selected (in order to have all modules listed and not filtered) + if seldata is not RECENTLY_USED_MODULES: + self.setToggles() + else: + self.marketBrowser.searchMode = True + self.setToggles() + + # Update filtered items + self.filterItemStore() + + def updateItemStore(self, items): + self.unfilteredStore = items + + def filterItemStore(self): + sMkt = self.sMkt + selectedMetas = set() + for btn in self.marketBrowser.metaButtons: + if btn.GetValue(): + selectedMetas.update(sMkt.META_MAP[btn.metaName]) + self.filteredStore = sMkt.filterItemsByMeta(self.unfilteredStore, selectedMetas) + self.update(list(self.filteredStore)) + + def setToggles(self): + metaIDs = set() + sMkt = self.sMkt + for item in self.unfilteredStore: + metaIDs.add(sMkt.getMetaGroupIdByItem(item)) + + for btn in self.marketBrowser.metaButtons: + btn.reset() + btnMetas = sMkt.META_MAP[btn.metaName] + if len(metaIDs.intersection(btnMetas)) > 0: + btn.setMetaAvailable(True) + else: + btn.setMetaAvailable(False) + + def scheduleSearch(self, event=None): + search = self.marketBrowser.search.GetLineText(0) + # Make sure we do not count wildcard as search symbol + realsearch = search.replace("*", "") + # Re-select market group if search query has zero length + if len(realsearch) == 0: + self.selectionMade() + return + # Show nothing if query is too short + elif len(realsearch) < 3: + self.clearSearch() + return + + self.marketBrowser.searchMode = True + self.sMkt.searchItems(search, self.populateSearch) + + def clearSearch(self, event=None): + # Wipe item store and update everything to accomodate with it + # If clearSearch was generated by SearchCtrl's Cancel button, clear the content also + + if event: + self.marketBrowser.search.Clear() + + self.marketBrowser.searchMode = False + self.updateItemStore(set()) + self.setToggles() + self.filterItemStore() + + def populateSearch(self, items): + # If we're no longer searching, dump the results + if self.marketBrowser.searchMode is False: + return + self.updateItemStore(items) + self.setToggles() + self.filterItemStore() + + def itemSort(self, item): + sMkt = self.sMkt + catname = sMkt.getCategoryByItem(item).name + try: + mktgrpid = sMkt.getMarketGroupByItem(item).ID + except AttributeError: + mktgrpid = None + print("unable to find market group for", item.name) + parentname = sMkt.getParentItemByItem(item).name + # Get position of market group + metagrpid = sMkt.getMetaGroupIdByItem(item) + metatab = self.metaMap.get(metagrpid) + metalvl = self.metalvls.get(item.ID, 0) + return catname, mktgrpid, parentname, metatab, metalvl, item.name + + def contextMenu(self, event): + # Check if something is selected, if so, spawn the menu for it + sel = self.GetFirstSelected() + if sel == -1: + return + + item = self.active[sel] + + sMkt = self.sMkt + sourceContext = "marketItemGroup" if self.marketBrowser.searchMode is False else "marketItemMisc" + itemContext = sMkt.getCategoryByItem(item).name + + menu = ContextMenu.getMenu((item,), (sourceContext, itemContext)) + self.PopupMenu(menu) + + def populate(self, items): + if len(items) > 0: + # Get dictionary with meta level attribute + sAttr = Attribute.getInstance() + attrs = sAttr.getAttributeInfo("metaLevel") + sMkt = self.sMkt + self.metalvls = sMkt.directAttrRequest(items, attrs) + # Clear selection + self.deselectItems() + # Perform sorting, using item's meta levels besides other stuff + items.sort(key=self.itemSort) + # Mark current item list as active + self.active = items + # Show them + Display.populate(self, items) + + def refresh(self, items): + if len(items) > 1: + # Get dictionary with meta level attribute + sAttr = Attribute.getInstance() + attrs = sAttr.getAttributeInfo("metaLevel") + sMkt = self.sMkt + self.metalvls = sMkt.directAttrRequest(items, attrs) + # Re-sort stuff + items.sort(key=self.itemSort) + + for i, item in enumerate(items[:9]): + # set shortcut info for first 9 modules + item.marketShortcut = i + 1 + + Display.refresh(self, items) + + def makeReverseMetaMap(self): + """ + Form map which tells in which tab items of given metagroup are located + """ + revmap = {} + i = 0 + for mgids in self.sMkt.META_MAP.itervalues(): + for mgid in mgids: + revmap[mgid] = i + i += 1 + return revmap diff --git a/gui/multiSwitch.py b/gui/multiSwitch.py index 91532785d..2efe80bc0 100644 --- a/gui/multiSwitch.py +++ b/gui/multiSwitch.py @@ -1,4 +1,4 @@ -#=============================================================================== +# ============================================================================= # Copyright (C) 2010 Diego Duclos # # This file is part of pyfa. @@ -15,16 +15,16 @@ # # You should have received a copy of the GNU General Public License # along with pyfa. If not, see . -#=============================================================================== +# ============================================================================= -import gui.chromeTabs +from gui.chromeTabs import PFNotebook import gui.builtinViews.emptyView -class MultiSwitch(gui.chromeTabs.PFNotebook): +class MultiSwitch(PFNotebook): def __init__(self, parent): - gui.chromeTabs.PFNotebook.__init__(self, parent) - #self.AddPage() # now handled by mainFrame + PFNotebook.__init__(self, parent) + # self.AddPage() # now handled by mainFrame self.handlers = handlers = [] for type in TabSpawner.tabTypes: handlers.append(type(self)) @@ -40,10 +40,10 @@ class MultiSwitch(gui.chromeTabs.PFNotebook): tabWnd = gui.builtinViews.emptyView.BlankPage(self) tabWnd.handleDrag = lambda type, info: self.handleDrag(type, info) - gui.chromeTabs.PFNotebook.AddPage(self, tabWnd, tabTitle, tabImage, True) + PFNotebook.AddPage(self, tabWnd, tabTitle, tabImage, True) def DeletePage(self, n, *args, **kwargs): - gui.chromeTabs.PFNotebook.DeletePage(self, n, *args, **kwargs) + PFNotebook.DeletePage(self, n, *args, **kwargs) if self.GetPageCount() == 0: self.AddPage() diff --git a/gui/notesView.py b/gui/notesView.py index efa933bb9..d4f8703cc 100644 --- a/gui/notesView.py +++ b/gui/notesView.py @@ -1,9 +1,11 @@ +# noinspection PyPackageRequirements import wx -import service +from service.fit import Fit import gui.globalEvents as GE import gui.mainFrame + class NotesView(wx.Panel): def __init__(self, parent): wx.Panel.__init__(self, parent) @@ -19,7 +21,7 @@ class NotesView(wx.Panel): self.Bind(wx.EVT_TIMER, self.delayedSave, self.saveTimer) def fitChanged(self, event): - sFit = service.Fit.getInstance() + sFit = Fit.getInstance() fit = sFit.getFit(event.fitID) self.Parent.Parent.DisablePage(self, not fit or fit.isStructure) @@ -34,11 +36,11 @@ class NotesView(wx.Panel): def onText(self, event): # delay the save so we're not writing to sqlite on every keystroke - self.saveTimer.Stop() # cancel the existing timer + self.saveTimer.Stop() # cancel the existing timer self.saveTimer.Start(1000, True) def delayedSave(self, event): - sFit = service.Fit.getInstance() + sFit = Fit.getInstance() fit = sFit.getFit(self.lastFitId) newNotes = self.editNotes.GetValue() fit.notes = newNotes diff --git a/gui/patternEditor.py b/gui/patternEditor.py index 171eed7a5..879683373 100644 --- a/gui/patternEditor.py +++ b/gui/patternEditor.py @@ -1,4 +1,4 @@ -#=============================================================================== +# ============================================================================= # Copyright (C) 2010 Diego Duclos # # This file is part of pyfa. @@ -15,18 +15,20 @@ # # You should have received a copy of the GNU General Public License # along with pyfa. If not, see . -#=============================================================================== +# ============================================================================= +# noinspection PyPackageRequirements import wx from gui.bitmapLoader import BitmapLoader -import service +# noinspection PyPackageRequirements from wx.lib.intctrl import IntCtrl from gui.utils.clipboard import toClipboard, fromClipboard -from service.damagePattern import ImportError from gui.builtinViews.entityEditor import EntityEditor, BaseValidator -########################################################################### -## Class DmgPatternEditorDlg -########################################################################### +from service.damagePattern import DamagePattern, ImportError +from logbook import Logger + +pyfalog = Logger(__name__) + class DmgPatternTextValidor(BaseValidator): def __init__(self): @@ -47,7 +49,8 @@ class DmgPatternTextValidor(BaseValidator): raise ValueError("Damage Profile name already in use, please choose another.") return True - except ValueError, e: + except ValueError as e: + pyfalog.error(e) wx.MessageBox(u"{}".format(e), "Error") textCtrl.SetFocus() return False @@ -59,33 +62,34 @@ class DmgPatternEntityEditor(EntityEditor): self.SetEditorValidator(DmgPatternTextValidor) def getEntitiesFromContext(self): - sDP = service.DamagePattern.getInstance() + sDP = DamagePattern.getInstance() choices = sorted(sDP.getDamagePatternList(), key=lambda p: p.name) return [c for c in choices if c.name != "Selected Ammo"] def DoNew(self, name): - sDP = service.DamagePattern.getInstance() + sDP = DamagePattern.getInstance() return sDP.newPattern(name) def DoRename(self, entity, name): - sDP = service.DamagePattern.getInstance() + sDP = DamagePattern.getInstance() sDP.renamePattern(entity, name) def DoCopy(self, entity, name): - sDP = service.DamagePattern.getInstance() + sDP = DamagePattern.getInstance() copy = sDP.copyPattern(entity) sDP.renamePattern(copy, name) return copy def DoDelete(self, entity): - sDP = service.DamagePattern.getInstance() + sDP = DamagePattern.getInstance() sDP.deletePattern(entity) + class DmgPatternEditorDlg(wx.Dialog): DAMAGE_TYPES = ("em", "thermal", "kinetic", "explosive") def __init__(self, parent): - wx.Dialog.__init__(self, parent, id = wx.ID_ANY, title = u"Damage Pattern Editor", size = wx.Size( 400,240 )) + wx.Dialog.__init__(self, parent, id=wx.ID_ANY, title=u"Damage Pattern Editor", size=wx.Size(400, 240)) self.block = False self.SetSizeHintsSz(wx.DefaultSize, wx.DefaultSize) @@ -111,11 +115,11 @@ class DmgPatternEditorDlg(wx.Dialog): dmgeditSizer.SetNonFlexibleGrowMode(wx.FLEX_GROWMODE_SPECIFIED) width = -1 - defSize = wx.Size(width,-1) + defSize = wx.Size(width, -1) - for i, type in enumerate(self.DAMAGE_TYPES): - bmp = wx.StaticBitmap(self, wx.ID_ANY, BitmapLoader.getBitmap("%s_big"%type, "gui")) - if i%2: + for i, type_ in enumerate(self.DAMAGE_TYPES): + bmp = wx.StaticBitmap(self, wx.ID_ANY, BitmapLoader.getBitmap("%s_big" % type_, "gui")) + if i % 2: style = wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_RIGHT | wx.LEFT border = 20 else: @@ -123,13 +127,13 @@ class DmgPatternEditorDlg(wx.Dialog): border = 5 # set text edit - setattr(self, "%sEdit"%type, IntCtrl(self, wx.ID_ANY, 0, wx.DefaultPosition, defSize)) - setattr(self, "%sPerc"%type, wx.StaticText(self, wx.ID_ANY, u"0%")) - editObj = getattr(self, "%sEdit"%type) + setattr(self, "%sEdit" % type_, IntCtrl(self, wx.ID_ANY, 0, wx.DefaultPosition, defSize)) + setattr(self, "%sPerc" % type_, wx.StaticText(self, wx.ID_ANY, u"0%")) + editObj = getattr(self, "%sEdit" % type_) dmgeditSizer.Add(bmp, 0, style, border) dmgeditSizer.Add(editObj, 0, wx.BOTTOM | wx.TOP | wx.ALIGN_CENTER_VERTICAL, 5) - dmgeditSizer.Add(getattr(self, "%sPerc"%type), 0, wx.LEFT | wx.ALIGN_CENTER_VERTICAL, 5) + dmgeditSizer.Add(getattr(self, "%sPerc" % type_), 0, wx.LEFT | wx.ALIGN_CENTER_VERTICAL, 5) editObj.Bind(wx.EVT_TEXT, self.ValuesUpdated) editObj.SetLimited(True) @@ -147,7 +151,7 @@ class DmgPatternEditorDlg(wx.Dialog): self.stNotice.Wrap(-1) perSizer.Add(self.stNotice, 0, wx.BOTTOM | wx.TOP | wx.LEFT, 5) - footerSizer.Add(perSizer, 1, wx.ALIGN_CENTER_VERTICAL, 5) + footerSizer.Add(perSizer, 1, wx.ALIGN_CENTER_VERTICAL, 5) self.totSizer = wx.BoxSizer(wx.VERTICAL) @@ -156,8 +160,8 @@ class DmgPatternEditorDlg(wx.Dialog): mainSizer.Add(contentSizer, 1, wx.EXPAND, 0) if "wxGTK" in wx.PlatformInfo: - self.closeBtn = wx.Button( self, wx.ID_ANY, u"Close", wx.DefaultPosition, wx.DefaultSize, 0 ) - mainSizer.Add( self.closeBtn, 0, wx.ALL|wx.ALIGN_RIGHT, 5 ) + self.closeBtn = wx.Button(self, wx.ID_ANY, u"Close", wx.DefaultPosition, wx.DefaultSize, 0) + mainSizer.Add(self.closeBtn, 0, wx.ALL | wx.ALIGN_RIGHT, 5) self.closeBtn.Bind(wx.EVT_BUTTON, self.closeEvent) self.SetSizer(mainSizer) @@ -166,18 +170,18 @@ class DmgPatternEditorDlg(wx.Dialog): ("Export", wx.ART_FILE_SAVE_AS, "to")) for name, art, direction in importExport: - bitmap = wx.ArtProvider.GetBitmap(art, wx.ART_BUTTON) - btn = wx.BitmapButton(self, wx.ID_ANY, bitmap) + bitmap = wx.ArtProvider.GetBitmap(art, wx.ART_BUTTON) + btn = wx.BitmapButton(self, wx.ID_ANY, bitmap) - btn.SetMinSize( btn.GetSize() ) - btn.SetMaxSize( btn.GetSize() ) + btn.SetMinSize(btn.GetSize()) + btn.SetMaxSize(btn.GetSize()) - btn.Layout() - setattr(self, name, btn) - btn.Enable(True) - btn.SetToolTipString("%s patterns %s clipboard" % (name, direction) ) - footerSizer.Add(btn, 0, wx.ALIGN_CENTER_HORIZONTAL | wx.ALIGN_RIGHT) - btn.Bind(wx.EVT_BUTTON, getattr(self, "{}Patterns".format(name.lower()))) + btn.Layout() + setattr(self, name, btn) + btn.Enable(True) + btn.SetToolTipString("%s patterns %s clipboard" % (name, direction)) + footerSizer.Add(btn, 0, wx.ALIGN_CENTER_HORIZONTAL | wx.ALIGN_RIGHT) + btn.Bind(wx.EVT_BUTTON, getattr(self, "{}Patterns".format(name.lower()))) self.Layout() bsize = self.GetBestSize() @@ -196,30 +200,30 @@ class DmgPatternEditorDlg(wx.Dialog): return p = self.entityEditor.getActiveEntity() - total = sum(map(lambda attr: getattr(self, "%sEdit"%attr).GetValue(), self.DAMAGE_TYPES)) - for type in self.DAMAGE_TYPES: - editObj = getattr(self, "%sEdit"%type) - percObj = getattr(self, "%sPerc"%type) - setattr(p, "%sAmount"%type, editObj.GetValue()) - percObj.SetLabel("%.1f%%"%(float(editObj.GetValue())*100/total if total > 0 else 0)) + total = sum(map(lambda attr: getattr(self, "%sEdit" % attr).GetValue(), self.DAMAGE_TYPES)) + for type_ in self.DAMAGE_TYPES: + editObj = getattr(self, "%sEdit" % type_) + percObj = getattr(self, "%sPerc" % type_) + setattr(p, "%sAmount" % type_, editObj.GetValue()) + percObj.SetLabel("%.1f%%" % (float(editObj.GetValue()) * 100 / total if total > 0 else 0)) self.totSizer.Layout() if event is not None: event.Skip() - service.DamagePattern.getInstance().saveChanges(p) + DamagePattern.getInstance().saveChanges(p) def restrict(self): - for type in self.DAMAGE_TYPES: - editObj = getattr(self, "%sEdit"%type) + for type_ in self.DAMAGE_TYPES: + editObj = getattr(self, "%sEdit" % type_) editObj.Enable(False) self.entityEditor.btnRename.Enable(False) self.entityEditor.btnDelete.Enable(False) def unrestrict(self): - for type in self.DAMAGE_TYPES: - editObj = getattr(self, "%sEdit"%type) + for type_ in self.DAMAGE_TYPES: + editObj = getattr(self, "%sEdit" % type_) editObj.Enable() self.entityEditor.btnRename.Enable() self.entityEditor.btnDelete.Enable() @@ -251,20 +255,24 @@ class DmgPatternEditorDlg(wx.Dialog): def importPatterns(self, event): text = fromClipboard() if text: - sDP = service.DamagePattern.getInstance() + sDP = DamagePattern.getInstance() try: sDP.importPatterns(text) self.stNotice.SetLabel("Patterns successfully imported from clipboard") - except service.damagePattern.ImportError, e: + except ImportError as e: + pyfalog.error(e) self.stNotice.SetLabel(str(e)) - except Exception, e: - self.stNotice.SetLabel("Could not import from clipboard: unknown errors") + except Exception as e: + msg = "Could not import from clipboard: unknown errors" + pyfalog.warning(msg) + pyfalog.error(e) + self.stNotice.SetLabel(msg) finally: self.entityEditor.refreshEntityList() else: self.stNotice.SetLabel("Could not import from clipboard") def exportPatterns(self, event): - sDP = service.DamagePattern.getInstance() - toClipboard( sDP.exportPatterns() ) + sDP = DamagePattern.getInstance() + toClipboard(sDP.exportPatterns()) self.stNotice.SetLabel("Patterns exported to clipboard") diff --git a/gui/preferenceDialog.py b/gui/preferenceDialog.py index ada936114..e6c5f12af 100644 --- a/gui/preferenceDialog.py +++ b/gui/preferenceDialog.py @@ -1,80 +1,81 @@ -#=============================================================================== -# Copyright (C) 2010 Diego Duclos -# -# This file is part of pyfa. -# -# pyfa is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# pyfa is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with pyfa. If not, see . -#=============================================================================== - -import wx -from gui.preferenceView import PreferenceView -from gui.bitmapLoader import BitmapLoader - -class PreferenceDialog(wx.Dialog): - - def __init__(self, parent): - wx.Dialog.__init__(self, parent, id=wx.ID_ANY, size=wx.DefaultSize, style=wx.DEFAULT_DIALOG_STYLE) - self.SetTitle("pyfa - Preferences") - i = wx.IconFromBitmap(BitmapLoader.getBitmap("preferences_small", "gui")) - self.SetIcon(i) - mainSizer = wx.BoxSizer(wx.VERTICAL) - - self.listbook = wx.Listbook(self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LB_DEFAULT) - - self.listview = self.listbook.GetListView() - #self.listview.SetMinSize((500, -1)) - #self.listview.SetSize((500, -1)) - - self.imageList = wx.ImageList(32,32) - self.listbook.SetImageList(self.imageList) - - mainSizer.Add(self.listbook, 1, wx.EXPAND | wx.TOP|wx.BOTTOM|wx.LEFT, 5) - - self.m_staticline2 = wx.StaticLine( self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LI_HORIZONTAL ) - mainSizer.Add( self.m_staticline2, 0, wx.EXPAND, 5 ) - - btnSizer = wx.BoxSizer( wx.HORIZONTAL ) - btnSizer.AddSpacer( ( 0, 0), 1, wx.EXPAND, 5 ) - self.btnOK = wx.Button( self, wx.ID_ANY, u"OK", wx.DefaultPosition, wx.DefaultSize, 0 ) - btnSizer.Add( self.btnOK, 0, wx.ALL, 5 ) - mainSizer.Add(btnSizer,0 , wx.EXPAND, 5) - self.SetSizer(mainSizer) - - self.Centre(wx.BOTH) - - for prefView in PreferenceView.views: - page = wx.Panel(self.listbook) - bmp = prefView.getImage() - if bmp: - imgID = self.imageList.Add(bmp) - else: - imgID = -1 - prefView.populatePanel(page) - self.listbook.AddPage(page, prefView.title, imageId = imgID) - - # Set the height based on a condition. Can all the panels fit in the current height? - # If not, use the .GetBestVirtualSize() to ensure that all content is available. - minHeight = 360 - bestFit = self.GetBestVirtualSize() - if minHeight > bestFit[1]: - self.SetSizeWH(450, minHeight) - else: - self.SetSizeWH(450, bestFit[1]) - - self.Layout() - - self.btnOK.Bind(wx.EVT_BUTTON, self.OnBtnOK) - - def OnBtnOK(self, event): - self.Close() +# ============================================================================= +# Copyright (C) 2010 Diego Duclos +# +# This file is part of pyfa. +# +# pyfa is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# pyfa is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with pyfa. If not, see . +# ============================================================================= + +# noinspection PyPackageRequirements +import wx +from gui.preferenceView import PreferenceView +from gui.bitmapLoader import BitmapLoader + + +class PreferenceDialog(wx.Dialog): + def __init__(self, parent): + wx.Dialog.__init__(self, parent, id=wx.ID_ANY, size=wx.DefaultSize, style=wx.DEFAULT_DIALOG_STYLE) + self.SetTitle("pyfa - Preferences") + i = wx.IconFromBitmap(BitmapLoader.getBitmap("preferences_small", "gui")) + self.SetIcon(i) + mainSizer = wx.BoxSizer(wx.VERTICAL) + + self.listbook = wx.Listbook(self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LB_DEFAULT) + + self.listview = self.listbook.GetListView() + # self.listview.SetMinSize((500, -1)) + # self.listview.SetSize((500, -1)) + + self.imageList = wx.ImageList(32, 32) + self.listbook.SetImageList(self.imageList) + + mainSizer.Add(self.listbook, 1, wx.EXPAND | wx.TOP | wx.BOTTOM | wx.LEFT, 5) + + self.m_staticline2 = wx.StaticLine(self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LI_HORIZONTAL) + mainSizer.Add(self.m_staticline2, 0, wx.EXPAND, 5) + + btnSizer = wx.BoxSizer(wx.HORIZONTAL) + btnSizer.AddSpacer((0, 0), 1, wx.EXPAND, 5) + self.btnOK = wx.Button(self, wx.ID_ANY, u"OK", wx.DefaultPosition, wx.DefaultSize, 0) + btnSizer.Add(self.btnOK, 0, wx.ALL, 5) + mainSizer.Add(btnSizer, 0, wx.EXPAND, 5) + self.SetSizer(mainSizer) + + self.Centre(wx.BOTH) + + for prefView in PreferenceView.views: + page = wx.Panel(self.listbook) + bmp = prefView.getImage() + if bmp: + imgID = self.imageList.Add(bmp) + else: + imgID = -1 + prefView.populatePanel(page) + self.listbook.AddPage(page, prefView.title, imageId=imgID) + + # Set the height based on a condition. Can all the panels fit in the current height? + # If not, use the .GetBestVirtualSize() to ensure that all content is available. + minHeight = 360 + bestFit = self.GetBestVirtualSize() + if minHeight > bestFit[1]: + self.SetSizeWH(450, minHeight) + else: + self.SetSizeWH(450, bestFit[1]) + + self.Layout() + + self.btnOK.Bind(wx.EVT_BUTTON, self.OnBtnOK) + + def OnBtnOK(self, event): + self.Close() diff --git a/gui/preferenceView.py b/gui/preferenceView.py index bf1ba76ed..662a2ff20 100644 --- a/gui/preferenceView.py +++ b/gui/preferenceView.py @@ -1,41 +1,48 @@ -#=============================================================================== -# Copyright (C) 2010 Diego Duclos -# -# This file is part of pyfa. -# -# pyfa is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# pyfa is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with pyfa. If not, see . -#=============================================================================== - -import wx - -class PreferenceView(object): - views = [] - def __init__(self): - pass - - @classmethod - def register(cls): - PreferenceView.views.append(cls()) - - def populatePanel(self, panel): - raise NotImplementedError() - - def refreshPanel(self, fit): - raise NotImplementedError() - - def getImage(self): - return wx.NullBitmap - -from gui.builtinPreferenceViews import * - +# ============================================================================= +# Copyright (C) 2010 Diego Duclos +# +# This file is part of pyfa. +# +# pyfa is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# pyfa is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with pyfa. If not, see . +# ============================================================================= + +# noinspection PyPackageRequirements +import wx + + +class PreferenceView(object): + views = [] + + @classmethod + def register(cls): + PreferenceView.views.append(cls()) + + def populatePanel(self, panel): + raise NotImplementedError() + + def refreshPanel(self, fit): + raise NotImplementedError() + + def getImage(self): + return wx.NullBitmap + + +# noinspection PyUnresolvedReferences +from gui.builtinPreferenceViews import ( # noqa: E402, F401 + pyfaGeneralPreferences, + pyfaNetworkPreferences, + pyfaHTMLExportPreferences, + pyfaCrestPreferences, + pyfaUpdatePreferences +) diff --git a/gui/projectedView.py b/gui/projectedView.py index f805ead1c..091c4a94d 100644 --- a/gui/projectedView.py +++ b/gui/projectedView.py @@ -1,4 +1,4 @@ -#=============================================================================== +# ============================================================================= # Copyright (C) 2010 Diego Duclos # # This file is part of pyfa. @@ -15,40 +15,47 @@ # # You should have received a copy of the GNU General Public License # along with pyfa. If not, see . -#=============================================================================== +# ============================================================================= +# noinspection PyPackageRequirements import wx import gui.display as d import gui.globalEvents as GE -import service import gui.droneView from gui.builtinViewColumns.state import State from gui.contextMenu import ContextMenu -import eos.types +from service.fit import Fit +from service.market import Market +from eos.saveddata.drone import Drone as es_Drone +from eos.saveddata.fighter import Fighter as es_Fighter +from eos.saveddata.module import Module as es_Module -class DummyItem: +class DummyItem(object): def __init__(self, txt): self.name = txt self.icon = None -class DummyEntry: + +class DummyEntry(object): def __init__(self, txt): self.item = DummyItem(txt) -class ProjectedViewDrop(wx.PyDropTarget): - def __init__(self, dropFn): - wx.PyDropTarget.__init__(self) - self.dropFn = dropFn - # this is really transferring an EVE itemID - self.dropData = wx.PyTextDataObject() - self.SetDataObject(self.dropData) - def OnData(self, x, y, t): - if self.GetData(): - data = self.dropData.GetText().split(':') - self.dropFn(x, y, data) - return t +class ProjectedViewDrop(wx.PyDropTarget): + def __init__(self, dropFn, *args, **kwargs): + super(ProjectedViewDrop, self).__init__(*args, **kwargs) + self.dropFn = dropFn + # this is really transferring an EVE itemID + self.dropData = wx.PyTextDataObject() + self.SetDataObject(self.dropData) + + def OnData(self, x, y, t): + if self.GetData(): + data = self.dropData.GetText().split(':') + self.dropFn(x, y, data) + return t + class ProjectedView(d.Display): DEFAULT_COLS = ["State", @@ -58,7 +65,7 @@ class ProjectedView(d.Display): "Ammo"] def __init__(self, parent): - d.Display.__init__(self, parent, style = wx.LC_SINGLE_SEL | wx.BORDER_NONE) + d.Display.__init__(self, parent, style=wx.LC_SINGLE_SEL | wx.BORDER_NONE) self.lastFitId = None @@ -70,7 +77,7 @@ class ProjectedView(d.Display): self.droneView = gui.droneView.DroneView - if "__WXGTK__" in wx.PlatformInfo: + if "__WXGTK__" in wx.PlatformInfo: self.Bind(wx.EVT_RIGHT_UP, self.scheduleMenu) else: self.Bind(wx.EVT_RIGHT_DOWN, self.scheduleMenu) @@ -79,72 +86,73 @@ class ProjectedView(d.Display): self.SetDropTarget(ProjectedViewDrop(self.handleListDrag)) def handleListDrag(self, x, y, data): - ''' + """ Handles dragging of items from various pyfa displays which support it data is list with two indices: data[0] is hard-coded str of originating source data[1] is typeID or index of data we want to manipulate - ''' + """ if data[0] == "projected": # if source is coming from projected, we are trying to combine drones. self.mergeDrones(x, y, int(data[1])) elif data[0] == "market": - sFit = service.Fit.getInstance() + sFit = Fit.getInstance() fitID = self.mainFrame.getActiveFit() sFit.project(fitID, int(data[1])) wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.mainFrame.getActiveFit())) - def kbEvent(self,event): + def kbEvent(self, event): keycode = event.GetKeyCode() if keycode == wx.WXK_DELETE or keycode == wx.WXK_NUMPAD_DELETE: fitID = self.mainFrame.getActiveFit() - sFit = service.Fit.getInstance() + sFit = Fit.getInstance() row = self.GetFirstSelected() if row != -1: sFit.removeProjected(fitID, self.get(row)) wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=fitID)) def handleDrag(self, type, fitID): - #Those are drags coming from pyfa sources, NOT builtin wx drags + # Those are drags coming from pyfa sources, NOT builtin wx drags if type == "fit": activeFit = self.mainFrame.getActiveFit() if activeFit: - sFit = service.Fit.getInstance() + sFit = Fit.getInstance() draggedFit = sFit.getFit(fitID) sFit.project(activeFit, draggedFit) wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=activeFit)) def startDrag(self, event): row = event.GetIndex() - if row != -1 and isinstance(self.get(row), eos.types.Drone): + if row != -1 and isinstance(self.get(row), es_Drone): data = wx.PyTextDataObject() - data.SetText("projected:"+str(self.GetItemData(row))) + data.SetText("projected:" + str(self.GetItemData(row))) dropSource = wx.DropSource(self) dropSource.SetData(data) dropSource.DoDragDrop() def mergeDrones(self, x, y, itemID): - srcRow = self.FindItemData(-1,itemID) + srcRow = self.FindItemData(-1, itemID) dstRow, _ = self.HitTest((x, y)) if srcRow != -1 and dstRow != -1: self._merge(srcRow, dstRow) def _merge(self, src, dst): dstDrone = self.get(dst) - if isinstance(dstDrone, eos.types.Drone): - sFit = service.Fit.getInstance() + if isinstance(dstDrone, es_Drone): + sFit = Fit.getInstance() fitID = self.mainFrame.getActiveFit() if sFit.mergeDrones(fitID, self.get(src), dstDrone, True): wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=fitID)) - - def moduleSort(self, module): + @staticmethod + def moduleSort(module): return module.item.name - def fighterSort(self, fighter): + @staticmethod + def fighterSort(fighter): return fighter.item.name def droneSort(self, drone): @@ -155,16 +163,17 @@ class ProjectedView(d.Display): return (self.droneView.DRONE_ORDER.index(item.marketGroup.name), drone.item.name) - def fitSort(self, fit): + @staticmethod + def fitSort(fit): return fit.name def fitChanged(self, event): - sFit = service.Fit.getInstance() + sFit = Fit.getInstance() fit = sFit.getFit(event.fitID) self.Parent.Parent.DisablePage(self, not fit or fit.isStructure) - #Clear list and get out if current fitId is None + # Clear list and get out if current fitId is None if event.fitID is None and self.lastFitId is not None: self.DeleteAllItems() self.lastFitId = None @@ -198,7 +207,7 @@ class ProjectedView(d.Display): self.deselectItems() - if stuff == []: + if not stuff: stuff = [DummyEntry("Drag an item or fit, or use right-click menu for system effects")] self.update(stuff) @@ -231,7 +240,7 @@ class ProjectedView(d.Display): col = self.getColumn(event.Position) if col == self.getColIndex(State): fitID = self.mainFrame.getActiveFit() - sFit = service.Fit.getInstance() + sFit = Fit.getInstance() sFit.toggleProjected(fitID, item, "right" if event.Button == 3 else "left") wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=fitID)) @@ -245,17 +254,18 @@ class ProjectedView(d.Display): menu = None if sel != -1: item = self.get(sel) - if item is None: return - sMkt = service.Market.getInstance() - if isinstance(item, eos.types.Drone): + if item is None: + return + sMkt = Market.getInstance() + if isinstance(item, es_Drone): srcContext = "projectedDrone" itemContext = sMkt.getCategoryByItem(item.item).name context = ((srcContext, itemContext),) - elif isinstance(item, eos.types.Fighter): + elif isinstance(item, es_Fighter): srcContext = "projectedFighter" itemContext = sMkt.getCategoryByItem(item.item).name context = ((srcContext, itemContext),) - elif isinstance(item, eos.types.Module): + elif isinstance(item, es_Module): modSrcContext = "projectedModule" modItemContext = sMkt.getCategoryByItem(item.item).name modFullContext = (modSrcContext, modItemContext) @@ -269,8 +279,8 @@ class ProjectedView(d.Display): else: fitSrcContext = "projectedFit" fitItemContext = item.name - context = ((fitSrcContext,fitItemContext),) - context = context + (("projected",),) + context = ((fitSrcContext, fitItemContext),) + context += ("projected",), menu = ContextMenu.getMenu((item,), *context) elif sel == -1: fitID = self.mainFrame.getActiveFit() @@ -287,6 +297,6 @@ class ProjectedView(d.Display): col = self.getColumn(event.Position) if col != self.getColIndex(State): fitID = self.mainFrame.getActiveFit() - sFit = service.Fit.getInstance() + sFit = Fit.getInstance() sFit.removeProjected(fitID, self.get(row)) wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=fitID)) diff --git a/gui/propertyEditor.py b/gui/propertyEditor.py index 48c2689cd..3416d591a 100644 --- a/gui/propertyEditor.py +++ b/gui/propertyEditor.py @@ -1,6 +1,11 @@ +import csv +from logbook import Logger + +# noinspection PyPackageRequirements import wx try: + # noinspection PyPackageRequirements import wx.propgrid as wxpg except: if wx.VERSION < (2, 9): @@ -8,24 +13,22 @@ except: else: raise -import gui.PFSearchBox as SBox -from gui.marketBrowser import SearchBox +from eos.db.gamedata.queries import getItem, getAttributeInfo +from service.market import Market import gui.display as d import gui.globalEvents as GE +import gui.PFSearchBox as SBox +from gui.marketBrowser import SearchBox from gui.bitmapLoader import BitmapLoader -import service -import csv -import eos.db -import logging +pyfalog = Logger(__name__) -logger = logging.getLogger(__name__) -class AttributeEditor( wx.Frame ): - - def __init__( self, parent ): +class AttributeEditor(wx.Frame): + def __init__(self, parent): wx.Frame.__init__(self, parent, wx.ID_ANY, title="Attribute Editor", pos=wx.DefaultPosition, - size=wx.Size(650, 600), style=wx.DEFAULT_FRAME_STYLE|wx.FRAME_FLOAT_ON_PARENT|wx.TAB_TRAVERSAL) + size=wx.Size(650, 600), + style=wx.DEFAULT_FRAME_STYLE | wx.FRAME_FLOAT_ON_PARENT | wx.TAB_TRAVERSAL) i = wx.IconFromBitmap(BitmapLoader.getBitmap("fit_rename_small", "gui")) self.SetIcon(i) @@ -45,7 +48,6 @@ class AttributeEditor( wx.Frame ): self.Bind(wx.EVT_MENU, self.OnExport, fileExport) self.Bind(wx.EVT_MENU, self.OnClear, fileClear) - i = wx.IconFromBitmap(BitmapLoader.getBitmap("fit_rename_small", "gui")) self.SetIcon(i) @@ -55,7 +57,8 @@ class AttributeEditor( wx.Frame ): mainSizer = wx.BoxSizer(wx.HORIZONTAL) leftSizer = wx.BoxSizer(wx.VERTICAL) - leftPanel = wx.Panel(panel, wx.ID_ANY, style=wx.DOUBLE_BORDER if 'wxMSW' in wx.PlatformInfo else wx.SIMPLE_BORDER) + leftPanel = wx.Panel(panel, wx.ID_ANY, + style=wx.DOUBLE_BORDER if 'wxMSW' in wx.PlatformInfo else wx.SIMPLE_BORDER) self.searchBox = SearchBox(leftPanel) self.itemView = ItemView(leftPanel) @@ -67,10 +70,11 @@ class AttributeEditor( wx.Frame ): mainSizer.Add(leftPanel, 1, wx.ALL | wx.EXPAND, 5) rightSizer = wx.BoxSizer(wx.VERTICAL) - self.btnRemoveOverrides = wx.Button( panel, wx.ID_ANY, u"Remove Overides for Item", wx.DefaultPosition, wx.DefaultSize, 0 ) + self.btnRemoveOverrides = wx.Button(panel, wx.ID_ANY, u"Remove Overides for Item", wx.DefaultPosition, + wx.DefaultSize, 0) self.pg = AttributeGrid(panel) - rightSizer.Add(self.pg, 1, wx.ALL|wx.EXPAND, 5) - rightSizer.Add(self.btnRemoveOverrides, 0, wx.ALL | wx.EXPAND, 5 ) + rightSizer.Add(self.pg, 1, wx.ALL | wx.EXPAND, 5) + rightSizer.Add(self.btnRemoveOverrides, 0, wx.ALL | wx.EXPAND, 5) self.btnRemoveOverrides.Bind(wx.EVT_BUTTON, self.pg.removeOverrides) self.btnRemoveOverrides.Enable(False) @@ -94,27 +98,27 @@ class AttributeEditor( wx.Frame ): def OnImport(self, event): dlg = wx.FileDialog(self, "Import pyfa override file", - wildcard = "pyfa override file (*.csv)|*.csv", - style = wx.FD_OPEN | wx.FD_FILE_MUST_EXIST) - if (dlg.ShowModal() == wx.ID_OK): + wildcard="pyfa override file (*.csv)|*.csv", + style=wx.FD_OPEN | wx.FD_FILE_MUST_EXIST) + if dlg.ShowModal() == wx.ID_OK: path = dlg.GetPath() with open(path, 'rb') as csvfile: spamreader = csv.reader(csvfile) for row in spamreader: itemID, attrID, value = row - item = eos.db.getItem(int(itemID)) - attr = eos.db.getAttributeInfo(int(attrID)) + item = getItem(int(itemID)) + attr = getAttributeInfo(int(attrID)) item.setOverride(attr, float(value)) self.itemView.updateItems(True) def OnExport(self, event): - sMkt = service.Market.getInstance() + sMkt = Market.getInstance() items = sMkt.getItemsWithOverrides() defaultFile = "pyfa_overrides.csv" dlg = wx.FileDialog(self, "Save Overrides As...", - wildcard = "pyfa overrides (*.csv)|*.csv", - style = wx.FD_SAVE, + wildcard="pyfa overrides (*.csv)|*.csv", + style=wx.FD_SAVE, defaultFile=defaultFile) if dlg.ShowModal() == wx.ID_OK: @@ -126,12 +130,15 @@ class AttributeEditor( wx.Frame ): writer.writerow([item.ID, override.attrID, override.value]) def OnClear(self, event): - dlg = wx.MessageDialog(self, - "Are you sure you want to delete all overrides?", - "Confirm Delete", wx.YES | wx.NO | wx.ICON_EXCLAMATION) + dlg = wx.MessageDialog( + self, + "Are you sure you want to delete all overrides?", + "Confirm Delete", + wx.YES | wx.NO | wx.ICON_EXCLAMATION + ) if dlg.ShowModal() == wx.ID_YES: - sMkt = service.Market.getInstance() + sMkt = Market.getInstance() items = sMkt.getItemsWithOverrides() # We can't just delete overrides, as loaded items will still have # them assigned. Deleting them from the database won't propagate @@ -143,6 +150,7 @@ class AttributeEditor( wx.Frame ): self.itemView.updateItems(True) self.pg.Clear() + # This is literally a stripped down version of the market. class ItemView(d.Display): DEFAULT_COLS = ["Base Icon", @@ -152,7 +160,7 @@ class ItemView(d.Display): def __init__(self, parent): d.Display.__init__(self, parent) - sMkt = service.Market.getInstance() + sMkt = Market.getInstance() self.things = sMkt.getItemsWithOverrides() self.items = self.things @@ -173,14 +181,14 @@ class ItemView(d.Display): self.update(self.items) def updateItems(self, updateDisplay=False): - sMkt = service.Market.getInstance() + sMkt = Market.getInstance() self.things = sMkt.getItemsWithOverrides() self.items = self.things if updateDisplay: self.update(self.things) def scheduleSearch(self, event=None): - sMkt = service.Market.getInstance() + sMkt = Market.getInstance() search = self.searchBox.GetLineText(0) # Make sure we do not count wildcard as search symbol @@ -198,9 +206,9 @@ class ItemView(d.Display): class AttributeGrid(wxpg.PropertyGrid): - def __init__(self, parent): - wxpg.PropertyGrid.__init__(self, parent, style=wxpg.PG_HIDE_MARGIN|wxpg.PG_HIDE_CATEGORIES|wxpg.PG_BOLD_MODIFIED|wxpg.PG_TOOLTIPS) + wxpg.PropertyGrid.__init__(self, parent, + style=wxpg.PG_HIDE_MARGIN | wxpg.PG_HIDE_CATEGORIES | wxpg.PG_BOLD_MODIFIED | wxpg.PG_TOOLTIPS) self.SetExtraStyle(wxpg.PG_EX_HELP_AS_TOOLTIPS) self.item = None @@ -209,9 +217,9 @@ class AttributeGrid(wxpg.PropertyGrid): self.btn = parent.Parent.btnRemoveOverrides - self.Bind( wxpg.EVT_PG_CHANGED, self.OnPropGridChange ) - self.Bind( wxpg.EVT_PG_SELECTED, self.OnPropGridSelect ) - self.Bind( wxpg.EVT_PG_RIGHT_CLICK, self.OnPropGridRightClick ) + self.Bind(wxpg.EVT_PG_CHANGED, self.OnPropGridChange) + self.Bind(wxpg.EVT_PG_SELECTED, self.OnPropGridSelect) + self.Bind(wxpg.EVT_PG_RIGHT_CLICK, self.OnPropGridRightClick) self.itemView.Bind(wx.EVT_LIST_ITEM_SELECTED, self.itemActivated) self.itemView.Bind(wx.EVT_LIST_ITEM_ACTIVATED, self.itemActivated) @@ -232,14 +240,14 @@ class AttributeGrid(wxpg.PropertyGrid): prop = wxpg.FloatProperty(key, value=default) prop.SetClientData(item.attributes[key]) # set this so that we may access it later - prop.SetHelpString("%s\n%s"%(item.attributes[key].displayName or key, "Default Value: %0.3f"%default)) + prop.SetHelpString("%s\n%s" % (item.attributes[key].displayName or key, "Default Value: %0.3f" % default)) self.Append(prop) def removeOverrides(self, event): if self.item is None: return - for _, x in self.item.overrides.items(): + for x in self.item.overrides.values(): self.item.deleteOverride(x.attr) self.itemView.updateItems(True) self.ClearModifiedStatus() @@ -262,7 +270,7 @@ class AttributeGrid(wxpg.PropertyGrid): self.itemView.updateItems() - logger.debug('%s changed to "%s"' % (p.GetName(), p.GetValueAsString())) + pyfalog.debug('{0} changed to "{1}"', p.GetName(), p.GetValueAsString()) def OnPropGridSelect(self, event): pass diff --git a/gui/pyfatogglepanel.py b/gui/pyfatogglepanel.py index 765a07727..cdd4398bb 100644 --- a/gui/pyfatogglepanel.py +++ b/gui/pyfatogglepanel.py @@ -1,65 +1,56 @@ -# -*- coding: utf-8 -*- - ########################################################################### -## pyfatogllepanel.py -## -## Author: Darriele - HomeWorld -## -## Project home: https://github.com/pyfa-org/Pyfa - pyfa project -## Some portions of code are based on -## AGW:pycollapsiblepane generic implementation of wx.CollapsiblePane -## AGW:pycollapsiblepane credits ( from the original source file used ): -## Andrea Gavana, @ 09 Aug 2007 -## Latest Revision: 12 Apr 2010, 12.00 GMT -## -## Module description: -## TogglePanel class is a wx.collipsablepane like implementation that uses -## some optimization from awg::pycollipsablepane to provide certain -## features tailored for PYFA needs. -## -## This module is part of PYFA (PYthon Fitting Assitant) and it shares the same -## licence ( read PYFA licence notice: gpl.txt ) -## -## Notes: leave the commented code as it is, those line will be removed someday +# pyfatogllepanel.py +# +# Author: Darriele - HomeWorld +# +# Project home: https://github.com/pyfa-org/Pyfa - pyfa project +# Some portions of code are based on +# AGW:pycollapsiblepane generic implementation of wx.CollapsiblePane +# AGW:pycollapsiblepane credits ( from the original source file used ): +# Andrea Gavana, @ 09 Aug 2007 +# Latest Revision: 12 Apr 2010, 12.00 GMT +# +# Module description: +# TogglePanel class is a wx.collipsablepane like implementation that uses +# some optimization from awg::pycollipsablepane to provide certain +# features tailored for PYFA needs. +# +# This module is part of PYFA (PYthon Fitting Assitant) and it shares the same +# licence ( read PYFA licence notice: gpl.txt ) +# +# Notes: leave the commented code as it is, those line will be removed someday ########################################################################### +# noinspection PyPackageRequirements import wx from gui.bitmapLoader import BitmapLoader -########################################################################### -## Class TogglePanel -########################################################################### -class TogglePanel ( wx.Panel ): - - def __init__( self, parent , forceLayout = -1): - wx.Panel.__init__ ( self, parent, id = wx.ID_ANY, pos = wx.DefaultPosition, size = wx.Size( -1,-1 ), style = wx.TAB_TRAVERSAL ) +class TogglePanel(wx.Panel): + def __init__(self, parent, forceLayout=-1): + wx.Panel.__init__(self, parent, id=wx.ID_ANY, pos=wx.DefaultPosition, size=wx.Size(-1, -1), + style=wx.TAB_TRAVERSAL) self._toggle = 1 self.parent = parent self.forceLayout = forceLayout self.bkColour = self.GetBackgroundColour() - # Create the main sizer of this panel - - self.mainSizer = wx.BoxSizer( wx.VERTICAL ) - self.SetSizer( self.mainSizer ) - parentSize = parent.GetMinSize() - - # Create the header panel + # Create the main sizer of this panel + self.mainSizer = wx.BoxSizer(wx.VERTICAL) + self.SetSizer(self.mainSizer) + # parentSize = parent.GetMinSize() + # Create the header panel self.headerPanel = wx.Panel(self) + self.mainSizer.Add(self.headerPanel, 0, wx.EXPAND | wx.TOP | wx.BOTTOM | wx.RIGHT, 1) - self.mainSizer.Add(self.headerPanel,0,wx.EXPAND | wx.TOP|wx.BOTTOM|wx.RIGHT, 1) + # Load expanded/collapsed bitmaps from the icons folder + self.bmpExpanded = BitmapLoader.getBitmap("down-arrow2", "gui") + self.bmpCollapsed = BitmapLoader.getBitmap("up-arrow2", "gui") - # Load expanded/collapsed bitmaps from the icons folder - - self.bmpExpanded = BitmapLoader.getBitmap("down-arrow2","gui") - self.bmpCollapsed = BitmapLoader.getBitmap("up-arrow2","gui") - - # Make the bitmaps have the same color as window text - - sysTextColour = wx.SystemSettings.GetColour( wx.SYS_COLOUR_WINDOWTEXT ) + # Make the bitmaps have the same color as window text + sysTextColour = wx.SystemSettings.GetColour(wx.SYS_COLOUR_WINDOWTEXT) img = self.bmpExpanded.ConvertToImage() img.Replace(0, 0, 0, sysTextColour[0], sysTextColour[1], sysTextColour[2]) @@ -69,55 +60,53 @@ class TogglePanel ( wx.Panel ): img.Replace(0, 0, 0, sysTextColour[0], sysTextColour[1], sysTextColour[2]) self.bmpCollapsed = wx.BitmapFromImage(img) - self.headerBmp = wx.StaticBitmap(self.headerPanel ) - self.headerBmp.SetBitmap( self.bmpExpanded) + self.headerBmp = wx.StaticBitmap(self.headerPanel) + self.headerBmp.SetBitmap(self.bmpExpanded) - # Create the header sizer and add static bitmap and static text controls to it + # Create the header sizer and add static bitmap and static text controls to it - headerSizer = wx.BoxSizer( wx.HORIZONTAL ) - self.headerPanel.SetSizer( headerSizer) + headerSizer = wx.BoxSizer(wx.HORIZONTAL) + self.headerPanel.SetSizer(headerSizer) - hbmpSizer = wx.BoxSizer( wx.HORIZONTAL ) - hlblSizer = wx.BoxSizer( wx.HORIZONTAL ) - self.hcntSizer = wx.BoxSizer( wx.HORIZONTAL) + hbmpSizer = wx.BoxSizer(wx.HORIZONTAL) + hlblSizer = wx.BoxSizer(wx.HORIZONTAL) + self.hcntSizer = wx.BoxSizer(wx.HORIZONTAL) - hbmpSizer.Add( self.headerBmp, 0,0, 5 ) + hbmpSizer.Add(self.headerBmp, 0, 0, 5) - self.headerLabel = wx.StaticText( self.headerPanel, wx.ID_ANY, u"PYFA", wx.DefaultPosition, wx.DefaultSize, 0 ) - hlblSizer.Add( self.headerLabel, 0, wx.EXPAND , 5 ) + self.headerLabel = wx.StaticText(self.headerPanel, wx.ID_ANY, u"PYFA", wx.DefaultPosition, wx.DefaultSize, 0) + hlblSizer.Add(self.headerLabel, 0, wx.EXPAND, 5) - headerSizer.Add( hbmpSizer, 0, wx.RIGHT, 5 ) - headerSizer.Add( hlblSizer, 0, wx.RIGHT, 5 ) - headerSizer.Add( self.hcntSizer, 0, wx.RIGHT, 5) + headerSizer.Add(hbmpSizer, 0, wx.RIGHT, 5) + headerSizer.Add(hlblSizer, 0, wx.RIGHT, 5) + headerSizer.Add(self.hcntSizer, 0, wx.RIGHT, 5) - # Set the static text font weight to BOLD + # Set the static text font weight to BOLD - headerFont=parent.GetFont() + headerFont = parent.GetFont() headerFont.SetWeight(wx.BOLD) self.headerLabel.SetFont(headerFont) - # Create the content panel and its main sizer + # Create the content panel and its main sizer - self.contentSizer = wx.BoxSizer( wx.VERTICAL ) + self.contentSizer = wx.BoxSizer(wx.VERTICAL) self.contentPanel = wx.Panel(self) self.contentPanel.SetSizer(self.contentSizer) - self.mainSizer.Add( self.contentPanel, 0, wx.EXPAND | wx.RIGHT | wx.LEFT , 5) - + self.mainSizer.Add(self.contentPanel, 0, wx.EXPAND | wx.RIGHT | wx.LEFT, 5) self.Layout() - # Connect Events + # Connect Events + self.headerLabel.Bind(wx.EVT_LEFT_UP, self.toggleContent) + self.headerBmp.Bind(wx.EVT_LEFT_UP, self.toggleContent) + self.headerPanel.Bind(wx.EVT_LEFT_UP, self.toggleContent) - self.headerLabel.Bind( wx.EVT_LEFT_UP, self.toggleContent ) - self.headerBmp.Bind( wx.EVT_LEFT_UP, self.toggleContent ) - self.headerPanel.Bind( wx.EVT_LEFT_UP, self.toggleContent ) - - def __del__( self ): + def __del__(self): pass def AddToggleItem(self, hitem): - hitem.Bind( wx.EVT_LEFT_UP, self.toggleContent ) + hitem.Bind(wx.EVT_LEFT_UP, self.toggleContent) def GetHeaderContentSizer(self): return self.hcntSizer @@ -126,7 +115,7 @@ class TogglePanel ( wx.Panel ): return self.headerPanel def InsertItemInHeader(self, item): - self.hcntSizer.Add(item,0,0,0) + self.hcntSizer.Add(item, 0, 0, 0) self.Layout() def AddSizer(self, sizer): @@ -157,7 +146,7 @@ class TogglePanel ( wx.Panel ): """ Handles the status changes (collapsing/expanding). - :param `sz`: an instance of `wx.Size`. + :param sz: an instance of `wx.Size`. """ # minimal size has priority over the best size so set here our min size diff --git a/gui/pygauge.py b/gui/pygauge.py index b63f4fea8..dbb0c4531 100644 --- a/gui/pygauge.py +++ b/gui/pygauge.py @@ -11,16 +11,17 @@ PyfaGauge is a generic Gauge implementation tailored for PYFA (Python Fitting As It uses the easeOutQuad equation from caurina.transitions.Tweener to do the animation stuff """ +# noinspection PyPackageRequirements import wx import copy -import math from gui.utils import colorUtils import gui.utils.drawUtils as drawUtils import gui.utils.animEffects as animEffects import gui.utils.fonts as fonts -from service import fit +from service.fit import Fit + class PyGauge(wx.PyWindow): """ @@ -29,7 +30,7 @@ class PyGauge(wx.PyWindow): """ def __init__(self, parent, id=wx.ID_ANY, range=100, pos=wx.DefaultPosition, - size=(-1,30), style=0): + size=(-1, 30), style=0): """ Default class constructor. @@ -46,8 +47,8 @@ class PyGauge(wx.PyWindow): self._size = size self._border_colour = wx.BLACK - self._barColour = self._barColourSorted = [wx.Colour(212,228,255)] - self._barGradient = self._barGradientSorted = None + self._barColour = self._barColourSorted = [wx.Colour(212, 228, 255)] + self._barGradient = self._barGradientSorted = None self._border_padding = 0 self._range = range @@ -67,10 +68,10 @@ class PyGauge(wx.PyWindow): self._animDirection = 0 self.animEffect = animEffects.OUT_QUAD - self.transitionsColors = [( wx.Colour(191, 191, 191, 255) , wx.Colour(96, 191, 0, 255) ), - ( wx.Colour(191, 167, 96, 255) , wx.Colour(255, 191, 0, 255) ), - ( wx.Colour(255, 191, 0, 255) , wx.Colour(255, 128, 0, 255) ), - ( wx.Colour(255, 128, 0, 255) , wx.Colour(255, 0, 0, 255) )] + self.transitionsColors = [(wx.Colour(191, 191, 191, 255), wx.Colour(96, 191, 0, 255)), + (wx.Colour(191, 167, 96, 255), wx.Colour(255, 191, 0, 255)), + (wx.Colour(255, 191, 0, 255), wx.Colour(255, 128, 0, 255)), + (wx.Colour(255, 128, 0, 255), wx.Colour(255, 0, 0, 255))] self.gradientEffect = -35 self._percentage = 0 @@ -79,8 +80,8 @@ class PyGauge(wx.PyWindow): self.font = wx.Font(fonts.NORMAL, wx.SWISS, wx.NORMAL, wx.NORMAL, False) - self.SetBarGradient((wx.Colour(119,119,119),wx.Colour(153,153,153))) - self.SetBackgroundColour(wx.Colour(51,51,51)) + self.SetBarGradient((wx.Colour(119, 119, 119), wx.Colour(153, 153, 153))) + self.SetBackgroundColour(wx.Colour(51, 51, 51)) self._tooltip = wx.ToolTip("") self.SetToolTip(self._tooltip) self._tooltip.SetTip("0.00/100.00") @@ -107,7 +108,6 @@ class PyGauge(wx.PyWindow): return wx.Size(self._size[0], self._size[1]) - def GetBorderColour(self): return self._border_colour @@ -121,7 +121,7 @@ class PyGauge(wx.PyWindow): return self._barColour[0] def SetBarColour(self, colour): - if type(colour) != type([]): + if not isinstance(colour, list): self._barColour = [colour] else: self._barColour = list(colour) @@ -130,31 +130,30 @@ class PyGauge(wx.PyWindow): GetBarColor = GetBarColour def SetFractionDigits(self, digits): - self._fractionDigits=digits + self._fractionDigits = digits def GetBarGradient(self): """ Returns a tuple containing the gradient start and end colours. """ - if self._barGradient == None: + if self._barGradient is None: return None return self._barGradient[0] - def SetBarGradient(self, gradient = None): + def SetBarGradient(self, gradient=None): """ Sets the bar gradient. This overrides the BarColour. - :param `gradient`: a tuple containing the gradient start and end colours. + :param gradient: a tuple containing the gradient start and end colours. """ - if gradient == None: + if gradient is None: self._barGradient = None else: - if type(gradient) != type([]): + if not isinstance(gradient, list): self._barGradient = [gradient] else: self._barGradient = list(gradient) - def GetBorderPadding(self): """ Gets the border padding. """ @@ -164,19 +163,18 @@ class PyGauge(wx.PyWindow): """ Sets the border padding. - :param `padding`: pixels between the border and the progress bar. + :param padding: pixels between the border and the progress bar. """ self._border_padding = padding - def GetRange(self): """ Returns the maximum value of the gauge. """ return self._range def Animate(self): - sFit = fit.Fit.getInstance() + sFit = Fit.getInstance() if sFit.serviceFittingOptions["enableGaugeAnimation"]: if not self._timer: self._timer = wx.Timer(self, self._timerId) @@ -186,27 +184,28 @@ class PyGauge(wx.PyWindow): self._animValue = self._percentage self.Refresh() - def SetRange(self, range, reinit = False): + def SetRange(self, range, reinit=False): """ Sets the range of the gauge. The gauge length is its value as a proportion of the range. - :param `range`: The maximum value of the gauge. + :param reinit: + :param range: The maximum value of the gauge. """ if self._range == range: return - range = float(range) + range_ = float(range) - if range <= 0: + if range_ <= 0: self._range = 0.01 else: - self._range = range + self._range = range_ if reinit is False: self._oldPercentage = self._percentage - self._percentage = (self._value/self._range) * 100 + self._percentage = (self._value / self._range) * 100 else: self._oldPercentage = self._percentage self._percentage = 0 @@ -214,9 +213,7 @@ class PyGauge(wx.PyWindow): self.Animate() - - self._tooltip.SetTip("%.2f/%.2f" % (self._value, self._range if self._range >0.01 else 0)) - + self._tooltip.SetTip("%.2f/%.2f" % (self._value, self._range if self._range > 0.01 else 0)) def GetValue(self): """ Returns the current position of the gauge. """ @@ -233,22 +230,22 @@ class PyGauge(wx.PyWindow): self._value = value if value < 0: self._value = 0 - self._percentage = (self._value/self._range) * 100 + self._percentage = (self._value / self._range) * 100 self.Animate() self._tooltip.SetTip("%.2f/%.2f" % (self._value, self._range)) - def SetValueRange(self, value, range, reinit = False): + def SetValueRange(self, value, range, reinit=False): if self._value == value and self._range == range: return - range = float(range) + range_ = float(range) - if range <= 0: + if range_ <= 0: self._range = 0.01 else: - self._range = range + self._range = range_ value = float(value) @@ -258,21 +255,21 @@ class PyGauge(wx.PyWindow): if reinit is False: self._oldPercentage = self._percentage - self._percentage = (self._value/self._range) * 100 + self._percentage = (self._value / self._range) * 100 else: self._oldPercentage = self._percentage self._percentage = 0 - self.Animate() - self._tooltip.SetTip("%.2f/%.2f" % (self._value, self._range if self._range >0.01 else 0)) + self._tooltip.SetTip("%.2f/%.2f" % (self._value, self._range if self._range > 0.01 else 0)) - def OnEraseBackground(self, event): + @staticmethod + def OnEraseBackground(event): """ Handles the ``wx.EVT_ERASE_BACKGROUND`` event for L{PyGauge}. - :param `event`: a `wx.EraseEvent` event to be processed. + :param event: a `wx.EraseEvent` event to be processed. :note: This method is intentionally empty to reduce flicker. """ @@ -283,7 +280,7 @@ class PyGauge(wx.PyWindow): """ Handles the ``wx.EVT_PAINT`` event for L{PyGauge}. - :param `event`: a `wx.PaintEvent` event to be processed. + :param event: a `wx.PaintEvent` event to be processed. """ dc = wx.BufferedPaintDC(self) @@ -308,7 +305,7 @@ class PyGauge(wx.PyWindow): dc.SetPen(wx.Pen(self.GetBorderColour())) dc.DrawRectangleRect(rect) pad = 1 + self.GetBorderPadding() - rect.Deflate(pad,pad) + rect.Deflate(pad, pad) if self.GetBarGradient(): @@ -326,48 +323,46 @@ class PyGauge(wx.PyWindow): # time on them if not needed. See GH issue #282 pv = value - xv=1 - transition = 0 if pv <= 100: - xv = pv/100 + xv = pv / 100 transition = 0 - elif pv <=101: - xv = pv -100 + elif pv <= 101: + xv = pv - 100 transition = 1 elif pv <= 103: - xv = (pv -101)/2 + xv = (pv - 101) / 2 transition = 2 elif pv <= 105: - xv = (pv -103)/2 + xv = (pv - 103) / 2 transition = 3 else: pv = 106 - xv = pv -100 + xv = pv - 100 transition = -1 if transition != -1: - colorS,colorE = self.transitionsColors[transition] + colorS, colorE = self.transitionsColors[transition] color = colorUtils.CalculateTransitionColor(colorS, colorE, xv) else: - color = wx.Colour(191,48,48) + color = wx.Colour(191, 48, 48) if self.gradientEffect > 0: - gcolor = colorUtils.BrightenColor(color, float(self.gradientEffect) / 100) - gMid = colorUtils.BrightenColor(color, float(self.gradientEffect/2) / 100) + gcolor = colorUtils.BrightenColor(color, float(self.gradientEffect) / 100) + gMid = colorUtils.BrightenColor(color, float(self.gradientEffect / 2) / 100) else: - gcolor = colorUtils.DarkenColor(color, float(-self.gradientEffect) / 100) - gMid = colorUtils.DarkenColor(color, float(-self.gradientEffect/2) / 100) + gcolor = colorUtils.DarkenColor(color, float(-self.gradientEffect) / 100) + gMid = colorUtils.DarkenColor(color, float(-self.gradientEffect / 2) / 100) gBmp = drawUtils.DrawGradientBar(r.width, r.height, gMid, color, gcolor) dc.DrawBitmap(gBmp, r.left, r.top) else: - colour=self.GetBarColour() + colour = self.GetBarColour() dc.SetBrush(wx.Brush(colour)) dc.SetPen(wx.Pen(colour)) if value > 100: @@ -381,14 +376,14 @@ class PyGauge(wx.PyWindow): dc.SetFont(self.font) r = copy.copy(rect) - r.left +=1 - r.top +=1 + r.left += 1 + r.top += 1 if self._range == 0.01 and self._value > 0: - formatStr = u'\u221e' - dc.SetTextForeground(wx.Colour(80,80,80)) + formatStr = u'\u221e' + dc.SetTextForeground(wx.Colour(80, 80, 80)) dc.DrawLabel(formatStr, r, wx.ALIGN_CENTER) - dc.SetTextForeground(wx.Colour(255,255,255)) + dc.SetTextForeground(wx.Colour(255, 255, 255)) dc.DrawLabel(formatStr, rect, wx.ALIGN_CENTER) else: if self.GetBarGradient() and self._showRemaining: @@ -404,20 +399,20 @@ class PyGauge(wx.PyWindow): else: formatStr = "{0:." + str(self._fractionDigits) + "f}%" - dc.SetTextForeground(wx.Colour(80,80,80)) + dc.SetTextForeground(wx.Colour(80, 80, 80)) dc.DrawLabel(formatStr.format(value), r, wx.ALIGN_CENTER) - dc.SetTextForeground(wx.Colour(255,255,255)) + dc.SetTextForeground(wx.Colour(255, 255, 255)) dc.DrawLabel(formatStr.format(value), rect, wx.ALIGN_CENTER) - def OnTimer(self,event): + def OnTimer(self, event): """ Handles the ``wx.EVT_TIMER`` event for L{PyfaGauge}. - :param `event`: a timer event + :param event: a timer event """ - oldValue=self._oldPercentage - value=self._percentage + oldValue = self._oldPercentage + value = self._percentage start = 0 direction = 1 if oldValue < value else -1 @@ -436,13 +431,13 @@ class PyGauge(wx.PyWindow): stop_timer = True if direction == 1: - if (oldValue+step) < value: - self._animValue = oldValue+step + if (oldValue + step) < value: + self._animValue = oldValue + step else: stop_timer = True else: - if (oldValue-step) > value: - self._animValue = oldValue-step + if (oldValue - step) > value: + self._animValue = oldValue - step else: stop_timer = True @@ -451,4 +446,3 @@ class PyGauge(wx.PyWindow): self._timer.Stop() self.Refresh() - diff --git a/gui/resistsEditor.py b/gui/resistsEditor.py index 37d04b032..c8bab84bb 100644 --- a/gui/resistsEditor.py +++ b/gui/resistsEditor.py @@ -1,4 +1,4 @@ -#=============================================================================== +# ============================================================================= # Copyright (C) 2014 Ryan Holmes # # This file is part of pyfa. @@ -15,14 +15,17 @@ # # You should have received a copy of the GNU General Public License # along with pyfa. If not, see . -#=============================================================================== +# ============================================================================= +# noinspection PyPackageRequirements import wx +from service.targetResists import TargetResists from gui.bitmapLoader import BitmapLoader -import service from gui.utils.clipboard import toClipboard, fromClipboard -from service.targetResists import ImportError from gui.builtinViews.entityEditor import EntityEditor, BaseValidator +from logbook import Logger + +pyfalog = Logger(__name__) class TargetResistsTextValidor(BaseValidator): @@ -44,7 +47,8 @@ class TargetResistsTextValidor(BaseValidator): raise ValueError("Target Resist Profile name already in use, please choose another.") return True - except ValueError, e: + except ValueError as e: + pyfalog.error(e) wx.MessageBox(u"{}".format(e), "Error") textCtrl.SetFocus() return False @@ -56,34 +60,34 @@ class TargetResistsEntityEditor(EntityEditor): self.SetEditorValidator(TargetResistsTextValidor) def getEntitiesFromContext(self): - sTR = service.TargetResists.getInstance() + sTR = TargetResists.getInstance() choices = sorted(sTR.getTargetResistsList(), key=lambda p: p.name) return choices def DoNew(self, name): - sTR = service.TargetResists.getInstance() + sTR = TargetResists.getInstance() return sTR.newPattern(name) def DoRename(self, entity, name): - sTR = service.TargetResists.getInstance() + sTR = TargetResists.getInstance() sTR.renamePattern(entity, name) def DoCopy(self, entity, name): - sTR = service.TargetResists.getInstance() + sTR = TargetResists.getInstance() copy = sTR.copyPattern(entity) sTR.renamePattern(copy, name) return copy def DoDelete(self, entity): - sTR = service.TargetResists.getInstance() + sTR = TargetResists.getInstance() sTR.deletePattern(entity) -class ResistsEditorDlg(wx.Dialog): +class ResistsEditorDlg(wx.Dialog): DAMAGE_TYPES = ("em", "thermal", "kinetic", "explosive") def __init__(self, parent): - wx.Dialog.__init__(self, parent, id = wx.ID_ANY, title = u"Target Resists Editor", size = wx.Size( 350,240 )) + wx.Dialog.__init__(self, parent, id=wx.ID_ANY, title=u"Target Resists Editor", size=wx.Size(350, 240)) self.block = False self.SetSizeHintsSz(wx.DefaultSize, wx.DefaultSize) @@ -104,24 +108,24 @@ class ResistsEditorDlg(wx.Dialog): resistEditSizer.SetFlexibleDirection(wx.BOTH) resistEditSizer.SetNonFlexibleGrowMode(wx.FLEX_GROWMODE_SPECIFIED) - width = -1 - defSize = wx.Size(50,-1) + defSize = wx.Size(50, -1) - for i, type in enumerate(self.DAMAGE_TYPES): - if i%2: + for i, type_ in enumerate(self.DAMAGE_TYPES): + if i % 2: style = wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_RIGHT | wx.LEFT border = 25 else: style = wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_RIGHT border = 5 - bmp = wx.StaticBitmap(self, wx.ID_ANY, BitmapLoader.getBitmap("%s_big"%type, "gui")) + bmp = wx.StaticBitmap(self, wx.ID_ANY, BitmapLoader.getBitmap("%s_big" % type_, "gui")) resistEditSizer.Add(bmp, 0, style, border) # set text edit - setattr(self, "%sEdit"%type, wx.TextCtrl(self, wx.ID_ANY, "", wx.DefaultPosition, defSize)) - editObj = getattr(self, "%sEdit"%type) + setattr(self, "%sEdit" % type_, wx.TextCtrl(self, wx.ID_ANY, "", wx.DefaultPosition, defSize)) + editObj = getattr(self, "%sEdit" % type_) resistEditSizer.Add(editObj, 0, wx.BOTTOM | wx.TOP | wx.ALIGN_CENTER_VERTICAL, 5) - resistEditSizer.Add(wx.StaticText( self, wx.ID_ANY, u"%", wx.DefaultPosition, wx.DefaultSize, 0 ), 0, wx.BOTTOM | wx.TOP | wx.ALIGN_CENTER_VERTICAL, 5) + resistEditSizer.Add(wx.StaticText(self, wx.ID_ANY, u"%", wx.DefaultPosition, wx.DefaultSize, 0), 0, + wx.BOTTOM | wx.TOP | wx.ALIGN_CENTER_VERTICAL, 5) editObj.Bind(wx.EVT_TEXT, self.ValuesUpdated) # Color we use to reset invalid value color @@ -138,7 +142,7 @@ class ResistsEditorDlg(wx.Dialog): self.stNotice.Wrap(-1) perSizer.Add(self.stNotice, 0, wx.BOTTOM | wx.TOP | wx.LEFT, 5) - footerSizer.Add(perSizer, 1, wx.ALIGN_CENTER_VERTICAL, 5) + footerSizer.Add(perSizer, 1, wx.ALIGN_CENTER_VERTICAL, 5) self.totSizer = wx.BoxSizer(wx.VERTICAL) @@ -147,8 +151,8 @@ class ResistsEditorDlg(wx.Dialog): mainSizer.Add(contentSizer, 1, wx.EXPAND, 0) if "wxGTK" in wx.PlatformInfo: - self.closeBtn = wx.Button( self, wx.ID_ANY, u"Close", wx.DefaultPosition, wx.DefaultSize, 0 ) - mainSizer.Add( self.closeBtn, 0, wx.ALL|wx.ALIGN_RIGHT, 5 ) + self.closeBtn = wx.Button(self, wx.ID_ANY, u"Close", wx.DefaultPosition, wx.DefaultSize, 0) + mainSizer.Add(self.closeBtn, 0, wx.ALL | wx.ALIGN_RIGHT, 5) self.closeBtn.Bind(wx.EVT_BUTTON, self.closeEvent) self.SetSizer(mainSizer) @@ -160,13 +164,13 @@ class ResistsEditorDlg(wx.Dialog): bitmap = wx.ArtProvider.GetBitmap(art, wx.ART_BUTTON) btn = wx.BitmapButton(self, wx.ID_ANY, bitmap) - btn.SetMinSize( btn.GetSize() ) - btn.SetMaxSize( btn.GetSize() ) + btn.SetMinSize(btn.GetSize()) + btn.SetMaxSize(btn.GetSize()) btn.Layout() setattr(self, name, btn) btn.Enable(True) - btn.SetToolTipString("%s patterns %s clipboard" % (name, direction) ) + btn.SetToolTipString("%s patterns %s clipboard" % (name, direction)) footerSizer.Add(btn, 0, wx.ALIGN_CENTER_HORIZONTAL | wx.ALIGN_RIGHT) btn.Bind(wx.EVT_BUTTON, getattr(self, "{}Patterns".format(name.lower()))) @@ -189,20 +193,22 @@ class ResistsEditorDlg(wx.Dialog): self.Destroy() def ValuesUpdated(self, event=None): - ''' + """ Event that is fired when resists values change. Iterates through all resist edit fields. If blank, sets it to 0.0. If it is not a proper decimal value, sets text color to red and refuses to save changes until issue is resolved - ''' + """ if self.block: return + editObj = None + try: p = self.entityEditor.getActiveEntity() - for type in self.DAMAGE_TYPES: - editObj = getattr(self, "%sEdit"%type) + for type_ in self.DAMAGE_TYPES: + editObj = getattr(self, "%sEdit" % type_) if editObj.GetValue() == "": # if we are blank, overwrite with 0 @@ -215,7 +221,7 @@ class ResistsEditorDlg(wx.Dialog): assert 0 <= value <= 100 # if everything checks out, set resist attribute - setattr(p, "%sAmount"%type, value/100) + setattr(p, "%sAmount" % type_, value / 100) editObj.SetForegroundColour(self.colorReset) self.stNotice.SetLabel("") @@ -224,19 +230,23 @@ class ResistsEditorDlg(wx.Dialog): if event is not None: event.Skip() - service.TargetResists.getInstance().saveChanges(p) + TargetResists.getInstance().saveChanges(p) except ValueError: editObj.SetForegroundColour(wx.RED) - self.stNotice.SetLabel("Incorrect Formatting (decimals only)") + msg = "Incorrect Formatting (decimals only)" + pyfalog.warning(msg) + self.stNotice.SetLabel(msg) except AssertionError: editObj.SetForegroundColour(wx.RED) - self.stNotice.SetLabel("Incorrect Range (must be 0-100)") + msg = "Incorrect Range (must be 0-100)" + pyfalog.warning(msg) + self.stNotice.SetLabel(msg) finally: # Refresh for color changes to take effect immediately self.Refresh() def patternChanged(self, event=None): - "Event fired when user selects pattern. Can also be called from script" + """Event fired when user selects pattern. Can also be called from script""" if not self.entityEditor.checkEntitiesExist(): self.Destroy() @@ -250,35 +260,39 @@ class ResistsEditorDlg(wx.Dialog): # Set new values for field in self.DAMAGE_TYPES: edit = getattr(self, "%sEdit" % field) - amount = getattr(p, "%sAmount" % field)*100 + amount = getattr(p, "%sAmount" % field) * 100 edit.ChangeValue(str(amount)) self.block = False self.ValuesUpdated() - def __del__( self ): + def __del__(self): pass def importPatterns(self, event): - "Event fired when import from clipboard button is clicked" + """Event fired when import from clipboard button is clicked""" text = fromClipboard() if text: - sTR = service.TargetResists.getInstance() + sTR = TargetResists.getInstance() try: sTR.importPatterns(text) self.stNotice.SetLabel("Patterns successfully imported from clipboard") - except service.targetResists.ImportError, e: + except ImportError as e: + pyfalog.error(e) self.stNotice.SetLabel(str(e)) - except Exception, e: - self.stNotice.SetLabel("Could not import from clipboard: unknown errors") + except Exception as e: + msg = "Could not import from clipboard:" + pyfalog.warning(msg) + pyfalog.error(e) + self.stNotice.SetLabel(msg) finally: self.entityEditor.refreshEntityList() else: self.stNotice.SetLabel("Could not import from clipboard") def exportPatterns(self, event): - "Event fired when export to clipboard button is clicked" - sTR = service.TargetResists.getInstance() - toClipboard( sTR.exportPatterns() ) + """Event fired when export to clipboard button is clicked""" + sTR = TargetResists.getInstance() + toClipboard(sTR.exportPatterns()) self.stNotice.SetLabel("Patterns exported to clipboard") diff --git a/gui/setEditor.py b/gui/setEditor.py index 84090f6ff..d199c1cc2 100644 --- a/gui/setEditor.py +++ b/gui/setEditor.py @@ -1,4 +1,4 @@ -#=============================================================================== +# ============================================================================= # Copyright (C) 2016 Ryan Holmes # # This file is part of pyfa. @@ -15,18 +15,19 @@ # # You should have received a copy of the GNU General Public License # along with pyfa. If not, see . -#=============================================================================== +# ============================================================================= +from logbook import Logger +# noinspection PyPackageRequirements import wx -from gui.bitmapLoader import BitmapLoader + +from service.implantSet import ImplantSets from gui.builtinViews.implantEditor import BaseImplantEditorView -import service from gui.utils.clipboard import toClipboard, fromClipboard -from service.implantSet import ImportError -import logging from gui.builtinViews.entityEditor import EntityEditor, BaseValidator -logger = logging.getLogger(__name__) +pyfalog = Logger(__name__) + class ImplantTextValidor(BaseValidator): def __init__(self): @@ -47,7 +48,8 @@ class ImplantTextValidor(BaseValidator): raise ValueError("Imlplant Set name already in use, please choose another.") return True - except ValueError, e: + except ValueError as e: + pyfalog.error(e) wx.MessageBox(u"{}".format(e), "Error") textCtrl.SetFocus() return False @@ -59,25 +61,25 @@ class ImplantSetEntityEditor(EntityEditor): self.SetEditorValidator(ImplantTextValidor) def getEntitiesFromContext(self): - sIS = service.ImplantSets.getInstance() + sIS = ImplantSets.getInstance() return sorted(sIS.getImplantSetList(), key=lambda c: c.name) def DoNew(self, name): - sIS = service.ImplantSets.getInstance() + sIS = ImplantSets.getInstance() return sIS.newSet(name) def DoRename(self, entity, name): - sIS = service.ImplantSets.getInstance() + sIS = ImplantSets.getInstance() sIS.renameSet(entity, name) def DoCopy(self, entity, name): - sIS = service.ImplantSets.getInstance() + sIS = ImplantSets.getInstance() copy = sIS.copySet(entity) sIS.renameSet(copy, name) return copy def DoDelete(self, entity): - sIS = service.ImplantSets.getInstance() + sIS = ImplantSets.getInstance() sIS.deleteSet(entity) @@ -91,28 +93,28 @@ class ImplantSetEditor(BaseImplantEditorView): self.Parent.entityEditor.Bind(wx.EVT_CHOICE, self.contextChanged) def getImplantsFromContext(self): - sIS = service.ImplantSets.getInstance() - set = self.Parent.entityEditor.getActiveEntity() - if set: - return sIS.getImplants(set.ID) + sIS = ImplantSets.getInstance() + set_ = self.Parent.entityEditor.getActiveEntity() + if set_: + return sIS.getImplants(set_.ID) return [] def addImplantToContext(self, item): - sIS = service.ImplantSets.getInstance() - set = self.Parent.entityEditor.getActiveEntity() + sIS = ImplantSets.getInstance() + set_ = self.Parent.entityEditor.getActiveEntity() - sIS.addImplant(set.ID, item.ID) + sIS.addImplant(set_.ID, item.ID) def removeImplantFromContext(self, implant): - sIS = service.ImplantSets.getInstance() - set = self.Parent.entityEditor.getActiveEntity() + sIS = ImplantSets.getInstance() + set_ = self.Parent.entityEditor.getActiveEntity() + + sIS.removeImplant(set_.ID, implant) - sIS.removeImplant(set.ID, implant) class ImplantSetEditorDlg(wx.Dialog): - def __init__(self, parent): - wx.Dialog.__init__(self, parent, id = wx.ID_ANY, title = u"Implant Set Editor", size = wx.Size(640, 600)) + wx.Dialog.__init__(self, parent, id=wx.ID_ANY, title=u"Implant Set Editor", size=wx.Size(640, 600)) self.block = False self.SetSizeHintsSz(wx.DefaultSize, wx.DefaultSize) @@ -138,25 +140,25 @@ class ImplantSetEditorDlg(wx.Dialog): footerSizer.Add(self.stNotice, 1, wx.BOTTOM | wx.TOP | wx.LEFT, 5) if "wxGTK" in wx.PlatformInfo: - self.closeBtn = wx.Button( self, wx.ID_ANY, u"Close", wx.DefaultPosition, wx.DefaultSize, 0 ) - mainSizer.Add( self.closeBtn, 0, wx.ALL|wx.ALIGN_RIGHT, 5 ) + self.closeBtn = wx.Button(self, wx.ID_ANY, u"Close", wx.DefaultPosition, wx.DefaultSize, 0) + mainSizer.Add(self.closeBtn, 0, wx.ALL | wx.ALIGN_RIGHT, 5) self.closeBtn.Bind(wx.EVT_BUTTON, self.closeEvent) importExport = (("Import", wx.ART_FILE_OPEN, "from"), ("Export", wx.ART_FILE_SAVE_AS, "to")) for name, art, direction in importExport: - bitmap = wx.ArtProvider.GetBitmap(art, wx.ART_BUTTON) - btn = wx.BitmapButton(self, wx.ID_ANY, bitmap) + bitmap = wx.ArtProvider.GetBitmap(art, wx.ART_BUTTON) + btn = wx.BitmapButton(self, wx.ID_ANY, bitmap) - btn.SetMinSize( btn.GetSize() ) - btn.SetMaxSize( btn.GetSize() ) + btn.SetMinSize(btn.GetSize()) + btn.SetMaxSize(btn.GetSize()) - btn.Layout() - setattr(self, name, btn) - btn.Enable(True) - btn.SetToolTipString("%s implant sets %s clipboard" % (name, direction) ) - footerSizer.Add(btn, 0, wx.ALIGN_CENTER_HORIZONTAL | wx.ALIGN_RIGHT) + btn.Layout() + setattr(self, name, btn) + btn.Enable(True) + btn.SetToolTipString("%s implant sets %s clipboard" % (name, direction)) + footerSizer.Add(btn, 0, wx.ALIGN_CENTER_HORIZONTAL | wx.ALIGN_RIGHT) mainSizer.Add(footerSizer, 0, wx.ALL | wx.EXPAND, 5) @@ -183,23 +185,24 @@ class ImplantSetEditorDlg(wx.Dialog): def closeEvent(self, event): self.Destroy() - def __del__( self ): + def __del__(self): pass def importPatterns(self, event): - "Event fired when import from clipboard button is clicked" + """Event fired when import from clipboard button is clicked""" text = fromClipboard() if text: - sIS = service.ImplantSets.getInstance() + sIS = ImplantSets.getInstance() try: sIS.importSets(text) self.stNotice.SetLabel("Patterns successfully imported from clipboard") self.showInput(False) - except ImportError, e: + except ImportError as e: + pyfalog.error(e) self.stNotice.SetLabel(str(e)) - except Exception, e: - logging.exception("Unhandled Exception") + except Exception as e: + pyfalog.error(e) self.stNotice.SetLabel("Could not import from clipboard: unknown errors") finally: self.updateChoices() @@ -207,8 +210,8 @@ class ImplantSetEditorDlg(wx.Dialog): self.stNotice.SetLabel("Could not import from clipboard") def exportPatterns(self, event): - "Event fired when export to clipboard button is clicked" + """Event fired when export to clipboard button is clicked""" - sIS = service.ImplantSets.getInstance() + sIS = ImplantSets.getInstance() toClipboard(sIS.exportSets()) self.stNotice.SetLabel("Sets exported to clipboard") diff --git a/gui/sfBrowserItem.py b/gui/sfBrowserItem.py index 81294cba3..de9eb4001 100644 --- a/gui/sfBrowserItem.py +++ b/gui/sfBrowserItem.py @@ -1,5 +1,5 @@ +# noinspection PyPackageRequirements import wx -import gui.utils.colorUtils as colorUtils import gui.utils.drawUtils as drawUtils SB_ITEM_NORMAL = 0 @@ -7,14 +7,15 @@ SB_ITEM_SELECTED = 1 SB_ITEM_HIGHLIGHTED = 2 SB_ITEM_DISABLED = 4 -BTN_NORMAL = 1 -BTN_PRESSED = 2 -BTN_HOVER = 4 +BTN_NORMAL = 1 +BTN_PRESSED = 2 +BTN_HOVER = 4 BTN_DISABLED = 8 class PFBaseButton(object): - def __init__(self, normalBitmap = wx.NullBitmap,label = "", callback = None, hoverBitmap = None, disabledBitmap = None, show = True): + def __init__(self, normalBitmap=wx.NullBitmap, label="", callback=None, hoverBitmap=None, disabledBitmap=None, + show=True): self.normalBmp = normalBitmap self.dropShadowOpacity = 0.2 @@ -48,7 +49,7 @@ class PFBaseButton(object): if self.callback: self.callback() - def SetState(self, state = BTN_NORMAL): + def SetState(self, state=BTN_NORMAL): self.state = state def GetState(self): @@ -57,7 +58,7 @@ class PFBaseButton(object): def GetSize(self): w = self.normalBmp.GetWidth() h = self.normalBmp.GetHeight() - return (w,h) + return w, h def GetBitmap(self): return self.normalBmp @@ -70,22 +71,23 @@ class PFBaseButton(object): return self.label def GetHoverBitmap(self): - if self.hoverBmp == None: + if self.hoverBmp is None: return self.normalBmp return self.hoverBmp def GetDisabledBitmap(self): - if self.disabledBmp == None: + if self.disabledBmp is None: return self.normalBmp return self.disabledBmp def GetDropShadowBitmap(self): return self.dropShadowBmp + class PFToolbar(object): def __init__(self, parent): self.Parent = parent - self.buttons =[] + self.buttons = [] self.toolbarX = 0 self.toolbarY = 0 self.padding = 2 @@ -94,7 +96,7 @@ class PFToolbar(object): def SetPosition(self, pos): self.toolbarX, self.toolbarY = pos - def AddButton(self, btnBitmap, label = "", clickCallback = None, hoverBitmap = None, disabledBitmap = None, show = True): + def AddButton(self, btnBitmap, label="", clickCallback=None, hoverBitmap=None, disabledBitmap=None, show=True): btn = PFBaseButton(btnBitmap, label, clickCallback, hoverBitmap, disabledBitmap, show) self.buttons.append(btn) return btn @@ -115,7 +117,7 @@ class PFToolbar(object): continue state = button.GetState() - if self.HitTest( (bx, self.toolbarY), event.GetPosition(), button.GetSize()): + if self.HitTest((bx, self.toolbarY), event.GetPosition(), button.GetSize()): changeCursor = True if not state & BTN_HOVER: button.SetState(state | BTN_HOVER) @@ -135,7 +137,6 @@ class PFToolbar(object): return doRefresh def MouseClick(self, event): - mx,my = event.GetPosition() bx = self.toolbarX for button in self.buttons: if not button.IsVisible(): @@ -143,8 +144,8 @@ class PFToolbar(object): state = button.GetState() if state & BTN_PRESSED: - button.SetState(state ^ BTN_PRESSED ) - if self.HitTest( (bx, self.toolbarY), event.GetPosition(), button.GetSize()): + button.SetState(state ^ BTN_PRESSED) + if self.HitTest((bx, self.toolbarY), event.GetPosition(), button.GetSize()): return button else: return False @@ -158,7 +159,7 @@ class PFToolbar(object): state = button.GetState() - if self.HitTest( (bx, self.toolbarY), event.GetPosition(), button.GetSize()): + if self.HitTest((bx, self.toolbarY), event.GetPosition(), button.GetSize()): if event.LeftDown() or event.LeftDClick(): button.SetState(state | BTN_PRESSED) @@ -195,11 +196,12 @@ class PFToolbar(object): return height - def HitTest(self, target, position, area): + @staticmethod + def HitTest(target, position, area): x, y = target px, py = position aX, aY = area - if (px > x and px < x + aX) and (py > y and py < y + aY): + if (x < px < x + aX) and (y < py < y + aY): return True return False @@ -230,14 +232,14 @@ class PFToolbar(object): bmpWidth = bmp.GetWidth() - pdc.DrawBitmap(dropShadowBmp,bx + self.padding / 2, self.toolbarY + self.padding / 2) + pdc.DrawBitmap(dropShadowBmp, bx + self.padding / 2, self.toolbarY + self.padding / 2) pdc.DrawBitmap(bmp, tbx, by) bx += bmpWidth + self.padding class SFBrowserItem(wx.Window): - def __init__(self, parent, id = wx.ID_ANY, pos = wx.DefaultPosition, size = (0,16), style = 0): + def __init__(self, parent, id=wx.ID_ANY, pos=wx.DefaultPosition, size=(0, 16), style=0): wx.Window.__init__(self, parent, id, pos, size, style) self.highlighted = False @@ -248,7 +250,6 @@ class SFBrowserItem(wx.Window): self.toolbar = PFToolbar(self) - self.Bind(wx.EVT_PAINT, self.OnPaint) self.Bind(wx.EVT_ERASE_BACKGROUND, self.OnEraseBackground) self.Bind(wx.EVT_LEFT_UP, self.OnLeftUp) @@ -256,8 +257,7 @@ class SFBrowserItem(wx.Window): if "wxMSW" in wx.PlatformInfo: self.Bind(wx.EVT_LEFT_DCLICK, self.OnLeftDown) - - self.Bind(wx.EVT_LEFT_DOWN,self.OnLeftDown) + self.Bind(wx.EVT_LEFT_DOWN, self.OnLeftDown) self.Bind(wx.EVT_ENTER_WINDOW, self.OnEnterWindow) self.Bind(wx.EVT_LEAVE_WINDOW, self.OnLeaveWindow) self.Bind(wx.EVT_MOTION, self.OnMotion) @@ -271,7 +271,7 @@ class SFBrowserItem(wx.Window): self.RenderBackground() - mdc.DrawBitmap(self.bkBitmap, 0,0) + mdc.DrawBitmap(self.bkBitmap, 0, 0) self.DrawItem(mdc) self.toolbar.Render(mdc) @@ -291,7 +291,7 @@ class SFBrowserItem(wx.Window): def MouseMove(self, event): pass - def SetDraggable(self, mode = True): + def SetDraggable(self, mode=True): self.canBeDragged = mode def OnLeftUp(self, event): @@ -302,14 +302,13 @@ class SFBrowserItem(wx.Window): mposx, mposy = wx.GetMousePosition() rect = self.GetRect() rect.top = rect.left = 0 - cx,cy = self.ScreenToClient((mposx,mposy)) - if not rect.Contains((cx,cy)): + cx, cy = self.ScreenToClient((mposx, mposy)) + if not rect.Contains((cx, cy)): self.SetHighlighted(False) self.toolbar.ClearState() self.Refresh() return - btn = self.toolbar.MouseClick(event) if btn is not None: @@ -323,7 +322,6 @@ class SFBrowserItem(wx.Window): self.MouseLeftUp(event) - def OnLeftDown(self, event): if not self.HasCapture(): self.CaptureMouse() @@ -357,13 +355,14 @@ class SFBrowserItem(wx.Window): event.Skip() - def GetType(self): + @staticmethod + def GetType(): return -1 - def SetSelected(self, select = True): + def SetSelected(self, select=True): self.selected = select - def SetHighlighted(self, highlight = True): + def SetHighlighted(self, highlight=True): self.highlighted = highlight def GetState(self): @@ -373,7 +372,7 @@ class SFBrowserItem(wx.Window): elif self.selected: if self.highlighted: - state = SB_ITEM_SELECTED | SB_ITEM_HIGHLIGHTED + state = SB_ITEM_SELECTED | SB_ITEM_HIGHLIGHTED else: state = SB_ITEM_SELECTED else: @@ -396,7 +395,7 @@ class SFBrowserItem(wx.Window): mFactor = 0.45 eFactor = 0.30 - elif state == SB_ITEM_SELECTED | SB_ITEM_HIGHLIGHTED: + elif state == SB_ITEM_SELECTED | SB_ITEM_HIGHLIGHTED: eFactor = 0.3 elif state == SB_ITEM_SELECTED: eFactor = 0.15 @@ -405,7 +404,7 @@ class SFBrowserItem(wx.Window): if self.bkBitmap: if self.bkBitmap.eFactor == eFactor and self.bkBitmap.sFactor == sFactor and self.bkBitmap.mFactor == mFactor \ - and rect.width == self.bkBitmap.GetWidth() and rect.height == self.bkBitmap.GetHeight() : + and rect.width == self.bkBitmap.GetWidth() and rect.height == self.bkBitmap.GetHeight(): return else: del self.bkBitmap diff --git a/gui/shipBrowser.py b/gui/shipBrowser.py index e1271b616..a0b777b79 100644 --- a/gui/shipBrowser.py +++ b/gui/shipBrowser.py @@ -1,26 +1,28 @@ -# -*- coding: utf-8 -*- -import wx -import re -import copy -from gui.bitmapLoader import BitmapLoader -import gui.mainFrame -import gui.globalEvents as GE -import time -from gui.PFListPane import PFListPane +# coding: utf-8 +import re +import time + +# noinspection PyPackageRequirements +import wx +# noinspection PyPackageRequirements from wx.lib.buttons import GenBitmapButton +from service.fit import Fit +from service.market import Market +import gui.mainFrame +import gui.utils.fonts as fonts +import gui.globalEvents as GE +import gui.sfBrowserItem as SFItem import gui.utils.colorUtils as colorUtils import gui.utils.drawUtils as drawUtils import gui.utils.animUtils as animUtils import gui.utils.animEffects as animEffects - -import gui.sfBrowserItem as SFItem +from gui.PFListPane import PFListPane from gui.contextMenu import ContextMenu -import gui.utils.fonts as fonts - -import service -import gui.utils.fonts as fonts +from gui.bitmapLoader import BitmapLoader +from logbook import Logger +pyfalog = Logger(__name__) FitRenamed, EVT_FIT_RENAMED = wx.lib.newevent.NewEvent() FitSelected, EVT_FIT_SELECTED = wx.lib.newevent.NewEvent() @@ -34,21 +36,22 @@ Stage3Selected, EVT_SB_STAGE3_SEL = wx.lib.newevent.NewEvent() SearchSelected, EVT_SB_SEARCH_SEL = wx.lib.newevent.NewEvent() ImportSelected, EVT_SB_IMPORT_SEL = wx.lib.newevent.NewEvent() -class PFWidgetsContainer(PFListPane): - def __init__(self,parent): - PFListPane.__init__(self,parent) - self.anim = animUtils.LoadAnimation(self,label = "", size=(100,12)) +class PFWidgetsContainer(PFListPane): + def __init__(self, parent): + PFListPane.__init__(self, parent) + + self.anim = animUtils.LoadAnimation(self, label="", size=(100, 12)) self.anim.Stop() self.anim.Show(False) - def ShowLoading(self, mode = True): + def ShowLoading(self, mode=True): if mode: - aweight,aheight = self.anim.GetSize() - cweight,cheight = self.GetSize() - ax = (cweight - aweight)/2 - ay = (cheight - aheight)/2 - self.anim.SetPosition((ax,ay)) + aweight, aheight = self.anim.GetSize() + cweight, cheight = self.GetSize() + ax = (cweight - aweight) / 2 + ay = (cheight - aheight) / 2 + self.anim.SetPosition((ax, ay)) self.anim.Show() self.anim.Play() else: @@ -67,8 +70,9 @@ class PFWidgetsContainer(PFListPane): class RaceSelector(wx.Window): - def __init__ (self, parent, id = wx.ID_ANY, label = "", pos = wx.DefaultPosition, size = wx.DefaultSize, style = 0, layout = wx.VERTICAL, animate = False): - wx.Window.__init__(self, parent, id, pos = pos, size = size, style = style) + def __init__(self, parent, id=wx.ID_ANY, label="", pos=wx.DefaultPosition, size=wx.DefaultSize, style=0, + layout=wx.VERTICAL, animate=False): + wx.Window.__init__(self, parent, id, pos=pos, size=size, style=style) self.animTimerID = wx.NewId() self.animTimer = wx.Timer(self, self.animTimerID) @@ -91,7 +95,6 @@ class RaceSelector(wx.Window): self.SetSize(wx.Size(-1, self.minHeight)) self.SetMinSize(wx.Size(-1, self.minHeight)) - self.checkTimerID = wx.NewId() self.checkTimer = wx.Timer(self, self.checkTimerID) self.checkPeriod = 250 @@ -102,20 +105,20 @@ class RaceSelector(wx.Window): self.hoveredItem = None if layout == wx.VERTICAL: - self.buttonsBarPos = (4,0) + self.buttonsBarPos = (4, 0) else: - self.buttonsBarPos = (0,4) + self.buttonsBarPos = (0, 4) self.buttonsPadding = 4 if layout == wx.VERTICAL: - self.bmpArrow = BitmapLoader.getBitmap("down-arrow2","gui") + self.bmpArrow = BitmapLoader.getBitmap("down-arrow2", "gui") else: - self.bmpArrow = BitmapLoader.getBitmap("up-arrow2","gui") + self.bmpArrow = BitmapLoader.getBitmap("up-arrow2", "gui") - # Make the bitmaps have the same color as window text + # Make the bitmaps have the same color as window text - sysTextColour = wx.SystemSettings.GetColour( wx.SYS_COLOUR_WINDOWTEXT ) + sysTextColour = wx.SystemSettings.GetColour(wx.SYS_COLOUR_WINDOWTEXT) img = self.bmpArrow.ConvertToImage() if layout == wx.VERTICAL: @@ -140,9 +143,9 @@ class RaceSelector(wx.Window): self.Layout() def OnMouseMove(self, event): - mx,my = event.GetPosition() + mx, my = event.GetPosition() - location = self.HitTest(mx,my) + location = self.HitTest(mx, my) if location != self.hoveredItem: self.hoveredItem = location self.Refresh() @@ -151,7 +154,7 @@ class RaceSelector(wx.Window): else: self.SetCursor(wx.StockCursor(wx.CURSOR_ARROW)) - def OnSizeUpdate(self,event): + def OnSizeUpdate(self, event): self.CalcButtonsBarPos() self.Refresh() @@ -175,9 +178,9 @@ class RaceSelector(wx.Window): def OnLeftUp(self, event): - mx,my = event.GetPosition() + mx, my = event.GetPosition() - toggle = self.HitTest(mx,my) + toggle = self.HitTest(mx, my) if toggle is not None: self.Refresh() @@ -188,15 +191,15 @@ class RaceSelector(wx.Window): if stage == 2: categoryID = self.shipBrowser.GetStageData(stage) - wx.PostEvent(self.shipBrowser,Stage2Selected(categoryID=categoryID, back = True)) + wx.PostEvent(self.shipBrowser, Stage2Selected(categoryID=categoryID, back=True)) event.Skip() - def HitTest(self, mx,my): - x,y = self.buttonsBarPos + def HitTest(self, mx, my): + x, y = self.buttonsBarPos padding = self.buttonsPadding for bmp in self.raceBmps: - if (mx > x and mx < x + bmp.GetWidth()) and (my > y and my < y + bmp.GetHeight()): + if (x < mx < x + bmp.GetWidth()) and (y < my < y + bmp.GetHeight()): return self.raceBmps.index(bmp) if self.layout == wx.VERTICAL: y += bmp.GetHeight() + padding @@ -221,16 +224,15 @@ class RaceSelector(wx.Window): rect = self.GetRect() windowColor = wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOW) - bkColor = colorUtils.GetSuitableColor(windowColor, 0.1) + # bkColor = colorUtils.GetSuitableColor(windowColor, 0.1) sepColor = colorUtils.GetSuitableColor(windowColor, 0.2) mdc = wx.BufferedPaintDC(self) bkBitmap = drawUtils.RenderGradientBar(windowColor, rect.width, rect.height, 0.1, 0.1, 0.2, 2) - mdc.DrawBitmap(bkBitmap,0,0,True) + mdc.DrawBitmap(bkBitmap, 0, 0, True) - - x ,y = self.buttonsBarPos + x, y = self.buttonsBarPos if self.direction == 1: for raceBmp in self.raceBmps: @@ -249,27 +251,25 @@ class RaceSelector(wx.Window): if self.layout == wx.VERTICAL: mdc.DrawBitmap(dropShadow, rect.width - self.buttonsPadding - bmp.GetWidth() + 1, y + 1) mdc.DrawBitmap(bmp, rect.width - self.buttonsPadding - bmp.GetWidth(), y) - y+=raceBmp.GetHeight() + self.buttonsPadding - mdc.SetPen(wx.Pen(sepColor,1)) - mdc.DrawLine(rect.width - 1, 0, rect.width -1, rect.height) + y += raceBmp.GetHeight() + self.buttonsPadding + mdc.SetPen(wx.Pen(sepColor, 1)) + mdc.DrawLine(rect.width - 1, 0, rect.width - 1, rect.height) else: mdc.DrawBitmap(dropShadow, x + 1, self.buttonsPadding + 1) mdc.DrawBitmap(bmp, x, self.buttonsPadding) - x+=raceBmp.GetWidth() + self.buttonsPadding - mdc.SetPen(wx.Pen(sepColor,1)) + x += raceBmp.GetWidth() + self.buttonsPadding + mdc.SetPen(wx.Pen(sepColor, 1)) mdc.DrawLine(0, 0, rect.width, 0) if self.direction < 1: if self.layout == wx.VERTICAL: mdc.DrawBitmap(self.bmpArrow, -2, (rect.height - self.bmpArrow.GetHeight()) / 2) else: - mdc.SetPen(wx.Pen(sepColor,1)) + mdc.SetPen(wx.Pen(sepColor, 1)) mdc.DrawLine(0, 0, rect.width, 0) mdc.DrawBitmap(self.bmpArrow, (rect.width - self.bmpArrow.GetWidth()) / 2, -2) - - - def OnTimer(self,event): + def OnTimer(self, event): if event.GetId() == self.animTimerID: start = 0 if self.layout == wx.VERTICAL: @@ -297,7 +297,7 @@ class RaceSelector(wx.Window): self.animTimer.Start(self.animPeriod) def AdjustSize(self, delta): - self.SetMinSize(wx.Size(delta,-1) if self.layout == wx.VERTICAL else wx.Size(-1, delta)) + self.SetMinSize(wx.Size(delta, -1) if self.layout == wx.VERTICAL else wx.Size(-1, delta)) self.Parent.Layout() self.Refresh() @@ -326,19 +326,20 @@ class RaceSelector(wx.Window): event.Skip() + class NavigationPanel(SFItem.SFBrowserItem): - def __init__(self,parent, size = (-1, 24)): - SFItem.SFBrowserItem.__init__(self,parent,size = size) + def __init__(self, parent, size=(-1, 24)): + SFItem.SFBrowserItem.__init__(self, parent, size=size) - self.rewBmpH = BitmapLoader.getBitmap("frewind_small","gui") - self.forwBmp = BitmapLoader.getBitmap("fforward_small","gui") - self.searchBmpH = BitmapLoader.getBitmap("fsearch_small","gui") - self.newBmpH = BitmapLoader.getBitmap("fit_add_small","gui") - self.resetBmpH = BitmapLoader.getBitmap("freset_small","gui") - self.switchBmpH = BitmapLoader.getBitmap("fit_switch_view_mode_small","gui") + self.rewBmpH = BitmapLoader.getBitmap("frewind_small", "gui") + self.forwBmp = BitmapLoader.getBitmap("fforward_small", "gui") + self.searchBmpH = BitmapLoader.getBitmap("fsearch_small", "gui") + self.newBmpH = BitmapLoader.getBitmap("fit_add_small", "gui") + self.resetBmpH = BitmapLoader.getBitmap("freset_small", "gui") + self.switchBmpH = BitmapLoader.getBitmap("fit_switch_view_mode_small", "gui") - switchImg = BitmapLoader.getImage("fit_switch_view_mode_small","gui") - switchImg = switchImg.AdjustChannels(1,1,1,0.4) + switchImg = BitmapLoader.getImage("fit_switch_view_mode_small", "gui") + switchImg = switchImg.AdjustChannels(1, 1, 1, 0.4) self.switchBmpD = wx.BitmapFromImage(switchImg) self.resetBmp = self.AdjustChannels(self.resetBmpH) @@ -347,11 +348,18 @@ class NavigationPanel(SFItem.SFBrowserItem): self.switchBmp = self.AdjustChannels(self.switchBmpH) self.newBmp = self.AdjustChannels(self.newBmpH) - self.toolbar.AddButton(self.resetBmp, "Ship groups", clickCallback = self.OnHistoryReset, hoverBitmap = self.resetBmpH) - self.toolbar.AddButton(self.rewBmp, "Back", clickCallback = self.OnHistoryBack, hoverBitmap = self.rewBmpH) - self.btnNew = self.toolbar.AddButton(self.newBmp, "New fitting", clickCallback = self.OnNewFitting, hoverBitmap = self.newBmpH, show = False) - self.btnSwitch = self.toolbar.AddButton(self.switchBmpD, "Hide empty ship groups", clickCallback = self.ToggleEmptyGroupsView, hoverBitmap = self.switchBmpH, show = False) - self.toolbar.AddButton(self.searchBmp, "Search fittings", clickCallback = self.ToggleSearchBox, hoverBitmap = self.searchBmpH) + self.toolbar.AddButton(self.resetBmp, "Ship groups", clickCallback=self.OnHistoryReset, + hoverBitmap=self.resetBmpH) + self.toolbar.AddButton(self.rewBmp, "Back", clickCallback=self.OnHistoryBack, hoverBitmap=self.rewBmpH) + self.btnNew = self.toolbar.AddButton(self.newBmp, "New fitting", clickCallback=self.OnNewFitting, + hoverBitmap=self.newBmpH, show=False) + self.btnSwitch = self.toolbar.AddButton(self.switchBmpD, "Hide empty ship groups", + clickCallback=self.ToggleEmptyGroupsView, hoverBitmap=self.switchBmpH, + show=False) + + modifier = "CTRL" if 'wxMac' not in wx.PlatformInfo else "CMD" + self.toolbar.AddButton(self.searchBmp, "Search fittings ({}+F)".format(modifier), clickCallback=self.ToggleSearchBox, + hoverBitmap=self.searchBmpH) self.padding = 4 self.lastSearch = "" @@ -359,8 +367,10 @@ class NavigationPanel(SFItem.SFBrowserItem): self.inSearch = False self.fontSmall = wx.Font(fonts.SMALL, wx.SWISS, wx.NORMAL, wx.NORMAL) - w,h = size - self.BrowserSearchBox = wx.TextCtrl(self, wx.ID_ANY, "", wx.DefaultPosition, (-1, h - 2 if 'wxGTK' in wx.PlatformInfo else -1 ), wx.TE_PROCESS_ENTER | (wx.BORDER_NONE if 'wxGTK' in wx.PlatformInfo else 0)) + w, h = size + self.BrowserSearchBox = wx.TextCtrl(self, wx.ID_ANY, "", wx.DefaultPosition, + (-1, h - 2 if 'wxGTK' in wx.PlatformInfo else -1), + wx.TE_PROCESS_ENTER | (wx.BORDER_NONE if 'wxGTK' in wx.PlatformInfo else 0)) self.BrowserSearchBox.Show(False) self.BrowserSearchBox.Bind(wx.EVT_TEXT_ENTER, self.OnBrowserSearchBoxEnter) @@ -380,8 +390,7 @@ class NavigationPanel(SFItem.SFBrowserItem): realsearch = search.replace("*", "") if len(realsearch) >= 3: self.lastSearch = search - wx.PostEvent(self.shipBrowser,SearchSelected(text=search, back = False)) - + wx.PostEvent(self.shipBrowser, SearchSelected(text=search, back=False)) def ToggleSearchBox(self): if self.BrowserSearchBox.IsShown(): @@ -419,10 +428,10 @@ class NavigationPanel(SFItem.SFBrowserItem): stage = self.shipBrowser.GetActiveStage() if stage == 1: - wx.PostEvent(self.shipBrowser,Stage1Selected()) + wx.PostEvent(self.shipBrowser, Stage1Selected()) elif stage == 2: categoryID = self.shipBrowser.GetStageData(stage) - wx.PostEvent(self.shipBrowser,Stage2Selected(categoryID=categoryID, back = True)) + wx.PostEvent(self.shipBrowser, Stage2Selected(categoryID=categoryID, back=True)) def ShowNewFitButton(self, show): self.btnNew.Show(show) @@ -437,23 +446,24 @@ class NavigationPanel(SFItem.SFBrowserItem): if stage == 3: shipID = self.Parent.GetStageData(stage) shipName = self.Parent.GetStage3ShipName() - sFit = service.Fit.getInstance() - fitID = sFit.newFit(shipID, "%s fit" %shipName) + sFit = Fit.getInstance() + fitID = sFit.newFit(shipID, "%s fit" % shipName) self.shipBrowser.fitIDMustEditName = fitID - wx.PostEvent(self.Parent,Stage3Selected(shipID=shipID)) + wx.PostEvent(self.Parent, Stage3Selected(shipID=shipID)) wx.PostEvent(self.mainFrame, FitSelected(fitID=fitID)) def OnHistoryReset(self): if self.shipBrowser.browseHist: self.shipBrowser.browseHist = [] - self.gotoStage(1,0) + self.gotoStage(1, 0) def OnHistoryBack(self): if len(self.shipBrowser.browseHist) > 0: - stage,data = self.shipBrowser.browseHist.pop() + stage, data = self.shipBrowser.browseHist.pop() self.gotoStage(stage, data) - def AdjustChannels(self, bitmap): + @staticmethod + def AdjustChannels(bitmap): img = wx.ImageFromBitmap(bitmap) img = img.AdjustChannels(1.05, 1.05, 1.05, 1) return wx.BitmapFromImage(img) @@ -466,10 +476,10 @@ class NavigationPanel(SFItem.SFBrowserItem): mdc.SetFont(self.fontSmall) - wlabel,hlabel = mdc.GetTextExtent(self.toolbar.hoverLabel) + wlabel, hlabel = mdc.GetTextExtent(self.toolbar.hoverLabel) self.thoverx = self.toolbar.GetWidth() + self.padding - self.thovery = (rect.height - hlabel)/2 + self.thovery = (rect.height - hlabel) / 2 self.thoverw = wlabel self.browserBoxX = self.thoverx @@ -494,8 +504,8 @@ class NavigationPanel(SFItem.SFBrowserItem): self.toolbar.SetPosition((self.toolbarx, self.toolbary)) mdc.SetFont(self.fontSmall) mdc.DrawText(self.toolbar.hoverLabel, self.thoverx, self.thovery) - mdc.SetPen(wx.Pen(sepColor,1)) - mdc.DrawLine(0,rect.height - 1, rect.width, rect.height - 1) + mdc.SetPen(wx.Pen(sepColor, 1)) + mdc.DrawLine(0, rect.height - 1, rect.width, rect.height - 1) def RenderBackground(self): rect = self.GetRect() @@ -504,7 +514,7 @@ class NavigationPanel(SFItem.SFBrowserItem): sFactor = 0.1 - shipGroupsFilter = getattr(self.shipBrowser,"filterShipsWithNoFits", None) + shipGroupsFilter = getattr(self.shipBrowser, "filterShipsWithNoFits", None) if shipGroupsFilter: sFactor = 0.15 mFactor = 0.25 @@ -515,7 +525,7 @@ class NavigationPanel(SFItem.SFBrowserItem): if self.bkBitmap: if self.bkBitmap.eFactor == eFactor and self.bkBitmap.sFactor == sFactor and self.bkBitmap.mFactor == mFactor \ - and rect.width == self.bkBitmap.GetWidth() and rect.height == self.bkBitmap.GetHeight() : + and rect.width == self.bkBitmap.GetWidth() and rect.height == self.bkBitmap.GetHeight(): return else: del self.bkBitmap @@ -526,7 +536,6 @@ class NavigationPanel(SFItem.SFBrowserItem): self.bkBitmap.eFactor = eFactor self.bkBitmap.mFactor = mFactor - def gotoStage(self, stage, data=None): if stage == 1: wx.PostEvent(self.Parent, Stage1Selected()) @@ -545,16 +554,16 @@ class NavigationPanel(SFItem.SFBrowserItem): class ShipBrowser(wx.Panel): def __init__(self, parent): - wx.Panel.__init__ (self, parent,style = 0) + wx.Panel.__init__(self, parent, style=0) self._lastWidth = 0 self._activeStage = 1 self._lastStage = 0 self.browseHist = [] - self.lastStage = (0,0) + self.lastStage = (0, 0) self.mainFrame = gui.mainFrame.MainFrame.getInstance() - self.categoryList=[] + self.categoryList = [] self.categoryFitCache = {} self._stage1Data = -1 @@ -580,16 +589,16 @@ class ShipBrowser(wx.Panel): layout = wx.HORIZONTAL self.navpanel = NavigationPanel(self) - mainSizer.Add(self.navpanel, 0 , wx.EXPAND) - self.raceselect = RaceSelector(self, layout = layout, animate = False) + mainSizer.Add(self.navpanel, 0, wx.EXPAND) + self.raceselect = RaceSelector(self, layout=layout, animate=False) container = wx.BoxSizer(wx.VERTICAL if layout == wx.HORIZONTAL else wx.HORIZONTAL) if layout == wx.HORIZONTAL: - container.Add(self.lpane,1,wx.EXPAND) - container.Add(self.raceselect,0,wx.EXPAND) + container.Add(self.lpane, 1, wx.EXPAND) + container.Add(self.raceselect, 0, wx.EXPAND) else: - container.Add(self.raceselect,0,wx.EXPAND) - container.Add(self.lpane,1,wx.EXPAND) + container.Add(self.raceselect, 0, wx.EXPAND) + container.Add(self.lpane, 1, wx.EXPAND) mainSizer.Add(container, 1, wx.EXPAND) self.SetSizer(mainSizer) @@ -624,7 +633,6 @@ class ShipBrowser(wx.Panel): event.Skip() def SizeRefreshList(self, event): - ewidth, eheight = event.GetSize() self.Layout() self.lpane.Layout() self.lpane.RefreshList(True) @@ -666,26 +674,29 @@ class ShipBrowser(wx.Panel): self._lastStage = self._activeStage self._activeStage = 1 self.lastdata = 0 - self.browseHist = [(1,0)] + self.browseHist = [(1, 0)] self.navpanel.ShowNewFitButton(False) self.navpanel.ShowSwitchEmptyGroupsButton(False) - sMkt = service.Market.getInstance() - sFit = service.Fit.getInstance() + sMkt = Market.getInstance() + sFit = Fit.getInstance() self.lpane.ShowLoading(False) self.lpane.Freeze() self.lpane.RemoveAllChildren() + pyfalog.debug("Populate ship category list.") if len(self.categoryList) == 0: # set cache of category list self.categoryList = list(sMkt.getShipRoot()) - self.categoryList.sort(key=lambda ship: ship.name) + self.categoryList.sort(key=lambda _ship: _ship.name) # set map & cache of fittings per category for cat in self.categoryList: - self.categoryFitCache[cat.ID] = sFit.groupHasFits(cat.ID) + itemIDs = [x.ID for x in cat.items] + num = sFit.countFitsWithShip(itemIDs) + self.categoryFitCache[cat.ID] = num > 0 for ship in self.categoryList: if self.filterShipsWithNoFits and not self.categoryFitCache[ship.ID]: @@ -718,7 +729,7 @@ class ShipBrowser(wx.Panel): categoryID = self._stage2Data ships = list(data[1]) - sFit = service.Fit.getInstance() + sFit = Fit.getInstance() ships.sort(key=self.raceNameKey) racesList = [] @@ -730,7 +741,7 @@ class ShipBrowser(wx.Panel): if ship.race not in racesList: racesList.append(ship.race) - for race,state in self.racesFilter.iteritems(): + for race, state in self.racesFilter.iteritems(): if race in racesList: subRacesFilter[race] = self.racesFilter[race] @@ -743,18 +754,18 @@ class ShipBrowser(wx.Panel): for ship in ships: fits = sFit.countFitsWithShip(ship.ID) t_fits += fits - filter = subRacesFilter[ship.race] if ship.race else True + filter_ = subRacesFilter[ship.race] if ship.race else True if override: - filter = True + filter_ = True shipTrait = ship.traits.traitText if (ship.traits is not None) else "" # empty string if no traits - + if self.filterShipsWithNoFits: - if fits>0: - if filter: + if fits > 0: + if filter_: self.lpane.AddWidget(ShipItem(self.lpane, ship.ID, (ship.name, shipTrait, fits), ship.race)) else: - if filter: + if filter_: self.lpane.AddWidget(ShipItem(self.lpane, ship.ID, (ship.name, shipTrait, fits), ship.race)) self.raceselect.RebuildRaces(racesList) @@ -774,9 +785,8 @@ class ShipBrowser(wx.Panel): self.Layout() def stage2(self, event): - back = event.back - - #if not back: + # back = event.back + # if not back: # self.browseHist.append( (1,0) ) self._lastStage = self._activeStage @@ -788,8 +798,7 @@ class ShipBrowser(wx.Panel): self.lpane.RemoveAllChildren() - - sMkt = service.Market.getInstance() + sMkt = Market.getInstance() sMkt.getShipListDelayed(categoryID, self.stage2Callback) self._stage2Data = categoryID @@ -797,7 +806,8 @@ class ShipBrowser(wx.Panel): self.navpanel.ShowNewFitButton(False) self.navpanel.ShowSwitchEmptyGroupsButton(True) - def nameKey(self, info): + @staticmethod + def nameKey(info): return info[1] def stage3(self, event): @@ -819,8 +829,8 @@ class ShipBrowser(wx.Panel): self._lastStage = self._activeStage self._activeStage = 3 - sFit = service.Fit.getInstance() - sMkt = service.Market.getInstance() + sFit = Fit.getInstance() + sMkt = Market.getInstance() ship = sMkt.getItem(shipID) categoryID = ship.group.ID @@ -830,9 +840,9 @@ class ShipBrowser(wx.Panel): fitList = sFit.getFitsWithShip(shipID) if len(fitList) == 0: - stage,data = self.browseHist.pop() + stage, data = self.browseHist.pop() self.lpane.Thaw() - self.navpanel.gotoStage(stage,data) + self.navpanel.gotoStage(stage, data) return self.categoryFitCache[categoryID] = True @@ -853,7 +863,7 @@ class ShipBrowser(wx.Panel): shipTrait = ship.traits.traitText if (ship.traits is not None) else "" # empty string if no traits for ID, name, booster, timestamp in fitList: - self.lpane.AddWidget(FitItem(self.lpane, ID, (shipName, shipTrait, name, booster, timestamp),shipID)) + self.lpane.AddWidget(FitItem(self.lpane, ID, (shipName, shipTrait, name, booster, timestamp), shipID)) self.lpane.RefreshList() self.lpane.Thaw() @@ -875,8 +885,8 @@ class ShipBrowser(wx.Panel): self._lastStage = self._activeStage self._activeStage = 4 - sMkt = service.Market.getInstance() - sFit = service.Fit.getInstance() + sMkt = Market.getInstance() + sFit = Fit.getInstance() query = event.text self.lpane.Freeze() @@ -885,20 +895,22 @@ class ShipBrowser(wx.Panel): if query: ships = sMkt.searchShips(query) fitList = sFit.searchFits(query) - - for ship in ships: - shipTrait = ship.traits.traitText if (ship.traits is not None) else "" # empty string if no traits - self.lpane.AddWidget(ShipItem(self.lpane, ship.ID, (ship.name, shipTrait, len(sFit.getFitsWithShip(ship.ID))), ship.race)) - + for ship in ships: + shipTrait = ship.traits.traitText if (ship.traits is not None) else "" # empty string if no traits + + self.lpane.AddWidget( + ShipItem(self.lpane, ship.ID, (ship.name, shipTrait, len(sFit.getFitsWithShip(ship.ID))), + ship.race)) + for ID, name, shipID, shipName, booster, timestamp in fitList: ship = sMkt.getItem(shipID) shipTrait = ship.traits.traitText if (ship.traits is not None) else "" # empty string if no traits self.lpane.AddWidget(FitItem(self.lpane, ID, (shipName, shipTrait, name, booster, timestamp), shipID)) - if len(ships) == 0 and len(fitList) == 0 : - self.lpane.AddWidget(PFStaticText(self.lpane, label = u"No matching results.")) - self.lpane.RefreshList(doFocus = False) + if len(ships) == 0 and len(fitList) == 0: + self.lpane.AddWidget(PFStaticText(self.lpane, label=u"No matching results.")) + self.lpane.RefreshList(doFocus=False) self.lpane.Thaw() self.raceselect.RebuildRaces(self.RACE_ORDER) @@ -922,7 +934,7 @@ class ShipBrowser(wx.Panel): fits = event.fits # sort by ship name, then fit name - fits.sort(key=lambda fit: (fit.ship.item.name, fit.name)) + fits.sort(key=lambda _fit: (_fit.ship.item.name, _fit.name)) self.lastdata = fits self.lpane.Freeze() @@ -930,17 +942,21 @@ class ShipBrowser(wx.Panel): if fits: for fit in fits: - shipTrait = fit.ship.traits.traitText if (fit.ship.traits is not None) else "" # empty string if no traits + shipTrait = fit.ship.item.traits.traitText if (fit.ship.item.traits is not None) else "" + # empty string if no traits self.lpane.AddWidget(FitItem( self.lpane, - fit.ID, ( + fit.ID, + ( fit.ship.item.name, shipTrait, fit.name, fit.booster, - fit.timestamp), - fit.ship.item.ID)) + fit.timestamp, + ), + fit.ship.item.ID, + )) self.lpane.RefreshList(doFocus=False) self.lpane.Thaw() @@ -950,20 +966,24 @@ class ShipBrowser(wx.Panel): self.raceselect.Show(False) self.Layout() + class PFStaticText(wx.Panel): def __init__(self, parent, label=wx.EmptyString): - wx.Panel.__init__ (self, parent, id=wx.ID_ANY, pos=wx.DefaultPosition, size = parent.GetSize()) + wx.Panel.__init__(self, parent, id=wx.ID_ANY, pos=wx.DefaultPosition, size=parent.GetSize()) self.SetBackgroundColour(wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOW)) mainSizer = wx.BoxSizer(wx.VERTICAL) - text = wx.StaticText( self, wx.ID_ANY, label, wx.DefaultPosition, wx.DefaultSize, wx.ALIGN_CENTRE ) - text.Wrap( -1 ) - mainSizer.Add( text, 1, wx.ALL, 10 ) + text = wx.StaticText(self, wx.ID_ANY, label, wx.DefaultPosition, wx.DefaultSize, wx.ALIGN_CENTRE) + text.Wrap(-1) + mainSizer.Add(text, 1, wx.ALL, 10) self.SetSizer(mainSizer) self.Layout() - def GetType(self): + + @staticmethod + def GetType(): return -1 + class PFGenBitmapButton(GenBitmapButton): def __init__(self, parent, id, bitmap, pos, size, style): GenBitmapButton.__init__(self, parent, id, bitmap, pos, size, style) @@ -975,14 +995,15 @@ class PFGenBitmapButton(GenBitmapButton): def GetBackgroundBrush(self, dc): return self.bgcolor + class CategoryItem(SFItem.SFBrowserItem): - def __init__(self,parent, categoryID, fittingInfo, size = (0,16)): - SFItem.SFBrowserItem.__init__(self,parent,size = size) + def __init__(self, parent, categoryID, fittingInfo, size=(0, 16)): + SFItem.SFBrowserItem.__init__(self, parent, size=size) if categoryID: - self.shipBmp = BitmapLoader.getBitmap("ship_small","gui") + self.shipBmp = BitmapLoader.getBitmap("ship_small", "gui") else: - self.shipBmp = wx.EmptyBitmap(16,16) + self.shipBmp = wx.EmptyBitmap(16, 16) self.dropShadowBitmap = drawUtils.CreateDropShadowBitmap(self.shipBmp, 0.2) @@ -1003,31 +1024,31 @@ class CategoryItem(SFItem.SFBrowserItem): self.Bind(wx.EVT_TIMER, self.OnTimer) - #======================================================================= + # ===================================================================== # Disabled - it will be added as an option to Preferences self.animCount = 0 # self.animTimer.Start(self.animPeriod) - #======================================================================= - + # ===================================================================== def OnTimer(self, event): step = self.OUT_QUAD(self.animStep, 0, 10, self.animDuration) self.animCount = 10 - step self.animStep += self.animPeriod - if self.animStep > self.animDuration or self.animCount < 0 : + if self.animStep > self.animDuration or self.animCount < 0: self.animCount = 0 self.animTimer.Stop() self.Refresh() - def OUT_QUAD (self, t, b, c, d): - t=float(t) - b=float(b) - c=float(c) - d=float(d) + @staticmethod + def OUT_QUAD(t, b, c, d): + t = float(t) + b = float(b) + c = float(c) + d = float(d) - t/=d + t /= d - return -c *(t)*(t-2) + b + return -c * t * (t - 2) + b def GetType(self): return 1 @@ -1035,12 +1056,12 @@ class CategoryItem(SFItem.SFBrowserItem): def MouseLeftUp(self, event): categoryID = self.categoryID - wx.PostEvent(self.shipBrowser,Stage2Selected(categoryID=categoryID, back=False)) + wx.PostEvent(self.shipBrowser, Stage2Selected(categoryID=categoryID, back=False)) def UpdateElementsPos(self, mdc): rect = self.GetRect() self.shipBmpx = self.padding - self.shipBmpy = (rect.height-self.shipBmp.GetWidth())/2 + self.shipBmpy = (rect.height - self.shipBmp.GetWidth()) / 2 self.shipBmpx -= self.animCount @@ -1048,13 +1069,11 @@ class CategoryItem(SFItem.SFBrowserItem): categoryName, fittings = self.fittingInfo wtext, htext = mdc.GetTextExtent(categoryName) - self.catx = self.shipBmpx + self.shipBmp.GetWidth() + self.padding self.caty = (rect.height - htext) / 2 def DrawItem(self, mdc): - rect = self.GetRect() - + # rect = self.GetRect() self.UpdateElementsPos(mdc) windowColor = wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOW) @@ -1062,7 +1081,7 @@ class CategoryItem(SFItem.SFBrowserItem): mdc.SetTextForeground(textColor) mdc.DrawBitmap(self.dropShadowBitmap, self.shipBmpx + 1, self.shipBmpy + 1) - mdc.DrawBitmap(self.shipBmp,self.shipBmpx,self.shipBmpy,0) + mdc.DrawBitmap(self.shipBmp, self.shipBmpx, self.shipBmpy, 0) mdc.SetFont(self.fontBig) @@ -1070,7 +1089,8 @@ class CategoryItem(SFItem.SFBrowserItem): mdc.DrawText(categoryName, self.catx, self.caty) -#=============================================================================== + +# ============================================================================= # Waiting for total #fits impl in eos/service # # mdc.SetFont(wx.Font(8, wx.SWISS, wx.NORMAL, wx.NORMAL, False)) @@ -1089,14 +1109,14 @@ class CategoryItem(SFItem.SFBrowserItem): # else: # xtext, ytext = mdc.GetTextExtent(fformat) # ypos = (rect.height - ytext)/2 -#=============================================================================== +# ============================================================================= class ShipItem(SFItem.SFBrowserItem): - def __init__(self, parent, shipID=None, shipFittingInfo=("Test","TestTrait", 2), itemData=None, + def __init__(self, parent, shipID=None, shipFittingInfo=("Test", "TestTrait", 2), itemData=None, id=wx.ID_ANY, pos=wx.DefaultPosition, size=(0, 40), style=0): - SFItem.SFBrowserItem.__init__(self, parent, size = size) + SFItem.SFBrowserItem.__init__(self, parent, size=size) self.mainFrame = gui.mainFrame.MainFrame.getInstance() @@ -1123,7 +1143,7 @@ class ShipItem(SFItem.SFBrowserItem): self.newBmp = BitmapLoader.getBitmap("fit_add_small", "gui") self.acceptBmp = BitmapLoader.getBitmap("faccept_small", "gui") - self.shipEffBk = BitmapLoader.getBitmap("fshipbk_big","gui") + self.shipEffBk = BitmapLoader.getBitmap("fshipbk_big", "gui") img = wx.ImageFromBitmap(self.shipEffBk) img = img.Mirror(False) @@ -1132,7 +1152,7 @@ class ShipItem(SFItem.SFBrowserItem): self.raceBmp = BitmapLoader.getBitmap("race_%s_small" % self.shipRace, "gui") if not self.raceBmp: - self.raceBmp = BitmapLoader.getBitmap("fit_delete_small","gui") + self.raceBmp = BitmapLoader.getBitmap("fit_delete_small", "gui") self.raceDropShadowBmp = drawUtils.CreateDropShadowBitmap(self.raceBmp, 0.2) @@ -1143,11 +1163,11 @@ class ShipItem(SFItem.SFBrowserItem): self.editWidth = 150 self.padding = 4 - self.tcFitName = wx.TextCtrl(self, wx.ID_ANY, "%s fit" % self.shipName, wx.DefaultPosition, (120,-1), wx.TE_PROCESS_ENTER) + self.tcFitName = wx.TextCtrl(self, wx.ID_ANY, "%s fit" % self.shipName, wx.DefaultPosition, (120, -1), + wx.TE_PROCESS_ENTER) self.tcFitName.Show(False) - - self.newBtn = self.toolbar.AddButton(self.newBmp,"New", self.newBtnCB) + self.newBtn = self.toolbar.AddButton(self.newBmp, "New", self.newBtnCB) self.tcFitName.Bind(wx.EVT_TEXT_ENTER, self.createNewFit) self.tcFitName.Bind(wx.EVT_KILL_FOCUS, self.editLostFocus) @@ -1162,10 +1182,10 @@ class ShipItem(SFItem.SFBrowserItem): self.Bind(wx.EVT_CONTEXT_MENU, self.OnShowPopup) - self.marketInstance = service.Market.getInstance() + self.marketInstance = Market.getInstance() self.baseItem = self.marketInstance.getItem(self.shipID) - #=======================================================================\ + # ===================================================================== # DISABLED - it will be added as an option in PREFERENCES self.animCount = 0 @@ -1175,14 +1195,12 @@ class ShipItem(SFItem.SFBrowserItem): # self.animTimer.Start(self.animPeriod) # else: # self.animCount = 0 - #======================================================================= - + # ===================================================================== def OnShowPopup(self, event): pos = event.GetPosition() pos = self.ScreenToClient(pos) - contexts = [] - contexts.append(("baseShip", "Ship Basic")) + contexts = [("baseShip", "Ship Basic")] menu = ContextMenu.getMenu(self.baseItem, *contexts) self.PopupMenu(menu, pos) @@ -1190,20 +1208,21 @@ class ShipItem(SFItem.SFBrowserItem): step = self.OUT_QUAD(self.animStep, 0, 10, self.animDuration) self.animCount = 10 - step self.animStep += self.animPeriod - if self.animStep > self.animDuration or self.animCount < 0 : + if self.animStep > self.animDuration or self.animCount < 0: self.animCount = 0 self.animTimer.Stop() self.Refresh() - def OUT_QUAD (self, t, b, c, d): - t=float(t) - b=float(b) - c=float(c) - d=float(d) + @staticmethod + def OUT_QUAD(t, b, c, d): + t = float(t) + b = float(b) + c = float(c) + d = float(d) - t/=d + t /= d - return -c *(t)*(t-2) + b + return -c * t * (t - 2) + b def GetType(self): return 2 @@ -1249,10 +1268,10 @@ class ShipItem(SFItem.SFBrowserItem): def createNewFit(self, event=None): self.tcFitName.Show(False) - sFit = service.Fit.getInstance() + sFit = Fit.getInstance() fitID = sFit.newFit(self.shipID, self.tcFitName.GetValue()) - wx.PostEvent(self.shipBrowser,Stage3Selected(shipID=self.shipID, back=False)) + wx.PostEvent(self.shipBrowser, Stage3Selected(shipID=self.shipID, back=False)) wx.PostEvent(self.mainFrame, FitSelected(fitID=fitID)) def UpdateElementsPos(self, mdc): @@ -1261,20 +1280,20 @@ class ShipItem(SFItem.SFBrowserItem): self.toolbarx = rect.width - self.toolbar.GetWidth() - self.padding self.toolbary = (rect.height - self.toolbar.GetHeight()) / 2 - self.toolbarx = self.toolbarx + self.animCount + self.toolbarx += self.animCount - self.shipEffx = self.padding + (rect.height - self.shipEffBk.GetWidth())/2 - self.shipEffy = (rect.height - self.shipEffBk.GetHeight())/2 + self.shipEffx = self.padding + (rect.height - self.shipEffBk.GetWidth()) / 2 + self.shipEffy = (rect.height - self.shipEffBk.GetHeight()) / 2 - self.shipEffx = self.shipEffx - self.animCount + self.shipEffx -= self.animCount self.shipBmpx = self.padding + (rect.height - self.shipBmp.GetWidth()) / 2 self.shipBmpy = (rect.height - self.shipBmp.GetHeight()) / 2 - self.shipBmpx= self.shipBmpx - self.animCount + self.shipBmpx -= self.animCount self.raceBmpx = self.shipEffx + self.shipEffBk.GetWidth() + self.padding - self.raceBmpy = (rect.height - self.raceBmp.GetHeight())/2 + self.raceBmpy = (rect.height - self.raceBmp.GetHeight()) / 2 self.textStartx = self.raceBmpx + self.raceBmp.GetWidth() + self.padding @@ -1289,14 +1308,14 @@ class ShipItem(SFItem.SFBrowserItem): mdc.SetFont(self.fontSmall) - wlabel,hlabel = mdc.GetTextExtent(self.toolbar.hoverLabel) + wlabel, hlabel = mdc.GetTextExtent(self.toolbar.hoverLabel) self.thoverx = self.toolbarx - self.padding - wlabel - self.thovery = (rect.height - hlabel)/2 + self.thovery = (rect.height - hlabel) / 2 self.thoverw = wlabel def DrawItem(self, mdc): - rect = self.GetRect() + # rect = self.GetRect() windowColor = wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOW) textColor = colorUtils.GetSuitableColor(windowColor, 1) @@ -1317,27 +1336,27 @@ class ShipItem(SFItem.SFBrowserItem): mdc.DrawBitmap(self.shipBmp, self.shipBmpx, self.shipBmpy, 0) mdc.DrawBitmap(self.raceDropShadowBmp, self.raceBmpx + 1, self.raceBmpy + 1) - mdc.DrawBitmap(self.raceBmp,self.raceBmpx, self.raceBmpy) + mdc.DrawBitmap(self.raceBmp, self.raceBmpx, self.raceBmpy) shipName, shipTrait, fittings = self.shipFittingInfo - if fittings <1: + if fittings < 1: fformat = "No fits" + elif fittings == 1: + fformat = "%d fit" else: - if fittings == 1: - fformat = "%d fit" - else: - fformat = "%d fits" + fformat = "%d fits" mdc.SetFont(self.fontNormal) - mdc.DrawText(fformat %fittings if fittings >0 else fformat, self.textStartx, self.fittingsy) + mdc.DrawText(fformat % fittings if fittings > 0 else fformat, self.textStartx, self.fittingsy) mdc.SetFont(self.fontSmall) mdc.DrawText(self.toolbar.hoverLabel, self.thoverx, self.thovery) mdc.SetFont(self.fontBig) - psname = drawUtils.GetPartialText(mdc, shipName, self.toolbarx - self.textStartx - self.padding * 2 - self.thoverw) + psname = drawUtils.GetPartialText(mdc, shipName, + self.toolbarx - self.textStartx - self.padding * 2 - self.thoverw) mdc.DrawText(psname, self.textStartx, self.shipNamey) @@ -1348,51 +1367,50 @@ class ShipItem(SFItem.SFBrowserItem): fnEditSize = editCtl.GetSize() wSize = self.GetSize() fnEditPosX = end - fnEditPosY = (wSize.height - fnEditSize.height)/2 + fnEditPosY = (wSize.height - fnEditSize.height) / 2 if fnEditPosX < start: - editCtl.SetSize((self.editWidth + fnEditPosX - start,-1)) - editCtl.SetPosition((start,fnEditPosY)) + editCtl.SetSize((self.editWidth + fnEditPosX - start, -1)) + editCtl.SetPosition((start, fnEditPosY)) else: - editCtl.SetSize((self.editWidth,-1)) - editCtl.SetPosition((fnEditPosX,fnEditPosY)) + editCtl.SetSize((self.editWidth, -1)) + editCtl.SetPosition((fnEditPosX, fnEditPosY)) + class PFBitmapFrame(wx.Frame): - def __init__ (self, parent, pos, bitmap): - wx.Frame.__init__(self, parent, id = wx.ID_ANY, title = wx.EmptyString, pos = pos, size = wx.DefaultSize, style = - wx.NO_BORDER - | wx.FRAME_NO_TASKBAR - | wx.STAY_ON_TOP) + def __init__(self, parent, pos, bitmap): + wx.Frame.__init__(self, parent, id=wx.ID_ANY, title=wx.EmptyString, pos=pos, size=wx.DefaultSize, + style=wx.NO_BORDER | wx.FRAME_NO_TASKBAR | wx.STAY_ON_TOP) img = bitmap.ConvertToImage() img = img.ConvertToGreyscale() bitmap = wx.BitmapFromImage(img) self.bitmap = bitmap self.SetSize((bitmap.GetWidth(), bitmap.GetHeight())) - self.Bind(wx.EVT_PAINT,self.OnWindowPaint) + self.Bind(wx.EVT_PAINT, self.OnWindowPaint) self.Bind(wx.EVT_ERASE_BACKGROUND, self.OnWindowEraseBk) self.Bind(wx.EVT_TIMER, self.OnTimer) - self.timer = wx.Timer(self,wx.ID_ANY) + self.timer = wx.Timer(self, wx.ID_ANY) self.direction = 1 self.transp = 0 - self.SetSize((bitmap.GetWidth(),bitmap.GetHeight())) + self.SetSize((bitmap.GetWidth(), bitmap.GetHeight())) self.SetTransparent(0) self.Refresh() def OnTimer(self, event): - self.transp += 20*self.direction + self.transp += 20 * self.direction if self.transp > 200: self.transp = 200 self.timer.Stop() if self.transp < 0: self.transp = 0 self.timer.Stop() - wx.Frame.Show(self,False) + wx.Frame.Show(self, False) self.Destroy() return self.SetTransparent(self.transp) - def Show(self, showWnd = True): + def Show(self, showWnd=True): if showWnd: wx.Frame.Show(self, showWnd) self.Parent.SetFocus() @@ -1402,33 +1420,34 @@ class PFBitmapFrame(wx.Frame): self.direction = -1 self.timer.Start(5) - def OnWindowEraseBk(self,event): + def OnWindowEraseBk(self, event): pass - def OnWindowPaint(self,event): + def OnWindowPaint(self, event): rect = self.GetRect() canvas = wx.EmptyBitmap(rect.width, rect.height) mdc = wx.BufferedPaintDC(self) mdc.SelectObject(canvas) mdc.DrawBitmap(self.bitmap, 0, 0) - mdc.SetPen( wx.Pen("#000000", width = 1 ) ) - mdc.SetBrush( wx.TRANSPARENT_BRUSH ) - mdc.DrawRectangle( 0,0,rect.width,rect.height) + mdc.SetPen(wx.Pen("#000000", width=1)) + mdc.SetBrush(wx.TRANSPARENT_BRUSH) + mdc.DrawRectangle(0, 0, rect.width, rect.height) class FitItem(SFItem.SFBrowserItem): - def __init__(self, parent, fitID=None, shipFittingInfo=("Test", "TestTrait", "cnc's avatar", 0, 0 ), shipID = None, itemData=None, + def __init__(self, parent, fitID=None, shipFittingInfo=("Test", "TestTrait", "cnc's avatar", 0, 0), shipID=None, + itemData=None, id=wx.ID_ANY, pos=wx.DefaultPosition, size=(0, 40), style=0): - #=============================================================================== + # ===================================================================== # animCount should be 10 if we enable animation in Preferences - #=============================================================================== + # ===================================================================== self.animCount = 0 self.selectedDelta = 0 - SFItem.SFBrowserItem.__init__(self,parent,size = size) + SFItem.SFBrowserItem.__init__(self, parent, size=size) self.mainFrame = gui.mainFrame.MainFrame.getInstance() @@ -1445,10 +1464,10 @@ class FitItem(SFItem.SFBrowserItem): self.deleted = False if shipID: - self.shipBmp = BitmapLoader.getBitmap(str(shipID),"renders") + self.shipBmp = BitmapLoader.getBitmap(str(shipID), "renders") if not self.shipBmp: - self.shipBmp = BitmapLoader.getBitmap("ship_no_image_big","gui") + self.shipBmp = BitmapLoader.getBitmap("ship_no_image_big", "gui") self.shipFittingInfo = shipFittingInfo self.shipName, self.shipTrait, self.fitName, self.fitBooster, self.timestamp = shipFittingInfo @@ -1456,16 +1475,15 @@ class FitItem(SFItem.SFBrowserItem): # see GH issue #62 # Disabling this due to change in gang boosts Nov 2016 - #if self.fitBooster is None: self.fitBooster = False + # if self.fitBooster is None: self.fitBooster = False self.fitBooster = False self.boosterBmp = BitmapLoader.getBitmap("fleet_fc_small", "gui") - self.copyBmp = BitmapLoader.getBitmap("fit_add_small", "gui") - self.renameBmp = BitmapLoader.getBitmap("fit_rename_small", "gui") - self.deleteBmp = BitmapLoader.getBitmap("fit_delete_small","gui") - self.acceptBmp = BitmapLoader.getBitmap("faccept_small", "gui") - - self.shipEffBk = BitmapLoader.getBitmap("fshipbk_big","gui") + self.copyBmp = BitmapLoader.getBitmap("fit_add_small", "gui") + self.renameBmp = BitmapLoader.getBitmap("fit_rename_small", "gui") + self.deleteBmp = BitmapLoader.getBitmap("fit_delete_small", "gui") + self.acceptBmp = BitmapLoader.getBitmap("faccept_small", "gui") + self.shipEffBk = BitmapLoader.getBitmap("fshipbk_big", "gui") img = wx.ImageFromBitmap(self.shipEffBk) img = img.Mirror(False) @@ -1474,8 +1492,8 @@ class FitItem(SFItem.SFBrowserItem): self.dragTLFBmp = None self.bkBitmap = None - if self.shipTrait != "": # show no tooltip if no trait available - self.SetToolTip(wx.ToolTip(u'{}\n{}\n{}'.format(self.shipName, u'─'*20, self.shipTrait))) + if self.shipTrait != "": # show no tooltip if no trait available + self.SetToolTip(wx.ToolTip(u'{}\n{}\n{}'.format(self.shipName, u'─' * 20, self.shipTrait))) self.padding = 4 self.editWidth = 150 @@ -1491,12 +1509,13 @@ class FitItem(SFItem.SFBrowserItem): self.SetDraggable() - self.boosterBtn = self.toolbar.AddButton(self.boosterBmp,"Booster", show=self.fitBooster) - self.toolbar.AddButton(self.copyBmp,"Copy", self.copyBtnCB) - self.renameBtn = self.toolbar.AddButton(self.renameBmp,"Rename", self.renameBtnCB) + self.boosterBtn = self.toolbar.AddButton(self.boosterBmp, "Booster", show=self.fitBooster) + self.toolbar.AddButton(self.copyBmp, "Copy", self.copyBtnCB) + self.renameBtn = self.toolbar.AddButton(self.renameBmp, "Rename", self.renameBtnCB) self.toolbar.AddButton(self.deleteBmp, "Delete", self.deleteBtnCB) - self.tcFitName = wx.TextCtrl(self, wx.ID_ANY, "%s" % self.fitName, wx.DefaultPosition, (self.editWidth,-1), wx.TE_PROCESS_ENTER) + self.tcFitName = wx.TextCtrl(self, wx.ID_ANY, "%s" % self.fitName, wx.DefaultPosition, (self.editWidth, -1), + wx.TE_PROCESS_ENTER) if self.shipBrowser.fitIDMustEditName != self.fitID: self.tcFitName.Show(False) @@ -1521,24 +1540,24 @@ class FitItem(SFItem.SFBrowserItem): self.Bind(wx.EVT_TIMER, self.OnTimer) - #======================================================================= + # ===================================================================== # DISABLED - it will be added as an option in PREFERENCES # if self.shipBrowser.GetActiveStage() != 4 and self.shipBrowser.GetLastStage() !=3: # self.animTimer.Start(self.animPeriod) # else: # self.animCount = 0 - #======================================================================= + # ===================================================================== self.selTimerID = wx.NewId() - self.selTimer = wx.Timer(self,self.selTimerID) + self.selTimer = wx.Timer(self, self.selTimerID) self.selTimer.Start(100) self.Bind(wx.EVT_RIGHT_UP, self.OnContextMenu) def OnToggleBooster(self, event): - sFit = service.Fit.getInstance() + sFit = Fit.getInstance() sFit.toggleBoostFit(self.fitID) self.fitBooster = not self.fitBooster self.boosterBtn.Show(self.fitBooster) @@ -1549,7 +1568,7 @@ class FitItem(SFItem.SFBrowserItem): def OnProjectToFit(self, event): activeFit = self.mainFrame.getActiveFit() if activeFit: - sFit = service.Fit.getInstance() + sFit = Fit.getInstance() projectedFit = sFit.getFit(self.fitID) sFit.project(activeFit, projectedFit) wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=activeFit)) @@ -1558,14 +1577,14 @@ class FitItem(SFItem.SFBrowserItem): def OnAddCommandFit(self, event): activeFit = self.mainFrame.getActiveFit() if activeFit: - sFit = service.Fit.getInstance() + sFit = Fit.getInstance() commandFit = sFit.getFit(self.fitID) sFit.addCommandFit(activeFit, commandFit) wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=activeFit)) self.mainFrame.additionsPane.select("Command") def OnMouseCaptureLost(self, event): - ''' Destroy drag information (GH issue #479)''' + """ Destroy drag information (GH issue #479)""" if self.dragging and self.dragged: self.dragging = False self.dragged = False @@ -1575,8 +1594,8 @@ class FitItem(SFItem.SFBrowserItem): self.dragWindow = None def OnContextMenu(self, event): - ''' Handles context menu for fit. Dragging is handled by MouseLeftUp() ''' - sFit = service.Fit.getInstance() + """ Handles context menu for fit. Dragging is handled by MouseLeftUp() """ + sFit = Fit.getInstance() fit = sFit.getFit(self.mainFrame.getActiveFit()) if not fit: @@ -1604,7 +1623,7 @@ class FitItem(SFItem.SFBrowserItem): self.Bind(wx.EVT_MENU, self.OnProjectToFit, projectedItem) commandItem = menu.Append(wx.ID_ANY, "Add Command Booster") - self.Bind(wx.EVT_MENU, self.OnAddCommandFit, commandItem ) + self.Bind(wx.EVT_MENU, self.OnAddCommandFit, commandItem) self.PopupMenu(menu, pos) @@ -1620,7 +1639,7 @@ class FitItem(SFItem.SFBrowserItem): interval = 5 if ctimestamp < self.timestamp + interval: delta = (ctimestamp - self.timestamp) / interval - self.selectedDelta = self.CalculateDelta(0x0,self.maxDelta,delta) + self.selectedDelta = self.CalculateDelta(0x0, self.maxDelta, delta) self.Refresh() else: self.selectedDelta = self.maxDelta @@ -1630,23 +1649,25 @@ class FitItem(SFItem.SFBrowserItem): step = self.OUT_QUAD(self.animStep, 0, 10, self.animDuration) self.animCount = 10 - step self.animStep += self.animPeriod - if self.animStep > self.animDuration or self.animCount < 0 : + if self.animStep > self.animDuration or self.animCount < 0: self.animCount = 0 self.animTimer.Stop() self.Refresh() - def CalculateDelta(self, start, end, delta): - return start + (end-start)*delta + @staticmethod + def CalculateDelta(start, end, delta): + return start + (end - start) * delta - def OUT_QUAD (self, t, b, c, d): - t=float(t) - b=float(b) - c=float(c) - d=float(d) + @staticmethod + def OUT_QUAD(t, b, c, d): + t = float(t) + b = float(b) + c = float(c) + d = float(d) - t/=d + t /= d - return -c *(t)*(t-2) + b + return -c * t * (t - 2) + b def editLostFocus(self, event): self.RestoreEditButton() @@ -1666,10 +1687,10 @@ class FitItem(SFItem.SFBrowserItem): self.copyFit() def copyFit(self, event=None): - sFit = service.Fit.getInstance() + sFit = Fit.getInstance() fitID = sFit.copyFit(self.fitID) self.shipBrowser.fitIDMustEditName = fitID - wx.PostEvent(self.shipBrowser,Stage3Selected(shipID=self.shipID)) + wx.PostEvent(self.shipBrowser, Stage3Selected(shipID=self.shipID)) wx.PostEvent(self.mainFrame, FitSelected(fitID=fitID)) def renameBtnCB(self): @@ -1686,7 +1707,7 @@ class FitItem(SFItem.SFBrowserItem): self.Refresh() def renameFit(self, event=None): - sFit = service.Fit.getInstance() + sFit = Fit.getInstance() self.tcFitName.Show(False) self.editWasShown = 0 fitName = self.tcFitName.GetValue() @@ -1707,9 +1728,12 @@ class FitItem(SFItem.SFBrowserItem): if wx.GetMouseState().ShiftDown() or wx.GetMouseState().MiddleDown(): self.deleteFit() else: - dlg = wx.MessageDialog(self, - "Do you really want to delete this fit?", - "Confirm Delete", wx.YES | wx.NO | wx.ICON_QUESTION) + dlg = wx.MessageDialog( + self, + "Do you really want to delete this fit?", + "Confirm Delete", + wx.YES | wx.NO | wx.ICON_QUESTION + ) if dlg.ShowModal() == wx.ID_YES: self.deleteFit() @@ -1720,14 +1744,14 @@ class FitItem(SFItem.SFBrowserItem): else: self.deleted = True - sFit = service.Fit.getInstance() + sFit = Fit.getInstance() fit = sFit.getFit(self.fitID) sFit.deleteFit(self.fitID) if self.shipBrowser.GetActiveStage() == 5: - if fit in self.shipBrowser.lastdata: # remove fit from import cache - self.shipBrowser.lastdata.remove(fit) + if fit in self.shipBrowser.lastdata: # remove fit from import cache + self.shipBrowser.lastdata.remove(fit) wx.PostEvent(self.shipBrowser, ImportSelected(fits=self.shipBrowser.lastdata)) elif self.shipBrowser.GetActiveStage() == 4: wx.PostEvent(self.shipBrowser, SearchSelected(text=self.shipBrowser.navpanel.lastSearch, back=True)) @@ -1792,9 +1816,9 @@ class FitItem(SFItem.SFBrowserItem): wx.PostEvent(self.mainFrame, FitSelected(fitID=self.fitID)) def RestoreEditButton(self): - self.tcFitName.Show(False) - self.renameBtn.SetBitmap(self.renameBmp) - self.Refresh() + self.tcFitName.Show(False) + self.renameBtn.SetBitmap(self.renameBmp) + self.Refresh() def UpdateElementsPos(self, mdc): rect = self.GetRect() @@ -1802,17 +1826,17 @@ class FitItem(SFItem.SFBrowserItem): self.toolbarx = rect.width - self.toolbar.GetWidth() - self.padding self.toolbary = (rect.height - self.toolbar.GetHeight()) / 2 - self.toolbarx = self.toolbarx + self.animCount + self.toolbarx += self.animCount - self.shipEffx = self.padding + (rect.height - self.shipEffBk.GetWidth())/2 - self.shipEffy = (rect.height - self.shipEffBk.GetHeight())/2 + self.shipEffx = self.padding + (rect.height - self.shipEffBk.GetWidth()) / 2 + self.shipEffy = (rect.height - self.shipEffBk.GetHeight()) / 2 - self.shipEffx = self.shipEffx - self.animCount + self.shipEffx -= self.animCount self.shipBmpx = self.padding + (rect.height - self.shipBmp.GetWidth()) / 2 self.shipBmpy = (rect.height - self.shipBmp.GetHeight()) / 2 - self.shipBmpx= self.shipBmpx - self.animCount + self.shipBmpx -= self.animCount self.textStartx = self.shipEffx + self.shipEffBk.GetWidth() + self.padding @@ -1825,10 +1849,10 @@ class FitItem(SFItem.SFBrowserItem): mdc.SetFont(self.fontSmall) - wlabel,hlabel = mdc.GetTextExtent(self.toolbar.hoverLabel) + wlabel, hlabel = mdc.GetTextExtent(self.toolbar.hoverLabel) self.thoverx = self.toolbarx - self.padding - wlabel - self.thovery = (rect.height - hlabel)/2 + self.thovery = (rect.height - hlabel) / 2 self.thoverw = wlabel def DrawItem(self, mdc): @@ -1852,13 +1876,12 @@ class FitItem(SFItem.SFBrowserItem): mdc.DrawBitmap(self.shipBmp, self.shipBmpx, self.shipBmpy, 0) - shipName, shipTrait, fittings, booster, timestamp = self.shipFittingInfo - mdc.SetFont(self.fontNormal) fitDate = time.localtime(self.timestamp) - fitLocalDate = "%d/%02d/%02d %02d:%02d" % ( fitDate[0], fitDate[1], fitDate[2], fitDate[3], fitDate[4]) - pfdate = drawUtils.GetPartialText(mdc, fitLocalDate, self.toolbarx - self.textStartx - self.padding * 2 - self.thoverw) + fitLocalDate = "%d/%02d/%02d %02d:%02d" % (fitDate[0], fitDate[1], fitDate[2], fitDate[3], fitDate[4]) + pfdate = drawUtils.GetPartialText(mdc, fitLocalDate, + self.toolbarx - self.textStartx - self.padding * 2 - self.thoverw) mdc.DrawText(pfdate, self.textStartx, self.timestampy) @@ -1867,7 +1890,8 @@ class FitItem(SFItem.SFBrowserItem): mdc.SetFont(self.fontBig) - psname = drawUtils.GetPartialText(mdc, self.fitName, self.toolbarx - self.textStartx - self.padding * 2 - self.thoverw) + psname = drawUtils.GetPartialText(mdc, self.fitName, + self.toolbarx - self.textStartx - self.padding * 2 - self.thoverw) mdc.DrawText(psname, self.textStartx, self.fitNamey) @@ -1884,13 +1908,13 @@ class FitItem(SFItem.SFBrowserItem): fnEditSize = editCtl.GetSize() wSize = self.GetSize() fnEditPosX = end - fnEditPosY = (wSize.height - fnEditSize.height)/2 + fnEditPosY = (wSize.height - fnEditSize.height) / 2 if fnEditPosX < start: - editCtl.SetSize((self.editWidth + fnEditPosX - start,-1)) - editCtl.SetPosition((start,fnEditPosY)) + editCtl.SetSize((self.editWidth + fnEditPosX - start, -1)) + editCtl.SetPosition((start, fnEditPosY)) else: - editCtl.SetSize((self.editWidth,-1)) - editCtl.SetPosition((fnEditPosX,fnEditPosY)) + editCtl.SetSize((self.editWidth, -1)) + editCtl.SetPosition((fnEditPosX, fnEditPosY)) def GetState(self): activeFitID = self.mainFrame.getActiveFit() @@ -1901,7 +1925,7 @@ class FitItem(SFItem.SFBrowserItem): else: if activeFitID == self.fitID: if self.highlighted: - state = SFItem.SB_ITEM_SELECTED | SFItem.SB_ITEM_HIGHLIGHTED + state = SFItem.SB_ITEM_SELECTED | SFItem.SB_ITEM_HIGHLIGHTED else: state = SFItem.SB_ITEM_SELECTED else: @@ -1913,8 +1937,7 @@ class FitItem(SFItem.SFBrowserItem): windowColor = wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOW) - activeFitID = self.mainFrame.getActiveFit() - + # activeFitID = self.mainFrame.getActiveFit() state = self.GetState() sFactor = 0.2 @@ -1925,18 +1948,18 @@ class FitItem(SFItem.SFBrowserItem): mFactor = 0.45 eFactor = 0.30 - elif state == SFItem.SB_ITEM_SELECTED | SFItem.SB_ITEM_HIGHLIGHTED: + elif state == SFItem.SB_ITEM_SELECTED | SFItem.SB_ITEM_HIGHLIGHTED: eFactor = 0.3 mFactor = 0.4 elif state == SFItem.SB_ITEM_SELECTED: - eFactor = (self.maxDelta - self.selectedDelta)/100 + 0.25 + eFactor = (self.maxDelta - self.selectedDelta) / 100 + 0.25 else: sFactor = 0 if self.bkBitmap: if self.bkBitmap.eFactor == eFactor and self.bkBitmap.sFactor == sFactor and self.bkBitmap.mFactor == mFactor \ - and rect.width == self.bkBitmap.GetWidth() and rect.height == self.bkBitmap.GetHeight() : + and rect.width == self.bkBitmap.GetWidth() and rect.height == self.bkBitmap.GetHeight(): return else: del self.bkBitmap @@ -1946,4 +1969,3 @@ class FitItem(SFItem.SFBrowserItem): self.bkBitmap.sFactor = sFactor self.bkBitmap.eFactor = eFactor self.bkBitmap.mFactor = mFactor - diff --git a/gui/statsPane.py b/gui/statsPane.py index 6c378878c..6aaf277f5 100644 --- a/gui/statsPane.py +++ b/gui/statsPane.py @@ -1,4 +1,4 @@ -#=============================================================================== +# ============================================================================= # Copyright (C) 2010 Diego Duclos # # This file is part of pyfa. @@ -15,25 +15,28 @@ # # You should have received a copy of the GNU General Public License # along with pyfa. If not, see . -#=============================================================================== +# ============================================================================= +# noinspection PyPackageRequirements import wx -from gui.statsView import StatsView -import service -from gui.pyfatogglepanel import TogglePanel -import gui.builtinStatsViews -from gui.contextMenu import ContextMenu -#import gui.builtinViews.fittingView as fv + +from service.fit import Fit import gui.mainFrame +import gui.builtinStatsViews import gui.globalEvents as GE +# import gui.builtinViews.fittingView as fv +from gui.statsView import StatsView +from gui.contextMenu import ContextMenu +from gui.pyfatogglepanel import TogglePanel + class StatsPane(wx.Panel): - DEFAULT_VIEWS = ["resourcesViewFull", "resistancesViewFull" ,"rechargeViewFull", "firepowerViewFull", + DEFAULT_VIEWS = ["resourcesViewFull", "resistancesViewFull", "rechargeViewFull", "firepowerViewFull", "capacitorViewFull", "targetingmiscViewFull", - "priceViewFull",] + "priceViewFull"] def fitChanged(self, event): - sFit = service.Fit.getInstance() + sFit = Fit.getInstance() fit = sFit.getFit(event.fitID) for view in self.views: view.refreshPanel(fit) @@ -44,7 +47,7 @@ class StatsPane(wx.Panel): # Use 25% smaller fonts if MAC or force font size to 8 for msw/linux - if "__WXMAC__" in wx.PlatformInfo : + if "__WXMAC__" in wx.PlatformInfo: self.SetWindowVariant(wx.WINDOW_VARIANT_SMALL) else: standardFont = wx.SystemSettings.GetFont(wx.SYS_DEFAULT_GUI_FONT) @@ -57,7 +60,7 @@ class StatsPane(wx.Panel): self.views = [] self.nameViewMap = {} maxviews = len(self.DEFAULT_VIEWS) - i=0 + i = 0 for viewName in self.DEFAULT_VIEWS: tp = TogglePanel(self) contentPanel = tp.GetContentPane() @@ -79,19 +82,21 @@ class StatsPane(wx.Panel): mainSizer.Add(tp, 0, wx.EXPAND | wx.LEFT, 3) if i < maxviews - 1: - mainSizer.Add(wx.StaticLine(self, wx.ID_ANY, style=wx.HORIZONTAL), 0, wx.EXPAND | wx.TOP | wx.LEFT | wx.RIGHT, 2) - i+=1 + mainSizer.Add(wx.StaticLine(self, wx.ID_ANY, style=wx.HORIZONTAL), 0, + wx.EXPAND | wx.TOP | wx.LEFT | wx.RIGHT, 2) + i += 1 tp.OnStateChange(tp.GetBestSize()) - width,height = self.GetSize() - self.SetMinSize((width+9,-1)) - + width, height = self.GetSize() + self.SetMinSize((width + 9, -1)) self.mainFrame = gui.mainFrame.MainFrame.getInstance() self.mainFrame.Bind(GE.FIT_CHANGED, self.fitChanged) - def contextHandler(self, contentPanel): + @staticmethod + def contextHandler(contentPanel): viewName = contentPanel.viewName + def handler(event): menu = ContextMenu.getMenu(None, (viewName,)) if menu is not None: diff --git a/gui/statsView.py b/gui/statsView.py index 33e2a74e9..773063ca4 100644 --- a/gui/statsView.py +++ b/gui/statsView.py @@ -1,4 +1,4 @@ -#=============================================================================== +# ============================================================================= # Copyright (C) 2010 Diego Duclos # # This file is part of pyfa. @@ -15,10 +15,12 @@ # # You should have received a copy of the GNU General Public License # along with pyfa. If not, see . -#=============================================================================== +# ============================================================================= + class StatsView(object): views = {} + def __init__(self): pass @@ -39,4 +41,15 @@ class StatsView(object): def refreshPanel(self, fit): raise NotImplementedError() -from gui.builtinStatsViews import * \ No newline at end of file + +# noinspection PyUnresolvedReferences +from gui.builtinStatsViews import ( # noqa: E402, F401 + resourcesViewFull, + resistancesViewFull, + firepowerViewFull, + miningyieldViewFull, + capacitorViewFull, + rechargeViewFull, + targetingMiscViewFull, + priceViewFull, +) diff --git a/gui/updateDialog.py b/gui/updateDialog.py index 0febdf3f8..019901819 100644 --- a/gui/updateDialog.py +++ b/gui/updateDialog.py @@ -1,4 +1,4 @@ -#=============================================================================== +# ============================================================================= # Copyright (C) 2010 Diego Duclos # # This file is part of pyfa. @@ -15,87 +15,97 @@ # # You should have received a copy of the GNU General Public License # along with pyfa. If not, see . -#=============================================================================== +# ============================================================================= +# noinspection PyPackageRequirements import wx -from gui.bitmapLoader import BitmapLoader -import config -import service +# noinspection PyPackageRequirements import dateutil.parser +from service.settings import UpdateSettings as svc_UpdateSettings + class UpdateDialog(wx.Dialog): - def __init__(self, parent, release): - wx.Dialog.__init__ ( self, parent, id = wx.ID_ANY, title = "Pyfa Update", pos = wx.DefaultPosition, size = wx.Size( 400,300 ), style = wx.DEFAULT_DIALOG_STYLE ) + wx.Dialog.__init__(self, parent, id=wx.ID_ANY, title="Pyfa Update", pos=wx.DefaultPosition, + size=wx.Size(400, 300), style=wx.DEFAULT_DIALOG_STYLE) - self.UpdateSettings = service.settings.UpdateSettings.getInstance() + self.UpdateSettings = svc_UpdateSettings.getInstance() self.releaseInfo = release - self.SetSizeHintsSz( wx.DefaultSize, wx.DefaultSize ) + self.SetSizeHintsSz(wx.DefaultSize, wx.DefaultSize) - mainSizer = wx.BoxSizer( wx.VERTICAL ) + mainSizer = wx.BoxSizer(wx.VERTICAL) - headSizer = wx.BoxSizer( wx.HORIZONTAL ) + headSizer = wx.BoxSizer(wx.HORIZONTAL) - self.headingText = wx.StaticText( self, wx.ID_ANY, "Pyfa Update Available!", wx.DefaultPosition, wx.DefaultSize, wx.ALIGN_CENTRE ) - self.headingText.Wrap( -1 ) - self.headingText.SetFont( wx.Font( 14, 74, 90, 92, False) ) + self.headingText = wx.StaticText(self, wx.ID_ANY, "Pyfa Update Available!", wx.DefaultPosition, wx.DefaultSize, + wx.ALIGN_CENTRE) + self.headingText.Wrap(-1) + self.headingText.SetFont(wx.Font(14, 74, 90, 92, False)) - headSizer.Add( self.headingText, 1, wx.ALL, 5 ) - mainSizer.Add( headSizer, 0, wx.EXPAND, 5 ) + headSizer.Add(self.headingText, 1, wx.ALL, 5) + mainSizer.Add(headSizer, 0, wx.EXPAND, 5) - mainSizer.Add( wx.StaticLine( self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LI_HORIZONTAL ), 0, wx.EXPAND |wx.ALL, 5 ) + mainSizer.Add(wx.StaticLine(self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LI_HORIZONTAL), 0, + wx.EXPAND | wx.ALL, 5) - versionSizer = wx.BoxSizer( wx.HORIZONTAL ) + versionSizer = wx.BoxSizer(wx.HORIZONTAL) - if(self.releaseInfo['prerelease']): - self.releaseText = wx.StaticText( self, wx.ID_ANY, "Pre-release", wx.DefaultPosition, wx.DefaultSize, wx.ALIGN_RIGHT ) - self.releaseText.SetFont( wx.Font( 12, 74, 90, 92, False) ) - self.releaseText.SetForegroundColour( wx.Colour( 230, 0, 0 ) ) + if self.releaseInfo['prerelease']: + self.releaseText = wx.StaticText(self, wx.ID_ANY, "Pre-release", wx.DefaultPosition, wx.DefaultSize, + wx.ALIGN_RIGHT) + self.releaseText.SetFont(wx.Font(12, 74, 90, 92, False)) + self.releaseText.SetForegroundColour(wx.Colour(230, 0, 0)) else: - self.releaseText = wx.StaticText( self, wx.ID_ANY, "Stable", wx.DefaultPosition, wx.DefaultSize, wx.ALIGN_RIGHT ) - self.releaseText.SetFont( wx.Font( 12, 74, 90, 90, False) ) + self.releaseText = wx.StaticText(self, wx.ID_ANY, "Stable", wx.DefaultPosition, wx.DefaultSize, + wx.ALIGN_RIGHT) + self.releaseText.SetFont(wx.Font(12, 74, 90, 90, False)) - self.releaseText.Wrap( -1 ) + self.releaseText.Wrap(-1) - versionSizer.Add( self.releaseText, 1, wx.ALL, 5 ) + versionSizer.Add(self.releaseText, 1, wx.ALL, 5) - self.versionText = wx.StaticText( self, wx.ID_ANY, self.releaseInfo['tag_name'], wx.DefaultPosition, wx.DefaultSize, wx.ALIGN_LEFT ) - self.versionText.Wrap( -1 ) - self.versionText.SetFont( wx.Font( 12, 74, 90, 90, False) ) + self.versionText = wx.StaticText(self, wx.ID_ANY, self.releaseInfo['tag_name'], wx.DefaultPosition, + wx.DefaultSize, wx.ALIGN_LEFT) + self.versionText.Wrap(-1) + self.versionText.SetFont(wx.Font(12, 74, 90, 90, False)) - versionSizer.Add( self.versionText, 1, wx.ALL, 5 ) - versionSizer.AddSpacer( ( 15, 5), 0, wx.EXPAND, 5 ) + versionSizer.Add(self.versionText, 1, wx.ALL, 5) + versionSizer.AddSpacer((15, 5), 0, wx.EXPAND, 5) - mainSizer.Add( versionSizer, 0, wx.EXPAND, 5 ) - mainSizer.AddSpacer( ( 0, 5), 0, wx.EXPAND, 5 ) + mainSizer.Add(versionSizer, 0, wx.EXPAND, 5) + mainSizer.AddSpacer((0, 5), 0, wx.EXPAND, 5) releaseDate = dateutil.parser.parse(self.releaseInfo['published_at']) - notesSizer = wx.BoxSizer( wx.HORIZONTAL ) - self.notesTextCtrl = wx.TextCtrl( self, wx.ID_ANY, str(releaseDate.date())+":\n\n"+self.releaseInfo['body'], wx.DefaultPosition, wx.DefaultSize, wx.TE_AUTO_URL|wx.TE_MULTILINE|wx.TE_READONLY|wx.DOUBLE_BORDER|wx.TRANSPARENT_WINDOW ) + notesSizer = wx.BoxSizer(wx.HORIZONTAL) + self.notesTextCtrl = wx.TextCtrl(self, wx.ID_ANY, str(releaseDate.date()) + ":\n\n" + self.releaseInfo['body'], + wx.DefaultPosition, wx.DefaultSize, + wx.TE_AUTO_URL | wx.TE_MULTILINE | wx.TE_READONLY | wx.DOUBLE_BORDER | wx.TRANSPARENT_WINDOW) - notesSizer.Add( self.notesTextCtrl, 1, wx.EXPAND|wx.LEFT|wx.RIGHT, 5 ) - mainSizer.Add( notesSizer, 1, wx.EXPAND, 5 ) + notesSizer.Add(self.notesTextCtrl, 1, wx.EXPAND | wx.LEFT | wx.RIGHT, 5) + mainSizer.Add(notesSizer, 1, wx.EXPAND, 5) - self.supressCheckbox = wx.CheckBox( self, wx.ID_ANY, "Don't remind me again for this release", wx.DefaultPosition, wx.DefaultSize, 0 ) + self.supressCheckbox = wx.CheckBox(self, wx.ID_ANY, "Don't remind me again for this release", + wx.DefaultPosition, wx.DefaultSize, 0) self.supressCheckbox.Bind(wx.EVT_CHECKBOX, self.SuppressChange) - mainSizer.Add( self.supressCheckbox, 0, wx.ALL, 5 ) - mainSizer.Add( wx.StaticLine( self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LI_HORIZONTAL ), 0, wx.EXPAND |wx.ALL, 5 ) + mainSizer.Add(self.supressCheckbox, 0, wx.ALL, 5) + mainSizer.Add(wx.StaticLine(self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LI_HORIZONTAL), 0, + wx.EXPAND | wx.ALL, 5) - actionSizer = wx.BoxSizer( wx.HORIZONTAL ) + actionSizer = wx.BoxSizer(wx.HORIZONTAL) - goSizer = wx.BoxSizer( wx.VERTICAL ) - self.downloadButton = wx.Button( self, wx.ID_ANY, "Download", wx.DefaultPosition, wx.DefaultSize, 0 ) + goSizer = wx.BoxSizer(wx.VERTICAL) + self.downloadButton = wx.Button(self, wx.ID_ANY, "Download", wx.DefaultPosition, wx.DefaultSize, 0) self.downloadButton.Bind(wx.EVT_BUTTON, self.OnDownload) - goSizer.Add( self.downloadButton, 0, wx.ALL, 5 ) - actionSizer.Add( goSizer, 1, wx.EXPAND, 5 ) + goSizer.Add(self.downloadButton, 0, wx.ALL, 5) + actionSizer.Add(goSizer, 1, wx.EXPAND, 5) self.closeButton = wx.Button(self, wx.ID_CLOSE) self.closeButton.Bind(wx.EVT_BUTTON, self.OnClose) - actionSizer.Add( self.closeButton, 0, wx.ALL, 5 ) - mainSizer.Add( actionSizer, 0, wx.EXPAND, 5 ) + actionSizer.Add(self.closeButton, 0, wx.ALL, 5) + mainSizer.Add(actionSizer, 0, wx.EXPAND, 5) - self.SetSizer( mainSizer ) + self.SetSizer(mainSizer) self.Layout() # Handle use-case of suppressing a release, then a new version becoming available. @@ -104,17 +114,17 @@ class UpdateDialog(wx.Dialog): # safely reset this setting self.UpdateSettings.set('version', None) - self.Centre( wx.BOTH ) + self.Centre(wx.BOTH) def OnClose(self, e): self.Close() def SuppressChange(self, e): - if (self.supressCheckbox.IsChecked()): + if self.supressCheckbox.IsChecked(): self.UpdateSettings.set('version', self.releaseInfo['tag_name']) else: self.UpdateSettings.set('version', None) def OnDownload(self, e): - wx.LaunchDefaultBrowser('https://github.com/pyfa-org/Pyfa/releases/tag/'+self.releaseInfo['tag_name']) + wx.LaunchDefaultBrowser('https://github.com/pyfa-org/Pyfa/releases/tag/' + self.releaseInfo['tag_name']) self.OnClose(e) diff --git a/gui/utils/animEffects.py b/gui/utils/animEffects.py index 519fe60c4..beb1b30aa 100644 --- a/gui/utils/animEffects.py +++ b/gui/utils/animEffects.py @@ -1,83 +1,97 @@ - import math -def OUT_CIRC (t, b, c, d): - t=float(t) - b=float(b) - c=float(c) - d=float(d) - t = t/d -1 - return c * math.sqrt(1 - t*t) + b; + +def OUT_CIRC(t, b, c, d): + t = float(t) + b = float(b) + c = float(c) + d = float(d) + + t = t / d - 1 + + return c * math.sqrt(1 - t * t) + b + def OUT_QUART(t, b, c, d): - t=float(t) - b=float(b) - c=float(c) - d=float(d) - t = t/d -1 - return -c * ((t)*t*t*t - 1) + b; + t = float(t) + b = float(b) + c = float(c) + d = float(d) + + t = t / d - 1 + + return -c * (t * t * t * t - 1) + b + def INOUT_CIRC(t, b, c, d): - t=float(t) - b=float(b) - c=float(c) - d=float(d) + t = float(t) + b = float(b) + c = float(c) + d = float(d) t1 = t / (d / 2) - if ((t / (d/2)) < 1): - return -c/2 * (math.sqrt(1 - (t/(d/2))**2) - 1) + b - return c/2 * (math.sqrt(1 - (t1-2)**2) + 1) + b; - -def IN_CUBIC (t, b, c, d): - t=float(t) - b=float(b) - c=float(c) - d=float(d) - t = t/d - return c*t*t*t + b - -def OUT_QUAD (t, b, c, d): - t=float(t) - b=float(b) - c=float(c) - d=float(d) - - t/=d - - return -c *(t)*(t-2) + b - -def OUT_BOUNCE (t, b, c, d): - t=float(t) - b=float(b) - c=float(c) - d=float(d) - - t/=d - - if ((t) < (1/2.75)): - return c*(7.5625*t*t) + b + if (t / (d / 2)) < 1: + return -c / 2 * (math.sqrt(1 - (t / (d / 2)) ** 2) - 1) + b else: - if (t < (2/2.75)): - t-=(1.5/2.75) - return c*(7.5625*t*t + .75) + b - else: - if (t < (2.5/2.75)): - t-=(2.25/2.75) - return c*(7.5625*(t)*t + .9375) + b - else: - t-=(2.625/2.75) - return c*(7.5625*(t)*t + .984375) + b + return c / 2 * (math.sqrt(1 - (t1 - 2) ** 2) + 1) + b + + +def IN_CUBIC(t, b, c, d): + t = float(t) + b = float(b) + c = float(c) + d = float(d) + + t /= d + + return c * t * t * t + b + + +def OUT_QUAD(t, b, c, d): + t = float(t) + b = float(b) + c = float(c) + d = float(d) + + t /= d + + return -c * t * (t - 2) + b + + +def OUT_BOUNCE(t, b, c, d): + t = float(t) + b = float(b) + c = float(c) + d = float(d) + + t /= d + + if t < (1 / 2.75): + return c * (7.5625 * t * t) + b + elif t < (2 / 2.75): + t -= (1.5 / 2.75) + return c * (7.5625 * t * t + .75) + b + elif t < (2.5 / 2.75): + t -= (2.25 / 2.75) + return c * (7.5625 * t * t + .9375) + b + else: + t -= (2.625 / 2.75) + return c * (7.5625 * t * t + .984375) + b + def INOUT_EXP(t, b, c, d): - t=float(t) - b=float(b) - c=float(c) - d=float(d) - t1 = t / (d/2) - if t==0: - return b - if t==d: - return b+c - if (t1) < 1: - return c/2 * math.pow(2, 10 * (t1 - 1)) + b - c * 0.0005 - return c/2 * 1.0005 * (-math.pow(2, -10 * (t1-1)) + 2) + b + t = float(t) + b = float(b) + c = float(c) + d = float(d) + + t1 = t / (d / 2) + + if t == 0: + return b + elif t == d: + return b + c + elif t1 < 1: + return c / 2 * math.pow(2, 10 * (t1 - 1)) + b - c * 0.0005 + else: + return c / 2 * 1.0005 * (-math.pow(2, -10 * (t1 - 1)) + 2) + b diff --git a/gui/utils/animUtils.py b/gui/utils/animUtils.py index 9ee81b03a..4003e7760 100644 --- a/gui/utils/animUtils.py +++ b/gui/utils/animUtils.py @@ -1,9 +1,11 @@ +# noinspection PyPackageRequirements import wx import gui.utils.colorUtils as colorUtils + class LoadAnimation(wx.Window): - def __init__ (self, parent, id = wx.ID_ANY, label = "", pos = wx.DefaultPosition, size = wx.DefaultSize, style = 0): - wx.Window.__init__(self, parent, id, pos = pos, size = size, style = style) + def __init__(self, parent, id=wx.ID_ANY, label="", pos=wx.DefaultPosition, size=wx.DefaultSize, style=0): + wx.Window.__init__(self, parent, id, pos=pos, size=size, style=style) self.label = label @@ -16,7 +18,6 @@ class LoadAnimation(wx.Window): self.bars = 10 self.padding = 2 - self.Bind(wx.EVT_ERASE_BACKGROUND, self.OnEraseBackground) self.Bind(wx.EVT_TIMER, self.OnTimer) self.Bind(wx.EVT_PAINT, self.OnPaint) @@ -59,19 +60,19 @@ class LoadAnimation(wx.Window): barColor = wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOWTEXT) shadeColor = colorUtils.GetSuitableColor(barColor, 0.75) - barWidth = (rect.width) / self.bars + barWidth = rect.width / self.bars barHeight = rect.height - self.padding * 2 x = self.padding - for bar in xrange(self.bars): + for bar in range(self.bars): if bar != self.animCount: dc.SetPen(wx.Pen(shadeColor)) dc.SetBrush(wx.Brush(shadeColor)) bh = barHeight y = self.padding else: - barColor = colorUtils.GetSuitableColor(barColor,float(self.animCount/2)/10) + barColor = colorUtils.GetSuitableColor(barColor, float(self.animCount / 2) / 10) dc.SetPen(wx.Pen(barColor)) dc.SetBrush(wx.Brush(barColor)) bh = rect.height @@ -82,17 +83,17 @@ class LoadAnimation(wx.Window): textColor = wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOWTEXT) dc.SetTextForeground(textColor) - dc.DrawLabel(self.label,rect,wx.ALIGN_CENTER) + dc.DrawLabel(self.label, rect, wx.ALIGN_CENTER) + class WaitDialog(wx.Dialog): - def __init__(self, parent, title = "Processing"): - wx.Dialog.__init__ (self, parent, id=wx.ID_ANY, title = title, size=(300,30), + def __init__(self, parent, title="Processing"): + wx.Dialog.__init__(self, parent, id=wx.ID_ANY, title=title, size=(300, 30), style=wx.NO_BORDER) - mainSizer = wx.BoxSizer( wx.HORIZONTAL ) + mainSizer = wx.BoxSizer(wx.HORIZONTAL) - self.progress = LoadAnimation(self,label = title, size=(300,30)) - mainSizer.Add( self.progress, 1, wx.ALL|wx.ALIGN_CENTER_VERTICAL, 0 ) - self.SetSizer( mainSizer ) + self.progress = LoadAnimation(self, label=title, size=(300, 30)) + mainSizer.Add(self.progress, 1, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 0) + self.SetSizer(mainSizer) self.Layout() self.CenterOnParent() - diff --git a/gui/utils/clipboard.py b/gui/utils/clipboard.py index 17427c6e3..2ca2345e4 100644 --- a/gui/utils/clipboard.py +++ b/gui/utils/clipboard.py @@ -1,5 +1,7 @@ +# noinspection PyPackageRequirements import wx + def toClipboard(text): clip = wx.TheClipboard clip.Open() @@ -7,6 +9,7 @@ def toClipboard(text): clip.SetData(data) clip.Close() + def fromClipboard(): clip = wx.TheClipboard clip.Open() @@ -16,4 +19,4 @@ def fromClipboard(): return data.GetText() else: clip.Close() - return None \ No newline at end of file + return None diff --git a/gui/utils/colorUtils.py b/gui/utils/colorUtils.py index ccf42ec18..d2f10336b 100644 --- a/gui/utils/colorUtils.py +++ b/gui/utils/colorUtils.py @@ -1,25 +1,27 @@ +# noinspection PyPackageRequirements import wx import math -#Brightens a color (wx.Colour), factor = [0,1] def BrightenColor(color, factor): + # Brightens a color (wx.Colour), factor = [0,1] - r,g,b = color + r, g, b = color a = color.Alpha() factor = min(max(factor, 0), 1) - r+=(255-r)*factor - b+=(255-b)*factor - g+=(255-g)*factor + r += (255 - r) * factor + b += (255 - b) * factor + g += (255 - g) * factor - return wx.Colour(r,g,b,a) + return wx.Colour(r, g, b, a) -#Darkens a color (wx.Colour), factor = [0, 1] def DarkenColor(color, factor): - bkR ,bkG , bkB = color + # Darkens a color (wx.Colour), factor = [0, 1] + + bkR, bkG, bkB = color alpha = color.Alpha() @@ -30,50 +32,50 @@ def DarkenColor(color, factor): g = float(bkG * factor) b = float(bkB * factor) - r = min(max(r,0),255) - b = min(max(b,0),255) - g = min(max(g,0),255) + r = min(max(r, 0), 255) + b = min(max(b, 0), 255) + g = min(max(g, 0), 255) return wx.Colour(r, g, b, alpha) -#Calculates the brightness of a color, different options - def GetBrightnessO1(color): - r,g,b = color - return (0.299*r + 0.587*g + 0.114*b) + # Calculates the brightness of a color, different options + + r, g, b = color + return 0.299 * r + 0.587 * g + 0.114 * b + def GetBrightnessO2(color): - r,g,b = color - return math.sqrt( 0.241 * r * r + 0.691 * g * g + 0.068 * b * b ) + r, g, b = color + return math.sqrt(0.241 * r * r + 0.691 * g * g + 0.068 * b * b) - -#Calculates a suitable color based on original color (wx.Colour), its brightness, a factor=[0,1] (darken/brighten by factor depending on calculated brightness) - def GetSuitableColor(color, factor): + # Calculates a suitable color based on original color (wx.Colour), its brightness, a factor=[0,1] (darken/brighten by factor depending on calculated brightness) brightness = GetBrightnessO1(color) - if brightness >129: + if brightness > 129: return DarkenColor(color, factor) else: return BrightenColor(color, factor) - -#Calculates the color between a given start and end colors, delta = [0,1] -#Colors are wx.Colour objects - def CalculateTransitionColor(startColor, endColor, delta): - sR,sG,sB = startColor - eR,eG,eB = endColor + """ + Calculates the color between a given start and end colors, delta = [0,1] + Colors are wx.Colour objects + """ + + sR, sG, sB = startColor + eR, eG, eB = endColor alphaS = startColor.Alpha() alphaE = endColor.Alpha() - tR = sR + (eR - sR) * delta - tG = sG + (eG - sG) * delta - tB = sB + (eB - sB) * delta + tR = sR + (eR - sR) * delta + tG = sG + (eG - sG) * delta + tB = sB + (eB - sB) * delta - return wx.Colour(tR, tG, tB, (alphaS + alphaE)/2) + return wx.Colour(tR, tG, tB, (alphaS + alphaE) / 2) diff --git a/gui/utils/compat.py b/gui/utils/compat.py deleted file mode 100644 index d8177973b..000000000 --- a/gui/utils/compat.py +++ /dev/null @@ -1,258 +0,0 @@ -# Backport of OrderedDict() class that runs on Python 2.4, 2.5, 2.6, 2.7 and pypy. -# Passes Python2.7's test suite and incorporates all the latest updates. - -try: - from thread import get_ident as _get_ident -except ImportError: - from dummy_thread import get_ident as _get_ident - -try: - from _abcoll import KeysView, ValuesView, ItemsView -except ImportError: - pass - - -class OrderedDict(dict): - 'Dictionary that remembers insertion order' - # An inherited dict maps keys to values. - # The inherited dict provides __getitem__, __len__, __contains__, and get. - # The remaining methods are order-aware. - # Big-O running times for all methods are the same as for regular dictionaries. - - # The internal self.__map dictionary maps keys to links in a doubly linked list. - # The circular doubly linked list starts and ends with a sentinel element. - # The sentinel element never gets deleted (this simplifies the algorithm). - # Each link is stored as a list of length three: [PREV, NEXT, KEY]. - - def __init__(self, *args, **kwds): - '''Initialize an ordered dictionary. Signature is the same as for - regular dictionaries, but keyword arguments are not recommended - because their insertion order is arbitrary. - - ''' - if len(args) > 1: - raise TypeError('expected at most 1 arguments, got %d' % len(args)) - try: - self.__root - except AttributeError: - self.__root = root = [] # sentinel node - root[:] = [root, root, None] - self.__map = {} - self.__update(*args, **kwds) - - def __setitem__(self, key, value, dict_setitem=dict.__setitem__): - 'od.__setitem__(i, y) <==> od[i]=y' - # Setting a new item creates a new link which goes at the end of the linked - # list, and the inherited dictionary is updated with the new key/value pair. - if key not in self: - root = self.__root - last = root[0] - last[1] = root[0] = self.__map[key] = [last, root, key] - dict_setitem(self, key, value) - - def __delitem__(self, key, dict_delitem=dict.__delitem__): - 'od.__delitem__(y) <==> del od[y]' - # Deleting an existing item uses self.__map to find the link which is - # then removed by updating the links in the predecessor and successor nodes. - dict_delitem(self, key) - link_prev, link_next, key = self.__map.pop(key) - link_prev[1] = link_next - link_next[0] = link_prev - - def __iter__(self): - 'od.__iter__() <==> iter(od)' - root = self.__root - curr = root[1] - while curr is not root: - yield curr[2] - curr = curr[1] - - def __reversed__(self): - 'od.__reversed__() <==> reversed(od)' - root = self.__root - curr = root[0] - while curr is not root: - yield curr[2] - curr = curr[0] - - def clear(self): - 'od.clear() -> None. Remove all items from od.' - try: - for node in self.__map.itervalues(): - del node[:] - root = self.__root - root[:] = [root, root, None] - self.__map.clear() - except AttributeError: - pass - dict.clear(self) - - def popitem(self, last=True): - '''od.popitem() -> (k, v), return and remove a (key, value) pair. - Pairs are returned in LIFO order if last is true or FIFO order if false. - - ''' - if not self: - raise KeyError('dictionary is empty') - root = self.__root - if last: - link = root[0] - link_prev = link[0] - link_prev[1] = root - root[0] = link_prev - else: - link = root[1] - link_next = link[1] - root[1] = link_next - link_next[0] = root - key = link[2] - del self.__map[key] - value = dict.pop(self, key) - return key, value - - # -- the following methods do not depend on the internal structure -- - - def keys(self): - 'od.keys() -> list of keys in od' - return list(self) - - def values(self): - 'od.values() -> list of values in od' - return [self[key] for key in self] - - def items(self): - 'od.items() -> list of (key, value) pairs in od' - return [(key, self[key]) for key in self] - - def iterkeys(self): - 'od.iterkeys() -> an iterator over the keys in od' - return iter(self) - - def itervalues(self): - 'od.itervalues -> an iterator over the values in od' - for k in self: - yield self[k] - - def iteritems(self): - 'od.iteritems -> an iterator over the (key, value) items in od' - for k in self: - yield (k, self[k]) - - def update(*args, **kwds): - '''od.update(E, **F) -> None. Update od from dict/iterable E and F. - - If E is a dict instance, does: for k in E: od[k] = E[k] - If E has a .keys() method, does: for k in E.keys(): od[k] = E[k] - Or if E is an iterable of items, does: for k, v in E: od[k] = v - In either case, this is followed by: for k, v in F.items(): od[k] = v - - ''' - if len(args) > 2: - raise TypeError('update() takes at most 2 positional ' - 'arguments (%d given)' % (len(args),)) - elif not args: - raise TypeError('update() takes at least 1 argument (0 given)') - self = args[0] - # Make progressively weaker assumptions about "other" - other = () - if len(args) == 2: - other = args[1] - if isinstance(other, dict): - for key in other: - self[key] = other[key] - elif hasattr(other, 'keys'): - for key in other.keys(): - self[key] = other[key] - else: - for key, value in other: - self[key] = value - for key, value in kwds.items(): - self[key] = value - - __update = update # let subclasses override update without breaking __init__ - - __marker = object() - - def pop(self, key, default=__marker): - '''od.pop(k[,d]) -> v, remove specified key and return the corresponding value. - If key is not found, d is returned if given, otherwise KeyError is raised. - - ''' - if key in self: - result = self[key] - del self[key] - return result - if default is self.__marker: - raise KeyError(key) - return default - - def setdefault(self, key, default=None): - 'od.setdefault(k[,d]) -> od.get(k,d), also set od[k]=d if k not in od' - if key in self: - return self[key] - self[key] = default - return default - - def __repr__(self, _repr_running={}): - 'od.__repr__() <==> repr(od)' - call_key = id(self), _get_ident() - if call_key in _repr_running: - return '...' - _repr_running[call_key] = 1 - try: - if not self: - return '%s()' % (self.__class__.__name__,) - return '%s(%r)' % (self.__class__.__name__, self.items()) - finally: - del _repr_running[call_key] - - def __reduce__(self): - 'Return state information for pickling' - items = [[k, self[k]] for k in self] - inst_dict = vars(self).copy() - for k in vars(OrderedDict()): - inst_dict.pop(k, None) - if inst_dict: - return (self.__class__, (items,), inst_dict) - return self.__class__, (items,) - - def copy(self): - 'od.copy() -> a shallow copy of od' - return self.__class__(self) - - @classmethod - def fromkeys(cls, iterable, value=None): - '''OD.fromkeys(S[, v]) -> New ordered dictionary with keys from S - and values equal to v (which defaults to None). - - ''' - d = cls() - for key in iterable: - d[key] = value - return d - - def __eq__(self, other): - '''od.__eq__(y) <==> od==y. Comparison to another OD is order-sensitive - while comparison to a regular mapping is order-insensitive. - - ''' - if isinstance(other, OrderedDict): - return len(self)==len(other) and self.items() == other.items() - return dict.__eq__(self, other) - - def __ne__(self, other): - return not self == other - - # -- the following methods are only used in Python 2.7 -- - - def viewkeys(self): - "od.viewkeys() -> a set-like object providing a view on od's keys" - return KeysView(self) - - def viewvalues(self): - "od.viewvalues() -> an object providing a view on od's values" - return ValuesView(self) - - def viewitems(self): - "od.viewitems() -> a set-like object providing a view on od's items" - return ItemsView(self) diff --git a/gui/utils/drawUtils.py b/gui/utils/drawUtils.py index 91e60eb98..d7034ece7 100644 --- a/gui/utils/drawUtils.py +++ b/gui/utils/drawUtils.py @@ -1,18 +1,18 @@ +# noinspection PyPackageRequirements import wx import gui.utils.colorUtils as colorUtils -def RenderGradientBar(windowColor, width, height, sFactor, eFactor, mFactor = None , fillRatio = 2): - - if sFactor == 0 and eFactor == 0 and mFactor == None: - return DrawFilledBitmap(width,height, windowColor) +def RenderGradientBar(windowColor, width, height, sFactor, eFactor, mFactor=None, fillRatio=2): + if sFactor == 0 and eFactor == 0 and mFactor is None: + return DrawFilledBitmap(width, height, windowColor) gStart = colorUtils.GetSuitableColor(windowColor, sFactor) if mFactor: gMid = colorUtils.GetSuitableColor(windowColor, mFactor) else: - gMid = colorUtils.GetSuitableColor(windowColor, sFactor + (eFactor - sFactor) / 2) + gMid = colorUtils.GetSuitableColor(windowColor, sFactor + (eFactor - sFactor) / 2) gEnd = colorUtils.GetSuitableColor(windowColor, eFactor) @@ -20,7 +20,7 @@ def RenderGradientBar(windowColor, width, height, sFactor, eFactor, mFactor = No def DrawFilledBitmap(width, height, color): - canvas = wx.EmptyBitmap(width,height) + canvas = wx.EmptyBitmap(width, height) mdc = wx.MemoryDC() mdc.SelectObject(canvas) @@ -32,10 +32,12 @@ def DrawFilledBitmap(width, height, color): return canvas -def DrawGradientBar(width, height, gStart, gEnd, gMid = None, fillRatio = 4): + +# noinspection PyPropertyAccess +def DrawGradientBar(width, height, gStart, gEnd, gMid=None, fillRatio=4): # we need to have dimensions to draw - #assert width > 0 and height > 0 - canvas = wx.EmptyBitmap(width,height) + # assert width > 0 and height > 0 + canvas = wx.EmptyBitmap(width, height) mdc = wx.MemoryDC() mdc.SelectObject(canvas) @@ -48,7 +50,7 @@ def DrawGradientBar(width, height, gStart, gEnd, gMid = None, fillRatio = 4): mdc.GradientFillLinear(r, gStart, gMid, wx.SOUTH) r.top = r.height - r.height = height * (fillRatio - 1)/fillRatio + (1 if height % fillRatio != 0 else 0) + r.height = height * (fillRatio - 1) / fillRatio + (1 if height % fillRatio != 0 else 0) mdc.GradientFillLinear(r, gMid, gEnd, wx.SOUTH) @@ -57,48 +59,51 @@ def DrawGradientBar(width, height, gStart, gEnd, gMid = None, fillRatio = 4): return canvas -def GetPartialText(dc, text , maxWidth, defEllipsis = "..."): - ellipsis = defEllipsis +def GetPartialText(dc, text, maxWidth, defEllipsis="..."): + ellipsis = defEllipsis + base_w, h = dc.GetTextExtent(ellipsis) + + lenText = len(text) + drawntext = text + w, dummy = dc.GetTextExtent(text) + + while lenText > 0: + + if w + base_w <= maxWidth: + break + + w_c, h_c = dc.GetTextExtent(drawntext[-1]) + drawntext = drawntext[0:-1] + lenText -= 1 + w -= w_c + + while len(ellipsis) > 0 and w + base_w > maxWidth: + ellipsis = ellipsis[0:-1] base_w, h = dc.GetTextExtent(ellipsis) + if len(text) > lenText: + return drawntext + ellipsis + else: + return text - lenText = len(text) - drawntext = text - w, dummy = dc.GetTextExtent(text) - while lenText > 0: - - if w + base_w <= maxWidth: - break - - w_c, h_c = dc.GetTextExtent(drawntext[-1]) - drawntext = drawntext[0:-1] - lenText -= 1 - w -= w_c - - while len(ellipsis) > 0 and w + base_w > maxWidth: - ellipsis = ellipsis[0:-1] - base_w, h = dc.GetTextExtent(ellipsis) - if len(text) > lenText: - return drawntext + ellipsis - else: - return text - -def GetRoundBitmap( w, h, r ): - maskColor = wx.Color(0,0,0) - shownColor = wx.Color(5,5,5) - b = wx.EmptyBitmap(w,h) +def GetRoundBitmap(w, h, r): + maskColor = wx.Color(0, 0, 0) + shownColor = wx.Color(5, 5, 5) + b = wx.EmptyBitmap(w, h) dc = wx.MemoryDC(b) dc.SetBrush(wx.Brush(maskColor)) - dc.DrawRectangle(0,0,w,h) + dc.DrawRectangle(0, 0, w, h) dc.SetBrush(wx.Brush(shownColor)) dc.SetPen(wx.Pen(shownColor)) - dc.DrawRoundedRectangle(0,0,w,h,r) + dc.DrawRoundedRectangle(0, 0, w, h, r) dc.SelectObject(wx.NullBitmap) b.SetMaskColour(maskColor) return b -def GetRoundShape( w, h, r ): - return wx.RegionFromBitmap( GetRoundBitmap(w,h,r) ) + +def GetRoundShape(w, h, r): + return wx.RegionFromBitmap(GetRoundBitmap(w, h, r)) + def CreateDropShadowBitmap(bitmap, opacity): img = wx.ImageFromBitmap(bitmap) diff --git a/gui/utils/exportHtml.py b/gui/utils/exportHtml.py index 9fc20eff6..ba075a35f 100644 --- a/gui/utils/exportHtml.py +++ b/gui/utils/exportHtml.py @@ -1,10 +1,20 @@ import threading import time -import service +# noinspection PyPackageRequirements import wx +from service.settings import HTMLExportSettings +from service.fit import Fit +from service.port import Port +from service.market import Market +from logbook import Logger +from eos.db import getFit -class exportHtml(): +pyfalog = Logger(__name__) + + +class exportHtml(object): _instance = None + @classmethod def getInstance(cls): if cls._instance is None: @@ -16,17 +26,18 @@ class exportHtml(): self.thread = exportHtmlThread() def refreshFittingHtml(self, force=False, callback=False): - settings = service.settings.HTMLExportSettings.getInstance() + settings = HTMLExportSettings.getInstance() if force or settings.getEnabled(): self.thread.stop() self.thread = exportHtmlThread(callback) self.thread.start() -class exportHtmlThread(threading.Thread): +class exportHtmlThread(threading.Thread): def __init__(self, callback=False): threading.Thread.__init__(self) + self.name = "HTMLExport" self.callback = callback self.stopRunning = False @@ -39,39 +50,34 @@ class exportHtmlThread(threading.Thread): if self.stopRunning: return - sMkt = service.Market.getInstance() - sFit = service.Fit.getInstance() - settings = service.settings.HTMLExportSettings.getInstance() + sMkt = Market.getInstance() + sFit = Fit.getInstance() + settings = HTMLExportSettings.getInstance() - timestamp = time.localtime(time.time()) - localDate = "%d/%02d/%02d %02d:%02d" % (timestamp[0], timestamp[1], timestamp[2], timestamp[3], timestamp[4]) - - minimal = settings.getMinimalEnabled(); + minimal = settings.getMinimalEnabled() dnaUrl = "https://o.smium.org/loadout/dna/" if minimal: - HTML = self.generateMinimalHTML(sMkt,sFit, dnaUrl) + HTML = self.generateMinimalHTML(sMkt, sFit, dnaUrl) else: - HTML = self.generateFullHTML(sMkt,sFit, dnaUrl) + HTML = self.generateFullHTML(sMkt, sFit, dnaUrl) try: FILE = open(settings.getPath(), "w") FILE.write(HTML.encode('utf-8')) FILE.close() except IOError: - print "Failed to write to " + settings.getPath() + print("Failed to write to " + settings.getPath()) pass if self.callback: wx.CallAfter(self.callback, -1) - - - def generateFullHTML(self,sMkt,sFit,dnaUrl): + def generateFullHTML(self, sMkt, sFit, dnaUrl): """ Generate the complete HTML with styling and javascript """ timestamp = time.localtime(time.time()) localDate = "%d/%02d/%02d %02d:%02d" % (timestamp[0], timestamp[1], timestamp[2], timestamp[3], timestamp[4]) - + HTML = """ @@ -150,7 +156,7 @@ class exportHtmlThread(threading.Thread): $('a[data-dna]').each(function( index ) { var dna = $(this).data('dna'); if (typeof CCPEVE !== 'undefined') { // inside IGB - $(this).attr('href', 'javascript:CCPEVE.showFitting("'+dna+'");');} + $(this).attr('href', 'javascript:CCPEVE.showFitting("'+dna+'");');} else { // outside IGB $(this).attr('href', '%s'+dna); } }); @@ -168,7 +174,7 @@ class exportHtmlThread(threading.Thread): """ % (time.time(), dnaUrl, localDate) HTML += '
    \n' categoryList = list(sMkt.getShipRoot()) - categoryList.sort(key=lambda ship: ship.name) + categoryList.sort(key=lambda _ship: _ship.name) count = 0 @@ -177,12 +183,13 @@ class exportHtmlThread(threading.Thread): HTMLgroup = '' ships = list(sMkt.getShipList(group.ID)) - ships.sort(key=lambda ship: ship.name) + ships.sort(key=lambda _ship: _ship.name) # Keep track of how many ships per group groupFits = 0 for ship in ships: fits = sFit.getFitsWithShip(ship.ID) + if len(fits) > 0: groupFits += len(fits) @@ -191,10 +198,11 @@ class exportHtmlThread(threading.Thread): return fit = fits[0] try: - dnaFit = sFit.exportDna(fit[0]) - HTMLgroup += ( - '
  • ' + ship.name + ": " + fit[1] + '
  • \n') + dnaFit = Port.exportDna(getFit(fit[0])) + HTMLgroup += '
  • ' + ship.name + ": " + \ + fit[1] + '
  • \n' except: + pyfalog.warning("Failed to export line") pass finally: if self.callback: @@ -203,17 +211,22 @@ class exportHtmlThread(threading.Thread): else: # Ship group header HTMLship = ( - '
  • \n' - '

    ' + ship.name + ' '+str(len(fits))+'

    \n' - '
      \n') + '
    • \n' + '

      ' + ship.name + ' ' + str( + len(fits)) + '

      \n' + '
        \n' + ) for fit in fits: if self.stopRunning: return try: - dnaFit = sFit.exportDna(fit[0]) - HTMLship += '
      • ' + fit[1] + '
      • \n' + dnaFit = Port.exportDna(getFit(fit[0])) + print dnaFit + HTMLship += '
      • ' + fit[ + 1] + '
      • \n' except: + pyfalog.warning("Failed to export line") continue finally: if self.callback: @@ -225,12 +238,12 @@ class exportHtmlThread(threading.Thread): if groupFits > 0: # Market group header HTML += ( - '
      • \n' - '

        ' + group.groupName + ' '+str(groupFits)+'

        \n' - '
          \n' - + HTMLgroup + - '
        \n' - '
      • ') + '
      • \n' + '

        ' + group.groupName + ' ' + str(groupFits) + '

        \n' + '
          \n' + HTMLgroup + + '
        \n' + '
      • ' + ) HTML += """
      @@ -241,21 +254,20 @@ class exportHtmlThread(threading.Thread): return HTML - def generateMinimalHTML(self,sMkt,sFit,dnaUrl): + def generateMinimalHTML(self, sMkt, sFit, dnaUrl): """ Generate a minimal HTML version of the fittings, without any javascript or styling""" categoryList = list(sMkt.getShipRoot()) - categoryList.sort(key=lambda ship: ship.name) + categoryList.sort(key=lambda _ship: _ship.name) count = 0 HTML = '' for group in categoryList: # init market group string to give ships something to attach to - ships = list(sMkt.getShipList(group.ID)) - ships.sort(key=lambda ship: ship.name) + ships.sort(key=lambda _ship: _ship.name) - ships.sort(key=lambda ship: ship.name) + ships.sort(key=lambda _ship: _ship.name) for ship in ships: fits = sFit.getFitsWithShip(ship.ID) @@ -263,9 +275,11 @@ class exportHtmlThread(threading.Thread): if self.stopRunning: return try: - dnaFit = sFit.exportDna(fit[0]) - HTML += ''+ship.name +': '+ fit[1]+ '
      \n' + dnaFit = Port.exportDna(getFit(fit[0])) + HTML += '' + ship.name + ': ' + \ + fit[1] + '
      \n' except: + pyfalog.error("Failed to export line") continue finally: if self.callback: diff --git a/gui/utils/fonts.py b/gui/utils/fonts.py index eb2dd95f5..6f8e109d3 100644 --- a/gui/utils/fonts.py +++ b/gui/utils/fonts.py @@ -1,8 +1,9 @@ -''' +""" Font file to handle the differences in font calculations between different wxPython versions -''' +""" +# noinspection PyPackageRequirements import wx if 'wxMac' in wx.PlatformInfo: diff --git a/gui/utils/numberFormatter.py b/gui/utils/numberFormatter.py index 8b64d6147..46aaf7d5a 100644 --- a/gui/utils/numberFormatter.py +++ b/gui/utils/numberFormatter.py @@ -1,5 +1,6 @@ import math + def formatAmount(val, prec=3, lowest=0, highest=0, currency=False, forceSign=False): """ Add suffix to value, transform value to match new suffix and round it. @@ -33,8 +34,8 @@ def formatAmount(val, prec=3, lowest=0, highest=0, currency=False, forceSign=Fal # Start from highest possible suffix for key in posOrders: # Find first suitable suffix and check if it's not above highest order - if abs(val) >= 10**key and key <= highest: - mantissa, suffix = val/float(10**key), posSuffixMap[key] + if abs(val) >= 10 ** key and key <= highest: + mantissa, suffix = val / float(10 ** key), posSuffixMap[key] # Do additional step to eliminate results like 999999 => 1000k # If we're already using our greatest order, we can't do anything useful if posOrders.index(key) == 0: @@ -48,12 +49,12 @@ def formatAmount(val, prec=3, lowest=0, highest=0, currency=False, forceSign=Fal # If it is, bail - we already have acceptable results break # Find multiplier to get from one order to another - orderDiff = 10**(prevKey - key) + orderDiff = 10 ** (prevKey - key) # If rounded mantissa according to our specifications is greater than # or equal to multiplier if roundToPrec(mantissa, prec) >= orderDiff: # Divide mantissa and use suffix of greater order - mantissa, suffix = mantissa/orderDiff, posSuffixMap[prevKey] + mantissa, suffix = mantissa / orderDiff, posSuffixMap[prevKey] # Otherwise consider current results as acceptable break # Take numbers between 0 and 1, and matching/below highest possible negative suffix @@ -66,8 +67,8 @@ def formatAmount(val, prec=3, lowest=0, highest=0, currency=False, forceSign=Fal except IndexError: nextKey = 0 # Check if mantissa with next suffix is in range [1, 1000) - if abs(val) < 10**(nextKey) and key >= lowest: - mantissa, suffix = val/float(10**key), negSuffixMap[key] + if abs(val) < 10 ** nextKey and key >= lowest: + mantissa, suffix = val / float(10 ** key), negSuffixMap[key] # Do additional step to eliminate results like 0.9999 => 1000m # Check if the key we're potentially switching to is greater than our # upper boundary @@ -75,13 +76,13 @@ def formatAmount(val, prec=3, lowest=0, highest=0, currency=False, forceSign=Fal # If it is, leave loop with results we already have break # Find the multiplier between current and next order - orderDiff = 10**(nextKey - key) + orderDiff = 10 ** (nextKey - key) # If rounded mantissa according to our specifications is greater than # or equal to multiplier if roundToPrec(mantissa, prec) >= orderDiff: # Divide mantissa and use suffix of greater order # Use special handling of zero key as it's not on the map - mantissa, suffix = mantissa/orderDiff, posSuffixMap[nextKey] if nextKey != 0 else "" + mantissa, suffix = mantissa / orderDiff, posSuffixMap[nextKey] if nextKey != 0 else "" # Otherwise consider current results as acceptable break # Round mantissa according to our prec variable @@ -91,6 +92,7 @@ def formatAmount(val, prec=3, lowest=0, highest=0, currency=False, forceSign=Fal result = u"{0}{1}{2}".format(sign, mantissa, suffix) return result + def roundToPrec(val, prec): # We're not rounding integers anyway # Also make sure that we do not ask to calculate logarithm of zero diff --git a/gui/viewColumn.py b/gui/viewColumn.py index c2ffff612..34c3c89ce 100644 --- a/gui/viewColumn.py +++ b/gui/viewColumn.py @@ -1,4 +1,4 @@ -#=============================================================================== +# ============================================================================= # Copyright (C) 2010 Diego Duclos # # This file is part of pyfa. @@ -15,17 +15,20 @@ # # You should have received a copy of the GNU General Public License # along with pyfa. If not, see . -#=============================================================================== +# ============================================================================= +# noinspection PyPackageRequirements import wx + class ViewColumn(object): - ''' + """ Abstract class that columns can inherit from. Once the missing methods are correctly implemented, they can be used as columns in a view. - ''' + """ columns = {} + def __init__(self, fittingView): self.fittingView = fittingView self.columnText = "" @@ -62,4 +65,19 @@ class ViewColumn(object): def delayedText(self, display, colItem): raise NotImplementedError() -from gui.builtinViewColumns import * + +# noinspection PyUnresolvedReferences +from gui.builtinViewColumns import ( # noqa: E402, F401 + abilities, + ammo, + ammoIcon, + attributeDisplay, + baseIcon, + baseName, + capacitorUse, + maxRange, + misc, + price, + propertyDisplay, + state +) diff --git a/pyfa.py b/pyfa.py index 146ea704d..488165724 100755 --- a/pyfa.py +++ b/pyfa.py @@ -1,5 +1,5 @@ #!/usr/bin/env python -#=============================================================================== +# ============================================================================== # Copyright (C) 2010 Diego Duclos # # This file is part of pyfa. @@ -16,14 +16,21 @@ # # You should have received a copy of the GNU General Public License # along with pyfa. If not, see . -#=============================================================================== +# ============================================================================== import sys +import os +import os.path import re import config from optparse import OptionParser, BadOptionError, AmbiguousOptionError +from logbook import TimedRotatingFileHandler, Logger, StreamHandler, NestedSetup, FingersCrossedHandler, NullHandler, \ + CRITICAL, ERROR, WARNING, DEBUG, INFO +pyfalog = Logger(__name__) + + class PassThroughOptionParser(OptionParser): """ An unknown option pass-through implementation of OptionParser. @@ -33,10 +40,32 @@ class PassThroughOptionParser(OptionParser): def _process_args(self, largs, rargs, values): while rargs: try: - OptionParser._process_args(self,largs,rargs,values) - except (BadOptionError,AmbiguousOptionError), e: + OptionParser._process_args(self, largs, rargs, values) + except (BadOptionError, AmbiguousOptionError) as e: + pyfalog.error("Bad startup option passed.") largs.append(e.opt_str) + +class LoggerWriter: + def __init__(self, level): + # self.level is really like using log.debug(message) + # at least in my case + self.level = level + + def write(self, message): + # if statement reduces the amount of newlines that are + # printed to the logger + if message not in {'\n', ' '}: + self.level(message.replace("\n", "")) + + def flush(self): + # create a flush method so things can be flushed when + # the system wants to. Not sure if simply 'printing' + # sys.stderr is the correct way to do it, but it seemed + # to work properly for me. + self.level(sys.stderr) + + # Parse command line options usage = "usage: %prog [--root]" parser = PassThroughOptionParser(usage=usage) @@ -45,18 +74,33 @@ parser.add_option("-w", "--wx28", action="store_true", dest="force28", help="For parser.add_option("-d", "--debug", action="store_true", dest="debug", help="Set logger to debug level.", default=False) parser.add_option("-t", "--title", action="store", dest="title", help="Set Window Title", default=None) parser.add_option("-s", "--savepath", action="store", dest="savepath", help="Set the folder for savedata", default=None) +parser.add_option("-l", "--logginglevel", action="store", dest="logginglevel", help="Set desired logging level [Critical|Error|Warning|Info|Debug]", default="Error") (options, args) = parser.parse_args() +if options.logginglevel == "Critical": + options.logginglevel = CRITICAL +elif options.logginglevel == "Error": + options.logginglevel = ERROR +elif options.logginglevel == "Warning": + options.logginglevel = WARNING +elif options.logginglevel == "Info": + options.logginglevel = INFO +elif options.logginglevel == "Debug": + options.logginglevel = DEBUG +else: + options.logginglevel = ERROR + if not hasattr(sys, 'frozen'): - if sys.version_info < (2,6) or sys.version_info > (3,0): + if sys.version_info < (2, 6) or sys.version_info > (3, 0): print("Pyfa requires python 2.x branch ( >= 2.6 )\nExiting.") sys.exit(1) try: import wxversion except ImportError: + wxversion = None print("Cannot find wxPython\nYou can download wxPython (2.8+) from http://www.wxpython.org/") sys.exit(1) @@ -66,13 +110,13 @@ if not hasattr(sys, 'frozen'): else: wxversion.select(['3.0', '2.8']) except wxversion.VersionError: - print "Installed wxPython version doesn't meet requirements.\nYou can download wxPython 2.8 or 3.0 from http://www.wxpython.org/" + print("Installed wxPython version doesn't meet requirements.\nYou can download wxPython 2.8 or 3.0 from http://www.wxpython.org/") sys.exit(1) try: import sqlalchemy - saVersion = sqlalchemy.__version__ + saVersion = sqlalchemy.__version__ saMatch = re.match("([0-9]+).([0-9]+)([b\.])([0-9]+)", saVersion) if saMatch: saMajor = int(saMatch.group(1)) @@ -80,19 +124,23 @@ if not hasattr(sys, 'frozen'): betaFlag = True if saMatch.group(3) == "b" else False saBuild = int(saMatch.group(4)) if not betaFlag else 0 if saMajor == 0 and (saMinor < 5 or (saMinor == 5 and saBuild < 8)): - print("Pyfa requires sqlalchemy 0.5.8 at least but current sqlalchemy version is %s\nYou can download sqlalchemy (0.5.8+) from http://www.sqlalchemy.org/".format(sqlalchemy.__version__)) + print("Pyfa requires sqlalchemy 0.5.8 at least but current sqlalchemy version is %s\n" + "You can download sqlalchemy (0.5.8+) from http://www.sqlalchemy.org/".format(sqlalchemy.__version__)) sys.exit(1) else: print("Unknown sqlalchemy version string format, skipping check") except ImportError: + sqlalchemy = None print("Cannot find sqlalchemy.\nYou can download sqlalchemy (0.6+) from http://www.sqlalchemy.org/") sys.exit(1) - + # check also for dateutil module installed. try: - import dateutil.parser # Copied import statement from service/update.py + # noinspection PyPackageRequirements + import dateutil.parser # noqa - Copied import statement from service/update.py except ImportError: + dateutil = None print("Cannot find python-dateutil.\nYou can download python-dateutil from https://pypi.python.org/pypi/python-dateutil") sys.exit(1) @@ -101,36 +149,141 @@ if __name__ == "__main__": # Configure paths if options.rootsavedata is True: config.saveInRoot = True - + # set title if it wasn't supplied by argument - if options.title == None: - options.title = "pyfa %s%s - Python Fitting Assistant"%(config.version, "" if config.tag.lower() != 'git' else " (git)") + if options.title is None: + options.title = "pyfa %s%s - Python Fitting Assistant" % (config.version, "" if config.tag.lower() != 'git' else " (git)") config.debug = options.debug - # convert to unicode if it is set - if options.savepath is not None: - options.savepath = unicode(options.savepath) - config.defPaths(options.savepath) - - # Basic logging initialization - import logging - logging.basicConfig() # Import everything + # noinspection PyPackageRequirements import wx import os import os.path - import eos.db - import service.prefetch - from gui.mainFrame import MainFrame + try: + # convert to unicode if it is set + if options.savepath is not None: + options.savepath = unicode(options.savepath) + config.defPaths(options.savepath) - #Make sure the saveddata db exists - if not os.path.exists(config.savePath): - os.mkdir(config.savePath) + # Basic logging initialization - eos.db.saveddata_meta.create_all() + # Logging levels: + ''' + logbook.CRITICAL + logbook.ERROR + logbook.WARNING + logbook.INFO + logbook.DEBUG + logbook.NOTSET + ''' - pyfa = wx.App(False) - MainFrame(options.title) - pyfa.MainLoop() + if options.debug: + savePath_filename = "Pyfa_debug.log" + else: + savePath_filename = "Pyfa.log" + + savePath_Destination = os.path.join(config.savePath, savePath_filename) + + try: + if options.debug: + logging_mode = "Debug" + logging_setup = NestedSetup([ + # make sure we never bubble up to the stderr handler + # if we run out of setup handling + NullHandler(), + StreamHandler( + sys.stdout, + bubble=False, + level=options.logginglevel + ), + TimedRotatingFileHandler( + savePath_Destination, + level=0, + backup_count=3, + bubble=True, + date_format='%Y-%m-%d', + ), + ]) + else: + logging_mode = "User" + logging_setup = NestedSetup([ + # make sure we never bubble up to the stderr handler + # if we run out of setup handling + NullHandler(), + FingersCrossedHandler( + TimedRotatingFileHandler( + savePath_Destination, + level=0, + backup_count=3, + bubble=False, + date_format='%Y-%m-%d', + ), + action_level=ERROR, + buffer_size=1000, + # pull_information=True, + # reset=False, + ) + ]) + except: + logging_mode = "Console Only" + logging_setup = NestedSetup([ + # make sure we never bubble up to the stderr handler + # if we run out of setup handling + NullHandler(), + StreamHandler( + sys.stdout, + bubble=False + ) + ]) + + import eos.db + # noinspection PyUnresolvedReferences + import service.prefetch # noqa: F401 + + # Make sure the saveddata db exists + if not os.path.exists(config.savePath): + os.mkdir(config.savePath) + + eos.db.saveddata_meta.create_all() + + except Exception, e: + import traceback + from gui.errorDialog import ErrorFrame + + tb = traceback.format_exc() + + pyfa = wx.App(False) + ErrorFrame(e, tb) + pyfa.MainLoop() + sys.exit() + + with logging_setup.threadbound(): + # Don't redirect if frozen + if not hasattr(sys, 'frozen'): + # Output all stdout (print) messages as warnings + try: + sys.stdout = LoggerWriter(pyfalog.warning) + except ValueError, Exception: + pyfalog.critical("Cannot access log file. Continuing without writing stdout to log.") + + if not options.debug: + # Output all stderr (stacktrace) messages as critical + try: + sys.stderr = LoggerWriter(pyfalog.critical) + except ValueError, Exception: + pyfalog.critical("Cannot access log file. Continuing without writing stderr to log.") + + pyfalog.info("Starting Pyfa") + pyfalog.info("Running in logging mode: {0}", logging_mode) + + if hasattr(sys, 'frozen') and options.debug: + pyfalog.critical("Running in frozen mode with debug turned on. Forcing all output to be written to log.") + + from gui.mainFrame import MainFrame + + pyfa = wx.App(False) + MainFrame(options.title) + pyfa.MainLoop() diff --git a/pyfa.spec b/pyfa.spec new file mode 100644 index 000000000..c528aeecb --- /dev/null +++ b/pyfa.spec @@ -0,0 +1,83 @@ +# -*- mode: python -*- + +# Note: This script is provided AS-IS for those that may be interested. +# pyfa does not currently support pyInstaller (or any other build process) 100% at the moment + +# Command line to build: +# (Run from directory where pyfa.py and pyfa.spec lives.) +# c:\Python27\scripts\pyinstaller.exe --clean --noconfirm --windowed --upx-dir=.\scripts\upx.exe pyfa.spec + +# Don't forget to change the path to where your pyfa.py and pyfa.spec lives +# pathex=['C:\\Users\\Ebag333\\Documents\\GitHub\\Ebag333\\Pyfa'], + +import os + +block_cipher = None + +added_files = [ + ( 'imgs/gui/*.png', 'imgs/gui' ), + ( 'imgs/gui/*.gif', 'imgs/gui' ), + ( 'imgs/icons/*.png', 'imgs/icons' ), + ( 'imgs/renders/*.png', 'imgs/renders' ), + ( 'dist_assets/win/pyfa.ico', '.' ), + ( 'dist_assets/cacert.pem', '.' ), + ( 'eve.db', '.' ), + ( 'README.md', '.' ), + ( 'LICENSE', '.' ), + ] + +import_these = [] + +# Walk eos.effects and add all effects so we can import them properly +for root, folders, files in os.walk("eos/effects"): + for file_ in files: + if file_.endswith(".py") and not file_.startswith("_"): + mod_name = "{}.{}".format( + root.replace("/", "."), + file_.split(".py")[0], + ) + import_these.append(mod_name) + +a = Analysis( + ['pyfa.py'], + pathex=['C:\\Users\\Ebag333\\Documents\\GitHub\\Ebag333\\Pyfa'], + binaries=[], + datas=added_files, + hiddenimports=import_these, + hookspath=[], + runtime_hooks=[], + excludes=[], + win_no_prefer_redirects=False, + win_private_assemblies=False, + cipher=block_cipher, + ) + +pyz = PYZ( + a.pure, + a.zipped_data, + cipher=block_cipher, + ) + +exe = EXE(pyz, + a.scripts, + exclude_binaries=True, + debug=False, + console=False, + strip=False, + upx=True, + name='pyfa', + icon='dist_assets/win/pyfa.ico', + onefile=False, + ) + +coll = COLLECT( + exe, + a.binaries, + a.zipfiles, + a.datas, + strip=False, + upx=True, + onefile=False, + name='pyfa', + icon='dist_assets/win/pyfa.ico', + ) diff --git a/requirements.txt b/requirements.txt index f8e777507..ed301fe25 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,6 +1,7 @@ +logbook matplotlib PyYAML python-dateutil urllib3 requests==2.11.1 -sqlalchemy +sqlalchemy>=1.0.5 diff --git a/requirements_build_OSx.txt b/requirements_build_OSx.txt new file mode 100644 index 000000000..51f6135fd --- /dev/null +++ b/requirements_build_OSx.txt @@ -0,0 +1,9 @@ +PyInstaller >= 3.2.1 +cycler >= 0.10.0 +functools32 >= 3.2.3 +future >= 0.16.0 +numpy >= 1.12. +pyparsing >= 2.1.10 +pypiwin32 >= 219 +pytz >= 2016.10 +six >= 1.10.0 diff --git a/requirements_build_linux.txt b/requirements_build_linux.txt new file mode 100644 index 000000000..3a4640baf --- /dev/null +++ b/requirements_build_linux.txt @@ -0,0 +1,8 @@ +PyInstaller >= 3.2.1 +cycler >= 0.10.0 +functools32 >= 3.2.3 +future >= 0.16.0 +numpy >= 1.12. +pyparsing >= 2.1.10 +pytz >= 2016.10 +six diff --git a/requirements_build_windows.txt b/requirements_build_windows.txt new file mode 100644 index 000000000..173ce92d0 --- /dev/null +++ b/requirements_build_windows.txt @@ -0,0 +1,10 @@ +PyInstaller >= 3.2.1 +cycler >= 0.10.0 +functools32 >= 3.2.3 +future >= 0.16.0 +numpy >= 1.12. +pyparsing >= 2.1.10 +pypiwin32 >= 219 +pytz >= 2016.10 +six >= 1.10.0 +tornado diff --git a/requirements_test.txt b/requirements_test.txt index 1bbef8b92..33f8973cd 100644 --- a/requirements_test.txt +++ b/requirements_test.txt @@ -5,3 +5,4 @@ pytest-capturelog==0.7 coverage==4.2 coveralls==1.1 codecov +tox diff --git a/scripts/dist.py b/scripts/dist.py index 3d6f7c3af..588418a39 100755 --- a/scripts/dist.py +++ b/scripts/dist.py @@ -1,257 +1,257 @@ -#!/usr/bin/env python -""" -Script for generating distributables based on platform skeletons. - -User supplies path for pyfa code base, root skeleton directory, and where the -builds go. The builds are automatically named depending on the pyfa config -values of `version` and `tag`. If it's a Stable release, the naming -convention is: - - pyfa-pyfaversion-expansion-expversion-platform - -If it is not Stable (tag=git), we determine if the pyfa code base includes -the git repo to use as an ID. If not, uses randomly generated 6-character ID. -The unstable naming convention: - - pyfa-YYYMMDD-id-platform - -dist.py can also build the Windows installer provided that it has a path to -Inno Setup (and, for generating on non-Windows platforms, that WINE is -installed). To build the EXE file, `win` must be included in the platforms to -be built. -""" - -#@todo: ensure build directory can be written to -# todo: default build and dist directories - -from optparse import OptionParser -import os.path -import shutil -import sys -import tarfile -import datetime -import random -import string -import zipfile -import errno -from subprocess import call - -class FileStub(): - def write(self, *args): - pass - - def flush(self, *args): - pass - -i = 0 -def loginfo(path, names): - # Print out a "progress" and return directories / files to ignore - global i - i += 1 - if i % 10 == 0: - sys.stdout.write(".") - sys.stdout.flush() - return () - -def copyanything(src, dst): - try: - shutil.copytree(src, dst, ignore=loginfo) - except: # python >2.5 - try: - shutil.copy(src, dst) - except: - raise - -def id_generator(size=6, chars=string.ascii_uppercase + string.digits): - return ''.join(random.choice(chars) for x in range(size)) - -def zipdir(path, zip): - for root, dirs, files in os.walk(path): - for file in files: - zip.write(os.path.join(root, file)) - -skels = ['win', 'src', 'mac', 'mac-deprecated'] -iscc = "C:\Program Files (x86)\Inno Setup 5\ISCC.exe" # inno script location via wine - -if __name__ == "__main__": - oldstd = sys.stdout - parser = OptionParser() - parser.add_option("-s", "--skeleton", dest="skeleton", help="Location of Pyfa-skel directory") - parser.add_option("-b", "--base", dest="base", help="Location of cleaned read-only base directory") - parser.add_option("-d", "--destination", dest="destination", help="Where to copy our distributable") - parser.add_option("-p", "--platforms", dest="platforms", help="Comma-separated list of platforms to build", default=','.join(skels)) - parser.add_option("-q", "--quiet", dest="silent", action="store_true") - parser.add_option("-w", "--winexe", dest="winexe", action="store_true", help="Build the Windows installer file (needs Inno Setup). Must include 'win' in platform options") - parser.add_option("-z", "--zip", dest="zip", action="store_true", help="zip archive instead of tar") - - options, args = parser.parse_args() - - if options.skeleton is None or options.base is None or options.destination is None: - print "Need --skeleton argument as well as --base and --destination argument" - parser.print_help() - sys.exit() - - if options.silent: - sys.stdout = FileStub() - - options.platforms = options.platforms.split(",") - - for skel in skels: - if skel not in options.platforms: - continue - - print "\n======== %s ========"%skel - - info = {} - config = {} - setup = {} - skeleton = os.path.expanduser(os.path.join(options.skeleton, skel)) - - execfile(os.path.join(options.base, "config.py"), config) - execfile(os.path.join(skeleton, "info.py"), info) - execfile(os.path.join(options.base, "setup.py"), setup) - - destination = os.path.expanduser(options.destination) - if not os.path.isdir(destination) or not os.access(destination, os.W_OK | os.X_OK): - print "Destination directory does not exist or is not writable: {}".format(destination) - sys.exit() - - dirName = info["arcname"] - - nowdt = datetime.datetime.now() - now = "%04d%02d%02d" % (nowdt.year, nowdt.month, nowdt.day) - - git = False - if config['tag'].lower() == "git": - try: # if there is a git repo associated with base, use master commit - with open(os.path.join(options.base, ".git", "refs", "heads", "master"), 'r') as f: - id = f.readline()[0:6] - git = True - except: # else, use custom ID - id = id_generator() - fileName = "pyfa-{}-{}-{}".format(now, id, info["os"]) - else: - fileName = "pyfa-{}-{}-{}-{}".format( - config['version'], - config['expansionName'].lower(), - config['expansionVersion'], - info["os"] - ) - - archiveName = "{}.{}".format(fileName, "zip" if options.zip else "tar.bz2") - tmpDir = os.path.join(os.getcwd(), dirName) # tmp directory where files are copied - tmpFile = os.path.join(os.getcwd(), archiveName) - - try: - print "Copying skeleton to ", tmpDir - shutil.copytree(skeleton, tmpDir, ignore=loginfo) - print - source = os.path.expanduser(options.base) - root = os.path.join(tmpDir, info["base"]) - - # it is easier to work from the source directory - oldcwd = os.getcwd() - os.chdir(source) - - if info["library"]: - print "Injecting files into", info["library"] - libraryFile = os.path.join(root, info["library"]) - - with zipfile.ZipFile(libraryFile, 'a') as library: - for dir in setup['packages']: - zipdir(dir, library) - library.write('pyfa.py', 'pyfa__main__.py') - library.write('config.py') - else: # platforms where we don't have a packaged library - print "Copying modules into", root - for dir in setup['packages']: - copyanything(dir, os.path.join(root, dir)) - - # add some additional files to root dir for these platforms - # (hopefully can figure out a way later for OS X to use the one in - # it's library) - if skel == 'mac': - setup['include_files'] += ['pyfa.py'] - if skel in ('src', 'mac-deprecated'): - setup['include_files'] += ['pyfa.py', 'config.py'] - - print - print "Copying included files:", - - for file in setup['include_files']: - if isinstance(file, basestring): - print file, - copyanything(file, os.path.join(root, file)) - - print - print "Creating images zipfile:", - os.chdir('imgs') - imagesFile = os.path.join(root, "imgs.zip") - - with zipfile.ZipFile(imagesFile, 'w') as images: - for dir in setup['icon_dirs']: - print dir, - zipdir(dir, images) - os.chdir(oldcwd) - - print - print "Creating archive" - if options.zip: - archive = zipfile.ZipFile(tmpFile, 'w', compression=zipfile.ZIP_DEFLATED) - zipdir(dirName, archive) - archive.close() - else: - archive = tarfile.open(tmpFile, "w:bz2") - archive.add(tmpDir, arcname=info["arcname"]) - archive.close() - - print "Moving archive to ", destination - shutil.move(tmpFile, destination) - - if "win" in skel and options.winexe: - print "Compiling EXE" - - if config['tag'].lower() == "git": - if git: # if git repo info available, use git commit - expansion = "git-%s"%(id) - else: # if there is no git repo, use timestamp - expansion = now - else: # if code is Stable, use expansion name - expansion = "%s %s"%(config['expansionName'], config['expansionVersion']), - - calllist = ["wine"] if 'win' not in sys.platform else [] - - call(calllist + [ - iscc, - "pyfa-setup.iss", - "/dMyAppVersion=%s"%(config['version']), - "/dMyAppExpansion=%s"%(expansion), - "/dMyAppDir=pyfa", - "/dMyOutputDir=%s"%destination, - "/dMyOutputFile=%s"%fileName]) #stdout=devnull, stderr=devnull - - print "EXE completed" - - except Exception as e: - print "Encountered an error: \n\t", e - raise - finally: - print "Deleting tmp files\n" - try: - shutil.rmtree("dist") # Inno dir - except: - pass - try: - shutil.rmtree(tmpDir) - except: - pass - try: - os.unlink(tmpFile) - except: - pass - - sys.stdout = oldstd - if os.path.isdir(destination): - print os.path.join(destination, os.path.split(tmpFile)[1]) - else: - print destination +#!/usr/bin/env python +""" +Script for generating distributables based on platform skeletons. + +User supplies path for pyfa code base, root skeleton directory, and where the +builds go. The builds are automatically named depending on the pyfa config +values of `version` and `tag`. If it's a Stable release, the naming +convention is: + + pyfa-pyfaversion-expansion-expversion-platform + +If it is not Stable (tag=git), we determine if the pyfa code base includes +the git repo to use as an ID. If not, uses randomly generated 6-character ID. +The unstable naming convention: + + pyfa-YYYMMDD-id-platform + +dist.py can also build the Windows installer provided that it has a path to +Inno Setup (and, for generating on non-Windows platforms, that WINE is +installed). To build the EXE file, `win` must be included in the platforms to +be built. +""" + +#@todo: ensure build directory can be written to +# todo: default build and dist directories + +from optparse import OptionParser +import os.path +import shutil +import sys +import tarfile +import datetime +import random +import string +import zipfile +import errno +from subprocess import call + +class FileStub(): + def write(self, *args): + pass + + def flush(self, *args): + pass + +i = 0 +def loginfo(path, names): + # Print out a "progress" and return directories / files to ignore + global i + i += 1 + if i % 10 == 0: + sys.stdout.write(".") + sys.stdout.flush() + return () + +def copyanything(src, dst): + try: + shutil.copytree(src, dst, ignore=loginfo) + except: # python >2.5 + try: + shutil.copy(src, dst) + except: + raise + +def id_generator(size=6, chars=string.ascii_uppercase + string.digits): + return ''.join(random.choice(chars) for x in range(size)) + +def zipdir(path, zip): + for root, dirs, files in os.walk(path): + for file in files: + zip.write(os.path.join(root, file)) + +skels = ['win', 'src', 'mac', 'mac-deprecated'] +iscc = "C:\Program Files (x86)\Inno Setup 5\ISCC.exe" # inno script location via wine + +if __name__ == "__main__": + oldstd = sys.stdout + parser = OptionParser() + parser.add_option("-s", "--skeleton", dest="skeleton", help="Location of Pyfa-skel directory") + parser.add_option("-b", "--base", dest="base", help="Location of cleaned read-only base directory") + parser.add_option("-d", "--destination", dest="destination", help="Where to copy our distributable") + parser.add_option("-p", "--platforms", dest="platforms", help="Comma-separated list of platforms to build", default=','.join(skels)) + parser.add_option("-q", "--quiet", dest="silent", action="store_true") + parser.add_option("-w", "--winexe", dest="winexe", action="store_true", help="Build the Windows installer file (needs Inno Setup). Must include 'win' in platform options") + parser.add_option("-z", "--zip", dest="zip", action="store_true", help="zip archive instead of tar") + + options, args = parser.parse_args() + + if options.skeleton is None or options.base is None or options.destination is None: + print "Need --skeleton argument as well as --base and --destination argument" + parser.print_help() + sys.exit() + + if options.silent: + sys.stdout = FileStub() + + options.platforms = options.platforms.split(",") + + for skel in skels: + if skel not in options.platforms: + continue + + print "\n======== %s ========"%skel + + info = {} + config = {} + setup = {} + skeleton = os.path.expanduser(os.path.join(options.skeleton, skel)) + + execfile(os.path.join(options.base, "config.py"), config) + execfile(os.path.join(skeleton, "info.py"), info) + execfile(os.path.join(options.base, "setup.py"), setup) + + destination = os.path.expanduser(options.destination) + if not os.path.isdir(destination) or not os.access(destination, os.W_OK | os.X_OK): + print "Destination directory does not exist or is not writable: {}".format(destination) + sys.exit() + + dirName = info["arcname"] + + nowdt = datetime.datetime.now() + now = "%04d%02d%02d" % (nowdt.year, nowdt.month, nowdt.day) + + git = False + if config['tag'].lower() == "git": + try: # if there is a git repo associated with base, use master commit + with open(os.path.join(options.base, ".git", "refs", "heads", "master"), 'r') as f: + id = f.readline()[0:6] + git = True + except: # else, use custom ID + id = id_generator() + fileName = "pyfa-{}-{}-{}".format(now, id, info["os"]) + else: + fileName = "pyfa-{}-{}-{}-{}".format( + config['version'], + config['expansionName'].lower(), + config['expansionVersion'], + info["os"] + ) + + archiveName = "{}.{}".format(fileName, "zip" if options.zip else "tar.bz2") + tmpDir = os.path.join(os.getcwd(), dirName) # tmp directory where files are copied + tmpFile = os.path.join(os.getcwd(), archiveName) + + try: + print "Copying skeleton to ", tmpDir + shutil.copytree(skeleton, tmpDir, ignore=loginfo) + print + source = os.path.expanduser(options.base) + root = os.path.join(tmpDir, info["base"]) + + # it is easier to work from the source directory + oldcwd = os.getcwd() + os.chdir(source) + + if info["library"]: + print "Injecting files into", info["library"] + libraryFile = os.path.join(root, info["library"]) + + with zipfile.ZipFile(libraryFile, 'a') as library: + for dir in setup['packages']: + zipdir(dir, library) + library.write('pyfa.py', 'pyfa__main__.py') + library.write('config.py') + else: # platforms where we don't have a packaged library + print "Copying modules into", root + for dir in setup['packages']: + copyanything(dir, os.path.join(root, dir)) + + # add some additional files to root dir for these platforms + # (hopefully can figure out a way later for OS X to use the one in + # it's library) + if skel == 'mac': + setup['include_files'] += ['pyfa.py'] + if skel in ('src', 'mac-deprecated'): + setup['include_files'] += ['pyfa.py', 'config.py'] + + print + print "Copying included files:", + + for file in setup['include_files']: + if isinstance(file, basestring): + print file, + copyanything(file, os.path.join(root, file)) + + print + print "Creating images zipfile:", + os.chdir('imgs') + imagesFile = os.path.join(root, "imgs.zip") + + with zipfile.ZipFile(imagesFile, 'w') as images: + for dir in setup['icon_dirs']: + print dir, + zipdir(dir, images) + os.chdir(oldcwd) + + print + print "Creating archive" + if options.zip: + archive = zipfile.ZipFile(tmpFile, 'w', compression=zipfile.ZIP_DEFLATED) + zipdir(dirName, archive) + archive.close() + else: + archive = tarfile.open(tmpFile, "w:bz2") + archive.add(tmpDir, arcname=info["arcname"]) + archive.close() + + print "Moving archive to ", destination + shutil.move(tmpFile, destination) + + if "win" in skel and options.winexe: + print "Compiling EXE" + + if config['tag'].lower() == "git": + if git: # if git repo info available, use git commit + expansion = "git-%s"%(id) + else: # if there is no git repo, use timestamp + expansion = now + else: # if code is Stable, use expansion name + expansion = "%s %s"%(config['expansionName'], config['expansionVersion']), + + calllist = ["wine"] if 'win' not in sys.platform else [] + + call(calllist + [ + iscc, + "pyfa-setup.iss", + "/dMyAppVersion=%s"%(config['version']), + "/dMyAppExpansion=%s"%(expansion), + "/dMyAppDir=pyfa", + "/dMyOutputDir=%s"%destination, + "/dMyOutputFile=%s"%fileName]) #stdout=devnull, stderr=devnull + + print "EXE completed" + + except Exception as e: + print "Encountered an error: \n\t", e + raise + finally: + print "Deleting tmp files\n" + try: + shutil.rmtree("dist") # Inno dir + except: + pass + try: + shutil.rmtree(tmpDir) + except: + pass + try: + os.unlink(tmpFile) + except: + pass + + sys.stdout = oldstd + if os.path.isdir(destination): + print os.path.join(destination, os.path.split(tmpFile)[1]) + else: + print destination diff --git a/scripts/jsonToSql.py b/scripts/jsonToSql.py index 1f431f3d8..173fc59f3 100755 --- a/scripts/jsonToSql.py +++ b/scripts/jsonToSql.py @@ -47,6 +47,7 @@ def main(db, json_path): # Config dict tables = { + "clonegrades": eos.gamedata.AlphaCloneSkill, "dgmattribs": eos.gamedata.AttributeInfo, "dgmeffects": eos.gamedata.EffectInfo, "dgmtypeattribs": eos.gamedata.Attribute, @@ -60,7 +61,8 @@ def main(db, json_path): "evetypes": eos.gamedata.Item, "phbtraits": eos.gamedata.Traits, "phbmetadata": eos.gamedata.MetaData, - "mapbulk_marketGroups": eos.gamedata.MarketGroup + "mapbulk_marketGroups": eos.gamedata.MarketGroup, + } fieldMapping = { @@ -109,6 +111,19 @@ def main(db, json_path): new.append(v) return new + def convertClones(data): + newData = [] + + for ID in data: + for skill in data[ID]["skills"]: + newData.append({ + "alphaCloneID": int(ID), + "alphaCloneName": data[ID]["internalDescription"], + "typeID": skill["typeID"], + "level": skill["level"]}) + + return newData + def convertTraits(data): def convertSection(sectionData): @@ -167,6 +182,8 @@ def main(db, json_path): tableData = convertTraits(tableData) if jsonName == "evetypes": tableData = convertTypes(tableData) + if jsonName == "clonegrades": + tableData = convertClones(tableData) data[jsonName] = tableData # Set with typeIDs which we will have in our database @@ -191,7 +208,10 @@ def main(db, json_path): # Loop through each json file and write it away, checking ignored rows for jsonName, table in data.iteritems(): fieldMap = fieldMapping.get(jsonName, {}) + tmp = [] + print "processing {}".format(jsonName) + for row in table: # We don't care about some kind of rows, filter it out if so if not isIgnored(jsonName, row): @@ -207,6 +227,14 @@ def main(db, json_path): if jsonName is "icons" and "modules/" in str(row["iconFile"]).lower(): row["iconFile"] = row["iconFile"].lower().replace("modules/", "").replace(".png", "") + if jsonName is "clonegrades": + if (row["alphaCloneID"] not in tmp): + cloneParent = eos.gamedata.AlphaClone() + setattr(cloneParent, "alphaCloneID", row["alphaCloneID"]) + setattr(cloneParent, "alphaCloneName", row["alphaCloneName"]) + eos.db.gamedata_session.add(cloneParent) + tmp.append(row['alphaCloneID']) + for k, v in row.iteritems(): if (isinstance(v, basestring)): v = v.strip() diff --git a/scripts/prep_data.py b/scripts/prep_data.py index 5df4d715f..7de1cfdc3 100644 --- a/scripts/prep_data.py +++ b/scripts/prep_data.py @@ -80,7 +80,7 @@ if not args.nojson: list = "dgmexpressions,dgmattribs,dgmeffects,dgmtypeattribs,dgmtypeeffects,"\ "dgmunits,invcategories,invgroups,invmetagroups,invmetatypes,"\ "invtypes,mapbulk_marketGroups,phbmetadata,phbtraits,fsdTypeOverrides,"\ - "evegroups,evetypes,evecategories,mapbulk_marketGroups" + "evegroups,evetypes,evecategories,mapbulk_marketGroups,clonegrades" FlowManager(miners, writers).run(list, "en-us") diff --git a/scripts/upx.exe b/scripts/upx.exe new file mode 100644 index 000000000..71f688acb Binary files /dev/null and b/scripts/upx.exe differ diff --git a/service/__init__.py b/service/__init__.py index 91ed07d82..e69de29bb 100644 --- a/service/__init__.py +++ b/service/__init__.py @@ -1,18 +0,0 @@ -from service.market import Market -from service.fit import Fit -from service.attribute import Attribute -from service.character import Character -from service.damagePattern import DamagePattern -from service.targetResists import TargetResists -from service.settings import SettingsProvider -from service.update import Update -from service.price import Price -from service.network import Network -from service.eveapi import EVEAPIConnection, ParseXML -from service.implantSet import ImplantSets - -import wx -if not 'wxMac' in wx.PlatformInfo or ('wxMac' in wx.PlatformInfo and wx.VERSION >= (3,0)): - from service.pycrest import EVE - from service.server import StoppableHTTPServer, AuthHandler - from service.crest import Crest diff --git a/service/attribute.py b/service/attribute.py index b6a9ea500..5b010bcc5 100644 --- a/service/attribute.py +++ b/service/attribute.py @@ -1,4 +1,4 @@ -#=============================================================================== +# ============================================================================= # Copyright (C) 2010 Diego Duclos # # This file is part of pyfa. @@ -15,12 +15,14 @@ # # You should have received a copy of the GNU General Public License # along with pyfa. If not, see . -#=============================================================================== +# ============================================================================= import eos.db -class Attribute(): + +class Attribute(object): instance = None + @classmethod def getInstance(cls): if cls.instance is None: @@ -28,10 +30,13 @@ class Attribute(): return cls.instance - def getAttributeInfo(self, identity): + @staticmethod + def getAttributeInfo(identity): if isinstance(identity, (int, basestring)): info = eos.db.getAttributeInfo(identity, eager=("icon", "unit")) elif isinstance(identity, (int, float)): - id = int(identity) - info = eos.db.getAttributeInfo(id, eager=("icon", "unit")) + id_ = int(identity) + info = eos.db.getAttributeInfo(id_, eager=("icon", "unit")) + else: + info = None return info diff --git a/service/character.py b/service/character.py index d439f1cd4..4eea8e54b 100644 --- a/service/character.py +++ b/service/character.py @@ -1,4 +1,4 @@ -#=============================================================================== +# ============================================================================= # Copyright (C) 2010 Diego Duclos # # This file is part of pyfa. @@ -15,32 +15,37 @@ # # You should have received a copy of the GNU General Public License # along with pyfa. If not, see . -#=============================================================================== +# ============================================================================= import copy import itertools import json +from logbook import Logger import threading from codecs import open from xml.etree import ElementTree from xml.dom import minidom import gzip +# noinspection PyPackageRequirements import wx -import eos.db -import eos.types -import service import config -import logging +import eos.db +from service.eveapi import EVEAPIConnection, ParseXML +from eos.saveddata.implant import Implant as es_Implant from eos.saveddata.character import Character as es_Character +from eos.saveddata.module import Slot as es_Slot, Module as es_Module +from eos.saveddata.fighter import Fighter as es_Fighter + +pyfalog = Logger(__name__) -logger = logging.getLogger(__name__) class CharacterImportThread(threading.Thread): def __init__(self, paths, callback): threading.Thread.__init__(self) + self.name = "CharacterImport" self.paths = paths self.callback = callback @@ -57,8 +62,8 @@ class CharacterImportThread(threading.Thread): try: # we try to parse api XML data first with open(path, mode='r') as charFile: - sheet = service.ParseXML(charFile) - char = sCharacter.new(sheet.name+" (imported)") + sheet = ParseXML(charFile) + char = sCharacter.new(sheet.name + " (imported)") sCharacter.apiUpdateCharSheet(char.ID, sheet.skills) except: # if it's not api XML data, try this @@ -67,7 +72,7 @@ class CharacterImportThread(threading.Thread): charFile = open(path, mode='r').read() doc = minidom.parseString(charFile) if doc.documentElement.tagName not in ("SerializableCCPCharacter", "SerializableUriCharacter"): - logger.error("Incorrect EVEMon XML sheet") + pyfalog.error("Incorrect EVEMon XML sheet") raise RuntimeError("Incorrect EVEMon XML sheet") name = doc.getElementsByTagName("name")[0].firstChild.nodeValue skill_els = doc.getElementsByTagName("skill") @@ -79,23 +84,26 @@ class CharacterImportThread(threading.Thread): "level": int(skill.getAttribute("level")), }) else: - logger.error("Attempted to import unknown skill %s (ID: %s) (Level: %s)", - skill.getAttribute("name"), - skill.getAttribute("typeID"), - skill.getAttribute("level"), - ) - char = sCharacter.new(name+" (EVEMon)") + pyfalog.error( + "Attempted to import unknown skill {0} (ID: {1}) (Level: {2})", + skill.getAttribute("name"), + skill.getAttribute("typeID"), + skill.getAttribute("level"), + ) + char = sCharacter.new(name + " (EVEMon)") sCharacter.apiUpdateCharSheet(char.ID, skills) except Exception, e: - logger.error("Exception on character import:") - logger.error(e) + pyfalog.error("Exception on character import:") + pyfalog.error(e) continue wx.CallAfter(self.callback) + class SkillBackupThread(threading.Thread): def __init__(self, path, saveFmt, activeFit, callback): threading.Thread.__init__(self) + self.name = "SkillBackup" self.path = path self.saveFmt = saveFmt self.activeFit = activeFit @@ -104,9 +112,6 @@ class SkillBackupThread(threading.Thread): def run(self): path = self.path sCharacter = Character.getInstance() - sFit = service.Fit.getInstance() - fit = sFit.getFit(self.activeFit) - backupData = "" if self.saveFmt == "xml" or self.saveFmt == "emp": backupData = sCharacter.exportXml() else: @@ -116,11 +121,12 @@ class SkillBackupThread(threading.Thread): with gzip.open(path, mode='wb') as backupFile: backupFile.write(backupData) else: - with open(path, mode='w',encoding='utf-8') as backupFile: + with open(path, mode='w', encoding='utf-8') as backupFile: backupFile.write(backupData) wx.CallAfter(self.callback) + class Character(object): instance = None skillReqsDict = {} @@ -138,7 +144,7 @@ class Character(object): self.all5() def exportText(self): - data = "Pyfa exported plan for \""+self.skillReqsDict['charname']+"\"\n" + data = "Pyfa exported plan for \"" + self.skillReqsDict['charname'] + "\"\n" data += "=" * 79 + "\n" data += "\n" item = "" @@ -154,7 +160,7 @@ class Character(object): def exportXml(self): root = ElementTree.Element("plan") - root.attrib["name"] = "Pyfa exported plan for "+self.skillReqsDict['charname'] + root.attrib["name"] = "Pyfa exported plan for " + self.skillReqsDict['charname'] root.attrib["revision"] = config.evemonMinVersion sorts = ElementTree.SubElement(root, "sorting") @@ -165,9 +171,9 @@ class Character(object): skillsSeen = set() for s in self.skillReqsDict['skills']: - skillKey = str(s["skillID"])+"::"+s["skill"]+"::"+str(int(s["level"])) + skillKey = str(s["skillID"]) + "::" + s["skill"] + "::" + str(int(s["level"])) if skillKey in skillsSeen: - pass # Duplicate skills confuse EVEMon + pass # Duplicate skills confuse EVEMon else: skillsSeen.add(skillKey) entry = ElementTree.SubElement(root, "entry") @@ -179,36 +185,46 @@ class Character(object): notes = ElementTree.SubElement(entry, "notes") notes.text = entry.attrib["skill"] - tree = ElementTree.ElementTree(root) + # tree = ElementTree.ElementTree(root) data = ElementTree.tostring(root, 'utf-8') prettydata = minidom.parseString(data).toprettyxml(indent=" ") return prettydata - def backupSkills(self, path, saveFmt, activeFit, callback): + @staticmethod + def backupSkills(path, saveFmt, activeFit, callback): thread = SkillBackupThread(path, saveFmt, activeFit, callback) thread.start() - def importCharacter(self, path, callback): + @staticmethod + def importCharacter(path, callback): thread = CharacterImportThread(path, callback) thread.start() - def all0(self): - return eos.types.Character.getAll0() + @staticmethod + def all0(): + return es_Character.getAll0() def all0ID(self): return self.all0().ID - def all5(self): - return eos.types.Character.getAll5() + @staticmethod + def all5(): + return es_Character.getAll5() def all5ID(self): return self.all5().ID - def getCharacterList(self): + @staticmethod + def getAlphaCloneList(): + return eos.db.getAlphaCloneList() + + @staticmethod + def getCharacterList(): return eos.db.getCharacterList() - def getCharacter(self, charID): + @staticmethod + def getCharacter(charID): char = eos.db.getCharacter(charID) return char @@ -219,7 +235,8 @@ class Character(object): char = eos.db.getCharacter(charID) char.saveLevels() - def saveCharacterAs(self, charID, newName): + @staticmethod + def saveCharacterAs(charID, newName): """Save edited skills as a new character""" char = eos.db.getCharacter(charID) newChar = copy.deepcopy(char) @@ -229,12 +246,14 @@ class Character(object): # revert old char char.revertLevels() - def revertCharacter(self, charID): + @staticmethod + def revertCharacter(charID): """Rollback edited skills""" char = eos.db.getCharacter(charID) char.revertLevels() - def getSkillGroups(self): + @staticmethod + def getSkillGroups(): cat = eos.db.getCategory(16) groups = [] for grp in cat.groups: @@ -242,69 +261,86 @@ class Character(object): groups.append((grp.ID, grp.name)) return groups - def getSkills(self, groupID): + @staticmethod + def getSkills(groupID): group = eos.db.getGroup(groupID) skills = [] for skill in group.items: - if skill.published == True: + if skill.published is True: skills.append((skill.ID, skill.name)) return skills - def getSkillDescription(self, itemID): + @staticmethod + def setAlphaClone(char, cloneID): + char.alphaCloneID = cloneID + eos.db.commit() + + @staticmethod + def getSkillDescription(itemID): return eos.db.getItem(itemID).description - def getGroupDescription(self, groupID): + @staticmethod + def getGroupDescription(groupID): return eos.db.getMarketGroup(groupID).description - def getSkillLevel(self, charID, skillID): + @staticmethod + def getSkillLevel(charID, skillID): skill = eos.db.getCharacter(charID).getSkill(skillID) - return (skill.level if skill.learned else "Not learned", skill.isDirty) + return skill.level if skill.learned else "Not learned", skill.isDirty - def getDirtySkills(self, charID): + @staticmethod + def getDirtySkills(charID): return eos.db.getCharacter(charID).dirtySkills - def getCharName(self, charID): + @staticmethod + def getCharName(charID): return eos.db.getCharacter(charID).name - def new(self, name="New Character"): - char = eos.types.Character(name) + @staticmethod + def new(name="New Character"): + char = es_Character(name) eos.db.save(char) return char - def rename(self, char, newName): + @staticmethod + def rename(char, newName): if char.name in ("All 0", "All 5"): - logger.info("Cannot rename built in characters.") + pyfalog.info("Cannot rename built in characters.") else: char.name = newName eos.db.commit() - def copy(self, char): + @staticmethod + def copy(char): newChar = copy.deepcopy(char) eos.db.save(newChar) return newChar - def delete(self, char): + @staticmethod + def delete(char): eos.db.remove(char) - def getApiDetails(self, charID): + @staticmethod + def getApiDetails(charID): char = eos.db.getCharacter(charID) if char.chars is not None: chars = json.loads(char.chars) else: chars = None - return (char.apiID or "", char.apiKey or "", char.defaultChar or "", chars or []) + return char.apiID or "", char.apiKey or "", char.defaultChar or "", chars or [] def apiEnabled(self, charID): - id, key, default, _ = self.getApiDetails(charID) - return id is not "" and key is not "" and default is not "" + id_, key, default, _ = self.getApiDetails(charID) + return id_ is not "" and key is not "" and default is not "" - def apiCharList(self, charID, userID, apiKey): + @staticmethod + def apiCharList(charID, userID, apiKey): char = eos.db.getCharacter(charID) char.apiID = userID char.apiKey = apiKey - api = service.EVEAPIConnection() + api = EVEAPIConnection() auth = api.auth(keyID=userID, vCode=apiKey) apiResult = auth.account.Characters() charList = map(lambda c: unicode(c.name), apiResult.characters) @@ -312,11 +348,12 @@ class Character(object): char.chars = json.dumps(charList) return charList - def apiFetch(self, charID, charName): + @staticmethod + def apiFetch(charID, charName): dbChar = eos.db.getCharacter(charID) dbChar.defaultChar = charName - api = service.EVEAPIConnection() + api = EVEAPIConnection() auth = api.auth(keyID=dbChar.apiID, vCode=dbChar.apiKey) apiResult = auth.account.Characters() charID = None @@ -324,7 +361,7 @@ class Character(object): if char.name == charName: charID = char.characterID - if charID == None: + if charID is None: return sheet = auth.character(charID).CharacterSheet() @@ -332,12 +369,14 @@ class Character(object): dbChar.apiUpdateCharSheet(sheet.skills) eos.db.commit() - def apiUpdateCharSheet(self, charID, skills): + @staticmethod + def apiUpdateCharSheet(charID, skills): char = eos.db.getCharacter(charID) char.apiUpdateCharSheet(skills) eos.db.commit() - def changeLevel(self, charID, skillID, level, persist=False): + @staticmethod + def changeLevel(charID, skillID, level, persist=False): char = eos.db.getCharacter(charID) skill = char.getSkill(skillID) if isinstance(level, basestring) or level > 5 or level < 0: @@ -350,50 +389,55 @@ class Character(object): eos.db.commit() - def revertLevel(self, charID, skillID): + @staticmethod + def revertLevel(charID, skillID): char = eos.db.getCharacter(charID) skill = char.getSkill(skillID) skill.revert() - def saveSkill(self, charID, skillID): + @staticmethod + def saveSkill(charID, skillID): char = eos.db.getCharacter(charID) skill = char.getSkill(skillID) skill.saveLevel() - def addImplant(self, charID, itemID): + @staticmethod + def addImplant(charID, itemID): char = eos.db.getCharacter(charID) if char.ro: - logger.error("Trying to add implant to read-only character") + pyfalog.error("Trying to add implant to read-only character") return - implant = eos.types.Implant(eos.db.getItem(itemID)) + implant = es_Implant(eos.db.getItem(itemID)) char.implants.append(implant) eos.db.commit() - def removeImplant(self, charID, implant): + @staticmethod + def removeImplant(charID, implant): char = eos.db.getCharacter(charID) char.implants.remove(implant) eos.db.commit() - def getImplants(self, charID): + @staticmethod + def getImplants(charID): char = eos.db.getCharacter(charID) return char.implants def checkRequirements(self, fit): - toCheck = [] + # toCheck = [] reqs = {} for thing in itertools.chain(fit.modules, fit.drones, fit.fighters, (fit.ship,)): - if isinstance(thing, eos.types.Module) and thing.slot == eos.types.Slot.RIG: + if isinstance(thing, es_Module) and thing.slot == es_Slot.RIG: continue for attr in ("item", "charge"): - if attr == "charge" and isinstance(thing, eos.types.Fighter): + if attr == "charge" and isinstance(thing, es_Fighter): # Fighter Bombers are automatically charged with micro bombs. # These have skill requirements attached, but aren't used in EVE. continue subThing = getattr(thing, attr, None) subReqs = {} if subThing is not None: - if isinstance(thing, eos.types.Fighter) and attr == "charge": + if isinstance(thing, es_Fighter) and attr == "charge": continue self._checkRequirements(fit, fit.character, subThing, subReqs) if subReqs: diff --git a/service/conversions/releaseApril2016.py b/service/conversions/releaseApril2016.py index a20914179..bd14944ee 100644 --- a/service/conversions/releaseApril2016.py +++ b/service/conversions/releaseApril2016.py @@ -42,4 +42,4 @@ CONVERSIONS = { "Unit W-634's Modified Drone Control Unit": "Unit W-634's Modified Fighter Support Unit", "Heavy Shadow Serpentis Stasis Grappler": "Shadow Serpentis Heavy Stasis Grappler", "Heavy Domination Stasis Grappler": "Domination Heavy Stasis Grappler", -} \ No newline at end of file +} diff --git a/service/conversions/releaseDecember15.py b/service/conversions/releaseDecember15.py index adcd9e552..456dedb59 100644 --- a/service/conversions/releaseDecember15.py +++ b/service/conversions/releaseDecember15.py @@ -96,4 +96,4 @@ CONVERSIONS = { "Micro S95a Remote Shield Booster": "'Micro' Remote Shield Booster", "Large 'Atonement' Remote Shield Booster": "Large Murky Compact Remote Shield Booster", "E50 Prototype Energy Vampire": "Medium Knave Scoped Energy Nosferatu", -} \ No newline at end of file +} diff --git a/service/conversions/releaseFeb2016.py b/service/conversions/releaseFeb2016.py index 4314ef173..9b2e8d838 100644 --- a/service/conversions/releaseFeb2016.py +++ b/service/conversions/releaseFeb2016.py @@ -7,4 +7,3 @@ CONVERSIONS = { "Capital Coaxial Remote Armor Repairer Blueprint": "CONCORD Capital Remote Armor Repairer Blueprint", "Capital Murky Remote Shield Booster Blueprint": "CONCORD Capital Remote Shield Booster Blueprint", } - diff --git a/service/conversions/releaseJan2016.py b/service/conversions/releaseJan2016.py index e5f9beba3..eb0763d6b 100644 --- a/service/conversions/releaseJan2016.py +++ b/service/conversions/releaseJan2016.py @@ -10,4 +10,4 @@ CONVERSIONS = { "'Distributor' Guidance Disruptor I Blueprint": "'Distributor' Guidance Disruptor Blueprint", "Highstroke Scoped Guidance Disruptor I": "Highstroke Scoped Guidance Disruptor", "A-211 Enduring Guidance Disruptor I": "A-211 Enduring Guidance Disruptor", -} \ No newline at end of file +} diff --git a/service/conversions/releaseMar2016.py b/service/conversions/releaseMar2016.py index 2af04e1af..65e26adb1 100644 --- a/service/conversions/releaseMar2016.py +++ b/service/conversions/releaseMar2016.py @@ -118,7 +118,6 @@ CONVERSIONS = { "'Full Duplex' Ballistic Targeting System": "'Full Duplex' Ballistic Control System", "'Kindred' Stabilization Actuator I": "'Kindred' Gyrostabilizer", "Process-Interruptive Warp Disruptor": "'Interruptive' Warp Disruptor", - "Multi Sensor Firewall": "'Firewall' Signal Amplifier", "'Inception' Target Painter I": "'Inception' Target Painter", "Citadel Torpedoes": "XL Torpedoes", "'Shady' ECCM - Gravimetric I": "'Shady' Sensor Booster", @@ -358,4 +357,4 @@ CONVERSIONS = { "Wavelength Signal Enhancer I": "F-89 Compact Signal Amplifier", "Type-D Attenuation Signal Augmentation": "F-89 Compact Signal Amplifier", "Indirect Scanning Dampening Unit I": "Phased Muon Scoped Sensor Dampener", -} \ No newline at end of file +} diff --git a/service/crest.py b/service/crest.py index b2771242b..27f5b6c5e 100644 --- a/service/crest.py +++ b/service/crest.py @@ -1,6 +1,6 @@ +# noinspection PyPackageRequirements import wx -import thread -import logging +from logbook import Logger import threading import copy import uuid @@ -8,26 +8,28 @@ import time import eos.db from eos.enum import Enum -from eos.types import CrestChar - -import service - +from eos.saveddata.crestchar import CrestChar import gui.globalEvents as GE +from service.settings import CRESTSettings +from service.server import StoppableHTTPServer, AuthHandler +from service.pycrest.eve import EVE + +pyfalog = Logger(__name__) -logger = logging.getLogger(__name__) class Servers(Enum): TQ = 0 SISI = 1 + class CrestModes(Enum): IMPLICIT = 0 USER = 1 -class Crest(): +class Crest(object): clientIDs = { - Servers.TQ: 'f9be379951c046339dc13a00e6be7704', + Servers.TQ : 'f9be379951c046339dc13a00e6be7704', Servers.SISI: 'af87365240d644f7950af563b8418bad' } @@ -36,9 +38,10 @@ class Crest(): clientTest = True _instance = None + @classmethod def getInstance(cls): - if cls._instance == None: + if cls._instance is None: cls._instance = Crest() return cls._instance @@ -64,7 +67,7 @@ class Crest(): characters still in the cache (if USER mode) """ - self.settings = service.settings.CRESTSettings.getInstance() + self.settings = CRESTSettings.getInstance() self.scopes = ['characterFittingsRead', 'characterFittingsWrite'] # these will be set when needed @@ -73,11 +76,12 @@ class Crest(): self.ssoTimer = None # Base EVE connection that is copied to all characters - self.eve = service.pycrest.EVE( - client_id=self.settings.get('clientID') if self.settings.get('mode') == CrestModes.USER else self.clientIDs.get(self.settings.get('server')), - api_key=self.settings.get('clientSecret') if self.settings.get('mode') == CrestModes.USER else None, - redirect_uri=self.clientCallback, - testing=self.isTestServer + self.eve = EVE( + client_id=self.settings.get('clientID') if self.settings.get( + 'mode') == CrestModes.USER else self.clientIDs.get(self.settings.get('server')), + api_key=self.settings.get('clientSecret') if self.settings.get('mode') == CrestModes.USER else None, + redirect_uri=self.clientCallback, + testing=self.isTestServer ) self.implicitCharacter = None @@ -114,9 +118,9 @@ class Crest(): return chars2 def getCrestCharacter(self, charID): - ''' + """ Get character, and modify to include the eve connection - ''' + """ if self.settings.get('mode') == CrestModes.IMPLICIT: if self.implicitCharacter.ID != charID: raise ValueError("CharacterID does not match currently logged in character.") @@ -134,54 +138,60 @@ class Crest(): def getFittings(self, charID): char = self.getCrestCharacter(charID) - return char.eve.get('%scharacters/%d/fittings/'%(char.eve._authed_endpoint,char.ID)) + return char.eve.get('%scharacters/%d/fittings/' % (char.eve._authed_endpoint, char.ID)) def postFitting(self, charID, json): - #@todo: new fitting ID can be recovered from Location header, ie: Location -> https://api-sisi.testeveonline.com/characters/1611853631/fittings/37486494/ + # @todo: new fitting ID can be recovered from Location header, + # ie: Location -> https://api-sisi.testeveonline.com/characters/1611853631/fittings/37486494/ char = self.getCrestCharacter(charID) - return char.eve.post('%scharacters/%d/fittings/'%(char.eve._authed_endpoint,char.ID), data=json) + return char.eve.post('%scharacters/%d/fittings/' % (char.eve._authed_endpoint, char.ID), data=json) def delFitting(self, charID, fittingID): char = self.getCrestCharacter(charID) - return char.eve.delete('%scharacters/%d/fittings/%d/'%(char.eve._authed_endpoint, char.ID, fittingID)) + return char.eve.delete('%scharacters/%d/fittings/%d/' % (char.eve._authed_endpoint, char.ID, fittingID)) def logout(self): """Logout of implicit character""" - logging.debug("Character logout") + pyfalog.debug("Character logout") self.implicitCharacter = None wx.PostEvent(self.mainFrame, GE.SsoLogout(type=self.settings.get('mode'))) def stopServer(self): - logging.debug("Stopping Server") + pyfalog.debug("Stopping Server") self.httpd.stop() self.httpd = None def startServer(self): - logging.debug("Starting server") + pyfalog.debug("Starting server") if self.httpd: self.stopServer() - time.sleep(1) # we need this to ensure that the previous get_request finishes, and then the socket will close - self.httpd = service.StoppableHTTPServer(('', 6461), service.AuthHandler) - thread.start_new_thread(self.httpd.serve, (self.handleLogin,)) + time.sleep(1) + # we need this to ensure that the previous get_request finishes, and then the socket will close + self.httpd = StoppableHTTPServer(('localhost', 6461), AuthHandler) + + self.serverThread = threading.Thread(target=self.httpd.serve, args=(self.handleLogin,)) + self.serverThread.name = "CRESTServer" + self.serverThread.daemon = True + self.serverThread.start() self.state = str(uuid.uuid4()) return self.eve.auth_uri(scopes=self.scopes, state=self.state) def handleLogin(self, message): if not message: - return + raise Exception("Could not parse out querystring parameters.") if message['state'][0] != self.state: - logger.warn("OAUTH state mismatch") - return + pyfalog.warn("OAUTH state mismatch") + raise Exception("OAUTH State Mismatch.") - logger.debug("Handling CREST login with: %s"%message) + pyfalog.debug("Handling CREST login with: {0}", message) if 'access_token' in message: # implicit eve = copy.deepcopy(self.eve) eve.temptoken_authorize( - access_token=message['access_token'][0], - expires_in=int(message['expires_in'][0]) + access_token=message['access_token'][0], + expires_in=int(message['expires_in'][0]) ) self.ssoTimer = threading.Timer(int(message['expires_in'][0]), self.logout) self.ssoTimer.start() @@ -189,11 +199,11 @@ class Crest(): eve() info = eve.whoami() - logger.debug("Got character info: %s" % info) + pyfalog.debug("Got character info: {0}", info) self.implicitCharacter = CrestChar(info['CharacterID'], info['CharacterName']) self.implicitCharacter.eve = eve - #self.implicitCharacter.fetchImage() + # self.implicitCharacter.fetchImage() wx.PostEvent(self.mainFrame, GE.SsoLogin(type=CrestModes.IMPLICIT)) elif 'code' in message: @@ -202,7 +212,7 @@ class Crest(): eve() info = eve.whoami() - logger.debug("Got character info: %s" % info) + pyfalog.debug("Got character info: {0}", info) # check if we have character already. If so, simply replace refresh_token char = self.getCrestCharacter(int(info['CharacterID'])) @@ -215,5 +225,3 @@ class Crest(): eos.db.save(char) wx.PostEvent(self.mainFrame, GE.SsoLogin(type=CrestModes.USER)) - - self.stopServer() diff --git a/service/damagePattern.py b/service/damagePattern.py index f617fa846..5a169734c 100644 --- a/service/damagePattern.py +++ b/service/damagePattern.py @@ -1,4 +1,4 @@ -#=============================================================================== +# ============================================================================= # Copyright (C) 2010 Diego Duclos # # This file is part of pyfa. @@ -15,19 +15,21 @@ # # You should have received a copy of the GNU General Public License # along with pyfa. If not, see . -#=============================================================================== +# ============================================================================= -import eos.db -import eos.types import copy -from eos.db.saveddata.loadDefaultDatabaseValues import DefaultDatabaseValues +import eos.db +from eos.saveddata.damagePattern import DamagePattern as es_DamagePattern + class ImportError(Exception): pass -class DamagePattern(): + +class DamagePattern(object): instance = None + @classmethod def getInstance(cls): if cls.instance is None: @@ -35,31 +37,38 @@ class DamagePattern(): return cls.instance - def getDamagePatternList(self): + @staticmethod + def getDamagePatternList(): return eos.db.getDamagePatternList() - def getDamagePattern(self, name): + @staticmethod + def getDamagePattern(name): return eos.db.getDamagePattern(name) - def newPattern(self, name): - p = eos.types.DamagePattern(0, 0, 0, 0) + @staticmethod + def newPattern(name): + p = es_DamagePattern(0, 0, 0, 0) p.name = name eos.db.save(p) return p - def renamePattern(self, p, newName): + @staticmethod + def renamePattern(p, newName): p.name = newName eos.db.save(p) - def deletePattern(self, p): + @staticmethod + def deletePattern(p): eos.db.remove(p) - def copyPattern(self, p): + @staticmethod + def copyPattern(p): newP = copy.deepcopy(p) eos.db.save(newP) return newP - def saveChanges(self, p): + @staticmethod + def saveChanges(p): eos.db.save(p) def importPatterns(self, text): @@ -68,7 +77,7 @@ class DamagePattern(): for pattern in current: lookup[pattern.name] = pattern - imports, num = eos.types.DamagePattern.importPatterns(text) + imports, num = es_DamagePattern.importPatterns(text) for pattern in imports: if pattern.name in lookup: match = lookup[pattern.name] @@ -81,7 +90,7 @@ class DamagePattern(): if lenImports == 0: raise ImportError("No patterns found for import") if lenImports != num: - raise ImportError("%d patterns imported from clipboard; %d had errors"%(num, num-lenImports)) + raise ImportError("%d patterns imported from clipboard; %d had errors" % (num, num - lenImports)) def exportPatterns(self): patterns = self.getDamagePatternList() @@ -90,4 +99,4 @@ class DamagePattern(): del patterns[i] patterns.sort(key=lambda p: p.name) - return eos.types.DamagePattern.exportPatterns(*patterns) + return es_DamagePattern.exportPatterns(*patterns) diff --git a/service/eveapi.py b/service/eveapi.py index 4b19340b0..c4ac681a0 100644 --- a/service/eveapi.py +++ b/service/eveapi.py @@ -1,4 +1,4 @@ -#----------------------------------------------------------------------------- +# ----------------------------------------------------------------------------- # eveapi - EVE Online API access # # Copyright (c)2007-2014 Jamie "Entity" van den Berge @@ -24,7 +24,7 @@ # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR # OTHER DEALINGS IN THE SOFTWARE # -#----------------------------------------------------------------------------- +# ----------------------------------------------------------------------------- # # Version: 1.3.0 - 27 May 2014 # - Added set_user_agent() module-level function to set the User-Agent header @@ -145,10 +145,10 @@ # Requirements: # Python 2.4+ # -#----------------------------------------------------------------------------- +# ----------------------------------------------------------------------------- -#----------------------------------------------------------------------------- +# ----------------------------------------------------------------------------- # This eveapi has been modified for pyfa. # # Specifically, the entire network request/response has been substituted for @@ -156,7 +156,7 @@ # # Additionally, various other parts have been changed to support urllib2 # responses instead of httplib -#----------------------------------------------------------------------------- +# ----------------------------------------------------------------------------- import urlparse @@ -166,7 +166,7 @@ from xml.parsers import expat from time import strptime from calendar import timegm -import service +from service.network import Network proxy = None proxySSL = False @@ -174,7 +174,9 @@ proxySSL = False _default_useragent = "eveapi.py/1.3" _useragent = None # use set_user_agent() to set this. -#----------------------------------------------------------------------------- + +# ----------------------------------------------------------------------------- + def set_cast_func(func): """Sets an alternative value casting function for the XML parser. @@ -185,25 +187,30 @@ def set_cast_func(func): global _castfunc _castfunc = _autocast if func is None else func + def set_user_agent(user_agent_string): """Sets a User-Agent for any requests sent by the library.""" global _useragent _useragent = user_agent_string -class Error(StandardError): +class Error(Exception): def __init__(self, code, message): self.code = code self.args = (message.rstrip("."),) + def __unicode__(self): return u'%s [code=%s]' % (self.args[0], self.code) + class RequestError(Error): pass + class AuthenticationError(Error): pass + class ServerError(Error): pass @@ -304,18 +311,16 @@ def _ParseXML(response, fromContext, storeFunc): return result - - - -#----------------------------------------------------------------------------- +# ----------------------------------------------------------------------------- # API Classes -#----------------------------------------------------------------------------- +# ----------------------------------------------------------------------------- + _listtypes = (list, tuple, dict) _unspecified = [] -class _Context(object): +class _Context(object): def __init__(self, root, path, parentDict, newKeywords=None): self._root = root or self self._path = path @@ -356,20 +361,18 @@ class _Context(object): class _AuthContext(_Context): - def character(self, characterID): # returns a copy of this connection object but for every call made # through it, it will add the folder "/char" to the url, and the # characterID to the parameters passed. - return _Context(self._root, self._path + "/char", self.parameters, {"characterID":characterID}) + return _Context(self._root, self._path + "/char", self.parameters, {"characterID": characterID}) def corporation(self, characterID): # same as character except for the folder "/corp" - return _Context(self._root, self._path + "/corp", self.parameters, {"characterID":characterID}) + return _Context(self._root, self._path + "/corp", self.parameters, {"characterID": characterID}) class _RootContext(_Context): - def auth(self, **kw): if len(kw) == 2 and (("keyID" in kw and "vCode" in kw) or ("userID" in kw and "apiKey" in kw)): return _AuthContext(self._root, self._path, self.parameters, kw) @@ -395,9 +398,9 @@ class _RootContext(_Context): response = None if response is None: - network = service.Network.getInstance() + network = Network.getInstance() - req = self._scheme+'://'+self._host+path + req = self._scheme + '://' + self._host + path response = network.request(req, network.EVE, kw) @@ -413,8 +416,9 @@ class _RootContext(_Context): if retrieve_fallback: # implementor is handling fallbacks... try: - return _ParseXML(response, True, store and (lambda obj: cache.store(self._host, path, kw, response, obj))) - except Error, e: + return _ParseXML(response, True, + store and (lambda obj: cache.store(self._host, path, kw, response, obj))) + except Error as e: response = retrieve_fallback(self._host, path, kw, reason=e) if response is not None: return response @@ -423,9 +427,11 @@ class _RootContext(_Context): # implementor is not handling fallbacks... return _ParseXML(response, True, store and (lambda obj: cache.store(self._host, path, kw, response, obj))) -#----------------------------------------------------------------------------- + +# ----------------------------------------------------------------------------- # XML Parser -#----------------------------------------------------------------------------- +# ----------------------------------------------------------------------------- + def _autocast(key, value): # attempts to cast an XML string to the most probable type. @@ -452,11 +458,11 @@ def _autocast(key, value): # couldn't cast. return string unchanged. return value + _castfunc = _autocast class _Parser(object): - def Parse(self, data, isStream=False): self.container = self.root = None self._cdata = False @@ -475,7 +481,6 @@ class _Parser(object): p.Parse(data, True) return self.root - def tag_cdatasection_enter(self): # encountered an explicit CDATA tag. self._cdata = True @@ -501,21 +506,20 @@ class _Parser(object): if name == "rowset": # for rowsets, use the given name try: - columns = attributes[attributes.index('columns')+1].replace(" ", "").split(",") + columns = attributes[attributes.index('columns') + 1].replace(" ", "").split(",") except ValueError: # rowset did not have columns tag set (this is a bug in API) # columns will be extracted from first row instead. columns = [] try: - priKey = attributes[attributes.index('key')+1] + priKey = attributes[attributes.index('key') + 1] this = IndexRowset(cols=columns, key=priKey) except ValueError: this = Rowset(cols=columns) - - this._name = attributes[attributes.index('name')+1] - this.__catch = "row" # tag to auto-add to rowset. + this._name = attributes[attributes.index('name') + 1] + this.__catch = "row" # tag to auto-add to rowset. else: this = Element() this._name = name @@ -528,7 +532,7 @@ class _Parser(object): if name != "eveapi": raise RuntimeError("Invalid API response") try: - this.version = attributes[attributes.index("version")+1] + this.version = attributes[attributes.index("version") + 1] except KeyError: raise RuntimeError("Invalid API response") self.root = this @@ -541,16 +545,18 @@ class _Parser(object): # such as rawQuantity in the assets lists. # In either case the tag is assumed to be correct and the rowset's # columns are overwritten with the tag's version, if required. - numAttr = len(attributes)/2 + numAttr = len(attributes) / 2 numCols = len(self.container._cols) if numAttr < numCols and (attributes[-2] == self.container._cols[-1]): # the row data is missing attributes that were defined in the rowset. # missing attributes' values will be set to None. fixed = [] - row_idx = 0; hdr_idx = 0; numAttr*=2 + row_idx = 0 + hdr_idx = 0 + numAttr *= 2 for col in self.container._cols: if col == attributes[row_idx]: - fixed.append(_castfunc(col, attributes[row_idx+1])) + fixed.append(_castfunc(col, attributes[row_idx + 1])) row_idx += 2 else: fixed.append(None) @@ -560,7 +566,9 @@ class _Parser(object): if not self.container._cols or (numAttr > numCols): # the row data contains more attributes than were defined. self.container._cols = attributes[0::2] - self.container.append([_castfunc(attributes[i], attributes[i+1]) for i in xrange(0, len(attributes), 2)]) + self.container.append( + [_castfunc(attributes[i], attributes[i + 1]) for i in xrange(0, len(attributes), 2)] + ) # this._isrow = True @@ -611,7 +619,7 @@ class _Parser(object): if this is self.root: del this._attributes - #this.__dict__.pop("_attributes", None) + # this.__dict__.pop("_attributes", None) return # we're done with current tag, so we can pop it off. This means that @@ -651,7 +659,7 @@ class _Parser(object): e._name = this._name setattr(self.container, this._name, e) for i in xrange(0, len(attributes), 2): - setattr(e, attributes[i], attributes[i+1]) + setattr(e, attributes[i], attributes[i + 1]) else: # tag of the form: , treat as empty string. setattr(self.container, this._name, "") @@ -663,7 +671,7 @@ class _Parser(object): # multiples of some tag or attribute. Code below handles this case. elif isinstance(sibling, Rowset): # its doppelganger is a rowset, append this as a row to that. - row = [_castfunc(attributes[i], attributes[i+1]) for i in xrange(0, len(attributes), 2)] + row = [_castfunc(attributes[i], attributes[i + 1]) for i in xrange(0, len(attributes), 2)] row.extend([getattr(this, col) for col in attributes2]) sibling.append(row) elif isinstance(sibling, Element): @@ -672,11 +680,13 @@ class _Parser(object): # into a Rowset, adding the sibling element and this one. rs = Rowset() rs.__catch = rs._name = this._name - row = [_castfunc(attributes[i], attributes[i+1]) for i in xrange(0, len(attributes), 2)]+[getattr(this, col) for col in attributes2] + row = [_castfunc(attributes[i], attributes[i + 1]) for i in xrange(0, len(attributes), 2)] + \ + [getattr(this, col) for col in attributes2] rs.append(row) - row = [getattr(sibling, attributes[i]) for i in xrange(0, len(attributes), 2)]+[getattr(sibling, col) for col in attributes2] + row = [getattr(sibling, attributes[i]) for i in xrange(0, len(attributes), 2)] + \ + [getattr(sibling, col) for col in attributes2] rs.append(row) - rs._cols = [attributes[i] for i in xrange(0, len(attributes), 2)]+[col for col in attributes2] + rs._cols = [attributes[i] for i in xrange(0, len(attributes), 2)] + [col for col in attributes2] setattr(self.container, this._name, rs) else: # something else must have set this attribute already. @@ -685,29 +695,33 @@ class _Parser(object): # Now fix up the attributes and be done with it. for i in xrange(0, len(attributes), 2): - this.__dict__[attributes[i]] = _castfunc(attributes[i], attributes[i+1]) + this.__dict__[attributes[i]] = _castfunc(attributes[i], attributes[i + 1]) return - - -#----------------------------------------------------------------------------- +# ----------------------------------------------------------------------------- # XML Data Containers -#----------------------------------------------------------------------------- +# ----------------------------------------------------------------------------- # The following classes are the various container types the XML data is # unpacked into. # # Note that objects returned by API calls are to be treated as read-only. This # is not enforced, but you have been warned. -#----------------------------------------------------------------------------- +# ----------------------------------------------------------------------------- + class Element(object): + _name = None + # Element is a namespace for attributes and nested tags def __str__(self): return "" % self._name + _fmt = u"%s:%s".__mod__ + + class Row(object): # A Row is a single database record associated with a Rowset. # The fields in the record are accessed as attributes by their respective @@ -750,7 +764,7 @@ class Row(object): try: return self._row[self._cols.index(this)] except: - raise AttributeError, this + raise AttributeError(this) def __getitem__(self, this): return self._row[self._cols.index(this)] @@ -823,7 +837,6 @@ class Rowset(object): for line in self._rows: yield [line[x] for x in i] - # ------------- def __init__(self, cols=None, rows=None): @@ -862,16 +875,15 @@ class Rowset(object): self._rows.sort(*args, **kw) def __str__(self): - return ("Rowset(columns=[%s], rows=%d)" % (','.join(self._cols), len(self))) + return "Rowset(columns=[%s], rows=%d)" % (','.join(self._cols), len(self)) def __getstate__(self): - return (self._cols, self._rows) + return self._cols, self._rows def __setstate__(self, state): self._cols, self._rows = state - class IndexRowset(Rowset): # An IndexRowset is a Rowset that keeps an index on a column. # @@ -888,7 +900,7 @@ class IndexRowset(Rowset): if row is None: if default: return default[0] - raise KeyError, key + raise KeyError(key) return Row(self._cols, row) # ------------- @@ -925,7 +937,7 @@ class IndexRowset(Rowset): self._items[row[self._ki]] = row def __getstate__(self): - return (Rowset.__getstate__(self), self._items, self._ki) + return Rowset.__getstate__(self), self._items, self._ki def __setstate__(self, state): state, self._items, self._ki = state @@ -939,28 +951,28 @@ class FilterRowset(object): # - Each key maps to a Rowset, containing only the rows where the value # of the column this FilterRowset was made on matches the key. - def __init__(self, cols=None, rows=None, key=None, key2=None, dict=None): - if dict is not None: - self._items = items = dict + def __init__(self, cols=None, rows=None, key=None, key2=None, dict_=None): + if dict_ is not None: + self._items = items = dict_ elif cols is not None: self._items = items = {} idfield = cols.index(key) if not key2: for row in rows: - id = row[idfield] - if id in items: - items[id].append(row) + id_ = row[idfield] + if id_ in items: + items[id_].append(row) else: - items[id] = [row] + items[id_] = [row] else: idfield2 = cols.index(key2) for row in rows: - id = row[idfield] - if id in items: - items[id][row[idfield2]] = row + id_ = row[idfield] + if id_ in items: + items[id_][row[idfield2]] = row else: - items[id] = {row[idfield2]:row} + items[id_] = {row[idfield2]: row} self._cols = cols self.key = key @@ -977,7 +989,7 @@ class FilterRowset(object): self.__iter__ = items.__iter__ def copy(self): - return FilterRowset(self._cols[:], None, self.key, self.key2, dict=copy.deepcopy(self._items)) + return FilterRowset(self._cols[:], None, self.key, self.key2, dict_=copy.deepcopy(self._items)) def get(self, key, default=_unspecified): try: @@ -993,7 +1005,7 @@ class FilterRowset(object): return Rowset(self._cols, self._items[i]) def __getstate__(self): - return (self._cols, self._rows, self._items, self.key, self.key2) + return self._cols, self._rows, self._items, self.key, self.key2 def __setstate__(self, state): self._cols, self._rows, self._items, self.key, self.key2 = state diff --git a/service/fit.py b/service/fit.py index 0373bb523..9eeb348fe 100644 --- a/service/fit.py +++ b/service/fit.py @@ -17,63 +17,26 @@ # along with pyfa. If not, see . # =============================================================================== -import locale import copy -import threading -import logging -import wx -from codecs import open - -import xml.parsers.expat +from logbook import Logger import eos.db -import eos.types - -from eos.types import State, Slot - -from service.market import Market -from service.damagePattern import DamagePattern +from eos.saveddata.booster import Booster as es_Booster +from eos.saveddata.cargo import Cargo as es_Cargo +from eos.saveddata.character import Character as saveddata_Character +from eos.saveddata.citadel import Citadel as es_Citadel +from eos.saveddata.damagePattern import DamagePattern as es_DamagePattern +from eos.saveddata.drone import Drone as es_Drone +from eos.saveddata.fighter import Fighter as es_Fighter +from eos.saveddata.implant import Implant as es_Implant +from eos.saveddata.ship import Ship as es_Ship +from eos.saveddata.module import Module as es_Module, State, Slot +from eos.saveddata.fit import Fit as FitType from service.character import Character +from service.damagePattern import DamagePattern from service.settings import SettingsProvider -from service.port import Port -logger = logging.getLogger(__name__) - - -class FitBackupThread(threading.Thread): - def __init__(self, path, callback): - threading.Thread.__init__(self) - self.path = path - self.callback = callback - - def run(self): - path = self.path - sFit = Fit.getInstance() - allFits = map(lambda x: x[0], sFit.getAllFits()) - backedUpFits = sFit.exportXml(self.callback, *allFits) - backupFile = open(path, "w", encoding="utf-8") - backupFile.write(backedUpFits) - backupFile.close() - - # Send done signal to GUI - wx.CallAfter(self.callback, -1) - - -class FitImportThread(threading.Thread): - def __init__(self, paths, callback): - threading.Thread.__init__(self) - self.paths = paths - self.callback = callback - - def run(self): - sFit = Fit.getInstance() - success, result = sFit.importFitFromFiles(self.paths, self.callback) - - if not success: # there was an error during processing - logger.error("Error while processing file import: %s", result) - wx.CallAfter(self.callback, -2, result) - else: # Send done signal to GUI - wx.CallAfter(self.callback, -1, result) +pyfalog = Logger(__name__) class Fit(object): @@ -87,9 +50,10 @@ class Fit(object): return cls.instance def __init__(self): + pyfalog.debug("Initialize Fit class") self.pattern = DamagePattern.getInstance().getDamagePattern("Uniform") self.targetResists = None - self.character = Character.getInstance().all5() + self.character = saveddata_Character.getAll5() self.booster = False self.dirtyFitIDs = set() @@ -106,21 +70,20 @@ class Fit(object): "showMarketShortcuts": False, "enableGaugeAnimation": True, "exportCharges": True, - "openFitInNew":False - } + "openFitInNew": False, + "priceSystem": "Jita", + } self.serviceFittingOptions = SettingsProvider.getInstance().getSettings( "pyfaServiceFittingOptions", serviceFittingDefaultOptions) - def getAllFits(self): + @staticmethod + def getAllFits(): fits = eos.db.getFitList() - names = [] - for fit in fits: - names.append((fit.ID, fit.name)) + return fits - return names - - def getFitsWithShip(self, shipID): + @staticmethod + def getFitsWithShip(shipID): """ Lists fits of shipID, used with shipBrowser """ fits = eos.db.getFitsWithShip(shipID) names = [] @@ -129,7 +92,8 @@ class Fit(object): return names - def getBoosterFits(self): + @staticmethod + def getBoosterFits(): """ Lists fits flagged as booster """ fits = eos.db.getBoosterFits() names = [] @@ -138,32 +102,26 @@ class Fit(object): return names - def countAllFits(self): + @staticmethod + def countAllFits(): return eos.db.countAllFits() - def countFitsWithShip(self, shipID): - count = eos.db.countFitsWithShip(shipID) + @staticmethod + def countFitsWithShip(stuff): + count = eos.db.countFitsWithShip(stuff) return count - def groupHasFits(self, groupID): - sMkt = Market.getInstance() - grp = sMkt.getGroup(groupID) - items = sMkt.getItemsByGroup(grp) - for item in items: - if self.countFitsWithShip(item.ID) > 0: - return True - return False - - def getModule(self, fitID, pos): + @staticmethod + def getModule(fitID, pos): fit = eos.db.getFit(fitID) return fit.modules[pos] def newFit(self, shipID, name=None): try: - ship = eos.types.Ship(eos.db.getItem(shipID)) + ship = es_Ship(eos.db.getItem(shipID)) except ValueError: - ship = eos.types.Citadel(eos.db.getItem(shipID)) - fit = eos.types.Fit(ship) + ship = es_Citadel(eos.db.getItem(shipID)) + fit = FitType(ship) fit.name = name if name is not None else "New %s" % fit.ship.item.name fit.damagePattern = self.pattern fit.targetResists = self.targetResists @@ -173,17 +131,20 @@ class Fit(object): self.recalc(fit) return fit.ID - def toggleBoostFit(self, fitID): + @staticmethod + def toggleBoostFit(fitID): fit = eos.db.getFit(fitID) fit.booster = not fit.booster eos.db.commit() - def renameFit(self, fitID, newName): + @staticmethod + def renameFit(fitID, newName): fit = eos.db.getFit(fitID) fit.name = newName eos.db.commit() - def deleteFit(self, fitID): + @staticmethod + def deleteFit(fitID): fit = eos.db.getFit(fitID) eos.db.remove(fit) @@ -194,13 +155,15 @@ class Fit(object): if projection.victim_fit in eos.db.saveddata_session: # GH issue #359 eos.db.saveddata_session.refresh(projection.victim_fit) - def copyFit(self, fitID): + @staticmethod + def copyFit(fitID): fit = eos.db.getFit(fitID) newFit = copy.deepcopy(fit) eos.db.save(newFit) return newFit.ID - def clearFit(self, fitID): + @staticmethod + def clearFit(fitID): if fitID is None: return None @@ -235,11 +198,12 @@ class Fit(object): self.recalc(fit, withBoosters=True) def getFit(self, fitID, projected=False, basic=False): - ''' Gets fit from database + """ + Gets fit from database Projected is a recursion flag that is set to reduce recursions into projected fits Basic is a flag to simply return the fit without any other processing - ''' + """ if fitID is None: return None fit = eos.db.getFit(fitID) @@ -263,7 +227,8 @@ class Fit(object): fit.inited = True return fit - def searchFits(self, name): + @staticmethod + def searchFits(name): results = eos.db.searchFits(name) fits = [] for fit in results: @@ -279,7 +244,7 @@ class Fit(object): fit = eos.db.getFit(fitID) item = eos.db.getItem(itemID, eager="attributes") try: - implant = eos.types.Implant(item) + implant = es_Implant(item) except ValueError: return False @@ -305,7 +270,7 @@ class Fit(object): fit = eos.db.getFit(fitID) item = eos.db.getItem(itemID, eager="attributes") try: - booster = eos.types.Booster(item) + booster = es_Booster(item) except ValueError: return False @@ -333,7 +298,7 @@ class Fit(object): thing = eos.db.getItem(thing, eager=("attributes", "group.category")) - if isinstance(thing, eos.types.Fit): + if isinstance(thing, FitType): if thing in fit.projectedFits: return @@ -350,19 +315,19 @@ class Fit(object): break if drone is None: - drone = eos.types.Drone(thing) + drone = es_Drone(thing) fit.projectedDrones.append(drone) drone.amount += 1 elif thing.category.name == "Fighter": - fighter = eos.types.Fighter(thing) + fighter = es_Fighter(thing) fit.projectedFighters.append(fighter) elif thing.group.name == "Effect Beacon": - module = eos.types.Module(thing) + module = es_Module(thing) module.state = State.ONLINE fit.projectedModules.append(module) else: - module = eos.types.Module(thing) + module = es_Module(thing) module.state = State.ACTIVE if not module.canHaveState(module.state, fit): module.state = State.OFFLINE @@ -393,18 +358,18 @@ class Fit(object): def toggleProjected(self, fitID, thing, click): fit = eos.db.getFit(fitID) - if isinstance(thing, eos.types.Drone): + if isinstance(thing, es_Drone): if thing.amountActive == 0 and thing.canBeApplied(fit): thing.amountActive = thing.amount else: thing.amountActive = 0 - elif isinstance(thing, eos.types.Fighter): + elif isinstance(thing, es_Fighter): thing.active = not thing.active - elif isinstance(thing, eos.types.Module): + elif isinstance(thing, es_Module): thing.state = self.__getProposedState(thing, click) if not thing.canHaveState(thing.state, fit): thing.state = State.OFFLINE - elif isinstance(thing, eos.types.Fit): + elif isinstance(thing, FitType): projectionInfo = thing.getProjectionInfo(fitID) if projectionInfo: projectionInfo.active = not projectionInfo.active @@ -441,11 +406,11 @@ class Fit(object): def removeProjected(self, fitID, thing): fit = eos.db.getFit(fitID) - if isinstance(thing, eos.types.Drone): + if isinstance(thing, es_Drone): fit.projectedDrones.remove(thing) - elif isinstance(thing, eos.types.Module): + elif isinstance(thing, es_Module): fit.projectedModules.remove(thing) - elif isinstance(thing, eos.types.Fighter): + elif isinstance(thing, es_Fighter): fit.projectedFighters.remove(thing) else: del fit.__projectedFits[thing.ID] @@ -465,7 +430,7 @@ class Fit(object): fit = eos.db.getFit(fitID) item = eos.db.getItem(itemID, eager=("attributes", "group.category")) try: - m = eos.types.Module(item) + m = es_Module(item) except ValueError: return False @@ -512,7 +477,7 @@ class Fit(object): item = eos.db.getItem(newItemID, eager=("attributes", "group.category")) try: - m = eos.types.Module(item) + m = es_Module(item) except ValueError: return False @@ -549,7 +514,7 @@ class Fit(object): # Gather modules and convert Cargo item to Module, silently return if not a module try: - cargoP = eos.types.Module(cargo.item) + cargoP = es_Module(cargo.item) cargoP.owner = fit if cargoP.isValidState(State.ACTIVE): cargoP.state = State.ACTIVE @@ -579,14 +544,15 @@ class Fit(object): x.amount += 1 break else: - moduleP = eos.types.Cargo(module.item) + moduleP = es_Cargo(module.item) moduleP.amount = 1 fit.cargo.insert(cargoIdx, moduleP) eos.db.commit() self.recalc(fit) - def swapModules(self, fitID, src, dst): + @staticmethod + def swapModules(fitID, src, dst): fit = eos.db.getFit(fitID) # Gather modules srcMod = fit.modules[src] @@ -644,7 +610,7 @@ class Fit(object): if cargo is None: # if we don't have the item already in cargo, use default values - cargo = eos.types.Cargo(item) + cargo = es_Cargo(item) fit.cargo.append(cargo) if replace: @@ -682,21 +648,21 @@ class Fit(object): break ''' if fighter is None: - fighter = eos.types.Fighter(item) + fighter = es_Fighter(item) used = fit.getSlotsUsed(fighter.slot) total = fit.getNumSlots(fighter.slot) - standardAttackActive = False; + standardAttackActive = False for ability in fighter.abilities: - if (ability.effect.isImplemented and ability.effect.handlerName == u'fighterabilityattackm'): + if ability.effect.isImplemented and ability.effect.handlerName == u'fighterabilityattackm': # Activate "standard attack" if available ability.active = True standardAttackActive = True else: # Activate all other abilities (Neut, Web, etc) except propmods if no standard attack is active - if (ability.effect.isImplemented - and standardAttackActive == False - and ability.effect.handlerName != u'fighterabilitymicrowarpdrive' - and ability.effect.handlerName != u'fighterabilityevasivemaneuvers'): + if ability.effect.isImplemented and \ + standardAttackActive is False and \ + ability.effect.handlerName != u'fighterabilitymicrowarpdrive' and \ + ability.effect.handlerName != u'fighterabilityevasivemaneuvers': ability.active = True if used >= total: @@ -736,7 +702,7 @@ class Fit(object): break if drone is None: - drone = eos.types.Drone(item) + drone = es_Drone(item) if drone.fits(fit) is True: fit.drones.append(drone) else: @@ -762,18 +728,25 @@ class Fit(object): fit.drones.remove(d1) d2.amount += d1.amount - d2.amountActive += d1.amountActive if d1.amountActive > 0 else -d2.amountActive + d2.amountActive += d1.amountActive + + # If we have less than the total number of drones active, make them all active. Fixes #728 + # This could be removed if we ever add an enhancement to make drone stacks partially active. + if d2.amount > d2.amountActive: + d2.amountActive = d2.amount + eos.db.commit() self.recalc(fit) return True - def splitDrones(self, fit, d, amount, l): + @staticmethod + def splitDrones(fit, d, amount, l): total = d.amount active = d.amountActive > 0 d.amount = amount d.amountActive = amount if active else 0 - newD = eos.types.Drone(d.item) + newD = es_Drone(d.item) newD.amount = total - amount newD.amountActive = newD.amount if active else 0 l.append(newD) @@ -871,7 +844,8 @@ class Fit(object): fit.character = self.character = eos.db.getCharacter(charID) self.recalc(fit) - def isAmmo(self, itemID): + @staticmethod + def isAmmo(itemID): return eos.db.getItem(itemID).category.name == "Charge" def setAmmo(self, fitID, ammoID, modules): @@ -887,7 +861,8 @@ class Fit(object): self.recalc(fit) - def getTargetResists(self, fitID): + @staticmethod + def getTargetResists(fitID): if fitID is None: return @@ -904,7 +879,8 @@ class Fit(object): self.recalc(fit) - def getDamagePattern(self, fitID): + @staticmethod + def getDamagePattern(fitID): if fitID is None: return @@ -938,7 +914,7 @@ class Fit(object): sDP = DamagePattern.getInstance() dp = sDP.getDamagePattern("Selected Ammo") if dp is None: - dp = eos.types.DamagePattern() + dp = es_DamagePattern() dp.name = "Selected Ammo" fit = eos.db.getFit(fitID) @@ -948,151 +924,21 @@ class Fit(object): fit.damagePattern = dp self.recalc(fit) - def exportFit(self, fitID): - fit = eos.db.getFit(fitID) - return Port.exportEft(fit) - - def exportEftImps(self, fitID): - fit = eos.db.getFit(fitID) - return Port.exportEftImps(fit) - - def exportDna(self, fitID): - fit = eos.db.getFit(fitID) - return Port.exportDna(fit) - - def exportCrest(self, fitID, callback=None): - fit = eos.db.getFit(fitID) - return Port.exportCrest(fit, callback) - - def exportXml(self, callback=None, *fitIDs): - fits = map(lambda fitID: eos.db.getFit(fitID), fitIDs) - return Port.exportXml(callback, *fits) - - def exportMultiBuy(self, fitID): - fit = eos.db.getFit(fitID) - return Port.exportMultiBuy(fit) - - def backupFits(self, path, callback): - thread = FitBackupThread(path, callback) - thread.start() - - def importFitsThreaded(self, paths, callback): - thread = FitImportThread(paths, callback) - thread.start() - - def importFitFromFiles(self, paths, callback=None): - """ - Imports fits from file(s). First processes all provided paths and stores - assembled fits into a list. This allows us to call back to the GUI as - fits are processed as well as when fits are being saved. - returns - """ - defcodepage = locale.getpreferredencoding() - - fits = [] - for path in paths: - if callback: # Pulse - wx.CallAfter(callback, 1, "Processing file:\n%s" % path) - - file = open(path, "r") - srcString = file.read() - - if len(srcString) == 0: # ignore blank files - continue - - codec_found = None - # If file had ANSI encoding, decode it to unicode using detection - # of BOM header or if there is no header try default - # codepage then fallback to utf-16, cp1252 - - if isinstance(srcString, str): - encoding_map = ( - ('\xef\xbb\xbf', 'utf-8'), - ('\xff\xfe\0\0', 'utf-32'), - ('\0\0\xfe\xff', 'UTF-32BE'), - ('\xff\xfe', 'utf-16'), - ('\xfe\xff', 'UTF-16BE')) - - for bom, encoding in encoding_map: - if srcString.startswith(bom): - codec_found = encoding - savebom = bom - - if codec_found is None: - logger.info("Unicode BOM not found in file %s.", path) - attempt_codecs = (defcodepage, "utf-8", "utf-16", "cp1252") - - for page in attempt_codecs: - try: - logger.info("Attempting to decode file %s using %s page.", path, page) - srcString = unicode(srcString, page) - codec_found = page - logger.info("File %s decoded using %s page.", path, page) - except UnicodeDecodeError: - logger.info("Error unicode decoding %s from page %s, trying next codec", path, page) - else: - break - else: - logger.info("Unicode BOM detected in %s, using %s page.", path, codec_found) - srcString = unicode(srcString[len(savebom):], codec_found) - - else: - # nasty hack to detect other transparent utf-16 loading - if srcString[0] == '<' and 'utf-16' in srcString[:128].lower(): - codec_found = "utf-16" - else: - codec_found = "utf-8" - - if codec_found is None: - return False, "Proper codec could not be established for %s" % path - - try: - _, fitsImport = Port.importAuto(srcString, path, callback=callback, encoding=codec_found) - fits += fitsImport - except xml.parsers.expat.ExpatError, e: - return False, "Malformed XML in %s" % path - except Exception, e: - logger.exception("Unknown exception processing: %s", path) - return False, "Unknown Error while processing %s" % path - - IDs = [] - numFits = len(fits) - for i, fit in enumerate(fits): - # Set some more fit attributes and save - fit.character = self.character - fit.damagePattern = self.pattern - fit.targetResists = self.targetResists - eos.db.save(fit) - IDs.append(fit.ID) - if callback: # Pulse - wx.CallAfter( - callback, 1, - "Processing complete, saving fits to database\n(%d/%d)" % - (i + 1, numFits) - ) - - return True, fits - - def importFitFromBuffer(self, bufferStr, activeFit=None): - _, fits = Port.importAuto(bufferStr, activeFit=activeFit) - for fit in fits: - fit.character = self.character - fit.damagePattern = self.pattern - fit.targetResists = self.targetResists - eos.db.save(fit) - return fits - def checkStates(self, fit, base): changed = False for mod in fit.modules: if mod != base: - if not mod.canHaveState(mod.state): + # fix for #529, where a module may be in incorrect state after CCP changes mechanics of module + if not mod.canHaveState(mod.state) or not mod.isValidState(mod.state): mod.state = State.ONLINE changed = True + for mod in fit.projectedModules: - if not mod.canHaveState(mod.state, fit): + # fix for #529, where a module may be in incorrect state after CCP changes mechanics of module + if not mod.canHaveState(mod.state, fit) or not mod.isValidState(mod.state): mod.state = State.OFFLINE changed = True + for drone in fit.projectedDrones: if drone.amountActive > 0 and not drone.canBeApplied(fit): drone.amountActive = 0 @@ -1103,21 +949,27 @@ class Fit(object): self.recalc(fit) def toggleModulesState(self, fitID, base, modules, click): + changed = False proposedState = self.__getProposedState(base, click) + if proposedState != base.state: + changed = True base.state = proposedState for mod in modules: if mod != base: - mod.state = self.__getProposedState(mod, click, - proposedState) + p = self.__getProposedState(mod, click, proposedState) + mod.state = p + if p != mod.state: + changed = True - eos.db.commit() - fit = eos.db.getFit(fitID) + if changed: + eos.db.commit() + fit = eos.db.getFit(fitID) - # As some items may affect state-limiting attributes of the ship, calculate new attributes first - self.recalc(fit) - # Then, check states of all modules and change where needed. This will recalc if needed - self.checkStates(fit, base) + # As some items may affect state-limiting attributes of the ship, calculate new attributes first + self.recalc(fit) + # Then, check states of all modules and change where needed. This will recalc if needed + self.checkStates(fit, base) # Old state : New State localMap = { @@ -1171,7 +1023,7 @@ class Fit(object): self.recalc(fit) def recalc(self, fit, withBoosters=True): - logger.debug("=" * 10 + "recalc" + "=" * 10) + pyfalog.info("=" * 10 + "recalc" + "=" * 10) if fit.factorReload is not self.serviceFittingOptions["useGlobalForceReload"]: fit.factorReload = self.serviceFittingOptions["useGlobalForceReload"] fit.clear() diff --git a/service/implantSet.py b/service/implantSet.py index 0d0df5808..8d86c816d 100644 --- a/service/implantSet.py +++ b/service/implantSet.py @@ -1,4 +1,4 @@ -#=============================================================================== +# ============================================================================= # Copyright (C) 2016 Ryan Holmes # # This file is part of pyfa. @@ -15,18 +15,23 @@ # # You should have received a copy of the GNU General Public License # along with pyfa. If not, see . -#=============================================================================== +# ============================================================================= + +import copy import eos.db -import eos.types -import copy -import service.market +from service.market import Market +from eos.saveddata.implant import Implant as es_Implant +from eos.saveddata.implantSet import ImplantSet as es_ImplantSet + class ImportError(Exception): pass -class ImplantSets(): + +class ImplantSets(object): instance = None + @classmethod def getInstance(cls): if cls.instance is None: @@ -34,50 +39,58 @@ class ImplantSets(): return cls.instance - def getImplantSetList(self): + @staticmethod + def getImplantSetList(): return eos.db.getImplantSetList(None) - def getImplantSet(self, name): + @staticmethod + def getImplantSet(name): return eos.db.getImplantSet(name) - def getImplants(self, setID): - set = eos.db.getImplantSet(setID) - return set.implants + @staticmethod + def getImplants(setID): + return eos.db.getImplantSet(setID).implants - def addImplant(self, setID, itemID): - set = eos.db.getImplantSet(setID) - implant = eos.types.Implant(eos.db.getItem(itemID)) - set.implants.append(implant) + @staticmethod + def addImplant(setID, itemID): + implant_set = eos.db.getImplantSet(setID) + implant = es_Implant(eos.db.getItem(itemID)) + implant_set.implants.append(implant) eos.db.commit() - def removeImplant(self, setID, implant): - set = eos.db.getImplantSet(setID) - set.implants.remove(implant) + @staticmethod + def removeImplant(setID, implant): + eos.db.getImplantSet(setID).implants.remove(implant) eos.db.commit() - def newSet(self, name): - s = eos.types.ImplantSet() - s.name = name - eos.db.save(s) - return s + @staticmethod + def newSet(name): + implant_set = es_ImplantSet() + implant_set.name = name + eos.db.save(implant_set) + return implant_set - def renameSet(self, s, newName): - s.name = newName - eos.db.save(s) + @staticmethod + def renameSet(implant_set, newName): + implant_set.name = newName + eos.db.save(implant_set) - def deleteSet(self, s): - eos.db.remove(s) + @staticmethod + def deleteSet(implant_set): + eos.db.remove(implant_set) - def copySet(self, s): - newS = copy.deepcopy(s) + @staticmethod + def copySet(implant_set): + newS = copy.deepcopy(implant_set) eos.db.save(newS) return newS - def saveChanges(self, s): - eos.db.save(s) - + @staticmethod + def saveChanges(implant_set): + eos.db.save(implant_set) + def importSets(self, text): - sMkt = service.Market.getInstance() + sMkt = Market.getInstance() lines = text.splitlines() newSets = [] errors = 0 @@ -90,25 +103,25 @@ class ImplantSets(): if line == '' or line[0] == "#": # comments / empty string continue if line[:1] == "[" and line[-1:] == "]": - current = eos.types.ImplantSet(line[1:-1]) + current = es_ImplantSet(line[1:-1]) newSets.append(current) else: item = sMkt.getItem(line) - current.implants.append(eos.types.Implant(item)) + current.implants.append(es_Implant(item)) except: errors += 1 continue - for set in self.getImplantSetList(): - lookup[set.name] = set + for implant_set in self.getImplantSetList(): + lookup[implant_set.name] = implant_set - for set in newSets: - if set.name in lookup: - match = lookup[set.name] - for implant in set.implants: - match.implants.append(eos.types.Implant(implant.item)) + for implant_set in newSets: + if implant_set.name in lookup: + match = lookup[implant_set.name] + for implant in implant_set.implants: + match.implants.append(es_Implant(implant.item)) else: - eos.db.save(set) + eos.db.save(implant_set) eos.db.commit() @@ -116,10 +129,10 @@ class ImplantSets(): if lenImports == 0: raise ImportError("No patterns found for import") if errors > 0: - raise ImportError("%d sets imported from clipboard; %d errors"%(lenImports, errors)) - + raise ImportError("%d sets imported from clipboard; %d errors" % + (lenImports, errors)) + def exportSets(self): patterns = self.getImplantSetList() patterns.sort(key=lambda p: p.name) - return eos.types.ImplantSet.exportSets(*patterns) - + return es_ImplantSet.exportSets(*patterns) diff --git a/service/market.py b/service/market.py index 313b711d8..925cbf9b4 100644 --- a/service/market.py +++ b/service/market.py @@ -1,4 +1,4 @@ -#=============================================================================== +# =============================================================================== # Copyright (C) 2010 Diego Duclos # # This file is part of pyfa. @@ -15,34 +15,44 @@ # # You should have received a copy of the GNU General Public License # along with pyfa. If not, see . -#=============================================================================== +# =============================================================================== import re import threading -import wx - +from logbook import Logger import Queue +# noinspection PyPackageRequirements +import wx +from sqlalchemy.sql import or_ + import config import eos.db -import eos.types -from sqlalchemy.sql import and_, or_ -from service.settings import SettingsProvider, NetworkSettings -import service -import service.conversions as conversions -import logging +from service import conversions +from service.settings import SettingsProvider +from service.price import Price + +from eos.gamedata import Category as types_Category, Group as types_Group, Item as types_Item, MarketGroup as types_MarketGroup, \ + MetaGroup as types_MetaGroup, MetaType as types_MetaType +from eos.saveddata.price import Price as types_Price try: from collections import OrderedDict except ImportError: from utils.compat import OrderedDict -logger = logging.getLogger(__name__) +pyfalog = Logger(__name__) # Event which tells threads dependent on Market that it's initialized mktRdy = threading.Event() + class ShipBrowserWorkerThread(threading.Thread): + def __init__(self): + threading.Thread.__init__(self) + pyfalog.debug("Initialize ShipBrowserWorkerThread.") + self.name = "ShipBrowser" + def run(self): self.queue = Queue.Queue() self.cache = {} @@ -57,26 +67,36 @@ class ShipBrowserWorkerThread(threading.Thread): sMkt = Market.getInstance() while True: try: - id, callback = queue.get() - set = cache.get(id) - if set is None: - set = sMkt.getShipList(id) - cache[id] = set + id_, callback = queue.get() + set_ = cache.get(id_) + if set_ is None: + set_ = sMkt.getShipList(id_) + cache[id_] = set_ - wx.CallAfter(callback, (id, set)) - except: - pass + wx.CallAfter(callback, (id_, set_)) + except Exception as e: + pyfalog.critical("Callback failed.") + pyfalog.critical(e) finally: try: queue.task_done() - except: - pass + except Exception as e: + pyfalog.critical("Queue task done failed.") + pyfalog.critical(e) + class PriceWorkerThread(threading.Thread): + def __init__(self): + threading.Thread.__init__(self) + self.name = "PriceWorker" + pyfalog.debug("Initialize PriceWorkerThread.") + def run(self): + pyfalog.debug("Run start") self.queue = Queue.Queue() self.wait = {} self.processUpdates() + pyfalog.debug("Run end") def processUpdates(self): queue = self.queue @@ -86,7 +106,7 @@ class PriceWorkerThread(threading.Thread): # Grab prices, this is the time-consuming part if len(requests) > 0: - service.Price.fetchPrices(requests) + Price.fetchPrices(requests) wx.CallAfter(callback) queue.task_done() @@ -106,7 +126,13 @@ class PriceWorkerThread(threading.Thread): self.wait[itemID] = [] self.wait[itemID].append(callback) + class SearchWorkerThread(threading.Thread): + def __init__(self): + threading.Thread.__init__(self) + self.name = "SearchWorker" + pyfalog.debug("Initialize SearchWorkerThread.") + def run(self): self.cv = threading.Condition() self.searchRequest = None @@ -126,14 +152,14 @@ class SearchWorkerThread(threading.Thread): sMkt = Market.getInstance() if filterOn is True: # Rely on category data provided by eos as we don't hardcode them much in service - filter = or_(eos.types.Category.name.in_(sMkt.SEARCH_CATEGORIES), eos.types.Group.name.in_(sMkt.SEARCH_GROUPS)) + filter_ = or_(types_Category.name.in_(sMkt.SEARCH_CATEGORIES), types_Group.name.in_(sMkt.SEARCH_GROUPS)) elif filterOn: # filter by selected categories - filter = eos.types.Category.name.in_(filterOn) + filter_ = types_Category.name.in_(filterOn) else: - filter=None + filter_ = None - results = eos.db.searchItems(request, where=filter, - join=(eos.types.Item.group, eos.types.Group.category), + results = eos.db.searchItems(request, where=filter_, + join=(types_Item.group, types_Group.category), eager=("icon", "group.category", "metaGroup", "metaGroup.parent")) items = set() @@ -149,15 +175,18 @@ class SearchWorkerThread(threading.Thread): self.cv.notify() self.cv.release() -class Market(): + +class Market(object): instance = None + def __init__(self): self.priceCache = {} - #Init recently used module storage + # Init recently used module storage serviceMarketRecentlyUsedModules = {"pyfaMarketRecentlyUsedModules": []} - self.serviceMarketRecentlyUsedModules = SettingsProvider.getInstance().getSettings("pyfaMarketRecentlyUsedModules", serviceMarketRecentlyUsedModules) + self.serviceMarketRecentlyUsedModules = SettingsProvider.getInstance().getSettings( + "pyfaMarketRecentlyUsedModules", serviceMarketRecentlyUsedModules) # Start price fetcher self.priceWorkerThread = PriceWorkerThread() @@ -177,7 +206,7 @@ class Market(): # Items' group overrides self.customGroups = set() # Limited edition ships - self.les_grp = eos.types.Group() + self.les_grp = types_Group() self.les_grp.ID = -1 self.les_grp.name = "Limited Issue Ships" self.les_grp.published = True @@ -187,38 +216,41 @@ class Market(): self.les_grp.description = "" self.les_grp.icon = None self.ITEMS_FORCEGROUP = { - "Opux Luxury Yacht": self.les_grp, # One of those is wedding present at CCP fanfest, another was hijacked from ISD guy during an event - "Silver Magnate": self.les_grp, # Amarr Championship prize - "Gold Magnate": self.les_grp, # Amarr Championship prize - "Armageddon Imperial Issue": self.les_grp, # Amarr Championship prize - "Apocalypse Imperial Issue": self.les_grp, # Amarr Championship prize - "Guardian-Vexor": self.les_grp, # Illegal rewards for the Gallente Frontier Tour Lines event arc - "Megathron Federate Issue": self.les_grp, # Reward during Crielere event - "Raven State Issue": self.les_grp, # AT4 prize - "Tempest Tribal Issue": self.les_grp, # AT4 prize - "Apotheosis": self.les_grp, # 5th EVE anniversary present - "Zephyr": self.les_grp, # 2010 new year gift - "Primae": self.les_grp, # Promotion of planetary interaction - "Freki": self.les_grp, # AT7 prize - "Mimir": self.les_grp, # AT7 prize - "Utu": self.les_grp, # AT8 prize - "Adrestia": self.les_grp, # AT8 prize - "Echelon": self.les_grp, # 2011 new year gift - "Malice": self.les_grp, # AT9 prize - "Vangel": self.les_grp, # AT9 prize - "Cambion": self.les_grp, # AT10 prize - "Etana": self.les_grp, # AT10 prize - "Chremoas": self.les_grp, # AT11 prize :( - "Moracha": self.les_grp, # AT11 prize - "Stratios Emergency Responder": self.les_grp, # Issued for Somer Blink lottery - "Miasmos Quafe Ultra Edition": self.les_grp, # Gift to people who purchased FF HD stream - "InterBus Shuttle": self.les_grp, - "Leopard": self.les_grp, # 2013 new year gift - "Whiptail": self.les_grp, # AT12 prize - "Chameleon": self.les_grp, # AT12 prize - "Victorieux Luxury Yacht": self.les_grp, # Worlds Collide prize \o/ chinese getting owned - "Imp": self.les_grp, # AT13 prize - "Fiend": self.les_grp, # AT13 prize + "Opux Luxury Yacht" : self.les_grp, + # One of those is wedding present at CCP fanfest, another was hijacked from ISD guy during an event + "Silver Magnate" : self.les_grp, # Amarr Championship prize + "Gold Magnate" : self.les_grp, # Amarr Championship prize + "Armageddon Imperial Issue" : self.les_grp, # Amarr Championship prize + "Apocalypse Imperial Issue" : self.les_grp, # Amarr Championship prize + "Guardian-Vexor" : self.les_grp, # Illegal rewards for the Gallente Frontier Tour Lines event arc + "Megathron Federate Issue" : self.les_grp, # Reward during Crielere event + "Raven State Issue" : self.les_grp, # AT4 prize + "Tempest Tribal Issue" : self.les_grp, # AT4 prize + "Apotheosis" : self.les_grp, # 5th EVE anniversary present + "Zephyr" : self.les_grp, # 2010 new year gift + "Primae" : self.les_grp, # Promotion of planetary interaction + "Freki" : self.les_grp, # AT7 prize + "Mimir" : self.les_grp, # AT7 prize + "Utu" : self.les_grp, # AT8 prize + "Adrestia" : self.les_grp, # AT8 prize + "Echelon" : self.les_grp, # 2011 new year gift + "Malice" : self.les_grp, # AT9 prize + "Vangel" : self.les_grp, # AT9 prize + "Cambion" : self.les_grp, # AT10 prize + "Etana" : self.les_grp, # AT10 prize + "Chremoas" : self.les_grp, # AT11 prize :( + "Moracha" : self.les_grp, # AT11 prize + "Stratios Emergency Responder": self.les_grp, # Issued for Somer Blink lottery + "Miasmos Quafe Ultra Edition" : self.les_grp, # Gift to people who purchased FF HD stream + "InterBus Shuttle" : self.les_grp, + "Leopard" : self.les_grp, # 2013 new year gift + "Whiptail" : self.les_grp, # AT12 prize + "Chameleon" : self.les_grp, # AT12 prize + "Victorieux Luxury Yacht" : self.les_grp, # Worlds Collide prize \o/ chinese getting owned + "Imp" : self.les_grp, # AT13 prize + "Fiend" : self.les_grp, # AT13 prize + "Caedes" : self.les_grp, # AT14 prize + "Rabisu" : self.les_grp, # AT14 prize } self.ITEMS_FORCEGROUP_R = self.__makeRevDict(self.ITEMS_FORCEGROUP) @@ -227,26 +259,26 @@ class Market(): # List of items which are forcibly published or hidden self.ITEMS_FORCEPUBLISHED = { - "Data Subverter I": False, # Not used in EVE, probably will appear with Dust link - "QA Cross Protocol Analyzer": False, # QA modules used by CCP internally - "QA Damage Module": False, - "QA ECCM": False, - "QA Immunity Module": False, - "QA Multiship Module - 10 Players": False, - "QA Multiship Module - 20 Players": False, - "QA Multiship Module - 40 Players": False, - "QA Multiship Module - 5 Players": False, + "Data Subverter I" : False, # Not used in EVE, probably will appear with Dust link + "QA Cross Protocol Analyzer" : False, # QA modules used by CCP internally + "QA Damage Module" : False, + "QA ECCM" : False, + "QA Immunity Module" : False, + "QA Multiship Module - 10 Players" : False, + "QA Multiship Module - 20 Players" : False, + "QA Multiship Module - 40 Players" : False, + "QA Multiship Module - 5 Players" : False, "QA Remote Armor Repair System - 5 Players": False, - "QA Shield Transporter - 5 Players": False, - "Goru's Shuttle": False, - "Guristas Shuttle": False, - "Mobile Decoy Unit": False, # Seems to be left over test mod for deployables - "Tournament Micro Jump Unit": False, # Normally seen only on tournament arenas - "Council Diplomatic Shuttle": False, # CSM X celebration - "Civilian Gatling Railgun": True, - "Civilian Gatling Pulse Laser": True, - "Civilian Gatling Autocannon": True, - "Civilian Light Electron Blaster": True, + "QA Shield Transporter - 5 Players" : False, + "Goru's Shuttle" : False, + "Guristas Shuttle" : False, + "Mobile Decoy Unit" : False, # Seems to be left over test mod for deployables + "Tournament Micro Jump Unit" : False, # Normally seen only on tournament arenas + "Council Diplomatic Shuttle" : False, # CSM X celebration + "Civilian Gatling Railgun" : True, + "Civilian Gatling Pulse Laser" : True, + "Civilian Gatling Autocannon" : True, + "Civilian Light Electron Blaster" : True, } # do not publish ships that we convert @@ -264,107 +296,142 @@ class Market(): # List of groups which are forcibly published self.GROUPS_FORCEPUBLISHED = { - "Prototype Exploration Ship": False } # We moved the only ship from this group to other group anyway + "Prototype Exploration Ship": False + } # We moved the only ship from this group to other group anyway # Dictionary of items with forced meta groups, uses following format: # Item name: (metagroup name, parent type name) 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"), + "'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"),} + "Khanid Navy Torpedo Launcher" : ("Faction", "Torpedo Launcher I"), + } # Parent type name: set(item names) self.ITEMS_FORCEDMETAGROUP_R = {} for item, value in self.ITEMS_FORCEDMETAGROUP.items(): parent = value[1] - if not parent in self.ITEMS_FORCEDMETAGROUP_R: + if parent not in self.ITEMS_FORCEDMETAGROUP_R: self.ITEMS_FORCEDMETAGROUP_R[parent] = set() self.ITEMS_FORCEDMETAGROUP_R[parent].add(item) # Dictionary of items with forced market group (service assumes they have no # market group assigned in db, otherwise they'll appear in both original and forced groups) self.ITEMS_FORCEDMARKETGROUP = { - "'Alpha' Data Analyzer I": 714, # Ship Equipment > Electronics and Sensor Upgrades > Scanners > Data and Composition Scanners - "'Codex' Data Analyzer I": 714, # Ship Equipment > Electronics and Sensor Upgrades > Scanners > Data and Composition Scanners - "'Daemon' Data Analyzer I": 714, # Ship Equipment > Electronics and Sensor Upgrades > Scanners > Data and Composition Scanners - "'Libram' Data Analyzer I": 714, # Ship Equipment > Electronics and Sensor Upgrades > Scanners > Data and Composition Scanners - "Advanced Cerebral Accelerator": 977, # Implants & Boosters > Booster - "Civilian Damage Control": 615, # Ship Equipment > Hull & Armor > Damage Controls - "Civilian EM Ward Field": 1695, # Ship Equipment > Shield > Shield Hardeners > EM Shield Hardeners - "Civilian Explosive Deflection Field": 1694, # Ship Equipment > Shield > Shield Hardeners > Explosive Shield Hardeners - "Civilian Hobgoblin": 837, # Drones > Combat Drones > Light Scout Drones - "Civilian Kinetic Deflection Field": 1693, # Ship Equipment > Shield > Shield Hardeners > Kinetic Shield Hardeners - "Civilian Light Missile Launcher": 640, # Ship Equipment > Turrets & Bays > Missile Launchers > Light Missile Launchers - "Civilian Scourge Light Missile": 920, # Ammunition & Charges > Missiles > Light Missiles > Standard Light Missiles - "Civilian Small Remote Armor Repairer": 1059, # Ship Equipment > Hull & Armor > Remote Armor Repairers > Small - "Civilian Small Remote Shield Booster": 603, # Ship Equipment > Shield > Remote Shield Boosters > Small - "Civilian Stasis Webifier": 683, # Ship Equipment > Electronic Warfare > Stasis Webifiers - "Civilian Thermic Dissipation Field": 1692, # Ship Equipment > Shield > Shield Hardeners > Thermal Shield Hardeners - "Civilian Warp Disruptor": 1935, # Ship Equipment > Electronic Warfare > Warp Disruptors - "Hardwiring - Zainou 'Sharpshooter' ZMX10": 1493, # Implants & Boosters > Implants > Skill Hardwiring > Missile Implants > Implant Slot 06 - "Hardwiring - Zainou 'Sharpshooter' ZMX100": 1493, # Implants & Boosters > Implants > Skill Hardwiring > Missile Implants > Implant Slot 06 - "Hardwiring - Zainou 'Sharpshooter' ZMX1000": 1493, # Implants & Boosters > Implants > Skill Hardwiring > Missile Implants > Implant Slot 06 - "Hardwiring - Zainou 'Sharpshooter' ZMX11": 1493, # Implants & Boosters > Implants > Skill Hardwiring > Missile Implants > Implant Slot 06 - "Hardwiring - Zainou 'Sharpshooter' ZMX110": 1493, # Implants & Boosters > Implants > Skill Hardwiring > Missile Implants > Implant Slot 06 - "Hardwiring - Zainou 'Sharpshooter' ZMX1100": 1493, # Implants & Boosters > Implants > Skill Hardwiring > Missile Implants > Implant Slot 06 - "Nugoehuvi Synth Blue Pill Booster": 977, # Implants & Boosters > Booster - "Prototype Cerebral Accelerator": 977, # Implants & Boosters > Booster - "Prototype Iris Probe Launcher": 712, # Ship Equipment > Turrets & Bays > Scan Probe Launchers - "Shadow": 1310, # Drones > Combat Drones > Fighter Bombers - "Sleeper Data Analyzer I": 714, # Ship Equipment > Electronics and Sensor Upgrades > Scanners > Data and Composition Scanners - "Standard Cerebral Accelerator": 977, # Implants & Boosters > Booster - "Talocan Data Analyzer I": 714, # Ship Equipment > Electronics and Sensor Upgrades > Scanners > Data and Composition Scanners - "Terran Data Analyzer I": 714, # Ship Equipment > Electronics and Sensor Upgrades > Scanners > Data and Composition Scanners - "Tetrimon Data Analyzer I": 714 # Ship Equipment > Electronics and Sensor Upgrades > Scanners > Data and Composition Scanners + "'Alpha' Data Analyzer I" : 714, + # Ship Equipment > Electronics and Sensor Upgrades > Scanners > Data and Composition Scanners + "'Codex' Data Analyzer I" : 714, + # Ship Equipment > Electronics and Sensor Upgrades > Scanners > Data and Composition Scanners + "'Daemon' Data Analyzer I" : 714, + # Ship Equipment > Electronics and Sensor Upgrades > Scanners > Data and Composition Scanners + "'Libram' Data Analyzer I" : 714, + # Ship Equipment > Electronics and Sensor Upgrades > Scanners > Data and Composition Scanners + "Advanced Cerebral Accelerator" : 977, # Implants & Boosters > Booster + "Civilian Damage Control" : 615, # Ship Equipment > Hull & Armor > Damage Controls + "Civilian EM Ward Field" : 1695, + # Ship Equipment > Shield > Shield Hardeners > EM Shield Hardeners + "Civilian Explosive Deflection Field" : 1694, + # Ship Equipment > Shield > Shield Hardeners > Explosive Shield Hardeners + "Civilian Hobgoblin" : 837, # Drones > Combat Drones > Light Scout Drones + "Civilian Kinetic Deflection Field" : 1693, + # Ship Equipment > Shield > Shield Hardeners > Kinetic Shield Hardeners + "Civilian Light Missile Launcher" : 640, + # Ship Equipment > Turrets & Bays > Missile Launchers > Light Missile Launchers + "Civilian Scourge Light Missile" : 920, + # Ammunition & Charges > Missiles > Light Missiles > Standard Light Missiles + "Civilian Small Remote Armor Repairer" : 1059, + # Ship Equipment > Hull & Armor > Remote Armor Repairers > Small + "Civilian Small Remote Shield Booster" : 603, # Ship Equipment > Shield > Remote Shield Boosters > Small + "Civilian Stasis Webifier" : 683, # Ship Equipment > Electronic Warfare > Stasis Webifiers + "Civilian Thermic Dissipation Field" : 1692, + # Ship Equipment > Shield > Shield Hardeners > Thermal Shield Hardeners + "Civilian Warp Disruptor" : 1935, # Ship Equipment > Electronic Warfare > Warp Disruptors + "Hardwiring - Zainou 'Sharpshooter' ZMX10" : 1493, + # Implants & Boosters > Implants > Skill Hardwiring > Missile Implants > Implant Slot 06 + "Hardwiring - Zainou 'Sharpshooter' ZMX100" : 1493, + # Implants & Boosters > Implants > Skill Hardwiring > Missile Implants > Implant Slot 06 + "Hardwiring - Zainou 'Sharpshooter' ZMX1000": 1493, + # Implants & Boosters > Implants > Skill Hardwiring > Missile Implants > Implant Slot 06 + "Hardwiring - Zainou 'Sharpshooter' ZMX11" : 1493, + # Implants & Boosters > Implants > Skill Hardwiring > Missile Implants > Implant Slot 06 + "Hardwiring - Zainou 'Sharpshooter' ZMX110" : 1493, + # Implants & Boosters > Implants > Skill Hardwiring > Missile Implants > Implant Slot 06 + "Hardwiring - Zainou 'Sharpshooter' ZMX1100": 1493, + # Implants & Boosters > Implants > Skill Hardwiring > Missile Implants > Implant Slot 06 + "Nugoehuvi Synth Blue Pill Booster" : 977, # Implants & Boosters > Booster + "Prototype Cerebral Accelerator" : 977, # Implants & Boosters > Booster + "Prototype Iris Probe Launcher" : 712, # Ship Equipment > Turrets & Bays > Scan Probe Launchers + "Shadow" : 1310, # Drones > Combat Drones > Fighter Bombers + "Sleeper Data Analyzer I" : 714, + # Ship Equipment > Electronics and Sensor Upgrades > Scanners > Data and Composition Scanners + "Standard Cerebral Accelerator" : 977, # Implants & Boosters > Booster + "Talocan Data Analyzer I" : 714, + # Ship Equipment > Electronics and Sensor Upgrades > Scanners > Data and Composition Scanners + "Terran Data Analyzer I" : 714, + # Ship Equipment > Electronics and Sensor Upgrades > Scanners > Data and Composition Scanners + "Tetrimon Data Analyzer I" : 714 + # Ship Equipment > Electronics and Sensor Upgrades > Scanners > Data and Composition Scanners } self.ITEMS_FORCEDMARKETGROUP_R = self.__makeRevDict(self.ITEMS_FORCEDMARKETGROUP) self.FORCEDMARKETGROUP = { - 685: False, # Ship Equipment > Electronic Warfare > ECCM - 681: False, # Ship Equipment > Electronic Warfare > Sensor Backup Arrays + 685: False, # Ship Equipment > Electronic Warfare > ECCM + 681: False, # Ship Equipment > Electronic Warfare > Sensor Backup Arrays } # Misc definitions # 0 is for items w/o meta group - self.META_MAP = OrderedDict([("normal", frozenset((0, 1, 2, 14))), + self.META_MAP = OrderedDict([("normal", frozenset((0, 1, 2, 14))), ("faction", frozenset((4, 3))), ("complex", frozenset((6,))), ("officer", frozenset((5,)))]) - self.SEARCH_CATEGORIES = ("Drone", "Module", "Subsystem", "Charge", "Implant", "Deployable", "Fighter", "Structure", "Structure Module") + self.SEARCH_CATEGORIES = ( + "Drone", + "Module", + "Subsystem", + "Charge", + "Implant", + "Deployable", + "Fighter", + "Structure", + "Structure Module", + ) self.SEARCH_GROUPS = ("Ice Product",) - self.ROOT_MARKET_GROUPS = (9, # Modules + self.ROOT_MARKET_GROUPS = (9, # Modules 1111, # Rigs - 157, # Drones - 11, # Ammo + 157, # Drones + 11, # Ammo 1112, # Subsystems - 24, # Implants & Boosters - 404, # Deployables + 24, # Implants & Boosters + 404, # Deployables 2202, # Structure Equipment - 2203 # Structure Modifications + 2203 # Structure Modifications ) # Tell other threads that Market is at their service mktRdy.set() @classmethod def getInstance(cls): - if cls.instance == None: + if cls.instance is None: cls.instance = Market() return cls.instance - def __makeRevDict(self, orig): + @staticmethod + def __makeRevDict(orig): """Creates reverse dictionary""" rev = {} for item, value in orig.items(): - if not value in rev: + if value not in rev: rev[value] = set() rev[value].add(item) return rev - def getItem(self, identity, *args, **kwargs): + @staticmethod + def getItem(identity, *args, **kwargs): """Get item by its ID or name""" try: - if isinstance(identity, eos.types.Item): + if isinstance(identity, types_Item): item = identity elif isinstance(identity, int): item = eos.db.getItem(identity, *args, **kwargs) @@ -373,20 +440,21 @@ class Market(): # features. Check against overrides identity = conversions.all.get(identity, identity) item = eos.db.getItem(identity, *args, **kwargs) + elif isinstance(identity, float): - id = int(identity) - item = eos.db.getItem(id, *args, **kwargs) + id_ = int(identity) + item = eos.db.getItem(id_, *args, **kwargs) else: raise TypeError("Need Item object, integer, float or string as argument") except: - logger.error("Could not get item: %s", identity) + pyfalog.error("Could not get item: {0}", identity) raise return item def getGroup(self, identity, *args, **kwargs): """Get group by its ID or name""" - if isinstance(identity, eos.types.Group): + if isinstance(identity, types_Group): return identity elif isinstance(identity, (int, float, basestring)): if isinstance(identity, float): @@ -402,39 +470,42 @@ class Market(): else: raise TypeError("Need Group object, integer, float or string as argument") - def getCategory(self, identity, *args, **kwargs): + @staticmethod + def getCategory(identity, *args, **kwargs): """Get category by its ID or name""" - if isinstance(identity, eos.types.Category): + if isinstance(identity, types_Category): category = identity elif isinstance(identity, (int, basestring)): category = eos.db.getCategory(identity, *args, **kwargs) elif isinstance(identity, float): - id = int(identity) - category = eos.db.getCategory(id, *args, **kwargs) + id_ = int(identity) + category = eos.db.getCategory(id_, *args, **kwargs) else: raise TypeError("Need Category object, integer, float or string as argument") return category - def getMetaGroup(self, identity, *args, **kwargs): + @staticmethod + def getMetaGroup(identity, *args, **kwargs): """Get meta group by its ID or name""" - if isinstance(identity, eos.types.MetaGroup): + if isinstance(identity, types_MetaGroup): metaGroup = identity elif isinstance(identity, (int, basestring)): metaGroup = eos.db.getMetaGroup(identity, *args, **kwargs) elif isinstance(identity, float): - id = int(identity) - metaGroup = eos.db.getMetaGroup(id, *args, **kwargs) + id_ = int(identity) + metaGroup = eos.db.getMetaGroup(id_, *args, **kwargs) else: raise TypeError("Need MetaGroup object, integer, float or string as argument") return metaGroup - def getMarketGroup(self, identity, *args, **kwargs): + @staticmethod + def getMarketGroup(identity, *args, **kwargs): """Get market group by its ID""" - if isinstance(identity, eos.types.MarketGroup): + if isinstance(identity, types_MarketGroup): marketGroup = identity elif isinstance(identity, (int, float)): - id = int(identity) - marketGroup = eos.db.getMarketGroup(id, *args, **kwargs) + id_ = int(identity) + marketGroup = eos.db.getMarketGroup(id_, *args, **kwargs) else: raise TypeError("Need MarketGroup object, integer or float as argument") return marketGroup @@ -458,7 +529,7 @@ class Market(): # Check if item is in forced metagroup map if item.name in self.ITEMS_FORCEDMETAGROUP: # Create meta group from scratch - metaGroup = eos.types.MetaType() + metaGroup = types_MetaType() # Get meta group info object based on meta group name metaGroupInfo = self.getMetaGroup(self.ITEMS_FORCEDMETAGROUP[item.name][0]) # Get parent item based on its name @@ -478,8 +549,8 @@ class Market(): def getMetaGroupIdByItem(self, item, fallback=0): """Get meta group ID by item""" - id = getattr(self.getMetaGroupByItem(item), "ID", fallback) - return id + id_ = getattr(self.getMetaGroupByItem(item), "ID", fallback) + return id_ def getMarketGroupByItem(self, item, parentcheck=True): """Get market group by item, its ID or name""" @@ -562,8 +633,8 @@ class Market(): parents.add(parent) # Check for overrides and add them if any if parent.name in self.ITEMS_FORCEDMETAGROUP_R: - for item in self.ITEMS_FORCEDMETAGROUP_R[parent.name]: - i = self.getItem(item) + for _item in self.ITEMS_FORCEDMETAGROUP_R[parent.name]: + i = self.getItem(_item) if i: variations.add(i) # Add all parents to variations set @@ -585,9 +656,11 @@ class Market(): def getGroupsByCategory(self, cat): """Get groups from given category""" groups = set(filter(lambda grp: self.getPublicityByGroup(grp), cat.groups)) + return groups - def getMarketGroupChildren(self, mg): + @staticmethod + def getMarketGroupChildren(mg): """Get the children marketGroups of marketGroup.""" children = set() for child in mg.children: @@ -601,10 +674,11 @@ class Market(): groupItems = set(group.items) if hasattr(group, 'addItems'): groupItems.update(group.addItems) - items = set(filter(lambda item: self.getPublicityByItem(item) and self.getGroupByItem(item) == group, groupItems)) + items = set( + filter(lambda item: self.getPublicityByItem(item) and self.getGroupByItem(item) == group, groupItems)) return items - def getItemsByMarketGroup(self, mg, vars=True): + def getItemsByMarketGroup(self, mg, vars_=True): """Get items in the given market group""" result = set() # Get items from eos market group @@ -613,7 +687,7 @@ class Market(): if mg.ID in self.ITEMS_FORCEDMARKETGROUP_R: forceditms = set(self.getItem(itmn) for itmn in self.ITEMS_FORCEDMARKETGROUP_R[mg.ID]) baseitms.update(forceditms) - if vars: + if vars_: parents = set() for item in baseitms: # Add one of the base market group items to result @@ -631,7 +705,7 @@ class Market(): else: result = baseitms # Get rid of unpublished items - result = set(filter(lambda item: self.getPublicityByItem(item), result)) + result = set(filter(lambda item_: self.getPublicityByItem(item_), result)) return result def marketGroupHasTypesCheck(self, mg): @@ -666,7 +740,7 @@ class Market(): elif self.marketGroupHasTypesCheck(mg): # Do not request variations to make process faster # Pick random item and use its icon - items = self.getItemsByMarketGroup(mg, vars=False) + items = self.getItemsByMarketGroup(mg, vars_=False) try: item = items.pop() except KeyError: @@ -704,8 +778,8 @@ class Market(): the ID, the name and the icon of the group """ root = set() - for id in self.ROOT_MARKET_GROUPS: - mg = self.getMarketGroup(id, eager="icon") + for id_ in self.ROOT_MARKET_GROUPS: + mg = self.getMarketGroup(id_, eager="icon") root.add(mg) return root @@ -721,19 +795,17 @@ class Market(): """Get ships for given group id""" grp = self.getGroup(grpid, eager=("items", "items.group", "items.marketGroup")) ships = self.getItemsByGroup(grp) - for ship in ships: - ship.race return ships - def getShipListDelayed(self, id, callback): + def getShipListDelayed(self, id_, callback): """Background version of getShipList""" - self.shipBrowserWorkerThread.queue.put((id, callback)) + self.shipBrowserWorkerThread.queue.put((id_, callback)) def searchShips(self, name): """Find ships according to given text pattern""" - filter = eos.types.Category.name.in_(["Ship", "Structure"]) - results = eos.db.searchItems(name, where=filter, - join=(eos.types.Item.group, eos.types.Group.category), + filter_ = types_Category.name.in_(["Ship", "Structure"]) + results = eos.db.searchItems(name, where=filter_, + join=(types_Item.group, types_Group.category), eager=("icon", "group.category", "metaGroup", "metaGroup.parent")) ships = set() for item in results: @@ -745,18 +817,20 @@ class Market(): """Find items according to given text pattern""" self.searchWorkerThread.scheduleSearch(name, callback, filterOn) - def getItemsWithOverrides(self): + @staticmethod + def getItemsWithOverrides(): overrides = eos.db.getAllOverrides() items = set() for x in overrides: - if (x.item is None): + if x.item is None: eos.db.saveddata_session.delete(x) eos.db.commit() else: items.add(x.item) return list(items) - def directAttrRequest(self, items, attribs): + @staticmethod + def directAttrRequest(items, attribs): try: itemIDs = tuple(map(lambda i: i.ID, items)) except TypeError: @@ -787,7 +861,7 @@ class Market(): if price is None: price = eos.db.getPrice(typeID) if price is None: - price = eos.types.Price(typeID) + price = types_Price(typeID) eos.db.add(price) self.priceCache[typeID] = price @@ -808,8 +882,9 @@ class Market(): def cb(): try: callback(requests) - except Exception, e: - pass + except Exception as e: + pyfalog.critical("Callback failed.") + pyfalog.critical(e) eos.db.commit() self.priceWorkerThread.trigger(requests, cb) @@ -824,14 +899,15 @@ class Market(): def cb(): try: callback(item) - except: - pass + except Exception as e: + pyfalog.critical("Callback failed.") + pyfalog.critical(e) self.priceWorkerThread.setToWait(item.ID, cb) def clearPriceCache(self): self.priceCache.clear() - deleted_rows = eos.db.clearPrices() + eos.db.clearPrices() def getSystemWideEffects(self): """ @@ -874,7 +950,7 @@ class Market(): groupname = re.sub(garbage, "", groupname) groupname = re.sub(" {2,}", " ", groupname).strip() # Add stuff to dictionary - if not groupname in effects: + if groupname not in effects: effects[groupname] = set() effects[groupname].add((beacon, beaconname, shortname)) # Break loop on 1st result diff --git a/service/network.py b/service/network.py index f7383e03d..d050e88e2 100644 --- a/service/network.py +++ b/service/network.py @@ -1,4 +1,4 @@ -#=============================================================================== +# ============================================================================= # Copyright (C) 2014 Ryan Holmes # # This file is part of pyfa. @@ -15,36 +15,43 @@ # # You should have received a copy of the GNU General Public License # along with pyfa. If not, see . -#=============================================================================== +# ============================================================================= + -from service.settings import NetworkSettings import urllib2 import urllib -import config import socket +import config +from service.settings import NetworkSettings + # network timeout, otherwise pyfa hangs for a long while if no internet connection timeout = 3 socket.setdefaulttimeout(timeout) + class Error(StandardError): def __init__(self, msg=None): self.message = msg + class RequestError(StandardError): pass + class AuthenticationError(StandardError): pass + class ServerError(StandardError): pass + class TimeoutError(StandardError): pass -class Network(): +class Network(object): # Request constants - every request must supply this, as it is checked if # enabled or not via settings ENABLED = 1 @@ -53,16 +60,17 @@ class Network(): UPDATE = 8 _instance = None + @classmethod def getInstance(cls): - if cls._instance == None: + if cls._instance is None: cls._instance = Network() return cls._instance def request(self, url, type, data=None): # URL is required to be https as of right now - #print "Starting request: %s\n\tType: %s\n\tPost Data: %s"%(url,type,data) + # print "Starting request: %s\n\tType: %s\n\tPost Data: %s"%(url,type,data) # Make sure request is enabled access = NetworkSettings.getInstance().getAccess() @@ -71,8 +79,9 @@ class Network(): raise Error("Access not enabled - please enable in Preferences > Network") # Set up some things for the request - versionString = "{0} {1} - {2} {3}".format(config.version, config.tag, config.expansionName, config.expansionVersion) - headers = {"User-Agent" : "pyfa {0} (Python-urllib2)".format(versionString)} + versionString = "{0} {1} - {2} {3}".format(config.version, config.tag, config.expansionName, + config.expansionVersion) + headers = {"User-Agent": "pyfa {0} (Python-urllib2)".format(versionString)} proxy = NetworkSettings.getInstance().getProxySettings() if proxy is not None: @@ -81,8 +90,10 @@ class Network(): # proxy_auth is a tuple of (login, password) or None if proxy_auth is not None: # add login:password@ in front of proxy address - proxy_handler = urllib2.ProxyHandler({'https': '{0}:{1}@{2}:{3}'.format( - proxy_auth[0], proxy_auth[1], proxy[0], proxy[1])}) + proxy_handler = urllib2.ProxyHandler({ + 'https': '{0}:{1}@{2}:{3}'.format( + proxy_auth[0], proxy_auth[1], proxy[0], proxy[1]) + }) else: # build proxy handler with no login/pass info proxy_handler = urllib2.ProxyHandler({'https': "{0}:{1}".format(proxy[0], proxy[1])}) @@ -101,14 +112,14 @@ class Network(): request = urllib2.Request(url, headers=headers, data=urllib.urlencode(data) if data else None) try: return urllib2.urlopen(request) - except urllib2.HTTPError, error: + except urllib2.HTTPError as error: if error.code == 404: raise RequestError() elif error.code == 403: raise AuthenticationError() elif error.code >= 500: raise ServerError() - except urllib2.URLError, error: + except urllib2.URLError as error: if "timed out" in error.reason: raise TimeoutError() else: diff --git a/service/port.py b/service/port.py index f0b24306b..31d1a0b94 100644 --- a/service/port.py +++ b/service/port.py @@ -1,4 +1,4 @@ -#=============================================================================== +# ============================================================================= # Copyright (C) 2014 Ryan Holmes # # This file is part of pyfa. @@ -15,21 +15,42 @@ # # You should have received a copy of the GNU General Public License # along with pyfa. If not, see . -#=============================================================================== +# ============================================================================= import re import os import xml.dom - -from eos.types import State, Slot, Module, Cargo, Fit, Ship, Drone, Implant, Booster, Citadel, Fighter -import service -import wx -import logging -import config +from logbook import Logger import collections import json +import threading +import locale -logger = logging.getLogger("pyfa.service.port") +from codecs import open + +import xml.parsers.expat + +from eos import db +from service.fit import Fit as svcFit + +# noinspection PyPackageRequirements +import wx + +from eos.saveddata.cargo import Cargo +from eos.saveddata.implant import Implant +from eos.saveddata.booster import Booster +from eos.saveddata.drone import Drone +from eos.saveddata.fighter import Fighter +from eos.saveddata.module import Module, State, Slot +from eos.saveddata.ship import Ship +from eos.saveddata.citadel import Citadel +from eos.saveddata.fit import Fit +from service.market import Market + +if 'wxMac' not in wx.PlatformInfo or ('wxMac' in wx.PlatformInfo and wx.VERSION >= (3, 0)): + from service.crest import Crest + +pyfalog = Logger(__name__) try: from collections import OrderedDict @@ -38,18 +59,149 @@ except ImportError: EFT_SLOT_ORDER = [Slot.LOW, Slot.MED, Slot.HIGH, Slot.RIG, Slot.SUBSYSTEM] INV_FLAGS = { - Slot.LOW: 11, - Slot.MED: 19, - Slot.HIGH: 27, - Slot.RIG: 92, - Slot.SUBSYSTEM: 125} + Slot.LOW: 11, + Slot.MED: 19, + Slot.HIGH: 27, + Slot.RIG: 92, + Slot.SUBSYSTEM: 125 +} INV_FLAG_CARGOBAY = 5 INV_FLAG_DRONEBAY = 87 INV_FLAG_FIGHTER = 158 + class Port(object): + instance = None + + @classmethod + def getInstance(cls): + if cls.instance is None: + cls.instance = Port() + + return cls.instance + + @staticmethod + def backupFits(path, callback): + thread = FitBackupThread(path, callback) + thread.start() + + @staticmethod + def importFitsThreaded(paths, callback): + thread = FitImportThread(paths, callback) + thread.start() + + @staticmethod + def importFitFromFiles(paths, callback=None): + """ + Imports fits from file(s). First processes all provided paths and stores + assembled fits into a list. This allows us to call back to the GUI as + fits are processed as well as when fits are being saved. + returns + """ + defcodepage = locale.getpreferredencoding() + sFit = svcFit.getInstance() + + fits = [] + for path in paths: + if callback: # Pulse + wx.CallAfter(callback, 1, "Processing file:\n%s" % path) + + file_ = open(path, "r") + srcString = file_.read() + + if len(srcString) == 0: # ignore blank files + continue + + codec_found = None + # If file had ANSI encoding, decode it to unicode using detection + # of BOM header or if there is no header try default + # codepage then fallback to utf-16, cp1252 + + if isinstance(srcString, str): + savebom = None + + encoding_map = ( + ('\xef\xbb\xbf', 'utf-8'), + ('\xff\xfe\0\0', 'utf-32'), + ('\0\0\xfe\xff', 'UTF-32BE'), + ('\xff\xfe', 'utf-16'), + ('\xfe\xff', 'UTF-16BE')) + + for bom, encoding in encoding_map: + if srcString.startswith(bom): + codec_found = encoding + savebom = bom + + if codec_found is None: + pyfalog.info("Unicode BOM not found in file {0}.", path) + attempt_codecs = (defcodepage, "utf-8", "utf-16", "cp1252") + + for page in attempt_codecs: + try: + pyfalog.info("Attempting to decode file {0} using {1} page.", path, page) + srcString = unicode(srcString, page) + codec_found = page + pyfalog.info("File {0} decoded using {1} page.", path, page) + except UnicodeDecodeError: + pyfalog.info("Error unicode decoding {0} from page {1}, trying next codec", path, page) + else: + break + else: + pyfalog.info("Unicode BOM detected in {0}, using {1} page.", path, codec_found) + srcString = unicode(srcString[len(savebom):], codec_found) + + else: + # nasty hack to detect other transparent utf-16 loading + if srcString[0] == '<' and 'utf-16' in srcString[:128].lower(): + codec_found = "utf-16" + else: + codec_found = "utf-8" + + if codec_found is None: + return False, "Proper codec could not be established for %s" % path + + try: + _, fitsImport = Port.importAuto(srcString, path, callback=callback, encoding=codec_found) + fits += fitsImport + except xml.parsers.expat.ExpatError: + return False, "Malformed XML in %s" % path + except Exception as e: + pyfalog.critical("Unknown exception processing: {0}", path) + pyfalog.critical(e) + return False, "Unknown Error while processing {0}" % path + + IDs = [] + numFits = len(fits) + for i, fit in enumerate(fits): + # Set some more fit attributes and save + fit.character = sFit.character + fit.damagePattern = sFit.pattern + fit.targetResists = sFit.targetResists + db.save(fit) + IDs.append(fit.ID) + if callback: # Pulse + wx.CallAfter( + callback, 1, + "Processing complete, saving fits to database\n(%d/%d)" % + (i + 1, numFits) + ) + + return True, fits + + @staticmethod + def importFitFromBuffer(bufferStr, activeFit=None): + sFit = svcFit.getInstance() + _, fits = Port.importAuto(bufferStr, activeFit=activeFit) + for fit in fits: + fit.character = sFit.character + fit.damagePattern = sFit.pattern + fit.targetResists = sFit.targetResists + db.save(fit) + return fits + """Service which houses all import/export format functions""" + @classmethod def exportCrest(cls, ofit, callback=None): # A few notes: @@ -58,19 +210,19 @@ class Port(object): nested_dict = lambda: collections.defaultdict(nested_dict) fit = nested_dict() - sCrest = service.Crest.getInstance() - sFit = service.Fit.getInstance() + sCrest = Crest.getInstance() + sFit = svcFit.getInstance() eve = sCrest.eve # max length is 50 characters name = ofit.name[:47] + '...' if len(ofit.name) > 50 else ofit.name fit['name'] = name - fit['ship']['href'] = "%sinventory/types/%d/"%(eve._authed_endpoint, ofit.ship.item.ID) + fit['ship']['href'] = "%sinventory/types/%d/" % (eve._authed_endpoint, ofit.ship.item.ID) fit['ship']['id'] = ofit.ship.item.ID fit['ship']['name'] = '' - fit['description'] = ""%ofit.ID + fit['description'] = "" % ofit.ID fit['items'] = [] slotNum = {} @@ -87,20 +239,20 @@ class Port(object): slot = int(module.getModifiedItemAttr("subSystemSlot")) item['flag'] = slot else: - if not slot in slotNum: + if slot not in slotNum: slotNum[slot] = INV_FLAGS[slot] item['flag'] = slotNum[slot] slotNum[slot] += 1 item['quantity'] = 1 - item['type']['href'] = "%sinventory/types/%d/"%(eve._authed_endpoint, module.item.ID) + item['type']['href'] = "%sinventory/types/%d/" % (eve._authed_endpoint, module.item.ID) item['type']['id'] = module.item.ID item['type']['name'] = '' fit['items'].append(item) if module.charge and sFit.serviceFittingOptions["exportCharges"]: - if not module.chargeID in charges: + if module.chargeID not in charges: charges[module.chargeID] = 0 # `or 1` because some charges (ie scripts) are without qty charges[module.chargeID] += module.numCharges or 1 @@ -109,7 +261,7 @@ class Port(object): item = nested_dict() item['flag'] = INV_FLAG_CARGOBAY item['quantity'] = cargo.amount - item['type']['href'] = "%sinventory/types/%d/"%(eve._authed_endpoint, cargo.item.ID) + item['type']['href'] = "%sinventory/types/%d/" % (eve._authed_endpoint, cargo.item.ID) item['type']['id'] = cargo.item.ID item['type']['name'] = '' fit['items'].append(item) @@ -118,7 +270,7 @@ class Port(object): item = nested_dict() item['flag'] = INV_FLAG_CARGOBAY item['quantity'] = amount - item['type']['href'] = "%sinventory/types/%d/"%(eve._authed_endpoint, chargeID) + item['type']['href'] = "%sinventory/types/%d/" % (eve._authed_endpoint, chargeID) item['type']['id'] = chargeID item['type']['name'] = '' fit['items'].append(item) @@ -127,7 +279,7 @@ class Port(object): item = nested_dict() item['flag'] = INV_FLAG_DRONEBAY item['quantity'] = drone.amount - item['type']['href'] = "%sinventory/types/%d/"%(eve._authed_endpoint, drone.item.ID) + item['type']['href'] = "%sinventory/types/%d/" % (eve._authed_endpoint, drone.item.ID) item['type']['id'] = drone.item.ID item['type']['name'] = '' fit['items'].append(item) @@ -136,7 +288,7 @@ class Port(object): item = nested_dict() item['flag'] = INV_FLAG_FIGHTER item['quantity'] = fighter.amountActive - item['type']['href'] = "%sinventory/types/%d/"%(eve._authed_endpoint, fighter.item.ID) + item['type']['href'] = "%sinventory/types/%d/" % (eve._authed_endpoint, fighter.item.ID) item['type']['id'] = fighter.item.ID item['type']['name'] = fighter.item.name fit['items'].append(item) @@ -176,9 +328,9 @@ class Port(object): return "DNA", (cls.importDna(string),) @staticmethod - def importCrest(str): - fit = json.loads(str) - sMkt = service.Market.getInstance() + def importCrest(str_): + fit = json.loads(str_) + sMkt = Market.getInstance() f = Fit() f.name = fit['name'] @@ -189,6 +341,7 @@ class Port(object): except ValueError: f.ship = Citadel(sMkt.getItem(fit['ship']['id'])) except: + pyfalog.warning("Caught exception in importCrest") return None items = fit['items'] @@ -214,6 +367,7 @@ class Port(object): m = Module(item) # When item can't be added to any slot (unknown item or just charge), ignore it except ValueError: + pyfalog.debug("Item can't be added to any slot (unknown item or just charge)") continue # Add subsystems before modules to make sure T3 cruisers have subsystems installed if item.category.name == "Subsystem": @@ -226,10 +380,11 @@ class Port(object): moduleList.append(m) except: + pyfalog.warning("Could not process module.") continue # Recalc to get slot numbers correct for T3 cruisers - service.Fit.getInstance().recalc(f) + svcFit.getInstance().recalc(f) for module in moduleList: if module.fits(f): @@ -239,21 +394,22 @@ class Port(object): @staticmethod def importDna(string): - sMkt = service.Market.getInstance() + sMkt = Market.getInstance() ids = map(int, re.findall(r'\d+', string)) - for id in ids: + for id_ in ids: try: try: try: - Ship(sMkt.getItem(sMkt.getItem(id))) + Ship(sMkt.getItem(sMkt.getItem(id_))) except ValueError: - Citadel(sMkt.getItem(sMkt.getItem(id))) + Citadel(sMkt.getItem(sMkt.getItem(id_))) except ValueError: - Citadel(sMkt.getItem(id)) - string = string[string.index(str(id)):] + Citadel(sMkt.getItem(id_)) + string = string[string.index(str(id_)):] break except: + pyfalog.warning("Exception caught in importDna") pass string = string[:string.index("::") + 2] info = string.split(":") @@ -265,12 +421,13 @@ class Port(object): except ValueError: f.ship = Citadel(sMkt.getItem(int(info[0]))) f.name = "{0} - DNA Imported".format(f.ship.item.name) - except UnicodeEncodeError as e: - def logtransform(s): - if len(s) > 10: - return s[:10] + "..." - return s - logger.exception("Couldn't import ship data %r", [ logtransform(s) for s in info ]) + except UnicodeEncodeError: + def logtransform(s_): + if len(s_) > 10: + return s_[:10] + "..." + return s_ + + pyfalog.exception("Couldn't import ship data {0}", [logtransform(s) for s in info]) return None moduleList = [] @@ -297,6 +454,7 @@ class Port(object): try: m = Module(item) except: + pyfalog.warning("Exception caught in importDna") continue # Add subsystems before modules to make sure T3 cruisers have subsystems installed if item.category.name == "Subsystem": @@ -309,7 +467,7 @@ class Port(object): moduleList.append(m) # Recalc to get slot numbers correct for T3 cruisers - service.Fit.getInstance().recalc(f) + svcFit.getInstance().recalc(f) for module in moduleList: if module.fits(f): @@ -322,7 +480,7 @@ class Port(object): @staticmethod def importEft(eftString): - sMkt = service.Market.getInstance() + sMkt = Market.getInstance() offineSuffix = " /OFFLINE" fit = Fit() @@ -345,6 +503,7 @@ class Port(object): fit.ship = Citadel(ship) fit.name = fitName except: + pyfalog.warning("Exception caught in importEft") return # maintain map of drones and their quantities @@ -385,24 +544,25 @@ class Port(object): item = sMkt.getItem(modName, eager="group.category") except: # if no data can be found (old names) + pyfalog.warning("no data can be found (old names)") continue if item.category.name == "Drone": extraAmount = int(extraAmount) if extraAmount is not None else 1 - if not modName in droneMap: + if modName not in droneMap: droneMap[modName] = 0 droneMap[modName] += extraAmount elif item.category.name == "Fighter": extraAmount = int(extraAmount) if extraAmount is not None else 1 fighterItem = Fighter(item) - if (extraAmount > fighterItem.fighterSquadronMaxSize): #Amount bigger then max fightergroup size + if extraAmount > fighterItem.fighterSquadronMaxSize: # Amount bigger then max fightergroup size extraAmount = fighterItem.fighterSquadronMaxSize if fighterItem.fits(fit): fit.fighters.append(fighterItem) if len(modExtra) == 2 and item.category.name != "Drone" and item.category.name != "Fighter": extraAmount = int(extraAmount) if extraAmount is not None else 1 - if not modName in cargoMap: + if modName not in cargoMap: cargoMap[modName] = 0 cargoMap[modName] += extraAmount elif item.category.name == "Implant": @@ -411,7 +571,7 @@ class Port(object): elif "boosterness" in item.attributes: fit.boosters.append(Booster(item)) else: - logger.error("Failed to import implant: %s", line) + pyfalog.error("Failed to import implant: {0}", line) # elif item.category.name == "Subsystem": # try: # subsystem = Module(item) @@ -446,13 +606,13 @@ class Port(object): moduleList.append(m) # Recalc to get slot numbers correct for T3 cruisers - service.Fit.getInstance().recalc(fit) + svcFit.getInstance().recalc(fit) for m in moduleList: if m.fits(fit): m.owner = fit if not m.isValidState(m.state): - print "Error: Module", m, "cannot have state", m.state + print("Error: Module", m, "cannot have state", m.state) fit.modules.append(m) @@ -473,7 +633,7 @@ class Port(object): """Handle import from EFT config store file""" # Check if we have such ship in database, bail if we don't - sMkt = service.Market.getInstance() + sMkt = Market.getInstance() try: sMkt.getItem(shipname) except: @@ -537,6 +697,7 @@ class Port(object): try: droneItem = sMkt.getItem(droneName, eager="group.category") except: + pyfalog.warning("Cannot get item.") continue if droneItem.category.name == "Drone": # Add drone to the fitting @@ -547,7 +708,7 @@ class Port(object): elif entityState == "Inactive": d.amountActive = 0 f.drones.append(d) - elif droneItem.category.name == "Fighter": # EFT saves fighter as drones + elif droneItem.category.name == "Fighter": # EFT saves fighter as drones ft = Fighter(droneItem) ft.amount = int(droneAmount) if ft.amount <= ft.fighterSquadronMaxSize else ft.fighterSquadronMaxSize f.fighters.append(ft) @@ -558,6 +719,7 @@ class Port(object): try: implantItem = sMkt.getItem(entityData, eager="group.category") except: + pyfalog.warning("Cannot get item.") continue if implantItem.category.name != "Implant": continue @@ -573,6 +735,7 @@ class Port(object): try: boosterItem = sMkt.getItem(entityData, eager="group.category") except: + pyfalog.warning("Cannot get item.") continue # All boosters have implant category if boosterItem.category.name != "Implant": @@ -593,6 +756,7 @@ class Port(object): try: item = sMkt.getItem(cargoName) except: + pyfalog.warning("Cannot get item.") continue # Add Cargo to the fitting c = Cargo(item) @@ -606,6 +770,7 @@ class Port(object): try: modItem = sMkt.getItem(modName) except: + pyfalog.warning("Cannot get item.") continue # Create module @@ -627,12 +792,13 @@ class Port(object): if chargeItem.category.name == "Charge": m.charge = chargeItem except: + pyfalog.warning("Cannot get item.") pass # Append module to fit moduleList.append(m) # Recalc to get slot numbers correct for T3 cruisers - service.Fit.getInstance().recalc(f) + svcFit.getInstance().recalc(f) for module in moduleList: if module.fits(f): @@ -645,13 +811,14 @@ class Port(object): wx.CallAfter(callback, None) # Skip fit silently if we get an exception except Exception: + pyfalog.error("Caught exception on fit.") pass return fits @staticmethod def importXml(text, callback=None, encoding="utf-8"): - sMkt = service.Market.getInstance() + sMkt = Market.getInstance() doc = xml.dom.minidom.parseString(text.encode(encoding)) fittings = doc.getElementsByTagName("fittings").item(0) @@ -669,6 +836,7 @@ class Port(object): except ValueError: f.ship = Citadel(sMkt.getItem(shipType)) except: + pyfalog.warning("Caught exception on importXml") continue hardwares = fitting.getElementsByTagName("hardware") moduleList = [] @@ -678,6 +846,7 @@ class Port(object): try: item = sMkt.getItem(moduleName, eager="group.category") except: + pyfalog.warning("Caught exception on importXml") continue if item: if item.category.name == "Drone": @@ -700,6 +869,7 @@ class Port(object): m = Module(item) # When item can't be added to any slot (unknown item or just charge), ignore it except ValueError: + pyfalog.warning("item can't be added to any slot (unknown item or just charge), ignore it") continue # Add subsystems before modules to make sure T3 cruisers have subsystems installed if item.category.name == "Subsystem": @@ -713,10 +883,11 @@ class Port(object): moduleList.append(m) except KeyboardInterrupt: + pyfalog.warning("Keyboard Interrupt") continue # Recalc to get slot numbers correct for T3 cruisers - service.Fit.getInstance().recalc(f) + svcFit.getInstance().recalc(f) for module in moduleList: if module.fits(f): @@ -734,12 +905,13 @@ class Port(object): offineSuffix = " /OFFLINE" export = "[%s, %s]\n" % (fit.ship.item.name, fit.name) stuff = {} - sFit = service.Fit.getInstance() + sFit = svcFit.getInstance() for module in fit.modules: slot = module.slot - if not slot in stuff: + if slot not in stuff: stuff[slot] = [] - curr = module.item.name if module.item else ("[Empty %s slot]" % Slot.getName(slot).capitalize() if slot is not None else "") + curr = module.item.name if module.item \ + else ("[Empty %s slot]" % Slot.getName(slot).capitalize() if slot is not None else "") if module.charge and sFit.serviceFittingOptions["exportCharges"]: curr += ", %s" % module.charge.name if module.state == State.OFFLINE: @@ -816,29 +988,27 @@ class Port(object): if mod.slot == Slot.SUBSYSTEM: subsystems.append(mod) continue - if not mod.itemID in mods: + if mod.itemID not in mods: mods[mod.itemID] = 0 mods[mod.itemID] += 1 if mod.charge: - if not mod.chargeID in charges: + if mod.chargeID not in charges: charges[mod.chargeID] = 0 # `or 1` because some charges (ie scripts) are without qty charges[mod.chargeID] += mod.numCharges or 1 - for subsystem in sorted(subsystems, key=lambda mod: mod.getModifiedItemAttr("subSystemSlot")): + for subsystem in sorted(subsystems, key=lambda mod_: mod_.getModifiedItemAttr("subSystemSlot")): dna += ":{0};1".format(subsystem.itemID) for mod in mods: dna += ":{0};{1}".format(mod, mods[mod]) - # drones are known to be in split stacks - groupedDrones = OrderedDict() for drone in fit.drones: - groupedDrones[drone.itemID] = groupedDrones.get(drone.itemID, 0) + drone.amount + dna += ":{0};{1}".format(drone.itemID, drone.amount) - for droneItemID in groupedDrones: - dna += ":{0};{1}".format(droneItemID, groupedDrones[droneItemID]) + for fighter in fit.fighters: + dna += ":{0};{1}".format(fighter.itemID, fighter.amountActive) for fighter in fit.fighters: dna += ":{0};{1}".format(fighter.itemID, fighter.amountActive) @@ -850,7 +1020,7 @@ class Port(object): # as being "Fitted" to whatever slot they are for, and it causes an corruption error in the # client when trying to save the fit if cargo.item.category.name == "Charge": - if not cargo.item.ID in charges: + if cargo.item.ID not in charges: charges[cargo.item.ID] = 0 charges[cargo.item.ID] += cargo.amount @@ -864,7 +1034,7 @@ class Port(object): doc = xml.dom.minidom.Document() fittings = doc.createElement("fittings") doc.appendChild(fittings) - sFit = service.Fit.getInstance() + sFit = svcFit.getInstance() for i, fit in enumerate(fits): try: @@ -890,7 +1060,7 @@ class Port(object): # Order of subsystem matters based on this attr. See GH issue #130 slotId = module.getModifiedItemAttr("subSystemSlot") - 125 else: - if not slot in slotNum: + if slot not in slotNum: slotNum[slot] = 0 slotId = slotNum[slot] @@ -904,7 +1074,7 @@ class Port(object): fitting.appendChild(hardware) if module.charge and sFit.serviceFittingOptions["exportCharges"]: - if not module.charge.name in charges: + if module.charge.name not in charges: charges[module.charge.name] = 0 # `or 1` because some charges (ie scripts) are without qty charges[module.charge.name] += module.numCharges or 1 @@ -924,7 +1094,7 @@ class Port(object): fitting.appendChild(hardware) for cargo in fit.cargo: - if not cargo.item.name in charges: + if cargo.item.name not in charges: charges[cargo.item.name] = 0 charges[cargo.item.name] += cargo.amount @@ -935,7 +1105,7 @@ class Port(object): hardware.setAttribute("type", name) fitting.appendChild(hardware) except: - print "Failed on fitID: %d"%fit.ID + print("Failed on fitID: %d" % fit.ID) continue finally: if callback: @@ -945,15 +1115,14 @@ class Port(object): @staticmethod def exportMultiBuy(fit): - export = "%s\n" % (fit.ship.item.name) + export = "%s\n" % fit.ship.item.name stuff = {} - sFit = service.Fit.getInstance() + sFit = svcFit.getInstance() for module in fit.modules: slot = module.slot - if not slot in stuff: + if slot not in stuff: stuff[slot] = [] - curr = "%s\n" % module.item.name if module.item else ( - "") + curr = "%s\n" % module.item.name if module.item else "" if module.charge and sFit.serviceFittingOptions["exportCharges"]: curr += "%s x%s\n" % (module.charge.name, module.numCharges) stuff[slot].append(curr) @@ -989,3 +1158,39 @@ class Port(object): export = export[:-1] return export + + +class FitBackupThread(threading.Thread): + def __init__(self, path, callback): + threading.Thread.__init__(self) + self.path = path + self.callback = callback + + def run(self): + path = self.path + sFit = svcFit.getInstance() + sPort = Port.getInstance() + backedUpFits = sPort.exportXml(self.callback, *sFit.getAllFits()) + backupFile = open(path, "w", encoding="utf-8") + backupFile.write(backedUpFits) + backupFile.close() + + # Send done signal to GUI + wx.CallAfter(self.callback, -1) + + +class FitImportThread(threading.Thread): + def __init__(self, paths, callback): + threading.Thread.__init__(self) + self.paths = paths + self.callback = callback + + def run(self): + sPort = Port.getInstance() + success, result = sPort.importFitFromFiles(self.paths, self.callback) + + if not success: # there was an error during processing + pyfalog.error("Error while processing file import: {0}", result) + wx.CallAfter(self.callback, -2, result) + else: # Send done signal to GUI + wx.CallAfter(self.callback, -1, result) diff --git a/service/prefetch.py b/service/prefetch.py index 6c45ffc63..e73da9ff5 100644 --- a/service/prefetch.py +++ b/service/prefetch.py @@ -1,82 +1,70 @@ -#=============================================================================== -# Copyright (C) 2010 Diego Duclos -# -# This file is part of pyfa. -# -# pyfa is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# pyfa is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with pyfa. If not, see . -#=============================================================================== - -import threading -import config -import os -import eos.types -import eos.db.migration as migration -from eos.db.saveddata.loadDefaultDatabaseValues import DefaultDatabaseValues -from eos.db.saveddata.databaseRepair import DatabaseCleanup -import logging - -logger = logging.getLogger(__name__) - - -class PrefetchThread(threading.Thread): - def run(self): - # We're a daemon thread, as such, interpreter might get shut down while we do stuff - # Make sure we don't throw tracebacks to console - try: - eos.types.Character.setSkillList(eos.db.getItemsByCategory("Skill", eager=("effects", "attributes", "attributes.info.icon", "attributes.info.unit", "icon"))) - except: - pass - -prefetch = PrefetchThread() -prefetch.daemon = True -prefetch.start() - -######## -# The following code does not belong here, however until we rebuild skeletons -# to include modified pyfa.py, this is the best place to put it. See GH issue -# #176 -# @ todo: move this to pyfa.py -######## - -#Make sure the saveddata db exists -if not os.path.exists(config.savePath): - os.mkdir(config.savePath) - -if os.path.isfile(config.saveDB): - # If database exists, run migration after init'd database - eos.db.saveddata_meta.create_all() - migration.update(eos.db.saveddata_engine) - # Import default database values - # Import values that must exist otherwise Pyfa breaks - DefaultDatabaseValues.importRequiredDefaults() - - logging.debug("Starting database validation.") - database_cleanup_instance = DatabaseCleanup() - database_cleanup_instance.OrphanedCharacterSkills(eos.db.saveddata_engine) - database_cleanup_instance.OrphanedFitCharacterIDs(eos.db.saveddata_engine) - database_cleanup_instance.OrphanedFitDamagePatterns(eos.db.saveddata_engine) - logging.debug("Completed database validation.") - -else: - # If database does not exist, do not worry about migration. Simply - # create and set version - eos.db.saveddata_meta.create_all() - eos.db.saveddata_engine.execute('PRAGMA user_version = {}'.format(migration.getAppVersion())) - #Import default database values - # Import values that must exist otherwise Pyfa breaks - DefaultDatabaseValues.importRequiredDefaults() - # Import default values for damage profiles - DefaultDatabaseValues.importDamageProfileDefaults() - # Import default values for target resist profiles - DefaultDatabaseValues.importResistProfileDefaults() +# ============================================================================= +# Copyright (C) 2010 Diego Duclos +# +# This file is part of pyfa. +# +# pyfa is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# pyfa is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with pyfa. If not, see . +# ============================================================================= + +import os + +import config +from eos import db +from eos.db import migration +from eos.db.saveddata.loadDefaultDatabaseValues import DefaultDatabaseValues +from eos.db.saveddata.databaseRepair import DatabaseCleanup + +from logbook import Logger + +pyfalog = Logger(__name__) + +# Make sure the saveddata db exists +if config.savePath and not os.path.exists(config.savePath): + os.mkdir(config.savePath) + +if config.saveDB and os.path.isfile(config.saveDB): + # If database exists, run migration after init'd database + pyfalog.debug("Run database migration.") + db.saveddata_meta.create_all() + migration.update(db.saveddata_engine) + # Import default database values + # Import values that must exist otherwise Pyfa breaks + pyfalog.debug("Import Required Database Values.") + DefaultDatabaseValues.importRequiredDefaults() + + # Finds and fixes database corruption issues. + pyfalog.debug("Starting database validation.") + database_cleanup_instance = DatabaseCleanup() + database_cleanup_instance.OrphanedCharacterSkills(db.saveddata_engine) + database_cleanup_instance.OrphanedFitCharacterIDs(db.saveddata_engine) + database_cleanup_instance.OrphanedFitDamagePatterns(db.saveddata_engine) + database_cleanup_instance.NullDamagePatternNames(db.saveddata_engine) + database_cleanup_instance.NullTargetResistNames(db.saveddata_engine) + database_cleanup_instance.OrphanedFitIDItemID(db.saveddata_engine) + database_cleanup_instance.NullDamageTargetPatternValues(db.saveddata_engine) + database_cleanup_instance.DuplicateSelectedAmmoName(db.saveddata_engine) + pyfalog.debug("Completed database validation.") + +else: + # If database does not exist, do not worry about migration. Simply + # create and set version + db.saveddata_meta.create_all() + db.saveddata_engine.execute('PRAGMA user_version = {}'.format(migration.getAppVersion())) + # Import default database values + # Import values that must exist otherwise Pyfa breaks + DefaultDatabaseValues.importRequiredDefaults() + # Import default values for damage profiles + DefaultDatabaseValues.importDamageProfileDefaults() + # Import default values for target resist profiles + DefaultDatabaseValues.importResistProfileDefaults() diff --git a/service/price.py b/service/price.py index 71858cf7c..facb5e389 100644 --- a/service/price.py +++ b/service/price.py @@ -1,4 +1,4 @@ -#=============================================================================== +# ============================================================================= # Copyright (C) 2010 Diego Duclos # # This file is part of pyfa. @@ -15,19 +15,38 @@ # # You should have received a copy of the GNU General Public License # along with pyfa. If not, see . -#=============================================================================== +# ============================================================================= + -import service -import eos.db -import eos.types import time from xml.dom import minidom -VALIDITY = 24*60*60 # Price validity period, 24 hours -REREQUEST = 4*60*60 # Re-request delay for failed fetches, 4 hours -TIMEOUT = 15*60 # Network timeout delay for connection issues, 15 minutes +from eos import db +from service.network import Network, TimeoutError +from service.fit import Fit +from logbook import Logger -class Price(): +pyfalog = Logger(__name__) + + +VALIDITY = 24 * 60 * 60 # Price validity period, 24 hours +REREQUEST = 4 * 60 * 60 # Re-request delay for failed fetches, 4 hours +TIMEOUT = 15 * 60 # Network timeout delay for connection issues, 15 minutes + + +class Price(object): + systemsList = { + "Jita": 30000142, + "Amarr": 30002187, + "Dodixie": 30002659, + "Rens": 30002510, + "Hek": 30002053 + } + + @classmethod + def invalidPrices(cls, prices): + for price in prices: + price.time = 0 @classmethod def fetchPrices(cls, prices): @@ -49,7 +68,7 @@ class Price(): # Compose list of items we're going to request for typeID in priceMap: # Get item object - item = eos.db.getItem(typeID) + item = db.getItem(typeID) # We're not going to request items only with market group, as eve-central # doesn't provide any data for items not on the market if item is not None and item.marketGroupID: @@ -62,24 +81,25 @@ class Price(): # This will store POST data for eve-central data = [] + sFit = Fit.getInstance() # Base request URL baseurl = "https://eve-central.com/api/marketstat" - data.append(("usesystem", 30000142)) # Use Jita for market + data.append(("usesystem", cls.systemsList[sFit.serviceFittingOptions["priceSystem"]])) # Use Jita for market for typeID in toRequest: # Add all typeID arguments data.append(("typeid", typeID)) # Attempt to send request and process it try: - network = service.Network.getInstance() + network = Network.getInstance() data = network.request(baseurl, network.PRICES, data) xml = minidom.parse(data) types = xml.getElementsByTagName("marketstat").item(0).getElementsByTagName("type") # Cycle through all types we've got from request - for type in types: + for type_ in types: # Get data out of each typeID details tree - typeID = int(type.getAttribute("id")) - sell = type.getElementsByTagName("sell").item(0) + typeID = int(type_.getAttribute("id")) + sell = type_.getElementsByTagName("sell").item(0) # If price data wasn't there, set price to zero try: percprice = float(sell.getElementsByTagName("percentile").item(0).firstChild.data) @@ -96,8 +116,9 @@ class Price(): del priceMap[typeID] # If getting or processing data returned any errors - except service.network.TimeoutError, e: + except TimeoutError: # Timeout error deserves special treatment + pyfalog.warning("Price fetch timout") for typeID in priceMap.keys(): priceobj = priceMap[typeID] priceobj.time = time.time() + TIMEOUT @@ -105,6 +126,7 @@ class Price(): del priceMap[typeID] except: # all other errors will pass and continue onward to the REREQUEST delay + pyfalog.warning("Caught exception in fetchPrices") pass # if we get to this point, then we've got an error. Set to REREQUEST delay @@ -112,3 +134,23 @@ class Price(): priceobj = priceMap[typeID] priceobj.time = time.time() + REREQUEST priceobj.failed = True + + @classmethod + def fitItemsList(cls, fit): + # Compose a list of all the data we need & request it + typeIDs = [fit.ship.item.ID] + + for mod in fit.modules: + if not mod.isEmpty: + typeIDs.append(mod.itemID) + + for drone in fit.drones: + typeIDs.append(drone.itemID) + + for fighter in fit.fighters: + typeIDs.append(fighter.itemID) + + for cargo in fit.cargo: + typeIDs.append(cargo.itemID) + + return typeIDs diff --git a/service/pycrest/__init__.py b/service/pycrest/__init__.py index 97244c957..5820a7290 100644 --- a/service/pycrest/__init__.py +++ b/service/pycrest/__init__.py @@ -1,13 +1 @@ -import logging - - -class NullHandler(logging.Handler): - def emit(self, record): - pass - -logger = logging.getLogger('pycrest') -logger.addHandler(NullHandler()) - version = "0.0.1" - -from .eve import EVE \ No newline at end of file diff --git a/service/pycrest/compat.py b/service/pycrest/compat.py index 06320069b..7484b41dd 100644 --- a/service/pycrest/compat.py +++ b/service/pycrest/compat.py @@ -21,4 +21,4 @@ def text_(s, encoding='latin-1', errors='strict'): # pragma: no cover def bytes_(s, encoding='latin-1', errors='strict'): # pragma: no cover if isinstance(s, text_type): return s.encode(encoding, errors) - return s \ No newline at end of file + return s diff --git a/service/pycrest/errors.py b/service/pycrest/errors.py index 33b9ca9ae..4216deabb 100644 --- a/service/pycrest/errors.py +++ b/service/pycrest/errors.py @@ -1,2 +1,2 @@ class APIException(Exception): - pass \ No newline at end of file + pass diff --git a/service/pycrest/eve.py b/service/pycrest/eve.py index c0f33becd..6a4799d74 100644 --- a/service/pycrest/eve.py +++ b/service/pycrest/eve.py @@ -1,34 +1,26 @@ -import os import base64 +from logbook import Logger +import os +import re import time import zlib import requests - -from . import version -from compat import bytes_, text_ -from errors import APIException from requests.adapters import HTTPAdapter -try: - from urllib.parse import urlparse, urlunparse, parse_qsl -except ImportError: # pragma: no cover - from urlparse import urlparse, urlunparse, parse_qsl +import config +from service.pycrest.compat import bytes_, text_ +from service.pycrest.errors import APIException + +from urlparse import urlparse, urlunparse, parse_qsl try: import pickle except ImportError: # pragma: no cover + # noinspection PyPep8Naming import cPickle as pickle -try: - from urllib.parse import quote -except ImportError: # pragma: no cover - from urllib import quote -import logging -import re -import config - -logger = logging.getLogger("pycrest.eve") +pyfalog = Logger(__name__) cache_re = re.compile(r'max-age=([0-9]+)') @@ -66,6 +58,7 @@ class FileCache(APICache): with open(self._getpath(key), 'rb') as f: return pickle.loads(zlib.decompress(f.read())) except IOError as ex: + pyfalog.debug("IO error opening zip file. (May not exist yet)") if ex.errno == 2: # file does not exist (yet) return None else: @@ -77,6 +70,8 @@ class FileCache(APICache): try: os.unlink(self._getpath(key)) except OSError as ex: + pyfalog.debug("Caught exception in invalidate") + pyfalog.debug(ex) if ex.errno == 2: # does not exist pass else: @@ -110,8 +105,7 @@ class APIConnection(object): "Accept": "application/json", }) session.headers.update(additional_headers) - session.mount('https://public-crest.eveonline.com', - HTTPAdapter()) + session.mount('https://public-crest.eveonline.com', HTTPAdapter()) self._session = session if cache: if isinstance(cache, APICache): @@ -125,7 +119,7 @@ class APIConnection(object): self.cache = DictCache() def get(self, resource, params=None): - logger.debug('Getting resource %s', resource) + pyfalog.debug('Getting resource {0}', resource) if params is None: params = {} @@ -145,18 +139,18 @@ class APIConnection(object): key = (resource, frozenset(self._session.headers.items()), frozenset(prms.items())) cached = self.cache.get(key) if cached and cached['cached_until'] > time.time(): - logger.debug('Cache hit for resource %s (params=%s)', resource, prms) + pyfalog.debug('Cache hit for resource {0} (params={1})', resource, prms) return cached elif cached: - logger.debug('Cache stale for resource %s (params=%s)', resource, prms) + pyfalog.debug('Cache stale for resource {0} (params={1})', resource, prms) self.cache.invalidate(key) else: - logger.debug('Cache miss for resource %s (params=%s', resource, prms) + pyfalog.debug('Cache miss for resource {0} (params={1})', resource, prms) - logger.debug('Getting resource %s (params=%s)', resource, prms) + pyfalog.debug('Getting resource {0} (params={1})', resource, prms) res = self._session.get(resource, params=prms) if res.status_code != 200: - raise APIException("Got unexpected status code from server: %i" % res.status_code) + raise APIException("Got unexpected status code from server: {0}" % res.status_code) ret = res.json() @@ -168,7 +162,8 @@ class APIConnection(object): return ret - def _get_expires(self, response): + @staticmethod + def _get_expires(response): if 'Cache-Control' not in response.headers: return 0 if any([s in response.headers['Cache-Control'] for s in ['no-cache', 'no-store']]): @@ -249,19 +244,18 @@ class EVE(APIConnection): def temptoken_authorize(self, access_token=None, expires_in=0, refresh_token=None): self.set_auth_values({'access_token': access_token, - 'refresh_token': refresh_token, - 'expires_in': expires_in}) + 'refresh_token': refresh_token, + 'expires_in': expires_in}) class AuthedConnection(EVE): - def __call__(self): if not self._data: self._data = APIObject(self.get(self._endpoint), self) return self._data def whoami(self): - #if 'whoami' not in self._cache: + # if 'whoami' not in self._cache: # print "Setting this whoami cache" # self._cache['whoami'] = self.get("%s/verify" % self._oauth_endpoint) return self.get("%s/verify" % self._oauth_endpoint) @@ -281,6 +275,7 @@ class AuthedConnection(EVE): self.refr_authorize(self.refresh_token) return self._session.delete(resource, params=params) + class APIObject(object): def __init__(self, parent, connection): self._dict = {} diff --git a/service/pycrest/weak_ciphers.py b/service/pycrest/weak_ciphers.py index 03de8321a..b18a1a552 100644 --- a/service/pycrest/weak_ciphers.py +++ b/service/pycrest/weak_ciphers.py @@ -1,36 +1,26 @@ import datetime import ssl -import sys import warnings from requests.adapters import HTTPAdapter try: - from requests.packages import urllib3 - from requests.packages.urllib3.util import ssl_ - - from requests.packages.urllib3.exceptions import ( - SystemTimeWarning, - SecurityWarning, - ) - from requests.packages.urllib3.packages.ssl_match_hostname import \ - match_hostname + from requests.packages import urllib3 + from requests.packages.urllib3.util import ssl_ + from requests.packages.urllib3.exceptions import ( + SystemTimeWarning, + SecurityWarning, + ) + from requests.packages.urllib3.packages.ssl_match_hostname import \ + match_hostname except: - import urllib3 - from urllib3.util import ssl_ - - from urllib3.exceptions import ( - SystemTimeWarning, - SecurityWarning, - ) - from urllib3.packages.ssl_match_hostname import \ - match_hostname + import urllib3 + from urllib3.util import ssl_ + from urllib3.exceptions import SystemTimeWarning, SecurityWarning + from urllib3.packages.ssl_match_hostname import match_hostname - - -class WeakCiphersHTTPSConnection( - urllib3.connection.VerifiedHTTPSConnection): # pragma: no cover +class WeakCiphersHTTPSConnection(urllib3.connection.VerifiedHTTPSConnection): # pragma: no cover # Python versions >=2.7.9 and >=3.4.1 do not (by default) allow ciphers # with MD5. Unfortunately, the CREST public server _only_ supports @@ -77,22 +67,26 @@ class WeakCiphersHTTPSConnection( warnings.warn(( 'System time is way off (before {0}). This will probably ' 'lead to SSL verification errors').format( - urllib3.connection.RECENT_DATE), + urllib3.connection.RECENT_DATE), SystemTimeWarning ) # Wrap socket using verification with the root certs in # trusted_root_certs - self.sock = ssl_.ssl_wrap_socket(conn, self.key_file, self.cert_file, - cert_reqs=resolved_cert_reqs, - ca_certs=self.ca_certs, - server_hostname=hostname, - ssl_version=resolved_ssl_version, - ciphers=self.ciphers) + self.sock = ssl_.ssl_wrap_socket( + conn, + self.key_file, + self.cert_file, + cert_reqs=resolved_cert_reqs, + ca_certs=self.ca_certs, + server_hostname=hostname, + ssl_version=resolved_ssl_version, + ciphers=self.ciphers, + ) if self.assert_fingerprint: ssl_.assert_fingerprint(self.sock.getpeercert(binary_form=True), - self.assert_fingerprint) + self.assert_fingerprint) elif resolved_cert_reqs != ssl.CERT_NONE \ and self.assert_hostname is not False: cert = self.sock.getpeercert() @@ -105,36 +99,34 @@ class WeakCiphersHTTPSConnection( ) match_hostname(cert, self.assert_hostname or hostname) - self.is_verified = (resolved_cert_reqs == ssl.CERT_REQUIRED - or self.assert_fingerprint is not None) + self.is_verified = (resolved_cert_reqs == ssl.CERT_REQUIRED or self.assert_fingerprint is not None) -class WeakCiphersHTTPSConnectionPool( - urllib3.connectionpool.HTTPSConnectionPool): - +class WeakCiphersHTTPSConnectionPool(urllib3.connectionpool.HTTPSConnectionPool): ConnectionCls = WeakCiphersHTTPSConnection class WeakCiphersPoolManager(urllib3.poolmanager.PoolManager): - def _new_pool(self, scheme, host, port): if scheme == 'https': - return WeakCiphersHTTPSConnectionPool(host, port, - **(self.connection_pool_kw)) - return super(WeakCiphersPoolManager, self)._new_pool(scheme, host, - port) + return WeakCiphersHTTPSConnectionPool(host, port, **self.connection_pool_kw) + return super(WeakCiphersPoolManager, self)._new_pool(scheme, host, port) class WeakCiphersAdapter(HTTPAdapter): """"Transport adapter" that allows us to use TLS_RSA_WITH_RC4_128_MD5.""" - def init_poolmanager(self, connections, maxsize, block=False, - **pool_kwargs): + def init_poolmanager(self, connections, maxsize, block=False, **pool_kwargs): # Rewrite of the requests.adapters.HTTPAdapter.init_poolmanager method # to use WeakCiphersPoolManager instead of urllib3's PoolManager self._pool_connections = connections self._pool_maxsize = maxsize self._pool_block = block - self.poolmanager = WeakCiphersPoolManager(num_pools=connections, - maxsize=maxsize, block=block, strict=True, **pool_kwargs) + self.poolmanager = WeakCiphersPoolManager( + num_pools=connections, + maxsize=maxsize, + block=block, + strict=True, + **pool_kwargs + ) diff --git a/service/server.py b/service/server.py index ed42d0082..ff71d2f92 100644 --- a/service/server.py +++ b/service/server.py @@ -1,14 +1,13 @@ import BaseHTTPServer import urlparse import socket -import thread -import wx +import threading +from logbook import Logger + from service.settings import CRESTSettings +pyfalog = Logger(__name__) -import logging - -logger = logging.getLogger(__name__) - +# noinspection PyPep8 HTML = ''' @@ -17,12 +16,13 @@ HTML = ''' pyfa Local Server @@ -31,59 +31,87 @@ HTML = '''

      pyfa

      -
      -

      If you see this message then it means you should be logged into CREST. You may close this window and return to the application.

      -
      + {0}
      + ''' + # https://github.com/fuzzysteve/CREST-Market-Downloader/ class AuthHandler(BaseHTTPServer.BaseHTTPRequestHandler): def do_GET(self): if self.path == "/favicon.ico": return + parsed_path = urlparse.urlparse(self.path) parts = urlparse.parse_qs(parsed_path.query) - self.send_response(200) - self.end_headers() - self.wfile.write(HTML) + msg = "" - wx.CallAfter(self.server.callback, parts) + step2 = 'step' in parts + + try: + if step2: + self.server.callback(parts) + msg = "If you see this message then it means you should be logged into CREST. You may close this window and return to the application." + else: + # For implicit mode, we have to serve up the page which will take the hash and redirect useing a querystring + msg = "Processing response from EVE Online" + except Exception, ex: + msg = "

      Error

      \n

      {}

      ".format(ex.message) + finally: + self.send_response(200) + self.end_headers() + self.wfile.write(HTML.format(msg)) + + if step2: + # Only stop once if we've received something in the querystring + self.server.stop() def log_message(self, format, *args): return + # http://code.activestate.com/recipes/425210-simple-stoppable-server-using-socket-timeout/ class StoppableHTTPServer(BaseHTTPServer.HTTPServer): - def server_bind(self): BaseHTTPServer.HTTPServer.server_bind(self) self.settings = CRESTSettings.getInstance() # Allow listening for x seconds sec = self.settings.get('timeout') - logger.debug("Running server for %d seconds", sec) + pyfalog.debug("Running server for {0} seconds", sec) - self.socket.settimeout(0.5) + self.socket.settimeout(1) self.max_tries = sec / self.socket.gettimeout() self.tries = 0 self.run = True @@ -93,32 +121,35 @@ class StoppableHTTPServer(BaseHTTPServer.HTTPServer): try: sock, addr = self.socket.accept() sock.settimeout(None) - return (sock, addr) + return sock, addr except socket.timeout: + pyfalog.warning("Server timed out waiting for connection") pass def stop(self): self.run = False def handle_timeout(self): - #logger.debug("Number of tries: %d"%self.tries) + pyfalog.debug("Number of tries: {0}", self.tries) self.tries += 1 if self.tries == self.max_tries: - logger.debug("Server timed out waiting for connection") + pyfalog.debug("Server timed out waiting for connection") self.stop() - def serve(self, callback): + def serve(self, callback=None): self.callback = callback while self.run: try: self.handle_request() except TypeError: + pyfalog.debug("Caught exception in serve") pass + self.server_close() + if __name__ == "__main__": httpd = StoppableHTTPServer(('', 6461), AuthHandler) - thread.start_new_thread(httpd.serve, ()) + t = threading.Thread(target=httpd.serve) raw_input("Press to stop server\n") httpd.stop() - diff --git a/service/settings.py b/service/settings.py index 89f871973..d3f28e0a4 100644 --- a/service/settings.py +++ b/service/settings.py @@ -1,4 +1,4 @@ -#=============================================================================== +# ============================================================================= # Copyright (C) 2010 Diego Duclos # # This file is part of pyfa. @@ -15,27 +15,30 @@ # # You should have received a copy of the GNU General Public License # along with pyfa. If not, see . -#=============================================================================== +# ============================================================================= import cPickle import os.path -import config import urllib2 -class SettingsProvider(): - BASE_PATH = os.path.join(config.savePath, "settings") +import config + + +class SettingsProvider(object): + BASE_PATH = os.path.join(config.savePath, 'settings') settings = {} _instance = None + @classmethod def getInstance(cls): - if cls._instance == None: + if cls._instance is None: cls._instance = SettingsProvider() return cls._instance def __init__(self): if not os.path.exists(self.BASE_PATH): - os.mkdir(self.BASE_PATH); + os.mkdir(self.BASE_PATH) def getSettings(self, area, defaults=None): @@ -71,7 +74,8 @@ class SettingsProvider(): for settings in self.settings.itervalues(): settings.save() -class Settings(): + +class Settings(object): def __init__(self, location, info): self.location = location self.info = info @@ -111,17 +115,17 @@ class Settings(): return self.info.items() -class NetworkSettings(): +class NetworkSettings(object): _instance = None # constants for serviceNetworkDefaultSettings["mode"] parameter - PROXY_MODE_NONE = 0 # 0 - No proxy + PROXY_MODE_NONE = 0 # 0 - No proxy PROXY_MODE_AUTODETECT = 1 # 1 - Auto-detected proxy settings - PROXY_MODE_MANUAL = 2 # 2 - Manual proxy settings + PROXY_MODE_MANUAL = 2 # 2 - Manual proxy settings @classmethod def getInstance(cls): - if cls._instance == None: + if cls._instance is None: cls._instance = NetworkSettings() return cls._instance @@ -184,17 +188,16 @@ class NetworkSettings(): def setAccess(self, access): self.serviceNetworkSettings["access"] = access - def autodetect(self): + @staticmethod + def autodetect(): proxy = None - proxAddr = proxPort = "" proxydict = urllib2.ProxyHandler().proxies - txt = "Auto-detected: " validPrefixes = ("http", "https") for prefix in validPrefixes: - if not prefix in proxydict: + if prefix not in proxydict: continue proxyline = proxydict[prefix] proto = "{0}://".format(prefix) @@ -214,7 +217,7 @@ class NetworkSettings(): if self.getMode() == self.PROXY_MODE_AUTODETECT: return self.autodetect() if self.getMode() == self.PROXY_MODE_MANUAL: - return (self.getAddress(), int(self.getPort())) + return self.getAddress(), int(self.getPort()) def getProxyAuthDetails(self): if self.getMode() == self.PROXY_MODE_NONE: @@ -222,7 +225,7 @@ class NetworkSettings(): if (self.serviceNetworkSettings["login"] is None) or (self.serviceNetworkSettings["password"] is None): return None # in all other cases, return tuple of (login, password) - return (self.serviceNetworkSettings["login"], self.serviceNetworkSettings["password"]) + return self.serviceNetworkSettings["login"], self.serviceNetworkSettings["password"] def setProxyAuthDetails(self, login, password): if (login is None) or (password is None): @@ -237,53 +240,58 @@ class NetworkSettings(): self.serviceNetworkSettings["password"] = password - -""" -Settings used by the HTML export feature. -""" -class HTMLExportSettings(): +class HTMLExportSettings(object): + """ + Settings used by the HTML export feature. + """ _instance = None @classmethod def getInstance(cls): - if cls._instance == None: + if cls._instance is None: cls._instance = HTMLExportSettings() return cls._instance def __init__(self): - serviceHTMLExportDefaultSettings = {"enabled": False, "path": config.pyfaPath + os.sep + 'pyfaFits.html', "minimal": False } - self.serviceHTMLExportSettings = SettingsProvider.getInstance().getSettings("pyfaServiceHTMLExportSettings", serviceHTMLExportDefaultSettings) + serviceHTMLExportDefaultSettings = { + "enabled": False, + "path": config.pyfaPath + os.sep + 'pyfaFits.html', + "minimal": False + } + self.serviceHTMLExportSettings = SettingsProvider.getInstance().getSettings( + "pyfaServiceHTMLExportSettings", + serviceHTMLExportDefaultSettings + ) def getEnabled(self): return self.serviceHTMLExportSettings["enabled"] def setEnabled(self, enabled): self.serviceHTMLExportSettings["enabled"] = enabled - - + def getMinimalEnabled(self): return self.serviceHTMLExportSettings["minimal"] def setMinimalEnabled(self, minimal): self.serviceHTMLExportSettings["minimal"] = minimal - def getPath(self): return self.serviceHTMLExportSettings["path"] def setPath(self, path): self.serviceHTMLExportSettings["path"] = path -""" -Settings used by update notification -""" -class UpdateSettings(): + +class UpdateSettings(object): + """ + Settings used by update notification + """ _instance = None @classmethod def getInstance(cls): - if cls._instance == None: + if cls._instance is None: cls._instance = UpdateSettings() return cls._instance @@ -293,8 +301,11 @@ class UpdateSettings(): # Updates are completely suppressed via network settings # prerelease - If True, suppress prerelease notifications # version - Set to release tag that user does not want notifications for - serviceUpdateDefaultSettings = {"prerelease": True, 'version': None } - self.serviceUpdateSettings = SettingsProvider.getInstance().getSettings("pyfaServiceUpdateSettings", serviceUpdateDefaultSettings) + serviceUpdateDefaultSettings = {"prerelease": True, 'version': None} + self.serviceUpdateSettings = SettingsProvider.getInstance().getSettings( + "pyfaServiceUpdateSettings", + serviceUpdateDefaultSettings + ) def get(self, type): return self.serviceUpdateSettings[type] @@ -302,7 +313,8 @@ class UpdateSettings(): def set(self, type, value): self.serviceUpdateSettings[type] = value -class CRESTSettings(): + +class CRESTSettings(object): _instance = None @classmethod @@ -313,13 +325,15 @@ class CRESTSettings(): return cls._instance def __init__(self): - # mode # 0 - Implicit authentication # 1 - User-supplied client details serviceCRESTDefaultSettings = {"mode": 0, "server": 0, "clientID": "", "clientSecret": "", "timeout": 60} - self.serviceCRESTSettings = SettingsProvider.getInstance().getSettings("pyfaServiceCRESTSettings", serviceCRESTDefaultSettings) + self.serviceCRESTSettings = SettingsProvider.getInstance().getSettings( + "pyfaServiceCRESTSettings", + serviceCRESTDefaultSettings + ) def get(self, type): return self.serviceCRESTSettings[type] @@ -327,5 +341,4 @@ class CRESTSettings(): def set(self, type, value): self.serviceCRESTSettings[type] = value - # @todo: migrate fit settings (from fit service) here? diff --git a/service/targetResists.py b/service/targetResists.py index 2640551f4..06df8c8c6 100644 --- a/service/targetResists.py +++ b/service/targetResists.py @@ -1,4 +1,4 @@ -#=============================================================================== +# ============================================================================= # Copyright (C) 2014 Ryan Holmes # # This file is part of pyfa. @@ -15,17 +15,21 @@ # # You should have received a copy of the GNU General Public License # along with pyfa. If not, see . -#=============================================================================== +# ============================================================================= -import eos.db -import eos.types import copy +from eos import db +from eos.saveddata.targetResists import TargetResists as es_TargetResists + + class ImportError(Exception): pass -class TargetResists(): + +class TargetResists(object): instance = None + @classmethod def getInstance(cls): if cls.instance is None: @@ -33,32 +37,39 @@ class TargetResists(): return cls.instance - def getTargetResistsList(self): - return eos.db.getTargetResistsList() + @staticmethod + def getTargetResistsList(): + return db.getTargetResistsList() - def getTargetResists(self, name): - return eos.db.getTargetResists(name) + @staticmethod + def getTargetResists(name): + return db.getTargetResists(name) - def newPattern(self, name): - p = eos.types.TargetResists(0.0, 0.0, 0.0, 0.0) + @staticmethod + def newPattern(name): + p = es_TargetResists(0.0, 0.0, 0.0, 0.0) p.name = name - eos.db.save(p) + db.save(p) return p - def renamePattern(self, p, newName): + @staticmethod + def renamePattern(p, newName): p.name = newName - eos.db.save(p) + db.save(p) - def deletePattern(self, p): - eos.db.remove(p) + @staticmethod + def deletePattern(p): + db.remove(p) - def copyPattern(self, p): + @staticmethod + def copyPattern(p): newP = copy.deepcopy(p) - eos.db.save(newP) + db.save(newP) return newP - def saveChanges(self, p): - eos.db.save(p) + @staticmethod + def saveChanges(p): + db.save(p) def importPatterns(self, text): lookup = {} @@ -66,23 +77,22 @@ class TargetResists(): for pattern in current: lookup[pattern.name] = pattern - imports, num = eos.types.TargetResists.importPatterns(text) + imports, num = es_TargetResists.importPatterns(text) for pattern in imports: if pattern.name in lookup: match = lookup[pattern.name] match.__dict__.update(pattern.__dict__) else: - eos.db.save(pattern) - eos.db.commit() + db.save(pattern) + db.commit() lenImports = len(imports) if lenImports == 0: raise ImportError("No patterns found for import") if lenImports != num: - raise ImportError("%d patterns imported from clipboard; %d had errors"%(num, num-lenImports)) + raise ImportError("%d patterns imported from clipboard; %d had errors" % (num, num - lenImports)) def exportPatterns(self): patterns = self.getTargetResistsList() patterns.sort(key=lambda p: p.name) - return eos.types.TargetResists.exportPatterns(*patterns) - + return es_TargetResists.exportPatterns(*patterns) diff --git a/service/update.py b/service/update.py index 47a3e35b0..2cffd9cec 100644 --- a/service/update.py +++ b/service/update.py @@ -1,4 +1,4 @@ -#=============================================================================== +# ============================================================================= # Copyright (C) 2014 Ryan Holmes # # This file is part of pyfa. @@ -15,43 +15,55 @@ # # You should have received a copy of the GNU General Public License # along with pyfa. If not, see . -#=============================================================================== +# ============================================================================= import threading -import wx -import urllib2 import json -import config -import service -import dateutil.parser import calendar +# noinspection PyPackageRequirements +import wx +# noinspection PyPackageRequirements +import dateutil.parser + +import config +from service.network import Network +from service.settings import UpdateSettings +from logbook import Logger + +pyfalog = Logger(__name__) + + class CheckUpdateThread(threading.Thread): def __init__(self, callback): threading.Thread.__init__(self) + self.name = "CheckUpdate" self.callback = callback - self.settings = service.settings.UpdateSettings.getInstance() - self.network = service.Network.getInstance() + self.settings = UpdateSettings.getInstance() + self.network = Network.getInstance() def run(self): - network = service.Network.getInstance() + network = Network.getInstance() try: response = network.request('https://api.github.com/repos/pyfa-org/Pyfa/releases', network.UPDATE) jsonResponse = json.loads(response.read()) - jsonResponse.sort(key=lambda x: calendar.timegm(dateutil.parser.parse(x['published_at']).utctimetuple()), reverse=True) + jsonResponse.sort( + key=lambda x: calendar.timegm(dateutil.parser.parse(x['published_at']).utctimetuple()), + reverse=True + ) for release in jsonResponse: # Suppress pre releases - if (release['prerelease'] and self.settings.get('prerelease')): + if release['prerelease'] and self.settings.get('prerelease'): continue # Handle use-case of updating to suppressed version - if self.settings.get('version') == 'v'+config.version: + if self.settings.get('version') == 'v' + config.version: self.settings.set('version', None) # Suppress version - if (release['tag_name'] == self.settings.get('version')): + if release['tag_name'] == self.settings.get('version'): break # Set the release version that we will be comparing with. @@ -60,36 +72,38 @@ class CheckUpdateThread(threading.Thread): else: rVersion = release['tag_name'].replace('v', '', 1) - if config.tag is 'git' and not release['prerelease'] and self.versiontuple(rVersion) >= self.versiontuple(config.version): - wx.CallAfter(self.callback, release) # git (dev/Singularity) -> Stable + if config.tag is 'git' and \ + not release['prerelease'] and \ + self.versiontuple(rVersion) >= self.versiontuple(config.version): + wx.CallAfter(self.callback, release) # git (dev/Singularity) -> Stable elif config.expansionName is not "Singularity": if release['prerelease']: - wx.CallAfter(self.callback, release) # Stable -> Singularity + wx.CallAfter(self.callback, release) # Stable -> Singularity elif self.versiontuple(rVersion) > self.versiontuple(config.version): - wx.CallAfter(self.callback, release) # Stable -> Stable + wx.CallAfter(self.callback, release) # Stable -> Stable else: if release['prerelease'] and rVersion > config.expansionVersion: - wx.CallAfter(self.callback, release) # Singularity -> Singularity + wx.CallAfter(self.callback, release) # Singularity -> Singularity break except: + pyfalog.warning("Caught exception in run") pass - def versiontuple(self, v): + @staticmethod + def versiontuple(v): return tuple(map(int, (v.split(".")))) -class Update(): - instance = None - def __init__(self): - pass - def CheckUpdate(self, callback): +class Update(object): + instance = None + + @staticmethod + def CheckUpdate(callback): thread = CheckUpdateThread(callback) thread.start() @classmethod def getInstance(cls): - if cls.instance == None: + if cls.instance is None: cls.instance = Update() return cls.instance - - diff --git a/setup.py b/setup.py index b4738824a..d35f69e87 100644 --- a/setup.py +++ b/setup.py @@ -9,7 +9,7 @@ import requests.certs # The modules that contain the bulk of teh source packages = ['eos', 'gui', 'service', 'utils'] # Extra files that will be copied into the root directory -include_files = ['eve.db', 'LICENSE', 'README.md', (requests.certs.where(),'cacert.pem')] +include_files = ['eve.db', 'LICENSE', 'README.md', (requests.certs.where(), 'cacert.pem')] # this is read by dist.py to package the icons icon_dirs = ['gui', 'icons', 'renders'] @@ -22,10 +22,10 @@ excludes = ['Tkinter', 'collections.abc', 'IPython', 'PyQt4', 'PIL', 'nose', 'to if __name__ == "__main__": import sys + # noinspection PyPackageRequirements,PyUnresolvedReferences from cx_Freeze import setup, Executable import config - app_name = 'pyfa' app_version = '{}'.format(config.version) app_description = 'Python fitting assistant' @@ -47,7 +47,6 @@ if __name__ == "__main__": 'initial_target_dir': r'[ProgramFilesFolder]\{}'.format(app_name), } - # Mac-specific options (untested) build_options_macapp = { 'iconfile': 'dist_assets/mac/pyfa.icns', @@ -59,13 +58,12 @@ if __name__ == "__main__": 'applications-shortcut': True, } - # Generic executable options executable_options = { 'script': 'pyfa.py', # Following are windows-specific options, they are stored # on a per-executable basis - 'base': 'Win32GUI' if sys.platform=='win32' else None, + 'base': 'Win32GUI' if sys.platform == 'win32' else None, 'icon': 'dist_assets/win/pyfa.ico', 'shortcutDir': 'DesktopFolder', 'shortcutName': app_name, @@ -75,7 +73,7 @@ if __name__ == "__main__": name=app_name, version=app_version, description=app_description, - options = { + options={ 'build_exe': build_options_winexe, 'bdist_msi': build_options_winmsi, 'bdist_mac': build_options_macapp, diff --git a/tests/test_modules/eos/test_mathUtils.py b/tests/test_modules/eos/test_mathUtils.py new file mode 100644 index 000000000..c5cf2b0bb --- /dev/null +++ b/tests/test_modules/eos/test_mathUtils.py @@ -0,0 +1,12 @@ +from eos.mathUtils import floorFloat + + +def test_floorFloat(): + assert type(floorFloat(1)) is not float + assert type(floorFloat(1)) is int + assert type(floorFloat(1.1)) is not float + assert type(floorFloat(1.1)) is int + assert floorFloat(1.1) == 1 + assert floorFloat(1.9) == 1 + assert floorFloat(1.5) == 1 + assert floorFloat(-1.5) == -2 diff --git a/tests/test_modules/gui/test_aboutData.py b/tests/test_modules/gui/test_aboutData.py new file mode 100644 index 000000000..8e7e862d6 --- /dev/null +++ b/tests/test_modules/gui/test_aboutData.py @@ -0,0 +1,9 @@ +from gui.aboutData import versionString, licenses, developers, credits, description + + +def test_aboutData(): + assert versionString.__len__() > 0 + assert licenses.__len__() > 0 + assert developers.__len__() > 0 + assert credits.__len__() > 0 + assert description.__len__() > 0 diff --git a/tests/test_modules/service/test_attribute.py b/tests/test_modules/service/test_attribute.py new file mode 100644 index 000000000..1ab44f2a8 --- /dev/null +++ b/tests/test_modules/service/test_attribute.py @@ -0,0 +1,42 @@ +from service.attribute import Attribute + + +def test_attribute(): + """ + We don't really have much to test here, to throw a generic attribute at it and validate we get the expected results + + :return: + """ + sAttr = Attribute.getInstance() + info = sAttr.getAttributeInfo("maxRange") + + assert info.attributeID == 54 + assert type(info.attributeID) is int + assert info.attributeName == 'maxRange' + assert type(info.attributeName) is unicode + assert info.defaultValue == 0.0 + assert type(info.defaultValue) is float + assert info.description == 'Distance below which range does not affect the to-hit equation.' + assert type(info.description) is unicode + assert info.displayName == 'Optimal Range' + assert type(info.displayName) is unicode + assert info.highIsGood is True + assert type(info.highIsGood) is bool + assert info.iconID == 1391 + assert type(info.iconID) is int + assert info.name == 'maxRange' + assert type(info.name) is unicode + assert info.published is True + assert type(info.published) is bool + assert info.unitID == 1 + assert type(info.unitID) is int + assert info.unit.ID == 1 + assert type(info.unit.ID) is int + assert info.unit.displayName == 'm' + assert type(info.unit.displayName) is unicode + assert info.unit.name == 'Length' + assert type(info.unit.name) is unicode + assert info.unit.unitID == 1 + assert type(info.unit.unitID) is int + assert info.unit.unitName == 'Length' + assert type(info.unit.unitName) is unicode diff --git a/tests/test_package.py b/tests/test_package.py new file mode 100644 index 000000000..5f97967d7 --- /dev/null +++ b/tests/test_package.py @@ -0,0 +1,58 @@ +"""import tests.""" + +import os +import sys +# import importlib + +# noinspection PyPackageRequirements +# import pytest + + +script_dir = os.path.dirname(os.path.abspath(__file__)) +# Add root to python paths, this allows us to import submodules +sys.path.append(os.path.realpath(os.path.join(script_dir, '..'))) + +# noinspection PyPep8 +import service +# noinspection PyPep8 +import gui +# noinspection PyPep8 +import eos +# noinspection PyPep8 +import utils + + +def test_packages(): + assert service + assert gui + assert eos + assert utils + + +def service_modules(): + for root, folders, files in os.walk("service"): + for file_ in files: + if file_.endswith(".py") and not file_.startswith("_"): + mod_name = "{}.{}".format( + root.replace("/", "."), + file_.split(".py")[0], + ) + yield mod_name + + +def eos_modules(): + for root, folders, files in os.walk("eos"): + for file_ in files: + if file_.endswith(".py") and not file_.startswith("_"): + mod_name = "{}.{}".format( + root.replace("/", "."), + file_.split(".py")[0], + ) + yield mod_name + +# TODO: Disable walk through Eos paths until eos.types is killed. eos.types causes the import to break +''' +@pytest.mark.parametrize("mod_name", eos_modules()) +def test_eos_imports(mod_name): + assert importlib.import_module(mod_name) +''' diff --git a/tox.ini b/tox.ini new file mode 100644 index 000000000..b92d0b45b --- /dev/null +++ b/tox.ini @@ -0,0 +1,16 @@ +[tox] +envlist = pep8 +skipsdist = True + +[testenv] +passenv = CI TRAVIS TRAVIS_* +deps = + -rrequirements.txt + -rrequirements_test.txt +basepython = python2.7 +commands = py.test -vv --cov Pyfa tests/ + +[testenv:pep8] +deps = flake8 +# TODO: Remove E731 and convert lambdas to defs +commands = flake8 --exclude=.svn,CVS,.bzr,.hg,.git,__pycache__,venv,tests,.tox,build,dist,__init__.py --ignore=E126,E127,E128,E203,E731 service gui eos utils config.py pyfa.py --max-line-length=165 diff --git a/utils/compat.py b/utils/compat.py index d8177973b..8dcf8a4d9 100644 --- a/utils/compat.py +++ b/utils/compat.py @@ -9,11 +9,11 @@ except ImportError: try: from _abcoll import KeysView, ValuesView, ItemsView except ImportError: - pass + KeysView = ValuesView = ItemsView = None class OrderedDict(dict): - 'Dictionary that remembers insertion order' + """Dictionary that remembers insertion order""" # An inherited dict maps keys to values. # The inherited dict provides __getitem__, __len__, __contains__, and get. # The remaining methods are order-aware. @@ -25,11 +25,12 @@ class OrderedDict(dict): # Each link is stored as a list of length three: [PREV, NEXT, KEY]. def __init__(self, *args, **kwds): - '''Initialize an ordered dictionary. Signature is the same as for + """Initialize an ordered dictionary. Signature is the same as for regular dictionaries, but keyword arguments are not recommended because their insertion order is arbitrary. - ''' + """ + super(OrderedDict, self).__init__(**kwds) if len(args) > 1: raise TypeError('expected at most 1 arguments, got %d' % len(args)) try: @@ -41,7 +42,7 @@ class OrderedDict(dict): self.__update(*args, **kwds) def __setitem__(self, key, value, dict_setitem=dict.__setitem__): - 'od.__setitem__(i, y) <==> od[i]=y' + """od.__setitem__(i, y) <==> od[i]=y""" # Setting a new item creates a new link which goes at the end of the linked # list, and the inherited dictionary is updated with the new key/value pair. if key not in self: @@ -51,7 +52,7 @@ class OrderedDict(dict): dict_setitem(self, key, value) def __delitem__(self, key, dict_delitem=dict.__delitem__): - 'od.__delitem__(y) <==> del od[y]' + """od.__delitem__(y) <==> del od[y]""" # Deleting an existing item uses self.__map to find the link which is # then removed by updating the links in the predecessor and successor nodes. dict_delitem(self, key) @@ -60,7 +61,7 @@ class OrderedDict(dict): link_next[0] = link_prev def __iter__(self): - 'od.__iter__() <==> iter(od)' + """od.__iter__() <==> iter(od)""" root = self.__root curr = root[1] while curr is not root: @@ -68,7 +69,7 @@ class OrderedDict(dict): curr = curr[1] def __reversed__(self): - 'od.__reversed__() <==> reversed(od)' + """od.__reversed__() <==> reversed(od)""" root = self.__root curr = root[0] while curr is not root: @@ -76,7 +77,7 @@ class OrderedDict(dict): curr = curr[0] def clear(self): - 'od.clear() -> None. Remove all items from od.' + """od.clear() -> None. Remove all items from od.""" try: for node in self.__map.itervalues(): del node[:] @@ -88,10 +89,10 @@ class OrderedDict(dict): dict.clear(self) def popitem(self, last=True): - '''od.popitem() -> (k, v), return and remove a (key, value) pair. + """od.popitem() -> (k, v), return and remove a (key, value) pair. Pairs are returned in LIFO order if last is true or FIFO order if false. - ''' + """ if not self: raise KeyError('dictionary is empty') root = self.__root @@ -113,40 +114,40 @@ class OrderedDict(dict): # -- the following methods do not depend on the internal structure -- def keys(self): - 'od.keys() -> list of keys in od' + """od.keys() -> list of keys in od""" return list(self) def values(self): - 'od.values() -> list of values in od' + """od.values() -> list of values in od""" return [self[key] for key in self] def items(self): - 'od.items() -> list of (key, value) pairs in od' + """od.items() -> list of (key, value) pairs in od""" return [(key, self[key]) for key in self] def iterkeys(self): - 'od.iterkeys() -> an iterator over the keys in od' + """od.iterkeys() -> an iterator over the keys in od""" return iter(self) def itervalues(self): - 'od.itervalues -> an iterator over the values in od' + """od.itervalues -> an iterator over the values in od""" for k in self: yield self[k] def iteritems(self): - 'od.iteritems -> an iterator over the (key, value) items in od' + """od.iteritems -> an iterator over the (key, value) items in od""" for k in self: yield (k, self[k]) def update(*args, **kwds): - '''od.update(E, **F) -> None. Update od from dict/iterable E and F. + """od.update(E, **F) -> None. Update od from dict/iterable E and F. If E is a dict instance, does: for k in E: od[k] = E[k] If E has a .keys() method, does: for k in E.keys(): od[k] = E[k] Or if E is an iterable of items, does: for k, v in E: od[k] = v In either case, this is followed by: for k, v in F.items(): od[k] = v - ''' + """ if len(args) > 2: raise TypeError('update() takes at most 2 positional ' 'arguments (%d given)' % (len(args),)) @@ -174,10 +175,10 @@ class OrderedDict(dict): __marker = object() def pop(self, key, default=__marker): - '''od.pop(k[,d]) -> v, remove specified key and return the corresponding value. + """od.pop(k[,d]) -> v, remove specified key and return the corresponding value. If key is not found, d is returned if given, otherwise KeyError is raised. - ''' + """ if key in self: result = self[key] del self[key] @@ -187,14 +188,16 @@ class OrderedDict(dict): return default def setdefault(self, key, default=None): - 'od.setdefault(k[,d]) -> od.get(k,d), also set od[k]=d if k not in od' + """od.setdefault(k[,d]) -> od.get(k,d), also set od[k]=d if k not in od""" if key in self: return self[key] self[key] = default return default - def __repr__(self, _repr_running={}): - 'od.__repr__() <==> repr(od)' + def __repr__(self, _repr_running=None): + """od.__repr__() <==> repr(od)""" + if _repr_running is None: + _repr_running = {} call_key = id(self), _get_ident() if call_key in _repr_running: return '...' @@ -207,37 +210,37 @@ class OrderedDict(dict): del _repr_running[call_key] def __reduce__(self): - 'Return state information for pickling' + """Return state information for pickling""" items = [[k, self[k]] for k in self] inst_dict = vars(self).copy() for k in vars(OrderedDict()): inst_dict.pop(k, None) if inst_dict: - return (self.__class__, (items,), inst_dict) + return self.__class__, (items,), inst_dict return self.__class__, (items,) def copy(self): - 'od.copy() -> a shallow copy of od' + """od.copy() -> a shallow copy of od""" return self.__class__(self) @classmethod def fromkeys(cls, iterable, value=None): - '''OD.fromkeys(S[, v]) -> New ordered dictionary with keys from S + """OD.fromkeys(S[, v]) -> New ordered dictionary with keys from S and values equal to v (which defaults to None). - ''' + """ d = cls() for key in iterable: d[key] = value return d def __eq__(self, other): - '''od.__eq__(y) <==> od==y. Comparison to another OD is order-sensitive + """od.__eq__(y) <==> od==y. Comparison to another OD is order-sensitive while comparison to a regular mapping is order-insensitive. - ''' + """ if isinstance(other, OrderedDict): - return len(self)==len(other) and self.items() == other.items() + return len(self) == len(other) and self.items() == other.items() return dict.__eq__(self, other) def __ne__(self, other): @@ -246,13 +249,22 @@ class OrderedDict(dict): # -- the following methods are only used in Python 2.7 -- def viewkeys(self): - "od.viewkeys() -> a set-like object providing a view on od's keys" - return KeysView(self) + """od.viewkeys() -> a set-like object providing a view on od's keys""" + if KeysView: + return KeysView(self) + else: + return None def viewvalues(self): - "od.viewvalues() -> an object providing a view on od's values" - return ValuesView(self) + """od.viewvalues() -> an object providing a view on od's values""" + if ValuesView: + return ValuesView(self) + else: + return None def viewitems(self): - "od.viewitems() -> a set-like object providing a view on od's items" - return ItemsView(self) + """od.viewitems() -> a set-like object providing a view on od's items""" + if ItemsView: + return ItemsView(self) + else: + return None diff --git a/utils/timer.py b/utils/timer.py index c1ca87a8b..8139f9101 100644 --- a/utils/timer.py +++ b/utils/timer.py @@ -1,6 +1,7 @@ import time -class Timer(): + +class Timer(object): def __init__(self, name='', logger=None): self.name = name self.start = time.time() @@ -9,24 +10,24 @@ class Timer(): @property def elapsed(self): - return (time.time() - self.start)*1000 + return (time.time() - self.start) * 1000 @property def last(self): - return (time.time() - self.__last)*1000 + return (time.time() - self.__last) * 1000 def checkpoint(self, name=''): text = u'Timer - {timer} - {checkpoint} - {last:.2f}ms ({elapsed:.2f}ms elapsed)'.format( - timer=self.name, - checkpoint=unicode(name, "utf-8"), - last=self.last, - elapsed=self.elapsed + timer=self.name, + checkpoint=unicode(name, "utf-8"), + last=self.last, + elapsed=self.elapsed ).strip() self.__last = time.time() if self.logger: self.logger.debug(text) else: - print text + print(text) def __enter__(self): return self