git41 - added a bunch of new cache fields containing placeholder data. okay totalProfit should actually be filled correctly already :)
This commit is contained in:
parent
16be2b39f9
commit
0f032bcc5e
|
@ -3,8 +3,6 @@ Everything is subject to change and especially the order will often change. Patc
|
|||
|
||||
alpha2 (release by 17Aug)
|
||||
======
|
||||
fold to CB/2B/3B
|
||||
move version into seperate file for fpdb gui and db
|
||||
auto-import
|
||||
seperate and improve instructions for update
|
||||
verify link in release notes
|
||||
|
@ -13,15 +11,16 @@ expand instructions for profile file, again, the release-creator will cat it.
|
|||
delete old mailing list and create fpdb-announce
|
||||
finish updating filelist
|
||||
update abbreviations.txt
|
||||
fix up bg colours in tv
|
||||
ebuild: symlink doesnt work, USE gtk, more automation, update install-in-gentoo.txt, set permissions in it, copy docs to correct place, use games eclass or whatever to get games group notice, print notice about install-in-gentoo.txt and mysql --config
|
||||
|
||||
verify positionalness of HudData
|
||||
printhand each and the 2/3 relevant printplayerflags respectively on ps-lhe-ring-successful-steal-by-cutoff.txt and ps-lhe-ring-call-3B-preflop-cb-no2b.txt
|
||||
|
||||
alpha3
|
||||
======
|
||||
check-raise/call-raise on all streets
|
||||
fix up bg colours in tv
|
||||
fill the fold to CB/2B/3B cache fields
|
||||
verify the totalProfit cache field
|
||||
fill the check-/call-raise cache fields
|
||||
move version into seperate file for fpdb gui and db
|
||||
anonymiser script to generate testdata without making a dozen find&replace all... remember to replace hand no with running no
|
||||
table with data for graphs for SD/F, W$wSF, W$@SD
|
||||
SD/F, W$wsF, W$@SD too low as reported by daedal in 2+2 forum on 12/13aug
|
||||
|
@ -29,8 +28,12 @@ show database version error in GUI and use fpdb_db class const for it, add it to
|
|||
split hud data generation into separate for loops and make it more efficient
|
||||
fix bug that sawFlop/Turn/River gets miscalculated if someone is allin - might as well add all-in recognition for this
|
||||
|
||||
verify positionalness of HudData
|
||||
printhand each and the 2/3 relevant printplayerflags respectively on ps-lhe-ring-successful-steal-by-cutoff.txt and ps-lhe-ring-call-3B-preflop-cb-no2b.txt
|
||||
|
||||
before beta
|
||||
===========
|
||||
change most cache fields to bigint to allow extremely big databases in excess of 2 or 4 million hands
|
||||
optionally make tv positional
|
||||
gentoo ebuild: USE postgresql
|
||||
skins
|
||||
|
|
|
@ -15,11 +15,12 @@ Ideally use git (see git-instructions.txt for some commands) and let me know whe
|
|||
|
||||
Contact/Communication
|
||||
=====================
|
||||
If you start working on something please open a bug or feature request at sf to avoid someone else from doing the same
|
||||
Please see readme-overview
|
||||
|
||||
Dependencies
|
||||
============
|
||||
Since all real OSs have easy built in handling for dependencies feel free to add requirements on new libraries etc. Unfortunately due to the reality of the online poker market (namely the complete absence of clients for free/libre systems) it doesn't make sense to write this without supporting Windows so all dependencies must have a source-compatible Windows version. Please ensure to list any new deps in requirements.txt or let me know.
|
||||
Please let me know before you add any new dependencies and ensure that they are source-compatible between *nix and Windows
|
||||
|
||||
Code/File/Class Structure
|
||||
=========================
|
||||
|
|
|
@ -543,16 +543,86 @@ The program itself is licensed under AGPLv3, see agpl-3.0.txt</p>
|
|||
<TD><P>int</P></TD>
|
||||
<TD><P>Player used chance to make third barrel bet</P></TD>
|
||||
</TR>
|
||||
|
||||
<TR VALIGN=TOP>
|
||||
<TD><P>position</P></TD>
|
||||
<TD><P>char(1)</P></TD>
|
||||
<TD><P>Position for which this row applies. In this table this can be B(BB), S(SB), D(Dealer/Button), C(Cutoff), M(Middle - the 3 before cutoff) or E (Early - the 3 before Middle)</P></TD>
|
||||
</TR>
|
||||
<TR VALIGN=TOP>
|
||||
<TD><P>tourneysGametypeId</P></TD>
|
||||
<TD><P>smallint</P></TD>
|
||||
<TD><P>References TourneysGametypes.id</P></TD>
|
||||
</TR>
|
||||
|
||||
<TR VALIGN=TOP>
|
||||
<TD><P>foldToContBetChance</P></TD>
|
||||
<TD><P>int</P></TD>
|
||||
<TD><P>Player had chance to fold to continuation bet</P></TD>
|
||||
</TR>
|
||||
<TR VALIGN=TOP>
|
||||
<TD><P>foldToContBetDone</P></TD>
|
||||
<TD><P>int</P></TD>
|
||||
<TD><P>Player used chance to fold to continuation bet</P></TD>
|
||||
</TR>
|
||||
<TR VALIGN=TOP>
|
||||
<TD><P>foldToSecondBarrelChance</P></TD>
|
||||
<TD><P>int</P></TD>
|
||||
<TD><P>Player had chance to fold to second barrel bet</P></TD>
|
||||
</TR>
|
||||
<TR VALIGN=TOP>
|
||||
<TD><P>foldToSecondBarrelDone</P></TD>
|
||||
<TD><P>int</P></TD>
|
||||
<TD><P>Player used chance to fold to second barrel bet</P></TD>
|
||||
</TR>
|
||||
<TR VALIGN=TOP>
|
||||
<TD><P>foldToThirdBarrelChance</P></TD>
|
||||
<TD><P>int</P></TD>
|
||||
<TD><P>Player had chance to fold to third barrel bet</P></TD>
|
||||
</TR>
|
||||
<TR VALIGN=TOP>
|
||||
<TD><P>foldToThirdBarrelDone</P></TD>
|
||||
<TD><P>int</P></TD>
|
||||
<TD><P>Player used chance to fold to third barrel bet</P></TD>
|
||||
</TR>
|
||||
|
||||
<TR VALIGN=TOP>
|
||||
<TD><P>totalProfit</P></TD>
|
||||
<TD><P>int</P></TD>
|
||||
<TD><P>how much money in cents the player made</P></TD>
|
||||
</TR>
|
||||
|
||||
<TR VALIGN=TOP>
|
||||
<TD><P>flopCheckCallRaiseChance</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 the flop</P></TD>
|
||||
</TR>
|
||||
<TR VALIGN=TOP>
|
||||
<TD><P>flopCheckCallRaiseDone</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 the flop</P></TD>
|
||||
</TR>
|
||||
<TR VALIGN=TOP>
|
||||
<TD><P>turnCheckCallRaiseChance</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 the turn</P></TD>
|
||||
</TR>
|
||||
<TR VALIGN=TOP>
|
||||
<TD><P>turnCheckCallRaiseDone</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 the turn</P></TD>
|
||||
</TR>
|
||||
<TR VALIGN=TOP>
|
||||
<TD><P>riverCheckCallRaiseChance</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 the river</P></TD>
|
||||
</TR>
|
||||
<TR VALIGN=TOP>
|
||||
<TD><P>riverCheckCallRaiseDone</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 the river</P></TD>
|
||||
</TR>
|
||||
|
||||
</TABLE>
|
||||
<P></P>
|
||||
<P><B>Table HandsActions</B></P>
|
||||
|
|
|
@ -363,7 +363,7 @@ This program is licensed under the AGPL3, see docs"""+os.sep+"agpl-3.0.txt")
|
|||
self.window = gtk.Window(gtk.WINDOW_TOPLEVEL)
|
||||
self.window.connect("delete_event", self.delete_event)
|
||||
self.window.connect("destroy", self.destroy)
|
||||
self.window.set_title("Free Poker DB - version: alpha1+, git40")
|
||||
self.window.set_title("Free Poker DB - version: alpha1+, git41")
|
||||
self.window.set_border_width(1)
|
||||
self.window.set_size_request(1020,400)
|
||||
self.window.set_resizable(True)
|
||||
|
|
|
@ -47,8 +47,8 @@ class fpdb_db:
|
|||
try:
|
||||
self.cursor.execute("SELECT * FROM Settings")
|
||||
settings=self.cursor.fetchone()
|
||||
if settings[0]!=40:
|
||||
print "outdated database version - please recreate tables"
|
||||
if settings[0]!=41:
|
||||
print "outdated or too new database version - please recreate tables"
|
||||
except:# _mysql_exceptions.ProgrammingError:
|
||||
print "failed to read settings table - please recreate tables"
|
||||
#end def connect
|
||||
|
@ -314,11 +314,28 @@ class fpdb_db:
|
|||
thirdBarrelDone INT,
|
||||
|
||||
position CHAR(1),
|
||||
tourneysGametypeId SMALLINT UNSIGNED, FOREIGN KEY (tourneysGametypeId) REFERENCES TourneysGametypes(id))""")
|
||||
tourneysGametypeId SMALLINT UNSIGNED, FOREIGN KEY (tourneysGametypeId) REFERENCES TourneysGametypes(id),
|
||||
|
||||
self.cursor.execute("INSERT INTO Settings VALUES (40);")
|
||||
foldToContBetChance INT,
|
||||
foldToContBetDone INT,
|
||||
foldToSecondBarrelChance INT,
|
||||
foldToSecondBarrelDone INT,
|
||||
foldToThirdBarrelChance INT,
|
||||
foldToThirdBarrelDone INT,
|
||||
|
||||
totalProfit INT,
|
||||
|
||||
flopCheckCallRaiseChance INT,
|
||||
flopCheckCallRaiseDone INT,
|
||||
turnCheckCallRaiseChance INT,
|
||||
turnCheckCallRaiseDone INT,
|
||||
riverCheckCallRaiseChance INT,
|
||||
riverCheckCallRaiseDone INT)""")
|
||||
|
||||
self.cursor.execute("INSERT INTO Settings VALUES (41);")
|
||||
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 TourneysGametypes (id) VALUES (DEFAULT);")
|
||||
self.db.commit()
|
||||
print "finished recreating tables"
|
||||
#end def recreate_tables
|
||||
|
|
|
@ -1581,6 +1581,71 @@ def generateHudData(player_ids, category, action_types, actionTypeByNo, winnings
|
|||
result['thirdBarrelDone']=thirdBarrelDone
|
||||
|
||||
result['position']=hudDataPositions
|
||||
|
||||
|
||||
foldToContBetChance=[]
|
||||
foldToContBetDone=[]
|
||||
foldToSecondBarrelChance=[]
|
||||
foldToSecondBarrelDone=[]
|
||||
foldToThirdBarrelChance=[]
|
||||
foldToThirdBarrelDone=[]
|
||||
|
||||
totalProfit=[]
|
||||
|
||||
flopCheckCallRaiseChance=[]
|
||||
flopCheckCallRaiseDone=[]
|
||||
turnCheckCallRaiseChance=[]
|
||||
turnCheckCallRaiseDone=[]
|
||||
riverCheckCallRaiseChance=[]
|
||||
riverCheckCallRaiseDone=[]
|
||||
for player in range (len(player_ids)):
|
||||
myFoldToContBetChance=False
|
||||
myFoldToContBetDone=False
|
||||
myFoldToSecondBarrelChance=False
|
||||
myFoldToSecondBarrelDone=False
|
||||
myFoldToThirdBarrelChance=False
|
||||
myFoldToThirdBarrelDone=False
|
||||
|
||||
myTotalProfit=0
|
||||
|
||||
myFlopCheckCallRaiseChance=False
|
||||
myFlopCheckCallRaiseDone=False
|
||||
myTurnCheckCallRaiseChance=False
|
||||
myTurnCheckCallRaiseDone=False
|
||||
myRiverCheckCallRaiseChance=False
|
||||
myRiverCheckCallRaiseDone=False
|
||||
|
||||
foldToContBetChance.append(myFoldToContBetChance)
|
||||
foldToContBetDone.append(myFoldToContBetDone)
|
||||
foldToSecondBarrelChance.append(myFoldToSecondBarrelChance)
|
||||
foldToSecondBarrelDone.append(myFoldToSecondBarrelDone)
|
||||
foldToThirdBarrelChance.append(myFoldToThirdBarrelChance)
|
||||
foldToThirdBarrelDone.append(myFoldToThirdBarrelDone)
|
||||
|
||||
totalProfit.append(myTotalProfit)
|
||||
|
||||
flopCheckCallRaiseChance.append(myFlopCheckCallRaiseChance)
|
||||
flopCheckCallRaiseDone.append(myFlopCheckCallRaiseDone)
|
||||
turnCheckCallRaiseChance.append(myTurnCheckCallRaiseChance)
|
||||
turnCheckCallRaiseDone.append(myTurnCheckCallRaiseDone)
|
||||
riverCheckCallRaiseChance.append(myRiverCheckCallRaiseChance)
|
||||
riverCheckCallRaiseDone.append(myRiverCheckCallRaiseDone)
|
||||
|
||||
result['foldToContBetChance']=foldToContBetChance
|
||||
result['foldToContBetDone']=foldToContBetDone
|
||||
result['foldToSecondBarrelChance']=foldToSecondBarrelChance
|
||||
result['foldToSecondBarrelDone']=foldToSecondBarrelDone
|
||||
result['foldToThirdBarrelChance']=foldToThirdBarrelChance
|
||||
result['foldToThirdBarrelDone']=foldToThirdBarrelDone
|
||||
|
||||
result['totalProfit']=totalProfit
|
||||
|
||||
result['flopCheckCallRaiseChance']=flopCheckCallRaiseChance
|
||||
result['flopCheckCallRaiseDone']=flopCheckCallRaiseDone
|
||||
result['turnCheckCallRaiseChance']=turnCheckCallRaiseChance
|
||||
result['turnCheckCallRaiseDone']=turnCheckCallRaiseDone
|
||||
result['riverCheckCallRaiseChance']=riverCheckCallRaiseChance
|
||||
result['riverCheckCallRaiseDone']=riverCheckCallRaiseDone
|
||||
return result
|
||||
#end def calculateHudImport
|
||||
|
||||
|
@ -1649,16 +1714,32 @@ def storeHudData(cursor, category, gametypeId, playerIds, hudImportData):
|
|||
if hudImportData['thirdBarrelDone'][player]: row[35]+=1
|
||||
row[36]=hudImportData['position'][player]
|
||||
|
||||
if hudImportData['foldToContBetChance'][player]: row[37]+=1
|
||||
if hudImportData['foldToContBetDone'][player]: row[38]+=1
|
||||
if hudImportData['foldToSecondBarrelChance'][player]: row[39]+=1
|
||||
if hudImportData['foldToSecondBarrelDone'][player]: row[40]+=1
|
||||
if hudImportData['foldToThirdBarrelChance'][player]: row[41]+=1
|
||||
if hudImportData['foldToThirdBarrelDone'][player]: row[42]+=1
|
||||
|
||||
row[43]+=hudImportData['totalProfit'][player]
|
||||
|
||||
if hudImportData['flopCheckCallRaiseChance'][player]: row[44]+=1
|
||||
if hudImportData['flopCheckCallRaiseDone'][player]: row[45]+=1
|
||||
if hudImportData['turnCheckCallRaiseChance'][player]: row[46]+=1
|
||||
if hudImportData['turnCheckCallRaiseDone'][player]: row[47]+=1
|
||||
if hudImportData['riverCheckCallRaiseChance'][player]: row[48]+=1
|
||||
if hudImportData['riverCheckCallRaiseDone'][player]: row[49]+=1
|
||||
|
||||
if doInsert:
|
||||
#print "playerid before insert:",row[2]
|
||||
cursor.execute("""INSERT INTO HudDataHoldemOmaha
|
||||
(gametypeId, playerId, activeSeats, HDs, VPIP, PFR, PF3B4BChance, PF3B4B, sawFlop, sawTurn, sawRiver, sawShowdown, raisedFlop, raisedTurn, raisedRiver, otherRaisedFlop, otherRaisedFlopFold, otherRaisedTurn, otherRaisedTurnFold, otherRaisedRiver, otherRaisedRiverFold, wonWhenSeenFlop, wonAtSD, stealAttemptChance, stealAttempted, foldBbToStealChance, foldedBbToSteal, foldSbToStealChance, foldedSbToSteal, contBetChance, contBetDone, secondBarrelChance, secondBarrelDone, thirdBarrelChance, thirdBarrelDone, position)
|
||||
VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s)""", (row[1], row[2], row[3], row[4], row[5], row[6], row[7], row[8], row[9], row[10], row[11], row[12], row[13], row[14], row[15], row[16], row[17], row[18], row[19], row[20], row[21], row[22], row[23], row[24], row[25], row[26], row[27], row[28], row[29], row[30], row[31], row[32], row[33], row[34], row[35], row[36]))
|
||||
(gametypeId, playerId, activeSeats, HDs, VPIP, PFR, PF3B4BChance, PF3B4B, sawFlop, sawTurn, sawRiver, sawShowdown, raisedFlop, raisedTurn, raisedRiver, otherRaisedFlop, otherRaisedFlopFold, otherRaisedTurn, otherRaisedTurnFold, otherRaisedRiver, otherRaisedRiverFold, wonWhenSeenFlop, wonAtSD, stealAttemptChance, stealAttempted, foldBbToStealChance, foldedBbToSteal, foldSbToStealChance, foldedSbToSteal, contBetChance, contBetDone, secondBarrelChance, secondBarrelDone, thirdBarrelChance, thirdBarrelDone, position, tourneysGametypeId, foldToContBetChance, foldToContBetDone, foldToSecondBarrelChance, foldToSecondBarrelDone, foldToThirdBarrelChance, foldToThirdBarrelDone, totalProfit, flopCheckCallRaiseChance, flopCheckCallRaiseDone, turnCheckCallRaiseChance, turnCheckCallRaiseDone, riverCheckCallRaiseChance, riverCheckCallRaiseDone)
|
||||
VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s)""", (row[1], row[2], row[3], row[4], row[5], row[6], row[7], row[8], row[9], row[10], row[11], row[12], row[13], row[14], row[15], row[16], row[17], row[18], row[19], row[20], row[21], row[22], row[23], row[24], row[25], row[26], row[27], row[28], row[29], row[30], row[31], row[32], row[33], row[34], row[35], row[36], 1, row[37], row[38], row[39], row[40], row[41], row[42], row[43], row[44], row[45], row[46], row[47], row[48], row[49]))
|
||||
else:
|
||||
#print "storing updated hud data line"
|
||||
cursor.execute("""UPDATE HudDataHoldemOmaha
|
||||
SET HDs=%s, VPIP=%s, PFR=%s, PF3B4BChance=%s, PF3B4B=%s, sawFlop=%s, sawTurn=%s, sawRiver=%s, sawShowdown=%s, raisedFlop=%s, raisedTurn=%s, raisedRiver=%s, otherRaisedFlop=%s, otherRaisedFlopFold=%s, otherRaisedTurn=%s, otherRaisedTurnFold=%s, otherRaisedRiver=%s, otherRaisedRiverFold=%s, wonWhenSeenFlop=%s, wonAtSD=%s, stealAttemptChance=%s, stealAttempted=%s, foldBbToStealChance=%s, foldedBbToSteal=%s, foldSbToStealChance=%s, foldedSbToSteal=%s, contBetChance=%s, contBetDone=%s, secondBarrelChance=%s, secondBarrelDone=%s, thirdBarrelChance=%s, thirdBarrelDone=%s
|
||||
WHERE gametypeId=%s AND playerId=%s AND activeSeats=%s AND position=%s""", (row[4], row[5], row[6], row[7], row[8], row[9], row[10], row[11], row[12], row[13], row[14], row[15], row[16], row[17], row[18], row[19], row[20], row[21], row[22], row[23], row[24], row[25], row[26], row[27], row[28], row[29], row[30], row[31], row[32], row[33], row[34], row[35], row[1], row[2], row[3], row[36]))
|
||||
SET HDs=%s, VPIP=%s, PFR=%s, PF3B4BChance=%s, PF3B4B=%s, sawFlop=%s, sawTurn=%s, sawRiver=%s, sawShowdown=%s, raisedFlop=%s, raisedTurn=%s, raisedRiver=%s, otherRaisedFlop=%s, otherRaisedFlopFold=%s, otherRaisedTurn=%s, otherRaisedTurnFold=%s, otherRaisedRiver=%s, otherRaisedRiverFold=%s, wonWhenSeenFlop=%s, wonAtSD=%s, stealAttemptChance=%s, stealAttempted=%s, foldBbToStealChance=%s, foldedBbToSteal=%s, foldSbToStealChance=%s, foldedSbToSteal=%s, contBetChance=%s, contBetDone=%s, secondBarrelChance=%s, secondBarrelDone=%s, thirdBarrelChance=%s, thirdBarrelDone=%s, tourneysGametypeId=%s, foldToContBetChance=%s, foldToContBetDone=%s, foldToSecondBarrelChance=%s, foldToSecondBarrelDone=%s, foldToThirdBarrelChance=%s, foldToThirdBarrelDone=%s, totalProfit=%s, flopCheckCallRaiseChance=%s, flopCheckCallRaiseDone=%s, turnCheckCallRaiseChance=%s, turnCheckCallRaiseDone=%s, riverCheckCallRaiseChance=%s, riverCheckCallRaiseDone=%s
|
||||
WHERE gametypeId=%s AND playerId=%s AND activeSeats=%s AND position=%s""", (row[4], row[5], row[6], row[7], row[8], row[9], row[10], row[11], row[12], row[13], row[14], row[15], row[16], row[17], row[18], row[19], row[20], row[21], row[22], row[23], row[24], row[25], row[26], row[27], row[28], row[29], row[30], row[31], row[32], row[33], row[34], row[35], 1, row[37], row[38], row[39], row[40], row[41], row[42], row[43], row[44], row[45], row[46], row[47], row[48], row[49], row[1], row[2], row[3], row[36]))
|
||||
else:
|
||||
raise FpdbError("todo")
|
||||
#end def storeHudData
|
||||
|
|
|
@ -117,6 +117,7 @@ class table_viewer (threading.Thread):
|
|||
if field_no<=3:
|
||||
pass
|
||||
else:
|
||||
#print "in prep data, row_no:",row_no,"field_no:",field_no
|
||||
row[field_no]+=rows[row_no][field_no]
|
||||
|
||||
tmp.append(str(row[4]))#Hands
|
||||
|
@ -186,6 +187,10 @@ class table_viewer (threading.Thread):
|
|||
bg_col="lightgrey"
|
||||
if column==0 or (column>=5 and column<=10):
|
||||
bg_col="grey"
|
||||
#style = eventBox.get_style()
|
||||
#style.font.height=8
|
||||
#eventBox.set_style(style)
|
||||
|
||||
eventBox.modify_bg(gtk.STATE_NORMAL, gtk.gdk.color_parse(bg_col))
|
||||
eventBox.add(new_label)
|
||||
self.data_table.attach(child=eventBox, left_attach=column, right_attach=column+1, top_attach=row, bottom_attach=row+1)
|
||||
|
|
Loading…
Reference in New Issue
Block a user