diff --git a/THANKS.txt b/THANKS.txt new file mode 100644 index 00000000..7adf9825 --- /dev/null +++ b/THANKS.txt @@ -0,0 +1,35 @@ +=**Thanks - Contributors to FPDB**= + +This page is to acknowledge the people developing and contributing to FPDB, be it with code, documentation, testing, forum support and everything else that is required to make an open source project work. + +Most people have found the project though the software forum at [[http://forumserver.twoplustwo.com/45/software/|2+2]] so most references to 2+2 aliases + +Sincere apologies to those not listed. FPDB started life in August 2008, but we only got around to putting together a proper contributers list in April 2009. Private messages and emails have been deleted so we dont have a full history of non-code based contributions + +==**Developers**== +Active: +Steffen123 - The initial code drop and project lead. +Eratosthenes - Initial HUD code drop, release manager +Ekdikeo - Contributor - contributed Everleaf support and much HUD work +Sorrow - Contributor - Graphing, import framework +mcturnbull (2+2?) - Contributor - import framework + +Inactive: +Sqlcoder - SQL query guru - Graphing filters, Player Stats, Positional Stats queries + general sql optimisation work +Bostik - Author of [[http://bostik.iki.fi/pokerstats/|PokerStats]] - Postgres linux work. PokerStats import code was the inspiration for the new framework. I'm sure well use more of his ideas in future. +FIXME - Original windows installer/packager + +==Documentation== +FIXME - Original web site guy +FIXME - Guy who wrote the Mac install page +FIXME - Any other wiki editors. + +==Other== +Eleatic Stranger - Super tester and contributor - Our #1 tester. Tracks the git repo and works the code harder than anyone and gives great feedback and bug reports. Thankyou. +Xaviax - Tester and honorary helpdesk - Another excellent tester tracking git, has responded to many queries in the fpdb thread on his own time. +KayosD - Hand History donation - Carbon Poker +freerollerjb - Hand History donation - Carbon Poker +puru - Hand History donation - Carbon Poker +freestailo - Hand History donation - Carbon Poker +MoDDe (Sourceforge) - Hand History Donation - Betfair +Jay10826 - Hand History donation - Ultimate Bet diff --git a/pyfpdb/Configuration.py b/pyfpdb/Configuration.py index 47617023..06d4f5d8 100755 --- a/pyfpdb/Configuration.py +++ b/pyfpdb/Configuration.py @@ -197,6 +197,15 @@ class Aux_window: temp = temp + "%s" % self.layout[layout] return temp +class HHC: + def __init__(self, node): + self.site = node.getAttribute("site") + self.converter = node.getAttribute("converter") + + def __str__(self): + return "%s:\t%s" % (self.site, self.converter) + + class Popup: def __init__(self, node): self.name = node.getAttribute("pu_name") @@ -277,6 +286,7 @@ class Config: self.supported_games = {} self.supported_databases = {} self.aux_windows = {} + self.hhcs = {} self.popup_windows = {} # s_sites = doc.getElementsByTagName("supported_sites") @@ -299,6 +309,11 @@ class Config: aw = Aux_window(node = aw_node) self.aux_windows[aw.name] = aw +# s_dbs = doc.getElementsByTagName("mucked_windows") + for hhc_node in doc.getElementsByTagName("hhc"): + hhc = HHC(node = hhc_node) + self.hhcs[hhc.site] = hhc + # s_dbs = doc.getElementsByTagName("popup_windows") for pu_node in doc.getElementsByTagName("pu"): pu = Popup(node = pu_node) @@ -703,6 +718,11 @@ if __name__== "__main__": for w in c.aux_windows.keys(): print c.aux_windows[w] print "----------- END AUX WINDOW FORMATS -----------" + + print "\n----------- HAND HISTORY CONVERTERS -----------" + for w in c.hhcs.keys(): + print c.hhcs[w] + print "----------- END HAND HISTORY CONVERTERS -----------" print "\n----------- POPUP WINDOW FORMATS -----------" for w in c.popup_windows.keys(): diff --git a/pyfpdb/Database.py b/pyfpdb/Database.py index 932a08dd..f2575f5f 100644 --- a/pyfpdb/Database.py +++ b/pyfpdb/Database.py @@ -201,6 +201,9 @@ class Database: t_dict = {} for name, val in zip(colnames, row): t_dict[name.lower()] = val +# print t_dict + t_dict['screen_name'] = names[t_dict['player_id']] + t_dict['seat'] = seats[t_dict['player_id']] stat_dict[t_dict['player_id']] = t_dict return stat_dict diff --git a/pyfpdb/EverleafToFpdb.py b/pyfpdb/EverleafToFpdb.py index 29808295..f353bc61 100755 --- a/pyfpdb/EverleafToFpdb.py +++ b/pyfpdb/EverleafToFpdb.py @@ -27,7 +27,8 @@ from HandHistoryConverter import * class Everleaf(HandHistoryConverter): # Static regexes - re_SplitHands = re.compile(r"(\n\n\n+)") + re_SplitHands = re.compile(r"\n\n\n+") + re_TailSplitHands = re.compile(r"(\n\n\n+)") re_GameInfo = re.compile(ur"^(Blinds )?(?P\$| €|)(?P[.0-9]+)/(?:\$| €)?(?P[.0-9]+) (?PNL|PL|) ?(?P(Hold\'em|Omaha|7 Card Stud))", re.MULTILINE) #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[.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) diff --git a/pyfpdb/Filters.py b/pyfpdb/Filters.py new file mode 100644 index 00000000..df6262a3 --- /dev/null +++ b/pyfpdb/Filters.py @@ -0,0 +1,393 @@ +#!/usr/bin/python + +#Copyright 2008 Steffen Jobbagy-Felso +#This program is free software: you can redistribute it and/or modify +#it under the terms of the GNU Affero General Public License as published by +#the Free Software Foundation, version 3 of the License. +# +#This program is distributed in the hope that it will be useful, +#but WITHOUT ANY WARRANTY; without even the implied warranty of +#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +#GNU General Public License for more details. +# +#You should have received a copy of the GNU Affero General Public License +#along with this program. If not, see . +#In the "official" distribution you can find the license in +#agpl-3.0.txt in the docs folder of the package. + +import threading +import pygtk +pygtk.require('2.0') +import gtk +import os +import sys +from optparse import OptionParser +from time import * +#import pokereval + +import Configuration +import fpdb_db +import FpdbSQLQueries + +class Filters(threading.Thread): + def __init__(self, db, settings, config, qdict, display = {},debug=True): + self.debug=debug + #print "start of GraphViewer constructor" + self.db=db + self.cursor=db.cursor + self.settings=settings + self.sql=qdict + self.conf = config + + self.sites = {} + self.games = {} + self.limits = {} + self.siteid = {} + self.heroes = {} + + # For use in date ranges. + self.start_date = gtk.Entry(max=12) + self.end_date = gtk.Entry(max=12) + self.start_date.set_property('editable', False) + self.end_date.set_property('editable', False) + + # Outer Packing box + self.mainVBox = gtk.VBox(False, 0) + + playerFrame = gtk.Frame("Hero:") + playerFrame.set_label_align(0.0, 0.0) + vbox = gtk.VBox(False, 0) + + self.fillPlayerFrame(vbox) + playerFrame.add(vbox) + + sitesFrame = gtk.Frame("Sites:") + sitesFrame.set_label_align(0.0, 0.0) + vbox = gtk.VBox(False, 0) + + self.fillSitesFrame(vbox) + sitesFrame.add(vbox) + + # Game types + gamesFrame = gtk.Frame("Games:") + gamesFrame.set_label_align(0.0, 0.0) + gamesFrame.show() + vbox = gtk.VBox(False, 0) + + self.fillGamesFrame(vbox) + gamesFrame.add(vbox) + + # Limits + limitsFrame = gtk.Frame("Limits:") + limitsFrame.set_label_align(0.0, 0.0) + limitsFrame.show() + vbox = gtk.VBox(False, 0) + + self.fillLimitsFrame(vbox) + limitsFrame.add(vbox) + + dateFrame = gtk.Frame("Date:") + dateFrame.set_label_align(0.0, 0.0) + dateFrame.show() + vbox = gtk.VBox(False, 0) + + self.fillDateFrame(vbox) + dateFrame.add(vbox) + + self.Button1=gtk.Button("Unamed 1") + + self.Button2=gtk.Button("Unamed 2") + #self.exportButton.connect("clicked", self.exportGraph, "show clicked") + self.Button2.set_sensitive(False) + + self.mainVBox.add(playerFrame) + self.mainVBox.add(sitesFrame) + self.mainVBox.add(gamesFrame) + self.mainVBox.add(limitsFrame) + self.mainVBox.add(dateFrame) + self.mainVBox.add(self.Button1) + self.mainVBox.add(self.Button2) + + self.mainVBox.show_all() + + # Should do this cleaner + if display["Heroes"] == False: + playerFrame.hide() + if display["Sites"] == False: + sitesFrame.hide() + if display["Games"] == False: + gamesFrame.hide() + if display["Limits"] == False: + limitsFrame.hide() + if display["Dates"] == False: + dateFrame.hide() + if display["Button1"] == False: + self.Button1.hide() + if display["Button2"] == False: + self.Button2.hide() + + def get_vbox(self): + """returns the vbox of this thread""" + return self.mainVBox + #end def get_vbox + + def getSites(self): + return self.sites + + def getSiteIds(self): + return self.siteid + + def getHeroes(self): + return self.heroes + + def getLimits(self): + ltuple = [] + for l in self.limits: + if self.limits[l] == True: + ltuple.append(l) + return ltuple + + def getDates(self): + return self.__get_dates() + + def registerButton1Name(self, title): + self.Button1.set_label(title) + + def registerButton1Callback(self, callback): + self.Button1.connect("clicked", callback, "clicked") + + def registerButton2Name(self, title): + self.Button2.set_label(title) + + def registerButton2Callback(self, callback): + self.Button2.connect("clicked", callback, "clicked") + + def cardCallback(self, widget, data=None): + print "DEBUG: %s was toggled %s" % (data, ("OFF", "ON")[widget.get_active()]) + + def createPlayerLine(self, hbox, site, player): + label = gtk.Label(site +" id:") + hbox.pack_start(label, False, False, 0) + + pname = gtk.Entry() + pname.set_text(player) + pname.set_width_chars(20) + hbox.pack_start(pname, False, True, 0) + pname.connect("changed", self.__set_hero_name, site) + #TODO: Look at GtkCompletion - to fill out usernames + + self.__set_hero_name(pname, site) + + def __set_hero_name(self, w, site): + self.heroes[site] = w.get_text() +# print "DEBUG: settings heroes[%s]: %s"%(site, self.heroes[site]) + + def createSiteLine(self, hbox, site): + cb = gtk.CheckButton(site) + cb.connect('clicked', self.__set_site_select, site) + cb.set_active(True) + hbox.pack_start(cb, False, False, 0) + + def createGameLine(self, hbox, game): + cb = gtk.CheckButton(game) + cb.connect('clicked', self.__set_game_select, game) + hbox.pack_start(cb, False, False, 0) + + def createLimitLine(self, hbox, limit): + cb = gtk.CheckButton(str(limit)) + cb.connect('clicked', self.__set_limit_select, limit) + hbox.pack_start(cb, False, False, 0) + + def __set_site_select(self, w, site): + print w.get_active() + self.sites[site] = w.get_active() + print "self.sites[%s] set to %s" %(site, self.sites[site]) + + def __set_game_select(self, w, game): + print w.get_active() + self.games[game] = w.get_active() + print "self.games[%s] set to %s" %(game, self.games[game]) + + def __set_limit_select(self, w, limit): + print w.get_active() + self.limits[limit] = w.get_active() + print "self.limit[%s] set to %s" %(limit, self.limits[limit]) + + def fillPlayerFrame(self, vbox): + for site in self.conf.get_supported_sites(): + pathHBox = gtk.HBox(False, 0) + vbox.pack_start(pathHBox, False, True, 0) + + player = self.conf.supported_sites[site].screen_name + self.createPlayerLine(pathHBox, site, player) + + def fillSitesFrame(self, vbox): + for site in self.conf.get_supported_sites(): + hbox = gtk.HBox(False, 0) + vbox.pack_start(hbox, False, True, 0) + self.createSiteLine(hbox, site) + #Get db site id for filtering later + self.cursor.execute(self.sql.query['getSiteId'], (site,)) + result = self.db.cursor.fetchall() + if len(result) == 1: + self.siteid[site] = result[0][0] + else: + print "Either 0 or more than one site matched - EEK" + + def fillGamesFrame(self, vbox): + self.cursor.execute(self.sql.query['getGames']) + result = self.db.cursor.fetchall() + if len(result) >= 1: + for line in result: + hbox = gtk.HBox(False, 0) + vbox.pack_start(hbox, False, True, 0) + self.createGameLine(hbox, line[0]) + else: + print "INFO: No games returned from database" + + def fillLimitsFrame(self, vbox): + self.cursor.execute(self.sql.query['getLimits']) + result = self.db.cursor.fetchall() + if len(result) >= 1: + for line in result: + hbox = gtk.HBox(False, 0) + vbox.pack_start(hbox, False, True, 0) + self.createLimitLine(hbox, line[0]) + else: + print "INFO: No games returned from database" + + def fillCardsFrame(self, vbox): + hbox1 = gtk.HBox(True,0) + hbox1.show() + vbox.pack_start(hbox1, True, True, 0) + + cards = [ "A", "K","Q","J","T","9","8","7","6","5","4","3","2" ] + + for j in range(0, len(cards)): + hbox1 = gtk.HBox(True,0) + hbox1.show() + vbox.pack_start(hbox1, True, True, 0) + for i in range(0, len(cards)): + if i < (j + 1): + suit = "o" + else: + suit = "s" + button = gtk.ToggleButton("%s%s%s" %(cards[i], cards[j], suit)) + button.connect("toggled", self.cardCallback, "%s%s%s" %(cards[i], cards[j], suit)) + hbox1.pack_start(button, True, True, 0) + button.show() + + def fillDateFrame(self, vbox): + # Hat tip to Mika Bostrom - calendar code comes from PokerStats + hbox = gtk.HBox() + vbox.pack_start(hbox, False, True, 0) + + lbl_start = gtk.Label('From:') + + btn_start = gtk.Button() + btn_start.set_image(gtk.image_new_from_stock(gtk.STOCK_INDEX, gtk.ICON_SIZE_BUTTON)) + btn_start.connect('clicked', self.__calendar_dialog, self.start_date) + + hbox.pack_start(lbl_start, expand=False, padding=3) + hbox.pack_start(btn_start, expand=False, padding=3) + hbox.pack_start(self.start_date, expand=False, padding=2) + + #New row for end date + hbox = gtk.HBox() + vbox.pack_start(hbox, False, True, 0) + + lbl_end = gtk.Label(' To:') + btn_end = gtk.Button() + btn_end.set_image(gtk.image_new_from_stock(gtk.STOCK_INDEX, gtk.ICON_SIZE_BUTTON)) + btn_end.connect('clicked', self.__calendar_dialog, self.end_date) + + btn_clear = gtk.Button(label=' Clear Dates ') + btn_clear.connect('clicked', self.__clear_dates) + + hbox.pack_start(lbl_end, expand=False, padding=3) + hbox.pack_start(btn_end, expand=False, padding=3) + hbox.pack_start(self.end_date, expand=False, padding=2) + + hbox.pack_start(btn_clear, expand=False, padding=15) + + def __calendar_dialog(self, widget, entry): + d = gtk.Window(gtk.WINDOW_TOPLEVEL) + d.set_title('Pick a date') + + vb = gtk.VBox() + cal = gtk.Calendar() + vb.pack_start(cal, expand=False, padding=0) + + btn = gtk.Button('Done') + btn.connect('clicked', self.__get_date, cal, entry, d) + + vb.pack_start(btn, expand=False, padding=4) + + d.add(vb) + d.set_position(gtk.WIN_POS_MOUSE) + d.show_all() + + def __clear_dates(self, w): + self.start_date.set_text('') + self.end_date.set_text('') + + def __get_dates(self): + t1 = self.start_date.get_text() + t2 = self.end_date.get_text() + + if t1 == '': + t1 = '1970-01-01' + if t2 == '': + t2 = '2020-12-12' + + return (t1, t2) + + def __get_date(self, widget, calendar, entry, win): +# year and day are correct, month is 0..11 + (year, month, day) = calendar.get_date() + month += 1 + ds = '%04d-%02d-%02d' % (year, month, day) + entry.set_text(ds) + win.destroy() + +def main(argv=None): + """main can also be called in the python interpreter, by supplying the command line as the argument.""" + if argv is None: + argv = sys.argv[1:] + + def destroy(*args): # call back for terminating the main eventloop + gtk.main_quit() + + parser = OptionParser() + (options, sys.argv) = parser.parse_args(args = argv) + + config = Configuration.Config() + db = None + + settings = {} + + settings.update(config.get_db_parameters()) + settings.update(config.get_tv_parameters()) + settings.update(config.get_import_parameters()) + settings.update(config.get_default_paths()) + + db = fpdb_db.fpdb_db() + db.connect(settings['db-backend'], + settings['db-host'], + settings['db-databaseName'], + settings['db-user'], + settings['db-password']) + + qdict = FpdbSQLQueries.FpdbSQLQueries(db.get_backend_name()) + + i = Filters(db, settings, config, qdict) + main_window = gtk.Window() + main_window.connect('destroy', destroy) + main_window.add(i.get_vbox()) + main_window.show() + gtk.main() + +if __name__ == '__main__': + sys.exit(main()) + + diff --git a/pyfpdb/FpdbSQLQueries.py b/pyfpdb/FpdbSQLQueries.py index 24948986..fca17a5f 100644 --- a/pyfpdb/FpdbSQLQueries.py +++ b/pyfpdb/FpdbSQLQueries.py @@ -41,8 +41,8 @@ class FpdbSQLQueries: self.query['list_tables'] = """ """ ################################################################## - # Drop Tables - MySQL, PostgreSQL and SQLite all share same syntax - ################################################################## + # Drop Tables - MySQL, PostgreSQL and SQLite all share same syntax + ################################################################## if(self.dbname == 'MySQL InnoDB') or (self.dbname == 'PostgreSQL') or (self.dbname == 'SQLite'): self.query['drop_table'] = """DROP TABLE IF EXISTS """ @@ -609,7 +609,7 @@ class FpdbSQLQueries: elif(self.dbname == 'SQLite'): self.query['getSiteId'] = """SELECT id from Sites where name = %s""" - if(self.dbname == 'MySQL InnoDB') or (self.dbname == 'PostgreSQL'): + if(self.dbname == 'MySQL InnoDB') or (self.dbname == 'PostgreSQL') or (self.dbname == 'SQLite'): self.query['getRingProfitAllHandsPlayerIdSite'] = """ SELECT hp.handId, hp.winnings, coalesce(hp.ante,0) + SUM(ha.amount) , hp.winnings - (coalesce(hp.ante,0) + SUM(ha.amount)) @@ -617,28 +617,15 @@ class FpdbSQLQueries: INNER JOIN Players pl ON hp.playerId = pl.id INNER JOIN Hands h ON h.id = hp.handId INNER JOIN HandsActions ha ON ha.handPlayerId = hp.id + INNER JOIN Gametypes g ON h.gametypeId = g.id where pl.id in AND pl.siteId in AND h.handStart > '' AND h.handStart < '' + AND g.bigBlind in AND hp.tourneysPlayersId IS NULL GROUP BY hp.handId, hp.winnings, h.handStart, hp.ante ORDER BY h.handStart""" - elif(self.dbname == 'SQLite'): - #Probably doesn't work. - self.query['getRingProfitAllHandsPlayerIdSite'] = """ - SELECT hp.handId, hp.winnings, SUM(ha.amount), hp.winnings - SUM(ha.amount) - FROM HandsPlayers hp - INNER JOIN Players pl ON hp.playerId = pl.id - INNER JOIN Hands h ON h.id = hp.handId - INNER JOIN HandsActions ha ON ha.handPlayerId = hp.id - where pl.id in - AND pl.siteId in - AND h.handStart > '' - AND h.handStart < '' - AND hp.tourneysPlayersId IS NULL - GROUP BY hp.handId, hp.winnings, h.handStart - ORDER BY h.handStart""" if(self.dbname == 'MySQL InnoDB'): self.query['playerStats'] = """ @@ -1153,6 +1140,12 @@ class FpdbSQLQueries: and hprof2.PlPosition = stats.PlPosition) order by stats.category, stats.limittype, stats.bigBlind, cast(stats.PlPosition as signed) """ + if(self.dbname == 'MySQL InnoDB') or (self.dbname == 'PostgreSQL') or (self.dbname == 'SQLite'): + self.query['getGames'] = """SELECT DISTINCT category from Gametypes""" + + if(self.dbname == 'MySQL InnoDB') or (self.dbname == 'PostgreSQL') or (self.dbname == 'SQLite'): + self.query['getLimits'] = """SELECT DISTINCT bigBlind from Gametypes ORDER by bigBlind DESC""" + if __name__== "__main__": from optparse import OptionParser diff --git a/pyfpdb/FulltiltToFpdb.py b/pyfpdb/FulltiltToFpdb.py index f3d30229..dd0927b9 100755 --- a/pyfpdb/FulltiltToFpdb.py +++ b/pyfpdb/FulltiltToFpdb.py @@ -28,11 +28,13 @@ class Fulltilt(HandHistoryConverter): # Static regexes re_GameInfo = re.compile('- (?P\$|)?(?P[.0-9]+)/\$?(?P[.0-9]+) (Ante \$(?P[.0-9]+) )?- (?P(No Limit|Pot Limit|Limit))? (?P(Hold\'em|Omaha Hi|Razz))') - re_SplitHands = re.compile(r"(\n\n+)") + re_SplitHands = re.compile(r"\n\n+") + re_TailSplitHands = re.compile(r"(\n\n+)") re_HandInfo = re.compile('.*#(?P[0-9]+): Table (?P
[- a-zA-Z]+) (\((?P.+)\) )?- \$?(?P[.0-9]+)/\$?(?P[.0-9]+) (Ante \$(?P[.0-9]+) )?- (?P[a-zA-Z\' ]+) - (?P.*)') re_Button = re.compile('^The button is in seat #(?P
[- a-zA-Z]+)\'(?P.+?$)?", re.MULTILINE) re_Button = re.compile('Seat #(?P
[- a-zA-Z]+)\'(?P.+?$)?", re.MULTILINE) + re_Button = re.compile('Seat #(?P
[ a-zA-Z]+) - \$?(?P[.0-9]+)/\$?(?P[.0-9]+) - (?P.*) - (?P
[0-9]+):(?P[0-9]+) ET - (?P[0-9]+)/(?P[0-9]+)/(?P[0-9]+)Table (?P
[ a-zA-Z]+)\nSeat (?P