merged carl's tree
This commit is contained in:
commit
150901cd6e
2
Makefile
2
Makefile
|
@ -1,5 +1,5 @@
|
||||||
# Variable definitions
|
# Variable definitions
|
||||||
VERSION = 0.12
|
VERSION = 0.20
|
||||||
DATE = $(shell date +%Y%m%d)
|
DATE = $(shell date +%Y%m%d)
|
||||||
|
|
||||||
all:
|
all:
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
README.txt
|
README.txt
|
||||||
updated 26 March 2009, REB
|
updated 22 February 2010, REB
|
||||||
|
|
||||||
fpdb - Free Poker Database
|
fpdb - Free Poker Database
|
||||||
|
|
||||||
|
@ -29,7 +29,7 @@ fpdb supports:
|
||||||
Omaha (incl Hi/low)
|
Omaha (incl Hi/low)
|
||||||
7 Card Stud (incl Hi/low)
|
7 Card Stud (incl Hi/low)
|
||||||
Razz
|
Razz
|
||||||
Draw support is under development
|
Triple Draw and Badugi
|
||||||
Mixed Games -- HUD under development
|
Mixed Games -- HUD under development
|
||||||
|
|
||||||
Operating Systems:
|
Operating Systems:
|
||||||
|
@ -38,23 +38,38 @@ fpdb supports:
|
||||||
Mac OS/X -- no support for HUD
|
Mac OS/X -- no support for HUD
|
||||||
|
|
||||||
Databases:
|
Databases:
|
||||||
|
SQLite configured by default
|
||||||
MySQL
|
MySQL
|
||||||
PostgreSQL
|
PostgreSQL
|
||||||
SQLite under development
|
|
||||||
|
|
||||||
Downloads:
|
Downloads:
|
||||||
Releases: http://sourceforge.net/project/showfiles.php?group_id=226872
|
Releases: http://sourceforge.net/project/showfiles.php?group_id=226872
|
||||||
Development code via git: http://www.assembla.com/spaces/free_poker_tools/trac_git_tool
|
Development code via git: http://www.assembla.com/spaces/free_poker_tools/trac_git_tool
|
||||||
|
|
||||||
Developers:
|
Developers:
|
||||||
At least 7 people have contributed code or patches. Others are welcome.
|
At least 10 people have contributed code or patches. Others are welcome.
|
||||||
|
|
||||||
|
Source Code:
|
||||||
|
If you received fpdb as the Windows compressed exe, then you did not
|
||||||
|
receive souce code for fpdb or the included libraries. If you wish, you can
|
||||||
|
obtain the source code here:
|
||||||
|
|
||||||
|
fpdb: see Downloads, above.
|
||||||
|
python: http://python.org/
|
||||||
|
gtk: http://www.gtk.org/download.html
|
||||||
|
pygtk: http://www.pygtk.org/downloads.html
|
||||||
|
psycopg2: http://initd.org/pub/software/psycopg/
|
||||||
|
mysqldb: http://sourceforge.net/projects/mysql-python/files/
|
||||||
|
sqlalchemy: http://www.sqlalchemy.org/download.html
|
||||||
|
numpy: http://www.scipy.org/Download
|
||||||
|
matplotlib: http://sourceforge.net/projects/matplotlib/files/
|
||||||
|
|
||||||
License
|
License
|
||||||
=======
|
=======
|
||||||
Trademarks of third parties have been used under Fair Use or similar laws.
|
Trademarks of third parties have been used under Fair Use or similar laws.
|
||||||
|
|
||||||
Copyright 2008 Steffen Jobbagy-Felso
|
Copyright 2008 Steffen Jobbagy-Felso
|
||||||
Copyright 2009 Ray E. Barker
|
Copyright 2009,2010 Ray E. Barker
|
||||||
Permission is granted to copy, distribute and/or modify this
|
Permission is granted to copy, distribute and/or modify this
|
||||||
document under the terms of the GNU Free Documentation License,
|
document under the terms of the GNU Free Documentation License,
|
||||||
Version 1.2 as published by the Free Software Foundation; with
|
Version 1.2 as published by the Free Software Foundation; with
|
||||||
|
|
BIN
gfx/fpdb_large_icon.ico
Normal file
BIN
gfx/fpdb_large_icon.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 4.2 KiB |
|
@ -1,8 +1,8 @@
|
||||||
free-poker-tools (0.12-1) unstable; urgency=low
|
free-poker-tools (0.12~git20100122) unstable; urgency=low
|
||||||
|
|
||||||
* New release
|
* New snapshot release with reworked import code
|
||||||
|
|
||||||
-- Mika Bostrom <bostik+fpdb@bostik.iki.fi> Mon, 26 Oct 2009 17:49:07 +0200
|
-- Mika Bostrom <bostik+fpdb@bostik.iki.fi> Fri, 22 Jan 2010 09:25:27 +0200
|
||||||
|
|
||||||
free-poker-tools (0.11.3+git20091023) unstable; urgency=low
|
free-poker-tools (0.11.3+git20091023) unstable; urgency=low
|
||||||
|
|
||||||
|
|
116
pyfpdb/AlchemyFacilities.py
Normal file
116
pyfpdb/AlchemyFacilities.py
Normal file
|
@ -0,0 +1,116 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
from decimal import Decimal
|
||||||
|
|
||||||
|
from sqlalchemy import types
|
||||||
|
from sqlalchemy.orm.exc import NoResultFound
|
||||||
|
from sqlalchemy.exc import IntegrityError
|
||||||
|
|
||||||
|
import Card
|
||||||
|
|
||||||
|
class CardColumn(types.TypeDecorator):
|
||||||
|
"""Stores cards as smallints
|
||||||
|
|
||||||
|
Automatically converts values like '9h' to smallint
|
||||||
|
|
||||||
|
>>> CardColumn().process_bind_param( 'Td', '' )
|
||||||
|
22
|
||||||
|
>>> CardColumn().process_bind_param( u'Td', '' )
|
||||||
|
22
|
||||||
|
>>> CardColumn().process_bind_param( 22, '' )
|
||||||
|
22
|
||||||
|
>>> CardColumn().process_result_value( 22, '' )
|
||||||
|
'Td'
|
||||||
|
"""
|
||||||
|
|
||||||
|
impl = types.SmallInteger
|
||||||
|
|
||||||
|
def process_bind_param(self, value, dialect):
|
||||||
|
if value is None or isinstance(value, int):
|
||||||
|
return value
|
||||||
|
elif isinstance(value, basestring) and len(value) == 2:
|
||||||
|
return Card.encodeCard(str(value))
|
||||||
|
else:
|
||||||
|
raise Exception, "Incorrect card value: " + repr(value)
|
||||||
|
|
||||||
|
def process_result_value(self, value, dialect):
|
||||||
|
return Card.valueSuitFromCard( value )
|
||||||
|
|
||||||
|
|
||||||
|
class MoneyColumn(types.TypeDecorator):
|
||||||
|
"""Stores money: bets, pots, etc
|
||||||
|
|
||||||
|
Understands:
|
||||||
|
Decimal as real amount
|
||||||
|
int as amount mupliplied by 100
|
||||||
|
string as decimal
|
||||||
|
Returns Decimal
|
||||||
|
>>> MoneyColumn().process_bind_param( 230, '' )
|
||||||
|
230
|
||||||
|
>>> MoneyColumn().process_bind_param( Decimal('2.30'), '' )
|
||||||
|
230
|
||||||
|
>>> MoneyColumn().process_bind_param( '2.30', '' )
|
||||||
|
230
|
||||||
|
>>> MoneyColumn().process_result_value( 230, '' )
|
||||||
|
Decimal('2.3')
|
||||||
|
"""
|
||||||
|
|
||||||
|
impl = types.Integer
|
||||||
|
|
||||||
|
def process_bind_param(self, value, dialect):
|
||||||
|
if value is None or isinstance(value, int):
|
||||||
|
return value
|
||||||
|
elif isinstance(value, basestring) or isinstance(value, Decimal):
|
||||||
|
return int(Decimal(value)*100)
|
||||||
|
else:
|
||||||
|
raise Exception, "Incorrect amount:" + repr(value)
|
||||||
|
|
||||||
|
def process_result_value(self, value, dialect):
|
||||||
|
if value is None:
|
||||||
|
return None
|
||||||
|
return Decimal(value)/100
|
||||||
|
|
||||||
|
|
||||||
|
class BigIntColumn(types.TypeDecorator, types.Integer):
|
||||||
|
"""Representing db-independent big integer """
|
||||||
|
# Integer inheritance required for auto_increment flag
|
||||||
|
|
||||||
|
impl = types.Integer
|
||||||
|
|
||||||
|
def load_dialect_impl(self, dialect):
|
||||||
|
from sqlalchemy import databases
|
||||||
|
if dialect.name == 'mysql':
|
||||||
|
return databases.mysql.MSBigInteger()
|
||||||
|
elif dialect.name == 'postgres':
|
||||||
|
return databases.mysql.PGBigInteger()
|
||||||
|
return types.Integer()
|
||||||
|
|
||||||
|
|
||||||
|
class MappedBase(object):
|
||||||
|
"""Provide dummy contrcutor"""
|
||||||
|
|
||||||
|
def __init__(self, **kwargs):
|
||||||
|
for k, v in kwargs.iteritems():
|
||||||
|
setattr(self, k, v)
|
||||||
|
|
||||||
|
def get_columns_names(self):
|
||||||
|
return [i.name for i in self._sa_class_manager.mapper.c]
|
||||||
|
|
||||||
|
def get_or_create(klass, session, **kwargs):
|
||||||
|
"""
|
||||||
|
Looks up an object with the given kwargs, creating one if necessary.
|
||||||
|
Returns a tuple of (object, created), where created is a boolean
|
||||||
|
specifying whether an object was created.
|
||||||
|
"""
|
||||||
|
assert kwargs, \
|
||||||
|
'get_or_create() must be passed at least one keyword argument'
|
||||||
|
try:
|
||||||
|
return session.query(klass).filter_by(**kwargs).one(), False
|
||||||
|
except NoResultFound:
|
||||||
|
try:
|
||||||
|
obj = klass(**kwargs)
|
||||||
|
session.add(obj)
|
||||||
|
session.flush()
|
||||||
|
return obj, True
|
||||||
|
except IntegrityError:
|
||||||
|
return session.query(klass).filter_by(**kwargs).one(), False
|
||||||
|
|
464
pyfpdb/AlchemyMappings.py
Normal file
464
pyfpdb/AlchemyMappings.py
Normal file
|
@ -0,0 +1,464 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
"""@package AlchemyMappings
|
||||||
|
This package contains all classes to be mapped and mappers themselves
|
||||||
|
"""
|
||||||
|
|
||||||
|
import logging
|
||||||
|
import re
|
||||||
|
from decimal import Decimal
|
||||||
|
from sqlalchemy.orm import mapper, relation, reconstructor
|
||||||
|
from sqlalchemy.sql import select
|
||||||
|
from collections import defaultdict
|
||||||
|
|
||||||
|
|
||||||
|
from AlchemyTables import *
|
||||||
|
from AlchemyFacilities import get_or_create, MappedBase
|
||||||
|
from DerivedStats import DerivedStats
|
||||||
|
from Exceptions import IncompleteHandError, FpdbError
|
||||||
|
|
||||||
|
|
||||||
|
class Player(MappedBase):
|
||||||
|
"""Class reflecting Players db table"""
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_or_create(session, siteId, name):
|
||||||
|
return get_or_create(Player, session, siteId=siteId, name=name)[0]
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return '<Player "%s" on %s>' % (self.name, self.site and self.site.name)
|
||||||
|
|
||||||
|
|
||||||
|
class Gametype(MappedBase):
|
||||||
|
"""Class reflecting Gametypes db table"""
|
||||||
|
|
||||||
|
@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', ])
|
||||||
|
gametype = dict([(new, gametype.get(old)) for new, old in map ])
|
||||||
|
|
||||||
|
hilo = "h"
|
||||||
|
if gametype['category'] in ('studhilo', 'omahahilo'):
|
||||||
|
hilo = "s"
|
||||||
|
elif gametype['category'] in ('razz','27_3draw','badugi'):
|
||||||
|
hilo = "l"
|
||||||
|
gametype['hiLo'] = hilo
|
||||||
|
|
||||||
|
for f in ['smallBlind', 'bigBlind', 'smallBet', 'bigBet']:
|
||||||
|
if gametype[f] is None:
|
||||||
|
gametype[f] = 0
|
||||||
|
gametype[f] = int(Decimal(gametype[f])*100)
|
||||||
|
|
||||||
|
gametype['siteId'] = siteId
|
||||||
|
return get_or_create(Gametype, session, **gametype)[0]
|
||||||
|
|
||||||
|
|
||||||
|
class HandActions(object):
|
||||||
|
"""Class reflecting HandsActions db table"""
|
||||||
|
def initFromImportedHand(self, hand, actions):
|
||||||
|
self.hand = hand
|
||||||
|
self.actions = {}
|
||||||
|
for street, street_actions in actions.iteritems():
|
||||||
|
self.actions[street] = []
|
||||||
|
for v in street_actions:
|
||||||
|
hp = hand.handplayers_by_name[v[0]]
|
||||||
|
self.actions[street].append({'street': street, 'pid': hp.id, 'seat': hp.seatNo, 'action':v})
|
||||||
|
|
||||||
|
@property
|
||||||
|
def flat_actions(self):
|
||||||
|
actions = []
|
||||||
|
for street in self.hand.allStreets:
|
||||||
|
actions += self.actions[street]
|
||||||
|
return actions
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class HandInternal(DerivedStats):
|
||||||
|
"""Class reflecting Hands db table"""
|
||||||
|
|
||||||
|
def parseImportedHandStep1(self, hand):
|
||||||
|
"""Extracts values to insert into from hand returned by HHC. No db is needed he"""
|
||||||
|
hand.players = hand.getAlivePlayers()
|
||||||
|
|
||||||
|
# also save some data for step2. Those fields aren't in Hands table
|
||||||
|
self.siteId = hand.siteId
|
||||||
|
self.gametype_dict = hand.gametype
|
||||||
|
|
||||||
|
self.attachHandPlayers(hand)
|
||||||
|
self.attachActions(hand)
|
||||||
|
|
||||||
|
self.assembleHands(hand)
|
||||||
|
self.assembleHandsPlayers(hand)
|
||||||
|
|
||||||
|
def parseImportedHandStep2(self, session):
|
||||||
|
"""Fetching ids for gametypes and players"""
|
||||||
|
gametype = Gametype.get_or_create(session, self.siteId, self.gametype_dict)
|
||||||
|
self.gametypeId = gametype.id
|
||||||
|
for hp in self.handPlayers:
|
||||||
|
hp.playerId = Player.get_or_create(session, self.siteId, hp.name).id
|
||||||
|
|
||||||
|
def getPlayerByName(self, name):
|
||||||
|
if not hasattr(self, 'handplayers_by_name'):
|
||||||
|
self.handplayers_by_name = {}
|
||||||
|
for hp in self.handPlayers:
|
||||||
|
pname = getattr(hp, 'name', None) or hp.player.name
|
||||||
|
self.handplayers_by_name[pname] = hp
|
||||||
|
return self.handplayers_by_name[name]
|
||||||
|
|
||||||
|
def attachHandPlayers(self, hand):
|
||||||
|
"""Fill HandInternal.handPlayers list. Create self.handplayers_by_name"""
|
||||||
|
hand.noSb = getattr(hand, 'noSb', None)
|
||||||
|
if hand.noSb is None and self.gametype_dict['base']=='hold':
|
||||||
|
saw_sb = False
|
||||||
|
for action in hand.actions[hand.actionStreets[0]]: # blindsantes
|
||||||
|
if action[1] == 'posts' and action[2] == 'small blind' and action[0] is not None:
|
||||||
|
saw_sb = True
|
||||||
|
hand.noSb = saw_sb
|
||||||
|
|
||||||
|
self.handplayers_by_name = {}
|
||||||
|
for seat, name, chips in hand.players:
|
||||||
|
p = HandPlayer(hand = self, imported_hand=hand, seatNo=seat,
|
||||||
|
name=name, startCash=chips)
|
||||||
|
self.handplayers_by_name[name] = p
|
||||||
|
|
||||||
|
def attachActions(self, hand):
|
||||||
|
"""Create HandActions object"""
|
||||||
|
a = HandActions()
|
||||||
|
a.initFromImportedHand(self, hand.actions)
|
||||||
|
|
||||||
|
def parseImportedTournament(self, hand, session):
|
||||||
|
"""Fetching tourney, its type and players
|
||||||
|
|
||||||
|
Must be called after Step2
|
||||||
|
"""
|
||||||
|
if self.gametype_dict['type'] != 'tour': return
|
||||||
|
|
||||||
|
# check for consistense
|
||||||
|
for i in ('buyin', 'tourNo'):
|
||||||
|
if not hasattr(hand, i):
|
||||||
|
raise IncompleteHandError(
|
||||||
|
"Field '%s' required for tournaments" % i, self.id, hand )
|
||||||
|
|
||||||
|
# repair old-style buyin value
|
||||||
|
m = re.match('\$(\d+)\+\$(\d+)', hand.buyin)
|
||||||
|
if m is not None:
|
||||||
|
hand.buyin, self.fee = m.groups()
|
||||||
|
|
||||||
|
# fetch tourney type
|
||||||
|
tour_type_hand2db = {
|
||||||
|
'buyin': 'buyin',
|
||||||
|
'fee': 'fee',
|
||||||
|
'speed': 'speed',
|
||||||
|
'maxSeats': 'maxseats',
|
||||||
|
'knockout': 'isKO',
|
||||||
|
'rebuyOrAddon': 'isRebuy',
|
||||||
|
'headsUp': 'isHU',
|
||||||
|
'shootout': 'isShootout',
|
||||||
|
'matrix': 'isMatrix',
|
||||||
|
'sng': 'isSNG',
|
||||||
|
}
|
||||||
|
tour_type_index = dict([
|
||||||
|
( i_db, getattr(hand, i_hand, None) )
|
||||||
|
for i_db, i_hand in tour_type_hand2db.iteritems()
|
||||||
|
])
|
||||||
|
tour_type_index['siteId'] = self.siteId
|
||||||
|
tour_type = TourneyType.get_or_create(session, **tour_type_index)
|
||||||
|
|
||||||
|
# fetch and update tourney
|
||||||
|
tour = Tourney.get_or_create(session, hand.tourNo, tour_type.id)
|
||||||
|
cols = tour.get_columns_names()
|
||||||
|
for col in cols:
|
||||||
|
hand_val = getattr(hand, col, None)
|
||||||
|
if col in ('id', 'tourneyTypeId', 'comment', 'commentTs') or hand_val is None:
|
||||||
|
continue
|
||||||
|
db_val = getattr(tour, col, None)
|
||||||
|
if db_val is None:
|
||||||
|
setattr(tour, col, hand_val)
|
||||||
|
elif col == 'koBounty':
|
||||||
|
setattr(tour, col, max(db_val, hand_val))
|
||||||
|
elif col == 'tourStartTime' and hand.handStart:
|
||||||
|
setattr(tour, col, min(db_val, hand.handStart))
|
||||||
|
|
||||||
|
if tour.entries is None and tour_type.sng:
|
||||||
|
tour.entries = tour_type.maxSeats
|
||||||
|
|
||||||
|
# fetch and update tourney players
|
||||||
|
for hp in self.handPlayers:
|
||||||
|
tp = TourneyPlayer.get_or_create(session, tour.id, hp.playerId)
|
||||||
|
# FIXME: other TourneysPlayers should be added here
|
||||||
|
|
||||||
|
session.flush()
|
||||||
|
|
||||||
|
def isDuplicate(self, session):
|
||||||
|
"""Checks if current hand already exists in db
|
||||||
|
|
||||||
|
siteHandNo ans gameTypeId have to be setted
|
||||||
|
"""
|
||||||
|
return session.query(HandInternal).filter_by(
|
||||||
|
siteHandNo=self.siteHandNo, gametypeId=self.gametypeId).count()!=0
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
s = list()
|
||||||
|
for i in self._sa_class_manager.mapper.c:
|
||||||
|
s.append('%25s %s' % (i, getattr(self, i.name)))
|
||||||
|
|
||||||
|
s+=['', '']
|
||||||
|
for i,p in enumerate(self.handPlayers):
|
||||||
|
s.append('%d. %s' % (i, p.name or '???'))
|
||||||
|
s.append(str(p))
|
||||||
|
return '\n'.join(s)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def boardcards(self):
|
||||||
|
cards = []
|
||||||
|
for i in range(5):
|
||||||
|
cards.append(getattr(self, 'boardcard%d' % (i+1), None))
|
||||||
|
return filter(bool, cards)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def HandClass(self):
|
||||||
|
"""Return HoldemOmahaHand or something like this"""
|
||||||
|
import Hand
|
||||||
|
if self.gametype.base == 'hold':
|
||||||
|
return Hand.HoldemOmahaHand
|
||||||
|
elif self.gametype.base == 'draw':
|
||||||
|
return Hand.DrawHand
|
||||||
|
elif self.gametype.base == 'stud':
|
||||||
|
return Hand.StudHand
|
||||||
|
raise Exception("Unknow gametype.base: '%s'" % self.gametype.base)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def allStreets(self):
|
||||||
|
return self.HandClass.allStreets
|
||||||
|
|
||||||
|
@property
|
||||||
|
def actionStreets(self):
|
||||||
|
return self.HandClass.actionStreets
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class HandPlayer(MappedBase):
|
||||||
|
"""Class reflecting HandsPlayers db table"""
|
||||||
|
def __init__(self, **kwargs):
|
||||||
|
if 'imported_hand' in kwargs and 'seatNo' in kwargs:
|
||||||
|
imported_hand = kwargs.pop('imported_hand')
|
||||||
|
self.position = self.getPosition(imported_hand, kwargs['seatNo'])
|
||||||
|
super(HandPlayer, self).__init__(**kwargs)
|
||||||
|
|
||||||
|
@reconstructor
|
||||||
|
def init_on_load(self):
|
||||||
|
self.name = self.player.name
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def getPosition(hand, seat):
|
||||||
|
"""Returns position value like 'B', 'S', '0', '1', ...
|
||||||
|
|
||||||
|
>>> class A(object): pass
|
||||||
|
...
|
||||||
|
>>> A.noSb = False
|
||||||
|
>>> A.maxseats = 6
|
||||||
|
>>> A.buttonpos = 2
|
||||||
|
>>> A.gametype = {'base': 'hold'}
|
||||||
|
>>> A.players = [(i, None, None) for i in (2, 4, 5, 6)]
|
||||||
|
>>> HandPlayer.getPosition(A, 6) # cut off
|
||||||
|
'1'
|
||||||
|
>>> HandPlayer.getPosition(A, 2) # button
|
||||||
|
'0'
|
||||||
|
>>> HandPlayer.getPosition(A, 4) # SB
|
||||||
|
'S'
|
||||||
|
>>> HandPlayer.getPosition(A, 5) # BB
|
||||||
|
'B'
|
||||||
|
>>> A.noSb = True
|
||||||
|
>>> HandPlayer.getPosition(A, 5) # MP3
|
||||||
|
'2'
|
||||||
|
>>> HandPlayer.getPosition(A, 6) # cut off
|
||||||
|
'1'
|
||||||
|
>>> HandPlayer.getPosition(A, 2) # button
|
||||||
|
'0'
|
||||||
|
>>> HandPlayer.getPosition(A, 4) # BB
|
||||||
|
'B'
|
||||||
|
"""
|
||||||
|
from itertools import chain
|
||||||
|
if hand.gametype['base'] == 'stud':
|
||||||
|
# FIXME: i've never played stud so plz check & del comment \\grindi
|
||||||
|
bringin = None
|
||||||
|
for action in chain(*[self.actions[street] for street in hand.allStreets]):
|
||||||
|
if action[1]=='bringin':
|
||||||
|
bringin = action[0]
|
||||||
|
break
|
||||||
|
if bringin is None:
|
||||||
|
raise Exception, "Cannot find bringin"
|
||||||
|
# name -> seat
|
||||||
|
bringin = int(filter(lambda p: p[1]==bringin, bringin)[0])
|
||||||
|
seat = (int(seat) - int(bringin))%int(hand.maxseats)
|
||||||
|
return str(seat)
|
||||||
|
else:
|
||||||
|
seats_occupied = sorted([seat_ for seat_, name, chips in hand.players], key=int)
|
||||||
|
if hand.buttonpos not in seats_occupied:
|
||||||
|
# i.e. something like
|
||||||
|
# Seat 3: PlayerX ($0), is sitting out
|
||||||
|
# The button is in seat #3
|
||||||
|
hand.buttonpos = max(seats_occupied,
|
||||||
|
key = lambda s: int(s)
|
||||||
|
if int(s) <= int(hand.buttonpos)
|
||||||
|
else int(s) - int(hand.maxseats)
|
||||||
|
)
|
||||||
|
seats_occupied = sorted(seats_occupied,
|
||||||
|
key = lambda seat_: (
|
||||||
|
- seats_occupied.index(seat_)
|
||||||
|
+ seats_occupied.index(hand.buttonpos)
|
||||||
|
+ 2) % len(seats_occupied)
|
||||||
|
)
|
||||||
|
# now (if SB presents) seats_occupied contains seats in order: BB, SB, BU, CO, MP3, ...
|
||||||
|
if hand.noSb:
|
||||||
|
# fix order in the case nosb
|
||||||
|
seats_occupied = seats_occupied[1:] + seats_occupied[0:1]
|
||||||
|
seats_occupied.insert(1, -1)
|
||||||
|
seat = seats_occupied.index(seat)
|
||||||
|
if seat == 0:
|
||||||
|
return 'B'
|
||||||
|
elif seat == 1:
|
||||||
|
return 'S'
|
||||||
|
else:
|
||||||
|
return str(seat-2)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def cards(self):
|
||||||
|
cards = []
|
||||||
|
for i in range(7):
|
||||||
|
cards.append(getattr(self, 'card%d' % (i+1), None))
|
||||||
|
return filter(bool, cards)
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
s = list()
|
||||||
|
for i in self._sa_class_manager.mapper.c:
|
||||||
|
s.append('%45s %s' % (i, getattr(self, i.name)))
|
||||||
|
return '\n'.join(s)
|
||||||
|
|
||||||
|
|
||||||
|
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'),
|
||||||
|
]
|
||||||
|
INITIAL_DATA_KEYS = ('id', 'name', 'currency')
|
||||||
|
|
||||||
|
INITIAL_DATA_DICTS = [ dict(zip(INITIAL_DATA_KEYS, datum)) for datum in INITIAL_DATA ]
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def insert_initial(cls, connection):
|
||||||
|
connection.execute(sites_table.insert(), cls.INITIAL_DATA_DICTS)
|
||||||
|
|
||||||
|
|
||||||
|
class Tourney(MappedBase):
|
||||||
|
"""Class reflecting Tourneys db table"""
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_or_create(cls, session, siteTourneyNo, tourneyTypeId):
|
||||||
|
"""Fetch tourney by index or creates one if none. """
|
||||||
|
return get_or_create(cls, session, siteTourneyNo=siteTourneyNo,
|
||||||
|
tourneyTypeId=tourneyTypeId)[0]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class TourneyType(MappedBase):
|
||||||
|
"""Class reflecting TourneysType db table"""
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_or_create(cls, session, **kwargs):
|
||||||
|
"""Fetch tourney type by index or creates one if none
|
||||||
|
|
||||||
|
Required kwargs:
|
||||||
|
buyin fee speed maxSeats knockout
|
||||||
|
rebuyOrAddon headsUp shootout matrix sng
|
||||||
|
"""
|
||||||
|
return get_or_create(cls, session, **kwargs)[0]
|
||||||
|
|
||||||
|
|
||||||
|
class TourneyPlayer(MappedBase):
|
||||||
|
"""Class reflecting TourneysPlayers db table"""
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_or_create(cls, session, tourneyId, playerId):
|
||||||
|
"""Fetch tourney player by index or creates one if none """
|
||||||
|
return get_or_create(cls, session, tourneyId=tourneyId, playerId=playerId)
|
||||||
|
|
||||||
|
|
||||||
|
class Version(object):
|
||||||
|
"""Provides read/write access for version var"""
|
||||||
|
CURRENT_VERSION = 120 # db version for current release
|
||||||
|
# 119 - first alchemy version
|
||||||
|
# 120 - add m_factor
|
||||||
|
|
||||||
|
conn = None
|
||||||
|
ver = None
|
||||||
|
def __init__(self, connection=None):
|
||||||
|
if self.__class__.conn is None:
|
||||||
|
self.__class__.conn = connection
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def is_wrong(cls):
|
||||||
|
return cls.get() != cls.CURRENT_VERSION
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get(cls):
|
||||||
|
if cls.ver is None:
|
||||||
|
try:
|
||||||
|
cls.ver = cls.conn.execute(select(['version'], settings_table)).fetchone()[0]
|
||||||
|
except:
|
||||||
|
return None
|
||||||
|
return cls.ver
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def set(cls, value):
|
||||||
|
if cls.conn.execute(settings_table.select()).rowcount==0:
|
||||||
|
cls.conn.execute(settings_table.insert(), version=value)
|
||||||
|
else:
|
||||||
|
cls.conn.execute(settings_table.update().values(version=value))
|
||||||
|
cls.ver = value
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def set_initial(cls):
|
||||||
|
cls.set(cls.CURRENT_VERSION)
|
||||||
|
|
||||||
|
|
||||||
|
mapper (Gametype, gametypes_table, properties={
|
||||||
|
'hands': relation(HandInternal, backref='gametype'),
|
||||||
|
})
|
||||||
|
mapper (Player, players_table, properties={
|
||||||
|
'playerHands': relation(HandPlayer, backref='player'),
|
||||||
|
'playerTourney': relation(TourneyPlayer, backref='player'),
|
||||||
|
})
|
||||||
|
mapper (Site, sites_table, properties={
|
||||||
|
'gametypes': relation(Gametype, backref = 'site'),
|
||||||
|
'players': relation(Player, backref = 'site'),
|
||||||
|
'tourneyTypes': relation(TourneyType, backref = 'site'),
|
||||||
|
})
|
||||||
|
mapper (HandActions, hands_actions_table, properties={})
|
||||||
|
mapper (HandInternal, hands_table, properties={
|
||||||
|
'handPlayers': relation(HandPlayer, backref='hand'),
|
||||||
|
'actions_all': relation(HandActions, backref='hand', uselist=False),
|
||||||
|
})
|
||||||
|
mapper (HandPlayer, hands_players_table, properties={})
|
||||||
|
|
||||||
|
mapper (Tourney, tourneys_table)
|
||||||
|
mapper (TourneyType, tourney_types_table, properties={
|
||||||
|
'tourneys': relation(Tourney, backref='type'),
|
||||||
|
})
|
||||||
|
mapper (TourneyPlayer, tourneys_players_table)
|
||||||
|
|
||||||
|
class LambdaKeyDict(defaultdict):
|
||||||
|
"""Operates like defaultdict but passes key argument to the factory function"""
|
||||||
|
def __missing__(key):
|
||||||
|
return self.default_factory(key)
|
||||||
|
|
438
pyfpdb/AlchemyTables.py
Normal file
438
pyfpdb/AlchemyTables.py
Normal file
|
@ -0,0 +1,438 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
"""@package AlchemyTables
|
||||||
|
Contains all sqlalchemy tables
|
||||||
|
"""
|
||||||
|
|
||||||
|
from sqlalchemy import Table, Float, Column, Integer, String, MetaData, \
|
||||||
|
ForeignKey, Boolean, SmallInteger, DateTime, Text, Index, CHAR, \
|
||||||
|
PickleType, Unicode
|
||||||
|
|
||||||
|
from AlchemyFacilities import CardColumn, MoneyColumn, BigIntColumn
|
||||||
|
|
||||||
|
|
||||||
|
metadata = MetaData()
|
||||||
|
|
||||||
|
|
||||||
|
autorates_table = Table('Autorates', metadata,
|
||||||
|
Column('id', Integer, primary_key=True, nullable=False),
|
||||||
|
Column('playerId', Integer, ForeignKey("Players.id"), nullable=False),
|
||||||
|
Column('gametypeId', SmallInteger, ForeignKey("Gametypes.id"), nullable=False),
|
||||||
|
Column('description', String(50), nullable=False),
|
||||||
|
Column('shortDesc', CHAR(8), nullable=False),
|
||||||
|
Column('ratingTime', DateTime, nullable=False),
|
||||||
|
Column('handCount', Integer, nullable=False),
|
||||||
|
mysql_charset='utf8',
|
||||||
|
mysql_engine='InnoDB',
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
gametypes_table = Table('Gametypes', metadata,
|
||||||
|
Column('id', SmallInteger, primary_key=True),
|
||||||
|
Column('siteId', SmallInteger, ForeignKey("Sites.id"), nullable=False), # SMALLINT
|
||||||
|
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
|
||||||
|
Column('limitType', CHAR(2), nullable=False), # char(2) NOT NULL
|
||||||
|
Column('hiLo', CHAR(1), nullable=False), # char(1) NOT NULL
|
||||||
|
Column('smallBlind', Integer(3)), # int
|
||||||
|
Column('bigBlind', Integer(3)), # int
|
||||||
|
Column('smallBet', Integer(3), nullable=False), # int NOT NULL
|
||||||
|
Column('bigBet', Integer(3), nullable=False), # int NOT NULL
|
||||||
|
mysql_charset='utf8',
|
||||||
|
mysql_engine='InnoDB',
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
hands_table = Table('Hands', metadata,
|
||||||
|
Column('id', BigIntColumn, primary_key=True),
|
||||||
|
Column('tableName', String(30), nullable=False),
|
||||||
|
Column('siteHandNo', BigIntColumn, nullable=False),
|
||||||
|
Column('gametypeId', SmallInteger, ForeignKey('Gametypes.id'), nullable=False),
|
||||||
|
Column('handStart', DateTime, nullable=False),
|
||||||
|
Column('importTime', DateTime, nullable=False),
|
||||||
|
Column('seats', SmallInteger, nullable=False),
|
||||||
|
Column('maxSeats', SmallInteger, nullable=False),
|
||||||
|
|
||||||
|
Column('boardcard1', CardColumn),
|
||||||
|
Column('boardcard2', CardColumn),
|
||||||
|
Column('boardcard3', CardColumn),
|
||||||
|
Column('boardcard4', CardColumn),
|
||||||
|
Column('boardcard5', CardColumn),
|
||||||
|
Column('texture', SmallInteger),
|
||||||
|
Column('playersVpi', SmallInteger, nullable=False),
|
||||||
|
Column('playersAtStreet1', SmallInteger, nullable=False, default=0),
|
||||||
|
Column('playersAtStreet2', SmallInteger, nullable=False, default=0),
|
||||||
|
Column('playersAtStreet3', SmallInteger, nullable=False, default=0),
|
||||||
|
Column('playersAtStreet4', SmallInteger, nullable=False, default=0),
|
||||||
|
Column('playersAtShowdown',SmallInteger, nullable=False),
|
||||||
|
Column('street0Raises', SmallInteger, nullable=False),
|
||||||
|
Column('street1Raises', SmallInteger, nullable=False),
|
||||||
|
Column('street2Raises', SmallInteger, nullable=False),
|
||||||
|
Column('street3Raises', SmallInteger, nullable=False),
|
||||||
|
Column('street4Raises', SmallInteger, nullable=False),
|
||||||
|
Column('street1Pot', MoneyColumn),
|
||||||
|
Column('street2Pot', MoneyColumn),
|
||||||
|
Column('street3Pot', MoneyColumn),
|
||||||
|
Column('street4Pot', MoneyColumn),
|
||||||
|
Column('showdownPot', MoneyColumn),
|
||||||
|
Column('comment', Text),
|
||||||
|
Column('commentTs', DateTime),
|
||||||
|
mysql_charset='utf8',
|
||||||
|
mysql_engine='InnoDB',
|
||||||
|
)
|
||||||
|
Index('siteHandNo', hands_table.c.siteHandNo, hands_table.c.gametypeId, unique=True)
|
||||||
|
|
||||||
|
|
||||||
|
hands_actions_table = Table('HandsActions', metadata,
|
||||||
|
Column('id', BigIntColumn, primary_key=True, nullable=False),
|
||||||
|
Column('handId', BigIntColumn, ForeignKey("Hands.id"), nullable=False),
|
||||||
|
Column('actions', PickleType, nullable=False),
|
||||||
|
mysql_charset='utf8',
|
||||||
|
mysql_engine='InnoDB',
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
hands_players_table = Table('HandsPlayers', metadata,
|
||||||
|
Column('id', BigIntColumn, primary_key=True),
|
||||||
|
Column('handId', BigIntColumn, ForeignKey("Hands.id"), nullable=False),
|
||||||
|
Column('playerId', Integer, ForeignKey("Players.id"), nullable=False),
|
||||||
|
Column('startCash', MoneyColumn),
|
||||||
|
Column('position', CHAR(1)), #CHAR(1)
|
||||||
|
Column('seatNo', SmallInteger, nullable=False), #SMALLINT NOT NULL
|
||||||
|
|
||||||
|
Column('card1', CardColumn), #smallint NOT NULL,
|
||||||
|
Column('card2', CardColumn), #smallint NOT NULL
|
||||||
|
Column('card3', CardColumn), #smallint
|
||||||
|
Column('card4', CardColumn), #smallint
|
||||||
|
Column('card5', CardColumn), #smallint
|
||||||
|
Column('card6', CardColumn), #smallint
|
||||||
|
Column('card7', CardColumn), #smallint
|
||||||
|
Column('startCards', SmallInteger), #smallint
|
||||||
|
|
||||||
|
Column('m_factor', Integer), # null for ring games
|
||||||
|
Column('ante', MoneyColumn), #INT
|
||||||
|
Column('winnings', MoneyColumn, nullable=False, default=0), #int NOT NULL
|
||||||
|
Column('rake', MoneyColumn, nullable=False, default=0), #int NOT NULL
|
||||||
|
Column('totalProfit', MoneyColumn), #INT
|
||||||
|
Column('comment', Text), #text
|
||||||
|
Column('commentTs', DateTime), #DATETIME
|
||||||
|
Column('tourneysPlayersId', BigIntColumn, ForeignKey("TourneysPlayers.id"),), #BIGINT UNSIGNED
|
||||||
|
Column('tourneyTypeId', Integer, ForeignKey("TourneyTypes.id"),), #SMALLINT UNSIGNED
|
||||||
|
|
||||||
|
Column('wonWhenSeenStreet1',Float), #FLOAT
|
||||||
|
Column('wonWhenSeenStreet2',Float), #FLOAT
|
||||||
|
Column('wonWhenSeenStreet3',Float), #FLOAT
|
||||||
|
Column('wonWhenSeenStreet4',Float), #FLOAT
|
||||||
|
Column('wonAtSD', Float), #FLOAT
|
||||||
|
|
||||||
|
Column('street0VPI', Boolean), #BOOLEAN
|
||||||
|
Column('street0Aggr', Boolean), #BOOLEAN
|
||||||
|
Column('street0_3BChance', Boolean), #BOOLEAN
|
||||||
|
Column('street0_3BDone', Boolean), #BOOLEAN
|
||||||
|
Column('street0_4BChance', Boolean), #BOOLEAN
|
||||||
|
Column('street0_4BDone', Boolean), #BOOLEAN
|
||||||
|
Column('other3BStreet0', Boolean), #BOOLEAN
|
||||||
|
Column('other4BStreet0', Boolean), #BOOLEAN
|
||||||
|
|
||||||
|
Column('street1Seen', Boolean), #BOOLEAN
|
||||||
|
Column('street2Seen', Boolean), #BOOLEAN
|
||||||
|
Column('street3Seen', Boolean), #BOOLEAN
|
||||||
|
Column('street4Seen', Boolean), #BOOLEAN
|
||||||
|
Column('sawShowdown', Boolean), #BOOLEAN
|
||||||
|
|
||||||
|
Column('street1Aggr', Boolean), #BOOLEAN
|
||||||
|
Column('street2Aggr', Boolean), #BOOLEAN
|
||||||
|
Column('street3Aggr', Boolean), #BOOLEAN
|
||||||
|
Column('street4Aggr', Boolean), #BOOLEAN
|
||||||
|
|
||||||
|
Column('otherRaisedStreet0',Boolean), #BOOLEAN
|
||||||
|
Column('otherRaisedStreet1',Boolean), #BOOLEAN
|
||||||
|
Column('otherRaisedStreet2',Boolean), #BOOLEAN
|
||||||
|
Column('otherRaisedStreet3',Boolean), #BOOLEAN
|
||||||
|
Column('otherRaisedStreet4',Boolean), #BOOLEAN
|
||||||
|
Column('foldToOtherRaisedStreet0', Boolean), #BOOLEAN
|
||||||
|
Column('foldToOtherRaisedStreet1', Boolean), #BOOLEAN
|
||||||
|
Column('foldToOtherRaisedStreet2', Boolean), #BOOLEAN
|
||||||
|
Column('foldToOtherRaisedStreet3', Boolean), #BOOLEAN
|
||||||
|
Column('foldToOtherRaisedStreet4', Boolean), #BOOLEAN
|
||||||
|
|
||||||
|
Column('stealAttemptChance', Boolean), #BOOLEAN
|
||||||
|
Column('stealAttempted', Boolean), #BOOLEAN
|
||||||
|
Column('foldBbToStealChance', Boolean), #BOOLEAN
|
||||||
|
Column('foldedBbToSteal', Boolean), #BOOLEAN
|
||||||
|
Column('foldSbToStealChance', Boolean), #BOOLEAN
|
||||||
|
Column('foldedSbToSteal', Boolean), #BOOLEAN
|
||||||
|
|
||||||
|
Column('street1CBChance', Boolean), #BOOLEAN
|
||||||
|
Column('street1CBDone', Boolean), #BOOLEAN
|
||||||
|
Column('street2CBChance', Boolean), #BOOLEAN
|
||||||
|
Column('street2CBDone', Boolean), #BOOLEAN
|
||||||
|
Column('street3CBChance', Boolean), #BOOLEAN
|
||||||
|
Column('street3CBDone', Boolean), #BOOLEAN
|
||||||
|
Column('street4CBChance', Boolean), #BOOLEAN
|
||||||
|
Column('street4CBDone', Boolean), #BOOLEAN
|
||||||
|
|
||||||
|
Column('foldToStreet1CBChance', Boolean), #BOOLEAN
|
||||||
|
Column('foldToStreet1CBDone', Boolean), #BOOLEAN
|
||||||
|
Column('foldToStreet2CBChance', Boolean), #BOOLEAN
|
||||||
|
Column('foldToStreet2CBDone', Boolean), #BOOLEAN
|
||||||
|
Column('foldToStreet3CBChance', Boolean), #BOOLEAN
|
||||||
|
Column('foldToStreet3CBDone', Boolean), #BOOLEAN
|
||||||
|
Column('foldToStreet4CBChance', Boolean), #BOOLEAN
|
||||||
|
Column('foldToStreet4CBDone', Boolean), #BOOLEAN
|
||||||
|
|
||||||
|
Column('street1CheckCallRaiseChance',Boolean), #BOOLEAN
|
||||||
|
Column('street1CheckCallRaiseDone', Boolean), #BOOLEAN
|
||||||
|
Column('street2CheckCallRaiseChance',Boolean), #BOOLEAN
|
||||||
|
Column('street2CheckCallRaiseDone', Boolean), #BOOLEAN
|
||||||
|
Column('street3CheckCallRaiseChance',Boolean), #BOOLEAN
|
||||||
|
Column('street3CheckCallRaiseDone', Boolean), #BOOLEAN
|
||||||
|
Column('street4CheckCallRaiseChance',Boolean), #BOOLEAN
|
||||||
|
Column('street4CheckCallRaiseDone', Boolean), #BOOLEAN
|
||||||
|
|
||||||
|
Column('street0Calls', SmallInteger), #TINYINT
|
||||||
|
Column('street1Calls', SmallInteger), #TINYINT
|
||||||
|
Column('street2Calls', SmallInteger), #TINYINT
|
||||||
|
Column('street3Calls', SmallInteger), #TINYINT
|
||||||
|
Column('street4Calls', SmallInteger), #TINYINT
|
||||||
|
Column('street0Bets', SmallInteger), #TINYINT
|
||||||
|
Column('street1Bets', SmallInteger), #TINYINT
|
||||||
|
Column('street2Bets', SmallInteger), #TINYINT
|
||||||
|
Column('street3Bets', SmallInteger), #TINYINT
|
||||||
|
Column('street4Bets', SmallInteger), #TINYINT
|
||||||
|
Column('street0Raises', SmallInteger), #TINYINT
|
||||||
|
Column('street1Raises', SmallInteger), #TINYINT
|
||||||
|
Column('street2Raises', SmallInteger), #TINYINT
|
||||||
|
Column('street3Raises', SmallInteger), #TINYINT
|
||||||
|
Column('street4Raises', SmallInteger), #TINYINT
|
||||||
|
|
||||||
|
Column('actionString', String(15)), #VARCHAR(15)
|
||||||
|
mysql_charset='utf8',
|
||||||
|
mysql_engine='InnoDB',
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
hud_cache_table = Table('HudCache', metadata,
|
||||||
|
Column('id', BigIntColumn, primary_key=True),
|
||||||
|
Column('gametypeId', SmallInteger, ForeignKey("Gametypes.id"), nullable=False), # SMALLINT
|
||||||
|
Column('playerId', Integer, ForeignKey("Players.id"), nullable=False), # SMALLINT
|
||||||
|
Column('activeSeats', SmallInteger, nullable=False), # SMALLINT NOT NULL
|
||||||
|
Column('position', CHAR(1)), # CHAR(1)
|
||||||
|
Column('tourneyTypeId', Integer, ForeignKey("TourneyTypes.id") ), # SMALLINT
|
||||||
|
Column('styleKey', CHAR(7), nullable=False), # CHAR(7) NOT NULL
|
||||||
|
Column('m_factor', Integer),
|
||||||
|
Column('HDs', Integer, nullable=False), # INT NOT NULL
|
||||||
|
|
||||||
|
Column('wonWhenSeenStreet1', Float), # FLOAT
|
||||||
|
Column('wonWhenSeenStreet2', Float), # FLOAT
|
||||||
|
Column('wonWhenSeenStreet3', Float), # FLOAT
|
||||||
|
Column('wonWhenSeenStreet4', Float), # FLOAT
|
||||||
|
Column('wonAtSD', Float), # FLOAT
|
||||||
|
|
||||||
|
Column('street0VPI', Integer), # INT
|
||||||
|
Column('street0Aggr', Integer), # INT
|
||||||
|
Column('street0_3BChance', Integer), # INT
|
||||||
|
Column('street0_3BDone', Integer), # INT
|
||||||
|
Column('street0_4BChance', Integer), # INT
|
||||||
|
Column('street0_4BDone', Integer), # INT
|
||||||
|
Column('other3BStreet0', Integer), # INT
|
||||||
|
Column('other4BStreet0', Integer), # INT
|
||||||
|
|
||||||
|
Column('street1Seen', Integer), # INT
|
||||||
|
Column('street2Seen', Integer), # INT
|
||||||
|
Column('street3Seen', Integer), # INT
|
||||||
|
Column('street4Seen', Integer), # INT
|
||||||
|
Column('sawShowdown', Integer), # INT
|
||||||
|
|
||||||
|
Column('street1Aggr', Integer), # INT
|
||||||
|
Column('street2Aggr', Integer), # INT
|
||||||
|
Column('street3Aggr', Integer), # INT
|
||||||
|
Column('street4Aggr', Integer), # INT
|
||||||
|
|
||||||
|
Column('otherRaisedStreet0', Integer), # INT
|
||||||
|
Column('otherRaisedStreet1', Integer), # INT
|
||||||
|
Column('otherRaisedStreet2', Integer), # INT
|
||||||
|
Column('otherRaisedStreet3', Integer), # INT
|
||||||
|
Column('otherRaisedStreet4', Integer), # INT
|
||||||
|
Column('foldToOtherRaisedStreet0', Integer), # INT
|
||||||
|
Column('foldToOtherRaisedStreet1', Integer), # INT
|
||||||
|
Column('foldToOtherRaisedStreet2', Integer), # INT
|
||||||
|
Column('foldToOtherRaisedStreet3', Integer), # INT
|
||||||
|
Column('foldToOtherRaisedStreet4', Integer), # INT
|
||||||
|
|
||||||
|
Column('stealAttemptChance', Integer), # INT
|
||||||
|
Column('stealAttempted', Integer), # INT
|
||||||
|
Column('foldBbToStealChance', Integer), # INT
|
||||||
|
Column('foldedBbToSteal', Integer), # INT
|
||||||
|
Column('foldSbToStealChance', Integer), # INT
|
||||||
|
Column('foldedSbToSteal', Integer), # INT
|
||||||
|
|
||||||
|
Column('street1CBChance', Integer), # INT
|
||||||
|
Column('street1CBDone', Integer), # INT
|
||||||
|
Column('street2CBChance', Integer), # INT
|
||||||
|
Column('street2CBDone', Integer), # INT
|
||||||
|
Column('street3CBChance', Integer), # INT
|
||||||
|
Column('street3CBDone', Integer), # INT
|
||||||
|
Column('street4CBChance', Integer), # INT
|
||||||
|
Column('street4CBDone', Integer), # INT
|
||||||
|
|
||||||
|
Column('foldToStreet1CBChance', Integer), # INT
|
||||||
|
Column('foldToStreet1CBDone', Integer), # INT
|
||||||
|
Column('foldToStreet2CBChance', Integer), # INT
|
||||||
|
Column('foldToStreet2CBDone', Integer), # INT
|
||||||
|
Column('foldToStreet3CBChance', Integer), # INT
|
||||||
|
Column('foldToStreet3CBDone', Integer), # INT
|
||||||
|
Column('foldToStreet4CBChance', Integer), # INT
|
||||||
|
Column('foldToStreet4CBDone', Integer), # INT
|
||||||
|
|
||||||
|
Column('totalProfit', Integer), # INT
|
||||||
|
|
||||||
|
Column('street1CheckCallRaiseChance', Integer), # INT
|
||||||
|
Column('street1CheckCallRaiseDone', Integer), # INT
|
||||||
|
Column('street2CheckCallRaiseChance', Integer), # INT
|
||||||
|
Column('street2CheckCallRaiseDone', Integer), # INT
|
||||||
|
Column('street3CheckCallRaiseChance', Integer), # INT
|
||||||
|
Column('street3CheckCallRaiseDone', Integer), # INT
|
||||||
|
Column('street4CheckCallRaiseChance', Integer), # INT
|
||||||
|
Column('street4CheckCallRaiseDone', Integer), # INT
|
||||||
|
|
||||||
|
Column('street0Calls', Integer), # INT
|
||||||
|
Column('street1Calls', Integer), # INT
|
||||||
|
Column('street2Calls', Integer), # INT
|
||||||
|
Column('street3Calls', Integer), # INT
|
||||||
|
Column('street4Calls', Integer), # INT
|
||||||
|
Column('street0Bets', Integer), # INT
|
||||||
|
Column('street1Bets', Integer), # INT
|
||||||
|
Column('street2Bets', Integer), # INT
|
||||||
|
Column('street3Bets', Integer), # INT
|
||||||
|
Column('street4Bets', Integer), # INT
|
||||||
|
Column('street0Raises', Integer), # INT
|
||||||
|
Column('street1Raises', Integer), # INT
|
||||||
|
Column('street2Raises', Integer), # INT
|
||||||
|
Column('street3Raises', Integer), # INT
|
||||||
|
Column('street4Raises', Integer), # INT
|
||||||
|
mysql_charset='utf8',
|
||||||
|
mysql_engine='InnoDB',
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
players_table = Table('Players', metadata,
|
||||||
|
Column('id', Integer, primary_key=True),
|
||||||
|
Column('name', Unicode(32), nullable=False), # VARCHAR(32) CHARACTER SET utf8 NOT NULL
|
||||||
|
Column('siteId', SmallInteger, ForeignKey("Sites.id"), nullable=False), # SMALLINT
|
||||||
|
Column('comment', Text), # text
|
||||||
|
Column('commentTs', DateTime), # DATETIME
|
||||||
|
mysql_charset='utf8',
|
||||||
|
mysql_engine='InnoDB',
|
||||||
|
)
|
||||||
|
Index('name', players_table.c.name, players_table.c.siteId, unique=True)
|
||||||
|
|
||||||
|
|
||||||
|
settings_table = Table('Settings', metadata,
|
||||||
|
Column('version', SmallInteger, nullable=False),
|
||||||
|
mysql_charset='utf8',
|
||||||
|
mysql_engine='InnoDB',
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
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
|
||||||
|
mysql_charset='utf8',
|
||||||
|
mysql_engine='InnoDB',
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
tourneys_table = Table('Tourneys', metadata,
|
||||||
|
Column('id', Integer, primary_key=True),
|
||||||
|
Column('tourneyTypeId', Integer, ForeignKey("TourneyTypes.id"), nullable=False, default=1),
|
||||||
|
Column('siteTourneyNo', BigIntColumn, nullable=False), # BIGINT NOT NULL
|
||||||
|
Column('entries', Integer), # INT NOT NULL
|
||||||
|
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('comment', Text), # TEXT
|
||||||
|
Column('commentTs', DateTime), # DATETIME
|
||||||
|
mysql_charset='utf8',
|
||||||
|
mysql_engine='InnoDB',
|
||||||
|
)
|
||||||
|
Index('siteTourneyNo', tourneys_table.c.siteTourneyNo, tourneys_table.c.tourneyTypeId, unique=True)
|
||||||
|
|
||||||
|
|
||||||
|
tourney_types_table = Table('TourneyTypes', metadata,
|
||||||
|
Column('id', Integer, primary_key=True),
|
||||||
|
Column('siteId', SmallInteger, ForeignKey("Sites.id"), nullable=False),
|
||||||
|
Column('buyin', Integer, nullable=False), # INT NOT NULL
|
||||||
|
Column('fee', Integer, nullable=False, default=0), # INT NOT NULL
|
||||||
|
Column('maxSeats', Boolean, nullable=False, default=-1), # INT NOT NULL DEFAULT -1
|
||||||
|
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('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
|
||||||
|
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)
|
||||||
|
|
||||||
|
|
||||||
|
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('comment', Text), # TEXT
|
||||||
|
Column('commentTs', DateTime), # DATETIME
|
||||||
|
mysql_charset='utf8',
|
||||||
|
mysql_engine='InnoDB',
|
||||||
|
)
|
||||||
|
Index('tourneyId', tourneys_players_table.c.tourneyId, tourneys_players_table.c.playerId, unique=True)
|
||||||
|
|
||||||
|
|
||||||
|
def sss():
|
||||||
|
"Debug function. Returns (config, sql, db)"
|
||||||
|
|
||||||
|
import Configuration, SQL, Database, os
|
||||||
|
class Dummy(object):
|
||||||
|
pass
|
||||||
|
self = Dummy()
|
||||||
|
self.config = Configuration.Config()
|
||||||
|
self.settings = {}
|
||||||
|
if (os.sep=="/"):
|
||||||
|
self.settings['os']="linuxmac"
|
||||||
|
else:
|
||||||
|
self.settings['os']="windows"
|
||||||
|
|
||||||
|
self.settings.update(self.config.get_db_parameters())
|
||||||
|
self.settings.update(self.config.get_tv_parameters())
|
||||||
|
self.settings.update(self.config.get_import_parameters())
|
||||||
|
self.settings.update(self.config.get_default_paths())
|
||||||
|
|
||||||
|
self.sql = SQL.Sql( db_server = self.settings['db-server'])
|
||||||
|
self.db = Database.Database(self.config, sql = self.sql)
|
||||||
|
|
||||||
|
return self.config, self.sql, self.db
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
#!/usr/bin/env python
|
#!/usr/bin/env python
|
||||||
# Copyright 2008, Carl Gherardi
|
# -*- coding: utf-8 -*-
|
||||||
|
#
|
||||||
|
# Copyright 2010, Matthew Boss
|
||||||
#
|
#
|
||||||
# This program is free software; you can redistribute it and/or modify
|
# 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
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
@ -18,93 +19,286 @@
|
||||||
|
|
||||||
########################################################################
|
########################################################################
|
||||||
|
|
||||||
# Standard Library modules
|
# This code is based heavily on EverleafToFpdb.py, by Carl Gherardi
|
||||||
import Configuration
|
#
|
||||||
import traceback
|
# OUTSTANDING MATTERS
|
||||||
|
#
|
||||||
|
# -- No siteID assigned
|
||||||
|
# -- No support for games other than NL hold 'em cash. Hand histories for other
|
||||||
|
# games required
|
||||||
|
# -- No support for limit hold 'em yet, though this would be easy to add
|
||||||
|
# -- No support for tournaments (see also the last item below)
|
||||||
|
# -- Assumes that the currency of ring games is USD
|
||||||
|
# -- Only works for 'gametype="2"'. What is 'gametype'?
|
||||||
|
# -- Only accepts 'realmoney="true"'
|
||||||
|
# -- A hand's time-stamp does not record seconds past the minute (a
|
||||||
|
# limitation of the history format)
|
||||||
|
# -- No support for a bring-in or for antes (is the latter in fact unnecessary
|
||||||
|
# for hold 'em on Carbon?)
|
||||||
|
# -- hand.maxseats can only be guessed at
|
||||||
|
# -- The last hand in a history file will often be incomplete and is therefore
|
||||||
|
# rejected
|
||||||
|
# -- Is behaviour currently correct when someone shows an uncalled hand?
|
||||||
|
# -- Information may be lost when the hand ID is converted from the native form
|
||||||
|
# xxxxxxxx-yyy(y*) to xxxxxxxxyyy(y*) (in principle this should be stored as
|
||||||
|
# a string, but the database does not support this). Is there a possibility
|
||||||
|
# of collision between hand IDs that ought to be distinct?
|
||||||
|
# -- Cannot parse tables that run it twice (nor is this likely ever to be
|
||||||
|
# possible)
|
||||||
|
# -- Cannot parse hands in which someone is all in in one of the blinds. Until
|
||||||
|
# this is corrected tournaments will be unparseable
|
||||||
|
|
||||||
import sys
|
import sys
|
||||||
import re
|
import logging
|
||||||
import xml.dom.minidom
|
from HandHistoryConverter import *
|
||||||
from xml.dom.minidom import Node
|
from decimal import Decimal
|
||||||
from HandHistoryConverter import HandHistoryConverter
|
|
||||||
|
|
||||||
# Carbon format looks like:
|
class Carbon(HandHistoryConverter):
|
||||||
|
|
||||||
# 1) <description type="Holdem" stakes="No Limit ($0.25/$0.50)"/>
|
sitename = "Carbon"
|
||||||
# 2) <game id="14902583-5578" starttime="20081006145401" numholecards="2" gametype="2" realmoney="true" data="20081006|Niagara Falls (14902583)|14902583|14902583-5578|false">
|
filetype = "text"
|
||||||
# 3) <players dealer="8">
|
codepage = "cp1252"
|
||||||
# <player seat="3" nickname="PlayerInSeat3" balance="$43.29" dealtin="true" />
|
siteID = 11
|
||||||
# ...
|
|
||||||
# 4) <round id="BLINDS" sequence="1">
|
|
||||||
# <event sequence="1" type="SMALL_BLIND" player="0" amount="0.25"/>
|
|
||||||
# <event sequence="2" type="BIG_BLIND" player="1" amount="0.50"/>
|
|
||||||
# 5) <round id="PREFLOP" sequence="2">
|
|
||||||
# <event sequence="3" type="CALL" player="2" amount="0.50"/>
|
|
||||||
# 6) <round id="POSTFLOP" sequence="3">
|
|
||||||
# <event sequence="16" type="BET" player="3" amount="1.00"/>
|
|
||||||
# ....
|
|
||||||
# <cards type="COMMUNITY" cards="7d,Jd,Jh"/>
|
|
||||||
|
|
||||||
# The full sequence for a NHLE cash game is:
|
# Static regexes
|
||||||
# BLINDS, PREFLOP, POSTFLOP, POSTTURN, POSTRIVER, SHOWDOWN, END_OF_GAME
|
re_SplitHands = re.compile(r'</game>\n+(?=<game)')
|
||||||
# This sequence can be terminated after BLINDS at any time by END_OF_FOLDED_GAME
|
re_TailSplitHands = re.compile(r'(</game>)')
|
||||||
|
re_GameInfo = re.compile(r'<description type="(?P<GAME>[a-zA-Z ]+)" stakes="(?P<LIMIT>[a-zA-Z ]+) \(\$(?P<SB>[.0-9]+)/\$(?P<BB>[.0-9]+)\)"/>', re.MULTILINE)
|
||||||
|
re_HandInfo = re.compile(r'<game id="(?P<HID1>[0-9]+)-(?P<HID2>[0-9]+)" starttime="(?P<DATETIME>[0-9]+)" numholecards="2" gametype="2" realmoney="true" data="[0-9]+\|(?P<TABLE>[^\(]+)', re.MULTILINE)
|
||||||
|
re_Button = re.compile(r'<players dealer="(?P<BUTTON>[0-9]+)">')
|
||||||
|
re_PlayerInfo = re.compile(r'<player seat="(?P<SEAT>[0-9]+)" nickname="(?P<PNAME>.+)" balance="\$(?P<CASH>[.0-9]+)" dealtin="(?P<DEALTIN>(true|false))" />', re.MULTILINE)
|
||||||
|
re_Board = re.compile(r'<cards type="COMMUNITY" cards="(?P<CARDS>[^"]+)"', re.MULTILINE)
|
||||||
|
re_EndOfHand = re.compile(r'<round id="END_OF_GAME"', re.MULTILINE)
|
||||||
|
|
||||||
|
# The following are also static regexes: there is no need to call
|
||||||
|
# compilePlayerRegexes (which does nothing), since players are identified
|
||||||
|
# not by name but by seat number
|
||||||
|
re_PostSB = re.compile(r'<event sequence="[0-9]+" type="(SMALL_BLIND|RETURN_BLIND)" player="(?P<PSEAT>[0-9])" amount="(?P<SB>[.0-9]+)"/>', re.MULTILINE)
|
||||||
|
re_PostBB = re.compile(r'<event sequence="[0-9]+" type="(BIG_BLIND|INITIAL_BLIND)" player="(?P<PSEAT>[0-9])" amount="(?P<BB>[.0-9]+)"/>', re.MULTILINE)
|
||||||
|
re_PostBoth = re.compile(r'<event sequence="[0-9]+" type="(RETURN_BLIND)" player="(?P<PSEAT>[0-9])" amount="(?P<SBBB>[.0-9]+)"/>', re.MULTILINE)
|
||||||
|
#re_Antes = ???
|
||||||
|
#re_BringIn = ???
|
||||||
|
re_HeroCards = re.compile(r'<cards type="HOLE" cards="(?P<CARDS>.+)" player="(?P<PSEAT>[0-9])"', re.MULTILINE)
|
||||||
|
re_Action = re.compile(r'<event sequence="[0-9]+" type="(?P<ATYPE>FOLD|CHECK|CALL|BET|RAISE|ALL_IN|SIT_OUT)" player="(?P<PSEAT>[0-9])"( amount="(?P<BET>[.0-9]+)")?/>', re.MULTILINE)
|
||||||
|
re_ShowdownAction = re.compile(r'<cards type="SHOWN" cards="(?P<CARDS>..,..)" player="(?P<PSEAT>[0-9])"/>', re.MULTILINE)
|
||||||
|
re_CollectPot = re.compile(r'<winner amount="(?P<POT>[.0-9]+)" uncalled="(true|false)" potnumber="[0-9]+" player="(?P<PSEAT>[0-9])"', re.MULTILINE)
|
||||||
|
re_SitsOut = re.compile(r'<event sequence="[0-9]+" type="SIT_OUT" player="(?P<PSEAT>[0-9])"/>', re.MULTILINE)
|
||||||
|
re_ShownCards = re.compile(r'<cards type="(SHOWN|MUCKED)" cards="(?P<CARDS>..,..)" player="(?P<PSEAT>[0-9])"/>', re.MULTILINE)
|
||||||
|
|
||||||
class CarbonPoker(HandHistoryConverter):
|
def compilePlayerRegexs(self, hand):
|
||||||
def __init__(self, config, filename):
|
pass
|
||||||
print "Initialising Carbon Poker converter class"
|
|
||||||
HandHistoryConverter.__init__(self, config, filename, "Carbon") # Call super class init
|
def playerNameFromSeatNo(self, seatNo, hand):
|
||||||
self.setFileType("xml")
|
# This special function is required because Carbon Poker records
|
||||||
self.siteId = 4 # Needs to match id entry in Sites database
|
# actions by seat number, not by the player's name
|
||||||
|
for p in hand.players:
|
||||||
|
if p[0] == int(seatNo):
|
||||||
|
return p[1]
|
||||||
|
|
||||||
def readSupportedGames(self):
|
def readSupportedGames(self):
|
||||||
pass
|
return [["ring", "hold", "nl"],
|
||||||
def determineGameType(self):
|
["tour", "hold", "nl"]]
|
||||||
gametype = []
|
|
||||||
desc_node = self.doc.getElementsByTagName("description")
|
def determineGameType(self, handText):
|
||||||
#TODO: no examples of non ring type yet
|
"""return dict with keys/values:
|
||||||
gametype = gametype + ["ring"]
|
'type' in ('ring', 'tour')
|
||||||
type = desc_node[0].getAttribute("type")
|
'limitType' in ('nl', 'cn', 'pl', 'cp', 'fl')
|
||||||
if(type == "Holdem"):
|
'base' in ('hold', 'stud', 'draw')
|
||||||
gametype = gametype + ["hold"]
|
'category' in ('holdem', 'omahahi', omahahilo', 'razz', 'studhi', 'studhilo', 'fivedraw', '27_1draw', '27_3draw', 'badugi')
|
||||||
|
'hilo' in ('h','l','s')
|
||||||
|
'smallBlind' int?
|
||||||
|
'bigBlind' int?
|
||||||
|
'smallBet'
|
||||||
|
'bigBet'
|
||||||
|
'currency' in ('USD', 'EUR', 'T$', <countrycode>)
|
||||||
|
or None if we fail to get the info """
|
||||||
|
|
||||||
|
m = self.re_GameInfo.search(handText)
|
||||||
|
if not m:
|
||||||
|
# Information about the game type appears only at the beginning of
|
||||||
|
# a hand history file; hence it is not supplied with the second
|
||||||
|
# and subsequent hands. In these cases we use the value previously
|
||||||
|
# stored.
|
||||||
|
return self.info
|
||||||
|
self.info = {}
|
||||||
|
mg = m.groupdict()
|
||||||
|
|
||||||
|
limits = { 'No Limit':'nl', 'Limit':'fl' }
|
||||||
|
games = { # base, category
|
||||||
|
'Holdem' : ('hold','holdem'),
|
||||||
|
'Holdem Tournament' : ('hold','holdem') }
|
||||||
|
|
||||||
|
if 'LIMIT' in mg:
|
||||||
|
self.info['limitType'] = limits[mg['LIMIT']]
|
||||||
|
if 'GAME' in mg:
|
||||||
|
(self.info['base'], self.info['category']) = games[mg['GAME']]
|
||||||
|
if 'SB' in mg:
|
||||||
|
self.info['sb'] = mg['SB']
|
||||||
|
if 'BB' in mg:
|
||||||
|
self.info['bb'] = mg['BB']
|
||||||
|
if mg['GAME'] == 'Holdem Tournament':
|
||||||
|
self.info['type'] = 'tour'
|
||||||
|
self.info['currency'] = 'T$'
|
||||||
else:
|
else:
|
||||||
print "Carbon: Unknown gametype: '%s'" % (type)
|
self.info['type'] = 'ring'
|
||||||
|
self.info['currency'] = 'USD'
|
||||||
|
|
||||||
stakes = desc_node[0].getAttribute("stakes")
|
return self.info
|
||||||
#TODO: no examples of anything except nlhe
|
|
||||||
m = re.match('(?P<LIMIT>No Limit)\s\(\$?(?P<SB>[.0-9]+)/\$?(?P<BB>[.0-9]+)\)', stakes)
|
|
||||||
|
|
||||||
if(m.group('LIMIT') == "No Limit"):
|
def readHandInfo(self, hand):
|
||||||
gametype = gametype + ["nl"]
|
m = self.re_HandInfo.search(hand.handText)
|
||||||
|
if m is None:
|
||||||
|
logging.info("Didn't match re_HandInfo")
|
||||||
|
logging.info(hand.handText)
|
||||||
|
return None
|
||||||
|
logging.debug("HID %s-%s, Table %s" % (m.group('HID1'),
|
||||||
|
m.group('HID2'), m.group('TABLE')[:-1]))
|
||||||
|
hand.handid = m.group('HID1') + m.group('HID2')
|
||||||
|
hand.tablename = m.group('TABLE')[:-1]
|
||||||
|
hand.maxseats = 2 # This value may be increased as necessary
|
||||||
|
hand.starttime = datetime.datetime.strptime(m.group('DATETIME')[:12],
|
||||||
|
'%Y%m%d%H%M')
|
||||||
|
# Check that the hand is complete up to the awarding of the pot; if
|
||||||
|
# not, the hand is unparseable
|
||||||
|
if self.re_EndOfHand.search(hand.handText) is None:
|
||||||
|
raise FpdbParseError(hid=m.group('HID1') + "-" + m.group('HID2'))
|
||||||
|
|
||||||
gametype = gametype + [self.float2int(m.group('SB'))]
|
def readPlayerStacks(self, hand):
|
||||||
gametype = gametype + [self.float2int(m.group('BB'))]
|
m = self.re_PlayerInfo.finditer(hand.handText)
|
||||||
|
for a in m:
|
||||||
|
seatno = int(a.group('SEAT'))
|
||||||
|
# It may be necessary to adjust 'hand.maxseats', which is an
|
||||||
|
# educated guess, starting with 2 (indicating a heads-up table) and
|
||||||
|
# adjusted upwards in steps to 6, then 9, then 10. An adjustment is
|
||||||
|
# made whenever a player is discovered whose seat number is
|
||||||
|
# currently above the maximum allowable for the table.
|
||||||
|
if seatno >= hand.maxseats:
|
||||||
|
if seatno > 8:
|
||||||
|
hand.maxseats = 10
|
||||||
|
elif seatno > 5:
|
||||||
|
hand.maxseats = 9
|
||||||
|
else:
|
||||||
|
hand.maxseats = 6
|
||||||
|
if a.group('DEALTIN') == "true":
|
||||||
|
hand.addPlayer(seatno, a.group('PNAME'), a.group('CASH'))
|
||||||
|
|
||||||
return gametype
|
def markStreets(self, hand):
|
||||||
|
#if hand.gametype['base'] == 'hold':
|
||||||
|
m = re.search(r'<round id="PREFLOP" sequence="[0-9]+">(?P<PREFLOP>.+(?=<round id="POSTFLOP")|.+)(<round id="POSTFLOP" sequence="[0-9]+">(?P<FLOP>.+(?=<round id="POSTTURN")|.+))?(<round id="POSTTURN" sequence="[0-9]+">(?P<TURN>.+(?=<round id="POSTRIVER")|.+))?(<round id="POSTRIVER" sequence="[0-9]+">(?P<RIVER>.+))?', hand.handText, re.DOTALL)
|
||||||
|
hand.addStreets(m)
|
||||||
|
|
||||||
def readPlayerStacks(self):
|
def readCommunityCards(self, hand, street):
|
||||||
pass
|
m = self.re_Board.search(hand.streets[street])
|
||||||
def readBlinds(self):
|
if street == 'FLOP':
|
||||||
pass
|
hand.setCommunityCards(street, m.group('CARDS').split(','))
|
||||||
def readAction(self):
|
elif street in ('TURN','RIVER'):
|
||||||
pass
|
hand.setCommunityCards(street, [m.group('CARDS').split(',')[-1]])
|
||||||
|
|
||||||
# Override read function as xml.minidom barfs on the Carbon layout
|
def readAntes(self, hand):
|
||||||
# This is pretty dodgy
|
pass # ???
|
||||||
def readFile(self, filename):
|
|
||||||
print "Carbon: Reading file: '%s'" %(filename)
|
def readBringIn(self, hand):
|
||||||
infile=open(filename, "rU")
|
pass # ???
|
||||||
self.obs = infile.read()
|
|
||||||
infile.close()
|
def readBlinds(self, hand):
|
||||||
self.obs = "<CarbonHHFile>\n" + self.obs + "</CarbonHHFile>"
|
|
||||||
try:
|
try:
|
||||||
doc = xml.dom.minidom.parseString(self.obs)
|
m = self.re_PostSB.search(hand.handText)
|
||||||
self.doc = doc
|
hand.addBlind(self.playerNameFromSeatNo(m.group('PSEAT'), hand),
|
||||||
except:
|
'small blind', m.group('SB'))
|
||||||
traceback.print_exc(file=sys.stderr)
|
except: # no small blind
|
||||||
|
hand.addBlind(None, None, None)
|
||||||
|
for a in self.re_PostBB.finditer(hand.handText):
|
||||||
|
hand.addBlind(self.playerNameFromSeatNo(a.group('PSEAT'), hand),
|
||||||
|
'big blind', a.group('BB'))
|
||||||
|
for a in self.re_PostBoth.finditer(hand.handText):
|
||||||
|
bb = Decimal(self.info['bb'])
|
||||||
|
amount = Decimal(a.group('SBBB'))
|
||||||
|
if amount < bb:
|
||||||
|
hand.addBlind(self.playerNameFromSeatNo(a.group('PSEAT'),
|
||||||
|
hand), 'small blind', a.group('SBBB'))
|
||||||
|
elif amount == bb:
|
||||||
|
hand.addBlind(self.playerNameFromSeatNo(a.group('PSEAT'),
|
||||||
|
hand), 'big blind', a.group('SBBB'))
|
||||||
|
else:
|
||||||
|
hand.addBlind(self.playerNameFromSeatNo(a.group('PSEAT'),
|
||||||
|
hand), 'both', a.group('SBBB'))
|
||||||
|
|
||||||
|
def readButton(self, hand):
|
||||||
|
hand.buttonpos = int(self.re_Button.search(hand.handText).group('BUTTON'))
|
||||||
|
|
||||||
|
def readHeroCards(self, hand):
|
||||||
|
m = self.re_HeroCards.search(hand.handText)
|
||||||
|
if m:
|
||||||
|
hand.hero = self.playerNameFromSeatNo(m.group('PSEAT'), hand)
|
||||||
|
cards = m.group('CARDS').split(',')
|
||||||
|
hand.addHoleCards('PREFLOP', hand.hero, closed=cards, shown=False,
|
||||||
|
mucked=False, dealt=True)
|
||||||
|
|
||||||
|
def readAction(self, hand, street):
|
||||||
|
logging.debug("readAction (%s)" % street)
|
||||||
|
m = self.re_Action.finditer(hand.streets[street])
|
||||||
|
for action in m:
|
||||||
|
logging.debug("%s %s" % (action.group('ATYPE'),
|
||||||
|
action.groupdict()))
|
||||||
|
player = self.playerNameFromSeatNo(action.group('PSEAT'), hand)
|
||||||
|
if action.group('ATYPE') == 'RAISE':
|
||||||
|
hand.addCallandRaise(street, player, action.group('BET'))
|
||||||
|
elif action.group('ATYPE') == 'CALL':
|
||||||
|
hand.addCall(street, player, action.group('BET'))
|
||||||
|
elif action.group('ATYPE') == 'BET':
|
||||||
|
hand.addBet(street, player, action.group('BET'))
|
||||||
|
elif action.group('ATYPE') in ('FOLD', 'SIT_OUT'):
|
||||||
|
hand.addFold(street, player)
|
||||||
|
elif action.group('ATYPE') == 'CHECK':
|
||||||
|
hand.addCheck(street, player)
|
||||||
|
elif action.group('ATYPE') == 'ALL_IN':
|
||||||
|
hand.addAllIn(street, player, action.group('BET'))
|
||||||
|
else:
|
||||||
|
logging.debug("Unimplemented readAction: %s %s"
|
||||||
|
% (action.group('PSEAT'),action.group('ATYPE'),))
|
||||||
|
|
||||||
|
def readShowdownActions(self, hand):
|
||||||
|
for shows in self.re_ShowdownAction.finditer(hand.handText):
|
||||||
|
cards = shows.group('CARDS').split(',')
|
||||||
|
hand.addShownCards(cards,
|
||||||
|
self.playerNameFromSeatNo(shows.group('PSEAT'),
|
||||||
|
hand))
|
||||||
|
|
||||||
|
def readCollectPot(self, hand):
|
||||||
|
pots = [Decimal(0) for n in range(hand.maxseats)]
|
||||||
|
for m in self.re_CollectPot.finditer(hand.handText):
|
||||||
|
pots[int(m.group('PSEAT'))] += Decimal(m.group('POT'))
|
||||||
|
# Regarding the processing logic for "committed", see Pot.end() in
|
||||||
|
# Hand.py
|
||||||
|
committed = sorted([(v,k) for (k,v) in hand.pot.committed.items()])
|
||||||
|
for p in range(hand.maxseats):
|
||||||
|
pname = self.playerNameFromSeatNo(p, hand)
|
||||||
|
if committed[-1][1] == pname:
|
||||||
|
pots[p] -= committed[-1][0] - committed[-2][0]
|
||||||
|
if pots[p] > 0:
|
||||||
|
hand.addCollectPot(player=pname, pot=pots[p])
|
||||||
|
|
||||||
|
def readShownCards(self, hand):
|
||||||
|
for m in self.re_ShownCards.finditer(hand.handText):
|
||||||
|
cards = m.group('CARDS').split(',')
|
||||||
|
hand.addShownCards(cards=cards, player=self.playerNameFromSeatNo(m.group('PSEAT'), hand))
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
c = Configuration.Config()
|
parser = OptionParser()
|
||||||
e = CarbonPoker(c, "regression-test-files/carbon-poker/Niagara Falls (15245216).xml")
|
parser.add_option("-i", "--input", dest="ipath", help="parse input hand history", default="-")
|
||||||
e.processFile()
|
parser.add_option("-o", "--output", dest="opath", help="output translation to", default="-")
|
||||||
print str(e)
|
parser.add_option("-f", "--follow", dest="follow", help="follow (tail -f) the input", action="store_true", default=False)
|
||||||
|
parser.add_option("-q", "--quiet", action="store_const", const=logging.CRITICAL, dest="verbosity", default=logging.INFO)
|
||||||
|
parser.add_option("-v", "--verbose", action="store_const", const=logging.INFO, dest="verbosity")
|
||||||
|
parser.add_option("--vv", action="store_const", const=logging.DEBUG, dest="verbosity")
|
||||||
|
|
||||||
|
(options, args) = parser.parse_args()
|
||||||
|
|
||||||
|
LOG_FILENAME = './logging.out'
|
||||||
|
logging.basicConfig(filename=LOG_FILENAME, level=options.verbosity)
|
||||||
|
|
||||||
|
e = Carbon(in_path = options.ipath,
|
||||||
|
out_path = options.opath,
|
||||||
|
follow = options.follow,
|
||||||
|
autostart = True)
|
||||||
|
|
||||||
|
|
|
@ -16,27 +16,59 @@
|
||||||
#agpl-3.0.txt in the docs folder of the package.
|
#agpl-3.0.txt in the docs folder of the package.
|
||||||
|
|
||||||
|
|
||||||
|
# From fpdb_simple
|
||||||
|
card_map = { "0": 0, "2": 2, "3" : 3, "4" : 4, "5" : 5, "6" : 6, "7" : 7, "8" : 8,
|
||||||
|
"9" : 9, "T" : 10, "J" : 11, "Q" : 12, "K" : 13, "A" : 14}
|
||||||
|
|
||||||
|
# FIXME: the following is a workaround until switching to newimport.
|
||||||
|
# This should be moved into DerivedStats
|
||||||
|
# I'd also like to change HandsPlayers.startCards to a different datatype
|
||||||
|
# so we can 'trivially' add different start card classifications
|
||||||
|
|
||||||
|
def calcStartCards(hand, player):
|
||||||
|
if hand.gametype['category'] == 'holdem':
|
||||||
|
hcs = hand.join_holecards(player, asList=True)
|
||||||
|
#print "DEBUG: hcs: %s" % hcs
|
||||||
|
value1 = card_map[hcs[0][0]]
|
||||||
|
value2 = card_map[hcs[1][0]]
|
||||||
|
return twoStartCards(value1, hcs[0][1], value2, hcs[1][1])
|
||||||
|
else:
|
||||||
|
# FIXME: Only do startCards value for holdem at the moment
|
||||||
|
return 0
|
||||||
|
|
||||||
|
|
||||||
def twoStartCards(value1, suit1, value2, suit2):
|
def twoStartCards(value1, suit1, value2, suit2):
|
||||||
""" Function to convert 2 value,suit pairs into a Holdem style starting hand e.g. AQo
|
""" Function to convert 2 value,suit pairs into a Holdem style starting hand e.g. AQo
|
||||||
Hand is stored as an int 13 * x + y where (x+2) represents rank of 1st card and
|
Incoming values should be ints 2-14 (2,3,...K,A), suits are 'd'/'h'/'c'/'s'
|
||||||
|
Hand is stored as an int 13 * x + y + 1 where (x+2) represents rank of 1st card and
|
||||||
(y+2) represents rank of second card (2=2 .. 14=Ace)
|
(y+2) represents rank of second card (2=2 .. 14=Ace)
|
||||||
If x > y then pair is suited, if x < y then unsuited"""
|
If x > y then pair is suited, if x < y then unsuited
|
||||||
if value1 < 2 or value2 < 2:
|
Examples:
|
||||||
|
0 Unknown / Illegal cards
|
||||||
|
1 22
|
||||||
|
2 32o
|
||||||
|
3 42o
|
||||||
|
...
|
||||||
|
14 32s
|
||||||
|
15 33
|
||||||
|
16 42o
|
||||||
|
...
|
||||||
|
170 AA
|
||||||
|
"""
|
||||||
|
if value1 is None or value1 < 2 or value1 > 14 or value2 is None or value2 < 2 or value2 > 14:
|
||||||
ret = 0
|
ret = 0
|
||||||
if value1 == value2: # pairs
|
elif value1 == value2: # pairs
|
||||||
ret = (13 * (value2-2) + (value2-2) )
|
ret = (13 * (value2-2) + (value2-2) ) + 1
|
||||||
elif suit1 == suit2:
|
elif suit1 == suit2:
|
||||||
if value1 > value2:
|
if value1 > value2:
|
||||||
ret = 13 * (value1-2) + (value2-2)
|
ret = 13 * (value1-2) + (value2-2) + 1
|
||||||
else:
|
else:
|
||||||
ret = 13 * (value2-2) + (value1-2)
|
ret = 13 * (value2-2) + (value1-2) + 1
|
||||||
else:
|
else:
|
||||||
if value1 > value2:
|
if value1 > value2:
|
||||||
ret = 13 * (value2-2) + (value1-2)
|
ret = 13 * (value2-2) + (value1-2) + 1
|
||||||
else:
|
else:
|
||||||
ret = 13 * (value1-2) + (value2-2)
|
ret = 13 * (value1-2) + (value2-2) + 1
|
||||||
|
|
||||||
# print "twoStartCards(", value1, suit1, value2, suit2, ")=", ret
|
# print "twoStartCards(", value1, suit1, value2, suit2, ")=", ret
|
||||||
return ret
|
return ret
|
||||||
|
@ -47,8 +79,8 @@ def twoStartCardString(card):
|
||||||
ret = 'xx'
|
ret = 'xx'
|
||||||
if card > 0:
|
if card > 0:
|
||||||
s = ('2', '3', '4', '5', '6', '7', '8', '9', 'T', 'J', 'Q', 'K', 'A')
|
s = ('2', '3', '4', '5', '6', '7', '8', '9', 'T', 'J', 'Q', 'K', 'A')
|
||||||
x = card / 13
|
x = (card-1) / 13
|
||||||
y = card - 13 * x
|
y = (card-1) - 13 * x
|
||||||
if x == y: ret = s[x] + s[y]
|
if x == y: ret = s[x] + s[y]
|
||||||
elif x > y: ret = s[x] + s[y] + 's'
|
elif x > y: ret = s[x] + s[y] + 's'
|
||||||
else: ret = s[y] + s[x] + 'o'
|
else: ret = s[y] + s[x] + 'o'
|
||||||
|
@ -109,7 +141,7 @@ def valueSuitFromCard(card):
|
||||||
|
|
||||||
encodeCardList = {'2h': 1, '3h': 2, '4h': 3, '5h': 4, '6h': 5, '7h': 6, '8h': 7, '9h': 8, 'Th': 9, 'Jh': 10, 'Qh': 11, 'Kh': 12, 'Ah': 13,
|
encodeCardList = {'2h': 1, '3h': 2, '4h': 3, '5h': 4, '6h': 5, '7h': 6, '8h': 7, '9h': 8, 'Th': 9, 'Jh': 10, 'Qh': 11, 'Kh': 12, 'Ah': 13,
|
||||||
'2d': 14, '3d': 15, '4d': 16, '5d': 17, '6d': 18, '7d': 19, '8d': 20, '9d': 21, 'Td': 22, 'Jd': 23, 'Qd': 24, 'Kd': 25, 'Ad': 26,
|
'2d': 14, '3d': 15, '4d': 16, '5d': 17, '6d': 18, '7d': 19, '8d': 20, '9d': 21, 'Td': 22, 'Jd': 23, 'Qd': 24, 'Kd': 25, 'Ad': 26,
|
||||||
'2c': 27, '3c': 28, '4c': 29, '5c': 30, '6c': 31, '7c': 32, '8c': 33, '9c': 34, 'Tc': 35, 'Jc': 36, 'Qc': 27, 'Kc': 38, 'Ac': 39,
|
'2c': 27, '3c': 28, '4c': 29, '5c': 30, '6c': 31, '7c': 32, '8c': 33, '9c': 34, 'Tc': 35, 'Jc': 36, 'Qc': 37, 'Kc': 38, 'Ac': 39,
|
||||||
'2s': 40, '3s': 41, '4s': 42, '5s': 43, '6s': 44, '7s': 45, '8s': 46, '9s': 47, 'Ts': 48, 'Js': 49, 'Qs': 50, 'Ks': 51, 'As': 52,
|
'2s': 40, '3s': 41, '4s': 42, '5s': 43, '6s': 44, '7s': 45, '8s': 46, '9s': 47, 'Ts': 48, 'Js': 49, 'Qs': 50, 'Ks': 51, 'As': 52,
|
||||||
' ': 0
|
' ': 0
|
||||||
}
|
}
|
||||||
|
|
77
pyfpdb/Charset.py
Normal file
77
pyfpdb/Charset.py
Normal file
|
@ -0,0 +1,77 @@
|
||||||
|
#!/usr/bin/python
|
||||||
|
|
||||||
|
#Copyright 2010 Mika Bostrom
|
||||||
|
#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 <http://www.gnu.org/licenses/>.
|
||||||
|
#In the "official" distribution you can find the license in
|
||||||
|
#agpl-3.0.txt in the docs folder of the package.
|
||||||
|
|
||||||
|
# Error logging
|
||||||
|
import sys
|
||||||
|
|
||||||
|
# String manipulation
|
||||||
|
import codecs
|
||||||
|
|
||||||
|
# Settings
|
||||||
|
import Configuration
|
||||||
|
|
||||||
|
encoder_to_utf = codecs.lookup('utf-8')
|
||||||
|
encoder_to_sys = codecs.lookup(Configuration.LOCALE_ENCODING)
|
||||||
|
|
||||||
|
# I'm saving a few cycles with this one
|
||||||
|
not_needed1, not_needed2, not_needed3 = False, False, False
|
||||||
|
if Configuration.LOCALE_ENCODING == 'UTF8':
|
||||||
|
not_needed1, not_needed2, not_needed3 = True, True, True
|
||||||
|
|
||||||
|
def to_utf8(s):
|
||||||
|
if not_needed1: return s
|
||||||
|
|
||||||
|
try:
|
||||||
|
#(_out, _len) = encoder_to_utf.encode(s)
|
||||||
|
_out = unicode(s, Configuration.LOCALE_ENCODING).encode('utf-8')
|
||||||
|
return _out
|
||||||
|
except UnicodeDecodeError:
|
||||||
|
sys.stderr.write('Could not convert: "%s"\n' % s)
|
||||||
|
raise
|
||||||
|
except UnicodeEncodeError:
|
||||||
|
sys.stderr.write('Could not encode: "%s"\n' % s)
|
||||||
|
raise
|
||||||
|
except TypeError: # TypeError is raised when we give unicode() an already encoded string
|
||||||
|
return s
|
||||||
|
|
||||||
|
def to_db_utf8(s):
|
||||||
|
if not_needed2: return s
|
||||||
|
|
||||||
|
try:
|
||||||
|
(_out, _len) = encoder_to_utf.encode(unicode(s))
|
||||||
|
return _out
|
||||||
|
except UnicodeDecodeError:
|
||||||
|
sys.stderr.write('Could not convert: "%s"\n' % s)
|
||||||
|
raise
|
||||||
|
except UnicodeEncodeError:
|
||||||
|
sys.stderr.write('Could not encode: "%s"\n' % s)
|
||||||
|
raise
|
||||||
|
|
||||||
|
def to_gui(s):
|
||||||
|
if not_needed3: return s
|
||||||
|
|
||||||
|
try:
|
||||||
|
# we usually don't want to use 'replace' but this is only for displaying
|
||||||
|
# in the gui so it doesn't matter if names are missing an accent or two
|
||||||
|
(_out, _len) = encoder_to_sys.encode(s, 'replace')
|
||||||
|
return _out
|
||||||
|
except UnicodeDecodeError:
|
||||||
|
sys.stderr.write('Could not convert: "%s"\n' % s)
|
||||||
|
raise
|
||||||
|
except UnicodeEncodeError:
|
||||||
|
sys.stderr.write('Could not encode: "%s"\n' % s)
|
||||||
|
raise
|
|
@ -1,66 +0,0 @@
|
||||||
#!/usr/bin/python
|
|
||||||
|
|
||||||
#Copyright 2008 Steffen Jobbagy-Felso
|
|
||||||
#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 <http://www.gnu.org/licenses/>.
|
|
||||||
#In the "official" distribution you can find the license in
|
|
||||||
#agpl-3.0.txt in the docs folder of the package.
|
|
||||||
|
|
||||||
import os
|
|
||||||
import sys
|
|
||||||
import fpdb_simple
|
|
||||||
from optparse import OptionParser
|
|
||||||
|
|
||||||
try:
|
|
||||||
import MySQLdb
|
|
||||||
except:
|
|
||||||
diaSQLLibMissing = gtk.Dialog(title="Fatal Error - SQL interface library missing", parent=None, flags=0, buttons=(gtk.STOCK_QUIT,gtk.RESPONSE_OK))
|
|
||||||
|
|
||||||
print "Please note that the CLI importer only works with MySQL, if you use PostgreSQL this error is expected."
|
|
||||||
|
|
||||||
import fpdb_import
|
|
||||||
import fpdb_db
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
#process CLI parameters
|
|
||||||
parser = OptionParser()
|
|
||||||
parser.add_option("-c", "--handCount", default="0", type="int",
|
|
||||||
help="Number of hands to import (default 0 means unlimited)")
|
|
||||||
parser.add_option("-d", "--database", default="fpdb", help="The MySQL database to use (default fpdb)")
|
|
||||||
parser.add_option("-e", "--errorFile", default="failed.txt",
|
|
||||||
help="File to store failed hands into. (default: failed.txt) Not implemented.")
|
|
||||||
parser.add_option("-f", "--inputFile", "--file", "--inputfile", default="stdin",
|
|
||||||
help="The file you want to import (remember to use quotes if necessary)")
|
|
||||||
parser.add_option("-m", "--minPrint", "--status", default="50", type="int",
|
|
||||||
help="How often to print a one-line status report (0 means never, default is 50)")
|
|
||||||
parser.add_option("-p", "--password", help="The password for the MySQL user")
|
|
||||||
parser.add_option("-q", "--quiet", action="store_true",
|
|
||||||
help="If this is passed it doesn't print a total at the end nor the opening line. Note that this purposely does NOT change --minPrint")
|
|
||||||
parser.add_option("-s", "--server", default="localhost",
|
|
||||||
help="Hostname/IP of the MySQL server (default localhost)")
|
|
||||||
parser.add_option("-u", "--user", default="fpdb", help="The MySQL username (default fpdb)")
|
|
||||||
parser.add_option("-x", "--failOnError", action="store_true",
|
|
||||||
help="If this option is passed it quits when it encounters any error")
|
|
||||||
|
|
||||||
(options, argv) = parser.parse_args()
|
|
||||||
|
|
||||||
settings={'callFpdbHud':False, 'db-backend':2}
|
|
||||||
settings['db-host']=options.server
|
|
||||||
settings['db-user']=options.user
|
|
||||||
settings['db-password']=options.password
|
|
||||||
settings['db-databaseName']=options.database
|
|
||||||
settings['handCount']=options.handCount
|
|
||||||
settings['failOnError']=options.failOnError
|
|
||||||
|
|
||||||
importer = fpdb_import.Importer(options, settings)
|
|
||||||
importer.addImportFile(options.inputFile)
|
|
||||||
importer.runImport()
|
|
|
@ -31,12 +31,17 @@ import inspect
|
||||||
import string
|
import string
|
||||||
import traceback
|
import traceback
|
||||||
import shutil
|
import shutil
|
||||||
|
import locale
|
||||||
import xml.dom.minidom
|
import xml.dom.minidom
|
||||||
from xml.dom.minidom import Node
|
from xml.dom.minidom import Node
|
||||||
|
|
||||||
import logging, logging.config
|
import logging, logging.config
|
||||||
import ConfigParser
|
import ConfigParser
|
||||||
|
|
||||||
|
# logging has been set up in fpdb.py or HUD_main.py, use their settings:
|
||||||
|
log = logging.getLogger("config")
|
||||||
|
|
||||||
|
|
||||||
##############################################################################
|
##############################################################################
|
||||||
# Functions for finding config files and setting up logging
|
# Functions for finding config files and setting up logging
|
||||||
# Also used in other modules that use logging.
|
# Also used in other modules that use logging.
|
||||||
|
@ -51,60 +56,100 @@ def get_default_config_path():
|
||||||
return config_path
|
return config_path
|
||||||
|
|
||||||
def get_exec_path():
|
def get_exec_path():
|
||||||
"""Returns the path to the fpdb.(py|exe) file we are executing"""
|
"""Returns the path to the fpdb(dir|.exe) file we are executing"""
|
||||||
if hasattr(sys, "frozen"): # compiled by py2exe
|
if hasattr(sys, "frozen"): # compiled by py2exe
|
||||||
return os.path.dirname(sys.executable)
|
return os.path.dirname(sys.executable)
|
||||||
else:
|
else:
|
||||||
pathname = os.path.dirname(sys.argv[0])
|
return os.path.dirname(sys.path[0]) # should be path to /fpdb
|
||||||
return os.path.abspath(pathname)
|
|
||||||
|
|
||||||
def get_config(file_name, fallback = True):
|
def get_config(file_name, fallback = True):
|
||||||
"""Looks in cwd and in self.default_config_path for a config file."""
|
"""Looks in cwd and in self.default_config_path for a config file."""
|
||||||
config_path = os.path.join(get_exec_path(), file_name)
|
exec_dir = get_exec_path()
|
||||||
|
if file_name == 'logging.conf' and not hasattr(sys, "frozen"):
|
||||||
|
config_path = os.path.join(exec_dir, 'pyfpdb', file_name)
|
||||||
|
else:
|
||||||
|
config_path = os.path.join(exec_dir, file_name)
|
||||||
# print "config_path=", config_path
|
# print "config_path=", config_path
|
||||||
if os.path.exists(config_path): # there is a file in the cwd
|
if os.path.exists(config_path): # there is a file in the cwd
|
||||||
return config_path # so we use it
|
return (config_path,False) # so we use it
|
||||||
else: # no file in the cwd, look where it should be in the first place
|
else: # no file in the cwd, look where it should be in the first place
|
||||||
config_path = os.path.join(get_default_config_path(), file_name)
|
default_dir = get_default_config_path()
|
||||||
|
config_path = os.path.join(default_dir, file_name)
|
||||||
# print "config path 2=", config_path
|
# print "config path 2=", config_path
|
||||||
if os.path.exists(config_path):
|
if os.path.exists(config_path):
|
||||||
return config_path
|
return (config_path,False)
|
||||||
|
|
||||||
# No file found
|
# No file found
|
||||||
if not fallback:
|
if not fallback:
|
||||||
return False
|
return (False,False)
|
||||||
|
|
||||||
# OK, fall back to the .example file, should be in the start dir
|
# OK, fall back to the .example file, should be in the start dir
|
||||||
if os.path.exists(file_name + ".example"):
|
if os.path.exists(file_name + ".example"):
|
||||||
try:
|
try:
|
||||||
shutil.copyfile(file_name + ".example", file_name)
|
print ""
|
||||||
print "No %s found, using %s.example.\n" % (file_name, file_name)
|
check_dir(default_dir)
|
||||||
print "A %s file has been created. You will probably have to edit it." % file_name
|
shutil.copyfile(file_name + ".example", config_path)
|
||||||
sys.stderr.write("No %s found, using %s.example.\n" % (file_name, file_name) )
|
msg = "No %s found\n in %s\n or %s\n" % (file_name, exec_dir, default_dir) \
|
||||||
|
+ "Config file has been created at %s.\n" % config_path
|
||||||
|
print msg
|
||||||
|
logging.info(msg)
|
||||||
|
file_name = config_path
|
||||||
except:
|
except:
|
||||||
|
print "Error copying .example file, cannot fall back. Exiting.\n"
|
||||||
|
sys.stderr.write("Error copying .example file, cannot fall back. Exiting.\n")
|
||||||
|
sys.stderr.write( str(sys.exc_info()) )
|
||||||
|
sys.exit()
|
||||||
|
else:
|
||||||
print "No %s found, cannot fall back. Exiting.\n" % file_name
|
print "No %s found, cannot fall back. Exiting.\n" % file_name
|
||||||
sys.stderr.write("No %s found, cannot fall back. Exiting.\n" % file_name)
|
sys.stderr.write("No %s found, cannot fall back. Exiting.\n" % file_name)
|
||||||
sys.exit()
|
sys.exit()
|
||||||
return file_name
|
return (file_name,True)
|
||||||
|
|
||||||
def get_logger(file_name, config = "config", fallback = False):
|
def get_logger(file_name, config = "config", fallback = False, log_dir=None, log_file=None):
|
||||||
conf = get_config(file_name, fallback = fallback)
|
(conf_file,copied) = get_config(file_name, fallback = fallback)
|
||||||
if conf:
|
|
||||||
|
if log_dir is None:
|
||||||
|
log_dir = os.path.join(get_exec_path(), 'log')
|
||||||
|
#print "\nget_logger: checking log_dir:", log_dir
|
||||||
|
check_dir(log_dir)
|
||||||
|
if log_file is None:
|
||||||
|
file = os.path.join(log_dir, 'fpdb-log.txt')
|
||||||
|
else:
|
||||||
|
file = os.path.join(log_dir, log_file)
|
||||||
|
|
||||||
|
if conf_file:
|
||||||
try:
|
try:
|
||||||
logging.config.fileConfig(conf)
|
file = file.replace('\\', '\\\\') # replace each \ with \\
|
||||||
|
# print " ="+file+" "+ str(type(file))+" len="+str(len(file))+"\n"
|
||||||
|
logging.config.fileConfig(conf_file, {"logFile":file})
|
||||||
log = logging.getLogger(config)
|
log = logging.getLogger(config)
|
||||||
log.debug("%s logger initialised" % config)
|
log.debug("%s logger initialised" % config)
|
||||||
return log
|
return log
|
||||||
except:
|
except:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
log = logging.basicConfig()
|
log = logging.basicConfig(filename=file, level=logging.INFO)
|
||||||
log = logging.getLogger()
|
log = logging.getLogger()
|
||||||
log.debug("config logger initialised")
|
# but it looks like default is no output :-( maybe because all the calls name a module?
|
||||||
|
log.debug("Default logger initialised for "+file)
|
||||||
|
print "Default logger intialised for "+file
|
||||||
return log
|
return log
|
||||||
|
|
||||||
# find a logging.conf file and set up logging
|
def check_dir(path, create = True):
|
||||||
log = get_logger("logging.conf")
|
"""Check if a dir exists, optionally creates if not."""
|
||||||
|
if os.path.exists(path):
|
||||||
|
if os.path.isdir(path):
|
||||||
|
return path
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
if create:
|
||||||
|
msg = "Creating directory: '%s'" % (path)
|
||||||
|
print msg
|
||||||
|
log.info(msg)
|
||||||
|
os.mkdir(path)
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
########################################################################
|
########################################################################
|
||||||
# application wide consts
|
# application wide consts
|
||||||
|
@ -112,10 +157,6 @@ log = get_logger("logging.conf")
|
||||||
APPLICATION_NAME_SHORT = 'fpdb'
|
APPLICATION_NAME_SHORT = 'fpdb'
|
||||||
APPLICATION_VERSION = 'xx.xx.xx'
|
APPLICATION_VERSION = 'xx.xx.xx'
|
||||||
|
|
||||||
DIR_SELF = os.path.dirname(get_exec_path())
|
|
||||||
#TODO: imo no good idea to place 'database' in parent dir
|
|
||||||
DIR_DATABASES = os.path.join(os.path.dirname(DIR_SELF), 'database')
|
|
||||||
|
|
||||||
DATABASE_TYPE_POSTGRESQL = 'postgresql'
|
DATABASE_TYPE_POSTGRESQL = 'postgresql'
|
||||||
DATABASE_TYPE_SQLITE = 'sqlite'
|
DATABASE_TYPE_SQLITE = 'sqlite'
|
||||||
DATABASE_TYPE_MYSQL = 'mysql'
|
DATABASE_TYPE_MYSQL = 'mysql'
|
||||||
|
@ -125,7 +166,20 @@ DATABASE_TYPES = (
|
||||||
DATABASE_TYPE_MYSQL,
|
DATABASE_TYPE_MYSQL,
|
||||||
)
|
)
|
||||||
|
|
||||||
NEWIMPORT = False
|
#LOCALE_ENCODING = locale.getdefaultlocale()[1]
|
||||||
|
LOCALE_ENCODING = locale.getpreferredencoding()
|
||||||
|
if LOCALE_ENCODING == "US-ASCII":
|
||||||
|
print "Default encoding set to US-ASCII, defaulting to CP1252 instead -- If you're not on a Mac, please report this problem."
|
||||||
|
LOCALE_ENCODING = "cp1252"
|
||||||
|
|
||||||
|
|
||||||
|
# needs LOCALE_ENCODING (above), imported for sqlite setup in Config class below
|
||||||
|
|
||||||
|
FROZEN = hasattr(sys, "frozen")
|
||||||
|
EXEC_PATH = get_exec_path()
|
||||||
|
|
||||||
|
import Charset
|
||||||
|
|
||||||
|
|
||||||
########################################################################
|
########################################################################
|
||||||
def string_to_bool(string, default=True):
|
def string_to_bool(string, default=True):
|
||||||
|
@ -267,6 +321,10 @@ class Game:
|
||||||
stat.hudprefix = stat_node.getAttribute("hudprefix")
|
stat.hudprefix = stat_node.getAttribute("hudprefix")
|
||||||
stat.hudsuffix = stat_node.getAttribute("hudsuffix")
|
stat.hudsuffix = stat_node.getAttribute("hudsuffix")
|
||||||
stat.hudcolor = stat_node.getAttribute("hudcolor")
|
stat.hudcolor = stat_node.getAttribute("hudcolor")
|
||||||
|
stat.stat_loth = stat_node.getAttribute("stat_loth")
|
||||||
|
stat.stat_hith = stat_node.getAttribute("stat_hith")
|
||||||
|
stat.stat_locolor = stat_node.getAttribute("stat_locolor")
|
||||||
|
stat.stat_hicolor = stat_node.getAttribute("stat_hicolor")
|
||||||
|
|
||||||
self.stats[stat.stat_name] = stat
|
self.stats[stat.stat_name] = stat
|
||||||
|
|
||||||
|
@ -356,6 +414,7 @@ class Import:
|
||||||
self.hhArchiveBase = node.getAttribute("hhArchiveBase")
|
self.hhArchiveBase = node.getAttribute("hhArchiveBase")
|
||||||
self.saveActions = string_to_bool(node.getAttribute("saveActions"), default=True)
|
self.saveActions = string_to_bool(node.getAttribute("saveActions"), default=True)
|
||||||
self.fastStoreHudCache = string_to_bool(node.getAttribute("fastStoreHudCache"), default=False)
|
self.fastStoreHudCache = string_to_bool(node.getAttribute("fastStoreHudCache"), default=False)
|
||||||
|
self.saveStarsHH = string_to_bool(node.getAttribute("saveStarsHH"), default=False)
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return " interval = %s\n callFpdbHud = %s\n hhArchiveBase = %s\n saveActions = %s\n fastStoreHudCache = %s\n" \
|
return " interval = %s\n callFpdbHud = %s\n hhArchiveBase = %s\n saveActions = %s\n fastStoreHudCache = %s\n" \
|
||||||
|
@ -395,11 +454,11 @@ class Tv:
|
||||||
|
|
||||||
class Config:
|
class Config:
|
||||||
def __init__(self, file = None, dbname = ''):
|
def __init__(self, file = None, dbname = ''):
|
||||||
|
|
||||||
# "file" is a path to an xml file with the fpdb/HUD configuration
|
# "file" is a path to an xml file with the fpdb/HUD configuration
|
||||||
# we check the existence of "file" and try to recover if it doesn't exist
|
# we check the existence of "file" and try to recover if it doesn't exist
|
||||||
|
|
||||||
# self.default_config_path = self.get_default_config_path()
|
# self.default_config_path = self.get_default_config_path()
|
||||||
|
self.example_copy = False
|
||||||
if file is not None: # config file path passed in
|
if file is not None: # config file path passed in
|
||||||
file = os.path.expanduser(file)
|
file = os.path.expanduser(file)
|
||||||
if not os.path.exists(file):
|
if not os.path.exists(file):
|
||||||
|
@ -407,7 +466,16 @@ class Config:
|
||||||
sys.stderr.write("Configuration file %s not found. Using defaults." % (file))
|
sys.stderr.write("Configuration file %s not found. Using defaults." % (file))
|
||||||
file = None
|
file = None
|
||||||
|
|
||||||
if file is None: file = get_config("HUD_config.xml")
|
if file is None: (file,self.example_copy) = get_config("HUD_config.xml", True)
|
||||||
|
|
||||||
|
self.file = file
|
||||||
|
self.dir_self = get_exec_path()
|
||||||
|
# self.dir_config = os.path.dirname(self.file)
|
||||||
|
self.dir_config = get_default_config_path()
|
||||||
|
self.dir_log = os.path.join(self.dir_config, 'log')
|
||||||
|
self.dir_database = os.path.join(self.dir_config, 'database')
|
||||||
|
self.log_file = os.path.join(self.dir_log, 'fpdb-log.txt')
|
||||||
|
log = get_logger("logging.conf", "config", log_dir=self.dir_log)
|
||||||
|
|
||||||
# Parse even if there was no real config file found and we are using the example
|
# Parse even if there was no real config file found and we are using the example
|
||||||
# If using the example, we'll edit it later
|
# If using the example, we'll edit it later
|
||||||
|
@ -415,15 +483,21 @@ class Config:
|
||||||
print "\nReading configuration file %s\n" % file
|
print "\nReading configuration file %s\n" % file
|
||||||
try:
|
try:
|
||||||
doc = xml.dom.minidom.parse(file)
|
doc = xml.dom.minidom.parse(file)
|
||||||
|
self.file_error = None
|
||||||
except:
|
except:
|
||||||
log.error("Error parsing %s. See error log file." % (file))
|
log.error("Error parsing %s. See error log file." % (file))
|
||||||
traceback.print_exc(file=sys.stderr)
|
traceback.print_exc(file=sys.stderr)
|
||||||
print "press enter to continue"
|
self.file_error = sys.exc_info()[1]
|
||||||
sys.stdin.readline()
|
# we could add a parameter to decide whether to return or read a line and exit?
|
||||||
sys.exit()
|
return
|
||||||
|
#print "press enter to continue"
|
||||||
|
#sys.stdin.readline()
|
||||||
|
#sys.exit()
|
||||||
|
#ExpatError: not well-formed (invalid token): line 511, column 4
|
||||||
|
#sys.exc_info = (<class 'xml.parsers.expat.ExpatError'>, ExpatError('not well-formed (invalid token): line 511,
|
||||||
|
# column 4',), <traceback object at 0x024503A0>)
|
||||||
|
|
||||||
self.doc = doc
|
self.doc = doc
|
||||||
self.file = file
|
|
||||||
self.supported_sites = {}
|
self.supported_sites = {}
|
||||||
self.supported_games = {}
|
self.supported_games = {}
|
||||||
self.supported_databases = {} # databaseName --> Database instance
|
self.supported_databases = {} # databaseName --> Database instance
|
||||||
|
@ -560,7 +634,11 @@ class Config:
|
||||||
def save(self, file = None):
|
def save(self, file = None):
|
||||||
if file is None:
|
if file is None:
|
||||||
file = self.file
|
file = self.file
|
||||||
|
try:
|
||||||
shutil.move(file, file+".backup")
|
shutil.move(file, file+".backup")
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
with open(file, 'w') as f:
|
with open(file, 'w') as f:
|
||||||
self.doc.writexml(f)
|
self.doc.writexml(f)
|
||||||
|
|
||||||
|
@ -619,14 +697,8 @@ class Config:
|
||||||
try: db['db-server'] = self.supported_databases[name].db_server
|
try: db['db-server'] = self.supported_databases[name].db_server
|
||||||
except: pass
|
except: pass
|
||||||
|
|
||||||
if self.supported_databases[name].db_server== DATABASE_TYPE_MYSQL:
|
db['db-backend'] = self.get_backend(self.supported_databases[name].db_server)
|
||||||
db['db-backend'] = 2
|
|
||||||
elif self.supported_databases[name].db_server== DATABASE_TYPE_POSTGRESQL:
|
|
||||||
db['db-backend'] = 3
|
|
||||||
elif self.supported_databases[name].db_server== DATABASE_TYPE_SQLITE:
|
|
||||||
db['db-backend'] = 4
|
|
||||||
else:
|
|
||||||
raise ValueError('Unsupported database backend: %s' % self.supported_databases[name].db_server)
|
|
||||||
return db
|
return db
|
||||||
|
|
||||||
def set_db_parameters(self, db_name = 'fpdb', db_ip = None, db_user = None,
|
def set_db_parameters(self, db_name = 'fpdb', db_ip = None, db_user = None,
|
||||||
|
@ -646,6 +718,23 @@ class Config:
|
||||||
if db_type is not None: self.supported_databases[db_name].dp_type = db_type
|
if db_type is not None: self.supported_databases[db_name].dp_type = db_type
|
||||||
return
|
return
|
||||||
|
|
||||||
|
def get_backend(self, name):
|
||||||
|
"""Returns the number of the currently used backend"""
|
||||||
|
if name == DATABASE_TYPE_MYSQL:
|
||||||
|
ret = 2
|
||||||
|
elif name == DATABASE_TYPE_POSTGRESQL:
|
||||||
|
ret = 3
|
||||||
|
elif name == DATABASE_TYPE_SQLITE:
|
||||||
|
ret = 4
|
||||||
|
# sqlcoder: this assignment fixes unicode problems for me with sqlite (windows, cp1252)
|
||||||
|
# feel free to remove or improve this if you understand the problems
|
||||||
|
# better than me (not hard!)
|
||||||
|
Charset.not_needed1, Charset.not_needed2, Charset.not_needed3 = True, True, True
|
||||||
|
else:
|
||||||
|
raise ValueError('Unsupported database backend: %s' % self.supported_databases[name].db_server)
|
||||||
|
|
||||||
|
return ret
|
||||||
|
|
||||||
def getDefaultSite(self):
|
def getDefaultSite(self):
|
||||||
"Returns first enabled site or None"
|
"Returns first enabled site or None"
|
||||||
for site_name,site in self.supported_sites.iteritems():
|
for site_name,site in self.supported_sites.iteritems():
|
||||||
|
@ -735,8 +824,12 @@ class Config:
|
||||||
try: imp['saveActions'] = self.imp.saveActions
|
try: imp['saveActions'] = self.imp.saveActions
|
||||||
except: imp['saveActions'] = True
|
except: imp['saveActions'] = True
|
||||||
|
|
||||||
|
try: imp['saveStarsHH'] = self.imp.saveStarsHH
|
||||||
|
except: imp['saveStarsHH'] = False
|
||||||
|
|
||||||
try: imp['fastStoreHudCache'] = self.imp.fastStoreHudCache
|
try: imp['fastStoreHudCache'] = self.imp.fastStoreHudCache
|
||||||
except: imp['fastStoreHudCache'] = True
|
except: imp['fastStoreHudCache'] = True
|
||||||
|
|
||||||
return imp
|
return imp
|
||||||
|
|
||||||
def get_default_paths(self, site = None):
|
def get_default_paths(self, site = None):
|
||||||
|
@ -973,3 +1066,9 @@ if __name__== "__main__":
|
||||||
PrettyPrint(site_node, stream=sys.stdout, encoding="utf-8")
|
PrettyPrint(site_node, stream=sys.stdout, encoding="utf-8")
|
||||||
except:
|
except:
|
||||||
print "xml.dom.ext needs PyXML to be installed!"
|
print "xml.dom.ext needs PyXML to be installed!"
|
||||||
|
|
||||||
|
print "FROZEN =", FROZEN
|
||||||
|
print "EXEC_PATH =", EXEC_PATH
|
||||||
|
|
||||||
|
print "press enter to end"
|
||||||
|
sys.stdin.readline()
|
||||||
|
|
1526
pyfpdb/Database.py
1526
pyfpdb/Database.py
File diff suppressed because it is too large
Load Diff
|
@ -47,33 +47,34 @@ class DerivedStats():
|
||||||
self.handsplayers[player[1]]['wonWhenSeenStreet1'] = 0.0
|
self.handsplayers[player[1]]['wonWhenSeenStreet1'] = 0.0
|
||||||
self.handsplayers[player[1]]['sawShowdown'] = False
|
self.handsplayers[player[1]]['sawShowdown'] = False
|
||||||
self.handsplayers[player[1]]['wonAtSD'] = 0.0
|
self.handsplayers[player[1]]['wonAtSD'] = 0.0
|
||||||
|
self.handsplayers[player[1]]['startCards'] = 0
|
||||||
|
self.handsplayers[player[1]]['position'] = 2
|
||||||
|
self.handsplayers[player[1]]['street0_3BChance'] = False
|
||||||
|
self.handsplayers[player[1]]['street0_3BDone'] = False
|
||||||
|
self.handsplayers[player[1]]['street0_4BChance'] = False
|
||||||
|
self.handsplayers[player[1]]['street0_4BDone'] = False
|
||||||
|
self.handsplayers[player[1]]['stealAttemptChance'] = False
|
||||||
|
self.handsplayers[player[1]]['stealAttempted'] = False
|
||||||
|
self.handsplayers[player[1]]['foldBbToStealChance'] = False
|
||||||
|
self.handsplayers[player[1]]['foldSbToStealChance'] = False
|
||||||
|
self.handsplayers[player[1]]['foldedSbToSteal'] = False
|
||||||
|
self.handsplayers[player[1]]['foldedBbToSteal'] = False
|
||||||
for i in range(5):
|
for i in range(5):
|
||||||
self.handsplayers[player[1]]['street%dCalls' % i] = 0
|
self.handsplayers[player[1]]['street%dCalls' % i] = 0
|
||||||
self.handsplayers[player[1]]['street%dBets' % i] = 0
|
self.handsplayers[player[1]]['street%dBets' % i] = 0
|
||||||
for i in range(1,5):
|
for i in range(1,5):
|
||||||
self.handsplayers[player[1]]['street%dCBChance' %i] = False
|
self.handsplayers[player[1]]['street%dCBChance' %i] = False
|
||||||
self.handsplayers[player[1]]['street%dCBDone' %i] = False
|
self.handsplayers[player[1]]['street%dCBDone' %i] = False
|
||||||
|
self.handsplayers[player[1]]['street%dCheckCallRaiseChance' %i] = False
|
||||||
|
self.handsplayers[player[1]]['street%dCheckCallRaiseDone' %i] = False
|
||||||
|
|
||||||
#FIXME - Everything below this point is incomplete.
|
#FIXME - Everything below this point is incomplete.
|
||||||
self.handsplayers[player[1]]['position'] = 2
|
|
||||||
self.handsplayers[player[1]]['tourneyTypeId'] = 1
|
self.handsplayers[player[1]]['tourneyTypeId'] = 1
|
||||||
self.handsplayers[player[1]]['startCards'] = 0
|
|
||||||
self.handsplayers[player[1]]['street0_3BChance'] = False
|
|
||||||
self.handsplayers[player[1]]['street0_3BDone'] = False
|
|
||||||
self.handsplayers[player[1]]['stealAttemptChance'] = False
|
|
||||||
self.handsplayers[player[1]]['stealAttempted'] = False
|
|
||||||
self.handsplayers[player[1]]['foldBbToStealChance'] = False
|
|
||||||
self.handsplayers[player[1]]['foldBbToStealChance'] = False
|
|
||||||
self.handsplayers[player[1]]['foldSbToStealChance'] = False
|
|
||||||
self.handsplayers[player[1]]['foldedSbToSteal'] = False
|
|
||||||
self.handsplayers[player[1]]['foldedBbToSteal'] = False
|
|
||||||
for i in range(1,5):
|
for i in range(1,5):
|
||||||
self.handsplayers[player[1]]['otherRaisedStreet%d' %i] = False
|
self.handsplayers[player[1]]['otherRaisedStreet%d' %i] = False
|
||||||
self.handsplayers[player[1]]['foldToOtherRaisedStreet%d' %i] = False
|
self.handsplayers[player[1]]['foldToOtherRaisedStreet%d' %i] = False
|
||||||
self.handsplayers[player[1]]['foldToStreet%dCBChance' %i] = False
|
self.handsplayers[player[1]]['foldToStreet%dCBChance' %i] = False
|
||||||
self.handsplayers[player[1]]['foldToStreet%dCBDone' %i] = False
|
self.handsplayers[player[1]]['foldToStreet%dCBDone' %i] = False
|
||||||
self.handsplayers[player[1]]['street%dCheckCallRaiseChance' %i] = False
|
|
||||||
self.handsplayers[player[1]]['street%dCheckCallRaiseDone' %i] = False
|
|
||||||
|
|
||||||
self.assembleHands(self.hand)
|
self.assembleHands(self.hand)
|
||||||
self.assembleHandsPlayers(self.hand)
|
self.assembleHandsPlayers(self.hand)
|
||||||
|
@ -161,7 +162,7 @@ class DerivedStats():
|
||||||
self.handsplayers[player]['wonAtSD'] = 1.0
|
self.handsplayers[player]['wonAtSD'] = 1.0
|
||||||
|
|
||||||
for player in hand.pot.committed:
|
for player in hand.pot.committed:
|
||||||
self.handsplayers[player]['totalProfit'] = int(self.handsplayers[player]['winnings'] - (100*hand.pot.committed[player]))
|
self.handsplayers[player]['totalProfit'] = int(self.handsplayers[player]['winnings'] - (100*hand.pot.committed[player])- (100*hand.pot.common[player]))
|
||||||
|
|
||||||
self.calcCBets(hand)
|
self.calcCBets(hand)
|
||||||
|
|
||||||
|
@ -172,35 +173,64 @@ class DerivedStats():
|
||||||
# self.handsplayers[player[1]]['card%s' % i] = Card.encodeCard(card)
|
# self.handsplayers[player[1]]['card%s' % i] = Card.encodeCard(card)
|
||||||
for i, card in enumerate(hcs[:7]):
|
for i, card in enumerate(hcs[:7]):
|
||||||
self.handsplayers[player[1]]['card%s' % (i+1)] = Card.encodeCard(card)
|
self.handsplayers[player[1]]['card%s' % (i+1)] = Card.encodeCard(card)
|
||||||
|
self.handsplayers[player[1]]['startCards'] = Card.calcStartCards(hand, player[1])
|
||||||
|
|
||||||
|
self.setPositions(hand)
|
||||||
# position,
|
self.calcCheckCallRaise(hand)
|
||||||
#Stud 3rd street card test
|
self.calc34BetStreet0(hand)
|
||||||
# denny501: brings in for $0.02
|
self.calcSteals(hand)
|
||||||
# s0rrow: calls $0.02
|
|
||||||
# TomSludge: folds
|
|
||||||
# Soroka69: calls $0.02
|
|
||||||
# rdiezchang: calls $0.02 (Seat 8)
|
|
||||||
# u.pressure: folds (Seat 1)
|
|
||||||
# 123smoothie: calls $0.02
|
|
||||||
# gashpor: calls $0.02
|
|
||||||
|
|
||||||
# Additional stats
|
# Additional stats
|
||||||
# 3betSB, 3betBB
|
# 3betSB, 3betBB
|
||||||
# Squeeze, Ratchet?
|
# Squeeze, Ratchet?
|
||||||
|
|
||||||
|
|
||||||
def getPosition(hand, seat):
|
def setPositions(self, hand):
|
||||||
"""Returns position value like 'B', 'S', 0, 1, ..."""
|
"""Sets the position for each player in HandsPlayers
|
||||||
# Flop/Draw games with blinds
|
any blinds are negative values, and the last person to act on the
|
||||||
# Need a better system???
|
first betting round is 0
|
||||||
# -2 BB - B (all)
|
NOTE: HU, both values are negative for non-stud games
|
||||||
# -1 SB - S (all)
|
NOTE2: I've never seen a HU stud match"""
|
||||||
# 0 Button
|
# The position calculation must be done differently for Stud and other games as
|
||||||
# 1 Cutoff
|
# Stud the 'blind' acts first - in all other games they act last.
|
||||||
# 2 Hijack
|
#
|
||||||
|
#This function is going to get it wrong when there in situations where there
|
||||||
|
# is no small blind. I can live with that.
|
||||||
|
actions = hand.actions[hand.holeStreets[0]]
|
||||||
|
# Note: pfbao list may not include big blind if all others folded
|
||||||
|
players = self.pfbao(actions)
|
||||||
|
|
||||||
|
if hand.gametype['base'] == 'stud':
|
||||||
|
positions = [7, 6, 5, 4, 3, 2, 1, 0, 'S', 'B']
|
||||||
|
seats = len(players)
|
||||||
|
map = []
|
||||||
|
# Could posibly change this to be either -2 or -1 depending if they complete or bring-in
|
||||||
|
# First player to act is -1, last player is 0 for 6 players it should look like:
|
||||||
|
# ['S', 4, 3, 2, 1, 0]
|
||||||
|
map = positions[-seats-1:-1] # Copy required positions from postions array anding in -1
|
||||||
|
map = map[-1:] + map[0:-1] # and move the -1 to the start of that array
|
||||||
|
|
||||||
|
for i, player in enumerate(players):
|
||||||
|
#print "player %s in posn %s" % (player, str(map[i]))
|
||||||
|
self.handsplayers[player]['position'] = map[i]
|
||||||
|
else:
|
||||||
|
# set blinds first, then others from pfbao list, avoids problem if bb
|
||||||
|
# is missing from pfbao list or if there is no small blind
|
||||||
|
bb = [x[0] for x in hand.actions[hand.actionStreets[0]] if x[2] == 'big blind']
|
||||||
|
sb = [x[0] for x in hand.actions[hand.actionStreets[0]] if x[2] == 'small blind']
|
||||||
|
# if there are > 1 sb or bb only the first is used!
|
||||||
|
if bb:
|
||||||
|
self.handsplayers[bb[0]]['position'] = 'B'
|
||||||
|
if bb[0] in players: players.remove(bb[0])
|
||||||
|
if sb:
|
||||||
|
self.handsplayers[sb[0]]['position'] = 'S'
|
||||||
|
if sb[0] in players: players.remove(sb[0])
|
||||||
|
|
||||||
|
#print "bb =", bb, "sb =", sb, "players =", players
|
||||||
|
for i,player in enumerate(reversed(players)):
|
||||||
|
self.handsplayers[player]['position'] = i
|
||||||
|
|
||||||
def assembleHudCache(self, hand):
|
def assembleHudCache(self, hand):
|
||||||
|
# No real work to be done - HandsPlayers data already contains the correct info
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def vpip(self, hand):
|
def vpip(self, hand):
|
||||||
|
@ -224,6 +254,7 @@ class DerivedStats():
|
||||||
# The number of unique players in the list per street gives the value for playersAtStreetXXX
|
# The number of unique players in the list per street gives the value for playersAtStreetXXX
|
||||||
|
|
||||||
# FIXME?? - This isn't couting people that are all in - at least showdown needs to reflect this
|
# FIXME?? - This isn't couting people that are all in - at least showdown needs to reflect this
|
||||||
|
# ... new code below hopefully fixes this
|
||||||
|
|
||||||
self.hands['playersAtStreet1'] = 0
|
self.hands['playersAtStreet1'] = 0
|
||||||
self.hands['playersAtStreet2'] = 0
|
self.hands['playersAtStreet2'] = 0
|
||||||
|
@ -231,23 +262,31 @@ class DerivedStats():
|
||||||
self.hands['playersAtStreet4'] = 0
|
self.hands['playersAtStreet4'] = 0
|
||||||
self.hands['playersAtShowdown'] = 0
|
self.hands['playersAtShowdown'] = 0
|
||||||
|
|
||||||
alliners = set()
|
# alliners = set()
|
||||||
for (i, street) in enumerate(hand.actionStreets[2:]):
|
# for (i, street) in enumerate(hand.actionStreets[2:]):
|
||||||
actors = set()
|
# actors = set()
|
||||||
for action in hand.actions[street]:
|
# for action in hand.actions[street]:
|
||||||
if len(action) > 2 and action[-1]: # allin
|
# if len(action) > 2 and action[-1]: # allin
|
||||||
alliners.add(action[0])
|
# alliners.add(action[0])
|
||||||
actors.add(action[0])
|
# actors.add(action[0])
|
||||||
if len(actors)==0 and len(alliners)<2:
|
# if len(actors)==0 and len(alliners)<2:
|
||||||
alliners = set()
|
# alliners = set()
|
||||||
self.hands['playersAtStreet%d' % (i+1)] = len(set.union(alliners, actors))
|
# self.hands['playersAtStreet%d' % (i+1)] = len(set.union(alliners, actors))
|
||||||
|
#
|
||||||
|
# actions = hand.actions[hand.actionStreets[-1]]
|
||||||
|
# print "p_actions:", self.pfba(actions), "p_folds:", self.pfba(actions, l=('folds',)), "alliners:", alliners
|
||||||
|
# pas = set.union(self.pfba(actions) - self.pfba(actions, l=('folds',)), alliners)
|
||||||
|
|
||||||
actions = hand.actions[hand.actionStreets[-1]]
|
p_in = set(x[1] for x in hand.players)
|
||||||
pas = set.union(self.pfba(actions) - self.pfba(actions, l=('folds',)), alliners)
|
for (i, street) in enumerate(hand.actionStreets):
|
||||||
self.hands['playersAtShowdown'] = len(pas)
|
actions = hand.actions[street]
|
||||||
|
p_in = p_in - self.pfba(actions, l=('folds',))
|
||||||
|
self.hands['playersAtStreet%d' % (i-1)] = len(p_in)
|
||||||
|
|
||||||
|
self.hands['playersAtShowdown'] = len(p_in)
|
||||||
|
|
||||||
if self.hands['playersAtShowdown'] > 1:
|
if self.hands['playersAtShowdown'] > 1:
|
||||||
for player in pas:
|
for player in p_in:
|
||||||
self.handsplayers[player]['sawShowdown'] = True
|
self.handsplayers[player]['sawShowdown'] = True
|
||||||
|
|
||||||
def streetXRaises(self, hand):
|
def streetXRaises(self, hand):
|
||||||
|
@ -262,13 +301,66 @@ class DerivedStats():
|
||||||
for (i, street) in enumerate(hand.actionStreets[1:]):
|
for (i, street) in enumerate(hand.actionStreets[1:]):
|
||||||
self.hands['street%dRaises' % i] = len(filter( lambda action: action[1] in ('raises','bets'), hand.actions[street]))
|
self.hands['street%dRaises' % i] = len(filter( lambda action: action[1] in ('raises','bets'), hand.actions[street]))
|
||||||
|
|
||||||
def calcCBets(self, hand):
|
def calcSteals(self, hand):
|
||||||
# Continuation Bet chance, action:
|
"""Fills stealAttempt(Chance|ed, fold(Bb|Sb)ToSteal(Chance|)
|
||||||
# Had the last bet (initiative) on previous street, got called, close street action
|
|
||||||
# Then no bets before the player with initiatives first action on current street
|
|
||||||
# ie. if player on street-1 had initiative
|
|
||||||
# and no donkbets occurred
|
|
||||||
|
|
||||||
|
Steal attempt - open raise on positions 1 0 S - i.e. MP3, CO, BU, SB
|
||||||
|
(note: I don't think PT2 counts SB steals in HU hands, maybe we shouldn't?)
|
||||||
|
Fold to steal - folding blind after steal attemp wo any other callers or raisers
|
||||||
|
"""
|
||||||
|
steal_attempt = False
|
||||||
|
steal_positions = (1, 0, 'S')
|
||||||
|
if hand.gametype['base'] == 'stud':
|
||||||
|
steal_positions = (2, 1, 0)
|
||||||
|
for action in hand.actions[hand.actionStreets[1]]:
|
||||||
|
pname, act = action[0], action[1]
|
||||||
|
posn = self.handsplayers[pname]['position']
|
||||||
|
#print "\naction:", action[0], posn, type(posn), steal_attempt, act
|
||||||
|
if posn == 'B':
|
||||||
|
#NOTE: Stud games will never hit this section
|
||||||
|
self.handsplayers[pname]['foldBbToStealChance'] = steal_attempt
|
||||||
|
self.handsplayers[pname]['foldedBbToSteal'] = steal_attempt and act == 'folds'
|
||||||
|
break
|
||||||
|
elif posn == 'S':
|
||||||
|
self.handsplayers[pname]['foldSbToStealChance'] = steal_attempt
|
||||||
|
self.handsplayers[pname]['foldedSbToSteal'] = steal_attempt and act == 'folds'
|
||||||
|
|
||||||
|
if steal_attempt and act != 'folds':
|
||||||
|
break
|
||||||
|
|
||||||
|
if posn in steal_positions and not steal_attempt:
|
||||||
|
self.handsplayers[pname]['stealAttemptChance'] = True
|
||||||
|
if act in ('bets', 'raises'):
|
||||||
|
self.handsplayers[pname]['stealAttempted'] = True
|
||||||
|
steal_attempt = True
|
||||||
|
if act == 'calls':
|
||||||
|
break
|
||||||
|
|
||||||
|
if posn not in steal_positions and act != 'folds':
|
||||||
|
break
|
||||||
|
|
||||||
|
def calc34BetStreet0(self, hand):
|
||||||
|
"""Fills street0_(3|4)B(Chance|Done), other(3|4)BStreet0"""
|
||||||
|
bet_level = 1 # bet_level after 3-bet is equal to 3
|
||||||
|
for action in hand.actions[hand.actionStreets[1]]:
|
||||||
|
# FIXME: fill other(3|4)BStreet0 - i have no idea what does it mean
|
||||||
|
pname, aggr = action[0], action[1] in ('raises', 'bets')
|
||||||
|
self.handsplayers[pname]['street0_3BChance'] = self.handsplayers[pname]['street0_3BChance'] or bet_level == 2
|
||||||
|
self.handsplayers[pname]['street0_4BChance'] = bet_level == 3
|
||||||
|
self.handsplayers[pname]['street0_3BDone'] = self.handsplayers[pname]['street0_3BDone'] or (aggr and self.handsplayers[pname]['street0_3BChance'])
|
||||||
|
self.handsplayers[pname]['street0_4BDone'] = aggr and (self.handsplayers[pname]['street0_4BChance'])
|
||||||
|
if aggr:
|
||||||
|
bet_level += 1
|
||||||
|
|
||||||
|
|
||||||
|
def calcCBets(self, hand):
|
||||||
|
"""Fill streetXCBChance, streetXCBDone, foldToStreetXCBDone, foldToStreetXCBChance
|
||||||
|
|
||||||
|
Continuation Bet chance, action:
|
||||||
|
Had the last bet (initiative) on previous street, got called, close street action
|
||||||
|
Then no bets before the player with initiatives first action on current street
|
||||||
|
ie. if player on street-1 had initiative and no donkbets occurred
|
||||||
|
"""
|
||||||
# XXX: enumerate(list, start=x) is python 2.6 syntax; 'start'
|
# XXX: enumerate(list, start=x) is python 2.6 syntax; 'start'
|
||||||
# came there
|
# came there
|
||||||
#for i, street in enumerate(hand.actionStreets[2:], start=1):
|
#for i, street in enumerate(hand.actionStreets[2:], start=1):
|
||||||
|
@ -276,10 +368,33 @@ class DerivedStats():
|
||||||
name = self.lastBetOrRaiser(hand.actionStreets[i+1])
|
name = self.lastBetOrRaiser(hand.actionStreets[i+1])
|
||||||
if name:
|
if name:
|
||||||
chance = self.noBetsBefore(hand.actionStreets[i+2], name)
|
chance = self.noBetsBefore(hand.actionStreets[i+2], name)
|
||||||
self.handsplayers[name]['street%dCBChance' % (i+1)] = True
|
|
||||||
if chance == True:
|
if chance == True:
|
||||||
|
self.handsplayers[name]['street%dCBChance' % (i+1)] = True
|
||||||
self.handsplayers[name]['street%dCBDone' % (i+1)] = self.betStreet(hand.actionStreets[i+2], name)
|
self.handsplayers[name]['street%dCBDone' % (i+1)] = self.betStreet(hand.actionStreets[i+2], name)
|
||||||
|
|
||||||
|
def calcCheckCallRaise(self, hand):
|
||||||
|
"""Fill streetXCheckCallRaiseChance, streetXCheckCallRaiseDone
|
||||||
|
|
||||||
|
streetXCheckCallRaiseChance = got raise/bet after check
|
||||||
|
streetXCheckCallRaiseDone = checked. got raise/bet. didn't fold
|
||||||
|
|
||||||
|
CG: CheckCall would be a much better name for this.
|
||||||
|
"""
|
||||||
|
#for i, street in enumerate(hand.actionStreets[2:], start=1):
|
||||||
|
for i, street in enumerate(hand.actionStreets[2:]):
|
||||||
|
actions = hand.actions[hand.actionStreets[i+1]]
|
||||||
|
checkers = set()
|
||||||
|
initial_raiser = None
|
||||||
|
for action in actions:
|
||||||
|
pname, act = action[0], action[1]
|
||||||
|
if act in ('bets', 'raises') and initial_raiser is None:
|
||||||
|
initial_raiser = pname
|
||||||
|
elif act == 'checks' and initial_raiser is None:
|
||||||
|
checkers.add(pname)
|
||||||
|
elif initial_raiser is not None and pname in checkers:
|
||||||
|
self.handsplayers[pname]['street%dCheckCallRaiseChance' % (i+1)] = True
|
||||||
|
self.handsplayers[pname]['street%dCheckCallRaiseDone' % (i+1)] = act!='folds'
|
||||||
|
|
||||||
def seen(self, hand, i):
|
def seen(self, hand, i):
|
||||||
pas = set()
|
pas = set()
|
||||||
for act in hand.actions[hand.actionStreets[i+1]]:
|
for act in hand.actions[hand.actionStreets[i+1]]:
|
||||||
|
@ -293,11 +408,13 @@ class DerivedStats():
|
||||||
|
|
||||||
def aggr(self, hand, i):
|
def aggr(self, hand, i):
|
||||||
aggrers = set()
|
aggrers = set()
|
||||||
for act in hand.actions[hand.actionStreets[i]]:
|
# Growl - actionStreets contains 'BLINDSANTES', which isn't actually an action street
|
||||||
if act[1] in ('completes', 'raises'):
|
for act in hand.actions[hand.actionStreets[i+1]]:
|
||||||
|
if act[1] in ('completes', 'bets', 'raises'):
|
||||||
aggrers.add(act[0])
|
aggrers.add(act[0])
|
||||||
|
|
||||||
for player in hand.players:
|
for player in hand.players:
|
||||||
|
#print "DEBUG: actionStreet[%s]: %s" %(hand.actionStreets[i+1], i)
|
||||||
if player[1] in aggrers:
|
if player[1] in aggrers:
|
||||||
self.handsplayers[player[1]]['street%sAggr' % i] = True
|
self.handsplayers[player[1]]['street%sAggr' % i] = True
|
||||||
else:
|
else:
|
||||||
|
@ -333,6 +450,44 @@ class DerivedStats():
|
||||||
players.add(action[0])
|
players.add(action[0])
|
||||||
return players
|
return players
|
||||||
|
|
||||||
|
def pfbao(self, actions, f=None, l=None, unique=True):
|
||||||
|
"""Helper method. Returns set of PlayersFilteredByActionsOrdered
|
||||||
|
|
||||||
|
f - forbidden actions
|
||||||
|
l - limited to actions
|
||||||
|
"""
|
||||||
|
# Note, this is an adaptation of function 5 from:
|
||||||
|
# http://www.peterbe.com/plog/uniqifiers-benchmark
|
||||||
|
seen = {}
|
||||||
|
players = []
|
||||||
|
for action in actions:
|
||||||
|
if l is not None and action[1] not in l: continue
|
||||||
|
if f is not None and action[1] in f: continue
|
||||||
|
if action[0] in seen and unique: continue
|
||||||
|
seen[action[0]] = 1
|
||||||
|
players.append(action[0])
|
||||||
|
return players
|
||||||
|
|
||||||
|
def firstsBetOrRaiser(self, actions):
|
||||||
|
"""Returns player name that placed the first bet or raise.
|
||||||
|
|
||||||
|
None if there were no bets or raises on that street
|
||||||
|
"""
|
||||||
|
for act in actions:
|
||||||
|
if act[1] in ('bets', 'raises'):
|
||||||
|
return act[0]
|
||||||
|
return None
|
||||||
|
|
||||||
|
def lastBetOrRaiser(self, street):
|
||||||
|
"""Returns player name that placed the last bet or raise for that street.
|
||||||
|
None if there were no bets or raises on that street"""
|
||||||
|
lastbet = None
|
||||||
|
for act in self.hand.actions[street]:
|
||||||
|
if act[1] in ('bets', 'raises'):
|
||||||
|
lastbet = act[0]
|
||||||
|
return lastbet
|
||||||
|
|
||||||
|
|
||||||
def noBetsBefore(self, street, player):
|
def noBetsBefore(self, street, player):
|
||||||
"""Returns true if there were no bets before the specified players turn, false otherwise"""
|
"""Returns true if there were no bets before the specified players turn, false otherwise"""
|
||||||
betOrRaise = False
|
betOrRaise = False
|
||||||
|
@ -345,22 +500,19 @@ class DerivedStats():
|
||||||
break
|
break
|
||||||
return betOrRaise
|
return betOrRaise
|
||||||
|
|
||||||
|
|
||||||
def betStreet(self, street, player):
|
def betStreet(self, street, player):
|
||||||
"""Returns true if player bet/raised the street as their first action"""
|
"""Returns true if player bet/raised the street as their first action"""
|
||||||
betOrRaise = False
|
betOrRaise = False
|
||||||
for act in self.hand.actions[street]:
|
for act in self.hand.actions[street]:
|
||||||
if act[0] == player and act[1] in ('bets', 'raises'):
|
if act[0] == player:
|
||||||
|
if act[1] in ('bets', 'raises'):
|
||||||
betOrRaise = True
|
betOrRaise = True
|
||||||
else:
|
else:
|
||||||
|
# player found but did not bet or raise as their first action
|
||||||
|
pass
|
||||||
break
|
break
|
||||||
|
#else:
|
||||||
|
# haven't found player's first action yet
|
||||||
return betOrRaise
|
return betOrRaise
|
||||||
|
|
||||||
|
|
||||||
def lastBetOrRaiser(self, street):
|
|
||||||
"""Returns player name that placed the last bet or raise for that street.
|
|
||||||
None if there were no bets or raises on that street"""
|
|
||||||
lastbet = None
|
|
||||||
for act in self.hand.actions[street]:
|
|
||||||
if act[1] in ('bets', 'raises'):
|
|
||||||
lastbet = act[0]
|
|
||||||
return lastbet
|
|
||||||
|
|
|
@ -38,8 +38,9 @@ class Everleaf(HandHistoryConverter):
|
||||||
#re.compile(ur"^(Blinds )?(?P<CURRENCY>\$| €|)(?P<SB>[.0-9]+)/(?:\$| €)?(?P<BB>[.0-9]+) (?P<LIMIT>NL|PL|) (?P<GAME>(Hold\'em|Omaha|7 Card Stud))", re.MULTILINE)
|
#re.compile(ur"^(Blinds )?(?P<CURRENCY>\$| €|)(?P<SB>[.0-9]+)/(?:\$| €)?(?P<BB>[.0-9]+) (?P<LIMIT>NL|PL|) (?P<GAME>(Hold\'em|Omaha|7 Card Stud))", re.MULTILINE)
|
||||||
re_HandInfo = re.compile(ur".*#(?P<HID>[0-9]+)\n.*\n(Blinds )?(?:\$| €|)(?P<SB>[.0-9]+)/(?:\$| €|)(?P<BB>[.0-9]+) (?P<GAMETYPE>.*) - (?P<DATETIME>\d\d\d\d/\d\d/\d\d - \d\d:\d\d:\d\d)\nTable (?P<TABLE>.+$)", re.MULTILINE)
|
re_HandInfo = re.compile(ur".*#(?P<HID>[0-9]+)\n.*\n(Blinds )?(?:\$| €|)(?P<SB>[.0-9]+)/(?:\$| €|)(?P<BB>[.0-9]+) (?P<GAMETYPE>.*) - (?P<DATETIME>\d\d\d\d/\d\d/\d\d - \d\d:\d\d:\d\d)\nTable (?P<TABLE>.+$)", re.MULTILINE)
|
||||||
re_Button = re.compile(ur"^Seat (?P<BUTTON>\d+) is the button", re.MULTILINE)
|
re_Button = re.compile(ur"^Seat (?P<BUTTON>\d+) is the button", re.MULTILINE)
|
||||||
re_PlayerInfo = re.compile(ur"^Seat (?P<SEAT>[0-9]+): (?P<PNAME>.*) \(\s+((?:\$| €|) (?P<CASH>[.0-9]+) (USD|EUR|)|new player|All-in) \)", re.MULTILINE)
|
re_PlayerInfo = re.compile(ur"^Seat (?P<SEAT>[0-9]+): (?P<PNAME>.*) \(\s+((?:\$| €|) (?P<CASH>[.0-9]+) (USD|EURO|Chips)|new player|All-in) \)", re.MULTILINE)
|
||||||
re_Board = re.compile(ur"\[ (?P<CARDS>.+) \]")
|
re_Board = re.compile(ur"\[ (?P<CARDS>.+) \]")
|
||||||
|
re_TourneyInfoFromFilename = re.compile(ur".*TID_(?P<TOURNO>[0-9]+)-(?P<TABLE>[0-9]+)\.txt")
|
||||||
|
|
||||||
|
|
||||||
def compilePlayerRegexs(self, hand):
|
def compilePlayerRegexs(self, hand):
|
||||||
|
@ -55,10 +56,10 @@ class Everleaf(HandHistoryConverter):
|
||||||
self.re_Antes = re.compile(ur"^%s: posts ante \[(?:\$| €|) (?P<ANTE>[.0-9]+)" % player_re, re.MULTILINE)
|
self.re_Antes = re.compile(ur"^%s: posts ante \[(?:\$| €|) (?P<ANTE>[.0-9]+)" % player_re, re.MULTILINE)
|
||||||
self.re_BringIn = re.compile(ur"^%s posts bring-in (?:\$| €|)(?P<BRINGIN>[.0-9]+)\." % player_re, re.MULTILINE)
|
self.re_BringIn = re.compile(ur"^%s posts bring-in (?:\$| €|)(?P<BRINGIN>[.0-9]+)\." % player_re, re.MULTILINE)
|
||||||
self.re_HeroCards = re.compile(ur"^Dealt to %s \[ (?P<CARDS>.*) \]" % player_re, re.MULTILINE)
|
self.re_HeroCards = re.compile(ur"^Dealt to %s \[ (?P<CARDS>.*) \]" % player_re, re.MULTILINE)
|
||||||
self.re_Action = re.compile(ur"^%s(?P<ATYPE>: bets| checks| raises| calls| folds)(\s\[(?:\$| €|) (?P<BET>[.\d]+) (USD|EUR|)\])?" % player_re, re.MULTILINE)
|
self.re_Action = re.compile(ur"^%s(?P<ATYPE>: bets| checks| raises| calls| folds)(\s\[(?:\$| €|) (?P<BET>[.,\d]+) (USD|EURO|Chips)\])?" % player_re, re.MULTILINE)
|
||||||
#self.re_Action = re.compile(ur"^%s(?P<ATYPE>: bets| checks| raises| calls| folds| complete to)(\s\[?(?:\$| €|) ?(?P<BET>\d+\.?\d*)\.?\s?(USD|EUR|)\]?)?" % player_re, re.MULTILINE)
|
#self.re_Action = re.compile(ur"^%s(?P<ATYPE>: bets| checks| raises| calls| folds| complete to)(\s\[?(?:\$| €|) ?(?P<BET>\d+\.?\d*)\.?\s?(USD|EUR|)\]?)?" % player_re, re.MULTILINE)
|
||||||
self.re_ShowdownAction = re.compile(ur"^%s shows \[ (?P<CARDS>.*) \]" % player_re, re.MULTILINE)
|
self.re_ShowdownAction = re.compile(ur"^%s shows \[ (?P<CARDS>.*) \]" % player_re, re.MULTILINE)
|
||||||
self.re_CollectPot = re.compile(ur"^%s wins (?:\$| €|) (?P<POT>[.\d]+) (USD|EUR|chips)(.*?\[ (?P<CARDS>.*?) \])?" % player_re, re.MULTILINE)
|
self.re_CollectPot = re.compile(ur"^%s wins (?:\$| €|) (?P<POT>[.\d]+) (USD|EURO|chips)(.*?\[ (?P<CARDS>.*?) \])?" % player_re, re.MULTILINE)
|
||||||
self.re_SitsOut = re.compile(ur"^%s sits out" % player_re, re.MULTILINE)
|
self.re_SitsOut = re.compile(ur"^%s sits out" % player_re, re.MULTILINE)
|
||||||
|
|
||||||
def readSupportedGames(self):
|
def readSupportedGames(self):
|
||||||
|
@ -66,7 +67,9 @@ class Everleaf(HandHistoryConverter):
|
||||||
["ring", "hold", "pl"],
|
["ring", "hold", "pl"],
|
||||||
["ring", "hold", "fl"],
|
["ring", "hold", "fl"],
|
||||||
["ring", "studhi", "fl"],
|
["ring", "studhi", "fl"],
|
||||||
["ring", "omahahi", "pl"]
|
["ring", "omahahi", "pl"],
|
||||||
|
["ring", "omahahilo", "pl"],
|
||||||
|
["tour", "hold", "nl"]
|
||||||
]
|
]
|
||||||
|
|
||||||
def determineGameType(self, handText):
|
def determineGameType(self, handText):
|
||||||
|
@ -138,6 +141,12 @@ or None if we fail to get the info """
|
||||||
hand.tablename = m.group('TABLE')
|
hand.tablename = m.group('TABLE')
|
||||||
hand.maxseats = 6 # assume 6-max unless we have proof it's a larger/smaller game, since everleaf doesn't give seat max info
|
hand.maxseats = 6 # assume 6-max unless we have proof it's a larger/smaller game, since everleaf doesn't give seat max info
|
||||||
|
|
||||||
|
t = self.re_TourneyInfoFromFilename.search(self.in_path)
|
||||||
|
if t:
|
||||||
|
tourno = t.group('TOURNO')
|
||||||
|
hand.tourNo = tourno
|
||||||
|
hand.tablename = t.group('TABLE')
|
||||||
|
|
||||||
# Believe Everleaf time is GMT/UTC, no transation necessary
|
# Believe Everleaf time is GMT/UTC, no transation necessary
|
||||||
# Stars format (Nov 10 2008): 2008/11/07 12:38:49 CET [2008/11/07 7:38:49 ET]
|
# Stars format (Nov 10 2008): 2008/11/07 12:38:49 CET [2008/11/07 7:38:49 ET]
|
||||||
# or : 2008/11/07 12:38:49 ET
|
# or : 2008/11/07 12:38:49 ET
|
||||||
|
@ -285,6 +294,12 @@ or None if we fail to get the info """
|
||||||
# hand.addShownCards(cards=None, player=m.group('PNAME'), holeandboard=cards)
|
# hand.addShownCards(cards=None, player=m.group('PNAME'), holeandboard=cards)
|
||||||
hand.addShownCards(cards=cards, player=m.group('PNAME'))
|
hand.addShownCards(cards=cards, player=m.group('PNAME'))
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def getTableTitleRe(type, table_name=None, tournament = None, table_number=None):
|
||||||
|
if tournament:
|
||||||
|
return "%s - Tournament ID: %s -" % (table_number, tournament)
|
||||||
|
return "%s -" % (table_name)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
@ -305,4 +320,3 @@ if __name__ == "__main__":
|
||||||
logging.basicConfig(filename=LOG_FILENAME,level=options.verbosity)
|
logging.basicConfig(filename=LOG_FILENAME,level=options.verbosity)
|
||||||
|
|
||||||
e = Everleaf(in_path = options.ipath, out_path = options.opath, follow = options.follow, autostart=True)
|
e = Everleaf(in_path = options.ipath, out_path = options.opath, follow = options.follow, autostart=True)
|
||||||
|
|
||||||
|
|
|
@ -48,5 +48,11 @@ class FpdbPostgresqlNoDatabase(FpdbDatabaseError):
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return repr(self.value +" " + self.errmsg)
|
return repr(self.value +" " + self.errmsg)
|
||||||
|
|
||||||
class DuplicateError(FpdbError):
|
class FpdbHandError(FpdbError):
|
||||||
|
pass
|
||||||
|
|
||||||
|
class FpdbHandDuplicate(FpdbHandError):
|
||||||
|
pass
|
||||||
|
|
||||||
|
class FpdbHandPartial(FpdbHandError):
|
||||||
pass
|
pass
|
||||||
|
|
|
@ -26,21 +26,47 @@ from time import *
|
||||||
import gobject
|
import gobject
|
||||||
#import pokereval
|
#import pokereval
|
||||||
|
|
||||||
|
import logging
|
||||||
|
# logging has been set up in fpdb.py or HUD_main.py, use their settings:
|
||||||
|
log = logging.getLogger("filter")
|
||||||
|
|
||||||
|
|
||||||
import Configuration
|
import Configuration
|
||||||
import fpdb_db
|
import Database
|
||||||
import FpdbSQLQueries
|
import SQL
|
||||||
|
import Charset
|
||||||
|
|
||||||
|
|
||||||
class Filters(threading.Thread):
|
class Filters(threading.Thread):
|
||||||
def __init__(self, db, config, qdict, display = {}, debug=True):
|
def __init__(self, db, config, qdict, display = {}, debug=True):
|
||||||
# config and qdict are now redundant
|
# config and qdict are now redundant
|
||||||
self.debug = debug
|
self.debug = debug
|
||||||
#print "start of GraphViewer constructor"
|
|
||||||
self.db = db
|
self.db = db
|
||||||
self.cursor = db.cursor
|
self.cursor = db.cursor
|
||||||
self.sql = db.sql
|
self.sql = db.sql
|
||||||
self.conf = db.config
|
self.conf = db.config
|
||||||
self.display = display
|
self.display = display
|
||||||
|
|
||||||
|
# text used on screen stored here so that it can be configured
|
||||||
|
self.filterText = {'limitsall':'All', 'limitsnone':'None', 'limitsshow':'Show _Limits'
|
||||||
|
,'seatsbetween':'Between:', 'seatsand':'And:', 'seatsshow':'Show Number of _Players'
|
||||||
|
,'playerstitle':'Hero:', 'sitestitle':'Sites:', 'gamestitle':'Games:'
|
||||||
|
,'limitstitle':'Limits:', 'seatstitle':'Number of Players:'
|
||||||
|
,'groupstitle':'Grouping:', 'posnshow':'Show Position Stats:'
|
||||||
|
,'datestitle':'Date:'
|
||||||
|
,'groupsall':'All Players'
|
||||||
|
,'limitsFL':'FL', 'limitsNL':'NL', 'limitsPL':'PL', 'ring':'Ring', 'tour':'Tourney'
|
||||||
|
}
|
||||||
|
|
||||||
|
# Outer Packing box
|
||||||
|
self.mainVBox = gtk.VBox(False, 0)
|
||||||
|
|
||||||
|
self.label = {}
|
||||||
|
self.callback = {}
|
||||||
|
|
||||||
|
self.make_filter()
|
||||||
|
|
||||||
|
def make_filter(self):
|
||||||
self.sites = {}
|
self.sites = {}
|
||||||
self.games = {}
|
self.games = {}
|
||||||
self.limits = {}
|
self.limits = {}
|
||||||
|
@ -50,14 +76,14 @@ class Filters(threading.Thread):
|
||||||
self.heroes = {}
|
self.heroes = {}
|
||||||
self.boxes = {}
|
self.boxes = {}
|
||||||
|
|
||||||
# text used on screen stored here so that it can be configured
|
for site in self.conf.get_supported_sites():
|
||||||
self.filterText = {'limitsall':'All', 'limitsnone':'None', 'limitsshow':'Show _Limits'
|
#Get db site id for filtering later
|
||||||
,'seatsbetween':'Between:', 'seatsand':'And:', 'seatsshow':'Show Number of _Players'
|
self.cursor.execute(self.sql.query['getSiteId'], (site,))
|
||||||
,'limitstitle':'Limits:', 'seatstitle':'Number of Players:'
|
result = self.db.cursor.fetchall()
|
||||||
,'groupstitle':'Grouping:', 'posnshow':'Show Position Stats:'
|
if len(result) == 1:
|
||||||
,'groupsall':'All Players'
|
self.siteid[site] = result[0][0]
|
||||||
,'limitsFL':'FL', 'limitsNL':'NL', 'ring':'Ring', 'tour':'Tourney'
|
else:
|
||||||
}
|
print "Either 0 or more than one site matched - EEK"
|
||||||
|
|
||||||
# For use in date ranges.
|
# For use in date ranges.
|
||||||
self.start_date = gtk.Entry(max=12)
|
self.start_date = gtk.Entry(max=12)
|
||||||
|
@ -69,34 +95,28 @@ class Filters(threading.Thread):
|
||||||
self.sbGroups = {}
|
self.sbGroups = {}
|
||||||
self.numHands = 0
|
self.numHands = 0
|
||||||
|
|
||||||
# Outer Packing box
|
playerFrame = gtk.Frame()
|
||||||
self.mainVBox = gtk.VBox(False, 0)
|
|
||||||
|
|
||||||
playerFrame = gtk.Frame("Hero:")
|
|
||||||
playerFrame.set_label_align(0.0, 0.0)
|
playerFrame.set_label_align(0.0, 0.0)
|
||||||
vbox = gtk.VBox(False, 0)
|
vbox = gtk.VBox(False, 0)
|
||||||
|
|
||||||
self.fillPlayerFrame(vbox, self.display)
|
self.fillPlayerFrame(vbox, self.display)
|
||||||
playerFrame.add(vbox)
|
playerFrame.add(vbox)
|
||||||
self.boxes['player'] = vbox
|
|
||||||
|
|
||||||
sitesFrame = gtk.Frame("Sites:")
|
sitesFrame = gtk.Frame()
|
||||||
sitesFrame.set_label_align(0.0, 0.0)
|
sitesFrame.set_label_align(0.0, 0.0)
|
||||||
vbox = gtk.VBox(False, 0)
|
vbox = gtk.VBox(False, 0)
|
||||||
|
|
||||||
self.fillSitesFrame(vbox)
|
self.fillSitesFrame(vbox)
|
||||||
sitesFrame.add(vbox)
|
sitesFrame.add(vbox)
|
||||||
self.boxes['sites'] = vbox
|
|
||||||
|
|
||||||
# Game types
|
# Game types
|
||||||
gamesFrame = gtk.Frame("Games:")
|
gamesFrame = gtk.Frame()
|
||||||
gamesFrame.set_label_align(0.0, 0.0)
|
gamesFrame.set_label_align(0.0, 0.0)
|
||||||
gamesFrame.show()
|
gamesFrame.show()
|
||||||
vbox = gtk.VBox(False, 0)
|
vbox = gtk.VBox(False, 0)
|
||||||
|
|
||||||
self.fillGamesFrame(vbox)
|
self.fillGamesFrame(vbox)
|
||||||
gamesFrame.add(vbox)
|
gamesFrame.add(vbox)
|
||||||
self.boxes['games'] = vbox
|
|
||||||
|
|
||||||
# Limits
|
# Limits
|
||||||
limitsFrame = gtk.Frame()
|
limitsFrame = gtk.Frame()
|
||||||
|
@ -107,6 +127,7 @@ class Filters(threading.Thread):
|
||||||
self.cbAllLimits = None
|
self.cbAllLimits = None
|
||||||
self.cbFL = None
|
self.cbFL = None
|
||||||
self.cbNL = None
|
self.cbNL = None
|
||||||
|
self.cbPL = None
|
||||||
self.rb = {} # radio buttons for ring/tour
|
self.rb = {} # radio buttons for ring/tour
|
||||||
self.type = None # ring/tour
|
self.type = None # ring/tour
|
||||||
self.types = {} # list of all ring/tour values
|
self.types = {} # list of all ring/tour values
|
||||||
|
@ -132,14 +153,13 @@ class Filters(threading.Thread):
|
||||||
groupsFrame.add(vbox)
|
groupsFrame.add(vbox)
|
||||||
|
|
||||||
# Date
|
# Date
|
||||||
dateFrame = gtk.Frame("Date:")
|
dateFrame = gtk.Frame()
|
||||||
dateFrame.set_label_align(0.0, 0.0)
|
dateFrame.set_label_align(0.0, 0.0)
|
||||||
dateFrame.show()
|
dateFrame.show()
|
||||||
vbox = gtk.VBox(False, 0)
|
vbox = gtk.VBox(False, 0)
|
||||||
|
|
||||||
self.fillDateFrame(vbox)
|
self.fillDateFrame(vbox)
|
||||||
dateFrame.add(vbox)
|
dateFrame.add(vbox)
|
||||||
self.boxes['date'] = vbox
|
|
||||||
|
|
||||||
# Buttons
|
# Buttons
|
||||||
self.Button1=gtk.Button("Unnamed 1")
|
self.Button1=gtk.Button("Unnamed 1")
|
||||||
|
@ -180,6 +200,20 @@ class Filters(threading.Thread):
|
||||||
if "Button2" not in self.display or self.display["Button2"] == False:
|
if "Button2" not in self.display or self.display["Button2"] == False:
|
||||||
self.Button2.hide()
|
self.Button2.hide()
|
||||||
|
|
||||||
|
if 'button1' in self.label and self.label['button1']:
|
||||||
|
self.Button1.set_label( self.label['button1'] )
|
||||||
|
if 'button2' in self.label and self.label['button2']:
|
||||||
|
self.Button2.set_label( self.label['button2'] )
|
||||||
|
if 'button1' in self.callback and self.callback['button1']:
|
||||||
|
self.Button1.connect("clicked", self.callback['button1'], "clicked")
|
||||||
|
self.Button1.set_sensitive(True)
|
||||||
|
if 'button2' in self.callback and self.callback['button2']:
|
||||||
|
self.Button2.connect("clicked", self.callback['button2'], "clicked")
|
||||||
|
self.Button2.set_sensitive(True)
|
||||||
|
|
||||||
|
# make sure any locks on db are released:
|
||||||
|
self.db.rollback()
|
||||||
|
|
||||||
def get_vbox(self):
|
def get_vbox(self):
|
||||||
"""returns the vbox of this thread"""
|
"""returns the vbox of this thread"""
|
||||||
return self.mainVBox
|
return self.mainVBox
|
||||||
|
@ -191,6 +225,9 @@ class Filters(threading.Thread):
|
||||||
def getSites(self):
|
def getSites(self):
|
||||||
return self.sites
|
return self.sites
|
||||||
|
|
||||||
|
def getGames(self):
|
||||||
|
return self.games
|
||||||
|
|
||||||
def getSiteIds(self):
|
def getSiteIds(self):
|
||||||
return self.siteid
|
return self.siteid
|
||||||
|
|
||||||
|
@ -222,24 +259,29 @@ class Filters(threading.Thread):
|
||||||
|
|
||||||
def registerButton1Name(self, title):
|
def registerButton1Name(self, title):
|
||||||
self.Button1.set_label(title)
|
self.Button1.set_label(title)
|
||||||
|
self.label['button1'] = title
|
||||||
|
|
||||||
def registerButton1Callback(self, callback):
|
def registerButton1Callback(self, callback):
|
||||||
self.Button1.connect("clicked", callback, "clicked")
|
self.Button1.connect("clicked", callback, "clicked")
|
||||||
self.Button1.set_sensitive(True)
|
self.Button1.set_sensitive(True)
|
||||||
|
self.callback['button1'] = callback
|
||||||
|
|
||||||
def registerButton2Name(self, title):
|
def registerButton2Name(self, title):
|
||||||
self.Button2.set_label(title)
|
self.Button2.set_label(title)
|
||||||
|
self.label['button2'] = title
|
||||||
|
|
||||||
def registerButton2Callback(self, callback):
|
def registerButton2Callback(self, callback):
|
||||||
self.Button2.connect("clicked", callback, "clicked")
|
self.Button2.connect("clicked", callback, "clicked")
|
||||||
self.Button2.set_sensitive(True)
|
self.Button2.set_sensitive(True)
|
||||||
|
self.callback['button2'] = callback
|
||||||
|
|
||||||
def cardCallback(self, widget, data=None):
|
def cardCallback(self, widget, data=None):
|
||||||
print "DEBUG: %s was toggled %s" % (data, ("OFF", "ON")[widget.get_active()])
|
log.debug( "%s was toggled %s" % (data, ("OFF", "ON")[widget.get_active()]) )
|
||||||
|
|
||||||
def createPlayerLine(self, hbox, site, player):
|
def createPlayerLine(self, hbox, site, player):
|
||||||
|
log.debug('add:"%s"' % player)
|
||||||
label = gtk.Label(site +" id:")
|
label = gtk.Label(site +" id:")
|
||||||
hbox.pack_start(label, False, False, 0)
|
hbox.pack_start(label, False, False, 3)
|
||||||
|
|
||||||
pname = gtk.Entry()
|
pname = gtk.Entry()
|
||||||
pname.set_text(player)
|
pname.set_text(player)
|
||||||
|
@ -253,22 +295,27 @@ class Filters(threading.Thread):
|
||||||
liststore = gtk.ListStore(gobject.TYPE_STRING)
|
liststore = gtk.ListStore(gobject.TYPE_STRING)
|
||||||
completion.set_model(liststore)
|
completion.set_model(liststore)
|
||||||
completion.set_text_column(0)
|
completion.set_text_column(0)
|
||||||
names = self.db.get_player_names(self.conf) # (config=self.conf, site_id=None, like_player_name="%")
|
names = self.db.get_player_names(self.conf, self.siteid[site]) # (config=self.conf, site_id=None, like_player_name="%")
|
||||||
for n in names:
|
for n in names: # list of single-element "tuples"
|
||||||
liststore.append(n)
|
_n = Charset.to_gui(n[0])
|
||||||
|
_nt = (_n, )
|
||||||
|
liststore.append(_nt)
|
||||||
|
|
||||||
self.__set_hero_name(pname, site)
|
self.__set_hero_name(pname, site)
|
||||||
|
|
||||||
def __set_hero_name(self, w, site):
|
def __set_hero_name(self, w, site):
|
||||||
self.heroes[site] = w.get_text()
|
_name = w.get_text()
|
||||||
# print "DEBUG: setting heroes[%s]: %s"%(site, self.heroes[site])
|
# get_text() returns a str but we want internal variables to be unicode:
|
||||||
|
_guiname = unicode(_name)
|
||||||
|
self.heroes[site] = _guiname
|
||||||
|
# log.debug("setting heroes[%s]: %s"%(site, self.heroes[site]))
|
||||||
|
|
||||||
def __set_num_hands(self, w, val):
|
def __set_num_hands(self, w, val):
|
||||||
try:
|
try:
|
||||||
self.numHands = int(w.get_text())
|
self.numHands = int(w.get_text())
|
||||||
except:
|
except:
|
||||||
self.numHands = 0
|
self.numHands = 0
|
||||||
# print "DEBUG: setting numHands:", self.numHands
|
# log.debug("setting numHands:", self.numHands)
|
||||||
|
|
||||||
def createSiteLine(self, hbox, site):
|
def createSiteLine(self, hbox, site):
|
||||||
cb = gtk.CheckButton(site)
|
cb = gtk.CheckButton(site)
|
||||||
|
@ -280,6 +327,7 @@ class Filters(threading.Thread):
|
||||||
cb = gtk.CheckButton(game)
|
cb = gtk.CheckButton(game)
|
||||||
cb.connect('clicked', self.__set_game_select, game)
|
cb.connect('clicked', self.__set_game_select, game)
|
||||||
hbox.pack_start(cb, False, False, 0)
|
hbox.pack_start(cb, False, False, 0)
|
||||||
|
cb.set_active(True)
|
||||||
|
|
||||||
def createLimitLine(self, hbox, limit, ltext):
|
def createLimitLine(self, hbox, limit, ltext):
|
||||||
cb = gtk.CheckButton(str(ltext))
|
cb = gtk.CheckButton(str(ltext))
|
||||||
|
@ -292,18 +340,18 @@ class Filters(threading.Thread):
|
||||||
def __set_site_select(self, w, site):
|
def __set_site_select(self, w, site):
|
||||||
#print w.get_active()
|
#print w.get_active()
|
||||||
self.sites[site] = w.get_active()
|
self.sites[site] = w.get_active()
|
||||||
print "self.sites[%s] set to %s" %(site, self.sites[site])
|
log.debug("self.sites[%s] set to %s" %(site, self.sites[site]))
|
||||||
|
|
||||||
def __set_game_select(self, w, game):
|
def __set_game_select(self, w, game):
|
||||||
#print w.get_active()
|
#print w.get_active()
|
||||||
self.games[game] = w.get_active()
|
self.games[game] = w.get_active()
|
||||||
print "self.games[%s] set to %s" %(game, self.games[game])
|
log.debug("self.games[%s] set to %s" %(game, self.games[game]))
|
||||||
|
|
||||||
def __set_limit_select(self, w, limit):
|
def __set_limit_select(self, w, limit):
|
||||||
#print w.get_active()
|
#print w.get_active()
|
||||||
self.limits[limit] = w.get_active()
|
self.limits[limit] = w.get_active()
|
||||||
print "self.limit[%s] set to %s" %(limit, self.limits[limit])
|
log.debug("self.limit[%s] set to %s" %(limit, self.limits[limit]))
|
||||||
if limit.isdigit() or (len(limit) > 2 and limit[-2:] == 'nl'):
|
if limit.isdigit() or (len(limit) > 2 and (limit[-2:] == 'nl' or limit[-2:] == 'fl' or limit[-2:] == 'pl')):
|
||||||
if self.limits[limit]:
|
if self.limits[limit]:
|
||||||
if self.cbNoLimits is not None:
|
if self.cbNoLimits is not None:
|
||||||
self.cbNoLimits.set_active(False)
|
self.cbNoLimits.set_active(False)
|
||||||
|
@ -314,9 +362,12 @@ class Filters(threading.Thread):
|
||||||
if limit.isdigit():
|
if limit.isdigit():
|
||||||
if self.cbFL is not None:
|
if self.cbFL is not None:
|
||||||
self.cbFL.set_active(False)
|
self.cbFL.set_active(False)
|
||||||
else:
|
elif (len(limit) > 2 and (limit[-2:] == 'nl')):
|
||||||
if self.cbNL is not None:
|
if self.cbNL is not None:
|
||||||
self.cbNL.set_active(False)
|
self.cbNL.set_active(False)
|
||||||
|
else:
|
||||||
|
if self.cbPL is not None:
|
||||||
|
self.cbPL.set_active(False)
|
||||||
elif limit == "all":
|
elif limit == "all":
|
||||||
if self.limits[limit]:
|
if self.limits[limit]:
|
||||||
#for cb in self.cbLimits.values():
|
#for cb in self.cbLimits.values():
|
||||||
|
@ -325,6 +376,8 @@ class Filters(threading.Thread):
|
||||||
self.cbFL.set_active(True)
|
self.cbFL.set_active(True)
|
||||||
if self.cbNL is not None:
|
if self.cbNL is not None:
|
||||||
self.cbNL.set_active(True)
|
self.cbNL.set_active(True)
|
||||||
|
if self.cbPL is not None:
|
||||||
|
self.cbPL.set_active(True)
|
||||||
elif limit == "none":
|
elif limit == "none":
|
||||||
if self.limits[limit]:
|
if self.limits[limit]:
|
||||||
for cb in self.cbLimits.values():
|
for cb in self.cbLimits.values():
|
||||||
|
@ -333,6 +386,8 @@ class Filters(threading.Thread):
|
||||||
self.cbNL.set_active(False)
|
self.cbNL.set_active(False)
|
||||||
if self.cbFL is not None:
|
if self.cbFL is not None:
|
||||||
self.cbFL.set_active(False)
|
self.cbFL.set_active(False)
|
||||||
|
if self.cbPL is not None:
|
||||||
|
self.cbPL.set_active(False)
|
||||||
elif limit == "fl":
|
elif limit == "fl":
|
||||||
if not self.limits[limit]:
|
if not self.limits[limit]:
|
||||||
# only toggle all fl limits off if they are all currently on
|
# only toggle all fl limits off if they are all currently on
|
||||||
|
@ -381,11 +436,39 @@ class Filters(threading.Thread):
|
||||||
if self.limits[limit]:
|
if self.limits[limit]:
|
||||||
if not found[self.type]:
|
if not found[self.type]:
|
||||||
if self.type == 'ring':
|
if self.type == 'ring':
|
||||||
|
if 'tour' in self.rb:
|
||||||
self.rb['tour'].set_active(True)
|
self.rb['tour'].set_active(True)
|
||||||
elif self.type == 'tour':
|
elif self.type == 'tour':
|
||||||
|
if 'ring' in self.rb:
|
||||||
|
self.rb['ring'].set_active(True)
|
||||||
|
elif limit == "pl":
|
||||||
|
if not self.limits[limit]:
|
||||||
|
# only toggle all nl limits off if they are all currently on
|
||||||
|
# this stops turning one off from cascading into 'nl' box off
|
||||||
|
# and then all nl limits being turned off
|
||||||
|
all_nl_on = True
|
||||||
|
for cb in self.cbLimits.values():
|
||||||
|
t = cb.get_children()[0].get_text()
|
||||||
|
if "pl" in t and len(t) > 2:
|
||||||
|
if not cb.get_active():
|
||||||
|
all_nl_on = False
|
||||||
|
found = {'ring':False, 'tour':False}
|
||||||
|
for cb in self.cbLimits.values():
|
||||||
|
t = cb.get_children()[0].get_text()
|
||||||
|
if "pl" in t and len(t) > 2:
|
||||||
|
if self.limits[limit] or all_nl_on:
|
||||||
|
cb.set_active(self.limits[limit])
|
||||||
|
found[self.types[t]] = True
|
||||||
|
if self.limits[limit]:
|
||||||
|
if not found[self.type]:
|
||||||
|
if self.type == 'ring':
|
||||||
|
if 'tour' in self.rb:
|
||||||
|
self.rb['tour'].set_active(True)
|
||||||
|
elif self.type == 'tour':
|
||||||
|
if 'ring' in self.rb:
|
||||||
self.rb['ring'].set_active(True)
|
self.rb['ring'].set_active(True)
|
||||||
elif limit == "ring":
|
elif limit == "ring":
|
||||||
print "set", limit, "to", self.limits[limit]
|
log.debug("set", limit, "to", self.limits[limit])
|
||||||
if self.limits[limit]:
|
if self.limits[limit]:
|
||||||
self.type = "ring"
|
self.type = "ring"
|
||||||
for cb in self.cbLimits.values():
|
for cb in self.cbLimits.values():
|
||||||
|
@ -393,7 +476,7 @@ class Filters(threading.Thread):
|
||||||
if self.types[cb.get_children()[0].get_text()] == 'tour':
|
if self.types[cb.get_children()[0].get_text()] == 'tour':
|
||||||
cb.set_active(False)
|
cb.set_active(False)
|
||||||
elif limit == "tour":
|
elif limit == "tour":
|
||||||
print "set", limit, "to", self.limits[limit]
|
log.debug( "set", limit, "to", self.limits[limit] )
|
||||||
if self.limits[limit]:
|
if self.limits[limit]:
|
||||||
self.type = "tour"
|
self.type = "tour"
|
||||||
for cb in self.cbLimits.values():
|
for cb in self.cbLimits.values():
|
||||||
|
@ -404,24 +487,38 @@ class Filters(threading.Thread):
|
||||||
def __set_seat_select(self, w, seat):
|
def __set_seat_select(self, w, seat):
|
||||||
#print "__set_seat_select: seat =", seat, "active =", w.get_active()
|
#print "__set_seat_select: seat =", seat, "active =", w.get_active()
|
||||||
self.seats[seat] = w.get_active()
|
self.seats[seat] = w.get_active()
|
||||||
print "self.seats[%s] set to %s" %(seat, self.seats[seat])
|
log.debug( "self.seats[%s] set to %s" %(seat, self.seats[seat]) )
|
||||||
|
|
||||||
def __set_group_select(self, w, group):
|
def __set_group_select(self, w, group):
|
||||||
#print "__set_seat_select: seat =", seat, "active =", w.get_active()
|
#print "__set_seat_select: seat =", seat, "active =", w.get_active()
|
||||||
self.groups[group] = w.get_active()
|
self.groups[group] = w.get_active()
|
||||||
print "self.groups[%s] set to %s" %(group, self.groups[group])
|
log.debug( "self.groups[%s] set to %s" %(group, self.groups[group]) )
|
||||||
|
|
||||||
def fillPlayerFrame(self, vbox, display):
|
def fillPlayerFrame(self, vbox, display):
|
||||||
|
top_hbox = gtk.HBox(False, 0)
|
||||||
|
vbox.pack_start(top_hbox, False, False, 0)
|
||||||
|
lbl_title = gtk.Label(self.filterText['playerstitle'])
|
||||||
|
lbl_title.set_alignment(xalign=0.0, yalign=0.5)
|
||||||
|
top_hbox.pack_start(lbl_title, expand=True, padding=3)
|
||||||
|
showb = gtk.Button(label="refresh", stock=None, use_underline=True)
|
||||||
|
showb.set_alignment(xalign=1.0, yalign=0.5)
|
||||||
|
showb.connect('clicked', self.__refresh, 'players')
|
||||||
|
|
||||||
|
vbox1 = gtk.VBox(False, 0)
|
||||||
|
vbox.pack_start(vbox1, False, False, 0)
|
||||||
|
self.boxes['players'] = vbox1
|
||||||
|
|
||||||
for site in self.conf.get_supported_sites():
|
for site in self.conf.get_supported_sites():
|
||||||
hBox = gtk.HBox(False, 0)
|
hBox = gtk.HBox(False, 0)
|
||||||
vbox.pack_start(hBox, False, True, 0)
|
vbox1.pack_start(hBox, False, True, 0)
|
||||||
|
|
||||||
player = self.conf.supported_sites[site].screen_name
|
player = self.conf.supported_sites[site].screen_name
|
||||||
self.createPlayerLine(hBox, site, player)
|
_pname = Charset.to_gui(player)
|
||||||
|
self.createPlayerLine(hBox, site, _pname)
|
||||||
|
|
||||||
if "GroupsAll" in display and display["GroupsAll"] == True:
|
if "GroupsAll" in display and display["GroupsAll"] == True:
|
||||||
hbox = gtk.HBox(False, 0)
|
hbox = gtk.HBox(False, 0)
|
||||||
vbox.pack_start(hbox, False, False, 0)
|
vbox1.pack_start(hbox, False, False, 0)
|
||||||
cb = gtk.CheckButton(self.filterText['groupsall'])
|
cb = gtk.CheckButton(self.filterText['groupsall'])
|
||||||
cb.connect('clicked', self.__set_group_select, 'allplayers')
|
cb.connect('clicked', self.__set_group_select, 'allplayers')
|
||||||
hbox.pack_start(cb, False, False, 0)
|
hbox.pack_start(cb, False, False, 0)
|
||||||
|
@ -437,30 +534,64 @@ class Filters(threading.Thread):
|
||||||
phands.set_width_chars(8)
|
phands.set_width_chars(8)
|
||||||
hbox.pack_start(phands, False, False, 0)
|
hbox.pack_start(phands, False, False, 0)
|
||||||
phands.connect("changed", self.__set_num_hands, site)
|
phands.connect("changed", self.__set_num_hands, site)
|
||||||
|
top_hbox.pack_start(showb, expand=False, padding=1)
|
||||||
|
|
||||||
def fillSitesFrame(self, vbox):
|
def fillSitesFrame(self, vbox):
|
||||||
|
top_hbox = gtk.HBox(False, 0)
|
||||||
|
top_hbox.show()
|
||||||
|
vbox.pack_start(top_hbox, False, False, 0)
|
||||||
|
|
||||||
|
lbl_title = gtk.Label(self.filterText['sitestitle'])
|
||||||
|
lbl_title.set_alignment(xalign=0.0, yalign=0.5)
|
||||||
|
top_hbox.pack_start(lbl_title, expand=True, padding=3)
|
||||||
|
|
||||||
|
showb = gtk.Button(label="hide", stock=None, use_underline=True)
|
||||||
|
showb.set_alignment(xalign=1.0, yalign=0.5)
|
||||||
|
showb.connect('clicked', self.__toggle_box, 'sites')
|
||||||
|
showb.show()
|
||||||
|
top_hbox.pack_start(showb, expand=False, padding=1)
|
||||||
|
|
||||||
|
vbox1 = gtk.VBox(False, 0)
|
||||||
|
self.boxes['sites'] = vbox1
|
||||||
|
vbox.pack_start(vbox1, False, False, 0)
|
||||||
|
|
||||||
for site in self.conf.get_supported_sites():
|
for site in self.conf.get_supported_sites():
|
||||||
hbox = gtk.HBox(False, 0)
|
hbox = gtk.HBox(False, 0)
|
||||||
vbox.pack_start(hbox, False, True, 0)
|
vbox1.pack_start(hbox, False, True, 0)
|
||||||
self.createSiteLine(hbox, site)
|
self.createSiteLine(hbox, site)
|
||||||
#Get db site id for filtering later
|
#Get db site id for filtering later
|
||||||
self.cursor.execute(self.sql.query['getSiteId'], (site,))
|
#self.cursor.execute(self.sql.query['getSiteId'], (site,))
|
||||||
result = self.db.cursor.fetchall()
|
#result = self.db.cursor.fetchall()
|
||||||
if len(result) == 1:
|
#if len(result) == 1:
|
||||||
self.siteid[site] = result[0][0]
|
# self.siteid[site] = result[0][0]
|
||||||
else:
|
#else:
|
||||||
print "Either 0 or more than one site matched - EEK"
|
# print "Either 0 or more than one site matched - EEK"
|
||||||
|
|
||||||
def fillGamesFrame(self, vbox):
|
def fillGamesFrame(self, vbox):
|
||||||
|
top_hbox = gtk.HBox(False, 0)
|
||||||
|
vbox.pack_start(top_hbox, False, False, 0)
|
||||||
|
lbl_title = gtk.Label(self.filterText['gamestitle'])
|
||||||
|
lbl_title.set_alignment(xalign=0.0, yalign=0.5)
|
||||||
|
top_hbox.pack_start(lbl_title, expand=True, padding=3)
|
||||||
|
showb = gtk.Button(label="hide", stock=None, use_underline=True)
|
||||||
|
showb.set_alignment(xalign=1.0, yalign=0.5)
|
||||||
|
showb.connect('clicked', self.__toggle_box, 'games')
|
||||||
|
top_hbox.pack_start(showb, expand=False, padding=1)
|
||||||
|
|
||||||
|
vbox1 = gtk.VBox(False, 0)
|
||||||
|
vbox.pack_start(vbox1, False, False, 0)
|
||||||
|
self.boxes['games'] = vbox1
|
||||||
|
|
||||||
self.cursor.execute(self.sql.query['getGames'])
|
self.cursor.execute(self.sql.query['getGames'])
|
||||||
result = self.db.cursor.fetchall()
|
result = self.db.cursor.fetchall()
|
||||||
if len(result) >= 1:
|
if len(result) >= 1:
|
||||||
for line in result:
|
for line in result:
|
||||||
hbox = gtk.HBox(False, 0)
|
hbox = gtk.HBox(False, 0)
|
||||||
vbox.pack_start(hbox, False, True, 0)
|
vbox1.pack_start(hbox, False, True, 0)
|
||||||
self.createGameLine(hbox, line[0])
|
self.createGameLine(hbox, line[0])
|
||||||
else:
|
else:
|
||||||
print "INFO: No games returned from database"
|
print "INFO: No games returned from database"
|
||||||
|
log.info("No games returned from database")
|
||||||
|
|
||||||
def fillLimitsFrame(self, vbox, display):
|
def fillLimitsFrame(self, vbox, display):
|
||||||
top_hbox = gtk.HBox(False, 0)
|
top_hbox = gtk.HBox(False, 0)
|
||||||
|
@ -476,10 +607,10 @@ class Filters(threading.Thread):
|
||||||
vbox.pack_start(vbox1, False, False, 0)
|
vbox.pack_start(vbox1, False, False, 0)
|
||||||
self.boxes['limits'] = vbox1
|
self.boxes['limits'] = vbox1
|
||||||
|
|
||||||
self.cursor.execute(self.sql.query['getLimits2'])
|
self.cursor.execute(self.sql.query['getLimits3'])
|
||||||
# selects limitType, bigBlind
|
# selects limitType, bigBlind
|
||||||
result = self.db.cursor.fetchall()
|
result = self.db.cursor.fetchall()
|
||||||
found = {'nl':False, 'fl':False, 'ring':False, 'tour':False}
|
found = {'nl':False, 'fl':False, 'pl':False, 'ring':False, 'tour':False}
|
||||||
|
|
||||||
if len(result) >= 1:
|
if len(result) >= 1:
|
||||||
hbox = gtk.HBox(True, 0)
|
hbox = gtk.HBox(True, 0)
|
||||||
|
@ -497,9 +628,13 @@ class Filters(threading.Thread):
|
||||||
vbox2.pack_start(hbox, False, False, 0)
|
vbox2.pack_start(hbox, False, False, 0)
|
||||||
else:
|
else:
|
||||||
vbox3.pack_start(hbox, False, False, 0)
|
vbox3.pack_start(hbox, False, False, 0)
|
||||||
|
if True: #line[0] == 'ring':
|
||||||
if line[1] == 'fl':
|
if line[1] == 'fl':
|
||||||
name = str(line[2])
|
name = str(line[2])
|
||||||
found['fl'] = True
|
found['fl'] = True
|
||||||
|
elif line[1] == 'pl':
|
||||||
|
name = str(line[2])+line[1]
|
||||||
|
found['pl'] = True
|
||||||
else:
|
else:
|
||||||
name = str(line[2])+line[1]
|
name = str(line[2])+line[1]
|
||||||
found['nl'] = True
|
found['nl'] = True
|
||||||
|
@ -532,9 +667,13 @@ class Filters(threading.Thread):
|
||||||
hbox = gtk.HBox(False, 0)
|
hbox = gtk.HBox(False, 0)
|
||||||
vbox3.pack_start(hbox, False, False, 0)
|
vbox3.pack_start(hbox, False, False, 0)
|
||||||
self.cbNL = self.createLimitLine(hbox, 'nl', self.filterText['limitsNL'])
|
self.cbNL = self.createLimitLine(hbox, 'nl', self.filterText['limitsNL'])
|
||||||
|
hbox = gtk.HBox(False, 0)
|
||||||
|
vbox3.pack_start(hbox, False, False, 0)
|
||||||
|
self.cbPL = self.createLimitLine(hbox, 'pl', self.filterText['limitsPL'])
|
||||||
dest = vbox2 # for ring/tour buttons
|
dest = vbox2 # for ring/tour buttons
|
||||||
else:
|
else:
|
||||||
print "INFO: No games returned from database"
|
print "INFO: No games returned from database"
|
||||||
|
log.info("No games returned from database")
|
||||||
|
|
||||||
if "Type" in display and display["Type"] == True and found['ring'] and found['tour']:
|
if "Type" in display and display["Type"] == True and found['ring'] and found['tour']:
|
||||||
rb1 = gtk.RadioButton(None, self.filterText['ring'])
|
rb1 = gtk.RadioButton(None, self.filterText['ring'])
|
||||||
|
@ -645,8 +784,22 @@ class Filters(threading.Thread):
|
||||||
|
|
||||||
def fillDateFrame(self, vbox):
|
def fillDateFrame(self, vbox):
|
||||||
# Hat tip to Mika Bostrom - calendar code comes from PokerStats
|
# Hat tip to Mika Bostrom - calendar code comes from PokerStats
|
||||||
|
top_hbox = gtk.HBox(False, 0)
|
||||||
|
vbox.pack_start(top_hbox, False, False, 0)
|
||||||
|
lbl_title = gtk.Label(self.filterText['datestitle'])
|
||||||
|
lbl_title.set_alignment(xalign=0.0, yalign=0.5)
|
||||||
|
top_hbox.pack_start(lbl_title, expand=True, padding=3)
|
||||||
|
showb = gtk.Button(label="hide", stock=None, use_underline=True)
|
||||||
|
showb.set_alignment(xalign=1.0, yalign=0.5)
|
||||||
|
showb.connect('clicked', self.__toggle_box, 'dates')
|
||||||
|
top_hbox.pack_start(showb, expand=False, padding=1)
|
||||||
|
|
||||||
|
vbox1 = gtk.VBox(False, 0)
|
||||||
|
vbox.pack_start(vbox1, False, False, 0)
|
||||||
|
self.boxes['dates'] = vbox1
|
||||||
|
|
||||||
hbox = gtk.HBox()
|
hbox = gtk.HBox()
|
||||||
vbox.pack_start(hbox, False, True, 0)
|
vbox1.pack_start(hbox, False, True, 0)
|
||||||
|
|
||||||
lbl_start = gtk.Label('From:')
|
lbl_start = gtk.Label('From:')
|
||||||
|
|
||||||
|
@ -660,7 +813,7 @@ class Filters(threading.Thread):
|
||||||
|
|
||||||
#New row for end date
|
#New row for end date
|
||||||
hbox = gtk.HBox()
|
hbox = gtk.HBox()
|
||||||
vbox.pack_start(hbox, False, True, 0)
|
vbox1.pack_start(hbox, False, True, 0)
|
||||||
|
|
||||||
lbl_end = gtk.Label(' To:')
|
lbl_end = gtk.Label(' To:')
|
||||||
btn_end = gtk.Button()
|
btn_end = gtk.Button()
|
||||||
|
@ -676,10 +829,13 @@ class Filters(threading.Thread):
|
||||||
|
|
||||||
hbox.pack_start(btn_clear, expand=False, padding=15)
|
hbox.pack_start(btn_clear, expand=False, padding=15)
|
||||||
|
|
||||||
|
def __refresh(self, widget, entry):
|
||||||
|
for w in self.mainVBox.get_children():
|
||||||
|
w.destroy()
|
||||||
|
self.make_filter()
|
||||||
|
|
||||||
def __toggle_box(self, widget, entry):
|
def __toggle_box(self, widget, entry):
|
||||||
if "Limits" not in self.display or self.display["Limits"] == False:
|
if self.boxes[entry].props.visible:
|
||||||
self.boxes[entry].hide()
|
|
||||||
elif self.boxes[entry].props.visible:
|
|
||||||
self.boxes[entry].hide()
|
self.boxes[entry].hide()
|
||||||
widget.set_label("show")
|
widget.set_label("show")
|
||||||
else:
|
else:
|
||||||
|
@ -740,10 +896,10 @@ def main(argv=None):
|
||||||
config = Configuration.Config()
|
config = Configuration.Config()
|
||||||
db = None
|
db = None
|
||||||
|
|
||||||
db = fpdb_db.fpdb_db()
|
db = Database.Database()
|
||||||
db.do_connect(config)
|
db.do_connect(config)
|
||||||
|
|
||||||
qdict = FpdbSQLQueries.FpdbSQLQueries(db.get_backend_name())
|
qdict = SQL.SQL(db.get_backend_name())
|
||||||
|
|
||||||
i = Filters(db, config, qdict)
|
i = Filters(db, config, qdict)
|
||||||
main_window = gtk.Window()
|
main_window = gtk.Window()
|
||||||
|
|
|
@ -18,12 +18,10 @@
|
||||||
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
########################################################################
|
########################################################################
|
||||||
|
|
||||||
import sys
|
|
||||||
import logging
|
import logging
|
||||||
from HandHistoryConverter import *
|
from HandHistoryConverter import *
|
||||||
|
|
||||||
# Fulltilt HH Format converter
|
# Fulltilt HH Format converter
|
||||||
# TODO: cat tourno and table to make table name for tournaments
|
|
||||||
|
|
||||||
class Fulltilt(HandHistoryConverter):
|
class Fulltilt(HandHistoryConverter):
|
||||||
|
|
||||||
|
@ -67,8 +65,8 @@ class Fulltilt(HandHistoryConverter):
|
||||||
(\s\((?P<TURBO>Turbo)\))?)|(?P<UNREADABLE_INFO>.+))
|
(\s\((?P<TURBO>Turbo)\))?)|(?P<UNREADABLE_INFO>.+))
|
||||||
''', re.VERBOSE)
|
''', re.VERBOSE)
|
||||||
re_Button = re.compile('^The button is in seat #(?P<BUTTON>\d+)', re.MULTILINE)
|
re_Button = re.compile('^The button is in seat #(?P<BUTTON>\d+)', re.MULTILINE)
|
||||||
re_PlayerInfo = re.compile('Seat (?P<SEAT>[0-9]+): (?P<PNAME>.*) \(\$(?P<CASH>[,.0-9]+)\)$', re.MULTILINE)
|
re_PlayerInfo = re.compile('Seat (?P<SEAT>[0-9]+): (?P<PNAME>.{2,15}) \(\$(?P<CASH>[,.0-9]+)\)$', re.MULTILINE)
|
||||||
re_TourneyPlayerInfo = re.compile('Seat (?P<SEAT>[0-9]+): (?P<PNAME>.*) \(\$?(?P<CASH>[,.0-9]+)\)', re.MULTILINE)
|
re_TourneyPlayerInfo = re.compile('Seat (?P<SEAT>[0-9]+): (?P<PNAME>.{2,15}) \(\$?(?P<CASH>[,.0-9]+)\)(, is sitting out)?$', re.MULTILINE)
|
||||||
re_Board = re.compile(r"\[(?P<CARDS>.+)\]")
|
re_Board = re.compile(r"\[(?P<CARDS>.+)\]")
|
||||||
|
|
||||||
#static regex for tourney purpose
|
#static regex for tourney purpose
|
||||||
|
@ -130,6 +128,7 @@ class Fulltilt(HandHistoryConverter):
|
||||||
player_re = "(?P<PNAME>" + "|".join(map(re.escape, players)) + ")"
|
player_re = "(?P<PNAME>" + "|".join(map(re.escape, players)) + ")"
|
||||||
logging.debug("player_re: " + player_re)
|
logging.debug("player_re: " + player_re)
|
||||||
self.re_PostSB = re.compile(r"^%s posts the small blind of \$?(?P<SB>[.0-9]+)" % player_re, re.MULTILINE)
|
self.re_PostSB = re.compile(r"^%s posts the small blind of \$?(?P<SB>[.0-9]+)" % player_re, re.MULTILINE)
|
||||||
|
self.re_PostDead = re.compile(r"^%s posts a dead small blind of \$?(?P<SB>[.0-9]+)" % player_re, re.MULTILINE)
|
||||||
self.re_PostBB = re.compile(r"^%s posts (the big blind of )?\$?(?P<BB>[.0-9]+)" % player_re, re.MULTILINE)
|
self.re_PostBB = re.compile(r"^%s posts (the big blind of )?\$?(?P<BB>[.0-9]+)" % player_re, re.MULTILINE)
|
||||||
self.re_Antes = re.compile(r"^%s antes \$?(?P<ANTE>[.0-9]+)" % player_re, re.MULTILINE)
|
self.re_Antes = re.compile(r"^%s antes \$?(?P<ANTE>[.0-9]+)" % player_re, re.MULTILINE)
|
||||||
self.re_BringIn = re.compile(r"^%s brings in for \$?(?P<BRINGIN>[.0-9]+)" % player_re, re.MULTILINE)
|
self.re_BringIn = re.compile(r"^%s brings in for \$?(?P<BRINGIN>[.0-9]+)" % player_re, re.MULTILINE)
|
||||||
|
@ -139,7 +138,7 @@ class Fulltilt(HandHistoryConverter):
|
||||||
self.re_ShowdownAction = re.compile(r"^%s shows \[(?P<CARDS>.*)\]" % player_re, re.MULTILINE)
|
self.re_ShowdownAction = re.compile(r"^%s shows \[(?P<CARDS>.*)\]" % player_re, re.MULTILINE)
|
||||||
self.re_CollectPot = re.compile(r"^Seat (?P<SEAT>[0-9]+): %s (\(button\) |\(small blind\) |\(big blind\) )?(collected|showed \[.*\] and won) \(\$?(?P<POT>[.,\d]+)\)(, mucked| with.*)" % player_re, re.MULTILINE)
|
self.re_CollectPot = re.compile(r"^Seat (?P<SEAT>[0-9]+): %s (\(button\) |\(small blind\) |\(big blind\) )?(collected|showed \[.*\] and won) \(\$?(?P<POT>[.,\d]+)\)(, mucked| with.*)" % player_re, re.MULTILINE)
|
||||||
self.re_SitsOut = re.compile(r"^%s sits out" % player_re, re.MULTILINE)
|
self.re_SitsOut = re.compile(r"^%s sits out" % player_re, re.MULTILINE)
|
||||||
self.re_ShownCards = re.compile(r"^Seat (?P<SEAT>[0-9]+): %s \(.*\) showed \[(?P<CARDS>.*)\].*" % player_re, re.MULTILINE)
|
self.re_ShownCards = re.compile(r"^Seat (?P<SEAT>[0-9]+): %s (\(button\) |\(small blind\) |\(big blind\) )?(?P<ACT>showed|mucked) \[(?P<CARDS>.*)\].*" % player_re, re.MULTILINE)
|
||||||
|
|
||||||
def readSupportedGames(self):
|
def readSupportedGames(self):
|
||||||
return [["ring", "hold", "nl"],
|
return [["ring", "hold", "nl"],
|
||||||
|
@ -191,7 +190,6 @@ class Fulltilt(HandHistoryConverter):
|
||||||
if mg['TOURNO'] is None: info['type'] = "ring"
|
if mg['TOURNO'] is None: info['type'] = "ring"
|
||||||
else: info['type'] = "tour"
|
else: info['type'] = "tour"
|
||||||
# NB: SB, BB must be interpreted as blinds or bets depending on limit type.
|
# NB: SB, BB must be interpreted as blinds or bets depending on limit type.
|
||||||
# if info['type'] == "tour": return None # importer is screwed on tournies, pass on those hands so we don't interrupt other autoimporting
|
|
||||||
return info
|
return info
|
||||||
|
|
||||||
def readHandInfo(self, hand):
|
def readHandInfo(self, hand):
|
||||||
|
@ -258,15 +256,16 @@ class Fulltilt(HandHistoryConverter):
|
||||||
#TODO: Need some date functions to convert to different timezones (Date::Manip for perl rocked for this)
|
#TODO: Need some date functions to convert to different timezones (Date::Manip for perl rocked for this)
|
||||||
#hand.starttime = "%d/%02d/%02d %d:%02d:%02d ET" %(int(m.group('YEAR')), int(m.group('MON')), int(m.group('DAY')),
|
#hand.starttime = "%d/%02d/%02d %d:%02d:%02d ET" %(int(m.group('YEAR')), int(m.group('MON')), int(m.group('DAY')),
|
||||||
##int(m.group('HR')), int(m.group('MIN')), int(m.group('SEC')))
|
##int(m.group('HR')), int(m.group('MIN')), int(m.group('SEC')))
|
||||||
#FIXME: hand.buttonpos = int(m.group('BUTTON'))
|
|
||||||
|
|
||||||
def readPlayerStacks(self, hand):
|
def readPlayerStacks(self, hand):
|
||||||
|
# Split hand text for FTP, as the regex matches the player names incorrectly
|
||||||
|
# in the summary section
|
||||||
|
pre, post = hand.handText.split('SUMMARY')
|
||||||
if hand.gametype['type'] == "ring" :
|
if hand.gametype['type'] == "ring" :
|
||||||
m = self.re_PlayerInfo.finditer(hand.handText)
|
m = self.re_PlayerInfo.finditer(pre)
|
||||||
else: #if hand.gametype['type'] == "tour"
|
else: #if hand.gametype['type'] == "tour"
|
||||||
m = self.re_TourneyPlayerInfo.finditer(hand.handText)
|
m = self.re_TourneyPlayerInfo.finditer(pre)
|
||||||
|
|
||||||
players = []
|
|
||||||
for a in m:
|
for a in m:
|
||||||
hand.addPlayer(int(a.group('SEAT')), a.group('PNAME'), a.group('CASH'))
|
hand.addPlayer(int(a.group('SEAT')), a.group('PNAME'), a.group('CASH'))
|
||||||
|
|
||||||
|
@ -300,6 +299,8 @@ class Fulltilt(HandHistoryConverter):
|
||||||
hand.addBlind(m.group('PNAME'), 'small blind', m.group('SB'))
|
hand.addBlind(m.group('PNAME'), 'small blind', m.group('SB'))
|
||||||
except: # no small blind
|
except: # no small blind
|
||||||
hand.addBlind(None, None, None)
|
hand.addBlind(None, None, None)
|
||||||
|
for a in self.re_PostDead.finditer(hand.handText):
|
||||||
|
hand.addBlind(a.group('PNAME'), 'secondsb', a.group('SB'))
|
||||||
for a in self.re_PostBB.finditer(hand.handText):
|
for a in self.re_PostBB.finditer(hand.handText):
|
||||||
hand.addBlind(a.group('PNAME'), 'big blind', a.group('BB'))
|
hand.addBlind(a.group('PNAME'), 'big blind', a.group('BB'))
|
||||||
for a in self.re_PostBoth.finditer(hand.handText):
|
for a in self.re_PostBoth.finditer(hand.handText):
|
||||||
|
@ -392,9 +393,10 @@ class Fulltilt(HandHistoryConverter):
|
||||||
def readShownCards(self,hand):
|
def readShownCards(self,hand):
|
||||||
for m in self.re_ShownCards.finditer(hand.handText):
|
for m in self.re_ShownCards.finditer(hand.handText):
|
||||||
if m.group('CARDS') is not None:
|
if m.group('CARDS') is not None:
|
||||||
cards = m.group('CARDS')
|
if m.group('ACT'):
|
||||||
cards = cards.split(' ')
|
hand.addShownCards(cards=m.group('CARDS').split(' '), player=m.group('PNAME'), shown = False, mucked = True)
|
||||||
hand.addShownCards(cards=cards, player=m.group('PNAME'))
|
else:
|
||||||
|
hand.addShownCards(cards=m.group('CARDS').split(' '), player=m.group('PNAME'), shown = True, mucked = False)
|
||||||
|
|
||||||
def guessMaxSeats(self, hand):
|
def guessMaxSeats(self, hand):
|
||||||
"""Return a guess at max_seats when not specified in HH."""
|
"""Return a guess at max_seats when not specified in HH."""
|
||||||
|
@ -422,7 +424,6 @@ class Fulltilt(HandHistoryConverter):
|
||||||
hand.mixed = self.mixes[m.groupdict()['MIXED']]
|
hand.mixed = self.mixes[m.groupdict()['MIXED']]
|
||||||
|
|
||||||
def readSummaryInfo(self, summaryInfoList):
|
def readSummaryInfo(self, summaryInfoList):
|
||||||
starttime = time.time()
|
|
||||||
self.status = True
|
self.status = True
|
||||||
|
|
||||||
m = re.search("Tournament Summary", summaryInfoList[0])
|
m = re.search("Tournament Summary", summaryInfoList[0])
|
||||||
|
@ -542,14 +543,14 @@ class Fulltilt(HandHistoryConverter):
|
||||||
tourney.buyin = 100*Decimal(re.sub(u',', u'', "%s" % mg['BUYIN']))
|
tourney.buyin = 100*Decimal(re.sub(u',', u'', "%s" % mg['BUYIN']))
|
||||||
else :
|
else :
|
||||||
if 100*Decimal(re.sub(u',', u'', "%s" % mg['BUYIN'])) != tourney.buyin:
|
if 100*Decimal(re.sub(u',', u'', "%s" % mg['BUYIN'])) != tourney.buyin:
|
||||||
log.error( "Conflict between buyins read in topline (%s) and in BuyIn field (%s)" % (touney.buyin, 100*Decimal(re.sub(u',', u'', "%s" % mg['BUYIN']))) )
|
log.error( "Conflict between buyins read in topline (%s) and in BuyIn field (%s)" % (tourney.buyin, 100*Decimal(re.sub(u',', u'', "%s" % mg['BUYIN']))) )
|
||||||
tourney.subTourneyBuyin = 100*Decimal(re.sub(u',', u'', "%s" % mg['BUYIN']))
|
tourney.subTourneyBuyin = 100*Decimal(re.sub(u',', u'', "%s" % mg['BUYIN']))
|
||||||
if mg['FEE'] is not None:
|
if mg['FEE'] is not None:
|
||||||
if tourney.fee is None:
|
if tourney.fee is None:
|
||||||
tourney.fee = 100*Decimal(re.sub(u',', u'', "%s" % mg['FEE']))
|
tourney.fee = 100*Decimal(re.sub(u',', u'', "%s" % mg['FEE']))
|
||||||
else :
|
else :
|
||||||
if 100*Decimal(re.sub(u',', u'', "%s" % mg['FEE'])) != tourney.fee:
|
if 100*Decimal(re.sub(u',', u'', "%s" % mg['FEE'])) != tourney.fee:
|
||||||
log.error( "Conflict between fees read in topline (%s) and in BuyIn field (%s)" % (touney.fee, 100*Decimal(re.sub(u',', u'', "%s" % mg['FEE']))) )
|
log.error( "Conflict between fees read in topline (%s) and in BuyIn field (%s)" % (tourney.fee, 100*Decimal(re.sub(u',', u'', "%s" % mg['FEE']))) )
|
||||||
tourney.subTourneyFee = 100*Decimal(re.sub(u',', u'', "%s" % mg['FEE']))
|
tourney.subTourneyFee = 100*Decimal(re.sub(u',', u'', "%s" % mg['FEE']))
|
||||||
|
|
||||||
if tourney.buyin is None:
|
if tourney.buyin is None:
|
||||||
|
|
|
@ -193,8 +193,13 @@ class GuiAutoImport (threading.Thread):
|
||||||
self.doAutoImportBool = True
|
self.doAutoImportBool = True
|
||||||
widget.set_label(u' _Stop Autoimport ')
|
widget.set_label(u' _Stop Autoimport ')
|
||||||
if self.pipe_to_hud is None:
|
if self.pipe_to_hud is None:
|
||||||
if os.name == 'nt':
|
if Configuration.FROZEN:
|
||||||
command = "python HUD_main.py " + self.settings['cl_options']
|
path = Configuration.EXEC_PATH
|
||||||
|
command = "HUD_main.exe"
|
||||||
|
bs = 0
|
||||||
|
elif os.name == 'nt':
|
||||||
|
path = sys.path[0].replace('\\','\\\\')
|
||||||
|
command = 'python "'+path+'\\HUD_main.py" ' + self.settings['cl_options']
|
||||||
bs = 0
|
bs = 0
|
||||||
else:
|
else:
|
||||||
command = os.path.join(sys.path[0], 'HUD_main.py')
|
command = os.path.join(sys.path[0], 'HUD_main.py')
|
||||||
|
@ -202,12 +207,14 @@ class GuiAutoImport (threading.Thread):
|
||||||
bs = 1
|
bs = 1
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
print "opening pipe to HUD"
|
||||||
self.pipe_to_hud = subprocess.Popen(command, bufsize=bs,
|
self.pipe_to_hud = subprocess.Popen(command, bufsize=bs,
|
||||||
stdin=subprocess.PIPE,
|
stdin=subprocess.PIPE,
|
||||||
universal_newlines=True)
|
universal_newlines=True)
|
||||||
except:
|
except:
|
||||||
err = traceback.extract_tb(sys.exc_info()[2])[-1]
|
err = traceback.extract_tb(sys.exc_info()[2])[-1]
|
||||||
self.addText( "\n*** GuiAutoImport Error opening pipe: " + err[2] + "(" + str(err[1]) + "): " + str(sys.exc_info()[1]))
|
#self.addText( "\n*** GuiAutoImport Error opening pipe: " + err[2] + "(" + str(err[1]) + "): " + str(sys.exc_info()[1]))
|
||||||
|
self.addText( "\n*** GuiAutoImport Error opening pipe: " + traceback.format_exc() )
|
||||||
else:
|
else:
|
||||||
for site in self.input_settings:
|
for site in self.input_settings:
|
||||||
self.importer.addImportDirectory(self.input_settings[site][0], True, site, self.input_settings[site][1])
|
self.importer.addImportDirectory(self.input_settings[site][0], True, site, self.input_settings[site][1])
|
||||||
|
@ -300,7 +307,6 @@ if __name__== "__main__":
|
||||||
(options, argv) = parser.parse_args()
|
(options, argv) = parser.parse_args()
|
||||||
|
|
||||||
config = Configuration.Config()
|
config = Configuration.Config()
|
||||||
# db = fpdb_db.fpdb_db()
|
|
||||||
|
|
||||||
settings = {}
|
settings = {}
|
||||||
settings['minPrint'] = options.minPrint
|
settings['minPrint'] = options.minPrint
|
||||||
|
|
|
@ -30,7 +30,6 @@ import gtk
|
||||||
import gobject
|
import gobject
|
||||||
|
|
||||||
# fpdb/FreePokerTools modules
|
# fpdb/FreePokerTools modules
|
||||||
import fpdb_simple
|
|
||||||
import fpdb_import
|
import fpdb_import
|
||||||
import Configuration
|
import Configuration
|
||||||
import Exceptions
|
import Exceptions
|
||||||
|
@ -90,7 +89,8 @@ class GuiBulkImport():
|
||||||
|
|
||||||
for selection in selected:
|
for selection in selected:
|
||||||
self.importer.addBulkImportImportFileOrDir(selection, site = sitename)
|
self.importer.addBulkImportImportFileOrDir(selection, site = sitename)
|
||||||
self.importer.setCallHud(False)
|
self.importer.setCallHud(self.cb_testmode.get_active())
|
||||||
|
self.importer.bHudTest = self.cb_testmode.get_active()
|
||||||
starttime = time()
|
starttime = time()
|
||||||
# try:
|
# try:
|
||||||
(stored, dups, partial, errs, ttime) = self.importer.runImport()
|
(stored, dups, partial, errs, ttime) = self.importer.runImport()
|
||||||
|
@ -106,6 +106,9 @@ class GuiBulkImport():
|
||||||
print 'GuiBulkImport.load done: Stored: %d \tDuplicates: %d \tPartial: %d \tErrors: %d in %s seconds - %.0f/sec'\
|
print 'GuiBulkImport.load done: Stored: %d \tDuplicates: %d \tPartial: %d \tErrors: %d in %s seconds - %.0f/sec'\
|
||||||
% (stored, dups, partial, errs, ttime, (stored+0.0) / ttime)
|
% (stored, dups, partial, errs, ttime, (stored+0.0) / ttime)
|
||||||
self.importer.clearFileList()
|
self.importer.clearFileList()
|
||||||
|
# This file should really be 'logging'
|
||||||
|
#log.info('GuiBulkImport.load done: Stored: %d \tDuplicates: %d \tPartial: %d \tErrors: %d in %s seconds - %.0f/sec'\
|
||||||
|
# % (stored, dups, partial, errs, ttime, (stored+0.0) / ttime))
|
||||||
if self.n_hands_in_db == 0 and stored > 0:
|
if self.n_hands_in_db == 0 and stored > 0:
|
||||||
self.cb_dropindexes.set_sensitive(True)
|
self.cb_dropindexes.set_sensitive(True)
|
||||||
self.cb_dropindexes.set_active(0)
|
self.cb_dropindexes.set_active(0)
|
||||||
|
@ -229,6 +232,10 @@ class GuiBulkImport():
|
||||||
ypadding=0, yoptions=gtk.SHRINK)
|
ypadding=0, yoptions=gtk.SHRINK)
|
||||||
self.cb_dropindexes.show()
|
self.cb_dropindexes.show()
|
||||||
|
|
||||||
|
self.cb_testmode = gtk.CheckButton('HUD Test mode')
|
||||||
|
self.table.attach(self.cb_testmode, 0, 1, 2, 3, xpadding=10, ypadding=0, yoptions=gtk.SHRINK)
|
||||||
|
self.cb_testmode.show()
|
||||||
|
|
||||||
# label - filter
|
# label - filter
|
||||||
self.lab_filter = gtk.Label("Site filter:")
|
self.lab_filter = gtk.Label("Site filter:")
|
||||||
self.table.attach(self.lab_filter, 1, 2, 2, 3, xpadding=0, ypadding=0,
|
self.table.attach(self.lab_filter, 1, 2, 2, 3, xpadding=0, ypadding=0,
|
||||||
|
@ -239,7 +246,17 @@ class GuiBulkImport():
|
||||||
|
|
||||||
# ComboBox - filter
|
# ComboBox - filter
|
||||||
self.cbfilter = gtk.combo_box_new_text()
|
self.cbfilter = gtk.combo_box_new_text()
|
||||||
|
disabled_sites = [] # move disabled sites to bottom of list
|
||||||
for w in self.config.hhcs:
|
for w in self.config.hhcs:
|
||||||
|
try:
|
||||||
|
if self.config.supported_sites[w].enabled: # include enabled ones first
|
||||||
|
print w
|
||||||
|
self.cbfilter.append_text(w)
|
||||||
|
else:
|
||||||
|
disabled_sites.append(w)
|
||||||
|
except: # self.supported_sites[w] may not exist if hud_config is bad
|
||||||
|
disabled_sites.append(w)
|
||||||
|
for w in disabled_sites: # then disabled ones
|
||||||
print w
|
print w
|
||||||
self.cbfilter.append_text(w)
|
self.cbfilter.append_text(w)
|
||||||
self.cbfilter.set_active(0)
|
self.cbfilter.set_active(0)
|
||||||
|
|
298
pyfpdb/GuiDatabase.py
Executable file
298
pyfpdb/GuiDatabase.py
Executable file
|
@ -0,0 +1,298 @@
|
||||||
|
#!/usr/bin/python
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
#Copyright 2008 Carl Gherardi
|
||||||
|
#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 <http://www.gnu.org/licenses/>.
|
||||||
|
#In the "official" distribution you can find the license in
|
||||||
|
#agpl-3.0.txt in the docs folder of the package.
|
||||||
|
|
||||||
|
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import traceback
|
||||||
|
import Queue
|
||||||
|
|
||||||
|
import pygtk
|
||||||
|
pygtk.require('2.0')
|
||||||
|
import gtk
|
||||||
|
import gobject
|
||||||
|
import pango
|
||||||
|
|
||||||
|
import logging
|
||||||
|
# logging has been set up in fpdb.py or HUD_main.py, use their settings:
|
||||||
|
log = logging.getLogger("maintdbs")
|
||||||
|
|
||||||
|
|
||||||
|
import Exceptions
|
||||||
|
import Database
|
||||||
|
|
||||||
|
|
||||||
|
class GuiDatabase:
|
||||||
|
|
||||||
|
COL_DBMS = 0
|
||||||
|
COL_NAME = 1
|
||||||
|
COL_DESC = 2
|
||||||
|
COL_USER = 3
|
||||||
|
COL_PASS = 4
|
||||||
|
COL_HOST = 5
|
||||||
|
COL_ICON = 6
|
||||||
|
|
||||||
|
def __init__(self, config, mainwin, dia):
|
||||||
|
self.config = config
|
||||||
|
self.main_window = mainwin
|
||||||
|
self.dia = dia
|
||||||
|
|
||||||
|
try:
|
||||||
|
#self.dia.set_modal(True)
|
||||||
|
self.vbox = self.dia.vbox
|
||||||
|
#gtk.Widget.set_size_request(self.vbox, 700, 400);
|
||||||
|
|
||||||
|
# list of databases in self.config.supported_databases:
|
||||||
|
self.liststore = gtk.ListStore(str, str, str, str
|
||||||
|
,str, str, str, str) #object, gtk.gdk.Pixbuf)
|
||||||
|
# dbms, name, comment, user, pass, ip, status(, icon?)
|
||||||
|
# this is how to add a filter:
|
||||||
|
#
|
||||||
|
# # Creation of the filter, from the model
|
||||||
|
# filter = self.liststore.filter_new()
|
||||||
|
# filter.set_visible_column(1)
|
||||||
|
#
|
||||||
|
# # The TreeView gets the filter as model
|
||||||
|
# self.listview = gtk.TreeView(filter)
|
||||||
|
self.listview = gtk.TreeView(model=self.liststore)
|
||||||
|
self.listview.set_grid_lines(gtk.TREE_VIEW_GRID_LINES_NONE)
|
||||||
|
self.listcols = []
|
||||||
|
|
||||||
|
scrolledwindow = gtk.ScrolledWindow()
|
||||||
|
scrolledwindow.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
|
||||||
|
scrolledwindow.add(self.listview)
|
||||||
|
self.vbox.pack_start(scrolledwindow, expand=True, fill=True, padding=0)
|
||||||
|
|
||||||
|
refreshbutton = gtk.Button("Refresh")
|
||||||
|
refreshbutton.connect("clicked", self.refresh, None)
|
||||||
|
self.vbox.pack_start(refreshbutton, False, False, 3)
|
||||||
|
refreshbutton.show()
|
||||||
|
|
||||||
|
col = self.addTextColumn("Type", 0, False)
|
||||||
|
col = self.addTextColumn("Name", 1, False)
|
||||||
|
col = self.addTextColumn("Description", 2, True)
|
||||||
|
col = self.addTextColumn("Username", 3, True)
|
||||||
|
col = self.addTextColumn("Password", 4, True)
|
||||||
|
col = self.addTextColumn("Host", 5, True)
|
||||||
|
col = self.addTextObjColumn("", 6)
|
||||||
|
|
||||||
|
self.loadDbs()
|
||||||
|
|
||||||
|
self.dia.connect('response', self.dialog_response_cb)
|
||||||
|
except:
|
||||||
|
err = traceback.extract_tb(sys.exc_info()[2])[-1]
|
||||||
|
print 'guidbmaint: '+ err[2] + "(" + str(err[1]) + "): " + str(sys.exc_info()[1])
|
||||||
|
|
||||||
|
def dialog_response_cb(self, dialog, response_id):
|
||||||
|
# this is called whether close button is pressed or window is closed
|
||||||
|
dialog.destroy()
|
||||||
|
|
||||||
|
|
||||||
|
def get_dialog(self):
|
||||||
|
return self.dia
|
||||||
|
|
||||||
|
def addTextColumn(self, title, n, editable=False):
|
||||||
|
col = gtk.TreeViewColumn(title)
|
||||||
|
self.listview.append_column(col)
|
||||||
|
|
||||||
|
cRender = gtk.CellRendererText()
|
||||||
|
cRender.set_property("wrap-mode", pango.WRAP_WORD_CHAR)
|
||||||
|
cRender.set_property('editable', editable)
|
||||||
|
cRender.connect('edited', self.edited_cb, (self.liststore,n))
|
||||||
|
|
||||||
|
col.pack_start(cRender, True)
|
||||||
|
col.add_attribute(cRender, 'text', n)
|
||||||
|
col.set_max_width(1000)
|
||||||
|
col.set_spacing(0) # no effect
|
||||||
|
self.listcols.append(col)
|
||||||
|
col.set_clickable(True)
|
||||||
|
col.connect("clicked", self.sortCols, n)
|
||||||
|
|
||||||
|
return(col)
|
||||||
|
|
||||||
|
def edited_cb(self, cell, path, new_text, user_data):
|
||||||
|
liststore, col = user_data
|
||||||
|
valid = True
|
||||||
|
name = self.liststore[path][self.COL_NAME]
|
||||||
|
|
||||||
|
# Validate new value (only for dbms so far, but dbms now not updateable so no validation at all!)
|
||||||
|
#if col == self.COL_DBMS:
|
||||||
|
# if new_text not in Configuration.DATABASE_TYPES:
|
||||||
|
# valid = False
|
||||||
|
|
||||||
|
if valid:
|
||||||
|
self.liststore[path][col] = new_text
|
||||||
|
|
||||||
|
self.config.set_db_parameters( db_server = self.liststore[path][self.COL_DBMS]
|
||||||
|
, db_name = name
|
||||||
|
, db_ip = self.liststore[path][self.COL_HOST]
|
||||||
|
, db_user = self.liststore[path][self.COL_USER]
|
||||||
|
, db_pass = self.liststore[path][self.COL_PASS] )
|
||||||
|
|
||||||
|
return
|
||||||
|
|
||||||
|
def check_new_name(self, path, new_text):
|
||||||
|
name_ok = True
|
||||||
|
for i,db in enumerate(self.liststore):
|
||||||
|
if i != path and new_text == db[self.COL_NAME]:
|
||||||
|
name_ok = False
|
||||||
|
#TODO: popup an error message telling user names must be unique
|
||||||
|
return name_ok
|
||||||
|
|
||||||
|
def addTextObjColumn(self, title, n):
|
||||||
|
col = gtk.TreeViewColumn(title)
|
||||||
|
self.listview.append_column(col)
|
||||||
|
|
||||||
|
cRenderT = gtk.CellRendererText()
|
||||||
|
cRenderT.set_property("wrap-mode", pango.WRAP_WORD_CHAR)
|
||||||
|
col.pack_start(cRenderT, False)
|
||||||
|
col.add_attribute(cRenderT, 'text', n)
|
||||||
|
|
||||||
|
cRenderP = gtk.CellRendererPixbuf()
|
||||||
|
col.pack_start(cRenderP, False)
|
||||||
|
col.add_attribute(cRenderP, 'stock-id', n+1)
|
||||||
|
|
||||||
|
col.set_max_width(1000)
|
||||||
|
col.set_spacing(0) # no effect
|
||||||
|
self.listcols.append(col)
|
||||||
|
#col.set_clickable(True)
|
||||||
|
#col.connect("clicked", self.sortCols, p)
|
||||||
|
return(col)
|
||||||
|
|
||||||
|
def loadDbs(self):
|
||||||
|
|
||||||
|
self.liststore.clear()
|
||||||
|
self.listcols = []
|
||||||
|
self.dbs = [] # list of tuples: (dbms, name, comment, user, passwd, host, status, icon)
|
||||||
|
|
||||||
|
try:
|
||||||
|
# want to fill: dbms, name, comment, user, passwd, host, status(, icon?)
|
||||||
|
for name in self.config.supported_databases: #db_ip/db_user/db_pass/db_server
|
||||||
|
dbms = self.config.supported_databases[name].db_server # mysql/postgresql/sqlite
|
||||||
|
dbms_num = self.config.get_backend(dbms) # 2 / 3 / 4
|
||||||
|
comment = ""
|
||||||
|
if dbms == 'sqlite':
|
||||||
|
user = ""
|
||||||
|
passwd = ""
|
||||||
|
else:
|
||||||
|
user = self.config.supported_databases[name].db_user
|
||||||
|
passwd = self.config.supported_databases[name].db_pass
|
||||||
|
host = self.config.supported_databases[name].db_ip
|
||||||
|
status = ""
|
||||||
|
icon = None
|
||||||
|
err_msg = ""
|
||||||
|
|
||||||
|
db = Database.Database(self.config, sql = None, autoconnect = False)
|
||||||
|
# try to connect to db, set status and err_msg if it fails
|
||||||
|
try:
|
||||||
|
# is creating empty db for sqlite ... mod db.py further?
|
||||||
|
# add noDbTables flag to db.py?
|
||||||
|
db.connect(backend=dbms_num, host=host, database=name, user=user, password=passwd, create=False)
|
||||||
|
if db.connected:
|
||||||
|
status = 'ok'
|
||||||
|
icon = gtk.STOCK_APPLY
|
||||||
|
if db.wrongDbVersion:
|
||||||
|
status = 'old'
|
||||||
|
icon = gtk.STOCK_INFO
|
||||||
|
except Exceptions.FpdbMySQLAccessDenied:
|
||||||
|
err_msg = "MySQL Server reports: Access denied. Are your permissions set correctly?"
|
||||||
|
status = "failed"
|
||||||
|
icon = gtk.STOCK_CANCEL
|
||||||
|
except Exceptions.FpdbMySQLNoDatabase:
|
||||||
|
err_msg = "MySQL client reports: 2002 or 2003 error. Unable to connect - " \
|
||||||
|
+ "Please check that the MySQL service has been started"
|
||||||
|
status = "failed"
|
||||||
|
icon = gtk.STOCK_CANCEL
|
||||||
|
except Exceptions.FpdbPostgresqlAccessDenied:
|
||||||
|
err_msg = "Postgres Server reports: Access denied. Are your permissions set correctly?"
|
||||||
|
status = "failed"
|
||||||
|
except Exceptions.FpdbPostgresqlNoDatabase:
|
||||||
|
err_msg = "Postgres client reports: Unable to connect - " \
|
||||||
|
+ "Please check that the Postgres service has been started"
|
||||||
|
status = "failed"
|
||||||
|
icon = gtk.STOCK_CANCEL
|
||||||
|
except:
|
||||||
|
err = traceback.extract_tb(sys.exc_info()[2])[-1]
|
||||||
|
log.info( 'db connection to '+str(dbms_num)+','+host+','+name+','+user+','+passwd+' failed: '
|
||||||
|
+ err[2] + "(" + str(err[1]) + "): " + str(sys.exc_info()[1]) )
|
||||||
|
status = "failed"
|
||||||
|
icon = gtk.STOCK_CANCEL
|
||||||
|
|
||||||
|
b = gtk.Button(name)
|
||||||
|
b.show()
|
||||||
|
iter = self.liststore.append( (dbms, name, comment, user, passwd, host, status, icon) )
|
||||||
|
|
||||||
|
self.listview.show()
|
||||||
|
scrolledwindow.show()
|
||||||
|
self.vbox.show()
|
||||||
|
self.dia.set_focus(self.listview)
|
||||||
|
|
||||||
|
self.vbox.show_all()
|
||||||
|
self.dia.show()
|
||||||
|
except:
|
||||||
|
err = traceback.extract_tb(sys.exc_info()[2])[-1]
|
||||||
|
print 'loaddbs error: '+str(dbms_num)+','+host+','+name+','+user+','+passwd+' failed: ' \
|
||||||
|
+ err[2] + "(" + str(err[1]) + "): " + str(sys.exc_info()[1])
|
||||||
|
|
||||||
|
def sortCols(self, col, n):
|
||||||
|
try:
|
||||||
|
if not col.get_sort_indicator() or col.get_sort_order() == gtk.SORT_ASCENDING:
|
||||||
|
col.set_sort_order(gtk.SORT_DESCENDING)
|
||||||
|
else:
|
||||||
|
col.set_sort_order(gtk.SORT_ASCENDING)
|
||||||
|
self.liststore.set_sort_column_id(n, col.get_sort_order())
|
||||||
|
#self.liststore.set_sort_func(n, self.sortnums, (n,grid))
|
||||||
|
for i in xrange(len(self.listcols)):
|
||||||
|
self.listcols[i].set_sort_indicator(False)
|
||||||
|
self.listcols[n].set_sort_indicator(True)
|
||||||
|
# use this listcols[col].set_sort_indicator(True)
|
||||||
|
# to turn indicator off for other cols
|
||||||
|
except:
|
||||||
|
err = traceback.extract_tb(sys.exc_info()[2])
|
||||||
|
print "***sortCols error: " + str(sys.exc_info()[1])
|
||||||
|
print "\n".join( [e[0]+':'+str(e[1])+" "+e[2] for e in err] )
|
||||||
|
|
||||||
|
def refresh(self, widget, data):
|
||||||
|
self.loadDbs()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
if __name__=="__main__":
|
||||||
|
|
||||||
|
config = Configuration.Config()
|
||||||
|
|
||||||
|
win = gtk.Window(gtk.WINDOW_TOPLEVEL)
|
||||||
|
win.set_title("Test Log Viewer")
|
||||||
|
win.set_border_width(1)
|
||||||
|
win.set_default_size(600, 500)
|
||||||
|
win.set_resizable(True)
|
||||||
|
|
||||||
|
dia = gtk.Dialog("Log Viewer",
|
||||||
|
win,
|
||||||
|
gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT,
|
||||||
|
(gtk.STOCK_CLOSE, gtk.RESPONSE_OK))
|
||||||
|
dia.set_default_size(500, 500)
|
||||||
|
log = GuiLogView(config, win, dia.vbox)
|
||||||
|
response = dia.run()
|
||||||
|
if response == gtk.RESPONSE_ACCEPT:
|
||||||
|
pass
|
||||||
|
dia.destroy()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -27,7 +27,7 @@ from time import *
|
||||||
|
|
||||||
try:
|
try:
|
||||||
import matplotlib
|
import matplotlib
|
||||||
matplotlib.use('GTK')
|
matplotlib.use('GTKCairo')
|
||||||
from matplotlib.figure import Figure
|
from matplotlib.figure import Figure
|
||||||
from matplotlib.backends.backend_gtk import FigureCanvasGTK as FigureCanvas
|
from matplotlib.backends.backend_gtk import FigureCanvasGTK as FigureCanvas
|
||||||
from matplotlib.backends.backend_gtkagg import NavigationToolbar2GTKAgg as NavigationToolbar
|
from matplotlib.backends.backend_gtkagg import NavigationToolbar2GTKAgg as NavigationToolbar
|
||||||
|
@ -44,6 +44,7 @@ except ImportError, inst:
|
||||||
import fpdb_import
|
import fpdb_import
|
||||||
import Database
|
import Database
|
||||||
import Filters
|
import Filters
|
||||||
|
import Charset
|
||||||
|
|
||||||
class GuiGraphViewer (threading.Thread):
|
class GuiGraphViewer (threading.Thread):
|
||||||
|
|
||||||
|
@ -137,6 +138,8 @@ class GuiGraphViewer (threading.Thread):
|
||||||
heroes = self.filters.getHeroes()
|
heroes = self.filters.getHeroes()
|
||||||
siteids = self.filters.getSiteIds()
|
siteids = self.filters.getSiteIds()
|
||||||
limits = self.filters.getLimits()
|
limits = self.filters.getLimits()
|
||||||
|
games = self.filters.getGames()
|
||||||
|
|
||||||
for i in ('show', 'none'):
|
for i in ('show', 'none'):
|
||||||
if i in limits:
|
if i in limits:
|
||||||
limits.remove(i)
|
limits.remove(i)
|
||||||
|
@ -144,11 +147,10 @@ class GuiGraphViewer (threading.Thread):
|
||||||
for site in sites:
|
for site in sites:
|
||||||
if sites[site] == True:
|
if sites[site] == True:
|
||||||
sitenos.append(siteids[site])
|
sitenos.append(siteids[site])
|
||||||
c = self.db.get_cursor()
|
_hname = Charset.to_utf8(heroes[site])
|
||||||
c.execute(self.sql.query['getPlayerId'], (heroes[site],))
|
result = self.db.get_player_id(self.conf, site, _hname)
|
||||||
result = c.fetchall()
|
if result is not None:
|
||||||
if len(result) == 1:
|
playerids.append(int(result))
|
||||||
playerids.append( int(result[0][0]) )
|
|
||||||
|
|
||||||
if not sitenos:
|
if not sitenos:
|
||||||
#Should probably pop up here.
|
#Should probably pop up here.
|
||||||
|
@ -171,20 +173,49 @@ class GuiGraphViewer (threading.Thread):
|
||||||
|
|
||||||
#Get graph data from DB
|
#Get graph data from DB
|
||||||
starttime = time()
|
starttime = time()
|
||||||
(green, blue, red) = self.getRingProfitGraph(playerids, sitenos, limits)
|
(green, blue, red) = self.getRingProfitGraph(playerids, sitenos, limits, games)
|
||||||
print "Graph generated in: %s" %(time() - starttime)
|
print "Graph generated in: %s" %(time() - starttime)
|
||||||
|
|
||||||
self.ax.set_title("Profit graph for ring games")
|
|
||||||
|
|
||||||
#Set axis labels and grid overlay properites
|
#Set axis labels and grid overlay properites
|
||||||
self.ax.set_xlabel("Hands", fontsize = 12)
|
self.ax.set_xlabel("Hands", fontsize = 12)
|
||||||
self.ax.set_ylabel("$", fontsize = 12)
|
self.ax.set_ylabel("$", fontsize = 12)
|
||||||
self.ax.grid(color='g', linestyle=':', linewidth=0.2)
|
self.ax.grid(color='g', linestyle=':', linewidth=0.2)
|
||||||
if green == None or green == []:
|
if green == None or green == []:
|
||||||
|
self.ax.set_title("No Data for Player(s) Found")
|
||||||
|
green = ([ 0., 0., 0., 0., 500., 1000., 900., 800.,
|
||||||
|
700., 600., 500., 400., 300., 200., 100., 0.,
|
||||||
|
500., 1000., 1000., 1000., 1000., 1000., 1000., 1000.,
|
||||||
|
1000., 1000., 1000., 1000., 1000., 1000., 875., 750.,
|
||||||
|
625., 500., 375., 250., 125., 0., 0., 0.,
|
||||||
|
0., 500., 1000., 900., 800., 700., 600., 500.,
|
||||||
|
400., 300., 200., 100., 0., 500., 1000., 1000.])
|
||||||
|
red = ([ 0., 0., 0., 0., 500., 1000., 900., 800.,
|
||||||
|
700., 600., 500., 400., 300., 200., 100., 0.,
|
||||||
|
0., 0., 0., 0., 0., 0., 125., 250.,
|
||||||
|
375., 500., 500., 500., 500., 500., 500., 500.,
|
||||||
|
500., 500., 375., 250., 125., 0., 0., 0.,
|
||||||
|
0., 500., 1000., 900., 800., 700., 600., 500.,
|
||||||
|
400., 300., 200., 100., 0., 500., 1000., 1000.])
|
||||||
|
blue = ([ 0., 0., 0., 0., 500., 1000., 900., 800.,
|
||||||
|
700., 600., 500., 400., 300., 200., 100., 0.,
|
||||||
|
0., 0., 0., 0., 0., 0., 125., 250.,
|
||||||
|
375., 500., 625., 750., 875., 1000., 875., 750.,
|
||||||
|
625., 500., 375., 250., 125., 0., 0., 0.,
|
||||||
|
0., 500., 1000., 900., 800., 700., 600., 500.,
|
||||||
|
400., 300., 200., 100., 0., 500., 1000., 1000.])
|
||||||
|
|
||||||
|
self.ax.plot(green, color='green', label='Hands: %d\nProfit: $%.2f' %(len(green), green[-1]))
|
||||||
|
self.ax.plot(blue, color='blue', label='Showdown: $%.2f' %(blue[-1]))
|
||||||
|
self.ax.plot(red, color='red', label='Non-showdown: $%.2f' %(red[-1]))
|
||||||
|
self.graphBox.add(self.canvas)
|
||||||
|
self.canvas.show()
|
||||||
|
self.canvas.draw()
|
||||||
|
|
||||||
#TODO: Do something useful like alert user
|
#TODO: Do something useful like alert user
|
||||||
print "No hands returned by graph query"
|
#print "No hands returned by graph query"
|
||||||
else:
|
else:
|
||||||
|
self.ax.set_title("Profit graph for ring games")
|
||||||
#text = "Profit: $%.2f\nTotal Hands: %d" %(green[-1], len(green))
|
#text = "Profit: $%.2f\nTotal Hands: %d" %(green[-1], len(green))
|
||||||
#self.ax.annotate(text,
|
#self.ax.annotate(text,
|
||||||
# xy=(10, -10),
|
# xy=(10, -10),
|
||||||
|
@ -201,7 +232,6 @@ class GuiGraphViewer (threading.Thread):
|
||||||
else:
|
else:
|
||||||
self.ax.legend(loc='best', fancybox=True, shadow=True, prop=FontProperties(size='smaller'))
|
self.ax.legend(loc='best', fancybox=True, shadow=True, prop=FontProperties(size='smaller'))
|
||||||
|
|
||||||
|
|
||||||
self.graphBox.add(self.canvas)
|
self.graphBox.add(self.canvas)
|
||||||
self.canvas.show()
|
self.canvas.show()
|
||||||
self.canvas.draw()
|
self.canvas.draw()
|
||||||
|
@ -212,7 +242,7 @@ class GuiGraphViewer (threading.Thread):
|
||||||
|
|
||||||
#end of def showClicked
|
#end of def showClicked
|
||||||
|
|
||||||
def getRingProfitGraph(self, names, sites, limits):
|
def getRingProfitGraph(self, names, sites, limits, games):
|
||||||
tmp = self.sql.query['getRingProfitAllHandsPlayerIdSite']
|
tmp = self.sql.query['getRingProfitAllHandsPlayerIdSite']
|
||||||
# print "DEBUG: getRingProfitGraph"
|
# print "DEBUG: getRingProfitGraph"
|
||||||
start_date, end_date = self.filters.getDates()
|
start_date, end_date = self.filters.getDates()
|
||||||
|
@ -224,6 +254,22 @@ class GuiGraphViewer (threading.Thread):
|
||||||
sitetest = str(tuple(sites))
|
sitetest = str(tuple(sites))
|
||||||
#nametest = nametest.replace("L", "")
|
#nametest = nametest.replace("L", "")
|
||||||
|
|
||||||
|
q = []
|
||||||
|
for m in self.filters.display.items():
|
||||||
|
if m[0] == 'Games' and m[1]:
|
||||||
|
for n in games:
|
||||||
|
if games[n]:
|
||||||
|
q.append(n)
|
||||||
|
if len(q) > 0:
|
||||||
|
gametest = str(tuple(q))
|
||||||
|
gametest = gametest.replace("L", "")
|
||||||
|
gametest = gametest.replace(",)",")")
|
||||||
|
gametest = gametest.replace("u'","'")
|
||||||
|
gametest = "and gt.category in %s" % gametest
|
||||||
|
else:
|
||||||
|
gametest = "and gt.category IS NULL"
|
||||||
|
tmp = tmp.replace("<game_test>", gametest)
|
||||||
|
|
||||||
lims = [int(x) for x in limits if x.isdigit()]
|
lims = [int(x) for x in limits if x.isdigit()]
|
||||||
potlims = [int(x[0:-2]) for x in limits if len(x) > 2 and x[-2:] == 'pl']
|
potlims = [int(x[0:-2]) for x in limits if len(x) > 2 and x[-2:] == 'pl']
|
||||||
nolims = [int(x[0:-2]) for x in limits if len(x) > 2 and x[-2:] == 'nl']
|
nolims = [int(x[0:-2]) for x in limits if len(x) > 2 and x[-2:] == 'nl']
|
||||||
|
@ -273,8 +319,8 @@ class GuiGraphViewer (threading.Thread):
|
||||||
winnings = self.db.cursor.fetchall()
|
winnings = self.db.cursor.fetchall()
|
||||||
self.db.rollback()
|
self.db.rollback()
|
||||||
|
|
||||||
if winnings == ():
|
if len(winnings) == 0:
|
||||||
return None
|
return (None, None, None)
|
||||||
|
|
||||||
green = map(lambda x:float(x[1]), winnings)
|
green = map(lambda x:float(x[1]), winnings)
|
||||||
blue = map(lambda x: float(x[1]) if x[2] == True else 0.0, winnings)
|
blue = map(lambda x: float(x[1]) if x[2] == True else 0.0, winnings)
|
||||||
|
|
|
@ -26,13 +26,18 @@ import gtk
|
||||||
import gobject
|
import gobject
|
||||||
import pango
|
import pango
|
||||||
|
|
||||||
import Configuration
|
import logging
|
||||||
|
# logging has been set up in fpdb.py or HUD_main.py, use their settings:
|
||||||
|
log = logging.getLogger("logview")
|
||||||
|
|
||||||
log = Configuration.get_logger("logging.conf", "logview")
|
|
||||||
|
|
||||||
MAX_LINES = 100000 # max lines to display in window
|
MAX_LINES = 100000 # max lines to display in window
|
||||||
EST_CHARS_PER_LINE = 150 # used to guesstimate number of lines in log file
|
EST_CHARS_PER_LINE = 150 # used to guesstimate number of lines in log file
|
||||||
logfile = 'logging.out' # name of logfile
|
LOGFILES = [ [ 'Fpdb Errors', 'fpdb-errors.txt', False ] # label, filename, start value
|
||||||
|
, [ 'Fpdb Log', 'fpdb-log.txt', True ]
|
||||||
|
, [ 'HUD Errors', 'HUD-errors.txt', False ]
|
||||||
|
, [ 'HUD Log', 'HUD-log.txt', False ]
|
||||||
|
]
|
||||||
|
|
||||||
class GuiLogView:
|
class GuiLogView:
|
||||||
|
|
||||||
|
@ -41,6 +46,7 @@ class GuiLogView:
|
||||||
self.main_window = mainwin
|
self.main_window = mainwin
|
||||||
self.closeq = closeq
|
self.closeq = closeq
|
||||||
|
|
||||||
|
self.logfile = os.path.join(self.config.dir_log, LOGFILES[1][1])
|
||||||
self.dia = gtk.Dialog(title="Log Messages"
|
self.dia = gtk.Dialog(title="Log Messages"
|
||||||
,parent=None
|
,parent=None
|
||||||
,flags=gtk.DIALOG_DESTROY_WITH_PARENT
|
,flags=gtk.DIALOG_DESTROY_WITH_PARENT
|
||||||
|
@ -68,10 +74,19 @@ class GuiLogView:
|
||||||
scrolledwindow.add(self.listview)
|
scrolledwindow.add(self.listview)
|
||||||
self.vbox.pack_start(scrolledwindow, expand=True, fill=True, padding=0)
|
self.vbox.pack_start(scrolledwindow, expand=True, fill=True, padding=0)
|
||||||
|
|
||||||
|
hb = gtk.HBox(False, 0)
|
||||||
|
grp = None
|
||||||
|
for logf in LOGFILES:
|
||||||
|
rb = gtk.RadioButton(group=grp, label=logf[0], use_underline=True)
|
||||||
|
if grp is None: grp = rb
|
||||||
|
rb.set_active(logf[2])
|
||||||
|
rb.connect('clicked', self.__set_logfile, logf[0])
|
||||||
|
hb.pack_start(rb, False, False, 3)
|
||||||
refreshbutton = gtk.Button("Refresh")
|
refreshbutton = gtk.Button("Refresh")
|
||||||
refreshbutton.connect("clicked", self.refresh, None)
|
refreshbutton.connect("clicked", self.refresh, None)
|
||||||
self.vbox.pack_start(refreshbutton, False, False, 3)
|
hb.pack_start(refreshbutton, False, False, 3)
|
||||||
refreshbutton.show()
|
refreshbutton.show()
|
||||||
|
self.vbox.pack_start(hb, False, False, 0)
|
||||||
|
|
||||||
self.listview.show()
|
self.listview.show()
|
||||||
scrolledwindow.show()
|
scrolledwindow.show()
|
||||||
|
@ -89,6 +104,14 @@ class GuiLogView:
|
||||||
|
|
||||||
self.dia.connect('response', self.dialog_response_cb)
|
self.dia.connect('response', self.dialog_response_cb)
|
||||||
|
|
||||||
|
def __set_logfile(self, w, file):
|
||||||
|
#print "w is", w, "file is", file, "active is", w.get_active()
|
||||||
|
if w.get_active():
|
||||||
|
for logf in LOGFILES:
|
||||||
|
if logf[0] == file:
|
||||||
|
self.logfile = os.path.join(self.config.dir_log, logf[1])
|
||||||
|
self.refresh(w, file) # params are not used
|
||||||
|
|
||||||
def dialog_response_cb(self, dialog, response_id):
|
def dialog_response_cb(self, dialog, response_id):
|
||||||
# this is called whether close button is pressed or window is closed
|
# this is called whether close button is pressed or window is closed
|
||||||
self.closeq.put(self.__class__)
|
self.closeq.put(self.__class__)
|
||||||
|
@ -117,10 +140,10 @@ class GuiLogView:
|
||||||
self.listcols = []
|
self.listcols = []
|
||||||
|
|
||||||
# guesstimate number of lines in file
|
# guesstimate number of lines in file
|
||||||
if os.path.exists(logfile):
|
if os.path.exists(self.logfile):
|
||||||
stat_info = os.stat(logfile)
|
stat_info = os.stat(self.logfile)
|
||||||
lines = stat_info.st_size / EST_CHARS_PER_LINE
|
lines = stat_info.st_size / EST_CHARS_PER_LINE
|
||||||
print "logview: size =", stat_info.st_size, "lines =", lines
|
#print "logview: size =", stat_info.st_size, "lines =", lines
|
||||||
|
|
||||||
# set startline to line number to start display from
|
# set startline to line number to start display from
|
||||||
startline = 0
|
startline = 0
|
||||||
|
@ -129,12 +152,15 @@ class GuiLogView:
|
||||||
startline = lines - MAX_LINES
|
startline = lines - MAX_LINES
|
||||||
|
|
||||||
l = 0
|
l = 0
|
||||||
for line in open(logfile):
|
for line in open(self.logfile):
|
||||||
# eg line:
|
# example line in logfile format:
|
||||||
# 2009-12-02 15:23:21,716 - config DEBUG config logger initialised
|
# 2009-12-02 15:23:21,716 - config DEBUG config logger initialised
|
||||||
l = l + 1
|
l = l + 1
|
||||||
if l > startline and len(line) > 49:
|
if l > startline:
|
||||||
|
if len(line) > 49 and line[23:26] == ' - ' and line[34:39] == ' ':
|
||||||
iter = self.liststore.append( (line[0:23], line[26:32], line[39:46], line[48:].strip(), True) )
|
iter = self.liststore.append( (line[0:23], line[26:32], line[39:46], line[48:].strip(), True) )
|
||||||
|
else:
|
||||||
|
iter = self.liststore.append( ('', '', '', line.strip(), True) )
|
||||||
|
|
||||||
def sortCols(self, col, n):
|
def sortCols(self, col, n):
|
||||||
try:
|
try:
|
||||||
|
|
|
@ -27,8 +27,8 @@ from time import time, strftime
|
||||||
import Card
|
import Card
|
||||||
import fpdb_import
|
import fpdb_import
|
||||||
import Database
|
import Database
|
||||||
import fpdb_db
|
|
||||||
import Filters
|
import Filters
|
||||||
|
import Charset
|
||||||
|
|
||||||
colalias,colshow,colheading,colxalign,colformat,coltype = 0,1,2,3,4,5
|
colalias,colshow,colheading,colxalign,colformat,coltype = 0,1,2,3,4,5
|
||||||
ranks = {'x':0, '2':2, '3':3, '4':4, '5':5, '6':6, '7':7, '8':8, '9':9, 'T':10, 'J':11, 'Q':12, 'K':13, 'A':14}
|
ranks = {'x':0, '2':2, '3':3, '4':4, '5':5, '6':6, '7':7, '8':8, '9':9, 'T':10, 'J':11, 'Q':12, 'K':13, 'A':14}
|
||||||
|
@ -64,7 +64,7 @@ class GuiPlayerStats (threading.Thread):
|
||||||
|
|
||||||
filters_display = { "Heroes" : True,
|
filters_display = { "Heroes" : True,
|
||||||
"Sites" : True,
|
"Sites" : True,
|
||||||
"Games" : False,
|
"Games" : True,
|
||||||
"Limits" : True,
|
"Limits" : True,
|
||||||
"LimitSep" : True,
|
"LimitSep" : True,
|
||||||
"LimitType" : True,
|
"LimitType" : True,
|
||||||
|
@ -92,7 +92,7 @@ class GuiPlayerStats (threading.Thread):
|
||||||
, ["hand", False, "Hand", 0.0, "%s", "str"] # true not allowed for this line
|
, ["hand", False, "Hand", 0.0, "%s", "str"] # true not allowed for this line
|
||||||
, ["plposition", False, "Posn", 1.0, "%s", "str"] # true not allowed for this line (set in code)
|
, ["plposition", False, "Posn", 1.0, "%s", "str"] # true not allowed for this line (set in code)
|
||||||
, ["pname", False, "Name", 0.0, "%s", "str"] # true not allowed for this line (set in code)
|
, ["pname", False, "Name", 0.0, "%s", "str"] # true not allowed for this line (set in code)
|
||||||
, ["n", True, "Hds", 1.0, "%d", "str"]
|
, ["n", True, "Hds", 1.0, "%1.0f", "str"]
|
||||||
, ["avgseats", False, "Seats", 1.0, "%3.1f", "str"]
|
, ["avgseats", False, "Seats", 1.0, "%3.1f", "str"]
|
||||||
, ["vpip", True, "VPIP", 1.0, "%3.1f", "str"]
|
, ["vpip", True, "VPIP", 1.0, "%3.1f", "str"]
|
||||||
, ["pfr", True, "PFR", 1.0, "%3.1f", "str"]
|
, ["pfr", True, "PFR", 1.0, "%3.1f", "str"]
|
||||||
|
@ -142,7 +142,7 @@ class GuiPlayerStats (threading.Thread):
|
||||||
self.stats_frame = gtk.Frame()
|
self.stats_frame = gtk.Frame()
|
||||||
self.stats_frame.show()
|
self.stats_frame.show()
|
||||||
|
|
||||||
self.stats_vbox = gtk.VBox(False, 0)
|
self.stats_vbox = gtk.VPaned()
|
||||||
self.stats_vbox.show()
|
self.stats_vbox.show()
|
||||||
self.stats_frame.add(self.stats_vbox)
|
self.stats_frame.add(self.stats_vbox)
|
||||||
# self.fillStatsFrame(self.stats_vbox)
|
# self.fillStatsFrame(self.stats_vbox)
|
||||||
|
@ -155,12 +155,15 @@ class GuiPlayerStats (threading.Thread):
|
||||||
|
|
||||||
# make sure Hand column is not displayed
|
# make sure Hand column is not displayed
|
||||||
[x for x in self.columns if x[0] == 'hand'][0][1] = False
|
[x for x in self.columns if x[0] == 'hand'][0][1] = False
|
||||||
|
self.last_pos = -1
|
||||||
|
|
||||||
|
|
||||||
def get_vbox(self):
|
def get_vbox(self):
|
||||||
"""returns the vbox of this thread"""
|
"""returns the vbox of this thread"""
|
||||||
return self.main_hbox
|
return self.main_hbox
|
||||||
|
|
||||||
def refreshStats(self, widget, data):
|
def refreshStats(self, widget, data):
|
||||||
|
self.last_pos = self.stats_vbox.get_position()
|
||||||
try: self.stats_vbox.destroy()
|
try: self.stats_vbox.destroy()
|
||||||
except AttributeError: pass
|
except AttributeError: pass
|
||||||
self.liststore = []
|
self.liststore = []
|
||||||
|
@ -170,6 +173,8 @@ class GuiPlayerStats (threading.Thread):
|
||||||
self.stats_vbox.show()
|
self.stats_vbox.show()
|
||||||
self.stats_frame.add(self.stats_vbox)
|
self.stats_frame.add(self.stats_vbox)
|
||||||
self.fillStatsFrame(self.stats_vbox)
|
self.fillStatsFrame(self.stats_vbox)
|
||||||
|
if self.last_pos > 0:
|
||||||
|
self.stats_vbox.set_position(self.last_pos)
|
||||||
|
|
||||||
def fillStatsFrame(self, vbox):
|
def fillStatsFrame(self, vbox):
|
||||||
sites = self.filters.getSites()
|
sites = self.filters.getSites()
|
||||||
|
@ -180,6 +185,7 @@ class GuiPlayerStats (threading.Thread):
|
||||||
seats = self.filters.getSeats()
|
seats = self.filters.getSeats()
|
||||||
groups = self.filters.getGroups()
|
groups = self.filters.getGroups()
|
||||||
dates = self.filters.getDates()
|
dates = self.filters.getDates()
|
||||||
|
games = self.filters.getGames()
|
||||||
sitenos = []
|
sitenos = []
|
||||||
playerids = []
|
playerids = []
|
||||||
|
|
||||||
|
@ -187,12 +193,10 @@ class GuiPlayerStats (threading.Thread):
|
||||||
for site in sites:
|
for site in sites:
|
||||||
if sites[site] == True:
|
if sites[site] == True:
|
||||||
sitenos.append(siteids[site])
|
sitenos.append(siteids[site])
|
||||||
# Nasty hack to deal with multiple sites + same player name -Eric
|
_hname = Charset.to_utf8(heroes[site])
|
||||||
que = self.sql.query['getPlayerId'] + " AND siteId=%d" % siteids[site]
|
result = self.db.get_player_id(self.conf, site, _hname)
|
||||||
self.cursor.execute(que, (heroes[site],))
|
if result is not None:
|
||||||
result = self.db.cursor.fetchall()
|
playerids.append(int(result))
|
||||||
if len(result) == 1:
|
|
||||||
playerids.append(result[0][0])
|
|
||||||
|
|
||||||
if not sitenos:
|
if not sitenos:
|
||||||
#Should probably pop up here.
|
#Should probably pop up here.
|
||||||
|
@ -205,10 +209,11 @@ class GuiPlayerStats (threading.Thread):
|
||||||
print "No limits found"
|
print "No limits found"
|
||||||
return
|
return
|
||||||
|
|
||||||
self.createStatsTable(vbox, playerids, sitenos, limits, type, seats, groups, dates)
|
self.createStatsTable(vbox, playerids, sitenos, limits, type, seats, groups, dates, games)
|
||||||
|
|
||||||
def createStatsTable(self, vbox, playerids, sitenos, limits, type, seats, groups, dates):
|
def createStatsTable(self, vbox, playerids, sitenos, limits, type, seats, groups, dates, games):
|
||||||
starttime = time()
|
starttime = time()
|
||||||
|
show_detail = True
|
||||||
|
|
||||||
# Scrolled window for summary table
|
# Scrolled window for summary table
|
||||||
swin = gtk.ScrolledWindow(hadjustment=None, vadjustment=None)
|
swin = gtk.ScrolledWindow(hadjustment=None, vadjustment=None)
|
||||||
|
@ -223,8 +228,13 @@ class GuiPlayerStats (threading.Thread):
|
||||||
# gridnum - index for grid data structures
|
# gridnum - index for grid data structures
|
||||||
flags = [False, self.filters.getNumHands(), 0]
|
flags = [False, self.filters.getNumHands(), 0]
|
||||||
self.addGrid(swin, 'playerDetailedStats', flags, playerids
|
self.addGrid(swin, 'playerDetailedStats', flags, playerids
|
||||||
,sitenos, limits, type, seats, groups, dates)
|
,sitenos, limits, type, seats, groups, dates, games)
|
||||||
|
|
||||||
|
if 'allplayers' in groups and groups['allplayers']:
|
||||||
|
# can't currently do this combination so skip detailed table
|
||||||
|
show_detail = False
|
||||||
|
|
||||||
|
if show_detail:
|
||||||
# Separator
|
# Separator
|
||||||
vbox2 = gtk.VBox(False, 0)
|
vbox2 = gtk.VBox(False, 0)
|
||||||
heading = gtk.Label(self.filterText['handhead'])
|
heading = gtk.Label(self.filterText['handhead'])
|
||||||
|
@ -243,7 +253,7 @@ class GuiPlayerStats (threading.Thread):
|
||||||
flags[0] = True
|
flags[0] = True
|
||||||
flags[2] = 1
|
flags[2] = 1
|
||||||
self.addGrid(swin, 'playerDetailedStats', flags, playerids
|
self.addGrid(swin, 'playerDetailedStats', flags, playerids
|
||||||
,sitenos, limits, type, seats, groups, dates)
|
,sitenos, limits, type, seats, groups, dates, games)
|
||||||
|
|
||||||
self.db.rollback()
|
self.db.rollback()
|
||||||
print "Stats page displayed in %4.2f seconds" % (time() - starttime)
|
print "Stats page displayed in %4.2f seconds" % (time() - starttime)
|
||||||
|
@ -275,7 +285,7 @@ class GuiPlayerStats (threading.Thread):
|
||||||
except: a = 0.0
|
except: a = 0.0
|
||||||
try: b = float(b)
|
try: b = float(b)
|
||||||
except: b = 0.0
|
except: b = 0.0
|
||||||
if n == 0:
|
if n == 0 and grid == 1: #make sure it only works on the starting hands
|
||||||
a1,a2,a3 = ranks[a[0]], ranks[a[1]], (a+'o')[2]
|
a1,a2,a3 = ranks[a[0]], ranks[a[1]], (a+'o')[2]
|
||||||
b1,b2,b3 = ranks[b[0]], ranks[b[1]], (b+'o')[2]
|
b1,b2,b3 = ranks[b[0]], ranks[b[1]], (b+'o')[2]
|
||||||
if a1 > b1 or ( a1 == b1 and (a2 > b2 or (a2 == b2 and a3 > b3) ) ):
|
if a1 > b1 or ( a1 == b1 and (a2 > b2 or (a2 == b2 and a3 > b3) ) ):
|
||||||
|
@ -317,7 +327,7 @@ class GuiPlayerStats (threading.Thread):
|
||||||
print "***sortcols error: " + str(sys.exc_info()[1])
|
print "***sortcols error: " + str(sys.exc_info()[1])
|
||||||
print "\n".join( [e[0]+':'+str(e[1])+" "+e[2] for e in err] )
|
print "\n".join( [e[0]+':'+str(e[1])+" "+e[2] for e in err] )
|
||||||
|
|
||||||
def addGrid(self, vbox, query, flags, playerids, sitenos, limits, type, seats, groups, dates):
|
def addGrid(self, vbox, query, flags, playerids, sitenos, limits, type, seats, groups, dates, games):
|
||||||
counter = 0
|
counter = 0
|
||||||
row = 0
|
row = 0
|
||||||
sqlrow = 0
|
sqlrow = 0
|
||||||
|
@ -325,7 +335,7 @@ class GuiPlayerStats (threading.Thread):
|
||||||
else: holecards,grid = flags[0],flags[2]
|
else: holecards,grid = flags[0],flags[2]
|
||||||
|
|
||||||
tmp = self.sql.query[query]
|
tmp = self.sql.query[query]
|
||||||
tmp = self.refineQuery(tmp, flags, playerids, sitenos, limits, type, seats, groups, dates)
|
tmp = self.refineQuery(tmp, flags, playerids, sitenos, limits, type, seats, groups, dates, games)
|
||||||
self.cursor.execute(tmp)
|
self.cursor.execute(tmp)
|
||||||
result = self.cursor.fetchall()
|
result = self.cursor.fetchall()
|
||||||
colnames = [desc[0].lower() for desc in self.cursor.description]
|
colnames = [desc[0].lower() for desc in self.cursor.description]
|
||||||
|
@ -422,13 +432,14 @@ class GuiPlayerStats (threading.Thread):
|
||||||
else:
|
else:
|
||||||
treerow.append(' ')
|
treerow.append(' ')
|
||||||
iter = self.liststore[grid].append(treerow)
|
iter = self.liststore[grid].append(treerow)
|
||||||
|
#print treerow
|
||||||
sqlrow += 1
|
sqlrow += 1
|
||||||
row += 1
|
row += 1
|
||||||
vbox.show_all()
|
vbox.show_all()
|
||||||
|
|
||||||
#end def addGrid(self, query, vars, playerids, sitenos, limits, type, seats, groups, dates):
|
#end def addGrid(self, query, vars, playerids, sitenos, limits, type, seats, groups, dates):
|
||||||
|
|
||||||
def refineQuery(self, query, flags, playerids, sitenos, limits, type, seats, groups, dates):
|
def refineQuery(self, query, flags, playerids, sitenos, limits, type, seats, groups, dates, games):
|
||||||
having = ''
|
having = ''
|
||||||
if not flags:
|
if not flags:
|
||||||
holecards = False
|
holecards = False
|
||||||
|
@ -466,6 +477,39 @@ class GuiPlayerStats (threading.Thread):
|
||||||
query = query.replace("<playerName>", pname)
|
query = query.replace("<playerName>", pname)
|
||||||
query = query.replace("<havingclause>", having)
|
query = query.replace("<havingclause>", having)
|
||||||
|
|
||||||
|
gametest = ""
|
||||||
|
q = []
|
||||||
|
for m in self.filters.display.items():
|
||||||
|
if m[0] == 'Games' and m[1]:
|
||||||
|
for n in games:
|
||||||
|
if games[n]:
|
||||||
|
q.append(n)
|
||||||
|
if len(q) > 0:
|
||||||
|
gametest = str(tuple(q))
|
||||||
|
gametest = gametest.replace("L", "")
|
||||||
|
gametest = gametest.replace(",)",")")
|
||||||
|
gametest = gametest.replace("u'","'")
|
||||||
|
gametest = "and gt.category in %s" % gametest
|
||||||
|
else:
|
||||||
|
gametest = "and gt.category IS NULL"
|
||||||
|
query = query.replace("<game_test>", gametest)
|
||||||
|
|
||||||
|
sitetest = ""
|
||||||
|
q = []
|
||||||
|
for m in self.filters.display.items():
|
||||||
|
if m[0] == 'Sites' and m[1]:
|
||||||
|
for n in sitenos:
|
||||||
|
q.append(n)
|
||||||
|
if len(q) > 0:
|
||||||
|
sitetest = str(tuple(q))
|
||||||
|
sitetest = sitetest.replace("L", "")
|
||||||
|
sitetest = sitetest.replace(",)",")")
|
||||||
|
sitetest = sitetest.replace("u'","'")
|
||||||
|
sitetest = "and gt.siteId in %s" % sitetest
|
||||||
|
else:
|
||||||
|
sitetest = "and gt.siteId IS NULL"
|
||||||
|
query = query.replace("<site_test>", sitetest)
|
||||||
|
|
||||||
if seats:
|
if seats:
|
||||||
query = query.replace('<seats_test>', 'between ' + str(seats['from']) + ' and ' + str(seats['to']))
|
query = query.replace('<seats_test>', 'between ' + str(seats['from']) + ' and ' + str(seats['to']))
|
||||||
if 'show' in seats and seats['show']:
|
if 'show' in seats and seats['show']:
|
||||||
|
@ -510,14 +554,14 @@ class GuiPlayerStats (threading.Thread):
|
||||||
if type == 'ring':
|
if type == 'ring':
|
||||||
bbtest = bbtest + " and gt.type = 'ring' "
|
bbtest = bbtest + " and gt.type = 'ring' "
|
||||||
elif type == 'tour':
|
elif type == 'tour':
|
||||||
bbtest = bbtest + " and gt.type = 'tour' "
|
bbtest = " and gt.type = 'tour' "
|
||||||
query = query.replace("<gtbigBlind_test>", bbtest)
|
query = query.replace("<gtbigBlind_test>", bbtest)
|
||||||
|
|
||||||
if holecards: # re-use level variables for hole card query
|
if holecards: # re-use level variables for hole card query
|
||||||
query = query.replace("<hgameTypeId>", "hp.startcards")
|
query = query.replace("<hgameTypeId>", "hp.startcards")
|
||||||
query = query.replace("<orderbyhgameTypeId>"
|
query = query.replace("<orderbyhgameTypeId>"
|
||||||
, ",case when floor(hp.startcards/13) >= mod(hp.startcards,13) then hp.startcards + 0.1 "
|
, ",case when floor((hp.startcards-1)/13) >= mod((hp.startcards-1),13) then hp.startcards + 0.1 "
|
||||||
+ " else 13*mod(hp.startcards,13) + floor(hp.startcards/13) "
|
+ " else 13*mod((hp.startcards-1),13) + floor((hp.startcards-1)/13) + 1 "
|
||||||
+ " end desc ")
|
+ " end desc ")
|
||||||
else:
|
else:
|
||||||
query = query.replace("<orderbyhgameTypeId>", "")
|
query = query.replace("<orderbyhgameTypeId>", "")
|
||||||
|
|
|
@ -33,7 +33,7 @@ try:
|
||||||
from matplotlib.backends.backend_gtkagg import NavigationToolbar2GTKAgg as NavigationToolbar
|
from matplotlib.backends.backend_gtkagg import NavigationToolbar2GTKAgg as NavigationToolbar
|
||||||
from matplotlib.finance import candlestick2
|
from matplotlib.finance import candlestick2
|
||||||
|
|
||||||
from numpy import diff, nonzero, sum, cumsum, max, min
|
from numpy import diff, nonzero, sum, cumsum, max, min, append
|
||||||
# from matplotlib.dates import DateFormatter, WeekdayLocator, HourLocator, \
|
# from matplotlib.dates import DateFormatter, WeekdayLocator, HourLocator, \
|
||||||
# DayLocator, MONDAY, timezone
|
# DayLocator, MONDAY, timezone
|
||||||
|
|
||||||
|
@ -47,6 +47,7 @@ import fpdb_import
|
||||||
import Database
|
import Database
|
||||||
import Filters
|
import Filters
|
||||||
import FpdbSQLQueries
|
import FpdbSQLQueries
|
||||||
|
import Charset
|
||||||
|
|
||||||
class GuiSessionViewer (threading.Thread):
|
class GuiSessionViewer (threading.Thread):
|
||||||
def __init__(self, config, querylist, mainwin, debug=True):
|
def __init__(self, config, querylist, mainwin, debug=True):
|
||||||
|
@ -181,7 +182,10 @@ class GuiSessionViewer (threading.Thread):
|
||||||
for site in sites:
|
for site in sites:
|
||||||
if sites[site] == True:
|
if sites[site] == True:
|
||||||
sitenos.append(siteids[site])
|
sitenos.append(siteids[site])
|
||||||
self.cursor.execute(self.sql.query['getPlayerId'], (heroes[site],))
|
_q = self.sql.query['getPlayerId']
|
||||||
|
_name = Charset.to_utf8(heroes[site])
|
||||||
|
#print 'DEBUG(_name) :: %s' % _name
|
||||||
|
self.cursor.execute(_q, (_name,)) # arg = tuple
|
||||||
result = self.db.cursor.fetchall()
|
result = self.db.cursor.fetchall()
|
||||||
if len(result) == 1:
|
if len(result) == 1:
|
||||||
playerids.append(result[0][0])
|
playerids.append(result[0][0])
|
||||||
|
@ -237,6 +241,9 @@ class GuiSessionViewer (threading.Thread):
|
||||||
#end def fillStatsFrame(self, vbox):
|
#end def fillStatsFrame(self, vbox):
|
||||||
|
|
||||||
def generateDatasets(self, playerids, sitenos, limits, seats):
|
def generateDatasets(self, playerids, sitenos, limits, seats):
|
||||||
|
THRESHOLD = 1800 # Minimum number of seconds between consecutive hands before being considered a new session
|
||||||
|
PADDING = 5 # Additional time in minutes to add to a session, session startup, shutdown etc (FiXME: user configurable)
|
||||||
|
|
||||||
# Get a list of all handids and their timestampts
|
# Get a list of all handids and their timestampts
|
||||||
#FIXME: Query still need to filter on blind levels
|
#FIXME: Query still need to filter on blind levels
|
||||||
|
|
||||||
|
@ -251,23 +258,31 @@ class GuiSessionViewer (threading.Thread):
|
||||||
q = q.replace("<ampersand_s>", "%s")
|
q = q.replace("<ampersand_s>", "%s")
|
||||||
|
|
||||||
self.db.cursor.execute(q)
|
self.db.cursor.execute(q)
|
||||||
THRESHOLD = 1800
|
|
||||||
hands = self.db.cursor.fetchall()
|
hands = self.db.cursor.fetchall()
|
||||||
|
|
||||||
# Take that list and create an array of the time between hands
|
# Take that list and create an array of the time between hands
|
||||||
times = map(lambda x:long(x[0]), hands)
|
times = map(lambda x:long(x[0]), hands)
|
||||||
handids = map(lambda x:int(x[1]), hands)
|
handids = map(lambda x:int(x[1]), hands)
|
||||||
winnings = map(lambda x:float(x[4]), hands)
|
winnings = map(lambda x:float(x[4]), hands)
|
||||||
print "DEBUG: len(times) %s" %(len(times))
|
#print "DEBUG: len(times) %s" %(len(times))
|
||||||
diffs = diff(times) # This array is the difference in starttime between consecutive hands
|
diffs = diff(times) # This array is the difference in starttime between consecutive hands
|
||||||
index = nonzero(diff(times) > THRESHOLD) # This array represents the indexes into 'times' for start/end times of sessions
|
diffs2 = append(diffs,THRESHOLD + 1) # Append an additional session to the end of the diffs, so the next line
|
||||||
# ie. times[index[0][0]] is the end of the first session
|
# includes an index into the last 'session'
|
||||||
|
index = nonzero(diffs2 > THRESHOLD) # This array represents the indexes into 'times' for start/end times of sessions
|
||||||
|
# times[index[0][0]] is the end of the first session,
|
||||||
#print "DEBUG: len(index[0]) %s" %(len(index[0]))
|
#print "DEBUG: len(index[0]) %s" %(len(index[0]))
|
||||||
|
if len(index[0]) > 0:
|
||||||
|
#print "DEBUG: index[0][0] %s" %(index[0][0])
|
||||||
|
#print "DEBUG: index %s" %(index)
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
index = [[0]]
|
||||||
#print "DEBUG: index %s" %(index)
|
#print "DEBUG: index %s" %(index)
|
||||||
#print "DEBUG: index[0][0] %s" %(index[0][0])
|
#print "DEBUG: index[0][0] %s" %(index[0][0])
|
||||||
|
pass
|
||||||
|
|
||||||
total = 0
|
total = 0
|
||||||
last_idx = 0
|
first_idx = 0
|
||||||
lowidx = 0
|
lowidx = 0
|
||||||
uppidx = 0
|
uppidx = 0
|
||||||
opens = []
|
opens = []
|
||||||
|
@ -277,27 +292,36 @@ class GuiSessionViewer (threading.Thread):
|
||||||
results = []
|
results = []
|
||||||
cum_sum = cumsum(winnings)
|
cum_sum = cumsum(winnings)
|
||||||
cum_sum = cum_sum/100
|
cum_sum = cum_sum/100
|
||||||
|
sid = 1
|
||||||
# Take all results and format them into a list for feeding into gui model.
|
# Take all results and format them into a list for feeding into gui model.
|
||||||
for i in range(len(index[0])):
|
for i in range(len(index[0])):
|
||||||
sid = i # Session id
|
hds = index[0][i] - first_idx + 1 # Number of hands in session
|
||||||
hds = index[0][i] - last_idx # Number of hands in session
|
|
||||||
if hds > 0:
|
if hds > 0:
|
||||||
stime = strftime("%d/%m/%Y %H:%M", localtime(times[last_idx])) # Formatted start time
|
stime = strftime("%d/%m/%Y %H:%M", localtime(times[first_idx])) # Formatted start time
|
||||||
etime = strftime("%d/%m/%Y %H:%M", localtime(times[index[0][i]])) # Formatted end time
|
etime = strftime("%d/%m/%Y %H:%M", localtime(times[index[0][i]])) # Formatted end time
|
||||||
hph = (times[index[0][i]] - times[last_idx])/60 # Hands per hour
|
minutesplayed = (times[index[0][i]] - times[first_idx])/60
|
||||||
won = sum(winnings[last_idx:index[0][i]])/100.0
|
if minutesplayed == 0:
|
||||||
hwm = max(cum_sum[last_idx:index[0][i]])
|
minutesplayed = 1
|
||||||
lwm = min(cum_sum[last_idx:index[0][i]])
|
minutesplayed = minutesplayed + PADDING
|
||||||
#print "DEBUG: range: (%s, %s) - (min, max): (%s, %s)" %(last_idx, index[0][i], hwm, lwm)
|
hph = hds*60/minutesplayed # Hands per hour
|
||||||
|
won = sum(winnings[first_idx:index[0][i]])/100.0
|
||||||
|
hwm = max(cum_sum[first_idx:index[0][i]])
|
||||||
|
lwm = min(cum_sum[first_idx:index[0][i]])
|
||||||
|
open = (sum(winnings[:first_idx]))/100
|
||||||
|
close = (sum(winnings[:index[0][i]]))/100
|
||||||
|
#print "DEBUG: range: (%s, %s) - (min, max): (%s, %s) - (open,close): (%s, %s)" %(first_idx, index[0][i], lwm, hwm, open, close)
|
||||||
|
|
||||||
results.append([sid, hds, stime, etime, hph, won])
|
results.append([sid, hds, stime, etime, hph, won])
|
||||||
opens.append((sum(winnings[:last_idx]))/100)
|
opens.append(open)
|
||||||
closes.append((sum(winnings[:index[0][i]]))/100)
|
closes.append(close)
|
||||||
highs.append(hwm)
|
highs.append(hwm)
|
||||||
lows.append(lwm)
|
lows.append(lwm)
|
||||||
#print "Hands in session %4s: %4s Start: %s End: %s HPH: %s Profit: %s" %(sid, hds, stime, etime, hph, won)
|
#print "DEBUG: Hands in session %4s: %4s Start: %s End: %s HPH: %s Profit: %s" %(sid, hds, stime, etime, hph, won)
|
||||||
total = total + (index[0][i] - last_idx)
|
total = total + (index[0][i] - first_idx)
|
||||||
last_idx = index[0][i] + 1
|
first_idx = index[0][i] + 1
|
||||||
|
sid = sid+1
|
||||||
|
else:
|
||||||
|
print "hds <= 0"
|
||||||
|
|
||||||
return (results, opens, closes, highs, lows)
|
return (results, opens, closes, highs, lows)
|
||||||
|
|
||||||
|
@ -326,11 +350,6 @@ class GuiSessionViewer (threading.Thread):
|
||||||
def generateGraph(self, opens, closes, highs, lows):
|
def generateGraph(self, opens, closes, highs, lows):
|
||||||
self.clearGraphData()
|
self.clearGraphData()
|
||||||
|
|
||||||
#FIXME: Weird - first data entry is crashing this for me
|
|
||||||
opens = opens[1:]
|
|
||||||
closes = closes[1:]
|
|
||||||
highs = highs[1:]
|
|
||||||
lows = lows[1:]
|
|
||||||
# print "DEBUG:"
|
# print "DEBUG:"
|
||||||
# print "highs = %s" % highs
|
# print "highs = %s" % highs
|
||||||
# print "lows = %s" % lows
|
# print "lows = %s" % lows
|
||||||
|
|
|
@ -20,7 +20,6 @@ import pygtk
|
||||||
pygtk.require('2.0')
|
pygtk.require('2.0')
|
||||||
import gtk
|
import gtk
|
||||||
import os
|
import os
|
||||||
import fpdb_simple
|
|
||||||
|
|
||||||
import fpdb_import
|
import fpdb_import
|
||||||
import fpdb_db
|
import fpdb_db
|
||||||
|
@ -80,7 +79,8 @@ class GuiTableViewer (threading.Thread):
|
||||||
#then the data rows
|
#then the data rows
|
||||||
for player in range(len(self.player_names)):
|
for player in range(len(self.player_names)):
|
||||||
tmp=[]
|
tmp=[]
|
||||||
tmp.append(self.player_names[player][0])
|
p_name = Charset.to_gui(self.player_names[player][0])
|
||||||
|
tmp.append(p_name)
|
||||||
|
|
||||||
seatCount=len(self.player_names)
|
seatCount=len(self.player_names)
|
||||||
if seatCount>=8:
|
if seatCount>=8:
|
||||||
|
|
|
@ -445,62 +445,101 @@ Left-Drag to Move"
|
||||||
<location seat="9" x="70" y="53"> </location>
|
<location seat="9" x="70" y="53"> </location>
|
||||||
</layout>
|
</layout>
|
||||||
</site>
|
</site>
|
||||||
|
|
||||||
|
|
||||||
|
<site HH_path="C:/Program Files/Carbon Poker/HandHistory/YOUR SCREEN NAME HERE/" converter="CarbonToFpdb" decoder="everleaf_decode_table" enabled="True" screen_name="YOUR SCREEN NAME HERE" site_name="Carbon" site_path="C:/Program Files/Carbin/" supported_games="holdem" table_finder="Carbon Poker.exe">
|
||||||
|
<layout fav_seat="0" height="547" max="8" width="794">
|
||||||
|
<location seat="1" x="640" y="64"> </location>
|
||||||
|
<location seat="2" x="650" y="230"> </location>
|
||||||
|
<location seat="3" x="650" y="385"> </location>
|
||||||
|
<location seat="4" x="588" y="425"> </location>
|
||||||
|
<location seat="5" x="92" y="425"> </location>
|
||||||
|
<location seat="6" x="0" y="373"> </location>
|
||||||
|
<location seat="7" x="0" y="223"> </location>
|
||||||
|
<location seat="8" x="25" y="50"> </location>
|
||||||
|
</layout>
|
||||||
|
<layout fav_seat="0" height="547" max="6" width="794">
|
||||||
|
<location seat="1" x="640" y="58"> </location>
|
||||||
|
<location seat="2" x="654" y="288"> </location>
|
||||||
|
<location seat="3" x="615" y="424"> </location>
|
||||||
|
<location seat="4" x="70" y="421"> </location>
|
||||||
|
<location seat="5" x="0" y="280"> </location>
|
||||||
|
<location seat="6" x="70" y="58"> </location>
|
||||||
|
</layout>
|
||||||
|
<layout fav_seat="0" height="547" max="2" width="794">
|
||||||
|
<location seat="1" x="651" y="288"> </location>
|
||||||
|
<location seat="2" x="10" y="288"> </location>
|
||||||
|
</layout>
|
||||||
|
<layout fav_seat="0" height="547" max="9" width="794">
|
||||||
|
<location seat="1" x="634" y="38"> </location>
|
||||||
|
<location seat="2" x="667" y="184"> </location>
|
||||||
|
<location seat="3" x="667" y="321"> </location>
|
||||||
|
<location seat="4" x="667" y="445"> </location>
|
||||||
|
<location seat="5" x="337" y="459"> </location>
|
||||||
|
<location seat="6" x="0" y="400"> </location>
|
||||||
|
<location seat="7" x="0" y="322"> </location>
|
||||||
|
<location seat="8" x="0" y="181"> </location>
|
||||||
|
<location seat="9" x="70" y="53"> </location>
|
||||||
|
</layout>
|
||||||
|
</site>
|
||||||
</supported_sites>
|
</supported_sites>
|
||||||
|
|
||||||
<supported_games>
|
<supported_games>
|
||||||
|
<game aux="mucked" cols="3" db="fpdb" game_name="holdem" rows="3">
|
||||||
<game cols="3" db="fpdb" game_name="holdem" rows="2" aux="mucked">
|
<stat click="tog_decorate" col="0" popup="default" row="0" stat_name="vpip" stat_loth="25" stat_locolor ="#408000" stat_hith="40" stat_hicolor ="#F05000" tip="tip1"> </stat>
|
||||||
<stat click="tog_decorate" col="0" popup="default" row="0" stat_name="vpip" tip="tip1"> </stat>
|
<stat click="tog_decorate" col="1" popup="default" row="0" stat_name="pfr" stat_loth="20" stat_locolor ="#408000" stat_hith="35" stat_hicolor ="#F05000" tip="tip1"> </stat>
|
||||||
<stat click="tog_decorate" col="1" popup="default" row="0" stat_name="pfr" tip="tip1"> </stat>
|
<stat click="tog_decorate" col="2" popup="default" row="0" stat_name="three_B" stat_loth="4" stat_locolor ="#408000" stat_hith="13" stat_hicolor ="#F05000" tip="tip1"> </stat>
|
||||||
<stat click="tog_decorate" col="2" popup="default" row="0" stat_name="ffreq1" tip="tip1"> </stat>
|
|
||||||
<stat click="tog_decorate" col="0" popup="default" row="1" stat_name="n" tip="tip1"> </stat>
|
<stat click="tog_decorate" col="0" popup="default" row="1" stat_name="n" tip="tip1"> </stat>
|
||||||
<stat click="tog_decorate" col="1" popup="default" row="1" stat_name="wtsd" tip="tip1"> </stat>
|
<stat click="tog_decorate" col="1" hudcolor="#98FFB0" hudprefix="" hudsuffix="" popup="default" row="1" stat_name="playername" tip="tip1"> </stat>
|
||||||
<stat click="tog_decorate" col="2" popup="default" row="1" stat_name="wmsd" tip="tip1"> </stat>
|
<stat click="tog_decorate" col="2" popup="default" row="1" stat_name="cb1" tip="tip1"> </stat>
|
||||||
|
<stat click="tog_decorate" col="0" popup="default" row="2" stat_name="wtsd" tip="tip1"> </stat>
|
||||||
|
<stat click="tog_decorate" col="1" popup="default" row="2" stat_name="steal" tip="tip1"> </stat>
|
||||||
|
<stat click="tog_decorate" col="2" popup="default" row="2" stat_name="totalprofit" stat_loth="0" stat_locolor ="#F05000" stat_hith="0" stat_hicolor ="#408000" tip="tip1"> </stat>
|
||||||
</game>
|
</game>
|
||||||
|
|
||||||
<game cols="3" db="fpdb" game_name="razz" rows="2" aux="stud_mucked">
|
<game aux="stud_mucked" cols="2" db="fpdb" game_name="razz" rows="3">
|
||||||
<stat click="tog_decorate" col="0" popup="default" row="0" stat_name="vpip" tip="tip1"> </stat>
|
<stat click="tog_decorate" col="0" hudcolor="#98FFB0" hudprefix="" hudsuffix="" popup="default" row="0" stat_name="playername" tip="tip1"> </stat>
|
||||||
<stat click="tog_decorate" col="1" popup="default" row="0" stat_name="pfr" tip="tip1"> </stat>
|
<stat click="tog_decorate" col="1" popup="default" row="0" stat_name="n" tip="tip1"> </stat>
|
||||||
<stat click="tog_decorate" col="2" popup="default" row="0" stat_name="ffreq1" tip="tip1"> </stat>
|
<stat click="tog_decorate" col="0" popup="default" row="1" stat_name="vpip" stat_loth="20" stat_locolor ="#408000" stat_hith="40" stat_hicolor ="#F05000" tip="tip1"> </stat>
|
||||||
<stat click="tog_decorate" col="0" popup="default" row="1" stat_name="n" tip="tip1"> </stat>
|
<stat click="tog_decorate" col="1" popup="default" row="1" stat_name="pfr" tip="tip1"> </stat>
|
||||||
<stat click="tog_decorate" col="1" popup="default" row="1" stat_name="wtsd" tip="tip1"> </stat>
|
<stat click="tog_decorate" col="0" popup="default" row="2" stat_name="saw_f" tip="tip1"> </stat>
|
||||||
<stat click="tog_decorate" col="2" popup="default" row="1" stat_name="wmsd" tip="tip1"> </stat>
|
<stat click="tog_decorate" col="1" popup="default" row="2" stat_name="ffreq1" tip="tip1"> </stat>
|
||||||
</game>
|
</game>
|
||||||
|
|
||||||
<game cols="3" db="fpdb" game_name="omahahi" rows="2" aux="mucked">
|
<game aux="mucked" cols="2" db="fpdb" game_name="omahahi" rows="3">
|
||||||
<stat click="tog_decorate" col="0" popup="default" row="0" stat_name="vpip" tip="tip1"> </stat>
|
<stat click="tog_decorate" col="0" hudcolor="#98FFB0" hudprefix="" hudsuffix="" popup="default" row="0" stat_name="playername" tip="tip1"> </stat>
|
||||||
<stat click="tog_decorate" col="1" popup="default" row="0" stat_name="pfr" tip="tip1"> </stat>
|
<stat click="tog_decorate" col="1" popup="default" row="0" stat_name="n" tip="tip1"> </stat>
|
||||||
<stat click="tog_decorate" col="2" popup="default" row="0" stat_name="ffreq1" tip="tip1"> </stat>
|
<stat click="tog_decorate" col="0" popup="default" row="1" stat_name="vpip" stat_loth="20" stat_locolor ="#408000" stat_hith="40" stat_hicolor ="#F05000" tip="tip1"> </stat>
|
||||||
<stat click="tog_decorate" col="0" popup="default" row="1" stat_name="n" tip="tip1"> </stat>
|
<stat click="tog_decorate" col="1" popup="default" row="1" stat_name="pfr" tip="tip1"> </stat>
|
||||||
<stat click="tog_decorate" col="1" popup="default" row="1" stat_name="wtsd" tip="tip1"> </stat>
|
<stat click="tog_decorate" col="0" popup="default" row="2" stat_name="saw_f" tip="tip1"> </stat>
|
||||||
<stat click="tog_decorate" col="2" popup="default" row="1" stat_name="wmsd" tip="tip1"> </stat>
|
<stat click="tog_decorate" col="1" popup="default" row="2" stat_name="ffreq1" tip="tip1"> </stat>
|
||||||
</game>
|
</game>
|
||||||
|
|
||||||
<game cols="3" db="fpdb" game_name="omahahilo" rows="2" aux="mucked">
|
<game aux="mucked" cols="2" db="fpdb" game_name="omahahilo" rows="3">
|
||||||
<stat click="tog_decorate" col="0" popup="default" row="0" stat_name="vpip" tip="tip1"> </stat>
|
<stat click="tog_decorate" col="0" hudcolor="#98FFB0" hudprefix="" hudsuffix="" popup="default" row="0" stat_name="playername" tip="tip1"> </stat>
|
||||||
<stat click="tog_decorate" col="1" popup="default" row="0" stat_name="pfr" tip="tip1"> </stat>
|
<stat click="tog_decorate" col="1" popup="default" row="0" stat_name="n" tip="tip1"> </stat>
|
||||||
<stat click="tog_decorate" col="2" popup="default" row="0" stat_name="ffreq1" tip="tip1"> </stat>
|
<stat click="tog_decorate" col="0" popup="default" row="1" stat_name="vpip" stat_loth="20" stat_locolor ="#408000" stat_hith="40" stat_hicolor ="#F05000" tip="tip1"> </stat>
|
||||||
<stat click="tog_decorate" col="0" popup="default" row="1" stat_name="n" tip="tip1"> </stat>
|
<stat click="tog_decorate" col="1" popup="default" row="1" stat_name="pfr" tip="tip1"> </stat>
|
||||||
<stat click="tog_decorate" col="1" popup="default" row="1" stat_name="wtsd" tip="tip1"> </stat>
|
<stat click="tog_decorate" col="0" popup="default" row="2" stat_name="saw_f" tip="tip1"> </stat>
|
||||||
<stat click="tog_decorate" col="2" popup="default" row="1" stat_name="wmsd" tip="tip1"> </stat>
|
<stat click="tog_decorate" col="1" popup="default" row="2" stat_name="ffreq1" tip="tip1"> </stat>
|
||||||
</game>
|
</game>
|
||||||
|
|
||||||
<game cols="3" db="fpdb" game_name="studhi" rows="2" aux="stud_mucked">
|
<game aux="stud_mucked" cols="2" db="fpdb" game_name="studhi" rows="3">
|
||||||
<stat click="tog_decorate" col="0" popup="default" row="0" stat_name="vpip" tip="tip1"> </stat>
|
<stat click="tog_decorate" col="0" hudcolor="#98FFB0" hudprefix="" hudsuffix="" popup="default" row="0" stat_name="playername" tip="tip1"> </stat>
|
||||||
<stat click="tog_decorate" col="1" popup="default" row="0" stat_name="pfr" tip="tip1"> </stat>
|
<stat click="tog_decorate" col="1" popup="default" row="0" stat_name="n" tip="tip1"> </stat>
|
||||||
<stat click="tog_decorate" col="2" popup="default" row="0" stat_name="ffreq1" tip="tip1"> </stat>
|
<stat click="tog_decorate" col="0" popup="default" row="1" stat_name="vpip" stat_loth="20" stat_locolor ="#408000" stat_hith="40" stat_hicolor ="#F05000" tip="tip1"> </stat>
|
||||||
<stat click="tog_decorate" col="0" popup="default" row="1" stat_name="n" tip="tip1"> </stat>
|
<stat click="tog_decorate" col="1" popup="default" row="1" stat_name="pfr" tip="tip1"> </stat>
|
||||||
<stat click="tog_decorate" col="1" popup="default" row="1" stat_name="wtsd" tip="tip1"> </stat>
|
<stat click="tog_decorate" col="0" popup="default" row="2" stat_name="saw_f" tip="tip1"> </stat>
|
||||||
<stat click="tog_decorate" col="2" popup="default" row="1" stat_name="wmsd" tip="tip1"> </stat>
|
<stat click="tog_decorate" col="1" popup="default" row="2" stat_name="ffreq1" tip="tip1"> </stat>
|
||||||
</game>
|
</game>
|
||||||
|
|
||||||
<game cols="3" db="fpdb" game_name="studhilo" rows="2" aux="stud_mucked">
|
<game aux="stud_mucked" cols="2" db="fpdb" game_name="studhilo" rows="3">
|
||||||
<stat click="tog_decorate" col="0" popup="default" row="0" stat_name="vpip" tip="tip1"> </stat>
|
<stat click="tog_decorate" col="0" hudcolor="#98FFB0" hudprefix="" hudsuffix="" popup="default" row="0" stat_name="playername" tip="tip1"> </stat>
|
||||||
<stat click="tog_decorate" col="1" popup="default" row="0" stat_name="pfr" tip="tip1"> </stat>
|
<stat click="tog_decorate" col="1" popup="default" row="0" stat_name="n" tip="tip1"> </stat>
|
||||||
<stat click="tog_decorate" col="2" popup="default" row="0" stat_name="ffreq1" tip="tip1"> </stat>
|
<stat click="tog_decorate" col="0" popup="default" row="1" stat_name="vpip" stat_loth="20" stat_locolor ="#408000" stat_hith="40" stat_hicolor ="#F05000" tip="tip1"> </stat>
|
||||||
<stat click="tog_decorate" col="0" popup="default" row="1" stat_name="n" tip="tip1"> </stat>
|
<stat click="tog_decorate" col="1" popup="default" row="1" stat_name="pfr" tip="tip1"> </stat>
|
||||||
<stat click="tog_decorate" col="1" popup="default" row="1" stat_name="wtsd" tip="tip1"> </stat>
|
<stat click="tog_decorate" col="0" popup="default" row="2" stat_name="saw_f" tip="tip1"> </stat>
|
||||||
<stat click="tog_decorate" col="2" popup="default" row="1" stat_name="wmsd" tip="tip1"> </stat>
|
<stat click="tog_decorate" col="1" popup="default" row="2" stat_name="ffreq1" tip="tip1"> </stat>
|
||||||
</game>
|
</game>
|
||||||
</supported_games>
|
</supported_games>
|
||||||
|
|
||||||
|
@ -583,11 +622,12 @@ Left-Drag to Move"
|
||||||
<hhc site="PartyPoker" converter="PartyPokerToFpdb"/>
|
<hhc site="PartyPoker" converter="PartyPokerToFpdb"/>
|
||||||
<hhc site="Betfair" converter="BetfairToFpdb"/>
|
<hhc site="Betfair" converter="BetfairToFpdb"/>
|
||||||
<hhc site="Partouche" converter="PartoucheToFpdb"/>
|
<hhc site="Partouche" converter="PartoucheToFpdb"/>
|
||||||
|
<hhc site="Carbon" converter="CarbonToFpdb"/>
|
||||||
</hhcs>
|
</hhcs>
|
||||||
|
|
||||||
<supported_databases>
|
<supported_databases>
|
||||||
<database db_name="fpdb" db_server="mysql" db_ip="localhost" db_user="fpdb" db_pass="YOUR MYSQL PASSWORD"></database>
|
<!-- <database db_name="fpdb" db_server="mysql" db_ip="localhost" db_user="fpdb" db_pass="YOUR MYSQL PASSWORD"></database> -->
|
||||||
<!-- <database db_ip="localhost" db_name="fpdb" db_pass="fpdb" db_server="sqlite" db_user="fpdb"/> -->
|
<database db_ip="localhost" db_server="sqlite" db_name="fpdb.db3" db_user="fpdb" db_pass="fpdb"/>
|
||||||
</supported_databases>
|
</supported_databases>
|
||||||
|
|
||||||
</FreePokerToolsConfig>
|
</FreePokerToolsConfig>
|
||||||
|
|
|
@ -35,11 +35,6 @@ import traceback
|
||||||
|
|
||||||
(options, argv) = Options.fpdb_options()
|
(options, argv) = Options.fpdb_options()
|
||||||
|
|
||||||
if not options.errorsToConsole:
|
|
||||||
print "Note: error output is being diverted to fpdb-error-log.txt and HUD-error.txt. Any major error will be reported there _only_."
|
|
||||||
errorFile = open('HUD-error.txt', 'w', 0)
|
|
||||||
sys.stderr = errorFile
|
|
||||||
|
|
||||||
import thread
|
import thread
|
||||||
import time
|
import time
|
||||||
import string
|
import string
|
||||||
|
@ -52,6 +47,8 @@ import gobject
|
||||||
|
|
||||||
# FreePokerTools modules
|
# FreePokerTools modules
|
||||||
import Configuration
|
import Configuration
|
||||||
|
|
||||||
|
|
||||||
import Database
|
import Database
|
||||||
from HandHistoryConverter import getTableTitleRe
|
from HandHistoryConverter import getTableTitleRe
|
||||||
# get the correct module for the current os
|
# get the correct module for the current os
|
||||||
|
@ -63,7 +60,9 @@ elif os.name == 'nt':
|
||||||
import Hud
|
import Hud
|
||||||
|
|
||||||
|
|
||||||
log = Configuration.get_logger("logging.conf")
|
# get config and set up logger
|
||||||
|
c = Configuration.Config(file=options.config, dbname=options.dbname)
|
||||||
|
log = Configuration.get_logger("logging.conf", "hud", log_dir=c.dir_log, log_file='HUD-log.txt')
|
||||||
|
|
||||||
|
|
||||||
class HUD_main(object):
|
class HUD_main(object):
|
||||||
|
@ -71,8 +70,23 @@ class HUD_main(object):
|
||||||
# This class mainly provides state for controlling the multiple HUDs.
|
# This class mainly provides state for controlling the multiple HUDs.
|
||||||
|
|
||||||
def __init__(self, db_name = 'fpdb'):
|
def __init__(self, db_name = 'fpdb'):
|
||||||
|
print "\nHUD_main: starting ..."
|
||||||
self.db_name = db_name
|
self.db_name = db_name
|
||||||
self.config = Configuration.Config(file=options.config, dbname=options.dbname)
|
self.config = c
|
||||||
|
print "Logfile is " + os.path.join(self.config.dir_log, 'HUD-log.txt')
|
||||||
|
log.info("HUD_main starting: using db name = %s" % (db_name))
|
||||||
|
|
||||||
|
try:
|
||||||
|
if not options.errorsToConsole:
|
||||||
|
fileName = os.path.join(self.config.dir_log, 'HUD-errors.txt')
|
||||||
|
print "Note: error output is being diverted to:\n"+fileName \
|
||||||
|
+ "\nAny major error will be reported there _only_.\n"
|
||||||
|
log.info("Note: error output is being diverted to:"+fileName)
|
||||||
|
log.info("Any major error will be reported there _only_.")
|
||||||
|
errorFile = open(fileName, 'w', 0)
|
||||||
|
sys.stderr = errorFile
|
||||||
|
sys.stderr.write("HUD_main: starting ...\n")
|
||||||
|
|
||||||
self.hud_dict = {}
|
self.hud_dict = {}
|
||||||
self.hud_params = self.config.get_hud_ui_parameters()
|
self.hud_params = self.config.get_hud_ui_parameters()
|
||||||
|
|
||||||
|
@ -89,8 +103,14 @@ class HUD_main(object):
|
||||||
self.main_window.add(self.vb)
|
self.main_window.add(self.vb)
|
||||||
self.main_window.set_title("HUD Main Window")
|
self.main_window.set_title("HUD Main Window")
|
||||||
self.main_window.show_all()
|
self.main_window.show_all()
|
||||||
|
except:
|
||||||
|
log.error( "*** Exception in HUD_main.init() *** " )
|
||||||
|
for e in traceback.format_tb(sys.exc_info()[2]):
|
||||||
|
log.error(e)
|
||||||
|
|
||||||
|
|
||||||
def destroy(self, *args): # call back for terminating the main eventloop
|
def destroy(self, *args): # call back for terminating the main eventloop
|
||||||
|
log.info("Terminating normally.")
|
||||||
gtk.main_quit()
|
gtk.main_quit()
|
||||||
|
|
||||||
def kill_hud(self, event, table):
|
def kill_hud(self, event, table):
|
||||||
|
@ -123,8 +143,9 @@ class HUD_main(object):
|
||||||
self.hud_dict[table_name].update(new_hand_id, self.config)
|
self.hud_dict[table_name].update(new_hand_id, self.config)
|
||||||
self.hud_dict[table_name].reposition_windows()
|
self.hud_dict[table_name].reposition_windows()
|
||||||
except:
|
except:
|
||||||
print "*** Exception in HUD_main::idle_func() *** "
|
log.error( "*** Exception in HUD_main::idle_func() *** " + str(sys.exc_info()) )
|
||||||
traceback.print_stack()
|
for e in traceback.format_tb(sys.exc_info()[2]):
|
||||||
|
log.error(e)
|
||||||
finally:
|
finally:
|
||||||
gtk.gdk.threads_leave()
|
gtk.gdk.threads_leave()
|
||||||
return False
|
return False
|
||||||
|
@ -186,30 +207,38 @@ class HUD_main(object):
|
||||||
|
|
||||||
# get hero's screen names and player ids
|
# get hero's screen names and player ids
|
||||||
self.hero, self.hero_ids = {}, {}
|
self.hero, self.hero_ids = {}, {}
|
||||||
for site in self.config.get_supported_sites():
|
found = False
|
||||||
result = self.db_connection.get_site_id(site)
|
|
||||||
if result:
|
|
||||||
site_id = result[0][0]
|
|
||||||
self.hero[site_id] = self.config.supported_sites[site].screen_name
|
|
||||||
self.hero_ids[site_id] = self.db_connection.get_player_id(self.config, site, self.hero[site_id])
|
|
||||||
|
|
||||||
while 1: # wait for a new hand number on stdin
|
while 1: # wait for a new hand number on stdin
|
||||||
new_hand_id = sys.stdin.readline()
|
new_hand_id = sys.stdin.readline()
|
||||||
t0 = time.time()
|
t0 = time.time()
|
||||||
t1 = t2 = t3 = t4 = t5 = t6 = t0
|
t1 = t2 = t3 = t4 = t5 = t6 = t0
|
||||||
new_hand_id = string.rstrip(new_hand_id)
|
new_hand_id = string.rstrip(new_hand_id)
|
||||||
|
log.debug("Received hand no %s" % new_hand_id)
|
||||||
if new_hand_id == "": # blank line means quit
|
if new_hand_id == "": # blank line means quit
|
||||||
self.destroy()
|
self.destroy()
|
||||||
break # this thread is not always killed immediately with gtk.main_quit()
|
break # this thread is not always killed immediately with gtk.main_quit()
|
||||||
|
|
||||||
|
if not found:
|
||||||
|
for site in self.config.get_supported_sites():
|
||||||
|
result = self.db_connection.get_site_id(site)
|
||||||
|
if result:
|
||||||
|
site_id = result[0][0]
|
||||||
|
self.hero[site_id] = self.config.supported_sites[site].screen_name
|
||||||
|
self.hero_ids[site_id] = self.db_connection.get_player_id(self.config, site, self.hero[site_id])
|
||||||
|
if self.hero_ids[site_id] is not None:
|
||||||
|
found = True
|
||||||
|
else:
|
||||||
|
self.hero_ids[site_id] = -1
|
||||||
|
|
||||||
# get basic info about the new hand from the db
|
# get basic info about the new hand from the db
|
||||||
# if there is a db error, complain, skip hand, and proceed
|
# if there is a db error, complain, skip hand, and proceed
|
||||||
|
log.info("HUD_main.read_stdin: hand processing starting ...")
|
||||||
try:
|
try:
|
||||||
(table_name, max, poker_game, type, site_id, site_name, num_seats, tour_number, tab_number) = \
|
(table_name, max, poker_game, type, site_id, site_name, num_seats, tour_number, tab_number) = \
|
||||||
self.db_connection.get_table_info(new_hand_id)
|
self.db_connection.get_table_info(new_hand_id)
|
||||||
except Exception, err: # TODO: we need to make this a much less generic Exception lulz
|
except Exception:
|
||||||
print "db error: skipping %s" % new_hand_id
|
log.error("db error: skipping %s" % new_hand_id)
|
||||||
sys.stderr.write("Database error: could not find hand %s.\n" % new_hand_id)
|
|
||||||
continue
|
continue
|
||||||
t1 = time.time()
|
t1 = time.time()
|
||||||
|
|
||||||
|
@ -228,18 +257,17 @@ class HUD_main(object):
|
||||||
,self.hero_ids[site_id], num_seats)
|
,self.hero_ids[site_id], num_seats)
|
||||||
t3 = time.time()
|
t3 = time.time()
|
||||||
try:
|
try:
|
||||||
self.db_connection.init_hud_stat_vars( self.hud_dict[temp_key].hud_params['hud_days']
|
|
||||||
, self.hud_dict[temp_key].hud_params['h_hud_days'])
|
|
||||||
stat_dict = self.db_connection.get_stats_from_hand(new_hand_id, type, self.hud_dict[temp_key].hud_params, self.hero_ids[site_id])
|
|
||||||
self.hud_dict[temp_key].stat_dict = stat_dict
|
self.hud_dict[temp_key].stat_dict = stat_dict
|
||||||
except KeyError: # HUD instance has been killed off, key is stale
|
except KeyError: # HUD instance has been killed off, key is stale
|
||||||
sys.stderr.write('hud_dict[%s] was not found\n' % temp_key)
|
log.error('hud_dict[%s] was not found\n' % temp_key)
|
||||||
sys.stderr.write('will not send hand\n')
|
log.error('will not send hand\n')
|
||||||
# Unlocks table, copied from end of function
|
# Unlocks table, copied from end of function
|
||||||
self.db_connection.connection.rollback()
|
self.db_connection.connection.rollback()
|
||||||
return
|
return
|
||||||
cards = self.db_connection.get_cards(new_hand_id)
|
cards = self.db_connection.get_cards(new_hand_id)
|
||||||
|
t4 = time.time()
|
||||||
comm_cards = self.db_connection.get_common_cards(new_hand_id)
|
comm_cards = self.db_connection.get_common_cards(new_hand_id)
|
||||||
|
t5 = time.time()
|
||||||
if comm_cards != {}: # stud!
|
if comm_cards != {}: # stud!
|
||||||
cards['common'] = comm_cards['common']
|
cards['common'] = comm_cards['common']
|
||||||
self.hud_dict[temp_key].cards = cards
|
self.hud_dict[temp_key].cards = cards
|
||||||
|
@ -250,24 +278,24 @@ class HUD_main(object):
|
||||||
else:
|
else:
|
||||||
# get stats using default params--also get cards
|
# get stats using default params--also get cards
|
||||||
self.db_connection.init_hud_stat_vars( self.hud_params['hud_days'], self.hud_params['h_hud_days'] )
|
self.db_connection.init_hud_stat_vars( self.hud_params['hud_days'], self.hud_params['h_hud_days'] )
|
||||||
t4 = time.time()
|
|
||||||
stat_dict = self.db_connection.get_stats_from_hand(new_hand_id, type, self.hud_params
|
stat_dict = self.db_connection.get_stats_from_hand(new_hand_id, type, self.hud_params
|
||||||
,self.hero_ids[site_id], num_seats)
|
,self.hero_ids[site_id], num_seats)
|
||||||
t5 = time.time()
|
|
||||||
cards = self.db_connection.get_cards(new_hand_id)
|
cards = self.db_connection.get_cards(new_hand_id)
|
||||||
comm_cards = self.db_connection.get_common_cards(new_hand_id)
|
comm_cards = self.db_connection.get_common_cards(new_hand_id)
|
||||||
if comm_cards != {}: # stud!
|
if comm_cards != {}: # stud!
|
||||||
cards['common'] = comm_cards['common']
|
cards['common'] = comm_cards['common']
|
||||||
|
|
||||||
table_kwargs = dict(table_name = table_name, tournament = tour_number, table_number = tab_number)
|
table_kwargs = dict(table_name = table_name, tournament = tour_number, table_number = tab_number)
|
||||||
search_string = getTableTitleRe(self.config, site, type, **table_kwargs)
|
search_string = getTableTitleRe(self.config, site_name, type, **table_kwargs)
|
||||||
|
# print "getTableTitleRe ", self.config, site_name, type, "=", search_string
|
||||||
tablewindow = Tables.Table(search_string, **table_kwargs)
|
tablewindow = Tables.Table(search_string, **table_kwargs)
|
||||||
|
|
||||||
if tablewindow is None:
|
if tablewindow is None:
|
||||||
# If no client window is found on the screen, complain and continue
|
# If no client window is found on the screen, complain and continue
|
||||||
if type == "tour":
|
if type == "tour":
|
||||||
table_name = "%s %s" % (tour_number, tab_number)
|
table_name = "%s %s" % (tour_number, tab_number)
|
||||||
sys.stderr.write("HUD create: table name "+table_name+" not found, skipping.\n")
|
# log.error("HUD create: table name "+table_name+" not found, skipping.\n")
|
||||||
|
log.error("HUD create: table name %s not found, skipping." % table_name)
|
||||||
else:
|
else:
|
||||||
tablewindow.max = max
|
tablewindow.max = max
|
||||||
tablewindow.site = site_name
|
tablewindow.site = site_name
|
||||||
|
@ -275,7 +303,7 @@ class HUD_main(object):
|
||||||
if hasattr(tablewindow, 'number'):
|
if hasattr(tablewindow, 'number'):
|
||||||
self.create_HUD(new_hand_id, tablewindow, temp_key, max, poker_game, type, stat_dict, cards)
|
self.create_HUD(new_hand_id, tablewindow, temp_key, max, poker_game, type, stat_dict, cards)
|
||||||
else:
|
else:
|
||||||
sys.stderr.write('Table "%s" no longer exists\n' % table_name)
|
log.error('Table "%s" no longer exists\n' % table_name)
|
||||||
|
|
||||||
t6 = time.time()
|
t6 = time.time()
|
||||||
log.info("HUD_main.read_stdin: hand read in %4.3f seconds (%4.3f,%4.3f,%4.3f,%4.3f,%4.3f,%4.3f)"
|
log.info("HUD_main.read_stdin: hand read in %4.3f seconds (%4.3f,%4.3f,%4.3f,%4.3f,%4.3f,%4.3f)"
|
||||||
|
@ -284,9 +312,6 @@ class HUD_main(object):
|
||||||
|
|
||||||
if __name__== "__main__":
|
if __name__== "__main__":
|
||||||
|
|
||||||
sys.stderr.write("HUD_main starting\n")
|
|
||||||
sys.stderr.write("Using db name = %s\n" % (options.dbname))
|
|
||||||
|
|
||||||
# start the HUD_main object
|
# start the HUD_main object
|
||||||
hm = HUD_main(db_name = options.dbname)
|
hm = HUD_main(db_name = options.dbname)
|
||||||
|
|
||||||
|
|
111
pyfpdb/Hand.py
111
pyfpdb/Hand.py
|
@ -21,19 +21,24 @@
|
||||||
import re
|
import re
|
||||||
import sys
|
import sys
|
||||||
import traceback
|
import traceback
|
||||||
import logging
|
|
||||||
import os
|
import os
|
||||||
import os.path
|
import os.path
|
||||||
from decimal import Decimal
|
from decimal import Decimal
|
||||||
import operator
|
import operator
|
||||||
import time,datetime
|
import time,datetime
|
||||||
from copy import deepcopy
|
from copy import deepcopy
|
||||||
from Exceptions import *
|
|
||||||
import pprint
|
import pprint
|
||||||
|
|
||||||
|
import logging
|
||||||
|
# logging has been set up in fpdb.py or HUD_main.py, use their settings:
|
||||||
|
log = logging.getLogger("parser")
|
||||||
|
|
||||||
|
|
||||||
|
import Configuration
|
||||||
|
from Exceptions import *
|
||||||
import DerivedStats
|
import DerivedStats
|
||||||
import Card
|
import Card
|
||||||
|
|
||||||
log = logging.getLogger("parser")
|
|
||||||
|
|
||||||
class Hand(object):
|
class Hand(object):
|
||||||
|
|
||||||
|
@ -43,10 +48,12 @@ class Hand(object):
|
||||||
LCS = {'H':'h', 'D':'d', 'C':'c', 'S':'s'}
|
LCS = {'H':'h', 'D':'d', 'C':'c', 'S':'s'}
|
||||||
SYMBOL = {'USD': '$', 'EUR': u'$', 'T$': '', 'play': ''}
|
SYMBOL = {'USD': '$', 'EUR': u'$', 'T$': '', 'play': ''}
|
||||||
MS = {'horse' : 'HORSE', '8game' : '8-Game', 'hose' : 'HOSE', 'ha': 'HA'}
|
MS = {'horse' : 'HORSE', '8game' : '8-Game', 'hose' : 'HOSE', 'ha': 'HA'}
|
||||||
SITEIDS = {'Fulltilt':1, 'PokerStars':2, 'Everleaf':3, 'Win2day':4, 'OnGame':5, 'UltimateBet':6, 'Betfair':7, 'Absolute':8, 'PartyPoker':9 }
|
SITEIDS = {'Fulltilt':1, 'PokerStars':2, 'Everleaf':3, 'Win2day':4, 'OnGame':5, 'UltimateBet':6, 'Betfair':7, 'Absolute':8, 'PartyPoker':9, 'Partouche':10, 'Carbon':11 }
|
||||||
|
|
||||||
|
|
||||||
def __init__(self, sitename, gametype, handText, builtFrom = "HHC"):
|
def __init__(self, config, sitename, gametype, handText, builtFrom = "HHC"):
|
||||||
|
self.config = config
|
||||||
|
#log = Configuration.get_logger("logging.conf", "db", log_dir=self.config.dir_log)
|
||||||
self.sitename = sitename
|
self.sitename = sitename
|
||||||
self.siteId = self.SITEIDS[sitename]
|
self.siteId = self.SITEIDS[sitename]
|
||||||
self.stats = DerivedStats.DerivedStats(self)
|
self.stats = DerivedStats.DerivedStats(self)
|
||||||
|
@ -118,6 +125,7 @@ class Hand(object):
|
||||||
# currency symbol for this hand
|
# currency symbol for this hand
|
||||||
self.sym = self.SYMBOL[self.gametype['currency']] # save typing! delete this attr when done
|
self.sym = self.SYMBOL[self.gametype['currency']] # save typing! delete this attr when done
|
||||||
self.pot.setSym(self.sym)
|
self.pot.setSym(self.sym)
|
||||||
|
self.is_duplicate = False # i.e. don't update hudcache if true
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
vars = ( ("BB", self.bb),
|
vars = ( ("BB", self.bb),
|
||||||
|
@ -205,7 +213,7 @@ dealt whether they were seen in a 'dealt to' line
|
||||||
def insert(self, db):
|
def insert(self, db):
|
||||||
""" Function to insert Hand into database
|
""" Function to insert Hand into database
|
||||||
Should not commit, and do minimal selects. Callers may want to cache commits
|
Should not commit, and do minimal selects. Callers may want to cache commits
|
||||||
db: a connected fpdb_db object"""
|
db: a connected Database object"""
|
||||||
|
|
||||||
|
|
||||||
self.stats.getStats(self)
|
self.stats.getStats(self)
|
||||||
|
@ -229,8 +237,11 @@ db: a connected fpdb_db object"""
|
||||||
# TourneysPlayers
|
# TourneysPlayers
|
||||||
else:
|
else:
|
||||||
log.info("Hand.insert(): hid #: %s is a duplicate" % hh['siteHandNo'])
|
log.info("Hand.insert(): hid #: %s is a duplicate" % hh['siteHandNo'])
|
||||||
#Raise Duplicate exception?
|
self.is_duplicate = True # i.e. don't update hudcache
|
||||||
pass
|
raise FpdbHandDuplicate(hh['siteHandNo'])
|
||||||
|
|
||||||
|
def updateHudCache(self, db):
|
||||||
|
db.storeHudCache(self.dbid_gt, self.dbid_pids, self.starttime, self.stats.getHandsPlayers())
|
||||||
|
|
||||||
def select(self, handId):
|
def select(self, handId):
|
||||||
""" Function to create Hand object from database """
|
""" Function to create Hand object from database """
|
||||||
|
@ -286,6 +297,24 @@ If a player has None chips he won't be added."""
|
||||||
c = c.replace(k,v)
|
c = c.replace(k,v)
|
||||||
return c
|
return c
|
||||||
|
|
||||||
|
def addAllIn(self, street, player, amount):
|
||||||
|
"""\
|
||||||
|
For sites (currently only Carbon Poker) which record "all in" as a special action, which can mean either "calls and is all in" or "raises all in".
|
||||||
|
"""
|
||||||
|
self.checkPlayerExists(player)
|
||||||
|
amount = re.sub(u',', u'', amount) #some sites have commas
|
||||||
|
Ai = Decimal(amount)
|
||||||
|
Bp = self.lastBet[street]
|
||||||
|
Bc = reduce(operator.add, self.bets[street][player], 0)
|
||||||
|
C = Bp - Bc
|
||||||
|
if Ai <= C:
|
||||||
|
self.addCall(street, player, amount)
|
||||||
|
elif Bp == 0:
|
||||||
|
self.addBet(street, player, amount)
|
||||||
|
else:
|
||||||
|
Rb = Ai - C
|
||||||
|
self._addRaise(street, player, C, Rb, Ai)
|
||||||
|
|
||||||
def addAnte(self, player, ante):
|
def addAnte(self, player, ante):
|
||||||
log.debug("%s %s antes %s" % ('BLINDSANTES', player, ante))
|
log.debug("%s %s antes %s" % ('BLINDSANTES', player, ante))
|
||||||
if player is not None:
|
if player is not None:
|
||||||
|
@ -294,7 +323,9 @@ If a player has None chips he won't be added."""
|
||||||
self.stacks[player] -= Decimal(ante)
|
self.stacks[player] -= Decimal(ante)
|
||||||
act = (player, 'posts', "ante", ante, self.stacks[player]==0)
|
act = (player, 'posts', "ante", ante, self.stacks[player]==0)
|
||||||
self.actions['BLINDSANTES'].append(act)
|
self.actions['BLINDSANTES'].append(act)
|
||||||
self.pot.addMoney(player, Decimal(ante))
|
# self.pot.addMoney(player, Decimal(ante))
|
||||||
|
self.pot.addCommonMoney(player, Decimal(ante))
|
||||||
|
#I think the antes should be common money, don't have enough hand history to check
|
||||||
|
|
||||||
def addBlind(self, player, blindtype, amount):
|
def addBlind(self, player, blindtype, amount):
|
||||||
# if player is None, it's a missing small blind.
|
# if player is None, it's a missing small blind.
|
||||||
|
@ -313,9 +344,16 @@ If a player has None chips he won't be added."""
|
||||||
self.actions['BLINDSANTES'].append(act)
|
self.actions['BLINDSANTES'].append(act)
|
||||||
|
|
||||||
if blindtype == 'both':
|
if blindtype == 'both':
|
||||||
|
# work with the real ammount. limit games are listed as $1, $2, where
|
||||||
|
# the SB 0.50 and the BB is $1, after the turn the minimum bet amount is $2....
|
||||||
amount = self.bb
|
amount = self.bb
|
||||||
self.bets['BLINDSANTES'][player].append(Decimal(self.sb))
|
self.bets['BLINDSANTES'][player].append(Decimal(self.sb))
|
||||||
self.pot.addCommonMoney(Decimal(self.sb))
|
self.pot.addCommonMoney(player, Decimal(self.sb))
|
||||||
|
|
||||||
|
if blindtype == 'secondsb':
|
||||||
|
amount = Decimal(0)
|
||||||
|
self.bets['BLINDSANTES'][player].append(Decimal(self.sb))
|
||||||
|
self.pot.addCommonMoney(player, Decimal(self.sb))
|
||||||
|
|
||||||
self.bets['PREFLOP'][player].append(Decimal(amount))
|
self.bets['PREFLOP'][player].append(Decimal(amount))
|
||||||
self.pot.addMoney(player, Decimal(amount))
|
self.pot.addMoney(player, Decimal(amount))
|
||||||
|
@ -393,7 +431,7 @@ Add a raise on [street] by [player] to [amountTo]
|
||||||
Bc = reduce(operator.add, self.bets[street][player], 0)
|
Bc = reduce(operator.add, self.bets[street][player], 0)
|
||||||
Rt = Decimal(amountTo)
|
Rt = Decimal(amountTo)
|
||||||
C = Bp - Bc
|
C = Bp - Bc
|
||||||
Rb = Rt - C
|
Rb = Rt - C - Bc
|
||||||
self._addRaise(street, player, C, Rb, Rt)
|
self._addRaise(street, player, C, Rb, Rt)
|
||||||
|
|
||||||
def _addRaise(self, street, player, C, Rb, Rt):
|
def _addRaise(self, street, player, C, Rb, Rt):
|
||||||
|
@ -479,7 +517,6 @@ Card ranks will be uppercased
|
||||||
for entry in self.collected:
|
for entry in self.collected:
|
||||||
self.totalcollected += Decimal(entry[1])
|
self.totalcollected += Decimal(entry[1])
|
||||||
|
|
||||||
|
|
||||||
def getGameTypeAsString(self):
|
def getGameTypeAsString(self):
|
||||||
"""\
|
"""\
|
||||||
Map the tuple self.gametype onto the pokerstars string describing it
|
Map the tuple self.gametype onto the pokerstars string describing it
|
||||||
|
@ -595,7 +632,8 @@ Map the tuple self.gametype onto the pokerstars string describing it
|
||||||
|
|
||||||
|
|
||||||
class HoldemOmahaHand(Hand):
|
class HoldemOmahaHand(Hand):
|
||||||
def __init__(self, hhc, sitename, gametype, handText, builtFrom = "HHC", handid=None):
|
def __init__(self, config, hhc, sitename, gametype, handText, builtFrom = "HHC", handid=None):
|
||||||
|
self.config = config
|
||||||
if gametype['base'] != 'hold':
|
if gametype['base'] != 'hold':
|
||||||
pass # or indeed don't pass and complain instead
|
pass # or indeed don't pass and complain instead
|
||||||
log.debug("HoldemOmahaHand")
|
log.debug("HoldemOmahaHand")
|
||||||
|
@ -603,7 +641,7 @@ class HoldemOmahaHand(Hand):
|
||||||
self.holeStreets = ['PREFLOP']
|
self.holeStreets = ['PREFLOP']
|
||||||
self.communityStreets = ['FLOP', 'TURN', 'RIVER']
|
self.communityStreets = ['FLOP', 'TURN', 'RIVER']
|
||||||
self.actionStreets = ['BLINDSANTES','PREFLOP','FLOP','TURN','RIVER']
|
self.actionStreets = ['BLINDSANTES','PREFLOP','FLOP','TURN','RIVER']
|
||||||
Hand.__init__(self, sitename, gametype, handText, builtFrom = "HHC")
|
Hand.__init__(self, self.config, sitename, gametype, handText, builtFrom = "HHC")
|
||||||
self.sb = gametype['sb']
|
self.sb = gametype['sb']
|
||||||
self.bb = gametype['bb']
|
self.bb = gametype['bb']
|
||||||
|
|
||||||
|
@ -612,6 +650,8 @@ class HoldemOmahaHand(Hand):
|
||||||
# which then invokes a 'addXXX' callback
|
# which then invokes a 'addXXX' callback
|
||||||
if builtFrom == "HHC":
|
if builtFrom == "HHC":
|
||||||
hhc.readHandInfo(self)
|
hhc.readHandInfo(self)
|
||||||
|
if self.gametype['type'] == 'tour':
|
||||||
|
self.tablename = "%s %s" % (self.tourNo, self.tablename)
|
||||||
hhc.readPlayerStacks(self)
|
hhc.readPlayerStacks(self)
|
||||||
hhc.compilePlayerRegexs(self)
|
hhc.compilePlayerRegexs(self)
|
||||||
hhc.markStreets(self)
|
hhc.markStreets(self)
|
||||||
|
@ -637,6 +677,7 @@ class HoldemOmahaHand(Hand):
|
||||||
if self.maxseats is None:
|
if self.maxseats is None:
|
||||||
self.maxseats = hhc.guessMaxSeats(self)
|
self.maxseats = hhc.guessMaxSeats(self)
|
||||||
hhc.readOther(self)
|
hhc.readOther(self)
|
||||||
|
#print "\nHand:\n"+str(self)
|
||||||
elif builtFrom == "DB":
|
elif builtFrom == "DB":
|
||||||
if handid is not None:
|
if handid is not None:
|
||||||
self.select(handid) # Will need a handId
|
self.select(handid) # Will need a handId
|
||||||
|
@ -676,7 +717,6 @@ class HoldemOmahaHand(Hand):
|
||||||
|
|
||||||
def join_holecards(self, player, asList=False):
|
def join_holecards(self, player, asList=False):
|
||||||
"""With asList = True it returns the set cards for a player including down cards if they aren't know"""
|
"""With asList = True it returns the set cards for a player including down cards if they aren't know"""
|
||||||
# FIXME: This should actually return
|
|
||||||
hcs = [u'0x', u'0x', u'0x', u'0x']
|
hcs = [u'0x', u'0x', u'0x', u'0x']
|
||||||
|
|
||||||
for street in self.holeStreets:
|
for street in self.holeStreets:
|
||||||
|
@ -893,7 +933,8 @@ class HoldemOmahaHand(Hand):
|
||||||
print >>fh, "\n\n"
|
print >>fh, "\n\n"
|
||||||
|
|
||||||
class DrawHand(Hand):
|
class DrawHand(Hand):
|
||||||
def __init__(self, hhc, sitename, gametype, handText, builtFrom = "HHC"):
|
def __init__(self, config, hhc, sitename, gametype, handText, builtFrom = "HHC"):
|
||||||
|
self.config = config
|
||||||
if gametype['base'] != 'draw':
|
if gametype['base'] != 'draw':
|
||||||
pass # or indeed don't pass and complain instead
|
pass # or indeed don't pass and complain instead
|
||||||
self.streetList = ['BLINDSANTES', 'DEAL', 'DRAWONE', 'DRAWTWO', 'DRAWTHREE']
|
self.streetList = ['BLINDSANTES', 'DEAL', 'DRAWONE', 'DRAWTWO', 'DRAWTHREE']
|
||||||
|
@ -901,12 +942,14 @@ class DrawHand(Hand):
|
||||||
self.holeStreets = ['DEAL', 'DRAWONE', 'DRAWTWO', 'DRAWTHREE']
|
self.holeStreets = ['DEAL', 'DRAWONE', 'DRAWTWO', 'DRAWTHREE']
|
||||||
self.actionStreets = ['BLINDSANTES', 'DEAL', 'DRAWONE', 'DRAWTWO', 'DRAWTHREE']
|
self.actionStreets = ['BLINDSANTES', 'DEAL', 'DRAWONE', 'DRAWTWO', 'DRAWTHREE']
|
||||||
self.communityStreets = []
|
self.communityStreets = []
|
||||||
Hand.__init__(self, sitename, gametype, handText)
|
Hand.__init__(self, self.config, sitename, gametype, handText)
|
||||||
self.sb = gametype['sb']
|
self.sb = gametype['sb']
|
||||||
self.bb = gametype['bb']
|
self.bb = gametype['bb']
|
||||||
# Populate the draw hand.
|
# Populate the draw hand.
|
||||||
if builtFrom == "HHC":
|
if builtFrom == "HHC":
|
||||||
hhc.readHandInfo(self)
|
hhc.readHandInfo(self)
|
||||||
|
if self.gametype['type'] == 'tour':
|
||||||
|
self.tablename = "%s %s" % (self.tourNo, self.tablename)
|
||||||
hhc.readPlayerStacks(self)
|
hhc.readPlayerStacks(self)
|
||||||
hhc.compilePlayerRegexs(self)
|
hhc.compilePlayerRegexs(self)
|
||||||
hhc.markStreets(self)
|
hhc.markStreets(self)
|
||||||
|
@ -952,11 +995,12 @@ class DrawHand(Hand):
|
||||||
self.lastBet['DEAL'] = Decimal(amount)
|
self.lastBet['DEAL'] = Decimal(amount)
|
||||||
elif blindtype == 'both':
|
elif blindtype == 'both':
|
||||||
# extra small blind is 'dead'
|
# extra small blind is 'dead'
|
||||||
self.lastBet['DEAL'] = Decimal(self.bb)
|
amount = Decimal(amount)/3
|
||||||
|
amount += amount
|
||||||
|
self.lastBet['DEAL'] = Decimal(amount)
|
||||||
self.posted = self.posted + [[player,blindtype]]
|
self.posted = self.posted + [[player,blindtype]]
|
||||||
#print "DEBUG: self.posted: %s" %(self.posted)
|
#print "DEBUG: self.posted: %s" %(self.posted)
|
||||||
|
|
||||||
|
|
||||||
def addShownCards(self, cards, player, shown=True, mucked=False, dealt=False):
|
def addShownCards(self, cards, player, shown=True, mucked=False, dealt=False):
|
||||||
if player == self.hero: # we have hero's cards just update shown/mucked
|
if player == self.hero: # we have hero's cards just update shown/mucked
|
||||||
if shown: self.shown.add(player)
|
if shown: self.shown.add(player)
|
||||||
|
@ -1083,7 +1127,8 @@ class DrawHand(Hand):
|
||||||
|
|
||||||
|
|
||||||
class StudHand(Hand):
|
class StudHand(Hand):
|
||||||
def __init__(self, hhc, sitename, gametype, handText, builtFrom = "HHC"):
|
def __init__(self, config, hhc, sitename, gametype, handText, builtFrom = "HHC"):
|
||||||
|
self.config = config
|
||||||
if gametype['base'] != 'stud':
|
if gametype['base'] != 'stud':
|
||||||
pass # or indeed don't pass and complain instead
|
pass # or indeed don't pass and complain instead
|
||||||
|
|
||||||
|
@ -1093,7 +1138,7 @@ class StudHand(Hand):
|
||||||
|
|
||||||
self.streetList = ['BLINDSANTES','THIRD','FOURTH','FIFTH','SIXTH','SEVENTH'] # a list of the observed street names in order
|
self.streetList = ['BLINDSANTES','THIRD','FOURTH','FIFTH','SIXTH','SEVENTH'] # a list of the observed street names in order
|
||||||
self.holeStreets = ['THIRD','FOURTH','FIFTH','SIXTH','SEVENTH']
|
self.holeStreets = ['THIRD','FOURTH','FIFTH','SIXTH','SEVENTH']
|
||||||
Hand.__init__(self, sitename, gametype, handText)
|
Hand.__init__(self, self.config, sitename, gametype, handText)
|
||||||
self.sb = gametype['sb']
|
self.sb = gametype['sb']
|
||||||
self.bb = gametype['bb']
|
self.bb = gametype['bb']
|
||||||
#Populate the StudHand
|
#Populate the StudHand
|
||||||
|
@ -1101,6 +1146,8 @@ class StudHand(Hand):
|
||||||
# which then invokes a 'addXXX' callback
|
# which then invokes a 'addXXX' callback
|
||||||
if builtFrom == "HHC":
|
if builtFrom == "HHC":
|
||||||
hhc.readHandInfo(self)
|
hhc.readHandInfo(self)
|
||||||
|
if self.gametype['type'] == 'tour':
|
||||||
|
self.tablename = "%s %s" % (self.tourNo, self.tablename)
|
||||||
hhc.readPlayerStacks(self)
|
hhc.readPlayerStacks(self)
|
||||||
hhc.compilePlayerRegexs(self)
|
hhc.compilePlayerRegexs(self)
|
||||||
hhc.markStreets(self)
|
hhc.markStreets(self)
|
||||||
|
@ -1368,7 +1415,7 @@ class Pot(object):
|
||||||
self.contenders = set()
|
self.contenders = set()
|
||||||
self.committed = {}
|
self.committed = {}
|
||||||
self.streettotals = {}
|
self.streettotals = {}
|
||||||
self.common = Decimal(0)
|
self.common = {}
|
||||||
self.total = None
|
self.total = None
|
||||||
self.returned = {}
|
self.returned = {}
|
||||||
self.sym = u'$' # this is the default currency symbol
|
self.sym = u'$' # this is the default currency symbol
|
||||||
|
@ -1378,13 +1425,14 @@ class Pot(object):
|
||||||
|
|
||||||
def addPlayer(self,player):
|
def addPlayer(self,player):
|
||||||
self.committed[player] = Decimal(0)
|
self.committed[player] = Decimal(0)
|
||||||
|
self.common[player] = Decimal(0)
|
||||||
|
|
||||||
def addFold(self, player):
|
def addFold(self, player):
|
||||||
# addFold must be called when a player folds
|
# addFold must be called when a player folds
|
||||||
self.contenders.discard(player)
|
self.contenders.discard(player)
|
||||||
|
|
||||||
def addCommonMoney(self, amount):
|
def addCommonMoney(self, player, amount):
|
||||||
self.common += amount
|
self.common[player] += amount
|
||||||
|
|
||||||
def addMoney(self, player, amount):
|
def addMoney(self, player, amount):
|
||||||
# addMoney must be called for any actions that put money in the pot, in the order they occur
|
# addMoney must be called for any actions that put money in the pot, in the order they occur
|
||||||
|
@ -1392,7 +1440,7 @@ class Pot(object):
|
||||||
self.committed[player] += amount
|
self.committed[player] += amount
|
||||||
|
|
||||||
def markTotal(self, street):
|
def markTotal(self, street):
|
||||||
self.streettotals[street] = sum(self.committed.values()) + self.common
|
self.streettotals[street] = sum(self.committed.values()) + sum(self.common.values())
|
||||||
|
|
||||||
def getTotalAtStreet(self, street):
|
def getTotalAtStreet(self, street):
|
||||||
if street in self.streettotals:
|
if street in self.streettotals:
|
||||||
|
@ -1400,12 +1448,15 @@ class Pot(object):
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
def end(self):
|
def end(self):
|
||||||
self.total = sum(self.committed.values()) + self.common
|
self.total = sum(self.committed.values()) + sum(self.common.values())
|
||||||
|
|
||||||
# Return any uncalled bet.
|
# Return any uncalled bet.
|
||||||
committed = sorted([ (v,k) for (k,v) in self.committed.items()])
|
committed = sorted([ (v,k) for (k,v) in self.committed.items()])
|
||||||
if len(committed)<2:
|
#print "DEBUG: committed: %s" % committed
|
||||||
raise FpdbParseError("length of committed array is too small")
|
#ERROR below. lastbet is correct in most cases, but wrong when
|
||||||
|
# additional money is committed to the pot in cash games
|
||||||
|
# due to an additional sb being posted. (Speculate that
|
||||||
|
# posting sb+bb is also potentially wrong)
|
||||||
lastbet = committed[-1][0] - committed[-2][0]
|
lastbet = committed[-1][0] - committed[-2][0]
|
||||||
if lastbet > 0: # uncalled
|
if lastbet > 0: # uncalled
|
||||||
returnto = committed[-1][1]
|
returnto = committed[-1][1]
|
||||||
|
@ -1486,7 +1537,8 @@ limit 1""", {'handid':handid})
|
||||||
#TODO: siteid should be in hands table - we took the scenic route through players here.
|
#TODO: siteid should be in hands table - we took the scenic route through players here.
|
||||||
res = c.fetchone()
|
res = c.fetchone()
|
||||||
gametype = {'category':res[1],'base':res[2],'type':res[3],'limitType':res[4],'hilo':res[5],'sb':res[6],'bb':res[7], 'currency':res[10]}
|
gametype = {'category':res[1],'base':res[2],'type':res[3],'limitType':res[4],'hilo':res[5],'sb':res[6],'bb':res[7], 'currency':res[10]}
|
||||||
h = HoldemOmahaHand(hhc = None, sitename=res[0], gametype = gametype, handText=None, builtFrom = "DB", handid=handid)
|
c = Configuration.Config()
|
||||||
|
h = HoldemOmahaHand(config = c, hhc = None, sitename=res[0], gametype = gametype, handText=None, builtFrom = "DB", handid=handid)
|
||||||
cards = map(Card.valueSuitFromCard, res[11:16] )
|
cards = map(Card.valueSuitFromCard, res[11:16] )
|
||||||
if cards[0]:
|
if cards[0]:
|
||||||
h.setCommunityCards('FLOP', cards[0:3])
|
h.setCommunityCards('FLOP', cards[0:3])
|
||||||
|
@ -1589,4 +1641,3 @@ ORDER BY
|
||||||
|
|
||||||
|
|
||||||
return h
|
return h
|
||||||
|
|
||||||
|
|
|
@ -16,8 +16,6 @@
|
||||||
#In the "official" distribution you can find the license in
|
#In the "official" distribution you can find the license in
|
||||||
#agpl-3.0.txt in the docs folder of the package.
|
#agpl-3.0.txt in the docs folder of the package.
|
||||||
|
|
||||||
import Hand
|
|
||||||
import Tourney
|
|
||||||
import re
|
import re
|
||||||
import sys
|
import sys
|
||||||
import traceback
|
import traceback
|
||||||
|
@ -31,13 +29,20 @@ import operator
|
||||||
from xml.dom.minidom import Node
|
from xml.dom.minidom import Node
|
||||||
import time
|
import time
|
||||||
import datetime
|
import datetime
|
||||||
|
|
||||||
|
import logging
|
||||||
|
# logging has been set up in fpdb.py or HUD_main.py, use their settings:
|
||||||
|
log = logging.getLogger("parser")
|
||||||
|
|
||||||
|
|
||||||
|
import Hand
|
||||||
|
import Tourney
|
||||||
from Exceptions import FpdbParseError
|
from Exceptions import FpdbParseError
|
||||||
import Configuration
|
import Configuration
|
||||||
|
|
||||||
import gettext
|
import gettext
|
||||||
gettext.install('fpdb')
|
gettext.install('fpdb')
|
||||||
|
|
||||||
log = Configuration.get_logger("logging.conf")
|
|
||||||
|
|
||||||
import pygtk
|
import pygtk
|
||||||
import gtk
|
import gtk
|
||||||
|
@ -57,16 +62,20 @@ class HandHistoryConverter():
|
||||||
codepage = "cp1252"
|
codepage = "cp1252"
|
||||||
|
|
||||||
|
|
||||||
def __init__(self, in_path = '-', out_path = '-', follow=False, index=0, autostart=True, starsArchive=False):
|
def __init__(self, config, in_path = '-', out_path = '-', follow=False, index=0, autostart=True, starsArchive=False, ftpArchive=False):
|
||||||
"""\
|
"""\
|
||||||
in_path (default '-' = sys.stdin)
|
in_path (default '-' = sys.stdin)
|
||||||
out_path (default '-' = sys.stdout)
|
out_path (default '-' = sys.stdout)
|
||||||
follow : whether to tail -f the input"""
|
follow : whether to tail -f the input"""
|
||||||
|
|
||||||
|
self.config = config
|
||||||
|
self.import_parameters = self.config.get_import_parameters()
|
||||||
|
#log = Configuration.get_logger("logging.conf", "parser", log_dir=self.config.dir_log)
|
||||||
log.info("HandHistory init - %s subclass, in_path '%s'; out_path '%s'" % (self.sitename, in_path, out_path) )
|
log.info("HandHistory init - %s subclass, in_path '%s'; out_path '%s'" % (self.sitename, in_path, out_path) )
|
||||||
|
|
||||||
self.index = 0
|
self.index = index
|
||||||
self.starsArchive = starsArchive
|
self.starsArchive = starsArchive
|
||||||
|
self.ftpArchive = ftpArchive
|
||||||
|
|
||||||
self.in_path = in_path
|
self.in_path = in_path
|
||||||
self.out_path = out_path
|
self.out_path = out_path
|
||||||
|
@ -80,28 +89,7 @@ follow : whether to tail -f the input"""
|
||||||
|
|
||||||
if in_path == '-':
|
if in_path == '-':
|
||||||
self.in_fh = sys.stdin
|
self.in_fh = sys.stdin
|
||||||
|
self.out_fh = get_out_fh(out_path, self.import_parameters)
|
||||||
if out_path == '-':
|
|
||||||
self.out_fh = sys.stdout
|
|
||||||
else:
|
|
||||||
# TODO: out_path should be sanity checked.
|
|
||||||
out_dir = os.path.dirname(self.out_path)
|
|
||||||
if not os.path.isdir(out_dir) and out_dir != '':
|
|
||||||
try:
|
|
||||||
os.makedirs(out_dir)
|
|
||||||
except: # we get a WindowsError here in Windows.. pretty sure something else for Linux :D
|
|
||||||
log.error("Unable to create output directory %s for HHC!" % out_dir)
|
|
||||||
print "*** ERROR: UNABLE TO CREATE OUTPUT DIRECTORY", out_dir
|
|
||||||
# TODO: pop up a box to allow person to choose output directory?
|
|
||||||
# TODO: shouldn't that be done when we startup, actually?
|
|
||||||
else:
|
|
||||||
log.info("Created directory '%s'" % out_dir)
|
|
||||||
try:
|
|
||||||
self.out_fh = codecs.open(self.out_path, 'w', 'utf8')
|
|
||||||
except:
|
|
||||||
log.error("out_path %s couldn't be opened" % (self.out_path))
|
|
||||||
else:
|
|
||||||
log.debug("out_path %s opened as %s" % (self.out_path, self.out_fh))
|
|
||||||
|
|
||||||
self.follow = follow
|
self.follow = follow
|
||||||
self.compiledPlayers = set()
|
self.compiledPlayers = set()
|
||||||
|
@ -260,6 +248,11 @@ which it expects to find at self.re_TailSplitHands -- see for e.g. Everleaf.py.
|
||||||
m = re.compile('^Hand #\d+', re.MULTILINE)
|
m = re.compile('^Hand #\d+', re.MULTILINE)
|
||||||
self.obs = m.sub('', self.obs)
|
self.obs = m.sub('', self.obs)
|
||||||
|
|
||||||
|
if self.ftpArchive == True:
|
||||||
|
log.debug("Converting ftpArchive format to readable")
|
||||||
|
m = re.compile('^\*\*\*\*\*\*+\s#\s\d+\s\*\*\*\*\*+$', re.MULTILINE)
|
||||||
|
self.obs = m.sub('', self.obs)
|
||||||
|
|
||||||
if self.obs is None or self.obs == "":
|
if self.obs is None or self.obs == "":
|
||||||
log.info("Read no hands.")
|
log.info("Read no hands.")
|
||||||
return []
|
return []
|
||||||
|
@ -283,17 +276,16 @@ which it expects to find at self.re_TailSplitHands -- see for e.g. Everleaf.py.
|
||||||
if l in self.readSupportedGames():
|
if l in self.readSupportedGames():
|
||||||
if gametype['base'] == 'hold':
|
if gametype['base'] == 'hold':
|
||||||
log.debug("hand = Hand.HoldemOmahaHand(self, self.sitename, gametype, handtext)")
|
log.debug("hand = Hand.HoldemOmahaHand(self, self.sitename, gametype, handtext)")
|
||||||
hand = Hand.HoldemOmahaHand(self, self.sitename, gametype, handText)
|
hand = Hand.HoldemOmahaHand(self.config, self, self.sitename, gametype, handText)
|
||||||
elif gametype['base'] == 'stud':
|
elif gametype['base'] == 'stud':
|
||||||
hand = Hand.StudHand(self, self.sitename, gametype, handText)
|
hand = Hand.StudHand(self.config, self, self.sitename, gametype, handText)
|
||||||
elif gametype['base'] == 'draw':
|
elif gametype['base'] == 'draw':
|
||||||
hand = Hand.DrawHand(self, self.sitename, gametype, handText)
|
hand = Hand.DrawHand(self.config, self, self.sitename, gametype, handText)
|
||||||
else:
|
else:
|
||||||
log.info("Unsupported game type: %s" % gametype)
|
log.info("Unsupported game type: %s" % gametype)
|
||||||
|
|
||||||
if hand:
|
if hand:
|
||||||
if Configuration.NEWIMPORT == False:
|
#hand.writeHand(self.out_fh)
|
||||||
hand.writeHand(self.out_fh)
|
|
||||||
return hand
|
return hand
|
||||||
else:
|
else:
|
||||||
log.info("Unsupported game type: %s" % gametype)
|
log.info("Unsupported game type: %s" % gametype)
|
||||||
|
@ -418,7 +410,7 @@ or None if we fail to get the info """
|
||||||
list.pop() #Last entry is empty
|
list.pop() #Last entry is empty
|
||||||
for l in list:
|
for l in list:
|
||||||
# print "'" + l + "'"
|
# print "'" + l + "'"
|
||||||
hands = hands + [Hand.Hand(self.sitename, self.gametype, l)]
|
hands = hands + [Hand.Hand(self.config, self.sitename, self.gametype, l)]
|
||||||
# TODO: This looks like it could be replaced with a list comp.. ?
|
# TODO: This looks like it could be replaced with a list comp.. ?
|
||||||
return hands
|
return hands
|
||||||
|
|
||||||
|
@ -441,10 +433,9 @@ or None if we fail to get the info """
|
||||||
#print "trying", kodec
|
#print "trying", kodec
|
||||||
try:
|
try:
|
||||||
in_fh = codecs.open(self.in_path, 'r', kodec)
|
in_fh = codecs.open(self.in_path, 'r', kodec)
|
||||||
in_fh.seek(self.index)
|
whole_file = in_fh.read()
|
||||||
log.debug("Opened in_path: '%s' with %s" % (self.in_path, kodec))
|
self.obs = whole_file[self.index:]
|
||||||
self.obs = in_fh.read()
|
self.index = len(whole_file)
|
||||||
self.index = in_fh.tell()
|
|
||||||
in_fh.close()
|
in_fh.close()
|
||||||
break
|
break
|
||||||
except:
|
except:
|
||||||
|
@ -458,8 +449,8 @@ or None if we fail to get the info """
|
||||||
def guessMaxSeats(self, hand):
|
def guessMaxSeats(self, hand):
|
||||||
"""Return a guess at maxseats when not specified in HH."""
|
"""Return a guess at maxseats when not specified in HH."""
|
||||||
# if some other code prior to this has already set it, return it
|
# if some other code prior to this has already set it, return it
|
||||||
if maxseats > 1 and maxseats < 11:
|
if self.maxseats > 1 and self.maxseats < 11:
|
||||||
return maxseats
|
return self.maxseats
|
||||||
mo = self.maxOccSeat(hand)
|
mo = self.maxOccSeat(hand)
|
||||||
|
|
||||||
if mo == 10: return 10 #that was easy
|
if mo == 10: return 10 #that was easy
|
||||||
|
@ -512,7 +503,7 @@ or None if we fail to get the info """
|
||||||
def getTableTitleRe(type, table_name=None, tournament = None, table_number=None):
|
def getTableTitleRe(type, table_name=None, tournament = None, table_number=None):
|
||||||
"Returns string to search in windows titles"
|
"Returns string to search in windows titles"
|
||||||
if type=="tour":
|
if type=="tour":
|
||||||
return "%s.+Table\s%s" % (tournament, table_number)
|
return "%s.+Table.+%s" % (tournament, table_number)
|
||||||
else:
|
else:
|
||||||
return table_name
|
return table_name
|
||||||
|
|
||||||
|
@ -528,5 +519,22 @@ def getSiteHhc(config, sitename):
|
||||||
hhcModule = __import__(hhcName)
|
hhcModule = __import__(hhcName)
|
||||||
return getattr(hhcModule, hhcName[:-6])
|
return getattr(hhcModule, hhcName[:-6])
|
||||||
|
|
||||||
|
def get_out_fh(out_path, parameters):
|
||||||
|
if out_path == '-':
|
||||||
|
return(sys.stdout)
|
||||||
|
elif parameters['saveStarsHH']:
|
||||||
|
out_dir = os.path.dirname(out_path)
|
||||||
|
if not os.path.isdir(out_dir) and out_dir != '':
|
||||||
|
try:
|
||||||
|
os.makedirs(out_dir)
|
||||||
|
except: # we get a WindowsError here in Windows.. pretty sure something else for Linux :D
|
||||||
|
log.error("Unable to create output directory %s for HHC!" % out_dir)
|
||||||
|
print "*** ERROR: UNABLE TO CREATE OUTPUT DIRECTORY", out_dir
|
||||||
|
else:
|
||||||
|
log.info("Created directory '%s'" % out_dir)
|
||||||
|
try:
|
||||||
|
return(codecs.open(out_path, 'w', 'utf8'))
|
||||||
|
except:
|
||||||
|
log.error("out_path %s couldn't be opened" % (out_path))
|
||||||
|
else:
|
||||||
|
return(sys.stdout)
|
||||||
|
|
|
@ -26,6 +26,10 @@ Create and manage the hud overlays.
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
|
import logging
|
||||||
|
# logging has been set up in fpdb.py or HUD_main.py, use their settings:
|
||||||
|
log = logging.getLogger("hud")
|
||||||
|
|
||||||
# pyGTK modules
|
# pyGTK modules
|
||||||
import pygtk
|
import pygtk
|
||||||
import gtk
|
import gtk
|
||||||
|
@ -365,7 +369,7 @@ class Hud:
|
||||||
self.create(*self.creation_attrs)
|
self.create(*self.creation_attrs)
|
||||||
self.update(self.hand, self.config)
|
self.update(self.hand, self.config)
|
||||||
except Exception, e:
|
except Exception, e:
|
||||||
print "Exception:",str(e)
|
log.error("Exception:",str(e))
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def set_aggregation(self, widget, val):
|
def set_aggregation(self, widget, val):
|
||||||
|
@ -377,7 +381,7 @@ class Hud:
|
||||||
|
|
||||||
if self.hud_params['h_agg_bb_mult'] != num \
|
if self.hud_params['h_agg_bb_mult'] != num \
|
||||||
and getattr(self, 'h_aggBBmultItem'+str(num)).get_active():
|
and getattr(self, 'h_aggBBmultItem'+str(num)).get_active():
|
||||||
print 'set_player_aggregation', num
|
log.debug('set_player_aggregation', num)
|
||||||
self.hud_params['h_agg_bb_mult'] = num
|
self.hud_params['h_agg_bb_mult'] = num
|
||||||
for mult in ('1', '2', '3', '10', '10000'):
|
for mult in ('1', '2', '3', '10', '10000'):
|
||||||
if mult != str(num):
|
if mult != str(num):
|
||||||
|
@ -388,7 +392,7 @@ class Hud:
|
||||||
|
|
||||||
if self.hud_params['agg_bb_mult'] != num \
|
if self.hud_params['agg_bb_mult'] != num \
|
||||||
and getattr(self, 'aggBBmultItem'+str(num)).get_active():
|
and getattr(self, 'aggBBmultItem'+str(num)).get_active():
|
||||||
print 'set_opponent_aggregation', num
|
log.debug('set_opponent_aggregation', num)
|
||||||
self.hud_params['agg_bb_mult'] = num
|
self.hud_params['agg_bb_mult'] = num
|
||||||
for mult in ('1', '2', '3', '10', '10000'):
|
for mult in ('1', '2', '3', '10', '10000'):
|
||||||
if mult != str(num):
|
if mult != str(num):
|
||||||
|
@ -415,7 +419,7 @@ class Hud:
|
||||||
self.hud_params[param] = 'E'
|
self.hud_params[param] = 'E'
|
||||||
getattr(self, prefix+'seatsStyleOptionA').set_active(False)
|
getattr(self, prefix+'seatsStyleOptionA').set_active(False)
|
||||||
getattr(self, prefix+'seatsStyleOptionC').set_active(False)
|
getattr(self, prefix+'seatsStyleOptionC').set_active(False)
|
||||||
print "setting self.hud_params[%s] = %s" % (param, style)
|
log.debug("setting self.hud_params[%s] = %s" % (param, style))
|
||||||
|
|
||||||
def set_hud_style(self, widget, val):
|
def set_hud_style(self, widget, val):
|
||||||
(player_opp, style) = val
|
(player_opp, style) = val
|
||||||
|
@ -438,7 +442,7 @@ class Hud:
|
||||||
self.hud_params[param] = 'T'
|
self.hud_params[param] = 'T'
|
||||||
getattr(self, prefix+'hudStyleOptionA').set_active(False)
|
getattr(self, prefix+'hudStyleOptionA').set_active(False)
|
||||||
getattr(self, prefix+'hudStyleOptionS').set_active(False)
|
getattr(self, prefix+'hudStyleOptionS').set_active(False)
|
||||||
print "setting self.hud_params[%s] = %s" % (param, style)
|
log.debug("setting self.hud_params[%s] = %s" % (param, style))
|
||||||
|
|
||||||
def update_table_position(self):
|
def update_table_position(self):
|
||||||
if os.name == 'nt':
|
if os.name == 'nt':
|
||||||
|
@ -515,7 +519,7 @@ class Hud:
|
||||||
# ask each aux to save its layout back to the config object
|
# ask each aux to save its layout back to the config object
|
||||||
[aux.save_layout() for aux in self.aux_windows]
|
[aux.save_layout() for aux in self.aux_windows]
|
||||||
# save the config object back to the file
|
# save the config object back to the file
|
||||||
print "saving new xml file"
|
print "Updating config file"
|
||||||
self.config.save()
|
self.config.save()
|
||||||
|
|
||||||
def adj_seats(self, hand, config):
|
def adj_seats(self, hand, config):
|
||||||
|
@ -606,12 +610,13 @@ class Hud:
|
||||||
if self.update_table_position() == False: # we got killed by finding our table was gone
|
if self.update_table_position() == False: # we got killed by finding our table was gone
|
||||||
return
|
return
|
||||||
|
|
||||||
|
self.label.modify_fg(gtk.STATE_NORMAL, gtk.gdk.color_parse(self.colors['hudfgcolor']))
|
||||||
for s in self.stat_dict:
|
for s in self.stat_dict:
|
||||||
try:
|
try:
|
||||||
statd = self.stat_dict[s]
|
statd = self.stat_dict[s]
|
||||||
except KeyError:
|
except KeyError:
|
||||||
print "KeyError at the start of the for loop in update in hud_main. How this can possibly happen is totally beyond my comprehension. Your HUD may be about to get really weird. -Eric"
|
log.error("KeyError at the start of the for loop in update in hud_main. How this can possibly happen is totally beyond my comprehension. Your HUD may be about to get really weird. -Eric")
|
||||||
print "(btw, the key was ", s, " and statd is...", statd
|
log.error("(btw, the key was ", s, " and statd is...", statd)
|
||||||
continue
|
continue
|
||||||
try:
|
try:
|
||||||
self.stat_windows[statd['seat']].player_id = statd['player_id']
|
self.stat_windows[statd['seat']].player_id = statd['player_id']
|
||||||
|
@ -629,8 +634,17 @@ class Hud:
|
||||||
window = self.stat_windows[statd['seat']]
|
window = self.stat_windows[statd['seat']]
|
||||||
|
|
||||||
if this_stat.hudcolor != "":
|
if this_stat.hudcolor != "":
|
||||||
self.label.modify_fg(gtk.STATE_NORMAL, gtk.gdk.color_parse(self.colors['hudfgcolor']))
|
|
||||||
window.label[r][c].modify_fg(gtk.STATE_NORMAL, gtk.gdk.color_parse(this_stat.hudcolor))
|
window.label[r][c].modify_fg(gtk.STATE_NORMAL, gtk.gdk.color_parse(this_stat.hudcolor))
|
||||||
|
else:
|
||||||
|
window.label[r][c].modify_fg(gtk.STATE_NORMAL, gtk.gdk.color_parse(self.colors['hudfgcolor']))
|
||||||
|
|
||||||
|
if this_stat.stat_loth != "":
|
||||||
|
if number[0] < (float(this_stat.stat_loth)/100):
|
||||||
|
window.label[r][c].modify_fg(gtk.STATE_NORMAL, gtk.gdk.color_parse(this_stat.stat_locolor))
|
||||||
|
|
||||||
|
if this_stat.stat_hith != "":
|
||||||
|
if number[0] > (float(this_stat.stat_hith)/100):
|
||||||
|
window.label[r][c].modify_fg(gtk.STATE_NORMAL, gtk.gdk.color_parse(this_stat.stat_hicolor))
|
||||||
|
|
||||||
window.label[r][c].set_text(statstring)
|
window.label[r][c].set_text(statstring)
|
||||||
if statstring != "xxx": # is there a way to tell if this particular stat window is visible already, or no?
|
if statstring != "xxx": # is there a way to tell if this particular stat window is visible already, or no?
|
||||||
|
@ -666,6 +680,11 @@ class Stat_Window:
|
||||||
return True
|
return True
|
||||||
|
|
||||||
if event.button == 1: # left button event
|
if event.button == 1: # left button event
|
||||||
|
# close on double click for a stat window
|
||||||
|
# for those that don't have a mouse with middle button
|
||||||
|
if event.type == gtk.gdk._2BUTTON_PRESS:
|
||||||
|
self.window.hide()
|
||||||
|
return True
|
||||||
# TODO: make position saving save sizes as well?
|
# TODO: make position saving save sizes as well?
|
||||||
if event.state & gtk.gdk.SHIFT_MASK:
|
if event.state & gtk.gdk.SHIFT_MASK:
|
||||||
self.window.begin_resize_drag(gtk.gdk.WINDOW_EDGE_SOUTH_EAST, event.button, int(event.x_root), int(event.y_root), event.time)
|
self.window.begin_resize_drag(gtk.gdk.WINDOW_EDGE_SOUTH_EAST, event.button, int(event.x_root), int(event.y_root), event.time)
|
||||||
|
|
|
@ -128,7 +128,8 @@ class Stud_mucked(Aux_Window):
|
||||||
self.container.show_all()
|
self.container.show_all()
|
||||||
|
|
||||||
def update_data(self, new_hand_id, db_connection):
|
def update_data(self, new_hand_id, db_connection):
|
||||||
self.mucked_cards.update_data(new_hand_id, db_connection)
|
# uncomment next line when action is available in the db
|
||||||
|
# self.mucked_cards.update_data(new_hand_id, db_connection)
|
||||||
self.mucked_list.update_data(new_hand_id, db_connection)
|
self.mucked_list.update_data(new_hand_id, db_connection)
|
||||||
|
|
||||||
def update_gui(self, new_hand_id):
|
def update_gui(self, new_hand_id):
|
||||||
|
@ -357,21 +358,24 @@ class Aux_Seats(Aux_Window):
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
return
|
return
|
||||||
loc = self.config.get_aux_locations(self.params['name'], int(self.hud.max))
|
loc = self.config.get_aux_locations(self.params['name'], int(self.hud.max))
|
||||||
|
width = self.hud.table.width
|
||||||
|
height = self.hud.table.height
|
||||||
|
|
||||||
for i in (range(1, self.hud.max + 1) + ['common']):
|
for i in (range(1, self.hud.max + 1) + ['common']):
|
||||||
if i == 'common':
|
if i == 'common':
|
||||||
(x, y) = self.params['layout'][self.hud.max].common
|
(x, y) = self.params['layout'][self.hud.max].common
|
||||||
else:
|
else:
|
||||||
(x, y) = loc[adj[i]]
|
(x, y) = loc[adj[i]]
|
||||||
self.positions[i] = self.card_positions(x, self.hud.table.x, y, self.hud.table.y)
|
self.positions[i] = self.card_positions((x * width) / 1000, self.hud.table.x, (y * height) /1000, self.hud.table.y)
|
||||||
self.m_windows[i].move(self.positions[i][0], self.positions[i][1])
|
self.m_windows[i].move(self.positions[i][0], self.positions[i][1])
|
||||||
|
|
||||||
|
|
||||||
def create(self):
|
def create(self):
|
||||||
self.adj = self.hud.adj_seats(0, self.config) # move adj_seats to aux and get rid of it in Hud.py
|
self.adj = self.hud.adj_seats(0, self.config) # move adj_seats to aux and get rid of it in Hud.py
|
||||||
loc = self.config.get_aux_locations(self.params['name'], int(self.hud.max))
|
loc = self.config.get_aux_locations(self.params['name'], int(self.hud.max))
|
||||||
|
|
||||||
self.m_windows = {} # windows to put the card images in
|
self.m_windows = {} # windows to put the card images in
|
||||||
|
width = self.hud.table.width
|
||||||
|
height = self.hud.table.height
|
||||||
for i in (range(1, self.hud.max + 1) + ['common']):
|
for i in (range(1, self.hud.max + 1) + ['common']):
|
||||||
if i == 'common':
|
if i == 'common':
|
||||||
(x, y) = self.params['layout'][self.hud.max].common
|
(x, y) = self.params['layout'][self.hud.max].common
|
||||||
|
@ -383,7 +387,7 @@ class Aux_Seats(Aux_Window):
|
||||||
self.m_windows[i].set_transient_for(self.hud.main_window)
|
self.m_windows[i].set_transient_for(self.hud.main_window)
|
||||||
self.m_windows[i].set_focus_on_map(False)
|
self.m_windows[i].set_focus_on_map(False)
|
||||||
self.m_windows[i].connect("configure_event", self.configure_event_cb, i)
|
self.m_windows[i].connect("configure_event", self.configure_event_cb, i)
|
||||||
self.positions[i] = self.card_positions(x, self.hud.table.x, y, self.hud.table.y)
|
self.positions[i] = self.card_positions((x * width) / 1000, self.hud.table.x, (y * height) /1000, self.hud.table.y)
|
||||||
self.m_windows[i].move(self.positions[i][0], self.positions[i][1])
|
self.m_windows[i].move(self.positions[i][0], self.positions[i][1])
|
||||||
if self.params.has_key('opacity'):
|
if self.params.has_key('opacity'):
|
||||||
self.m_windows[i].set_opacity(float(self.params['opacity']))
|
self.m_windows[i].set_opacity(float(self.params['opacity']))
|
||||||
|
@ -425,11 +429,13 @@ class Aux_Seats(Aux_Window):
|
||||||
"""Save new layout back to the aux element in the config file."""
|
"""Save new layout back to the aux element in the config file."""
|
||||||
new_locs = {}
|
new_locs = {}
|
||||||
# print "adj =", self.adj
|
# print "adj =", self.adj
|
||||||
|
witdh = self.hud.table.width
|
||||||
|
height = self.hud.table.height
|
||||||
for (i, pos) in self.positions.iteritems():
|
for (i, pos) in self.positions.iteritems():
|
||||||
if i != 'common':
|
if i != 'common':
|
||||||
new_locs[self.adj[int(i)]] = (pos[0] - self.hud.table.x, pos[1] - self.hud.table.y)
|
new_locs[self.adj[int(i)]] = ((pos[0] - self.hud.table.x) * 1000 / witdh, (pos[1] - self.hud.table.y) * 1000 / height)
|
||||||
else:
|
else:
|
||||||
new_locs[i] = (pos[0] - self.hud.table.x, pos[1] - self.hud.table.y)
|
new_locs[i] = ((pos[0] - self.hud.table.x) * 1000 / witdh, (pos[1] - self.hud.table.y) * 1000 / height)
|
||||||
self.config.edit_aux_layout(self.params['name'], self.hud.max, locations = new_locs)
|
self.config.edit_aux_layout(self.params['name'], self.hud.max, locations = new_locs)
|
||||||
|
|
||||||
def configure_event_cb(self, widget, event, i, *args):
|
def configure_event_cb(self, widget, event, i, *args):
|
||||||
|
|
|
@ -15,9 +15,9 @@
|
||||||
#In the "official" distribution you can find the license in
|
#In the "official" distribution you can find the license in
|
||||||
#agpl-3.0.txt in the docs folder of the package.
|
#agpl-3.0.txt in the docs folder of the package.
|
||||||
|
|
||||||
import os
|
|
||||||
import sys
|
import sys
|
||||||
from optparse import OptionParser
|
from optparse import OptionParser
|
||||||
|
# http://docs.python.org/library/optparse.html
|
||||||
|
|
||||||
def fpdb_options():
|
def fpdb_options():
|
||||||
|
|
||||||
|
@ -27,7 +27,7 @@ def fpdb_options():
|
||||||
action="store_true",
|
action="store_true",
|
||||||
help="If passed error output will go to the console rather than .")
|
help="If passed error output will go to the console rather than .")
|
||||||
parser.add_option("-d", "--databaseName",
|
parser.add_option("-d", "--databaseName",
|
||||||
dest="dbname", default="fpdb",
|
dest="dbname",
|
||||||
help="Overrides the default database name")
|
help="Overrides the default database name")
|
||||||
parser.add_option("-c", "--configFile",
|
parser.add_option("-c", "--configFile",
|
||||||
dest="config", default=None,
|
dest="config", default=None,
|
||||||
|
@ -41,6 +41,14 @@ def fpdb_options():
|
||||||
parser.add_option("-k", "--konverter",
|
parser.add_option("-k", "--konverter",
|
||||||
dest="hhc", default="PokerStarsToFpdb",
|
dest="hhc", default="PokerStarsToFpdb",
|
||||||
help="Module name for Hand History Converter")
|
help="Module name for Hand History Converter")
|
||||||
|
parser.add_option("-l", "--logging",
|
||||||
|
dest = "log_level",
|
||||||
|
choices = ('DEBUG', 'INFO', 'WARNING', 'ERROR', 'CRITICAL', 'EMPTY'),
|
||||||
|
help = "Error logging level. (DEBUG, INFO, WARNING, ERROR, CRITICAL, EMPTY)",
|
||||||
|
default = 'EMPTY')
|
||||||
|
parser.add_option("-v", "--version", action = "store_true",
|
||||||
|
help = "Print version information and exit.")
|
||||||
|
|
||||||
(options, argv) = parser.parse_args()
|
(options, argv) = parser.parse_args()
|
||||||
return (options, argv)
|
return (options, argv)
|
||||||
|
|
||||||
|
|
|
@ -26,51 +26,43 @@ from HandHistoryConverter import *
|
||||||
|
|
||||||
# PartyPoker HH Format
|
# PartyPoker HH Format
|
||||||
|
|
||||||
class PartyPokerParseError(FpdbParseError):
|
class FpdbParseError(FpdbParseError):
|
||||||
"Usage: raise PartyPokerParseError(<msg>[, hh=<hh>][, hid=<hid>])"
|
"Usage: raise FpdbParseError(<msg>[, hh=<hh>][, hid=<hid>])"
|
||||||
|
|
||||||
def __init__(self, msg='', hh=None, hid=None):
|
def __init__(self, msg='', hh=None, hid=None):
|
||||||
if hh is not None:
|
return super(FpdbParseError, self).__init__(msg, hid=hid)
|
||||||
msg += "\n\nHand history attached below:\n" + self.wrapHh(hh)
|
|
||||||
return super(PartyPokerParseError, self).__init__(msg, hid=hid)
|
|
||||||
|
|
||||||
def wrapHh(self, hh):
|
def wrapHh(self, hh):
|
||||||
return ("%(DELIMETER)s\n%(HH)s\n%(DELIMETER)s") % \
|
return ("%(DELIMETER)s\n%(HH)s\n%(DELIMETER)s") % \
|
||||||
{'DELIMETER': '#'*50, 'HH': hh}
|
{'DELIMETER': '#'*50, 'HH': hh}
|
||||||
|
|
||||||
class PartyPoker(HandHistoryConverter):
|
class PartyPoker(HandHistoryConverter):
|
||||||
|
|
||||||
############################################################
|
|
||||||
# Class Variables
|
|
||||||
|
|
||||||
sitename = "PartyPoker"
|
sitename = "PartyPoker"
|
||||||
codepage = "cp1252"
|
codepage = "cp1252"
|
||||||
siteId = 9 # TODO: automate; it's a class variable so shouldn't hit DB too often
|
siteId = 9
|
||||||
filetype = "text" # "text" or "xml". I propose we subclass HHC to HHC_Text and HHC_XML.
|
filetype = "text"
|
||||||
|
|
||||||
|
|
||||||
sym = {'USD': "\$", }
|
sym = {'USD': "\$", }
|
||||||
|
|
||||||
# Static regexes
|
# Static regexes
|
||||||
# $5 USD NL Texas Hold'em - Saturday, July 25, 07:53:52 EDT 2009
|
# $5 USD NL Texas Hold'em - Saturday, July 25, 07:53:52 EDT 2009
|
||||||
# NL Texas Hold'em $1 USD Buy-in Trny:45685440 Level:8 Blinds-Antes(600/1 200 -50) - Sunday, May 17, 11:25:07 MSKS 2009
|
# NL Texas Hold'em $1 USD Buy-in Trny:45685440 Level:8 Blinds-Antes(600/1 200 -50) - Sunday, May 17, 11:25:07 MSKS 2009
|
||||||
re_GameInfoRing = re.compile("""
|
re_GameInfoRing = re.compile("""
|
||||||
(?P<CURRENCY>\$|)\s*(?P<RINGLIMIT>[0-9,]+)\s*(?:USD)?\s*
|
(?P<CURRENCY>\$|)\s*(?P<RINGLIMIT>[.,0-9]+)([.,0-9/$]+)?\s*(?:USD)?\s*
|
||||||
(?P<LIMIT>(NL|PL|))\s+
|
(?P<LIMIT>(NL|PL|))\s*
|
||||||
(?P<GAME>(Texas\ Hold\'em|Omaha))
|
(?P<GAME>(Texas\ Hold\'em|Omaha))
|
||||||
\s*\-\s*
|
\s*\-\s*
|
||||||
(?P<DATETIME>.+)
|
(?P<DATETIME>.+)
|
||||||
""", re.VERBOSE)
|
""", re.VERBOSE)
|
||||||
re_GameInfoTrny = re.compile("""
|
re_GameInfoTrny = re.compile("""
|
||||||
(?P<LIMIT>(NL|PL|))\s+
|
(?P<LIMIT>(NL|PL|))\s*
|
||||||
(?P<GAME>(Texas\ Hold\'em|Omaha))\s+
|
(?P<GAME>(Texas\ Hold\'em|Omaha))\s+
|
||||||
(?P<BUYIN>\$?[.0-9]+)\s*(?P<BUYIN_CURRENCY>USD)?\s*Buy-in\s+
|
(?:(?P<BUYIN>\$?[.,0-9]+)\s*(?P<BUYIN_CURRENCY>USD)?\s*Buy-in\s+)?
|
||||||
Trny:\s?(?P<TOURNO>\d+)\s+
|
Trny:\s?(?P<TOURNO>\d+)\s+
|
||||||
Level:\s*(?P<LEVEL>\d+)\s+
|
Level:\s*(?P<LEVEL>\d+)\s+
|
||||||
Blinds(?:-Antes)?\(
|
((Blinds|Stakes)(?:-Antes)?)\(
|
||||||
(?P<SB>[.0-9 ]+)\s*
|
(?P<SB>[.,0-9 ]+)\s*
|
||||||
/(?P<BB>[.0-9 ]+)
|
/(?P<BB>[.,0-9 ]+)
|
||||||
(?:\s*-\s*(?P<ANTE>[.0-9 ]+)\$?)?
|
(?:\s*-\s*(?P<ANTE>[.,0-9 ]+)\$?)?
|
||||||
\)
|
\)
|
||||||
\s*\-\s*
|
\s*\-\s*
|
||||||
(?P<DATETIME>.+)
|
(?P<DATETIME>.+)
|
||||||
|
@ -85,16 +77,17 @@ class PartyPoker(HandHistoryConverter):
|
||||||
re.VERBOSE)
|
re.VERBOSE)
|
||||||
|
|
||||||
re_HandInfo = re.compile("""
|
re_HandInfo = re.compile("""
|
||||||
^Table\s+
|
^Table\s+(?P<TTYPE>[$a-zA-Z0-9 ]+)?\s+
|
||||||
(?P<TTYPE>[a-zA-Z0-9 ]+)\s+
|
|
||||||
(?: \#|\(|)(?P<TABLE>\d+)\)?\s+
|
(?: \#|\(|)(?P<TABLE>\d+)\)?\s+
|
||||||
(?:[^ ]+\s+\#(?P<MTTTABLE>\d+).+)? # table number for mtt
|
(?:[a-zA-Z0-9 ]+\s+\#(?P<MTTTABLE>\d+).+)?
|
||||||
|
(\(No\sDP\)\s)?
|
||||||
\((?P<PLAY>Real|Play)\s+Money\)\s+ # FIXME: check if play money is correct
|
\((?P<PLAY>Real|Play)\s+Money\)\s+ # FIXME: check if play money is correct
|
||||||
Seat\s+(?P<BUTTON>\d+)\sis\sthe\sbutton
|
Seat\s+(?P<BUTTON>\d+)\sis\sthe\sbutton
|
||||||
|
\s+Total\s+number\s+of\s+players\s+\:\s+(?P<PLYRS>\d+)/?(?P<MAX>\d+)?
|
||||||
""",
|
""",
|
||||||
re.MULTILINE|re.VERBOSE)
|
re.VERBOSE|re.MULTILINE|re.DOTALL)
|
||||||
|
|
||||||
# re_TotalPlayers = re.compile("^Total\s+number\s+of\s+players\s*:\s*(?P<MAXSEATS>\d+)", re.MULTILINE)
|
re_CountedSeats = re.compile("^Total\s+number\s+of\s+players\s*:\s*(?P<COUNTED_SEATS>\d+)", re.MULTILINE)
|
||||||
re_SplitHands = re.compile('\x00+')
|
re_SplitHands = re.compile('\x00+')
|
||||||
re_TailSplitHands = re.compile('(\x00+)')
|
re_TailSplitHands = re.compile('(\x00+)')
|
||||||
lineSplitter = '\n'
|
lineSplitter = '\n'
|
||||||
|
@ -114,7 +107,6 @@ class PartyPoker(HandHistoryConverter):
|
||||||
def guessMaxSeats(self, hand):
|
def guessMaxSeats(self, hand):
|
||||||
"""Return a guess at max_seats when not specified in HH."""
|
"""Return a guess at max_seats when not specified in HH."""
|
||||||
mo = self.maxOccSeat(hand)
|
mo = self.maxOccSeat(hand)
|
||||||
|
|
||||||
if mo == 10: return mo
|
if mo == 10: return mo
|
||||||
if mo == 2: return 2
|
if mo == 2: return 2
|
||||||
if mo <= 6: return 6
|
if mo <= 6: return 6
|
||||||
|
@ -131,21 +123,17 @@ class PartyPoker(HandHistoryConverter):
|
||||||
'CUR': hand.gametype['currency'] if hand.gametype['currency']!='T$' else ''}
|
'CUR': hand.gametype['currency'] if hand.gametype['currency']!='T$' else ''}
|
||||||
for key in ('CUR_SYM', 'CUR'):
|
for key in ('CUR_SYM', 'CUR'):
|
||||||
subst[key] = re.escape(subst[key])
|
subst[key] = re.escape(subst[key])
|
||||||
log.debug("player_re: '%s'" % subst['PLYR'])
|
|
||||||
log.debug("CUR_SYM: '%s'" % subst['CUR_SYM'])
|
|
||||||
log.debug("CUR: '%s'" % subst['CUR'])
|
|
||||||
self.re_PostSB = re.compile(
|
self.re_PostSB = re.compile(
|
||||||
r"^%(PLYR)s posts small blind \[%(CUR_SYM)s(?P<SB>[.0-9]+) ?%(CUR)s\]\." % subst,
|
r"^%(PLYR)s posts small blind \[%(CUR_SYM)s(?P<SB>[.,0-9]+) ?%(CUR)s\]\." % subst,
|
||||||
re.MULTILINE)
|
re.MULTILINE)
|
||||||
self.re_PostBB = re.compile(
|
self.re_PostBB = re.compile(
|
||||||
r"^%(PLYR)s posts big blind \[%(CUR_SYM)s(?P<BB>[.0-9]+) ?%(CUR)s\]\." % subst,
|
r"^%(PLYR)s posts big blind \[%(CUR_SYM)s(?P<BB>[.,0-9]+) ?%(CUR)s\]\." % subst,
|
||||||
re.MULTILINE)
|
re.MULTILINE)
|
||||||
# NOTE: comma is used as a fraction part delimeter in re below
|
|
||||||
self.re_PostDead = re.compile(
|
self.re_PostDead = re.compile(
|
||||||
r"^%(PLYR)s posts big blind \+ dead \[(?P<BBNDEAD>[.,0-9]+) ?%(CUR_SYM)s\]\." % subst,
|
r"^%(PLYR)s posts big blind \+ dead \[(?P<BBNDEAD>[.,0-9]+) ?%(CUR_SYM)s\]\." % subst,
|
||||||
re.MULTILINE)
|
re.MULTILINE)
|
||||||
self.re_Antes = re.compile(
|
self.re_Antes = re.compile(
|
||||||
r"^%(PLYR)s posts ante \[%(CUR_SYM)s(?P<ANTE>[.0-9]+) ?%(CUR)s\]" % subst,
|
r"^%(PLYR)s posts ante \[%(CUR_SYM)s(?P<ANTE>[.,0-9]+) ?%(CUR)s\]" % subst,
|
||||||
re.MULTILINE)
|
re.MULTILINE)
|
||||||
self.re_HeroCards = re.compile(
|
self.re_HeroCards = re.compile(
|
||||||
r"^Dealt to %(PLYR)s \[\s*(?P<NEWCARDS>.+)\s*\]" % subst,
|
r"^Dealt to %(PLYR)s \[\s*(?P<NEWCARDS>.+)\s*\]" % subst,
|
||||||
|
@ -195,8 +183,6 @@ class PartyPoker(HandHistoryConverter):
|
||||||
gametype dict is:
|
gametype dict is:
|
||||||
{'limitType': xxx, 'base': xxx, 'category': xxx}"""
|
{'limitType': xxx, 'base': xxx, 'category': xxx}"""
|
||||||
|
|
||||||
log.debug(PartyPokerParseError().wrapHh( handText ))
|
|
||||||
|
|
||||||
info = {}
|
info = {}
|
||||||
m = self._getGameType(handText)
|
m = self._getGameType(handText)
|
||||||
if m is None:
|
if m is None:
|
||||||
|
@ -213,22 +199,16 @@ class PartyPoker(HandHistoryConverter):
|
||||||
|
|
||||||
for expectedField in ['LIMIT', 'GAME']:
|
for expectedField in ['LIMIT', 'GAME']:
|
||||||
if mg[expectedField] is None:
|
if mg[expectedField] is None:
|
||||||
raise PartyPokerParseError(
|
raise FpdbParseError( "Cannot fetch field '%s'" % expectedField)
|
||||||
"Cannot fetch field '%s'" % expectedField,
|
|
||||||
hh = handText)
|
|
||||||
try:
|
try:
|
||||||
info['limitType'] = limits[mg['LIMIT'].strip()]
|
info['limitType'] = limits[mg['LIMIT'].strip()]
|
||||||
except:
|
except:
|
||||||
raise PartyPokerParseError(
|
raise FpdbParseError("Unknown limit '%s'" % mg['LIMIT'])
|
||||||
"Unknown limit '%s'" % mg['LIMIT'],
|
|
||||||
hh = handText)
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
(info['base'], info['category']) = games[mg['GAME']]
|
(info['base'], info['category']) = games[mg['GAME']]
|
||||||
except:
|
except:
|
||||||
raise PartyPokerParseError(
|
raise FpdbParseError("Unknown game type '%s'" % mg['GAME'])
|
||||||
"Unknown game type '%s'" % mg['GAME'],
|
|
||||||
hh = handText)
|
|
||||||
|
|
||||||
if 'TOURNO' in mg:
|
if 'TOURNO' in mg:
|
||||||
info['type'] = 'tour'
|
info['type'] = 'tour'
|
||||||
|
@ -251,23 +231,21 @@ class PartyPoker(HandHistoryConverter):
|
||||||
try:
|
try:
|
||||||
info.update(self.re_Hid.search(hand.handText).groupdict())
|
info.update(self.re_Hid.search(hand.handText).groupdict())
|
||||||
except:
|
except:
|
||||||
raise PartyPokerParseError("Cannot read HID for current hand", hh=hand.handText)
|
raise FpdbParseError("Cannot read HID for current hand")
|
||||||
|
|
||||||
try:
|
try:
|
||||||
info.update(self.re_HandInfo.search(hand.handText,re.DOTALL).groupdict())
|
info.update(self.re_HandInfo.search(hand.handText,re.DOTALL).groupdict())
|
||||||
except:
|
except:
|
||||||
raise PartyPokerParseError("Cannot read Handinfo for current hand",
|
raise FpdbParseError("Cannot read Handinfo for current hand", hid = info['HID'])
|
||||||
hh=hand.handText, hid = info['HID'])
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
info.update(self._getGameType(hand.handText).groupdict())
|
info.update(self._getGameType(hand.handText).groupdict())
|
||||||
except:
|
except:
|
||||||
raise PartyPokerParseError("Cannot read GameType for current hand",
|
raise FpdbParseError("Cannot read GameType for current hand", hid = info['HID'])
|
||||||
hh=hand.handText, hid = info['HID'])
|
|
||||||
|
|
||||||
|
|
||||||
# m = self.re_TotalPlayers.search(hand.handText)
|
m = self.re_CountedSeats.search(hand.handText)
|
||||||
# if m: info.update(m.groupdict())
|
if m: info.update(m.groupdict())
|
||||||
|
|
||||||
|
|
||||||
# FIXME: it's dirty hack
|
# FIXME: it's dirty hack
|
||||||
|
@ -282,6 +260,7 @@ class PartyPoker(HandHistoryConverter):
|
||||||
for i,v in enumerate(self.collected):
|
for i,v in enumerate(self.collected):
|
||||||
if v[0] in self.pot.returned:
|
if v[0] in self.pot.returned:
|
||||||
self.collected[i][1] = Decimal(v[1]) - self.pot.returned[v[0]]
|
self.collected[i][1] = Decimal(v[1]) - self.pot.returned[v[0]]
|
||||||
|
self.collectees[v[0]] -= self.pot.returned[v[0]]
|
||||||
return origTotalPot()
|
return origTotalPot()
|
||||||
return totalPot
|
return totalPot
|
||||||
instancemethod = type(hand.totalPot)
|
instancemethod = type(hand.totalPot)
|
||||||
|
@ -294,6 +273,7 @@ class PartyPoker(HandHistoryConverter):
|
||||||
if key == 'DATETIME':
|
if key == 'DATETIME':
|
||||||
#Saturday, July 25, 07:53:52 EDT 2009
|
#Saturday, July 25, 07:53:52 EDT 2009
|
||||||
#Thursday, July 30, 21:40:41 MSKS 2009
|
#Thursday, July 30, 21:40:41 MSKS 2009
|
||||||
|
#Sunday, October 25, 13:39:07 MSK 2009
|
||||||
m2 = re.search("\w+, (?P<M>\w+) (?P<D>\d+), (?P<H>\d+):(?P<MIN>\d+):(?P<S>\d+) (?P<TZ>[A-Z]+) (?P<Y>\d+)", info[key])
|
m2 = re.search("\w+, (?P<M>\w+) (?P<D>\d+), (?P<H>\d+):(?P<MIN>\d+):(?P<S>\d+) (?P<TZ>[A-Z]+) (?P<Y>\d+)", info[key])
|
||||||
# we cant use '%B' due to locale problems
|
# we cant use '%B' due to locale problems
|
||||||
months = ['January', 'February', 'March', 'April','May', 'June',
|
months = ['January', 'February', 'March', 'April','May', 'June',
|
||||||
|
@ -309,20 +289,34 @@ class PartyPoker(HandHistoryConverter):
|
||||||
hand.handid = info[key]
|
hand.handid = info[key]
|
||||||
if key == 'TABLE':
|
if key == 'TABLE':
|
||||||
hand.tablename = info[key]
|
hand.tablename = info[key]
|
||||||
|
if key == 'MTTTABLE':
|
||||||
|
if info[key] != None:
|
||||||
|
hand.tablename = info[key]
|
||||||
|
hand.tourNo = info['TABLE']
|
||||||
if key == 'BUTTON':
|
if key == 'BUTTON':
|
||||||
hand.buttonpos = info[key]
|
hand.buttonpos = info[key]
|
||||||
if key == 'TOURNO':
|
if key == 'TOURNO':
|
||||||
hand.tourNo = info[key]
|
hand.tourNo = info[key]
|
||||||
|
if key == 'TABLE_ID_WRAPPER':
|
||||||
|
if info[key] == '#':
|
||||||
|
# FIXME: there is no such property in Hand class
|
||||||
|
self.isSNG = True
|
||||||
if key == 'BUYIN':
|
if key == 'BUYIN':
|
||||||
# FIXME: it's dirty hack T_T
|
# FIXME: it's dirty hack T_T
|
||||||
# code below assumes that tournament rake is equal to zero
|
# code below assumes that tournament rake is equal to zero
|
||||||
|
if info[key] == None:
|
||||||
|
hand.buyin = '$0+$0'
|
||||||
|
else:
|
||||||
cur = info[key][0] if info[key][0] not in '0123456789' else ''
|
cur = info[key][0] if info[key][0] not in '0123456789' else ''
|
||||||
hand.buyin = info[key] + '+%s0' % cur
|
hand.buyin = info[key] + '+%s0' % cur
|
||||||
if key == 'LEVEL':
|
if key == 'LEVEL':
|
||||||
hand.level = info[key]
|
hand.level = info[key]
|
||||||
if key == 'PLAY' and info['PLAY'] != 'Real':
|
if key == 'PLAY' and info['PLAY'] != 'Real':
|
||||||
# if realy there's no play money hh on party
|
# if realy party doesn's save play money hh
|
||||||
hand.gametype['currency'] = 'play'
|
hand.gametype['currency'] = 'play'
|
||||||
|
if key == 'MAX' and info[key] is not None:
|
||||||
|
hand.maxseats = int(info[key])
|
||||||
|
|
||||||
|
|
||||||
def readButton(self, hand):
|
def readButton(self, hand):
|
||||||
m = self.re_Button.search(hand.handText)
|
m = self.re_Button.search(hand.handText)
|
||||||
|
@ -406,8 +400,6 @@ class PartyPoker(HandHistoryConverter):
|
||||||
blind = smartMin(hand.bb, playersMap[bigBlindSeat][1])
|
blind = smartMin(hand.bb, playersMap[bigBlindSeat][1])
|
||||||
hand.addBlind(playersMap[bigBlindSeat][0], 'big blind', blind)
|
hand.addBlind(playersMap[bigBlindSeat][0], 'big blind', blind)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def readHeroCards(self, hand):
|
def readHeroCards(self, hand):
|
||||||
# we need to grab hero's cards
|
# we need to grab hero's cards
|
||||||
for street in ('PREFLOP',):
|
for street in ('PREFLOP',):
|
||||||
|
@ -418,7 +410,6 @@ class PartyPoker(HandHistoryConverter):
|
||||||
newcards = renderCards(found.group('NEWCARDS'))
|
newcards = renderCards(found.group('NEWCARDS'))
|
||||||
hand.addHoleCards(street, hand.hero, closed=newcards, shown=False, mucked=False, dealt=True)
|
hand.addHoleCards(street, hand.hero, closed=newcards, shown=False, mucked=False, dealt=True)
|
||||||
|
|
||||||
|
|
||||||
def readAction(self, hand, street):
|
def readAction(self, hand, street):
|
||||||
m = self.re_Action.finditer(hand.streets[street])
|
m = self.re_Action.finditer(hand.streets[street])
|
||||||
for action in m:
|
for action in m:
|
||||||
|
@ -453,10 +444,9 @@ class PartyPoker(HandHistoryConverter):
|
||||||
elif actionType == 'checks':
|
elif actionType == 'checks':
|
||||||
hand.addCheck( street, playerName )
|
hand.addCheck( street, playerName )
|
||||||
else:
|
else:
|
||||||
raise PartyPokerParseError(
|
raise FpdbParseError(
|
||||||
"Unimplemented readAction: '%s' '%s'" % (playerName,actionType,),
|
"Unimplemented readAction: '%s' '%s'" % (playerName,actionType,),
|
||||||
hid = hand.hid, hh = hand.handText )
|
hid = hand.hid, )
|
||||||
|
|
||||||
|
|
||||||
def readShowdownActions(self, hand):
|
def readShowdownActions(self, hand):
|
||||||
# all action in readShownCards
|
# all action in readShownCards
|
||||||
|
@ -475,6 +465,18 @@ class PartyPoker(HandHistoryConverter):
|
||||||
|
|
||||||
hand.addShownCards(cards=cards, player=m.group('PNAME'), shown=True, mucked=mucked)
|
hand.addShownCards(cards=cards, player=m.group('PNAME'), shown=True, mucked=mucked)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def getTableTitleRe(type, table_name=None, tournament = None, table_number=None):
|
||||||
|
"Returns string to search in windows titles"
|
||||||
|
if type=="tour":
|
||||||
|
TableName = table_name.split(" ")
|
||||||
|
print 'party', 'getTableTitleRe', "%s.+Table\s#%s" % (TableName[0], table_number)
|
||||||
|
return "%s.+Table\s#%s" % (TableName[0], table_number)
|
||||||
|
else:
|
||||||
|
print 'party', 'getTableTitleRe', table_number
|
||||||
|
return table_name
|
||||||
|
|
||||||
|
|
||||||
def ringBlinds(ringLimit):
|
def ringBlinds(ringLimit):
|
||||||
"Returns blinds for current limit in cash games"
|
"Returns blinds for current limit in cash games"
|
||||||
ringLimit = float(clearMoneyString(ringLimit))
|
ringLimit = float(clearMoneyString(ringLimit))
|
||||||
|
|
74
pyfpdb/PokerStarsToFpdb.py
Executable file → Normal file
74
pyfpdb/PokerStarsToFpdb.py
Executable file → Normal file
|
@ -46,12 +46,12 @@ class PokerStars(HandHistoryConverter):
|
||||||
PokerStars\sGame\s\#(?P<HID>[0-9]+):\s+
|
PokerStars\sGame\s\#(?P<HID>[0-9]+):\s+
|
||||||
(Tournament\s\# # open paren of tournament info
|
(Tournament\s\# # open paren of tournament info
|
||||||
(?P<TOURNO>\d+),\s
|
(?P<TOURNO>\d+),\s
|
||||||
(?P<BUYIN>[%(LS)s\+\d\.]+ # here's how I plan to use LS
|
# here's how I plan to use LS
|
||||||
\s?(?P<TOUR_ISO>%(LEGAL_ISO)s)?
|
(?P<BUYIN>([%(LS)s\+\d\.]+\s?(?P<TOUR_ISO>%(LEGAL_ISO)s)?)|Freeroll)\s+)?
|
||||||
)\s)? # close paren of tournament info
|
# close paren of tournament info
|
||||||
(?P<MIXED>HORSE|8\-Game|HOSE)?\s?\(?
|
(?P<MIXED>HORSE|8\-Game|HOSE)?\s?\(?
|
||||||
(?P<GAME>Hold\'em|Razz|7\sCard\sStud|7\sCard\sStud\sHi/Lo|Omaha|Omaha\sHi/Lo|Badugi|Triple\sDraw\s2\-7\sLowball|5\sCard\sDraw)\s
|
(?P<GAME>Hold\'em|Razz|RAZZ|7\sCard\sStud|7\sCard\sStud\sHi/Lo|Omaha|Omaha\sHi/Lo|Badugi|Triple\sDraw\s2\-7\sLowball|5\sCard\sDraw)\s
|
||||||
(?P<LIMIT>No\sLimit|Limit|Pot\sLimit)\)?,?\s
|
(?P<LIMIT>No\sLimit|Limit|LIMIT|Pot\sLimit)\)?,?\s
|
||||||
(-\sLevel\s(?P<LEVEL>[IVXLC]+)\s)?
|
(-\sLevel\s(?P<LEVEL>[IVXLC]+)\s)?
|
||||||
\(? # open paren of the stakes
|
\(? # open paren of the stakes
|
||||||
(?P<CURRENCY>%(LS)s|)?
|
(?P<CURRENCY>%(LS)s|)?
|
||||||
|
@ -69,7 +69,7 @@ class PokerStars(HandHistoryConverter):
|
||||||
re.MULTILINE|re.VERBOSE)
|
re.MULTILINE|re.VERBOSE)
|
||||||
|
|
||||||
re_HandInfo = re.compile("""
|
re_HandInfo = re.compile("""
|
||||||
^Table\s\'(?P<TABLE>[-\ a-zA-Z\d]+)\'\s
|
^Table\s\'(?P<TABLE>[-\ \#a-zA-Z\d]+)\'\s
|
||||||
((?P<MAX>\d+)-max\s)?
|
((?P<MAX>\d+)-max\s)?
|
||||||
(?P<PLAY>\(Play\sMoney\)\s)?
|
(?P<PLAY>\(Play\sMoney\)\s)?
|
||||||
(Seat\s\#(?P<BUTTON>\d+)\sis\sthe\sbutton)?""",
|
(Seat\s\#(?P<BUTTON>\d+)\sis\sthe\sbutton)?""",
|
||||||
|
@ -81,6 +81,7 @@ class PokerStars(HandHistoryConverter):
|
||||||
re_Board = re.compile(r"\[(?P<CARDS>.+)\]")
|
re_Board = re.compile(r"\[(?P<CARDS>.+)\]")
|
||||||
# self.re_setHandInfoRegex('.*#(?P<HID>[0-9]+): Table (?P<TABLE>[ a-zA-Z]+) - \$?(?P<SB>[.0-9]+)/\$?(?P<BB>[.0-9]+) - (?P<GAMETYPE>.*) - (?P<HR>[0-9]+):(?P<MIN>[0-9]+) ET - (?P<YEAR>[0-9]+)/(?P<MON>[0-9]+)/(?P<DAY>[0-9]+)Table (?P<TABLE>[ a-zA-Z]+)\nSeat (?P<BUTTON>[0-9]+)')
|
# self.re_setHandInfoRegex('.*#(?P<HID>[0-9]+): Table (?P<TABLE>[ a-zA-Z]+) - \$?(?P<SB>[.0-9]+)/\$?(?P<BB>[.0-9]+) - (?P<GAMETYPE>.*) - (?P<HR>[0-9]+):(?P<MIN>[0-9]+) ET - (?P<YEAR>[0-9]+)/(?P<MON>[0-9]+)/(?P<DAY>[0-9]+)Table (?P<TABLE>[ a-zA-Z]+)\nSeat (?P<BUTTON>[0-9]+)')
|
||||||
|
|
||||||
|
re_DateTime = re.compile("""(?P<Y>[0-9]{4})\/(?P<M>[0-9]{2})\/(?P<D>[0-9]{2})[\- ]+(?P<H>[0-9]+):(?P<MIN>[0-9]+):(?P<S>[0-9]+)""", re.MULTILINE)
|
||||||
|
|
||||||
def compilePlayerRegexs(self, hand):
|
def compilePlayerRegexs(self, hand):
|
||||||
players = set([player[1] for player in hand.players])
|
players = set([player[1] for player in hand.players])
|
||||||
|
@ -97,15 +98,16 @@ class PokerStars(HandHistoryConverter):
|
||||||
self.re_PostBB = re.compile(r"^%(PLYR)s: posts big blind %(CUR)s(?P<BB>[.0-9]+)" % subst, re.MULTILINE)
|
self.re_PostBB = re.compile(r"^%(PLYR)s: posts big blind %(CUR)s(?P<BB>[.0-9]+)" % subst, re.MULTILINE)
|
||||||
self.re_Antes = re.compile(r"^%(PLYR)s: posts the ante %(CUR)s(?P<ANTE>[.0-9]+)" % subst, re.MULTILINE)
|
self.re_Antes = re.compile(r"^%(PLYR)s: posts the ante %(CUR)s(?P<ANTE>[.0-9]+)" % subst, re.MULTILINE)
|
||||||
self.re_BringIn = re.compile(r"^%(PLYR)s: brings[- ]in( low|) for %(CUR)s(?P<BRINGIN>[.0-9]+)" % subst, re.MULTILINE)
|
self.re_BringIn = re.compile(r"^%(PLYR)s: brings[- ]in( low|) for %(CUR)s(?P<BRINGIN>[.0-9]+)" % subst, re.MULTILINE)
|
||||||
self.re_PostBoth = re.compile(r"^%(PLYR)s: posts small \& big blinds \[%(CUR)s (?P<SBBB>[.0-9]+)" % subst, re.MULTILINE)
|
self.re_PostBoth = re.compile(r"^%(PLYR)s: posts small \& big blinds %(CUR)s(?P<SBBB>[.0-9]+)" % subst, re.MULTILINE)
|
||||||
self.re_HeroCards = re.compile(r"^Dealt to %(PLYR)s(?: \[(?P<OLDCARDS>.+?)\])?( \[(?P<NEWCARDS>.+?)\])" % subst, re.MULTILINE)
|
self.re_HeroCards = re.compile(r"^Dealt to %(PLYR)s(?: \[(?P<OLDCARDS>.+?)\])?( \[(?P<NEWCARDS>.+?)\])" % subst, re.MULTILINE)
|
||||||
self.re_Action = re.compile(r"""
|
self.re_Action = re.compile(r"""
|
||||||
^%(PLYR)s:(?P<ATYPE>\sbets|\schecks|\sraises|\scalls|\sfolds|\sdiscards|\sstands\spat)
|
^%(PLYR)s:(?P<ATYPE>\sbets|\schecks|\sraises|\scalls|\sfolds|\sdiscards|\sstands\spat)
|
||||||
(\s(%(CUR)s)?(?P<BET>[.\d]+))?(\sto\s%(CUR)s(?P<BETTO>[.\d]+))? # the number discarded goes in <BET>
|
(\s(%(CUR)s)?(?P<BET>[.\d]+))?(\sto\s%(CUR)s(?P<BETTO>[.\d]+))? # the number discarded goes in <BET>
|
||||||
(\scards?(\s\[(?P<DISCARDED>.+?)\])?)?"""
|
\s*(and\sis\sall.in)?
|
||||||
|
(\scards?(\s\[(?P<DISCARDED>.+?)\])?)?\s*$"""
|
||||||
% subst, re.MULTILINE|re.VERBOSE)
|
% subst, re.MULTILINE|re.VERBOSE)
|
||||||
self.re_ShowdownAction = re.compile(r"^%s: shows \[(?P<CARDS>.*)\]" % player_re, re.MULTILINE)
|
self.re_ShowdownAction = re.compile(r"^%s: shows \[(?P<CARDS>.*)\]" % player_re, re.MULTILINE)
|
||||||
self.re_CollectPot = re.compile(r"Seat (?P<SEAT>[0-9]+): %(PLYR)s (\(button\) |\(small blind\) |\(big blind\) )?(collected|showed \[.*\] and won) \(%(CUR)s(?P<POT>[.\d]+)\)(, mucked| with.*|)" % subst, re.MULTILINE)
|
self.re_CollectPot = re.compile(r"Seat (?P<SEAT>[0-9]+): %(PLYR)s (\(button\) |\(small blind\) |\(big blind\) |\(button\) \(small blind\) )?(collected|showed \[.*\] and won) \(%(CUR)s(?P<POT>[.\d]+)\)(, mucked| with.*|)" % subst, re.MULTILINE)
|
||||||
self.re_sitsOut = re.compile("^%s sits out" % player_re, re.MULTILINE)
|
self.re_sitsOut = re.compile("^%s sits out" % player_re, re.MULTILINE)
|
||||||
self.re_ShownCards = re.compile("^Seat (?P<SEAT>[0-9]+): %s (\(.*\) )?(?P<SHOWED>showed|mucked) \[(?P<CARDS>.*)\].*" % player_re, re.MULTILINE)
|
self.re_ShownCards = re.compile("^Seat (?P<SEAT>[0-9]+): %s (\(.*\) )?(?P<SHOWED>showed|mucked) \[(?P<CARDS>.*)\].*" % player_re, re.MULTILINE)
|
||||||
|
|
||||||
|
@ -133,17 +135,29 @@ class PokerStars(HandHistoryConverter):
|
||||||
info = {}
|
info = {}
|
||||||
m = self.re_GameInfo.search(handText)
|
m = self.re_GameInfo.search(handText)
|
||||||
if not m:
|
if not m:
|
||||||
print "DEBUG: determineGameType(): did not match"
|
tmp = handText[0:100]
|
||||||
return None
|
log.error("determineGameType: Unable to recognise gametype from: '%s'" % tmp)
|
||||||
|
log.error("determineGameType: Raising FpdbParseError")
|
||||||
|
raise FpdbParseError
|
||||||
|
|
||||||
mg = m.groupdict()
|
mg = m.groupdict()
|
||||||
# translations from captured groups to fpdb info strings
|
# translations from captured groups to fpdb info strings
|
||||||
limits = { 'No Limit':'nl', 'Pot Limit':'pl', 'Limit':'fl' }
|
Lim_Blinds = { '0.04': ('0.01', '0.02'), '0.10': ('0.02', '0.05'), '0.20': ('0.05', '0.10'),
|
||||||
|
'0.50': ('0.10', '0.25'), '1.00': ('0.25', '0.50'), '2.00': ('0.50', '1.00'),
|
||||||
|
'2': ('0.50', '1.00'), '4': ('1.00', '2.00'), '6': ('1.00', '3.00'),
|
||||||
|
'4.00': ('1.00', '2.00'), '6.00': ('1.00', '3.00'), '10.00': ('2.00', '5.00'),
|
||||||
|
'20.00': ('5.00', '10.00'), '30.00': ('10.00', '15.00'), '60.00': ('15.00', '30.00'),
|
||||||
|
'100.00': ('25.00', '50.00'),'200.00': ('50.00', '100.00'),'400.00': ('100.00', '200.00'),
|
||||||
|
'1000.00': ('250.00', '500.00')}
|
||||||
|
|
||||||
|
|
||||||
|
limits = { 'No Limit':'nl', 'Pot Limit':'pl', 'Limit':'fl', 'LIMIT':'fl' }
|
||||||
games = { # base, category
|
games = { # base, category
|
||||||
"Hold'em" : ('hold','holdem'),
|
"Hold'em" : ('hold','holdem'),
|
||||||
'Omaha' : ('hold','omahahi'),
|
'Omaha' : ('hold','omahahi'),
|
||||||
'Omaha Hi/Lo' : ('hold','omahahilo'),
|
'Omaha Hi/Lo' : ('hold','omahahilo'),
|
||||||
'Razz' : ('stud','razz'),
|
'Razz' : ('stud','razz'),
|
||||||
|
'RAZZ' : ('stud','razz'),
|
||||||
'7 Card Stud' : ('stud','studhi'),
|
'7 Card Stud' : ('stud','studhi'),
|
||||||
'7 Card Stud Hi/Lo' : ('stud','studhilo'),
|
'7 Card Stud Hi/Lo' : ('stud','studhilo'),
|
||||||
'Badugi' : ('draw','badugi'),
|
'Badugi' : ('draw','badugi'),
|
||||||
|
@ -171,6 +185,15 @@ class PokerStars(HandHistoryConverter):
|
||||||
else:
|
else:
|
||||||
info['type'] = 'tour'
|
info['type'] = 'tour'
|
||||||
|
|
||||||
|
if info['limitType'] == 'fl' and info['bb'] is not None and info['type'] == 'ring' and info['base'] != 'stud':
|
||||||
|
try:
|
||||||
|
info['sb'] = Lim_Blinds[mg['BB']][0]
|
||||||
|
info['bb'] = Lim_Blinds[mg['BB']][1]
|
||||||
|
except KeyError:
|
||||||
|
log.error("determineGameType: Lim_Blinds has no lookup for '%s'" % mg['BB'])
|
||||||
|
log.error("determineGameType: Raising FpdbParseError")
|
||||||
|
raise FpdbParseError
|
||||||
|
|
||||||
# NB: SB, BB must be interpreted as blinds or bets depending on limit type.
|
# NB: SB, BB must be interpreted as blinds or bets depending on limit type.
|
||||||
return info
|
return info
|
||||||
|
|
||||||
|
@ -194,15 +217,21 @@ class PokerStars(HandHistoryConverter):
|
||||||
#2008/11/12 10:00:48 CET [2008/11/12 4:00:48 ET]
|
#2008/11/12 10:00:48 CET [2008/11/12 4:00:48 ET]
|
||||||
#2008/08/17 - 01:14:43 (ET)
|
#2008/08/17 - 01:14:43 (ET)
|
||||||
#2008/09/07 06:23:14 ET
|
#2008/09/07 06:23:14 ET
|
||||||
m2 = re.search("(?P<Y>[0-9]{4})\/(?P<M>[0-9]{2})\/(?P<D>[0-9]{2})[\- ]+(?P<H>[0-9]+):(?P<MIN>[0-9]+):(?P<S>[0-9]+)", info[key])
|
m1 = self.re_DateTime.finditer(info[key])
|
||||||
datetimestr = "%s/%s/%s %s:%s:%s" % (m2.group('Y'), m2.group('M'),m2.group('D'),m2.group('H'),m2.group('MIN'),m2.group('S'))
|
# m2 = re.search("(?P<Y>[0-9]{4})\/(?P<M>[0-9]{2})\/(?P<D>[0-9]{2})[\- ]+(?P<H>[0-9]+):(?P<MIN>[0-9]+):(?P<S>[0-9]+)", info[key])
|
||||||
|
for a in m1:
|
||||||
|
datetimestr = "%s/%s/%s %s:%s:%s" % (a.group('Y'), a.group('M'),a.group('D'),a.group('H'),a.group('MIN'),a.group('S'))
|
||||||
hand.starttime = datetime.datetime.strptime(datetimestr, "%Y/%m/%d %H:%M:%S")
|
hand.starttime = datetime.datetime.strptime(datetimestr, "%Y/%m/%d %H:%M:%S")
|
||||||
if key == 'HID':
|
if key == 'HID':
|
||||||
hand.handid = info[key]
|
hand.handid = info[key]
|
||||||
|
|
||||||
if key == 'TOURNO':
|
if key == 'TOURNO':
|
||||||
hand.tourNo = info[key]
|
hand.tourNo = info[key]
|
||||||
if key == 'BUYIN':
|
if key == 'BUYIN':
|
||||||
|
if info[key] == 'Freeroll':
|
||||||
|
hand.buyin = '$0+$0'
|
||||||
|
else:
|
||||||
|
#FIXME: The key looks like: '€0.82+€0.18 EUR'
|
||||||
|
# This should be parsed properly and used
|
||||||
hand.buyin = info[key]
|
hand.buyin = info[key]
|
||||||
if key == 'LEVEL':
|
if key == 'LEVEL':
|
||||||
hand.level = info[key]
|
hand.level = info[key]
|
||||||
|
@ -233,7 +262,6 @@ class PokerStars(HandHistoryConverter):
|
||||||
def readPlayerStacks(self, hand):
|
def readPlayerStacks(self, hand):
|
||||||
log.debug("readPlayerStacks")
|
log.debug("readPlayerStacks")
|
||||||
m = self.re_PlayerInfo.finditer(hand.handText)
|
m = self.re_PlayerInfo.finditer(hand.handText)
|
||||||
players = []
|
|
||||||
for a in m:
|
for a in m:
|
||||||
hand.addPlayer(int(a.group('SEAT')), a.group('PNAME'), a.group('CASH'))
|
hand.addPlayer(int(a.group('SEAT')), a.group('PNAME'), a.group('CASH'))
|
||||||
|
|
||||||
|
@ -280,11 +308,14 @@ class PokerStars(HandHistoryConverter):
|
||||||
hand.addBringIn(m.group('PNAME'), m.group('BRINGIN'))
|
hand.addBringIn(m.group('PNAME'), m.group('BRINGIN'))
|
||||||
|
|
||||||
def readBlinds(self, hand):
|
def readBlinds(self, hand):
|
||||||
try:
|
liveBlind = True
|
||||||
m = self.re_PostSB.search(hand.handText)
|
for a in self.re_PostSB.finditer(hand.handText):
|
||||||
hand.addBlind(m.group('PNAME'), 'small blind', m.group('SB'))
|
if liveBlind:
|
||||||
except: # no small blind
|
hand.addBlind(a.group('PNAME'), 'small blind', a.group('SB'))
|
||||||
hand.addBlind(None, None, None)
|
liveBlind = False
|
||||||
|
else:
|
||||||
|
# Post dead blinds as ante
|
||||||
|
hand.addBlind(a.group('PNAME'), 'secondsb', a.group('SB'))
|
||||||
for a in self.re_PostBB.finditer(hand.handText):
|
for a in self.re_PostBB.finditer(hand.handText):
|
||||||
hand.addBlind(a.group('PNAME'), 'big blind', a.group('BB'))
|
hand.addBlind(a.group('PNAME'), 'big blind', a.group('BB'))
|
||||||
for a in self.re_PostBoth.finditer(hand.handText):
|
for a in self.re_PostBoth.finditer(hand.handText):
|
||||||
|
@ -330,6 +361,7 @@ class PokerStars(HandHistoryConverter):
|
||||||
m = self.re_Action.finditer(hand.streets[street])
|
m = self.re_Action.finditer(hand.streets[street])
|
||||||
for action in m:
|
for action in m:
|
||||||
acts = action.groupdict()
|
acts = action.groupdict()
|
||||||
|
#print "DEBUG: acts: %s" %acts
|
||||||
if action.group('ATYPE') == ' raises':
|
if action.group('ATYPE') == ' raises':
|
||||||
hand.addRaiseBy( street, action.group('PNAME'), action.group('BET') )
|
hand.addRaiseBy( street, action.group('PNAME'), action.group('BET') )
|
||||||
elif action.group('ATYPE') == ' calls':
|
elif action.group('ATYPE') == ' calls':
|
||||||
|
|
191
pyfpdb/SQL.py
191
pyfpdb/SQL.py
|
@ -58,6 +58,27 @@ class Sql:
|
||||||
self.query['drop_table'] = """DROP TABLE IF EXISTS """
|
self.query['drop_table'] = """DROP TABLE IF EXISTS """
|
||||||
|
|
||||||
|
|
||||||
|
##################################################################
|
||||||
|
# Set transaction isolation level
|
||||||
|
##################################################################
|
||||||
|
|
||||||
|
if db_server == 'mysql' or db_server == 'postgresql':
|
||||||
|
self.query['set tx level'] = """SET SESSION TRANSACTION
|
||||||
|
ISOLATION LEVEL READ COMMITTED"""
|
||||||
|
elif db_server == 'sqlite':
|
||||||
|
self.query['set tx level'] = """ """
|
||||||
|
|
||||||
|
|
||||||
|
################################
|
||||||
|
# Select basic info
|
||||||
|
################################
|
||||||
|
|
||||||
|
self.query['getSiteId'] = """SELECT id from Sites where name = %s"""
|
||||||
|
|
||||||
|
self.query['getGames'] = """SELECT DISTINCT category from Gametypes"""
|
||||||
|
|
||||||
|
self.query['getLimits'] = """SELECT DISTINCT bigBlind from Gametypes ORDER by bigBlind DESC"""
|
||||||
|
|
||||||
################################
|
################################
|
||||||
# Create Settings
|
# Create Settings
|
||||||
################################
|
################################
|
||||||
|
@ -214,6 +235,7 @@ class Sql:
|
||||||
id BIGINT UNSIGNED AUTO_INCREMENT NOT NULL, PRIMARY KEY (id),
|
id BIGINT UNSIGNED AUTO_INCREMENT NOT NULL, PRIMARY KEY (id),
|
||||||
tableName VARCHAR(22) NOT NULL,
|
tableName VARCHAR(22) NOT NULL,
|
||||||
siteHandNo BIGINT NOT NULL,
|
siteHandNo BIGINT NOT NULL,
|
||||||
|
tourneyId INT UNSIGNED NOT NULL,
|
||||||
gametypeId SMALLINT UNSIGNED NOT NULL, FOREIGN KEY (gametypeId) REFERENCES Gametypes(id),
|
gametypeId SMALLINT UNSIGNED NOT NULL, FOREIGN KEY (gametypeId) REFERENCES Gametypes(id),
|
||||||
handStart DATETIME NOT NULL,
|
handStart DATETIME NOT NULL,
|
||||||
importTime DATETIME NOT NULL,
|
importTime DATETIME NOT NULL,
|
||||||
|
@ -249,6 +271,7 @@ class Sql:
|
||||||
id BIGSERIAL, PRIMARY KEY (id),
|
id BIGSERIAL, PRIMARY KEY (id),
|
||||||
tableName VARCHAR(22) NOT NULL,
|
tableName VARCHAR(22) NOT NULL,
|
||||||
siteHandNo BIGINT NOT NULL,
|
siteHandNo BIGINT NOT NULL,
|
||||||
|
tourneyId INT NOT NULL,
|
||||||
gametypeId INT NOT NULL, FOREIGN KEY (gametypeId) REFERENCES Gametypes(id),
|
gametypeId INT NOT NULL, FOREIGN KEY (gametypeId) REFERENCES Gametypes(id),
|
||||||
handStart timestamp without time zone NOT NULL,
|
handStart timestamp without time zone NOT NULL,
|
||||||
importTime timestamp without time zone NOT NULL,
|
importTime timestamp without time zone NOT NULL,
|
||||||
|
@ -283,6 +306,7 @@ class Sql:
|
||||||
id INTEGER PRIMARY KEY,
|
id INTEGER PRIMARY KEY,
|
||||||
tableName TEXT(22) NOT NULL,
|
tableName TEXT(22) NOT NULL,
|
||||||
siteHandNo INT NOT NULL,
|
siteHandNo INT NOT NULL,
|
||||||
|
tourneyId INT NOT NULL,
|
||||||
gametypeId INT NOT NULL,
|
gametypeId INT NOT NULL,
|
||||||
handStart REAL NOT NULL,
|
handStart REAL NOT NULL,
|
||||||
importTime REAL NOT NULL,
|
importTime REAL NOT NULL,
|
||||||
|
@ -1322,6 +1346,7 @@ class Sql:
|
||||||
|
|
||||||
# same as above except stats are aggregated for all blind/limit levels
|
# same as above except stats are aggregated for all blind/limit levels
|
||||||
self.query['get_stats_from_hand_aggregated'] = """
|
self.query['get_stats_from_hand_aggregated'] = """
|
||||||
|
/* explain query plan */
|
||||||
SELECT hc.playerId AS player_id,
|
SELECT hc.playerId AS player_id,
|
||||||
max(case when hc.gametypeId = h.gametypeId
|
max(case when hc.gametypeId = h.gametypeId
|
||||||
then hp.seatNo
|
then hp.seatNo
|
||||||
|
@ -1824,6 +1849,15 @@ class Sql:
|
||||||
self.query['getLimits2'] = """SELECT DISTINCT type, limitType, bigBlind
|
self.query['getLimits2'] = """SELECT DISTINCT type, limitType, bigBlind
|
||||||
from Gametypes
|
from Gametypes
|
||||||
ORDER by type, limitType DESC, bigBlind DESC"""
|
ORDER by type, limitType DESC, bigBlind DESC"""
|
||||||
|
self.query['getLimits3'] = """select DISTINCT type
|
||||||
|
, limitType
|
||||||
|
, case type
|
||||||
|
when 'ring' then bigBlind
|
||||||
|
else buyin
|
||||||
|
end as bb_or_buyin
|
||||||
|
from Gametypes gt
|
||||||
|
cross join TourneyTypes tt
|
||||||
|
order by type, limitType DESC, bb_or_buyin DESC"""
|
||||||
|
|
||||||
if db_server == 'mysql':
|
if db_server == 'mysql':
|
||||||
self.query['playerDetailedStats'] = """
|
self.query['playerDetailedStats'] = """
|
||||||
|
@ -1881,6 +1915,8 @@ class Sql:
|
||||||
inner join Sites s on (s.Id = gt.siteId)
|
inner join Sites s on (s.Id = gt.siteId)
|
||||||
inner join Players p on (p.Id = hp.playerId)
|
inner join Players p on (p.Id = hp.playerId)
|
||||||
where hp.playerId in <player_test>
|
where hp.playerId in <player_test>
|
||||||
|
<game_test>
|
||||||
|
<site_test>
|
||||||
/*and hp.tourneysPlayersId IS NULL*/
|
/*and hp.tourneysPlayersId IS NULL*/
|
||||||
and h.seats <seats_test>
|
and h.seats <seats_test>
|
||||||
<flagtest>
|
<flagtest>
|
||||||
|
@ -1964,6 +2000,8 @@ class Sql:
|
||||||
inner join Sites s on (s.Id = gt.siteId)
|
inner join Sites s on (s.Id = gt.siteId)
|
||||||
inner join Players p on (p.Id = hp.playerId)
|
inner join Players p on (p.Id = hp.playerId)
|
||||||
where hp.playerId in <player_test>
|
where hp.playerId in <player_test>
|
||||||
|
<game_test>
|
||||||
|
<site_test>
|
||||||
/*and hp.tourneysPlayersId IS NULL*/
|
/*and hp.tourneysPlayersId IS NULL*/
|
||||||
and h.seats <seats_test>
|
and h.seats <seats_test>
|
||||||
<flagtest>
|
<flagtest>
|
||||||
|
@ -1995,6 +2033,7 @@ class Sql:
|
||||||
elif db_server == 'sqlite':
|
elif db_server == 'sqlite':
|
||||||
self.query['playerDetailedStats'] = """
|
self.query['playerDetailedStats'] = """
|
||||||
select <hgameTypeId> AS hgametypeid
|
select <hgameTypeId> AS hgametypeid
|
||||||
|
,<playerName> AS pname
|
||||||
,gt.base
|
,gt.base
|
||||||
,gt.category AS category
|
,gt.category AS category
|
||||||
,upper(gt.limitType) AS limittype
|
,upper(gt.limitType) AS limittype
|
||||||
|
@ -2040,13 +2079,15 @@ class Sql:
|
||||||
,100.0*avg((hp.totalProfit+hp.rake)/(gt.bigBlind+0.0)) AS bb100xr
|
,100.0*avg((hp.totalProfit+hp.rake)/(gt.bigBlind+0.0)) AS bb100xr
|
||||||
,avg((hp.totalProfit+hp.rake)/100.0) AS profhndxr
|
,avg((hp.totalProfit+hp.rake)/100.0) AS profhndxr
|
||||||
,avg(h.seats+0.0) AS avgseats
|
,avg(h.seats+0.0) AS avgseats
|
||||||
/*,variance(hp.totalProfit/100.0) AS variance*/
|
,variance(hp.totalProfit/100.0) AS variance
|
||||||
,0.0 AS variance
|
|
||||||
from HandsPlayers hp
|
from HandsPlayers hp
|
||||||
inner join Hands h on (h.id = hp.handId)
|
inner join Hands h on (h.id = hp.handId)
|
||||||
inner join Gametypes gt on (gt.Id = h.gameTypeId)
|
inner join Gametypes gt on (gt.Id = h.gameTypeId)
|
||||||
inner join Sites s on (s.Id = gt.siteId)
|
inner join Sites s on (s.Id = gt.siteId)
|
||||||
|
inner join Players p on (p.Id = hp.playerId)
|
||||||
where hp.playerId in <player_test>
|
where hp.playerId in <player_test>
|
||||||
|
<game_test>
|
||||||
|
<site_test>
|
||||||
/*and hp.tourneysPlayersId IS NULL*/
|
/*and hp.tourneysPlayersId IS NULL*/
|
||||||
and h.seats <seats_test>
|
and h.seats <seats_test>
|
||||||
<flagtest>
|
<flagtest>
|
||||||
|
@ -2060,6 +2101,7 @@ class Sql:
|
||||||
,plposition
|
,plposition
|
||||||
,upper(gt.limitType)
|
,upper(gt.limitType)
|
||||||
,s.name
|
,s.name
|
||||||
|
having 1 = 1 <havingclause>
|
||||||
order by hp.playerId
|
order by hp.playerId
|
||||||
,gt.base
|
,gt.base
|
||||||
,gt.category
|
,gt.category
|
||||||
|
@ -2573,6 +2615,7 @@ class Sql:
|
||||||
AND h.handStart > '<startdate_test>'
|
AND h.handStart > '<startdate_test>'
|
||||||
AND h.handStart < '<enddate_test>'
|
AND h.handStart < '<enddate_test>'
|
||||||
<limit_test>
|
<limit_test>
|
||||||
|
<game_test>
|
||||||
AND hp.tourneysPlayersId IS NULL
|
AND hp.tourneysPlayersId IS NULL
|
||||||
GROUP BY h.handStart, hp.handId, hp.sawShowdown, hp.totalProfit
|
GROUP BY h.handStart, hp.handId, hp.sawShowdown, hp.totalProfit
|
||||||
ORDER BY h.handStart"""
|
ORDER BY h.handStart"""
|
||||||
|
@ -3088,6 +3131,147 @@ class Sql:
|
||||||
,'d' || substr(strftime('%Y%m%d', h.handStart),3,7)
|
,'d' || substr(strftime('%Y%m%d', h.handStart),3,7)
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
self.query['insert_hudcache'] = """
|
||||||
|
INSERT INTO HudCache (
|
||||||
|
gametypeId,
|
||||||
|
playerId,
|
||||||
|
activeSeats,
|
||||||
|
position,
|
||||||
|
tourneyTypeId,
|
||||||
|
styleKey,
|
||||||
|
HDs,
|
||||||
|
street0VPI,
|
||||||
|
street0Aggr,
|
||||||
|
street0_3BChance,
|
||||||
|
street0_3BDone,
|
||||||
|
street1Seen,
|
||||||
|
street2Seen,
|
||||||
|
street3Seen,
|
||||||
|
street4Seen,
|
||||||
|
sawShowdown,
|
||||||
|
street1Aggr,
|
||||||
|
street2Aggr,
|
||||||
|
street3Aggr,
|
||||||
|
street4Aggr,
|
||||||
|
otherRaisedStreet1,
|
||||||
|
otherRaisedStreet2,
|
||||||
|
otherRaisedStreet3,
|
||||||
|
otherRaisedStreet4,
|
||||||
|
foldToOtherRaisedStreet1,
|
||||||
|
foldToOtherRaisedStreet2,
|
||||||
|
foldToOtherRaisedStreet3,
|
||||||
|
foldToOtherRaisedStreet4,
|
||||||
|
wonWhenSeenStreet1,
|
||||||
|
wonAtSD,
|
||||||
|
stealAttemptChance,
|
||||||
|
stealAttempted,
|
||||||
|
foldBbToStealChance,
|
||||||
|
foldedBbToSteal,
|
||||||
|
foldSbToStealChance,
|
||||||
|
foldedSbToSteal,
|
||||||
|
street1CBChance,
|
||||||
|
street1CBDone,
|
||||||
|
street2CBChance,
|
||||||
|
street2CBDone,
|
||||||
|
street3CBChance,
|
||||||
|
street3CBDone,
|
||||||
|
street4CBChance,
|
||||||
|
street4CBDone,
|
||||||
|
foldToStreet1CBChance,
|
||||||
|
foldToStreet1CBDone,
|
||||||
|
foldToStreet2CBChance,
|
||||||
|
foldToStreet2CBDone,
|
||||||
|
foldToStreet3CBChance,
|
||||||
|
foldToStreet3CBDone,
|
||||||
|
foldToStreet4CBChance,
|
||||||
|
foldToStreet4CBDone,
|
||||||
|
totalProfit,
|
||||||
|
street1CheckCallRaiseChance,
|
||||||
|
street1CheckCallRaiseDone,
|
||||||
|
street2CheckCallRaiseChance,
|
||||||
|
street2CheckCallRaiseDone,
|
||||||
|
street3CheckCallRaiseChance,
|
||||||
|
street3CheckCallRaiseDone,
|
||||||
|
street4CheckCallRaiseChance,
|
||||||
|
street4CheckCallRaiseDone)
|
||||||
|
VALUES (%s, %s, %s, %s, %s,
|
||||||
|
%s, %s, %s, %s, %s,
|
||||||
|
%s, %s, %s, %s, %s,
|
||||||
|
%s, %s, %s, %s, %s,
|
||||||
|
%s, %s, %s, %s, %s,
|
||||||
|
%s, %s, %s, %s, %s,
|
||||||
|
%s, %s, %s, %s, %s,
|
||||||
|
%s, %s, %s, %s, %s,
|
||||||
|
%s, %s, %s, %s, %s,
|
||||||
|
%s, %s, %s, %s, %s,
|
||||||
|
%s, %s, %s, %s, %s,
|
||||||
|
%s, %s, %s, %s, %s,
|
||||||
|
%s)"""
|
||||||
|
|
||||||
|
self.query['update_hudcache'] = """
|
||||||
|
UPDATE HudCache SET
|
||||||
|
HDs=HDs+%s,
|
||||||
|
street0VPI=street0VPI+%s,
|
||||||
|
street0Aggr=street0Aggr+%s,
|
||||||
|
street0_3BChance=street0_3BChance+%s,
|
||||||
|
street0_3BDone=street0_3BDone+%s,
|
||||||
|
street1Seen=street1Seen+%s,
|
||||||
|
street2Seen=street2Seen+%s,
|
||||||
|
street3Seen=street3Seen+%s,
|
||||||
|
street4Seen=street4Seen+%s,
|
||||||
|
sawShowdown=sawShowdown+%s,
|
||||||
|
street1Aggr=street1Aggr+%s,
|
||||||
|
street2Aggr=street2Aggr+%s,
|
||||||
|
street3Aggr=street3Aggr+%s,
|
||||||
|
street4Aggr=street4Aggr+%s,
|
||||||
|
otherRaisedStreet1=otherRaisedStreet1+%s,
|
||||||
|
otherRaisedStreet2=otherRaisedStreet2+%s,
|
||||||
|
otherRaisedStreet3=otherRaisedStreet3+%s,
|
||||||
|
otherRaisedStreet4=otherRaisedStreet4+%s,
|
||||||
|
foldToOtherRaisedStreet1=foldToOtherRaisedStreet1+%s,
|
||||||
|
foldToOtherRaisedStreet2=foldToOtherRaisedStreet2+%s,
|
||||||
|
foldToOtherRaisedStreet3=foldToOtherRaisedStreet3+%s,
|
||||||
|
foldToOtherRaisedStreet4=foldToOtherRaisedStreet4+%s,
|
||||||
|
wonWhenSeenStreet1=wonWhenSeenStreet1+%s,
|
||||||
|
wonAtSD=wonAtSD+%s,
|
||||||
|
stealAttemptChance=stealAttemptChance+%s,
|
||||||
|
stealAttempted=stealAttempted+%s,
|
||||||
|
foldBbToStealChance=foldBbToStealChance+%s,
|
||||||
|
foldedBbToSteal=foldedBbToSteal+%s,
|
||||||
|
foldSbToStealChance=foldSbToStealChance+%s,
|
||||||
|
foldedSbToSteal=foldedSbToSteal+%s,
|
||||||
|
street1CBChance=street1CBChance+%s,
|
||||||
|
street1CBDone=street1CBDone+%s,
|
||||||
|
street2CBChance=street2CBChance+%s,
|
||||||
|
street2CBDone=street2CBDone+%s,
|
||||||
|
street3CBChance=street3CBChance+%s,
|
||||||
|
street3CBDone=street3CBDone+%s,
|
||||||
|
street4CBChance=street4CBChance+%s,
|
||||||
|
street4CBDone=street4CBDone+%s,
|
||||||
|
foldToStreet1CBChance=foldToStreet1CBChance+%s,
|
||||||
|
foldToStreet1CBDone=foldToStreet1CBDone+%s,
|
||||||
|
foldToStreet2CBChance=foldToStreet2CBChance+%s,
|
||||||
|
foldToStreet2CBDone=foldToStreet2CBDone+%s,
|
||||||
|
foldToStreet3CBChance=foldToStreet3CBChance+%s,
|
||||||
|
foldToStreet3CBDone=foldToStreet3CBDone+%s,
|
||||||
|
foldToStreet4CBChance=foldToStreet4CBChance+%s,
|
||||||
|
foldToStreet4CBDone=foldToStreet4CBDone+%s,
|
||||||
|
totalProfit=totalProfit+%s,
|
||||||
|
street1CheckCallRaiseChance=street1CheckCallRaiseChance+%s,
|
||||||
|
street1CheckCallRaiseDone=street1CheckCallRaiseDone+%s,
|
||||||
|
street2CheckCallRaiseChance=street2CheckCallRaiseChance+%s,
|
||||||
|
street2CheckCallRaiseDone=street2CheckCallRaiseDone+%s,
|
||||||
|
street3CheckCallRaiseChance=street3CheckCallRaiseChance+%s,
|
||||||
|
street3CheckCallRaiseDone=street3CheckCallRaiseDone+%s,
|
||||||
|
street4CheckCallRaiseChance=street4CheckCallRaiseChance+%s,
|
||||||
|
street4CheckCallRaiseDone=street4CheckCallRaiseDone+%s
|
||||||
|
WHERE gametypeId+0=%s
|
||||||
|
AND playerId=%s
|
||||||
|
AND activeSeats=%s
|
||||||
|
AND position=%s
|
||||||
|
AND tourneyTypeId+0=%s
|
||||||
|
AND styleKey=%s"""
|
||||||
|
|
||||||
self.query['get_hero_hudcache_start'] = """select min(hc.styleKey)
|
self.query['get_hero_hudcache_start'] = """select min(hc.styleKey)
|
||||||
from HudCache hc
|
from HudCache hc
|
||||||
where hc.playerId in <playerid_list>
|
where hc.playerId in <playerid_list>
|
||||||
|
@ -3292,6 +3476,7 @@ class Sql:
|
||||||
tablename,
|
tablename,
|
||||||
gametypeid,
|
gametypeid,
|
||||||
sitehandno,
|
sitehandno,
|
||||||
|
tourneyId,
|
||||||
handstart,
|
handstart,
|
||||||
importtime,
|
importtime,
|
||||||
seats,
|
seats,
|
||||||
|
@ -3322,7 +3507,7 @@ class Sql:
|
||||||
VALUES
|
VALUES
|
||||||
(%s, %s, %s, %s, %s, %s, %s, %s, %s, %s,
|
(%s, %s, %s, %s, %s, %s, %s, %s, %s, %s,
|
||||||
%s, %s, %s, %s, %s, %s, %s, %s, %s, %s,
|
%s, %s, %s, %s, %s, %s, %s, %s, %s, %s,
|
||||||
%s, %s, %s, %s, %s, %s, %s, %s, %s)"""
|
%s, %s, %s, %s, %s, %s, %s, %s, %s, %s)"""
|
||||||
|
|
||||||
|
|
||||||
self.query['store_hands_players'] = """INSERT INTO HandsPlayers (
|
self.query['store_hands_players'] = """INSERT INTO HandsPlayers (
|
||||||
|
|
|
@ -62,9 +62,13 @@ import Database
|
||||||
re_Places = re.compile("_[0-9]$")
|
re_Places = re.compile("_[0-9]$")
|
||||||
re_Percent = re.compile("%$")
|
re_Percent = re.compile("%$")
|
||||||
|
|
||||||
|
# String manipulation
|
||||||
|
import codecs
|
||||||
|
encoder = codecs.lookup(Configuration.LOCALE_ENCODING)
|
||||||
|
|
||||||
def do_tip(widget, tip):
|
def do_tip(widget, tip):
|
||||||
widget.set_tooltip_text(tip)
|
(_tip, _len) = encoder.encode(tip)
|
||||||
|
widget.set_tooltip_text(_tip)
|
||||||
|
|
||||||
def do_stat(stat_dict, player = 24, stat = 'vpip'):
|
def do_stat(stat_dict, player = 24, stat = 'vpip'):
|
||||||
match = re_Places.search(stat)
|
match = re_Places.search(stat)
|
||||||
|
@ -241,8 +245,20 @@ def saw_f(stat_dict, player):
|
||||||
def n(stat_dict, player):
|
def n(stat_dict, player):
|
||||||
""" Number of hands played."""
|
""" Number of hands played."""
|
||||||
try:
|
try:
|
||||||
|
# If sample is large enough, use X.Yk notation instead
|
||||||
|
_n = stat_dict[player]['n']
|
||||||
|
fmt = '%d' % _n
|
||||||
|
if _n >= 10000:
|
||||||
|
k = _n / 1000
|
||||||
|
c = _n % 1000
|
||||||
|
_c = float(c) / 100.0
|
||||||
|
d = int(round(_c))
|
||||||
|
if d == 10:
|
||||||
|
k += 1
|
||||||
|
d = 0
|
||||||
|
fmt = '%d.%dk' % (k, d)
|
||||||
return (stat_dict[player]['n'],
|
return (stat_dict[player]['n'],
|
||||||
'%d' % (stat_dict[player]['n']),
|
'%s' % fmt,
|
||||||
'n=%d' % (stat_dict[player]['n']),
|
'n=%d' % (stat_dict[player]['n']),
|
||||||
'n=%d' % (stat_dict[player]['n']),
|
'n=%d' % (stat_dict[player]['n']),
|
||||||
'(%d)' % (stat_dict[player]['n']),
|
'(%d)' % (stat_dict[player]['n']),
|
||||||
|
|
|
@ -101,7 +101,7 @@ class Table_Window(object):
|
||||||
self.table = int(table_number)
|
self.table = int(table_number)
|
||||||
self.name = "%s - %s" % (self.tournament, self.table)
|
self.name = "%s - %s" % (self.tournament, self.table)
|
||||||
elif table_name is not None:
|
elif table_name is not None:
|
||||||
search_string = table_name
|
# search_string = table_name
|
||||||
self.name = table_name
|
self.name = table_name
|
||||||
self.tournament = None
|
self.tournament = None
|
||||||
else:
|
else:
|
||||||
|
|
|
@ -39,7 +39,6 @@ if os.name == 'nt':
|
||||||
|
|
||||||
# FreePokerTools modules
|
# FreePokerTools modules
|
||||||
import Configuration
|
import Configuration
|
||||||
from fpdb_simple import LOCALE_ENCODING
|
|
||||||
|
|
||||||
# Each TableWindow object must have the following attributes correctly populated:
|
# Each TableWindow object must have the following attributes correctly populated:
|
||||||
# tw.name = the table name from the title bar, which must to match the table name
|
# tw.name = the table name from the title bar, which must to match the table name
|
||||||
|
@ -161,7 +160,7 @@ def discover_posix_by_name(c, tablename):
|
||||||
|
|
||||||
def discover_posix_tournament(c, t_number, s_number):
|
def discover_posix_tournament(c, t_number, s_number):
|
||||||
"""Finds the X window for a client, given tournament and table nos."""
|
"""Finds the X window for a client, given tournament and table nos."""
|
||||||
search_string = "%s.+Table\s%s" % (t_number, s_number)
|
search_string = "%s.+Table.+%s" % (t_number, s_number)
|
||||||
for listing in os.popen('xwininfo -root -tree').readlines():
|
for listing in os.popen('xwininfo -root -tree').readlines():
|
||||||
if re.search(search_string, listing):
|
if re.search(search_string, listing):
|
||||||
return decode_xwininfo(c, listing)
|
return decode_xwininfo(c, listing)
|
||||||
|
@ -238,7 +237,7 @@ def discover_nt_by_name(c, tablename):
|
||||||
try:
|
try:
|
||||||
# maybe it's better to make global titles[hwnd] decoding?
|
# maybe it's better to make global titles[hwnd] decoding?
|
||||||
# this can blow up in XP on some windows, eg firefox displaying http://docs.python.org/tutorial/classes.html
|
# this can blow up in XP on some windows, eg firefox displaying http://docs.python.org/tutorial/classes.html
|
||||||
if not tablename.lower() in titles[hwnd].decode(LOCALE_ENCODING).lower():
|
if not tablename.lower() in titles[hwnd].decode(Configuration.LOCALE_ENCODING).lower():
|
||||||
continue
|
continue
|
||||||
except:
|
except:
|
||||||
continue
|
continue
|
||||||
|
|
|
@ -32,12 +32,16 @@ import gtk
|
||||||
import gobject
|
import gobject
|
||||||
|
|
||||||
# fpdb/free poker tools modules
|
# fpdb/free poker tools modules
|
||||||
|
import Configuration
|
||||||
|
from HandHistoryConverter import getTableTitleRe
|
||||||
|
|
||||||
# get the correct module for the current os
|
# get the correct module for the current os
|
||||||
if os.name == 'posix':
|
if os.name == 'posix':
|
||||||
import XTables as Tables
|
import XTables as Tables
|
||||||
elif os.name == 'nt':
|
elif os.name == 'nt':
|
||||||
import WinTables as Tables
|
import WinTables as Tables
|
||||||
|
|
||||||
|
config = Configuration.Config()
|
||||||
# Main function used for testing
|
# Main function used for testing
|
||||||
if __name__=="__main__":
|
if __name__=="__main__":
|
||||||
# c = Configuration.Config()
|
# c = Configuration.Config()
|
||||||
|
@ -82,11 +86,16 @@ if __name__=="__main__":
|
||||||
(tour_no, tab_no) = table_name.split(",", 1)
|
(tour_no, tab_no) = table_name.split(",", 1)
|
||||||
tour_no = tour_no.rstrip()
|
tour_no = tour_no.rstrip()
|
||||||
tab_no = tab_no.rstrip()
|
tab_no = tab_no.rstrip()
|
||||||
table = Tables.Table(None, tournament = tour_no, table_number = tab_no)
|
type = "tour"
|
||||||
|
table_kwargs = dict(tournament = tour_no, table_number = tab_no)
|
||||||
else: # not a tournament
|
else: # not a tournament
|
||||||
print "cash game"
|
print "cash game"
|
||||||
table_name = table_name.rstrip()
|
table_name = table_name.rstrip()
|
||||||
table = Tables.Table(None, table_name = table_name)
|
type = "cash"
|
||||||
|
table_kwargs = dict(table_name = table_name)
|
||||||
|
|
||||||
|
search_string = getTableTitleRe(config, "Full Tilt Poker", type, **table_kwargs)
|
||||||
|
table = Tables.Table(search_string, **table_kwargs)
|
||||||
table.gdk_handle = gtk.gdk.window_foreign_new(table.number)
|
table.gdk_handle = gtk.gdk.window_foreign_new(table.number)
|
||||||
|
|
||||||
print "table =", table
|
print "table =", table
|
||||||
|
|
|
@ -185,7 +185,7 @@ class Tourney(object):
|
||||||
def old_insert_from_Hand(self, db):
|
def old_insert_from_Hand(self, db):
|
||||||
""" Function to insert Hand into database
|
""" Function to insert Hand into database
|
||||||
Should not commit, and do minimal selects. Callers may want to cache commits
|
Should not commit, and do minimal selects. Callers may want to cache commits
|
||||||
db: a connected fpdb_db object"""
|
db: a connected Database object"""
|
||||||
# TODO:
|
# TODO:
|
||||||
# Players - base playerid and siteid tuple
|
# Players - base playerid and siteid tuple
|
||||||
sqlids = db.getSqlPlayerIDs([p[1] for p in self.players], self.siteId)
|
sqlids = db.getSqlPlayerIDs([p[1] for p in self.players], self.siteId)
|
||||||
|
|
|
@ -24,6 +24,10 @@ Routines for detecting and handling poker client windows for MS Windows.
|
||||||
# Standard Library modules
|
# Standard Library modules
|
||||||
import re
|
import re
|
||||||
|
|
||||||
|
import logging
|
||||||
|
# logging has been set up in fpdb.py or HUD_main.py, use their settings:
|
||||||
|
log = logging.getLogger("hud")
|
||||||
|
|
||||||
# pyGTK modules
|
# pyGTK modules
|
||||||
import pygtk
|
import pygtk
|
||||||
import gtk
|
import gtk
|
||||||
|
@ -50,28 +54,31 @@ class Table(Table_Window):
|
||||||
titles = {}
|
titles = {}
|
||||||
win32gui.EnumWindows(win_enum_handler, titles)
|
win32gui.EnumWindows(win_enum_handler, titles)
|
||||||
for hwnd in titles:
|
for hwnd in titles:
|
||||||
|
if titles[hwnd] == "": continue
|
||||||
|
# print "searching ", search_string, " in ", titles[hwnd]
|
||||||
if re.search(search_string, titles[hwnd]):
|
if re.search(search_string, titles[hwnd]):
|
||||||
if 'History for table:' in titles[hwnd]: continue # Everleaf Network HH viewer window
|
if 'History for table:' in titles[hwnd]: continue # Everleaf Network HH viewer window
|
||||||
if 'HUD:' in titles[hwnd]: continue # FPDB HUD window
|
if 'HUD:' in titles[hwnd]: continue # FPDB HUD window
|
||||||
if 'Chat:' in titles[hwnd]: continue # Some sites (FTP? PS? Others?) have seperable or seperately constructed chat windows
|
if 'Chat:' in titles[hwnd]: continue # Some sites (FTP? PS? Others?) have seperable or seperately constructed chat windows
|
||||||
|
if 'FPDBHUD' in titles[hwnd]: continue # can't attach to ourselves!
|
||||||
self.window = hwnd
|
self.window = hwnd
|
||||||
break
|
break
|
||||||
|
|
||||||
try:
|
try:
|
||||||
if self.window == None:
|
if self.window == None:
|
||||||
print "Window %s not found. Skipping." % search_string
|
log.error( "Window %s not found. Skipping." % search_string )
|
||||||
return None
|
return None
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
print "self.window doesn't exist? why?"
|
log.error( "self.window doesn't exist? why?" )
|
||||||
return None
|
return None
|
||||||
|
|
||||||
(x, y, width, height) = win32gui.GetWindowRect(hwnd)
|
(x, y, width, height) = win32gui.GetWindowRect(hwnd)
|
||||||
print "x = %s y = %s width = %s height = %s" % (x, y, width, height)
|
log.debug("x = %s y = %s width = %s height = %s" % (x, y, width, height))
|
||||||
self.x = int(x) + b_width
|
self.x = int(x) + b_width
|
||||||
self.y = int(y) + tb_height
|
self.y = int(y) + tb_height
|
||||||
self.width = width - x
|
self.width = width - x
|
||||||
self.height = height - y
|
self.height = height - y
|
||||||
print "x = %s y = %s width = %s height = %s" % (self.x, self.y, self.width, self.height)
|
log.debug("x = %s y = %s width = %s height = %s" % (self.x, self.y, self.width, self.height))
|
||||||
#self.height = int(height) - b_width - tb_height
|
#self.height = int(height) - b_width - tb_height
|
||||||
#self.width = int(width) - 2*b_width
|
#self.width = int(width) - 2*b_width
|
||||||
|
|
||||||
|
|
|
@ -68,7 +68,7 @@ class Table(Table_Window):
|
||||||
window_number = None
|
window_number = None
|
||||||
for listing in os.popen('xwininfo -root -tree').readlines():
|
for listing in os.popen('xwininfo -root -tree').readlines():
|
||||||
if re.search(search_string, listing):
|
if re.search(search_string, listing):
|
||||||
print listing
|
# print listing
|
||||||
mo = re.match('\s+([\dxabcdef]+) (.+):\s\(\"([a-zA-Z.]+)\".+ (\d+)x(\d+)\+\d+\+\d+ \+(\d+)\+(\d+)', listing)
|
mo = re.match('\s+([\dxabcdef]+) (.+):\s\(\"([a-zA-Z.]+)\".+ (\d+)x(\d+)\+\d+\+\d+ \+(\d+)\+(\d+)', listing)
|
||||||
self.number = int( mo.group(1), 0)
|
self.number = int( mo.group(1), 0)
|
||||||
self.width = int( mo.group(4) )
|
self.width = int( mo.group(4) )
|
||||||
|
@ -89,7 +89,6 @@ class Table(Table_Window):
|
||||||
# break
|
# break
|
||||||
|
|
||||||
if window_number is None:
|
if window_number is None:
|
||||||
print "Window %s not found. Skipping." % search_string
|
|
||||||
return None
|
return None
|
||||||
|
|
||||||
# my_geo = self.window.get_geometry()
|
# my_geo = self.window.get_geometry()
|
||||||
|
|
|
@ -53,7 +53,7 @@ if os.name == 'nt':
|
||||||
raw_input("Press ENTER to continue.")
|
raw_input("Press ENTER to continue.")
|
||||||
exit()
|
exit()
|
||||||
|
|
||||||
print "Python " + sys.version[0:3] + '...\n'
|
print "Python " + sys.version[0:3] + '...'
|
||||||
|
|
||||||
import traceback
|
import traceback
|
||||||
import threading
|
import threading
|
||||||
|
@ -62,12 +62,6 @@ import string
|
||||||
cl_options = string.join(sys.argv[1:])
|
cl_options = string.join(sys.argv[1:])
|
||||||
(options, argv) = Options.fpdb_options()
|
(options, argv) = Options.fpdb_options()
|
||||||
|
|
||||||
if not options.errorsToConsole:
|
|
||||||
print "Note: error output is being diverted to fpdb-error-log.txt and HUD-error.txt. Any major error will be reported there _only_."
|
|
||||||
errorFile = open('fpdb-error-log.txt', 'w', 0)
|
|
||||||
sys.stderr = errorFile
|
|
||||||
|
|
||||||
#import logging
|
|
||||||
import logging, logging.config
|
import logging, logging.config
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
@ -103,6 +97,7 @@ except:
|
||||||
|
|
||||||
import GuiPrefs
|
import GuiPrefs
|
||||||
import GuiLogView
|
import GuiLogView
|
||||||
|
import GuiDatabase
|
||||||
import GuiBulkImport
|
import GuiBulkImport
|
||||||
import GuiPlayerStats
|
import GuiPlayerStats
|
||||||
import GuiPositionalStats
|
import GuiPositionalStats
|
||||||
|
@ -112,13 +107,11 @@ import GuiGraphViewer
|
||||||
import GuiSessionViewer
|
import GuiSessionViewer
|
||||||
import SQL
|
import SQL
|
||||||
import Database
|
import Database
|
||||||
import FpdbSQLQueries
|
|
||||||
import Configuration
|
import Configuration
|
||||||
import Exceptions
|
import Exceptions
|
||||||
|
|
||||||
VERSION = "0.12"
|
VERSION = "0.12"
|
||||||
|
|
||||||
log = Configuration.get_logger("logging.conf", "fpdb")
|
|
||||||
|
|
||||||
class fpdb:
|
class fpdb:
|
||||||
def tab_clicked(self, widget, tab_name):
|
def tab_clicked(self, widget, tab_name):
|
||||||
|
@ -296,11 +289,32 @@ class fpdb:
|
||||||
|
|
||||||
dia.destroy()
|
dia.destroy()
|
||||||
|
|
||||||
def dia_create_del_database(self, widget, data=None):
|
def dia_maintain_dbs(self, widget, data=None):
|
||||||
self.warning_box("Unimplemented: Create/Delete Database")
|
self.warning_box("Unimplemented: Maintain Databases")
|
||||||
self.obtain_global_lock()
|
return
|
||||||
|
if len(self.tab_names) == 1:
|
||||||
|
if self.obtain_global_lock(): # returns true if successful
|
||||||
|
# only main tab has been opened, open dialog
|
||||||
|
dia = gtk.Dialog("Maintain Databases",
|
||||||
|
self.window,
|
||||||
|
gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT,
|
||||||
|
(gtk.STOCK_CANCEL, gtk.RESPONSE_REJECT,
|
||||||
|
gtk.STOCK_SAVE, gtk.RESPONSE_ACCEPT))
|
||||||
|
dia.set_default_size(700, 320)
|
||||||
|
|
||||||
|
prefs = GuiDatabase.GuiDatabase(self.config, self.window, dia)
|
||||||
|
response = dia.run()
|
||||||
|
if response == gtk.RESPONSE_ACCEPT:
|
||||||
|
# save updated config
|
||||||
|
self.config.save()
|
||||||
|
|
||||||
self.release_global_lock()
|
self.release_global_lock()
|
||||||
|
|
||||||
|
dia.destroy()
|
||||||
|
else:
|
||||||
|
self.warning_box("Cannot open Database Maintenance window because "
|
||||||
|
+ "other windows have been opened. Re-start fpdb to use this option.")
|
||||||
|
|
||||||
def dia_create_del_user(self, widget, data=None):
|
def dia_create_del_user(self, widget, data=None):
|
||||||
self.warning_box("Unimplemented: Create/Delete user")
|
self.warning_box("Unimplemented: Create/Delete user")
|
||||||
self.obtain_global_lock()
|
self.obtain_global_lock()
|
||||||
|
@ -628,7 +642,7 @@ class fpdb:
|
||||||
<menuitem action="tableviewer"/>
|
<menuitem action="tableviewer"/>
|
||||||
</menu>
|
</menu>
|
||||||
<menu action="database">
|
<menu action="database">
|
||||||
<menuitem action="createdb"/>
|
<menuitem action="maintaindbs"/>
|
||||||
<menuitem action="createuser"/>
|
<menuitem action="createuser"/>
|
||||||
<menuitem action="createtabs"/>
|
<menuitem action="createtabs"/>
|
||||||
<menuitem action="rebuildhudcache"/>
|
<menuitem action="rebuildhudcache"/>
|
||||||
|
@ -671,7 +685,7 @@ class fpdb:
|
||||||
('sessionreplay', None, '_Session Replayer (todo)', None, 'Session Replayer (todo)', self.not_implemented),
|
('sessionreplay', None, '_Session Replayer (todo)', None, 'Session Replayer (todo)', self.not_implemented),
|
||||||
('tableviewer', None, 'Poker_table Viewer (mostly obselete)', None, 'Poker_table Viewer (mostly obselete)', self.tab_table_viewer),
|
('tableviewer', None, 'Poker_table Viewer (mostly obselete)', None, 'Poker_table Viewer (mostly obselete)', self.tab_table_viewer),
|
||||||
('database', None, '_Database'),
|
('database', None, '_Database'),
|
||||||
('createdb', None, 'Create or Delete _Database (todo)', None, 'Create or Delete Database', self.dia_create_del_database),
|
('maintaindbs', None, '_Maintain Databases (todo)', None, 'Maintain Databases', self.dia_maintain_dbs),
|
||||||
('createuser', None, 'Create or Delete _User (todo)', None, 'Create or Delete User', self.dia_create_del_user),
|
('createuser', None, 'Create or Delete _User (todo)', None, 'Create or Delete User', self.dia_create_del_user),
|
||||||
('createtabs', None, 'Create or Recreate _Tables', None, 'Create or Recreate Tables ', self.dia_recreate_tables),
|
('createtabs', None, 'Create or Recreate _Tables', None, 'Create or Recreate Tables ', self.dia_recreate_tables),
|
||||||
('rebuildhudcache', None, 'Rebuild HUD Cache', None, 'Rebuild HUD Cache', self.dia_recreate_hudcache),
|
('rebuildhudcache', None, 'Rebuild HUD Cache', None, 'Rebuild HUD Cache', self.dia_recreate_hudcache),
|
||||||
|
@ -693,9 +707,22 @@ class fpdb:
|
||||||
window.add_accel_group(accel_group)
|
window.add_accel_group(accel_group)
|
||||||
return menubar
|
return menubar
|
||||||
|
|
||||||
def load_profile(self):
|
def load_profile(self, create_db = False):
|
||||||
"""Loads profile from the provided path name."""
|
"""Loads profile from the provided path name."""
|
||||||
self.config = Configuration.Config(file=options.config, dbname=options.dbname)
|
self.config = Configuration.Config(file=options.config, dbname=options.dbname)
|
||||||
|
if self.config.file_error:
|
||||||
|
self.warning_box( "There is an error in your config file\n" + self.config.file
|
||||||
|
+ "\n\nError is: " + str(self.config.file_error)
|
||||||
|
, diatitle="CONFIG FILE ERROR" )
|
||||||
|
exit()
|
||||||
|
|
||||||
|
log = Configuration.get_logger("logging.conf", "fpdb", log_dir=self.config.dir_log)
|
||||||
|
print "Logfile is " + os.path.join(self.config.dir_log, self.config.log_file) + "\n"
|
||||||
|
if self.config.example_copy:
|
||||||
|
self.info_box( "Config file"
|
||||||
|
, "has been created at:\n%s.\n" % self.config.file
|
||||||
|
+ "Edit your screen_name and hand history path in the supported_sites "
|
||||||
|
+ "section of the Preferences window (Main menu) before trying to import hands.")
|
||||||
self.settings = {}
|
self.settings = {}
|
||||||
self.settings['global_lock'] = self.lock
|
self.settings['global_lock'] = self.lock
|
||||||
if (os.sep=="/"):
|
if (os.sep=="/"):
|
||||||
|
@ -716,6 +743,9 @@ class fpdb:
|
||||||
err_msg = None
|
err_msg = None
|
||||||
try:
|
try:
|
||||||
self.db = Database.Database(self.config, sql = self.sql)
|
self.db = Database.Database(self.config, sql = self.sql)
|
||||||
|
if self.db.get_backend_name() == 'SQLite':
|
||||||
|
# tell sqlite users where the db file is
|
||||||
|
print "Connected to SQLite: %(database)s" % {'database':self.db.db_path}
|
||||||
except Exceptions.FpdbMySQLAccessDenied:
|
except Exceptions.FpdbMySQLAccessDenied:
|
||||||
err_msg = "MySQL Server reports: Access denied. Are your permissions set correctly?"
|
err_msg = "MySQL Server reports: Access denied. Are your permissions set correctly?"
|
||||||
except Exceptions.FpdbMySQLNoDatabase:
|
except Exceptions.FpdbMySQLNoDatabase:
|
||||||
|
@ -794,8 +824,11 @@ class fpdb:
|
||||||
# TODO: can we get some / all of the stuff done in this function to execute on any kind of abort?
|
# TODO: can we get some / all of the stuff done in this function to execute on any kind of abort?
|
||||||
print "Quitting normally"
|
print "Quitting normally"
|
||||||
# TODO: check if current settings differ from profile, if so offer to save or abort
|
# TODO: check if current settings differ from profile, if so offer to save or abort
|
||||||
|
try:
|
||||||
if self.db is not None and self.db.connected:
|
if self.db is not None and self.db.connected:
|
||||||
self.db.disconnect()
|
self.db.disconnect()
|
||||||
|
except _mysql_exceptions.OperationalError: # oh, damn, we're already disconnected
|
||||||
|
pass
|
||||||
self.statusIcon.set_visible(False)
|
self.statusIcon.set_visible(False)
|
||||||
gtk.main_quit()
|
gtk.main_quit()
|
||||||
|
|
||||||
|
@ -900,11 +933,20 @@ This program is licensed under the AGPL3, see docs"""+os.sep+"agpl-3.0.txt")
|
||||||
self.tab_main_help(None, None)
|
self.tab_main_help(None, None)
|
||||||
|
|
||||||
self.window.show()
|
self.window.show()
|
||||||
self.load_profile()
|
self.load_profile(create_db = True)
|
||||||
|
|
||||||
|
if not options.errorsToConsole:
|
||||||
|
fileName = os.path.join(self.config.dir_log, 'fpdb-errors.txt')
|
||||||
|
print "\nNote: error output is being diverted to fpdb-errors.txt and HUD-errors.txt in:\n" \
|
||||||
|
+ self.config.dir_log + "\nAny major error will be reported there _only_.\n"
|
||||||
|
errorFile = open(fileName, 'w', 0)
|
||||||
|
sys.stderr = errorFile
|
||||||
|
|
||||||
self.statusIcon = gtk.StatusIcon()
|
self.statusIcon = gtk.StatusIcon()
|
||||||
if os.path.exists('../gfx/fpdb-cards.png'):
|
# use getcwd() here instead of sys.path[0] so that py2exe works:
|
||||||
self.statusIcon.set_from_file('../gfx/fpdb-cards.png')
|
cards = os.path.join(os.getcwd(), '..','gfx','fpdb-cards.png')
|
||||||
|
if os.path.exists(cards):
|
||||||
|
self.statusIcon.set_from_file(cards)
|
||||||
elif os.path.exists('/usr/share/pixmaps/fpdb-cards.png'):
|
elif os.path.exists('/usr/share/pixmaps/fpdb-cards.png'):
|
||||||
self.statusIcon.set_from_file('/usr/share/pixmaps/fpdb-cards.png')
|
self.statusIcon.set_from_file('/usr/share/pixmaps/fpdb-cards.png')
|
||||||
else:
|
else:
|
||||||
|
@ -963,6 +1005,14 @@ This program is licensed under the AGPL3, see docs"""+os.sep+"agpl-3.0.txt")
|
||||||
self.window.show()
|
self.window.show()
|
||||||
self.window.present()
|
self.window.present()
|
||||||
|
|
||||||
|
def info_box(self, str1, str2):
|
||||||
|
diapath = gtk.MessageDialog( parent=None, flags=0, type=gtk.MESSAGE_INFO
|
||||||
|
, buttons=(gtk.BUTTONS_OK), message_format=str1 )
|
||||||
|
diapath.format_secondary_text(str2)
|
||||||
|
response = diapath.run()
|
||||||
|
diapath.destroy()
|
||||||
|
return response
|
||||||
|
|
||||||
def warning_box(self, str, diatitle="FPDB WARNING"):
|
def warning_box(self, str, diatitle="FPDB WARNING"):
|
||||||
diaWarning = gtk.Dialog(title=diatitle, parent=None, flags=0, buttons=(gtk.STOCK_OK,gtk.RESPONSE_OK))
|
diaWarning = gtk.Dialog(title=diatitle, parent=None, flags=0, buttons=(gtk.STOCK_OK,gtk.RESPONSE_OK))
|
||||||
|
|
||||||
|
@ -975,6 +1025,7 @@ This program is licensed under the AGPL3, see docs"""+os.sep+"agpl-3.0.txt")
|
||||||
return response
|
return response
|
||||||
|
|
||||||
def validate_config(self):
|
def validate_config(self):
|
||||||
|
if self.config.get_import_parameters().get('saveStarsHH'):
|
||||||
hhbase = self.config.get_import_parameters().get("hhArchiveBase")
|
hhbase = self.config.get_import_parameters().get("hhArchiveBase")
|
||||||
hhbase = os.path.expanduser(hhbase)
|
hhbase = os.path.expanduser(hhbase)
|
||||||
#hhdir = os.path.join(hhbase,site)
|
#hhdir = os.path.join(hhbase,site)
|
||||||
|
|
|
@ -16,208 +16,6 @@
|
||||||
#In the "official" distribution you can find the license in
|
#In the "official" distribution you can find the license in
|
||||||
#agpl-3.0.txt in the docs folder of the package.
|
#agpl-3.0.txt in the docs folder of the package.
|
||||||
|
|
||||||
import os
|
|
||||||
import re
|
|
||||||
import sys
|
|
||||||
import logging
|
|
||||||
import math
|
|
||||||
from time import time, strftime
|
|
||||||
from Exceptions import *
|
|
||||||
|
|
||||||
try:
|
|
||||||
import sqlalchemy.pool as pool
|
|
||||||
use_pool = True
|
|
||||||
except ImportError:
|
|
||||||
logging.info("Not using sqlalchemy connection pool.")
|
|
||||||
use_pool = False
|
|
||||||
|
|
||||||
try:
|
|
||||||
from numpy import var
|
|
||||||
use_numpy = True
|
|
||||||
except ImportError:
|
|
||||||
logging.info("Not using numpy to define variance in sqlite.")
|
|
||||||
use_numpy = False
|
|
||||||
|
|
||||||
import fpdb_simple
|
|
||||||
import FpdbSQLQueries
|
|
||||||
import Configuration
|
|
||||||
|
|
||||||
# Variance created as sqlite has a bunch of undefined aggregate functions.
|
|
||||||
|
|
||||||
class VARIANCE:
|
|
||||||
def __init__(self):
|
|
||||||
self.store = []
|
|
||||||
|
|
||||||
def step(self, value):
|
|
||||||
self.store.append(value)
|
|
||||||
|
|
||||||
def finalize(self):
|
|
||||||
return float(var(self.store))
|
|
||||||
|
|
||||||
class sqlitemath:
|
|
||||||
def mod(self, a, b):
|
|
||||||
return a%b
|
|
||||||
|
|
||||||
class fpdb_db:
|
|
||||||
MYSQL_INNODB = 2
|
|
||||||
PGSQL = 3
|
|
||||||
SQLITE = 4
|
|
||||||
|
|
||||||
def __init__(self):
|
|
||||||
"""Simple constructor, doesnt really do anything"""
|
|
||||||
self.db = None
|
|
||||||
self.cursor = None
|
|
||||||
self.sql = {}
|
|
||||||
#end def __init__
|
|
||||||
|
|
||||||
def do_connect(self, config=None):
|
|
||||||
"""Connects a database using information in config"""
|
|
||||||
if config is None:
|
|
||||||
raise FpdbError('Configuration not defined')
|
|
||||||
|
|
||||||
self.settings = {}
|
|
||||||
self.settings['os'] = "linuxmac" if os.name != "nt" else "windows"
|
|
||||||
|
|
||||||
db = config.get_db_parameters()
|
|
||||||
self.connect(backend=db['db-backend'],
|
|
||||||
host=db['db-host'],
|
|
||||||
database=db['db-databaseName'],
|
|
||||||
user=db['db-user'],
|
|
||||||
password=db['db-password'])
|
|
||||||
#end def do_connect
|
|
||||||
|
|
||||||
def connect(self, backend=None, host=None, database=None,
|
|
||||||
user=None, password=None):
|
|
||||||
"""Connects a database with the given parameters"""
|
|
||||||
if backend is None:
|
|
||||||
raise FpdbError('Database backend not defined')
|
|
||||||
self.backend = backend
|
|
||||||
self.host = host
|
|
||||||
self.user = user
|
|
||||||
self.password = password
|
|
||||||
self.database = database
|
|
||||||
if backend == fpdb_db.MYSQL_INNODB:
|
|
||||||
import MySQLdb
|
|
||||||
if use_pool:
|
|
||||||
MySQLdb = pool.manage(MySQLdb, pool_size=5)
|
|
||||||
try:
|
|
||||||
self.db = MySQLdb.connect(host=host, user=user, passwd=password, db=database, use_unicode=True)
|
|
||||||
#TODO: Add port option
|
|
||||||
except MySQLdb.Error, ex:
|
|
||||||
if ex.args[0] == 1045:
|
|
||||||
raise FpdbMySQLAccessDenied(ex.args[0], ex.args[1])
|
|
||||||
elif ex.args[0] == 2002 or ex.args[0] == 2003: # 2002 is no unix socket, 2003 is no tcp socket
|
|
||||||
raise FpdbMySQLNoDatabase(ex.args[0], ex.args[1])
|
|
||||||
else:
|
|
||||||
print "*** WARNING UNKNOWN MYSQL ERROR", ex
|
|
||||||
elif backend == fpdb_db.PGSQL:
|
|
||||||
import psycopg2
|
|
||||||
import psycopg2.extensions
|
|
||||||
if use_pool:
|
|
||||||
psycopg2 = pool.manage(psycopg2, pool_size=5)
|
|
||||||
psycopg2.extensions.register_type(psycopg2.extensions.UNICODE)
|
|
||||||
# If DB connection is made over TCP, then the variables
|
|
||||||
# host, user and password are required
|
|
||||||
# For local domain-socket connections, only DB name is
|
|
||||||
# needed, and everything else is in fact undefined and/or
|
|
||||||
# flat out wrong
|
|
||||||
# sqlcoder: This database only connect failed in my windows setup??
|
|
||||||
# Modifed it to try the 4 parameter style if the first connect fails - does this work everywhere?
|
|
||||||
connected = False
|
|
||||||
if self.host == "localhost" or self.host == "127.0.0.1":
|
|
||||||
try:
|
|
||||||
self.db = psycopg2.connect(database = database)
|
|
||||||
connected = True
|
|
||||||
except:
|
|
||||||
# direct connection failed so try user/pass/... version
|
|
||||||
pass
|
|
||||||
if not connected:
|
|
||||||
try:
|
|
||||||
self.db = psycopg2.connect(host = host,
|
|
||||||
user = user,
|
|
||||||
password = password,
|
|
||||||
database = database)
|
|
||||||
except Exception, ex:
|
|
||||||
if 'Connection refused' in ex.args[0]:
|
|
||||||
# meaning eg. db not running
|
|
||||||
raise FpdbPostgresqlNoDatabase(errmsg = ex.args[0])
|
|
||||||
elif 'password authentication' in ex.args[0]:
|
|
||||||
raise FpdbPostgresqlAccessDenied(errmsg = ex.args[0])
|
|
||||||
else:
|
|
||||||
msg = ex.args[0]
|
|
||||||
print msg
|
|
||||||
raise FpdbError(msg)
|
|
||||||
elif backend == fpdb_db.SQLITE:
|
|
||||||
logging.info("Connecting to SQLite:%(database)s" % {'database':database})
|
|
||||||
import sqlite3
|
|
||||||
if use_pool:
|
|
||||||
sqlite3 = pool.manage(sqlite3, pool_size=1)
|
|
||||||
else:
|
|
||||||
logging.warning("SQLite won't work well without 'sqlalchemy' installed.")
|
|
||||||
|
|
||||||
if not os.path.isdir(Configuration.DIR_DATABASES) and not database == ":memory:":
|
|
||||||
print "Creating directory: '%s'" % (Configuration.DIR_DATABASES)
|
|
||||||
os.mkdir(Configuration.DIR_DATABASES)
|
|
||||||
database = os.path.join(Configuration.DIR_DATABASES, database)
|
|
||||||
self.db = sqlite3.connect(database, detect_types=sqlite3.PARSE_DECLTYPES )
|
|
||||||
sqlite3.register_converter("bool", lambda x: bool(int(x)))
|
|
||||||
sqlite3.register_adapter(bool, lambda x: "1" if x else "0")
|
|
||||||
self.db.create_function("floor", 1, math.floor)
|
|
||||||
tmp = sqlitemath()
|
|
||||||
self.db.create_function("mod", 2, tmp.mod)
|
|
||||||
if use_numpy:
|
|
||||||
self.db.create_aggregate("variance", 1, VARIANCE)
|
|
||||||
else:
|
|
||||||
logging.warning("Some database functions will not work without NumPy support")
|
|
||||||
else:
|
|
||||||
raise FpdbError("unrecognised database backend:"+backend)
|
|
||||||
|
|
||||||
self.cursor = self.db.cursor()
|
|
||||||
# Set up query dictionary as early in the connection process as we can.
|
|
||||||
self.sql = FpdbSQLQueries.FpdbSQLQueries(self.get_backend_name())
|
|
||||||
self.cursor.execute(self.sql.query['set tx level'])
|
|
||||||
self.wrongDbVersion = False
|
|
||||||
try:
|
|
||||||
self.cursor.execute("SELECT * FROM Settings")
|
|
||||||
settings = self.cursor.fetchone()
|
|
||||||
if settings[0] != 118:
|
|
||||||
print "outdated or too new database version - please recreate tables"
|
|
||||||
self.wrongDbVersion = True
|
|
||||||
except:# _mysql_exceptions.ProgrammingError:
|
|
||||||
if database != ":memory:": print "failed to read settings table - please recreate tables"
|
|
||||||
self.wrongDbVersion = True
|
|
||||||
#end def connect
|
|
||||||
|
|
||||||
def disconnect(self, due_to_error=False):
|
|
||||||
"""Disconnects the DB"""
|
|
||||||
if due_to_error:
|
|
||||||
self.db.rollback()
|
|
||||||
else:
|
|
||||||
self.db.commit()
|
|
||||||
self.cursor.close()
|
|
||||||
self.db.close()
|
|
||||||
#end def disconnect
|
|
||||||
|
|
||||||
def reconnect(self, due_to_error=False):
|
|
||||||
"""Reconnects the DB"""
|
|
||||||
#print "started fpdb_db.reconnect"
|
|
||||||
self.disconnect(due_to_error)
|
|
||||||
self.connect(self.backend, self.host, self.database, self.user, self.password)
|
|
||||||
|
|
||||||
def get_backend_name(self):
|
|
||||||
"""Returns the name of the currently used backend"""
|
|
||||||
if self.backend==2:
|
|
||||||
return "MySQL InnoDB"
|
|
||||||
elif self.backend==3:
|
|
||||||
return "PostgreSQL"
|
|
||||||
elif self.backend==4:
|
|
||||||
return "SQLite"
|
|
||||||
else:
|
|
||||||
raise FpdbError("invalid backend")
|
|
||||||
#end def get_backend_name
|
|
||||||
|
|
||||||
def get_db_info(self):
|
|
||||||
return (self.host, self.database, self.user, self.password)
|
|
||||||
#end def get_db_info
|
|
||||||
|
|
||||||
#end class fpdb_db
|
#end class fpdb_db
|
||||||
|
|
207
pyfpdb/fpdb_import.py
Normal file → Executable file
207
pyfpdb/fpdb_import.py
Normal file → Executable file
|
@ -30,19 +30,19 @@ import Queue
|
||||||
from collections import deque # using Queue for now
|
from collections import deque # using Queue for now
|
||||||
import threading
|
import threading
|
||||||
|
|
||||||
|
import logging
|
||||||
|
# logging has been set up in fpdb.py or HUD_main.py, use their settings:
|
||||||
|
log = logging.getLogger("importer")
|
||||||
|
|
||||||
import pygtk
|
import pygtk
|
||||||
import gtk
|
import gtk
|
||||||
|
|
||||||
# fpdb/FreePokerTools modules
|
# fpdb/FreePokerTools modules
|
||||||
|
|
||||||
import fpdb_simple
|
|
||||||
import fpdb_db
|
|
||||||
import Database
|
import Database
|
||||||
import fpdb_parse_logic
|
|
||||||
import Configuration
|
import Configuration
|
||||||
import Exceptions
|
import Exceptions
|
||||||
|
|
||||||
log = Configuration.get_logger("logging.conf", "importer")
|
|
||||||
|
|
||||||
# database interface modules
|
# database interface modules
|
||||||
try:
|
try:
|
||||||
|
@ -68,6 +68,7 @@ class Importer:
|
||||||
self.config = config
|
self.config = config
|
||||||
self.sql = sql
|
self.sql = sql
|
||||||
|
|
||||||
|
#log = Configuration.get_logger("logging.conf", "importer", log_dir=self.config.dir_log)
|
||||||
self.filelist = {}
|
self.filelist = {}
|
||||||
self.dirlist = {}
|
self.dirlist = {}
|
||||||
self.siteIds = {}
|
self.siteIds = {}
|
||||||
|
@ -100,8 +101,6 @@ class Importer:
|
||||||
for i in xrange(self.settings['threads']):
|
for i in xrange(self.settings['threads']):
|
||||||
self.writerdbs.append( Database.Database(self.config, sql = self.sql) )
|
self.writerdbs.append( Database.Database(self.config, sql = self.sql) )
|
||||||
|
|
||||||
self.NEWIMPORT = Configuration.NEWIMPORT
|
|
||||||
|
|
||||||
clock() # init clock in windows
|
clock() # init clock in windows
|
||||||
|
|
||||||
#Set functions
|
#Set functions
|
||||||
|
@ -370,7 +369,7 @@ class Importer:
|
||||||
pass
|
pass
|
||||||
(stored, duplicates, partial, errors, ttime) = self.import_file_dict(self.database, file, self.filelist[file][0], self.filelist[file][1], None)
|
(stored, duplicates, partial, errors, ttime) = self.import_file_dict(self.database, file, self.filelist[file][0], self.filelist[file][1], None)
|
||||||
try:
|
try:
|
||||||
if not os.path.isdir(file):
|
if not os.path.isdir(file): # Note: This assumes that whatever calls us has an "addText" func
|
||||||
self.caller.addText(" %d stored, %d duplicates, %d partial, %d errors (time = %f)" % (stored, duplicates, partial, errors, ttime))
|
self.caller.addText(" %d stored, %d duplicates, %d partial, %d errors (time = %f)" % (stored, duplicates, partial, errors, ttime))
|
||||||
except KeyError: # TODO: Again, what error happens here? fix when we find out ..
|
except KeyError: # TODO: Again, what error happens here? fix when we find out ..
|
||||||
pass
|
pass
|
||||||
|
@ -407,9 +406,9 @@ class Importer:
|
||||||
return (0,0,0,0,0)
|
return (0,0,0,0,0)
|
||||||
|
|
||||||
conv = None
|
conv = None
|
||||||
(stored, duplicates, partial, errors, ttime) = (0, 0, 0, 0, 0)
|
(stored, duplicates, partial, errors, ttime) = (0, 0, 0, 0, time())
|
||||||
|
|
||||||
file = file.decode(fpdb_simple.LOCALE_ENCODING)
|
file = file.decode(Configuration.LOCALE_ENCODING)
|
||||||
|
|
||||||
# Load filter, process file, pass returned filename to import_fpdb_file
|
# Load filter, process file, pass returned filename to import_fpdb_file
|
||||||
if self.settings['threads'] > 0 and self.writeq is not None:
|
if self.settings['threads'] > 0 and self.writeq is not None:
|
||||||
|
@ -429,24 +428,36 @@ class Importer:
|
||||||
mod = __import__(filter)
|
mod = __import__(filter)
|
||||||
obj = getattr(mod, filter_name, None)
|
obj = getattr(mod, filter_name, None)
|
||||||
if callable(obj):
|
if callable(obj):
|
||||||
hhc = obj(in_path = file, out_path = out_path, index = 0, starsArchive = self.settings['starsArchive']) # Index into file 0 until changeover
|
idx = 0
|
||||||
if hhc.getStatus() and self.NEWIMPORT == False:
|
if file in self.pos_in_file:
|
||||||
(stored, duplicates, partial, errors, ttime) = self.import_fpdb_file(db, out_path, site, q)
|
idx = self.pos_in_file[file]
|
||||||
elif hhc.getStatus() and self.NEWIMPORT == True:
|
else:
|
||||||
#This code doesn't do anything yet
|
self.pos_in_file[file] = 0
|
||||||
|
hhc = obj(self.config, in_path = file, out_path = out_path, index = idx, starsArchive = self.settings['starsArchive'])
|
||||||
|
if hhc.getStatus():
|
||||||
handlist = hhc.getProcessedHands()
|
handlist = hhc.getProcessedHands()
|
||||||
self.pos_in_file[file] = hhc.getLastCharacterRead()
|
self.pos_in_file[file] = hhc.getLastCharacterRead()
|
||||||
to_hud = []
|
to_hud = []
|
||||||
|
|
||||||
for hand in handlist:
|
for hand in handlist:
|
||||||
if hand is not None:
|
if hand is not None:
|
||||||
#try, except duplicates here?
|
|
||||||
hand.prepInsert(self.database)
|
hand.prepInsert(self.database)
|
||||||
|
try:
|
||||||
hand.insert(self.database)
|
hand.insert(self.database)
|
||||||
|
except Exceptions.FpdbHandDuplicate:
|
||||||
|
duplicates += 1
|
||||||
|
else:
|
||||||
if self.callHud and hand.dbid_hands != 0:
|
if self.callHud and hand.dbid_hands != 0:
|
||||||
to_hud.append(hand.dbid_hands)
|
to_hud.append(hand.dbid_hands)
|
||||||
else:
|
else: # TODO: Treat empty as an error, or just ignore?
|
||||||
log.error("Hand processed but empty")
|
log.error("Hand processed but empty")
|
||||||
|
|
||||||
|
# Call hudcache update if not in bulk import mode
|
||||||
|
# FIXME: Need to test for bulk import that isn't rebuilding the cache
|
||||||
|
if self.callHud:
|
||||||
|
for hand in handlist:
|
||||||
|
if hand is not None and not hand.is_duplicate:
|
||||||
|
hand.updateHudCache(self.database)
|
||||||
self.database.commit()
|
self.database.commit()
|
||||||
|
|
||||||
#pipe the Hands.id out to the HUD
|
#pipe the Hands.id out to the HUD
|
||||||
|
@ -456,174 +467,22 @@ class Importer:
|
||||||
|
|
||||||
errors = getattr(hhc, 'numErrors')
|
errors = getattr(hhc, 'numErrors')
|
||||||
stored = getattr(hhc, 'numHands')
|
stored = getattr(hhc, 'numHands')
|
||||||
|
stored -= duplicates
|
||||||
|
stored -= errors
|
||||||
else:
|
else:
|
||||||
# conversion didn't work
|
# conversion didn't work
|
||||||
# TODO: appropriate response?
|
# TODO: appropriate response?
|
||||||
return (0, 0, 0, 1, 0)
|
return (0, 0, 0, 1, time() - ttime)
|
||||||
else:
|
else:
|
||||||
log.warning("Unknown filter filter_name:'%s' in filter:'%s'" %(filter_name, filter))
|
log.warning("Unknown filter filter_name:'%s' in filter:'%s'" %(filter_name, filter))
|
||||||
return (0, 0, 0, 1, 0)
|
return (0, 0, 0, 1, time() - ttime)
|
||||||
|
|
||||||
|
ttime = time() - ttime
|
||||||
|
|
||||||
#This will barf if conv.getStatus != True
|
#This will barf if conv.getStatus != True
|
||||||
return (stored, duplicates, partial, errors, ttime)
|
return (stored, duplicates, partial, errors, ttime)
|
||||||
|
|
||||||
|
|
||||||
def import_fpdb_file(self, db, file, site, q):
|
|
||||||
starttime = time()
|
|
||||||
last_read_hand = 0
|
|
||||||
loc = 0
|
|
||||||
(stored, duplicates, partial, errors, ttime) = (0, 0, 0, 0, 0)
|
|
||||||
# print "file =", file
|
|
||||||
if file == "stdin":
|
|
||||||
inputFile = sys.stdin
|
|
||||||
else:
|
|
||||||
if os.path.exists(file):
|
|
||||||
inputFile = open(file, "rU")
|
|
||||||
else:
|
|
||||||
self.removeFromFileList[file] = True
|
|
||||||
return (0, 0, 0, 1, 0)
|
|
||||||
try:
|
|
||||||
loc = self.pos_in_file[file]
|
|
||||||
#size = os.path.getsize(file)
|
|
||||||
#print "loc =", loc, 'size =', size
|
|
||||||
except KeyError:
|
|
||||||
pass
|
|
||||||
# Read input file into class and close file
|
|
||||||
inputFile.seek(loc)
|
|
||||||
#tmplines = inputFile.readlines()
|
|
||||||
#if tmplines == None or tmplines == []:
|
|
||||||
# print "tmplines = ", tmplines
|
|
||||||
#else:
|
|
||||||
# print "tmplines[0] =", tmplines[0]
|
|
||||||
self.lines = fpdb_simple.removeTrailingEOL(inputFile.readlines())
|
|
||||||
self.pos_in_file[file] = inputFile.tell()
|
|
||||||
inputFile.close()
|
|
||||||
|
|
||||||
x = clock()
|
|
||||||
(stored, duplicates, partial, errors, ttime, handsId) = self.import_fpdb_lines(db, self.lines, starttime, file, site, q)
|
|
||||||
|
|
||||||
db.commit()
|
|
||||||
y = clock()
|
|
||||||
ttime = y - x
|
|
||||||
#ttime = time() - starttime
|
|
||||||
if q is None:
|
|
||||||
log.info("Total stored: %(stored)d\tduplicates:%(duplicates)d\terrors:%(errors)d\ttime:%(ttime)s" % locals())
|
|
||||||
|
|
||||||
if not stored:
|
|
||||||
if duplicates:
|
|
||||||
for line_no in xrange(len(self.lines)):
|
|
||||||
if self.lines[line_no].find("Game #") != -1:
|
|
||||||
final_game_line = self.lines[line_no]
|
|
||||||
handsId=fpdb_simple.parseSiteHandNo(final_game_line)
|
|
||||||
else:
|
|
||||||
print "failed to read a single hand from file:", inputFile
|
|
||||||
handsId = 0
|
|
||||||
#todo: this will cause return of an unstored hand number if the last hand was error
|
|
||||||
self.handsId = handsId
|
|
||||||
|
|
||||||
return (stored, duplicates, partial, errors, ttime)
|
|
||||||
# end def import_fpdb_file
|
|
||||||
|
|
||||||
|
|
||||||
def import_fpdb_lines(self, db, lines, starttime, file, site, q = None):
|
|
||||||
"""Import an fpdb hand history held in the list lines, could be one hand or many"""
|
|
||||||
|
|
||||||
#db.lock_for_insert() # should be ok when using one thread, but doesn't help??
|
|
||||||
while gtk.events_pending():
|
|
||||||
gtk.main_iteration(False)
|
|
||||||
|
|
||||||
try: # sometimes we seem to be getting an empty self.lines, in which case, we just want to return.
|
|
||||||
firstline = lines[0]
|
|
||||||
except:
|
|
||||||
# just skip the debug message and return silently:
|
|
||||||
#print "DEBUG: import_fpdb_file: failed on lines[0]: '%s' '%s' '%s' '%s' " %( file, site, lines, loc)
|
|
||||||
return (0,0,0,1,0,0)
|
|
||||||
|
|
||||||
if "Tournament Summary" in firstline:
|
|
||||||
print "TODO: implement importing tournament summaries"
|
|
||||||
#self.faobs = readfile(inputFile)
|
|
||||||
#self.parseTourneyHistory()
|
|
||||||
return (0,0,0,1,0,0)
|
|
||||||
|
|
||||||
category = fpdb_simple.recogniseCategory(firstline)
|
|
||||||
|
|
||||||
startpos = 0
|
|
||||||
stored = 0 #counter
|
|
||||||
duplicates = 0 #counter
|
|
||||||
partial = 0 #counter
|
|
||||||
errors = 0 #counter
|
|
||||||
ttime = 0
|
|
||||||
handsId = 0
|
|
||||||
|
|
||||||
for i in xrange(len(lines)):
|
|
||||||
if len(lines[i]) < 2: #Wierd way to detect for '\r\n' or '\n'
|
|
||||||
endpos = i
|
|
||||||
hand = lines[startpos:endpos]
|
|
||||||
|
|
||||||
if len(hand[0]) < 2:
|
|
||||||
hand=hand[1:]
|
|
||||||
|
|
||||||
if len(hand) < 3:
|
|
||||||
pass
|
|
||||||
#TODO: This is ugly - we didn't actually find the start of the
|
|
||||||
# hand with the outer loop so we test again...
|
|
||||||
else:
|
|
||||||
isTourney = fpdb_simple.isTourney(hand[0])
|
|
||||||
if not isTourney:
|
|
||||||
hand = fpdb_simple.filterAnteBlindFold(hand)
|
|
||||||
self.hand = hand
|
|
||||||
|
|
||||||
try:
|
|
||||||
handsId = fpdb_parse_logic.mainParser( self.settings, self.siteIds[site]
|
|
||||||
, category, hand, self.config
|
|
||||||
, db, q )
|
|
||||||
db.commit()
|
|
||||||
|
|
||||||
stored += 1
|
|
||||||
if self.callHud:
|
|
||||||
#print "call to HUD here. handsId:",handsId
|
|
||||||
#pipe the Hands.id out to the HUD
|
|
||||||
# print "fpdb_import: sending hand to hud", handsId, "pipe =", self.caller.pipe_to_hud
|
|
||||||
try:
|
|
||||||
self.caller.pipe_to_hud.stdin.write("%s" % (handsId) + os.linesep)
|
|
||||||
except IOError: # hud closed
|
|
||||||
self.callHud = False
|
|
||||||
pass # continue import without hud
|
|
||||||
except Exceptions.DuplicateError:
|
|
||||||
duplicates += 1
|
|
||||||
db.rollback()
|
|
||||||
except (ValueError), fe:
|
|
||||||
errors += 1
|
|
||||||
self.printEmailErrorMessage(errors, file, hand)
|
|
||||||
|
|
||||||
if (self.settings['failOnError']):
|
|
||||||
db.commit() #dont remove this, in case hand processing was cancelled.
|
|
||||||
raise
|
|
||||||
else:
|
|
||||||
db.rollback()
|
|
||||||
except (fpdb_simple.FpdbError), fe:
|
|
||||||
errors += 1
|
|
||||||
self.printEmailErrorMessage(errors, file, hand)
|
|
||||||
db.rollback()
|
|
||||||
|
|
||||||
if self.settings['failOnError']:
|
|
||||||
db.commit() #dont remove this, in case hand processing was cancelled.
|
|
||||||
raise
|
|
||||||
|
|
||||||
if self.settings['minPrint']:
|
|
||||||
if not ((stored+duplicates+errors) % self.settings['minPrint']):
|
|
||||||
print "stored:", stored, " duplicates:", duplicates, "errors:", errors
|
|
||||||
|
|
||||||
if self.settings['handCount']:
|
|
||||||
if ((stored+duplicates+errors) >= self.settings['handCount']):
|
|
||||||
if not self.settings['quiet']:
|
|
||||||
print "quitting due to reaching the amount of hands to be imported"
|
|
||||||
print "Total stored:", stored, "duplicates:", duplicates, "errors:", errors, " time:", (time() - starttime)
|
|
||||||
sys.exit(0)
|
|
||||||
startpos = endpos
|
|
||||||
return (stored, duplicates, partial, errors, ttime, handsId)
|
|
||||||
# end def import_fpdb_lines
|
|
||||||
|
|
||||||
def printEmailErrorMessage(self, errors, filename, line):
|
def printEmailErrorMessage(self, errors, filename, line):
|
||||||
traceback.print_exc(file=sys.stderr)
|
traceback.print_exc(file=sys.stderr)
|
||||||
print "Error No.",errors,", please send the hand causing this to steffen@sycamoretest.info so I can fix it."
|
print "Error No.",errors,", please send the hand causing this to steffen@sycamoretest.info so I can fix it."
|
||||||
|
|
|
@ -1,235 +0,0 @@
|
||||||
#!/usr/bin/python
|
|
||||||
|
|
||||||
#Copyright 2008 Steffen Jobbagy-Felso
|
|
||||||
#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 <http://www.gnu.org/licenses/>.
|
|
||||||
#In the "official" distribution you can find the license in
|
|
||||||
#agpl-3.0.txt in the docs folder of the package.
|
|
||||||
|
|
||||||
#parses an in-memory fpdb hand history and calls db routine to store it
|
|
||||||
|
|
||||||
import sys
|
|
||||||
from time import time, strftime
|
|
||||||
from Exceptions import *
|
|
||||||
|
|
||||||
import fpdb_simple
|
|
||||||
import Database
|
|
||||||
|
|
||||||
def mainParser(settings, siteID, category, hand, config, db = None, writeq = None):
|
|
||||||
""" mainParser for Holdem Hands """
|
|
||||||
t0 = time()
|
|
||||||
backend = settings['db-backend']
|
|
||||||
# Ideally db connection is passed in, if not use sql list if passed in,
|
|
||||||
# otherwise start from scratch
|
|
||||||
if db is None:
|
|
||||||
db = Database.Database(c = config, sql = None)
|
|
||||||
category = fpdb_simple.recogniseCategory(hand[0])
|
|
||||||
|
|
||||||
base = "hold" if (category == "holdem" or category == "omahahi" or
|
|
||||||
category == "omahahilo") else "stud"
|
|
||||||
|
|
||||||
#part 0: create the empty arrays
|
|
||||||
# lineTypes valid values: header, name, cards, action, win, rake, ignore
|
|
||||||
# lineStreets valid values: predeal, preflop, flop, turn, river
|
|
||||||
lineTypes = []
|
|
||||||
lineStreets = []
|
|
||||||
cardValues = []
|
|
||||||
cardSuits = []
|
|
||||||
boardValues = []
|
|
||||||
boardSuits = []
|
|
||||||
antes = []
|
|
||||||
allIns = []
|
|
||||||
actionAmounts = []
|
|
||||||
actionNos = []
|
|
||||||
actionTypes = []
|
|
||||||
actionTypeByNo = []
|
|
||||||
seatLines = []
|
|
||||||
winnings = []
|
|
||||||
rakes = []
|
|
||||||
|
|
||||||
#part 1: read hand no and check for duplicate
|
|
||||||
siteHandNo = fpdb_simple.parseSiteHandNo(hand[0])
|
|
||||||
handStartTime = fpdb_simple.parseHandStartTime(hand[0])
|
|
||||||
isTourney = fpdb_simple.isTourney(hand[0])
|
|
||||||
|
|
||||||
smallBlindLine = None
|
|
||||||
for i, line in enumerate(hand):
|
|
||||||
if 'posts small blind' in line or 'posts the small blind' in line:
|
|
||||||
if line[-2:] == "$0": continue
|
|
||||||
smallBlindLine = i
|
|
||||||
break
|
|
||||||
else:
|
|
||||||
smallBlindLine = 0
|
|
||||||
# If we did not find a small blind line, what happens?
|
|
||||||
# if we leave it at None, it errors two lines down.
|
|
||||||
|
|
||||||
gametypeID = fpdb_simple.recogniseGametypeID(backend, db, db.get_cursor(),
|
|
||||||
hand[0], hand[smallBlindLine],
|
|
||||||
siteID, category, isTourney)
|
|
||||||
if isTourney:
|
|
||||||
siteTourneyNo = fpdb_simple.parseTourneyNo(hand[0])
|
|
||||||
buyin = fpdb_simple.parseBuyin(hand[0])
|
|
||||||
fee = fpdb_simple.parseFee(hand[0])
|
|
||||||
entries = -1 #todo: parse this
|
|
||||||
prizepool = -1 #todo: parse this
|
|
||||||
knockout = False
|
|
||||||
tourneyStartTime= handStartTime #todo: read tourney start time
|
|
||||||
rebuyOrAddon = fpdb_simple.isRebuyOrAddon(hand[0])
|
|
||||||
|
|
||||||
# The tourney site id has to be searched because it may already be in
|
|
||||||
# db with a TourneyTypeId which is different from the one automatically
|
|
||||||
# calculated (Summary import first)
|
|
||||||
tourneyTypeId = fpdb_simple.recogniseTourneyTypeId(db, siteID,
|
|
||||||
siteTourneyNo,
|
|
||||||
buyin, fee,
|
|
||||||
knockout,
|
|
||||||
rebuyOrAddon)
|
|
||||||
else:
|
|
||||||
siteTourneyNo = -1
|
|
||||||
buyin = -1
|
|
||||||
fee = -1
|
|
||||||
entries = -1
|
|
||||||
prizepool = -1
|
|
||||||
knockout = 0
|
|
||||||
tourneyStartTime= None
|
|
||||||
rebuyOrAddon = -1
|
|
||||||
|
|
||||||
tourneyTypeId = 1
|
|
||||||
fpdb_simple.isAlreadyInDB(db, gametypeID, siteHandNo)
|
|
||||||
|
|
||||||
hand = fpdb_simple.filterCrap(hand, isTourney)
|
|
||||||
|
|
||||||
#part 2: classify lines by type (e.g. cards, action, win, sectionchange) and street
|
|
||||||
fpdb_simple.classifyLines(hand, category, lineTypes, lineStreets)
|
|
||||||
|
|
||||||
#part 3: read basic player info
|
|
||||||
#3a read player names, startcashes
|
|
||||||
for i, line in enumerate(hand):
|
|
||||||
if lineTypes[i] == "name":
|
|
||||||
seatLines.append(line)
|
|
||||||
|
|
||||||
names = fpdb_simple.parseNames(seatLines)
|
|
||||||
playerIDs = db.recognisePlayerIDs(names, siteID) # inserts players as needed
|
|
||||||
tmp = fpdb_simple.parseCashesAndSeatNos(seatLines)
|
|
||||||
startCashes = tmp['startCashes']
|
|
||||||
seatNos = tmp['seatNos']
|
|
||||||
|
|
||||||
fpdb_simple.createArrays(category, len(names), cardValues, cardSuits, antes,
|
|
||||||
winnings, rakes, actionTypes, allIns,
|
|
||||||
actionAmounts, actionNos, actionTypeByNo)
|
|
||||||
|
|
||||||
#3b read positions
|
|
||||||
if base == "hold":
|
|
||||||
positions = fpdb_simple.parsePositions(hand, names)
|
|
||||||
|
|
||||||
#part 4: take appropriate action for each line based on linetype
|
|
||||||
for i, line in enumerate(hand):
|
|
||||||
if lineTypes[i] == "cards":
|
|
||||||
fpdb_simple.parseCardLine(category, lineStreets[i], line, names,
|
|
||||||
cardValues, cardSuits, boardValues,
|
|
||||||
boardSuits)
|
|
||||||
#if category=="studhilo":
|
|
||||||
# print "hand[i]:", hand[i]
|
|
||||||
# print "cardValues:", cardValues
|
|
||||||
# print "cardSuits:", cardSuits
|
|
||||||
elif lineTypes[i] == "action":
|
|
||||||
fpdb_simple.parseActionLine(base, isTourney, line, lineStreets[i],
|
|
||||||
playerIDs, names, actionTypes, allIns,
|
|
||||||
actionAmounts, actionNos, actionTypeByNo)
|
|
||||||
elif lineTypes[i] == "win":
|
|
||||||
fpdb_simple.parseWinLine(line, names, winnings, isTourney)
|
|
||||||
elif lineTypes[i] == "rake":
|
|
||||||
totalRake = 0 if isTourney else fpdb_simple.parseRake(line)
|
|
||||||
fpdb_simple.splitRake(winnings, rakes, totalRake)
|
|
||||||
elif (lineTypes[i] == "header" or lineTypes[i] == "rake" or
|
|
||||||
lineTypes[i] == "name" or lineTypes[i] == "ignore"):
|
|
||||||
pass
|
|
||||||
elif lineTypes[i] == "ante":
|
|
||||||
fpdb_simple.parseAnteLine(line, isTourney, names, antes)
|
|
||||||
elif lineTypes[i] == "table":
|
|
||||||
tableResult=fpdb_simple.parseTableLine(base, line)
|
|
||||||
else:
|
|
||||||
raise FpdbError("unrecognised lineType:" + lineTypes[i])
|
|
||||||
|
|
||||||
maxSeats = tableResult['maxSeats']
|
|
||||||
tableName = tableResult['tableName']
|
|
||||||
#print "before part5, antes:", antes
|
|
||||||
|
|
||||||
#part 5: final preparations, then call Database.* with
|
|
||||||
# the arrays as they are - that file will fill them.
|
|
||||||
fpdb_simple.convertCardValues(cardValues)
|
|
||||||
if base == "hold":
|
|
||||||
fpdb_simple.convertCardValuesBoard(boardValues)
|
|
||||||
fpdb_simple.convertBlindBet(actionTypes, actionAmounts)
|
|
||||||
fpdb_simple.checkPositions(positions)
|
|
||||||
|
|
||||||
c = db.get_cursor()
|
|
||||||
c.execute("SELECT limitType FROM Gametypes WHERE id=%s" % (db.sql.query['placeholder'],), (gametypeID, ))
|
|
||||||
limit_type = c.fetchone()[0]
|
|
||||||
fpdb_simple.convert3B4B(category, limit_type, actionTypes, actionAmounts)
|
|
||||||
|
|
||||||
totalWinnings = sum(winnings)
|
|
||||||
|
|
||||||
# if hold'em, use positions and not antes, if stud do not use positions, use antes
|
|
||||||
# this is used for handsplayers inserts, so still needed even if hudcache update is being skipped
|
|
||||||
if base == "hold":
|
|
||||||
hudImportData = fpdb_simple.generateHudCacheData(playerIDs, base,
|
|
||||||
category, actionTypes,
|
|
||||||
allIns, actionTypeByNo,
|
|
||||||
winnings,
|
|
||||||
totalWinnings,
|
|
||||||
positions, actionTypes,
|
|
||||||
actionAmounts, None)
|
|
||||||
else:
|
|
||||||
hudImportData = fpdb_simple.generateHudCacheData(playerIDs, base,
|
|
||||||
category, actionTypes,
|
|
||||||
allIns, actionTypeByNo,
|
|
||||||
winnings,
|
|
||||||
totalWinnings, None,
|
|
||||||
actionTypes,
|
|
||||||
actionAmounts, antes)
|
|
||||||
|
|
||||||
try:
|
|
||||||
db.commit() # need to commit new players as different db connection used
|
|
||||||
# for other writes. maybe this will change maybe not ...
|
|
||||||
except: # TODO: this really needs to be narrowed down
|
|
||||||
print "parse: error during commit: " + str(sys.exc_value)
|
|
||||||
|
|
||||||
# HERE's an ugly kludge to keep from failing when positions is undef
|
|
||||||
# We'll fix this by getting rid of the legacy importer. REB
|
|
||||||
try:
|
|
||||||
if positions:
|
|
||||||
pass
|
|
||||||
except NameError:
|
|
||||||
positions = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
|
|
||||||
# save data structures in a HandToWrite instance and then insert into database:
|
|
||||||
htw = Database.HandToWrite()
|
|
||||||
htw.set_all( config, settings, base, category, siteTourneyNo, buyin
|
|
||||||
, fee, knockout, entries, prizepool, tourneyStartTime
|
|
||||||
, isTourney, tourneyTypeId, siteID, siteHandNo
|
|
||||||
, gametypeID, handStartTime, names, playerIDs, startCashes
|
|
||||||
, positions, antes, cardValues, cardSuits, boardValues, boardSuits
|
|
||||||
, winnings, rakes, actionTypes, allIns, actionAmounts
|
|
||||||
, actionNos, hudImportData, maxSeats, tableName, seatNos)
|
|
||||||
|
|
||||||
# save hand in db via direct call or via q if in a thread
|
|
||||||
if writeq is None:
|
|
||||||
result = db.store_the_hand(htw)
|
|
||||||
else:
|
|
||||||
writeq.put(htw)
|
|
||||||
result = -999 # meaning unknown
|
|
||||||
|
|
||||||
t9 = time()
|
|
||||||
#print "parse and save=(%4.3f)" % (t9-t0)
|
|
||||||
return result
|
|
||||||
#end def mainParser
|
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,64 +1,75 @@
|
||||||
[loggers]
|
[loggers]
|
||||||
keys=root,parser,importer,config,db
|
keys=root,fpdb,logview,parser,importer,config,db,hud,filter
|
||||||
|
|
||||||
[handlers]
|
[handlers]
|
||||||
keys=consoleHandler,fileHandler
|
keys=consoleHandler,rotatingFileHandler
|
||||||
|
|
||||||
[formatters]
|
[formatters]
|
||||||
keys=fileFormatter,stderrFormatter
|
keys=fileFormatter,stderrFormatter
|
||||||
|
|
||||||
[logger_root]
|
[logger_root]
|
||||||
level=INFO
|
level=INFO
|
||||||
handlers=consoleHandler,fileHandler
|
handlers=consoleHandler,rotatingFileHandler
|
||||||
|
|
||||||
[logger_fpdb]
|
[logger_fpdb]
|
||||||
level=INFO
|
level=INFO
|
||||||
handlers=consoleHandler,fileHandler
|
handlers=consoleHandler,rotatingFileHandler
|
||||||
qualname=fpdb
|
qualname=fpdb
|
||||||
propagate=0
|
propagate=0
|
||||||
|
|
||||||
[logger_logview]
|
[logger_logview]
|
||||||
level=INFO
|
level=INFO
|
||||||
handlers=consoleHandler,fileHandler
|
handlers=consoleHandler,rotatingFileHandler
|
||||||
qualname=logview
|
qualname=logview
|
||||||
propagate=0
|
propagate=0
|
||||||
|
|
||||||
[logger_parser]
|
[logger_parser]
|
||||||
level=INFO
|
level=INFO
|
||||||
handlers=consoleHandler,fileHandler
|
handlers=consoleHandler,rotatingFileHandler
|
||||||
qualname=parser
|
qualname=parser
|
||||||
propagate=0
|
propagate=0
|
||||||
|
|
||||||
[logger_importer]
|
[logger_importer]
|
||||||
level=DEBUG
|
level=DEBUG
|
||||||
handlers=consoleHandler,fileHandler
|
handlers=consoleHandler,rotatingFileHandler
|
||||||
qualname=importer
|
qualname=importer
|
||||||
propagate=0
|
propagate=0
|
||||||
|
|
||||||
[logger_config]
|
[logger_config]
|
||||||
level=INFO
|
level=INFO
|
||||||
handlers=consoleHandler,fileHandler
|
handlers=consoleHandler,rotatingFileHandler
|
||||||
qualname=config
|
qualname=config
|
||||||
propagate=0
|
propagate=0
|
||||||
|
|
||||||
[logger_db]
|
[logger_db]
|
||||||
level=DEBUG
|
level=DEBUG
|
||||||
handlers=consoleHandler,fileHandler
|
handlers=consoleHandler,rotatingFileHandler
|
||||||
qualname=db
|
qualname=db
|
||||||
propagate=0
|
propagate=0
|
||||||
|
|
||||||
|
[logger_hud]
|
||||||
|
level=DEBUG
|
||||||
|
handlers=consoleHandler,rotatingFileHandler
|
||||||
|
qualname=hud
|
||||||
|
propagate=0
|
||||||
|
|
||||||
|
[logger_filter]
|
||||||
|
level=INFO
|
||||||
|
handlers=consoleHandler,rotatingFileHandler
|
||||||
|
qualname=filter
|
||||||
|
propagate=0
|
||||||
|
|
||||||
[handler_consoleHandler]
|
[handler_consoleHandler]
|
||||||
class=StreamHandler
|
class=StreamHandler
|
||||||
level=ERROR
|
level=ERROR
|
||||||
formatter=stderrFormatter
|
formatter=stderrFormatter
|
||||||
args=(sys.stderr,)
|
args=(sys.stderr,)
|
||||||
|
|
||||||
[handler_fileHandler]
|
[handler_rotatingFileHandler]
|
||||||
class=FileHandler
|
class=handlers.RotatingFileHandler
|
||||||
level=DEBUG
|
level=DEBUG
|
||||||
formatter=fileFormatter
|
formatter=fileFormatter
|
||||||
args=('logging.out', 'a')
|
args=('%(logFile)s', 'a', 100000000, 5)
|
||||||
|
|
||||||
|
|
||||||
[formatter_fileFormatter]
|
[formatter_fileFormatter]
|
||||||
format=%(asctime)s - %(name)-12s %(levelname)-8s %(message)s
|
format=%(asctime)s - %(name)-12s %(levelname)-8s %(message)s
|
||||||
|
|
|
@ -22,52 +22,189 @@ Py2exe script for fpdb.
|
||||||
|
|
||||||
########################################################################
|
########################################################################
|
||||||
|
|
||||||
#TODO: change GuiAutoImport so that it knows to start HUD_main.exe, when appropriate
|
#TODO:
|
||||||
# include the lib needed to handle png files in mucked
|
|
||||||
# get rid of all the uneeded libraries (e.g., pyQT)
|
# get rid of all the uneeded libraries (e.g., pyQT)
|
||||||
# think about an installer
|
# think about an installer
|
||||||
|
|
||||||
|
# done: change GuiAutoImport so that it knows to start HUD_main.exe, when appropriate
|
||||||
|
# include the lib needed to handle png files in mucked
|
||||||
|
|
||||||
#HOW TO USE this script:
|
#HOW TO USE this script:
|
||||||
#
|
#
|
||||||
# cd to the folder where this script is stored, usually .../pyfpdb.
|
#- cd to the folder where this script is stored, usually .../pyfpdb.
|
||||||
# If there are build and dist subfolders present , delete them to get
|
# [If there are build and dist subfolders present , delete them to get
|
||||||
# rid of earlier builds.
|
# rid of earlier builds. Update: script now does this for you]
|
||||||
# Run the script with "py2exe_setup.py py2exe"
|
#- Run the script with "py2exe_setup.py py2exe"
|
||||||
# You will frequently get messages about missing .dll files. E. g.,
|
#- You will frequently get messages about missing .dll files. E. g.,
|
||||||
# MSVCP90.dll. These are somewhere in your windows install, so you
|
# MSVCP90.dll. These are somewhere in your windows install, so you
|
||||||
# can just copy them to your working folder.
|
# can just copy them to your working folder. (or just assume other
|
||||||
# If it works, you'll have 2 new folders, build and dist. Build is
|
# person will have them? any copyright issues with including them?)
|
||||||
# working space and should be deleted. Dist contains the files to be
|
#- [ If it works, you'll have 3 new folders, build and dist and gfx. Build is
|
||||||
# distributed. Last, you must copy the etc/, lib/ and share/ folders
|
# working space and should be deleted. Dist and gfx contain the files to be
|
||||||
# from your gtk/bin/ folder to the dist folder. (the whole folders, not
|
# distributed. ]
|
||||||
# just the contents) You can (should) then prune the etc/, lib/ and
|
# If it works, you'll have a new dir fpdb-XXX-YYYYMMDD-exe which should
|
||||||
# share/ folders to remove components we don't need.
|
# contain 2 dirs; gfx and pyfpdb and run_fpdb.bat
|
||||||
|
#- Last, you must copy the etc/, lib/ and share/ folders from your
|
||||||
|
# gtk/bin/ (just /gtk/?) folder to the pyfpdb folder. (the whole folders,
|
||||||
|
# not just the contents)
|
||||||
|
#- You can (should) then prune the etc/, lib/ and share/ folders to
|
||||||
|
# remove components we don't need.
|
||||||
|
|
||||||
|
# sqlcoder notes: this worked for me with the following notes:
|
||||||
|
#- I used the following versions:
|
||||||
|
# python 2.5.4
|
||||||
|
# gtk+ 2.14.7 (gtk_2.14.7-20090119)
|
||||||
|
# pycairo 1.4.12-2
|
||||||
|
# pygobject 2.14.2-2
|
||||||
|
# pygtk 2.12.1-3
|
||||||
|
# matplotlib 0.98.3
|
||||||
|
# numpy 1.4.0
|
||||||
|
# py2exe-0.6.9 for python 2.5
|
||||||
|
#
|
||||||
|
#- I also copied these dlls manually from <gtk>/bin to /dist :
|
||||||
|
#
|
||||||
|
# libgobject-2.0-0.dll
|
||||||
|
# libgdk-win32-2.0-0.dll
|
||||||
|
|
||||||
|
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
from distutils.core import setup
|
from distutils.core import setup
|
||||||
import py2exe
|
import py2exe
|
||||||
|
import glob
|
||||||
|
import matplotlib
|
||||||
|
import shutil
|
||||||
|
from datetime import date
|
||||||
|
|
||||||
|
|
||||||
|
origIsSystemDLL = py2exe.build_exe.isSystemDLL
|
||||||
|
def isSystemDLL(pathname):
|
||||||
|
if os.path.basename(pathname).lower() in ("msvcp71.dll", "dwmapi.dll"):
|
||||||
|
return 0
|
||||||
|
return origIsSystemDLL(pathname)
|
||||||
|
py2exe.build_exe.isSystemDLL = isSystemDLL
|
||||||
|
|
||||||
|
|
||||||
|
def remove_tree(top):
|
||||||
|
# Delete everything reachable from the directory named in 'top',
|
||||||
|
# assuming there are no symbolic links.
|
||||||
|
# CAUTION: This is dangerous! For example, if top == '/', it
|
||||||
|
# could delete all your disk files.
|
||||||
|
# sc: Nicked this from somewhere, added the if statement to try
|
||||||
|
# make it a bit safer
|
||||||
|
if top in ('build','dist','gfx') and os.path.basename(os.getcwd()) == 'pyfpdb':
|
||||||
|
#print "removing directory '"+top+"' ..."
|
||||||
|
for root, dirs, files in os.walk(top, topdown=False):
|
||||||
|
for name in files:
|
||||||
|
os.remove(os.path.join(root, name))
|
||||||
|
for name in dirs:
|
||||||
|
os.rmdir(os.path.join(root, name))
|
||||||
|
os.rmdir(top)
|
||||||
|
|
||||||
|
def test_and_remove(top):
|
||||||
|
if os.path.exists(top):
|
||||||
|
if os.path.isdir(top):
|
||||||
|
remove_tree(top)
|
||||||
|
else:
|
||||||
|
print "Unexpected file '"+top+"' found. Exiting."
|
||||||
|
exit()
|
||||||
|
|
||||||
|
# remove build and dist dirs if they exist
|
||||||
|
test_and_remove('dist')
|
||||||
|
test_and_remove('build')
|
||||||
|
#test_and_remove('gfx')
|
||||||
|
|
||||||
|
|
||||||
|
today = date.today().strftime('%Y%m%d')
|
||||||
|
print "\n" + r"Output will be created in \pyfpdb\ and \fpdb_XXX_"+today+'\\'
|
||||||
|
print "Enter value for XXX (any length): ", # the comma means no newline
|
||||||
|
xxx = sys.stdin.readline().rstrip()
|
||||||
|
dist_dirname = r'fpdb-' + xxx + '-' + today + '-exe'
|
||||||
|
dist_dir = r'..\fpdb-' + xxx + '-' + today + '-exe'
|
||||||
|
print
|
||||||
|
|
||||||
|
test_and_remove(dist_dir)
|
||||||
|
|
||||||
setup(
|
setup(
|
||||||
name = 'fpdb',
|
name = 'fpdb',
|
||||||
description = 'Free Poker DataBase',
|
description = 'Free Poker DataBase',
|
||||||
version = '0.12',
|
version = '0.12',
|
||||||
|
|
||||||
console = [ {'script': 'fpdb.py', },
|
console = [ {'script': 'fpdb.py', "icon_resources": [(1, "../gfx/fpdb_large_icon.ico")]},
|
||||||
{'script': 'HUD_main.py', },
|
{'script': 'HUD_main.py', },
|
||||||
{'script': 'Configuration.py', }
|
{'script': 'Configuration.py', }
|
||||||
],
|
],
|
||||||
|
|
||||||
options = {'py2exe': {
|
options = {'py2exe': {
|
||||||
'packages' :'encodings',
|
'packages' : ['encodings', 'matplotlib'],
|
||||||
'includes' : 'cairo, pango, pangocairo, atk, gobject, PokerStarsToFpdb',
|
'includes' : ['cairo', 'pango', 'pangocairo', 'atk', 'gobject'
|
||||||
'excludes' : '_tkagg, _agg2, cocoaagg, fltkagg',
|
,'matplotlib.numerix.random_array'
|
||||||
'dll_excludes': 'libglade-2.0-0.dll',
|
,'AbsoluteToFpdb', 'BetfairToFpdb'
|
||||||
|
,'CarbonToFpdb', 'EverleafToFpdb'
|
||||||
|
,'FulltiltToFpdb', 'OnGameToFpdb'
|
||||||
|
,'PartyPokerToFpdb', 'PokerStarsToFpdb'
|
||||||
|
,'UltimateBetToFpdb', 'Win2dayToFpdb'
|
||||||
|
],
|
||||||
|
'excludes' : ['_tkagg', '_agg2', 'cocoaagg', 'fltkagg'], # surely we need this? '_gtkagg'
|
||||||
|
'dll_excludes': ['libglade-2.0-0.dll', 'libgdk-win32-2.0-0.dll'
|
||||||
|
,'libgobject-2.0-0.dll'],
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
data_files = ['HUD_config.xml.example',
|
# files in 2nd value in tuple are moved to dir named in 1st value
|
||||||
'Cards01.png',
|
data_files = [('', ['HUD_config.xml.example', 'Cards01.png', 'logging.conf', '../docs/readme.txt'])
|
||||||
'logging.conf',
|
,(dist_dir, [r'..\run_fpdb.bat'])
|
||||||
(r'matplotlibdata', glob.glob(r'c:\python26\Lib\site-packages\matplotlib\mpl-data\*'))
|
,( dist_dir + r'\gfx', glob.glob(r'..\gfx\*.*') )
|
||||||
]
|
# line below has problem with fonts subdir ('not a regular file')
|
||||||
|
#,(r'matplotlibdata', glob.glob(r'c:\python25\Lib\site-packages\matplotlib\mpl-data\*'))
|
||||||
|
] + matplotlib.get_py2exe_datafiles()
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
os.rename('dist', 'pyfpdb')
|
||||||
|
|
||||||
|
print '\n' + 'If py2exe was successful add the \\etc \\lib and \\share dirs '
|
||||||
|
print 'from your gtk dir to \\%s\\pyfpdb\\\n' % dist_dirname
|
||||||
|
print 'Also copy libgobject-2.0-0.dll and libgdk-win32-2.0-0.dll from <gtk_dir>\\bin'
|
||||||
|
print 'into there'
|
||||||
|
|
||||||
|
dest = os.path.join(dist_dirname, 'pyfpdb')
|
||||||
|
#print "try renaming pyfpdb to", dest
|
||||||
|
dest = dest.replace('\\', '\\\\')
|
||||||
|
#print "dest is now", dest
|
||||||
|
os.rename( 'pyfpdb', dest )
|
||||||
|
|
||||||
|
|
||||||
|
print "Enter directory name for GTK 2.14 (e.g. c:\code\gtk_2.14.7-20090119)\n: ", # the comma means no newline
|
||||||
|
gtk_dir = sys.stdin.readline().rstrip()
|
||||||
|
|
||||||
|
|
||||||
|
print "\ncopying files and dirs from ", gtk_dir, "to", dest.replace('\\\\', '\\'), "..."
|
||||||
|
src = os.path.join(gtk_dir, 'bin', 'libgdk-win32-2.0-0.dll')
|
||||||
|
src = src.replace('\\', '\\\\')
|
||||||
|
shutil.copy( src, dest )
|
||||||
|
|
||||||
|
src = os.path.join(gtk_dir, 'bin', 'libgobject-2.0-0.dll')
|
||||||
|
src = src.replace('\\', '\\\\')
|
||||||
|
shutil.copy( src, dest )
|
||||||
|
|
||||||
|
|
||||||
|
src_dir = os.path.join(gtk_dir, 'etc')
|
||||||
|
src_dir = src_dir.replace('\\', '\\\\')
|
||||||
|
dest_dir = os.path.join(dest, 'etc')
|
||||||
|
dest_dir = dest_dir.replace('\\', '\\\\')
|
||||||
|
shutil.copytree( src_dir, dest_dir )
|
||||||
|
|
||||||
|
src_dir = os.path.join(gtk_dir, 'lib')
|
||||||
|
src_dir = src_dir.replace('\\', '\\\\')
|
||||||
|
dest_dir = os.path.join(dest, 'lib')
|
||||||
|
dest_dir = dest_dir.replace('\\', '\\\\')
|
||||||
|
shutil.copytree( src_dir, dest_dir )
|
||||||
|
|
||||||
|
src_dir = os.path.join(gtk_dir, 'share')
|
||||||
|
src_dir = src_dir.replace('\\', '\\\\')
|
||||||
|
dest_dir = os.path.join(dest, 'share')
|
||||||
|
dest_dir = dest_dir.replace('\\', '\\\\')
|
||||||
|
shutil.copytree( src_dir, dest_dir )
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,41 @@
|
||||||
|
PokerStars Game #37165169101: Hold'em No Limit ($0.10/$0.25 USD) - 2009/12/25 9:50:09 ET
|
||||||
|
Table 'Lucretia IV' 6-max Seat #2 is the button
|
||||||
|
Seat 1: Blåveis ($55.10 in chips)
|
||||||
|
Seat 2: Kinewma ($31.40 in chips)
|
||||||
|
Seat 3: AAALISAAAA ($20.20 in chips)
|
||||||
|
Seat 4: Arbaz ($25 in chips)
|
||||||
|
Seat 5: s0rrow ($29.85 in chips)
|
||||||
|
Seat 6: bys7 ($41.35 in chips)
|
||||||
|
AAALISAAAA: posts small blind $0.10
|
||||||
|
Arbaz: posts big blind $0.25
|
||||||
|
*** HOLE CARDS ***
|
||||||
|
Dealt to s0rrow [Ac As]
|
||||||
|
s0rrow: raises $0.50 to $0.75
|
||||||
|
bys7: calls $0.75
|
||||||
|
Blåveis: folds
|
||||||
|
Kinewma: folds
|
||||||
|
AAALISAAAA: raises $1.50 to $2.25
|
||||||
|
Arbaz: folds
|
||||||
|
s0rrow: raises $3.50 to $5.75
|
||||||
|
bys7: folds
|
||||||
|
AAALISAAAA: raises $14.45 to $20.20 and is all-in
|
||||||
|
s0rrow: calls $14.45
|
||||||
|
*** FLOP *** [3d 7h Kh]
|
||||||
|
*** TURN *** [3d 7h Kh] [Ts]
|
||||||
|
*** RIVER *** [3d 7h Kh Ts] [5c]
|
||||||
|
*** SHOW DOWN ***
|
||||||
|
AAALISAAAA: shows [Kd 5d] (two pair, Kings and Fives)
|
||||||
|
s0rrow: shows [Ac As] (a pair of Aces)
|
||||||
|
AAALISAAAA collected $39.35 from pot
|
||||||
|
*** SUMMARY ***
|
||||||
|
Total pot $41.40 | Rake $2.05
|
||||||
|
Board [3d 7h Kh Ts 5c]
|
||||||
|
Seat 1: Blåveis folded before Flop (didn't bet)
|
||||||
|
Seat 2: Kinewma (button) folded before Flop (didn't bet)
|
||||||
|
Seat 3: AAALISAAAA (small blind) showed [Kd 5d] and won ($39.35) with two pair, Kings and Fives
|
||||||
|
Seat 4: Arbaz (big blind) folded before Flop
|
||||||
|
Seat 5: s0rrow showed [Ac As] and lost with a pair of Aces
|
||||||
|
Seat 6: bys7 folded before Flop
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -22,12 +22,39 @@ Test if gtk is working.
|
||||||
########################################################################
|
########################################################################
|
||||||
|
|
||||||
import sys
|
import sys
|
||||||
|
import os
|
||||||
|
import Configuration
|
||||||
|
|
||||||
|
config_path = Configuration.get_default_config_path()
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
import gobject as _gobject
|
||||||
|
print "Import of gobject:\tSuccess"
|
||||||
import pygtk
|
import pygtk
|
||||||
|
print "Import of pygtk:\tSuccess"
|
||||||
pygtk.require('2.0')
|
pygtk.require('2.0')
|
||||||
import gtk
|
import gtk
|
||||||
|
print "Import of gtk:\t\tSuccess"
|
||||||
|
import pango
|
||||||
|
print "Import of pango:\tSuccess"
|
||||||
|
|
||||||
|
if os.name == 'nt':
|
||||||
|
import win32
|
||||||
|
import win32api
|
||||||
|
print "Import of win32:\tSuccess"
|
||||||
|
|
||||||
|
try:
|
||||||
|
import matplotlib
|
||||||
|
matplotlib.use('GTK')
|
||||||
|
print "Import of matplotlib:\tSuccess"
|
||||||
|
import numpy
|
||||||
|
print "Import of numpy:\tSuccess"
|
||||||
|
import pylab
|
||||||
|
print "Import of pylab:\tSuccess"
|
||||||
|
except:
|
||||||
|
print "\nError:", sys.exc_info()
|
||||||
|
print "\npress return to finish"
|
||||||
|
sys.stdin.readline()
|
||||||
|
|
||||||
win = gtk.Window(gtk.WINDOW_TOPLEVEL)
|
win = gtk.Window(gtk.WINDOW_TOPLEVEL)
|
||||||
win.set_title("Test GTK")
|
win.set_title("Test GTK")
|
||||||
|
@ -42,7 +69,7 @@ try:
|
||||||
(gtk.STOCK_CLOSE, gtk.RESPONSE_OK))
|
(gtk.STOCK_CLOSE, gtk.RESPONSE_OK))
|
||||||
dia.set_default_size(500, 300)
|
dia.set_default_size(500, 300)
|
||||||
|
|
||||||
l = gtk.Label("GTK is working!")
|
l = gtk.Label("GTK is working!\nConfig location: %s" %config_path)
|
||||||
dia.vbox.add(l)
|
dia.vbox.add(l)
|
||||||
l.show()
|
l.show()
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
import sqlite3
|
import sqlite3
|
||||||
import fpdb_db
|
import Database
|
||||||
import math
|
import math
|
||||||
|
|
||||||
# Should probably use our wrapper classes - creating sqlite db in memory
|
# Should probably use our wrapper classes - creating sqlite db in memory
|
||||||
|
@ -14,11 +14,11 @@ con.isolation_level = None
|
||||||
con.create_function("floor", 1, math.floor)
|
con.create_function("floor", 1, math.floor)
|
||||||
|
|
||||||
#Mod function
|
#Mod function
|
||||||
tmp = fpdb_db.sqlitemath()
|
tmp = Database.sqlitemath()
|
||||||
con.create_function("mod", 2, tmp.mod)
|
con.create_function("mod", 2, tmp.mod)
|
||||||
|
|
||||||
# Aggregate function VARIANCE()
|
# Aggregate function VARIANCE()
|
||||||
con.create_aggregate("variance", 1, fpdb_db.VARIANCE)
|
con.create_aggregate("variance", 1, Database.VARIANCE)
|
||||||
|
|
||||||
|
|
||||||
cur = con.cursor()
|
cur = con.cursor()
|
||||||
|
|
44
pyfpdb/test_PokerStars.py
Normal file → Executable file
44
pyfpdb/test_PokerStars.py
Normal file → Executable file
|
@ -27,9 +27,9 @@ settings.update(config.get_default_paths())
|
||||||
gametype = {'type':'ring', 'base':'draw', 'category':'badugi', 'limitType':'fl', 'sb':'0.25', 'bb':'0.50','currency':'USD'}
|
gametype = {'type':'ring', 'base':'draw', 'category':'badugi', 'limitType':'fl', 'sb':'0.25', 'bb':'0.50','currency':'USD'}
|
||||||
text = ""
|
text = ""
|
||||||
|
|
||||||
hhc = PokerStarsToFpdb.PokerStars(autostart=False)
|
hhc = PokerStarsToFpdb.PokerStars(config, autostart=False)
|
||||||
|
|
||||||
h = HoldemOmahaHand(None, "PokerStars", gametype, text, builtFrom = "Test")
|
h = HoldemOmahaHand(config, None, "PokerStars", gametype, text, builtFrom = "Test")
|
||||||
h.addPlayer("1", "s0rrow", "100000")
|
h.addPlayer("1", "s0rrow", "100000")
|
||||||
|
|
||||||
hhc.compilePlayerRegexs(h)
|
hhc.compilePlayerRegexs(h)
|
||||||
|
@ -39,7 +39,7 @@ def checkGameInfo(hhc, header, info):
|
||||||
assert hhc.determineGameType(header) == info
|
assert hhc.determineGameType(header) == info
|
||||||
|
|
||||||
def testGameInfo():
|
def testGameInfo():
|
||||||
hhc = PokerStarsToFpdb.PokerStars(autostart=False)
|
hhc = PokerStarsToFpdb.PokerStars(config, autostart=False)
|
||||||
pairs = (
|
pairs = (
|
||||||
(u"PokerStars Game #20461877044: Hold'em No Limit ($1/$2) - 2008/09/16 18:58:01 ET",
|
(u"PokerStars Game #20461877044: Hold'em No Limit ($1/$2) - 2008/09/16 18:58:01 ET",
|
||||||
{'type':'ring', 'base':"hold", 'category':'holdem', 'limitType':'nl', 'sb':'1', 'bb':'2', 'currency':'USD'}),
|
{'type':'ring', 'base':"hold", 'category':'holdem', 'limitType':'nl', 'sb':'1', 'bb':'2', 'currency':'USD'}),
|
||||||
|
@ -82,18 +82,21 @@ def testFlopImport():
|
||||||
# River: hero (continuation bets?) all-in and is not called
|
# River: hero (continuation bets?) all-in and is not called
|
||||||
importer.addBulkImportImportFileOrDir(
|
importer.addBulkImportImportFileOrDir(
|
||||||
"""regression-test-files/cash/Stars/Flop/NLHE-6max-USD-0.05-0.10-200912.Stats-comparision.txt""", site="PokerStars")
|
"""regression-test-files/cash/Stars/Flop/NLHE-6max-USD-0.05-0.10-200912.Stats-comparision.txt""", site="PokerStars")
|
||||||
|
importer.addBulkImportImportFileOrDir(
|
||||||
|
"""regression-test-files/cash/Stars/Flop/NLHE-6max-USD-0.05-0.10-200912.Allin-pre.txt""", site="PokerStars")
|
||||||
importer.setCallHud(False)
|
importer.setCallHud(False)
|
||||||
(stored, dups, partial, errs, ttime) = importer.runImport()
|
(stored, dups, partial, errs, ttime) = importer.runImport()
|
||||||
print "DEBUG: stored: %s dups: %s partial: %s errs: %s ttime: %s" %(stored, dups, partial, errs, ttime)
|
print "DEBUG: stored: %s dups: %s partial: %s errs: %s ttime: %s" %(stored, dups, partial, errs, ttime)
|
||||||
importer.clearFileList()
|
importer.clearFileList()
|
||||||
|
|
||||||
col = { 'sawShowdown': 2
|
col = { 'sawShowdown': 2, 'street0Aggr':3
|
||||||
}
|
}
|
||||||
|
|
||||||
q = """SELECT
|
q = """SELECT
|
||||||
s.name,
|
s.name,
|
||||||
p.name,
|
p.name,
|
||||||
hp.sawShowdown
|
hp.sawShowdown,
|
||||||
|
hp.street0Aggr
|
||||||
FROM
|
FROM
|
||||||
Hands as h,
|
Hands as h,
|
||||||
Sites as s,
|
Sites as s,
|
||||||
|
@ -114,6 +117,33 @@ and s.id = p.siteid"""
|
||||||
# Assert if any sawShowdown = True
|
# Assert if any sawShowdown = True
|
||||||
assert result[row][col['sawShowdown']] == 0
|
assert result[row][col['sawShowdown']] == 0
|
||||||
|
|
||||||
|
q = """SELECT
|
||||||
|
s.name,
|
||||||
|
p.name,
|
||||||
|
hp.sawShowdown,
|
||||||
|
hp.street0Aggr
|
||||||
|
FROM
|
||||||
|
Hands as h,
|
||||||
|
Sites as s,
|
||||||
|
Gametypes as g,
|
||||||
|
HandsPlayers as hp,
|
||||||
|
Players as p
|
||||||
|
WHERE
|
||||||
|
h.siteHandNo = 37165169101
|
||||||
|
and g.id = h.gametypeid
|
||||||
|
and hp.handid = h.id
|
||||||
|
and p.id = hp.playerid
|
||||||
|
and s.id = p.siteid"""
|
||||||
|
c = db.get_cursor()
|
||||||
|
c.execute(q)
|
||||||
|
result = c.fetchall()
|
||||||
|
pstats = { u'Kinewma':0, u'Arbaz':0, u's0rrow':1, u'bys7':0, u'AAALISAAAA':1, u'Bl\xe5veis':0 }
|
||||||
|
for row, data in enumerate(result):
|
||||||
|
print "DEBUG: result[%s]: %s == %s" %(row, result[row], pstats[data[1]])
|
||||||
|
assert result[row][col['sawShowdown']] == pstats[data[1]]
|
||||||
|
|
||||||
|
assert 0 == 1
|
||||||
|
|
||||||
def testStudImport():
|
def testStudImport():
|
||||||
db.recreate_tables()
|
db.recreate_tables()
|
||||||
importer = fpdb_import.Importer(False, settings, config)
|
importer = fpdb_import.Importer(False, settings, config)
|
||||||
|
@ -148,10 +178,6 @@ def testDrawImport():
|
||||||
(stored, dups, partial, errs, ttime) = importer.runImport()
|
(stored, dups, partial, errs, ttime) = importer.runImport()
|
||||||
importer.clearFileList()
|
importer.clearFileList()
|
||||||
except FpdbError:
|
except FpdbError:
|
||||||
if Configuration.NEWIMPORT == False:
|
|
||||||
#Old import code doesn't support draw
|
|
||||||
pass
|
|
||||||
else:
|
|
||||||
assert 0 == 1
|
assert 0 == 1
|
||||||
|
|
||||||
# Should actually do some testing here
|
# Should actually do some testing here
|
||||||
|
|
|
@ -1,27 +0,0 @@
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
import fpdb_simple
|
|
||||||
import datetime
|
|
||||||
import py
|
|
||||||
|
|
||||||
def checkDateParse(header, site, result):
|
|
||||||
assert fpdb_simple.parseHandStartTime(header, site) == result
|
|
||||||
|
|
||||||
def testPokerStarsHHDate():
|
|
||||||
tuples = (
|
|
||||||
("PokerStars Game #21969660557: Hold'em No Limit ($0.50/$1.00) - 2008/11/12 10:00:48 CET [2008/11/12 4:00:48 ET]", "ps",
|
|
||||||
datetime.datetime(2008,11,12,15,00,48)),
|
|
||||||
("PokerStars Game #21969660557: Hold'em No Limit ($0.50/$1.00) - 2008/08/17 - 01:14:43 (ET)", "ps",
|
|
||||||
datetime.datetime(2008,8,17,6,14,43)),
|
|
||||||
("PokerStars Game #21969660557: Hold'em No Limit ($0.50/$1.00) - 2008/09/07 06:23:14 ET", "ps",
|
|
||||||
datetime.datetime(2008,9,7,11,23,14))
|
|
||||||
)
|
|
||||||
|
|
||||||
#def testTableDetection():
|
|
||||||
# result = Tables.clean_title("French (deep)")
|
|
||||||
# assert result == "French"
|
|
||||||
# result = Tables.clean_title("French (deep) - $0.25/$0.50 - No Limit Hold'em - Logged In As xxxx")
|
|
||||||
# assert result == "French"
|
|
||||||
#
|
|
||||||
# for (header, site, result) in tuples:
|
|
||||||
# yield checkDateParse, header, site, result
|
|
||||||
|
|
7
run_fpdb.bat
Executable file
7
run_fpdb.bat
Executable file
|
@ -0,0 +1,7 @@
|
||||||
|
|
||||||
|
rem .bat script to run fpdb
|
||||||
|
|
||||||
|
cd pyfpdb
|
||||||
|
|
||||||
|
fpdb.exe
|
||||||
|
|
33
run_fpdb.py
Normal file
33
run_fpdb.py
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
#!/usr/bin/python
|
||||||
|
|
||||||
|
#Copyright 2008 Carl Gherardi
|
||||||
|
#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 <http://www.gnu.org/licenses/>.
|
||||||
|
#In the "official" distribution you can find the license in
|
||||||
|
#agpl-3.0.txt in the docs folder of the package.
|
||||||
|
|
||||||
|
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
|
||||||
|
# sys.path[0] holds the directory run_fpdb.py is in
|
||||||
|
sys.path[0] = sys.path[0]+os.sep+"pyfpdb"
|
||||||
|
os.chdir(sys.path[0])
|
||||||
|
#print "sys.path[0] =", sys.path[0], "cwd =", os.getcwd()
|
||||||
|
|
||||||
|
import fpdb
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
me = fpdb.fpdb()
|
||||||
|
me.main()
|
||||||
|
exit()
|
Loading…
Reference in New Issue
Block a user