Merge branch 'charImplants' into singularity

# Conflicts:
#	eos/db/saveddata/fit.py
#	gui/builtinContextMenus/itemStats.py
This commit is contained in:
blitzmann
2016-04-12 20:16:53 -04:00
66 changed files with 1598 additions and 871 deletions

View File

@@ -4,6 +4,9 @@ import time
import re
import os
import migrations
import logging
logger = logging.getLogger(__name__)
def getVersion(db):
cursor = db.execute('PRAGMA user_version')
@@ -30,10 +33,9 @@ def update(saveddata_engine):
shutil.copyfile(config.saveDB, toFile)
for version in xrange(dbVersion, appVersion):
func = migrations.updates[version+1]
if func:
print "applying update",version+1
logger.info("Applying database update: %d", version+1)
func(saveddata_engine)
# when all is said and done, set version to current

View File

@@ -0,0 +1,15 @@
"""
Migration 13
- Alters fits table to introduce implant location attribute
"""
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")

View File

@@ -13,6 +13,7 @@ __all__ = [
"miscData",
"targetResists",
"override",
"crest"
"crest",
"implantSet"
]

View File

@@ -36,9 +36,23 @@ characters_table = Table("characters", saveddata_meta,
Column("ownerID", ForeignKey("users.ID"), nullable = True))
mapper(Character, characters_table,
properties = {"_Character__owner" : relation(User, backref = "characters"),
"_Character__skills" : relation(Skill, backref="character", cascade = "all,delete-orphan"),
"_Character__implants" : relation(Implant, collection_class = HandledImplantBoosterList, cascade='all,delete-orphan', single_parent=True,
primaryjoin = charImplants_table.c.charID == characters_table.c.ID,
secondaryjoin = charImplants_table.c.implantID == Implant.ID,
secondary = charImplants_table),})
properties = {
"savedName": characters_table.c.name,
"_Character__owner": relation(
User,
backref = "characters"),
"_Character__skills": relation(
Skill,
backref="character",
cascade = "all,delete-orphan"),
"_Character__implants": relation(
Implant,
collection_class = HandledImplantBoosterList,
cascade='all,delete-orphan',
backref='character',
single_parent=True,
primaryjoin = charImplants_table.c.charID == characters_table.c.ID,
secondaryjoin = charImplants_table.c.implantID == Implant.ID,
secondary = charImplants_table),
}
)

View File

@@ -29,7 +29,7 @@ from eos.db.saveddata.drone import drones_table
from eos.db.saveddata.fighter import fighters_table
from eos.db.saveddata.cargo import cargo_table
from eos.db.saveddata.implant import fitImplants_table
from eos.types import Fit, Module, User, Booster, Drone, Fighter, Cargo, Implant, Character, DamagePattern, TargetResists
from eos.types import Fit, Module, User, Booster, Drone, Fighter, Cargo, Implant, Character, DamagePattern, TargetResists, ImplantLocation
from eos.effectHandlerHelpers import *
fits_table = Table("fits", saveddata_meta,
@@ -43,6 +43,7 @@ fits_table = Table("fits", saveddata_meta,
Column("booster", Boolean, nullable = False, index = True, default = 0),
Column("targetResistsID", ForeignKey("targetResists.ID"), nullable=True),
Column("modeID", Integer, nullable=True),
Column("implantLocation", Integer, nullable=False, default=ImplantLocation.FIT),
)
projectedFits_table = Table("projectedFits", saveddata_meta,

View File

@@ -36,4 +36,8 @@ charImplants_table = Table("charImplants", saveddata_meta,
Column("charID", ForeignKey("characters.ID"), index = True),
Column("implantID", ForeignKey("implants.ID"), primary_key = True))
implantsSetMap_table = Table("implantSetMap", saveddata_meta,
Column("setID", ForeignKey("implantSets.ID"), index = True),
Column("implantID", ForeignKey("implants.ID"), primary_key = True))
mapper(Implant, implants_table)

View File

@@ -0,0 +1,45 @@
#===============================================================================
# Copyright (C) 2016 Ryan Holmes
#
# 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 <http://www.gnu.org/licenses/>.
#===============================================================================
from sqlalchemy import Table, Column, Integer, ForeignKey, String
from sqlalchemy.orm import relation, mapper
from eos.db import saveddata_meta
from eos.db.saveddata.implant import implantsSetMap_table
from eos.types import Implant, ImplantSet
from eos.effectHandlerHelpers import HandledImplantBoosterList
implant_set_table = Table("implantSets", saveddata_meta,
Column("ID", Integer, primary_key = True),
Column("name", String, nullable = False),
)
mapper(ImplantSet, implant_set_table,
properties = {
"_ImplantSet__implants": relation(
Implant,
collection_class = HandledImplantBoosterList,
cascade='all, delete, delete-orphan',
backref='set',
single_parent=True,
primaryjoin = implantsSetMap_table.c.setID == implant_set_table.c.ID,
secondaryjoin = implantsSetMap_table.c.implantID == Implant.ID,
secondary = implantsSetMap_table),
}
)

View File

@@ -20,7 +20,7 @@
from eos.db.util import processEager, processWhere
from eos.db import saveddata_session, sd_lock
from eos.types import User, Character, Fit, Price, DamagePattern, Fleet, MiscData, Wing, Squad, TargetResists, Override, CrestChar
from eos.types import *
from eos.db.saveddata.fleet import squadmembers_table
from eos.db.saveddata.fit import projectedFits_table
from sqlalchemy.sql import and_
@@ -154,7 +154,7 @@ def getCharacter(lookfor, eager=None):
elif isinstance(lookfor, basestring):
eager = processEager(eager)
with sd_lock:
character = saveddata_session.query(Character).options(*eager).filter(Character.name == 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
@@ -349,6 +349,12 @@ def getTargetResistsList(eager=None):
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):
@@ -385,6 +391,24 @@ def getTargetResists(lookfor, eager=None):
raise TypeError("Need integer or string as argument")
return pattern
@cachedQuery(ImplantSet, 1, "lookfor")
def getImplantSet(lookfor, eager=None):
if isinstance(lookfor, int):
if eager is None:
with sd_lock:
pattern = saveddata_session.query(ImplantSet).get(lookfor)
else:
eager = processEager(eager)
with sd_lock:
pattern = saveddata_session.query(ImplantSet).options(*eager).filter(TargetResists.ID == lookfor).first()
elif isinstance(lookfor, basestring):
eager = processEager(eager)
with sd_lock:
pattern = saveddata_session.query(ImplantSet).options(*eager).filter(TargetResists.name == lookfor).first()
else:
raise TypeError("Improper argument")
return pattern
def searchFits(nameLike, where=None, eager=None):
if not isinstance(nameLike, basestring):
raise TypeError("Need string as argument")

View File

@@ -5,7 +5,7 @@
runTime = "early"
type = "passive"
def handler(fit, implant, context):
fit.implants.filteredItemMultiply(
fit.appliedImplants.filteredItemMultiply(
lambda implant: "signatureRadiusBonus" in implant.itemModifiedAttributes and "implantSetAngel" in implant.itemModifiedAttributes,
"signatureRadiusBonus",
implant.getModifiedItemAttr("implantSetAngel"))

View File

@@ -5,5 +5,5 @@
runTime = "early"
type = "passive"
def handler(fit, implant, context):
fit.implants.filteredItemMultiply(lambda target: target.item.requiresSkill("Cybernetics"),
fit.appliedImplants.filteredItemMultiply(lambda target: target.item.requiresSkill("Cybernetics"),
"scanGravimetricStrengthPercent", implant.getModifiedItemAttr("implantSetCaldariNavy"))

View File

@@ -5,5 +5,5 @@
runTime = "early"
type = "passive"
def handler(fit, implant, context):
fit.implants.filteredItemMultiply(lambda target: target.item.requiresSkill("Cybernetics"),
fit.appliedImplants.filteredItemMultiply(lambda target: target.item.requiresSkill("Cybernetics"),
"scanGravimetricStrengthModifier", implant.getModifiedItemAttr("implantSetLGCaldariNavy"))

View File

@@ -5,5 +5,5 @@
type = "passive"
runTime = "early"
def handler(fit, implant, context):
fit.implants.filteredItemMultiply(lambda target: target.item.requiresSkill("Cybernetics"),
fit.appliedImplants.filteredItemMultiply(lambda target: target.item.requiresSkill("Cybernetics"),
"scanMagnetometricStrengthPercent", implant.getModifiedItemAttr("implantSetFederationNavy"))

View File

@@ -5,5 +5,5 @@
type = "passive"
runTime = "early"
def handler(fit, implant, context):
fit.implants.filteredItemMultiply(lambda target: target.item.requiresSkill("Cybernetics"),
fit.appliedImplants.filteredItemMultiply(lambda target: target.item.requiresSkill("Cybernetics"),
"scanMagnetometricStrengthModifier", implant.getModifiedItemAttr("implantSetLGFederationNavy"))

View File

@@ -5,5 +5,5 @@
type = "passive"
runTime = "early"
def handler(fit, implant, context):
fit.implants.filteredItemMultiply(lambda target: target.item.requiresSkill("Cybernetics"),
fit.appliedImplants.filteredItemMultiply(lambda target: target.item.requiresSkill("Cybernetics"),
"scanRadarStrengthPercent", implant.getModifiedItemAttr("implantSetImperialNavy"))

View File

@@ -5,5 +5,5 @@
type = "passive"
runTime = "early"
def handler(fit, implant, context):
fit.implants.filteredItemMultiply(lambda target: target.item.requiresSkill("Cybernetics"),
fit.appliedImplants.filteredItemMultiply(lambda target: target.item.requiresSkill("Cybernetics"),
"scanRadarStrengthModifier", implant.getModifiedItemAttr("implantSetLGImperialNavy"))

View File

@@ -5,5 +5,5 @@
runTime = "early"
type = "passive"
def handler(fit, implant, context):
fit.implants.filteredItemMultiply(lambda mod: mod.item.group.name == "Cyberimplant",
fit.appliedImplants.filteredItemMultiply(lambda mod: mod.item.group.name == "Cyberimplant",
"WarpSBonus", implant.getModifiedItemAttr("implantSetWarpSpeed"))

View File

@@ -5,5 +5,5 @@
runTime = "early"
type = "passive"
def handler(fit, implant, context):
fit.implants.filteredItemMultiply(lambda target: target.item.requiresSkill("Cybernetics"),
fit.appliedImplants.filteredItemMultiply(lambda target: target.item.requiresSkill("Cybernetics"),
"scanLadarStrengthPercent", implant.getModifiedItemAttr("implantSetRepublicFleet"))

View File

@@ -5,5 +5,5 @@
runTime = "early"
type = "passive"
def handler(fit, implant, context):
fit.implants.filteredItemMultiply(lambda target: target.item.requiresSkill("Cybernetics"),
fit.appliedImplants.filteredItemMultiply(lambda target: target.item.requiresSkill("Cybernetics"),
"scanLadarStrengthModifier", implant.getModifiedItemAttr("implantSetLGRepublicFleet"))

View File

@@ -5,5 +5,5 @@
runTime = "early"
type = "passive"
def handler(fit, implant, context):
fit.implants.filteredItemMultiply(lambda mod: mod.item.group.name == "Cyberimplant",
fit.appliedImplants.filteredItemMultiply(lambda mod: mod.item.group.name == "Cyberimplant",
"durationBonus", implant.getModifiedItemAttr("implantSetBloodraider"))

View File

@@ -5,5 +5,5 @@
runTime = "early"
type = "passive"
def handler(fit, implant, context):
fit.implants.filteredItemMultiply(lambda mod: mod.item.group.name == "Cyberimplant",
fit.appliedImplants.filteredItemMultiply(lambda mod: mod.item.group.name == "Cyberimplant",
"agilityBonus", implant.getModifiedItemAttr("implantSetChristmas"))

View File

@@ -5,5 +5,5 @@
runTime = "early"
type = "passive"
def handler(fit, implant, context):
fit.implants.filteredItemMultiply(lambda mod: mod.item.group.name == "Cyberimplant",
fit.appliedImplants.filteredItemMultiply(lambda mod: mod.item.group.name == "Cyberimplant",
"armorHpBonus2", implant.getModifiedItemAttr("implantSetChristmas"))

View File

@@ -5,5 +5,5 @@
runTime = "early"
type = "passive"
def handler(fit, implant, context):
fit.implants.filteredItemMultiply(lambda mod: mod.item.group.name == "Cyberimplant",
fit.appliedImplants.filteredItemMultiply(lambda mod: mod.item.group.name == "Cyberimplant",
"implantBonusVelocity", implant.getModifiedItemAttr("implantSetChristmas"))

View File

@@ -5,5 +5,5 @@
runTime = "early"
type = "passive"
def handler(fit, implant, context):
fit.implants.filteredItemMultiply(lambda mod: mod.item.group.name == "Cyberimplant",
fit.appliedImplants.filteredItemMultiply(lambda mod: mod.item.group.name == "Cyberimplant",
"capacitorCapacityBonus", implant.getModifiedItemAttr("implantSetChristmas"))

View File

@@ -5,5 +5,5 @@
runTime = "early"
type = "passive"
def handler(fit, implant, context):
fit.implants.filteredItemMultiply(lambda mod: mod.item.group.name == "Cyberimplant",
fit.appliedImplants.filteredItemMultiply(lambda mod: mod.item.group.name == "Cyberimplant",
"capRechargeBonus", implant.getModifiedItemAttr("implantSetChristmas"))

View File

@@ -5,5 +5,5 @@
runTime = "early"
type = "passive"
def handler(fit, implant, context):
fit.implants.filteredItemMultiply(lambda mod: mod.item.group.name == "Cyberimplant",
fit.appliedImplants.filteredItemMultiply(lambda mod: mod.item.group.name == "Cyberimplant",
"cpuOutputBonus2", implant.getModifiedItemAttr("implantSetChristmas"))

View File

@@ -5,5 +5,5 @@
runTime = "early"
type = "passive"
def handler(fit, implant, context):
fit.implants.filteredItemMultiply(lambda mod: mod.item.group.name == "Cyberimplant",
fit.appliedImplants.filteredItemMultiply(lambda mod: mod.item.group.name == "Cyberimplant",
"powerEngineeringOutputBonus", implant.getModifiedItemAttr("implantSetChristmas"))

View File

@@ -5,5 +5,5 @@
runTime = "early"
type = "passive"
def handler(fit, implant, context):
fit.implants.filteredItemMultiply(lambda mod: mod.item.group.name == "Cyberimplant",
fit.appliedImplants.filteredItemMultiply(lambda mod: mod.item.group.name == "Cyberimplant",
"shieldCapacityBonus", implant.getModifiedItemAttr("implantSetChristmas"))

View File

@@ -5,5 +5,5 @@
runTime = "early"
type = "passive"
def handler(fit, implant, context):
fit.implants.filteredItemMultiply(lambda mod: mod.item.group.name == "Cyberimplant",
fit.appliedImplants.filteredItemMultiply(lambda mod: mod.item.group.name == "Cyberimplant",
"shieldBoostMultiplier", implant.getModifiedItemAttr("implantSetGuristas"))

View File

@@ -5,5 +5,5 @@
runTime = "early"
type = "passive"
def handler(fit, implant, context):
fit.implants.filteredItemMultiply(lambda mod: mod.item.group.name == "Cyberimplant",
fit.appliedImplants.filteredItemMultiply(lambda mod: mod.item.group.name == "Cyberimplant",
"rangeSkillBonus", implant.getModifiedItemAttr("implantSetMordus"))

View File

@@ -5,5 +5,5 @@
runTime = "early"
type = "passive"
def handler(fit, implant, context):
fit.implants.filteredItemMultiply(lambda mod: mod.item.group.name == "Cyberimplant",
fit.appliedImplants.filteredItemMultiply(lambda mod: mod.item.group.name == "Cyberimplant",
"maxRangeBonus", implant.getModifiedItemAttr("implantSetORE"))

View File

@@ -6,5 +6,5 @@
runTime = "early"
type = "passive"
def handler(fit, implant, context):
fit.implants.filteredItemMultiply(lambda target: target.item.requiresSkill("Cybernetics"),
fit.appliedImplants.filteredItemMultiply(lambda target: target.item.requiresSkill("Cybernetics"),
"armorHpBonus", implant.getModifiedItemAttr("implantSetSansha") or 1)

View File

@@ -5,5 +5,5 @@
runTime = "early"
type = "passive"
def handler(fit, implant, context):
fit.implants.filteredItemMultiply(lambda mod: mod.item.group.name == "Cyberimplant",
fit.appliedImplants.filteredItemMultiply(lambda mod: mod.item.group.name == "Cyberimplant",
"velocityBonus", implant.getModifiedItemAttr("implantSetSerpentis"))

View File

@@ -5,5 +5,5 @@
runTime = "early"
type = "passive"
def handler(fit, implant, context):
fit.implants.filteredItemMultiply(lambda mod: mod.item.group.name == "Cyberimplant",
fit.appliedImplants.filteredItemMultiply(lambda mod: mod.item.group.name == "Cyberimplant",
"scanStrengthBonus", implant.getModifiedItemAttr("implantSetSisters"))

View File

@@ -5,5 +5,5 @@
runTime = "early"
type = "passive"
def handler(fit, implant, context):
fit.implants.filteredItemMultiply(lambda mod: mod.item.group.name == "Cyberimplant",
fit.appliedImplants.filteredItemMultiply(lambda mod: mod.item.group.name == "Cyberimplant",
"boosterAttributeModifier", implant.getModifiedItemAttr("implantSetSyndicate"))

View File

@@ -5,5 +5,5 @@
runTime = "early"
type = "passive"
def handler(fit, implant, context):
fit.implants.filteredItemMultiply(lambda mod: mod.item.group.name == "Cyberimplant",
fit.appliedImplants.filteredItemMultiply(lambda mod: mod.item.group.name == "Cyberimplant",
"agilityBonus", implant.getModifiedItemAttr("implantSetThukker"))

View File

@@ -19,8 +19,9 @@
from sqlalchemy.orm import validates, reconstructor
from itertools import chain
from eos.effectHandlerHelpers import HandledItem
from eos.effectHandlerHelpers import HandledItem, HandledImplantBoosterList
import eos.db
import eos
import logging
@@ -89,7 +90,7 @@ class Character(object):
return all0
def __init__(self, name, defaultLevel=None, initSkills=True):
self.name = name
self.savedName = name
self.__owner = None
self.defaultLevel = defaultLevel
self.__skills = []
@@ -100,7 +101,7 @@ class Character(object):
for item in self.getSkillList():
self.addSkill(Skill(item.ID, self.defaultLevel))
self.__implants = eos.saveddata.fit.HandledImplantBoosterList()
self.__implants = HandledImplantBoosterList()
self.apiKey = None
@reconstructor
@@ -128,6 +129,14 @@ class Character(object):
def owner(self, owner):
self.__owner = owner
@property
def name(self):
return self.savedName if not self.isDirty else "{} *".format(self.savedName)
@name.setter
def name(self, name):
self.savedName = name
@property
def skills(self):
return self.__skills
@@ -200,8 +209,13 @@ class Character(object):
skill.calculateModifiedAttributes(fit, runTime)
def clear(self):
for skill in self.skills:
skill.clear()
c = chain(
self.skills,
self.implants
)
for stuff in c:
if stuff is not None and stuff != self:
stuff.clear()
def __deepcopy__(self, memo):
copy = Character("%s copy" % self.name, initSkills=False)

View File

@@ -31,6 +31,8 @@ import eos.db
import time
import copy
from utils.timer import Timer
from eos.enum import Enum
import logging
@@ -41,6 +43,10 @@ try:
except ImportError:
from utils.compat import OrderedDict
class ImplantLocation(Enum):
FIT = 0
CHARACTER = 1
class Fit(object):
"""Represents a fitting, with modules, ship, implants, etc."""
@@ -323,17 +329,20 @@ class Fit(object):
return -log(0.25) * agility * mass / 1000000
@property
def implantSource(self):
return self.implantLocation
@implantSource.setter
def implantSource(self, source):
self.implantLocation = source
@property
def appliedImplants(self):
implantsBySlot = {}
if self.character:
for implant in self.character.implants:
implantsBySlot[implant.slot] = implant
for implant in self.implants:
implantsBySlot[implant.slot] = implant
return implantsBySlot.values()
if self.implantLocation == ImplantLocation.CHARACTER:
return self.character.implants
else:
return self.implants
@validates("ID", "ownerID", "shipID")
def validator(self, key, val):

View File

@@ -0,0 +1,58 @@
#===============================================================================
# Copyright (C) 2016 Ryan Holmes
#
# 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 <http://www.gnu.org/licenses/>.
#===============================================================================
from eos.effectHandlerHelpers import HandledImplantBoosterList
from copy import deepcopy
class ImplantSet(object):
def __init__(self, name=None):
self.name = name
self.__implants = HandledImplantBoosterList()
@property
def implants(self):
return self.__implants
@classmethod
def exportSets(cls, *sets):
out = "# Exported from pyfa\n#\n" \
"# Values are in following format:\n" \
"# [Implant Set name]\n" \
"# [Implant name]\n" \
"# [Implant name]\n" \
"# ...\n\n"
for set in sets:
out += "[{}]\n".format(set.name)
for implant in set.implants:
out += "{}\n".format(implant.item.name)
out += "\n"
return out.strip()
def __deepcopy__(self, memo):
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))
return copy

View File

@@ -30,10 +30,11 @@ from eos.saveddata.drone import Drone
from eos.saveddata.fighter import Fighter
from eos.saveddata.cargo import Cargo
from eos.saveddata.implant import Implant
from eos.saveddata.implantSet import ImplantSet
from eos.saveddata.booster import SideEffect
from eos.saveddata.booster import Booster
from eos.saveddata.ship import Ship
from eos.saveddata.fit import Fit
from eos.saveddata.fit import Fit, ImplantLocation
from eos.saveddata.mode import Mode
from eos.saveddata.fleet import Fleet, Wing, Squad
from eos.saveddata.miscData import MiscData