fpdb/pyfpdb/Database.py

338 lines
13 KiB
Python
Executable File

#!/usr/bin/env python
"""Database.py
Create and manage the database objects.
"""
# Copyright 2008, Ray E. Barker
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# 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 General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
########################################################################
# postmaster -D /var/lib/pgsql/data
# Standard Library modules
import sys
import traceback
from datetime import datetime, date, time, timedelta
import string
# pyGTK modules
# FreePokerTools modules
import Configuration
import SQL
import Card
class Database:
def __init__(self, c, db_name, game):
db_params = c.get_db_parameters()
if (string.lower(db_params['db-server']) == 'postgresql' or
string.lower(db_params['db-server']) == 'postgres'):
import psycopg2 # posgres via DB-API
import psycopg2.extensions
psycopg2.extensions.register_type(psycopg2.extensions.UNICODE)
try:
if db_params['db-host'] == 'localhost' or db_params['db-host'] == '127.0.0.1':
self.connection = psycopg2.connect(database = db_params['db-databaseName'])
else:
self.connection = psycopg2.connect(host = db_params['db-host'],
user = db_params['db-user'],
password = db_params['db-password'],
database = db_params['db-databaseName'])
except:
print "Error opening database connection %s. See error log file." % (file)
traceback.print_exc(file=sys.stderr)
print "press enter to continue"
sys.stdin.readline()
sys.exit()
elif string.lower(db_params['db-server']) == 'mysql':
import MySQLdb # mysql bindings
try:
self.connection = MySQLdb.connect(host = db_params['db-host'],
user = db_params['db-user'],
passwd = db_params['db-password'],
db = db_params['db-databaseName'])
cur_iso = self.connection.cursor()
cur_iso.execute('SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED')
cur_iso.close()
except:
print "Error opening database connection %s. See error log file." % (file)
traceback.print_exc(file=sys.stderr)
print "press enter to continue"
sys.stdin.readline()
sys.exit()
else:
print "Database = %s not recognized." % (c.supported_databases[db_name].db_server)
sys.stderr.write("Database not recognized, exiting.\n")
print "press enter to continue"
sys.exit()
self.type = db_params['db-type']
self.sql = SQL.Sql(game = game, type = self.type)
self.connection.rollback()
# To add to config:
self.hud_style = 'T' # A=All-time
# S=Session
# T=timed (last n days)
# Future values may also include:
# H=Hands (last n hands)
self.hud_hands = 1000 # Max number of hands from each player to use for hud stats
self.hud_days = 90 # Max number of days from each player to use for hud stats
self.hud_session_gap = 30 # Gap (minutes) between hands that indicates a change of session
# (hands every 2 mins for 1 hour = one session, if followed
# by a 40 minute gap and then more hands on same table that is
# a new session)
cur = self.connection.cursor()
self.hand_1day_ago = 0
cur.execute(self.sql.query['get_hand_1day_ago'])
row = cur.fetchone()
if row and row[0]:
self.hand_1day_ago = row[0]
#print "hand 1day ago =", self.hand_1day_ago
d = timedelta(days=self.hud_days)
now = datetime.utcnow() - d
self.date_ndays_ago = "d%02d%02d%02d" % (now.year-2000, now.month, now.day)
self.hand_nhands_ago = 0 # todo
#cur.execute(self.sql.query['get_table_name'], (hand_id, ))
#row = cur.fetchone()
def close_connection(self):
self.connection.close()
def get_table_name(self, hand_id):
c = self.connection.cursor()
c.execute(self.sql.query['get_table_name'], (hand_id, ))
row = c.fetchone()
return row
def get_last_hand(self):
c = self.connection.cursor()
c.execute(self.sql.query['get_last_hand'])
row = c.fetchone()
return row[0]
def get_xml(self, hand_id):
c = self.connection.cursor()
c.execute(self.sql.query['get_xml'], (hand_id))
row = c.fetchone()
return row[0]
def get_recent_hands(self, last_hand):
c = self.connection.cursor()
c.execute(self.sql.query['get_recent_hands'], {'last_hand': last_hand})
return c.fetchall()
def get_hand_info(self, new_hand_id):
c = self.connection.cursor()
c.execute(self.sql.query['get_hand_info'], new_hand_id)
return c.fetchall()
def get_actual_seat(self, hand_id, name):
c = self.connection.cursor()
c.execute(self.sql.query['get_actual_seat'], (hand_id, name))
row = c.fetchone()
return row[0]
def get_cards(self, hand):
"""Get and return the cards for each player in the hand."""
cards = {} # dict of cards, the key is the seat number,
# the value is a tuple of the players cards
# example: {1: (0, 0, 20, 21, 22, 0 , 0)}
c = self.connection.cursor()
c.execute(self.sql.query['get_cards'], [hand])
for row in c.fetchall():
cards[row[0]] = row[1:]
return cards
def get_common_cards(self, hand):
"""Get and return the community cards for the specified hand."""
cards = {}
c = self.connection.cursor()
c.execute(self.sql.query['get_common_cards'], [hand])
colnames = [desc[0] for desc in c.description]
for row in c.fetchall():
s_dict = {}
for name, val in zip(colnames, row):
s_dict[name] = val
cards['common'] = (self.convert_cards(s_dict))
return cards
def convert_cards(self, d):
ranks = ('', '', '2', '3', '4', '5', '6', '7', '8', '9', 'T', 'J', 'Q', 'K', 'A')
cards = ""
for i in xrange(1, 8):
# key = 'card' + str(i) + 'Value'
# if not d.has_key(key): continue
# if d[key] == None:
# break
# elif d[key] == 0:
# cards += "xx"
# else:
# cards += ranks[d['card' + str(i) + 'Value']] + d['card' +str(i) + 'Suit']
cv = "card%dvalue" % i
if cv not in d or d[cv] == None:
break
elif d[cv] == 0:
cards += "xx"
else:
cs = "card%dsuit" % i
cards = "%s%s%s" % (cards, ranks[d[cv]], d[cs])
return cards
def get_action_from_hand(self, hand_no):
action = [ [], [], [], [], [] ]
c = self.connection.cursor()
c.execute(self.sql.query['get_action_from_hand'], (hand_no, ))
for row in c.fetchall():
street = row[0]
act = row[1:]
action[street].append(act)
return action
def get_winners_from_hand(self, hand):
"""Returns a hash of winners:amount won, given a hand number."""
winners = {}
c = self.connection.cursor()
c.execute(self.sql.query['get_winners_from_hand'], (hand, ))
for row in c.fetchall():
winners[row[0]] = row[1]
return winners
def get_stats_from_hand(self, hand, aggregate = False):
if self.hud_style == 'S':
return( self.get_stats_from_hand_session(hand) )
else: # self.hud_style == A
if aggregate:
query = 'get_stats_from_hand_aggregated'
else:
query = 'get_stats_from_hand'
if self.hud_style == 'T':
stylekey = self.date_ndays_ago
else: # assume A (all-time)
stylekey = '0000000' # all stylekey values should be higher than this
subs = (hand, hand, stylekey)
#print "get stats: hud style =", self.hud_style, "subs =", subs
c = self.connection.cursor()
# now get the stats
c.execute(self.sql.query[query], subs)
colnames = [desc[0] for desc in c.description]
stat_dict = {}
for row in c.fetchall():
t_dict = {}
for name, val in zip(colnames, row):
t_dict[name.lower()] = val
# print t_dict
stat_dict[t_dict['player_id']] = t_dict
return stat_dict
# uses query on handsplayers instead of hudcache to get stats on just this session
def get_stats_from_hand_session(self, hand):
if self.hud_style == 'S':
query = self.sql.query['get_stats_from_hand_session']
if self.db_server == 'mysql':
query = query.replace("<signed>", 'signed ')
else:
query = query.replace("<signed>", '')
else: # self.hud_style == A
return None
subs = (self.hand_1day_ago, hand)
c = self.connection.cursor()
# now get the stats
#print "sess_stats: subs =", subs, "subs[0] =", subs[0]
c.execute(query, subs)
colnames = [desc[0] for desc in c.description]
n,stat_dict = 0,{}
row = c.fetchone()
while row:
if colnames[0].lower() == 'player_id':
playerid = row[0]
else:
print "ERROR: query %s result does not have player_id as first column" % (query,)
break
for name, val in zip(colnames, row):
if not playerid in stat_dict:
stat_dict[playerid] = {}
stat_dict[playerid][name.lower()] = val
elif not name.lower() in stat_dict[playerid]:
stat_dict[playerid][name.lower()] = val
elif name.lower() not in ('hand_id', 'player_id', 'seat', 'screen_name', 'seats'):
stat_dict[playerid][name.lower()] += val
n += 1
if n >= 4000: break # todo: don't think this is needed so set nice and high
# for now - comment out or remove?
row = c.fetchone()
#print " %d rows fetched, len(stat_dict) = %d" % (n, len(stat_dict))
#print "session stat_dict =", stat_dict
return stat_dict
def get_player_id(self, config, site, player_name):
c = self.connection.cursor()
c.execute(self.sql.query['get_player_id'], {'player': player_name, 'site': site})
row = c.fetchone()
if row:
return row[0]
else:
return None
if __name__=="__main__":
c = Configuration.Config()
db_connection = Database(c, 'fpdb', 'holdem') # mysql fpdb holdem
# db_connection = Database(c, 'fpdb-p', 'test') # mysql fpdb holdem
# db_connection = Database(c, 'PTrackSv2', 'razz') # mysql razz
# db_connection = Database(c, 'ptracks', 'razz') # postgres
print "database connection object = ", db_connection.connection
print "database type = ", db_connection.type
h = db_connection.get_last_hand()
print "last hand = ", h
hero = db_connection.get_player_id(c, 'PokerStars', 'nutOmatic')
if hero:
print "nutOmatic is id_player = %d" % hero
stat_dict = db_connection.get_stats_from_hand(h)
for p in stat_dict.keys():
print p, " ", stat_dict[p]
# print "nutOmatics stats:"
# stat_dict = db_connection.get_stats_from_hand(h, hero)
# for p in stat_dict.keys():
# print p, " ", stat_dict[p]
print "cards =", db_connection.get_cards(u'1')
db_connection.close_connection
print "press enter to continue"
sys.stdin.readline()