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