From e53e386346a2636b516124d808cdd1001f366e8a Mon Sep 17 00:00:00 2001 From: Steffen Schaumburg Date: Sat, 20 Nov 2010 09:05:52 +0100 Subject: [PATCH 01/22] add TODO to gettextify stove --- pyfpdb/Stove.py | 1 + 1 file changed, 1 insertion(+) diff --git a/pyfpdb/Stove.py b/pyfpdb/Stove.py index ffbdff4f..eb939c0f 100755 --- a/pyfpdb/Stove.py +++ b/pyfpdb/Stove.py @@ -9,6 +9,7 @@ # it under the terms of the GNU General Public License as published by # the Free Software Foundation, version 3 of the License. # +#TODO: gettextify import sys, random import pokereval From da800b7abb5e205f340d833a1b9982d9b4b23afc Mon Sep 17 00:00:00 2001 From: Steffen Schaumburg Date: Mon, 22 Nov 2010 04:25:31 +0100 Subject: [PATCH 02/22] restore HUD executable flag --- pyfpdb/HUD_main.pyw | 0 1 file changed, 0 insertions(+), 0 deletions(-) mode change 100644 => 100755 pyfpdb/HUD_main.pyw diff --git a/pyfpdb/HUD_main.pyw b/pyfpdb/HUD_main.pyw old mode 100644 new mode 100755 From ba1b15048b87fe11b552b28a4942d76666db2b8c Mon Sep 17 00:00:00 2001 From: Worros Date: Mon, 22 Nov 2010 13:28:25 +0800 Subject: [PATCH 03/22] fpdb.pyw: Fix comment --- pyfpdb/fpdb.pyw | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyfpdb/fpdb.pyw b/pyfpdb/fpdb.pyw index 91a0ebef..612f007f 100755 --- a/pyfpdb/fpdb.pyw +++ b/pyfpdb/fpdb.pyw @@ -1000,7 +1000,7 @@ class fpdb: self.add_and_display_tab(bulk_tab, _("Bulk Import")) def tab_tourney_import(self, widget, data=None): - """opens a tab for bulk importing""" + """opens a tab for bulk importing tournament summaries""" new_import_thread = GuiTourneyImport.GuiTourneyImport(self.settings, self.config, self.sql, self.window) self.threads.append(new_import_thread) bulk_tab=new_import_thread.get_vbox() From 2f72f5416eb15488ecc8d7cde9cbcbb7731038dd Mon Sep 17 00:00:00 2001 From: Eratosthenes Date: Mon, 22 Nov 2010 16:37:37 -0500 Subject: [PATCH 04/22] Handle tourney table changes and closures. --- pyfpdb/HUD_main.pyw | 87 +++++++++++++++++++++++-------------------- pyfpdb/Mucked.py | 9 +++-- pyfpdb/TableWindow.py | 14 ++++--- pyfpdb/XTables.py | 6 ++- 4 files changed, 65 insertions(+), 51 deletions(-) diff --git a/pyfpdb/HUD_main.pyw b/pyfpdb/HUD_main.pyw index cd29f4ae..145e9048 100644 --- a/pyfpdb/HUD_main.pyw +++ b/pyfpdb/HUD_main.pyw @@ -24,9 +24,6 @@ Main for FreePokerTools HUD. """ # TODO allow window resizing -# TODO hud to echo, but ignore non numbers -# TODO no stat window for hero -# TODO things to add to config.xml # Standard Library modules import sys @@ -39,26 +36,21 @@ import traceback import thread import time import string -import re # pyGTK modules -import pygtk import gtk import gobject # FreePokerTools modules import Configuration - - import Database -from HandHistoryConverter import getTableTitleRe +import Hud + # get the correct module for the current os if os.name == 'posix': import XTables as Tables elif os.name == 'nt': import WinTables as Tables -#import Tables -import Hud import locale lang = locale.getdefaultlocale()[0][0:2] @@ -79,7 +71,6 @@ else: c = Configuration.Config(file=options.config, dbname=options.dbname) log = Configuration.get_logger("logging.conf", "hud", log_dir=c.dir_log, log_file='HUD-log.txt') - class HUD_main(object): """A main() object to own both the read_stdin thread and the gui.""" # This class mainly provides state for controlling the multiple HUDs. @@ -144,14 +135,13 @@ class HUD_main(object): pass def client_destroyed(self, widget, hud): # call back for terminating the main eventloop - self.kill_hud(None, hud.table.name) + self.kill_hud(None, hud.table.key) def game_changed(self, widget, hud): print _("hud_main: Game changed.") def table_changed(self, widget, hud): - print _("hud_main: Table changed.") - self.kill_hud(None, hud.table.name) + self.kill_hud(None, hud.table.key) def destroy(self, *args): # call back for terminating the main eventloop log.info(_("Terminating normally.")) @@ -159,12 +149,24 @@ class HUD_main(object): def kill_hud(self, event, table): # called by an event in the HUD, to kill this specific HUD - if table in self.hud_dict: - self.hud_dict[table].kill() - self.hud_dict[table].main_window.destroy() - self.vb.remove(self.hud_dict[table].tablehudlabel) - del(self.hud_dict[table]) - self.main_window.resize(1, 1) + +# This method can be called by either gui or non-gui thread. It doesn't +# cost much to always do it in a thread-safe manner. + def idle(): + gtk.gdk.threads_enter() + try: + if table in self.hud_dict: + self.hud_dict[table].kill() + self.hud_dict[table].main_window.destroy() + self.vb.remove(self.hud_dict[table].tablehudlabel) + del(self.hud_dict[table]) + self.main_window.resize(1, 1) + except: + pass + finally: + gtk.gdk.threads_leave() + + gobject.idle_add(idle) def check_tables(self): for hud in self.hud_dict.keys(): @@ -178,42 +180,42 @@ class HUD_main(object): gtk.gdk.threads_enter() try: - table.gdkhandle = gtk.gdk.window_foreign_new(table.number) newlabel = gtk.Label("%s - %s" % (table.site, table_name)) self.vb.add(newlabel) newlabel.show() self.main_window.resize_children() - - self.hud_dict[table_name].tablehudlabel = newlabel - self.hud_dict[table_name].create(new_hand_id, self.config, stat_dict, cards) - for m in self.hud_dict[table_name].aux_windows: + + self.hud_dict[table.key].tablehudlabel = newlabel + self.hud_dict[table.key].create(new_hand_id, self.config, stat_dict, cards) + for m in self.hud_dict[table.key].aux_windows: m.create() m.update_gui(new_hand_id) - self.hud_dict[table_name].update(new_hand_id, self.config) - self.hud_dict[table_name].reposition_windows() + self.hud_dict[table.key].update(new_hand_id, self.config) + self.hud_dict[table.key].reposition_windows() except: log.error("*** Exception in HUD_main::idle_func() *** " + str(sys.exc_info())) for e in traceback.format_tb(sys.exc_info()[2]): log.error(e) finally: gtk.gdk.threads_leave() - return False - - self.hud_dict[table_name] = Hud.Hud(self, table, max, poker_game, self.config, self.db_connection) - self.hud_dict[table_name].table_name = table_name - self.hud_dict[table_name].stat_dict = stat_dict - self.hud_dict[table_name].cards = cards + return False + self.hud_dict[table.key] = Hud.Hud(self, table, max, poker_game, self.config, self.db_connection) + self.hud_dict[table.key].table_name = table_name + self.hud_dict[table.key].stat_dict = stat_dict + self.hud_dict[table.key].cards = cards + table.hud = self.hud_dict[table.key] + # set agg_bb_mult so that aggregate_tour and aggregate_ring can be ignored, # agg_bb_mult == 1 means no aggregation after these if statements: if type == "tour" and self.hud_params['aggregate_tour'] == False: - self.hud_dict[table_name].hud_params['agg_bb_mult'] = 1 + self.hud_dict[table.key].hud_params['agg_bb_mult'] = 1 elif type == "ring" and self.hud_params['aggregate_ring'] == False: - self.hud_dict[table_name].hud_params['agg_bb_mult'] = 1 + self.hud_dict[table.key].hud_params['agg_bb_mult'] = 1 if type == "tour" and self.hud_params['h_aggregate_tour'] == False: - self.hud_dict[table_name].hud_params['h_agg_bb_mult'] = 1 + self.hud_dict[table.key].hud_params['h_agg_bb_mult'] = 1 elif type == "ring" and self.hud_params['h_aggregate_ring'] == False: - self.hud_dict[table_name].hud_params['h_agg_bb_mult'] = 1 + self.hud_dict[table.key].hud_params['h_agg_bb_mult'] = 1 # sqlcoder: I forget why these are set to true (aren't they ignored from now on?) # but I think it's needed: self.hud_params['aggregate_ring'] = True @@ -222,7 +224,7 @@ class HUD_main(object): self.hud_params['aggregate_tour'] = True self.hud_params['h_aggregate_tour'] = True - [aw.update_data(new_hand_id, self.db_connection) for aw in self.hud_dict[table_name].aux_windows] + [aw.update_data(new_hand_id, self.db_connection) for aw in self.hud_dict[table.key].aux_windows] gobject.idle_add(idle_func) def update_HUD(self, new_hand_id, table_name, config): @@ -268,6 +270,8 @@ class HUD_main(object): self.destroy() break # this thread is not always killed immediately with gtk.main_quit() +# This block cannot be hoisted outside the while loop, because it would +# cause a problem when auto importing into an empty db. if not found: for site in self.config.get_supported_sites(): result = self.db_connection.get_site_id(site) @@ -355,9 +359,10 @@ class HUD_main(object): log.info(_("HUD_main.read_stdin: hand read in %4.3f seconds (%4.3f,%4.3f,%4.3f,%4.3f,%4.3f,%4.3f)") % (t6 - t0,t1 - t0,t2 - t0,t3 - t0,t4 - t0,t5 - t0,t6 - t0)) self.db_connection.connection.rollback() -# if type == "tour": -# tablewindow.check_table_no(None) -# # Ray!! tablewindow::check_table_no expects a HUD as an argument! + + if type == "tour": + self.hud_dict[temp_key].table.check_table_no(self.hud_dict[temp_key]) + if __name__== "__main__": # start the HUD_main object diff --git a/pyfpdb/Mucked.py b/pyfpdb/Mucked.py index cda24631..cf948221 100755 --- a/pyfpdb/Mucked.py +++ b/pyfpdb/Mucked.py @@ -415,9 +415,12 @@ class Aux_Seats(Aux_Window): # Methods likely to be of use for any Seat_Window implementation def destroy(self): """Destroy all of the seat windows.""" - for i in self.m_windows.keys(): - self.m_windows[i].destroy() - del(self.m_windows[i]) + try: + for i in self.m_windows.keys(): + self.m_windows[i].destroy() + del(self.m_windows[i]) + except AttributeError: + pass # Methods likely to be useful for mucked card windows (or similar) only def hide(self): diff --git a/pyfpdb/TableWindow.py b/pyfpdb/TableWindow.py index d780bdbc..61d7bd80 100644 --- a/pyfpdb/TableWindow.py +++ b/pyfpdb/TableWindow.py @@ -116,6 +116,7 @@ class Table_Window(object): self.config = config self.site = site + self.hud = None # fill in later if tournament is not None and table_number is not None: self.tournament = int(tournament) self.table = int(table_number) @@ -123,11 +124,13 @@ class Table_Window(object): self.type = "tour" table_kwargs = dict(tournament = self.tournament, table_number = self.table) self.tableno_re = getTableNoRe(self.config, self.site, tournament = self.tournament) + self.key = tournament # used as key for the hud_dict in HUD_main elif table_name is not None: self.name = table_name self.type = "cash" self.tournament = None table_kwargs = dict(table_name = table_name) + self.key = table_name else: return None @@ -135,6 +138,7 @@ class Table_Window(object): self.search_string = getTableTitleRe(self.config, self.site, self.type, **table_kwargs) self.find_table_parameters() + self.gdkhandle = gtk.gdk.window_foreign_new(self.number) geo = self.get_geometry() if geo is None: return None self.width = geo['width'] @@ -150,7 +154,7 @@ class Table_Window(object): # __str__ method for testing likely_attrs = ("number", "title", "site", "width", "height", "x", "y", "tournament", "table", "gdkhandle", "window", "parent", - "game", "search_string", "tableno_re") + "key", "hud", "game", "search_string", "tableno_re") temp = 'TableWindow object\n' for a in likely_attrs: if getattr(self, a, 0): @@ -185,13 +189,13 @@ class Table_Window(object): return False try: - mo = re.search(self.tableno_re, new_title) + mo = re.search(self.tableno_re, new_title) except AttributeError: #'Table' object has no attribute 'tableno_re' - return False + return False if mo is not None: #print "get_table_no: mo=",mo.groups() - return mo.group(1) + return int(mo.group(1)) return False #################################################################### @@ -256,7 +260,7 @@ class Table_Window(object): if result != False and result != self.table: self.table = result if hud is not None: - hud.main_window.emit("table_changed", hud) + hud.parent.main_window.emit("table_changed", hud) return True def check_bad_words(self, title): diff --git a/pyfpdb/XTables.py b/pyfpdb/XTables.py index b9a363d0..b082b8b2 100644 --- a/pyfpdb/XTables.py +++ b/pyfpdb/XTables.py @@ -44,6 +44,10 @@ class Table(Table_Window): def find_table_parameters(self): +# This is called by __init__(). Find the poker table window of interest, +# given the self.search_string. Then populate self.number, self.title, +# self.window, and self.parent (if required). + reg = ''' \s+(?P[\dxabcdef]+) # XID in hex \s(?P.+): # window title @@ -53,12 +57,10 @@ class Table(Table_Window): for listing in os.popen('xwininfo -root -tree').readlines(): if re.search(self.search_string, listing, re.I): mo = re.match(reg, listing, re.VERBOSE) -# mo = re.match('\s+([\dxabcdef]+) (.+):\s\(\"([a-zA-Z0-9\-.]+)\".+ (\d+)x(\d+)\+\d+\+\d+ \+(\d+)\+(\d+)', listing) title = re.sub('\"', '', mo.groupdict()["TITLE"]) if self.check_bad_words(title): continue self.number = int( mo.groupdict()["XID"], 0 ) self.title = title - self.hud = None # specified later break if self.number is None: From ac2937dc78208e7f3d418e6fa4d098bc1a40f2ea Mon Sep 17 00:00:00 2001 From: Worros <carl.gherardi@gmail.com> Date: Tue, 23 Nov 2010 12:59:23 +0800 Subject: [PATCH 05/22] Session Viewer: Add a warning dialog on loading --- pyfpdb/GuiSessionViewer.py | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/pyfpdb/GuiSessionViewer.py b/pyfpdb/GuiSessionViewer.py index d12f1d10..6f0fd269 100644 --- a/pyfpdb/GuiSessionViewer.py +++ b/pyfpdb/GuiSessionViewer.py @@ -50,11 +50,14 @@ import Database import Filters import Charset +DEBUG = False + class GuiSessionViewer (threading.Thread): def __init__(self, config, querylist, mainwin, debug=True): self.debug = debug self.conf = config self.sql = querylist + self.window = mainwin self.liststore = None @@ -153,6 +156,28 @@ class GuiSessionViewer (threading.Thread): # make sure Hand column is not displayed #[x for x in self.columns if x[0] == 'hand'][0][1] = False + if DEBUG == False: + warning_string = """ +Session Viewer is proof of concept code only, and contains many bugs. + +Feel free to use the viewer, but there is no guarantee that the data is accurate. + +If you are interested in developing the code further please contact us via the usual channels. + +Thankyou +""" + self.warning_box(warning_string) + + def warning_box(self, str, diatitle=_("FPDB WARNING")): + diaWarning = gtk.Dialog(title=diatitle, parent=self.window, flags=gtk.DIALOG_DESTROY_WITH_PARENT, buttons=(gtk.STOCK_OK,gtk.RESPONSE_OK)) + + label = gtk.Label(str) + diaWarning.vbox.add(label) + label.show() + + response = diaWarning.run() + diaWarning.destroy() + return response def get_vbox(self): """returns the vbox of this thread""" From 9d89f9807e7338e09288498659a531b3f05f7e20 Mon Sep 17 00:00:00 2001 From: Eratosthenes <ray.barker@hughes.net> Date: Tue, 23 Nov 2010 16:56:41 -0500 Subject: [PATCH 06/22] Make HUD_main.pyw executable, again. --- pyfpdb/HUD_main.pyw | 0 1 file changed, 0 insertions(+), 0 deletions(-) mode change 100644 => 100755 pyfpdb/HUD_main.pyw diff --git a/pyfpdb/HUD_main.pyw b/pyfpdb/HUD_main.pyw old mode 100644 new mode 100755 From d3f07aae45667b7fd1648fa0bb12aa82dabe5f6c Mon Sep 17 00:00:00 2001 From: Worros <carl.gherardi@gmail.com> Date: Wed, 24 Nov 2010 11:30:13 +0800 Subject: [PATCH 07/22] Stove: Mockup interface for a Stovelike EV calculator page --- pyfpdb/GuiStove.py | 205 +++++++++++++++++++++++++++++++++++++++++++++ pyfpdb/fpdb.pyw | 10 +++ 2 files changed, 215 insertions(+) create mode 100644 pyfpdb/GuiStove.py diff --git a/pyfpdb/GuiStove.py b/pyfpdb/GuiStove.py new file mode 100644 index 00000000..1ee825e2 --- /dev/null +++ b/pyfpdb/GuiStove.py @@ -0,0 +1,205 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +#Copyright 2008-2010 Steffen Schaumburg +#This program is free software: you can redistribute it and/or modify +#it under the terms of the GNU Affero General Public License as published by +#the Free Software Foundation, version 3 of the License. +# +#This program is distributed in the hope that it will be useful, +#but WITHOUT ANY WARRANTY; without even the implied warranty of +#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +#GNU General Public License for more details. +# +#You should have received a copy of the GNU Affero General Public License +#along with this program. If not, see <http://www.gnu.org/licenses/>. +#In the "official" distribution you can find the license in agpl-3.0.txt. + +import L10n +_ = L10n.get_translation() + +import pygtk +pygtk.require('2.0') +import gtk +import os +import sys + +import Charset + +DEBUG = False + +class GuiStove(): + + def __init__(self, config, parent, debug=True): + """Constructor for GraphViewer""" + self.conf = config + self.parent = parent + + self.mainHBox = gtk.HBox(False, 0) + + # hierarchy: self.mainHBox / self.notebook + + self.notebook = gtk.Notebook() + self.notebook.set_tab_pos(gtk.POS_TOP) + self.notebook.set_show_tabs(True) + self.notebook.set_show_border(True) + + self.createFlopTab() + #self.createStudTab() + #self.createDrawTab() + + + self.mainHBox.add(self.notebook) + + self.mainHBox.show_all() + + if DEBUG == False: + warning_string = """ +Stove is a GUI mockup of a EV calculation page, and completely non functional. + +Unless you are interested in developing this feature, please ignore this page. + +If you are interested in developing the code further see GuiStove.py and Stove.py + +Thankyou +""" + self.warning_box(warning_string) + + + def warning_box(self, str, diatitle=_("FPDB WARNING")): + diaWarning = gtk.Dialog(title=diatitle, parent=self.parent, flags=gtk.DIALOG_DESTROY_WITH_PARENT, buttons=(gtk.STOCK_OK,gtk.RESPONSE_OK)) + + label = gtk.Label(str) + diaWarning.vbox.add(label) + label.show() + + response = diaWarning.run() + diaWarning.destroy() + return response + + + def get_active_text(combobox): + model = combobox.get_model() + active = combobox.get_active() + if active < 0: + return None + return model[active][0] + + def create_combo_box(self, strings): + combobox = gtk.combo_box_new_text() + for label in strings: + combobox.append_text(label) + combobox.set_active(0) + return combobox + + + def createFlopTab(self): + # hierarchy: hbox / ddbox / ddhbox / Label + flop_games_cb | label + players_cb + # / gamehbox / in_frame / table / + # / out_frame + + tab_title = "Flop" + label = gtk.Label(tab_title) + + ddbox = gtk.VBox(False, 0) + self.notebook.append_page(ddbox, label) + + ddhbox = gtk.HBox(False, 0) + gamehbox = gtk.HBox(False, 0) + + ddbox.add(ddhbox) + ddbox.add(gamehbox) + + # Combo boxes in the top row + + games = [ "Holdem", "Omaha", "Omaha 8", ] + players = [ "2", "3", "4", "5", "6", "7", "8", "9", "10" ] + flop_games_cb = self.create_combo_box(games) + players_cb = self.create_combo_box(players) + + label = gtk.Label("Gametype:") + ddhbox.add(label) + ddhbox.add(flop_games_cb) + label = gtk.Label("Players:") + ddhbox.add(label) + ddhbox.add(players_cb) + + # Frames for Stove input and output + + in_frame = gtk.Frame("Input:") + out_frame = gtk.Frame("Output:") + + gamehbox.add(in_frame) + gamehbox.add(out_frame) + + outstring = """ +No board given. Using Monte-Carlo simulation... +Enumerated 2053443 possible plays. +Your hand: (Ad Ac) +Against the range: { + AhAd, AhAs, AdAs, KhKd, KhKs, + KhKc, KdKs, KdKc, KsKc, QhQd, + QhQs, QhQc, QdQs, QdQc, QsQc, + JhJd, JhJs, JhJc, JdJs, JdJc, + JsJc + } + + Win Lose Tie + 69.91% 15.83% 14.26% + +""" + label = gtk.Label(outstring) + out_frame.add(label) + + # Input Frame + table = gtk.Table(4, 4, True) + label = gtk.Label("Board:") + board = gtk.Entry() + #board.connect("changed", self._some_function, arg) + + btn1 = gtk.Button() + btn1.set_image(gtk.image_new_from_stock(gtk.STOCK_INDEX, gtk.ICON_SIZE_BUTTON)) + #btn.connect('clicked', self._some_function, arg) + table.attach(label, 0, 1, 0, 1, xoptions=gtk.SHRINK, yoptions=gtk.SHRINK) + table.attach(board, 1, 2, 0, 1, xoptions=gtk.SHRINK, yoptions=gtk.SHRINK) + table.attach(btn1, 2, 3, 0, 1, xoptions=gtk.SHRINK, yoptions=gtk.SHRINK) + + + label = gtk.Label("Player1:") + board = gtk.Entry() + #board.connect("changed", self._some_function, arg) + btn2 = gtk.Button() + btn2.set_image(gtk.image_new_from_stock(gtk.STOCK_INDEX, gtk.ICON_SIZE_BUTTON)) + #btn.connect('clicked', self._some_function, arg) + btn3 = gtk.Button() + btn3.set_image(gtk.image_new_from_stock(gtk.STOCK_INDEX, gtk.ICON_SIZE_BUTTON)) + #btn.connect('clicked', self._some_function, arg) + table.attach(label, 0, 1, 1, 2, xoptions=gtk.SHRINK, yoptions=gtk.SHRINK) + table.attach(board, 1, 2, 1, 2, xoptions=gtk.SHRINK, yoptions=gtk.SHRINK) + table.attach(btn2, 2, 3, 1, 2, xoptions=gtk.SHRINK, yoptions=gtk.SHRINK) + table.attach(btn3, 3, 4, 1, 2, xoptions=gtk.SHRINK, yoptions=gtk.SHRINK) + + + label = gtk.Label("Player2:") + board = gtk.Entry() + #board.connect("changed", self._some_function, arg) + btn4 = gtk.Button() + btn4.set_image(gtk.image_new_from_stock(gtk.STOCK_INDEX, gtk.ICON_SIZE_BUTTON)) + #btn.connect('clicked', self._some_function, arg) + btn5 = gtk.Button() + btn5.set_image(gtk.image_new_from_stock(gtk.STOCK_INDEX, gtk.ICON_SIZE_BUTTON)) + #btn.connect('clicked', self._some_function, arg) + table.attach(label, 0, 1, 2, 3, xoptions=gtk.SHRINK, yoptions=gtk.SHRINK) + table.attach(board, 1, 2, 2, 3, xoptions=gtk.SHRINK, yoptions=gtk.SHRINK) + table.attach(btn4, 2, 3, 2, 3, xoptions=gtk.SHRINK, yoptions=gtk.SHRINK) + table.attach(btn5, 3, 4, 2, 3, xoptions=gtk.SHRINK, yoptions=gtk.SHRINK) + + #table.attach(label, i, i+1, j, j+1,) + in_frame.add(table) + + + + def get_vbox(self): + """returns the vbox of this thread""" + return self.mainHBox + #end def get_vbox diff --git a/pyfpdb/fpdb.pyw b/pyfpdb/fpdb.pyw index 612f007f..3660dd57 100755 --- a/pyfpdb/fpdb.pyw +++ b/pyfpdb/fpdb.pyw @@ -116,6 +116,7 @@ import GuiAutoImport import GuiGraphViewer import GuiTourneyGraphViewer import GuiSessionViewer +import GuiStove import SQL import Database import Configuration @@ -778,6 +779,7 @@ class fpdb: <menuitem action="tourneyviewer"/> <menuitem action="posnstats"/> <menuitem action="sessionstats"/> + <menuitem action="stove"/> </menu> <menu action="database"> <menuitem action="maintaindbs"/> @@ -814,6 +816,7 @@ class fpdb: ('hudConfigurator', None, _('_HUD Configurator'), _('<control>H'), 'HUD Configurator', self.diaHudConfigurator), ('graphs', None, _('_Graphs'), _('<control>G'), 'Graphs', self.tabGraphViewer), ('tourneygraphs', None, _('Tourney Graphs'), None, 'TourneyGraphs', self.tabTourneyGraphViewer), + ('stove', None, _('Stove'), None, 'Stove', self.tabStove), ('ringplayerstats', None, _('Ring _Player Stats (tabulated view, not on pgsql)'), _('<control>P'), 'Ring Player Stats (tabulated view, not on pgsql)', self.tab_ring_player_stats), ('tourneyplayerstats', None, _('_Tourney Stats (tabulated view, not on pgsql)'), _('<control>T'), 'Tourney Stats (tabulated view, not on pgsql)', self.tab_tourney_player_stats), ('tourneyviewer', None, _('Tourney _Viewer'), None, 'Tourney Viewer)', self.tab_tourney_viewer_stats), @@ -1078,6 +1081,13 @@ You can find the full license texts in agpl-3.0.txt, gpl-2.0.txt, gpl-3.0.txt an gv_tab = new_gv_thread.get_vbox() self.add_and_display_tab(gv_tab, _("Tourney Graphs")) + def tabStove(self, widget, data=None): + """opens a tab for bulk importing tournament summaries""" + thread = GuiStove.GuiStove(self.config, self.window) + self.threads.append(thread) + tab = thread.get_vbox() + self.add_and_display_tab(tab, _("Stove")) + def __init__(self): # no more than 1 process can this lock at a time: self.lock = interlocks.InterProcessLock(name="fpdb_global_lock") From 18df2ef2c6fb77d9ece345d2c051b911fa6d0f8d Mon Sep 17 00:00:00 2001 From: Forrest <forrestdg@gmail.com> Date: Tue, 23 Nov 2010 15:03:40 +0100 Subject: [PATCH 08/22] add a regression test file for winamax tourney --- .../Winamax/Flop/NLHE-EUR-STT-FullHist.txt | 310 ++++++++++++++++++ 1 file changed, 310 insertions(+) create mode 100644 pyfpdb/regression-test-files/tour/Winamax/Flop/NLHE-EUR-STT-FullHist.txt diff --git a/pyfpdb/regression-test-files/tour/Winamax/Flop/NLHE-EUR-STT-FullHist.txt b/pyfpdb/regression-test-files/tour/Winamax/Flop/NLHE-EUR-STT-FullHist.txt new file mode 100644 index 00000000..2443c0be --- /dev/null +++ b/pyfpdb/regression-test-files/tour/Winamax/Flop/NLHE-EUR-STT-FullHist.txt @@ -0,0 +1,310 @@ +Winamax Poker - Tournament "No Limit Hold'em" buyIn: 0,90€ + 0,10€ level: 0 - HandId: #7959970263859201-1-1288800933 - Holdem no limit (10/20) - 2010/11/03 17:15:33 UTC +Table: 'No Limit Hold'em(1853325)#0' 10-max (real money) Seat #3 is the button +Seat 1: Player6 (1500) +Seat 2: Player3 (1500) +Seat 3: Player9 (1500) +Seat 4: Player1 (1500) +Seat 5: Player0 (1500) +Seat 6: Player7 (1500) +Seat 7: Player4 (1500) +Seat 8: Player2 (1500) +Seat 9: Player5 (1500) +Seat 10: Player8 (1500) +*** ANTE/BLINDS *** +Player1 posts small blind 10 +Player0 posts big blind 20 +Dealt to Player6 [Jd 7h] +*** PRE-FLOP *** +Player7 folds +Player4 folds +Player2 folds +Player5 calls 20 +Player8 folds +Player6 calls 20 +Player3 calls 20 +Player9 calls 20 +Player1 calls 10 +Player0 checks +*** FLOP *** [Td 4c 5h] +Player1 checks +Player0 checks +Player5 checks +Player6 bets 80 +Player3 calls 80 +Player9 folds +Player1 folds +Player0 calls 80 +Player5 folds +*** TURN *** [Td 4c 5h][3h] +Player0 checks +Player6 checks +Player3 checks +*** RIVER *** [Td 4c 5h 3h][Kc] +Player0 checks +Player6 bets 240 +Player3 calls 240 +Player0 folds +*** SHOW DOWN *** +Player6 shows [Jd 7h] (High card : King) +Player3 shows [Kd 4d] (Two pairs : Kings and 4) +Player3 collected 840 from pot +*** SUMMARY *** +Total pot 840 | No rake +Board: [Td 4c 5h 3h Kc] +Seat 1: Player6 showed [Jd 7h] and lost with High card : King +Seat 2: Player3 showed [Kd 4d] and won 840 with Two pairs : Kings and 4 +Seat 3: Player9 (button) folded on the flop +Seat 4: Player1 (small blind) folded on the flop +Seat 5: Player0 (big blind) folded on the river +Seat 6: Player7 folded on the pre-flop +Seat 7: Player4 folded +Seat 8: Player2 folded on the pre-flop +Seat 9: Player5 folded on the flop +Seat 10: Player8 folded on the pre-flop + + +Winamax Poker - Tournament "No Limit Hold'em" buyIn: 0,90€ + 0,10€ level: 0 - HandId: #7959970263859201-2-1288801071 - Holdem no limit (10/20) - 2010/11/03 17:17:51 UTC +Table: 'No Limit Hold'em(1853325)#0' 10-max (real money) Seat #4 is the button +Seat 1: Player6 (1160) +Seat 2: Player3 (2000) +Seat 3: Player9 (1480) +Seat 4: Player1 (1480) +Seat 5: Player0 (1400) +Seat 6: Player7 (1500) +Seat 7: Player4 (1500) +Seat 8: Player2 (1500) +Seat 9: Player5 (1480) +Seat 10: Player8 (1500) +*** ANTE/BLINDS *** +Player0 posts small blind 10 +Player7 posts big blind 20 +Dealt to Player6 [7h 3s] +*** PRE-FLOP *** +Player4 folds +Player2 folds +Player5 calls 20 +Player8 calls 20 +Player6 folds +Player3 calls 20 +Player9 folds +Player1 calls 20 +Player0 calls 10 +Player7 checks +*** FLOP *** [Jh 6h Qd] +Player0 checks +Player7 bets 20 +Player5 calls 20 +Player8 calls 20 +Player3 folds +Player1 calls 20 +Player0 calls 20 +*** TURN *** [Jh 6h Qd][5s] +Player0 checks +Player7 checks +Player5 bets 220 +Player8 folds +Player1 folds +Player0 folds +Player7 calls 220 +*** RIVER *** [Jh 6h Qd 5s][3c] +Player7 checks +Player5 bets 660 +Player7 folds +Player5 collected 1320 from pot +*** SUMMARY *** +Total pot 1.32k | No rake +Board: [Jh 6h Qd 5s 3c] +Seat 1: Player6 folded on the pre-flop +Seat 2: Player3 folded on the flop +Seat 3: Player9 folded on the pre-flop +Seat 4: Player1 (button) folded on the turn +Seat 5: Player0 (small blind) folded on the turn +Seat 6: Player7 (big blind) folded on the river +Seat 7: Player4 folded on the pre-flop +Seat 8: Player2 folded on the pre-flop +Seat 9: Player5 won 1320 +Seat 10: Player8 folded on the turn + + +Winamax Poker - Tournament "No Limit Hold'em" buyIn: 0,90€ + 0,10€ level: 0 - HandId: #7959970263859201-3-1288801160 - Holdem no limit (10/20) - 2010/11/03 17:19:20 UTC +Table: 'No Limit Hold'em(1853325)#0' 10-max (real money) Seat #5 is the button +Seat 1: Player6 (1160) +Seat 2: Player3 (1980) +Seat 3: Player9 (1480) +Seat 4: Player1 (1440) +Seat 5: Player0 (1360) +Seat 6: Player7 (1240) +Seat 7: Player4 (1500) +Seat 8: Player2 (1500) +Seat 9: Player5 (1880) +Seat 10: Player8 (1460) +*** ANTE/BLINDS *** +Player7 posts small blind 10 +Player4 posts big blind 20 +Dealt to Player6 [4h 3d] +*** PRE-FLOP *** +Player2 folds +Player5 calls 20 +Player8 calls 20 +Player6 folds +Player3 folds +Player9 folds +Player1 calls 20 +Player0 folds +Player7 raises 20 to 40 +Player4 folds +Player5 calls 20 +Player8 calls 20 +Player1 calls 20 +*** FLOP *** [Qd Kh 4s] +Player7 bets 1200 and is all-in +Player5 folds +Player8 folds +Player1 folds +Player7 collected 1380 from pot +*** SUMMARY *** +Total pot 1.38k | No rake +Board: [Qd Kh 4s] +Seat 1: Player6 folded on the pre-flop +Seat 2: Player3 folded on the pre-flop +Seat 3: Player9 folded on the pre-flop +Seat 4: Player1 folded on the flop +Seat 5: Player0 (button) folded on the pre-flop +Seat 6: Player7 (small blind) won 1380 +Seat 7: Player4 (big blind) folded on the pre-flop +Seat 8: Player2 folded on the pre-flop +Seat 9: Player5 folded on the flop +Seat 10: Player8 folded on the flop + + +Winamax Poker - Tournament "No Limit Hold'em" buyIn: 0,90€ + 0,10€ level: 0 - HandId: #7959970263859201-4-1288801277 - Holdem no limit (10/20) - 2010/11/03 17:21:17 UTC +Table: 'No Limit Hold'em(1853325)#0' 10-max (real money) Seat #6 is the button +Seat 1: Player6 (1160) +Seat 2: Player3 (1980) +Seat 3: Player9 (1480) +Seat 4: Player1 (1400) +Seat 5: Player0 (1360) +Seat 6: Player7 (1380) +Seat 7: Player4 (1480) +Seat 8: Player2 (1500) +Seat 9: Player5 (1840) +Seat 10: Player8 (1420) +*** ANTE/BLINDS *** +Player4 posts small blind 10 +Player2 posts big blind 20 +Dealt to Player6 [Qc 7d] +*** PRE-FLOP *** +Player5 calls 20 +Player8 folds +Player6 folds +Player3 folds +Player9 folds +Player1 calls 20 +Player0 folds +Player7 calls 20 +Player4 calls 10 +Player2 checks +*** FLOP *** [9s 7h 5h] +Player4 checks +Player2 checks +Player5 checks +Player1 checks +Player7 checks +*** TURN *** [9s 7h 5h][Ts] +Player4 checks +Player2 checks +Player5 checks +Player1 checks +Player7 bets 20 +Player4 calls 20 +Player2 folds +Player5 folds +Player1 calls 20 +*** RIVER *** [9s 7h 5h Ts][7s] +Player4 checks +Player1 checks +Player7 bets 160 +Player4 folds +Player1 calls 160 +*** SHOW DOWN *** +Player1 shows [4c 4d] (Two pairs : 7 and 4) +Player7 shows [4s As] (Flush Ace high) +Player7 collected 480 from pot +*** SUMMARY *** +Total pot 480 | No rake +Board: [9s 7h 5h Ts 7s] +Seat 1: Player6 folded on the pre-flop +Seat 2: Player3 folded on the pre-flop +Seat 3: Player9 folded on the pre-flop +Seat 4: Player1 showed [4c 4d] and lost with Two pairs : 7 and 4 +Seat 5: Player0 folded on the pre-flop +Seat 6: Player7 (button) showed [4s As] and won 480 with Flush Ace high +Seat 7: Player4 (small blind) folded on the river +Seat 8: Player2 (big blind) folded on the turn +Seat 9: Player5 folded on the turn +Seat 10: Player8 folded on the pre-flop + + +Winamax Poker - Tournament "No Limit Hold'em" buyIn: 0,90€ + 0,10€ level: 1 - HandId: #7959970263859201-5-1288801360 - Holdem no limit (10/20) - 2010/11/03 17:22:40 UTC +Table: 'No Limit Hold'em(1853325)#0' 10-max (real money) Seat #7 is the button +Seat 1: Player6 (1160) +Seat 2: Player3 (1980) +Seat 3: Player9 (1480) +Seat 4: Player1 (1200) +Seat 5: Player0 (1360) +Seat 6: Player7 (1660) +Seat 7: Player4 (1440) +Seat 8: Player2 (1480) +Seat 9: Player5 (1820) +Seat 10: Player8 (1420) +*** ANTE/BLINDS *** +Player2 posts small blind 10 +Player5 posts big blind 20 +Dealt to Player6 [Kd Ah] +*** PRE-FLOP *** +Player8 calls 20 +Player6 raises 70 to 90 +Player3 folds +Player9 calls 90 +Player1 calls 90 +Player0 calls 90 +Player7 calls 90 +Player4 calls 90 +Player2 folds +Player5 calls 70 +Player8 calls 70 +*** FLOP *** [2s 5h 3h] +Player5 checks +Player8 checks +Player6 bets 365 +Player9 folds +Player1 folds +Player0 folds +Player7 calls 365 +Player4 folds +Player5 folds +Player8 folds +*** TURN *** [2s 5h 3h][6d] +Player6 bets 705 and is all-in +Player7 calls 705 +*** RIVER *** [2s 5h 3h 6d][5s] +*** SHOW DOWN *** +Player6 shows [Kd Ah] (One pair : 5) +Player7 shows [7d 7h] (Two pairs : 7 and 5) +Player7 collected 2870 from pot +*** SUMMARY *** +Total pot 2.87k | No rake +Board: [2s 5h 3h 6d 5s] +Seat 1: Player6 showed [Kd Ah] and lost with One pair : 5 +Seat 2: Player3 folded on the pre-flop +Seat 3: Player9 folded on the flop +Seat 4: Player1 folded on the flop +Seat 5: Player0 folded on the flop +Seat 6: Player7 showed [7d 7h] and won 2870 with Two pairs : 7 and 5 +Seat 7: Player4 (button) folded on the flop +Seat 8: Player2 (small blind) folded on the pre-flop +Seat 9: Player5 (big blind) folded on the flop +Seat 10: Player8 folded on the flop + + + From ac4cae5abffa42dc189ade386637d80fde8d72cb Mon Sep 17 00:00:00 2001 From: Chaz <Chaz@Chaz-THINK> Date: Wed, 24 Nov 2010 14:14:29 -0600 Subject: [PATCH 09/22] * Updated the methods, variables, and SQL in Database.py and SQL.py to add Hours to the HudCache.styleKey field --- pyfpdb/Database.py | 26 +++++++++++++------------- pyfpdb/SQL.py | 18 +++++++++--------- 2 files changed, 22 insertions(+), 22 deletions(-) diff --git a/pyfpdb/Database.py b/pyfpdb/Database.py index 0647baf2..1fb94cbd 100644 --- a/pyfpdb/Database.py +++ b/pyfpdb/Database.py @@ -291,8 +291,8 @@ class Database: # vars for hand ids or dates fetched according to above config: self.hand_1day_ago = 0 # max hand id more than 24 hrs earlier than now - self.date_ndays_ago = 'd000000' # date N days ago ('d' + YYMMDD) - self.h_date_ndays_ago = 'd000000' # date N days ago ('d' + YYMMDD) for hero + self.date_ndays_ago = 'd00000000' # date N days ago ('d' + YYMMDD) + self.h_date_ndays_ago = 'd00000000' # date N days ago ('d' + YYMMDD) for hero self.date_nhands_ago = {} # dates N hands ago per player - not used yet self.saveActions = False if self.import_options['saveActions'] == False else True @@ -703,7 +703,7 @@ class Database: try: # self.date_nhands_ago is used for fetching stats for last n hands (hud_style = 'H') # This option not used yet - needs to be called for each player :-( - self.date_nhands_ago[str(playerid)] = 'd000000' + self.date_nhands_ago[str(playerid)] = 'd00000000' # should use aggregated version of query if appropriate c.execute(self.sql.query['get_date_nhands_ago'], (self.hud_hands, playerid)) @@ -771,11 +771,11 @@ class Database: if hud_style == 'T': stylekey = self.date_ndays_ago elif hud_style == 'A': - stylekey = '0000000' # all stylekey values should be higher than this + stylekey = '000000000' # all stylekey values should be higher than this elif hud_style == 'S': - stylekey = 'zzzzzzz' # all stylekey values should be lower than this + stylekey = 'zzzzzzzzz' # all stylekey values should be lower than this else: - stylekey = '0000000' + stylekey = '000000000' log.info('hud_style: %s' % hud_style) #elif hud_style == 'H': @@ -784,11 +784,11 @@ class Database: if h_hud_style == 'T': h_stylekey = self.h_date_ndays_ago elif h_hud_style == 'A': - h_stylekey = '0000000' # all stylekey values should be higher than this + h_stylekey = '000000000' # all stylekey values should be higher than this elif h_hud_style == 'S': - h_stylekey = 'zzzzzzz' # all stylekey values should be lower than this + h_stylekey = 'zzzzzzzzz' # all stylekey values should be lower than this else: - h_stylekey = '000000' + h_stylekey = '00000000' log.info('h_hud_style: %s' % h_hud_style) #elif h_hud_style == 'H': @@ -1824,11 +1824,11 @@ class Database: """Update cached statistics. If update fails because no record exists, do an insert.""" if self.use_date_in_hudcache: - styleKey = datetime.strftime(starttime, 'd%y%m%d') - #styleKey = "d%02d%02d%02d" % (hand_start_time.year-2000, hand_start_time.month, hand_start_time.day) + styleKey = datetime.strftime(starttime, 'd%y%m%d%h') + #styleKey = "d%02d%02d%02d%02d" % (hand_start_time.year-2000, hand_start_time.month, hand_start_time.day, hand_start_time.hour) else: - # hard-code styleKey as 'A000000' (all-time cache, no key) for now - styleKey = 'A000000' + # hard-code styleKey as 'A00000000' (all-time cache, no key) for now + styleKey = 'A00000000' update_hudcache = self.sql.query['update_hudcache'] update_hudcache = update_hudcache.replace('%s', self.sql.query['placeholder']) diff --git a/pyfpdb/SQL.py b/pyfpdb/SQL.py index d45ccd38..560930c3 100644 --- a/pyfpdb/SQL.py +++ b/pyfpdb/SQL.py @@ -2047,7 +2047,7 @@ class Sql: # gets a date, would need to use handsplayers (not hudcache) to get exact hand Id if db_server == 'mysql': self.query['get_date_nhands_ago'] = """ - select concat( 'd', date_format(max(h.startTime), '%Y%m%d') ) + select concat( 'd', date_format(max(h.startTime), '%Y%m%d%h') ) from (select hp.playerId ,coalesce(greatest(max(hp.handId)-%s,1),1) as maxminusx from HandsPlayers hp @@ -2059,7 +2059,7 @@ class Sql: """ elif db_server == 'postgresql': self.query['get_date_nhands_ago'] = """ - select 'd' || to_char(max(h3.startTime), 'YYMMDD') + select 'd' || to_char(max(h3.startTime), 'YYMMDDHH') from (select hp.playerId ,coalesce(greatest(max(hp.handId)-%s,1),1) as maxminusx from HandsPlayers hp @@ -2071,7 +2071,7 @@ class Sql: """ elif db_server == 'sqlite': # untested guess at query: self.query['get_date_nhands_ago'] = """ - select 'd' || strftime(max(h3.startTime), 'YYMMDD') + select 'd' || strftime(max(h3.startTime), 'YYMMDDHH') from (select hp.playerId ,coalesce(greatest(max(hp.handId)-%s,1),1) as maxminusx from HandsPlayers hp @@ -3290,7 +3290,7 @@ class Sql: else 'E' end AS hc_position <tourney_select_clause> - ,date_format(h.startTime, 'd%y%m%d') + ,date_format(h.startTime, 'd%y%m%d%h') ,count(1) ,sum(wonWhenSeenStreet1) ,sum(wonWhenSeenStreet2) @@ -3379,7 +3379,7 @@ class Sql: ,h.seats ,hc_position <tourney_group_clause> - ,date_format(h.startTime, 'd%y%m%d') + ,date_format(h.startTime, 'd%y%m%d%h') """ elif db_server == 'postgresql': self.query['rebuildHudCache'] = """ @@ -3488,7 +3488,7 @@ class Sql: else 'E' end AS hc_position <tourney_select_clause> - ,'d' || to_char(h.startTime, 'YYMMDD') + ,'d' || to_char(h.startTime, 'YYMMDDHH') ,count(1) ,sum(wonWhenSeenStreet1) ,sum(wonWhenSeenStreet2) @@ -3577,7 +3577,7 @@ class Sql: ,h.seats ,hc_position <tourney_group_clause> - ,to_char(h.startTime, 'YYMMDD') + ,to_char(h.startTime, 'YYMMDDHH') """ else: # assume sqlite self.query['rebuildHudCache'] = """ @@ -3686,7 +3686,7 @@ class Sql: else 'E' end AS hc_position <tourney_select_clause> - ,'d' || substr(strftime('%Y%m%d', h.startTime),3,7) + ,'d' || substr(strftime('%Y%m%d%h', h.startTime),3,7) ,count(1) ,sum(wonWhenSeenStreet1) ,sum(wonWhenSeenStreet2) @@ -3775,7 +3775,7 @@ class Sql: ,h.seats ,hc_position <tourney_group_clause> - ,'d' || substr(strftime('%Y%m%d', h.startTime),3,7) + ,'d' || substr(strftime('%Y%m%d%h', h.startTime),3,7) """ self.query['insert_hudcache'] = """ From ee926ac9ca84e8c07b72673a2d1c6c5f3c720d38 Mon Sep 17 00:00:00 2001 From: Chaz <Chaz@Chaz-THINK> Date: Wed, 24 Nov 2010 14:18:42 -0600 Subject: [PATCH 10/22] * Modified init_hud_stat_vars() so that it uses the timezone & day_start offset from Filters.py --- pyfpdb/Database.py | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/pyfpdb/Database.py b/pyfpdb/Database.py index 1fb94cbd..e5cdfa6b 100644 --- a/pyfpdb/Database.py +++ b/pyfpdb/Database.py @@ -55,6 +55,7 @@ import Card import Charset from Exceptions import * import Configuration +import Filters # Other library modules @@ -689,14 +690,16 @@ class Database: else: if row and row[0]: self.hand_1day_ago = int(row[0]) - - d = timedelta(days=hud_days) + + offset = strptime(Filters.Filters(self, self.config, self.sql).getDates()[0],"%Y-%m-%d %H:%M:%S").tm_hour + + d = timedelta(days=hud_days, hours=offset) now = datetime.utcnow() - d - self.date_ndays_ago = "d%02d%02d%02d" % (now.year - 2000, now.month, now.day) + self.date_ndays_ago = "d%02d%02d%02d%02d" % (now.year - 2000, now.month, now.day, offset) - d = timedelta(days=h_hud_days) + d = timedelta(days=h_hud_days, hours=offset) now = datetime.utcnow() - d - self.h_date_ndays_ago = "d%02d%02d%02d" % (now.year - 2000, now.month, now.day) + self.h_date_ndays_ago = "d%02d%02d%02d%02d" % (now.year - 2000, now.month, now.day, offset) def init_player_hud_stat_vars(self, playerid): # not sure if this is workable, to be continued ... From 8026ba256a1b29f6f7e66724098a0e5e6c8c0d63 Mon Sep 17 00:00:00 2001 From: Chaz <Chaz@Chaz-THINK> Date: Wed, 24 Nov 2010 14:41:12 -0600 Subject: [PATCH 11/22] It's %H, not %h --- pyfpdb/Database.py | 2 +- pyfpdb/SQL.py | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/pyfpdb/Database.py b/pyfpdb/Database.py index e5cdfa6b..f2ce36b5 100644 --- a/pyfpdb/Database.py +++ b/pyfpdb/Database.py @@ -1827,7 +1827,7 @@ class Database: """Update cached statistics. If update fails because no record exists, do an insert.""" if self.use_date_in_hudcache: - styleKey = datetime.strftime(starttime, 'd%y%m%d%h') + styleKey = datetime.strftime(starttime, 'd%y%m%d%H') #styleKey = "d%02d%02d%02d%02d" % (hand_start_time.year-2000, hand_start_time.month, hand_start_time.day, hand_start_time.hour) else: # hard-code styleKey as 'A00000000' (all-time cache, no key) for now diff --git a/pyfpdb/SQL.py b/pyfpdb/SQL.py index 560930c3..ffe1ee6d 100644 --- a/pyfpdb/SQL.py +++ b/pyfpdb/SQL.py @@ -2047,7 +2047,7 @@ class Sql: # gets a date, would need to use handsplayers (not hudcache) to get exact hand Id if db_server == 'mysql': self.query['get_date_nhands_ago'] = """ - select concat( 'd', date_format(max(h.startTime), '%Y%m%d%h') ) + select concat( 'd', date_format(max(h.startTime), '%Y%m%d%H') ) from (select hp.playerId ,coalesce(greatest(max(hp.handId)-%s,1),1) as maxminusx from HandsPlayers hp @@ -3290,7 +3290,7 @@ class Sql: else 'E' end AS hc_position <tourney_select_clause> - ,date_format(h.startTime, 'd%y%m%d%h') + ,date_format(h.startTime, 'd%y%m%d%H') ,count(1) ,sum(wonWhenSeenStreet1) ,sum(wonWhenSeenStreet2) @@ -3379,7 +3379,7 @@ class Sql: ,h.seats ,hc_position <tourney_group_clause> - ,date_format(h.startTime, 'd%y%m%d%h') + ,date_format(h.startTime, 'd%y%m%d%H') """ elif db_server == 'postgresql': self.query['rebuildHudCache'] = """ @@ -3686,7 +3686,7 @@ class Sql: else 'E' end AS hc_position <tourney_select_clause> - ,'d' || substr(strftime('%Y%m%d%h', h.startTime),3,7) + ,'d' || substr(strftime('%Y%m%d%H', h.startTime),3,7) ,count(1) ,sum(wonWhenSeenStreet1) ,sum(wonWhenSeenStreet2) @@ -3775,7 +3775,7 @@ class Sql: ,h.seats ,hc_position <tourney_group_clause> - ,'d' || substr(strftime('%Y%m%d%h', h.startTime),3,7) + ,'d' || substr(strftime('%Y%m%d%H', h.startTime),3,7) """ self.query['insert_hudcache'] = """ From b820cd4895ac6039fbe29df22efc3b2972f2d949 Mon Sep 17 00:00:00 2001 From: Chaz <Chaz@Chaz-THINK> Date: Wed, 24 Nov 2010 14:49:39 -0600 Subject: [PATCH 12/22] * Old Stars HHs don't display max seats. Added regression-test-file showing this and handling in the Stars HHC --- pyfpdb/PokerStarsToFpdb.py | 2 +- .../LHE-10-max-USD-1.00-2.00-No_max_seats.txt | 42 +++++++++++++++++++ 2 files changed, 43 insertions(+), 1 deletion(-) create mode 100644 pyfpdb/regression-test-files/cash/Stars/Flop/LHE-10-max-USD-1.00-2.00-No_max_seats.txt diff --git a/pyfpdb/PokerStarsToFpdb.py b/pyfpdb/PokerStarsToFpdb.py index 16c4091e..3817ae3f 100644 --- a/pyfpdb/PokerStarsToFpdb.py +++ b/pyfpdb/PokerStarsToFpdb.py @@ -298,7 +298,7 @@ class PokerStars(HandHistoryConverter): if key == 'BUTTON': hand.buttonpos = info[key] if key == 'MAX': - hand.maxseats = int(info[key]) + if info[key]: hand.maxseats = int(info[key]) if key == 'MIXED': hand.mixed = self.mixes[info[key]] if info[key] is not None else None diff --git a/pyfpdb/regression-test-files/cash/Stars/Flop/LHE-10-max-USD-1.00-2.00-No_max_seats.txt b/pyfpdb/regression-test-files/cash/Stars/Flop/LHE-10-max-USD-1.00-2.00-No_max_seats.txt new file mode 100644 index 00000000..173da216 --- /dev/null +++ b/pyfpdb/regression-test-files/cash/Stars/Flop/LHE-10-max-USD-1.00-2.00-No_max_seats.txt @@ -0,0 +1,42 @@ +PokerStars Game #2428142447: Hold'em Limit ($1/$2 USD) - 2005/08/26 16:12:22 ET +Table 'Teucer II' Seat #5 is the button +Seat 1: Frankson34 ($24 in chips) +Seat 2: webb22 ($64 in chips) +Seat 3: eric_mtx ($26 in chips) +Seat 4: sososolid ($147.75 in chips) +Seat 5: DRILHER ($48.25 in chips) +Seat 6: Naughtychic ($60 in chips) +Seat 7: Terps78 ($71.50 in chips) +Seat 8: ChazDazzle ($69.25 in chips) +Seat 9: alekos ($55 in chips) +Seat 10: BigNards84 ($64.25 in chips) +Naughtychic: posts small blind $0.50 +Terps78: posts big blind $1 +*** HOLE CARDS *** +Dealt to ChazDazzle [8c Kd] +ChazDazzle: folds +alekos: folds +BigNards84: raises $1 to $2 +Frankson34: folds +webb22: folds +eric_mtx: folds +ChazDazzle leaves the table +sososolid: folds +DRILHER: folds +cdhender joins the table at seat #8 +Naughtychic: folds +Terps78: folds +BigNards84 collected $2.50 from pot +BigNards84: doesn't show hand +*** SUMMARY *** +Total pot $2.50 | Rake $0 +Seat 1: Frankson34 folded before Flop (didn't bet) +Seat 2: webb22 folded before Flop (didn't bet) +Seat 3: eric_mtx folded before Flop (didn't bet) +Seat 4: sososolid folded before Flop (didn't bet) +Seat 5: DRILHER (button) folded before Flop (didn't bet) +Seat 6: Naughtychic (small blind) folded before Flop +Seat 7: Terps78 (big blind) folded before Flop +Seat 8: ChazDazzle folded before Flop (didn't bet) +Seat 9: alekos folded before Flop (didn't bet) +Seat 10: BigNards84 collected ($2.50) From 43ec9498e8d08db49cf86bbaade16948b7f5dc31 Mon Sep 17 00:00:00 2001 From: Chaz <Chaz@Chaz-THINK> Date: Wed, 24 Nov 2010 14:51:13 -0600 Subject: [PATCH 13/22] Just renamed the new regression file --- ...-No_max_seats.txt => LHE-10max-USD-1.00-2.00-No_max_seats.txt} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename pyfpdb/regression-test-files/cash/Stars/Flop/{LHE-10-max-USD-1.00-2.00-No_max_seats.txt => LHE-10max-USD-1.00-2.00-No_max_seats.txt} (100%) diff --git a/pyfpdb/regression-test-files/cash/Stars/Flop/LHE-10-max-USD-1.00-2.00-No_max_seats.txt b/pyfpdb/regression-test-files/cash/Stars/Flop/LHE-10max-USD-1.00-2.00-No_max_seats.txt similarity index 100% rename from pyfpdb/regression-test-files/cash/Stars/Flop/LHE-10-max-USD-1.00-2.00-No_max_seats.txt rename to pyfpdb/regression-test-files/cash/Stars/Flop/LHE-10max-USD-1.00-2.00-No_max_seats.txt From 92c8e689146efd9bab24d8ba74c9eeba07699b5a Mon Sep 17 00:00:00 2001 From: Chaz <Chaz@Chaz-THINK> Date: Wed, 24 Nov 2010 14:53:53 -0600 Subject: [PATCH 14/22] It's actually an OSError, not an IOError --- pyfpdb/interlocks.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) mode change 100755 => 100644 pyfpdb/interlocks.py diff --git a/pyfpdb/interlocks.py b/pyfpdb/interlocks.py old mode 100755 new mode 100644 index c2e6abd6..496c5d0b --- a/pyfpdb/interlocks.py +++ b/pyfpdb/interlocks.py @@ -108,7 +108,7 @@ class InterProcessLockFcntl(InterProcessLockBase): self.lockfd = 0 try: os.unlink(self.lock_file_name) - except IOError: + except OSError: # We don't care about the existence of the file too much here. It's the flock() we care about, # And that should just go away magically. pass From ead567c5cc8c5783cdf1236d4312fe6ce22f5539 Mon Sep 17 00:00:00 2001 From: Chaz <Chaz@Chaz-THINK> Date: Wed, 24 Nov 2010 15:13:26 -0600 Subject: [PATCH 15/22] * Added encoding error handling and the ability to identify the worker responsible for splitting a particular file if several were launched via threading --- pyfpdb/SplitHandHistory.py | 76 +++++++++++++++++++++++--------------- 1 file changed, 47 insertions(+), 29 deletions(-) diff --git a/pyfpdb/SplitHandHistory.py b/pyfpdb/SplitHandHistory.py index 52c1d340..c996f903 100644 --- a/pyfpdb/SplitHandHistory.py +++ b/pyfpdb/SplitHandHistory.py @@ -1,19 +1,22 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- +#!/usr/bin/env python -#Copyright 2010 Chaz Littlejohn -#This program is free software: you can redistribute it and/or modify -#it under the terms of the GNU Affero General Public License as published by -#the Free Software Foundation, version 3 of the License. -# -#This program is distributed in the hope that it will be useful, -#but WITHOUT ANY WARRANTY; without even the implied warranty of -#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -#GNU General Public License for more details. -# -#You should have received a copy of the GNU Affero General Public License -#along with this program. If not, see <http://www.gnu.org/licenses/>. -#In the "official" distribution you can find the license in agpl-3.0.txt. +# Copyright 2010, Chaz Littlejohn +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +######################################################################## import L10n _ = L10n.get_translation() @@ -28,6 +31,7 @@ import Options import Configuration from Exceptions import * from cStringIO import StringIO +import time (options, argv) = Options.fpdb_options() @@ -37,7 +41,7 @@ codepage = ["utf-16", "utf-8", "cp1252"] class SplitHandHistory: - def __init__(self, config, in_path = '-', out_path = None, hands = 100, filter = "PokerStarsToFpdb", archive = False): + def __init__(self, config, in_path = '-', out_path = None, hands = 100, filter = "PokerStarsToFpdb", archive = False, workerid = 0): self.config = config self.in_path = in_path self.out_path = out_path @@ -50,22 +54,25 @@ class SplitHandHistory: self.line_addendum = None self.filedone = False + self.timestamp = str(time.time()) + self.workerid = '%02d' % workerid + #Acquire re_SplitHands for this hh - filter_name = filter.replace("ToFpdb", "") + self.filter_name = filter.replace("ToFpdb", "") mod = __import__(filter) - obj = getattr(mod, filter_name, None) + obj = getattr(mod, self.filter_name, None) self.re_SplitHands = obj.re_SplitHands #Determine line delimiter type if any - if self.re_SplitHands.match('\n\n'): - self.line_delimiter = '\n\n' if self.re_SplitHands.match('\n\n\n'): self.line_delimiter = '\n\n\n' + if self.re_SplitHands.match('\n\n'): + self.line_delimiter = '\n\n' #Add new line addendum for sites which match SplitHand to next line as well - if filter_name == 'OnGame': + if self.filter_name == 'OnGame': self.line_addendum = '*' - if filter_name == 'Carbon': + if self.filter_name == 'Carbon': self.line_addendum = '<game' #Open the gargantuan file @@ -75,6 +82,7 @@ class SplitHandHistory: except IOError: print _('File not found') sys.exit(2) + self.kodec = kodec #Split with do_hands_per_file if archive and paragraphs if a regular hh if self.archive: @@ -105,9 +113,10 @@ class SplitHandHistory: print _('Nope, will not work (fileno=%d)' % fileno) sys.exit(2) basename = os.path.splitext(os.path.basename(self.in_path))[0] - name = os.path.join(self.out_path, basename+'-%06d.txt' % fileno) + name = os.path.join(self.out_path, self.filter_name+'-'+basename+'_'+self.workerid+'_'+self.timestamp+'_%06d.txt' % fileno) print '-> %s' % name newfile = file(name, 'w') + os.chmod(name, 0775) return newfile #Archive Hand Splitter @@ -122,8 +131,11 @@ class SplitHandHistory: except FpdbEndOfFile: done = True break + except UnicodeEncodeError: + print _('Absurd character done messed you up') + sys.exit(2) except: - print _("Unexpected error processing file") + print _('Unexpected error processing file') sys.exit(2) n += 1 outfile.close() @@ -174,7 +186,7 @@ class SplitHandHistory: l = infile.readline() l = l.replace('\r\n', '\n') outfile.write(l) - l = infile.readline() + l = infile.readline().encode(self.kodec) while len(l) < 3: l = infile.readline() @@ -182,7 +194,7 @@ class SplitHandHistory: while len(l) > 2: l = l.replace('\r\n', '\n') outfile.write(l) - l = infile.readline() + l = infile.readline().encode(self.kodec) outfile.write(self.line_delimiter) return infile @@ -195,13 +207,19 @@ class SplitHandHistory: def main(argv=None): if argv is None: argv = sys.argv[1:] + + if not options.filename: + options.filename = sys.argv[1] if not options.config: - options.config = Configuration.Config(file = "HUD_config.test.xml") - + options.config = sys.argv[2] + + if sys.argv[3] == "True": + options.archive = True + if options.filename: SplitHH = SplitHandHistory(options.config, options.filename, options.outpath, options.hands, - options.hhc, options.archive) + options.hhc, options.archive, options.workerid) if __name__ == '__main__': sys.exit(main()) From 9bb58ca77950ba56bf5216cf5fd4c3a2ccbf9c14 Mon Sep 17 00:00:00 2001 From: Chaz <Chaz@Chaz-THINK> Date: Wed, 24 Nov 2010 15:16:09 -0600 Subject: [PATCH 16/22] Adds the worker id command line option --- pyfpdb/Options.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pyfpdb/Options.py b/pyfpdb/Options.py index b8be3d2a..6369b485 100644 --- a/pyfpdb/Options.py +++ b/pyfpdb/Options.py @@ -59,6 +59,8 @@ def fpdb_options(): help=_("File to be split is a PokerStars or Full Tilt Poker archive file")) parser.add_option("-n", "--numhands", dest="hands", default="100", type="int", help=_("How many hands do you want saved to each file. Default is 100")) + parser.add_option("-w", "--workerid", dest="workerid", default="0", type="int", + help=_("Specifies the worker id running the script")) (options, argv) = parser.parse_args() From 9137347acb00957d548ade66d65687d91be15236 Mon Sep 17 00:00:00 2001 From: Chaz <Chaz@Chaz-THINK> Date: Wed, 24 Nov 2010 15:20:48 -0600 Subject: [PATCH 17/22] Just fixed a few bugs --- pyfpdb/IdentifySite.py | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/pyfpdb/IdentifySite.py b/pyfpdb/IdentifySite.py index b4f1a8a7..723991f3 100644 --- a/pyfpdb/IdentifySite.py +++ b/pyfpdb/IdentifySite.py @@ -1,4 +1,4 @@ -#!/usr/bin/python +#!/usr/bin/env python # -*- coding: utf-8 -*- #Copyright 2010 Chaz Littlejohn @@ -28,18 +28,21 @@ import Configuration import Database __ARCHIVE_PRE_HEADER_REGEX='^Hand #(\d+)\s*$|\*{20}\s#\s\d+\s\*+\s+' -re_SplitArchive = re.compile(__ARCHIVE_PRE_HEADER_REGEX) +re_SplitArchive = re.compile(__ARCHIVE_PRE_HEADER_REGEX, re.MULTILINE) class IdentifySite: def __init__(self, config, in_path = '-'): self.in_path = in_path self.config = config - self.db = Database.Database(config) + self.db = Database.Database(self.config) self.sitelist = {} self.filelist = {} self.generateSiteList() - self.walkDirectory(self.in_path, self.sitelist) + if os.path.isdir(self.in_path): + self.walkDirectory(self.in_path, self.sitelist) + else: + self.idSite(self.in_path, self.sitelist) def generateSiteList(self): """Generates a ordered dictionary of site, filter and filter name for each site in hhcs""" @@ -80,7 +83,7 @@ class IdentifySite: for kodec in self.__listof(obj.codepage): try: in_fh = codecs.open(file, 'r', kodec) - whole_file = in_fh.read() + whole_file = in_fh.read(2000) in_fh.close() if info[2] in ('OnGame', 'Winamax'): @@ -94,7 +97,7 @@ class IdentifySite: if re_SplitArchive.search(whole_file): archive = True if m: - self.filelist[file] = [info[0]] + [info[1]] + [kodec] + [archive] + self.filelist[file] = [info[1]] + [kodec] + [archive] break except: pass From 872ea008ddb1205eef9a5fc07c7659c365767041 Mon Sep 17 00:00:00 2001 From: Chaz <Chaz@Chaz-THINK.gateway.2wire.net> Date: Thu, 25 Nov 2010 02:35:50 -0600 Subject: [PATCH 18/22] Updated database version to 145 --- pyfpdb/Database.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyfpdb/Database.py b/pyfpdb/Database.py index f2ce36b5..65e7a87d 100644 --- a/pyfpdb/Database.py +++ b/pyfpdb/Database.py @@ -74,7 +74,7 @@ except ImportError: use_numpy = False -DB_VERSION = 144 +DB_VERSION = 145 # Variance created as sqlite has a bunch of undefined aggregate functions. From 21ac5229cba63068408ce975c2db26b431499f2d Mon Sep 17 00:00:00 2001 From: Chaz <Chaz@Chaz-THINK.gateway.2wire.net> Date: Thu, 25 Nov 2010 02:36:34 -0600 Subject: [PATCH 19/22] Fixed styleKey char length to 9 --- pyfpdb/SQL.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pyfpdb/SQL.py b/pyfpdb/SQL.py index ffe1ee6d..6a505eae 100644 --- a/pyfpdb/SQL.py +++ b/pyfpdb/SQL.py @@ -1064,7 +1064,7 @@ class Sql: activeSeats SMALLINT NOT NULL, position CHAR(1), tourneyTypeId SMALLINT UNSIGNED, FOREIGN KEY (tourneyTypeId) REFERENCES TourneyTypes(id), - styleKey CHAR(7) NOT NULL, /* 1st char is style (A/T/H/S), other 6 are the key */ + styleKey CHAR(9) NOT NULL, /* 1st char is style (A/T/H/S), other 8 are the key */ HDs INT NOT NULL, wonWhenSeenStreet1 FLOAT, @@ -1165,7 +1165,7 @@ class Sql: activeSeats SMALLINT, position CHAR(1), tourneyTypeId INT, FOREIGN KEY (tourneyTypeId) REFERENCES TourneyTypes(id), - styleKey CHAR(7) NOT NULL, /* 1st char is style (A/T/H/S), other 6 are the key */ + styleKey CHAR(9) NOT NULL, /* 1st char is style (A/T/H/S), other 8 are the key */ HDs INT, wonWhenSeenStreet1 FLOAT, @@ -3686,7 +3686,7 @@ class Sql: else 'E' end AS hc_position <tourney_select_clause> - ,'d' || substr(strftime('%Y%m%d%H', h.startTime),3,7) + ,'d' || substr(strftime('%Y%m%d%H', h.startTime),3,9) ,count(1) ,sum(wonWhenSeenStreet1) ,sum(wonWhenSeenStreet2) @@ -3775,7 +3775,7 @@ class Sql: ,h.seats ,hc_position <tourney_group_clause> - ,'d' || substr(strftime('%Y%m%d%H', h.startTime),3,7) + ,'d' || substr(strftime('%Y%m%d%H', h.startTime),3,9) """ self.query['insert_hudcache'] = """ From b017fa298d9e8cc0ffd232cb8597439a5fdf8187 Mon Sep 17 00:00:00 2001 From: Worros <carl.gherardi@gmail.com> Date: Thu, 25 Nov 2010 20:11:19 +0800 Subject: [PATCH 20/22] Carbon: Fix regression test parse --- pyfpdb/CarbonToFpdb.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pyfpdb/CarbonToFpdb.py b/pyfpdb/CarbonToFpdb.py index 1a1541ee..a2d2f56f 100644 --- a/pyfpdb/CarbonToFpdb.py +++ b/pyfpdb/CarbonToFpdb.py @@ -133,8 +133,9 @@ or None if we fail to get the info """ self.info = {} mg = m.groupdict() + print mg - limits = { 'No Limit':'nl', 'Limit':'fl' } + limits = { 'No Limit':'nl', 'No Limit ':'nl', 'Limit':'fl' } games = { # base, category 'Holdem' : ('hold','holdem'), 'Holdem Tournament' : ('hold','holdem') } From 31da6ff170997742b7231195edaeae185dac85d0 Mon Sep 17 00:00:00 2001 From: Worros <carl.gherardi@gmail.com> Date: Fri, 26 Nov 2010 12:45:41 +0800 Subject: [PATCH 21/22] Winamax: Pull in tournament support from Forrest Also add tournament directory to regression test file --- pyfpdb/TestHandsPlayers.py | 1 + pyfpdb/WinamaxToFpdb.py | 81 +++++++++++++++++++++++++++++++++++--- 2 files changed, 76 insertions(+), 6 deletions(-) diff --git a/pyfpdb/TestHandsPlayers.py b/pyfpdb/TestHandsPlayers.py index ee3be1e9..e0013ecc 100755 --- a/pyfpdb/TestHandsPlayers.py +++ b/pyfpdb/TestHandsPlayers.py @@ -201,6 +201,7 @@ def main(argv=None): walk_testfiles("regression-test-files/cash/iPoker/", compare, importer, iPokerErrors, "iPoker") if sites['Winamax'] == True: walk_testfiles("regression-test-files/cash/Winamax/", compare, importer, WinamaxErrors, "Winamax") + walk_testfiles("regression-test-files/tour/Winamax/", compare, importer, WinamaxErrors, "Winamax") if sites['Win2day'] == True: walk_testfiles("regression-test-files/cash/Win2day/", compare, importer, Win2dayErrors, "Win2day") diff --git a/pyfpdb/WinamaxToFpdb.py b/pyfpdb/WinamaxToFpdb.py index c49f4e09..47721515 100644 --- a/pyfpdb/WinamaxToFpdb.py +++ b/pyfpdb/WinamaxToFpdb.py @@ -82,15 +82,26 @@ class Winamax(HandHistoryConverter): # Winamax Poker - CashGame - HandId: #279823-223-1285031451 - Holdem no limit (0.02€/0.05€) - 2010/09/21 03:10:51 UTC # Table: 'Charenton-le-Pont' 9-max (real money) Seat #5 is the button re_HandInfo = re.compile(u""" - \s*Winamax\sPoker\s-\sCashGame\s-\sHandId:\s\#(?P<HID1>\d+)-(?P<HID2>\d+)-(?P<HID3>\d+).*\s + \s*Winamax\sPoker\s-\s + (?P<RING>CashGame)? + (?P<TOUR>Tournament\s + (?P<TOURNAME>.+)?\s + buyIn:\s(?P<BUYIN>(?P<BIAMT>[%(LS)s\d\,]+)?\s\+?\s(?P<BIRAKE>[%(LS)s\d\,]+)?\+?(?P<BOUNTY>[%(LS)s\d\.]+)?\s?(?P<TOUR_ISO>%(LEGAL_ISO)s)?|Gratuit|Ticket\suniquement)?\s + (level:\s(?P<LEVEL>\d+))? + .*)? + \s-\sHandId:\s\#(?P<HID1>\d+)-(?P<HID2>\d+)-(?P<HID3>\d+).*\s (?P<GAME>Holdem|Omaha)\s (?P<LIMIT>no\slimit|pot\slimit)\s \( + (((%(LS)s)?(?P<ANTE>[.0-9]+)(%(LS)s)?)/)? ((%(LS)s)?(?P<SB>[.0-9]+)(%(LS)s)?)/ ((%(LS)s)?(?P<BB>[.0-9]+)(%(LS)s)?) \)\s-\s (?P<DATETIME>.*) - Table:\s\'(?P<TABLE>[^']+)\'\s(?P<MAXPLAYER>\d+)\-max + Table:\s\'(?P<TABLE>[^(]+) + (.(?P<TOURNO>\d+).\#(?P<TABLENO>\d+))?.* + \' + \s(?P<MAXPLAYER>\d+)\-max """ % substitutions, re.MULTILINE|re.DOTALL|re.VERBOSE) re_TailSplitHands = re.compile(r'\n\s*\n') @@ -126,8 +137,8 @@ class Winamax(HandHistoryConverter): self.re_PostSB = re.compile('%(PLYR)s posts small blind (%(CUR)s)?(?P<SB>[\.0-9]+)(%(CUR)s)?' % subst, re.MULTILINE) self.re_PostBB = re.compile('%(PLYR)s posts big blind (%(CUR)s)?(?P<BB>[\.0-9]+)(%(CUR)s)?' % subst, re.MULTILINE) self.re_DenySB = re.compile('(?P<PNAME>.*) deny SB' % subst, re.MULTILINE) - self.re_Antes = re.compile(r"^%(PLYR)s: posts the ante (%(CUR)s)?(?P<ANTE>[\.0-9]+)(%(CUR)s)?" % subst, re.MULTILINE) - self.re_BringIn = re.compile(r"^%(PLYR)s: brings[- ]in( low|) for (%(CUR)s)?(?P<BRINGIN>[\.0-9]+(%(CUR)s)?)" % subst, re.MULTILINE) + self.re_Antes = re.compile(r"^%(PLYR)s posts ante (%(CUR)s)?(?P<ANTE>[\.0-9]+)(%(CUR)s)?" % subst, re.MULTILINE) + self.re_BringIn = re.compile(r"^%(PLYR)s brings[- ]in( low|) for (%(CUR)s)?(?P<BRINGIN>[\.0-9]+(%(CUR)s)?)" % subst, re.MULTILINE) self.re_PostBoth = re.compile('(?P<PNAME>.*): posts small \& big blind \( (%(CUR)s)?(?P<SBBB>[\.0-9]+)(%(CUR)s)?\)' % subst) self.re_PostDead = re.compile('(?P<PNAME>.*) posts dead blind \((%(CUR)s)?(?P<DEAD>[\.0-9]+)(%(CUR)s)?\)' % subst, re.MULTILINE) self.re_HeroCards = re.compile('Dealt\sto\s%(PLYR)s\s\[(?P<CARDS>.*)\]' % subst) @@ -144,6 +155,9 @@ class Winamax(HandHistoryConverter): ["ring", "hold", "fl"], ["ring", "hold", "nl"], ["ring", "hold", "pl"], + ["tour", "hold", "fl"], + ["tour", "hold", "nl"], + ["tour", "hold", "pl"], ] def determineGameType(self, handText): @@ -160,7 +174,11 @@ class Winamax(HandHistoryConverter): mg = m.groupdict() - info['type'] = 'ring' + if mg.get('TOUR'): + info['type'] = 'tour' + elif mg.get('RING'): + info['type'] = 'ring' + info['currency'] = 'EUR' if 'LIMIT' in mg: @@ -202,11 +220,62 @@ class Winamax(HandHistoryConverter): hand.startTime = datetime.datetime.strptime(datetimestr, "%Y/%m/%d %H:%M:%S") # also timezone at end, e.g. " ET" hand.startTime = HandHistoryConverter.changeTimezone(hand.startTime, "CET", "UTC") if key == 'HID1': - hand.handid = "1%.4d%s%s"%(int(info['HID2']),info['HID1'],info['HID3']) # Need to remove non-alphanumerics for MySQL + hand.handid = "1%.9d%s%s"%(int(info['HID2']),info['HID1'],info['HID3']) + if key == 'TOURNO': + hand.tourNo = info[key] if key == 'TABLE': hand.tablename = info[key] + if key == 'BUYIN': + if hand.tourNo!=None: + #print "DEBUG: info['BUYIN']: %s" % info['BUYIN'] + #print "DEBUG: info['BIAMT']: %s" % info['BIAMT'] + #print "DEBUG: info['BIRAKE']: %s" % info['BIRAKE'] + #print "DEBUG: info['BOUNTY']: %s" % info['BOUNTY'] + for k in ['BIAMT','BIRAKE']: + if k in info.keys() and info[k]: + info[k] = info[k].replace(',','.') + + if info[key] == 'Freeroll': + hand.buyin = 0 + hand.fee = 0 + hand.buyinCurrency = "FREE" + else: + if info[key].find("$")!=-1: + hand.buyinCurrency="USD" + elif info[key].find(u"€")!=-1: + hand.buyinCurrency="EUR" + elif info[key].find("FPP")!=-1: + hand.buyinCurrency="PSFP" + else: + #FIXME: handle other currencies, FPP, play money + raise FpdbParseError(_("failed to detect currency")) + + info['BIAMT'] = info['BIAMT'].strip(u'$€FPP') + + if hand.buyinCurrency!="PSFP": + if info['BOUNTY'] != None: + # There is a bounty, Which means we need to switch BOUNTY and BIRAKE values + tmp = info['BOUNTY'] + info['BOUNTY'] = info['BIRAKE'] + info['BIRAKE'] = tmp + info['BOUNTY'] = info['BOUNTY'].strip(u'$€') # Strip here where it isn't 'None' + hand.koBounty = int(100*Decimal(info['BOUNTY'])) + hand.isKO = True + else: + hand.isKO = False + + info['BIRAKE'] = info['BIRAKE'].strip(u'$€') + hand.buyin = int(100*Decimal(info['BIAMT'])) + hand.fee = int(100*Decimal(info['BIRAKE'])) + else: + hand.buyin = int(Decimal(info['BIAMT'])) + hand.fee = 0 + + if key == 'LEVEL': + hand.level = info[key] + # TODO: These hand.buttonpos = 1 hand.maxseats = 10 # Set to None - Hand.py will guessMaxSeats() From 9098c3ba036aa13ce786f4d1bae11cd8a1b9f348 Mon Sep 17 00:00:00 2001 From: Worros <carl.gherardi@gmail.com> Date: Fri, 26 Nov 2010 13:38:39 +0800 Subject: [PATCH 22/22] Regression: Party PLO tourney --- .../Flop/PLO-USD-STT-1.00.201008.Sample.txt | Bin 0 -> 1006 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 pyfpdb/regression-test-files/tour/PartyPoker/Flop/PLO-USD-STT-1.00.201008.Sample.txt diff --git a/pyfpdb/regression-test-files/tour/PartyPoker/Flop/PLO-USD-STT-1.00.201008.Sample.txt b/pyfpdb/regression-test-files/tour/PartyPoker/Flop/PLO-USD-STT-1.00.201008.Sample.txt new file mode 100644 index 0000000000000000000000000000000000000000..a57df9c2e6cd7adf203fd83a6eb5e4d74dd64786 GIT binary patch literal 1006 zcmah{%Wm5+5bRli{$YUh;23g4y=+If25#cCX&WSJFGdfvL`MW<(h#W_{`)RvtCiD3 z1&9QOJF_$7-tn!##dSK5B9=17U@UK}`QUrLcX8(qziDI`6!_lQeOPi;VkwQ)%>f(T zpsOAfc4fZz_<-+QUU9q+@#A@coAxk~3VEXr8PX)C#?<yfyox$Q4BXUGm1dN(tB|ov zOz>5!k{<$mY+qVq5wiecnx!lw+NVX1h=t7ea=xwwjw`|$o24m6PlDHuFAkT!_bj-@ z6w<&}0_)aVt%#>B)u?v677g?UyP6+FV<>ZcHN}Y)X80-Vnv?GlW<e4$^ny`_rD(Ln zlfURR!(%5jrGq$_ACHEa3Hg)RJJ0NH7z?s4)eB0!S15Q>8jQ!j=U@w~k4;y=)F7#l z(^}#e-S(+#!=(`ht2kwZ_T~#-*A8S3>};Qqs4E+^y{+|*8e)aW1W2m@QQ`D^rM0jm zL{Ilts6%=e8(o*?EKc4w>Ztp<^Lws0Fh<WrW+(RwScTWF&A{E0e5Kdw?9I<?nM&`; z7xFT|7y9!eKtdIEud(B@p)9#jBu%rM@3our?izaDlnN3Go4cJ14tTe^rLh<gd+U2i zf?>!|RB~sqY4o<6Fe6KWowf+Em$t${>#c`#sbC)eBer$3=fwUe)y3IJB~2wt5Gi&e KT?k70&if5Q=ON$# literal 0 HcmV?d00001