Prepull commit
This commit is contained in:
parent
9a145e14ad
commit
1eaa00321b
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
|
||||
|
||||
|
|
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
|
||||
# Copyright 2008, Carl Gherardi
|
||||
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright 2010, Matthew Boss
|
||||
#
|
||||
# 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
|
||||
|
@ -18,93 +19,286 @@
|
|||
|
||||
########################################################################
|
||||
|
||||
# Standard Library modules
|
||||
import Configuration
|
||||
import traceback
|
||||
# This code is based heavily on EverleafToFpdb.py, by Carl Gherardi
|
||||
#
|
||||
# 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 re
|
||||
import xml.dom.minidom
|
||||
from xml.dom.minidom import Node
|
||||
from HandHistoryConverter import HandHistoryConverter
|
||||
import logging
|
||||
from HandHistoryConverter import *
|
||||
from decimal import Decimal
|
||||
|
||||
# Carbon format looks like:
|
||||
class Carbon(HandHistoryConverter):
|
||||
|
||||
# 1) <description type="Holdem" stakes="No Limit ($0.25/$0.50)"/>
|
||||
# 2) <game id="14902583-5578" starttime="20081006145401" numholecards="2" gametype="2" realmoney="true" data="20081006|Niagara Falls (14902583)|14902583|14902583-5578|false">
|
||||
# 3) <players dealer="8">
|
||||
# <player seat="3" nickname="PlayerInSeat3" balance="$43.29" dealtin="true" />
|
||||
# ...
|
||||
# 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"/>
|
||||
sitename = "Carbon"
|
||||
filetype = "text"
|
||||
codepage = "cp1252"
|
||||
siteID = 11
|
||||
|
||||
# The full sequence for a NHLE cash game is:
|
||||
# BLINDS, PREFLOP, POSTFLOP, POSTTURN, POSTRIVER, SHOWDOWN, END_OF_GAME
|
||||
# This sequence can be terminated after BLINDS at any time by END_OF_FOLDED_GAME
|
||||
# Static regexes
|
||||
re_SplitHands = re.compile(r'</game>\n+(?=<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 __init__(self, config, filename):
|
||||
print "Initialising Carbon Poker converter class"
|
||||
HandHistoryConverter.__init__(self, config, filename, "Carbon") # Call super class init
|
||||
self.setFileType("xml")
|
||||
self.siteId = 4 # Needs to match id entry in Sites database
|
||||
def compilePlayerRegexs(self, hand):
|
||||
pass
|
||||
|
||||
def readSupportedGames(self):
|
||||
pass
|
||||
def determineGameType(self):
|
||||
gametype = []
|
||||
desc_node = self.doc.getElementsByTagName("description")
|
||||
#TODO: no examples of non ring type yet
|
||||
gametype = gametype + ["ring"]
|
||||
type = desc_node[0].getAttribute("type")
|
||||
if(type == "Holdem"):
|
||||
gametype = gametype + ["hold"]
|
||||
else:
|
||||
print "Carbon: Unknown gametype: '%s'" % (type)
|
||||
def playerNameFromSeatNo(self, seatNo, hand):
|
||||
# This special function is required because Carbon Poker records
|
||||
# actions by seat number, not by the player's name
|
||||
for p in hand.players:
|
||||
if p[0] == int(seatNo):
|
||||
return p[1]
|
||||
|
||||
stakes = desc_node[0].getAttribute("stakes")
|
||||
#TODO: no examples of anything except nlhe
|
||||
m = re.match('(?P<LIMIT>No Limit)\s\(\$?(?P<SB>[.0-9]+)/\$?(?P<BB>[.0-9]+)\)', stakes)
|
||||
def readSupportedGames(self):
|
||||
return [["ring", "hold", "nl"],
|
||||
["tour", "hold", "nl"]]
|
||||
|
||||
if(m.group('LIMIT') == "No Limit"):
|
||||
gametype = gametype + ["nl"]
|
||||
def determineGameType(self, handText):
|
||||
"""return dict with keys/values:
|
||||
'type' in ('ring', 'tour')
|
||||
'limitType' in ('nl', 'cn', 'pl', 'cp', 'fl')
|
||||
'base' in ('hold', 'stud', 'draw')
|
||||
'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 """
|
||||
|
||||
gametype = gametype + [self.float2int(m.group('SB'))]
|
||||
gametype = gametype + [self.float2int(m.group('BB'))]
|
||||
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()
|
||||
|
||||
return gametype
|
||||
limits = { 'No Limit':'nl', 'Limit':'fl' }
|
||||
games = { # base, category
|
||||
'Holdem' : ('hold','holdem'),
|
||||
'Holdem Tournament' : ('hold','holdem') }
|
||||
|
||||
def readPlayerStacks(self):
|
||||
pass
|
||||
def readBlinds(self):
|
||||
pass
|
||||
def readAction(self):
|
||||
pass
|
||||
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:
|
||||
self.info['type'] = 'ring'
|
||||
self.info['currency'] = 'USD'
|
||||
|
||||
# Override read function as xml.minidom barfs on the Carbon layout
|
||||
# This is pretty dodgy
|
||||
def readFile(self, filename):
|
||||
print "Carbon: Reading file: '%s'" %(filename)
|
||||
infile=open(filename, "rU")
|
||||
self.obs = infile.read()
|
||||
infile.close()
|
||||
self.obs = "<CarbonHHFile>\n" + self.obs + "</CarbonHHFile>"
|
||||
try:
|
||||
doc = xml.dom.minidom.parseString(self.obs)
|
||||
self.doc = doc
|
||||
except:
|
||||
traceback.print_exc(file=sys.stderr)
|
||||
return self.info
|
||||
|
||||
def readHandInfo(self, hand):
|
||||
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'))
|
||||
|
||||
def readPlayerStacks(self, hand):
|
||||
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'))
|
||||
|
||||
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 readCommunityCards(self, hand, street):
|
||||
m = self.re_Board.search(hand.streets[street])
|
||||
if street == 'FLOP':
|
||||
hand.setCommunityCards(street, m.group('CARDS').split(','))
|
||||
elif street in ('TURN','RIVER'):
|
||||
hand.setCommunityCards(street, [m.group('CARDS').split(',')[-1]])
|
||||
|
||||
def readAntes(self, hand):
|
||||
pass # ???
|
||||
|
||||
def readBringIn(self, hand):
|
||||
pass # ???
|
||||
|
||||
def readBlinds(self, hand):
|
||||
try:
|
||||
m = self.re_PostSB.search(hand.handText)
|
||||
hand.addBlind(self.playerNameFromSeatNo(m.group('PSEAT'), hand),
|
||||
'small blind', m.group('SB'))
|
||||
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__":
|
||||
c = Configuration.Config()
|
||||
e = CarbonPoker(c, "regression-test-files/carbon-poker/Niagara Falls (15245216).xml")
|
||||
e.processFile()
|
||||
print str(e)
|
||||
parser = OptionParser()
|
||||
parser.add_option("-i", "--input", dest="ipath", help="parse input hand history", default="-")
|
||||
parser.add_option("-o", "--output", dest="opath", help="output translation to", default="-")
|
||||
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)
|
||||
|
||||
|
|
|
@ -39,23 +39,36 @@ def calcStartCards(hand, player):
|
|||
|
||||
def twoStartCards(value1, suit1, value2, suit2):
|
||||
""" 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)
|
||||
If x > y then pair is suited, if x < y then unsuited"""
|
||||
if value1 < 2 or value2 < 2:
|
||||
If x > y then pair is suited, if x < y then unsuited
|
||||
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
|
||||
if value1 == value2: # pairs
|
||||
ret = (13 * (value2-2) + (value2-2) )
|
||||
elif value1 == value2: # pairs
|
||||
ret = (13 * (value2-2) + (value2-2) ) + 1
|
||||
elif suit1 == suit2:
|
||||
if value1 > value2:
|
||||
ret = 13 * (value1-2) + (value2-2)
|
||||
ret = 13 * (value1-2) + (value2-2) + 1
|
||||
else:
|
||||
ret = 13 * (value2-2) + (value1-2)
|
||||
ret = 13 * (value2-2) + (value1-2) + 1
|
||||
else:
|
||||
if value1 > value2:
|
||||
ret = 13 * (value2-2) + (value1-2)
|
||||
ret = 13 * (value2-2) + (value1-2) + 1
|
||||
else:
|
||||
ret = 13 * (value1-2) + (value2-2)
|
||||
ret = 13 * (value1-2) + (value2-2) + 1
|
||||
|
||||
# print "twoStartCards(", value1, suit1, value2, suit2, ")=", ret
|
||||
return ret
|
||||
|
@ -66,8 +79,8 @@ def twoStartCardString(card):
|
|||
ret = 'xx'
|
||||
if card > 0:
|
||||
s = ('2', '3', '4', '5', '6', '7', '8', '9', 'T', 'J', 'Q', 'K', 'A')
|
||||
x = card / 13
|
||||
y = card - 13 * x
|
||||
x = (card-1) / 13
|
||||
y = (card-1) - 13 * x
|
||||
if x == y: ret = s[x] + s[y]
|
||||
elif x > y: ret = s[x] + s[y] + 's'
|
||||
else: ret = s[y] + s[x] + 'o'
|
||||
|
@ -127,10 +140,10 @@ def valueSuitFromCard(card):
|
|||
return suitFromCardList[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,
|
||||
'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,
|
||||
'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
|
||||
'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': 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,
|
||||
' ': 0
|
||||
}
|
||||
|
||||
def encodeCard(cardString):
|
||||
|
|
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
|
|
@ -31,12 +31,17 @@ import inspect
|
|||
import string
|
||||
import traceback
|
||||
import shutil
|
||||
import locale
|
||||
import xml.dom.minidom
|
||||
from xml.dom.minidom import Node
|
||||
|
||||
import logging, logging.config
|
||||
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
|
||||
# Also used in other modules that use logging.
|
||||
|
@ -51,60 +56,100 @@ def get_default_config_path():
|
|||
return config_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
|
||||
return os.path.dirname(sys.executable)
|
||||
else:
|
||||
pathname = os.path.dirname(sys.argv[0])
|
||||
return os.path.abspath(pathname)
|
||||
return os.path.dirname(sys.path[0]) # should be path to /fpdb
|
||||
|
||||
def get_config(file_name, fallback = True):
|
||||
"""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
|
||||
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
|
||||
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
|
||||
if os.path.exists(config_path):
|
||||
return config_path
|
||||
return (config_path,False)
|
||||
|
||||
# No file found
|
||||
if not fallback:
|
||||
return False
|
||||
return (False,False)
|
||||
|
||||
# OK, fall back to the .example file, should be in the start dir
|
||||
if os.path.exists(file_name + ".example"):
|
||||
try:
|
||||
shutil.copyfile(file_name + ".example", file_name)
|
||||
print "No %s found, using %s.example.\n" % (file_name, file_name)
|
||||
print "A %s file has been created. You will probably have to edit it." % file_name
|
||||
sys.stderr.write("No %s found, using %s.example.\n" % (file_name, file_name) )
|
||||
print ""
|
||||
check_dir(default_dir)
|
||||
shutil.copyfile(file_name + ".example", config_path)
|
||||
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:
|
||||
print "No %s found, cannot fall back. Exiting.\n" % file_name
|
||||
sys.stderr.write("No %s found, cannot fall back. Exiting.\n" % file_name)
|
||||
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()
|
||||
return file_name
|
||||
else:
|
||||
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.exit()
|
||||
return (file_name,True)
|
||||
|
||||
def get_logger(file_name, config = "config", fallback = False):
|
||||
conf = get_config(file_name, fallback = fallback)
|
||||
if conf:
|
||||
def get_logger(file_name, config = "config", fallback = False, log_dir=None, log_file=None):
|
||||
(conf_file,copied) = get_config(file_name, fallback = fallback)
|
||||
|
||||
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:
|
||||
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.debug("%s logger initialised" % config)
|
||||
return log
|
||||
except:
|
||||
pass
|
||||
|
||||
log = logging.basicConfig()
|
||||
log = logging.basicConfig(filename=file, level=logging.INFO)
|
||||
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
|
||||
|
||||
# find a logging.conf file and set up logging
|
||||
log = get_logger("logging.conf")
|
||||
def check_dir(path, create = True):
|
||||
"""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
|
||||
|
@ -112,10 +157,6 @@ log = get_logger("logging.conf")
|
|||
APPLICATION_NAME_SHORT = 'fpdb'
|
||||
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_SQLITE = 'sqlite'
|
||||
DATABASE_TYPE_MYSQL = 'mysql'
|
||||
|
@ -125,7 +166,20 @@ DATABASE_TYPES = (
|
|||
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):
|
||||
|
@ -399,11 +453,11 @@ class Tv:
|
|||
|
||||
class Config:
|
||||
def __init__(self, file = None, dbname = ''):
|
||||
|
||||
# "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
|
||||
|
||||
# self.default_config_path = self.get_default_config_path()
|
||||
self.example_copy = False
|
||||
if file is not None: # config file path passed in
|
||||
file = os.path.expanduser(file)
|
||||
if not os.path.exists(file):
|
||||
|
@ -411,7 +465,15 @@ class Config:
|
|||
sys.stderr.write("Configuration file %s not found. Using defaults." % (file))
|
||||
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_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
|
||||
# If using the example, we'll edit it later
|
||||
|
@ -427,7 +489,6 @@ class Config:
|
|||
sys.exit()
|
||||
|
||||
self.doc = doc
|
||||
self.file = file
|
||||
self.supported_sites = {}
|
||||
self.supported_games = {}
|
||||
self.supported_databases = {} # databaseName --> Database instance
|
||||
|
@ -564,7 +625,11 @@ class Config:
|
|||
def save(self, file = None):
|
||||
if file is None:
|
||||
file = self.file
|
||||
shutil.move(file, file+".backup")
|
||||
try:
|
||||
shutil.move(file, file+".backup")
|
||||
except:
|
||||
pass
|
||||
|
||||
with open(file, 'w') as f:
|
||||
self.doc.writexml(f)
|
||||
|
||||
|
@ -629,6 +694,10 @@ class Config:
|
|||
db['db-backend'] = 3
|
||||
elif self.supported_databases[name].db_server== DATABASE_TYPE_SQLITE:
|
||||
db['db-backend'] = 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 db
|
||||
|
@ -977,3 +1046,9 @@ if __name__== "__main__":
|
|||
PrettyPrint(site_node, stream=sys.stdout, encoding="utf-8")
|
||||
except:
|
||||
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()
|
||||
|
|
1489
pyfpdb/Database.py
1489
pyfpdb/Database.py
File diff suppressed because it is too large
Load Diff
|
@ -48,32 +48,33 @@ class DerivedStats():
|
|||
self.handsplayers[player[1]]['sawShowdown'] = False
|
||||
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):
|
||||
self.handsplayers[player[1]]['street%dCalls' % i] = 0
|
||||
self.handsplayers[player[1]]['street%dBets' % i] = 0
|
||||
for i in range(1,5):
|
||||
self.handsplayers[player[1]]['street%dCBChance' %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.
|
||||
self.handsplayers[player[1]]['position'] = 2
|
||||
self.handsplayers[player[1]]['tourneyTypeId'] = 1
|
||||
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):
|
||||
self.handsplayers[player[1]]['otherRaisedStreet%d' %i] = False
|
||||
self.handsplayers[player[1]]['foldToOtherRaisedStreet%d' %i] = False
|
||||
self.handsplayers[player[1]]['foldToStreet%dCBChance' %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.assembleHandsPlayers(self.hand)
|
||||
|
@ -174,33 +175,62 @@ class DerivedStats():
|
|||
self.handsplayers[player[1]]['card%s' % (i+1)] = Card.encodeCard(card)
|
||||
self.handsplayers[player[1]]['startCards'] = Card.calcStartCards(hand, player[1])
|
||||
|
||||
# position,
|
||||
#Stud 3rd street card test
|
||||
# denny501: brings in for $0.02
|
||||
# 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
|
||||
|
||||
self.setPositions(hand)
|
||||
self.calcCheckCallRaise(hand)
|
||||
self.calc34BetStreet0(hand)
|
||||
self.calcSteals(hand)
|
||||
# Additional stats
|
||||
# 3betSB, 3betBB
|
||||
# Squeeze, Ratchet?
|
||||
|
||||
|
||||
def getPosition(hand, seat):
|
||||
"""Returns position value like 'B', 'S', 0, 1, ..."""
|
||||
# Flop/Draw games with blinds
|
||||
# Need a better system???
|
||||
# -2 BB - B (all)
|
||||
# -1 SB - S (all)
|
||||
# 0 Button
|
||||
# 1 Cutoff
|
||||
# 2 Hijack
|
||||
def setPositions(self, hand):
|
||||
"""Sets the position for each player in HandsPlayers
|
||||
any blinds are negative values, and the last person to act on the
|
||||
first betting round is 0
|
||||
NOTE: HU, both values are negative for non-stud games
|
||||
NOTE2: I've never seen a HU stud match"""
|
||||
# The position calculation must be done differently for Stud and other games as
|
||||
# Stud the 'blind' acts first - in all other games they act last.
|
||||
#
|
||||
#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'] = str(i)
|
||||
|
||||
def assembleHudCache(self, hand):
|
||||
# No real work to be done - HandsPlayers data already contains the correct info
|
||||
pass
|
||||
|
||||
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
|
||||
|
||||
# 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['playersAtStreet2'] = 0
|
||||
|
@ -231,23 +262,31 @@ class DerivedStats():
|
|||
self.hands['playersAtStreet4'] = 0
|
||||
self.hands['playersAtShowdown'] = 0
|
||||
|
||||
alliners = set()
|
||||
for (i, street) in enumerate(hand.actionStreets[2:]):
|
||||
actors = set()
|
||||
for action in hand.actions[street]:
|
||||
if len(action) > 2 and action[-1]: # allin
|
||||
alliners.add(action[0])
|
||||
actors.add(action[0])
|
||||
if len(actors)==0 and len(alliners)<2:
|
||||
alliners = set()
|
||||
self.hands['playersAtStreet%d' % (i+1)] = len(set.union(alliners, actors))
|
||||
# alliners = set()
|
||||
# for (i, street) in enumerate(hand.actionStreets[2:]):
|
||||
# actors = set()
|
||||
# for action in hand.actions[street]:
|
||||
# if len(action) > 2 and action[-1]: # allin
|
||||
# alliners.add(action[0])
|
||||
# actors.add(action[0])
|
||||
# if len(actors)==0 and len(alliners)<2:
|
||||
# alliners = set()
|
||||
# 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]]
|
||||
pas = set.union(self.pfba(actions) - self.pfba(actions, l=('folds',)), alliners)
|
||||
self.hands['playersAtShowdown'] = len(pas)
|
||||
p_in = set(x[1] for x in hand.players)
|
||||
for (i, street) in enumerate(hand.actionStreets):
|
||||
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:
|
||||
for player in pas:
|
||||
for player in p_in:
|
||||
self.handsplayers[player]['sawShowdown'] = True
|
||||
|
||||
def streetXRaises(self, hand):
|
||||
|
@ -262,13 +301,65 @@ class DerivedStats():
|
|||
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]))
|
||||
|
||||
def calcCBets(self, hand):
|
||||
# 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
|
||||
def calcSteals(self, hand):
|
||||
"""Fills stealAttempt(Chance|ed, fold(Bb|Sb)ToSteal(Chance|)
|
||||
|
||||
Steal attempt - open raise on positions 1 0 S - i.e. MP3, CO, BU, SB
|
||||
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'] = bet_level == 2
|
||||
self.handsplayers[pname]['street0_4BChance'] = bet_level == 3
|
||||
self.handsplayers[pname]['street0_3BDone'] = 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'
|
||||
# came there
|
||||
#for i, street in enumerate(hand.actionStreets[2:], start=1):
|
||||
|
@ -280,6 +371,29 @@ class DerivedStats():
|
|||
if chance == True:
|
||||
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):
|
||||
pas = set()
|
||||
for act in hand.actions[hand.actionStreets[i+1]]:
|
||||
|
@ -293,11 +407,13 @@ class DerivedStats():
|
|||
|
||||
def aggr(self, hand, i):
|
||||
aggrers = set()
|
||||
for act in hand.actions[hand.actionStreets[i]]:
|
||||
if act[1] in ('completes', 'raises'):
|
||||
# Growl - actionStreets contains 'BLINDSANTES', which isn't actually an action street
|
||||
for act in hand.actions[hand.actionStreets[i+1]]:
|
||||
if act[1] in ('completes', 'bets', 'raises'):
|
||||
aggrers.add(act[0])
|
||||
|
||||
for player in hand.players:
|
||||
#print "DEBUG: actionStreet[%s]: %s" %(hand.actionStreets[i+1], i)
|
||||
if player[1] in aggrers:
|
||||
self.handsplayers[player[1]]['street%sAggr' % i] = True
|
||||
else:
|
||||
|
@ -333,6 +449,44 @@ class DerivedStats():
|
|||
players.add(action[0])
|
||||
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):
|
||||
"""Returns true if there were no bets before the specified players turn, false otherwise"""
|
||||
betOrRaise = False
|
||||
|
@ -345,6 +499,7 @@ class DerivedStats():
|
|||
break
|
||||
return betOrRaise
|
||||
|
||||
|
||||
def betStreet(self, street, player):
|
||||
"""Returns true if player bet/raised the street as their first action"""
|
||||
betOrRaise = False
|
||||
|
@ -355,12 +510,3 @@ class DerivedStats():
|
|||
break
|
||||
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_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_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_TourneyInfoFromFilename = re.compile(ur".*TID_(?P<TOURNO>[0-9]+)-(?P<TABLE>[0-9]+)\.txt")
|
||||
|
||||
|
||||
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_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_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_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)
|
||||
|
||||
def readSupportedGames(self):
|
||||
|
@ -66,7 +67,9 @@ class Everleaf(HandHistoryConverter):
|
|||
["ring", "hold", "pl"],
|
||||
["ring", "hold", "fl"],
|
||||
["ring", "studhi", "fl"],
|
||||
["ring", "omahahi", "pl"]
|
||||
["ring", "omahahi", "pl"],
|
||||
["ring", "omahahilo", "pl"],
|
||||
["tour", "hold", "nl"]
|
||||
]
|
||||
|
||||
def determineGameType(self, handText):
|
||||
|
@ -138,6 +141,12 @@ or None if we fail to get the info """
|
|||
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
|
||||
|
||||
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
|
||||
# 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
|
||||
|
@ -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=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__":
|
||||
|
@ -305,4 +320,3 @@ if __name__ == "__main__":
|
|||
logging.basicConfig(filename=LOG_FILENAME,level=options.verbosity)
|
||||
|
||||
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):
|
||||
return repr(self.value +" " + self.errmsg)
|
||||
|
||||
class DuplicateError(FpdbError):
|
||||
class FpdbHandError(FpdbError):
|
||||
pass
|
||||
|
||||
class FpdbHandDuplicate(FpdbHandError):
|
||||
pass
|
||||
|
||||
class FpdbHandPartial(FpdbHandError):
|
||||
pass
|
||||
|
|
|
@ -26,21 +26,47 @@ from time import *
|
|||
import gobject
|
||||
#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 fpdb_db
|
||||
import FpdbSQLQueries
|
||||
import Database
|
||||
import SQL
|
||||
import Charset
|
||||
|
||||
|
||||
class Filters(threading.Thread):
|
||||
def __init__(self, db, config, qdict, display = {}, debug=True):
|
||||
# config and qdict are now redundant
|
||||
self.debug = debug
|
||||
#print "start of GraphViewer constructor"
|
||||
self.db = db
|
||||
self.cursor = db.cursor
|
||||
self.sql = db.sql
|
||||
self.conf = db.config
|
||||
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.games = {}
|
||||
self.limits = {}
|
||||
|
@ -50,14 +76,14 @@ class Filters(threading.Thread):
|
|||
self.heroes = {}
|
||||
self.boxes = {}
|
||||
|
||||
# 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'
|
||||
,'limitstitle':'Limits:', 'seatstitle':'Number of Players:'
|
||||
,'groupstitle':'Grouping:', 'posnshow':'Show Position Stats:'
|
||||
,'groupsall':'All Players'
|
||||
,'limitsFL':'FL', 'limitsNL':'NL', 'ring':'Ring', 'tour':'Tourney'
|
||||
}
|
||||
for site in self.conf.get_supported_sites():
|
||||
#Get db site id for filtering later
|
||||
self.cursor.execute(self.sql.query['getSiteId'], (site,))
|
||||
result = self.db.cursor.fetchall()
|
||||
if len(result) == 1:
|
||||
self.siteid[site] = result[0][0]
|
||||
else:
|
||||
print "Either 0 or more than one site matched - EEK"
|
||||
|
||||
# For use in date ranges.
|
||||
self.start_date = gtk.Entry(max=12)
|
||||
|
@ -69,34 +95,28 @@ class Filters(threading.Thread):
|
|||
self.sbGroups = {}
|
||||
self.numHands = 0
|
||||
|
||||
# Outer Packing box
|
||||
self.mainVBox = gtk.VBox(False, 0)
|
||||
|
||||
playerFrame = gtk.Frame("Hero:")
|
||||
playerFrame = gtk.Frame()
|
||||
playerFrame.set_label_align(0.0, 0.0)
|
||||
vbox = gtk.VBox(False, 0)
|
||||
|
||||
self.fillPlayerFrame(vbox, self.display)
|
||||
playerFrame.add(vbox)
|
||||
self.boxes['player'] = vbox
|
||||
|
||||
sitesFrame = gtk.Frame("Sites:")
|
||||
sitesFrame = gtk.Frame()
|
||||
sitesFrame.set_label_align(0.0, 0.0)
|
||||
vbox = gtk.VBox(False, 0)
|
||||
|
||||
self.fillSitesFrame(vbox)
|
||||
sitesFrame.add(vbox)
|
||||
self.boxes['sites'] = vbox
|
||||
|
||||
# Game types
|
||||
gamesFrame = gtk.Frame("Games:")
|
||||
gamesFrame = gtk.Frame()
|
||||
gamesFrame.set_label_align(0.0, 0.0)
|
||||
gamesFrame.show()
|
||||
vbox = gtk.VBox(False, 0)
|
||||
|
||||
self.fillGamesFrame(vbox)
|
||||
gamesFrame.add(vbox)
|
||||
self.boxes['games'] = vbox
|
||||
|
||||
# Limits
|
||||
limitsFrame = gtk.Frame()
|
||||
|
@ -107,6 +127,7 @@ class Filters(threading.Thread):
|
|||
self.cbAllLimits = None
|
||||
self.cbFL = None
|
||||
self.cbNL = None
|
||||
self.cbPL = None
|
||||
self.rb = {} # radio buttons for ring/tour
|
||||
self.type = None # ring/tour
|
||||
self.types = {} # list of all ring/tour values
|
||||
|
@ -132,14 +153,13 @@ class Filters(threading.Thread):
|
|||
groupsFrame.add(vbox)
|
||||
|
||||
# Date
|
||||
dateFrame = gtk.Frame("Date:")
|
||||
dateFrame = gtk.Frame()
|
||||
dateFrame.set_label_align(0.0, 0.0)
|
||||
dateFrame.show()
|
||||
vbox = gtk.VBox(False, 0)
|
||||
|
||||
self.fillDateFrame(vbox)
|
||||
dateFrame.add(vbox)
|
||||
self.boxes['date'] = vbox
|
||||
|
||||
# Buttons
|
||||
self.Button1=gtk.Button("Unnamed 1")
|
||||
|
@ -180,6 +200,17 @@ class Filters(threading.Thread):
|
|||
if "Button2" not in self.display or self.display["Button2"] == False:
|
||||
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)
|
||||
|
||||
def get_vbox(self):
|
||||
"""returns the vbox of this thread"""
|
||||
return self.mainVBox
|
||||
|
@ -191,6 +222,9 @@ class Filters(threading.Thread):
|
|||
def getSites(self):
|
||||
return self.sites
|
||||
|
||||
def getGames(self):
|
||||
return self.games
|
||||
|
||||
def getSiteIds(self):
|
||||
return self.siteid
|
||||
|
||||
|
@ -222,24 +256,29 @@ class Filters(threading.Thread):
|
|||
|
||||
def registerButton1Name(self, title):
|
||||
self.Button1.set_label(title)
|
||||
self.label['button1'] = title
|
||||
|
||||
def registerButton1Callback(self, callback):
|
||||
self.Button1.connect("clicked", callback, "clicked")
|
||||
self.Button1.set_sensitive(True)
|
||||
self.callback['button1'] = callback
|
||||
|
||||
def registerButton2Name(self, title):
|
||||
self.Button2.set_label(title)
|
||||
self.label['button2'] = title
|
||||
|
||||
def registerButton2Callback(self, callback):
|
||||
self.Button2.connect("clicked", callback, "clicked")
|
||||
self.Button2.set_sensitive(True)
|
||||
self.callback['button2'] = callback
|
||||
|
||||
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):
|
||||
log.debug('add:"%s"' % player)
|
||||
label = gtk.Label(site +" id:")
|
||||
hbox.pack_start(label, False, False, 0)
|
||||
hbox.pack_start(label, False, False, 3)
|
||||
|
||||
pname = gtk.Entry()
|
||||
pname.set_text(player)
|
||||
|
@ -253,22 +292,27 @@ class Filters(threading.Thread):
|
|||
liststore = gtk.ListStore(gobject.TYPE_STRING)
|
||||
completion.set_model(liststore)
|
||||
completion.set_text_column(0)
|
||||
names = self.db.get_player_names(self.conf) # (config=self.conf, site_id=None, like_player_name="%")
|
||||
for n in names:
|
||||
liststore.append(n)
|
||||
names = self.db.get_player_names(self.conf, self.siteid[site]) # (config=self.conf, site_id=None, like_player_name="%")
|
||||
for n in names: # list of single-element "tuples"
|
||||
_n = Charset.to_gui(n[0])
|
||||
_nt = (_n, )
|
||||
liststore.append(_nt)
|
||||
|
||||
self.__set_hero_name(pname, site)
|
||||
|
||||
def __set_hero_name(self, w, site):
|
||||
self.heroes[site] = w.get_text()
|
||||
# print "DEBUG: setting heroes[%s]: %s"%(site, self.heroes[site])
|
||||
_name = w.get_text()
|
||||
# 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):
|
||||
try:
|
||||
self.numHands = int(w.get_text())
|
||||
except:
|
||||
self.numHands = 0
|
||||
# print "DEBUG: setting numHands:", self.numHands
|
||||
# log.debug("setting numHands:", self.numHands)
|
||||
|
||||
def createSiteLine(self, hbox, site):
|
||||
cb = gtk.CheckButton(site)
|
||||
|
@ -280,6 +324,7 @@ class Filters(threading.Thread):
|
|||
cb = gtk.CheckButton(game)
|
||||
cb.connect('clicked', self.__set_game_select, game)
|
||||
hbox.pack_start(cb, False, False, 0)
|
||||
cb.set_active(True)
|
||||
|
||||
def createLimitLine(self, hbox, limit, ltext):
|
||||
cb = gtk.CheckButton(str(ltext))
|
||||
|
@ -292,18 +337,18 @@ class Filters(threading.Thread):
|
|||
def __set_site_select(self, w, site):
|
||||
#print 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):
|
||||
#print 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):
|
||||
#print w.get_active()
|
||||
self.limits[limit] = w.get_active()
|
||||
print "self.limit[%s] set to %s" %(limit, self.limits[limit])
|
||||
if limit.isdigit() or (len(limit) > 2 and limit[-2:] == 'nl'):
|
||||
log.debug("self.limit[%s] set to %s" %(limit, self.limits[limit]))
|
||||
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.cbNoLimits is not None:
|
||||
self.cbNoLimits.set_active(False)
|
||||
|
@ -314,9 +359,12 @@ class Filters(threading.Thread):
|
|||
if limit.isdigit():
|
||||
if self.cbFL is not None:
|
||||
self.cbFL.set_active(False)
|
||||
else:
|
||||
elif (len(limit) > 2 and (limit[-2:] == 'nl')):
|
||||
if self.cbNL is not None:
|
||||
self.cbNL.set_active(False)
|
||||
else:
|
||||
if self.cbPL is not None:
|
||||
self.cbPL.set_active(False)
|
||||
elif limit == "all":
|
||||
if self.limits[limit]:
|
||||
#for cb in self.cbLimits.values():
|
||||
|
@ -325,6 +373,8 @@ class Filters(threading.Thread):
|
|||
self.cbFL.set_active(True)
|
||||
if self.cbNL is not None:
|
||||
self.cbNL.set_active(True)
|
||||
if self.cbPL is not None:
|
||||
self.cbPL.set_active(True)
|
||||
elif limit == "none":
|
||||
if self.limits[limit]:
|
||||
for cb in self.cbLimits.values():
|
||||
|
@ -333,6 +383,8 @@ class Filters(threading.Thread):
|
|||
self.cbNL.set_active(False)
|
||||
if self.cbFL is not None:
|
||||
self.cbFL.set_active(False)
|
||||
if self.cbPL is not None:
|
||||
self.cbPL.set_active(False)
|
||||
elif limit == "fl":
|
||||
if not self.limits[limit]:
|
||||
# only toggle all fl limits off if they are all currently on
|
||||
|
@ -381,11 +433,39 @@ class Filters(threading.Thread):
|
|||
if self.limits[limit]:
|
||||
if not found[self.type]:
|
||||
if self.type == 'ring':
|
||||
self.rb['tour'].set_active(True)
|
||||
if 'tour' in self.rb:
|
||||
self.rb['tour'].set_active(True)
|
||||
elif self.type == 'tour':
|
||||
self.rb['ring'].set_active(True)
|
||||
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)
|
||||
elif limit == "ring":
|
||||
print "set", limit, "to", self.limits[limit]
|
||||
log.debug("set", limit, "to", self.limits[limit])
|
||||
if self.limits[limit]:
|
||||
self.type = "ring"
|
||||
for cb in self.cbLimits.values():
|
||||
|
@ -393,7 +473,7 @@ class Filters(threading.Thread):
|
|||
if self.types[cb.get_children()[0].get_text()] == 'tour':
|
||||
cb.set_active(False)
|
||||
elif limit == "tour":
|
||||
print "set", limit, "to", self.limits[limit]
|
||||
log.debug( "set", limit, "to", self.limits[limit] )
|
||||
if self.limits[limit]:
|
||||
self.type = "tour"
|
||||
for cb in self.cbLimits.values():
|
||||
|
@ -404,24 +484,38 @@ class Filters(threading.Thread):
|
|||
def __set_seat_select(self, w, seat):
|
||||
#print "__set_seat_select: seat =", seat, "active =", 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):
|
||||
#print "__set_seat_select: seat =", seat, "active =", 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):
|
||||
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():
|
||||
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
|
||||
self.createPlayerLine(hBox, site, player)
|
||||
_pname = Charset.to_gui(player)
|
||||
self.createPlayerLine(hBox, site, _pname)
|
||||
|
||||
if "GroupsAll" in display and display["GroupsAll"] == True:
|
||||
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.connect('clicked', self.__set_group_select, 'allplayers')
|
||||
hbox.pack_start(cb, False, False, 0)
|
||||
|
@ -437,30 +531,64 @@ class Filters(threading.Thread):
|
|||
phands.set_width_chars(8)
|
||||
hbox.pack_start(phands, False, False, 0)
|
||||
phands.connect("changed", self.__set_num_hands, site)
|
||||
top_hbox.pack_start(showb, expand=False, padding=1)
|
||||
|
||||
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():
|
||||
hbox = gtk.HBox(False, 0)
|
||||
vbox.pack_start(hbox, False, True, 0)
|
||||
vbox1.pack_start(hbox, False, True, 0)
|
||||
self.createSiteLine(hbox, site)
|
||||
#Get db site id for filtering later
|
||||
self.cursor.execute(self.sql.query['getSiteId'], (site,))
|
||||
result = self.db.cursor.fetchall()
|
||||
if len(result) == 1:
|
||||
self.siteid[site] = result[0][0]
|
||||
else:
|
||||
print "Either 0 or more than one site matched - EEK"
|
||||
#self.cursor.execute(self.sql.query['getSiteId'], (site,))
|
||||
#result = self.db.cursor.fetchall()
|
||||
#if len(result) == 1:
|
||||
# self.siteid[site] = result[0][0]
|
||||
#else:
|
||||
# print "Either 0 or more than one site matched - EEK"
|
||||
|
||||
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'])
|
||||
result = self.db.cursor.fetchall()
|
||||
if len(result) >= 1:
|
||||
for line in result:
|
||||
hbox = gtk.HBox(False, 0)
|
||||
vbox.pack_start(hbox, False, True, 0)
|
||||
vbox1.pack_start(hbox, False, True, 0)
|
||||
self.createGameLine(hbox, line[0])
|
||||
else:
|
||||
print "INFO: No games returned from database"
|
||||
log.info("No games returned from database")
|
||||
|
||||
def fillLimitsFrame(self, vbox, display):
|
||||
top_hbox = gtk.HBox(False, 0)
|
||||
|
@ -476,10 +604,10 @@ class Filters(threading.Thread):
|
|||
vbox.pack_start(vbox1, False, False, 0)
|
||||
self.boxes['limits'] = vbox1
|
||||
|
||||
self.cursor.execute(self.sql.query['getLimits2'])
|
||||
self.cursor.execute(self.sql.query['getLimits3'])
|
||||
# selects limitType, bigBlind
|
||||
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:
|
||||
hbox = gtk.HBox(True, 0)
|
||||
|
@ -497,14 +625,18 @@ class Filters(threading.Thread):
|
|||
vbox2.pack_start(hbox, False, False, 0)
|
||||
else:
|
||||
vbox3.pack_start(hbox, False, False, 0)
|
||||
if line[1] == 'fl':
|
||||
name = str(line[2])
|
||||
found['fl'] = True
|
||||
else:
|
||||
name = str(line[2])+line[1]
|
||||
found['nl'] = True
|
||||
self.cbLimits[name] = self.createLimitLine(hbox, name, name)
|
||||
self.types[name] = line[0]
|
||||
if True: #line[0] == 'ring':
|
||||
if line[1] == 'fl':
|
||||
name = str(line[2])
|
||||
found['fl'] = True
|
||||
elif line[1] == 'pl':
|
||||
name = str(line[2])+line[1]
|
||||
found['pl'] = True
|
||||
else:
|
||||
name = str(line[2])+line[1]
|
||||
found['nl'] = True
|
||||
self.cbLimits[name] = self.createLimitLine(hbox, name, name)
|
||||
self.types[name] = line[0]
|
||||
found[line[0]] = True # type is ring/tour
|
||||
self.type = line[0] # if only one type, set it now
|
||||
if "LimitSep" in display and display["LimitSep"] == True and len(result) >= 2:
|
||||
|
@ -532,9 +664,13 @@ class Filters(threading.Thread):
|
|||
hbox = gtk.HBox(False, 0)
|
||||
vbox3.pack_start(hbox, False, False, 0)
|
||||
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
|
||||
else:
|
||||
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']:
|
||||
rb1 = gtk.RadioButton(None, self.filterText['ring'])
|
||||
|
@ -645,8 +781,22 @@ class Filters(threading.Thread):
|
|||
|
||||
def fillDateFrame(self, vbox):
|
||||
# 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()
|
||||
vbox.pack_start(hbox, False, True, 0)
|
||||
vbox1.pack_start(hbox, False, True, 0)
|
||||
|
||||
lbl_start = gtk.Label('From:')
|
||||
|
||||
|
@ -660,7 +810,7 @@ class Filters(threading.Thread):
|
|||
|
||||
#New row for end date
|
||||
hbox = gtk.HBox()
|
||||
vbox.pack_start(hbox, False, True, 0)
|
||||
vbox1.pack_start(hbox, False, True, 0)
|
||||
|
||||
lbl_end = gtk.Label(' To:')
|
||||
btn_end = gtk.Button()
|
||||
|
@ -676,10 +826,13 @@ class Filters(threading.Thread):
|
|||
|
||||
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):
|
||||
if "Limits" not in self.display or self.display["Limits"] == False:
|
||||
self.boxes[entry].hide()
|
||||
elif self.boxes[entry].props.visible:
|
||||
if self.boxes[entry].props.visible:
|
||||
self.boxes[entry].hide()
|
||||
widget.set_label("show")
|
||||
else:
|
||||
|
@ -740,10 +893,10 @@ def main(argv=None):
|
|||
config = Configuration.Config()
|
||||
db = None
|
||||
|
||||
db = fpdb_db.fpdb_db()
|
||||
db = Database.Database()
|
||||
db.do_connect(config)
|
||||
|
||||
qdict = FpdbSQLQueries.FpdbSQLQueries(db.get_backend_name())
|
||||
qdict = SQL.SQL(db.get_backend_name())
|
||||
|
||||
i = Filters(db, config, qdict)
|
||||
main_window = gtk.Window()
|
||||
|
|
|
@ -18,12 +18,10 @@
|
|||
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
########################################################################
|
||||
|
||||
import sys
|
||||
import logging
|
||||
from HandHistoryConverter import *
|
||||
|
||||
# Fulltilt HH Format converter
|
||||
# TODO: cat tourno and table to make table name for tournaments
|
||||
|
||||
class Fulltilt(HandHistoryConverter):
|
||||
|
||||
|
@ -67,8 +65,8 @@ class Fulltilt(HandHistoryConverter):
|
|||
(\s\((?P<TURBO>Turbo)\))?)|(?P<UNREADABLE_INFO>.+))
|
||||
''', re.VERBOSE)
|
||||
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_TourneyPlayerInfo = 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>.{3,15}) \(\$(?P<CASH>[,.0-9]+)\)$', re.MULTILINE)
|
||||
re_TourneyPlayerInfo = re.compile('Seat (?P<SEAT>[0-9]+): (?P<PNAME>.{3,15}) \(\$?(?P<CASH>[,.0-9]+)\)(, is sitting out)?$', re.MULTILINE)
|
||||
re_Board = re.compile(r"\[(?P<CARDS>.+)\]")
|
||||
|
||||
#static regex for tourney purpose
|
||||
|
@ -139,7 +137,7 @@ class Fulltilt(HandHistoryConverter):
|
|||
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_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):
|
||||
return [["ring", "hold", "nl"],
|
||||
|
@ -191,7 +189,6 @@ class Fulltilt(HandHistoryConverter):
|
|||
if mg['TOURNO'] is None: info['type'] = "ring"
|
||||
else: info['type'] = "tour"
|
||||
# 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
|
||||
|
||||
def readHandInfo(self, hand):
|
||||
|
@ -258,15 +255,16 @@ class Fulltilt(HandHistoryConverter):
|
|||
#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')),
|
||||
##int(m.group('HR')), int(m.group('MIN')), int(m.group('SEC')))
|
||||
#FIXME: hand.buttonpos = int(m.group('BUTTON'))
|
||||
|
||||
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" :
|
||||
m = self.re_PlayerInfo.finditer(hand.handText)
|
||||
m = self.re_PlayerInfo.finditer(pre)
|
||||
else: #if hand.gametype['type'] == "tour"
|
||||
m = self.re_TourneyPlayerInfo.finditer(hand.handText)
|
||||
m = self.re_TourneyPlayerInfo.finditer(pre)
|
||||
|
||||
players = []
|
||||
for a in m:
|
||||
hand.addPlayer(int(a.group('SEAT')), a.group('PNAME'), a.group('CASH'))
|
||||
|
||||
|
@ -392,9 +390,10 @@ class Fulltilt(HandHistoryConverter):
|
|||
def readShownCards(self,hand):
|
||||
for m in self.re_ShownCards.finditer(hand.handText):
|
||||
if m.group('CARDS') is not None:
|
||||
cards = m.group('CARDS')
|
||||
cards = cards.split(' ')
|
||||
hand.addShownCards(cards=cards, player=m.group('PNAME'))
|
||||
if m.group('ACT'):
|
||||
hand.addShownCards(cards=m.group('CARDS').split(' '), player=m.group('PNAME'), shown = False, mucked = True)
|
||||
else:
|
||||
hand.addShownCards(cards=m.group('CARDS').split(' '), player=m.group('PNAME'), shown = True, mucked = False)
|
||||
|
||||
def guessMaxSeats(self, hand):
|
||||
"""Return a guess at max_seats when not specified in HH."""
|
||||
|
@ -422,7 +421,6 @@ class Fulltilt(HandHistoryConverter):
|
|||
hand.mixed = self.mixes[m.groupdict()['MIXED']]
|
||||
|
||||
def readSummaryInfo(self, summaryInfoList):
|
||||
starttime = time.time()
|
||||
self.status = True
|
||||
|
||||
m = re.search("Tournament Summary", summaryInfoList[0])
|
||||
|
@ -542,14 +540,14 @@ class Fulltilt(HandHistoryConverter):
|
|||
tourney.buyin = 100*Decimal(re.sub(u',', u'', "%s" % mg['BUYIN']))
|
||||
else :
|
||||
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']))
|
||||
if mg['FEE'] is not None:
|
||||
if tourney.fee is None:
|
||||
tourney.fee = 100*Decimal(re.sub(u',', u'', "%s" % mg['FEE']))
|
||||
else :
|
||||
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']))
|
||||
|
||||
if tourney.buyin is None:
|
||||
|
|
|
@ -193,8 +193,13 @@ class GuiAutoImport (threading.Thread):
|
|||
self.doAutoImportBool = True
|
||||
widget.set_label(u' _Stop Autoimport ')
|
||||
if self.pipe_to_hud is None:
|
||||
if os.name == 'nt':
|
||||
command = "python HUD_main.py " + self.settings['cl_options']
|
||||
if Configuration.FROZEN:
|
||||
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
|
||||
else:
|
||||
command = os.path.join(sys.path[0], 'HUD_main.py')
|
||||
|
@ -202,12 +207,14 @@ class GuiAutoImport (threading.Thread):
|
|||
bs = 1
|
||||
|
||||
try:
|
||||
print "opening pipe to HUD"
|
||||
self.pipe_to_hud = subprocess.Popen(command, bufsize=bs,
|
||||
stdin=subprocess.PIPE,
|
||||
universal_newlines=True)
|
||||
except:
|
||||
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:
|
||||
for site in self.input_settings:
|
||||
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()
|
||||
|
||||
config = Configuration.Config()
|
||||
# db = fpdb_db.fpdb_db()
|
||||
|
||||
settings = {}
|
||||
settings['minPrint'] = options.minPrint
|
||||
|
|
|
@ -30,7 +30,6 @@ import gtk
|
|||
import gobject
|
||||
|
||||
# fpdb/FreePokerTools modules
|
||||
import fpdb_simple
|
||||
import fpdb_import
|
||||
import Configuration
|
||||
import Exceptions
|
||||
|
@ -90,7 +89,8 @@ class GuiBulkImport():
|
|||
|
||||
for selection in selected:
|
||||
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()
|
||||
# try:
|
||||
(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'\
|
||||
% (stored, dups, partial, errs, ttime, (stored+0.0) / ttime)
|
||||
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:
|
||||
self.cb_dropindexes.set_sensitive(True)
|
||||
self.cb_dropindexes.set_active(0)
|
||||
|
@ -229,6 +232,10 @@ class GuiBulkImport():
|
|||
ypadding=0, yoptions=gtk.SHRINK)
|
||||
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
|
||||
self.lab_filter = gtk.Label("Site filter:")
|
||||
self.table.attach(self.lab_filter, 1, 2, 2, 3, xpadding=0, ypadding=0,
|
||||
|
@ -239,7 +246,17 @@ class GuiBulkImport():
|
|||
|
||||
# ComboBox - filter
|
||||
self.cbfilter = gtk.combo_box_new_text()
|
||||
disabled_sites = [] # move disabled sites to bottom of list
|
||||
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
|
||||
self.cbfilter.append_text(w)
|
||||
self.cbfilter.set_active(0)
|
||||
|
|
|
@ -27,7 +27,7 @@ from time import *
|
|||
|
||||
try:
|
||||
import matplotlib
|
||||
matplotlib.use('GTK')
|
||||
matplotlib.use('GTKCairo')
|
||||
from matplotlib.figure import Figure
|
||||
from matplotlib.backends.backend_gtk import FigureCanvasGTK as FigureCanvas
|
||||
from matplotlib.backends.backend_gtkagg import NavigationToolbar2GTKAgg as NavigationToolbar
|
||||
|
@ -44,6 +44,7 @@ except ImportError, inst:
|
|||
import fpdb_import
|
||||
import Database
|
||||
import Filters
|
||||
import Charset
|
||||
|
||||
class GuiGraphViewer (threading.Thread):
|
||||
|
||||
|
@ -137,6 +138,8 @@ class GuiGraphViewer (threading.Thread):
|
|||
heroes = self.filters.getHeroes()
|
||||
siteids = self.filters.getSiteIds()
|
||||
limits = self.filters.getLimits()
|
||||
games = self.filters.getGames()
|
||||
|
||||
for i in ('show', 'none'):
|
||||
if i in limits:
|
||||
limits.remove(i)
|
||||
|
@ -144,11 +147,10 @@ class GuiGraphViewer (threading.Thread):
|
|||
for site in sites:
|
||||
if sites[site] == True:
|
||||
sitenos.append(siteids[site])
|
||||
c = self.db.get_cursor()
|
||||
c.execute(self.sql.query['getPlayerId'], (heroes[site],))
|
||||
result = c.fetchall()
|
||||
if len(result) == 1:
|
||||
playerids.append( int(result[0][0]) )
|
||||
_hname = Charset.to_utf8(heroes[site])
|
||||
result = self.db.get_player_id(self.conf, site, _hname)
|
||||
if result is not None:
|
||||
playerids.append(int(result))
|
||||
|
||||
if not sitenos:
|
||||
#Should probably pop up here.
|
||||
|
@ -171,20 +173,49 @@ class GuiGraphViewer (threading.Thread):
|
|||
|
||||
#Get graph data from DB
|
||||
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)
|
||||
|
||||
self.ax.set_title("Profit graph for ring games")
|
||||
|
||||
#Set axis labels and grid overlay properites
|
||||
self.ax.set_xlabel("Hands", fontsize = 12)
|
||||
self.ax.set_ylabel("$", fontsize = 12)
|
||||
self.ax.grid(color='g', linestyle=':', linewidth=0.2)
|
||||
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
|
||||
print "No hands returned by graph query"
|
||||
#print "No hands returned by graph query"
|
||||
else:
|
||||
self.ax.set_title("Profit graph for ring games")
|
||||
#text = "Profit: $%.2f\nTotal Hands: %d" %(green[-1], len(green))
|
||||
#self.ax.annotate(text,
|
||||
# xy=(10, -10),
|
||||
|
@ -201,7 +232,6 @@ class GuiGraphViewer (threading.Thread):
|
|||
else:
|
||||
self.ax.legend(loc='best', fancybox=True, shadow=True, prop=FontProperties(size='smaller'))
|
||||
|
||||
|
||||
self.graphBox.add(self.canvas)
|
||||
self.canvas.show()
|
||||
self.canvas.draw()
|
||||
|
@ -212,7 +242,7 @@ class GuiGraphViewer (threading.Thread):
|
|||
|
||||
#end of def showClicked
|
||||
|
||||
def getRingProfitGraph(self, names, sites, limits):
|
||||
def getRingProfitGraph(self, names, sites, limits, games):
|
||||
tmp = self.sql.query['getRingProfitAllHandsPlayerIdSite']
|
||||
# print "DEBUG: getRingProfitGraph"
|
||||
start_date, end_date = self.filters.getDates()
|
||||
|
@ -224,6 +254,22 @@ class GuiGraphViewer (threading.Thread):
|
|||
sitetest = str(tuple(sites))
|
||||
#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()]
|
||||
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']
|
||||
|
@ -273,8 +319,8 @@ class GuiGraphViewer (threading.Thread):
|
|||
winnings = self.db.cursor.fetchall()
|
||||
self.db.rollback()
|
||||
|
||||
if winnings == ():
|
||||
return None
|
||||
if len(winnings) == 0:
|
||||
return (None, None, None)
|
||||
|
||||
green = map(lambda x:float(x[1]), winnings)
|
||||
blue = map(lambda x: float(x[1]) if x[2] == True else 0.0, winnings)
|
||||
|
|
|
@ -26,13 +26,13 @@ import gtk
|
|||
import gobject
|
||||
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
|
||||
EST_CHARS_PER_LINE = 150 # used to guesstimate number of lines in log file
|
||||
logfile = 'logging.out' # name of logfile
|
||||
|
||||
class GuiLogView:
|
||||
|
||||
|
@ -41,6 +41,7 @@ class GuiLogView:
|
|||
self.main_window = mainwin
|
||||
self.closeq = closeq
|
||||
|
||||
self.logfile = self.config.log_file # name of logfile
|
||||
self.dia = gtk.Dialog(title="Log Messages"
|
||||
,parent=None
|
||||
,flags=gtk.DIALOG_DESTROY_WITH_PARENT
|
||||
|
@ -117,10 +118,10 @@ class GuiLogView:
|
|||
self.listcols = []
|
||||
|
||||
# guesstimate number of lines in file
|
||||
if os.path.exists(logfile):
|
||||
stat_info = os.stat(logfile)
|
||||
if os.path.exists(self.logfile):
|
||||
stat_info = os.stat(self.logfile)
|
||||
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
|
||||
startline = 0
|
||||
|
@ -129,7 +130,7 @@ class GuiLogView:
|
|||
startline = lines - MAX_LINES
|
||||
|
||||
l = 0
|
||||
for line in open(logfile):
|
||||
for line in open(self.logfile):
|
||||
# eg line:
|
||||
# 2009-12-02 15:23:21,716 - config DEBUG config logger initialised
|
||||
l = l + 1
|
||||
|
|
|
@ -27,8 +27,8 @@ from time import time, strftime
|
|||
import Card
|
||||
import fpdb_import
|
||||
import Database
|
||||
import fpdb_db
|
||||
import Filters
|
||||
import Charset
|
||||
|
||||
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}
|
||||
|
@ -64,7 +64,7 @@ class GuiPlayerStats (threading.Thread):
|
|||
|
||||
filters_display = { "Heroes" : True,
|
||||
"Sites" : True,
|
||||
"Games" : False,
|
||||
"Games" : True,
|
||||
"Limits" : True,
|
||||
"LimitSep" : True,
|
||||
"LimitType" : True,
|
||||
|
@ -180,6 +180,7 @@ class GuiPlayerStats (threading.Thread):
|
|||
seats = self.filters.getSeats()
|
||||
groups = self.filters.getGroups()
|
||||
dates = self.filters.getDates()
|
||||
games = self.filters.getGames()
|
||||
sitenos = []
|
||||
playerids = []
|
||||
|
||||
|
@ -187,12 +188,10 @@ class GuiPlayerStats (threading.Thread):
|
|||
for site in sites:
|
||||
if sites[site] == True:
|
||||
sitenos.append(siteids[site])
|
||||
# Nasty hack to deal with multiple sites + same player name -Eric
|
||||
que = self.sql.query['getPlayerId'] + " AND siteId=%d" % siteids[site]
|
||||
self.cursor.execute(que, (heroes[site],))
|
||||
result = self.db.cursor.fetchall()
|
||||
if len(result) == 1:
|
||||
playerids.append(result[0][0])
|
||||
_hname = Charset.to_utf8(heroes[site])
|
||||
result = self.db.get_player_id(self.conf, site, _hname)
|
||||
if result is not None:
|
||||
playerids.append(int(result))
|
||||
|
||||
if not sitenos:
|
||||
#Should probably pop up here.
|
||||
|
@ -205,9 +204,9 @@ class GuiPlayerStats (threading.Thread):
|
|||
print "No limits found"
|
||||
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()
|
||||
|
||||
# Scrolled window for summary table
|
||||
|
@ -223,7 +222,7 @@ class GuiPlayerStats (threading.Thread):
|
|||
# gridnum - index for grid data structures
|
||||
flags = [False, self.filters.getNumHands(), 0]
|
||||
self.addGrid(swin, 'playerDetailedStats', flags, playerids
|
||||
,sitenos, limits, type, seats, groups, dates)
|
||||
,sitenos, limits, type, seats, groups, dates, games)
|
||||
|
||||
# Separator
|
||||
vbox2 = gtk.VBox(False, 0)
|
||||
|
@ -243,7 +242,7 @@ class GuiPlayerStats (threading.Thread):
|
|||
flags[0] = True
|
||||
flags[2] = 1
|
||||
self.addGrid(swin, 'playerDetailedStats', flags, playerids
|
||||
,sitenos, limits, type, seats, groups, dates)
|
||||
,sitenos, limits, type, seats, groups, dates, games)
|
||||
|
||||
self.db.rollback()
|
||||
print "Stats page displayed in %4.2f seconds" % (time() - starttime)
|
||||
|
@ -275,7 +274,7 @@ class GuiPlayerStats (threading.Thread):
|
|||
except: a = 0.0
|
||||
try: b = float(b)
|
||||
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]
|
||||
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) ) ):
|
||||
|
@ -317,7 +316,7 @@ class GuiPlayerStats (threading.Thread):
|
|||
print "***sortcols error: " + str(sys.exc_info()[1])
|
||||
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
|
||||
row = 0
|
||||
sqlrow = 0
|
||||
|
@ -325,7 +324,7 @@ class GuiPlayerStats (threading.Thread):
|
|||
else: holecards,grid = flags[0],flags[2]
|
||||
|
||||
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)
|
||||
result = self.cursor.fetchall()
|
||||
colnames = [desc[0].lower() for desc in self.cursor.description]
|
||||
|
@ -428,7 +427,7 @@ class GuiPlayerStats (threading.Thread):
|
|||
|
||||
#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 = ''
|
||||
if not flags:
|
||||
holecards = False
|
||||
|
@ -466,6 +465,23 @@ class GuiPlayerStats (threading.Thread):
|
|||
query = query.replace("<playerName>", pname)
|
||||
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)
|
||||
|
||||
if seats:
|
||||
query = query.replace('<seats_test>', 'between ' + str(seats['from']) + ' and ' + str(seats['to']))
|
||||
if 'show' in seats and seats['show']:
|
||||
|
@ -504,26 +520,26 @@ class GuiPlayerStats (threading.Thread):
|
|||
blindtest = str(tuple(nolims))
|
||||
blindtest = blindtest.replace("L", "")
|
||||
blindtest = blindtest.replace(",)",")")
|
||||
bbtest = bbtest + blindtest + ' ) )'
|
||||
bbtest = bbtest + blindtest + ' ) ) )'
|
||||
else:
|
||||
bbtest = bbtest + '(-1) ) )'
|
||||
if type == 'ring':
|
||||
bbtest = bbtest + " and gt.type = 'ring' "
|
||||
elif type == 'tour':
|
||||
bbtest = bbtest + " and gt.type = 'tour' "
|
||||
bbtest = " and gt.type = 'tour' "
|
||||
query = query.replace("<gtbigBlind_test>", bbtest)
|
||||
|
||||
if holecards: # re-use level variables for hole card query
|
||||
query = query.replace("<hgameTypeId>", "hp.startcards")
|
||||
query = query.replace("<orderbyhgameTypeId>"
|
||||
, ",case when floor(hp.startcards/13) >= mod(hp.startcards,13) then hp.startcards + 0.1 "
|
||||
+ " else 13*mod(hp.startcards,13) + floor(hp.startcards/13) "
|
||||
, ",case when floor((hp.startcards-1)/13) >= mod((hp.startcards-1),13) then hp.startcards + 0.1 "
|
||||
+ " else 13*mod((hp.startcards-1),13) + floor((hp.startcards-1)/13) + 1 "
|
||||
+ " end desc ")
|
||||
else:
|
||||
query = query.replace("<orderbyhgameTypeId>", "")
|
||||
groupLevels = "show" not in str(limits)
|
||||
if groupLevels:
|
||||
query = query.replace("<hgameTypeId>", "-1")
|
||||
query = query.replace("<hgameTypeId>", "p.name")
|
||||
else:
|
||||
query = query.replace("<hgameTypeId>", "h.gameTypeId")
|
||||
|
||||
|
|
|
@ -47,6 +47,7 @@ import fpdb_import
|
|||
import Database
|
||||
import Filters
|
||||
import FpdbSQLQueries
|
||||
import Charset
|
||||
|
||||
class GuiSessionViewer (threading.Thread):
|
||||
def __init__(self, config, querylist, mainwin, debug=True):
|
||||
|
@ -181,7 +182,10 @@ class GuiSessionViewer (threading.Thread):
|
|||
for site in sites:
|
||||
if sites[site] == True:
|
||||
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()
|
||||
if len(result) == 1:
|
||||
playerids.append(result[0][0])
|
||||
|
|
|
@ -20,7 +20,6 @@ import pygtk
|
|||
pygtk.require('2.0')
|
||||
import gtk
|
||||
import os
|
||||
import fpdb_simple
|
||||
|
||||
import fpdb_import
|
||||
import fpdb_db
|
||||
|
@ -80,7 +79,8 @@ class GuiTableViewer (threading.Thread):
|
|||
#then the data rows
|
||||
for player in range(len(self.player_names)):
|
||||
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)
|
||||
if seatCount>=8:
|
||||
|
|
|
@ -445,64 +445,102 @@ Left-Drag to Move"
|
|||
<location seat="9" x="70" y="53"> </location>
|
||||
</layout>
|
||||
</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_games>
|
||||
<game aux="mucked" cols="3" db="fpdb" game_name="holdem" rows="3">
|
||||
<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="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="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="0" popup="default" row="1" stat_name="n" 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="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 aux="mucked" cols="2" db="fpdb" game_name="holdem" rows="3">
|
||||
<game aux="stud_mucked" cols="2" db="fpdb" game_name="razz" rows="3">
|
||||
<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="n" 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="1" popup="default" row="1" stat_name="pfr" 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="cb1" tip="tip1"> </stat>
|
||||
</game>
|
||||
<stat click="tog_decorate" col="1" popup="default" row="0" stat_name="n" 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="1" popup="default" row="1" stat_name="pfr" 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="1" popup="default" row="2" stat_name="ffreq1" tip="tip1"> </stat>
|
||||
</game>
|
||||
|
||||
<game aux="stud_mucked" cols="2" db="fpdb" game_name="razz" rows="3">
|
||||
<game aux="mucked" cols="2" db="fpdb" game_name="omahahi" rows="3">
|
||||
<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="n" 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="1" popup="default" row="1" stat_name="pfr" 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="1" popup="default" row="2" stat_name="ffreq1" tip="tip1"> </stat>
|
||||
</game>
|
||||
<stat click="tog_decorate" col="1" popup="default" row="0" stat_name="n" 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="1" popup="default" row="1" stat_name="pfr" 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="1" popup="default" row="2" stat_name="ffreq1" tip="tip1"> </stat>
|
||||
</game>
|
||||
|
||||
<game aux="mucked" cols="2" db="fpdb" game_name="omahahi" rows="3">
|
||||
<game aux="mucked" cols="2" db="fpdb" game_name="omahahilo" rows="3">
|
||||
<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="n" 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="1" popup="default" row="1" stat_name="pfr" 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="1" popup="default" row="2" stat_name="ffreq1" tip="tip1"> </stat>
|
||||
</game>
|
||||
<stat click="tog_decorate" col="1" popup="default" row="0" stat_name="n" 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="1" popup="default" row="1" stat_name="pfr" 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="1" popup="default" row="2" stat_name="ffreq1" tip="tip1"> </stat>
|
||||
</game>
|
||||
|
||||
<game aux="mucked" cols="2" db="fpdb" game_name="omahahilo" rows="3">
|
||||
<game aux="stud_mucked" cols="2" db="fpdb" game_name="studhi" rows="3">
|
||||
<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="n" 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="1" popup="default" row="1" stat_name="pfr" 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="1" popup="default" row="2" stat_name="ffreq1" tip="tip1"> </stat>
|
||||
</game>
|
||||
<stat click="tog_decorate" col="1" popup="default" row="0" stat_name="n" 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="1" popup="default" row="1" stat_name="pfr" 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="1" popup="default" row="2" stat_name="ffreq1" tip="tip1"> </stat>
|
||||
</game>
|
||||
|
||||
<game aux="stud_mucked" cols="2" db="fpdb" game_name="studhi" rows="3">
|
||||
<game aux="stud_mucked" cols="2" db="fpdb" game_name="studhilo" rows="3">
|
||||
<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="n" 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="1" popup="default" row="1" stat_name="pfr" 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="1" popup="default" row="2" stat_name="ffreq1" tip="tip1"> </stat>
|
||||
</game>
|
||||
|
||||
|
||||
<game aux="stud_mucked" cols="2" db="fpdb" game_name="studhilo" rows="3">
|
||||
<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="n" 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="1" popup="default" row="1" stat_name="pfr" 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="1" popup="default" row="2" stat_name="ffreq1" tip="tip1"> </stat>
|
||||
</game>
|
||||
<stat click="tog_decorate" col="1" popup="default" row="0" stat_name="n" 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="1" popup="default" row="1" stat_name="pfr" 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="1" popup="default" row="2" stat_name="ffreq1" tip="tip1"> </stat>
|
||||
</game>
|
||||
</supported_games>
|
||||
|
||||
<popup_windows>
|
||||
|
@ -584,11 +622,12 @@ Left-Drag to Move"
|
|||
<hhc site="PartyPoker" converter="PartyPokerToFpdb"/>
|
||||
<hhc site="Betfair" converter="BetfairToFpdb"/>
|
||||
<hhc site="Partouche" converter="PartoucheToFpdb"/>
|
||||
<hhc site="Carbon" converter="CarbonToFpdb"/>
|
||||
</hhcs>
|
||||
|
||||
<supported_databases>
|
||||
<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_name="fpdb" db_server="mysql" db_ip="localhost" db_user="fpdb" db_pass="YOUR MYSQL PASSWORD"></database> -->
|
||||
<database db_ip="localhost" db_server="sqlite" db_name="fpdb.db3" db_user="fpdb" db_pass="fpdb"/>
|
||||
</supported_databases>
|
||||
|
||||
</FreePokerToolsConfig>
|
||||
|
|
|
@ -35,11 +35,6 @@ import traceback
|
|||
|
||||
(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 time
|
||||
import string
|
||||
|
@ -52,6 +47,8 @@ import gobject
|
|||
|
||||
# FreePokerTools modules
|
||||
import Configuration
|
||||
|
||||
|
||||
import Database
|
||||
from HandHistoryConverter import getTableTitleRe
|
||||
# get the correct module for the current os
|
||||
|
@ -63,7 +60,9 @@ elif os.name == 'nt':
|
|||
import Hud
|
||||
|
||||
|
||||
log = Configuration.get_logger("logging.conf")
|
||||
# get config and set up logger
|
||||
c = Configuration.Config(file=options.config)
|
||||
log = Configuration.get_logger("logging.conf", "hud", log_dir=c.dir_log, log_file='HUD-log.txt')
|
||||
|
||||
|
||||
class HUD_main(object):
|
||||
|
@ -71,26 +70,47 @@ class HUD_main(object):
|
|||
# This class mainly provides state for controlling the multiple HUDs.
|
||||
|
||||
def __init__(self, db_name = 'fpdb'):
|
||||
print "\nHUD_main: starting ..."
|
||||
self.db_name = db_name
|
||||
self.config = Configuration.Config(file=options.config, dbname=options.dbname)
|
||||
self.hud_dict = {}
|
||||
self.hud_params = self.config.get_hud_ui_parameters()
|
||||
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))
|
||||
|
||||
# a thread to read stdin
|
||||
gobject.threads_init() # this is required
|
||||
thread.start_new_thread(self.read_stdin, ()) # starts the thread
|
||||
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_params = self.config.get_hud_ui_parameters()
|
||||
|
||||
# a thread to read stdin
|
||||
gobject.threads_init() # this is required
|
||||
thread.start_new_thread(self.read_stdin, ()) # starts the thread
|
||||
|
||||
# a main window
|
||||
self.main_window = gtk.Window()
|
||||
self.main_window.connect("destroy", self.destroy)
|
||||
self.vb = gtk.VBox()
|
||||
self.label = gtk.Label('Closing this window will exit from the HUD.')
|
||||
self.vb.add(self.label)
|
||||
self.main_window.add(self.vb)
|
||||
self.main_window.set_title("HUD Main Window")
|
||||
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)
|
||||
|
||||
# a main window
|
||||
self.main_window = gtk.Window()
|
||||
self.main_window.connect("destroy", self.destroy)
|
||||
self.vb = gtk.VBox()
|
||||
self.label = gtk.Label('Closing this window will exit from the HUD.')
|
||||
self.vb.add(self.label)
|
||||
self.main_window.add(self.vb)
|
||||
self.main_window.set_title("HUD Main Window")
|
||||
self.main_window.show_all()
|
||||
|
||||
def destroy(self, *args): # call back for terminating the main eventloop
|
||||
log.info("Terminating normally.")
|
||||
gtk.main_quit()
|
||||
|
||||
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].reposition_windows()
|
||||
except:
|
||||
print "*** Exception in HUD_main::idle_func() *** "
|
||||
traceback.print_stack()
|
||||
log.error( "*** Exception in HUD_main::idle_func() *** " + str(sys.exc_info()) )
|
||||
for e in traceback.format_tb(sys.exc_info()[2]):
|
||||
log.error(e)
|
||||
finally:
|
||||
gtk.gdk.threads_leave()
|
||||
return False
|
||||
|
@ -186,30 +207,38 @@ class HUD_main(object):
|
|||
|
||||
# get hero's screen names and player ids
|
||||
self.hero, self.hero_ids = {}, {}
|
||||
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])
|
||||
found = False
|
||||
|
||||
while 1: # wait for a new hand number on stdin
|
||||
new_hand_id = sys.stdin.readline()
|
||||
t0 = time.time()
|
||||
t1 = t2 = t3 = t4 = t5 = t6 = t0
|
||||
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
|
||||
self.destroy()
|
||||
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
|
||||
# if there is a db error, complain, skip hand, and proceed
|
||||
log.info("HUD_main.read_stdin: hand processing starting ...")
|
||||
try:
|
||||
(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)
|
||||
except Exception, err: # TODO: we need to make this a much less generic Exception lulz
|
||||
print "db error: skipping %s" % new_hand_id
|
||||
sys.stderr.write("Database error: could not find hand %s.\n" % new_hand_id)
|
||||
except Exception, err:
|
||||
log.error("db error: skipping %s" % new_hand_id)
|
||||
continue
|
||||
t1 = time.time()
|
||||
|
||||
|
@ -228,18 +257,17 @@ class HUD_main(object):
|
|||
,self.hero_ids[site_id], num_seats)
|
||||
t3 = time.time()
|
||||
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
|
||||
except KeyError: # HUD instance has been killed off, key is stale
|
||||
sys.stderr.write('hud_dict[%s] was not found\n' % temp_key)
|
||||
sys.stderr.write('will not send hand\n')
|
||||
log.error('hud_dict[%s] was not found\n' % temp_key)
|
||||
log.error('will not send hand\n')
|
||||
# Unlocks table, copied from end of function
|
||||
self.db_connection.connection.rollback()
|
||||
return
|
||||
cards = self.db_connection.get_cards(new_hand_id)
|
||||
t4 = time.time()
|
||||
comm_cards = self.db_connection.get_common_cards(new_hand_id)
|
||||
t5 = time.time()
|
||||
if comm_cards != {}: # stud!
|
||||
cards['common'] = comm_cards['common']
|
||||
self.hud_dict[temp_key].cards = cards
|
||||
|
@ -250,24 +278,24 @@ class HUD_main(object):
|
|||
else:
|
||||
# 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'] )
|
||||
t4 = time.time()
|
||||
stat_dict = self.db_connection.get_stats_from_hand(new_hand_id, type, self.hud_params
|
||||
,self.hero_ids[site_id], num_seats)
|
||||
t5 = time.time()
|
||||
cards = self.db_connection.get_cards(new_hand_id)
|
||||
comm_cards = self.db_connection.get_common_cards(new_hand_id)
|
||||
if comm_cards != {}: # stud!
|
||||
cards['common'] = comm_cards['common']
|
||||
|
||||
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)
|
||||
|
||||
if tablewindow is None:
|
||||
# If no client window is found on the screen, complain and continue
|
||||
if type == "tour":
|
||||
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:
|
||||
tablewindow.max = max
|
||||
tablewindow.site = site_name
|
||||
|
@ -275,7 +303,7 @@ class HUD_main(object):
|
|||
if hasattr(tablewindow, 'number'):
|
||||
self.create_HUD(new_hand_id, tablewindow, temp_key, max, poker_game, type, stat_dict, cards)
|
||||
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()
|
||||
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__":
|
||||
|
||||
sys.stderr.write("HUD_main starting\n")
|
||||
sys.stderr.write("Using db name = %s\n" % (options.dbname))
|
||||
|
||||
# start the HUD_main object
|
||||
hm = HUD_main(db_name = options.dbname)
|
||||
|
||||
|
|
|
@ -21,19 +21,24 @@
|
|||
import re
|
||||
import sys
|
||||
import traceback
|
||||
import logging
|
||||
import os
|
||||
import os.path
|
||||
from decimal import Decimal
|
||||
import operator
|
||||
import time,datetime
|
||||
from copy import deepcopy
|
||||
from Exceptions import *
|
||||
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 Card
|
||||
|
||||
log = logging.getLogger("parser")
|
||||
|
||||
class Hand(object):
|
||||
|
||||
|
@ -43,10 +48,12 @@ class Hand(object):
|
|||
LCS = {'H':'h', 'D':'d', 'C':'c', 'S':'s'}
|
||||
SYMBOL = {'USD': '$', 'EUR': u'$', 'T$': '', 'play': ''}
|
||||
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.siteId = self.SITEIDS[sitename]
|
||||
self.stats = DerivedStats.DerivedStats(self)
|
||||
|
@ -192,7 +199,7 @@ dealt whether they were seen in a 'dealt to' line
|
|||
self.holecards[street][player] = [open, closed]
|
||||
|
||||
def prepInsert(self, db):
|
||||
#####
|
||||
#####
|
||||
# Players, Gametypes, TourneyTypes are all shared functions that are needed for additional tables
|
||||
# These functions are intended for prep insert eventually
|
||||
#####
|
||||
|
@ -205,7 +212,7 @@ dealt whether they were seen in a 'dealt to' line
|
|||
def insert(self, db):
|
||||
""" Function to insert Hand into database
|
||||
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)
|
||||
|
@ -229,8 +236,10 @@ db: a connected fpdb_db object"""
|
|||
# TourneysPlayers
|
||||
else:
|
||||
log.info("Hand.insert(): hid #: %s is a duplicate" % hh['siteHandNo'])
|
||||
#Raise Duplicate exception?
|
||||
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):
|
||||
""" Function to create Hand object from database """
|
||||
|
@ -286,6 +295,24 @@ If a player has None chips he won't be added."""
|
|||
c = c.replace(k,v)
|
||||
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):
|
||||
log.debug("%s %s antes %s" % ('BLINDSANTES', player, ante))
|
||||
if player is not None:
|
||||
|
@ -317,6 +344,11 @@ If a player has None chips he won't be added."""
|
|||
self.bets['BLINDSANTES'][player].append(Decimal(self.sb))
|
||||
self.pot.addCommonMoney(Decimal(self.sb))
|
||||
|
||||
if blindtype == 'secondsb':
|
||||
amount = Decimal(0)
|
||||
self.bets['BLINDSANTES'][player].append(Decimal(self.sb))
|
||||
self.pot.addCommonMoney(Decimal(self.sb))
|
||||
|
||||
self.bets['PREFLOP'][player].append(Decimal(amount))
|
||||
self.pot.addMoney(player, Decimal(amount))
|
||||
self.lastBet['PREFLOP'] = Decimal(amount)
|
||||
|
@ -393,7 +425,7 @@ Add a raise on [street] by [player] to [amountTo]
|
|||
Bc = reduce(operator.add, self.bets[street][player], 0)
|
||||
Rt = Decimal(amountTo)
|
||||
C = Bp - Bc
|
||||
Rb = Rt - C
|
||||
Rb = Rt - C - Bc
|
||||
self._addRaise(street, player, C, Rb, Rt)
|
||||
|
||||
def _addRaise(self, street, player, C, Rb, Rt):
|
||||
|
@ -597,7 +629,8 @@ Map the tuple self.gametype onto the pokerstars string describing it
|
|||
|
||||
|
||||
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':
|
||||
pass # or indeed don't pass and complain instead
|
||||
log.debug("HoldemOmahaHand")
|
||||
|
@ -605,7 +638,7 @@ class HoldemOmahaHand(Hand):
|
|||
self.holeStreets = ['PREFLOP']
|
||||
self.communityStreets = ['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.bb = gametype['bb']
|
||||
|
||||
|
@ -614,6 +647,8 @@ class HoldemOmahaHand(Hand):
|
|||
# which then invokes a 'addXXX' callback
|
||||
if builtFrom == "HHC":
|
||||
hhc.readHandInfo(self)
|
||||
if self.gametype['type'] == 'tour':
|
||||
self.tablename = "%s %s" % (self.tourNo, self.tablename)
|
||||
hhc.readPlayerStacks(self)
|
||||
hhc.compilePlayerRegexs(self)
|
||||
hhc.markStreets(self)
|
||||
|
@ -678,7 +713,6 @@ class HoldemOmahaHand(Hand):
|
|||
|
||||
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"""
|
||||
# FIXME: This should actually return
|
||||
hcs = [u'0x', u'0x', u'0x', u'0x']
|
||||
|
||||
for street in self.holeStreets:
|
||||
|
@ -895,7 +929,8 @@ class HoldemOmahaHand(Hand):
|
|||
print >>fh, "\n\n"
|
||||
|
||||
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':
|
||||
pass # or indeed don't pass and complain instead
|
||||
self.streetList = ['BLINDSANTES', 'DEAL', 'DRAWONE', 'DRAWTWO', 'DRAWTHREE']
|
||||
|
@ -903,12 +938,14 @@ class DrawHand(Hand):
|
|||
self.holeStreets = ['DEAL', 'DRAWONE', 'DRAWTWO', 'DRAWTHREE']
|
||||
self.actionStreets = ['BLINDSANTES', 'DEAL', 'DRAWONE', 'DRAWTWO', 'DRAWTHREE']
|
||||
self.communityStreets = []
|
||||
Hand.__init__(self, sitename, gametype, handText)
|
||||
Hand.__init__(self, self.config, sitename, gametype, handText)
|
||||
self.sb = gametype['sb']
|
||||
self.bb = gametype['bb']
|
||||
# Populate the draw hand.
|
||||
if builtFrom == "HHC":
|
||||
hhc.readHandInfo(self)
|
||||
if self.gametype['type'] == 'tour':
|
||||
self.tablename = "%s %s" % (self.tourNo, self.tablename)
|
||||
hhc.readPlayerStacks(self)
|
||||
hhc.compilePlayerRegexs(self)
|
||||
hhc.markStreets(self)
|
||||
|
@ -1085,7 +1122,8 @@ class DrawHand(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':
|
||||
pass # or indeed don't pass and complain instead
|
||||
|
||||
|
@ -1095,7 +1133,7 @@ class StudHand(Hand):
|
|||
|
||||
self.streetList = ['BLINDSANTES','THIRD','FOURTH','FIFTH','SIXTH','SEVENTH'] # a list of the observed street names in order
|
||||
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.bb = gametype['bb']
|
||||
#Populate the StudHand
|
||||
|
@ -1103,6 +1141,8 @@ class StudHand(Hand):
|
|||
# which then invokes a 'addXXX' callback
|
||||
if builtFrom == "HHC":
|
||||
hhc.readHandInfo(self)
|
||||
if self.gametype['type'] == 'tour':
|
||||
self.tablename = "%s %s" % (self.tourNo, self.tablename)
|
||||
hhc.readPlayerStacks(self)
|
||||
hhc.compilePlayerRegexs(self)
|
||||
hhc.markStreets(self)
|
||||
|
@ -1406,6 +1446,11 @@ class Pot(object):
|
|||
|
||||
# Return any uncalled bet.
|
||||
committed = sorted([ (v,k) for (k,v) in self.committed.items()])
|
||||
print "DEBUG: committed: %s" % committed
|
||||
#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]
|
||||
if lastbet > 0: # uncalled
|
||||
returnto = committed[-1][1]
|
||||
|
@ -1486,7 +1531,8 @@ limit 1""", {'handid':handid})
|
|||
#TODO: siteid should be in hands table - we took the scenic route through players here.
|
||||
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]}
|
||||
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] )
|
||||
if cards[0]:
|
||||
h.setCommunityCards('FLOP', cards[0:3])
|
||||
|
@ -1589,4 +1635,3 @@ ORDER BY
|
|||
|
||||
|
||||
return h
|
||||
|
||||
|
|
|
@ -16,8 +16,6 @@
|
|||
#In the "official" distribution you can find the license in
|
||||
#agpl-3.0.txt in the docs folder of the package.
|
||||
|
||||
import Hand
|
||||
import Tourney
|
||||
import re
|
||||
import sys
|
||||
import traceback
|
||||
|
@ -31,13 +29,20 @@ import operator
|
|||
from xml.dom.minidom import Node
|
||||
import time
|
||||
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
|
||||
import Configuration
|
||||
|
||||
import gettext
|
||||
gettext.install('fpdb')
|
||||
|
||||
log = Configuration.get_logger("logging.conf")
|
||||
|
||||
import pygtk
|
||||
import gtk
|
||||
|
@ -57,15 +62,17 @@ class HandHistoryConverter():
|
|||
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):
|
||||
"""\
|
||||
in_path (default '-' = sys.stdin)
|
||||
out_path (default '-' = sys.stdout)
|
||||
follow : whether to tail -f the input"""
|
||||
|
||||
self.config = config
|
||||
#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) )
|
||||
|
||||
self.index = 0
|
||||
self.index = index
|
||||
self.starsArchive = starsArchive
|
||||
|
||||
self.in_path = in_path
|
||||
|
@ -85,24 +92,7 @@ follow : whether to tail -f the input"""
|
|||
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.out_fh = sys.stdout
|
||||
self.follow = follow
|
||||
self.compiledPlayers = set()
|
||||
self.maxseats = 10
|
||||
|
@ -283,17 +273,16 @@ which it expects to find at self.re_TailSplitHands -- see for e.g. Everleaf.py.
|
|||
if l in self.readSupportedGames():
|
||||
if gametype['base'] == 'hold':
|
||||
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':
|
||||
hand = Hand.StudHand(self, self.sitename, gametype, handText)
|
||||
hand = Hand.StudHand(self.config, self, self.sitename, gametype, handText)
|
||||
elif gametype['base'] == 'draw':
|
||||
hand = Hand.DrawHand(self, self.sitename, gametype, handText)
|
||||
hand = Hand.DrawHand(self.config, self, self.sitename, gametype, handText)
|
||||
else:
|
||||
log.info("Unsupported game type: %s" % gametype)
|
||||
|
||||
if hand:
|
||||
if Configuration.NEWIMPORT == False:
|
||||
hand.writeHand(self.out_fh)
|
||||
#hand.writeHand(self.out_fh)
|
||||
return hand
|
||||
else:
|
||||
log.info("Unsupported game type: %s" % gametype)
|
||||
|
@ -418,7 +407,7 @@ or None if we fail to get the info """
|
|||
list.pop() #Last entry is empty
|
||||
for l in list:
|
||||
# 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.. ?
|
||||
return hands
|
||||
|
||||
|
@ -441,10 +430,9 @@ or None if we fail to get the info """
|
|||
#print "trying", kodec
|
||||
try:
|
||||
in_fh = codecs.open(self.in_path, 'r', kodec)
|
||||
in_fh.seek(self.index)
|
||||
log.debug("Opened in_path: '%s' with %s" % (self.in_path, kodec))
|
||||
self.obs = in_fh.read()
|
||||
self.index = in_fh.tell()
|
||||
whole_file = in_fh.read()
|
||||
self.obs = whole_file[self.index:]
|
||||
self.index = len(whole_file)
|
||||
in_fh.close()
|
||||
break
|
||||
except:
|
||||
|
@ -527,6 +515,3 @@ def getSiteHhc(config, sitename):
|
|||
hhcName = config.supported_sites[sitename].converter
|
||||
hhcModule = __import__(hhcName)
|
||||
return getattr(hhcModule, hhcName[:-6])
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -26,6 +26,10 @@ Create and manage the hud overlays.
|
|||
import os
|
||||
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
|
||||
import pygtk
|
||||
import gtk
|
||||
|
@ -365,7 +369,7 @@ class Hud:
|
|||
self.create(*self.creation_attrs)
|
||||
self.update(self.hand, self.config)
|
||||
except Exception, e:
|
||||
print "Exception:",str(e)
|
||||
log.error("Exception:",str(e))
|
||||
pass
|
||||
|
||||
def set_aggregation(self, widget, val):
|
||||
|
@ -377,7 +381,7 @@ class Hud:
|
|||
|
||||
if self.hud_params['h_agg_bb_mult'] != num \
|
||||
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
|
||||
for mult in ('1', '2', '3', '10', '10000'):
|
||||
if mult != str(num):
|
||||
|
@ -388,7 +392,7 @@ class Hud:
|
|||
|
||||
if self.hud_params['agg_bb_mult'] != num \
|
||||
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
|
||||
for mult in ('1', '2', '3', '10', '10000'):
|
||||
if mult != str(num):
|
||||
|
@ -415,7 +419,7 @@ class Hud:
|
|||
self.hud_params[param] = 'E'
|
||||
getattr(self, prefix+'seatsStyleOptionA').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):
|
||||
(player_opp, style) = val
|
||||
|
@ -438,7 +442,7 @@ class Hud:
|
|||
self.hud_params[param] = 'T'
|
||||
getattr(self, prefix+'hudStyleOptionA').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):
|
||||
if os.name == 'nt':
|
||||
|
@ -515,7 +519,7 @@ class Hud:
|
|||
# ask each aux to save its layout back to the config object
|
||||
[aux.save_layout() for aux in self.aux_windows]
|
||||
# save the config object back to the file
|
||||
print "saving new xml file"
|
||||
print "Updating config file"
|
||||
self.config.save()
|
||||
|
||||
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
|
||||
return
|
||||
|
||||
self.label.modify_fg(gtk.STATE_NORMAL, gtk.gdk.color_parse(self.colors['hudfgcolor']))
|
||||
for s in self.stat_dict:
|
||||
try:
|
||||
statd = self.stat_dict[s]
|
||||
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"
|
||||
print "(btw, the key was ", s, " and statd is...", statd
|
||||
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")
|
||||
log.error("(btw, the key was ", s, " and statd is...", statd)
|
||||
continue
|
||||
try:
|
||||
self.stat_windows[statd['seat']].player_id = statd['player_id']
|
||||
|
@ -629,21 +634,17 @@ class Hud:
|
|||
window = self.stat_windows[statd['seat']]
|
||||
|
||||
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))
|
||||
else:
|
||||
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("#FFFFFF"))
|
||||
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):
|
||||
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.stat_locolor))
|
||||
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):
|
||||
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.stat_hicolor))
|
||||
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)
|
||||
if statstring != "xxx": # is there a way to tell if this particular stat window is visible already, or no?
|
||||
|
@ -679,9 +680,11 @@ class Stat_Window:
|
|||
return True
|
||||
|
||||
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
|
||||
self.window.hide()
|
||||
return True
|
||||
# TODO: make position saving save sizes as well?
|
||||
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)
|
||||
|
|
|
@ -128,7 +128,8 @@ class Stud_mucked(Aux_Window):
|
|||
self.container.show_all()
|
||||
|
||||
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)
|
||||
|
||||
def update_gui(self, new_hand_id):
|
||||
|
@ -357,21 +358,24 @@ class Aux_Seats(Aux_Window):
|
|||
except AttributeError:
|
||||
return
|
||||
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']):
|
||||
if i == 'common':
|
||||
(x, y) = self.params['layout'][self.hud.max].common
|
||||
else:
|
||||
(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])
|
||||
|
||||
|
||||
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
|
||||
loc = self.config.get_aux_locations(self.params['name'], int(self.hud.max))
|
||||
|
||||
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']):
|
||||
if i == '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_focus_on_map(False)
|
||||
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])
|
||||
if self.params.has_key('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."""
|
||||
new_locs = {}
|
||||
# print "adj =", self.adj
|
||||
witdh = self.hud.table.width
|
||||
height = self.hud.table.height
|
||||
for (i, pos) in self.positions.iteritems():
|
||||
if i != 'common':
|
||||
new_locs[self.adj[int(i)]] = (pos[0] - self.hud.table.x, pos[1] - self.hud.table.y)
|
||||
else:
|
||||
new_locs[i] = (pos[0] - self.hud.table.x, pos[1] - self.hud.table.y)
|
||||
if i != 'common':
|
||||
new_locs[self.adj[int(i)]] = ((pos[0] - self.hud.table.x) * 1000 / witdh, (pos[1] - self.hud.table.y) * 1000 / height)
|
||||
else:
|
||||
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)
|
||||
|
||||
def configure_event_cb(self, widget, event, i, *args):
|
||||
|
|
|
@ -15,9 +15,9 @@
|
|||
#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
|
||||
from optparse import OptionParser
|
||||
# http://docs.python.org/library/optparse.html
|
||||
|
||||
def fpdb_options():
|
||||
|
||||
|
@ -41,6 +41,14 @@ def fpdb_options():
|
|||
parser.add_option("-k", "--konverter",
|
||||
dest="hhc", default="PokerStarsToFpdb",
|
||||
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()
|
||||
return (options, argv)
|
||||
|
||||
|
|
|
@ -47,7 +47,7 @@ class PartyPoker(HandHistoryConverter):
|
|||
# $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
|
||||
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<GAME>(Texas\ Hold\'em|Omaha))
|
||||
\s*\-\s*
|
||||
|
@ -60,9 +60,9 @@ class PartyPoker(HandHistoryConverter):
|
|||
Trny:\s?(?P<TOURNO>\d+)\s+
|
||||
Level:\s*(?P<LEVEL>\d+)\s+
|
||||
((Blinds|Stakes)(?:-Antes)?)\(
|
||||
(?P<SB>[,.0-9 ]+)\s*
|
||||
/(?P<BB>[,.0-9 ]+)
|
||||
(?:\s*-\s*(?P<ANTE>[,.0-9 ]+)\$?)?
|
||||
(?P<SB>[.,0-9 ]+)\s*
|
||||
/(?P<BB>[.,0-9 ]+)
|
||||
(?:\s*-\s*(?P<ANTE>[.,0-9 ]+)\$?)?
|
||||
\)
|
||||
\s*\-\s*
|
||||
(?P<DATETIME>.+)
|
||||
|
@ -72,21 +72,17 @@ class PartyPoker(HandHistoryConverter):
|
|||
re_PlayerInfo = re.compile("""
|
||||
Seat\s(?P<SEAT>\d+):\s
|
||||
(?P<PNAME>.*)\s
|
||||
\(\s*\$?(?P<CASH>[.,0-9]+)\s*(?:USD|)\s*\)
|
||||
\(\s*\$?(?P<CASH>[0-9,.]+)\s*(?:USD|)\s*\)
|
||||
""" ,
|
||||
re.VERBOSE)
|
||||
|
||||
# Table $250 Freeroll (1810210) Table #309 (Real Money)
|
||||
# Table Speed #1465003 (Real Money)
|
||||
|
||||
re_HandInfo = re.compile("""
|
||||
^Table\s+
|
||||
(?P<TABLE_TYPE>[^#()]+)\s+ # Regular, Speed, etc
|
||||
(?P<TABLE_ID_WRAPPER>\(|\#| ) # \# means sng, ( - mtt, nothing - cash game
|
||||
(?P<TABLE_ID>\d+) \)? \s+ # it's global unique id for this table
|
||||
(?:Table\s+\#(?P<TABLE_NUM>\d+).+)? # table num for mtt tournaments
|
||||
^Table\s+(?P<TTYPE>[$a-zA-Z0-9 ]+)\s+
|
||||
(?: \#|\(|)(?P<TABLE>\d+)\)?\s+
|
||||
(?:[a-zA-Z0-9 ]+\s+\#(?P<MTTTABLE>\d+).+)?
|
||||
(\(No\sDP\)\s)?
|
||||
\((?P<PLAY>Real|Play)\s+Money\)\s*
|
||||
\((?P<PLAY>Real|Play)\s+Money\)\s+ # FIXME: check if play money is correct
|
||||
Seat\s+(?P<BUTTON>\d+)\sis\sthe\sbutton
|
||||
""",
|
||||
re.VERBOSE|re.MULTILINE)
|
||||
|
||||
|
@ -290,6 +286,14 @@ class PartyPoker(HandHistoryConverter):
|
|||
|
||||
if key == 'HID':
|
||||
hand.handid = info[key]
|
||||
if key == 'TABLE':
|
||||
hand.tablename = info[key]
|
||||
if key == 'MTTTABLE':
|
||||
if info[key] != None:
|
||||
hand.tablename = info[key]
|
||||
hand.tourNo = info['TABLE']
|
||||
if key == 'BUTTON':
|
||||
hand.buttonpos = info[key]
|
||||
if key == 'TOURNO':
|
||||
hand.tourNo = info[key]
|
||||
if key == 'TABLE_ID_WRAPPER':
|
||||
|
@ -299,30 +303,17 @@ class PartyPoker(HandHistoryConverter):
|
|||
if key == 'BUYIN':
|
||||
# FIXME: it's dirty hack T_T
|
||||
# code below assumes that tournament rake is equal to zero
|
||||
# added handle for freeroll tourney's
|
||||
# code added for freeroll the buyin is overwritten by $0+$0
|
||||
if info[key] == None: #assume freeroll
|
||||
cur = '$'
|
||||
hand.buyin = "$0" + '+%s0' % cur
|
||||
if info[key] == None:
|
||||
hand.buyin = '$0+$0'
|
||||
else:
|
||||
cur = info[key][0] if info[key][0] not in '0123456789' else ''
|
||||
hand.buyin = info[key] + '+%s0' % cur
|
||||
if key == 'TABLE_ID':
|
||||
hand.tablename = info[key]
|
||||
if key == 'TABLE_NUM':
|
||||
# FIXME: there is no such property in Hand class
|
||||
if info[key] != None:
|
||||
hand.tablename = info[key]
|
||||
hand.tourNo = info['TABLE_ID']
|
||||
if key == 'COUNTED_SEATS':
|
||||
hand.counted_seats = info[key]
|
||||
cur = info[key][0] if info[key][0] not in '0123456789' else ''
|
||||
hand.buyin = info[key] + '+%s0' % cur
|
||||
if key == 'LEVEL':
|
||||
hand.level = info[key]
|
||||
if key == 'PLAY' and info['PLAY'] != 'Real':
|
||||
# if realy party doesn's save play money hh
|
||||
hand.gametype['currency'] = 'play'
|
||||
|
||||
|
||||
def readButton(self, hand):
|
||||
m = self.re_Button.search(hand.handText)
|
||||
if m:
|
||||
|
|
36
pyfpdb/PokerStarsToFpdb.py
Normal file → Executable file
36
pyfpdb/PokerStarsToFpdb.py
Normal file → Executable file
|
@ -40,13 +40,15 @@ class PokerStars(HandHistoryConverter):
|
|||
'LEGAL_ISO' : "USD|EUR|GBP|CAD|FPP", # legal ISO currency codes
|
||||
'LS' : "\$|\xe2\x82\xac|" # legal currency symbols - Euro(cp1252, utf-8)
|
||||
}
|
||||
|
||||
# Static regexes
|
||||
re_GameInfo = re.compile(u"""
|
||||
PokerStars\sGame\s\#(?P<HID>[0-9]+):\s+
|
||||
(Tournament\s\# # open paren of tournament info
|
||||
(?P<TOURNO>\d+),\s
|
||||
# here's how I plan to use LS
|
||||
(?P<BUYIN>([%(LS)s\+\d\.]+\s?(?P<TOUR_ISO>%(LEGAL_ISO)s)?)|Freeroll)\s+)? # close paren of tournament info
|
||||
(?P<BUYIN>([%(LS)s\+\d\.]+\s?(?P<TOUR_ISO>%(LEGAL_ISO)s)?)|Freeroll)\s+)?
|
||||
# close paren of tournament info
|
||||
(?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<LIMIT>No\sLimit|Limit|Pot\sLimit)\)?,?\s
|
||||
|
@ -67,7 +69,7 @@ class PokerStars(HandHistoryConverter):
|
|||
re.MULTILINE|re.VERBOSE)
|
||||
|
||||
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<PLAY>\(Play\sMoney\)\s)?
|
||||
(Seat\s\#(?P<BUTTON>\d+)\sis\sthe\sbutton)?""",
|
||||
|
@ -79,6 +81,7 @@ class PokerStars(HandHistoryConverter):
|
|||
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]+)')
|
||||
|
||||
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):
|
||||
players = set([player[1] for player in hand.players])
|
||||
|
@ -95,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_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_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_Action = re.compile(r"""
|
||||
^%(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>
|
||||
(\scards?(\s\[(?P<DISCARDED>.+?)\])?)?"""
|
||||
\s*(and\sis\sall.in)?
|
||||
(\scards?(\s\[(?P<DISCARDED>.+?)\])?)?\s*$"""
|
||||
% subst, re.MULTILINE|re.VERBOSE)
|
||||
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_ShownCards = re.compile("^Seat (?P<SEAT>[0-9]+): %s (\(.*\) )?(?P<SHOWED>showed|mucked) \[(?P<CARDS>.*)\].*" % player_re, re.MULTILINE)
|
||||
|
||||
|
@ -146,7 +150,7 @@ class PokerStars(HandHistoryConverter):
|
|||
'7 Card Stud Hi/Lo' : ('stud','studhilo'),
|
||||
'Badugi' : ('draw','badugi'),
|
||||
'Triple Draw 2-7 Lowball' : ('draw','27_3draw'),
|
||||
'5 Card Draw' : ('draw','fivedraw'),
|
||||
'5 Card Draw' : ('draw','fivedraw')
|
||||
}
|
||||
currencies = { u'€':'EUR', '$':'USD', '':'T$' }
|
||||
# I don't think this is doing what we think. mg will always have all
|
||||
|
@ -192,18 +196,21 @@ class PokerStars(HandHistoryConverter):
|
|||
#2008/11/12 10:00:48 CET [2008/11/12 4:00:48 ET]
|
||||
#2008/08/17 - 01:14:43 (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])
|
||||
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'))
|
||||
m1 = self.re_DateTime.finditer(info[key])
|
||||
# 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")
|
||||
if key == 'HID':
|
||||
hand.handid = info[key]
|
||||
|
||||
if key == 'TOURNO':
|
||||
hand.tourNo = info[key]
|
||||
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]
|
||||
if key == 'LEVEL':
|
||||
hand.level = info[key]
|
||||
|
@ -234,7 +241,6 @@ class PokerStars(HandHistoryConverter):
|
|||
def readPlayerStacks(self, hand):
|
||||
log.debug("readPlayerStacks")
|
||||
m = self.re_PlayerInfo.finditer(hand.handText)
|
||||
players = []
|
||||
for a in m:
|
||||
hand.addPlayer(int(a.group('SEAT')), a.group('PNAME'), a.group('CASH'))
|
||||
|
||||
|
@ -282,8 +288,13 @@ class PokerStars(HandHistoryConverter):
|
|||
|
||||
def readBlinds(self, hand):
|
||||
try:
|
||||
m = self.re_PostSB.search(hand.handText)
|
||||
hand.addBlind(m.group('PNAME'), 'small blind', m.group('SB'))
|
||||
count = 0
|
||||
for a in self.re_PostSB.finditer(hand.handText):
|
||||
if count == 0:
|
||||
hand.addBlind(a.group('PNAME'), 'small blind', a.group('SB'))
|
||||
count = 1
|
||||
else:
|
||||
hand.addBlind(a.group('PNAME'), 'secondsb', a.group('SB'))
|
||||
except: # no small blind
|
||||
hand.addBlind(None, None, None)
|
||||
for a in self.re_PostBB.finditer(hand.handText):
|
||||
|
@ -331,6 +342,7 @@ class PokerStars(HandHistoryConverter):
|
|||
m = self.re_Action.finditer(hand.streets[street])
|
||||
for action in m:
|
||||
acts = action.groupdict()
|
||||
#print "DEBUG: acts: %s" %acts
|
||||
if action.group('ATYPE') == ' raises':
|
||||
hand.addRaiseBy( street, action.group('PNAME'), action.group('BET') )
|
||||
elif action.group('ATYPE') == ' calls':
|
||||
|
|
183
pyfpdb/SQL.py
183
pyfpdb/SQL.py
|
@ -58,6 +58,27 @@ class Sql:
|
|||
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
|
||||
################################
|
||||
|
@ -214,6 +235,7 @@ class Sql:
|
|||
id BIGINT UNSIGNED AUTO_INCREMENT NOT NULL, PRIMARY KEY (id),
|
||||
tableName VARCHAR(22) NOT NULL,
|
||||
siteHandNo BIGINT NOT NULL,
|
||||
tourneyId INT UNSIGNED NOT NULL,
|
||||
gametypeId SMALLINT UNSIGNED NOT NULL, FOREIGN KEY (gametypeId) REFERENCES Gametypes(id),
|
||||
handStart DATETIME NOT NULL,
|
||||
importTime DATETIME NOT NULL,
|
||||
|
@ -249,6 +271,7 @@ class Sql:
|
|||
id BIGSERIAL, PRIMARY KEY (id),
|
||||
tableName VARCHAR(22) NOT NULL,
|
||||
siteHandNo BIGINT NOT NULL,
|
||||
tourneyId INT NOT NULL,
|
||||
gametypeId INT NOT NULL, FOREIGN KEY (gametypeId) REFERENCES Gametypes(id),
|
||||
handStart timestamp without time zone NOT NULL,
|
||||
importTime timestamp without time zone NOT NULL,
|
||||
|
@ -283,6 +306,7 @@ class Sql:
|
|||
id INTEGER PRIMARY KEY,
|
||||
tableName TEXT(22) NOT NULL,
|
||||
siteHandNo INT NOT NULL,
|
||||
tourneyId INT NOT NULL,
|
||||
gametypeId INT NOT NULL,
|
||||
handStart REAL NOT NULL,
|
||||
importTime REAL NOT NULL,
|
||||
|
@ -1824,6 +1848,15 @@ class Sql:
|
|||
self.query['getLimits2'] = """SELECT DISTINCT type, limitType, bigBlind
|
||||
from Gametypes
|
||||
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':
|
||||
self.query['playerDetailedStats'] = """
|
||||
|
@ -1881,6 +1914,7 @@ class Sql:
|
|||
inner join Sites s on (s.Id = gt.siteId)
|
||||
inner join Players p on (p.Id = hp.playerId)
|
||||
where hp.playerId in <player_test>
|
||||
<game_test>
|
||||
/*and hp.tourneysPlayersId IS NULL*/
|
||||
and h.seats <seats_test>
|
||||
<flagtest>
|
||||
|
@ -1964,6 +1998,7 @@ class Sql:
|
|||
inner join Sites s on (s.Id = gt.siteId)
|
||||
inner join Players p on (p.Id = hp.playerId)
|
||||
where hp.playerId in <player_test>
|
||||
<game_test>
|
||||
/*and hp.tourneysPlayersId IS NULL*/
|
||||
and h.seats <seats_test>
|
||||
<flagtest>
|
||||
|
@ -1995,6 +2030,7 @@ class Sql:
|
|||
elif db_server == 'sqlite':
|
||||
self.query['playerDetailedStats'] = """
|
||||
select <hgameTypeId> AS hgametypeid
|
||||
,<playerName> AS pname
|
||||
,gt.base
|
||||
,gt.category AS category
|
||||
,upper(gt.limitType) AS limittype
|
||||
|
@ -2046,7 +2082,9 @@ class Sql:
|
|||
inner join Hands h on (h.id = hp.handId)
|
||||
inner join Gametypes gt on (gt.Id = h.gameTypeId)
|
||||
inner join Sites s on (s.Id = gt.siteId)
|
||||
inner join Players p on (p.Id = hp.playerId)
|
||||
where hp.playerId in <player_test>
|
||||
<game_test>
|
||||
/*and hp.tourneysPlayersId IS NULL*/
|
||||
and h.seats <seats_test>
|
||||
<flagtest>
|
||||
|
@ -2573,6 +2611,7 @@ class Sql:
|
|||
AND h.handStart > '<startdate_test>'
|
||||
AND h.handStart < '<enddate_test>'
|
||||
<limit_test>
|
||||
<game_test>
|
||||
AND hp.tourneysPlayersId IS NULL
|
||||
GROUP BY h.handStart, hp.handId, hp.sawShowdown, hp.totalProfit
|
||||
ORDER BY h.handStart"""
|
||||
|
@ -3088,6 +3127,147 @@ class Sql:
|
|||
,'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)
|
||||
from HudCache hc
|
||||
where hc.playerId in <playerid_list>
|
||||
|
@ -3292,6 +3472,7 @@ class Sql:
|
|||
tablename,
|
||||
gametypeid,
|
||||
sitehandno,
|
||||
tourneyId,
|
||||
handstart,
|
||||
importtime,
|
||||
seats,
|
||||
|
@ -3322,7 +3503,7 @@ class Sql:
|
|||
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)"""
|
||||
|
||||
|
||||
self.query['store_hands_players'] = """INSERT INTO HandsPlayers (
|
||||
|
|
|
@ -62,9 +62,13 @@ import Database
|
|||
re_Places = re.compile("_[0-9]$")
|
||||
re_Percent = re.compile("%$")
|
||||
|
||||
# String manipulation
|
||||
import codecs
|
||||
encoder = codecs.lookup(Configuration.LOCALE_ENCODING)
|
||||
|
||||
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'):
|
||||
match = re_Places.search(stat)
|
||||
|
@ -241,8 +245,20 @@ def saw_f(stat_dict, player):
|
|||
def n(stat_dict, player):
|
||||
""" Number of hands played."""
|
||||
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'],
|
||||
'%d' % (stat_dict[player]['n']),
|
||||
'%s' % fmt,
|
||||
'n=%d' % (stat_dict[player]['n']),
|
||||
'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.name = "%s - %s" % (self.tournament, self.table)
|
||||
elif table_name is not None:
|
||||
search_string = table_name
|
||||
# search_string = table_name
|
||||
self.name = table_name
|
||||
self.tournament = None
|
||||
else:
|
||||
|
|
|
@ -39,7 +39,6 @@ if os.name == 'nt':
|
|||
|
||||
# FreePokerTools modules
|
||||
import Configuration
|
||||
from fpdb_simple import LOCALE_ENCODING
|
||||
|
||||
# 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
|
||||
|
@ -238,7 +237,7 @@ def discover_nt_by_name(c, tablename):
|
|||
try:
|
||||
# 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
|
||||
if not tablename.lower() in titles[hwnd].decode(LOCALE_ENCODING).lower():
|
||||
if not tablename.lower() in titles[hwnd].decode(Configuration.LOCALE_ENCODING).lower():
|
||||
continue
|
||||
except:
|
||||
continue
|
||||
|
|
|
@ -185,7 +185,7 @@ class Tourney(object):
|
|||
def old_insert_from_Hand(self, db):
|
||||
""" Function to insert Hand into database
|
||||
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:
|
||||
# Players - base playerid and siteid tuple
|
||||
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
|
||||
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
|
||||
import pygtk
|
||||
import gtk
|
||||
|
@ -50,28 +54,31 @@ class Table(Table_Window):
|
|||
titles = {}
|
||||
win32gui.EnumWindows(win_enum_handler, titles)
|
||||
for hwnd in titles:
|
||||
if titles[hwnd] == "": continue
|
||||
# print "searching ", search_string, " in ", titles[hwnd]
|
||||
if re.search(search_string, titles[hwnd]):
|
||||
if 'History for table:' in titles[hwnd]: continue # Everleaf Network HH viewer 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 'FPDBHUD' in titles[hwnd]: continue # can't attach to ourselves!
|
||||
self.window = hwnd
|
||||
break
|
||||
|
||||
try:
|
||||
if self.window == None:
|
||||
print "Window %s not found. Skipping." % search_string
|
||||
log.error( "Window %s not found. Skipping." % search_string )
|
||||
return None
|
||||
except AttributeError:
|
||||
print "self.window doesn't exist? why?"
|
||||
log.error( "self.window doesn't exist? why?" )
|
||||
return None
|
||||
|
||||
(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.y = int(y) + tb_height
|
||||
self.width = width - x
|
||||
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.width = int(width) - 2*b_width
|
||||
|
||||
|
|
222
pyfpdb/fpdb.py
Normal file → Executable file
222
pyfpdb/fpdb.py
Normal file → Executable file
|
@ -18,6 +18,7 @@
|
|||
import os
|
||||
import sys
|
||||
import re
|
||||
import Queue
|
||||
|
||||
# if path is set to use an old version of python look for a new one:
|
||||
# (does this work in linux?)
|
||||
|
@ -52,7 +53,7 @@ if os.name == 'nt':
|
|||
raw_input("Press ENTER to continue.")
|
||||
exit()
|
||||
|
||||
print "Python " + sys.version[0:3] + '...\n'
|
||||
print "Python " + sys.version[0:3] + '...'
|
||||
|
||||
import traceback
|
||||
import threading
|
||||
|
@ -61,17 +62,13 @@ import string
|
|||
cl_options = string.join(sys.argv[1:])
|
||||
(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
|
||||
|
||||
try:
|
||||
import pygtk
|
||||
pygtk.require('2.0')
|
||||
import gtk
|
||||
import pango
|
||||
except:
|
||||
print "Unable to load PYGTK modules required for GUI. Please install PyCairo, PyGObject, and PyGTK from www.pygtk.org."
|
||||
raw_input("Press ENTER to continue.")
|
||||
|
@ -79,8 +76,27 @@ except:
|
|||
|
||||
import interlocks
|
||||
|
||||
# these imports not required in this module, imported here to report version in About dialog
|
||||
try:
|
||||
import matplotlib
|
||||
matplotlib_version = matplotlib.__version__
|
||||
except:
|
||||
matplotlib_version = 'not found'
|
||||
try:
|
||||
import numpy
|
||||
numpy_version = numpy.__version__
|
||||
except:
|
||||
numpy_version = 'not found'
|
||||
try:
|
||||
import sqlite3
|
||||
sqlite3_version = sqlite3.version
|
||||
sqlite_version = sqlite3.sqlite_version
|
||||
except:
|
||||
sqlite3_version = 'not found'
|
||||
sqlite_version = 'not found'
|
||||
|
||||
import GuiPrefs
|
||||
import GuiLogView
|
||||
import GuiBulkImport
|
||||
import GuiPlayerStats
|
||||
import GuiPositionalStats
|
||||
|
@ -90,12 +106,12 @@ import GuiGraphViewer
|
|||
import GuiSessionViewer
|
||||
import SQL
|
||||
import Database
|
||||
import FpdbSQLQueries
|
||||
import Configuration
|
||||
import Exceptions
|
||||
|
||||
VERSION = "0.12"
|
||||
|
||||
|
||||
class fpdb:
|
||||
def tab_clicked(self, widget, tab_name):
|
||||
"""called when a tab button is clicked to activate that tab"""
|
||||
|
@ -108,12 +124,12 @@ class fpdb:
|
|||
|
||||
def add_tab(self, new_page, new_tab_name):
|
||||
"""adds a tab, namely creates the button and displays it and appends all the relevant arrays"""
|
||||
for name in self.nb_tabs: #todo: check this is valid
|
||||
for name in self.nb_tab_names: #todo: check this is valid
|
||||
if name == new_tab_name:
|
||||
return # if tab already exists, just go to it
|
||||
|
||||
used_before = False
|
||||
for i, name in enumerate(self.tab_names): #todo: check this is valid
|
||||
for i, name in enumerate(self.tab_names):
|
||||
if name == new_tab_name:
|
||||
used_before = True
|
||||
event_box = self.tabs[i]
|
||||
|
@ -129,13 +145,13 @@ class fpdb:
|
|||
|
||||
#self.nb.append_page(new_page, gtk.Label(new_tab_name))
|
||||
self.nb.append_page(page, event_box)
|
||||
self.nb_tabs.append(new_tab_name)
|
||||
self.nb_tab_names.append(new_tab_name)
|
||||
page.show()
|
||||
|
||||
def display_tab(self, new_tab_name):
|
||||
"""displays the indicated tab"""
|
||||
tab_no = -1
|
||||
for i, name in enumerate(self.nb_tabs):
|
||||
for i, name in enumerate(self.nb_tab_names):
|
||||
if new_tab_name == name:
|
||||
tab_no = i
|
||||
break
|
||||
|
@ -186,13 +202,13 @@ class fpdb:
|
|||
(nb, text) = data
|
||||
page = -1
|
||||
#print "\n remove_tab: start", text
|
||||
for i, tab in enumerate(self.nb_tabs):
|
||||
for i, tab in enumerate(self.nb_tab_names):
|
||||
if text == tab:
|
||||
page = i
|
||||
#print " page =", page
|
||||
if page >= 0 and page < self.nb.get_n_pages():
|
||||
#print " removing page", page
|
||||
del self.nb_tabs[page]
|
||||
del self.nb_tab_names[page]
|
||||
nb.remove_page(page)
|
||||
# Need to refresh the widget --
|
||||
# This forces the widget to redraw itself.
|
||||
|
@ -213,10 +229,42 @@ class fpdb:
|
|||
dia.set_comments("GTK AboutDialog comments here")
|
||||
dia.set_license("GPL v3")
|
||||
dia.set_website("http://fpdb.sourceforge.net/")
|
||||
dia.set_authors("Steffen, Eratosthenes, s0rrow, EricBlade, _mt, sqlcoder, Bostik, and others")
|
||||
dia.set_authors(['Steffen', 'Eratosthenes', 's0rrow',
|
||||
'EricBlade', '_mt', 'sqlcoder', 'Bostik', 'and others'])
|
||||
dia.set_program_name("Free Poker Database (FPDB)")
|
||||
|
||||
db_version = ""
|
||||
#if self.db is not None:
|
||||
# db_version = self.db.get_version()
|
||||
nums = [ ('Operating System', os.name)
|
||||
, ('Python', sys.version[0:3])
|
||||
, ('GTK+', '.'.join([str(x) for x in gtk.gtk_version]))
|
||||
, ('PyGTK', '.'.join([str(x) for x in gtk.pygtk_version]))
|
||||
, ('matplotlib', matplotlib_version)
|
||||
, ('numpy', numpy_version)
|
||||
, ('sqlite3', sqlite3_version)
|
||||
, ('sqlite', sqlite_version)
|
||||
, ('database', self.settings['db-server'] + db_version)
|
||||
]
|
||||
versions = gtk.TextBuffer()
|
||||
w = 20 # width used for module names and version numbers
|
||||
versions.set_text( '\n'.join( [x[0].rjust(w)+' '+ x[1].ljust(w) for x in nums] ) )
|
||||
view = gtk.TextView(versions)
|
||||
view.set_editable(False)
|
||||
view.set_justification(gtk.JUSTIFY_CENTER)
|
||||
view.modify_font(pango.FontDescription('monospace 10'))
|
||||
view.show()
|
||||
dia.vbox.pack_end(view, True, True, 2)
|
||||
l = gtk.Label('Version Information:')
|
||||
l.set_alignment(0.5, 0.5)
|
||||
l.show()
|
||||
dia.vbox.pack_end(l, True, True, 2)
|
||||
|
||||
dia.run()
|
||||
dia.destroy()
|
||||
log.debug("Threads: ")
|
||||
for t in self.threads:
|
||||
log.debug("........." + str(t.__class__))
|
||||
|
||||
def dia_preferences(self, widget, data=None):
|
||||
dia = gtk.Dialog("Preferences",
|
||||
|
@ -432,6 +480,53 @@ class fpdb:
|
|||
|
||||
self.release_global_lock()
|
||||
|
||||
def dia_logs(self, widget, data=None):
|
||||
"""opens the log viewer window"""
|
||||
|
||||
#lock_set = False
|
||||
#if self.obtain_global_lock():
|
||||
# lock_set = True
|
||||
|
||||
# remove members from self.threads if close messages received
|
||||
self.process_close_messages()
|
||||
|
||||
viewer = None
|
||||
for i, t in enumerate(self.threads):
|
||||
if str(t.__class__) == 'GuiLogView.GuiLogView':
|
||||
viewer = t
|
||||
break
|
||||
|
||||
if viewer is None:
|
||||
#print "creating new log viewer"
|
||||
new_thread = GuiLogView.GuiLogView(self.config, self.window, self.closeq)
|
||||
self.threads.append(new_thread)
|
||||
else:
|
||||
#print "showing existing log viewer"
|
||||
viewer.get_dialog().present()
|
||||
|
||||
#if lock_set:
|
||||
# self.release_global_lock()
|
||||
|
||||
def addLogText(self, text):
|
||||
end_iter = self.logbuffer.get_end_iter()
|
||||
self.logbuffer.insert(end_iter, text)
|
||||
self.logview.scroll_to_mark(self.logbuffer.get_insert(), 0)
|
||||
|
||||
|
||||
def process_close_messages(self):
|
||||
# check for close messages
|
||||
try:
|
||||
while True:
|
||||
name = self.closeq.get(False)
|
||||
for i, t in enumerate(self.threads):
|
||||
if str(t.__class__) == str(name):
|
||||
# thread has ended so remove from list:
|
||||
del self.threads[i]
|
||||
break
|
||||
except Queue.Empty:
|
||||
# no close messages on queue, do nothing
|
||||
pass
|
||||
|
||||
def __calendar_dialog(self, widget, entry):
|
||||
self.dia_confirm.set_modal(False)
|
||||
d = gtk.Window(gtk.WINDOW_TOPLEVEL)
|
||||
|
@ -534,6 +629,7 @@ class fpdb:
|
|||
</menu>
|
||||
<menu action="help">
|
||||
<menuitem action="Abbrev"/>
|
||||
<menuitem action="Logs"/>
|
||||
<separator/>
|
||||
<menuitem action="About"/>
|
||||
<menuitem action="License"/>
|
||||
|
@ -575,6 +671,7 @@ class fpdb:
|
|||
('stats', None, '_Statistics (todo)', None, 'View Database Statistics', self.dia_database_stats),
|
||||
('help', None, '_Help'),
|
||||
('Abbrev', None, '_Abbrevations (todo)', None, 'List of Abbrevations', self.tab_abbreviations),
|
||||
('Logs', None, '_Log Messages', None, 'Log and Debug Messages', self.dia_logs),
|
||||
('About', None, 'A_bout', None, 'About the program', self.dia_about),
|
||||
('License', None, '_License and Copying (todo)', None, 'License and Copying', self.dia_licensing),
|
||||
])
|
||||
|
@ -591,6 +688,13 @@ class fpdb:
|
|||
def load_profile(self):
|
||||
"""Loads profile from the provided path name."""
|
||||
self.config = Configuration.Config(file=options.config, dbname=options.dbname)
|
||||
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['global_lock'] = self.lock
|
||||
if (os.sep=="/"):
|
||||
|
@ -604,19 +708,29 @@ class fpdb:
|
|||
self.settings.update(self.config.get_import_parameters())
|
||||
self.settings.update(self.config.get_default_paths())
|
||||
|
||||
if self.db is not None and self.db.fdb is not None:
|
||||
if self.db is not None and self.db.connected:
|
||||
self.db.disconnect()
|
||||
|
||||
self.sql = SQL.Sql(db_server = self.settings['db-server'])
|
||||
err_msg = None
|
||||
try:
|
||||
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:
|
||||
self.warning_box("MySQL Server reports: Access denied. Are your permissions set correctly?")
|
||||
exit()
|
||||
err_msg = "MySQL Server reports: Access denied. Are your permissions set correctly?"
|
||||
except Exceptions.FpdbMySQLNoDatabase:
|
||||
msg = "MySQL client reports: 2002 error. Unable to connect - Please check that the MySQL service has been started"
|
||||
self.warning_box(msg)
|
||||
exit
|
||||
err_msg = "MySQL client reports: 2002 or 2003 error. Unable to connect - " \
|
||||
+ "Please check that the MySQL service has been started"
|
||||
except Exceptions.FpdbPostgresqlAccessDenied:
|
||||
err_msg = "Postgres Server reports: Access denied. Are your permissions set correctly?"
|
||||
except Exceptions.FpdbPostgresqlNoDatabase:
|
||||
err_msg = "Postgres client reports: Unable to connect - " \
|
||||
+ "Please check that the Postgres service has been started"
|
||||
if err_msg is not None:
|
||||
self.db = None
|
||||
self.warning_box(err_msg)
|
||||
|
||||
# except FpdbMySQLFailedError:
|
||||
# self.warning_box("Unable to connect to MySQL! Is the MySQL server running?!", "FPDB ERROR")
|
||||
|
@ -634,7 +748,7 @@ class fpdb:
|
|||
# print "*** Error: " + err[2] + "(" + str(err[1]) + "): " + str(sys.exc_info()[1])
|
||||
# sys.stderr.write("Failed to connect to %s database with username %s." % (self.settings['db-server'], self.settings['db-user']))
|
||||
|
||||
if self.db.wrongDbVersion:
|
||||
if self.db is not None and self.db.wrongDbVersion:
|
||||
diaDbVersionWarning = gtk.Dialog(title="Strong Warning - Invalid database version", parent=None, flags=0, buttons=(gtk.STOCK_OK,gtk.RESPONSE_OK))
|
||||
|
||||
label = gtk.Label("An invalid DB version or missing tables have been detected.")
|
||||
|
@ -653,14 +767,15 @@ class fpdb:
|
|||
diaDbVersionWarning.destroy()
|
||||
|
||||
if self.status_bar is None:
|
||||
self.status_bar = gtk.Label("Status: Connected to %s database named %s on host %s"%(self.db.get_backend_name(),self.db.database, self.db.host))
|
||||
self.status_bar = gtk.Label("")
|
||||
self.main_vbox.pack_end(self.status_bar, False, True, 0)
|
||||
self.status_bar.show()
|
||||
else:
|
||||
self.status_bar.set_text("Status: Connected to %s database named %s on host %s" % (self.db.get_backend_name(),self.db.database, self.db.host))
|
||||
|
||||
# Database connected to successfully, load queries to pass on to other classes
|
||||
self.db.rollback()
|
||||
if self.db is not None and self.db.connected:
|
||||
self.status_bar.set_text("Status: Connected to %s database named %s on host %s"
|
||||
% (self.db.get_backend_name(),self.db.database, self.db.host))
|
||||
# rollback to make sure any locks are cleared:
|
||||
self.db.rollback()
|
||||
|
||||
self.validate_config()
|
||||
|
||||
|
@ -681,7 +796,11 @@ class fpdb:
|
|||
# TODO: can we get some / all of the stuff done in this function to execute on any kind of abort?
|
||||
print "Quitting normally"
|
||||
# TODO: check if current settings differ from profile, if so offer to save or abort
|
||||
self.db.disconnect()
|
||||
try:
|
||||
if self.db is not None and self.db.connected:
|
||||
self.db.disconnect()
|
||||
except _mysql_exceptions.OperationalError: # oh, damn, we're already disconnected
|
||||
pass
|
||||
self.statusIcon.set_visible(False)
|
||||
gtk.main_quit()
|
||||
|
||||
|
@ -748,7 +867,6 @@ This program is licensed under the AGPL3, see docs"""+os.sep+"agpl-3.0.txt")
|
|||
self.add_and_display_tab(gv_tab, "Graphs")
|
||||
|
||||
def __init__(self):
|
||||
self.threads = []
|
||||
# no more than 1 process can this lock at a time:
|
||||
self.lock = interlocks.InterProcessLock(name="fpdb_global_lock")
|
||||
self.db = None
|
||||
|
@ -772,23 +890,35 @@ This program is licensed under the AGPL3, see docs"""+os.sep+"agpl-3.0.txt")
|
|||
menubar.show()
|
||||
#done menubar
|
||||
|
||||
self.threads = [] # objects used by tabs - no need for threads, gtk handles it
|
||||
self.closeq = Queue.Queue(20) # used to signal ending of a thread (only logviewer for now)
|
||||
|
||||
self.nb = gtk.Notebook()
|
||||
self.nb.set_show_tabs(True)
|
||||
self.nb.show()
|
||||
self.main_vbox.pack_start(self.nb, True, True, 0)
|
||||
self.pages=[]
|
||||
self.tabs=[]
|
||||
self.tab_names=[]
|
||||
self.nb_tabs=[]
|
||||
self.tabs=[] # the event_boxes forming the actual tabs
|
||||
self.tab_names=[] # names of tabs used since program started, not removed if tab is closed
|
||||
self.pages=[] # the contents of the page, not removed if tab is closed
|
||||
self.nb_tab_names=[] # list of tab names currently displayed in notebook
|
||||
|
||||
self.tab_main_help(None, None)
|
||||
|
||||
self.window.show()
|
||||
self.load_profile()
|
||||
|
||||
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()
|
||||
if os.path.exists('../gfx/fpdb-cards.png'):
|
||||
self.statusIcon.set_from_file('../gfx/fpdb-cards.png')
|
||||
# use getcwd() here instead of sys.path[0] so that py2exe works:
|
||||
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'):
|
||||
self.statusIcon.set_from_file('/usr/share/pixmaps/fpdb-cards.png')
|
||||
else:
|
||||
|
@ -847,16 +977,24 @@ This program is licensed under the AGPL3, see docs"""+os.sep+"agpl-3.0.txt")
|
|||
self.window.show()
|
||||
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"):
|
||||
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))
|
||||
|
||||
label = gtk.Label(str)
|
||||
diaWarning.vbox.add(label)
|
||||
label.show()
|
||||
label = gtk.Label(str)
|
||||
diaWarning.vbox.add(label)
|
||||
label.show()
|
||||
|
||||
response = diaWarning.run()
|
||||
diaWarning.destroy()
|
||||
return response
|
||||
response = diaWarning.run()
|
||||
diaWarning.destroy()
|
||||
return response
|
||||
|
||||
def validate_config(self):
|
||||
hhbase = self.config.get_import_parameters().get("hhArchiveBase")
|
||||
|
|
|
@ -16,208 +16,6 @@
|
|||
#In the "official" distribution you can find the license in
|
||||
#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
|
||||
|
|
212
pyfpdb/fpdb_import.py
Normal file → Executable file
212
pyfpdb/fpdb_import.py
Normal file → Executable file
|
@ -30,19 +30,19 @@ import Queue
|
|||
from collections import deque # using Queue for now
|
||||
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 gtk
|
||||
|
||||
# fpdb/FreePokerTools modules
|
||||
|
||||
import fpdb_simple
|
||||
import fpdb_db
|
||||
import Database
|
||||
import fpdb_parse_logic
|
||||
import Configuration
|
||||
import Exceptions
|
||||
|
||||
log = Configuration.get_logger("logging.conf", "importer")
|
||||
|
||||
# database interface modules
|
||||
try:
|
||||
|
@ -68,6 +68,7 @@ class Importer:
|
|||
self.config = config
|
||||
self.sql = sql
|
||||
|
||||
#log = Configuration.get_logger("logging.conf", "importer", log_dir=self.config.dir_log)
|
||||
self.filelist = {}
|
||||
self.dirlist = {}
|
||||
self.siteIds = {}
|
||||
|
@ -100,8 +101,6 @@ class Importer:
|
|||
for i in xrange(self.settings['threads']):
|
||||
self.writerdbs.append( Database.Database(self.config, sql = self.sql) )
|
||||
|
||||
self.NEWIMPORT = Configuration.NEWIMPORT
|
||||
|
||||
clock() # init clock in windows
|
||||
|
||||
#Set functions
|
||||
|
@ -370,7 +369,7 @@ class Importer:
|
|||
pass
|
||||
(stored, duplicates, partial, errors, ttime) = self.import_file_dict(self.database, file, self.filelist[file][0], self.filelist[file][1], None)
|
||||
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))
|
||||
except KeyError: # TODO: Again, what error happens here? fix when we find out ..
|
||||
pass
|
||||
|
@ -407,9 +406,9 @@ class Importer:
|
|||
return (0,0,0,0,0)
|
||||
|
||||
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
|
||||
if self.settings['threads'] > 0 and self.writeq is not None:
|
||||
|
@ -429,24 +428,36 @@ class Importer:
|
|||
mod = __import__(filter)
|
||||
obj = getattr(mod, filter_name, None)
|
||||
if callable(obj):
|
||||
hhc = obj(in_path = file, out_path = out_path, index = 0, starsArchive = self.settings['starsArchive']) # Index into file 0 until changeover
|
||||
if hhc.getStatus() and self.NEWIMPORT == False:
|
||||
(stored, duplicates, partial, errors, ttime) = self.import_fpdb_file(db, out_path, site, q)
|
||||
elif hhc.getStatus() and self.NEWIMPORT == True:
|
||||
#This code doesn't do anything yet
|
||||
idx = 0
|
||||
if file in self.pos_in_file:
|
||||
idx = self.pos_in_file[file]
|
||||
else:
|
||||
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()
|
||||
self.pos_in_file[file] = hhc.getLastCharacterRead()
|
||||
to_hud = []
|
||||
|
||||
for hand in handlist:
|
||||
if hand is not None:
|
||||
#try, except duplicates here?
|
||||
hand.prepInsert(self.database)
|
||||
hand.insert(self.database)
|
||||
if self.callHud and hand.dbid_hands != 0:
|
||||
to_hud.append(hand.dbid_hands)
|
||||
else:
|
||||
try:
|
||||
hand.insert(self.database)
|
||||
except Exceptions.FpdbHandDuplicate:
|
||||
duplicates += 1
|
||||
else:
|
||||
if self.callHud and hand.dbid_hands != 0:
|
||||
to_hud.append(hand.dbid_hands)
|
||||
else: # TODO: Treat empty as an error, or just ignore?
|
||||
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:
|
||||
hand.updateHudCache(self.database)
|
||||
self.database.commit()
|
||||
|
||||
#pipe the Hands.id out to the HUD
|
||||
|
@ -456,174 +467,21 @@ class Importer:
|
|||
|
||||
errors = getattr(hhc, 'numErrors')
|
||||
stored = getattr(hhc, 'numHands')
|
||||
stored -= duplicates
|
||||
else:
|
||||
# conversion didn't work
|
||||
# TODO: appropriate response?
|
||||
return (0, 0, 0, 1, 0)
|
||||
return (0, 0, 0, 1, time() - ttime)
|
||||
else:
|
||||
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
|
||||
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):
|
||||
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."
|
||||
|
|
|
@ -1,64 +1,75 @@
|
|||
[loggers]
|
||||
keys=root,parser,importer,config,db
|
||||
keys=root,fpdb,logview,parser,importer,config,db,hud,filter
|
||||
|
||||
[handlers]
|
||||
keys=consoleHandler,fileHandler
|
||||
keys=consoleHandler,rotatingFileHandler
|
||||
|
||||
[formatters]
|
||||
keys=fileFormatter,stderrFormatter
|
||||
|
||||
[logger_root]
|
||||
level=INFO
|
||||
handlers=consoleHandler,fileHandler
|
||||
handlers=consoleHandler,rotatingFileHandler
|
||||
|
||||
[logger_fpdb]
|
||||
level=INFO
|
||||
handlers=consoleHandler,fileHandler
|
||||
handlers=consoleHandler,rotatingFileHandler
|
||||
qualname=fpdb
|
||||
propagate=0
|
||||
|
||||
[logger_logview]
|
||||
level=INFO
|
||||
handlers=consoleHandler,fileHandler
|
||||
handlers=consoleHandler,rotatingFileHandler
|
||||
qualname=logview
|
||||
propagate=0
|
||||
|
||||
[logger_parser]
|
||||
level=INFO
|
||||
handlers=consoleHandler,fileHandler
|
||||
handlers=consoleHandler,rotatingFileHandler
|
||||
qualname=parser
|
||||
propagate=0
|
||||
|
||||
[logger_importer]
|
||||
level=DEBUG
|
||||
handlers=consoleHandler,fileHandler
|
||||
handlers=consoleHandler,rotatingFileHandler
|
||||
qualname=importer
|
||||
propagate=0
|
||||
|
||||
[logger_config]
|
||||
level=INFO
|
||||
handlers=consoleHandler,fileHandler
|
||||
handlers=consoleHandler,rotatingFileHandler
|
||||
qualname=config
|
||||
propagate=0
|
||||
|
||||
[logger_db]
|
||||
level=DEBUG
|
||||
handlers=consoleHandler,fileHandler
|
||||
handlers=consoleHandler,rotatingFileHandler
|
||||
qualname=db
|
||||
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]
|
||||
class=StreamHandler
|
||||
level=ERROR
|
||||
formatter=stderrFormatter
|
||||
args=(sys.stderr,)
|
||||
|
||||
[handler_fileHandler]
|
||||
class=FileHandler
|
||||
[handler_rotatingFileHandler]
|
||||
class=handlers.RotatingFileHandler
|
||||
level=DEBUG
|
||||
formatter=fileFormatter
|
||||
args=('logging.out', 'a')
|
||||
|
||||
args=('%(logFile)s', 'a', 100000000, 5)
|
||||
|
||||
[formatter_fileFormatter]
|
||||
format=%(asctime)s - %(name)-12s %(levelname)-8s %(message)s
|
||||
|
|
|
@ -22,52 +22,155 @@ Py2exe script for fpdb.
|
|||
|
||||
########################################################################
|
||||
|
||||
#TODO: change GuiAutoImport so that it knows to start HUD_main.exe, when appropriate
|
||||
# include the lib needed to handle png files in mucked
|
||||
#TODO:
|
||||
# get rid of all the uneeded libraries (e.g., pyQT)
|
||||
# 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:
|
||||
#
|
||||
# cd to the folder where this script is stored, usually .../pyfpdb.
|
||||
# If there are build and dist subfolders present , delete them to get
|
||||
# rid of earlier builds.
|
||||
# Run the script with "py2exe_setup.py py2exe"
|
||||
# You will frequently get messages about missing .dll files. E. g.,
|
||||
#- cd to the folder where this script is stored, usually .../pyfpdb.
|
||||
# [If there are build and dist subfolders present , delete them to get
|
||||
# rid of earlier builds. Update: script now does this for you]
|
||||
#- Run the script with "py2exe_setup.py py2exe"
|
||||
#- You will frequently get messages about missing .dll files. E. g.,
|
||||
# MSVCP90.dll. These are somewhere in your windows install, so you
|
||||
# can just copy them to your working folder.
|
||||
# If it works, you'll have 2 new folders, build and dist. Build is
|
||||
# working space and should be deleted. Dist contains the files to be
|
||||
# distributed. Last, you must copy the etc/, lib/ and share/ folders
|
||||
# from your gtk/bin/ folder to the dist 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.
|
||||
# can just copy them to your working folder. (or just assume other
|
||||
# person will have them? any copyright issues with including them?)
|
||||
#- [ If it works, you'll have 3 new folders, build and dist and gfx. Build is
|
||||
# working space and should be deleted. Dist and gfx contain the files to be
|
||||
# distributed. ]
|
||||
# If it works, you'll have a new dir fpdb-XXX-YYYYMMDD-exe which should
|
||||
# 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
|
||||
import py2exe
|
||||
import glob
|
||||
import matplotlib
|
||||
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(
|
||||
name = 'fpdb',
|
||||
description = 'Free Poker DataBase',
|
||||
version = '0.12',
|
||||
|
||||
console = [ {'script': 'fpdb.py', },
|
||||
console = [ {'script': 'fpdb.py', "icon_resources": [(1, "../gfx/fpdb_large_icon.ico")]},
|
||||
{'script': 'HUD_main.py', },
|
||||
{'script': 'Configuration.py', }
|
||||
],
|
||||
|
||||
options = {'py2exe': {
|
||||
'packages' :'encodings',
|
||||
'includes' : 'cairo, pango, pangocairo, atk, gobject, PokerStarsToFpdb',
|
||||
'excludes' : '_tkagg, _agg2, cocoaagg, fltkagg',
|
||||
'dll_excludes': 'libglade-2.0-0.dll',
|
||||
'packages' : ['encodings', 'matplotlib'],
|
||||
'includes' : ['cairo', 'pango', 'pangocairo', 'atk', 'gobject'
|
||||
,'matplotlib.numerix.random_array'
|
||||
,'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',
|
||||
'Cards01.png',
|
||||
'logging.conf',
|
||||
(r'matplotlibdata', glob.glob(r'c:\python26\Lib\site-packages\matplotlib\mpl-data\*'))
|
||||
]
|
||||
# files in 2nd value in tuple are moved to dir named in 1st value
|
||||
data_files = [('', ['HUD_config.xml.example', 'Cards01.png', 'logging.conf'])
|
||||
,(dist_dir, [r'..\run_fpdb.bat'])
|
||||
,( 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 )
|
||||
|
||||
|
||||
|
|
|
@ -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 os
|
||||
import Configuration
|
||||
|
||||
config_path = Configuration.get_default_config_path()
|
||||
|
||||
try:
|
||||
import gobject as _gobject
|
||||
print "Import of gobject:\tSuccess"
|
||||
import pygtk
|
||||
print "Import of pygtk:\tSuccess"
|
||||
pygtk.require('2.0')
|
||||
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.set_title("Test GTK")
|
||||
|
@ -42,7 +69,7 @@ try:
|
|||
(gtk.STOCK_CLOSE, gtk.RESPONSE_OK))
|
||||
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)
|
||||
l.show()
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
import sqlite3
|
||||
import fpdb_db
|
||||
import Database
|
||||
import math
|
||||
|
||||
# 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)
|
||||
|
||||
#Mod function
|
||||
tmp = fpdb_db.sqlitemath()
|
||||
tmp = Database.sqlitemath()
|
||||
con.create_function("mod", 2, tmp.mod)
|
||||
|
||||
# Aggregate function VARIANCE()
|
||||
con.create_aggregate("variance", 1, fpdb_db.VARIANCE)
|
||||
con.create_aggregate("variance", 1, Database.VARIANCE)
|
||||
|
||||
|
||||
cur = con.cursor()
|
||||
|
|
46
pyfpdb/test_PokerStars.py
Normal file → Executable file
46
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'}
|
||||
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")
|
||||
|
||||
hhc.compilePlayerRegexs(h)
|
||||
|
@ -39,7 +39,7 @@ def checkGameInfo(hhc, header, info):
|
|||
assert hhc.determineGameType(header) == info
|
||||
|
||||
def testGameInfo():
|
||||
hhc = PokerStarsToFpdb.PokerStars(autostart=False)
|
||||
hhc = PokerStarsToFpdb.PokerStars(config, autostart=False)
|
||||
pairs = (
|
||||
(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'}),
|
||||
|
@ -82,18 +82,21 @@ def testFlopImport():
|
|||
# River: hero (continuation bets?) all-in and is not called
|
||||
importer.addBulkImportImportFileOrDir(
|
||||
"""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)
|
||||
(stored, dups, partial, errs, ttime) = importer.runImport()
|
||||
print "DEBUG: stored: %s dups: %s partial: %s errs: %s ttime: %s" %(stored, dups, partial, errs, ttime)
|
||||
importer.clearFileList()
|
||||
|
||||
col = { 'sawShowdown': 2
|
||||
col = { 'sawShowdown': 2, 'street0Aggr':3
|
||||
}
|
||||
|
||||
q = """SELECT
|
||||
s.name,
|
||||
p.name,
|
||||
hp.sawShowdown
|
||||
hp.sawShowdown,
|
||||
hp.street0Aggr
|
||||
FROM
|
||||
Hands as h,
|
||||
Sites as s,
|
||||
|
@ -114,6 +117,33 @@ and s.id = p.siteid"""
|
|||
# Assert if any sawShowdown = True
|
||||
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():
|
||||
db.recreate_tables()
|
||||
importer = fpdb_import.Importer(False, settings, config)
|
||||
|
@ -148,11 +178,7 @@ def testDrawImport():
|
|||
(stored, dups, partial, errs, ttime) = importer.runImport()
|
||||
importer.clearFileList()
|
||||
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
|
||||
assert 1 == 1
|
||||
|
|
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
Executable file
33
run_fpdb.py
Executable 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