diff --git a/pyfpdb/AlchemyMappings.py b/pyfpdb/AlchemyMappings.py
index 0330c656..055cbf25 100644
--- a/pyfpdb/AlchemyMappings.py
+++ b/pyfpdb/AlchemyMappings.py
@@ -50,8 +50,8 @@ class Gametype(MappedBase):
@staticmethod
def get_or_create(session, siteId, gametype):
map = zip(
- ['type', 'base', 'category', 'limitType', 'smallBlind', 'bigBlind', 'smallBet', 'bigBet'],
- ['type', 'base', 'category', 'limitType', 'sb', 'bb', 'dummy', 'dummy', ])
+ ['type', 'base', 'category', 'limitType', 'smallBlind', 'bigBlind', 'smallBet', 'bigBet', 'currency'],
+ ['type', 'base', 'category', 'limitType', 'sb', 'bb', 'dummy', 'dummy', 'currency'])
gametype = dict([(new, gametype.get(old)) for new, old in map ])
hilo = "h"
@@ -168,8 +168,8 @@ class HandInternal(DerivedStats):
'speed': 'speed',
'maxSeats': 'maxseats',
'knockout': 'isKO',
- 'rebuyOrAddon': 'isRebuy',
- 'headsUp': 'isHU',
+ 'rebuy': 'isRebuy',
+ 'addOn': 'isAddOn',
'shootout': 'isShootout',
'matrix': 'isMatrix',
'sng': 'isSNG',
@@ -201,7 +201,7 @@ class HandInternal(DerivedStats):
# fetch and update tourney players
for hp in self.handPlayers:
- tp = TourneyPlayer.get_or_create(session, tour.id, hp.playerId)
+ tp = TourneysPlayer.get_or_create(session, tour.id, hp.playerId)
# FIXME: other TourneysPlayers should be added here
session.flush()
@@ -356,18 +356,20 @@ class HandPlayer(MappedBase):
class Site(object):
"""Class reflecting Players db table"""
INITIAL_DATA = [
- (1 , 'Full Tilt Poker','USD'),
- (2 , 'PokerStars', 'USD'),
- (3 , 'Everleaf', 'USD'),
- (4 , 'Win2day', 'USD'),
- (5 , 'OnGame', 'USD'),
- (6 , 'UltimateBet', 'USD'),
- (7 , 'Betfair', 'USD'),
- (8 , 'Absolute', 'USD'),
- (9 , 'PartyPoker', 'USD'),
- (10, 'Partouche', 'EUR'),
+ (1 , 'Full Tilt Poker','FT'),
+ (2 , 'PokerStars', 'PS'),
+ (3 , 'Everleaf', 'EV'),
+ (4 , 'Win2day', 'W2'),
+ (5 , 'OnGame', 'OG'),
+ (6 , 'UltimateBet', 'UB'),
+ (7 , 'Betfair', 'BF'),
+ (8 , 'Absolute', 'AB'),
+ (9 , 'PartyPoker', 'PP'),
+ (10, 'Partouche', 'PA'),
+ (11, 'Carbon', 'CA'),
+ (12, 'PKR', 'PK'),
]
- INITIAL_DATA_KEYS = ('id', 'name', 'currency')
+ INITIAL_DATA_KEYS = ('id', 'name', 'code')
INITIAL_DATA_DICTS = [ dict(zip(INITIAL_DATA_KEYS, datum)) for datum in INITIAL_DATA ]
@@ -388,7 +390,7 @@ class Tourney(MappedBase):
class TourneyType(MappedBase):
- """Class reflecting TourneysType db table"""
+ """Class reflecting TourneyType db table"""
@classmethod
def get_or_create(cls, session, **kwargs):
@@ -396,12 +398,12 @@ class TourneyType(MappedBase):
Required kwargs:
buyin fee speed maxSeats knockout
- rebuyOrAddon headsUp shootout matrix sng
+ rebuy addOn shootout matrix sng currency
"""
return get_or_create(cls, session, **kwargs)[0]
-class TourneyPlayer(MappedBase):
+class TourneysPlayer(MappedBase):
"""Class reflecting TourneysPlayers db table"""
@classmethod
@@ -453,7 +455,7 @@ mapper (Gametype, gametypes_table, properties={
})
mapper (Player, players_table, properties={
'playerHands': relation(HandPlayer, backref='player'),
- 'playerTourney': relation(TourneyPlayer, backref='player'),
+ 'playerTourney': relation(TourneysPlayer, backref='player'),
})
mapper (Site, sites_table, properties={
'gametypes': relation(Gametype, backref = 'site'),
@@ -471,7 +473,7 @@ mapper (Tourney, tourneys_table)
mapper (TourneyType, tourney_types_table, properties={
'tourneys': relation(Tourney, backref='type'),
})
-mapper (TourneyPlayer, tourneys_players_table)
+mapper (TourneysPlayer, tourneys_players_table)
class LambdaKeyDict(defaultdict):
"""Operates like defaultdict but passes key argument to the factory function"""
diff --git a/pyfpdb/AlchemyTables.py b/pyfpdb/AlchemyTables.py
index b751fd82..f919fd09 100644
--- a/pyfpdb/AlchemyTables.py
+++ b/pyfpdb/AlchemyTables.py
@@ -45,6 +45,7 @@ autorates_table = Table('Autorates', metadata,
gametypes_table = Table('Gametypes', metadata,
Column('id', SmallInteger, primary_key=True),
Column('siteId', SmallInteger, ForeignKey("Sites.id"), nullable=False), # SMALLINT
+ Column('currency', String(4), nullable=False), # varchar(4) NOT NULL
Column('type', String(4), nullable=False), # char(4) NOT NULL
Column('base', String(4), nullable=False), # char(4) NOT NULL
Column('category', String(9), nullable=False), # varchar(9) NOT NULL
@@ -354,7 +355,7 @@ settings_table = Table('Settings', metadata,
sites_table = Table('Sites', metadata,
Column('id', SmallInteger, primary_key=True),
Column('name', String(32), nullable=False), # varchar(32) NOT NULL
- Column('currency', String(3), nullable=False), # char(3) NOT NULL
+ Column('code', String(2), nullable=False), # char(2) NOT NULL
mysql_charset='utf8',
mysql_engine='InnoDB',
)
@@ -368,17 +369,11 @@ tourneys_table = Table('Tourneys', metadata,
Column('prizepool', Integer), # INT NOT NULL
Column('tourStartTime', DateTime), # DATETIME NOT NULL
Column('tourEndTime', DateTime), # DATETIME
- Column('buyinChips', Integer), # INT
Column('tourneyName', String(40)), # varchar(40)
# Mask use : 1=Positionnal Winnings|2=Match1|4=Match2|...|pow(2,n)=Matchn
Column('matrixIdProcessed',SmallInteger, default=0), # TINYINT UNSIGNED DEFAULT 0
- Column('rebuyChips', Integer, default=0), # INT DEFAULT 0
- Column('addonChips', Integer, default=0), # INT DEFAULT 0
- Column('rebuyAmount', MoneyColumn, default=0), # INT DEFAULT 0
- Column('addonAmount', MoneyColumn, default=0), # INT DEFAULT 0
- Column('totalRebuys', Integer, default=0), # INT DEFAULT 0
- Column('totalAddons', Integer, default=0), # INT DEFAULT 0
- Column('koBounty', Integer, default=0), # INT DEFAULT 0
+ Column('totalRebuyCount', Integer, default=0), # INT DEFAULT 0
+ Column('totalAddOnCount', Integer, default=0), # INT DEFAULT 0
Column('comment', Text), # TEXT
Column('commentTs', DateTime), # DATETIME
mysql_charset='utf8',
@@ -390,36 +385,46 @@ Index('siteTourneyNo', tourneys_table.c.siteTourneyNo, tourneys_table.c.tourneyT
tourney_types_table = Table('TourneyTypes', metadata,
Column('id', Integer, primary_key=True),
Column('siteId', SmallInteger, ForeignKey("Sites.id"), nullable=False),
+ Column('currency', String(4), nullable=False), # varchar(4) NOT NULL
Column('buyin', Integer, nullable=False), # INT NOT NULL
- Column('fee', Integer, nullable=False, default=0), # INT NOT NULL
+ Column('fee', Integer, nullable=False), # INT NOT NULL
+ Column('buyInChips', Integer, nullable=False), # INT NOT NULL
Column('maxSeats', Boolean, nullable=False, default=-1), # INT NOT NULL DEFAULT -1
+ Column('rebuy', Boolean, nullable=False, default=False), # BOOLEAN NOT NULL DEFAULT False
+ Column('rebuyCost', Integer), # INT
+ Column('rebuyChips', Integer), # INT
+ Column('addOn', Boolean, nullable=False, default=False), # BOOLEAN NOT NULL DEFAULT False
+ Column('addOnCost', Integer), # INT
+ Column('addOnChips', Integer), # INT
Column('knockout', Boolean, nullable=False, default=False), # BOOLEAN NOT NULL DEFAULT False
- Column('rebuyOrAddon', Boolean, nullable=False, default=False), # BOOLEAN NOT NULL DEFAULT False
+ Column('koBounty', Integer), # INT
Column('speed', String(10)), # varchar(10)
- Column('headsUp', Boolean, nullable=False, default=False), # BOOLEAN NOT NULL DEFAULT False
Column('shootout', Boolean, nullable=False, default=False), # BOOLEAN NOT NULL DEFAULT False
Column('matrix', Boolean, nullable=False, default=False), # BOOLEAN NOT NULL DEFAULT False
Column('sng', Boolean, nullable=False, default=False), # BOOLEAN NOT NULL DEFAULT False
+ Column('satellite', Boolean, nullable=False, default=False), # BOOLEAN NOT NULL DEFAULT False
+ Column('doubleOrNothing', Boolean, nullable=False, default=False), # BOOLEAN NOT NULL DEFAULT False
+ Column('guarantee', Integer, nullable=False, default=0), # INT NOT NULL DEFAULT 0
mysql_charset='utf8',
mysql_engine='InnoDB',
)
Index('tourneyTypes_all',
tourney_types_table.c.siteId, tourney_types_table.c.buyin, tourney_types_table.c.fee,
- tourney_types_table.c.maxSeats, tourney_types_table.c.knockout, tourney_types_table.c.rebuyOrAddon,
- tourney_types_table.c.speed, tourney_types_table.c.headsUp, tourney_types_table.c.shootout,
- tourney_types_table.c.matrix, tourney_types_table.c.sng)
+ tourney_types_table.c.maxSeats, tourney_types_table.c.knockout, tourney_types_table.c.rebuy,
+ tourney_types_table.c.addOn, tourney_types_table.c.speed,
+ tourney_types_table.c.shootout, tourney_types_table.c.matrix, tourney_types_table.c.sng)
tourneys_players_table = Table('TourneysPlayers', metadata,
Column('id', BigIntColumn, primary_key=True),
Column('tourneyId', Integer, ForeignKey("Tourneys.id"), nullable=False),
Column('playerId', Integer, ForeignKey("Players.id"), nullable=False),
- Column('payinAmount', Integer), # INT NOT NULL
Column('rank', Integer), # INT NOT NULL
Column('winnings', Integer), # INT NOT NULL
- Column('nbRebuys', Integer, default=0), # INT DEFAULT 0
- Column('nbAddons', Integer, default=0), # INT DEFAULT 0
- Column('nbKO', Integer, default=0), # INT DEFAULT 0
+ Column('winningsCurrency', Text), # TEXT
+ Column('rebuyCount', Integer, default=0), # INT DEFAULT 0
+ Column('addOnCount', Integer, default=0), # INT DEFAULT 0
+ Column('koCount', Integer, default=0), # INT DEFAULT 0
Column('comment', Text), # TEXT
Column('commentTs', DateTime), # DATETIME
mysql_charset='utf8',
diff --git a/pyfpdb/Database.py b/pyfpdb/Database.py
index b68d9f49..7b9262f1 100644
--- a/pyfpdb/Database.py
+++ b/pyfpdb/Database.py
@@ -53,7 +53,6 @@ log = logging.getLogger("db")
# FreePokerTools modules
import SQL
import Card
-import Tourney
import Charset
from Exceptions import *
import Configuration
@@ -75,7 +74,7 @@ except ImportError:
use_numpy = False
-DB_VERSION = 119
+DB_VERSION = 127
# Variance created as sqlite has a bunch of undefined aggregate functions.
@@ -1344,29 +1343,30 @@ class Database:
def fillDefaultData(self):
c = self.get_cursor()
c.execute("INSERT INTO Settings (version) VALUES (%s);" % (DB_VERSION))
- c.execute("INSERT INTO Sites (name,currency) VALUES ('Full Tilt Poker', 'USD')")
- c.execute("INSERT INTO Sites (name,currency) VALUES ('PokerStars', 'USD')")
- c.execute("INSERT INTO Sites (name,currency) VALUES ('Everleaf', 'USD')")
- c.execute("INSERT INTO Sites (name,currency) VALUES ('Win2day', 'USD')")
- c.execute("INSERT INTO Sites (name,currency) VALUES ('OnGame', 'USD')")
- c.execute("INSERT INTO Sites (name,currency) VALUES ('UltimateBet', 'USD')")
- c.execute("INSERT INTO Sites (name,currency) VALUES ('Betfair', 'USD')")
- c.execute("INSERT INTO Sites (name,currency) VALUES ('Absolute', 'USD')")
- c.execute("INSERT INTO Sites (name,currency) VALUES ('PartyPoker', 'USD')")
- c.execute("INSERT INTO Sites (name,currency) VALUES ('Partouche', 'EUR')")
- c.execute("INSERT INTO Sites (name,currency) VALUES ('Carbon', 'USD')")
- c.execute("INSERT INTO Sites (name,currency) VALUES ('PKR', 'USD')")
+ c.execute("INSERT INTO Sites (name,code) VALUES ('Full Tilt Poker', 'FT')")
+ c.execute("INSERT INTO Sites (name,code) VALUES ('PokerStars', 'PS')")
+ c.execute("INSERT INTO Sites (name,code) VALUES ('Everleaf', 'EV')")
+ c.execute("INSERT INTO Sites (name,code) VALUES ('Win2day', 'W2')")
+ c.execute("INSERT INTO Sites (name,code) VALUES ('OnGame', 'OG')")
+ c.execute("INSERT INTO Sites (name,code) VALUES ('UltimateBet', 'UB')")
+ c.execute("INSERT INTO Sites (name,code) VALUES ('Betfair', 'BF')")
+ c.execute("INSERT INTO Sites (name,code) VALUES ('Absolute', 'AB')")
+ c.execute("INSERT INTO Sites (name,code) VALUES ('PartyPoker', 'PP')")
+ c.execute("INSERT INTO Sites (name,code) VALUES ('Partouche', 'PA')")
+ c.execute("INSERT INTO Sites (name,code) VALUES ('Carbon', 'CA')")
+ c.execute("INSERT INTO Sites (name,code) VALUES ('PKR', 'PK')")
if self.backend == self.SQLITE:
- c.execute("INSERT INTO TourneyTypes (id, siteId, buyin, fee) VALUES (NULL, 1, 0, 0);")
+ c.execute("""INSERT INTO TourneyTypes (id, siteId, currency, buyin, fee, buyInChips, maxSeats, knockout,
+ rebuy, addOn, speed, shootout, matrix)
+ VALUES (NULL, 1, 'USD', 0, 0, 0, 0, 0, 0, 0, NULL, 0, 0);""")
elif self.backend == self.PGSQL:
- c.execute("""insert into TourneyTypes(siteId, buyin, fee, maxSeats, knockout
- ,rebuyOrAddon, speed, headsUp, shootout, matrix)
- values (1, 0, 0, 0, False, False, null, False, False, False);""")
+ c.execute("""insert into TourneyTypes(siteId, currency, buyin, fee, buyInChips, maxSeats, knockout
+ ,rebuy, addOn, speed, shootout, matrix)
+ values (1, 'USD', 0, 0, 0, 0, False, False, False, null, False, False);""")
elif self.backend == self.MYSQL_INNODB:
- c.execute("""insert into TourneyTypes(id, siteId, buyin, fee, maxSeats, knockout
- ,rebuyOrAddon, speed, headsUp, shootout, matrix)
- values (DEFAULT, 1, 0, 0, 0, False, False, null, False, False, False);""")
-
+ c.execute("""insert into TourneyTypes(id, siteId, currency, buyin, fee, buyInChips, maxSeats, knockout
+ ,rebuy, addOn, speed, shootout, matrix)
+ values (DEFAULT, 1, 'USD', 0, 0, 0, 0, False, False, False, null, False, False);""")
#end def fillDefaultData
def rebuild_indexes(self, start=None):
@@ -1374,6 +1374,7 @@ class Database:
self.createAllIndexes()
self.dropAllForeignKeys()
self.createAllForeignKeys()
+ #end def rebuild_indexes
def rebuild_hudcache(self, h_start=None, v_start=None):
"""clears hudcache and rebuilds from the individual handsplayers records"""
@@ -1559,6 +1560,7 @@ class Database:
pids[p],
pdata[p]['startCash'],
pdata[p]['seatNo'],
+ pdata[p]['sitout'],
pdata[p]['card1'],
pdata[p]['card2'],
pdata[p]['card3'],
@@ -1793,8 +1795,9 @@ class Database:
hilo = "s"
elif game['category'] in ['razz','27_3draw','badugi']:
hilo = "l"
- tmp = self.insertGameTypes( (siteid, game['type'], game['base'], game['category'], game['limitType'], hilo,
+ tmp = self.insertGameTypes( (siteid, 'USD', game['type'], game['base'], game['category'], game['limitType'], hilo,
int(Decimal(game['sb'])*100), int(Decimal(game['bb'])*100), 0, 0) )
+ #FIXME: recognise currency
return tmp[0]
def getSqlPlayerIDs(self, pnames, siteid):
@@ -1929,59 +1932,88 @@ class Database:
print "***Error sending finish: "+err[2]+"("+str(err[1])+"): "+str(sys.exc_info()[1])
# end def send_finish_msg():
- def tRecogniseTourneyType(self, tourney):
- log.debug("Database.tRecogniseTourneyType")
- typeId = 1
+ def createOrUpdateTourneyType(self, hand):
+ tourneyTypeId = 1
+
# Check if Tourney exists, and if so retrieve TTypeId : in that case, check values of the ttype
cursor = self.get_cursor()
cursor.execute (self.sql.query['getTourneyTypeIdByTourneyNo'].replace('%s', self.sql.query['placeholder']),
- (tourney.tourNo, tourney.siteId)
+ (hand.tourNo, hand.siteId)
)
result=cursor.fetchone()
expectedValues = { 1 : "buyin", 2 : "fee", 4 : "isKO", 5 : "isRebuy", 6 : "speed",
- 7 : "isHU", 8 : "isShootout", 9 : "isMatrix" }
- typeIdMatch = True
+ 7 : "isShootout", 8 : "isMatrix" }
+ tourneyTypeIdMatch = True
try:
- len(result)
- typeId = result[0]
- log.debug("Tourney found in db with Tourney_Type_ID = %d" % typeId)
+ tourneyTypeId = result[0]
+ log.debug("Tourney found in db with Tourney_Type_ID = %d" % tourneyTypeId)
for ev in expectedValues :
- if ( getattr( tourney, expectedValues.get(ev) ) <> result[ev] ):
- log.debug("TypeId mismatch : wrong %s : Tourney=%s / db=%s" % (expectedValues.get(ev), getattr( tourney, expectedValues.get(ev)), result[ev]) )
- typeIdMatch = False
+ if ( getattr( hand, expectedValues.get(ev) ) <> result[ev] ):
+ log.debug("TypeId mismatch : wrong %s : Tourney=%s / db=%s" % (expectedValues.get(ev), getattr( hand, expectedValues.get(ev)), result[ev]) )
+ tourneyTypeIdMatch = False
#break
except:
# Tourney not found : a TourneyTypeId has to be found or created for that specific tourney
- typeIdMatch = False
+ tourneyTypeIdMatch = False
- if typeIdMatch == False :
- # Check for an existing TTypeId that matches tourney info (buyin/fee, knockout, rebuy, speed, matrix, shootout)
- # if not found create it
- log.debug("Searching for a TourneyTypeId matching TourneyType data")
+ if tourneyTypeIdMatch == False :
+ # Check for an existing TTypeId that matches tourney info, if not found create it
cursor.execute (self.sql.query['getTourneyTypeId'].replace('%s', self.sql.query['placeholder']),
- (tourney.siteId, tourney.buyin, tourney.fee, tourney.isKO,
- tourney.isRebuy, tourney.speed, tourney.isHU, tourney.isShootout, tourney.isMatrix)
+ (hand.siteId, hand.buyinCurrency, hand.buyin, hand.fee, hand.isKO,
+ hand.isRebuy, hand.isRebuy, hand.speed, hand.isShootout, hand.isMatrix)
)
result=cursor.fetchone()
try:
- len(result)
- typeId = result[0]
- log.debug("Existing Tourney Type Id found : %d" % typeId)
+ tourneyTypeId = result[0]
except TypeError: #this means we need to create a new entry
- log.debug("Tourney Type Id not found : create one")
- cursor.execute (self.sql.query['insertTourneyTypes'].replace('%s', self.sql.query['placeholder']),
- (tourney.siteId, tourney.buyin, tourney.fee, tourney.isKO, tourney.isRebuy,
- tourney.speed, tourney.isHU, tourney.isShootout, tourney.isMatrix)
+ cursor.execute (self.sql.query['insertTourneyType'].replace('%s', self.sql.query['placeholder']),
+ (hand.siteId, hand.buyinCurrency, hand.buyin, hand.fee, hand.buyInChips,
+ hand.isKO, hand.isRebuy,
+ hand.isAddOn, hand.speed, hand.isShootout, hand.isMatrix)
)
- typeId = self.get_last_insert_id(cursor)
-
- return typeId
- #end def tRecogniseTourneyType
+ tourneyTypeId = self.get_last_insert_id(cursor)
+ return tourneyTypeId
+ #end def createOrUpdateTourneyType
+
+ def createOrUpdateTourney(self, hand):
+ cursor = self.get_cursor()
+ cursor.execute (self.sql.query['getTourneyIdByTourneyNo'].replace('%s', self.sql.query['placeholder']),
+ (hand.siteId, hand.tourNo))
+ result=cursor.fetchone()
+ if result != None and len(result)==1:
+ tourneyId = result[0]
+ else:
+ cursor.execute (self.sql.query['insertTourney'].replace('%s', self.sql.query['placeholder']),
+ (hand.tourneyTypeId, hand.tourNo, None, None,
+ hand.startTime, None, None, None,
+ None, None))
+ tourneyId = self.get_last_insert_id(cursor)
+ return tourneyId
+ #end def createOrUpdateTourney
+ def createOrUpdateTourneysPlayers(self, hand):
+ tourneysPlayersIds=[]
+ for player in hand.players:
+ playerId = hand.dbid_pids[player[1]]
+
+ cursor = self.get_cursor()
+ cursor.execute (self.sql.query['getTourneysPlayersId'].replace('%s', self.sql.query['placeholder']),
+ (hand.tourneyId, playerId))
+ result=cursor.fetchone()
+
+ if result != None and len(result)==1:
+ tourneysPlayersIds.append(result[0])
+ else:
+ cursor.execute (self.sql.query['insertTourneysPlayer'].replace('%s', self.sql.query['placeholder']),
+ (hand.tourneyId, playerId, None, None, None, None, None, None, None, None))
+ tourneysPlayersIds.append(self.get_last_insert_id(cursor))
+ return tourneysPlayersIds
+ #end def createOrUpdateTourneysPlayers
+#end class Database
# Class used to hold all the data needed to write a hand to the db
# mainParser() in fpdb_parse_logic.py creates one of these and then passes it to
@@ -2028,7 +2060,6 @@ class HandToWrite:
self.maxSeats = None
self.tableName = None
self.seatNos = None
- self.payin_amounts = None # tourney import was complaining mightily about this missing
except:
print "htw.init error: " + str(sys.exc_info())
raise
diff --git a/pyfpdb/DatabaseManager.py b/pyfpdb/DatabaseManager.py
deleted file mode 100644
index fb52af63..00000000
--- a/pyfpdb/DatabaseManager.py
+++ /dev/null
@@ -1,827 +0,0 @@
-#!/usr/bin/python2
-# -*- coding: utf-8 -*-
-
-#Copyright 2008-2010 J. Urner
-#This program is free software: you can redistribute it and/or modify
-#it under the terms of the GNU Affero General Public License as published by
-#the Free Software Foundation, version 3 of the License.
-#
-#This program 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 Affero General Public License
-#along with this program. If not, see .
-#In the "official" distribution you can find the license in agpl-3.0.txt.
-
-"""Database manager
-
-@todo: (gtk) how to validate user input in gtk.Dialog? as soon as the user clicks ok the dialog is dead. we use a while loop as workaround. not nice
-@todo: (fpdb) we need the application name 'fpdb' from somewhere to put it in dialog titles
-@todo: (fpdb) config object should be initialized globally and accessible from all modules via Configuration.py
-
-@todo: (all dialogs) save/restore size and pos
-
-@todo: (WidgetDatabaseManager) give database status meaningful colors
-@todo: (WidgetDatabaseManager) implement database purging
-@todo: (WidgetDatabaseManager) implement database export
-@todo: (WidgetDatabaseManager) what to do on database doubleclick?
-@todo: (WidgetDatabaseManager) context menu for database tree
-@todo: (WidgetDatabaseManager) initializing/validating databases may take a while. how to give feedback?
-
-"""
-
-import os
-import pygtk
-pygtk.require('2.0')
-import gtk
-import gobject
-
-#*******************************************************************************************************
-class DatabaseManager(gobject.GObject):
- DatabaseTypes = {}
-
- @classmethod
- def from_fpdb(klass, data, defaultDatabaseType=None):
-
- #NOTE: if no databases are present in config fpdb fails with
- # Traceback (most recent call last):
- # File "/home/me2/Scr/Repos/fpdb-mme/pyfpdb/DatabaseManager.py", line 783, in
- # databaseManager = DatabaseManager.from_fpdb('', defaultDatabaseType=DatabaseTypeSqLite)
- # File "/home/me2/Scr/Repos/fpdb-mme/pyfpdb/DatabaseManager.py", line 36, in from_fpdb
- # config = Configuration.Config(file=options.config, dbname=options.dbname)
- # File "/home/me2/Scr/Repos/fpdb-mme/pyfpdb/Configuration.py", line 436, in __init__
- # db = self.get_db_parameters()
- # File "/home/me2/Scr/Repos/fpdb-mme/pyfpdb/Configuration.py", line 583, in get_db_parameters
- # name = self.db_selected
- # AttributeError: Config instance has no attribute 'db_selected'
- import sys
- import Options
- import Configuration
- #NOTE: fpdb should perform this globally
- (options, argv) = Options.fpdb_options()
- config = Configuration.Config(file=options.config, dbname=options.dbname)
- #TODO: handle no database present
- defaultDatabaseName = config.get_db_parameters().get('db-databaseName', None)
- #TODO: fpdb stores databases in no particular order. this has to be fixed somehow
- databases = []
- for name, fpdbDatabase in config.supported_databases.items():
- databaseKlass = klass.DatabaseTypes.get(fpdbDatabase.db_server, None)
- #NOTE: Config does not seem to validate user input, so anything may end up here
- if databaseKlass is None:
- raise ValueError('Unknown databasetype: %s' % fpdbDatabase.db_server)
-
- database = databaseKlass()
- if database.Type == 'sqlite':
- database.name = fpdbDatabase.db_name
- database.file = fpdbDatabase.db_server
- else:
- database.name = fpdbDatabase.db_name
- database.host = fpdbDatabase.db_server
- #NOTE: fpdbDatabase.db_ip is no is a string
- database.port = int(fpdbDatabase.db_ip)
- database.user = fpdbDatabase.db_user
- database.password = fpdbDatabase.db_pass
- databases.append(database)
-
- return klass(databases=databases, defaultDatabaseType=defaultDatabaseType)
-
- def to_fpdb(self):
- pass
-
-
- def __init__(self, databases=None, defaultDatabaseType=None):
- gobject.GObject.__init__(self)
-
- self._defaultDatabaseType = defaultDatabaseType
- self._databases = [] if databases is None else list(databases)
- self._activeDatabase = None
- def __iter__(self):
- return iter(self._databases)
- def set_default_database_type(self, databaseType):
- self._defaultDatabaseType = defaultDatabaseType
- def get_default_database_type(self):
- return self._defaultDatabaseType
- def database_from_id(self, idDatabase):
- for database in self._databases:
- if idDatabase == self.database_id(database):
- return database
- def database_id(self, database):
- return id(database)
- def add_database(self, database):
- if database in self._databases:
- raise ValueError('database already registered')
- self._databases.append(database)
- def remove_database(self, database):
- self._databases.remove(database)
-
- def activate_database(self, database):
- if self._activeDatabase is not None:
- self._activeDatabase.status = self._activeDatabase.StatusInactive
- #TODO: finalize database
- self.emit('database-deactivated', self.database_id(self._activeDatabase) )
-
- database.status = database.StatusActive
- #TODO: activate database
- self._activeDatabase = database
- self.emit('database-activated', self.database_id(database) )
-
- def active_database(self):
- return self._activeDatabase
-
-# register DatabaseManager signals
-gobject.type_register(DatabaseManager)
-gobject.signal_new('database-activated', DatabaseManager, gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, (int, ))
-gobject.signal_new('database-deactivated', DatabaseManager, gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, (int, ))
-gobject.signal_new('database-error', DatabaseManager, gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, (int, ))
-
-class DatabaseTypeMeta(type):
- def __new__(klass, name, bases, kws):
- newKlass = type.__new__(klass, name, bases, kws)
- if newKlass.Type is not None:
- if newKlass.Type in DatabaseManager.DatabaseTypes:
- raise ValueError('data base type already registered for: %s' % newKlass.Type)
- DatabaseManager.DatabaseTypes[newKlass.Type] = newKlass
- return newKlass
-
-class DatabaseTypeBase(object):
- __metaclass__ = DatabaseTypeMeta
- Type = None
- StatusActive = 'active'
- StatusInactive = 'inactive'
- StatusError = 'error' #TODO: not implemented
-
- #TODO: not happy with returning error string. just being too lazy to impl dozens of error codes for later translation
- def init_new_database(self):
- """initializes a new empty database
- @return: (str) error if something goes wrong, None otherwise
- """
- raise NotImplementedError()
-
- def validate_database(self):
- """checks if the database is valid
- @return: (str) error if something goes wrong, None otherwise
- """
- raise NotImplementedError()
-
-class DatabaseTypePostgres(DatabaseTypeBase):
- Type = 'postgresql'
- @classmethod
- def display_name(klass):
- return 'Postgres'
- def __init__(self, name='', host='localhost', port=5432, user='postgres', password='', database='fpdb'):
- self.name = name
- self.host = host
- self.port = port
- self.user = user
- self.password = password
- self.database = database
- self.status = self.StatusInactive
-
- #TODO: implement
- def init_new_database(self):
- pass
-
- #TODO: implement
- def validate_database(self):
- pass
-
-class DatabaseTypeMysql(DatabaseTypeBase):
- Type = 'mysql'
- @classmethod
- def display_name(klass):
- return 'MySql'
- def __init__(self, name='', host='localhost', port=3306, user='root', password='', database='fpdb'):
- self.name = name
- self.host = host
- self.port = port
- self.user = user
- self.password = password
- self.database = database
- self.status = self.StatusInactive
-
- #TODO: implement
- def init_new_database(self):
- pass
-
- #TODO: implement
- def validate_database(self):
- pass
-
-
-class DatabaseTypeSqLite(DatabaseTypeBase):
- Type = 'sqlite'
- @classmethod
- def display_name(klass):
- return 'SqLite'
- def __init__(self, name='', host='', file='', database='fpdb'):
- self.name = name
- self.file = file
- self.status = self.StatusInactive
-
- def init_new_database(self):
- # make shure all attrs are specified
- if not self.file:
- return 'no database file specified'
- # create file if necessary (this will truncate file if it exists)
- try:
- open(self.file, 'w').close()
- except IOError:
- return 'can not write file'
-
- #TODO: init tables (...)
-
-
- def validate_database(self):
- pass
- #TODO: check if tables (...) exist
-
-
-
-#TODO: how do we want to handle unsupported database types?
-# ..uncomment to remove unsupported database types
-#try: import psycopg2
-#except ImportError: del DatabaseManager.DatabaseTypes['postgres']
-#try: import MySQLdb
-#except ImportError: del DatabaseManager.DatabaseTypes['mysql']
-#try: import sqlite3
-#except ImportError: del DatabaseManager.DatabaseTypes['sqlite']
-
-#***************************************************************************************************************************
-#TODO: there is no title (on linux), wtf?
-def DialogError(parent=None, msg=''):
- dlg = gtk.MessageDialog(
- parent=parent,
- flags=gtk.DIALOG_MODAL|gtk.DIALOG_DESTROY_WITH_PARENT,
- type=gtk.MESSAGE_ERROR,
- buttons=gtk.BUTTONS_OK,
- message_format=msg,
- )
- dlg.run()
- dlg.destroy()
- return None
-
-
-#TODO: derrive from gtk.VBox?
-class WidgetDatabaseProperties(gtk.VBox):
-
- ModeNew = 0
- ModeEdit = 1
- ModeAdd = 2
-
- class SqLiteFileChooserButton(gtk.HBox):
- #NOTE: for some weird reason it is impossible to let the user choose a non exiting filename with gtk.FileChooserButton, so impl our own on the fly
- def __init__(self, widgetDatabaseProperties, parentWidget):
- gtk.HBox.__init__(self)
- self.set_homogeneous(False)
-
- self.parentWidget = parentWidget
- self.widgetDatabaseProperties = widgetDatabaseProperties
- self.entry = gtk.Entry()
- self.button = gtk.Button('...')
- self.button.connect('clicked', self.on_button_clicked)
-
- # layout widgets
- self.pack_start(self.entry, True, True)
- self.pack_start(self.button, False, False)
-
- def get_filename(self):
- return self.entry.get_text()
-
- def set_filename(self, name):
- self.entry.set_text(name)
-
- def on_button_clicked(self, button):
- if self.widgetDatabaseProperties.mode == WidgetDatabaseProperties.ModeAdd:
- action = gtk.FILE_CHOOSER_ACTION_OPEN
- elif self.widgetDatabaseProperties.mode == WidgetDatabaseProperties.ModeNew:
- action = gtk.FILE_CHOOSER_ACTION_SAVE
- else:
- raise ValueError('unsupported dialog mode')
- dlg = gtk.FileChooserDialog(
- title='Choose an exiting database file or type in name of a new one',
- parent=self.parentWidget,
- action=action,
- buttons=(
- gtk.STOCK_CANCEL, gtk.RESPONSE_REJECT,
- gtk.STOCK_OK, gtk.RESPONSE_OK,
- ),
- backend=None
- )
- dlg.set_default_response(gtk.RESPONSE_OK)
- dlg.set_do_overwrite_confirmation(True)
- if dlg.run() == gtk.RESPONSE_OK:
- fileName = dlg.get_filename()
- self.set_filename(fileName)
- dlg.destroy()
-
-
- #TODO: bit ugly this thingy. try to find a better way to map database attrs to gtk widgets
- class FieldWidget(object):
- def __init__(self, text='', attrDatabase='', widget=None, attrGet=None, attrSet=None, defaultValue=None, canEdit=False, tooltip=''):
- """
- @param canEdit: True if the user can edit the attr in edit mode, False otherwise
- """
- self._label = gtk.Label(text)
- self._attrDatabase = attrDatabase
- self._widget = widget
- self._defaultValue = defaultValue
- self._attrGetter=None,
- self._attrGet = attrGet
- self._attrSet = attrSet
- self._canEdit = canEdit
-
- self._label.set_tooltip_text(tooltip)
- self._widget.set_tooltip_text(tooltip)
-
- def widget(self):
- return self._widget
- def label(self):
- return self._label
- def is_sensitive(self, database):
- return hasattr(database, self._attrDatabase)
- def can_edit(self):
- return self._canEdit
- def set_sensitive(self, flag):
- self._label.set_sensitive(flag)
- self._widget.set_sensitive(flag)
- def value_from_database(self, database):
- getattr(self._widget, self._attrSet)( getattr(database, self._attrDatabase) )
- def value_to_database(self, database):
- setattr(database, self._attrDatabase, getattr(self._widget, self._attrGet)() )
- def reset_value(self):
- getattr(self._widget, self._attrSet)(self._defaultValue)
-
- def __init__(self, databaseManager, database, mode=ModeEdit, parentWidget=None):
- gtk.VBox.__init__(self)
-
- self.databaseManager = databaseManager
- self.database = database
- self.mode = mode
- self.parentWidget = parentWidget
- self.fieldWidgets = (
- self.FieldWidget(
- text='Name:',
- attrDatabase='name',
- widget=gtk.Entry(),
- defaultValue='',
- attrGet='get_text',
- attrSet='set_text',
- canEdit=True,
- tooltip='Any name you like to name the database '
- ),
- self.FieldWidget(
- text='File:',
- attrDatabase='file',
- widget=self.SqLiteFileChooserButton(self, self.parentWidget),
- defaultValue='',
- attrGet='get_filename',
- attrSet='set_filename',
- canEdit=False,
- tooltip='Fully qualified path of the file to hold the database '
- ),
- self.FieldWidget(
- text='Host:',
- attrDatabase='host',
- widget=gtk.Entry(),
- defaultValue='',
- attrGet='get_text',
- attrSet='set_text',
- canEdit=False,
- tooltip='Host the database is located at'
- ),
- self.FieldWidget(
- text='Port:',
- attrDatabase='port',
- widget=gtk.SpinButton(adjustment=gtk.Adjustment(value=0, lower=0, upper=999999, step_incr=1, page_incr=10) ),
- defaultValue=0,
- attrGet='get_value',
- attrSet='set_value',
- canEdit=False,
- tooltip='Port to use to connect to the host'
- ),
- self.FieldWidget(
- text='User:',
- attrDatabase='user',
- widget=gtk.Entry(),
- defaultValue='',
- attrGet='get_text',
- attrSet='set_text',
- canEdit=False,
- tooltip='User name used to login to the host'
- ),
- self.FieldWidget(
- text='Pwd:',
- attrDatabase='password',
- widget=gtk.Entry(),
- defaultValue='',
- attrGet='get_text',
- attrSet='set_text',
- canEdit=False,
- tooltip='Password used to login to the host'
- ),
- self.FieldWidget(
- text='Db:',
- attrDatabase='database',
- widget=gtk.Entry(),
- defaultValue='',
- attrGet='get_text',
- attrSet='set_text',
- canEdit=False,
- tooltip='Name of the database'
- ),
- )
-
- # setup database type combo
- self.comboType = gtk.ComboBox()
- listStore= gtk.ListStore(str, str)
- self.comboType.set_model(listStore)
- cell = gtk.CellRendererText()
- self.comboType.pack_start(cell, True)
- self.comboType.add_attribute(cell, 'text', 0)
- self.comboType.connect('changed', self.on_combo_type_changed)
-
- # fill database type combo with available database klasses. we store (databaseDisplayName, databaseType) in our model for later lookup
- iCurrentDatabase = 0
- databaseTypes = [(klass.display_name(), klass.Type) for klass in databaseManager.DatabaseTypes.values()]
- databaseTypes.sort()
- for i, (databaseDisplayName, databaseType) in enumerate(databaseTypes):
- listStore.append( (databaseDisplayName, databaseType) )
- if databaseType == self.database.Type:
- iCurrentDatabase = i
- if self.mode == self.ModeEdit or len(databaseTypes) < 2:
- self.comboType.set_button_sensitivity(gtk.SENSITIVITY_OFF)
-
- # init and layout field widgets
- self.pack_start(self.comboType, False, False, 2)
- table = gtk.Table(rows=len(self.fieldWidgets) +1, columns=2, homogeneous=False)
- self.pack_start(table, False, False, 2)
- for i,fieldWidget in enumerate(self.fieldWidgets):
- table.attach(fieldWidget.label(), 0, 1, i, i+1, xoptions=gtk.FILL)
- table.attach(fieldWidget.widget(), 1, 2, i, i+1)
-
- # init widget
- self.comboType.set_active(iCurrentDatabase)
- self._adjust_widgets(self.database)
-
- def _adjust_widgets(self, database):
- for fieldWidget in self.fieldWidgets:
- isSensitive = fieldWidget.is_sensitive(database)
- if isSensitive:
- fieldWidget.value_from_database(database)
- else:
- fieldWidget.reset_value()
- if self.mode == self.ModeEdit:
- isSensitive = isSensitive and fieldWidget.can_edit()
- fieldWidget.set_sensitive(isSensitive)
-
-
- def on_combo_type_changed(self, combo):
- i = self.comboType.get_active()
- if i < 0:
- return
-
- # check if we need to init a new database
- currentDatabaseType = self.comboType.get_model()[i][1]
- if currentDatabaseType == self.database.Type:
- return
-
- # create new empty database
- #NOTE: we dont register it in DatabaseManager
- self.database = self.databaseManager.DatabaseTypes[currentDatabaseType]()
- self._adjust_widgets(self.database)
-
-
- def get_database(self):
- for fieldWidget in self.fieldWidgets:
- if fieldWidget.is_sensitive(self.database):
- fieldWidget.value_to_database(self.database)
- return self.database
-
-
-class DialogDatabaseProperties(gtk.Dialog):
- def __init__(self, databaseManager, database, parent=None, mode=WidgetDatabaseProperties.ModeEdit, title=''):
- gtk.Dialog.__init__(self,
- title=title,
- parent=parent,
- flags=gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT,
- buttons=(
- gtk.STOCK_CANCEL, gtk.RESPONSE_REJECT,
- gtk.STOCK_OK, gtk.RESPONSE_ACCEPT,
- )
- )
- self.connect('response', self.on_dialog_response)
-
- # setup widget
- self.widgetDatabaseProperties = WidgetDatabaseProperties(databaseManager,database, mode=mode, parentWidget=self)
- self.vbox.pack_start(self.widgetDatabaseProperties, True, True)
- self.show_all()
-
- def get_widget_database_properties(self):
- return self.widgetDatabaseProperties
-
- def on_dialog_response(self, dlg, responseId):
- if responseId == gtk.RESPONSE_REJECT:
- pass
- elif responseId == gtk.RESPONSE_ACCEPT:
- pass
-
-
-#TODO: derrive from gtk.VBox?
-# ..is there a way to derrive from gtk.Widget or similar? this would make parentWidget kw obsolete
-class WidgetDatabaseManager(gtk.VBox):
- """
- """
-
- def __init__(self, databaseManager, parentWidget=None):
- gtk.VBox.__init__(self)
-
- self.parentWidget = parentWidget
- self.databaseManager = databaseManager
- self.databaseManager.connect('database-activated', self.on_database_manager_database_activated)
- self.databaseManager.connect('database-deactivated', self.on_database_manager_database_deactivated)
- self.databaseStatusNames = {
- DatabaseTypeBase.StatusActive: 'Active',
- DatabaseTypeBase.StatusInactive: 'Inactive',
- DatabaseTypeBase.StatusError: 'Error',
- }
-
-
- #TODO: dono how to make word wrap work as expected
- self.labelInfo = gtk.Label('database management')
- self.labelInfo.set_line_wrap(True)
- self.labelInfo.set_selectable(True)
- self.labelInfo.set_single_line_mode(False)
- self.labelInfo.set_alignment(0, 0)
-
- # database management buttons
-
- #TODO: bit messy the distinction New/Add/Edit. we'd have to pass three flags to DialogDatabaseProperties
- # to handle this. maybe drop Edit (is just a Remove + Add), to keep things simple
- self.buttonDatabaseActivate = gtk.Button("Activate")
- self.buttonDatabaseActivate.set_tooltip_text('activates the database')
- self.buttonDatabaseActivate.connect('clicked', self.on_button_database_activate_clicked)
- self.buttonDatabaseActivate.set_sensitive(False)
- self.buttonDatabaseNew = gtk.Button("New..")
- self.buttonDatabaseNew.set_tooltip_text('creates a new database')
- self.buttonDatabaseNew.connect('clicked', self.on_button_database_new_clicked)
- self.buttonDatabaseAdd = gtk.Button("Add..")
- self.buttonDatabaseAdd.set_tooltip_text('adds an existing database')
- self.buttonDatabaseAdd.connect('clicked', self.on_button_database_add_clicked)
- self.buttonDatabaseEdit = gtk.Button("Edit..")
- self.buttonDatabaseEdit.set_tooltip_text('edit database settings')
- self.buttonDatabaseEdit.connect('clicked', self.on_button_database_edit_clicked)
- self.buttonDatabaseEdit.set_sensitive(False)
- self.buttonDatabaseRemove = gtk.Button("Remove")
- self.buttonDatabaseRemove.set_tooltip_text('removes the database from the list')
- self.buttonDatabaseRemove.set_sensitive(False)
- self.buttonDatabaseRemove.connect('clicked', self.on_button_database_remove_clicked)
-
- #TODO: i dont think we should do any real database management here. maybe drop it
- #self.buttonDatabaseDelete = gtk.Button("Delete")
- #self.buttonDatabaseDelete.set_tooltip_text('removes the database from the list and deletes it')
- #self.buttonDatabaseDelete.set_sensitive(False)
-
- # init database tree
- self.treeDatabases = gtk.TreeView()
- treeDatabaseColumns = ( # name, displayName, dataType
- ('name', 'Name', str),
- ('status', 'Status', str),
- ('type', 'Type', str),
- ('_id', '', int),
- )
- self.treeDatabaseColumns = {} # name --> index
- store = gtk.ListStore( *[i[2] for i in treeDatabaseColumns] )
- self.treeDatabases.set_model(store)
- for i, (name, displayName, dataType) in enumerate(treeDatabaseColumns):
- col = gtk.TreeViewColumn(displayName, gtk.CellRendererText(), text=i)
- self.treeDatabases.append_column(col)
- if name.startswith('_'):
- col.set_visible(False)
- self.treeDatabaseColumns[name] = i
- self.treeDatabases.get_selection().connect('changed', self.on_tree_databases_selection_changed)
-
- # layout widgets
- vbox = gtk.VBox(self)
- vbox.pack_start(self.labelInfo, False, False, 2)
- vbox.pack_start(gtk.HSeparator(), False, False, 2)
- hbox = gtk.HBox()
- self.add(hbox)
- hbox.set_homogeneous(False)
- vbox = gtk.VBox()
- hbox.pack_start(vbox, False, False, 2)
- vbox.pack_start(self.buttonDatabaseActivate, False, False, 2)
- vbox.pack_start(self.buttonDatabaseNew, False, False, 2)
- vbox.pack_start(self.buttonDatabaseAdd, False, False, 2)
- vbox.pack_start(self.buttonDatabaseEdit, False, False, 2)
- vbox.pack_start(self.buttonDatabaseRemove, False, False, 2)
- #vbox.pack_start(self.buttonDatabaseDelete, False, False, 2)
- box = gtk.VBox()
- vbox.pack_start(box, True, True, 0)
-
- hbox.pack_start(gtk.VSeparator(), False, False, 2)
- hbox.pack_end(self.treeDatabases, True, True, 2)
-
- self.show_all()
-
- # init widget
- model = self.treeDatabases.get_model()
- for database in self.databaseManager:
- it = model.append()
- model.set_value(it, self.treeDatabaseColumns['name'], database.name)
- model.set_value(it, self.treeDatabaseColumns['status'], self.databaseStatusNames[database.status] )
- model.set_value(it, self.treeDatabaseColumns['type'], database.display_name() )
- model.set_value(it, self.treeDatabaseColumns['_id'], self.databaseManager.database_id(database))
-
-
- def on_database_manager_database_activated(self, databaseManager, idDatabase):
- database = self.databaseManager.database_from_id(idDatabase)
- model = self.treeDatabases.get_model()
- for row in iter(model):
- if row[self.treeDatabaseColumns['_id']] == idDatabase:
- row[self.treeDatabaseColumns['status']] = self.databaseStatusNames[database.StatusActive]
- break
- else:
- raise ValueError('database not found')
-
-
- def on_database_manager_database_deactivated(self, databaseManager, idDatabase):
- database = self.databaseManager.database_from_id(idDatabase)
- model = self.treeDatabases.get_model()
- for row in iter(model):
- if row[self.treeDatabaseColumns['_id']] == idDatabase:
- row[self.treeDatabaseColumns['status']] = self.databaseStatusNames[database.StatusInactive]
- break
- else:
- raise ValueError('database not found')
-
-
- def on_button_database_activate_clicked(self, button):
- selection = self.treeDatabases.get_selection()
- if selection is None:
- return
-
- model, it = selection.get_selected()
- idDatabase = model.get_value(it, self.treeDatabaseColumns['_id'])
- database = self.databaseManager.database_from_id(idDatabase)
- self.databaseManager.activate_database(database)
-
-
- #TODO: for some reason i have to click OK/Cancel twice to close the dialog
- def on_button_database_new_clicked(self, button):
- databaseKlass = self.databaseManager.get_default_database_type()
- if databaseKlass is None:
- raise ValueError('no default database type set')
- database = databaseKlass()
-
- while True:
- dlg = DialogDatabaseProperties(
- self.databaseManager,
- database,
- parent=self.parentWidget,
- mode=WidgetDatabaseProperties.ModeNew,
- title='New database'
- )
- response = dlg.run()
- if response == gtk.RESPONSE_ACCEPT:
- database = dlg.get_widget_database_properties().get_database()
- #TODO: initing may or may not take a while. how to handle?
- error = database.init_new_database()
- if error:
- DialogError(parent=dlg, msg=error)
- dlg.destroy()
- continue
- else:
- database = None
- dlg.destroy()
- break
-
-
- if database is None:
- return
-
- self.databaseManager.add_database(database)
- model = self.treeDatabases.get_model()
- it = model.append()
- model.set_value(it, self.treeDatabaseColumns['name'], database.name)
- model.set_value(it, self.treeDatabaseColumns['status'], self.databaseStatusNames[database.status] )
- model.set_value(it, self.treeDatabaseColumns['type'], database.display_name() )
- model.set_value(it, self.treeDatabaseColumns['_id'], self.databaseManager.database_id(database))
-
-
- def on_button_database_add_clicked(self, button):
- databaseKlass = self.databaseManager.get_default_database_type()
- if databaseKlass is None:
- raise ValueError('no defult database type set')
- database = databaseKlass()
-
- while True:
- dlg = DialogDatabaseProperties(
- self.databaseManager,
- database,
- parent=self.parentWidget,
- mode=WidgetDatabaseProperties.ModeAdd,
- title='Add database'
- )
- response = dlg.run()
- if response == gtk.RESPONSE_ACCEPT:
- database = dlg.get_widget_database_properties().get_database()
- #TODO: validating may or may not take a while. how to handle?
- error = database.validate_database()
- if error:
- DialogError(parent=self.parentWidget, msg=error)
- dlg.destroy()
- continue
- else:
- database = None
- dlg.destroy()
- break
-
- if database is None:
- return
-
- self.databaseManager.add_database(database)
- model = self.treeDatabases.get_model()
- it = model.append()
- model.set_value(it, self.treeDatabaseColumns['name'], database.name)
- model.set_value(it, self.treeDatabaseColumns['status'], self.databaseStatusNames[database.status] )
- model.set_value(it, self.treeDatabaseColumns['type'], database.display_name() )
- model.set_value(it, self.treeDatabaseColumns['_id'], self.databaseManager.database_id(database))
- dlg.destroy()
-
- def on_button_database_edit_clicked(self, button):
- selection = self.treeDatabases.get_selection()
- if selection is None:
- return
-
- model, it = selection.get_selected()
- idDatabase = model.get_value(it, self.treeDatabaseColumns['_id'])
- database = self.databaseManager.database_from_id(idDatabase)
- dlg = DialogDatabaseProperties(
- self.databaseManager,
- database,
- parent=self.parentWidget,
- mode=WidgetDatabaseProperties.ModeEdit,
- title='Edit database'
- )
- response = dlg.run()
- if response == gtk.RESPONSE_REJECT:
- pass
- elif response == gtk.RESPONSE_ACCEPT:
- database = dlg.get_database()
- selection = self.treeDatabases.get_selection()
- if selection is not None:
- model, it = selection.get_selected()
- model.set_value(it, self.treeDatabaseColumns['name'], database.name)
- dlg.destroy()
-
-
- def on_button_database_remove_clicked(self, button):
- selection = self.treeDatabases.get_selection()
- if selection is None:
- return
-
- model, it = selection.get_selected()
- #TODO: finalize database
- model.remove(it)
-
-
- def on_tree_databases_selection_changed(self, treeSelection):
- hasSelection = bool(treeSelection.count_selected_rows())
-
- # enable/disable selection dependend widgets
- self.buttonDatabaseActivate.set_sensitive(hasSelection)
- self.buttonDatabaseEdit.set_sensitive(hasSelection)
- self.buttonDatabaseRemove.set_sensitive(hasSelection)
- #self.buttonDatabaseDelete.set_sensitive(hasSelection)
-
-
-class DialogDatabaseManager(gtk.Dialog):
- def __init__(self, databaseManager, parent=None):
- gtk.Dialog.__init__(self,
- title="Databases",
- parent=parent,
- flags=gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT,
- buttons=(
- gtk.STOCK_CANCEL, gtk.RESPONSE_REJECT,
- gtk.STOCK_OK, gtk.RESPONSE_ACCEPT,
- ))
- #self.set_size_request(260, 250)
- self.widgetDatabaseManager = WidgetDatabaseManager(databaseManager, parentWidget=self)
- self.vbox.pack_start(self.widgetDatabaseManager, True, True)
- self.show_all()
-
-#**************************************************************************************************
-if __name__ == '__main__':
- databaseManager = DatabaseManager.from_fpdb('', defaultDatabaseType=DatabaseTypeSqLite)
-
- #d = DialogDatabaseProperties(
- # DatabaseManager(defaultDatabaseType=DatabaseTypeSqLite),
- #database=DatabaseTypePostgres(),
- # database=None,
- # )
- d = DialogDatabaseManager(databaseManager)
- d.connect("destroy", gtk.main_quit)
- d.run()
- #gtk.main()
diff --git a/pyfpdb/DerivedStats.py b/pyfpdb/DerivedStats.py
index 7154696a..ee86a689 100644
--- a/pyfpdb/DerivedStats.py
+++ b/pyfpdb/DerivedStats.py
@@ -139,6 +139,7 @@ class DerivedStats():
for player in hand.players:
self.handsplayers[player[1]]['seatNo'] = player[0]
self.handsplayers[player[1]]['startCash'] = int(100 * Decimal(player[2]))
+ self.handsplayers[player[1]]['sitout'] = False #TODO: implement actual sitout detection
# XXX: enumerate(list, start=x) is python 2.6 syntax; 'start'
#for i, street in enumerate(hand.actionStreets[2:], start=1):
@@ -431,6 +432,11 @@ class DerivedStats():
self.handsplayers[player[1]]['street%sAggr' % i] = True
else:
self.handsplayers[player[1]]['street%sAggr' % i] = False
+
+ if len(aggrers)>0 and i>0:
+ for playername in others:
+ self.handsplayers[playername]['otherRaisedStreet%s' % i] = True
+ #print "otherRaised detected on handid "+str(hand.handid)+" for "+playername+" on street "+str(i)
if i > 0 and len(aggrers) > 0:
for playername in others:
@@ -450,8 +456,7 @@ class DerivedStats():
for act in hand.actions[hand.actionStreets[i+1]]:
if act[1] in ('bets'):
self.handsplayers[act[0]]['street%sBets' % i] = 1 + self.handsplayers[act[0]]['street%sBets' % i]
-
-
+
def folds(self, hand, i):
for act in hand.actions[hand.actionStreets[i+1]]:
if act[1] in ('folds'):
diff --git a/pyfpdb/FulltiltToFpdb.py b/pyfpdb/FulltiltToFpdb.py
index 4ab962f0..9b287edc 100755
--- a/pyfpdb/FulltiltToFpdb.py
+++ b/pyfpdb/FulltiltToFpdb.py
@@ -20,6 +20,7 @@
import logging
from HandHistoryConverter import *
+import TourneySummary
# Fulltilt HH Format converter
@@ -66,7 +67,7 @@ class Fulltilt(HandHistoryConverter):
''', re.VERBOSE)
re_Button = re.compile('^The button is in seat #(?P