init
18
INSTALL
Normal file
|
@ -0,0 +1,18 @@
|
|||
----------------- creating the database --------------------
|
||||
#installing PostgreSQL the Debian way
|
||||
apt-get install postgresql
|
||||
su postgres
|
||||
#in template1
|
||||
psql template1
|
||||
#at the prompt write:
|
||||
CREATE DATABASE webgo ;
|
||||
#now create user who is allowed to administrate this database
|
||||
#user and password should be changed;>
|
||||
CREATE USER name PASSWORD 'password';
|
||||
#now make a super user out of this user:
|
||||
#TODO:tighter rights for user, this should be unsecure
|
||||
ALTER USER name CREATEUSER;
|
||||
#now change the user and password variables in the script
|
||||
|
||||
webgouser
|
||||
webgopassword
|
39
README
Normal file
|
@ -0,0 +1,39 @@
|
|||
-------------------------------------------
|
||||
WebGo Development:
|
||||
|
||||
Steps to do to get a runnable version:
|
||||
1. create /etc/apache2/conf.d/webgo.conf with content:
|
||||
|
||||
Alias /webgo /home/mtsrc/daten/projekte/webgo/stderr.py
|
||||
|
||||
<DirectoryMatch /home/mtsrc/daten/projekte/webgo/>
|
||||
Options ExecCGI
|
||||
AddHandler cgi-script .py
|
||||
AllowOverride None
|
||||
order allow,deny
|
||||
allow from all
|
||||
</DirectoryMatch>
|
||||
|
||||
You should replace the directory settings for optimal results ;>
|
||||
|
||||
2. set ownership of webgo directory to ownership of the web server
|
||||
|
||||
this should be all. Errors get written to ./cgi_errors.log.
|
||||
|
||||
-------------------------------------------
|
||||
|
||||
How to make the goban clickable:
|
||||
data += '<form method="post">'
|
||||
data += '<input type=image src="img/bottomrightline.png" name="untenrechts">'
|
||||
#insert rest of goban here
|
||||
data += '</form>'
|
||||
print data
|
||||
|
||||
How to extract the data from this POST:
|
||||
import cgi
|
||||
#get instance of fieldstorage
|
||||
form = cgi.FieldStorage()
|
||||
#if form == empty (which means if page is displayed for the first time):
|
||||
if form.keys() != []:
|
||||
#cut out the name of the clicked button
|
||||
print string.split(form.keys()[0],".x")[0]
|
55
ROADMAP
Normal file
|
@ -0,0 +1,55 @@
|
|||
|
||||
M0: debug messages as html
|
||||
Create a script that prints the python stderr of thea main script as html,
|
||||
so debugging the cgi becomes more easy.
|
||||
|
||||
M1: user interaction thorugh images
|
||||
Completed, when a user can click on an image, and the image gets replaced
|
||||
as a result.
|
||||
|
||||
m2: autogenerate images
|
||||
There are partly transparent images of black and white stones, center,
|
||||
border and hoshi squares, all available in png format. The Milestone is
|
||||
considered complete when the program can automatically generate combined
|
||||
images of all squares with all stones. The stones have to be on top ;>
|
||||
|
||||
m3: display working 9x9 goban
|
||||
Completed, when the cgi script has an internal representation of a goban
|
||||
that can handle stones, and this goban is displayed on a html page.
|
||||
|
||||
m4: handle player turns
|
||||
The program knows wether it is the turn of black or white, and prints the
|
||||
current turn color on the html page displaying the goban. If player white
|
||||
clicks on an empty square, a white stone will be layed onto that square.
|
||||
Same for Player black.
|
||||
|
||||
m5: goban inside database
|
||||
Manage the goban inside a postgresql database, so all user input changes a
|
||||
table in the database. Includes a 'size' item that contains the length of
|
||||
on side of the board, so that gobans of size 9x9, 13x13 and 19x19 can be
|
||||
displayed.
|
||||
|
||||
. <--- YOU ARE HERE.
|
||||
|
||||
m6: manage users and game sessions inside database
|
||||
manage user accounts and the game(s) they currently play inside the data-
|
||||
base. each game has its own table, which contains players, current turn,
|
||||
size, the goban inside the database. users are managed in a different
|
||||
table. For each user the following data is stored: password, current games
|
||||
(up to 8 or so). So if the user calls the cgi script, she has to send
|
||||
username and password and is then given the choice of game to play. If
|
||||
there is only one active game, directly present the goban.
|
||||
|
||||
m7: implement Go rules
|
||||
Do not accept player moves which violate go rules. Remove beaten stones.
|
||||
Count and display the score for each player.
|
||||
|
||||
m8: make it pretty, make it fast.
|
||||
Create nicer images replacing the old ones which were just for testing
|
||||
Find bottlenecks and optimize them for speed.
|
||||
-- python profiler
|
||||
-- http://peter.mapledesign.co.uk/weblog/archives/python_is_slow.html
|
||||
create a set of images smaller than 50 pixels (for the 19x19 goban)
|
||||
|
||||
m9: make it standards compliant
|
||||
w3c validation
|
1
TODO
Normal file
|
@ -0,0 +1 @@
|
|||
- hoshi Bilder fuer weisse und schwarze Steine erstellen
|
25
generate_goban_images.py
Executable file
|
@ -0,0 +1,25 @@
|
|||
#!/usr/bin/python
|
||||
"""
|
||||
a simple script using pil to generate the goban from background, grid and stone images.
|
||||
"""
|
||||
import Image
|
||||
gridlist = ["bottomleftline","bottomline","bottomrightline",
|
||||
"centerline","hoshi","leftline","rightline",
|
||||
"topleftline", "topline","toprightline"]
|
||||
|
||||
whitestone = Image.open("imgsource/whitestone.png").convert("RGBA")
|
||||
blackstone = Image.open("imgsource/blackstone.png").convert("RGBA")
|
||||
|
||||
for item in gridlist:
|
||||
#creating empty goban
|
||||
bg = Image.open("imgsource/background.png").convert("RGBA")
|
||||
img = Image.open("imgsource/"+item+".png").convert("RGBA")
|
||||
bg.paste(img,None,img)
|
||||
bg.save("img/"+item+".png")
|
||||
tmp = bg #for the black stones
|
||||
#filling with white stones
|
||||
bg.paste(whitestone,None,whitestone)
|
||||
bg.save("img/"+item+"_white.png")
|
||||
#filling with black stones
|
||||
tmp.paste(blackstone,None,blackstone)
|
||||
tmp.save("img/"+item+"_black.png")
|
174
goban.py
Executable file
|
@ -0,0 +1,174 @@
|
|||
#!/usr/bin/python
|
||||
|
||||
"""
|
||||
the main file for WebGo.
|
||||
"""
|
||||
import sys,string
|
||||
import cgi
|
||||
import pickle
|
||||
import psql,helper
|
||||
|
||||
picklefile = "goban.pickledump"
|
||||
|
||||
###############################################################################
|
||||
|
||||
|
||||
|
||||
def display_goban(goban):
|
||||
"""
|
||||
gets: dictionary containing the layout of the used goban.
|
||||
returns: string containing the HTML code for a clickable goban.
|
||||
"""
|
||||
data = ""
|
||||
|
||||
hoshis19x19 = [(4,4),(4,10),(4,16),(10,4),(10,10),(10,16),(16,4),(16,10),(16,16)]
|
||||
hoshis13x13 = [(4,4),(4,10),(7,7),(10,4),(10,10)]
|
||||
hoshis9x9 = [(3,3),(3,7),(5,5),(7,3),(7,7)]
|
||||
|
||||
|
||||
data += '<form method="post">'
|
||||
try:
|
||||
size = goban["size"]
|
||||
except:
|
||||
#TODO: proper error management
|
||||
raise "[EE] display_goban: got broken goban dictionary."
|
||||
sys.exit(1)
|
||||
for x in range(1,size+1):
|
||||
for y in range(1,size+1):
|
||||
# check for white or black stone
|
||||
if goban[(x,y)] == 1:
|
||||
stone = "_white"
|
||||
elif goban[(x,y)] == 2:
|
||||
stone = "_black"
|
||||
else:
|
||||
stone = ""
|
||||
sx = str(x)
|
||||
sy = str(y)
|
||||
# check position:
|
||||
if (x == 1) and (y == 1): # upper left
|
||||
data += '<input type=image src="img/topleftline'+stone+'.png" name="('+sx+','+sy+')">'
|
||||
elif (x == 1) and (y == size): # upper right
|
||||
data += '<input type=image src="img/toprightline'+stone+'.png" name="('+sx+','+sy+')"><br>'
|
||||
elif (x == size) and (y == size): # lower right
|
||||
data += '<input type=image src="img/bottomrightline'+stone+'.png" name="('+sx+','+sy+')"><br>'
|
||||
elif (x == size) and (y == 1): # lower left
|
||||
data += '<input type=image src="img/bottomleftline'+stone+'.png" name="('+sx+','+sy+')">'
|
||||
elif (y == 1): #left line
|
||||
data += '<input type=image src="img/leftline'+stone+'.png" name="('+sx+','+sy+')">'
|
||||
elif (x == 1): # top line
|
||||
data += '<input type=image src="img/topline'+stone+'.png" name="('+sx+','+sy+')">'
|
||||
elif (y == size): # right line
|
||||
data += '<input type=image src="img/rightline'+stone+'.png" name="('+sx+','+sy+')"><br>'
|
||||
elif (x == size): #bottom line
|
||||
data += '<input type=image src="img/bottomline'+stone+'.png" name="('+sx+','+sy+')">'
|
||||
else: # hoshi or empty inner field
|
||||
defaultfield = '<input type=image src="img/centerline'+stone+'.png" name="('+sx+','+sy+')">'
|
||||
#too lazy to make special images for hoshi fields with stones:
|
||||
if goban[(x,y)] == 1:
|
||||
hoshifield = '<input type=image src="img/centerline_white.png" name="('+sx+','+sy+')">'
|
||||
elif goban[(x,y)] == 2:
|
||||
hoshifield = '<input type=image src="img/centerline_black.png" name="('+sx+','+sy+')">'
|
||||
else: #empty hoshi
|
||||
hoshifield = '<input type=image src="img/hoshi.png" name="('+sx+','+sy+')">'
|
||||
if size == 19: # 9 hoshis
|
||||
if (x,y) in hoshis19x19:
|
||||
data += hoshifield
|
||||
else:
|
||||
data += defaultfield
|
||||
elif size == 13:
|
||||
if (x,y) in hoshis13x13:
|
||||
data += hoshifield
|
||||
else:
|
||||
data += defaultfield
|
||||
elif size == 9:
|
||||
if (x,y) in hoshis9x9:
|
||||
data += hoshifield
|
||||
else:
|
||||
data += defaultfield
|
||||
data += '</form>'
|
||||
return data
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
def string_to_tuple(str):
|
||||
"""
|
||||
gets a string. If the string contains '(',')' and ',', then return
|
||||
a tuple processed from the string. If the partial string is empty, then
|
||||
-1 will be returned for that value.
|
||||
"""
|
||||
if (str[0] =='(') and (str[-1] ==')') and (string.find(str,',')):
|
||||
splitlist = string.split(str[1:-1],",")
|
||||
returnlist = []
|
||||
for item in splitlist:
|
||||
try:
|
||||
returnlist.append(int(item))
|
||||
except: #empty string
|
||||
returnlist.append(-1)
|
||||
return tuple(returnlist)
|
||||
|
||||
|
||||
def process_form(goban):
|
||||
"""
|
||||
gets a goban dictionary.
|
||||
|
||||
reads out the returned CGI form.
|
||||
if the goban has been clicked, return a (x,y) tuple of the position.
|
||||
|
||||
"""
|
||||
#get instance of fieldstorage
|
||||
form = cgi.FieldStorage()
|
||||
#if form == empty (which means if page is displayed for the first time):
|
||||
if form.keys() != []:
|
||||
#cut out the name of the clicked button
|
||||
namestring = string.split(form.keys()[0],".x")[0]
|
||||
position = string_to_tuple(namestring)
|
||||
ret = set_stone(goban, position)
|
||||
if (type(ret) == type("")):
|
||||
return (goban,ret) #return old goban and error string
|
||||
else:
|
||||
goban = ret
|
||||
return (goban,"") # return new goban and empty string
|
||||
|
||||
|
||||
def set_stone(goban, position):
|
||||
"""gets a goban dictionary and a (x,y)-tuple. Returns a modified goban."""
|
||||
turn = goban["turn_number"]
|
||||
if (goban[position] == 0): #empty field
|
||||
goban[position] = (turn % 2) + 1 #even turn: white tone (1), else black stone(2)
|
||||
goban["turn_number"] += 1
|
||||
#now write changed values to database
|
||||
psql.update_goban_field(goban["name"],position[0],position[1],(turn % 2) + 1)
|
||||
psql.update_turn_number(goban["name"],goban["turn_number"])
|
||||
return goban
|
||||
else:
|
||||
return "Could not make move: Field not empty."
|
||||
|
||||
###############################################################################
|
||||
|
||||
def main():
|
||||
# Print the required header that tells the browser how to render the text.
|
||||
#(currently done by error logger)
|
||||
#print "Content-Type: text/plain\n\n"
|
||||
|
||||
#do stuff
|
||||
data = helper.header()
|
||||
|
||||
#read goban table from database
|
||||
tmplist = psql.read_table("test")
|
||||
#make a dictionary out of the list
|
||||
goban = psql.fetchall_list_to_goban_dict(tmplist)
|
||||
#print goban
|
||||
|
||||
(goban,str) = process_form(goban)
|
||||
data += "<p> Turn number: %s, %s Player's Move.</p>" % (goban["turn_number"],("White","Black")[goban["turn_number"] % 2]) #eleet ;>
|
||||
data += display_goban(goban)
|
||||
if str != "":
|
||||
data +="\n<p>"+str+"</p>\n"
|
||||
|
||||
|
||||
data += helper.footer()
|
||||
print data
|
||||
|
||||
|
38
helper.py
Normal file
|
@ -0,0 +1,38 @@
|
|||
def header():
|
||||
"""return html header"""
|
||||
data = """
|
||||
<html><HEAD>
|
||||
|
||||
</HEAD><body>
|
||||
"""
|
||||
return data
|
||||
|
||||
def footer():
|
||||
"""return html footer"""
|
||||
data = """
|
||||
</body></html>
|
||||
"""
|
||||
return data
|
||||
|
||||
# create a unique session id
|
||||
def generate_session_id():
|
||||
import md5, time, base64, random, string
|
||||
m = md5.new()
|
||||
m.update(str(time.time()))
|
||||
m.update(str(random.random()))
|
||||
return string.replace(base64.encodestring(m.digest())[:-3], '/', '$')
|
||||
|
||||
|
||||
def check_for_int(data):
|
||||
"""
|
||||
gets a string. if string is an integer: return integer.
|
||||
else return given string.
|
||||
"""
|
||||
#check if value is int
|
||||
num = [n for n in data if n.isdigit()]
|
||||
tmp = "".join(num)
|
||||
if tmp == data:
|
||||
ret = int(data)
|
||||
else:
|
||||
ret = data
|
||||
return ret
|
BIN
img/bottomleftline.png
Normal file
After Width: | Height: | Size: 5.6 KiB |
BIN
img/bottomleftline_black.png
Normal file
After Width: | Height: | Size: 4.2 KiB |
BIN
img/bottomleftline_white.png
Normal file
After Width: | Height: | Size: 4.7 KiB |
BIN
img/bottomline.png
Normal file
After Width: | Height: | Size: 5.5 KiB |
BIN
img/bottomline_black.png
Normal file
After Width: | Height: | Size: 4.3 KiB |
BIN
img/bottomline_white.png
Normal file
After Width: | Height: | Size: 4.6 KiB |
BIN
img/bottomrightline.png
Normal file
After Width: | Height: | Size: 5.6 KiB |
BIN
img/bottomrightline_black.png
Normal file
After Width: | Height: | Size: 4.2 KiB |
BIN
img/bottomrightline_white.png
Normal file
After Width: | Height: | Size: 4.6 KiB |
BIN
img/centerline.png
Normal file
After Width: | Height: | Size: 5.5 KiB |
BIN
img/centerline_black.png
Normal file
After Width: | Height: | Size: 4.3 KiB |
BIN
img/centerline_white.png
Normal file
After Width: | Height: | Size: 4.7 KiB |
BIN
img/centerwhite.png
Normal file
After Width: | Height: | Size: 3.3 KiB |
BIN
img/hoshi.png
Normal file
After Width: | Height: | Size: 5.6 KiB |
BIN
img/hoshi_black.png
Normal file
After Width: | Height: | Size: 4.3 KiB |
BIN
img/hoshi_white.png
Normal file
After Width: | Height: | Size: 4.7 KiB |
BIN
img/leftline.png
Normal file
After Width: | Height: | Size: 5.6 KiB |
BIN
img/leftline_black.png
Normal file
After Width: | Height: | Size: 4.2 KiB |
BIN
img/leftline_white.png
Normal file
After Width: | Height: | Size: 4.7 KiB |
BIN
img/rightline.png
Normal file
After Width: | Height: | Size: 5.6 KiB |
BIN
img/rightline_black.png
Normal file
After Width: | Height: | Size: 4.2 KiB |
BIN
img/rightline_white.png
Normal file
After Width: | Height: | Size: 4.7 KiB |
BIN
img/topleftline.png
Normal file
After Width: | Height: | Size: 5.6 KiB |
BIN
img/topleftline_black.png
Normal file
After Width: | Height: | Size: 4.2 KiB |
BIN
img/topleftline_white.png
Normal file
After Width: | Height: | Size: 4.7 KiB |
BIN
img/topline.png
Normal file
After Width: | Height: | Size: 5.5 KiB |
BIN
img/topline_black.png
Normal file
After Width: | Height: | Size: 4.2 KiB |
BIN
img/topline_white.png
Normal file
After Width: | Height: | Size: 4.7 KiB |
BIN
img/toprightline.png
Normal file
After Width: | Height: | Size: 5.6 KiB |
BIN
img/toprightline_black.png
Normal file
After Width: | Height: | Size: 4.2 KiB |
BIN
img/toprightline_white.png
Normal file
After Width: | Height: | Size: 4.6 KiB |
BIN
imgsource/background.png
Normal file
After Width: | Height: | Size: 4.4 KiB |
BIN
imgsource/blackstone.png
Normal file
After Width: | Height: | Size: 1.5 KiB |
BIN
imgsource/bottomleftline.png
Normal file
After Width: | Height: | Size: 187 B |
BIN
imgsource/bottomline.png
Normal file
After Width: | Height: | Size: 188 B |
BIN
imgsource/bottomrightline.png
Normal file
After Width: | Height: | Size: 189 B |
BIN
imgsource/centerline.png
Normal file
After Width: | Height: | Size: 194 B |
BIN
imgsource/hoshi.png
Normal file
After Width: | Height: | Size: 229 B |
BIN
imgsource/leftline.png
Normal file
After Width: | Height: | Size: 195 B |
BIN
imgsource/rightline.png
Normal file
After Width: | Height: | Size: 194 B |
BIN
imgsource/topleftline.png
Normal file
After Width: | Height: | Size: 188 B |
BIN
imgsource/topline.png
Normal file
After Width: | Height: | Size: 187 B |
BIN
imgsource/toprightline.png
Normal file
After Width: | Height: | Size: 189 B |
BIN
imgsource/whitestone.png
Normal file
After Width: | Height: | Size: 1.6 KiB |
23
init_webgo.py
Executable file
|
@ -0,0 +1,23 @@
|
|||
import psql
|
||||
|
||||
|
||||
def clear():
|
||||
try:
|
||||
psql.drop_table("test")
|
||||
psql.drop_table("users")
|
||||
except:
|
||||
pass
|
||||
|
||||
def create():
|
||||
psql.create_goban_table(9)
|
||||
psql.create_user_table()
|
||||
psql.add_webgo_user("gast","gast")
|
||||
psql.add_webgo_user("gast2","gast2")
|
||||
|
||||
|
||||
def main():
|
||||
clear()
|
||||
create()
|
||||
|
||||
|
||||
|
130
login.py
Executable file
|
@ -0,0 +1,130 @@
|
|||
import psql,init_webgo,helper
|
||||
import time
|
||||
from mod_python import *
|
||||
|
||||
def process_form(req,form):
|
||||
"""
|
||||
reads username and passsword from form
|
||||
"""
|
||||
#if form == empty (which means if page is displayed for the first time):
|
||||
req.write(str(form.keys()))
|
||||
#req.write("<br>"+"name="+form['name']+", password="+form['password']+"<hr>")
|
||||
if form.keys() != []:
|
||||
|
||||
if ("name" in form.keys()) and ("password" in form.keys()):
|
||||
#extract name and password
|
||||
name = form["name"]
|
||||
password = form["password"]
|
||||
sessionid = form["sessionid"]
|
||||
origpassword = psql.get_user_info(name,"password")
|
||||
#check if user exists (else we would get an error string)
|
||||
if origpassword != "no such user": #no error message, now check password
|
||||
req.write("hier bin ich in der Schleife.<br>")
|
||||
req.write(str(origpassword)+"<br>")
|
||||
if password == origpassword:
|
||||
#login accepted
|
||||
psql.set_user_sessionid(name,sessionid)
|
||||
psql.set_user_timeout(name)
|
||||
#now display list of games.
|
||||
game_overview_form(req,name,sessionid)
|
||||
else:
|
||||
req.write("Login incorrect. Please try again.<br>")
|
||||
req.write(login_form())
|
||||
else: #no such user
|
||||
req.write("Login incorrect. Please try again.<br>")
|
||||
req.write(login_form())
|
||||
else: #one of (name,password) is missing:
|
||||
req.write("Please enter your name and password.")
|
||||
req.write(login_form())
|
||||
|
||||
|
||||
def game_overview_form(req,user,sessionid):
|
||||
"""
|
||||
gets the name of a user and the queue of games.
|
||||
prints a form with the option to select,create and delete games.
|
||||
"""
|
||||
data = helper.header()+ """
|
||||
<h2> Current Games: </h2>
|
||||
<form method="post">
|
||||
"""
|
||||
gamelist = psql.get_user_game_list(user)
|
||||
req.write(str(gamelist)+"<hr>\n")
|
||||
#display list of current games
|
||||
counter = 10
|
||||
tmp = ""
|
||||
for item in gamelist:
|
||||
if (item != None) and (item != "None"):
|
||||
tmp += '<input type="radio"> name="game" value="%s"> %s\n' % (item,item)
|
||||
tmp += '<input type="hidden" name="sessionid" value="%s">\n' % sessionid
|
||||
tmp += '<input type="hidden" name="username" value="%s">\n' % user
|
||||
#later write partners name and whether its our turn or not
|
||||
#data += "Your partner: %s."
|
||||
tmp +="<br>"
|
||||
counter -= 1
|
||||
if tmp == "": #no current games
|
||||
data += "You don't have any running games.\n"
|
||||
else:
|
||||
data += tmp
|
||||
data += '<input type=submit name="play" value="Play selected game">\n<input type=submit name="delete" value="Delete selected game">\n</form>'
|
||||
#now comes the option for creating new games.
|
||||
data += "<h2>Start a new Game</h2>\n"
|
||||
if counter > 0:
|
||||
data+= "You have %s free game slots.<br>" % counter
|
||||
data += """
|
||||
<form>
|
||||
<input type=submit name="create" value="Start a new game">
|
||||
<input type="hidden" name="sessionid" value="%s">
|
||||
<input type="hidden" name="username" value="%s">
|
||||
|
||||
</form>
|
||||
""" % (sessionid, user)
|
||||
else:
|
||||
data+= "Sorry, all your game slots are in use."
|
||||
#display "You have ... free game slots" + Button "create game"
|
||||
#bei "create game neue funktion zum Erstellen + Mitspieler aus Liste auswählen.
|
||||
data+=helper.footer()
|
||||
#check for timeout:
|
||||
if (psql.get_user_info(user,'timeout') >= int(time.time())) and (sessionid == psql.get_user_info(user,'sessionid')):
|
||||
req.write(data)
|
||||
else:
|
||||
req.write(helper.header()+ 'your session timed out'+helper.footer())
|
||||
|
||||
def login_form():
|
||||
"""
|
||||
print welcome message and html form.
|
||||
"""
|
||||
data = helper.header() + """
|
||||
|
||||
<form method="post">
|
||||
<p>Name:<br>
|
||||
<input name="name" size="40"></p>
|
||||
<p>Pasword:<br>
|
||||
<input name="password" size="40"></p>
|
||||
<input type="hidden" name="sessionid" value="%s">
|
||||
<p><input type="submit" value="login"></p>
|
||||
</form>
|
||||
""" % helper.generate_session_id()
|
||||
data += helper.footer()
|
||||
|
||||
|
||||
return data
|
||||
|
||||
|
||||
def create_new_game(req,user):
|
||||
"""
|
||||
create a new game for user.
|
||||
gets: request object, username.
|
||||
"""
|
||||
|
||||
|
||||
|
||||
def main(req):
|
||||
#get instance of fieldstorage
|
||||
form = util.FieldStorage(req)
|
||||
if "sessionid" not in form.keys():
|
||||
req.write(login_form())
|
||||
elif ("create" in form.keys()) and ("username" in form.keys()):
|
||||
#user wants to create a new game
|
||||
create_new_game(req,form["username"])
|
||||
else:
|
||||
process_form(req,form)
|
73
main.py
Executable file
|
@ -0,0 +1,73 @@
|
|||
#!/usr/bin/python
|
||||
"""
|
||||
simple cgi wrapper for a cgi script, prints the error of the script as html
|
||||
taken from http://gnosis.cx/publish/programming/feature_5min_python.html
|
||||
modified a little
|
||||
"""
|
||||
|
||||
from mod_python import apache
|
||||
|
||||
import sys, traceback
|
||||
import init_webgo
|
||||
|
||||
DEBUG = 1
|
||||
|
||||
|
||||
def handler(req):
|
||||
# "Content-type: text/html\n\n"
|
||||
req.content_type = "text/html"
|
||||
try: # use explicit exception handling
|
||||
#reinitialize database
|
||||
init_webgo.main()
|
||||
|
||||
login = apache.import_module("login")
|
||||
login.main(req)
|
||||
return apache.OK
|
||||
except:
|
||||
import time
|
||||
errtime = '----- '+ time.ctime(time.time()) +' -----\n'
|
||||
errlog = open('/tmp/cgi_errors.log', 'a')
|
||||
errlog.write(errtime)
|
||||
errlog.write(ErrorMsg())
|
||||
data = """<html><head><title>CGI Error Encountered!</title></head>
|
||||
<body><p>Sorry, a problem was encountered running WebGo.</p>
|
||||
<p>Please check the error log on the server for details.</p>
|
||||
<hr><pre>"""
|
||||
data += ErrorMsg()
|
||||
data+="</pre>\n</body></html>"
|
||||
req.write(data)
|
||||
return apache.OK
|
||||
|
||||
|
||||
|
||||
|
||||
def ErrorMsg(escape=0):
|
||||
"""
|
||||
returns: string
|
||||
|
||||
simualtes the traceback output and if argemument
|
||||
<escape> set to 1 (true) the string will be
|
||||
converted to fit into html documents without problems.
|
||||
from Dirk Holtwick
|
||||
"""
|
||||
import traceback, sys, string
|
||||
|
||||
type=None
|
||||
value=None
|
||||
tb=None
|
||||
limit=None
|
||||
type, value, tb = sys.exc_info()
|
||||
body = "Traceback (innermost last):\n"
|
||||
list = traceback.format_tb(tb, limit) + traceback.format_exception_only(type, value)
|
||||
body = body + "%-20s %s" % (
|
||||
string.join(list[:-1], ""),
|
||||
list[-1],
|
||||
)
|
||||
if escape:
|
||||
import cgi
|
||||
body = cgi.escape(body)
|
||||
return body
|
||||
|
||||
|
||||
|
||||
#start(req)
|
7
mptest.py
Executable file
|
@ -0,0 +1,7 @@
|
|||
from mod_python import apache
|
||||
|
||||
def handler(req):
|
||||
req.write("Hello World!")
|
||||
return apache.OK
|
||||
|
||||
|
51
old_functions.py
Executable file
|
@ -0,0 +1,51 @@
|
|||
def init_goban(size=9):
|
||||
"""
|
||||
Returns an empty goban in the form of a dictionary where the keys are
|
||||
x,y-tuples: (1,1) is the upper left corner, (9,9) the lower right one
|
||||
for a 9x9 goban.
|
||||
The additional key named size contains the length of a side of the goban.
|
||||
Content of a field:
|
||||
0 == empty field
|
||||
1 == white stone on field
|
||||
2 == black stone on field
|
||||
Field turn_number: Current turn to be played. Even: white, uneven: black.
|
||||
Optional argument size: define length of a side of the goban (one of
|
||||
9, 12 or 19). Defaults to 9.
|
||||
Returns dictionary.
|
||||
"""
|
||||
goban = {}
|
||||
for x in range(1,size+1):
|
||||
for y in range(1,size+1):
|
||||
goban[(x,y)] = 0
|
||||
if (size == 9) or (size == 13) or (size == 19):
|
||||
goban["size"] = size
|
||||
else:
|
||||
goban["size"] = 9
|
||||
goban["turn_number"] = 1
|
||||
|
||||
#some stones for testing purposes
|
||||
goban[(1,2)] = 1; goban[(2,1)] = 1; goban[(2,3)] = 1; goban[(3,3)] = 1; goban[(6,7)] = 1; goban[(6,9)] = 1
|
||||
goban[(2,2)] = 2; goban[(1,3)] = 2; goban[(4,7)] = 2; goban[(4,9)] = 2; goban[(5,8)] = 2; goban[(7,9)] = 2
|
||||
|
||||
return goban
|
||||
|
||||
|
||||
def save_goban(goban):
|
||||
"""gets a goban dictionary and pickles it to a file.
|
||||
"""
|
||||
try:
|
||||
f = open(picklefile,"w")
|
||||
pickle.dump(goban,f)
|
||||
f.close()
|
||||
except:
|
||||
print "error: could not write to file "+picklefile+"!"
|
||||
|
||||
def load_goban():
|
||||
"""trys to load and return a pickled goban. If this does not work return
|
||||
a newly initialized goban."""
|
||||
try:
|
||||
f = open(picklefile,"r")
|
||||
return pickle.load(f)
|
||||
f.close() #datei ist jetzt genullt
|
||||
except:
|
||||
return init_goban()
|
274
psql.py
Executable file
|
@ -0,0 +1,274 @@
|
|||
import helper
|
||||
import pgdb,sys
|
||||
|
||||
dbusername="webgouser"
|
||||
dbpassword="webgopassword"
|
||||
dbname="webgo"
|
||||
|
||||
|
||||
|
||||
#pgdb.connect(connect_string) -> connection
|
||||
#connect_string = 'host:database:user:password:opt:tty'
|
||||
connect_string='localhost:'+dbname+':'+dbusername+':'+dbpassword
|
||||
db=pgdb.connect(connect_string)
|
||||
|
||||
|
||||
def create_table(name,layout=""):
|
||||
"""
|
||||
create_table(name:string, layout:string)
|
||||
returns name if successfull, None if not.
|
||||
|
||||
|
||||
simple function to create a table. the function itself would just
|
||||
create an empty table with the command 'CREATE TABLE ();'.
|
||||
The real layout is given to the function as an argument of type
|
||||
string. This string contains all column declarations in SQL syntax:
|
||||
"var1 real, var2 int"
|
||||
name is the name of the table
|
||||
|
||||
"""
|
||||
executestring = "CREATE TABLE %s ( %s );" % (name,layout)
|
||||
sql_one_liner(executestring)
|
||||
|
||||
|
||||
|
||||
def insert_into_table(table, content):
|
||||
"""
|
||||
gets the name of a table and a content string.
|
||||
executes INSERT INTO name VALUES content;
|
||||
"""
|
||||
executestring = "INSERT INTO %s VALUES %s" % (table,content)
|
||||
sql_one_liner(executestring)
|
||||
|
||||
|
||||
|
||||
def drop_table(name):
|
||||
"""
|
||||
gets: name of table.
|
||||
executes DROP TABLE <name>
|
||||
"""
|
||||
executestring = "DROP TABLE %s;" % (name)
|
||||
sql_one_liner(executestring)
|
||||
|
||||
|
||||
def sql_one_liner(data):
|
||||
"""
|
||||
gets:SQL statement
|
||||
creates a cursor, executes <statement>, closes cursor.
|
||||
"""
|
||||
cursor=db.cursor()
|
||||
cursor.execute(data)
|
||||
# Commit the changes
|
||||
db.commit()
|
||||
cursor.close()
|
||||
|
||||
|
||||
def create_goban_table(size):
|
||||
"""
|
||||
gets:size of goban.
|
||||
creates postgresql table containing goban data.
|
||||
returns: name of created table.
|
||||
"""
|
||||
tablename="test"
|
||||
data="line varchar(15)"
|
||||
for i in range(1,size+1):
|
||||
if data != "":
|
||||
data += ", "
|
||||
data += "x"+str(i) + ' varchar(15)'
|
||||
create_table(tablename,data)
|
||||
#table created, now fill the table
|
||||
for i in range(1,size+1):
|
||||
tmplist=[]
|
||||
tmplist.append("y"+str(i))
|
||||
for k in range(1,size+1):
|
||||
tmplist.append(0)
|
||||
insert_into_table(tablename,str(tuple(tmplist)))
|
||||
#ok, goban itself has been created and filled,
|
||||
#now insert additional variables
|
||||
tmplist=[]
|
||||
tmplist.append("turn_number")
|
||||
tmplist.append("1")
|
||||
insert_into_table(tablename,str(tuple(tmplist)))
|
||||
#size of goban
|
||||
tmplist=[]
|
||||
tmplist.append("size")
|
||||
tmplist.append(size)
|
||||
insert_into_table(tablename,str(tuple(tmplist)))
|
||||
#name of goban (=name of table in database)
|
||||
tmplist=[]
|
||||
tmplist.append("name")
|
||||
tmplist.append(tablename)
|
||||
insert_into_table(tablename,str(tuple(tmplist)))
|
||||
return tablename
|
||||
|
||||
def create_user_table():
|
||||
"""
|
||||
creates a table named users with following columns:
|
||||
name - name of user
|
||||
password - passsword of user
|
||||
game1 - the 10 game slots of this user contain names
|
||||
... of goban tables
|
||||
game10
|
||||
sessionid - id of current session
|
||||
timeout - when does session time out?
|
||||
"""
|
||||
data = "username varchar(15)"
|
||||
data += ", password varchar(15)"
|
||||
for i in range(1,11):
|
||||
data += ", game"+str(i)+" varchar(15)"
|
||||
data += ", sessionid text"
|
||||
data += ", timeout int"
|
||||
create_table("users",data)
|
||||
|
||||
|
||||
def read_table(table):
|
||||
"""
|
||||
gets the name of a table, does a
|
||||
SELECT * FROM table;
|
||||
returns output.
|
||||
"""
|
||||
cursor=db.cursor()
|
||||
data="SELECT * FROM %s;" % (table)
|
||||
cursor.execute(data)
|
||||
# Commit the changes
|
||||
db.commit()
|
||||
ret = cursor.fetchall()
|
||||
cursor.close()
|
||||
return ret
|
||||
|
||||
def get_user_game_list(name):
|
||||
"""
|
||||
gets a username, returns the list of games for user.
|
||||
"""
|
||||
cursor=db.cursor()
|
||||
ret = []
|
||||
for x in range(1,11):
|
||||
data="SELECT game%s FROM users WHERE username = '%s'" % (x,name)
|
||||
try:
|
||||
cursor.execute(data)
|
||||
# Commit the changes
|
||||
db.commit()
|
||||
ret.append(cursor.fetchone()[0]) #[0], because return is a list
|
||||
except:
|
||||
ret = "could not get info of all games -- table corrupt?"
|
||||
return ret
|
||||
cursor.close()
|
||||
return ret
|
||||
|
||||
def get_user_info(name,infotype):
|
||||
"""
|
||||
gets the name of a user and the type of requested info.
|
||||
returns info from database.
|
||||
"""
|
||||
cursor=db.cursor()
|
||||
if infotype in ("password","sessionid","timeout"):
|
||||
|
||||
data="SELECT %s FROM users WHERE username = '%s'" % (infotype,name)
|
||||
try:
|
||||
cursor.execute(data)
|
||||
# Commit the changes
|
||||
db.commit()
|
||||
ret = cursor.fetchone()[0] #[0], because return is a list
|
||||
except:
|
||||
ret = "no such user"
|
||||
cursor.close()
|
||||
else:
|
||||
ret = "Are your sure about the infotype?"
|
||||
return ret
|
||||
|
||||
def set_user_sessionid(username,sessionid):
|
||||
"""
|
||||
gets username and sessionid, writes sessid into database
|
||||
"""
|
||||
executestring ="UPDATE users SET sessionid = '%s' WHERE username = '%s'" %(sessionid, username)
|
||||
sql_one_liner(executestring)
|
||||
|
||||
def set_user_timeout(username):
|
||||
"""
|
||||
gets username, sets timeout to time.time + 30min
|
||||
"""
|
||||
import time
|
||||
timeout = int(time.time()) + 1800 #current time in seconds + seconds for session
|
||||
executestring ="UPDATE users SET timeout = '%s' WHERE username = '%s'" %(timeout, username)
|
||||
sql_one_liner(executestring)
|
||||
|
||||
|
||||
def update_database_field(table,column,line,data):
|
||||
"""
|
||||
gets: table name, column name, line name, new content for field.
|
||||
executes an SQL UPDATE statement for line.
|
||||
"""
|
||||
#TODO:schreiben
|
||||
executestring ="UPDATE %s SET %s = '%s' WHERE line = '%s'" %(table,column,data,line)
|
||||
sql_one_liner(executestring)
|
||||
|
||||
def update_goban_field(table,x,y,content):
|
||||
"""
|
||||
gets: goban dictionary, x,y coordinates, new content for field.
|
||||
modifies goban in database.
|
||||
"""
|
||||
update_database_field(table,"x"+str(x),"y"+str(y),content)
|
||||
|
||||
def update_turn_number(table,new_number):
|
||||
"""
|
||||
gets: name of table,new turn number
|
||||
modifies 'turn_number' in table
|
||||
"""
|
||||
update_database_field(table,"x1","turn_number",new_number)
|
||||
|
||||
def add_webgo_user(name,password):
|
||||
"""
|
||||
adds a database entry for a user.
|
||||
gets: username and password
|
||||
"""
|
||||
#size of goban
|
||||
tmplist=[]
|
||||
tmplist.append(name)
|
||||
tmplist.append(password)
|
||||
insert_into_table("users",str(tuple(tmplist)))
|
||||
|
||||
def fetchall_list_to_goban_dict(list):
|
||||
"""
|
||||
gets the output from read_table (a list),
|
||||
returns a goban dictionary.
|
||||
"""
|
||||
#create dictionary
|
||||
ret = {}
|
||||
#get size of goban
|
||||
for item in list:
|
||||
if item[0] == "size":
|
||||
size=int(item[1])
|
||||
ret["size"] = size
|
||||
#populate dictionary with goban field
|
||||
for item in list:
|
||||
if item[0][0] == "y": #goban fields
|
||||
#get current y coordinate
|
||||
y = int(item[0][1:])
|
||||
#fill dictionary for current y coordinate
|
||||
for x in range(1,size+1):
|
||||
ret[(x,y)]=helper.check_for_int(item[x])
|
||||
else: #other variables
|
||||
ret[item[0]]=helper.check_for_int(item[1])
|
||||
|
||||
return ret
|
||||
|
||||
|
||||
|
||||
def test():
|
||||
#create_table("test")
|
||||
drop_table("test")
|
||||
create_goban_table(9)
|
||||
update_database_field("test","x1","turn_number",4)
|
||||
list = read_table("test")
|
||||
print list
|
||||
dict = fetchall_list_to_goban_dict(list)
|
||||
print dict
|
||||
print dict["turn_number"]
|
||||
print dict["name"]
|
||||
print type(dict["turn_number"])
|
||||
|
||||
|
||||
|
||||
#test()
|
||||
|
||||
|
3
test.py
Normal file
|
@ -0,0 +1,3 @@
|
|||
def hello(req):
|
||||
x = "Hello, world!"
|
||||
return x
|