diff --git a/packaging/announce-0.22.txt b/packaging/announce-0.22.txt new file mode 100644 index 00000000..dedee525 --- /dev/null +++ b/packaging/announce-0.22.txt @@ -0,0 +1,112 @@ +Hello everyone, +The new fpdb release 0.22 is now available for download as source or as packages/installers for Debian, Gentoo, Ubuntu and Windows. +This version brings many improvements and bugfixes. A quick word on our version naming: We have decided to stop using the usual alpha/beta/rc release system as it doesn't suit fpdb's development style and resources. From now on all releases are created equal (ie. considered "testing"). If they proof themselves we declare them "stable" and make them the default download on sourceforge. If you wish to be notified of new releases or stabilisation please subscribe to the fpdb-announe mailing list (see Links further down). Since this version has been available for some time we consider it stable and recommend all users to update. + +So... what is FPDB? +=================== +FPDB (Free Poker Database) is a free/open source suite of steadily growing tools to track and analyse your online poker game. FPDB is able to import the hand histories that poker sites write to your computer and to store additional data from each hand in a database for use in later analysis. + +FPDB is ambitious, intending to support all games. Currently we support flop based games (Hold’em, Omaha, Omaha Hi/Lo), stud games (7 Card Stud, 7 Card Stud Hi/Lo, and Razz) and draw games (Triple Draw Lowball, 5 Card Draw) on most major poker sites. + +FPDB provides a Heads Up Display (HUD), Profit Grapher and Stats viewers with a list of features that is growing. + +What's changed +============== +417 changesets (incl. merges) have gone in since 0.21-rc2. Some of the improvements are: + +New stats: + Three bet: name="three_B" (note this stat was previously "three_B_0") + Four bet: name="four_B" + Cold four bet: name="cfour_B" + Squeeze bet preflop: name="squeeze" + Fold to three bet preflop: name="f_3bet" + Fold to four bet preflop: name="f_4bet" + Steal Success : name="s_steal" + +Pokerstars Home Games now supported +Everest now supported +Winamax now supported + +Translation progress: + Complete: English, Hungarian. If you notice an untranslated string in any output please let us know. + Partial: French (85%), German (32%), Polish (27%), Spanish (17%), Russian (6%). + + +Significant work on the underlying HUD code, including hud dragging and table closing +Problems where some stats became > 100% are now fixed +sqlite database performance improvements (sqlite is the default database for fpdb) +pokerstars.it tournament HUD now working +Cap games added to filter options +Initial work to support Rush poker - this will need further testing and improvement before we can release it. One or two testers would be welcome, please PM gimick if you want to help. + +Various updates and fixes when importing Hand histories. +Party freeroll tournaments now recognised. +Stars 20/40 limit and FTP 1k/2k limit now recognised. +Initial work on a Stove function for holdem (see the viewers menu) +The windows exe has been upgraded from python 2.6 to python 2.7. + + +Where to get it +=============== +Please note that you will have to either recreate your database or use a new one if you're updating from 0.21-rc2 or older. Fpdb will warn you if your database is incompatible and will not work unless you recreate or use a different one. +Config files from 0.20 and later should work. Please report if you have problems with config files from that version or later. +You can find checksums (MD5 and SHA512) in the download folder. +To download: http://sourceforge.net/projects/fpdb/files/fpdb/0.22/ + +Links +===== +To be notified by email of new versions you can subscribe to our announce mailing list here: https://lists.sourceforge.net/lists/listinfo/fpdb-announce +Link to fpdb website (wiki) http://fpdb.wiki.sourceforge.net/ +Features page: http://sourceforge.net/apps/mediawiki/fpdb/index.php?title=Features +How to install&setup fpdb in 5 Minutes with the Windows installer: http://sf.net/projects/fpdb/files/fpdb/help/ Choose exe021install.avi (18mb, no sound) +Installation instructions are available from: http://sourceforge.net/apps/mediawiki/fpdb/index.php?title=Installation +Getting Started Instructions: http://sourceforge.net/apps/mediawiki/fpdb/index.php?title=Getting_Started +FAQ: http://sourceforge.net/apps/mediawiki/fpdb/index.php?title=FAQ +Screenshots: http://sourceforge.net/apps/mediawiki/fpdb/index.php?title=Screenshots + +How to help +=========== +Join us in IRC (see below) and say you want to do something :) + +How to get help +=============== +Please also see the "Bug reports" section below to enable us to help you. +These are listed in order of usefulness - the 2+2 thread in particular is rather chaotic so if we miss your post feel free to bring it up again +1) Chat/IRC +One of the developers is usually around, but it is IRC. Dont get discouraged because we didn't answer within 10 minutes, once we do you've probably got our full attention. +This link takes you to a website where you can talk to us directly: http://webchat.freenode.net/?channels=fpdb +Alternatively use can use any IRC client to connect to #fpdb on freenode.net +2) Mailing List +Visit http://lists.sourceforge.net/lists/listinfo/fpdb-main to subscribe to the development&support mailing list and send us an email +3) The monster 2+2 forum thread (registration required). +http://forumserver.twoplustwo.com/16...2009-a-273001/ Please dont let the size of the thread phase you. It is monitored and sends email notifications to some of the developers. (Note: If you play poker and don't frequent the 2+2 forums, you may want to become better acquainted. Its a bit daunting at first, but well worth the effort) + +Bug reports +=========== +When you do report issues, try to make it easier for us help you. We usually need at least the following information: +- Operating system and version (e.g. Ubuntu 10.10 or Windows XP) +- Database type (MySQL, SQLite, PostgreSQL) +- Install method (exe, tarball, deb, git, ebuild) +- Fpdb version +- Site name and gametype (e.g. $10 Pokerstars Razz SnG) +You can find most of these details for easy copy&paste in the Help menu under About. + +Important known problems +======================== +Hud will not work if using postgres database - fixed in 0.23 +PKR is not working correctly + +Special information for Windows installations +============================================= +First-time installs will need to download and install the C++ runtime executable from Microsoft. +Windows 7 and windows Vista users will need to set fpdb.exe and hud_main.exe to run as administrator (see http://sourceforge.net/apps/mediawiki/fpdb/index.php?title=Install_in_Windows) + +Contributors +============ +Code/translations: Worros, Donoban, Nutomatic, Steffen, Chaz Littlejohn, Scott Wolchok, tribumarchal, Erki Ferenc, Eric Blade, atinm, Mika Bostrom, Robert Wielinga, Chris Moore, Gerko de Roo, Gimick +Special shoutout to Eleatic Stranger who has written an initial converter for Carbon poker, without ever having played on the site! +Documentation: RSOAndrew, Buggernaut, ChazDazzle +Testers and everyone answering forum questions - xaviax, Dog, uncooper etc. You know who you are. +Hand History donations: Quite a few people on this list now. Every donation for alternate sites and various corner cases is appreciated. + +And a particular thanks to all the libraries, interpreters etc. that we use to create fpdb! \ No newline at end of file diff --git a/packaging/debian/changelog b/packaging/debian/changelog index 4c48b057..604b4e21 100644 --- a/packaging/debian/changelog +++ b/packaging/debian/changelog @@ -1,3 +1,15 @@ +free-poker-tools (0.22-1) unstable; urgency=low + + * New release: 0.22 + + -- Mika Bostrom Sun, 13 Mar 2011 05:15:24 +0200 + +free-poker-tools (0.21-1) unstable; urgency=low + + * New release: 0.21 + + -- Mika Bostrom Mon, 28 Feb 2011 04:09:57 +0200 + free-poker-tools (0.21~rc2) unstable; urgency=low * Second 0.21 release-candidate diff --git a/packaging/gentoo/current_testing.ebuild b/packaging/gentoo/current.ebuild similarity index 60% rename from packaging/gentoo/current_testing.ebuild rename to packaging/gentoo/current.ebuild index 7abb0f96..66c07db4 100644 --- a/packaging/gentoo/current_testing.ebuild +++ b/packaging/gentoo/current.ebuild @@ -10,14 +10,14 @@ inherit eutils games DESCRIPTION="A free/open source tracker/HUD for use with online poker" HOMEPAGE="http://fpdb.wiki.sourceforge.net/" -SRC_URI="mirror://sourceforge/${PN}/Snapshots/${P}.tar.bz2" +SRC_URI="mirror://sourceforge/${PN}/${PV}/${P}.tar.bz2" LICENSE="AGPL-3" SLOT="0" KEYWORDS="~amd64 ~x86" -#note: this should work on other architectures too, please send me your experiences +#note: fpdb has only been tested on x86 and amd64, but should work on other arches, too -IUSE="graph mysql postgres sqlite linguas_de linguas_hu linguas_fr" +IUSE="graph mysql postgres sqlite linguas_de linguas_es linguas_fr linguas_hu linguas_it linguas_pl linguas_ru" RDEPEND=" mysql? ( virtual/mysql dev-python/mysql-python ) @@ -39,8 +39,32 @@ src_install() { doins -r gfx || die "failed to install gfx directory" doins -r pyfpdb || die "failed to install pyfpdb directory" + if use linguas_de; then + msgfmt pyfpdb/locale/fpdb-de_DE.po -o pyfpdb/locale/de.mo || die "failed to create German mo file" + fi + + if use linguas_es; then + msgfmt pyfpdb/locale/fpdb-es_ES.po -o pyfpdb/locale/es.mo || die "failed to create Spanish mo file" + fi + + if use linguas_fr; then + msgfmt pyfpdb/locale/fpdb-fr_FR.po -o pyfpdb/locale/fr.mo || die "failed to create French mo file" + fi + if use linguas_hu; then - msgfmt pyfpdb/locale/fpdb-hu_HU.po -o pyfpdb/locale/hu.mo || die "failed to create hungarian mo file" + msgfmt pyfpdb/locale/fpdb-hu_HU.po -o pyfpdb/locale/hu.mo || die "failed to create Hungarian mo file" + fi + + if use linguas_it; then + msgfmt pyfpdb/locale/fpdb-it_IT.po -o pyfpdb/locale/it.mo || die "failed to create Italian mo file" + fi + + if use linguas_pl; then + msgfmt pyfpdb/locale/fpdb-pl_PL.po -o pyfpdb/locale/pl.mo || die "failed to create Polish mo file" + fi + + if use linguas_ru; then + msgfmt pyfpdb/locale/fpdb-ru_RU.po -o pyfpdb/locale/ru.mo || die "failed to create Russian mo file" fi domo pyfpdb/locale/*.mo || die "failed to install mo files" @@ -51,7 +75,7 @@ src_install() { doexe run_fpdb.py || die "failed to install executable run_fpdb.py" dodir "${GAMES_BINDIR}" - dosym "${GAMES_DATADIR}"/${PN}/run_fpdb.py "${GAMES_BINDIR}"/${PN} || die "failed to create symlink for starting fpdb" + dosym "${GAMES_DATADIR}"/${PN}/run_fpdb.py "${GAMES_BINDIR}"/${PN} || die "failed to create symlink for starting fpdb" newicon gfx/fpdb-icon.png ${PN}.png || die "failed to install fpdb icon" make_desktop_entry ${PN} || die "failed to create desktop entry" diff --git a/packaging/gentoo/current_stable.ebuild b/packaging/gentoo/current_stable.ebuild deleted file mode 100644 index 2d458f95..00000000 --- a/packaging/gentoo/current_stable.ebuild +++ /dev/null @@ -1,60 +0,0 @@ -# Copyright 1999-2011 Gentoo Foundation -# Distributed under the terms of the GNU General Public License v2 -# $Header: $ - -#TODO: Header, add cdecimal - -EAPI="2" - -inherit eutils games - -DESCRIPTION="A free/open source tracker/HUD for use with online poker" -HOMEPAGE="http://fpdb.wiki.sourceforge.net/" -SRC_URI="mirror://sourceforge/${PN}/${PV}/${P}.tar.bz2" - -LICENSE="AGPL-3" -SLOT="0" -KEYWORDS="~amd64 ~x86" -#note: this should work on other architectures too, please send me your experiences - -IUSE="graph mysql postgres sqlite" -RDEPEND=" - mysql? ( virtual/mysql - dev-python/mysql-python ) - postgres? ( dev-db/postgresql-server - dev-python/psycopg ) - sqlite? ( dev-lang/python[sqlite] - dev-python/numpy ) - >=x11-libs/gtk+-2.10 - dev-python/pygtk - graph? ( dev-python/numpy - dev-python/matplotlib[gtk] ) - dev-python/python-xlib - dev-python/pytz" -DEPEND="${RDEPEND}" - -src_install() { - insinto "${GAMES_DATADIR}"/${PN} - doins -r gfx - doins -r pyfpdb - doins readme.txt - - exeinto "${GAMES_DATADIR}"/${PN} - doexe run_fpdb.py - - dodir "${GAMES_BINDIR}" - dosym "${GAMES_DATADIR}"/${PN}/run_fpdb.py "${GAMES_BINDIR}"/${PN} - - newicon gfx/fpdb-icon.png ${PN}.png - make_desktop_entry ${PN} - - fperms +x "${GAMES_DATADIR}"/${PN}/pyfpdb/*.pyw - prepgamesdirs -} - -pkg_postinst() { - games_pkg_postinst - elog "Note that if you really want to use mysql or postgresql you will have to create" - elog "the database and user yourself and enter it into the fpdb config." - elog "You can find the instructions on the project's website." -} diff --git a/packaging/gentoo/fpdb-9999.ebuild b/packaging/gentoo/fpdb-9999.ebuild index 2569e002..f65255eb 100644 --- a/packaging/gentoo/fpdb-9999.ebuild +++ b/packaging/gentoo/fpdb-9999.ebuild @@ -15,8 +15,9 @@ EGIT_REPO_URI="git://git.assembla.com/fpdb.git" LICENSE="AGPL-3" SLOT="0" KEYWORDS="" +#note: fpdb has only been tested on x86 and amd64, but should work on other arches, too -IUSE="graph mysql postgres sqlite linguas_de linguas_hu linguas_fr" +IUSE="graph mysql postgres sqlite linguas_de linguas_es linguas_fr linguas_hu linguas_it linguas_pl linguas_ru" RDEPEND=" mysql? ( virtual/mysql dev-python/mysql-python ) @@ -29,7 +30,7 @@ RDEPEND=" graph? ( dev-python/numpy dev-python/matplotlib[gtk] ) dev-python/python-xlib - dev-python/pytz" + x11-apps/xwininfo" DEPEND="${RDEPEND}" src_unpack() { @@ -38,29 +39,49 @@ src_unpack() { src_install() { insinto "${GAMES_DATADIR}"/${PN} - doins -r gfx - doins -r pyfpdb + doins -r gfx || die "failed to install gfx directory" + doins -r pyfpdb || die "failed to install pyfpdb directory" if use linguas_de; then - msgfmt pyfpdb/locale/fpdb-de_DE.po -o pyfpdb/locale/de.mo + msgfmt pyfpdb/locale/fpdb-de_DE.po -o pyfpdb/locale/de.mo || die "failed to create German mo file" + fi + + if use linguas_es; then + msgfmt pyfpdb/locale/fpdb-es_ES.po -o pyfpdb/locale/es.mo || die "failed to create Spanish mo file" + fi + + if use linguas_fr; then + msgfmt pyfpdb/locale/fpdb-fr_FR.po -o pyfpdb/locale/fr.mo || die "failed to create French mo file" fi if use linguas_hu; then - msgfmt pyfpdb/locale/fpdb-hu_HU.po -o pyfpdb/locale/hu.mo + msgfmt pyfpdb/locale/fpdb-hu_HU.po -o pyfpdb/locale/hu.mo || die "failed to create Hungarian mo file" fi - domo pyfpdb/locale/*.mo + if use linguas_it; then + msgfmt pyfpdb/locale/fpdb-it_IT.po -o pyfpdb/locale/it.mo || die "failed to create Italian mo file" + fi - doins readme.txt + if use linguas_pl; then + msgfmt pyfpdb/locale/fpdb-pl_PL.po -o pyfpdb/locale/pl.mo || die "failed to create Polish mo file" + fi + + if use linguas_ru; then + msgfmt pyfpdb/locale/fpdb-ru_RU.po -o pyfpdb/locale/ru.mo || die "failed to create Russian mo file" + fi + + domo pyfpdb/locale/*.mo || die "failed to install mo files" + + doins readme.txt || die "failed to install readme.txt file" exeinto "${GAMES_DATADIR}"/${PN} - doexe run_fpdb.py + doexe run_fpdb.py || die "failed to install executable run_fpdb.py" dodir "${GAMES_BINDIR}" - dosym "${GAMES_DATADIR}"/${PN}/run_fpdb.py "${GAMES_BINDIR}"/${PN} + dosym "${GAMES_DATADIR}"/${PN}/run_fpdb.py "${GAMES_BINDIR}"/${PN} || die "failed to create symlink for starting fpdb" - newicon gfx/fpdb-icon.png ${PN}.png - make_desktop_entry ${PN} + newicon gfx/fpdb-icon.png ${PN}.png || die "failed to install fpdb icon" + make_desktop_entry ${PN} || die "failed to create desktop entry" fperms +x "${GAMES_DATADIR}"/${PN}/pyfpdb/*.pyw prepgamesdirs diff --git a/pyfpdb/AbsoluteToFpdb.py b/pyfpdb/AbsoluteToFpdb.py index 26e0a564..8969459d 100755 --- a/pyfpdb/AbsoluteToFpdb.py +++ b/pyfpdb/AbsoluteToFpdb.py @@ -138,7 +138,7 @@ class Absolute(HandHistoryConverter): if not m: tmp = handText[0:100] log.error(_("Unable to recognise gametype from: '%s'") % tmp) - log.error(_("determineGameType: Raising FpdbParseError")) + log.error("determineGameType: " + _("Raising FpdbParseError")) raise FpdbParseError(_("Unable to recognise gametype from: '%s'") % tmp) @@ -349,7 +349,7 @@ class Absolute(HandHistoryConverter): bet = action.group('BET').replace(',', '') hand.addComplete( street, action.group('PNAME'), bet) else: - logging.debug(_("Unimplemented readAction: '%s' '%s'") % (action.group('PNAME'),action.group('ATYPE'))) + logging.debug(_("Unimplemented readAction: '%s' '%s'") % (action.group('PNAME'), action.group('ATYPE'))) def readShowdownActions(self, hand): diff --git a/pyfpdb/AlchemyFacilities.py b/pyfpdb/AlchemyFacilities.py deleted file mode 100644 index 260a6c1d..00000000 --- a/pyfpdb/AlchemyFacilities.py +++ /dev/null @@ -1,134 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - -#Copyright 2009-2011 Grigorij Indigirkin -#This program is free software: you can redistribute it and/or modify -#it under the terms of the GNU Affero General Public License as published by -#the Free Software Foundation, version 3 of the License. -# -#This program is distributed in the hope that it will be useful, -#but WITHOUT ANY WARRANTY; without even the implied warranty of -#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -#GNU General Public License for more details. -# -#You should have received a copy of the GNU Affero General Public License -#along with this program. If not, see . -#In the "official" distribution you can find the license in agpl-3.0.txt. - -#TODO: gettextify if file is used again - -from decimal_wrapper 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 - diff --git a/pyfpdb/AlchemyMappings.py b/pyfpdb/AlchemyMappings.py deleted file mode 100644 index 53f59a1a..00000000 --- a/pyfpdb/AlchemyMappings.py +++ /dev/null @@ -1,484 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - -#Copyright 2009-2011 Grigorij Indigirkin -#This program is free software: you can redistribute it and/or modify -#it under the terms of the GNU Affero General Public License as published by -#the Free Software Foundation, version 3 of the License. -# -#This program is distributed in the hope that it will be useful, -#but WITHOUT ANY WARRANTY; without even the implied warranty of -#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -#GNU General Public License for more details. -# -#You should have received a copy of the GNU Affero General Public License -#along with this program. If not, see . -#In the "official" distribution you can find the license in agpl-3.0.txt. - -"""@package AlchemyMappings -This package contains all classes to be mapped and mappers themselves -""" - -#TODO: gettextify if file is used again - -import logging -import re -from decimal_wrapper 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 '' % (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', 'currency'], - ['type', 'base', 'category', 'limitType', 'sb', 'bb', 'dummy', 'dummy', 'currency']) - 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', - 'rebuy': 'isRebuy', - 'addOn': 'isAddOn', - '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.startTime: - setattr(tour, col, min(db_val, hand.startTime)) - - 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 = TourneysPlayer.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','FT'), - (2 , 'PokerStars', 'PS'), - (3 , 'Everleaf', 'EV'), - (4 , 'Win2day', 'W2'), - (5 , 'OnGame', 'OG'), - (6 , 'UltimateBet', 'UB'), - (7 , 'Betfair', 'BF'), - (8 , 'Absolute', 'AB'), - (9 , 'PartyPoker', 'PP'), - (10, 'Partouche', 'PA'), - (11, 'Carbon', 'CA'), - (12, 'PKR', 'PK'), - ] - INITIAL_DATA_KEYS = ('id', 'name', 'code') - - 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 TourneyType 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 - rebuy addOn shootout matrix sng currency - """ - return get_or_create(cls, session, **kwargs)[0] - - -class TourneysPlayer(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(TourneysPlayer, 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 (TourneysPlayer, 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) - diff --git a/pyfpdb/AlchemyTables.py b/pyfpdb/AlchemyTables.py deleted file mode 100644 index 0e42375e..00000000 --- a/pyfpdb/AlchemyTables.py +++ /dev/null @@ -1,460 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - -#Copyright 2009-2011 Grigorij Indigirkin -#This program is free software: you can redistribute it and/or modify -#it under the terms of the GNU Affero General Public License as published by -#the Free Software Foundation, version 3 of the License. -# -#This program is distributed in the hope that it will be useful, -#but WITHOUT ANY WARRANTY; without even the implied warranty of -#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -#GNU General Public License for more details. -# -#You should have received a copy of the GNU Affero General Public License -#along with this program. If not, see . -#In the "official" distribution you can find the license in agpl-3.0.txt. - -"""@package AlchemyTables -Contains all sqlalchemy tables -""" - -#TODO: gettextify if file is used again - -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('currency', String(4), nullable=False), # varchar(4) NOT NULL - Column('type', String(4), nullable=False), # char(4) NOT NULL - Column('base', String(4), nullable=False), # char(4) NOT NULL - Column('category', String(9), nullable=False), # varchar(9) NOT NULL - 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('startTime', 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('code', String(2), nullable=False), # char(2) 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('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('totalRebuyCount', Integer, default=0), # INT DEFAULT 0 - Column('totalAddOnCount', Integer, default=0), # INT DEFAULT 0 - Column('comment', Text), # TEXT - Column('commentTs', DateTime), # DATETIME - mysql_charset='utf8', - 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('currency', String(4), nullable=False), # varchar(4) NOT NULL - Column('buyin', Integer, nullable=False), # INT NOT NULL - Column('fee', Integer, nullable=False), # INT NOT NULL - Column('buyInChips', Integer, nullable=False), # INT NOT NULL - Column('maxSeats', Boolean, nullable=False, default=-1), # INT NOT NULL DEFAULT -1 - Column('rebuy', Boolean, nullable=False, default=False), # BOOLEAN NOT NULL DEFAULT False - Column('rebuyCost', Integer), # INT - Column('rebuyChips', Integer), # INT - Column('addOn', Boolean, nullable=False, default=False), # BOOLEAN NOT NULL DEFAULT False - Column('addOnCost', Integer), # INT - Column('addOnChips', Integer), # INT - Column('knockout', Boolean, nullable=False, default=False), # BOOLEAN NOT NULL DEFAULT False - Column('koBounty', Integer), # INT - Column('speed', String(10)), # varchar(10) - Column('shootout', Boolean, nullable=False, default=False), # BOOLEAN NOT NULL DEFAULT False - Column('matrix', Boolean, nullable=False, default=False), # BOOLEAN NOT NULL DEFAULT False - Column('sng', Boolean, nullable=False, default=False), # BOOLEAN NOT NULL DEFAULT False - Column('satellite', Boolean, nullable=False, default=False), # BOOLEAN NOT NULL DEFAULT False - Column('doubleOrNothing', Boolean, nullable=False, default=False), # BOOLEAN NOT NULL DEFAULT False - Column('guarantee', Integer, nullable=False, default=0), # INT NOT NULL DEFAULT 0 - mysql_charset='utf8', - mysql_engine='InnoDB', -) -Index('tourneyTypes_all', - tourney_types_table.c.siteId, tourney_types_table.c.buyin, tourney_types_table.c.fee, - tourney_types_table.c.maxSeats, tourney_types_table.c.knockout, tourney_types_table.c.rebuy, - tourney_types_table.c.addOn, tourney_types_table.c.speed, - tourney_types_table.c.shootout, tourney_types_table.c.matrix, tourney_types_table.c.sng) - - -tourneys_players_table = Table('TourneysPlayers', metadata, - Column('id', BigIntColumn, primary_key=True), - Column('tourneyId', Integer, ForeignKey("Tourneys.id"), nullable=False), - Column('playerId', Integer, ForeignKey("Players.id"), nullable=False), - Column('rank', Integer), # INT NOT NULL - Column('winnings', Integer), # INT NOT NULL - Column('winningsCurrency', Text), # TEXT - Column('rebuyCount', Integer, default=0), # INT DEFAULT 0 - Column('addOnCount', Integer, default=0), # INT DEFAULT 0 - Column('koCount', Integer, default=0), # INT DEFAULT 0 - Column('comment', Text), # TEXT - Column('commentTs', DateTime), # DATETIME - mysql_charset='utf8', - 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_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 - diff --git a/pyfpdb/BetfairToFpdb.py b/pyfpdb/BetfairToFpdb.py index 446bbbee..2ef533da 100755 --- a/pyfpdb/BetfairToFpdb.py +++ b/pyfpdb/BetfairToFpdb.py @@ -74,7 +74,7 @@ class Betfair(HandHistoryConverter): if not m: tmp = handText[0:100] log.error(_("Unable to recognise gametype from: '%s'") % tmp) - log.error(_("determineGameType: Raising FpdbParseError")) + log.error("determineGameType: " + _("Raising FpdbParseError")) raise FpdbParseError(_("Unable to recognise gametype from: '%s'") % tmp) mg = m.groupdict() @@ -196,7 +196,7 @@ class Betfair(HandHistoryConverter): elif action.group('ATYPE') == 'checks': hand.addCheck( street, action.group('PNAME')) else: - sys.stderr.write(_("DEBUG: ") + _("Unimplemented readAction: '%s' '%s'") % (action.group('PNAME'),action.group('ATYPE'))) + sys.stderr.write(_("DEBUG: ") + _("Unimplemented readAction: '%s' '%s'") % (action.group('PNAME'), action.group('ATYPE'))) def readShowdownActions(self, hand): diff --git a/pyfpdb/CarbonToFpdb.py b/pyfpdb/CarbonToFpdb.py index 0f0822bf..e5b94f96 100644 --- a/pyfpdb/CarbonToFpdb.py +++ b/pyfpdb/CarbonToFpdb.py @@ -128,7 +128,7 @@ or None if we fail to get the info """ except AttributeError: tmp = handText[0:100] log.error(_("Unable to recognise gametype from: '%s'") % tmp) - log.error(_("determineGameType: Raising FpdbParseError")) + log.error("determineGameType: " + _("Raising FpdbParseError")) raise FpdbParseError(_("Unable to recognise gametype from: '%s'") % tmp) self.info = {} @@ -264,7 +264,7 @@ or None if we fail to get the info """ 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'))) + 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): diff --git a/pyfpdb/Card.py b/pyfpdb/Card.py index 1bcc8dd5..4df18c46 100755 --- a/pyfpdb/Card.py +++ b/pyfpdb/Card.py @@ -101,36 +101,6 @@ def twoStartCardString(card): #print "twoStartCardString(", card ,") = " + ret return ret -def fourStartCards(value1, suit1, value2, suit2, value3, suit3, value4, suit4): - """ Function to convert 4 value,suit pairs into a Omaha style starting hand, - haven't decided how to encode this yet """ - # This doesn't actually do anything yet - CG - - # What combinations do we need to store? just cards: AA23? some suits as well e.g. when - # double suited ATcKTd? Lots more possible combos than holdem :-( 270K vs 1326? not sure - # Probably need to use this field as a key into some other table - sc - - #AAKKds - #AAKKs - #AAKKr - # Is probably what we are looking for - - # mct: - # my maths says there are 4 classes of suitedness - # SSSS SSSx SSxy SSHH - # encode them as follows: - # SSSS (K, J, 6, 3) - # - 13C4 = 715 possibilities - # SSSx (K, J, 6),(3) - # - 13C3 * 13 = 3718 possibilities - # SSxy (K, J),(6),(3) - # - 13C2 * 13*13 = 13182 possibilities - # SSHH (K, J),(6, 3) - # - 13C2 * 13C2 = 6084 possibilities - # Needless to say they won't fit on a 13x13 grid. - # The actual number of hands in each class is far greater - return(0) - def cardFromValueSuit(value, suit): """ 0=none, 1-13=2-Ah 14-26=2-Ad 27-39=2-Ac 40-52=2-As """ if suit == 'h': return(value-1) diff --git a/pyfpdb/Configuration.py b/pyfpdb/Configuration.py index 4d468405..409c289f 100644 --- a/pyfpdb/Configuration.py +++ b/pyfpdb/Configuration.py @@ -78,7 +78,6 @@ def get_exec_path(): def get_config(file_name, fallback = True): """Looks in cwd and in self.default_config_path for a config file.""" - # look for example file even if not used here, path is returned to caller config_found,example_found,example_copy = False,False,False config_path, example_path = None,None @@ -88,17 +87,17 @@ def get_config(file_name, fallback = True): config_path = os.path.join(exec_dir, 'pyfpdb', file_name) else: config_path = os.path.join(exec_dir, file_name) -# print "config_path=", config_path + #print "config_path=", config_path if os.path.exists(config_path): # there is a file in the cwd config_found = True # so we use it else: # no file in the cwd, look where it should be in the first place default_dir = get_default_config_path() config_path = os.path.join(default_dir, file_name) -# print "config path 2=", config_path + #print "config path 2=", config_path if os.path.exists(config_path): config_found = True - -# Example configuration for debian package + + # Example configuration for debian package if os.name == 'posix': # If we're on linux, try to copy example from the place # debian package puts it; get_default_config_path() creates @@ -112,12 +111,19 @@ def get_config(file_name, fallback = True): msg = _("Config file has been created at %s.\n") % config_path logging.info(msg) except IOError: - pass + try: + example_path = file_name + '.example' + shutil.copyfile(example_path, config_path) + example_copy = True + msg = _("Config file has been created at %s.\n") % config_path + logging.info(msg) + except IOError: + pass # OK, fall back to the .example file, should be in the start dir elif os.path.exists(file_name + ".example"): try: - print "" + #print "" example_path = file_name + ".example" check_dir(default_dir) if not config_found and fallback: @@ -167,7 +173,7 @@ def get_logger(file_name, config = "config", fallback = False, log_dir=None, log log = logging.getLogger() # but it looks like default is no output :-( maybe because all the calls name a module? log.debug(_("Default logger initialised for %s") % file) - print(_("Default logger initialised for %s") % file) + #print(_("Default logger initialised for %s") % file) return log def check_dir(path, create = True): @@ -308,7 +314,7 @@ class Site: self.layout = {} self.emails = {} - print _("Loading site"), self.site_name + #print _("Loading site"), self.site_name for layout_node in node.getElementsByTagName('layout'): lo = Layout(layout_node) @@ -482,21 +488,20 @@ class Popup: class Import: def __init__(self, node): - self.node = node - self.interval = node.getAttribute("interval") - self.callFpdbHud = node.getAttribute("callFpdbHud") - self.hhArchiveBase = node.getAttribute("hhArchiveBase") - self.ResultsDirectory = node.getAttribute("ResultsDirectory") - self.hhBulkPath = node.getAttribute("hhBulkPath") - self.saveActions = string_to_bool(node.getAttribute("saveActions"), default=False) - self.cacheSessions = string_to_bool(node.getAttribute("cacheSessions"), default=False) - self.sessionTimeout = string_to_bool(node.getAttribute("sessionTimeout"), default=30) - self.fastStoreHudCache = string_to_bool(node.getAttribute("fastStoreHudCache"), default=False) - self.saveStarsHH = string_to_bool(node.getAttribute("saveStarsHH"), default=False) + self.node = node + self.interval = node.getAttribute("interval") + self.sessionTimeout = node.getAttribute("sessionTimeout") + self.ResultsDirectory = node.getAttribute("ResultsDirectory") + self.hhBulkPath = node.getAttribute("hhBulkPath") + self.saveActions = string_to_bool(node.getAttribute("saveActions") , default=False) + self.cacheSessions = string_to_bool(node.getAttribute("cacheSessions") , default=False) + self.callFpdbHud = string_to_bool(node.getAttribute("callFpdbHud") , default=False) + self.fastStoreHudCache = string_to_bool(node.getAttribute("fastStoreHudCache"), default=False) + self.saveStarsHH = string_to_bool(node.getAttribute("saveStarsHH") , default=False) def __str__(self): - return " interval = %s\n callFpdbHud = %s\n hhArchiveBase = %s\n saveActions = %s\n fastStoreHudCache = %s\nResultsDirectory = %s" \ - % (self.interval, self.callFpdbHud, self.hhArchiveBase, self.saveActions, self.cacheSessions, self.sessionTimeout, self.fastStoreHudCache, self.ResultsDirectory) + return " interval = %s\n callFpdbHud = %s\n saveActions = %s\n fastStoreHudCache = %s\nResultsDirectory = %s" \ + % (self.interval, self.callFpdbHud, self.saveActions, self.cacheSessions, self.sessionTimeout, self.fastStoreHudCache, self.ResultsDirectory) class HudUI: def __init__(self, node): @@ -626,20 +631,20 @@ class RawHands: if node==None: self.save="error" self.compression="none" - print _("missing config section raw_hands") + #print _("missing config section raw_hands") else: save=node.getAttribute("save") if save in ("none", "error", "all"): self.save=save else: - print _("Invalid config value for raw_hands.save, defaulting to \"error\"") + print (_("Invalid config value for %s, defaulting to %s") % (raw_hands.save, "\"error\"")) self.save="error" compression=node.getAttribute("compression") if save in ("none", "gzip", "bzip2"): self.compression=compression else: - print _("Invalid config value for raw_hands.compression, defaulting to \"none\"") + print (_("Invalid config value for %s, defaulting to %s") % (raw_hands.compression, "\"none\"")) self.compression="none" #end def __init__ @@ -652,20 +657,20 @@ class RawTourneys: if node==None: self.save="error" self.compression="none" - print _("missing config section raw_tourneys") + #print _("missing config section raw_tourneys") else: save=node.getAttribute("save") if save in ("none", "error", "all"): self.save=save else: - print _("Invalid config value for raw_tourneys.save, defaulting to \"error\"") + print (_("Invalid config value for %s, defaulting to %s") % (raw_tourneys.save, "\"error\"")) self.save="error" compression=node.getAttribute("compression") if save in ("none", "gzip", "bzip2"): self.compression=compression else: - print _("Invalid config value for raw_tourneys.compression, defaulting to \"none\"") + print (_("Invalid config value for %s, defaulting to %s") % (raw_tourneys.compression, "\"none\"")) self.compression="none" #end def __init__ @@ -683,8 +688,8 @@ class Config: if file is not None: # config file path passed in file = os.path.expanduser(file) if not os.path.exists(file): - print _("Configuration file %s not found. Using defaults.") % (file) - sys.stderr.write(_("Configuration file %s not found. Using defaults.") % (file)) + print _("Configuration file %s not found. Using defaults.") % (file) + sys.stderr.write(_("Configuration file %s not found. Using defaults.") % (file)) file = None self.example_copy,example_file = True,None @@ -715,13 +720,13 @@ class Config: while added > 0 and n < 2: n = n + 1 log.info(_("Reading configuration file %s") % file) - print (("\n"+_("Reading configuration file %s")+"\n") % file) + #print (("\n"+_("Reading configuration file %s")+"\n") % file) try: doc = xml.dom.minidom.parse(file) self.doc = doc self.file_error = None except: - log.error(_("Error parsing %s. See error log file.") % (file)) + log.error((_("Error parsing %s.") % (file)) + _("See error log file.")) traceback.print_exc(file=sys.stderr) self.file_error = sys.exc_info()[1] # we could add a parameter to decide whether to return or read a line and exit? @@ -824,7 +829,7 @@ class Config: for raw_tourneys_node in doc.getElementsByTagName('raw_tourneys'): self.raw_tourneys = RawTourneys(raw_tourneys_node) - print "" + #print "" #end def __init__ def add_missing_elements(self, doc, example_file): @@ -837,7 +842,7 @@ class Config: try: example_doc = xml.dom.minidom.parse(example_file) except: - log.error(_("Error parsing example configuration file %s. See error log file.") % (example_file)) + log.error((_("Error parsing example configuration file %s.") % (example_file)) + _("See error log file.")) return nodes_added for cnode in doc.getElementsByTagName("FreePokerToolsConfig"): @@ -861,9 +866,6 @@ class Config: return nodes_added - def set_hhArchiveBase(self, path): - self.imp.node.setAttribute("hhArchiveBase", path) - def find_default_conf(self): if os.name == 'posix': config_path = os.path.join(os.path.expanduser("~"), '.fpdb', 'default.conf') @@ -1261,10 +1263,6 @@ class Config: try: imp['interval'] = self.imp.interval except: imp['interval'] = 10 - # hhArchiveBase is the temp store for part-processed hand histories - should be redundant eventually - try: imp['hhArchiveBase'] = self.imp.hhArchiveBase - except: imp['hhArchiveBase'] = "~/.fpdb/HandHistories/" - # ResultsDirectory is the local cache for downloaded results # NOTE: try: except: doesn'tseem to be triggering # using if instead diff --git a/pyfpdb/Database.py b/pyfpdb/Database.py index 4efc832f..41b970a4 100644 --- a/pyfpdb/Database.py +++ b/pyfpdb/Database.py @@ -73,7 +73,7 @@ except ImportError: use_numpy = False -DB_VERSION = 150 +DB_VERSION = 152 # Variance created as sqlite has a bunch of undefined aggregate functions. @@ -134,6 +134,9 @@ class Database: , {'tab':'HudCache', 'col':'gametypeId', 'drop':1} , {'tab':'HudCache', 'col':'playerId', 'drop':0} , {'tab':'HudCache', 'col':'tourneyTypeId', 'drop':0} + , {'tab':'SessionsCache', 'col':'gametypeId', 'drop':1} + , {'tab':'SessionsCache', 'col':'playerId', 'drop':0} + , {'tab':'SessionsCache', 'col':'tourneyTypeId', 'drop':0} , {'tab':'Players', 'col':'siteId', 'drop':1} #, {'tab':'Players', 'col':'name', 'drop':0} unique indexes not dropped , {'tab':'Tourneys', 'col':'tourneyTypeId', 'drop':1} @@ -157,6 +160,9 @@ class Database: , {'tab':'HudCache', 'col':'gametypeId', 'drop':1} , {'tab':'HudCache', 'col':'playerId', 'drop':0} , {'tab':'HudCache', 'col':'tourneyTypeId', 'drop':0} + , {'tab':'SessionsCache', 'col':'gametypeId', 'drop':1} + , {'tab':'SessionsCache', 'col':'playerId', 'drop':0} + , {'tab':'SessionsCache', 'col':'tourneyTypeId', 'drop':0} , {'tab':'Players', 'col':'siteId', 'drop':1} , {'tab':'Tourneys', 'col':'tourneyTypeId', 'drop':1} , {'tab':'TourneysPlayers', 'col':'playerId', 'drop':0} @@ -182,6 +188,9 @@ class Database: , {'fktab':'HudCache', 'fkcol':'gametypeId', 'rtab':'Gametypes', 'rcol':'id', 'drop':1} , {'fktab':'HudCache', 'fkcol':'playerId', 'rtab':'Players', 'rcol':'id', 'drop':0} , {'fktab':'HudCache', 'fkcol':'tourneyTypeId', 'rtab':'TourneyTypes', 'rcol':'id', 'drop':1} + , {'fktab':'SessionsCache','fkcol':'gametypeId', 'rtab':'Gametypes', 'rcol':'id', 'drop':1} + , {'fktab':'SessionsCache','fkcol':'playerId', 'rtab':'Players', 'rcol':'id', 'drop':0} + , {'fktab':'SessionsCache','fkcol':'tourneyTypeId', 'rtab':'TourneyTypes', 'rcol':'id', 'drop':1} ] , [ # foreign keys for postgres (index 3) {'fktab':'Hands', 'fkcol':'gametypeId', 'rtab':'Gametypes', 'rcol':'id', 'drop':1} @@ -193,6 +202,9 @@ class Database: , {'fktab':'HudCache', 'fkcol':'gametypeId', 'rtab':'Gametypes', 'rcol':'id', 'drop':1} , {'fktab':'HudCache', 'fkcol':'playerId', 'rtab':'Players', 'rcol':'id', 'drop':0} , {'fktab':'HudCache', 'fkcol':'tourneyTypeId', 'rtab':'TourneyTypes', 'rcol':'id', 'drop':1} + , {'fktab':'SessionsCache','fkcol':'gametypeId', 'rtab':'Gametypes', 'rcol':'id', 'drop':1} + , {'fktab':'SessionsCache','fkcol':'playerId', 'rtab':'Players', 'rcol':'id', 'drop':0} + , {'fktab':'SessionsCache','fkcol':'tourneyTypeId', 'rtab':'TourneyTypes', 'rcol':'id', 'drop':1} ] , [ # no foreign keys in sqlite (index 4) ] @@ -261,6 +273,7 @@ class Database: self.db_path = '' gen = c.get_general_params() self.day_start = 0 + self._has_lock = False if 'day_start' in gen: self.day_start = float(gen['day_start']) @@ -386,7 +399,12 @@ class Database: if use_pool: MySQLdb = pool.manage(MySQLdb, pool_size=5) try: - self.connection = MySQLdb.connect(host=host, user=user, passwd=password, db=database, use_unicode=True) + self.connection = MySQLdb.connect(host=host + ,user=user + ,passwd=password + ,db=database + ,charset='utf8' + ,use_unicode=True) self.__connected = True #TODO: Add port option except MySQLdb.Error, ex: @@ -449,7 +467,7 @@ class Database: os.mkdir(self.config.dir_database) database = os.path.join(self.config.dir_database, database) self.db_path = database - log.info(_("Connecting to SQLite: %(database)s") % {'database':self.db_path}) + log.info(_("Connecting to SQLite: %s") % self.db_path) if os.path.exists(database) or create: self.connection = sqlite3.connect(self.db_path, detect_types=sqlite3.PARSE_DECLTYPES ) self.__connected = True @@ -492,19 +510,18 @@ class Database: self.cursor.execute("SELECT * FROM Settings") settings = self.cursor.fetchone() if settings[0] != DB_VERSION: - log.error(_("outdated or too new database version (%s) - please recreate tables") - % (settings[0])) + log.error((_("Outdated or too new database version (%s).") % (settings[0])) + " " + _("Please recreate tables.")) self.wrongDbVersion = True except:# _mysql_exceptions.ProgrammingError: if database != ":memory:": if create: - print _("Failed to read settings table - recreating tables") - log.info(_("Failed to read settings table - recreating tables")) + print (_("Failed to read settings table.") + " - " + _("Recreating tables.")) + log.info(_("Failed to read settings table.") + " - " + _("Recreating tables.")) self.recreate_tables() self.check_version(database=database, create=False) else: - print _("Failed to read settings table - please recreate tables") - log.info(_("Failed to read settings table - please recreate tables")) + print (_("Failed to read settings table.") + " - " + _("Please recreate tables.")) + log.info(_("Failed to read settings table.") + " - " + _("Please recreate tables.")) self.wrongDbVersion = True else: self.wrongDbVersion = True @@ -1252,6 +1269,12 @@ class Database: c.execute(self.sql.query['createBackingsTable']) c.execute(self.sql.query['createRawHands']) c.execute(self.sql.query['createRawTourneys']) + + # Create sessionscache indexes + log.debug("Creating SessionsCache indexes") + c.execute(self.sql.query['addSessionIdIndex']) + c.execute(self.sql.query['addHandsSessionIdIndex']) + c.execute(self.sql.query['addHandsGameSessionIdIndex']) # Create unique indexes: log.debug("Creating unique indexes") @@ -1519,6 +1542,7 @@ class Database: c.execute("INSERT INTO Sites (name,code) VALUES ('Betfair', 'BF')") c.execute("INSERT INTO Sites (name,code) VALUES ('Absolute', 'AB')") c.execute("INSERT INTO Sites (name,code) VALUES ('PartyPoker', 'PP')") + c.execute("INSERT INTO Sites (name,code) VALUES ('PacificPoker', 'P8')") c.execute("INSERT INTO Sites (name,code) VALUES ('Partouche', 'PA')") c.execute("INSERT INTO Sites (name,code) VALUES ('Carbon', 'CA')") c.execute("INSERT INTO Sites (name,code) VALUES ('PKR', 'PK')") @@ -1617,10 +1641,86 @@ class Database: print err #end def rebuild_hudcache - def rebuild_sessionscache(self, h_start=None, v_start=None): - """clears sessionscache and rebuilds from the individual handsplayers records""" - #Will get to this soon - pass + def rebuild_sessionscache(self): + """clears sessionscache and rebuilds from the individual records""" + heros = [] + for site in self.config.get_supported_sites(): + result = self.get_site_id(site) + if result: + site_id = result[0][0] + hero = self.config.supported_sites[site].screen_name + p_id = self.get_player_id(self.config, site, hero) + if p_id: + heros.append(int(p_id)) + + rebuildSessionsCache = self.sql.query['rebuildSessionsCache'] + rebuildSessionsCacheSum = self.sql.query['rebuildSessionsCacheSum'] + + if len(heros) == 0: + where = '0' + where_summary = '0' + elif len(heros) > 0: + where = str(heros[0]) + where_summary = str(heros[0]) + if len(heros) > 1: + for i in heros: + if i != heros[0]: + where = where + ' OR HandsPlayers.playerId = %s' % str(i) + where_summary = where_summary + ' OR TourneysPlayers.playerId = %s' % str(i) + rebuildSessionsCache = rebuildSessionsCache.replace('', where) + rebuildSessionsCacheSum = rebuildSessionsCacheSum.replace('', where_summary) + + c = self.get_cursor() + c.execute(self.sql.query['clearSessionsCache']) + self.commit() + + sc, gsc = {'bk': []}, {'bk': []} + c.execute(rebuildSessionsCache) + tmp = c.fetchone() + while True: + pids, game, pdata = {}, {}, {} + pdata['pname'] = {} + id = tmp[0] + startTime = tmp[1] + pids['pname'] = tmp[2] + gid = tmp[3] + game['type'] = tmp[4] + pdata['pname']['totalProfit'] = tmp[5] + pdata['pname']['tourneyTypeId'] = tmp[6] + tmp = c.fetchone() + sc = self.prepSessionsCache (id, pids, startTime, sc , heros, tmp == None) + gsc = self.storeSessionsCache(id, pids, startTime, game, gid, pdata, sc, gsc, None, heros, tmp == None) + if tmp == None: + for i, id in sc.iteritems(): + if i!='bk': + sid = id['id'] + gid = gsc[i]['id'] + c.execute("UPDATE Hands SET sessionId = %s, gameSessionId = %s WHERE id = %s", (sid, gid, i)) + break + self.commit() + + sc, gsc = {'bk': []}, {'bk': []} + c.execute(rebuildSessionsCacheSum) + tmp = c.fetchone() + while True: + pids, game, info = {}, {}, {} + id = tmp[0] + startTime = tmp[1] + pids['pname'] = tmp[2] + game['type'] = 'summary' + info['tourneyTypeId'] = tmp[3] + info['winnings'] = {} + info['winnings']['pname'] = tmp[4] + info['winningsCurrency'] = {} + info['winningsCurrency']['pname'] = tmp[5] + info['buyinCurrency'] = tmp[6] + info['buyin'] = tmp[7] + info['fee'] = tmp[8] + tmp = c.fetchone() + sc = self.prepSessionsCache (id, pids, startTime, sc , heros, tmp == None) + gsc = self.storeSessionsCache(id, pids, startTime, game, None, info, sc, gsc, None, heros, tmp == None) + if tmp == None: + break def get_hero_hudcache_start(self): """fetches earliest stylekey from hudcache for one of hero's player ids""" @@ -1700,6 +1800,34 @@ class Database: # however the calling prog requires. Main aims: # - existing static routines from fpdb_simple just modified + def setThreadId(self, threadid): + self.threadId = threadid + + def acquireLock(self, wait=True, retry_time=.01): + while not self._has_lock: + cursor = self.get_cursor() + cursor.execute(self.sql.query['selectLock']) + record = cursor.fetchall() + self.commit() + if not len(record): + cursor.execute(self.sql.query['switchLock'], (True, self.threadId)) + self.commit() + self._has_lock = True + return True + else: + cursor.execute(self.sql.query['missedLock'], (1, self.threadId)) + self.commit() + if not wait: + return False + sleep(retry_time) + + def releaseLock(self): + if self._has_lock: + cursor = self.get_cursor() + num = cursor.execute(self.sql.query['switchLock'], (False, self.threadId)) + self.commit() + self._has_lock = False + def lock_for_insert(self): """Lock tables in MySQL to try to speed inserts up""" try: @@ -1712,70 +1840,74 @@ class Database: # NEWIMPORT CODE ########################### - def storeHand(self, p, printdata = False): + def storeHand(self, hdata, hbulk, doinsert = False, printdata = False): if printdata: print _("######## Hands ##########") import pprint pp = pprint.PrettyPrinter(indent=4) - pp.pprint(p) + pp.pprint(hdata) print _("###### End Hands ########") - + # Tablename can have odd charachers - p['tableName'] = Charset.to_db_utf8(p['tableName']) + hdata['tableName'] = Charset.to_db_utf8(hdata['tableName']) + + hbulk.append( [ hdata['tableName'], + hdata['siteHandNo'], + hdata['tourneyId'], + hdata['gametypeId'], + hdata['sessionId'], + hdata['gameSessionId'], + hdata['startTime'], + datetime.utcnow(), #importtime + hdata['seats'], + hdata['maxSeats'], + hdata['texture'], + hdata['playersVpi'], + hdata['boardcard1'], + hdata['boardcard2'], + hdata['boardcard3'], + hdata['boardcard4'], + hdata['boardcard5'], + hdata['playersAtStreet1'], + hdata['playersAtStreet2'], + hdata['playersAtStreet3'], + hdata['playersAtStreet4'], + hdata['playersAtShowdown'], + hdata['street0Raises'], + hdata['street1Raises'], + hdata['street2Raises'], + hdata['street3Raises'], + hdata['street4Raises'], + hdata['street1Pot'], + hdata['street2Pot'], + hdata['street3Pot'], + hdata['street4Pot'], + hdata['showdownPot'], + hdata['id'] + ]) - #stores into table hands: - q = self.sql.query['store_hand'] + if doinsert: + for h in hbulk: + id = h.pop() + if hdata['sc'] and hdata['gsc']: + h[4] = hdata['sc'][id]['id'] + h[5] = hdata['gsc'][id]['id'] + q = self.sql.query['store_hand'] + q = q.replace('%s', self.sql.query['placeholder']) + c = self.get_cursor() + c.executemany(q, hbulk) + self.commit() + return hbulk - q = q.replace('%s', self.sql.query['placeholder']) - - c = self.get_cursor() - - c.execute(q, ( - p['tableName'], - p['siteHandNo'], - p['tourneyId'], - p['gametypeId'], - p['sessionId'], - p['startTime'], - datetime.utcnow(), #importtime - p['seats'], - p['maxSeats'], - p['texture'], - p['playersVpi'], - p['boardcard1'], - p['boardcard2'], - p['boardcard3'], - p['boardcard4'], - p['boardcard5'], - p['playersAtStreet1'], - p['playersAtStreet2'], - p['playersAtStreet3'], - p['playersAtStreet4'], - p['playersAtShowdown'], - p['street0Raises'], - p['street1Raises'], - p['street2Raises'], - p['street3Raises'], - p['street4Raises'], - p['street1Pot'], - p['street2Pot'], - p['street3Pot'], - p['street4Pot'], - p['showdownPot'] - )) - return self.get_last_insert_id(c) - # def storeHand - - def storeHandsPlayers(self, hid, pids, pdata, hp_bulk = None, insert = False, printdata = False): + def storeHandsPlayers(self, hid, pids, pdata, hpbulk, doinsert = False, printdata = False): #print "DEBUG: %s %s %s" %(hid, pids, pdata) if printdata: import pprint pp = pprint.PrettyPrinter(indent=4) pp.pprint(pdata) - inserts = [] for p in pdata: - inserts.append( (hid, + hpbulk.append( ( hid, pids[p], pdata[p]['startCash'], pdata[p]['seatNo'], @@ -1787,6 +1919,19 @@ class Database: pdata[p]['card5'], pdata[p]['card6'], pdata[p]['card7'], + pdata[p]['card8'], + pdata[p]['card9'], + pdata[p]['card10'], + pdata[p]['card11'], + pdata[p]['card12'], + pdata[p]['card13'], + pdata[p]['card14'], + pdata[p]['card15'], + pdata[p]['card16'], + pdata[p]['card17'], + pdata[p]['card18'], + pdata[p]['card19'], + pdata[p]['card20'], pdata[p]['winnings'], pdata[p]['rake'], pdata[p]['totalProfit'], @@ -1881,16 +2026,14 @@ class Database: pdata[p]['street4Raises'] ) ) - if insert: - hp_bulk += inserts + if doinsert: q = self.sql.query['store_hands_players'] q = q.replace('%s', self.sql.query['placeholder']) c = self.get_cursor() - c.executemany(q, hp_bulk) - - return inserts + c.executemany(q, hpbulk) + return hpbulk - def storeHandsActions(self, hid, pids, adata, ha_bulk = None, insert = False, printdata = False): + def storeHandsActions(self, hid, pids, adata, habulk, doinsert = False, printdata = False): #print "DEBUG: %s %s %s" %(hid, pids, adata) # This can be used to generate test data. Currently unused @@ -1898,10 +2041,9 @@ class Database: # import pprint # pp = pprint.PrettyPrinter(indent=4) # pp.pprint(adata) - - inserts = [] + for a in adata: - inserts.append( (hid, + habulk.append( (hid, pids[adata[a]['player']], adata[a]['street'], adata[a]['actionNo'], @@ -1914,17 +2056,15 @@ class Database: adata[a]['cardsDiscarded'], adata[a]['allIn'] ) ) - - if insert: - ha_bulk += inserts + + if doinsert: q = self.sql.query['store_hands_actions'] q = q.replace('%s', self.sql.query['placeholder']) c = self.get_cursor() - c.executemany(q, ha_bulk) + c.executemany(q, habulk) + return habulk - return inserts - - def storeHudCache(self, gid, pids, starttime, pdata): + def storeHudCache(self, gid, pids, starttime, pdata, hcbulk, doinsert = False): """Update cached statistics. If update fails because no record exists, do an insert.""" tz = datetime.utcnow() - datetime.today() @@ -1947,11 +2087,9 @@ class Database: insert_hudcache = insert_hudcache.replace('%s', self.sql.query['placeholder']) #print "DEBUG: %s %s %s" %(hid, pids, pdata) - inserts = [] for p in pdata: #NOTE: Insert new stats at right place because SQL needs strict order line = [] - line.append(1) # HDs line.append(pdata[p]['street0VPI']) line.append(pdata[p]['street0Aggr']) @@ -2040,162 +2178,367 @@ class Database: line.append(pdata[p]['street2Raises']) line.append(pdata[p]['street3Raises']) line.append(pdata[p]['street4Raises']) - - line.append(gid) # gametypeId - line.append(pids[p]) # playerId - line.append(len(pids)) # activeSeats - pos = {'B':'B', 'S':'S', 0:'D', 1:'C', 2:'M', 3:'M', 4:'M', 5:'E', 6:'E', 7:'E', 8:'E', 9:'E' } - line.append(pos[pdata[p]['position']]) - line.append(pdata[p]['tourneyTypeId']) - line.append(styleKey) # styleKey - inserts.append(line) - - - cursor = self.get_cursor() - - for row in inserts: - # Try to do the update first: - num = cursor.execute(update_hudcache, row) - #print "DEBUG: values: %s" % row[-6:] - # Test statusmessage to see if update worked, do insert if not - # num is a cursor in sqlite - if ((self.backend == self.PGSQL and cursor.statusmessage != "UPDATE 1") - or (self.backend == self.MYSQL_INNODB and num == 0) - or (self.backend == self.SQLITE and num.rowcount == 0)): - #move the last 6 items in WHERE clause of row from the end of the array - # to the beginning for the INSERT statement - #print "DEBUG: using INSERT: %s" % num - row = row[-6:] + row[:-6] - num = cursor.execute(insert_hudcache, row) - #print "DEBUG: Successfully(?: %s) updated HudCacho using INSERT" % num - else: - #print "DEBUG: Successfully updated HudCacho using UPDATE" - pass - def storeSessionsCache(self, pids, startTime, game, pdata): - """Update cached sessions. If no record exists, do an insert""" + hc, hcs = {}, [] + hc['gametypeId'] = gid + hc['playerId'] = pids[p] + hc['activeSeats'] = len(pids) + pos = {'B':'B', 'S':'S', 0:'D', 1:'C', 2:'M', 3:'M', 4:'M', 5:'E', 6:'E', 7:'E', 8:'E', 9:'E' } + hc['position'] = pos[pdata[p]['position']] + hc['tourneyTypeId'] = pdata[p]['tourneyTypeId'] + hc['styleKey'] = styleKey + hc['line'] = line + hc['game'] = [hc['gametypeId'] + ,hc['playerId'] + ,hc['activeSeats'] + ,hc['position'] + ,hc['tourneyTypeId'] + ,hc['styleKey']] + hcs.append(hc) + + for h in hcs: + match = False + for b in hcbulk: + if h['game']==b['game']: + b['line'] = [sum(l) for l in zip(b['line'], h['line'])] + match = True + if not match: hcbulk.append(h) + if doinsert: + inserts = [] + exists = [] + updates = [] + for hc in hcbulk: + row = hc['line'] + hc['game'] + if hc['game'] in exists: + updates.append(row) + continue + c = self.get_cursor() + num = c.execute(update_hudcache, row) + # Try to do the update first. Do insert it did not work + if ((self.backend == self.PGSQL and c.statusmessage != "UPDATE 1") + or (self.backend == self.MYSQL_INNODB and num == 0) + or (self.backend == self.SQLITE and num.rowcount == 0)): + inserts.append(hc['game'] + hc['line']) + #row = hc['game'] + hc['line'] + #num = c.execute(insert_hudcache, row) + #print "DEBUG: Successfully(?: %s) updated HudCacho using INSERT" % num + else: + exists.append(hc['game']) + #print "DEBUG: Successfully updated HudCacho using UPDATE" + if inserts: c.executemany(insert_hudcache, inserts) + if updates: c.executemany(update_hudcache, updates) + + return hcbulk + + def prepSessionsCache(self, hid, pids, startTime, sc, heros, doinsert = False): + """Update cached sessions. If no record exists, do an insert""" THRESHOLD = timedelta(seconds=int(self.sessionTimeout * 60)) - select_sessionscache = self.sql.query['select_sessionscache'] - select_sessionscache = select_sessionscache.replace('%s', self.sql.query['placeholder']) - select_sessionscache_mid = self.sql.query['select_sessionscache_mid'] - select_sessionscache_mid = select_sessionscache_mid.replace('%s', self.sql.query['placeholder']) - select_sessionscache_start = self.sql.query['select_sessionscache_start'] - select_sessionscache_start = select_sessionscache_start.replace('%s', self.sql.query['placeholder']) + select_prepSC = self.sql.query['select_prepSC'].replace('%s', self.sql.query['placeholder']) + update_Hands_sid = self.sql.query['update_Hands_sid'].replace('%s', self.sql.query['placeholder']) + update_SC_sid = self.sql.query['update_SC_sid'].replace('%s', self.sql.query['placeholder']) + update_prepSC = self.sql.query['update_prepSC'].replace('%s', self.sql.query['placeholder']) + + #print "DEBUG: %s %s %s" %(hid, pids, pdata) + hand = {} + for p, id in pids.iteritems(): + if id in heros: + hand['startTime'] = startTime.replace(tzinfo=None) + hand['ids'] = [] + + if hand: + id = [] + lower = hand['startTime']-THRESHOLD + upper = hand['startTime']+THRESHOLD + for i in range(len(sc['bk'])): + if ((lower <= sc['bk'][i]['sessionEnd']) + and (upper >= sc['bk'][i]['sessionStart'])): + if ((hand['startTime'] <= sc['bk'][i]['sessionEnd']) + and (hand['startTime'] >= sc['bk'][i]['sessionStart'])): + id.append(i) + elif hand['startTime'] < sc['bk'][i]['sessionStart']: + sc['bk'][i]['sessionStart'] = hand['startTime'] + id.append(i) + elif hand['startTime'] > sc['bk'][i]['sessionEnd']: + sc['bk'][i]['sessionEnd'] = hand['startTime'] + id.append(i) + if len(id) == 1: + id = id[0] + sc['bk'][id]['ids'].append(hid) + elif len(id) == 2: + if sc['bk'][id[0]]['startTime'] < sc['bk'][id[1]]['startTime']: + sc['bk'][id[0]]['endTime'] = sc['bk'][id[1]]['endTime'] + else: + sc['bk'][id[0]]['startTime'] = sc['bk'][id[1]]['startTime'] + sc['bk'].pop[id[1]] + id = id[0] + sc['bk'][id]['ids'].append(hid) + elif len(id) == 0: + hand['id'] = None + hand['sessionStart'] = hand['startTime'] + hand['sessionEnd'] = hand['startTime'] + id = len(sc['bk']) + hand['ids'].append(hid) + sc['bk'].append(hand) + + if doinsert: + c = self.get_cursor() + c.execute("SELECT max(sessionId) FROM SessionsCache") + id = c.fetchone()[0] + if id: sid = id + else: sid = 0 + for i in range(len(sc['bk'])): + lower = sc['bk'][i]['sessionStart'] - THRESHOLD + upper = sc['bk'][i]['sessionEnd'] + THRESHOLD + c.execute(select_prepSC, (lower, upper)) + r = self.fetchallDict(c) + num = len(r) + if (num == 1): + start, end, update = r[0]['sessionStart'], r[0]['sessionEnd'], False + if sc['bk'][i]['sessionStart'] < start: + start, update = sc['bk'][i]['sessionStart'], True + if sc['bk'][i]['sessionEnd'] > end: + end, update = sc['bk'][i]['sessionEnd'], True + if update: + c.execute(update_prepSC, [start, end, r[0]['id']]) + for h in sc['bk'][i]['ids']: + sc[h] = {'id': r[0]['id'], 'data': [start, end]} + elif (num > 1): + start, end, merge, merge_h, merge_sc = None, None, [], [], [] + sid += 1 + r.append(sc['bk'][i]) + for n in r: + if start: + if start > n['sessionStart']: + start = n['sessionStart'] + else: start = n['sessionStart'] + if end: + if end < n['sessionEnd']: + end = n['sessionEnd'] + else: end = n['sessionEnd'] + for n in r: + if n['id']: + if n['id'] in merge: continue + merge.append(n['id']) + merge_h.append([sid, n['id']]) + merge_sc.append([start, end, sid, n['id']]) + c.executemany(update_Hands_sid, merge_h) + c.executemany(update_SC_sid, merge_sc) + for k, v in sc.iteritems(): + if k!='bk' and v['id'] in merge: + sc[k]['id'] = sid + for h in sc['bk'][i]['ids']: + sc[h] = {'id': sid, 'data': [start, end]} + elif (num == 0): + sid += 1 + start = sc['bk'][i]['sessionStart'] + end = sc['bk'][i]['sessionEnd'] + for h in sc['bk'][i]['ids']: + sc[h] = {'id': sid, 'data': [start, end]} + return sc - update_sessionscache_mid = self.sql.query['update_sessionscache_mid'] - update_sessionscache_mid = update_sessionscache_mid.replace('%s', self.sql.query['placeholder']) - update_sessionscache_start = self.sql.query['update_sessionscache_start'] - update_sessionscache_start = update_sessionscache_start.replace('%s', self.sql.query['placeholder']) - update_sessionscache_end = self.sql.query['update_sessionscache_end'] - update_sessionscache_end = update_sessionscache_end.replace('%s', self.sql.query['placeholder']) + def storeSessionsCache(self, hid, pids, startTime, game, gid, pdata, sc, gsc, tz, heros, doinsert = False): + """Update cached sessions. If no record exists, do an insert""" + if not tz: + tz_dt = datetime.utcnow() - datetime.today() + tz = tz_dt.seconds/3600 + + THRESHOLD = timedelta(seconds=int(self.sessionTimeout * 60)) + local = startTime + timedelta(hours=int(tz)) + date = "d%02d%02d%02d" % (local.year - 2000, local.month, local.day) - insert_sessionscache = self.sql.query['insert_sessionscache'] - insert_sessionscache = insert_sessionscache.replace('%s', self.sql.query['placeholder']) - merge_sessionscache = self.sql.query['merge_sessionscache'] - merge_sessionscache = merge_sessionscache.replace('%s', self.sql.query['placeholder']) - delete_sessions = self.sql.query['delete_sessions'] - delete_sessions = delete_sessions.replace('%s', self.sql.query['placeholder']) + select_SC = self.sql.query['select_SC'].replace('%s', self.sql.query['placeholder']) + update_SC = self.sql.query['update_SC'].replace('%s', self.sql.query['placeholder']) + insert_SC = self.sql.query['insert_SC'].replace('%s', self.sql.query['placeholder']) + delete_SC = self.sql.query['delete_SC'].replace('%s', self.sql.query['placeholder']) + update_Hands_gsid = self.sql.query['update_Hands_gsid'].replace('%s', self.sql.query['placeholder']) + + #print "DEBUG: %s %s %s" %(hid, pids, pdata) + hand = {} + for p, id in pids.iteritems(): + if id in heros: + hand['hands'] = 0 + hand['totalProfit'] = 0 + hand['playerId'] = id + hand['gametypeId'] = None + hand['date'] = date + hand['startTime'] = startTime.replace(tzinfo=None) + hand['hid'] = hid + hand['tourneys'] = 0 + hand['tourneyTypeId'] = None + hand['ids'] = [] + if (game['type']=='summary'): + hand['type'] = 'tour' + hand['tourneys'] = 1 + hand['tourneyTypeId'] = pdata['tourneyTypeId'] + if pdata['buyinCurrency'] == pdata['winningsCurrency'][p]: + hand['totalProfit'] = pdata['winnings'][p] - (pdata['buyin'] + pdata['fee']) + else: hand['totalProfit'] = pdata['winnings'][p] + elif (game['type']=='ring'): + hand['type'] = game['type'] + hand['hands'] = 1 + hand['gametypeId'] = gid + hand['totalProfit'] = pdata[p]['totalProfit'] + elif (game['type']=='tour'): + hand['type'] = game['type'] + hand['hands'] = 1 + hand['tourneyTypeId'] = pdata[p]['tourneyTypeId'] - update_hands_sessionid = self.sql.query['update_hands_sessionid'] - update_hands_sessionid = update_hands_sessionid.replace('%s', self.sql.query['placeholder']) + if hand: + id = [] + lower = hand['startTime']-THRESHOLD + upper = hand['startTime']+THRESHOLD + for i in range(len(gsc['bk'])): + if ((hand['date'] == gsc['bk'][i]['date']) + and (hand['gametypeId'] == gsc['bk'][i]['gametypeId']) + and (hand['playerId'] == gsc['bk'][i]['playerId']) + and (hand['tourneyTypeId'] == gsc['bk'][i]['tourneyTypeId'])): + if ((lower <= gsc['bk'][i]['gameEnd']) + and (upper >= gsc['bk'][i]['gameStart'])): + if ((hand['startTime'] <= gsc['bk'][i]['gameEnd']) + and (hand['startTime'] >= gsc['bk'][i]['gameStart'])): + gsc['bk'][i]['hands'] += hand['hands'] + gsc['bk'][i]['tourneys'] += hand['tourneys'] + gsc['bk'][i]['totalProfit'] += hand['totalProfit'] + elif hand['startTime'] < gsc['bk'][i]['gameStart']: + gsc['bk'][i]['hands'] += hand['hands'] + gsc['bk'][i]['tourneys'] += hand['tourneys'] + gsc['bk'][i]['totalProfit'] += hand['totalProfit'] + gsc['bk'][i]['gameStart'] = hand['startTime'] + elif hand['startTime'] > gsc['bk'][i]['gameEnd']: + gsc['bk'][i]['hands'] += hand['hands'] + gsc['bk'][i]['tourneys'] += hand['tourneys'] + gsc['bk'][i]['totalProfit'] += hand['totalProfit'] + gsc['bk'][i]['gameEnd'] = hand['startTime'] + id.append(i) + if len(id) == 1: + gsc['bk'][id[0]]['ids'].append(hid) + elif len(id) == 2: + if gsc['bk'][id[0]]['gameStart'] < gsc['bk'][id[1]]['gameStart']: + gsc['bk'][id[0]]['gameEnd'] = gsc['bk'][id[1]]['gameEnd'] + else: gsc['bk'][id[0]]['gameStart'] = gsc['bk'][id[1]]['gameStart'] + gsc['bk'][id[0]]['hands'] += hand['hands'] + gsc['bk'][id[0]]['tourneys'] += hand['tourneys'] + gsc['bk'][id[0]]['totalProfit'] += hand['totalProfit'] + gsc['bk'].pop[id[1]] + gsc['bk'][id[0]]['ids'].append(hid) + elif len(id) == 0: + hand['gameStart'] = hand['startTime'] + hand['gameEnd'] = hand['startTime'] + id = len(gsc['bk']) + hand['ids'].append(hid) + gsc['bk'].append(hand) + if doinsert: + c = self.get_cursor() + for i in range(len(gsc['bk'])): + hid = gsc['bk'][i]['hid'] + sid, start, end = sc[hid]['id'], sc[hid]['data'][0], sc[hid]['data'][1] + lower = gsc['bk'][i]['gameStart'] - THRESHOLD + upper = gsc['bk'][i]['gameEnd'] + THRESHOLD + game = [gsc['bk'][i]['date'] + ,gsc['bk'][i]['type'] + ,gsc['bk'][i]['gametypeId'] + ,gsc['bk'][i]['tourneyTypeId'] + ,gsc['bk'][i]['playerId']] + row = [lower, upper] + game + c.execute(select_SC, row) + r = self.fetchallDict(c) + num = len(r) + if (num == 1): + gstart, gend = r[0]['gameStart'], r[0]['gameEnd'] + if gsc['bk'][i]['gameStart'] < gstart: + gstart = gsc['bk'][i]['gameStart'] + if gsc['bk'][i]['gameEnd'] > gend: + gend = gsc['bk'][i]['gameEnd'] + row = [start, end, gstart, gend + ,gsc['bk'][i]['hands'] + ,gsc['bk'][i]['tourneys'] + ,gsc['bk'][i]['totalProfit'] + ,r[0]['id']] + c.execute(update_SC, row) + for h in gsc['bk'][i]['ids']: gsc[h] = {'id': r[0]['id']} + elif (num > 1): + gstart, gend, hands, tourneys, totalProfit, delete, merge = None, None, 0, 0, 0, [], [] + for n in r: delete.append(n['id']) + delete.sort() + for d in delete: c.execute(delete_SC, d) + r.append(gsc['bk'][i]) + for n in r: + if gstart: + if gstart > n['gameStart']: + gstart = n['gameStart'] + else: gstart = n['gameStart'] + if gend: + if gend < n['gameEnd']: + gend = n['gameEnd'] + else: gend = n['gameEnd'] + hands += n['hands'] + tourneys += n['tourneys'] + totalProfit += n['totalProfit'] + row = [start, end, gstart, gend, sid] + game + [hands, tourneys, totalProfit] + c.execute(insert_SC, row) + gsid = self.get_last_insert_id(c) + for h in gsc['bk'][i]['ids']: gsc[h] = {'id': gsid} + for m in delete: merge.append([gsid, m]) + c.executemany(update_Hands_gsid, merge) + elif (num == 0): + gstart = gsc['bk'][i]['gameStart'] + gend = gsc['bk'][i]['gameEnd'] + hands = gsc['bk'][i]['hands'] + tourneys = gsc['bk'][i]['tourneys'] + totalProfit = gsc['bk'][i]['totalProfit'] + row = [start, end, gstart, gend, sid] + game + [hands, tourneys, totalProfit] + c.execute(insert_SC, row) + gsid = self.get_last_insert_id(c) + for h in gsc['bk'][i]['ids']: gsc[h] = {'id': gsid} + else: + # Something bad happened + pass + self.commit() + + return gsc + + def getHeroIds(self, pids, sitename): #Grab playerIds using hero names in HUD_Config.xml try: # derive list of program owner's player ids - self.hero = {} # name of program owner indexed by site id - self.hero_ids = [] + hero = {} # name of program owner indexed by site id + hero_ids = [] # make sure at least two values in list # so that tuple generation creates doesn't use # () or (1,) style for site in self.config.get_supported_sites(): - result = self.get_site_id(site) - if result: - site_id = result[0][0] - self.hero[site_id] = self.config.supported_sites[site].screen_name - p_id = self.get_player_id(self.config, site, self.hero[site_id]) - if p_id: - self.hero_ids.append(int(p_id)) - + hero = self.config.supported_sites[site].screen_name + for n, v in pids.iteritems(): + if n == hero and sitename == site: + hero_ids.append(v) + except: err = traceback.extract_tb(sys.exc_info()[2])[-1] - print _("Error aquiring hero ids:"), str(sys.exc_value) - print err - - inserts = [] - for p in pdata: - if pids[p] in self.hero_ids: - line = [0]*5 + #print _("Error aquiring hero ids:"), str(sys.exc_value) + return hero_ids - if (game['type']=='ring'): line[0] = 1 # count ring hands - if (game['type']=='tour'): line[1] = 1 # count tour hands - if (game['type']=='ring' and game['currency']=='USD'): line[2] = pdata[p]['totalProfit'] #sum of ring profit in USD - if (game['type']=='ring' and game['currency']=='EUR'): line[3] = pdata[p]['totalProfit'] #sum of ring profit in EUR - line[4] = startTime - inserts.append(line) - - cursor = self.get_cursor() - id = None - - for row in inserts: - threshold = [] - threshold.append(row[-1]-THRESHOLD) - threshold.append(row[-1]+THRESHOLD) - cursor.execute(select_sessionscache, threshold) - session_records = cursor.fetchall() - num = len(session_records) - if (num == 1): - id = session_records[0][0] #grab the sessionId - # Try to do the update first: - #print "DEBUG: found 1 record to update" - update_mid = row + row[-1:] - cursor.execute(select_sessionscache_mid, update_mid[-2:]) - mid = len(cursor.fetchall()) - if (mid == 0): - update_startend = row[-1:] + row + threshold - cursor.execute(select_sessionscache_start, update_startend[-3:]) - start = len(cursor.fetchall()) - if (start == 0): - #print "DEBUG:", start, " start record found. Update stats and start time" - cursor.execute(update_sessionscache_end, update_startend) - else: - #print "DEBUG: 1 end record found. Update stats and end time time" - cursor.execute(update_sessionscache_start, update_startend) - else: - #print "DEBUG: update stats mid-session" - cursor.execute(update_sessionscache_mid, update_mid) - elif (num > 1): - session_ids = [session_records[0][0], session_records[1][0]] - session_ids.sort() - # Multiple matches found - merge them into one session and update: - # - Obtain the session start and end times for the new combined session - cursor.execute(merge_sessionscache, session_ids) - merge = cursor.fetchone() - # - Delete the old records - for id in session_ids: - cursor.execute(delete_sessions, id) - # - Insert the new updated record - cursor.execute(insert_sessionscache, merge) - # - Obtain the new sessionId and write over the old ids in Hands - id = self.get_last_insert_id(cursor) #grab the sessionId - update_hands = [id] + session_ids - cursor.execute(update_hands_sessionid, update_hands) - # - Update the newly combined record in SessionsCache with data from this hand - update_mid = row + row[-1:] - cursor.execute(update_sessionscache_mid, update_mid) - elif (num == 0): - # No matches found, insert new session: - insert = row + row[-1:] - insert = insert[-2:] + insert[:-2] - #print "DEBUG: No matches found. Insert record", insert - cursor.execute(insert_sessionscache, insert) - id = self.get_last_insert_id(cursor) #grab the sessionId - else: - # Something bad happened - pass - - return id + def fetchallDict(self, cursor): + data = cursor.fetchall() + if not data: return [] + desc = cursor.description + results = [0]*len(data) + for i in range(len(data)): + results[i] = {} + for n in range(len(desc)): + name = desc[n][0] + results[i][name] = data[i][n] + return results + + def nextHandId(self): + c = self.get_cursor() + c.execute("SELECT max(id) FROM Hands") + id = c.fetchone()[0] + if not id: id = 0 + id += 1 + return id def isDuplicate(self, gametypeID, siteHandNo): dup = False diff --git a/pyfpdb/DerivedStats.py b/pyfpdb/DerivedStats.py index 262a3fee..d29a33ad 100644 --- a/pyfpdb/DerivedStats.py +++ b/pyfpdb/DerivedStats.py @@ -119,16 +119,17 @@ class DerivedStats(): return self.handsactions def assembleHands(self, hand): - self.hands['tableName'] = hand.tablename - self.hands['siteHandNo'] = hand.handid - self.hands['gametypeId'] = None # Leave None, handled later after checking db - self.hands['sessionId'] = None # Leave None, added later if caching sessions - self.hands['startTime'] = hand.startTime # format this! - self.hands['importTime'] = None - self.hands['seats'] = self.countPlayers(hand) - self.hands['maxSeats'] = hand.maxseats - self.hands['texture'] = None # No calculation done for this yet. - self.hands['tourneyId'] = hand.tourneyId + self.hands['tableName'] = hand.tablename + self.hands['siteHandNo'] = hand.handid + self.hands['gametypeId'] = None # Leave None, handled later after checking db + self.hands['sessionId'] = None # Leave None, added later if caching sessions + self.hands['gameSessionId'] = None # Leave None, added later if caching sessions + self.hands['startTime'] = hand.startTime # format this! + self.hands['importTime'] = None + self.hands['seats'] = self.countPlayers(hand) + self.hands['maxSeats'] = hand.maxseats + self.hands['texture'] = None # No calculation done for this yet. + self.hands['tourneyId'] = hand.tourneyId # This (i think...) is correct for both stud and flop games, as hand.board['street'] disappears, and # those values remain default in stud. @@ -213,10 +214,10 @@ class DerivedStats(): for player in hand.players: hcs = hand.join_holecards(player[1], asList=True) - hcs = hcs + [u'0x', u'0x', u'0x', u'0x', u'0x'] - #for i, card in enumerate(hcs[:7], 1): #Python 2.6 syntax + hcs = hcs + [u'0x']*18 + #for i, card in enumerate(hcs[:20, 1): #Python 2.6 syntax # self.handsplayers[player[1]]['card%s' % i] = Card.encodeCard(card) - for i, card in enumerate(hcs[:7]): + for i, card in enumerate(hcs[:20]): self.handsplayers[player[1]]['card%s' % (i+1)] = Card.encodeCard(card) self.handsplayers[player[1]]['startCards'] = Card.calcStartCards(hand, player[1]) diff --git a/pyfpdb/EverestToFpdb.py b/pyfpdb/EverestToFpdb.py index 662d2889..ee7e2135 100644 --- a/pyfpdb/EverestToFpdb.py +++ b/pyfpdb/EverestToFpdb.py @@ -99,12 +99,12 @@ class Everest(HandHistoryConverter): except AttributeError: tmp = handText[0:100] log.error(_("Unable to recognise gametype from: '%s'") % tmp) - log.error(_("determineGameType: Raising FpdbParseError")) + log.error("determineGameType: " + _("Raising FpdbParseError")) raise FpdbParseError(_("Unable to recognise gametype from: '%s'") % tmp) if not m2: tmp = handText[0:100] - log.error(_("determineGameType: Raising FpdbParseError")) + log.error("determineGameType: " + _("Raising FpdbParseError")) raise FpdbParseError(_("Unable to recognise handinfo from: '%s'") % tmp) self.info = {} @@ -231,8 +231,8 @@ class Everest(HandHistoryConverter): elif action.group('ATYPE') in ('FOLD', 'SIT_OUT'): hand.addFold(street, player) else: - print (_("Unimplemented readAction: '%s' '%s'") % (action.group('PSEAT'),action.group('ATYPE'))) - logging.debug(_("Unimplemented readAction: '%s' '%s'") % (action.group('PSEAT'),action.group('ATYPE'))) + print (_("Unimplemented readAction: '%s' '%s'") % (action.group('PSEAT'), action.group('ATYPE'))) + 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): diff --git a/pyfpdb/EverleafToFpdb.py b/pyfpdb/EverleafToFpdb.py index bcd37af9..c5525e36 100755 --- a/pyfpdb/EverleafToFpdb.py +++ b/pyfpdb/EverleafToFpdb.py @@ -41,7 +41,12 @@ class Everleaf(HandHistoryConverter): #re.compile(ur"^(Blinds )?(?P\$| €|)(?P[.0-9]+)/(?:\$| €)?(?P[.0-9]+) (?PNL|PL|) ?(?P(Hold\'em|Omaha|7 Card Stud))", re.MULTILINE) re_HandInfo = re.compile(ur".*#(?P[0-9]+)\n.*\n(Blinds )?(?P[$€])?(?P[.0-9]+)/(?:[$€])?(?P[.0-9]+) (?P.*) - (?P\d\d\d\d/\d\d/\d\d - \d\d:\d\d:\d\d)\nTable (?P.+$)", re.MULTILINE) re_Button = re.compile(ur"^Seat (?P
[0-9]+)\.txt") @@ -91,8 +96,6 @@ class Everleaf(HandHistoryConverter): 'bigBet' 'currency' in ('USD', 'EUR', 'T$', ) or None if we fail to get the info """ - #(TODO: which parts are optional/required?) - # Blinds $0.50/$1 PL Omaha - 2008/12/07 - 21:59:48 # Blinds $0.05/$0.10 NL Hold'em - 2009/02/21 - 11:21:57 # $0.25/$0.50 7 Card Stud - 2008/12/05 - 21:43:59 @@ -108,7 +111,7 @@ or None if we fail to get the info """ if not m: tmp = handText[0:100] log.error(_("Unable to recognise gametype from: '%s'") % tmp) - log.error(_("determineGameType: Raising FpdbParseError")) + log.error("determineGameType: " + _("Raising FpdbParseError")) raise FpdbParseError(_("Unable to recognise gametype from: '%s'") % tmp) mg = m.groupdict() @@ -164,11 +167,6 @@ or None if we fail to get the info """ # https://www.poker4ever.com/tourney/%TOURNEY_NUMBER% # 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 - # Not getting it in my HH files yet, so using - # 2008/11/10 3:58:52 ET - #TODO: Need some date functions to convert to different timezones (Date::Manip for perl rocked for this) hand.startTime = datetime.datetime.strptime(m.group('DATETIME'), "%Y/%m/%d - %H:%M:%S") return @@ -187,9 +185,6 @@ or None if we fail to get the info """ def markStreets(self, hand): - # PREFLOP = ** Dealing down cards ** - # This re fails if, say, river is missing; then we don't get the ** that starts the river. - #m = re.search('(\*\* Dealing down cards \*\*\n)(?P.*?\n\*\*)?( Dealing Flop \*\* \[ (?P\S\S), (?P\S\S), (?P\S\S) \])?(?P.*?\*\*)?( Dealing Turn \*\* \[ (?P\S\S) \])?(?P.*?\*\*)?( Dealing River \*\* \[ (?P\S\S) \])?(?P.*)', hand.handText,re.DOTALL) if hand.gametype['base'] == 'hold': m = re.search(r"\*\* Dealing down cards \*\*(?P.+(?=\*\* Dealing Flop \*\*)|.+)" r"(\*\* Dealing Flop \*\*(?P \[ \S\S, \S\S, \S\S \].+(?=\*\* Dealing Turn \*\*)|.+))?" @@ -285,7 +280,7 @@ or None if we fail to get the info """ elif action.group('ATYPE') == ' complete to': hand.addComplete( street, action.group('PNAME'), action.group('BET')) else: - logging.debug(_("Unimplemented readAction: %s %s") % (action.group('PNAME'),action.group('ATYPE'))) + logging.debug(_("Unimplemented readAction: '%s' '%s'") % (action.group('PNAME'), action.group('ATYPE'))) def readShowdownActions(self, hand): diff --git a/pyfpdb/FullTiltPokerSummary.py b/pyfpdb/FullTiltPokerSummary.py index 98169393..cb02b9c9 100644 --- a/pyfpdb/FullTiltPokerSummary.py +++ b/pyfpdb/FullTiltPokerSummary.py @@ -25,7 +25,6 @@ import datetime from Exceptions import FpdbParseError from HandHistoryConverter import * -import PokerStarsToFpdb from TourneySummary import * class FullTiltPokerSummary(TourneySummary): @@ -45,10 +44,10 @@ class FullTiltPokerSummary(TourneySummary): } substitutions = { - 'LEGAL_ISO' : "USD|EUR|GBP|CAD|FPP", # legal ISO currency codes - 'LS' : "\$|\xe2\x82\xac|", # legal currency symbols - Euro(cp1252, utf-8) - 'TAB' : u"-\u2013'\s\da-zA-Z", # legal characters for tablename - 'NUM' : u".,\d", # legal characters in number format + 'LEGAL_ISO' : "USD|EUR|GBP|CAD|FPP", # legal ISO currency codes + 'LS' : u"\$|\xe2\x82\xac|\u20ac|", # legal currency symbols - Euro(cp1252, utf-8) + 'TAB' : u"-\u2013'\s\da-zA-Z", # legal characters for tablename + 'NUM' : u".,\d", # legal characters in number format } re_SplitTourneys = re.compile("^Full Tilt Poker Tournament Summary") @@ -90,8 +89,8 @@ class FullTiltPokerSummary(TourneySummary): m = self.re_TourneyInfo.search(self.summaryText[:2000]) if m == None: tmp = self.summaryText[0:200] - log.error(_("parseSummary: Unable to recognise Tourney Info: '%s'") % tmp) - log.error(_("parseSummary: Raising FpdbParseError")) + log.error("parseSummary: " + _("Unable to recognise Tourney Info: '%s'") % tmp) + log.error("parseSummary: " + _("Raising FpdbParseError")) raise FpdbParseError(_("Unable to recognise Tourney Info: '%s'") % tmp) #print "DEBUG: m.groupdict(): %s" % m.groupdict() @@ -116,8 +115,8 @@ class FullTiltPokerSummary(TourneySummary): m = self.re_Currency.search(self.summaryText) if m == None: - log.error(_("parseSummary: Unable to locate currency")) - log.error(_("parseSummary: Raising FpdbParseError")) + log.error("parseSummary: " + _("Unable to locate currency")) + log.error("parseSummary: " + _("Raising FpdbParseError")) raise FpdbParseError(_("Unable to locate currency")) #print "DEBUG: m.groupdict(): %s" % m.groupdict() diff --git a/pyfpdb/FulltiltToFpdb.py b/pyfpdb/FulltiltToFpdb.py index 69caa79b..e3e0db09 100755 --- a/pyfpdb/FulltiltToFpdb.py +++ b/pyfpdb/FulltiltToFpdb.py @@ -37,7 +37,7 @@ class Fulltilt(HandHistoryConverter): substitutions = { 'LEGAL_ISO' : "USD|EUR|GBP|CAD|FPP", # legal ISO currency codes 'LS' : u"\$|\u20AC|\xe2\x82\xac|", # legal currency symbols - Euro(cp1252, utf-8) - 'TAB' : u"-\u2013'\s\da-zA-Z", # legal characters for tablename + 'TAB' : u"-\u2013'\s\da-zA-Z#_", # legal characters for tablename 'NUM' : u".,\d", # legal characters in number format } @@ -61,7 +61,9 @@ class Fulltilt(HandHistoryConverter): '400.00': ('100.00', '200.00'), '400': ('100.00', '200.00'), '500.00': ('125.00', '250.00'), '500': ('125.00', '250.00'), '800.00': ('200.00', '400.00'), '800': ('200.00', '400.00'), - '1000.00': ('250.00', '500.00'),'1000': ('250.00', '500.00') + '1000.00': ('250.00', '500.00'),'1000': ('250.00', '500.00'), + '2000.00': ('500.00', '750.00'),'2000': ('500.00', '1000.00'), + '3000.00': ('750.00', '1500.00'),'3000': ('750.00', '1500.00'), } # Static regexes @@ -74,7 +76,7 @@ class Fulltilt(HandHistoryConverter): (Ante\s\$?(?P[%(NUM)s]+)\s)?-\s [%(LS)s]?(?P[%(NUM)s]+\sCap\s)? (?P(No\sLimit|Pot\sLimit|Limit))?\s - (?P(Hold\'em|Omaha(\sH/L|\sHi/Lo|\sHi|)|7\sCard\sStud|Stud\sH/L|Razz|Stud\sHi|2-7\sTriple\sDraw|5\sCard\sDraw|Badugi)) + (?P(Hold\'em|Omaha(\sH/L|\sHi/Lo|\sHi|)|7\sCard\sStud|Stud\sH/L|Razz|Stud\sHi|2-7\sTriple\sDraw|5\sCard\sDraw|Badugi|2-7\sSingle\sDraw)) ''' % substitutions, re.VERBOSE) re_SplitHands = re.compile(r"\n\n\n+") re_TailSplitHands = re.compile(r"(\n\n+)") @@ -172,7 +174,7 @@ class Fulltilt(HandHistoryConverter): self.re_BringIn = re.compile(r"^%(PLAYERS)s brings in for [%(LS)s]?(?P[%(NUM)s]+)" % self.substitutions, re.MULTILINE) self.re_PostBoth = re.compile(r"^%(PLAYERS)s posts small \& big blinds \[[%(LS)s]? (?P[%(NUM)s]+)" % self.substitutions, re.MULTILINE) self.re_HeroCards = re.compile(r"^Dealt to %s(?: \[(?P.+?)\])?( \[(?P.+?)\])" % player_re, re.MULTILINE) - self.re_Action = re.compile(r"^%(PLAYERS)s(?P bets| checks| raises to| completes it to| calls| folds)( [%(LS)s]?(?P[%(NUM)s]+))?" % self.substitutions, re.MULTILINE) + self.re_Action = re.compile(r"^%(PLAYERS)s(?P bets| checks| raises to| completes it to| calls| folds| discards| stands pat)( [%(LS)s]?(?P[%(NUM)s]+))?(\son|\scards?)?(\s\[(?P.+?)\])?" % self.substitutions, re.MULTILINE) self.re_ShowdownAction = re.compile(r"^%s shows \[(?P.*)\]" % player_re, re.MULTILINE) self.re_CollectPot = re.compile(r"^Seat (?P[0-9]+): %(PLAYERS)s (\(button\) |\(small blind\) |\(big blind\) )?(collected|showed \[.*\] and won) \([%(LS)s]?(?P[%(NUM)s]+)\)(, mucked| with.*)?" % self.substitutions, re.MULTILINE) self.re_SitsOut = re.compile(r"^%s sits out" % player_re, re.MULTILINE) @@ -204,7 +206,7 @@ class Fulltilt(HandHistoryConverter): if not m: tmp = handText[0:100] log.error(_("Unable to recognise gametype from: '%s'") % tmp) - log.error(_("determineGameType: Raising FpdbParseError for file '%s'") % self.in_path) + log.error("determineGameType: " + _("Raising FpdbParseError for file '%s'") % self.in_path) raise FpdbParseError(_("Unable to recognise gametype from: '%s'") % tmp) mg = m.groupdict() @@ -222,6 +224,7 @@ class Fulltilt(HandHistoryConverter): '2-7 Triple Draw' : ('draw','27_3draw'), '5 Card Draw' : ('draw','fivedraw'), 'Badugi' : ('draw','badugi'), + '2-7 Single Draw' : ('draw','27_1draw') } currencies = { u'€':'EUR', '$':'USD', '':'T$' } @@ -241,11 +244,12 @@ class Fulltilt(HandHistoryConverter): if info['limitType'] == 'fl' and info['bb'] is not None and info['type'] == 'ring': try: - info['sb'] = self.Lim_Blinds[mg['BB']][0] - info['bb'] = self.Lim_Blinds[mg['BB']][1] + bb = self.clearMoneyString(mg['BB']) + info['sb'] = self.Lim_Blinds[bb][0] + info['bb'] = self.Lim_Blinds[bb][1] except KeyError: log.error(_("Lim_Blinds has no lookup for '%s'") % mg['BB']) - log.error(_("determineGameType: Raising FpdbParseError")) + log.error("determineGameType: " + _("Raising FpdbParseError")) raise FpdbParseError(_("Lim_Blinds has no lookup for '%s'") % mg['BB']) if mg['GAME'] is not None: @@ -362,7 +366,7 @@ class Fulltilt(HandHistoryConverter): if plist == {}: #No players! The hand is either missing stacks or everyone is sitting out - raise FpdbParseError(_("FTP: readPlayerStacks: No players detected (hand #%s)") % hand.handid) + raise FpdbParseError(_("readPlayerStacks: No players detected (hand #%s)") % hand.handid) def markStreets(self, hand): @@ -422,14 +426,14 @@ class Fulltilt(HandHistoryConverter): logging.debug(_("Player bringing in: %s for %s") %(m.group('PNAME'), m.group('BRINGIN'))) hand.addBringIn(m.group('PNAME'), m.group('BRINGIN')) else: - logging.warning(_("No bringin found, handid =%s") % hand.handid) + logging.debug(_("No bringin found, handid =%s") % hand.handid) def readButton(self, hand): try: hand.buttonpos = int(self.re_Button.search(hand.handText).group('BUTTON')) except AttributeError, e: # FTP has no indication that a hand is cancelled. - raise FpdbParseError(_("FTP: readButton: Failed to detect button (hand #%s cancelled?)") % hand.handid) + raise FpdbParseError(_("readButton: Failed to detect button (hand #%s cancelled?)") % hand.handid) def readHeroCards(self, hand): # streets PREFLOP, PREDRAW, and THIRD are special cases beacause @@ -482,8 +486,12 @@ class Fulltilt(HandHistoryConverter): hand.addFold( street, action.group('PNAME')) elif action.group('ATYPE') == ' checks': hand.addCheck( street, action.group('PNAME')) + elif action.group('ATYPE') == ' discards': + hand.addDiscard(street, action.group('PNAME'), action.group('BET'), action.group('CARDS')) + elif action.group('ATYPE') == ' stands pat': + hand.addStandsPat( street, action.group('PNAME'), action.group('CARDS')) else: - print _("FullTilt: DEBUG: unimplemented readAction: '%s' '%s'") %(action.group('PNAME'),action.group('ATYPE'),) + print (_("DEBUG: ") + " " + _("Unimplemented readAction: '%s' '%s'") % (action.group('PNAME'), action.group('ATYPE'))) def readShowdownActions(self, hand): @@ -760,7 +768,7 @@ class Fulltilt(HandHistoryConverter): tourney.addPlayer(rank, a.group('PNAME'), winnings, "USD", 0, 0, 0) #TODO: make it store actual winnings currency else: - print (_("FullTilt: Player finishing stats unreadable : %s") % a) + print (_("Player finishing stats unreadable : %s") % a) # Find Hero n = self.re_TourneyHeroFinishingP.search(playersText) @@ -769,9 +777,9 @@ class Fulltilt(HandHistoryConverter): tourney.hero = heroName # Is this really useful ? if heroName not in tourney.ranks: - print (_("FullTilt: %s not found in tourney.ranks ...") % heroName) + print (_("%s not found in tourney.ranks ...") % heroName) elif (tourney.ranks[heroName] != Decimal(n.group('HERO_FINISHING_POS'))): - print (_("FullTilt: Bad parsing : finish position incoherent : %s / %s") % (tourney.ranks[heroName], n.group('HERO_FINISHING_POS'))) + print (_("Bad parsing : finish position incoherent : %s / %s") % (tourney.ranks[heroName], n.group('HERO_FINISHING_POS'))) return True diff --git a/pyfpdb/GuiImapFetcher.py b/pyfpdb/GuiImapFetcher.py index c50d44e4..b738b139 100644 --- a/pyfpdb/GuiImapFetcher.py +++ b/pyfpdb/GuiImapFetcher.py @@ -107,7 +107,7 @@ class GuiImapFetcher (threading.Thread): def displayConfig(self): box=gtk.HBox(homogeneous=True) - for text in (_("Site"), _("Fetch Type"), _("Mailserver"), _("Username"), _("Password"), _("Mail Folder"), _("Use SSL")): + for text in (_("Site"), _("Fetch Type"), _("Mail Server"), _("Username"), _("Password"), _("Mail Folder"), _("Use SSL")): label=gtk.Label(text) box.add(label) self.mainVBox.pack_start(box, expand=False) diff --git a/pyfpdb/GuiTourneyImport.py b/pyfpdb/GuiTourneyImport.py index 0bc71f3d..0ed33b18 100755 --- a/pyfpdb/GuiTourneyImport.py +++ b/pyfpdb/GuiTourneyImport.py @@ -223,13 +223,18 @@ class SummaryImporter: print "Found %s summaries" %(len(summaryTexts)) errors = 0 imported = 0 + ####Lock Placeholder#### for j, summaryText in enumerate(summaryTexts, start=1): + sc, gsc = {'bk': []}, {'bk': []} + doinsert = len(summaryTexts)==j try: - conv = obj(db=None, config=self.config, siteName=site, summaryText=summaryText, builtFrom = "IMAP") + conv = obj(db=self.database, config=self.config, siteName=site, summaryText=summaryText, builtFrom = "IMAP") + sc, gsc = conv.updateSessionsCache(sc, gsc, None, doinsert) except FpdbParseError, e: errors += 1 print _("Finished importing %s/%s tournament summaries") %(j, len(summaryTexts)) imported = j + ####Lock Placeholder#### return (imported - errors, errors) def clearFileList(self): diff --git a/pyfpdb/HUD_config.test.xml b/pyfpdb/HUD_config.test.xml index f8a99ee3..fba76fe1 100644 --- a/pyfpdb/HUD_config.test.xml +++ b/pyfpdb/HUD_config.test.xml @@ -2,7 +2,7 @@ - +