Merge branch 'master' of git://git.assembla.com/fpdboz
This commit is contained in:
commit
2e08c6f9b3
|
@ -312,11 +312,13 @@ The program itself is licensed under AGPLv3, see agpl-3.0.txt</p>
|
||||||
</TABLE>
|
</TABLE>
|
||||||
<p><BR></P>
|
<p><BR></P>
|
||||||
<p><B>Table HandsPlayers</B></P>
|
<p><B>Table HandsPlayers</B></P>
|
||||||
<p>cardX: can be 1 through 20, one for each card. In holdem only 1-2 of these are used, in omaha 1-4, in stud/razz 1-7, in single draw 1-10, in tripple draw all 20 and in badugi 1-16 (4*4).</P>
|
<p>cardX: can be 1 through 20, one for each card. In holdem only 1-2 of these are used, in omaha 1-4, in stud/razz 1-7, in single draw games 1-10 is used and in badugi 1-16 (4*4) is used.</P>
|
||||||
<p>For the draw games: the first 5 (badugi: 4) cards are the initial cards, the next 5 (badugi: 4) are after the first draw, etc.<br>
|
<p>For the draw games: the first 5 (badugi: 4) cards are the initial cards, the next 5 (badugi: 4) are after the first draw. If a player keeps some cards then those cards' spaces are filled with "k", short for "kept".<br>
|
||||||
Example 1: If a player gets 2-6 spades for his first five cards and decides to throw away the 4 and then gets a 7 of spades then the first 10 fields of cardXValue would be as follows: 2, 3, 4, 5, 6, 2, 3, 5, 6, 7<br>
|
Example 1: If a player gets 2-6 spades for his first five cards and decides to throw away the 4 and then gets a 7 of spades then the first 10 fields of cardXValue would be as follows: 2, 3, 4, 5, 6, k, k, 7, k, k<br>
|
||||||
Example 2: If a player gets 2, 3, 5, 8, J of spades for his first five cards and decides to throw away the 2 and the 3 and then gets a Q and K of spades then the first 10 fields of cardXValue would be as follows: 2, 3, 5, 8, J, 5, 8, J, Q, K.</p>
|
Example 2: If a player gets 2, 3, 5, 8, J of spades for his first five cards and decides to throw away the 2 and the 3 and then gets a Q and K of spades then the first 10 fields of cardXValue would be as follows: 2, 3, 5, 8, J, Q, K, k, k, k<br>
|
||||||
|
Note that it will k in the space of which card was there previously, so in example 2 where the player kept the last 3 cards, the last 3 fields of the first draw (ie. card8-10Value) are replaced with k.</p>
|
||||||
<p>I did not separate this into an extra table because I felt the lost space is not sufficiently large. Also the benefit for searching is far less relevant.</P>
|
<p>I did not separate this into an extra table because I felt the lost space is not sufficiently large. Also the benefit for searching is far less relevant.</P>
|
||||||
|
<p>ToDo: Original plan was to implement the many flags from hudcache as booleans - need to try this out as it will save space and may therefore be quicker.</p>
|
||||||
<TABLE BORDER=1 CELLPADDING=2 CELLSPACING=0>
|
<TABLE BORDER=1 CELLPADDING=2 CELLSPACING=0>
|
||||||
<TR VALIGN=TOP>
|
<TR VALIGN=TOP>
|
||||||
<TD><P>Field Name</P></TD>
|
<TD><P>Field Name</P></TD>
|
||||||
|
@ -353,33 +355,24 @@ Example 2: If a player gets 2, 3, 5, 8, J of spades for his first five cards and
|
||||||
<TD><P>smallint</P></TD>
|
<TD><P>smallint</P></TD>
|
||||||
<TD><p>The seat in which the person was sitting - necessary for HUD</P></TD>
|
<TD><p>The seat in which the person was sitting - necessary for HUD</P></TD>
|
||||||
</TR>
|
</TR>
|
||||||
|
<TR VALIGN=TOP>
|
||||||
|
<TD><P>card1(..7)</P></TD>
|
||||||
|
<TD><P>smallint</P></TD>
|
||||||
|
<TD><p>0=none/unknown, 1-13=2-Ah 14-26=2-Ad 27-39=2-Ac 40-52=2-As</P></TD>
|
||||||
|
</TR>
|
||||||
|
<TR VALIGN=TOP>
|
||||||
|
<TD><P>startCards</P></TD>
|
||||||
|
<TD><P>smallint</P></TD>
|
||||||
|
<TD><p>int representing Holdem starting cards.<br/>Hand is stored as an int 13 * x + y where x and y
|
||||||
|
are in range 0..12, and (x+2) and (y+2) represents rank of each card (2=2 .. 14=Ace). <br/>
|
||||||
|
If x > y then pair is suited, if x < y then unsuited.<br/>
|
||||||
|
Omaha and other games may need to use this as a key into another table. (to be decided ...)</P></TD>
|
||||||
|
</TR>
|
||||||
<TR VALIGN=TOP>
|
<TR VALIGN=TOP>
|
||||||
<TD><P>ante</P></TD>
|
<TD><P>ante</P></TD>
|
||||||
<TD><P>int</P></TD>
|
<TD><P>int</P></TD>
|
||||||
<TD><P>note: for cash this could be boolean, but in tourneys you may enter a hand with less than the full ante</P></TD>
|
<TD><P>note: for cash this could be boolean, but in tourneys you may enter a hand with less than the full ante</P></TD>
|
||||||
</TR>
|
</TR>
|
||||||
<TR VALIGN=TOP>
|
|
||||||
<TD><P>cardXValue</P></TD>
|
|
||||||
<TD><P>smallint</P></TD>
|
|
||||||
<TD><p>2-10=2-10, J=11, Q=12, K=13, A=14 (even in razz), unknown/no card=x<br>
|
|
||||||
see note above table</P></TD>
|
|
||||||
</TR>
|
|
||||||
<TR VALIGN=TOP>
|
|
||||||
<TD><P>cardXSuit</P></TD>
|
|
||||||
<TD><P>char(1)</P></TD>
|
|
||||||
<TD><P>h=hearts, s=spades, d=diamonds, c=clubs, unknown/no card=x</P></TD>
|
|
||||||
</TR>
|
|
||||||
<TR VALIGN=TOP>
|
|
||||||
<TD><P>cardXDiscarded</P></TD>
|
|
||||||
<TD><P>boolean</P></TD>
|
|
||||||
<TD><P>Whether the card was discarded (this only applies to draw games, X can be 1 through 15 since the final cards can obviously not be discarded).</P></TD>
|
|
||||||
</TR>
|
|
||||||
<TR VALIGN=TOP>
|
|
||||||
<TD><P>DrawnX</P></TD>
|
|
||||||
<TD><P>smallint</P></TD>
|
|
||||||
<TD><p>X can be 1 through 3.<br>
|
|
||||||
This field denotes how many cards the player has drawn on each draw.</P></TD>
|
|
||||||
</TR>
|
|
||||||
<TR VALIGN=TOP>
|
<TR VALIGN=TOP>
|
||||||
<TD><P>winnings</P></TD>
|
<TD><P>winnings</P></TD>
|
||||||
<TD><P>int</P></TD>
|
<TD><P>int</P></TD>
|
||||||
|
@ -388,7 +381,12 @@ Example 2: If a player gets 2, 3, 5, 8, J of spades for his first five cards and
|
||||||
<TR VALIGN=TOP>
|
<TR VALIGN=TOP>
|
||||||
<TD><P>rake</P></TD>
|
<TD><P>rake</P></TD>
|
||||||
<TD><P>int</P></TD>
|
<TD><P>int</P></TD>
|
||||||
<TD><P>rake for this player for this hand</P></TD>
|
<TD><P>rake for this player for this hand (i.e. final pot(s) size = winnings + rake)</P></TD>
|
||||||
|
</TR>
|
||||||
|
<TR VALIGN=TOP>
|
||||||
|
<TD><P>totalProfit</P></TD>
|
||||||
|
<TD><P>int</P></TD>
|
||||||
|
<TD><P>profit for this player for this hand ( i.e. winnings - (ante + bets) )</P></TD>
|
||||||
</TR>
|
</TR>
|
||||||
<TR VALIGN=TOP>
|
<TR VALIGN=TOP>
|
||||||
<TD><P>comment</P></TD>
|
<TD><P>comment</P></TD>
|
||||||
|
@ -405,6 +403,384 @@ Example 2: If a player gets 2, 3, 5, 8, J of spades for his first five cards and
|
||||||
<TD><P>bigint</P></TD>
|
<TD><P>bigint</P></TD>
|
||||||
<TD><P>references TourneysPlayers.id</P></TD>
|
<TD><P>references TourneysPlayers.id</P></TD>
|
||||||
</TR>
|
</TR>
|
||||||
|
<TR VALIGN=TOP>
|
||||||
|
<TD><P>tourneyTypeId</P></TD>
|
||||||
|
<TD><P>bigint</P></TD>
|
||||||
|
<TD><P>references TourneyTypes.id (maybe this should be on Hands?)</P></TD>
|
||||||
|
</TR>
|
||||||
|
<TR VALIGN=TOP>
|
||||||
|
<TD><P>wonWhenSeenStreet1(..4)</P></TD>
|
||||||
|
<TD><P>float</P></TD>
|
||||||
|
<TD><P>How many hands the player won after seeing the flop/street4 - this can be a "partial win" if the pot is split.<br>
|
||||||
|
To be completely clear, this stores a hand count, NOT a money amount.<br/>
|
||||||
|
(2/3/4: Same for turn/street5, river/street6, street7)</P></TD>
|
||||||
|
</TR>
|
||||||
|
<TR VALIGN=TOP>
|
||||||
|
<TD><P>wonAtSD</P></TD>
|
||||||
|
<TD><P>float</P></TD>
|
||||||
|
<TD><P>As wonWhenSeenStreet1, but for showdown.</P></TD>
|
||||||
|
</TR>
|
||||||
|
<TR VALIGN=TOP>
|
||||||
|
<TD><P>street0VPI</P></TD>
|
||||||
|
<TD><P>int</P></TD>
|
||||||
|
<TD><P>did player pay to see flop, 1 or 0</P></TD>
|
||||||
|
</TR>
|
||||||
|
<TR VALIGN=TOP>
|
||||||
|
<TD><P>street0Aggr</P></TD>
|
||||||
|
<TD><P>int</P></TD>
|
||||||
|
<TD><P>did player raise before flop, 1 or 0</P></TD>
|
||||||
|
</TR>
|
||||||
|
<TR VALIGN=TOP>
|
||||||
|
<TD><P>street0_3BChance</P></TD>
|
||||||
|
<TD><P>int</P></TD>
|
||||||
|
<TD><P>did player have chance to 3B, 1 or 0</P></TD>
|
||||||
|
</TR>
|
||||||
|
<TR VALIGN=TOP>
|
||||||
|
<TD><P>street0_3BDone</P></TD>
|
||||||
|
<TD><P>int</P></TD>
|
||||||
|
<TD><P>did player 3bet before flop, 1 or 0</P></TD>
|
||||||
|
</TR>
|
||||||
|
<TR VALIGN=TOP>
|
||||||
|
<TD><P>street0_4BChance</P></TD>
|
||||||
|
<TD><P>int</P></TD>
|
||||||
|
<TD><P>did player have chance to 4B, 1 or 0</P></TD>
|
||||||
|
</TR>
|
||||||
|
<TR VALIGN=TOP>
|
||||||
|
<TD><P>street0_4BDone</P></TD>
|
||||||
|
<TD><P>int</P></TD>
|
||||||
|
<TD><P>did player 4bet before flop, 1 or 0</P></TD>
|
||||||
|
</TR>
|
||||||
|
<TR VALIGN=TOP>
|
||||||
|
<TD><P>other_3BStreet0</P></TD>
|
||||||
|
<TD><P>int</P></TD>
|
||||||
|
<TD><P>did other player 3bet before flop, 1 or 0</P></TD>
|
||||||
|
</TR>
|
||||||
|
<TR VALIGN=TOP>
|
||||||
|
<TD><P>other_4BStreet0</P></TD>
|
||||||
|
<TD><P>int</P></TD>
|
||||||
|
<TD><P>did other player 4bet before flop, 1 or 0</P></TD>
|
||||||
|
</TR>
|
||||||
|
<TR VALIGN=TOP>
|
||||||
|
<TD><P>street1Seen(/2/3/4)</P></TD>
|
||||||
|
<TD><P>int</P></TD>
|
||||||
|
<TD><P>did player see flop/street4 (.. etc)</P></TD>
|
||||||
|
</TR>
|
||||||
|
<TR VALIGN=TOP>
|
||||||
|
<TD><P>sawShowdown</P></TD>
|
||||||
|
<TD><P>int</P></TD>
|
||||||
|
<TD><P>did player see showdown</P></TD>
|
||||||
|
</TR>
|
||||||
|
<TR VALIGN=TOP>
|
||||||
|
<TD><P>street1Aggr</P></TD>
|
||||||
|
<TD><P>int</P></TD>
|
||||||
|
<TD><P>number of hands where player raised flop/street4</P></TD>
|
||||||
|
</TR>
|
||||||
|
<TR VALIGN=TOP>
|
||||||
|
<TD><P>street2Aggr</P></TD>
|
||||||
|
<TD><P>int</P></TD>
|
||||||
|
<TD><P>number of hands where player raised turn/street5</P></TD>
|
||||||
|
</TR>
|
||||||
|
<TR VALIGN=TOP>
|
||||||
|
<TD><P>street3Aggr</P></TD>
|
||||||
|
<TD><P>int</P></TD>
|
||||||
|
<TD><P>number of hands where player raised river/street6</P></TD>
|
||||||
|
</TR>
|
||||||
|
<TR VALIGN=TOP>
|
||||||
|
<TD><P>street4Aggr</P></TD>
|
||||||
|
<TD><P>int</P></TD>
|
||||||
|
<TD><P>number of hands where player raised street7</P></TD>
|
||||||
|
</TR>
|
||||||
|
<TR VALIGN=TOP>
|
||||||
|
<TD><P>otherRaisedStreet0</P></TD>
|
||||||
|
<TD><P>int</P></TD>
|
||||||
|
<TD><P>number of hands where someone else raised pre-flop/street3</P></TD>
|
||||||
|
</TR>
|
||||||
|
<TR VALIGN=TOP>
|
||||||
|
<TD><P>otherRaisedStreet1</P></TD>
|
||||||
|
<TD><P>int</P></TD>
|
||||||
|
<TD><P>number of hands where someone else raised flop/street4</P></TD>
|
||||||
|
</TR>
|
||||||
|
<TR VALIGN=TOP>
|
||||||
|
<TD><P>otherRaisedStreet2</P></TD>
|
||||||
|
<TD><P>int</P></TD>
|
||||||
|
<TD><P>number of hands where someone else raised turn/street5</P></TD>
|
||||||
|
</TR>
|
||||||
|
<TR VALIGN=TOP>
|
||||||
|
<TD><P>otherRaisedStreet3</P></TD>
|
||||||
|
<TD><P>int</P></TD>
|
||||||
|
<TD><P>number of hands where someone else raised river/street6</P></TD>
|
||||||
|
</TR>
|
||||||
|
<TR VALIGN=TOP>
|
||||||
|
<TD><P>otherRaisedStreet4</P></TD>
|
||||||
|
<TD><P>int</P></TD>
|
||||||
|
<TD><P>number of hands where someone else raised street7</P></TD>
|
||||||
|
</TR>
|
||||||
|
<TR VALIGN=TOP>
|
||||||
|
<TD><P>foldToOtherRaisedStreet0</P></TD>
|
||||||
|
<TD><P>int</P></TD>
|
||||||
|
<TD><P>number of hands where someone else raised flop/street4 and the player folded</P></TD>
|
||||||
|
</TR>
|
||||||
|
<TR VALIGN=TOP>
|
||||||
|
<TD><P>foldToOtherRaisedStreet1</P></TD>
|
||||||
|
<TD><P>int</P></TD>
|
||||||
|
<TD><P>number of hands where someone else raised flop/street4 and the player folded</P></TD>
|
||||||
|
</TR>
|
||||||
|
<TR VALIGN=TOP>
|
||||||
|
<TD><P>foldToOtherRaisedStreet2</P></TD>
|
||||||
|
<TD><P>int</P></TD>
|
||||||
|
<TD><P>number of hands where someone else raised Turn/street5 and the player folded</P></TD>
|
||||||
|
</TR>
|
||||||
|
<TR VALIGN=TOP>
|
||||||
|
<TD><P>foldToOtherRaisedStreet3</P></TD>
|
||||||
|
<TD><P>int</P></TD>
|
||||||
|
<TD><P>number of hands where someone else raised River/street6 and the player folded</P></TD>
|
||||||
|
</TR>
|
||||||
|
<TR VALIGN=TOP>
|
||||||
|
<TD><P>foldToOtherRaisedStreet4</P></TD>
|
||||||
|
<TD><P>int</P></TD>
|
||||||
|
<TD><P>number of hands where someone else raised street7 and the player folded</P></TD>
|
||||||
|
</TR>
|
||||||
|
<TR VALIGN=TOP>
|
||||||
|
<TD><P>stealAttemptChance</P></TD>
|
||||||
|
<TD><P>int</P></TD>
|
||||||
|
<TD><P>Player was in CO, BTN or SB and nobody has called yet</P></TD>
|
||||||
|
</TR>
|
||||||
|
<TR VALIGN=TOP>
|
||||||
|
<TD><P>stealAttempted</P></TD>
|
||||||
|
<TD><P>int</P></TD>
|
||||||
|
<TD><P>Player took a chance per the above condition</P></TD>
|
||||||
|
</TR>
|
||||||
|
<TR VALIGN=TOP>
|
||||||
|
<TD><P>foldBbToStealChance</P></TD>
|
||||||
|
<TD><P>int</P></TD>
|
||||||
|
<TD><P>Somebody tried to steal BB from player</P></TD>
|
||||||
|
</TR>
|
||||||
|
<TR VALIGN=TOP>
|
||||||
|
<TD><P>foldedBbToSteal</P></TD>
|
||||||
|
<TD><P>int</P></TD>
|
||||||
|
<TD><P>Player folded BB to steal attempt</P></TD>
|
||||||
|
</TR>
|
||||||
|
<TR VALIGN=TOP>
|
||||||
|
<TD><P>foldSbToStealChance</P></TD>
|
||||||
|
<TD><P>int</P></TD>
|
||||||
|
<TD><P>Somebody tried to steal SB from player</P></TD>
|
||||||
|
</TR>
|
||||||
|
<TR VALIGN=TOP>
|
||||||
|
<TD><P>foldedSbToSteal</P></TD>
|
||||||
|
<TD><P>int</P></TD>
|
||||||
|
<TD><P>Player folded SB to steal attempt</P></TD>
|
||||||
|
</TR>
|
||||||
|
|
||||||
|
<TR VALIGN=TOP>
|
||||||
|
<TD><P>street1CBChance</P></TD>
|
||||||
|
<TD><P>int</P></TD>
|
||||||
|
<TD><P>Player had chance to make continuation bet on flop/street4</P></TD>
|
||||||
|
</TR>
|
||||||
|
<TR VALIGN=TOP>
|
||||||
|
<TD><P>street1CBDone</P></TD>
|
||||||
|
<TD><P>int</P></TD>
|
||||||
|
<TD><P>Player used chance to make continuation bet on flop/street4</P></TD>
|
||||||
|
</TR>
|
||||||
|
<TR VALIGN=TOP>
|
||||||
|
<TD><P>street2CBChance</P></TD>
|
||||||
|
<TD><P>int</P></TD>
|
||||||
|
<TD><P>Player had chance to make continuation bet on turn/street5</P></TD>
|
||||||
|
</TR>
|
||||||
|
<TR VALIGN=TOP>
|
||||||
|
<TD><P>street2CBDone</P></TD>
|
||||||
|
<TD><P>int</P></TD>
|
||||||
|
<TD><P>Player used chance to make continuation bet on turn/street5</P></TD>
|
||||||
|
</TR>
|
||||||
|
<TR VALIGN=TOP>
|
||||||
|
<TD><P>street3CBChance</P></TD>
|
||||||
|
<TD><P>int</P></TD>
|
||||||
|
<TD><P>Player had chance to make continuation bet on river/street6</P></TD>
|
||||||
|
</TR>
|
||||||
|
<TR VALIGN=TOP>
|
||||||
|
<TD><P>street3CBDone</P></TD>
|
||||||
|
<TD><P>int</P></TD>
|
||||||
|
<TD><P>Player used chance to make continuation bet on river/street6</P></TD>
|
||||||
|
</TR>
|
||||||
|
<TR VALIGN=TOP>
|
||||||
|
<TD><P>street4CBChance</P></TD>
|
||||||
|
<TD><P>int</P></TD>
|
||||||
|
<TD><P>Player had chance to make continuation bet on street7</P></TD>
|
||||||
|
</TR>
|
||||||
|
<TR VALIGN=TOP>
|
||||||
|
<TD><P>street4CBDone</P></TD>
|
||||||
|
<TD><P>int</P></TD>
|
||||||
|
<TD><P>Player used chance to make continuation bet on street7</P></TD>
|
||||||
|
</TR>
|
||||||
|
|
||||||
|
<TR VALIGN=TOP>
|
||||||
|
<TD><P>foldToStreet1CBChance</P></TD>
|
||||||
|
<TD><P>int</P></TD>
|
||||||
|
<TD><P>Player had chance to fold to continuation bet on this street</P></TD>
|
||||||
|
</TR>
|
||||||
|
<TR VALIGN=TOP>
|
||||||
|
<TD><P>foldToStreet1CBDone</P></TD>
|
||||||
|
<TD><P>int</P></TD>
|
||||||
|
<TD><P>Player used chance to fold to continuation bet on this street</P></TD>
|
||||||
|
</TR>
|
||||||
|
<TR VALIGN=TOP>
|
||||||
|
<TD><P>foldToStreet2CBChance</P></TD>
|
||||||
|
<TD><P>int</P></TD>
|
||||||
|
<TD><P>Player had chance to fold to continuation bet on this street</P></TD>
|
||||||
|
</TR>
|
||||||
|
<TR VALIGN=TOP>
|
||||||
|
<TD><P>foldToStreet2CBDone</P></TD>
|
||||||
|
<TD><P>int</P></TD>
|
||||||
|
<TD><P>Player used chance to fold to continuation bet on this street</P></TD>
|
||||||
|
</TR>
|
||||||
|
<TR VALIGN=TOP>
|
||||||
|
<TD><P>foldToStreet3CBChance</P></TD>
|
||||||
|
<TD><P>int</P></TD>
|
||||||
|
<TD><P>Player had chance to fold to continuation bet on this street</P></TD>
|
||||||
|
</TR>
|
||||||
|
<TR VALIGN=TOP>
|
||||||
|
<TD><P>foldToStreet3CBDone</P></TD>
|
||||||
|
<TD><P>int</P></TD>
|
||||||
|
<TD><P>Player used chance to fold to continuation bet on this street</P></TD>
|
||||||
|
</TR>
|
||||||
|
<TR VALIGN=TOP>
|
||||||
|
<TD><P>foldToStreet4CBChance</P></TD>
|
||||||
|
<TD><P>int</P></TD>
|
||||||
|
<TD><P>Player had chance to fold to continuation bet on this street</P></TD>
|
||||||
|
</TR>
|
||||||
|
<TR VALIGN=TOP>
|
||||||
|
<TD><P>foldToStreet4CBDone</P></TD>
|
||||||
|
<TD><P>int</P></TD>
|
||||||
|
<TD><P>Player used chance to fold to continuation bet on this street</P></TD>
|
||||||
|
</TR>
|
||||||
|
|
||||||
|
<TR VALIGN=TOP>
|
||||||
|
<TD><P>street1CheckCallRaiseChance</P></TD>
|
||||||
|
<TD><P>int</P></TD>
|
||||||
|
<TD><P>How often player had the chance to do a check-raise or a call-raise on this street</P></TD>
|
||||||
|
</TR>
|
||||||
|
<TR VALIGN=TOP>
|
||||||
|
<TD><P>street1CheckCallRaiseDone</P></TD>
|
||||||
|
<TD><P>int</P></TD>
|
||||||
|
<TD><P>How often player used the chance to do a check-raise or a call-raise on this street</P></TD>
|
||||||
|
</TR>
|
||||||
|
<TR VALIGN=TOP>
|
||||||
|
<TD><P>street2CheckCallRaiseChance</P></TD>
|
||||||
|
<TD><P>int</P></TD>
|
||||||
|
<TD><P>How often player had the chance to do a check-raise or a call-raise on this street</P></TD>
|
||||||
|
</TR>
|
||||||
|
<TR VALIGN=TOP>
|
||||||
|
<TD><P>street2CheckCallRaiseDone</P></TD>
|
||||||
|
<TD><P>int</P></TD>
|
||||||
|
<TD><P>How often player used the chance to do a check-raise or a call-raise on this street</P></TD>
|
||||||
|
</TR>
|
||||||
|
<TR VALIGN=TOP>
|
||||||
|
<TD><P>street3CheckCallRaiseChance</P></TD>
|
||||||
|
<TD><P>int</P></TD>
|
||||||
|
<TD><P>How often player had the chance to do a check-raise or a call-raise on this street</P></TD>
|
||||||
|
</TR>
|
||||||
|
<TR VALIGN=TOP>
|
||||||
|
<TD><P>street3CheckCallRaiseDone</P></TD>
|
||||||
|
<TD><P>int</P></TD>
|
||||||
|
<TD><P>How often player used the chance to do a check-raise or a call-raise on this street</P></TD>
|
||||||
|
</TR>
|
||||||
|
<TR VALIGN=TOP>
|
||||||
|
<TD><P>street4CheckCallRaiseChance</P></TD>
|
||||||
|
<TD><P>int</P></TD>
|
||||||
|
<TD><P>How often player had the chance to do a check-raise or a call-raise on this street</P></TD>
|
||||||
|
</TR>
|
||||||
|
<TR VALIGN=TOP>
|
||||||
|
<TD><P>street4CheckCallRaiseDone</P></TD>
|
||||||
|
<TD><P>int</P></TD>
|
||||||
|
<TD><P>How often player used the chance to do a check-raise or a call-raise on this street</P></TD>
|
||||||
|
</TR>
|
||||||
|
|
||||||
|
<TR VALIGN=TOP>
|
||||||
|
<TD><P>street0Calls</P></TD>
|
||||||
|
<TD><P>int</P></TD>
|
||||||
|
<TD><P>Number of times player called on this street</P></TD>
|
||||||
|
</TR>
|
||||||
|
<TR VALIGN=TOP>
|
||||||
|
<TD><P>street1Calls</P></TD>
|
||||||
|
<TD><P>int</P></TD>
|
||||||
|
<TD><P>Number of times player called on this street</P></TD>
|
||||||
|
</TR>
|
||||||
|
<TR VALIGN=TOP>
|
||||||
|
<TD><P>street2Calls</P></TD>
|
||||||
|
<TD><P>int</P></TD>
|
||||||
|
<TD><P>Number of times player called on this street</P></TD>
|
||||||
|
</TR>
|
||||||
|
<TR VALIGN=TOP>
|
||||||
|
<TD><P>street3Calls</P></TD>
|
||||||
|
<TD><P>int</P></TD>
|
||||||
|
<TD><P>Number of times player called on this street</P></TD>
|
||||||
|
</TR>
|
||||||
|
<TR VALIGN=TOP>
|
||||||
|
<TD><P>street4Calls</P></TD>
|
||||||
|
<TD><P>int</P></TD>
|
||||||
|
<TD><P>Number of times player called on this street</P></TD>
|
||||||
|
</TR>
|
||||||
|
|
||||||
|
<TR VALIGN=TOP>
|
||||||
|
<TD><P>street0Bets</P></TD>
|
||||||
|
<TD><P>int</P></TD>
|
||||||
|
<TD><P>Number of times player bet on this street</P></TD>
|
||||||
|
</TR>
|
||||||
|
<TR VALIGN=TOP>
|
||||||
|
<TD><P>street1Bets</P></TD>
|
||||||
|
<TD><P>int</P></TD>
|
||||||
|
<TD><P>Number of times player bet on this street</P></TD>
|
||||||
|
</TR>
|
||||||
|
<TR VALIGN=TOP>
|
||||||
|
<TD><P>street2Bets</P></TD>
|
||||||
|
<TD><P>int</P></TD>
|
||||||
|
<TD><P>Number of times player bet on this street</P></TD>
|
||||||
|
</TR>
|
||||||
|
<TR VALIGN=TOP>
|
||||||
|
<TD><P>street3Bets</P></TD>
|
||||||
|
<TD><P>int</P></TD>
|
||||||
|
<TD><P>Number of times player bet on this street</P></TD>
|
||||||
|
</TR>
|
||||||
|
<TR VALIGN=TOP>
|
||||||
|
<TD><P>street4Bets</P></TD>
|
||||||
|
<TD><P>int</P></TD>
|
||||||
|
<TD><P>Number of times player bet on this street</P></TD>
|
||||||
|
</TR>
|
||||||
|
|
||||||
|
<TR VALIGN=TOP>
|
||||||
|
<TD><P>street0Raises</P></TD>
|
||||||
|
<TD><P>int</P></TD>
|
||||||
|
<TD><P>Number of times player raised on this street</P></TD>
|
||||||
|
</TR>
|
||||||
|
<TR VALIGN=TOP>
|
||||||
|
<TD><P>street1Raises</P></TD>
|
||||||
|
<TD><P>int</P></TD>
|
||||||
|
<TD><P>Number of times player raised on this street</P></TD>
|
||||||
|
</TR>
|
||||||
|
<TR VALIGN=TOP>
|
||||||
|
<TD><P>street2Raises</P></TD>
|
||||||
|
<TD><P>int</P></TD>
|
||||||
|
<TD><P>Number of times player raised on this street</P></TD>
|
||||||
|
</TR>
|
||||||
|
<TR VALIGN=TOP>
|
||||||
|
<TD><P>street3Raises</P></TD>
|
||||||
|
<TD><P>int</P></TD>
|
||||||
|
<TD><P>Number of times player raised on this street</P></TD>
|
||||||
|
</TR>
|
||||||
|
<TR VALIGN=TOP>
|
||||||
|
<TD><P>street4Raises</P></TD>
|
||||||
|
<TD><P>int</P></TD>
|
||||||
|
<TD><P>Number of times player raised on this street</P></TD>
|
||||||
|
</TR>
|
||||||
|
|
||||||
|
<TR VALIGN=TOP>
|
||||||
|
<TD><P>actionString</P></TD>
|
||||||
|
<TD><P>int</P></TD>
|
||||||
|
<TD><P>Experimental - idea is to store the action on this street as a string: e.g. kkBrcfC, with
|
||||||
|
player's own choices in upper case and other players in lower case. k=check, b=bet, c=call,
|
||||||
|
r=raise. (Perhaps NL would miss out bet sizes for this?) It would then be possible to do complex
|
||||||
|
ad-hoc queries using queries like: actionString like '%B%r%C%
|
||||||
|
</P></TD>
|
||||||
|
</TR>
|
||||||
</TABLE>
|
</TABLE>
|
||||||
<p><BR></P>
|
<p><BR></P>
|
||||||
<P><B>Table HudCache</B></P>
|
<P><B>Table HudCache</B></P>
|
||||||
|
@ -444,12 +820,23 @@ Example 2: If a player gets 2, 3, 5, 8, J of spades for his first five cards and
|
||||||
<TD><P>smallint</P></TD>
|
<TD><P>smallint</P></TD>
|
||||||
<TD><P>References TourneyTypes.id</P></TD>
|
<TD><P>References TourneyTypes.id</P></TD>
|
||||||
</TR>
|
</TR>
|
||||||
|
|
||||||
<TR VALIGN=TOP>
|
<TR VALIGN=TOP>
|
||||||
<TD><P>HDs</P></TD>
|
<TD><P>HDs</P></TD>
|
||||||
<TD><P>int</P></TD>
|
<TD><P>int</P></TD>
|
||||||
<TD><P>number of hands this player played in this gametype with this number of seats</P></TD>
|
<TD><P>number of hands this player played in this gametype with this number of seats</P></TD>
|
||||||
</TR>
|
</TR>
|
||||||
|
<TR VALIGN=TOP>
|
||||||
|
<TD><P>wonWhenSeenStreet1(/2/3/4)</P></TD>
|
||||||
|
<TD><P>float</P></TD>
|
||||||
|
<TD><P>How many hands the player won after seeing the flop/street4 - this can be a "partial win" if the pot is split.<br>
|
||||||
|
To be completely clear, this stores a hand count, NOT a money amount.<br/>
|
||||||
|
(/2/3/4: Same for turn/street5, river/street6, street7)</P></TD>
|
||||||
|
</TR>
|
||||||
|
<TR VALIGN=TOP>
|
||||||
|
<TD><P>wonAtSD</P></TD>
|
||||||
|
<TD><P>float</P></TD>
|
||||||
|
<TD><P>As wonWhenSeenStreet1, but for showdown.</P></TD>
|
||||||
|
</TR>
|
||||||
<TR VALIGN=TOP>
|
<TR VALIGN=TOP>
|
||||||
<TD><P>street0VPI</P></TD>
|
<TD><P>street0VPI</P></TD>
|
||||||
<TD><P>int</P></TD>
|
<TD><P>int</P></TD>
|
||||||
|
@ -463,14 +850,24 @@ Example 2: If a player gets 2, 3, 5, 8, J of spades for his first five cards and
|
||||||
<TD><P>number of hands where player raised before flop</P></TD>
|
<TD><P>number of hands where player raised before flop</P></TD>
|
||||||
</TR>
|
</TR>
|
||||||
<TR VALIGN=TOP>
|
<TR VALIGN=TOP>
|
||||||
<TD><P>street0_3B4BChance</P></TD>
|
<TD><P>street0_3BChance</P></TD>
|
||||||
<TD><P>int</P></TD>
|
<TD><P>int</P></TD>
|
||||||
<TD><P>number of hands where player had chance to 3B or 4B</P></TD>
|
<TD><P>number of hands where player had chance to 3B before flop</P></TD>
|
||||||
</TR>
|
</TR>
|
||||||
<TR VALIGN=TOP>
|
<TR VALIGN=TOP>
|
||||||
<TD><P>street0_3B4BDone</P></TD>
|
<TD><P>street0_3BDone</P></TD>
|
||||||
<TD><P>int</P></TD>
|
<TD><P>int</P></TD>
|
||||||
<TD><P>number of hands where player 3bet/4bet before flop</P></TD>
|
<TD><P>number of hands where player 3bet before flop</P></TD>
|
||||||
|
</TR>
|
||||||
|
<TR VALIGN=TOP>
|
||||||
|
<TD><P>street0_4BChance</P></TD>
|
||||||
|
<TD><P>int</P></TD>
|
||||||
|
<TD><P>number of hands where player had chance to 4B before flop</P></TD>
|
||||||
|
</TR>
|
||||||
|
<TR VALIGN=TOP>
|
||||||
|
<TD><P>street0_4BDone</P></TD>
|
||||||
|
<TD><P>int</P></TD>
|
||||||
|
<TD><P>number of hands where player 4bet before flop</P></TD>
|
||||||
</TR>
|
</TR>
|
||||||
<TR VALIGN=TOP>
|
<TR VALIGN=TOP>
|
||||||
<TD><P>street1Seen</P></TD>
|
<TD><P>street1Seen</P></TD>
|
||||||
|
@ -517,6 +914,11 @@ Example 2: If a player gets 2, 3, 5, 8, J of spades for his first five cards and
|
||||||
<TD><P>int</P></TD>
|
<TD><P>int</P></TD>
|
||||||
<TD><P>number of hands where player raised street7</P></TD>
|
<TD><P>number of hands where player raised street7</P></TD>
|
||||||
</TR>
|
</TR>
|
||||||
|
<TR VALIGN=TOP>
|
||||||
|
<TD><P>otherRaisedStreet0</P></TD>
|
||||||
|
<TD><P>int</P></TD>
|
||||||
|
<TD><P>number of hands where someone else raised pre-flop/street3</P></TD>
|
||||||
|
</TR>
|
||||||
<TR VALIGN=TOP>
|
<TR VALIGN=TOP>
|
||||||
<TD><P>otherRaisedStreet1</P></TD>
|
<TD><P>otherRaisedStreet1</P></TD>
|
||||||
<TD><P>int</P></TD>
|
<TD><P>int</P></TD>
|
||||||
|
@ -537,6 +939,11 @@ Example 2: If a player gets 2, 3, 5, 8, J of spades for his first five cards and
|
||||||
<TD><P>int</P></TD>
|
<TD><P>int</P></TD>
|
||||||
<TD><P>number of hands where someone else raised street7</P></TD>
|
<TD><P>number of hands where someone else raised street7</P></TD>
|
||||||
</TR>
|
</TR>
|
||||||
|
<TR VALIGN=TOP>
|
||||||
|
<TD><P>foldToOtherRaisedStreet0</P></TD>
|
||||||
|
<TD><P>int</P></TD>
|
||||||
|
<TD><P>number of hands where someone else raised pre-flop/street3 and the player folded</P></TD>
|
||||||
|
</TR>
|
||||||
<TR VALIGN=TOP>
|
<TR VALIGN=TOP>
|
||||||
<TD><P>foldToOtherRaisedStreet1</P></TD>
|
<TD><P>foldToOtherRaisedStreet1</P></TD>
|
||||||
<TD><P>int</P></TD>
|
<TD><P>int</P></TD>
|
||||||
|
@ -557,18 +964,6 @@ Example 2: If a player gets 2, 3, 5, 8, J of spades for his first five cards and
|
||||||
<TD><P>int</P></TD>
|
<TD><P>int</P></TD>
|
||||||
<TD><P>number of hands where someone else raised street7 and the player folded</P></TD>
|
<TD><P>number of hands where someone else raised street7 and the player folded</P></TD>
|
||||||
</TR>
|
</TR>
|
||||||
<TR VALIGN=TOP>
|
|
||||||
<TD><P>wonWhenSeenStreet1</P></TD>
|
|
||||||
<TD><P>float</P></TD>
|
|
||||||
<TD><P>How many hands the player won after seeing the flop/street4 - this can be a "partial win" if the pot is split.<br>
|
|
||||||
To be completely clear, this stores a hand count, NOT a money amount.</P></TD>
|
|
||||||
</TR>
|
|
||||||
<TR VALIGN=TOP>
|
|
||||||
<TD><P>wonAtSD</P></TD>
|
|
||||||
<TD><P>float</P></TD>
|
|
||||||
<TD><P>As wonWhenSeenStreet1, but for showdown.</P></TD>
|
|
||||||
</TR>
|
|
||||||
|
|
||||||
<TR VALIGN=TOP>
|
<TR VALIGN=TOP>
|
||||||
<TD><P>stealAttemptChance</P></TD>
|
<TD><P>stealAttemptChance</P></TD>
|
||||||
<TD><P>int</P></TD>
|
<TD><P>int</P></TD>
|
||||||
|
@ -729,6 +1124,84 @@ Example 2: If a player gets 2, 3, 5, 8, J of spades for his first five cards and
|
||||||
<TD><P>How often player used the chance to do a check-raise or a call-raise on this street</P></TD>
|
<TD><P>How often player used the chance to do a check-raise or a call-raise on this street</P></TD>
|
||||||
</TR>
|
</TR>
|
||||||
|
|
||||||
|
<TR VALIGN=TOP>
|
||||||
|
<TD><P>street0Calls</P></TD>
|
||||||
|
<TD><P>int</P></TD>
|
||||||
|
<TD><P>Number of times player called on this street</P></TD>
|
||||||
|
</TR>
|
||||||
|
<TR VALIGN=TOP>
|
||||||
|
<TD><P>street1Calls</P></TD>
|
||||||
|
<TD><P>int</P></TD>
|
||||||
|
<TD><P>Number of times player called on this street</P></TD>
|
||||||
|
</TR>
|
||||||
|
<TR VALIGN=TOP>
|
||||||
|
<TD><P>street2Calls</P></TD>
|
||||||
|
<TD><P>int</P></TD>
|
||||||
|
<TD><P>Number of times player called on this street</P></TD>
|
||||||
|
</TR>
|
||||||
|
<TR VALIGN=TOP>
|
||||||
|
<TD><P>street3Calls</P></TD>
|
||||||
|
<TD><P>int</P></TD>
|
||||||
|
<TD><P>Number of times player called on this street</P></TD>
|
||||||
|
</TR>
|
||||||
|
<TR VALIGN=TOP>
|
||||||
|
<TD><P>street4Calls</P></TD>
|
||||||
|
<TD><P>int</P></TD>
|
||||||
|
<TD><P>Number of times player called on this street</P></TD>
|
||||||
|
</TR>
|
||||||
|
|
||||||
|
<TR VALIGN=TOP>
|
||||||
|
<TD><P>street0Bets</P></TD>
|
||||||
|
<TD><P>int</P></TD>
|
||||||
|
<TD><P>Number of times player bet on this street</P></TD>
|
||||||
|
</TR>
|
||||||
|
<TR VALIGN=TOP>
|
||||||
|
<TD><P>street1Bets</P></TD>
|
||||||
|
<TD><P>int</P></TD>
|
||||||
|
<TD><P>Number of times player bet on this street</P></TD>
|
||||||
|
</TR>
|
||||||
|
<TR VALIGN=TOP>
|
||||||
|
<TD><P>street2Bets</P></TD>
|
||||||
|
<TD><P>int</P></TD>
|
||||||
|
<TD><P>Number of times player bet on this street</P></TD>
|
||||||
|
</TR>
|
||||||
|
<TR VALIGN=TOP>
|
||||||
|
<TD><P>street3Bets</P></TD>
|
||||||
|
<TD><P>int</P></TD>
|
||||||
|
<TD><P>Number of times player bet on this street</P></TD>
|
||||||
|
</TR>
|
||||||
|
<TR VALIGN=TOP>
|
||||||
|
<TD><P>street4Bets</P></TD>
|
||||||
|
<TD><P>int</P></TD>
|
||||||
|
<TD><P>Number of times player bet on this street</P></TD>
|
||||||
|
</TR>
|
||||||
|
|
||||||
|
<TR VALIGN=TOP>
|
||||||
|
<TD><P>street0Raises</P></TD>
|
||||||
|
<TD><P>int</P></TD>
|
||||||
|
<TD><P>Number of times player raised on this street</P></TD>
|
||||||
|
</TR>
|
||||||
|
<TR VALIGN=TOP>
|
||||||
|
<TD><P>street1Raises</P></TD>
|
||||||
|
<TD><P>int</P></TD>
|
||||||
|
<TD><P>Number of times player raised on this street</P></TD>
|
||||||
|
</TR>
|
||||||
|
<TR VALIGN=TOP>
|
||||||
|
<TD><P>street2Raises</P></TD>
|
||||||
|
<TD><P>int</P></TD>
|
||||||
|
<TD><P>Number of times player raised on this street</P></TD>
|
||||||
|
</TR>
|
||||||
|
<TR VALIGN=TOP>
|
||||||
|
<TD><P>street3Raises</P></TD>
|
||||||
|
<TD><P>int</P></TD>
|
||||||
|
<TD><P>Number of times player raised on this street</P></TD>
|
||||||
|
</TR>
|
||||||
|
<TR VALIGN=TOP>
|
||||||
|
<TD><P>street4Raises</P></TD>
|
||||||
|
<TD><P>int</P></TD>
|
||||||
|
<TD><P>Number of times player raised on this street</P></TD>
|
||||||
|
</TR>
|
||||||
|
|
||||||
</TABLE>
|
</TABLE>
|
||||||
<P></P>
|
<P></P>
|
||||||
<P><B>Table HandsActions</B></P>
|
<P><B>Table HandsActions</B></P>
|
||||||
|
@ -926,5 +1399,32 @@ Example 2: If a player gets 2, 3, 5, 8, J of spades for his first five cards and
|
||||||
<TD><P><BR></P></TD>
|
<TD><P><BR></P></TD>
|
||||||
</TR>
|
</TR>
|
||||||
</TABLE>
|
</TABLE>
|
||||||
|
<p><BR></P>
|
||||||
|
<p><B>Possible Changes</B></P>
|
||||||
|
<TABLE BORDER=1 CELLPADDING=2 CELLSPACING=0>
|
||||||
|
<TR VALIGN=TOP>
|
||||||
|
<TD><P>Table</P></TD>
|
||||||
|
<TD><P>Comment</P></TD>
|
||||||
|
</TR>
|
||||||
|
<TR VALIGN=TOP>
|
||||||
|
<TD><P>BoardCards</P></TD>
|
||||||
|
<TD><P>Remove as these attributes are now stored on Hands</P></TD>
|
||||||
|
</TR>
|
||||||
|
<TR VALIGN=TOP>
|
||||||
|
<TD><P>HandsActions</P></TD>
|
||||||
|
<TD><P>Remove if/when these attributes are stored on Hands or elsewhere</P></TD>
|
||||||
|
</TR>
|
||||||
|
<TR VALIGN=TOP>
|
||||||
|
<TD><P>HandsPlayers</P></TD>
|
||||||
|
<TD><P>Move tourneyTypeId field to Hands table.</P></TD>
|
||||||
|
</TR>
|
||||||
|
<TR VALIGN=TOP>
|
||||||
|
<TD><P>Comments</P></TD>
|
||||||
|
<TD><P>Comment fields on various tables should probably be moved to a single comment table. Aim
|
||||||
|
should be to where possible reduce tables to a list of fixed length not-null columns and have
|
||||||
|
the larger, sparser comment columns in a dedicated table. (May not be possible or practical but
|
||||||
|
something to aim at.)</P></TD>
|
||||||
|
</TR>
|
||||||
|
</TABLE>
|
||||||
</BODY>
|
</BODY>
|
||||||
</HTML>
|
</HTML>
|
||||||
|
|
|
@ -1,178 +0,0 @@
|
||||||
#!/usr/bin/pugs
|
|
||||||
|
|
||||||
#Copyright 2008 Steffen Jobbagy-Felso
|
|
||||||
#This program is free software: you can redistribute it and/or modify
|
|
||||||
#it under the terms of the GNU Affero General Public License as published by
|
|
||||||
#the Free Software Foundation, version 3 of the License.
|
|
||||||
#
|
|
||||||
#This program is distributed in the hope that it will be useful,
|
|
||||||
#but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
#GNU General Public License for more details.
|
|
||||||
#
|
|
||||||
#You should have received a copy of the GNU Affero General Public License
|
|
||||||
#along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
#In the "official" distribution you can find the license in
|
|
||||||
#agpl-3.0.txt in the docs folder of the package.
|
|
||||||
|
|
||||||
module LibFpdbImport;
|
|
||||||
use v6;
|
|
||||||
#use strict;
|
|
||||||
use LibFpdbShared;
|
|
||||||
#use LibFpdbImport2;
|
|
||||||
|
|
||||||
class Player {
|
|
||||||
has Str $name;
|
|
||||||
has Int $start_cash;
|
|
||||||
has Card @.cards;
|
|
||||||
has Char $position;
|
|
||||||
|
|
||||||
submethod BUILD (Str @strings) {
|
|
||||||
say "todo: implement Player.BUILD";
|
|
||||||
}#end Player.BUILD
|
|
||||||
|
|
||||||
our Player method find_players(@strings) {
|
|
||||||
#todo: i think this should be sub since its a class method not an instance method
|
|
||||||
say "todo: implement Player.find_players";
|
|
||||||
}
|
|
||||||
}#end class Player
|
|
||||||
|
|
||||||
class Line {
|
|
||||||
has Str $line;
|
|
||||||
has Bool $processed;
|
|
||||||
|
|
||||||
our protected submethod BUILD() {
|
|
||||||
say "todo: implement Line.BUILD?"
|
|
||||||
}#end Line.BUILD
|
|
||||||
|
|
||||||
our Line method recognise_and_parse(@strings) {
|
|
||||||
#todo: i think this should be sub since its a class method not an instance method
|
|
||||||
say "todo: implement Line.recognise_and_parse";
|
|
||||||
}#end Line.recognise_and_parse
|
|
||||||
}#end class Line
|
|
||||||
|
|
||||||
class ActionLine is Line {
|
|
||||||
has Player $player;
|
|
||||||
has Str $type;
|
|
||||||
has Int $amount;
|
|
||||||
has Bool $all_in;
|
|
||||||
has Int $action_no;
|
|
||||||
}#end class ActionLine
|
|
||||||
|
|
||||||
class WinLine is Line {
|
|
||||||
has Player $player;
|
|
||||||
has Int $amount;
|
|
||||||
}#end class WinLine
|
|
||||||
|
|
||||||
class RakeLine is Line {
|
|
||||||
has Int $amount;
|
|
||||||
}#end class RakeLine
|
|
||||||
|
|
||||||
class CardLine is Line {
|
|
||||||
has Bool $board_line;
|
|
||||||
has Player $player;
|
|
||||||
has Card @cards;
|
|
||||||
}#end class CardLine
|
|
||||||
|
|
||||||
#for useless lines
|
|
||||||
class CrapLine is Line {
|
|
||||||
has Str $type;
|
|
||||||
}#end class CrapLine
|
|
||||||
|
|
||||||
class Hand {
|
|
||||||
has Line @.lines;
|
|
||||||
#has Str @strings;
|
|
||||||
has Site $site;
|
|
||||||
has Str $currency;
|
|
||||||
|
|
||||||
has Str $type;
|
|
||||||
has Str $category;
|
|
||||||
has Str $limit_type;#todo: above ; missing causes error, but that doesnt list ; as a possibility
|
|
||||||
has Player @.players;
|
|
||||||
has Card @.board;
|
|
||||||
has Int $db_id;
|
|
||||||
|
|
||||||
submethod BUILD(Str @strings) {
|
|
||||||
Util.debug("running Hand.BUILD");
|
|
||||||
say "strings:",@strings;
|
|
||||||
#this contructor automatically parses the hand. call .store for storing
|
|
||||||
|
|
||||||
@.players=Player.find_players(@strings);
|
|
||||||
@.lines=Line.recognise_and_parse(@strings);
|
|
||||||
|
|
||||||
for @strings -> $line {
|
|
||||||
if class_of(line)==CardLine {
|
|
||||||
if line.board {
|
|
||||||
board=line.cards;
|
|
||||||
} else {
|
|
||||||
for player in players {
|
|
||||||
if line.player==player {
|
|
||||||
player.cards=line.cards;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}#end Hand.BUILD
|
|
||||||
|
|
||||||
our Bool method is_holdem(){
|
|
||||||
if category==("holdem"|"omahahi"|"omahahilo") {
|
|
||||||
return True;
|
|
||||||
} else {
|
|
||||||
return False;
|
|
||||||
}
|
|
||||||
}#end Hand.is_holdem
|
|
||||||
|
|
||||||
our Bool method is_stud(){
|
|
||||||
return not is_holdem();
|
|
||||||
}#end Hand.is_stud
|
|
||||||
|
|
||||||
our Bool method store($db) {
|
|
||||||
say "todo: Hand.store";
|
|
||||||
}#end Hand.store
|
|
||||||
}#end class Hand
|
|
||||||
|
|
||||||
class Importer {
|
|
||||||
#todo: be Thread?
|
|
||||||
submethod BUILD (Database $db, Str $filename) {
|
|
||||||
Util.debug("running Importer.BUILD");
|
|
||||||
if (not ($db.is_connected())) {
|
|
||||||
Util.fatal("not connected to DB");
|
|
||||||
}
|
|
||||||
|
|
||||||
my IO $?filehandle=$filename;
|
|
||||||
#for =$filehandle -> $line {say $line}
|
|
||||||
my Str @lines =$filehandle;
|
|
||||||
|
|
||||||
my Int $hand_start=0;
|
|
||||||
my Int $hand_end=0;
|
|
||||||
my Int $loopcount=0;
|
|
||||||
loop {#one loop of this per hand
|
|
||||||
$loopcount++;
|
|
||||||
say "loopcount", $loopcount;
|
|
||||||
my Int $current_line_index=$hand_end+1; #previous hand end is new hand start
|
|
||||||
for (my Int $i, $i<5, $i++) {#remove blank hands
|
|
||||||
if (@lines[$current_line_index].bytes) < 6 {
|
|
||||||
$current_line_index++;
|
|
||||||
} else {
|
|
||||||
$hand_start=$current_line_index;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
my Bool $continue=True; #todo: this is dumb, find out correct loop
|
|
||||||
while $continue {#loop through the lines to find end of hand
|
|
||||||
$current_line_index++;
|
|
||||||
if (@lines[$current_line_index].bytes) < 6 {
|
|
||||||
$hand_end=$current_line_index;
|
|
||||||
$continue=False;
|
|
||||||
}
|
|
||||||
}#end of find end of hand loop
|
|
||||||
my Str @handlines=@lines[$hand_start..$hand_end];
|
|
||||||
my Hand $hand .= new(:lines(@handlines));
|
|
||||||
$hand.store($db);
|
|
||||||
say "todo: record \$db_id";
|
|
||||||
say "todo: terminate on EOF";
|
|
||||||
}
|
|
||||||
}#end new Importer
|
|
||||||
}#end class Importer
|
|
||||||
|
|
|
@ -1,84 +0,0 @@
|
||||||
#!/usr/bin/pugs
|
|
||||||
|
|
||||||
#Copyright 2008 Steffen Jobbagy-Felso
|
|
||||||
#This program is free software: you can redistribute it and/or modify
|
|
||||||
#it under the terms of the GNU Affero General Public License as published by
|
|
||||||
#the Free Software Foundation, version 3 of the License.
|
|
||||||
#
|
|
||||||
#This program is distributed in the hope that it will be useful,
|
|
||||||
#but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
#GNU General Public License for more details.
|
|
||||||
#
|
|
||||||
#You should have received a copy of the GNU Affero General Public License
|
|
||||||
#along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
#In the "official" distribution you can find the license in
|
|
||||||
#agpl-3.0.txt in the docs folder of the package.
|
|
||||||
|
|
||||||
module LibFpdbShared;
|
|
||||||
use v6;
|
|
||||||
#use strict;
|
|
||||||
|
|
||||||
class Util {
|
|
||||||
method debug(Str $string) {
|
|
||||||
#todo: i think this should be sub since its a class method not an instance method
|
|
||||||
say "debug notice: ", $string;
|
|
||||||
}#end debug_msg
|
|
||||||
|
|
||||||
sub warn(Str $string) {
|
|
||||||
say "todo: Util.warning";
|
|
||||||
}#end warning
|
|
||||||
|
|
||||||
sub fatal(Str $string, Database $db) {
|
|
||||||
say "todo: Util.fatal_error";
|
|
||||||
}#end fatal_error
|
|
||||||
}#end class Util
|
|
||||||
|
|
||||||
class Database {
|
|
||||||
has Str $backend;
|
|
||||||
has Str $host;
|
|
||||||
has Str $name;
|
|
||||||
has Str $user;
|
|
||||||
my Str $password;
|
|
||||||
submethod BUILD (Str $!backend, Str $!host, Str $!name, Str $!user, Str $!password) {
|
|
||||||
Util.debug("running Database.BUILD");
|
|
||||||
self.connect();
|
|
||||||
}#end new Database
|
|
||||||
|
|
||||||
our method connect() {
|
|
||||||
say "todo: db.connect";
|
|
||||||
}#end connect
|
|
||||||
|
|
||||||
method disconnect() {
|
|
||||||
say "todo: db.disconnect";
|
|
||||||
}#end disconnect
|
|
||||||
|
|
||||||
method cancel_import() {
|
|
||||||
say "todo: db.cancel_import";
|
|
||||||
}#end cancel_import
|
|
||||||
|
|
||||||
my method drop_tables() {
|
|
||||||
#todo: make this one private
|
|
||||||
say "todo: db.drop_tables";
|
|
||||||
}#end drop_tables
|
|
||||||
|
|
||||||
method recreate_tables() {
|
|
||||||
say "todo: db.recreate_tables";
|
|
||||||
}#end recreate_tables
|
|
||||||
|
|
||||||
#returns the id of the insert
|
|
||||||
our Int method insert(Str $sql_command) {
|
|
||||||
#todo: is it a bug that i need the "our" above?
|
|
||||||
say "todo: db.insert";
|
|
||||||
return 0;
|
|
||||||
}#end insert
|
|
||||||
|
|
||||||
our Str method fetch(Str $sql_command) {
|
|
||||||
say "todo: db.fetch";
|
|
||||||
}#end fetch
|
|
||||||
|
|
||||||
our Bool method is_connected() {
|
|
||||||
say "todo: db.is_connected";
|
|
||||||
}#end
|
|
||||||
}#end class Database
|
|
||||||
|
|
|
@ -1,17 +0,0 @@
|
||||||
#!/usr/bin/pugs
|
|
||||||
|
|
||||||
#Copyright 2008 Steffen Jobbagy-Felso
|
|
||||||
#This program is free software: you can redistribute it and/or modify
|
|
||||||
#it under the terms of the GNU Affero General Public License as published by
|
|
||||||
#the Free Software Foundation, version 3 of the License.
|
|
||||||
#
|
|
||||||
#This program is distributed in the hope that it will be useful,
|
|
||||||
#but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
#GNU General Public License for more details.
|
|
||||||
#
|
|
||||||
#You should have received a copy of the GNU Affero General Public License
|
|
||||||
#along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
#In the "official" distribution you can find the license in
|
|
||||||
#agpl-3.0.txt in the docs folder of the package.
|
|
||||||
|
|
|
@ -1,29 +0,0 @@
|
||||||
#!/usr/bin/pugs
|
|
||||||
|
|
||||||
#Copyright 2008 Steffen Jobbagy-Felso
|
|
||||||
#This program is free software: you can redistribute it and/or modify
|
|
||||||
#it under the terms of the GNU Affero General Public License as published by
|
|
||||||
#the Free Software Foundation, version 3 of the License.
|
|
||||||
#
|
|
||||||
#This program is distributed in the hope that it will be useful,
|
|
||||||
#but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
#GNU General Public License for more details.
|
|
||||||
#
|
|
||||||
#You should have received a copy of the GNU Affero General Public License
|
|
||||||
#along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
#In the "official" distribution you can find the license in
|
|
||||||
#agpl-3.0.txt in the docs folder of the package.
|
|
||||||
|
|
||||||
use v6;
|
|
||||||
#use strict;
|
|
||||||
use LibFpdbImport;
|
|
||||||
use LibFpdbShared;
|
|
||||||
|
|
||||||
|
|
||||||
my Database $db .= new(:backend<MySQL InnoDB>, :host<localhost>, :database<fpdb>, :user<fpdb>, :password<myPW>);
|
|
||||||
#todo: below doesnt work
|
|
||||||
my Importer $imp .= new(:db($db), :filename<HH-LHE1.txt>);
|
|
||||||
#perlbug?: adding another named argument that isnt listed in the constructor gave very weird error.
|
|
||||||
say $imp;
|
|
||||||
|
|
|
@ -485,6 +485,9 @@ class Config:
|
||||||
try: db['db-server'] = self.supported_databases[name].db_server
|
try: db['db-server'] = self.supported_databases[name].db_server
|
||||||
except: pass
|
except: pass
|
||||||
|
|
||||||
|
try: db['db-type'] = self.supported_databases[name].db_type
|
||||||
|
except: pass
|
||||||
|
|
||||||
if string.lower(self.supported_databases[name].db_server) == 'mysql':
|
if string.lower(self.supported_databases[name].db_server) == 'mysql':
|
||||||
db['db-backend'] = 2
|
db['db-backend'] = 2
|
||||||
elif string.lower(self.supported_databases[name].db_server) == 'postgresql':
|
elif string.lower(self.supported_databases[name].db_server) == 'postgresql':
|
||||||
|
|
|
@ -27,6 +27,7 @@ Create and manage the database objects.
|
||||||
import sys
|
import sys
|
||||||
import traceback
|
import traceback
|
||||||
from datetime import datetime, date, time, timedelta
|
from datetime import datetime, date, time, timedelta
|
||||||
|
import string
|
||||||
|
|
||||||
# pyGTK modules
|
# pyGTK modules
|
||||||
|
|
||||||
|
@ -37,15 +38,21 @@ import Card
|
||||||
|
|
||||||
class Database:
|
class Database:
|
||||||
def __init__(self, c, db_name, game):
|
def __init__(self, c, db_name, game):
|
||||||
if c.supported_databases[db_name].db_server == 'postgresql':
|
db_params = c.get_db_parameters()
|
||||||
# psycopg2 database module for posgres via DB-API
|
if (string.lower(db_params['db-server']) == 'postgresql' or
|
||||||
import psycopg2
|
string.lower(db_params['db-server']) == 'postgres'):
|
||||||
|
import psycopg2 # posgres via DB-API
|
||||||
|
import psycopg2.extensions
|
||||||
|
psycopg2.extensions.register_type(psycopg2.extensions.UNICODE)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
self.connection = psycopg2.connect(host = c.supported_databases[db_name].db_ip,
|
if db_params['db-host'] == 'localhost' or db_params['db-host'] == '127.0.0.1':
|
||||||
user = c.supported_databases[db_name].db_user,
|
self.connection = psycopg2.connect(database = db_params['db-databaseName'])
|
||||||
password = c.supported_databases[db_name].db_pass,
|
else:
|
||||||
database = c.supported_databases[db_name].db_name)
|
self.connection = psycopg2.connect(host = db_params['db-host'],
|
||||||
|
user = db_params['db-user'],
|
||||||
|
password = db_params['db-password'],
|
||||||
|
database = db_params['db-databaseName'])
|
||||||
except:
|
except:
|
||||||
print "Error opening database connection %s. See error log file." % (file)
|
print "Error opening database connection %s. See error log file." % (file)
|
||||||
traceback.print_exc(file=sys.stderr)
|
traceback.print_exc(file=sys.stderr)
|
||||||
|
@ -53,14 +60,13 @@ class Database:
|
||||||
sys.stdin.readline()
|
sys.stdin.readline()
|
||||||
sys.exit()
|
sys.exit()
|
||||||
|
|
||||||
elif c.supported_databases[db_name].db_server == 'mysql':
|
elif string.lower(db_params['db-server']) == 'mysql':
|
||||||
# mysql bindings
|
import MySQLdb # mysql bindings
|
||||||
import MySQLdb
|
|
||||||
try:
|
try:
|
||||||
self.connection = MySQLdb.connect(host = c.supported_databases[db_name].db_ip,
|
self.connection = MySQLdb.connect(host = db_params['db-host'],
|
||||||
user = c.supported_databases[db_name].db_user,
|
user = db_params['db-user'],
|
||||||
passwd = c.supported_databases[db_name].db_pass,
|
passwd = db_params['db-password'],
|
||||||
db = c.supported_databases[db_name].db_name)
|
db = db_params['db-databaseName'])
|
||||||
cur_iso = self.connection.cursor()
|
cur_iso = self.connection.cursor()
|
||||||
cur_iso.execute('SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED')
|
cur_iso.execute('SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED')
|
||||||
cur_iso.close()
|
cur_iso.close()
|
||||||
|
@ -78,9 +84,8 @@ class Database:
|
||||||
print "press enter to continue"
|
print "press enter to continue"
|
||||||
sys.exit()
|
sys.exit()
|
||||||
|
|
||||||
self.db_server = c.supported_databases[db_name].db_server
|
self.type = db_params['db-type']
|
||||||
self.type = c.supported_databases[db_name].db_type
|
self.sql = SQL.Sql(game = game, type = self.type)
|
||||||
self.sql = SQL.Sql(game = game, type = self.type, db_server = self.db_server)
|
|
||||||
self.connection.rollback()
|
self.connection.rollback()
|
||||||
|
|
||||||
# To add to config:
|
# To add to config:
|
||||||
|
@ -193,20 +198,20 @@ class Database:
|
||||||
# cards += "xx"
|
# cards += "xx"
|
||||||
# else:
|
# else:
|
||||||
# cards += ranks[d['card' + str(i) + 'Value']] + d['card' +str(i) + 'Suit']
|
# cards += ranks[d['card' + str(i) + 'Value']] + d['card' +str(i) + 'Suit']
|
||||||
cv = "card%dValue" % i
|
cv = "card%dvalue" % i
|
||||||
if cv not in d or d[cv] == None:
|
if cv not in d or d[cv] == None:
|
||||||
break
|
break
|
||||||
elif d[cv] == 0:
|
elif d[cv] == 0:
|
||||||
cards += "xx"
|
cards += "xx"
|
||||||
else:
|
else:
|
||||||
cs = "card%dSuit" % i
|
cs = "card%dsuit" % i
|
||||||
cards = "%s%s%s" % (cards, ranks[d[cv]], d[cs])
|
cards = "%s%s%s" % (cards, ranks[d[cv]], d[cs])
|
||||||
return cards
|
return cards
|
||||||
|
|
||||||
def get_action_from_hand(self, hand_no):
|
def get_action_from_hand(self, hand_no):
|
||||||
action = [ [], [], [], [], [] ]
|
action = [ [], [], [], [], [] ]
|
||||||
c = self.connection.cursor()
|
c = self.connection.cursor()
|
||||||
c.execute(self.sql.query['get_action_from_hand'], (hand_no))
|
c.execute(self.sql.query['get_action_from_hand'], (hand_no, ))
|
||||||
for row in c.fetchall():
|
for row in c.fetchall():
|
||||||
street = row[0]
|
street = row[0]
|
||||||
act = row[1:]
|
act = row[1:]
|
||||||
|
@ -217,7 +222,7 @@ class Database:
|
||||||
"""Returns a hash of winners:amount won, given a hand number."""
|
"""Returns a hash of winners:amount won, given a hand number."""
|
||||||
winners = {}
|
winners = {}
|
||||||
c = self.connection.cursor()
|
c = self.connection.cursor()
|
||||||
c.execute(self.sql.query['get_winners_from_hand'], (hand))
|
c.execute(self.sql.query['get_winners_from_hand'], (hand, ))
|
||||||
for row in c.fetchall():
|
for row in c.fetchall():
|
||||||
winners[row[0]] = row[1]
|
winners[row[0]] = row[1]
|
||||||
return winners
|
return winners
|
||||||
|
@ -299,7 +304,6 @@ class Database:
|
||||||
return stat_dict
|
return stat_dict
|
||||||
|
|
||||||
def get_player_id(self, config, site, player_name):
|
def get_player_id(self, config, site, player_name):
|
||||||
print "site = %s, player name = %s" % (site, player_name)
|
|
||||||
c = self.connection.cursor()
|
c = self.connection.cursor()
|
||||||
c.execute(self.sql.query['get_player_id'], {'player': player_name, 'site': site})
|
c.execute(self.sql.query['get_player_id'], {'player': player_name, 'site': site})
|
||||||
row = c.fetchone()
|
row = c.fetchone()
|
||||||
|
@ -334,7 +338,7 @@ if __name__=="__main__":
|
||||||
# for p in stat_dict.keys():
|
# for p in stat_dict.keys():
|
||||||
# print p, " ", stat_dict[p]
|
# print p, " ", stat_dict[p]
|
||||||
|
|
||||||
print "cards =", db_connection.get_cards(73525)
|
print "cards =", db_connection.get_cards(u'1')
|
||||||
db_connection.close_connection
|
db_connection.close_connection
|
||||||
|
|
||||||
print "press enter to continue"
|
print "press enter to continue"
|
||||||
|
|
|
@ -158,7 +158,7 @@ or None if we fail to get the info """
|
||||||
# 2008/11/10 3:58:52 ET
|
# 2008/11/10 3:58:52 ET
|
||||||
#TODO: Do conversion from GMT to ET
|
#TODO: Do conversion from GMT to ET
|
||||||
#TODO: Need some date functions to convert to different timezones (Date::Manip for perl rocked for this)
|
#TODO: Need some date functions to convert to different timezones (Date::Manip for perl rocked for this)
|
||||||
hand.starttime = time.strptime(m.group('DATETIME'), "%Y/%m/%d - %H:%M:%S")
|
hand.starttime = datetime.datetime.strptime(m.group('DATETIME'), "%Y/%m/%d - %H:%M:%S")
|
||||||
return
|
return
|
||||||
|
|
||||||
def readPlayerStacks(self, hand):
|
def readPlayerStacks(self, hand):
|
||||||
|
|
|
@ -44,6 +44,7 @@ class Filters(threading.Thread):
|
||||||
self.games = {}
|
self.games = {}
|
||||||
self.limits = {}
|
self.limits = {}
|
||||||
self.seats = {}
|
self.seats = {}
|
||||||
|
self.groups = {}
|
||||||
self.siteid = {}
|
self.siteid = {}
|
||||||
self.heroes = {}
|
self.heroes = {}
|
||||||
self.boxes = {}
|
self.boxes = {}
|
||||||
|
@ -52,6 +53,7 @@ class Filters(threading.Thread):
|
||||||
self.filterText = {'limitsall':'All', 'limitsnone':'None', 'limitsshow':'Show _Limits'
|
self.filterText = {'limitsall':'All', 'limitsnone':'None', 'limitsshow':'Show _Limits'
|
||||||
,'seatsbetween':'Between:', 'seatsand':'And:', 'seatsshow':'Show Number of _Players'
|
,'seatsbetween':'Between:', 'seatsand':'And:', 'seatsshow':'Show Number of _Players'
|
||||||
,'limitstitle':'Limits:', 'seatstitle':'Number of Players:'
|
,'limitstitle':'Limits:', 'seatstitle':'Number of Players:'
|
||||||
|
,'groupstitle':'Grouping:', 'posnshow':'Show Position Stats:'
|
||||||
}
|
}
|
||||||
|
|
||||||
# For use in date ranges.
|
# For use in date ranges.
|
||||||
|
@ -109,6 +111,15 @@ class Filters(threading.Thread):
|
||||||
self.fillSeatsFrame(vbox, self.display)
|
self.fillSeatsFrame(vbox, self.display)
|
||||||
seatsFrame.add(vbox)
|
seatsFrame.add(vbox)
|
||||||
|
|
||||||
|
# Groups
|
||||||
|
groupsFrame = gtk.Frame()
|
||||||
|
groupsFrame.show()
|
||||||
|
vbox = gtk.VBox(False, 0)
|
||||||
|
self.sbGroups = {}
|
||||||
|
|
||||||
|
self.fillGroupsFrame(vbox, self.display)
|
||||||
|
groupsFrame.add(vbox)
|
||||||
|
|
||||||
# Date
|
# Date
|
||||||
dateFrame = gtk.Frame("Date:")
|
dateFrame = gtk.Frame("Date:")
|
||||||
dateFrame.set_label_align(0.0, 0.0)
|
dateFrame.set_label_align(0.0, 0.0)
|
||||||
|
@ -131,6 +142,7 @@ class Filters(threading.Thread):
|
||||||
self.mainVBox.add(gamesFrame)
|
self.mainVBox.add(gamesFrame)
|
||||||
self.mainVBox.add(limitsFrame)
|
self.mainVBox.add(limitsFrame)
|
||||||
self.mainVBox.add(seatsFrame)
|
self.mainVBox.add(seatsFrame)
|
||||||
|
self.mainVBox.add(groupsFrame)
|
||||||
self.mainVBox.add(dateFrame)
|
self.mainVBox.add(dateFrame)
|
||||||
self.mainVBox.add(self.Button1)
|
self.mainVBox.add(self.Button1)
|
||||||
self.mainVBox.add(self.Button2)
|
self.mainVBox.add(self.Button2)
|
||||||
|
@ -148,6 +160,8 @@ class Filters(threading.Thread):
|
||||||
limitsFrame.hide()
|
limitsFrame.hide()
|
||||||
if "Seats" not in self.display or self.display["Seats"] == False:
|
if "Seats" not in self.display or self.display["Seats"] == False:
|
||||||
seatsFrame.hide()
|
seatsFrame.hide()
|
||||||
|
if "Groups" not in self.display or self.display["Groups"] == False:
|
||||||
|
groupsFrame.hide()
|
||||||
if "Dates" not in self.display or self.display["Dates"] == False:
|
if "Dates" not in self.display or self.display["Dates"] == False:
|
||||||
dateFrame.hide()
|
dateFrame.hide()
|
||||||
if "Button1" not in self.display or self.display["Button1"] == False:
|
if "Button1" not in self.display or self.display["Button1"] == False:
|
||||||
|
@ -183,6 +197,9 @@ class Filters(threading.Thread):
|
||||||
self.seats['to'] = self.sbSeats['to'].get_value_as_int()
|
self.seats['to'] = self.sbSeats['to'].get_value_as_int()
|
||||||
return self.seats
|
return self.seats
|
||||||
|
|
||||||
|
def getGroups(self):
|
||||||
|
return self.groups
|
||||||
|
|
||||||
def getDates(self):
|
def getDates(self):
|
||||||
return self.__get_dates()
|
return self.__get_dates()
|
||||||
|
|
||||||
|
@ -274,6 +291,11 @@ class Filters(threading.Thread):
|
||||||
self.seats[seat] = w.get_active()
|
self.seats[seat] = w.get_active()
|
||||||
print "self.seats[%s] set to %s" %(seat, self.seats[seat])
|
print "self.seats[%s] set to %s" %(seat, self.seats[seat])
|
||||||
|
|
||||||
|
def __set_group_select(self, w, group):
|
||||||
|
#print "__set_seat_select: seat =", seat, "active =", w.get_active()
|
||||||
|
self.groups[group] = w.get_active()
|
||||||
|
print "self.groups[%s] set to %s" %(group, self.groups[group])
|
||||||
|
|
||||||
def fillPlayerFrame(self, vbox):
|
def fillPlayerFrame(self, vbox):
|
||||||
for site in self.conf.get_supported_sites():
|
for site in self.conf.get_supported_sites():
|
||||||
pathHBox = gtk.HBox(False, 0)
|
pathHBox = gtk.HBox(False, 0)
|
||||||
|
@ -389,10 +411,33 @@ class Filters(threading.Thread):
|
||||||
self.sbSeats['show'] = cb
|
self.sbSeats['show'] = cb
|
||||||
self.seats['show'] = False
|
self.seats['show'] = False
|
||||||
|
|
||||||
|
|
||||||
self.sbSeats['from'] = sb1
|
self.sbSeats['from'] = sb1
|
||||||
self.sbSeats['to'] = sb2
|
self.sbSeats['to'] = sb2
|
||||||
|
|
||||||
|
def fillGroupsFrame(self, vbox, display):
|
||||||
|
hbox = gtk.HBox(False, 0)
|
||||||
|
vbox.pack_start(hbox, False, False, 0)
|
||||||
|
lbl_title = gtk.Label(self.filterText['groupstitle'])
|
||||||
|
lbl_title.set_alignment(xalign=0.0, yalign=0.5)
|
||||||
|
hbox.pack_start(lbl_title, expand=True, padding=3)
|
||||||
|
showb = gtk.Button(label="hide", stock=None, use_underline=True)
|
||||||
|
showb.set_alignment(xalign=1.0, yalign=0.5)
|
||||||
|
showb.connect('clicked', self.__toggle_box, 'groups')
|
||||||
|
hbox.pack_start(showb, expand=False, padding=1)
|
||||||
|
|
||||||
|
vbox1 = gtk.VBox(False, 0)
|
||||||
|
vbox.pack_start(vbox1, False, False, 0)
|
||||||
|
self.boxes['groups'] = vbox1
|
||||||
|
|
||||||
|
hbox = gtk.HBox(False, 0)
|
||||||
|
vbox1.pack_start(hbox, False, True, 0)
|
||||||
|
|
||||||
|
cb = gtk.CheckButton(self.filterText['posnshow'])
|
||||||
|
cb.connect('clicked', self.__set_group_select, 'posn')
|
||||||
|
hbox.pack_start(cb, False, False, 0)
|
||||||
|
self.sbGroups['posn'] = cb
|
||||||
|
self.groups['posn'] = False
|
||||||
|
|
||||||
def fillCardsFrame(self, vbox):
|
def fillCardsFrame(self, vbox):
|
||||||
hbox1 = gtk.HBox(True,0)
|
hbox1 = gtk.HBox(True,0)
|
||||||
hbox1.show()
|
hbox1.show()
|
||||||
|
|
|
@ -910,6 +910,7 @@ class FpdbSQLQueries:
|
||||||
,min(gt.bigBlind) AS minbigblind
|
,min(gt.bigBlind) AS minbigblind
|
||||||
,max(gt.bigBlind) AS maxbigblind
|
,max(gt.bigBlind) AS maxbigblind
|
||||||
/*,<hcgametypeId> AS gtid*/
|
/*,<hcgametypeId> AS gtid*/
|
||||||
|
,<position> AS plposition
|
||||||
,count(1) AS n
|
,count(1) AS n
|
||||||
,100.0*sum(cast(hp.street0VPI as <signed>integer))/count(1) AS vpip
|
,100.0*sum(cast(hp.street0VPI as <signed>integer))/count(1) AS vpip
|
||||||
,100.0*sum(cast(hp.street0Aggr as <signed>integer))/count(1) AS pfr
|
,100.0*sum(cast(hp.street0Aggr as <signed>integer))/count(1) AS pfr
|
||||||
|
@ -963,12 +964,17 @@ class FpdbSQLQueries:
|
||||||
,gt.base
|
,gt.base
|
||||||
,gt.category
|
,gt.category
|
||||||
<groupbyseats>
|
<groupbyseats>
|
||||||
|
,plposition
|
||||||
,upper(gt.limitType)
|
,upper(gt.limitType)
|
||||||
,s.name
|
,s.name
|
||||||
order by hp.playerId
|
order by hp.playerId
|
||||||
,gt.base
|
,gt.base
|
||||||
,gt.category
|
,gt.category
|
||||||
<orderbyseats>
|
<orderbyseats>
|
||||||
|
,case <position> when 'B' then 'B'
|
||||||
|
when 'S' then 'S'
|
||||||
|
else concat('Z', <position>)
|
||||||
|
end
|
||||||
<orderbyhgameTypeId>
|
<orderbyhgameTypeId>
|
||||||
,maxbigblind desc
|
,maxbigblind desc
|
||||||
,upper(gt.limitType)
|
,upper(gt.limitType)
|
||||||
|
@ -984,6 +990,7 @@ class FpdbSQLQueries:
|
||||||
,min(gt.bigBlind) AS minbigblind
|
,min(gt.bigBlind) AS minbigblind
|
||||||
,max(gt.bigBlind) AS maxbigblind
|
,max(gt.bigBlind) AS maxbigblind
|
||||||
/*,<hcgametypeId> AS gtid*/
|
/*,<hcgametypeId> AS gtid*/
|
||||||
|
,<position> AS plposition
|
||||||
,count(1) AS n
|
,count(1) AS n
|
||||||
,100.0*sum(cast(hp.street0VPI as <signed>integer))/count(1) AS vpip
|
,100.0*sum(cast(hp.street0VPI as <signed>integer))/count(1) AS vpip
|
||||||
,100.0*sum(cast(hp.street0Aggr as <signed>integer))/count(1) AS pfr
|
,100.0*sum(cast(hp.street0Aggr as <signed>integer))/count(1) AS pfr
|
||||||
|
@ -1037,12 +1044,17 @@ class FpdbSQLQueries:
|
||||||
,gt.base
|
,gt.base
|
||||||
,gt.category
|
,gt.category
|
||||||
<groupbyseats>
|
<groupbyseats>
|
||||||
|
,plposition
|
||||||
,upper(gt.limitType)
|
,upper(gt.limitType)
|
||||||
,s.name
|
,s.name
|
||||||
order by hp.playerId
|
order by hp.playerId
|
||||||
,gt.base
|
,gt.base
|
||||||
,gt.category
|
,gt.category
|
||||||
<orderbyseats>
|
<orderbyseats>
|
||||||
|
,case <position> when 'B' then 'B'
|
||||||
|
when 'S' then 'S'
|
||||||
|
else 'Z'||<position>
|
||||||
|
end
|
||||||
<orderbyhgameTypeId>
|
<orderbyhgameTypeId>
|
||||||
,maxbigblind desc
|
,maxbigblind desc
|
||||||
,upper(gt.limitType)
|
,upper(gt.limitType)
|
||||||
|
|
|
@ -124,7 +124,7 @@ follow : whether to tail -f the input"""
|
||||||
|
|
||||||
hand.handid = m.group('HID')
|
hand.handid = m.group('HID')
|
||||||
hand.tablename = m.group('TABLE')
|
hand.tablename = m.group('TABLE')
|
||||||
hand.starttime = time.strptime(m.group('DATETIME'), "%H:%M:%S ET - %Y/%m/%d")
|
hand.starttime = datetime.datetime.strptime(m.group('DATETIME'), "%H:%M:%S ET - %Y/%m/%d")
|
||||||
hand.maxseats = 8 # assume 8-max until we see otherwise
|
hand.maxseats = 8 # assume 8-max until we see otherwise
|
||||||
if m.group('TABLEATTRIBUTES'):
|
if m.group('TABLEATTRIBUTES'):
|
||||||
m2 = re.search("(deep )?(\d+)( max)?", m.group('TABLEATTRIBUTES'))
|
m2 = re.search("(deep )?(\d+)( max)?", m.group('TABLEATTRIBUTES'))
|
||||||
|
|
|
@ -76,27 +76,28 @@ class GuiPlayerStats (threading.Thread):
|
||||||
# ToDo: create popup to adjust column config
|
# ToDo: create popup to adjust column config
|
||||||
# columns to display, keys match column name returned by sql, values in tuple are:
|
# columns to display, keys match column name returned by sql, values in tuple are:
|
||||||
# is column displayed, column heading, xalignment, formatting
|
# is column displayed, column heading, xalignment, formatting
|
||||||
self.columns = [ ("game", True, "Game", 0.0, "%s")
|
self.columns = [ ["game", True, "Game", 0.0, "%s"]
|
||||||
, ("hand", False, "Hand", 0.0, "%s") # true not allowed for this line
|
, ["hand", False, "Hand", 0.0, "%s"] # true not allowed for this line
|
||||||
, ("n", True, "Hds", 1.0, "%d")
|
, ["plposition", False, "Posn", 1.0, "%s"] # true not allowed for this line (set in code)
|
||||||
, ("avgseats", True, "Seats", 1.0, "%3.1f")
|
, ["n", True, "Hds", 1.0, "%d"]
|
||||||
, ("vpip", True, "VPIP", 1.0, "%3.1f")
|
, ["avgseats", True, "Seats", 1.0, "%3.1f"]
|
||||||
, ("pfr", True, "PFR", 1.0, "%3.1f")
|
, ["vpip", True, "VPIP", 1.0, "%3.1f"]
|
||||||
, ("pf3", True, "PF3", 1.0, "%3.1f")
|
, ["pfr", True, "PFR", 1.0, "%3.1f"]
|
||||||
, ("steals", True, "Steals", 1.0, "%3.1f")
|
, ["pf3", True, "PF3", 1.0, "%3.1f"]
|
||||||
, ("saw_f", True, "Saw_F", 1.0, "%3.1f")
|
, ["steals", True, "Steals", 1.0, "%3.1f"]
|
||||||
, ("sawsd", True, "SawSD", 1.0, "%3.1f")
|
, ["saw_f", True, "Saw_F", 1.0, "%3.1f"]
|
||||||
, ("wtsdwsf", True, "WtSDwsF", 1.0, "%3.1f")
|
, ["sawsd", True, "SawSD", 1.0, "%3.1f"]
|
||||||
, ("wmsd", True, "W$SD", 1.0, "%3.1f")
|
, ["wtsdwsf", True, "WtSDwsF", 1.0, "%3.1f"]
|
||||||
, ("flafq", True, "FlAFq", 1.0, "%3.1f")
|
, ["wmsd", True, "W$SD", 1.0, "%3.1f"]
|
||||||
, ("tuafq", True, "TuAFq", 1.0, "%3.1f")
|
, ["flafq", True, "FlAFq", 1.0, "%3.1f"]
|
||||||
, ("rvafq", True, "RvAFq", 1.0, "%3.1f")
|
, ["tuafq", True, "TuAFq", 1.0, "%3.1f"]
|
||||||
, ("pofafq", False, "PoFAFq", 1.0, "%3.1f")
|
, ["rvafq", True, "RvAFq", 1.0, "%3.1f"]
|
||||||
, ("net", True, "Net($)", 1.0, "%6.2f")
|
, ["pofafq", False, "PoFAFq", 1.0, "%3.1f"]
|
||||||
, ("bbper100", True, "bb/100", 1.0, "%4.2f")
|
, ["net", True, "Net($)", 1.0, "%6.2f"]
|
||||||
, ("rake", True, "Rake($)", 1.0, "%6.2f")
|
, ["bbper100", True, "bb/100", 1.0, "%4.2f"]
|
||||||
, ("bb100xr", True, "bbxr/100", 1.0, "%4.2f")
|
, ["rake", True, "Rake($)", 1.0, "%6.2f"]
|
||||||
, ("variance", True, "Variance", 1.0, "%5.2f")
|
, ["bb100xr", True, "bbxr/100", 1.0, "%4.2f"]
|
||||||
|
, ["variance", True, "Variance", 1.0, "%5.2f"]
|
||||||
]
|
]
|
||||||
|
|
||||||
# Detail filters: This holds the data used in the popup window, extra values are
|
# Detail filters: This holds the data used in the popup window, extra values are
|
||||||
|
@ -136,7 +137,7 @@ class GuiPlayerStats (threading.Thread):
|
||||||
self.main_hbox.pack_start(self.stats_frame, expand=True, fill=True)
|
self.main_hbox.pack_start(self.stats_frame, expand=True, fill=True)
|
||||||
|
|
||||||
# make sure Hand column is not displayed
|
# make sure Hand column is not displayed
|
||||||
[x for x in self.columns if x[0] == 'hand'][0][1] == False
|
[x for x in self.columns if x[0] == 'hand'][0][1] = False
|
||||||
|
|
||||||
def get_vbox(self):
|
def get_vbox(self):
|
||||||
"""returns the vbox of this thread"""
|
"""returns the vbox of this thread"""
|
||||||
|
@ -156,6 +157,7 @@ class GuiPlayerStats (threading.Thread):
|
||||||
siteids = self.filters.getSiteIds()
|
siteids = self.filters.getSiteIds()
|
||||||
limits = self.filters.getLimits()
|
limits = self.filters.getLimits()
|
||||||
seats = self.filters.getSeats()
|
seats = self.filters.getSeats()
|
||||||
|
groups = self.filters.getGroups()
|
||||||
dates = self.filters.getDates()
|
dates = self.filters.getDates()
|
||||||
sitenos = []
|
sitenos = []
|
||||||
playerids = []
|
playerids = []
|
||||||
|
@ -180,16 +182,16 @@ class GuiPlayerStats (threading.Thread):
|
||||||
print "No limits found"
|
print "No limits found"
|
||||||
return
|
return
|
||||||
|
|
||||||
self.createStatsTable(vbox, playerids, sitenos, limits, seats, dates)
|
self.createStatsTable(vbox, playerids, sitenos, limits, seats, groups, dates)
|
||||||
|
|
||||||
def createStatsTable(self, vbox, playerids, sitenos, limits, seats, dates):
|
def createStatsTable(self, vbox, playerids, sitenos, limits, seats, groups, dates):
|
||||||
starttime = time()
|
starttime = time()
|
||||||
|
|
||||||
# Display summary table at top of page
|
# Display summary table at top of page
|
||||||
# 3rd parameter passes extra flags, currently includes:
|
# 3rd parameter passes extra flags, currently includes:
|
||||||
# holecards - whether to display card breakdown (True/False)
|
# holecards - whether to display card breakdown (True/False)
|
||||||
flags = [False]
|
flags = [False]
|
||||||
self.addTable(vbox, 'playerDetailedStats', flags, playerids, sitenos, limits, seats, dates)
|
self.addTable(vbox, 'playerDetailedStats', flags, playerids, sitenos, limits, seats, groups, dates)
|
||||||
|
|
||||||
# Separator
|
# Separator
|
||||||
sep = gtk.HSeparator()
|
sep = gtk.HSeparator()
|
||||||
|
@ -212,13 +214,13 @@ class GuiPlayerStats (threading.Thread):
|
||||||
|
|
||||||
# Detailed table
|
# Detailed table
|
||||||
flags = [True]
|
flags = [True]
|
||||||
self.addTable(vbox1, 'playerDetailedStats', flags, playerids, sitenos, limits, seats, dates)
|
self.addTable(vbox1, 'playerDetailedStats', flags, playerids, sitenos, limits, seats, groups, dates)
|
||||||
|
|
||||||
self.db.db.commit()
|
self.db.db.commit()
|
||||||
print "Stats page displayed in %4.2f seconds" % (time() - starttime)
|
print "Stats page displayed in %4.2f seconds" % (time() - starttime)
|
||||||
#end def fillStatsFrame(self, vbox):
|
#end def fillStatsFrame(self, vbox):
|
||||||
|
|
||||||
def addTable(self, vbox, query, flags, playerids, sitenos, limits, seats, dates):
|
def addTable(self, vbox, query, flags, playerids, sitenos, limits, seats, groups, dates):
|
||||||
row = 0
|
row = 0
|
||||||
sqlrow = 0
|
sqlrow = 0
|
||||||
colalias,colshow,colheading,colxalign,colformat = 0,1,2,3,4
|
colalias,colshow,colheading,colxalign,colformat = 0,1,2,3,4
|
||||||
|
@ -231,7 +233,7 @@ class GuiPlayerStats (threading.Thread):
|
||||||
self.stats_table.show()
|
self.stats_table.show()
|
||||||
|
|
||||||
tmp = self.sql.query[query]
|
tmp = self.sql.query[query]
|
||||||
tmp = self.refineQuery(tmp, flags, playerids, sitenos, limits, seats, dates)
|
tmp = self.refineQuery(tmp, flags, playerids, sitenos, limits, seats, groups, dates)
|
||||||
self.cursor.execute(tmp)
|
self.cursor.execute(tmp)
|
||||||
result = self.cursor.fetchall()
|
result = self.cursor.fetchall()
|
||||||
colnames = [desc[0].lower() for desc in self.cursor.description]
|
colnames = [desc[0].lower() for desc in self.cursor.description]
|
||||||
|
@ -245,6 +247,8 @@ class GuiPlayerStats (threading.Thread):
|
||||||
view.set_grid_lines(gtk.TREE_VIEW_GRID_LINES_BOTH)
|
view.set_grid_lines(gtk.TREE_VIEW_GRID_LINES_BOTH)
|
||||||
vbox.pack_start(view, expand=False, padding=3)
|
vbox.pack_start(view, expand=False, padding=3)
|
||||||
textcell = gtk.CellRendererText()
|
textcell = gtk.CellRendererText()
|
||||||
|
textcell50 = gtk.CellRendererText()
|
||||||
|
textcell50.set_property('xalign', 0.5)
|
||||||
numcell = gtk.CellRendererText()
|
numcell = gtk.CellRendererText()
|
||||||
numcell.set_property('xalign', 1.0)
|
numcell.set_property('xalign', 1.0)
|
||||||
listcols = []
|
listcols = []
|
||||||
|
@ -258,17 +262,18 @@ class GuiPlayerStats (threading.Thread):
|
||||||
listcols.append(gtk.TreeViewColumn(s))
|
listcols.append(gtk.TreeViewColumn(s))
|
||||||
view.append_column(listcols[col])
|
view.append_column(listcols[col])
|
||||||
if column[colformat] == '%s':
|
if column[colformat] == '%s':
|
||||||
if col == 1 and holecards:
|
if column[colxalign] == 0.0:
|
||||||
listcols[col].pack_start(textcell, expand=True)
|
|
||||||
else:
|
|
||||||
listcols[col].pack_start(textcell, expand=True)
|
listcols[col].pack_start(textcell, expand=True)
|
||||||
listcols[col].add_attribute(textcell, 'text', col)
|
listcols[col].add_attribute(textcell, 'text', col)
|
||||||
|
else:
|
||||||
|
listcols[col].pack_start(textcell50, expand=True)
|
||||||
|
listcols[col].add_attribute(textcell50, 'text', col)
|
||||||
listcols[col].set_expand(True)
|
listcols[col].set_expand(True)
|
||||||
else:
|
else:
|
||||||
listcols[col].pack_start(numcell, expand=True)
|
listcols[col].pack_start(numcell, expand=True)
|
||||||
listcols[col].add_attribute(numcell, 'text', col)
|
listcols[col].add_attribute(numcell, 'text', col)
|
||||||
listcols[col].set_alignment(1.0)
|
|
||||||
listcols[col].set_expand(True)
|
listcols[col].set_expand(True)
|
||||||
|
#listcols[col].set_alignment(column[colxalign]) # no effect?
|
||||||
|
|
||||||
rows = len(result) # +1 for title row
|
rows = len(result) # +1 for title row
|
||||||
|
|
||||||
|
@ -281,6 +286,11 @@ class GuiPlayerStats (threading.Thread):
|
||||||
for col,column in enumerate(cols_to_show):
|
for col,column in enumerate(cols_to_show):
|
||||||
if column[colalias] in colnames:
|
if column[colalias] in colnames:
|
||||||
value = result[sqlrow][colnames.index(column[colalias])]
|
value = result[sqlrow][colnames.index(column[colalias])]
|
||||||
|
if column[colalias] == 'plposition':
|
||||||
|
if value == 'B':
|
||||||
|
value = 'BB'
|
||||||
|
if value == 'S':
|
||||||
|
value = 'SB'
|
||||||
else:
|
else:
|
||||||
if column[colalias] == 'game':
|
if column[colalias] == 'game':
|
||||||
if holecards:
|
if holecards:
|
||||||
|
@ -313,7 +323,7 @@ class GuiPlayerStats (threading.Thread):
|
||||||
|
|
||||||
#end def addTable(self, query, vars, playerids, sitenos, limits, seats):
|
#end def addTable(self, query, vars, playerids, sitenos, limits, seats):
|
||||||
|
|
||||||
def refineQuery(self, query, flags, playerids, sitenos, limits, seats, dates):
|
def refineQuery(self, query, flags, playerids, sitenos, limits, seats, groups, dates):
|
||||||
if not flags: holecards = False
|
if not flags: holecards = False
|
||||||
else: holecards = flags[0]
|
else: holecards = flags[0]
|
||||||
|
|
||||||
|
@ -376,6 +386,16 @@ class GuiPlayerStats (threading.Thread):
|
||||||
# Filter on dates
|
# Filter on dates
|
||||||
query = query.replace("<datestest>", " between '" + dates[0] + "' and '" + dates[1] + "'")
|
query = query.replace("<datestest>", " between '" + dates[0] + "' and '" + dates[1] + "'")
|
||||||
|
|
||||||
|
# Group by position?
|
||||||
|
if groups['posn']:
|
||||||
|
query = query.replace("<position>", 'hp.position')
|
||||||
|
# set flag in self.columns to show posn column
|
||||||
|
[x for x in self.columns if x[0] == 'plposition'][0][1] = True
|
||||||
|
else:
|
||||||
|
query = query.replace("<position>", "'1'")
|
||||||
|
# unset flag in self.columns to hide posn column
|
||||||
|
[x for x in self.columns if x[0] == 'plposition'][0][1] = False
|
||||||
|
|
||||||
#print "query =\n", query
|
#print "query =\n", query
|
||||||
return(query)
|
return(query)
|
||||||
#end def refineQuery(self, query, playerids, sitenos, limits):
|
#end def refineQuery(self, query, playerids, sitenos, limits):
|
||||||
|
|
|
@ -148,7 +148,6 @@ class HUD_main(object):
|
||||||
if new_hand_id == "": # blank line means quit
|
if new_hand_id == "": # blank line means quit
|
||||||
self.destroy()
|
self.destroy()
|
||||||
break # this thread is not always killed immediately with gtk.main_quit()
|
break # this thread is not always killed immediately with gtk.main_quit()
|
||||||
|
|
||||||
# get basic info about the new hand from the db
|
# get basic info about the new hand from the db
|
||||||
# if there is a db error, complain, skip hand, and proceed
|
# if there is a db error, complain, skip hand, and proceed
|
||||||
try:
|
try:
|
||||||
|
|
|
@ -92,16 +92,14 @@ db: a connected fpdb_db object"""
|
||||||
# HandsActions - all actions for all players for all streets - self.actions
|
# HandsActions - all actions for all players for all streets - self.actions
|
||||||
# BoardCards - Skip - no longer necessary?
|
# BoardCards - Skip - no longer necessary?
|
||||||
# Hands - Summary information of hand indexed by handId - gameinfo
|
# Hands - Summary information of hand indexed by handId - gameinfo
|
||||||
# self.tablename = tableName
|
#hh['siteHandNo'] = self.handid
|
||||||
# self.handid = siteHandNo
|
|
||||||
# gametypeId SMALLINT UNSIGNED NOT NULL, FOREIGN KEY (gametypeId) REFERENCES Gametypes(id),
|
# gametypeId SMALLINT UNSIGNED NOT NULL, FOREIGN KEY (gametypeId) REFERENCES Gametypes(id),
|
||||||
#
|
#
|
||||||
# self.starttime = handStart
|
#hh['handStart'] = self.starttime
|
||||||
# importTime DATETIME NOT NULL,
|
|
||||||
#
|
|
||||||
# seats TINYINT NOT NULL,
|
# seats TINYINT NOT NULL,
|
||||||
#
|
#
|
||||||
# self.maxseats = maxSeats
|
#hh['tableName'] = self.tablenam
|
||||||
|
#hh['maxSeats'] = self.maxseats
|
||||||
# boardcard1 smallint, /* 0=none, 1-13=2-Ah 14-26=2-Ad 27-39=2-Ac 40-52=2-As */
|
# boardcard1 smallint, /* 0=none, 1-13=2-Ah 14-26=2-Ad 27-39=2-Ac 40-52=2-As */
|
||||||
# boardcard2 smallint,
|
# boardcard2 smallint,
|
||||||
# boardcard3 smallint,
|
# boardcard3 smallint,
|
||||||
|
@ -141,6 +139,7 @@ db: a connected fpdb_db object"""
|
||||||
# showdownPot INT, /* pot size at sd/street7 */
|
# showdownPot INT, /* pot size at sd/street7 */
|
||||||
# comment TEXT,
|
# comment TEXT,
|
||||||
# commentTs DATETIME
|
# commentTs DATETIME
|
||||||
|
# handid = db.storeHand(hh)
|
||||||
# HandsPlayers - ? ... Do we fix winnings?
|
# HandsPlayers - ? ... Do we fix winnings?
|
||||||
# Tourneys ?
|
# Tourneys ?
|
||||||
# TourneysPlayers
|
# TourneysPlayers
|
||||||
|
|
|
@ -132,7 +132,7 @@ Otherwise, finish at eof...
|
||||||
self.processHand(handText)
|
self.processHand(handText)
|
||||||
numHands= len(handsList)
|
numHands= len(handsList)
|
||||||
endtime = time.time()
|
endtime = time.time()
|
||||||
print "Processed %d hands in %.3f seconds" % (numHands, endtime - starttime)
|
print "read %d hands in %.3f seconds" % (numHands, endtime - starttime)
|
||||||
if self.out_fh != sys.stdout:
|
if self.out_fh != sys.stdout:
|
||||||
self.out_fh.close()
|
self.out_fh.close()
|
||||||
|
|
||||||
|
|
|
@ -543,11 +543,16 @@ class Sql:
|
||||||
|
|
||||||
self.query['get_common_cards'] = """
|
self.query['get_common_cards'] = """
|
||||||
select
|
select
|
||||||
card1Value, card1Suit,
|
card1Value AS card1value,
|
||||||
card2Value, card2Suit,
|
card1Suit AS card1suit,
|
||||||
card3Value, card3Suit,
|
card2Value AS card2value,
|
||||||
card4Value, card4Suit,
|
card2Suit AS card2suit,
|
||||||
card5Value, card5Suit
|
card3Value AS card3value,
|
||||||
|
card3Suit AS card3suit,
|
||||||
|
card4Value AS card4value,
|
||||||
|
card4Suit AS card4suit,
|
||||||
|
card5Value AS card5value,
|
||||||
|
card5Suit AS card5suit
|
||||||
from BoardCards
|
from BoardCards
|
||||||
where handId = %s
|
where handId = %s
|
||||||
"""
|
"""
|
||||||
|
|
0
pyfpdb/Stats.py
Normal file → Executable file
0
pyfpdb/Stats.py
Normal file → Executable file
154
pyfpdb/TableWindow.py
Normal file
154
pyfpdb/TableWindow.py
Normal file
|
@ -0,0 +1,154 @@
|
||||||
|
#!/usr/bin/env python
|
||||||
|
"""Discover_TableWindow.py
|
||||||
|
|
||||||
|
Inspects the currently open windows and finds those of interest to us--that is
|
||||||
|
poker table windows from supported sites. Returns a list
|
||||||
|
of Table_Window objects representing the windows found.
|
||||||
|
"""
|
||||||
|
# Copyright 2008 - 2009, Ray E. Barker
|
||||||
|
|
||||||
|
# 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
|
||||||
|
|
||||||
|
########################################################################
|
||||||
|
|
||||||
|
# Standard Library modules
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
|
||||||
|
# pyGTK modules
|
||||||
|
import pygtk
|
||||||
|
import gtk
|
||||||
|
import gobject
|
||||||
|
|
||||||
|
# FreePokerTools modules
|
||||||
|
import Configuration
|
||||||
|
#if os.name == "posix":
|
||||||
|
# import XTables
|
||||||
|
#elif os.name == "nt":
|
||||||
|
# import WinTables
|
||||||
|
|
||||||
|
# Global used for figuring out the current game being played from the title
|
||||||
|
# The dict key is the fpdb name for the game
|
||||||
|
# The list is the names for those games used by the supported poker sites
|
||||||
|
# This is currently only used for HORSE, so it only needs to support those
|
||||||
|
# games on PokerStars and Full Tilt.
|
||||||
|
game_names = { #fpdb name Stars Name FTP Name
|
||||||
|
"holdem" : ("Hold\'em" , ),
|
||||||
|
"omahahilo" : ("Omaha H/L" , ),
|
||||||
|
"studhilo" : ("Stud H/L" , ),
|
||||||
|
"razz" : ("Razz" , ),
|
||||||
|
"studhi" : ("Stud" , "Stud Hi")
|
||||||
|
}
|
||||||
|
|
||||||
|
# A window title might have our table name + one of theses words/
|
||||||
|
# phrases. If it has this word in the title, it is not a table.
|
||||||
|
bad_words = ('History for table:', 'HUD:', 'Chat:')
|
||||||
|
|
||||||
|
# Here are the custom signals we define for allowing the 'client watcher'
|
||||||
|
# thread to communicate with the gui thread. Any time a poker client is
|
||||||
|
# is moved, resized, or closed on of these signals is emitted to the
|
||||||
|
# HUD main window.
|
||||||
|
gobject.signal_new("client_moved", gtk.Window,
|
||||||
|
gobject.SIGNAL_RUN_LAST,
|
||||||
|
gobject.TYPE_NONE,
|
||||||
|
(gobject.TYPE_PYOBJECT,))
|
||||||
|
|
||||||
|
gobject.signal_new("client_resized", gtk.Window,
|
||||||
|
gobject.SIGNAL_RUN_LAST,
|
||||||
|
gobject.TYPE_NONE,
|
||||||
|
(gobject.TYPE_PYOBJECT,))
|
||||||
|
|
||||||
|
gobject.signal_new("client_destroyed", gtk.Window,
|
||||||
|
gobject.SIGNAL_RUN_LAST,
|
||||||
|
gobject.TYPE_NONE,
|
||||||
|
(gobject.TYPE_PYOBJECT,))
|
||||||
|
|
||||||
|
# Each TableWindow object must have the following attributes correctly populated:
|
||||||
|
# tw.name = the table name from the title bar, which must to match the table name
|
||||||
|
# from the corresponding hand history.
|
||||||
|
# tw.site = the site name, e.g. PokerStars, FullTilt. This must match the site
|
||||||
|
# name specified in the config file.
|
||||||
|
# tw.number = This is the system id number for the client table window in the
|
||||||
|
# format that the system presents it. This is Xid in Xwindows and
|
||||||
|
# hwnd in Microsoft Windows.
|
||||||
|
# tw.title = The full title from the window title bar.
|
||||||
|
# tw.width, tw.height = The width and height of the window in pixels. This is
|
||||||
|
# the internal width and height, not including the title bar and
|
||||||
|
# window borders.
|
||||||
|
# tw.x, tw.y = The x, y (horizontal, vertical) location of the window relative
|
||||||
|
# to the top left of the display screen. This also does not include the
|
||||||
|
# title bar and window borders. To put it another way, this is the
|
||||||
|
# screen location of (0, 0) in the working window.
|
||||||
|
|
||||||
|
class Table_Window(object):
|
||||||
|
def __init__(self, table_name = None, tournament = None, table_number = None):
|
||||||
|
|
||||||
|
if table_name != None:
|
||||||
|
search_string = table_name
|
||||||
|
self.name = table_name
|
||||||
|
self.tournament = None
|
||||||
|
self.table = None
|
||||||
|
elif tournament != None and table_number != None:
|
||||||
|
print "tournament %s, table %s" % (tournament, table_number)
|
||||||
|
self.tournament = int(tournament)
|
||||||
|
self.table = int(table_number)
|
||||||
|
self.name = "%s - %s" % (self.tournament, self.table)
|
||||||
|
search_string = "%s.+Table\s%s" % (tournament, table_number)
|
||||||
|
else:
|
||||||
|
return None
|
||||||
|
|
||||||
|
self.find_table_parameters(search_string)
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
# __str__ method for testing
|
||||||
|
temp = 'TableWindow object\n'
|
||||||
|
temp = temp + " name = %s\n site = %s\n number = %s\n title = %s\n" % (self.name, self.site, self.number, self.title)
|
||||||
|
# temp = temp + " game = %s\n structure = %s\n max = %s\n" % (self.game, self.structure, self.max)
|
||||||
|
temp = temp + " width = %d\n height = %d\n x = %d\n y = %d\n" % (self.width, self.height, self.x, self.y)
|
||||||
|
if getattr(self, 'tournament', 0):
|
||||||
|
temp = temp + " tournament = %d\n table = %d" % (self.tournament, self.table)
|
||||||
|
return temp
|
||||||
|
|
||||||
|
def get_game(self):
|
||||||
|
title = self.get_window_title()
|
||||||
|
print title
|
||||||
|
for game, names in game_names.iteritems():
|
||||||
|
for name in names:
|
||||||
|
if name in title:
|
||||||
|
return game
|
||||||
|
return None
|
||||||
|
|
||||||
|
def check_geometry(self):
|
||||||
|
new_geo = self.get_geometry()
|
||||||
|
|
||||||
|
if new_geo == None: # window destroyed
|
||||||
|
return "client_destroyed"
|
||||||
|
|
||||||
|
elif self.x != new_geo['x'] or self.y != new_geo['y']: # window moved
|
||||||
|
self.x = new_geo['x']
|
||||||
|
self.y = new_geo['y']
|
||||||
|
return "client_moved"
|
||||||
|
|
||||||
|
elif self.width != new_geo['width'] or self.height != new_geo['height']: # window resized
|
||||||
|
self.width = new_geo['width']
|
||||||
|
self.height = new_geo['height']
|
||||||
|
return "client_resized"
|
||||||
|
|
||||||
|
else: return False # window not changed
|
||||||
|
|
||||||
|
def check_bad_words(self, title):
|
||||||
|
for word in bad_words:
|
||||||
|
if word in title: return True
|
||||||
|
return False
|
97
pyfpdb/Tables_Demo.py
Normal file
97
pyfpdb/Tables_Demo.py
Normal file
|
@ -0,0 +1,97 @@
|
||||||
|
#!/usr/bin/env python
|
||||||
|
"""Tables_Demo.py
|
||||||
|
|
||||||
|
Main program module to test/demo the Tables subclasses.
|
||||||
|
"""
|
||||||
|
# Copyright 2008 - 2009, Ray E. Barker
|
||||||
|
|
||||||
|
# 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
|
||||||
|
|
||||||
|
########################################################################
|
||||||
|
|
||||||
|
# Standard Library modules
|
||||||
|
import sys
|
||||||
|
import os
|
||||||
|
import re
|
||||||
|
|
||||||
|
# pyGTK modules
|
||||||
|
import pygtk
|
||||||
|
import gtk
|
||||||
|
import gobject
|
||||||
|
|
||||||
|
# fpdb/free poker tools modules
|
||||||
|
# get the correct module for the current os
|
||||||
|
if os.name == 'posix':
|
||||||
|
import XTables as Tables
|
||||||
|
elif os.name == 'nt':
|
||||||
|
import WinTables as Tables
|
||||||
|
|
||||||
|
# Main function used for testing
|
||||||
|
if __name__=="__main__":
|
||||||
|
# c = Configuration.Config()
|
||||||
|
|
||||||
|
class fake_hud(object):
|
||||||
|
def __init__(self, table, dx = 100, dy = 100):
|
||||||
|
self.table = table
|
||||||
|
self.dx = dx
|
||||||
|
self.dy = dy
|
||||||
|
|
||||||
|
self.main_window = gtk.Window()
|
||||||
|
self.main_window.connect("destroy", self.client_destroyed)
|
||||||
|
self.label = gtk.Label('Fake Fake Fake Fake\nFake\nFake\nFake')
|
||||||
|
self.main_window.add(self.label)
|
||||||
|
self.main_window.set_title("Fake HUD Main Window")
|
||||||
|
self.main_window.move(table.x + dx, table.y + dy)
|
||||||
|
self.main_window.show_all()
|
||||||
|
table.topify(self)
|
||||||
|
self.main_window.connect("client_moved", self.client_moved)
|
||||||
|
self.main_window.connect("client_resized", self.client_resized)
|
||||||
|
self.main_window.connect("client_destroyed", self.client_destroyed)
|
||||||
|
|
||||||
|
def client_moved(self, widget, hud):
|
||||||
|
self.main_window.move(self.table.x + self.dx, self.table.y + self.dy)
|
||||||
|
|
||||||
|
def client_resized(self, *args):
|
||||||
|
print "client resized"
|
||||||
|
|
||||||
|
def client_destroyed(self, *args): # call back for terminating the main eventloop
|
||||||
|
gtk.main_quit()
|
||||||
|
|
||||||
|
def check_on_table(table, hud):
|
||||||
|
result = table.check_geometry()
|
||||||
|
if result != False:
|
||||||
|
hud.main_window.emit(result, hud)
|
||||||
|
return True
|
||||||
|
|
||||||
|
print "enter table name to find: ",
|
||||||
|
table_name = sys.stdin.readline()
|
||||||
|
if "," in table_name: # tournament
|
||||||
|
print "tournament"
|
||||||
|
(tour_no, tab_no) = table_name.split(",", 1)
|
||||||
|
tour_no = tour_no.rstrip()
|
||||||
|
tab_no = tab_no.rstrip()
|
||||||
|
table = Tables.Table(tournament = tour_no, table_number = tab_no)
|
||||||
|
else: # not a tournament
|
||||||
|
print "cash game"
|
||||||
|
table_name = table_name.rstrip()
|
||||||
|
table = Tables.Table(table_name = table_name)
|
||||||
|
|
||||||
|
print "table =", table
|
||||||
|
print "game =", table.get_game()
|
||||||
|
|
||||||
|
fake = fake_hud(table)
|
||||||
|
gobject.timeout_add(100, check_on_table, table, fake)
|
||||||
|
gtk.main()
|
||||||
|
|
143
pyfpdb/WinTables.py
Normal file
143
pyfpdb/WinTables.py
Normal file
|
@ -0,0 +1,143 @@
|
||||||
|
#!/usr/bin/env python
|
||||||
|
"""WinTables.py
|
||||||
|
|
||||||
|
Routines for detecting and handling poker client windows for MS Windows.
|
||||||
|
"""
|
||||||
|
# Copyright 2008 - 2009, Ray E. Barker
|
||||||
|
|
||||||
|
# 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
|
||||||
|
|
||||||
|
########################################################################
|
||||||
|
|
||||||
|
# Standard Library modules
|
||||||
|
import re
|
||||||
|
|
||||||
|
# pyGTK modules
|
||||||
|
import pygtk
|
||||||
|
import gtk
|
||||||
|
|
||||||
|
# Other Library modules
|
||||||
|
import win32gui
|
||||||
|
import win32process
|
||||||
|
import win32api
|
||||||
|
import win32con
|
||||||
|
import win32security
|
||||||
|
|
||||||
|
# FreePokerTools modules
|
||||||
|
from TableWindow import Table_Window
|
||||||
|
|
||||||
|
# We don't know the border width or title bar height
|
||||||
|
# so we guess here. We can probably get these from a windows call.
|
||||||
|
b_width = 3
|
||||||
|
tb_height = 29
|
||||||
|
|
||||||
|
class Table(Table_Window):
|
||||||
|
|
||||||
|
def find_table_parameters(self, search_string):
|
||||||
|
"""Finds poker client window with the given table name."""
|
||||||
|
titles = {}
|
||||||
|
win32gui.EnumWindows(win_enum_handler, titles)
|
||||||
|
for hwnd in titles:
|
||||||
|
if re.search(search_string, titles[hwnd]):
|
||||||
|
if 'History for table:' in titles[hwnd]: continue # Everleaf Network HH viewer window
|
||||||
|
if 'HUD:' in titles[hwnd]: continue # FPDB HUD window
|
||||||
|
if 'Chat:' in titles[hwnd]: continue # Some sites (FTP? PS? Others?) have seperable or seperately constructed chat windows
|
||||||
|
self.window = hwnd
|
||||||
|
break
|
||||||
|
|
||||||
|
if self.window == None:
|
||||||
|
print "Window %s not found. Skipping." % search_string
|
||||||
|
return None
|
||||||
|
|
||||||
|
(x, y, width, height) = win32gui.GetWindowRect(hwnd)
|
||||||
|
print "x = %s y = %s width = %s height = %s" % (x, y, width, height)
|
||||||
|
self.x = int(x) + b_width
|
||||||
|
self.y = int(y) + tb_height
|
||||||
|
self.height = int(height) - b_width - tb_height
|
||||||
|
self.width = int(width) - 2*b_width
|
||||||
|
|
||||||
|
self.exe = self.get_nt_exe(hwnd)
|
||||||
|
self.title = titles[hwnd]
|
||||||
|
self.site = ""
|
||||||
|
self.hud = None
|
||||||
|
self.number = gtk.gdk.window_foreign_new(long(self.window))
|
||||||
|
|
||||||
|
def get_geometry(self):
|
||||||
|
|
||||||
|
if not win32gui.IsWindow(self.window): # window closed
|
||||||
|
return None
|
||||||
|
|
||||||
|
try:
|
||||||
|
(x, y, width, height) = win32gui.GetWindowRect(hwnd)
|
||||||
|
return {'x' : int(x) + b_width,
|
||||||
|
'y' : int(y) + tb_height,
|
||||||
|
'width' : int(height) - b_width - tb_height,
|
||||||
|
'height' : int(width) - 2*b_width
|
||||||
|
}
|
||||||
|
except:
|
||||||
|
return None
|
||||||
|
|
||||||
|
def get_window_title(self):
|
||||||
|
return win32gui.GetWindowText(self.window)
|
||||||
|
|
||||||
|
def get_nt_exe(self, hwnd):
|
||||||
|
"""Finds the name of the executable that the given window handle belongs to."""
|
||||||
|
|
||||||
|
# Request privileges to enable "debug process", so we can later use PROCESS_VM_READ, retardedly required to GetModuleFileNameEx()
|
||||||
|
priv_flags = win32security.TOKEN_ADJUST_PRIVILEGES | win32security.TOKEN_QUERY
|
||||||
|
hToken = win32security.OpenProcessToken (win32api.GetCurrentProcess(), priv_flags)
|
||||||
|
# enable "debug process"
|
||||||
|
privilege_id = win32security.LookupPrivilegeValue (None, win32security.SE_DEBUG_NAME)
|
||||||
|
old_privs = win32security.AdjustTokenPrivileges (hToken, 0, [(privilege_id, win32security.SE_PRIVILEGE_ENABLED)])
|
||||||
|
|
||||||
|
# Open the process, and query it's filename
|
||||||
|
processid = win32process.GetWindowThreadProcessId(hwnd)
|
||||||
|
pshandle = win32api.OpenProcess(win32con.PROCESS_QUERY_INFORMATION | win32con.PROCESS_VM_READ, False, processid[1])
|
||||||
|
exename = win32process.GetModuleFileNameEx(pshandle, 0)
|
||||||
|
|
||||||
|
# clean up
|
||||||
|
win32api.CloseHandle(pshandle)
|
||||||
|
win32api.CloseHandle(hToken)
|
||||||
|
|
||||||
|
return exename
|
||||||
|
def win_enum_handler(hwnd, titles):
|
||||||
|
titles[hwnd] = win32gui.GetWindowText(hwnd)
|
||||||
|
|
||||||
|
def topify_window(hud, window):
|
||||||
|
"""Set the specified gtk window to stayontop in MS Windows."""
|
||||||
|
|
||||||
|
def windowEnumerationHandler(hwnd, resultList):
|
||||||
|
'''Callback for win32gui.EnumWindows() to generate list of window handles.'''
|
||||||
|
resultList.append((hwnd, win32gui.GetWindowText(hwnd)))
|
||||||
|
|
||||||
|
unique_name = 'unique name for finding this window'
|
||||||
|
real_name = window.get_title()
|
||||||
|
window.set_title(unique_name)
|
||||||
|
tl_windows = []
|
||||||
|
win32gui.EnumWindows(windowEnumerationHandler, tl_windows)
|
||||||
|
|
||||||
|
for w in tl_windows:
|
||||||
|
if w[1] == unique_name:
|
||||||
|
hud.main_window.parentgdkhandle = gtk.gdk.window_foreign_new(long(hud.table.number))
|
||||||
|
hud.main_window.gdkhandle = gtk.gdk.window_foreign_new(w[0])
|
||||||
|
hud.main_window.gdkhandle.set_transient_for(hud.main_window.parentgdkhandle)
|
||||||
|
|
||||||
|
style = win32gui.GetWindowLong(self.table.number, win32con.GWL_EXSTYLE)
|
||||||
|
style |= win32con.WS_CLIPCHILDREN
|
||||||
|
win32gui.SetWindowLong(hud.table.number, win32con.GWL_EXSTYLE, style)
|
||||||
|
break
|
||||||
|
|
||||||
|
window.set_title(real_name)
|
||||||
|
|
101
pyfpdb/XTables.py
Normal file
101
pyfpdb/XTables.py
Normal file
|
@ -0,0 +1,101 @@
|
||||||
|
#!/usr/bin/env python
|
||||||
|
"""Discover_Tables.py
|
||||||
|
|
||||||
|
Inspects the currently open windows and finds those of interest to us--that is
|
||||||
|
poker table windows from supported sites. Returns a list
|
||||||
|
of Table_Window objects representing the windows found.
|
||||||
|
"""
|
||||||
|
# Copyright 2008 - 2009, Ray E. Barker
|
||||||
|
|
||||||
|
# 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
|
||||||
|
|
||||||
|
########################################################################
|
||||||
|
|
||||||
|
# Standard Library modules
|
||||||
|
import re
|
||||||
|
|
||||||
|
# pyGTK modules
|
||||||
|
import pygtk
|
||||||
|
import gtk
|
||||||
|
|
||||||
|
# Other Library modules
|
||||||
|
import Xlib
|
||||||
|
import Xlib.display
|
||||||
|
|
||||||
|
# FreePokerTools modules
|
||||||
|
from TableWindow import Table_Window
|
||||||
|
|
||||||
|
# We might as well do this once and make them globals
|
||||||
|
disp = Xlib.display.Display()
|
||||||
|
root = disp.screen().root
|
||||||
|
|
||||||
|
class Table(Table_Window):
|
||||||
|
|
||||||
|
def find_table_parameters(self, search_string):
|
||||||
|
self.window = None
|
||||||
|
done_looping = False
|
||||||
|
for outside in root.query_tree().children:
|
||||||
|
for inside in outside.query_tree().children:
|
||||||
|
if done_looping: break
|
||||||
|
if inside.get_wm_name() and re.search(search_string, inside.get_wm_name()):
|
||||||
|
if self.check_bad_words(inside.get_wm_name()): continue
|
||||||
|
self.window = inside
|
||||||
|
self.parent = outside
|
||||||
|
done_looping = True
|
||||||
|
break
|
||||||
|
|
||||||
|
if self.window == None or self.parent == None:
|
||||||
|
print "Window %s not found. Skipping." % search_string
|
||||||
|
return None
|
||||||
|
|
||||||
|
my_geo = self.window.get_geometry()
|
||||||
|
pa_geo = self.parent.get_geometry()
|
||||||
|
|
||||||
|
self.x = pa_geo.x + my_geo.x
|
||||||
|
self.y = pa_geo.y + my_geo.y
|
||||||
|
self.width = my_geo.width
|
||||||
|
self.height = my_geo.height
|
||||||
|
self.exe = self.window.get_wm_class()[0]
|
||||||
|
self.title = self.window.get_wm_name()
|
||||||
|
self.site = ""
|
||||||
|
self.hud = None
|
||||||
|
|
||||||
|
window_string = str(self.window)
|
||||||
|
mo = re.match('Xlib\.display\.Window\(([\dxabcdef]+)', window_string)
|
||||||
|
if not mo:
|
||||||
|
print "Not matched"
|
||||||
|
self.gdk_handle = None
|
||||||
|
else:
|
||||||
|
self.number = int( mo.group(1), 0)
|
||||||
|
self.gdk_handle = gtk.gdk.window_foreign_new(int(self.number))
|
||||||
|
|
||||||
|
def get_geometry(self):
|
||||||
|
try:
|
||||||
|
my_geo = self.window.get_geometry()
|
||||||
|
pa_geo = self.parent.get_geometry()
|
||||||
|
return {'x' : pa_geo.x + my_geo.x,
|
||||||
|
'y' : pa_geo.y + my_geo.y,
|
||||||
|
'width' : my_geo.width,
|
||||||
|
'height' : my_geo.height
|
||||||
|
}
|
||||||
|
except:
|
||||||
|
return None
|
||||||
|
|
||||||
|
def get_window_title(self):
|
||||||
|
return self.window.get_wm_name()
|
||||||
|
|
||||||
|
def topify(self, hud):
|
||||||
|
hud.main_window.gdkhandle = gtk.gdk.window_foreign_new(hud.main_window.window.xid)
|
||||||
|
hud.main_window.gdkhandle.set_transient_for(self.gdk_handle)
|
|
@ -181,7 +181,7 @@ class fpdb:
|
||||||
|
|
||||||
def dia_load_profile(self, widget, data=None):
|
def dia_load_profile(self, widget, data=None):
|
||||||
"""Dialogue to select a file to load a profile from"""
|
"""Dialogue to select a file to load a profile from"""
|
||||||
if self.obtain_global_lock():
|
if self.obtain_global_lock() == 0: # returns 0 if successful
|
||||||
try:
|
try:
|
||||||
chooser = gtk.FileChooserDialog(title="Please select a profile file to load",
|
chooser = gtk.FileChooserDialog(title="Please select a profile file to load",
|
||||||
action=gtk.FILE_CHOOSER_ACTION_OPEN,
|
action=gtk.FILE_CHOOSER_ACTION_OPEN,
|
||||||
|
@ -201,7 +201,7 @@ class fpdb:
|
||||||
|
|
||||||
def dia_recreate_tables(self, widget, data=None):
|
def dia_recreate_tables(self, widget, data=None):
|
||||||
"""Dialogue that asks user to confirm that he wants to delete and recreate the tables"""
|
"""Dialogue that asks user to confirm that he wants to delete and recreate the tables"""
|
||||||
if self.obtain_global_lock():
|
if self.obtain_global_lock() in (0,2): # returns 0 if successful, 2 if Hands table does not exist
|
||||||
|
|
||||||
lock_released = False
|
lock_released = False
|
||||||
try:
|
try:
|
||||||
|
@ -406,7 +406,7 @@ class fpdb:
|
||||||
self.settings['db-databaseName'],
|
self.settings['db-databaseName'],
|
||||||
self.settings['db-user'],
|
self.settings['db-user'],
|
||||||
self.settings['db-password'])
|
self.settings['db-password'])
|
||||||
return fpdb_simple.get_global_lock(self.fdb_lock)
|
return self.fdb_lock.get_global_lock()
|
||||||
#end def obtain_global_lock
|
#end def obtain_global_lock
|
||||||
|
|
||||||
def quit(self, widget, data):
|
def quit(self, widget, data):
|
||||||
|
@ -455,7 +455,7 @@ class fpdb:
|
||||||
ps_tab=new_ps_thread.get_vbox()
|
ps_tab=new_ps_thread.get_vbox()
|
||||||
self.add_and_display_tab(ps_tab, "Positional Stats")
|
self.add_and_display_tab(ps_tab, "Positional Stats")
|
||||||
|
|
||||||
def tab_main_help(self, widget, data):
|
def tab_main_help(self, widget, data=None):
|
||||||
"""Displays a tab with the main fpdb help screen"""
|
"""Displays a tab with the main fpdb help screen"""
|
||||||
#print "start of tab_main_help"
|
#print "start of tab_main_help"
|
||||||
mh_tab=gtk.Label("""Welcome to Fpdb!
|
mh_tab=gtk.Label("""Welcome to Fpdb!
|
||||||
|
|
|
@ -17,6 +17,9 @@
|
||||||
|
|
||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
|
import sys
|
||||||
|
from time import time, strftime
|
||||||
|
|
||||||
import fpdb_simple
|
import fpdb_simple
|
||||||
import FpdbSQLQueries
|
import FpdbSQLQueries
|
||||||
|
|
||||||
|
@ -29,6 +32,110 @@ class fpdb_db:
|
||||||
self.MYSQL_INNODB = 2
|
self.MYSQL_INNODB = 2
|
||||||
self.PGSQL = 3
|
self.PGSQL = 3
|
||||||
self.SQLITE = 4
|
self.SQLITE = 4
|
||||||
|
|
||||||
|
# Data Structures for index and foreign key creation
|
||||||
|
# drop_code is an int with possible values: 0 - don't drop for bulk import
|
||||||
|
# 1 - drop during bulk import
|
||||||
|
# db differences:
|
||||||
|
# - note that mysql automatically creates indexes on constrained columns when
|
||||||
|
# foreign keys are created, while postgres does not. Hence the much longer list
|
||||||
|
# of indexes is required for postgres.
|
||||||
|
# all primary keys are left on all the time
|
||||||
|
#
|
||||||
|
# table column drop_code
|
||||||
|
|
||||||
|
self.indexes = [
|
||||||
|
[ ] # no db with index 0
|
||||||
|
, [ ] # no db with index 1
|
||||||
|
, [ # indexes for mysql (list index 2)
|
||||||
|
{'tab':'Players', 'col':'name', 'drop':0}
|
||||||
|
, {'tab':'Hands', 'col':'siteHandNo', 'drop':0}
|
||||||
|
, {'tab':'Tourneys', 'col':'siteTourneyNo', 'drop':0}
|
||||||
|
]
|
||||||
|
, [ # indexes for postgres (list index 3)
|
||||||
|
{'tab':'Boardcards', 'col':'handId', 'drop':0}
|
||||||
|
, {'tab':'Gametypes', 'col':'siteId', 'drop':0}
|
||||||
|
, {'tab':'Hands', 'col':'gametypeId', 'drop':0} # mct 22/3/09
|
||||||
|
, {'tab':'Hands', 'col':'siteHandNo', 'drop':0}
|
||||||
|
, {'tab':'HandsActions', 'col':'handsPlayerId', 'drop':0}
|
||||||
|
, {'tab':'HandsPlayers', 'col':'handId', 'drop':1}
|
||||||
|
, {'tab':'HandsPlayers', 'col':'playerId', 'drop':1}
|
||||||
|
, {'tab':'HandsPlayers', 'col':'tourneysPlayersId', 'drop':0}
|
||||||
|
, {'tab':'HudCache', 'col':'gametypeId', 'drop':1}
|
||||||
|
, {'tab':'HudCache', 'col':'playerId', 'drop':0}
|
||||||
|
, {'tab':'HudCache', 'col':'tourneyTypeId', 'drop':0}
|
||||||
|
, {'tab':'Players', 'col':'siteId', 'drop':1}
|
||||||
|
, {'tab':'Players', 'col':'name', 'drop':0}
|
||||||
|
, {'tab':'Tourneys', 'col':'tourneyTypeId', 'drop':1}
|
||||||
|
, {'tab':'Tourneys', 'col':'siteTourneyNo', 'drop':0}
|
||||||
|
, {'tab':'TourneysPlayers', 'col':'playerId', 'drop':0}
|
||||||
|
, {'tab':'TourneysPlayers', 'col':'tourneyId', 'drop':0}
|
||||||
|
, {'tab':'TourneyTypes', 'col':'siteId', 'drop':0}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
|
||||||
|
self.foreignKeys = [
|
||||||
|
[ ] # no db with index 0
|
||||||
|
, [ ] # no db with index 1
|
||||||
|
, [ # foreign keys for mysql
|
||||||
|
{'fktab':'Hands', 'fkcol':'gametypeId', 'rtab':'Gametypes', 'rcol':'id', 'drop':1}
|
||||||
|
, {'fktab':'HandsPlayers', 'fkcol':'handId', 'rtab':'Hands', 'rcol':'id', 'drop':1}
|
||||||
|
, {'fktab':'HandsPlayers', 'fkcol':'playerId', 'rtab':'Players', 'rcol':'id', 'drop':1}
|
||||||
|
, {'fktab':'HandsActions', 'fkcol':'handsPlayerId', 'rtab':'HandsPlayers', 'rcol':'id', 'drop':1}
|
||||||
|
, {'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}
|
||||||
|
]
|
||||||
|
, [ # foreign keys for postgres
|
||||||
|
{'fktab':'Hands', 'fkcol':'gametypeId', 'rtab':'Gametypes', 'rcol':'id', 'drop':1}
|
||||||
|
, {'fktab':'HandsPlayers', 'fkcol':'handId', 'rtab':'Hands', 'rcol':'id', 'drop':1}
|
||||||
|
, {'fktab':'HandsPlayers', 'fkcol':'playerId', 'rtab':'Players', 'rcol':'id', 'drop':1}
|
||||||
|
, {'fktab':'HandsActions', 'fkcol':'handsPlayerId', 'rtab':'HandsPlayers', 'rcol':'id', 'drop':1}
|
||||||
|
, {'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}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
# MySQL Notes:
|
||||||
|
# "FOREIGN KEY (handId) REFERENCES Hands(id)" - requires index on Hands.id
|
||||||
|
# - creates index handId on <thistable>.handId
|
||||||
|
# alter table t drop foreign key fk
|
||||||
|
# alter table t add foreign key (fkcol) references tab(rcol)
|
||||||
|
# alter table t add constraint c foreign key (fkcol) references tab(rcol)
|
||||||
|
# (fkcol is used for foreigh key name)
|
||||||
|
|
||||||
|
# mysql to list indexes:
|
||||||
|
# SELECT table_name, index_name, non_unique, column_name
|
||||||
|
# FROM INFORMATION_SCHEMA.STATISTICS
|
||||||
|
# WHERE table_name = 'tbl_name'
|
||||||
|
# AND table_schema = 'db_name'
|
||||||
|
# ORDER BY table_name, index_name, seq_in_index
|
||||||
|
#
|
||||||
|
# ALTER TABLE Tourneys ADD INDEX siteTourneyNo(siteTourneyNo)
|
||||||
|
# ALTER TABLE tab DROP INDEX idx
|
||||||
|
|
||||||
|
# mysql to list fks:
|
||||||
|
# SELECT constraint_name, table_name, column_name, referenced_table_name, referenced_column_name
|
||||||
|
# FROM information_schema.KEY_COLUMN_USAGE
|
||||||
|
# WHERE REFERENCED_TABLE_SCHEMA = (your schema name here)
|
||||||
|
# AND REFERENCED_TABLE_NAME is not null
|
||||||
|
# ORDER BY TABLE_NAME, COLUMN_NAME;
|
||||||
|
|
||||||
|
# this may indicate missing object
|
||||||
|
# _mysql_exceptions.OperationalError: (1025, "Error on rename of '.\\fpdb\\hands' to '.\\fpdb\\#sql2-7f0-1b' (errno: 152)")
|
||||||
|
|
||||||
|
|
||||||
|
# PG notes:
|
||||||
|
|
||||||
|
# To add a foreign key constraint to a table:
|
||||||
|
# ALTER TABLE tab ADD CONSTRAINT c FOREIGN KEY (col) REFERENCES t2(col2) MATCH FULL;
|
||||||
|
# ALTER TABLE tab DROP CONSTRAINT zipchk
|
||||||
|
#
|
||||||
|
# Note: index names must be unique across a schema
|
||||||
|
# CREATE INDEX idx ON tab(col)
|
||||||
|
# DROP INDEX idx
|
||||||
#end def __init__
|
#end def __init__
|
||||||
|
|
||||||
def do_connect(self, config=None):
|
def do_connect(self, config=None):
|
||||||
|
@ -69,23 +176,16 @@ class fpdb_db:
|
||||||
psycopg2.extensions.register_type(psycopg2.extensions.UNICODE)
|
psycopg2.extensions.register_type(psycopg2.extensions.UNICODE)
|
||||||
# If DB connection is made over TCP, then the variables
|
# If DB connection is made over TCP, then the variables
|
||||||
# host, user and password are required
|
# host, user and password are required
|
||||||
print "host=%s user=%s pass=%s." % (host, user, password)
|
# For local domain-socket connections, only DB name is
|
||||||
if self.host and self.user and self.password:
|
# needed, and everything else is in fact undefined and/or
|
||||||
try:
|
# flat out wrong
|
||||||
|
if self.host == "localhost" or self.host == "127.0.0.1":
|
||||||
|
self.db = psycopg2.connect(database = database)
|
||||||
|
else:
|
||||||
self.db = psycopg2.connect(host = host,
|
self.db = psycopg2.connect(host = host,
|
||||||
user = user,
|
user = user,
|
||||||
password = password,
|
password = password,
|
||||||
database = database)
|
database = database)
|
||||||
except:
|
|
||||||
raise fpdb_simple.FpdbError("PostgreSQL connection failed")
|
|
||||||
# For local domain-socket connections, only DB name is
|
|
||||||
# needed, and everything else is in fact undefined and/or
|
|
||||||
# flat out wrong
|
|
||||||
else:
|
|
||||||
try:
|
|
||||||
self.db = psycopg2.connect(database = database)
|
|
||||||
except:
|
|
||||||
raise fpdb_simple.FpdbError("PostgreSQL connection failed")
|
|
||||||
else:
|
else:
|
||||||
raise fpdb_simple.FpdbError("unrecognised database backend:"+backend)
|
raise fpdb_simple.FpdbError("unrecognised database backend:"+backend)
|
||||||
self.cursor=self.db.cursor()
|
self.cursor=self.db.cursor()
|
||||||
|
@ -96,7 +196,7 @@ class fpdb_db:
|
||||||
try:
|
try:
|
||||||
self.cursor.execute("SELECT * FROM Settings")
|
self.cursor.execute("SELECT * FROM Settings")
|
||||||
settings=self.cursor.fetchone()
|
settings=self.cursor.fetchone()
|
||||||
if settings[0]!=119:
|
if settings[0]!=118:
|
||||||
print "outdated or too new database version - please recreate tables"
|
print "outdated or too new database version - please recreate tables"
|
||||||
self.wrongDbVersion=True
|
self.wrongDbVersion=True
|
||||||
except:# _mysql_exceptions.ProgrammingError:
|
except:# _mysql_exceptions.ProgrammingError:
|
||||||
|
@ -201,14 +301,10 @@ class fpdb_db:
|
||||||
#end def get_db_info
|
#end def get_db_info
|
||||||
|
|
||||||
def fillDefaultData(self):
|
def fillDefaultData(self):
|
||||||
self.cursor.execute("INSERT INTO Settings VALUES (119);")
|
self.cursor.execute("INSERT INTO Settings VALUES (118);")
|
||||||
self.cursor.execute("INSERT INTO Sites VALUES (DEFAULT, 'Full Tilt Poker', 'USD');")
|
self.cursor.execute("INSERT INTO Sites VALUES (DEFAULT, 'Full Tilt Poker', 'USD');")
|
||||||
self.cursor.execute("INSERT INTO Sites VALUES (DEFAULT, 'PokerStars', 'USD');")
|
self.cursor.execute("INSERT INTO Sites VALUES (DEFAULT, 'PokerStars', 'USD');")
|
||||||
self.cursor.execute("INSERT INTO Sites VALUES (DEFAULT, 'Everleaf', 'USD');")
|
self.cursor.execute("INSERT INTO Sites VALUES (DEFAULT, 'Everleaf', 'USD');")
|
||||||
self.cursor.execute("INSERT INTO Sites VALUES (DEFAULT, 'Carbon', 'USD');")
|
|
||||||
self.cursor.execute("INSERT INTO Sites VALUES (DEFAULT, 'OnGame', 'USD');")
|
|
||||||
self.cursor.execute("INSERT INTO Sites VALUES (DEFAULT, 'UltimateBet', 'USD');")
|
|
||||||
self.cursor.execute("INSERT INTO Sites VALUES (DEFAULT, 'Betfair', 'USD');")
|
|
||||||
self.cursor.execute("INSERT INTO TourneyTypes VALUES (DEFAULT, 1, 0, 0, 0, False);")
|
self.cursor.execute("INSERT INTO TourneyTypes VALUES (DEFAULT, 1, 0, 0, 0, False);")
|
||||||
#end def fillDefaultData
|
#end def fillDefaultData
|
||||||
|
|
||||||
|
@ -217,27 +313,297 @@ class fpdb_db:
|
||||||
|
|
||||||
self.drop_tables()
|
self.drop_tables()
|
||||||
self.create_tables()
|
self.create_tables()
|
||||||
fpdb_simple.createAllIndexes(self)
|
self.createAllIndexes()
|
||||||
self.db.commit()
|
self.db.commit()
|
||||||
print "Finished recreating tables"
|
print "Finished recreating tables"
|
||||||
#end def recreate_tables
|
#end def recreate_tables
|
||||||
|
|
||||||
def getSqlPlayerIDs(names, site_id):
|
def prepareBulkImport(self):
|
||||||
result = []
|
"""Drop some indexes/foreign keys to prepare for bulk import.
|
||||||
notfound = []
|
Currently keeping the standalone indexes as needed to import quickly"""
|
||||||
self.cursor.execute("SELECT name,id FROM Players WHERE name='%s'" % "' OR name='".join(names))
|
stime = time()
|
||||||
tmp = dict(self.cursor.fetchall())
|
if self.backend == self.PGSQL:
|
||||||
for n in names:
|
self.db.set_isolation_level(0) # allow table/index operations to work
|
||||||
if n not in tmp:
|
for fk in self.foreignKeys[self.backend]:
|
||||||
notfound.append(n)
|
if fk['drop'] == 1:
|
||||||
|
if self.backend == self.MYSQL_INNODB:
|
||||||
|
self.cursor.execute("SELECT constraint_name " +
|
||||||
|
"FROM information_schema.KEY_COLUMN_USAGE " +
|
||||||
|
#"WHERE REFERENCED_TABLE_SCHEMA = 'fpdb'
|
||||||
|
"WHERE 1=1 " +
|
||||||
|
"AND table_name = %s AND column_name = %s " +
|
||||||
|
"AND referenced_table_name = %s " +
|
||||||
|
"AND referenced_column_name = %s ",
|
||||||
|
(fk['fktab'], fk['fkcol'], fk['rtab'], fk['rcol']) )
|
||||||
|
cons = self.cursor.fetchone()
|
||||||
|
#print "preparebulk: cons=", cons
|
||||||
|
if cons:
|
||||||
|
print "dropping mysql fk", cons[0], fk['fktab'], fk['fkcol']
|
||||||
|
try:
|
||||||
|
self.cursor.execute("alter table " + fk['fktab'] + " drop foreign key " + cons[0])
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
elif self.backend == self.PGSQL:
|
||||||
|
# DON'T FORGET TO RECREATE THEM!!
|
||||||
|
print "dropping pg fk", fk['fktab'], fk['fkcol']
|
||||||
|
try:
|
||||||
|
# try to lock table to see if index drop will work:
|
||||||
|
# hmmm, tested by commenting out rollback in grapher. lock seems to work but
|
||||||
|
# then drop still hangs :-( does work in some tests though??
|
||||||
|
# will leave code here for now pending further tests/enhancement ...
|
||||||
|
self.cursor.execute( "lock table %s in exclusive mode nowait" % (fk['fktab'],) )
|
||||||
|
#print "after lock, status:", self.cursor.statusmessage
|
||||||
|
#print "alter table %s drop constraint %s_%s_fkey" % (fk['fktab'], fk['fktab'], fk['fkcol'])
|
||||||
|
try:
|
||||||
|
self.cursor.execute("alter table %s drop constraint %s_%s_fkey" % (fk['fktab'], fk['fktab'], fk['fkcol']))
|
||||||
|
print "dropped pg fk pg fk %s_%s_fkey, continuing ..." % (fk['fktab'], fk['fkcol'])
|
||||||
|
except:
|
||||||
|
if "does not exist" not in str(sys.exc_value):
|
||||||
|
print "warning: drop pg fk %s_%s_fkey failed: %s, continuing ..." \
|
||||||
|
% (fk['fktab'], fk['fkcol'], str(sys.exc_value).rstrip('\n') )
|
||||||
|
except:
|
||||||
|
print "warning: constraint %s_%s_fkey not dropped: %s, continuing ..." \
|
||||||
|
% (fk['fktab'],fk['fkcol'], str(sys.exc_value).rstrip('\n'))
|
||||||
else:
|
else:
|
||||||
result.append(tmp[n])
|
print "Only MySQL and Postgres supported so far"
|
||||||
if notfound:
|
return -1
|
||||||
cursor.executemany("INSERT INTO Players (name, siteId) VALUES (%s, "+str(site_id)+")", (notfound))
|
|
||||||
cursor.execute("SELECT id FROM Players WHERE name='%s'" % "' OR name='".join(notfound))
|
|
||||||
tmp = cursor.fetchall()
|
|
||||||
for n in tmp:
|
|
||||||
result.append(n[0])
|
|
||||||
|
|
||||||
#We proabably want to cache this
|
for idx in self.indexes[self.backend]:
|
||||||
return result
|
if idx['drop'] == 1:
|
||||||
|
if self.backend == self.MYSQL_INNODB:
|
||||||
|
print "dropping mysql index ", idx['tab'], idx['col']
|
||||||
|
try:
|
||||||
|
# apparently nowait is not implemented in mysql so this just hands if there are locks
|
||||||
|
# preventing the index drop :-(
|
||||||
|
self.cursor.execute( "alter table %s drop index %s", (idx['tab'],idx['col']) )
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
elif self.backend == self.PGSQL:
|
||||||
|
# DON'T FORGET TO RECREATE THEM!!
|
||||||
|
print "dropping pg index ", idx['tab'], idx['col']
|
||||||
|
try:
|
||||||
|
# try to lock table to see if index drop will work:
|
||||||
|
self.cursor.execute( "lock table %s in exclusive mode nowait" % (idx['tab'],) )
|
||||||
|
#print "after lock, status:", self.cursor.statusmessage
|
||||||
|
try:
|
||||||
|
# table locked ok so index drop should work:
|
||||||
|
#print "drop index %s_%s_idx" % (idx['tab'],idx['col'])
|
||||||
|
self.cursor.execute( "drop index if exists %s_%s_idx" % (idx['tab'],idx['col']) )
|
||||||
|
#print "dropped pg index ", idx['tab'], idx['col']
|
||||||
|
except:
|
||||||
|
if "does not exist" not in str(sys.exc_value):
|
||||||
|
print "warning: drop index %s_%s_idx failed: %s, continuing ..." \
|
||||||
|
% (idx['tab'],idx['col'], str(sys.exc_value).rstrip('\n'))
|
||||||
|
except:
|
||||||
|
print "warning: index %s_%s_idx not dropped %s, continuing ..." \
|
||||||
|
% (idx['tab'],idx['col'], str(sys.exc_value).rstrip('\n'))
|
||||||
|
else:
|
||||||
|
print "Error: Only MySQL and Postgres supported so far"
|
||||||
|
return -1
|
||||||
|
|
||||||
|
if self.backend == self.PGSQL:
|
||||||
|
self.db.set_isolation_level(1) # go back to normal isolation level
|
||||||
|
self.db.commit() # seems to clear up errors if there were any in postgres
|
||||||
|
ptime = time() - stime
|
||||||
|
print "prepare import took", ptime, "seconds"
|
||||||
|
#end def prepareBulkImport
|
||||||
|
|
||||||
|
def afterBulkImport(self):
|
||||||
|
"""Re-create any dropped indexes/foreign keys after bulk import"""
|
||||||
|
stime = time()
|
||||||
|
if self.backend == self.PGSQL:
|
||||||
|
self.db.set_isolation_level(0) # allow table/index operations to work
|
||||||
|
for fk in self.foreignKeys[self.backend]:
|
||||||
|
if fk['drop'] == 1:
|
||||||
|
if self.backend == self.MYSQL_INNODB:
|
||||||
|
self.cursor.execute("SELECT constraint_name " +
|
||||||
|
"FROM information_schema.KEY_COLUMN_USAGE " +
|
||||||
|
#"WHERE REFERENCED_TABLE_SCHEMA = 'fpdb'
|
||||||
|
"WHERE 1=1 " +
|
||||||
|
"AND table_name = %s AND column_name = %s " +
|
||||||
|
"AND referenced_table_name = %s " +
|
||||||
|
"AND referenced_column_name = %s ",
|
||||||
|
(fk['fktab'], fk['fkcol'], fk['rtab'], fk['rcol']) )
|
||||||
|
cons = self.cursor.fetchone()
|
||||||
|
print "afterbulk: cons=", cons
|
||||||
|
if cons:
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
print "creating fk ", fk['fktab'], fk['fkcol'], "->", fk['rtab'], fk['rcol']
|
||||||
|
try:
|
||||||
|
self.cursor.execute("alter table " + fk['fktab'] + " add foreign key ("
|
||||||
|
+ fk['fkcol'] + ") references " + fk['rtab'] + "("
|
||||||
|
+ fk['rcol'] + ")")
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
elif self.backend == self.PGSQL:
|
||||||
|
print "creating fk ", fk['fktab'], fk['fkcol'], "->", fk['rtab'], fk['rcol']
|
||||||
|
try:
|
||||||
|
self.cursor.execute("alter table " + fk['fktab'] + " add constraint "
|
||||||
|
+ fk['fktab'] + '_' + fk['fkcol'] + '_fkey'
|
||||||
|
+ " foreign key (" + fk['fkcol']
|
||||||
|
+ ") references " + fk['rtab'] + "(" + fk['rcol'] + ")")
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
print "Only MySQL and Postgres supported so far"
|
||||||
|
return -1
|
||||||
|
|
||||||
|
for idx in self.indexes[self.backend]:
|
||||||
|
if idx['drop'] == 1:
|
||||||
|
if self.backend == self.MYSQL_INNODB:
|
||||||
|
print "creating mysql index ", idx['tab'], idx['col']
|
||||||
|
try:
|
||||||
|
self.cursor.execute( "alter table %s add index %s(%s)"
|
||||||
|
, (idx['tab'],idx['col'],idx['col']) )
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
elif self.backend == self.PGSQL:
|
||||||
|
# pass
|
||||||
|
# mod to use tab_col for index name?
|
||||||
|
print "creating pg index ", idx['tab'], idx['col']
|
||||||
|
try:
|
||||||
|
print "create index %s_%s_idx on %s(%s)" % (idx['tab'], idx['col'], idx['tab'], idx['col'])
|
||||||
|
self.cursor.execute( "create index %s_%s_idx on %s(%s)"
|
||||||
|
% (idx['tab'], idx['col'], idx['tab'], idx['col']) )
|
||||||
|
except:
|
||||||
|
print " ERROR! :-("
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
print "Only MySQL and Postgres supported so far"
|
||||||
|
return -1
|
||||||
|
|
||||||
|
if self.backend == self.PGSQL:
|
||||||
|
self.db.set_isolation_level(1) # go back to normal isolation level
|
||||||
|
self.db.commit() # seems to clear up errors if there were any in postgres
|
||||||
|
atime = time() - stime
|
||||||
|
print "after import took", atime, "seconds"
|
||||||
|
#end def afterBulkImport
|
||||||
|
|
||||||
|
def createAllIndexes(self):
|
||||||
|
"""Create new indexes"""
|
||||||
|
if self.backend == self.PGSQL:
|
||||||
|
self.db.set_isolation_level(0) # allow table/index operations to work
|
||||||
|
for idx in self.indexes[self.backend]:
|
||||||
|
if self.backend == self.MYSQL_INNODB:
|
||||||
|
print "creating mysql index ", idx['tab'], idx['col']
|
||||||
|
try:
|
||||||
|
self.cursor.execute( "alter table %s add index %s(%s)"
|
||||||
|
, (idx['tab'],idx['col'],idx['col']) )
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
elif self.backend == self.PGSQL:
|
||||||
|
# mod to use tab_col for index name?
|
||||||
|
print "creating pg index ", idx['tab'], idx['col']
|
||||||
|
try:
|
||||||
|
print "create index %s_%s_idx on %s(%s)" % (idx['tab'], idx['col'], idx['tab'], idx['col'])
|
||||||
|
self.cursor.execute( "create index %s_%s_idx on %s(%s)"
|
||||||
|
% (idx['tab'], idx['col'], idx['tab'], idx['col']) )
|
||||||
|
except:
|
||||||
|
print " ERROR! :-("
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
print "Only MySQL and Postgres supported so far"
|
||||||
|
return -1
|
||||||
|
if self.backend == self.PGSQL:
|
||||||
|
self.db.set_isolation_level(1) # go back to normal isolation level
|
||||||
|
#end def createAllIndexes
|
||||||
|
|
||||||
|
def dropAllIndexes(self):
|
||||||
|
"""Drop all standalone indexes (i.e. not including primary keys or foreign keys)
|
||||||
|
using list of indexes in indexes data structure"""
|
||||||
|
# maybe upgrade to use data dictionary?? (but take care to exclude PK and FK)
|
||||||
|
if self.backend == self.PGSQL:
|
||||||
|
self.db.set_isolation_level(0) # allow table/index operations to work
|
||||||
|
for idx in self.indexes[self.backend]:
|
||||||
|
if self.backend == self.MYSQL_INNODB:
|
||||||
|
print "dropping mysql index ", idx['tab'], idx['col']
|
||||||
|
try:
|
||||||
|
self.cursor.execute( "alter table %s drop index %s"
|
||||||
|
, (idx['tab'],idx['col']) )
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
elif self.backend == self.PGSQL:
|
||||||
|
print "dropping pg index ", idx['tab'], idx['col']
|
||||||
|
# mod to use tab_col for index name?
|
||||||
|
try:
|
||||||
|
self.cursor.execute( "drop index %s_%s_idx"
|
||||||
|
% (idx['tab'],idx['col']) )
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
print "Only MySQL and Postgres supported so far"
|
||||||
|
return -1
|
||||||
|
if self.backend == self.PGSQL:
|
||||||
|
self.db.set_isolation_level(1) # go back to normal isolation level
|
||||||
|
#end def dropAllIndexes
|
||||||
|
|
||||||
|
def analyzeDB(self):
|
||||||
|
"""Do whatever the DB can offer to update index/table statistics"""
|
||||||
|
stime = time()
|
||||||
|
if self.backend == self.PGSQL:
|
||||||
|
self.db.set_isolation_level(0) # allow vacuum to work
|
||||||
|
try:
|
||||||
|
self.cursor.execute("vacuum analyze")
|
||||||
|
except:
|
||||||
|
print "Error during vacuum"
|
||||||
|
self.db.set_isolation_level(1) # go back to normal isolation level
|
||||||
|
self.db.commit()
|
||||||
|
atime = time() - stime
|
||||||
|
print "analyze took", atime, "seconds"
|
||||||
|
#end def analyzeDB
|
||||||
|
|
||||||
|
# Currently uses an exclusive lock on the Hands table as a global lock
|
||||||
|
# Return values are Unix style, 0 for success, positive integers for errors
|
||||||
|
# 1 = generic error
|
||||||
|
# 2 = hands table does not exist (error message is suppressed)
|
||||||
|
def get_global_lock(self):
|
||||||
|
if self.backend == self.MYSQL_INNODB:
|
||||||
|
try:
|
||||||
|
self.cursor.execute( "lock tables Hands write" )
|
||||||
|
except:
|
||||||
|
# Table 'fpdb.hands' doesn't exist
|
||||||
|
if str(sys.exc_value).find(".hands' doesn't exist") >= 0:
|
||||||
|
return(2)
|
||||||
|
print "Error! failed to obtain global lock. Close all programs accessing " \
|
||||||
|
+ "database (including fpdb) and try again (%s)." \
|
||||||
|
% ( str(sys.exc_value).rstrip('\n'), )
|
||||||
|
return(1)
|
||||||
|
elif self.backend == self.PGSQL:
|
||||||
|
try:
|
||||||
|
self.cursor.execute( "lock table Hands in exclusive mode nowait" )
|
||||||
|
#print "... after lock table, status =", self.cursor.statusmessage
|
||||||
|
except:
|
||||||
|
# relation "hands" does not exist
|
||||||
|
if str(sys.exc_value).find('relation "hands" does not exist') >= 0:
|
||||||
|
return(2)
|
||||||
|
print "Error! failed to obtain global lock. Close all programs accessing " \
|
||||||
|
+ "database (including fpdb) and try again (%s)." \
|
||||||
|
% ( str(sys.exc_value).rstrip('\n'), )
|
||||||
|
return(1)
|
||||||
|
return(0)
|
||||||
|
|
||||||
|
def storeHand(self, p):
|
||||||
|
#stores into table hands:
|
||||||
|
self.cursor.execute ("""INSERT INTO Hands
|
||||||
|
(siteHandNo, gametypeId, handStart, seats, tableName, importTime, maxSeats
|
||||||
|
,playersVpi, playersAtStreet1, playersAtStreet2
|
||||||
|
,playersAtStreet3, playersAtStreet4, playersAtShowdown
|
||||||
|
,street0Raises, street1Raises, street2Raises
|
||||||
|
,street3Raises, street4Raises, street1Pot
|
||||||
|
,street2Pot, street3Pot, street4Pot
|
||||||
|
,showdownPot
|
||||||
|
)
|
||||||
|
VALUES
|
||||||
|
(%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s)"""
|
||||||
|
,(p['siteHandNo'], gametype_id, p['handStart'], len(names), p['tableName'], datetime.datetime.today(), p['maxSeats']
|
||||||
|
,hudCache['playersVpi'], hudCache['playersAtStreet1'], hudCache['playersAtStreet2']
|
||||||
|
,hudCache['playersAtStreet3'], hudCache['playersAtStreet4'], hudCache['playersAtShowdown']
|
||||||
|
,hudCache['street0Raises'], hudCache['street1Raises'], hudCache['street2Raises']
|
||||||
|
,hudCache['street3Raises'], hudCache['street4Raises'], hudCache['street1Pot']
|
||||||
|
,hudCache['street2Pot'], hudCache['street3Pot'], hudCache['street4Pot']
|
||||||
|
,hudCache['showdownPot']
|
||||||
|
)
|
||||||
|
)
|
||||||
|
#return getLastInsertId(backend, conn, cursor)
|
||||||
|
#end class fpdb_db
|
||||||
|
|
|
@ -44,6 +44,9 @@ except:
|
||||||
try:
|
try:
|
||||||
import psycopg2
|
import psycopg2
|
||||||
pgsqlLibFound=True
|
pgsqlLibFound=True
|
||||||
|
import psycopg2.extensions
|
||||||
|
psycopg2.extensions.register_type(psycopg2.extensions.UNICODE)
|
||||||
|
|
||||||
except:
|
except:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
@ -150,7 +153,9 @@ class Importer:
|
||||||
self.monitor = True
|
self.monitor = True
|
||||||
self.dirlist[site] = [dir] + [filter]
|
self.dirlist[site] = [dir] + [filter]
|
||||||
|
|
||||||
|
#print "addImportDirectory: checking files in", dir
|
||||||
for file in os.listdir(dir):
|
for file in os.listdir(dir):
|
||||||
|
#print " adding file ", file
|
||||||
self.addImportFile(os.path.join(dir, file), site, filter)
|
self.addImportFile(os.path.join(dir, file), site, filter)
|
||||||
else:
|
else:
|
||||||
print "Warning: Attempted to add non-directory: '" + str(dir) + "' as an import directory"
|
print "Warning: Attempted to add non-directory: '" + str(dir) + "' as an import directory"
|
||||||
|
@ -162,7 +167,7 @@ class Importer:
|
||||||
if self.settings['dropIndexes'] == 'auto':
|
if self.settings['dropIndexes'] == 'auto':
|
||||||
self.settings['dropIndexes'] = self.calculate_auto()
|
self.settings['dropIndexes'] = self.calculate_auto()
|
||||||
if self.settings['dropIndexes'] == 'drop':
|
if self.settings['dropIndexes'] == 'drop':
|
||||||
fpdb_simple.prepareBulkImport(self.fdb)
|
self.fdb.prepareBulkImport()
|
||||||
totstored = 0
|
totstored = 0
|
||||||
totdups = 0
|
totdups = 0
|
||||||
totpartial = 0
|
totpartial = 0
|
||||||
|
@ -177,8 +182,8 @@ class Importer:
|
||||||
toterrors += errors
|
toterrors += errors
|
||||||
tottime += ttime
|
tottime += ttime
|
||||||
if self.settings['dropIndexes'] == 'drop':
|
if self.settings['dropIndexes'] == 'drop':
|
||||||
fpdb_simple.afterBulkImport(self.fdb)
|
self.fdb.afterBulkImport()
|
||||||
fpdb_simple.analyzeDB(self.fdb)
|
self.fdb.analyzeDB()
|
||||||
return (totstored, totdups, totpartial, toterrors, tottime)
|
return (totstored, totdups, totpartial, toterrors, tottime)
|
||||||
# else: import threaded
|
# else: import threaded
|
||||||
|
|
||||||
|
@ -203,14 +208,18 @@ class Importer:
|
||||||
#todo: make efficient - always checks for new file, should be able to use mtime of directory
|
#todo: make efficient - always checks for new file, should be able to use mtime of directory
|
||||||
# ^^ May not work on windows
|
# ^^ May not work on windows
|
||||||
|
|
||||||
|
#rulog = open('runUpdated.txt', 'a')
|
||||||
|
#rulog.writelines("runUpdated ... ")
|
||||||
for site in self.dirlist:
|
for site in self.dirlist:
|
||||||
self.addImportDirectory(self.dirlist[site][0], False, site, self.dirlist[site][1])
|
self.addImportDirectory(self.dirlist[site][0], False, site, self.dirlist[site][1])
|
||||||
|
|
||||||
for file in self.filelist:
|
for file in self.filelist:
|
||||||
if os.path.exists(file):
|
if os.path.exists(file):
|
||||||
stat_info = os.stat(file)
|
stat_info = os.stat(file)
|
||||||
|
#rulog.writelines("path exists ")
|
||||||
try:
|
try:
|
||||||
lastupdate = self.updated[file]
|
lastupdate = self.updated[file]
|
||||||
|
#rulog.writelines("lastupdate = %d, mtime = %d" % (lastupdate,stat_info.st_mtime))
|
||||||
if stat_info.st_mtime > lastupdate:
|
if stat_info.st_mtime > lastupdate:
|
||||||
self.import_file_dict(file, self.filelist[file][0], self.filelist[file][1])
|
self.import_file_dict(file, self.filelist[file][0], self.filelist[file][1])
|
||||||
self.updated[file] = time()
|
self.updated[file] = time()
|
||||||
|
@ -236,7 +245,8 @@ class Importer:
|
||||||
self.addToDirList = {}
|
self.addToDirList = {}
|
||||||
self.removeFromFileList = {}
|
self.removeFromFileList = {}
|
||||||
self.fdb.db.rollback()
|
self.fdb.db.rollback()
|
||||||
|
#rulog.writelines(" finished\n")
|
||||||
|
#rulog.close()
|
||||||
|
|
||||||
# This is now an internal function that should not be called directly.
|
# This is now an internal function that should not be called directly.
|
||||||
def import_file_dict(self, file, site, filter):
|
def import_file_dict(self, file, site, filter):
|
||||||
|
@ -249,7 +259,7 @@ class Importer:
|
||||||
conv = None
|
conv = None
|
||||||
# Load filter, process file, pass returned filename to import_fpdb_file
|
# Load filter, process file, pass returned filename to import_fpdb_file
|
||||||
|
|
||||||
print "converting %s" % file
|
print "\nConverting %s" % file
|
||||||
hhbase = self.config.get_import_parameters().get("hhArchiveBase")
|
hhbase = self.config.get_import_parameters().get("hhArchiveBase")
|
||||||
hhbase = os.path.expanduser(hhbase)
|
hhbase = os.path.expanduser(hhbase)
|
||||||
hhdir = os.path.join(hhbase,site)
|
hhdir = os.path.join(hhbase,site)
|
||||||
|
@ -282,6 +292,7 @@ class Importer:
|
||||||
starttime = time()
|
starttime = time()
|
||||||
last_read_hand = 0
|
last_read_hand = 0
|
||||||
loc = 0
|
loc = 0
|
||||||
|
#print "file =", file
|
||||||
if file == "stdin":
|
if file == "stdin":
|
||||||
inputFile = sys.stdin
|
inputFile = sys.stdin
|
||||||
else:
|
else:
|
||||||
|
@ -292,10 +303,17 @@ class Importer:
|
||||||
return (0, 0, 0, 1, 0)
|
return (0, 0, 0, 1, 0)
|
||||||
try:
|
try:
|
||||||
loc = self.pos_in_file[file]
|
loc = self.pos_in_file[file]
|
||||||
|
#size = os.path.getsize(file)
|
||||||
|
#print "loc =", loc, 'size =', size
|
||||||
except:
|
except:
|
||||||
pass
|
pass
|
||||||
# Read input file into class and close file
|
# Read input file into class and close file
|
||||||
inputFile.seek(loc)
|
inputFile.seek(loc)
|
||||||
|
#tmplines = inputFile.readlines()
|
||||||
|
#if tmplines == None or tmplines == []:
|
||||||
|
# print "tmplines = ", tmplines
|
||||||
|
#else:
|
||||||
|
# print "tmplines[0] =", tmplines[0]
|
||||||
self.lines = fpdb_simple.removeTrailingEOL(inputFile.readlines())
|
self.lines = fpdb_simple.removeTrailingEOL(inputFile.readlines())
|
||||||
self.pos_in_file[file] = inputFile.tell()
|
self.pos_in_file[file] = inputFile.tell()
|
||||||
inputFile.close()
|
inputFile.close()
|
||||||
|
@ -303,7 +321,8 @@ class Importer:
|
||||||
try: # sometimes we seem to be getting an empty self.lines, in which case, we just want to return.
|
try: # sometimes we seem to be getting an empty self.lines, in which case, we just want to return.
|
||||||
firstline = self.lines[0]
|
firstline = self.lines[0]
|
||||||
except:
|
except:
|
||||||
print "DEBUG: import_fpdb_file: failed on self.lines[0]: '%s' '%s' '%s' '%s' " %( file, site, self.lines, loc)
|
# just skip the debug message and return silently:
|
||||||
|
#print "DEBUG: import_fpdb_file: failed on self.lines[0]: '%s' '%s' '%s' '%s' " %( file, site, self.lines, loc)
|
||||||
return (0,0,0,1,0)
|
return (0,0,0,1,0)
|
||||||
|
|
||||||
if firstline.find("Tournament Summary")!=-1:
|
if firstline.find("Tournament Summary")!=-1:
|
||||||
|
@ -348,6 +367,7 @@ class Importer:
|
||||||
if self.callHud:
|
if self.callHud:
|
||||||
#print "call to HUD here. handsId:",handsId
|
#print "call to HUD here. handsId:",handsId
|
||||||
#pipe the Hands.id out to the HUD
|
#pipe the Hands.id out to the HUD
|
||||||
|
print "sending hand to hud", handsId, "pipe =", self.caller.pipe_to_hud
|
||||||
self.caller.pipe_to_hud.stdin.write("%s" % (handsId) + os.linesep)
|
self.caller.pipe_to_hud.stdin.write("%s" % (handsId) + os.linesep)
|
||||||
except fpdb_simple.DuplicateError:
|
except fpdb_simple.DuplicateError:
|
||||||
duplicates += 1
|
duplicates += 1
|
||||||
|
@ -364,7 +384,6 @@ class Importer:
|
||||||
except (fpdb_simple.FpdbError), fe:
|
except (fpdb_simple.FpdbError), fe:
|
||||||
errors += 1
|
errors += 1
|
||||||
self.printEmailErrorMessage(errors, file, hand)
|
self.printEmailErrorMessage(errors, file, hand)
|
||||||
|
|
||||||
self.fdb.db.rollback()
|
self.fdb.db.rollback()
|
||||||
|
|
||||||
if self.settings['failOnError']:
|
if self.settings['failOnError']:
|
||||||
|
|
|
@ -26,13 +26,14 @@ MYSQL_INNODB = 2
|
||||||
PGSQL = 3
|
PGSQL = 3
|
||||||
SQLITE = 4
|
SQLITE = 4
|
||||||
|
|
||||||
fastStoreHudCache = False # set this to True to test the new storeHudCache routine
|
#fastStoreHudCache = False # set this to True to test the new storeHudCache routine
|
||||||
|
#
|
||||||
|
#saveActions = True # set this to False to avoid storing action data
|
||||||
|
# # Pros: speeds up imports
|
||||||
|
# # Cons: no action data is saved, so you need to keep the hand histories
|
||||||
|
# # variance not available on stats page
|
||||||
|
# # no graphs
|
||||||
|
|
||||||
saveActions = True # set this to False to avoid storing action data
|
|
||||||
# Pros: speeds up imports
|
|
||||||
# Cons: no action data is saved, so you need to keep the hand histories
|
|
||||||
# variance not available on stats page
|
|
||||||
# : No graphs
|
|
||||||
#stores a stud/razz hand into the database
|
#stores a stud/razz hand into the database
|
||||||
def ring_stud(config, backend, db, cursor, base, category, site_hand_no, gametype_id, hand_start_time
|
def ring_stud(config, backend, db, cursor, base, category, site_hand_no, gametype_id, hand_start_time
|
||||||
,names, player_ids, start_cashes, antes, card_values, card_suits, winnings, rakes
|
,names, player_ids, start_cashes, antes, card_values, card_suits, winnings, rakes
|
||||||
|
|
|
@ -17,6 +17,10 @@
|
||||||
|
|
||||||
#This file contains simple functions for fpdb
|
#This file contains simple functions for fpdb
|
||||||
|
|
||||||
|
#Aiming to eventually remove this module, functions will move to, eg:
|
||||||
|
#fpdb_db db create/re-create/management/etc
|
||||||
|
#Hands or related files for saving hands to db, etc
|
||||||
|
|
||||||
import datetime
|
import datetime
|
||||||
import time
|
import time
|
||||||
import re
|
import re
|
||||||
|
@ -28,6 +32,7 @@ PS = 1
|
||||||
FTP = 2
|
FTP = 2
|
||||||
|
|
||||||
# TODO: these constants are also used in fpdb_save_to_db and others, is there a way to do like C #define, and #include ?
|
# TODO: these constants are also used in fpdb_save_to_db and others, is there a way to do like C #define, and #include ?
|
||||||
|
# answer - yes. These are defined in fpdb_db so are accessible through that class.
|
||||||
MYSQL_INNODB = 2
|
MYSQL_INNODB = 2
|
||||||
PGSQL = 3
|
PGSQL = 3
|
||||||
SQLITE = 4
|
SQLITE = 4
|
||||||
|
@ -35,359 +40,6 @@ SQLITE = 4
|
||||||
# config while trying out new hudcache mechanism
|
# config while trying out new hudcache mechanism
|
||||||
use_date_in_hudcache = True
|
use_date_in_hudcache = True
|
||||||
|
|
||||||
# Data Structures for index and foreign key creation
|
|
||||||
# drop_code is an int with possible values: 0 - don't drop for bulk import
|
|
||||||
# 1 - drop during bulk import
|
|
||||||
# db differences:
|
|
||||||
# - note that mysql automatically creates indexes on constrained columns when
|
|
||||||
# foreign keys are created, while postgres does not. Hence the much longer list
|
|
||||||
# of indexes is required for postgres.
|
|
||||||
# all primary keys are left on all the time
|
|
||||||
#
|
|
||||||
# table column drop_code
|
|
||||||
|
|
||||||
indexes = [
|
|
||||||
[ ] # no db with index 0
|
|
||||||
, [ ] # no db with index 1
|
|
||||||
, [ # indexes for mysql (list index 2)
|
|
||||||
{'tab':'Players', 'col':'name', 'drop':0}
|
|
||||||
, {'tab':'Hands', 'col':'siteHandNo', 'drop':0}
|
|
||||||
, {'tab':'Tourneys', 'col':'siteTourneyNo', 'drop':0}
|
|
||||||
]
|
|
||||||
, [ # indexes for postgres (list index 3)
|
|
||||||
{'tab':'Boardcards', 'col':'handId', 'drop':0}
|
|
||||||
, {'tab':'Gametypes', 'col':'siteId', 'drop':0}
|
|
||||||
, {'tab':'Hands', 'col':'gametypeId', 'drop':0} # mct 22/3/09
|
|
||||||
, {'tab':'Hands', 'col':'siteHandNo', 'drop':0}
|
|
||||||
, {'tab':'HandsActions', 'col':'handsPlayerId', 'drop':0}
|
|
||||||
, {'tab':'HandsPlayers', 'col':'handId', 'drop':1}
|
|
||||||
, {'tab':'HandsPlayers', 'col':'playerId', 'drop':1}
|
|
||||||
, {'tab':'HandsPlayers', 'col':'tourneysPlayersId', 'drop':0}
|
|
||||||
, {'tab':'HudCache', 'col':'gametypeId', 'drop':1}
|
|
||||||
, {'tab':'HudCache', 'col':'playerId', 'drop':0}
|
|
||||||
, {'tab':'HudCache', 'col':'tourneyTypeId', 'drop':0}
|
|
||||||
, {'tab':'Players', 'col':'siteId', 'drop':1}
|
|
||||||
, {'tab':'Players', 'col':'name', 'drop':0}
|
|
||||||
, {'tab':'Tourneys', 'col':'tourneyTypeId', 'drop':1}
|
|
||||||
, {'tab':'Tourneys', 'col':'siteTourneyNo', 'drop':0}
|
|
||||||
, {'tab':'TourneysPlayers', 'col':'playerId', 'drop':0}
|
|
||||||
, {'tab':'TourneysPlayers', 'col':'tourneyId', 'drop':0}
|
|
||||||
, {'tab':'TourneyTypes', 'col':'siteId', 'drop':0}
|
|
||||||
]
|
|
||||||
]
|
|
||||||
|
|
||||||
foreignKeys = [
|
|
||||||
[ ] # no db with index 0
|
|
||||||
, [ ] # no db with index 1
|
|
||||||
, [ # foreign keys for mysql
|
|
||||||
{'fktab':'Hands', 'fkcol':'gametypeId', 'rtab':'Gametypes', 'rcol':'id', 'drop':1}
|
|
||||||
, {'fktab':'HandsPlayers', 'fkcol':'handId', 'rtab':'Hands', 'rcol':'id', 'drop':1}
|
|
||||||
, {'fktab':'HandsPlayers', 'fkcol':'playerId', 'rtab':'Players', 'rcol':'id', 'drop':1}
|
|
||||||
, {'fktab':'HandsActions', 'fkcol':'handsPlayerId', 'rtab':'HandsPlayers', 'rcol':'id', 'drop':1}
|
|
||||||
, {'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}
|
|
||||||
]
|
|
||||||
, [ # foreign keys for postgres
|
|
||||||
{'fktab':'Hands', 'fkcol':'gametypeId', 'rtab':'Gametypes', 'rcol':'id', 'drop':1}
|
|
||||||
, {'fktab':'HandsPlayers', 'fkcol':'handId', 'rtab':'Hands', 'rcol':'id', 'drop':1}
|
|
||||||
, {'fktab':'HandsPlayers', 'fkcol':'playerId', 'rtab':'Players', 'rcol':'id', 'drop':1}
|
|
||||||
, {'fktab':'HandsActions', 'fkcol':'handsPlayerId', 'rtab':'HandsPlayers', 'rcol':'id', 'drop':1}
|
|
||||||
, {'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}
|
|
||||||
]
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
# MySQL Notes:
|
|
||||||
# "FOREIGN KEY (handId) REFERENCES Hands(id)" - requires index on Hands.id
|
|
||||||
# - creates index handId on <thistable>.handId
|
|
||||||
# alter table t drop foreign key fk
|
|
||||||
# alter table t add foreign key (fkcol) references tab(rcol)
|
|
||||||
# alter table t add constraint c foreign key (fkcol) references tab(rcol)
|
|
||||||
# (fkcol is used for foreigh key name)
|
|
||||||
|
|
||||||
# mysql to list indexes:
|
|
||||||
# SELECT table_name, index_name, non_unique, column_name
|
|
||||||
# FROM INFORMATION_SCHEMA.STATISTICS
|
|
||||||
# WHERE table_name = 'tbl_name'
|
|
||||||
# AND table_schema = 'db_name'
|
|
||||||
# ORDER BY table_name, index_name, seq_in_index
|
|
||||||
#
|
|
||||||
# ALTER TABLE Tourneys ADD INDEX siteTourneyNo(siteTourneyNo)
|
|
||||||
# ALTER TABLE tab DROP INDEX idx
|
|
||||||
|
|
||||||
# mysql to list fks:
|
|
||||||
# SELECT constraint_name, table_name, column_name, referenced_table_name, referenced_column_name
|
|
||||||
# FROM information_schema.KEY_COLUMN_USAGE
|
|
||||||
# WHERE REFERENCED_TABLE_SCHEMA = (your schema name here)
|
|
||||||
# AND REFERENCED_TABLE_NAME is not null
|
|
||||||
# ORDER BY TABLE_NAME, COLUMN_NAME;
|
|
||||||
|
|
||||||
# this may indicate missing object
|
|
||||||
# _mysql_exceptions.OperationalError: (1025, "Error on rename of '.\\fpdb\\hands' to '.\\fpdb\\#sql2-7f0-1b' (errno: 152)")
|
|
||||||
|
|
||||||
|
|
||||||
# PG notes:
|
|
||||||
|
|
||||||
# To add a foreign key constraint to a table:
|
|
||||||
# ALTER TABLE tab ADD CONSTRAINT c FOREIGN KEY (col) REFERENCES t2(col2) MATCH FULL;
|
|
||||||
# ALTER TABLE tab DROP CONSTRAINT zipchk
|
|
||||||
#
|
|
||||||
# Note: index names must be unique across a schema
|
|
||||||
# CREATE INDEX idx ON tab(col)
|
|
||||||
# DROP INDEX idx
|
|
||||||
|
|
||||||
def prepareBulkImport(fdb):
|
|
||||||
"""Drop some indexes/foreign keys to prepare for bulk import.
|
|
||||||
Currently keeping the standalone indexes as needed to import quickly"""
|
|
||||||
# fdb is a fpdb_db object including backend, db, cursor, sql variables
|
|
||||||
if fdb.backend == PGSQL:
|
|
||||||
fdb.db.set_isolation_level(0) # allow table/index operations to work
|
|
||||||
for fk in foreignKeys[fdb.backend]:
|
|
||||||
if fk['drop'] == 1:
|
|
||||||
if fdb.backend == MYSQL_INNODB:
|
|
||||||
fdb.cursor.execute("SELECT constraint_name " +
|
|
||||||
"FROM information_schema.KEY_COLUMN_USAGE " +
|
|
||||||
#"WHERE REFERENCED_TABLE_SCHEMA = 'fpdb'
|
|
||||||
"WHERE 1=1 " +
|
|
||||||
"AND table_name = %s AND column_name = %s " +
|
|
||||||
"AND referenced_table_name = %s " +
|
|
||||||
"AND referenced_column_name = %s ",
|
|
||||||
(fk['fktab'], fk['fkcol'], fk['rtab'], fk['rcol']) )
|
|
||||||
cons = fdb.cursor.fetchone()
|
|
||||||
#print "preparebulk: cons=", cons
|
|
||||||
if cons:
|
|
||||||
print "dropping mysql fk", cons[0], fk['fktab'], fk['fkcol']
|
|
||||||
try:
|
|
||||||
fdb.cursor.execute("alter table " + fk['fktab'] + " drop foreign key " + cons[0])
|
|
||||||
except:
|
|
||||||
pass
|
|
||||||
elif fdb.backend == PGSQL:
|
|
||||||
# DON'T FORGET TO RECREATE THEM!!
|
|
||||||
print "dropping pg fk", fk['fktab'], fk['fkcol']
|
|
||||||
try:
|
|
||||||
# try to lock table to see if index drop will work:
|
|
||||||
# hmmm, tested by commenting out rollback in grapher. lock seems to work but
|
|
||||||
# then drop still hangs :-( does work in some tests though??
|
|
||||||
# will leave code here for now pending further tests/enhancement ...
|
|
||||||
fdb.cursor.execute( "lock table %s in exclusive mode nowait" % (fk['fktab'],) )
|
|
||||||
#print "after lock, status:", fdb.cursor.statusmessage
|
|
||||||
#print "alter table %s drop constraint %s_%s_fkey" % (fk['fktab'], fk['fktab'], fk['fkcol'])
|
|
||||||
try:
|
|
||||||
fdb.cursor.execute("alter table %s drop constraint %s_%s_fkey" % (fk['fktab'], fk['fktab'], fk['fkcol']))
|
|
||||||
print "dropped pg fk pg fk %s_%s_fkey, continuing ..." % (fk['fktab'], fk['fkcol'])
|
|
||||||
except:
|
|
||||||
if "does not exist" not in str(sys.exc_value):
|
|
||||||
print "warning: drop pg fk %s_%s_fkey failed: %s, continuing ..." \
|
|
||||||
% (fk['fktab'], fk['fkcol'], str(sys.exc_value).rstrip('\n') )
|
|
||||||
except:
|
|
||||||
print "warning: constraint %s_%s_fkey not dropped: %s, continuing ..." \
|
|
||||||
% (fk['fktab'],fk['fkcol'], str(sys.exc_value).rstrip('\n'))
|
|
||||||
else:
|
|
||||||
print "Only MySQL and Postgres supported so far"
|
|
||||||
return -1
|
|
||||||
|
|
||||||
for idx in indexes[fdb.backend]:
|
|
||||||
if idx['drop'] == 1:
|
|
||||||
if fdb.backend == MYSQL_INNODB:
|
|
||||||
print "dropping mysql index ", idx['tab'], idx['col']
|
|
||||||
try:
|
|
||||||
# apparently nowait is not implemented in mysql so this just hands if there are locks
|
|
||||||
# preventing the index drop :-(
|
|
||||||
fdb.cursor.execute( "alter table %s drop index %s", (idx['tab'],idx['col']) )
|
|
||||||
except:
|
|
||||||
pass
|
|
||||||
elif fdb.backend == PGSQL:
|
|
||||||
# DON'T FORGET TO RECREATE THEM!!
|
|
||||||
print "dropping pg index ", idx['tab'], idx['col']
|
|
||||||
try:
|
|
||||||
# try to lock table to see if index drop will work:
|
|
||||||
fdb.cursor.execute( "lock table %s in exclusive mode nowait" % (idx['tab'],) )
|
|
||||||
#print "after lock, status:", fdb.cursor.statusmessage
|
|
||||||
try:
|
|
||||||
# table locked ok so index drop should work:
|
|
||||||
#print "drop index %s_%s_idx" % (idx['tab'],idx['col'])
|
|
||||||
fdb.cursor.execute( "drop index if exists %s_%s_idx" % (idx['tab'],idx['col']) )
|
|
||||||
#print "dropped pg index ", idx['tab'], idx['col']
|
|
||||||
except:
|
|
||||||
if "does not exist" not in str(sys.exc_value):
|
|
||||||
print "warning: drop index %s_%s_idx failed: %s, continuing ..." \
|
|
||||||
% (idx['tab'],idx['col'], str(sys.exc_value).rstrip('\n'))
|
|
||||||
except:
|
|
||||||
print "warning: index %s_%s_idx not dropped %s, continuing ..." \
|
|
||||||
% (idx['tab'],idx['col'], str(sys.exc_value).rstrip('\n'))
|
|
||||||
else:
|
|
||||||
print "Error: Only MySQL and Postgres supported so far"
|
|
||||||
return -1
|
|
||||||
|
|
||||||
if fdb.backend == PGSQL:
|
|
||||||
fdb.db.set_isolation_level(1) # go back to normal isolation level
|
|
||||||
fdb.db.commit() # seems to clear up errors if there were any in postgres
|
|
||||||
#end def prepareBulkImport
|
|
||||||
|
|
||||||
def afterBulkImport(fdb):
|
|
||||||
"""Re-create any dropped indexes/foreign keys after bulk import"""
|
|
||||||
# fdb is a fpdb_db object including backend, db, cursor, sql variables
|
|
||||||
if fdb.backend == PGSQL:
|
|
||||||
fdb.db.set_isolation_level(0) # allow table/index operations to work
|
|
||||||
for fk in foreignKeys[fdb.backend]:
|
|
||||||
if fk['drop'] == 1:
|
|
||||||
if fdb.backend == MYSQL_INNODB:
|
|
||||||
fdb.cursor.execute("SELECT constraint_name " +
|
|
||||||
"FROM information_schema.KEY_COLUMN_USAGE " +
|
|
||||||
#"WHERE REFERENCED_TABLE_SCHEMA = 'fpdb'
|
|
||||||
"WHERE 1=1 " +
|
|
||||||
"AND table_name = %s AND column_name = %s " +
|
|
||||||
"AND referenced_table_name = %s " +
|
|
||||||
"AND referenced_column_name = %s ",
|
|
||||||
(fk['fktab'], fk['fkcol'], fk['rtab'], fk['rcol']) )
|
|
||||||
cons = fdb.cursor.fetchone()
|
|
||||||
print "afterbulk: cons=", cons
|
|
||||||
if cons:
|
|
||||||
pass
|
|
||||||
else:
|
|
||||||
print "creating fk ", fk['fktab'], fk['fkcol'], "->", fk['rtab'], fk['rcol']
|
|
||||||
try:
|
|
||||||
fdb.cursor.execute("alter table " + fk['fktab'] + " add foreign key ("
|
|
||||||
+ fk['fkcol'] + ") references " + fk['rtab'] + "("
|
|
||||||
+ fk['rcol'] + ")")
|
|
||||||
except:
|
|
||||||
pass
|
|
||||||
elif fdb.backend == PGSQL:
|
|
||||||
print "creating fk ", fk['fktab'], fk['fkcol'], "->", fk['rtab'], fk['rcol']
|
|
||||||
try:
|
|
||||||
fdb.cursor.execute("alter table " + fk['fktab'] + " add constraint "
|
|
||||||
+ fk['fktab'] + '_' + fk['fkcol'] + '_fkey'
|
|
||||||
+ " foreign key (" + fk['fkcol']
|
|
||||||
+ ") references " + fk['rtab'] + "(" + fk['rcol'] + ")")
|
|
||||||
except:
|
|
||||||
pass
|
|
||||||
else:
|
|
||||||
print "Only MySQL and Postgres supported so far"
|
|
||||||
return -1
|
|
||||||
|
|
||||||
for idx in indexes[fdb.backend]:
|
|
||||||
if idx['drop'] == 1:
|
|
||||||
if fdb.backend == MYSQL_INNODB:
|
|
||||||
print "creating mysql index ", idx['tab'], idx['col']
|
|
||||||
try:
|
|
||||||
fdb.cursor.execute( "alter table %s add index %s(%s)"
|
|
||||||
, (idx['tab'],idx['col'],idx['col']) )
|
|
||||||
except:
|
|
||||||
pass
|
|
||||||
elif fdb.backend == PGSQL:
|
|
||||||
# pass
|
|
||||||
# mod to use tab_col for index name?
|
|
||||||
print "creating pg index ", idx['tab'], idx['col']
|
|
||||||
try:
|
|
||||||
print "create index %s_%s_idx on %s(%s)" % (idx['tab'], idx['col'], idx['tab'], idx['col'])
|
|
||||||
fdb.cursor.execute( "create index %s_%s_idx on %s(%s)"
|
|
||||||
% (idx['tab'], idx['col'], idx['tab'], idx['col']) )
|
|
||||||
except:
|
|
||||||
print " ERROR! :-("
|
|
||||||
pass
|
|
||||||
else:
|
|
||||||
print "Only MySQL and Postgres supported so far"
|
|
||||||
return -1
|
|
||||||
|
|
||||||
if fdb.backend == PGSQL:
|
|
||||||
fdb.db.set_isolation_level(1) # go back to normal isolation level
|
|
||||||
fdb.db.commit() # seems to clear up errors if there were any in postgres
|
|
||||||
#end def afterBulkImport
|
|
||||||
|
|
||||||
def createAllIndexes(fdb):
|
|
||||||
"""Create new indexes"""
|
|
||||||
if fdb.backend == PGSQL:
|
|
||||||
fdb.db.set_isolation_level(0) # allow table/index operations to work
|
|
||||||
for idx in indexes[fdb.backend]:
|
|
||||||
if fdb.backend == MYSQL_INNODB:
|
|
||||||
print "creating mysql index ", idx['tab'], idx['col']
|
|
||||||
try:
|
|
||||||
fdb.cursor.execute( "alter table %s add index %s(%s)"
|
|
||||||
, (idx['tab'],idx['col'],idx['col']) )
|
|
||||||
except:
|
|
||||||
pass
|
|
||||||
elif fdb.backend == PGSQL:
|
|
||||||
# mod to use tab_col for index name?
|
|
||||||
print "creating pg index ", idx['tab'], idx['col']
|
|
||||||
try:
|
|
||||||
print "create index %s_%s_idx on %s(%s)" % (idx['tab'], idx['col'], idx['tab'], idx['col'])
|
|
||||||
fdb.cursor.execute( "create index %s_%s_idx on %s(%s)"
|
|
||||||
% (idx['tab'], idx['col'], idx['tab'], idx['col']) )
|
|
||||||
except:
|
|
||||||
print " ERROR! :-("
|
|
||||||
pass
|
|
||||||
else:
|
|
||||||
print "Only MySQL and Postgres supported so far"
|
|
||||||
return -1
|
|
||||||
if fdb.backend == PGSQL:
|
|
||||||
fdb.db.set_isolation_level(1) # go back to normal isolation level
|
|
||||||
#end def createAllIndexes
|
|
||||||
|
|
||||||
def dropAllIndexes(fdb):
|
|
||||||
"""Drop all standalone indexes (i.e. not including primary keys or foreign keys)
|
|
||||||
using list of indexes in indexes data structure"""
|
|
||||||
# maybe upgrade to use data dictionary?? (but take care to exclude PK and FK)
|
|
||||||
if fdb.backend == PGSQL:
|
|
||||||
fdb.db.set_isolation_level(0) # allow table/index operations to work
|
|
||||||
for idx in indexes[fdb.backend]:
|
|
||||||
if fdb.backend == MYSQL_INNODB:
|
|
||||||
print "dropping mysql index ", idx['tab'], idx['col']
|
|
||||||
try:
|
|
||||||
fdb.cursor.execute( "alter table %s drop index %s"
|
|
||||||
, (idx['tab'],idx['col']) )
|
|
||||||
except:
|
|
||||||
pass
|
|
||||||
elif fdb.backend == PGSQL:
|
|
||||||
print "dropping pg index ", idx['tab'], idx['col']
|
|
||||||
# mod to use tab_col for index name?
|
|
||||||
try:
|
|
||||||
fdb.cursor.execute( "drop index %s_%s_idx"
|
|
||||||
% (idx['tab'],idx['col']) )
|
|
||||||
except:
|
|
||||||
pass
|
|
||||||
else:
|
|
||||||
print "Only MySQL and Postgres supported so far"
|
|
||||||
return -1
|
|
||||||
if fdb.backend == PGSQL:
|
|
||||||
fdb.db.set_isolation_level(1) # go back to normal isolation level
|
|
||||||
#end def dropAllIndexes
|
|
||||||
|
|
||||||
def analyzeDB(fdb):
|
|
||||||
"""Do whatever the DB can offer to update index/table statistics"""
|
|
||||||
if fdb.backend == PGSQL:
|
|
||||||
fdb.db.set_isolation_level(0) # allow vacuum to work
|
|
||||||
try:
|
|
||||||
fdb.cursor.execute("vacuum analyze")
|
|
||||||
except:
|
|
||||||
print "Error during vacuum"
|
|
||||||
fdb.db.set_isolation_level(1) # go back to normal isolation level
|
|
||||||
fdb.db.commit()
|
|
||||||
#end def analyzeDB
|
|
||||||
|
|
||||||
def get_global_lock(fdb):
|
|
||||||
if fdb.backend == MYSQL_INNODB:
|
|
||||||
try:
|
|
||||||
fdb.cursor.execute( "lock tables Hands write" )
|
|
||||||
except:
|
|
||||||
print "Error! failed to obtain global lock. Close all programs accessing " \
|
|
||||||
+ "database (including fpdb) and try again (%s)." \
|
|
||||||
% ( str(sys.exc_value).rstrip('\n'), )
|
|
||||||
return(False)
|
|
||||||
elif fdb.backend == PGSQL:
|
|
||||||
try:
|
|
||||||
fdb.cursor.execute( "lock table Hands in exclusive mode nowait" )
|
|
||||||
#print "... after lock table, status =", fdb.cursor.statusmessage
|
|
||||||
except:
|
|
||||||
print "Error! failed to obtain global lock. Close all programs accessing " \
|
|
||||||
+ "database (including fpdb) and try again (%s)." \
|
|
||||||
% ( str(sys.exc_value).rstrip('\n'), )
|
|
||||||
return(False)
|
|
||||||
return(True)
|
|
||||||
|
|
||||||
|
|
||||||
class DuplicateError(Exception):
|
class DuplicateError(Exception):
|
||||||
def __init__(self, value):
|
def __init__(self, value):
|
||||||
self.value = value
|
self.value = value
|
||||||
|
@ -1390,6 +1042,27 @@ def recognisePlayerIDs(cursor, names, site_id):
|
||||||
#end def recognisePlayerIDs
|
#end def recognisePlayerIDs
|
||||||
|
|
||||||
|
|
||||||
|
# Here's a version that would work if it wasn't for the fact that it needs to have the output in the same order as input
|
||||||
|
# this version could also be improved upon using list comprehensions, etc
|
||||||
|
|
||||||
|
#def recognisePlayerIDs(cursor, names, site_id):
|
||||||
|
# result = []
|
||||||
|
# notfound = []
|
||||||
|
# cursor.execute("SELECT name,id FROM Players WHERE name='%s'" % "' OR name='".join(names))
|
||||||
|
# tmp = dict(cursor.fetchall())
|
||||||
|
# for n in names:
|
||||||
|
# if n not in tmp:
|
||||||
|
# notfound.append(n)
|
||||||
|
# else:
|
||||||
|
# result.append(tmp[n])
|
||||||
|
# if notfound:
|
||||||
|
# cursor.executemany("INSERT INTO Players (name, siteId) VALUES (%s, "+str(site_id)+")", (notfound))
|
||||||
|
# cursor.execute("SELECT id FROM Players WHERE name='%s'" % "' OR name='".join(notfound))
|
||||||
|
# tmp = cursor.fetchall()
|
||||||
|
# for n in tmp:
|
||||||
|
# result.append(n[0])
|
||||||
|
#
|
||||||
|
# return result
|
||||||
|
|
||||||
#recognises the name in the given line and returns its array position in the given array
|
#recognises the name in the given line and returns its array position in the given array
|
||||||
def recognisePlayerNo(line, names, atype):
|
def recognisePlayerNo(line, names, atype):
|
||||||
|
|
|
@ -16,24 +16,12 @@ def testPokerStarsHHDate():
|
||||||
datetime.datetime(2008,9,7,11,23,14))
|
datetime.datetime(2008,9,7,11,23,14))
|
||||||
)
|
)
|
||||||
|
|
||||||
def testFullTiltHHDate():
|
#def testTableDetection():
|
||||||
sitngo1 = "Full Tilt Poker Game #10311865543: $1 + $0.25 Sit & Go (78057629), Table 1 - 25/50 - No Limit Hold'em - 0:07:45 ET - 2009/01/29"
|
# result = Tables.clean_title("French (deep)")
|
||||||
cash1 = "Full Tilt Poker Game #9403951181: Table CR - tay - $0.05/$0.10 - No Limit Hold'em - 9:40:20 ET - 2008/12/09"
|
# assert result == "French"
|
||||||
cash2 = "Full Tilt Poker Game #9468383505: Table Bike (deep 6) - $0.05/$0.10 - No Limit Hold'em - 5:09:36 ET - 2008/12/13"
|
# result = Tables.clean_title("French (deep) - $0.25/$0.50 - No Limit Hold'em - Logged In As xxxx")
|
||||||
|
# assert result == "French"
|
||||||
result = fpdb_simple.parseHandStartTime(sitngo1,"ftp")
|
#
|
||||||
assert result==datetime.datetime(2009,1,29,05,07,45)
|
# for (header, site, result) in tuples:
|
||||||
result = fpdb_simple.parseHandStartTime(cash1,"ftp")
|
# yield checkDateParse, header, site, result
|
||||||
assert result==datetime.datetime(2008,12,9,14,40,20)
|
|
||||||
result = fpdb_simple.parseHandStartTime(cash2,"ftp")
|
|
||||||
assert result==datetime.datetime(2008,12,13,10,9,36)
|
|
||||||
|
|
||||||
def testTableDetection():
|
|
||||||
result = Tables.clean_title("French (deep)")
|
|
||||||
assert result == "French"
|
|
||||||
result = Tables.clean_title("French (deep) - $0.25/$0.50 - No Limit Hold'em - Logged In As xxxx")
|
|
||||||
assert result == "French"
|
|
||||||
|
|
||||||
for (header, site, result) in tuples:
|
|
||||||
yield checkDateParse, header, site, result
|
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
|
|
||||||
#Copyright 2008 Steffen Jobbagy-Felso
|
#Copyright 2008 Steffen Jobbagy-Felso
|
||||||
|
#Copyright 2009 Ray E. Barker
|
||||||
#This program is free software: you can redistribute it and/or modify
|
#This program is free software: you can redistribute it and/or modify
|
||||||
#it under the terms of the GNU Affero General Public License as published by
|
#it under the terms of the GNU Affero General Public License as published by
|
||||||
#the Free Software Foundation, version 3 of the License.
|
#the Free Software Foundation, version 3 of the License.
|
||||||
|
@ -15,10 +16,22 @@
|
||||||
#In the "official" distribution you can find the license in
|
#In the "official" distribution you can find the license in
|
||||||
#agpl-3.0.txt in the docs folder of the package.
|
#agpl-3.0.txt in the docs folder of the package.
|
||||||
|
|
||||||
|
# This script prepares the compressed distribution files for
|
||||||
|
# uploading to sourceforge.
|
||||||
|
#
|
||||||
|
# Run from the root of your git repo (the folder that has .git in it)
|
||||||
|
|
||||||
|
# USAGE: $ utils/create-release.sh V
|
||||||
|
# where V is the current version. e.g. utils/create-release.sh 0.55
|
||||||
|
|
||||||
#get rid of extraneous stuff
|
#get rid of extraneous stuff
|
||||||
rm regression-test/*.found.txt
|
rm regression-test/*.found.txt
|
||||||
rm regression-test/*.pyc
|
rm regression-test/*.pyc
|
||||||
rm pyfpdb/*.pyc
|
rm pyfpdb/*.pyc
|
||||||
|
rm pyfpdb/*~
|
||||||
|
rm pyfpdb/fpdb-error-log.txt
|
||||||
|
rm pyfpdb/HUD-error.txt
|
||||||
|
rm pyfpdb/hand-errors.txt
|
||||||
|
|
||||||
# make the fpdb_$1.zip file for windows
|
# make the fpdb_$1.zip file for windows
|
||||||
echo "*** making zip file"
|
echo "*** making zip file"
|
||||||
|
|
Loading…
Reference in New Issue
Block a user