diff --git a/eos/db/migrations/upgrade16.py b/eos/db/migrations/upgrade16.py new file mode 100644 index 000000000..7dfaac97c --- /dev/null +++ b/eos/db/migrations/upgrade16.py @@ -0,0 +1,14 @@ +""" +Migration 16 + +- Alters fits table to introduce notes attribute +""" + +import sqlalchemy + +def upgrade(saveddata_engine): + # Update fits schema to include notes attribute + try: + saveddata_engine.execute("SELECT notes FROM fits LIMIT 1") + except sqlalchemy.exc.DatabaseError: + saveddata_engine.execute("ALTER TABLE fits ADD COLUMN notes VARCHAR;") diff --git a/eos/db/saveddata/fit.py b/eos/db/saveddata/fit.py index ccc29832a..ba8f81a79 100644 --- a/eos/db/saveddata/fit.py +++ b/eos/db/saveddata/fit.py @@ -1,4 +1,4 @@ -#=============================================================================== +# =============================================================================== # Copyright (C) 2010 Diego Duclos # # This file is part of eos. @@ -15,49 +15,46 @@ # # You should have received a copy of the GNU Lesser General Public License # along with eos. If not, see . -#=============================================================================== +# =============================================================================== from sqlalchemy import * -from sqlalchemy.orm import * -from sqlalchemy.sql import and_ 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 eos.db import saveddata_meta -from eos.db.saveddata.module import modules_table +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.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, ImplantLocation +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 fits_table = Table("fits", saveddata_meta, - Column("ID", Integer, primary_key = True), - Column("ownerID", ForeignKey("users.ID"), nullable = True, index = True), - Column("shipID", Integer, nullable = False, index = True), - Column("name", String, nullable = False), - Column("timestamp", Integer, nullable = False), - Column("characterID", ForeignKey("characters.ID"), nullable = True), - Column("damagePatternID", ForeignKey("damagePatterns.ID"), nullable=True), - 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), -) + Column("ID", Integer, primary_key=True), + Column("ownerID", ForeignKey("users.ID"), nullable=True, index=True), + Column("shipID", Integer, nullable=False, index=True), + Column("name", String, nullable=False), + Column("timestamp", Integer, nullable=False), + Column("characterID", ForeignKey("characters.ID"), nullable=True), + Column("damagePatternID", ForeignKey("damagePatterns.ID"), nullable=True), + 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), + Column("notes", String, nullable = True), + ) projectedFits_table = Table("projectedFits", saveddata_meta, - Column("sourceID", ForeignKey("fits.ID"), primary_key = True), - Column("victimID", ForeignKey("fits.ID"), primary_key = True), - Column("amount", Integer, nullable = False, default = 1), - Column("active", Boolean, nullable = False, default = 1), -) + Column("sourceID", ForeignKey("fits.ID"), primary_key=True), + Column("victimID", ForeignKey("fits.ID"), primary_key=True), + Column("amount", Integer, nullable=False, default=1), + Column("active", Boolean, nullable=False, default=1), + ) -commandFits_table = Table("commandFits", saveddata_meta, - Column("boosterID", ForeignKey("fits.ID"), primary_key = True), - Column("boostedID", ForeignKey("fits.ID"), primary_key = True), - Column("active", Boolean, nullable = False, default = 1) -) class ProjectedFit(object): def __init__(self, sourceID, source_fit, amount=1, active=True): @@ -89,25 +86,6 @@ 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 - self.booster_fit = booster_fit - self.active = active - - @reconstructor - 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) - - def __repr__(self): - return "CommandFit(boosterID={}, boostedID={}, active={}) at {}".format( - self.boosterID, self.boostedID, self.active, hex(id(self)) - ) - Fit._Fit__projectedFits = association_proxy( "victimOf", # look at the victimOf association... @@ -115,111 +93,91 @@ Fit._Fit__projectedFits = association_proxy( creator=lambda sourceID, source_fit: ProjectedFit(sourceID, source_fit) ) -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'), + 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'), } -) + ) mapper(ProjectedFit, projectedFits_table, - properties = { - "_ProjectedFit__amount": projectedFits_table.c.amount, + properties={ + "_ProjectedFit__amount": projectedFits_table.c.amount, } -) - -mapper(CommandFit, commandFits_table) + ) diff --git a/eos/saveddata/fit.py b/eos/saveddata/fit.py index c4ffd25b5..cf9437dbe 100644 --- a/eos/saveddata/fit.py +++ b/eos/saveddata/fit.py @@ -1137,6 +1137,7 @@ class Fit(object): copy.name = "%s copy" % self.name copy.damagePattern = self.damagePattern copy.targetResists = self.targetResists + copy.notes = self.notes toCopy = ( "modules", diff --git a/gui/additionsPane.py b/gui/additionsPane.py index 794e97cac..c7b28b48b 100644 --- a/gui/additionsPane.py +++ b/gui/additionsPane.py @@ -25,7 +25,9 @@ from gui.fighterView import FighterView from gui.cargoView import CargoView from gui.implantView import ImplantView from gui.projectedView import ProjectedView + from gui.commandView import CommandView +from gui.notesView import NotesView from gui.pyfatogglepanel import TogglePanel from gui.gangView import GangView from gui.bitmapLoader import BitmapLoader @@ -58,6 +60,7 @@ class AdditionsPane(TogglePanel): projectedImg = BitmapLoader.getImage("projected_small", "gui") gangImg = BitmapLoader.getImage("fleet_fc_small", "gui") cargoImg = BitmapLoader.getImage("cargo_small", "gui") + notesImg = BitmapLoader.getImage("skill_small", "gui") self.drone = DroneView(self.notebook) self.notebook.AddPage(self.drone, "Drones", tabImage = droneImg, showClose = False) @@ -80,9 +83,13 @@ class AdditionsPane(TogglePanel): self.gangPage = CommandView(self.notebook) 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.SetSelection(0) - PANES = ["Drones", "Fighters", "Cargo", "Implants", "Boosters", "Projected", "Command"] + PANES = ["Drones", "Fighters", "Cargo", "Implants", "Boosters", "Projected", "Command", "Notes"] + def select(self, name): self.notebook.SetSelection(self.PANES.index(name)) diff --git a/gui/notesView.py b/gui/notesView.py new file mode 100644 index 000000000..74a1992f7 --- /dev/null +++ b/gui/notesView.py @@ -0,0 +1,45 @@ +import wx + +import service +import gui.globalEvents as GE +import gui.mainFrame + +class NotesView(wx.Panel): + def __init__(self, parent): + wx.Panel.__init__(self, parent) + self.lastFitId = None + self.mainFrame = gui.mainFrame.MainFrame.getInstance() + mainSizer = wx.BoxSizer(wx.VERTICAL) + self.editNotes = wx.TextCtrl(self, style=wx.TE_MULTILINE) + mainSizer.Add(self.editNotes, 1, wx.EXPAND) + self.SetSizer(mainSizer) + self.mainFrame.Bind(GE.FIT_CHANGED, self.fitChanged) + self.Bind(wx.EVT_TEXT, self.onText) + self.saveTimer = wx.Timer(self) + self.Bind(wx.EVT_TIMER, self.delayedSave, self.saveTimer) + + def fitChanged(self, event): + sFit = service.Fit.getInstance() + fit = sFit.getFit(event.fitID) + + self.Parent.Parent.DisablePage(self, not fit or fit.isStructure) + + if event.fitID is None and self.lastFitId is not None: + self.lastFitId = None + event.Skip() + return + elif event.fitID != self.lastFitId: + self.lastFitId = event.fitID + self.editNotes.SetValue(fit.notes or "") + + 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.Start(1000, True) + + def delayedSave(self, event): + sFit = service.Fit.getInstance() + fit = sFit.getFit(self.lastFitId) + newNotes = self.editNotes.GetValue() + fit.notes = newNotes + wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=fit.ID))