diff --git a/effs_stat_export.py b/effs_stat_export.py new file mode 100755 index 000000000..604cc250e --- /dev/null +++ b/effs_stat_export.py @@ -0,0 +1,474 @@ +import inspect +import os +import platform +import re +import sys +import traceback +from optparse import AmbiguousOptionError, BadOptionError, OptionParser + +from logbook import CRITICAL, DEBUG, ERROR, FingersCrossedHandler, INFO, Logger, NestedSetup, NullHandler, StreamHandler, TimedRotatingFileHandler, WARNING, \ + __version__ as logbook_version + +import config + +try: + import wxversion +except ImportError: + wxversion = None + +try: + import sqlalchemy +except ImportError: + sqlalchemy = None + +pyfalog = Logger(__name__) + +class PassThroughOptionParser(OptionParser): + + def _process_args(self, largs, rargs, values): + while rargs: + try: + OptionParser._process_args(self, largs, rargs, values) + except (BadOptionError, AmbiguousOptionError) as e: + pyfalog.error("Bad startup option passed.") + largs.append(e.opt_str) + +usage = "usage: %prog [--root]" +parser = PassThroughOptionParser(usage=usage) +parser.add_option("-r", "--root", action="store_true", dest="rootsavedata", help="if you want pyfa to store its data in root folder, use this option", default=False) +parser.add_option("-w", "--wx28", action="store_true", dest="force28", help="Force usage of wxPython 2.8", default=False) +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.rootsavedata is True: + config.saveInRoot = True + +# set title if it wasn't supplied by argument +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) + +try: + # noinspection PyPackageRequirements + import wx +except: + exit_message = "Cannot import wxPython. You can download wxPython (2.8+) from http://www.wxpython.org/" + raise PreCheckException(exit_message) + +try: + import requests + config.requestsVersion = requests.__version__ +except ImportError: + raise PreCheckException("Cannot import requests. You can download requests from https://pypi.python.org/pypi/requests.") + +import eos.db + +#if config.saVersion[0] > 0 or config.saVersion[1] >= 7: + # <0.7 doesn't have support for events ;_; (mac-deprecated) +config.sa_events = True +import eos.events + + # 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() + + +armorLinkShip = eos.db.searchFits('armor links')[0] +infoLinkShip = eos.db.searchFits('information links')[0] +shieldLinkShip = eos.db.searchFits('shield links')[0] +skirmishLinkShip = eos.db.searchFits('skirmish links')[0] +import json + +def processExportedHtml(fileLocation): + output = open('./jsonShipStatExport.js', 'w') + output.write('let shipJSON = JSON.stringify([') + outputBaseline = open('./jsonShipBaseStatExport.js', 'w') + outputBaseline.write('let shipBaseJSON = JSON.stringify([') + shipCata = eos.db.getItemsByCategory('Ship') + #shipCata = eos.db.getItem(638) + #shipCata = eos.db.getMetaGroup(638) + #shipCata = eos.db.getAttributeInfo(638) + #shipCata = eos.db.getItemsByCategory('Traits') + #shipCata = eos.db.getGroup('invtraits') + #shipCata = eos.db.getCategory('Traits') + from sqlalchemy import Column, String, Integer, ForeignKey, Boolean, Table + from sqlalchemy.orm import relation, mapper, synonym, deferred + from eos.db import gamedata_session + from eos.db import gamedata_meta + from eos.db.gamedata.metaGroup import metatypes_table, items_table + from eos.db.gamedata.group import groups_table + + from eos.gamedata import AlphaClone, Attribute, Category, Group, Item, MarketGroup, \ + MetaGroup, AttributeInfo, MetaData, Effect, ItemEffect, Traits + from eos.db.gamedata.traits import traits_table + #shipCata = traits_table #categories_table + #print shipCata + #print shipCata.columns + #print shipCata.categoryName + #print vars(shipCata) + data = category = gamedata_session.query(Category).all() + #print data + #print iter(data) + eff = gamedata_session.query(Category).get(53) #Bonus (id14) #Effects (id 53) + data = eff; + #print eff + #print vars(eff) + things = []#[Category, MetaGroup, AttributeInfo, MetaData, Item, Attribute, Effect, ItemEffect, Traits]#, Attribute] + if False: + for dataTab in things : + print 'Data for: ' + str(dataTab) + try: + filter = dataTab.typeID == 638 + except: + filter = dataTab.ID == 638 + data = gamedata_session.query(dataTab).options().filter(filter).all() + print data + try: + varDict = vars(data) + print varDict + except: + print 'Not a Dict' + try: + varDict = data.__doc__ + print varDict + except: + print 'No items()' + try: + for varDict in data: + print varDict + print vars(varDict) + except: + print 'Not a list of dicts' + + #print vars(shipCata._sa_instance_state) + baseLimit = 500 + baseN = 0 + for ship in iter(shipCata): + if baseN < baseLimit: + #print ship + #print ship.ID + #print ship.categoryName + #print vars(ship) + dna = str(ship.ID) + stats = setFitFromString(dna, ship.name, ship.groupID) + outputBaseline.write(stats) + outputBaseline.write(',\n') + baseN += 1; + limit = 500 + skipTill = 0 + n = 0 + try: + with open('pyfaFits.html'): + fileLocation = 'pyfaFits.html' + except: + try: + with open('.pyfa/pyfaFits.html'): + fileLocation = '.pyfa/pyfaFits.html' + except: + try: + with open('../.pyfa/pyfaFits.html'): + fileLocation = '../.pyfa/pyfaFits.html' + except: + try: + with open('../../.pyfa/pyfaFits.html'): + fileLocation = '../../.pyfa/pyfaFits.html' + except: + fileLocation = None; + if fileLocation != None: + with open(fileLocation) as f: + for fullLine in f: + if limit == None or n < limit: + n += 1 + startInd = fullLine.find('/dna/') + 5 + line = fullLine[startInd:len(fullLine)] + endInd = line.find('::') + dna = line[0:endInd] + name = line[line.find('>') + 1:line.find('<')] + if n >= skipTill: + print 'name: ' + name + ' DNA: ' + dna + stats = setFitFromString(dna, name, 0) + output.write(stats) + output.write(',\n') + output.write(']);\nexport {shipJSON};') + output.close() + outputBaseline.write(']);\nexport {shipBaseJSON};') + outputBaseline.close() +def parseNeededFitDetails(fit, groupID): + singleRunPrintPreformed = False + weaponSystems = [] + groups = {} + #help(fit.modules) + #help(fit.modules[0]) + for mod in fit.modules: + if mod.dps > 0: + keystr = str(mod.itemID) + '-' + str(mod.chargeID) + if keystr in groups: + groups[keystr][1] += 1 + else: + groups[keystr] = [mod, 1] + for wepGroup in groups: + stats = groups[wepGroup][0] + c = groups[wepGroup][1] + tracking = 0 + maxVelocity = 0 + explosionDelay = 0 + damageReductionFactor = 0 + explosionRadius = 0 + explosionVelocity = 0 + aoeFieldRange = 0 + if stats.hardpoint == 2: + tracking = stats.itemModifiedAttributes['trackingSpeed'] + typeing = 'Turret' + name = stats.item.name + ', ' + stats.charge.name + elif stats.hardpoint == 1: + maxVelocity = stats.chargeModifiedAttributes['maxVelocity'] + explosionDelay = stats.chargeModifiedAttributes['explosionDelay'] + damageReductionFactor = stats.chargeModifiedAttributes['aoeDamageReductionFactor'] + explosionRadius = stats.chargeModifiedAttributes['aoeCloudSize'] + explosionVelocity = stats.chargeModifiedAttributes['aoeVelocity'] + typeing = 'Missile' + name = stats.item.name + ', ' + stats.charge.name + elif stats.hardpoint == 0: + aoeFieldRange = stats.itemModifiedAttributes['empFieldRange'] + typeing = 'SmartBomb' + name = stats.item.name + statDict = {'dps': stats.dps * c, 'capUse': stats.capUse * c, 'falloff': stats.falloff,\ + 'type': typeing, 'name': name, 'optimal': stats.maxRange,\ + 'numCharges': stats.numCharges, 'numShots': stats.numShots, 'reloadTime': stats.reloadTime,\ + 'cycleTime': stats.cycleTime, 'volley': stats.volley * c, 'tracking': tracking,\ + 'maxVelocity': maxVelocity, 'explosionDelay': explosionDelay, 'damageReductionFactor': damageReductionFactor,\ + 'explosionRadius': explosionRadius, 'explosionVelocity': explosionVelocity, 'aoeFieldRange': aoeFieldRange\ + } + weaponSystems.append(statDict) + #if fit.droneDPS > 0: + for drone in fit.drones: + if drone.dps[0] > 0 and drone.amountActive > 0: + newTracking = drone.itemModifiedAttributes['trackingSpeed'] / (drone.itemModifiedAttributes['optimalSigRadius'] / 40000) + statDict = {'dps': drone.dps[0], 'cycleTime': drone.cycleTime, 'type': 'Drone',\ + 'optimal': drone.maxRange, 'name': drone.item.name, 'falloff': drone.falloff,\ + 'maxSpeed': drone.itemModifiedAttributes['maxVelocity'], 'tracking': newTracking,\ + 'volley': drone.dps[1]\ + } + weaponSystems.append(statDict) + for fighter in fit.fighters: + if fighter.dps[0] > 0 and fighter.amountActive > 0: + abilities = [] + #for ability in fighter.abilities: + if 'fighterAbilityAttackMissileDamageEM' in fighter.itemModifiedAttributes: + baseRef = 'fighterAbilityAttackMissile' + baseRefDam = baseRef + 'Damage' + abBaseDamage = fighter.itemModifiedAttributes[baseRefDam + 'EM'] + fighter.itemModifiedAttributes[baseRefDam + 'Therm'] + fighter.itemModifiedAttributes[baseRefDam + 'Exp'] + fighter.itemModifiedAttributes[baseRefDam + 'Kin'] + abDamage = abBaseDamage * fighter.itemModifiedAttributes[baseRefDam + 'Multiplier'] + ability = {'name': 'RegularAttack', 'volley': abDamage * fighter.amountActive, 'explosionRadius': fighter.itemModifiedAttributes[baseRef + 'ExplosionRadius'],\ + 'explosionVelocity': fighter.itemModifiedAttributes[baseRef + 'ExplosionVelocity'], 'optimal': fighter.itemModifiedAttributes[baseRef + 'RangeOptimal'],\ + 'damageReductionFactor': fighter.itemModifiedAttributes[baseRef + 'ReductionFactor'], 'rof': fighter.itemModifiedAttributes[baseRef + 'Duration'],\ + } + abilities.append(ability) + if 'fighterAbilityMissilesDamageEM' in fighter.itemModifiedAttributes: + baseRef = 'fighterAbilityMissiles' + baseRefDam = baseRef + 'Damage' + abBaseDamage = fighter.itemModifiedAttributes[baseRefDam + 'EM'] + fighter.itemModifiedAttributes[baseRefDam + 'Therm'] + fighter.itemModifiedAttributes[baseRefDam + 'Exp'] + fighter.itemModifiedAttributes[baseRefDam + 'Kin'] + abDamage = abBaseDamage * fighter.itemModifiedAttributes[baseRefDam + 'Multiplier'] + ability = {'name': 'MissileAttack', 'volley': abDamage * fighter.amountActive, 'explosionRadius': fighter.itemModifiedAttributes[baseRef + 'ExplosionRadius'],\ + 'explosionVelocity': fighter.itemModifiedAttributes[baseRef + 'ExplosionVelocity'], 'optimal': fighter.itemModifiedAttributes[baseRef + 'Range'],\ + 'damageReductionFactor': fighter.itemModifiedAttributes[baseRefDam + 'ReductionFactor'], 'rof': fighter.itemModifiedAttributes[baseRef + 'Duration'],\ + } + abilities.append(ability) + statDict = {'dps': fighter.dps[0], 'type': 'Fighter', 'name': fighter.item.name,\ + 'maxSpeed': fighter.itemModifiedAttributes['maxVelocity'], 'abilities': abilities, 'ehp': fighter.itemModifiedAttributes['shieldCapacity'] / 0.8875 * fighter.amountActive,\ + 'volley': fighter.dps[1], 'signatureRadius': fighter.itemModifiedAttributes['signatureRadius']\ + } + weaponSystems.append(statDict) + turretSlots = fit.ship.itemModifiedAttributes['turretSlotsLeft'] + launcherSlots = fit.ship.itemModifiedAttributes['launcherSlotsLeft'] + droneBandwidth = fit.ship.itemModifiedAttributes['droneBandwidth'] + if turretSlots == None: + turretSlots = 0 + if launcherSlots == None: + launcherSlots = 0 + if droneBandwidth == None: + droneBandwidth = 0 + effectiveTurretSlots = turretSlots + effectiveLauncherSlots = launcherSlots + effectiveDroneBandwidth = droneBandwidth + from eos.db import gamedata_session + from eos.gamedata import Traits + filter = Traits.typeID == fit.shipID + data = gamedata_session.query(Traits).options().filter(filter).all() + roleBonusMode = False + if len(data) != 0: + print data[0].traitText + for bonusText in data[0].traitText.splitlines(): + bonusText = bonusText.lower() + #print 'bonus text line: ' + bonusText + if 'per skill level' in bonusText: + roleBonusMode = False + if 'role bonus' in bonusText: + roleBonusMode = True + multi = 1 + if 'damage' in bonusText and not 'control' in bonusText: + splitText = bonusText.split('%') + if float(splitText[0]) > 0 == False: + pyfalog.error('damage bonus split did not parse correctly!') + if roleBonusMode: + addedMulti = float(splitText[0]) + else: + addedMulti = float(splitText[0]) * 5 + multi = 1 + (addedMulti / 100) + elif 'rate of fire' in bonusText: + splitText = bonusText.split('%') + if splitText[0] > 0 == False: + pyfalog.error('rate of fire bonus split did not parse correctly!') + if roleBonusMode: + rofMulti = float(splitText[0]) + else: + rofMulti = float(splitText[0]) * 5 + multi = 1 / (1 - (rofMulti / 100)) + if multi > 1: + if 'drone' in bonusText.lower(): + effectiveDroneBandwidth *= multi + elif 'turret' in bonusText.lower(): + effectiveTurretSlots *= multi + elif 'missile' in bonusText.lower(): + effectiveLauncherSlots *= multi + effectiveTurretSlots = round(effectiveTurretSlots, 2); + effectiveLauncherSlots = round(effectiveLauncherSlots, 2); + effectiveDroneBandwidth = round(effectiveDroneBandwidth, 2); + hullResonance = {'exp': fit.ship.itemModifiedAttributes['explosiveDamageResonance'], 'kin': fit.ship.itemModifiedAttributes['kineticDamageResonance'], \ + 'therm': fit.ship.itemModifiedAttributes['thermalDamageResonance'], 'em': fit.ship.itemModifiedAttributes['emDamageResonance']} + armorResonance = {'exp': fit.ship.itemModifiedAttributes['armorExplosiveDamageResonance'], 'kin': fit.ship.itemModifiedAttributes['armorKineticDamageResonance'], \ + 'therm': fit.ship.itemModifiedAttributes['armorThermalDamageResonance'], 'em': fit.ship.itemModifiedAttributes['armorEmDamageResonance']} + shieldResonance = {'exp': fit.ship.itemModifiedAttributes['shieldExplosiveDamageResonance'], 'kin': fit.ship.itemModifiedAttributes['shieldKineticDamageResonance'], \ + 'therm': fit.ship.itemModifiedAttributes['shieldThermalDamageResonance'], 'em': fit.ship.itemModifiedAttributes['shieldEmDamageResonance']} + resonance = {'hull': hullResonance, 'armor': armorResonance, 'shield': shieldResonance} + shipSizes = ['Frigate', 'Destroyer', 'Cruiser', 'Battlecruiser', 'Battleship', 'Capital', 'Industrial', 'Misc'] + if groupID in [25, 31, 237, 324, 830, 831, 834, 893, 1283, 1527]: + shipSize = shipSizes[0] + elif groupID in [420, 541, 1305, 1534]: + shipSize = shipSizes[1] + elif groupID in [26, 358, 832, 833, 894, 906, 963]: + shipSize = shipSizes[2] + elif groupID in [419, 540, 1201]: + shipSize = shipSizes[3] + elif groupID in [27, 381, 898, 900]: + shipSize = shipSizes[4] + elif groupID in [30, 485, 513, 547, 659, 883, 902, 1538]: + shipSize = shipSizes[5] + elif groupID in [28, 380, 1202, 463, 543, 941]: + shipSize = shipSizes[6] + elif groupID in [29, 1022]: + shipSize = shipSizes[7] + else: + shipSize = 'ShipSize not found for ' + fit.name + ' groupID: ' + str(groupID) + print shipSize + try: + parsable = {'name': fit.name, 'ehp': fit.ehp, 'droneDPS': fit.droneDPS, \ + 'droneVolley': fit.droneVolley, 'hp': fit.hp, 'maxTargets': fit.maxTargets, \ + 'maxSpeed': fit.maxSpeed, 'weaponVolley': fit.weaponVolley, 'totalVolley': fit.totalVolley,\ + 'maxTargetRange': fit.maxTargetRange, 'scanStrength': fit.scanStrength,\ + 'weaponDPS': fit.weaponDPS, 'alignTime': fit.alignTime, 'signatureRadius': fit.ship.itemModifiedAttributes['signatureRadius'],\ + 'weapons': weaponSystems, 'scanRes': fit.ship.itemModifiedAttributes['scanResolution'],\ + 'projectedModules': fit.projectedModules, 'capUsed': fit.capUsed, 'capRecharge': fit.capRecharge,\ + 'rigSlots': fit.ship.itemModifiedAttributes['rigSlots'], 'lowSlots': fit.ship.itemModifiedAttributes['lowSlots'],\ + 'midSlots': fit.ship.itemModifiedAttributes['medSlots'], 'highSlots': fit.ship.itemModifiedAttributes['hiSlots'],\ + 'turretSlots': fit.ship.itemModifiedAttributes['turretSlotsLeft'], 'launcherSlots': fit.ship.itemModifiedAttributes['launcherSlotsLeft'],\ + 'powerOutput': fit.ship.itemModifiedAttributes['powerOutput'], 'rigSize': fit.ship.itemModifiedAttributes['rigSize'],\ + 'effectiveTurrets': effectiveTurretSlots, 'effectiveLaunchers': effectiveLauncherSlots, 'effectiveDroneBandwidth': effectiveDroneBandwidth,\ + 'resonance': resonance, 'typeID': fit.shipID, 'groupID': groupID, 'shipSize': shipSize\ + } + except TypeError: + print 'Error parsing fit:' + str(fit) + print TypeError + parsable = {'name': fit.name + 'Fit could not be correctly parsed'} + #print fit.ship.itemModifiedAttributes.items() + #help(fit) + #if len(fit.fighters) > 5: + #print fit.fighters + #help(fit.fighters[0]) + stringified = json.dumps(parsable, skipkeys=True) + return stringified +def setFitFromString(dnaString, fitName, groupID) : + modArray = dnaString.split(':') + fitL = Fit() + print modArray[0] + fitID = fitL.newFit(int(modArray[0]), fitName) + fit = eos.db.getFit(fitID) + ammoArray = [] + n = -1 + for mod in iter(modArray): + n = n + 1 + if n > 0: + #print n + #print mod + modSp = mod.split(';') + if len(modSp) == 2: + k = 0 + while k < int(modSp[1]): + k = k + 1 + itemID = int(modSp[0]) + item = eos.db.getItem(int(modSp[0]), eager=("attributes", "group.category")) + cat = item.category.name + if cat == 'Drone': + fitL.addDrone(fitID, itemID, int(modSp[1]), recalc=False) + k += int(modSp[1]) + if cat == 'Fighter': + fitL.addFighter(fitID, itemID, recalc=False) + #fit.fighters.last.abilities.active = True + k += 100 + if fitL.isAmmo(int(modSp[0])): + k += 100 + ammoArray.append(int(modSp[0])); + fitL.appendModule(fitID, int(modSp[0])) + fit = eos.db.getFit(fitID) + #nonEmptyModules = fit.modules + #while nonEmptyModules.find(None) >= 0: + # print 'ssssssssssssssss' + # nonEmptyModules.remove(None) + for ammo in iter(ammoArray): + fitL.setAmmo(fitID, ammo, fit.modules) + if len(fit.drones) > 0: + fit.drones[0].amountActive = fit.drones[0].amount + eos.db.commit() + for fighter in iter(fit.fighters): + for ability in fighter.abilities: + if ability.effect.handlerName == u'fighterabilityattackm' and ability.active == True: + for abilityAltRef in fighter.abilities: + if abilityAltRef.effect.isImplemented: + abilityAltRef.active = True + fitL.recalc(fit) + fit = eos.db.getFit(fitID) + #print fit.modules + #fit.calculateWeaponStats() + fitL.addCommandFit(fit.ID, armorLinkShip) + fitL.addCommandFit(fit.ID, shieldLinkShip) + fitL.addCommandFit(fit.ID, skirmishLinkShip) + fitL.addCommandFit(fit.ID, infoLinkShip) + #def anonfunc(unusedArg): True + jsonStr = parseNeededFitDetails(fit, groupID) + #print vars(fit.ship._Ship__item) + #help(fit) + Fit.deleteFit(fitID) + return jsonStr +launchUI = False +#launchUI = True +if launchUI == False: + from service.fit import Fit + #setFitFromString(dnaChim, 'moMachsD') + #help(eos.db.getItem) + #ship = es_Ship(eos.db.getItem(27)) + processExportedHtml('../.pyfa/pyfaFits.html')