def leave_game(): """ Leave game, flash status, redirect back to /home """ alt = ensure_user() if alt: return alt api = API() gameid = request.args.get('gameid', None) if gameid is None: flash("Invalid game ID.") return redirect(url_for('error_page')) try: gameid = int(gameid) except ValueError: flash("Invalid game ID.") return redirect(url_for('error_page')) userid = session['userid'] response = api.leave_game(userid, gameid) if response is api.ERR_USER_NOT_IN_GAME: msg = "You are not registered in game %s." % (gameid,) elif response is api.ERR_NO_SUCH_OPEN_GAME: msg = "Invalid game ID." elif isinstance(response, APIError): msg = "An unknown error occurred." else: msg = "You have left game %s." % (gameid,) flash(msg) return redirect(url_for('home_page')) flash(msg) return redirect(url_for('error_page'))
def leave_game(): """ Leave game, flash status, redirect back to /home """ alt = ensure_user() if alt: return alt api = API() gameid = request.args.get('gameid', None) if gameid is None: flash("Invalid game ID.") return redirect(url_for('error_page')) try: gameid = int(gameid) except ValueError: flash("Invalid game ID.") return redirect(url_for('error_page')) userid = session['userid'] response = api.leave_game(userid, gameid) if response is api.ERR_USER_NOT_IN_GAME: msg = "You are not registered in game %s." % (gameid,) elif response is api.ERR_NO_SUCH_OPEN_GAME: msg = "Invalid game ID." elif isinstance(response, APIError): msg = "An unknown error occurred leaving game %d, sorry." % (gameid,) else: msg = "You have left game %s." % (gameid,) flash(msg) return redirect(url_for('home_page')) flash(msg) return redirect(url_for('error_page'))
def ensure_user(): """ Commit user to database and determine userid. May return a complete web response (e.g. in case of error). May flash messages. """ # TODO: REVISIT: automagically hook this into OIDC.check api = API() if not is_authenticated(): # user is not authenticated yet return if is_logged_in(): # user is authenticated and authorised (logged in) api.resubscribe(session['userid']) return identity, email = get_oidc_token_details() req = LoginRequest(identity=identity, email=email) result = api.login(req) if isinstance(result, APIError): flash("Error registering user details.") logging.debug("login error: %s", result) return redirect(url_for('error_page')) session['userid'] = result.userid session['screenname'] = result.screenname flash("You have logged in as '%s'" % (session['screenname'],))
def tree_page(): groupid = request.args.get('id', None) if groupid is None: flash("Invalid group ID.") return redirect(url_for('error_page')) try: groupid = int(groupid) except ValueError: flash("Invalid group ID.") return redirect(url_for('error_page')) api = API() result = api.get_group_tree(groupid) if result == API.ERR_NO_SUCH_GAME: flash("No such group.") return redirect(url_for('error_page')) group_tree = result treeview_data = make_treeview_data(group_tree) _groupid, games = api.get_group_games(gameid=groupid) # TODO: 0.2: https://github.com/jonmiles/bootstrap-treeview navbar_items = default_navbar_items() return render_template('web/tree.html', description=games[0].situation.description, groupid=group_tree.groupid, treeview_data=treeview_data, navbar_items=navbar_items, is_logged_in=is_logged_in(), url=request.url, my_screenname=get_my_screenname())
def join_game(): """ Join game, flash status, redirect back to /home """ alt = ensure_user() if alt: return alt api = API() gameid = request.args.get('gameid', None) if gameid is None: flash("Invalid game ID.") return redirect(url_for('error_page')) try: gameid = int(gameid) except ValueError: flash("Invalid game ID.") return redirect(url_for('error_page')) userid = session['userid'] response = api.join_game(userid, gameid) if response is api.ERR_JOIN_GAME_ALREADY_IN: msg = "You are already registered in game %s." % (gameid,) elif response is api.ERR_JOIN_GAME_GAME_FULL: msg = "Game %s is full." % (gameid,) elif response is api.ERR_NO_SUCH_OPEN_GAME: msg = "Invalid game ID." elif isinstance(response, APIError): msg = "An unknown error occurred." else: msg = "You have joined game %s." % (gameid,) flash(msg) return redirect(url_for('home_page')) flash(msg) return redirect(url_for('error_page'))
def game_page(): """ User's view of the specified game """ alt = ensure_user() if alt: return alt gameid = request.args.get('gameid', None) if gameid is None: flash("Invalid game ID.") return redirect(url_for('error_page')) try: gameid = int(gameid) except ValueError: flash("Invalid game ID.") return redirect(url_for('error_page')) userid = session['userid'] api = API() response = api.get_private_game(gameid, userid) if isinstance(response, APIError): if response is api.ERR_NO_SUCH_RUNNING_GAME: msg = "Invalid game ID." else: msg = "An unknown error occurred." flash(msg) return redirect(url_for('error_page')) if response.is_finished(): return _finished_game(response, gameid) else: return _running_game(response, gameid, userid, api)
def authenticated_game_page(gameid): """ Game page when user is authenticated (i.e. the private view) """ alternate_response = ensure_user() if alternate_response: return alternate_response userid = session['userid'] api = API() response = api.get_private_game(gameid, userid) if isinstance(response, APIError): if response is api.ERR_NO_SUCH_GAME: msg = "Invalid game ID." else: msg = "An unknown error occurred retrieving game %d, sorry." % \ (gameid,) flash(msg) return redirect(url_for('error_page')) if response.is_finished(): return _finished_game(response, gameid, userid) elif response.game_details.current_player == None: return _paused_game(response, gameid, userid) else: return _running_game(response, gameid, userid, api)
def chat_page(): """ Chat history for game, and a text field to chat into game. Requires that user is registered in game. """ gameid = request.args.get('gameid', None) if gameid is None: flash("Invalid game ID.") return redirect(url_for('error_page')) try: gameid = int(gameid) except ValueError: flash("Invalid game ID.") return redirect(url_for('error_page')) userid = session['userid'] api = API() response = api.get_private_game(gameid, userid) if isinstance(response, APIError): if response is api.ERR_NO_SUCH_GAME: msg = "Invalid game ID." else: msg = "An unknown error occurred retrieving game %d, sorry." % \ (gameid,) flash(msg) return redirect(url_for('error_page')) return _chat_page(response, gameid, userid, api)
def group_page(): gameid = request.args.get('id', None) if gameid is None: flash("Invalid game ID.") return redirect(url_for('error_page')) try: gameid = int(gameid) except ValueError: flash("Invalid game ID.") return redirect(url_for('error_page')) if is_logged_in(): userid = session['userid'] else: userid = None api = API() result = api.get_group_games(gameid=gameid, userid=userid) if result == API.ERR_NO_SUCH_GAME: flash("No such game.") return redirect(url_for('error_page')) del gameid groupid, games = result # pylint:disable=unpacking-non-sequence # We rely on all games having the same players here - and there being at # least one of them! screennames = [rgp.user.screenname for rgp in games[0].rgp_details] results_by_player = {screenname: 0.0 for screenname in screennames} for i, screenname in enumerate(screennames): for game in games: if not game.rgp_details[i].results: continue results_by_player[screenname] += game.spawn_factor * \ game.rgp_details[i].results['ev'] total_results = [{'screenname': screenname, 'result': results_by_player[screenname]} for screenname in screennames] total_weight = sum(game.spawn_factor for game in games if game.is_analysed) # TODO: 2: (big work! awesome!) betting line "roll-up" and/or EV tree view games = sorted(games, key=game_line_key) navbar_items = default_navbar_items() return render_template('web/group.html', description=games[0].situation.description, games=games, screennames=[user.screenname for user in games[0].users], total_weight=total_weight, total_results=total_results, userid=userid, groupid=groupid, navbar_items=navbar_items, is_logged_in=is_logged_in(), is_first_action=is_what_now(), url=request.url, my_screenname=get_my_screenname())
def ensure_user(): """ Commit user to database and determine userid """ # TODO: REVISIT: automagically hook this into AUTH.required if not is_authenticated(): # user is not authenticated yet return if is_logged_in(): # user is authenticated and authorised (logged in) return if 'screenname' in session: # user had changed screenname but not yet logged in screenname = session['screenname'] else: # regular login screenname = g.user['name'] api = API() req = LoginRequest(identity=g.user['identity'], # @UndefinedVariable email=g.user['email'], # @UndefinedVariable screenname=screenname) result = api.login(req) if result == API.ERR_DUPLICATE_SCREENNAME: session['screenname'] = g.user['name'] # User is authenticated with OpenID, but not yet authorised (logged # in). We redirect them to a page that allows them to choose a # different screenname. if request.endpoint != 'change_screenname': flash("The screenname '%s' is already taken." % (screenname,)) return redirect(url_for('change_screenname')) elif isinstance(result, APIError): flash("Error registering user details.") logging.debug("login error: %s", result) return redirect(url_for('error_page')) else: # If their Google name is "Player <X>", they will not be able to have # their Google name as their screenname. Unless their login errors. session['userid'] = result.userid session['screenname'] = result.screenname req2 = ChangeScreennameRequest(result.userid, "Player %d" % (result.userid,)) if not result.existed and result.screenname != req2.screenname: result2 = api.change_screenname(req2) if isinstance(result2, APIError): flash("Couldn't give you screenname '%s', " "you are stuck with '%s' until you change it." % (req2.screenname, result.screenname)) else: session['screenname'] = req2.screenname flash("You have logged in as '%s'" % (session['screenname'],))
def join_game(): """ Join game, flash status, redirect back to /home """ # TODO: REVISIT: vulnerable to cross-site request forgery alternate_response = ensure_user() if alternate_response: return alternate_response api = API() gameid = request.args.get('gameid', None) if gameid is None: flash("Invalid game ID.") return redirect(url_for('error_page')) try: gameid = int(gameid) except ValueError: flash("Invalid game ID.") return redirect(url_for('error_page')) userid = session['userid'] response = api.join_game(userid, gameid) if response is api.ERR_JOIN_GAME_ALREADY_IN: msg = "You are already registered in game %s." % (gameid,) elif response is api.ERR_JOIN_GAME_GAME_FULL: msg = "Game %d is full." % (gameid,) elif response is api.ERR_NO_SUCH_OPEN_GAME: msg = "Invalid game ID." elif response is api.ERR_NO_SUCH_USER: msg = "Oddly, your account does not seem to exist. " + \ "Try logging out and logging back in." logging.debug("userid %d can't register for game %d, " + "because user doesn't exist.", userid, gameid) elif isinstance(response, APIError): msg = "An unknown error occurred joining game %d, sorry." % (gameid,) logging.debug("unrecognised error from api.join_game: %s", response) else: msg = "Game joined!" flash(msg) if response is None: return redirect(url_for('home_page')) else: return redirect(url_for('game_page', gameid=response)) flash(msg) return redirect(url_for('error_page'))
def user_page(): screenname = request.args.get('screenname', None) if screenname is None: return error("Invalid screenname.") min_hands = request.args.get('min', '1') try: min_hands = int(min_hands) except ValueError: return error("Invalid min.") is_competition = request.args.get('mode', 'competition') == 'competition' api = API() result = api.get_user_by_screenname(screenname) if result == API.ERR_NO_SUCH_USER: return error("Unrecognised screenname.") userid = result.userid screenname = result.screenname result = api.get_user_statistics(userid, min_hands=min_hands, is_competition=is_competition) if result == API.ERR_NO_SUCH_USER: return error("No such user.") if isinstance(result, APIError): return error("An unknown error occurred retrieving results.") min_visible = 5 # stats only shown if 5 hands played # suppress user's situation results where insufficient position hands for situation in result: for position in situation.positions: if position.played < min_visible: situation.average = None navbar_items = default_navbar_items() return render_template('web/user.html', screenname=screenname, situations=result, min_visible=min_visible, mode='competition' if is_competition else 'optimization', navbar_items=navbar_items, is_logged_in=is_logged_in(), is_first_action=is_what_now(), url=request.url, my_screenname=get_my_screenname())
def unauthenticated_game_page(gameid): """ Game page when user is authenticated (i.e. the private view) """ api = API() response = api.get_public_game(gameid) if isinstance(response, APIError): if response is api.ERR_NO_SUCH_RUNNING_GAME: msg = "Invalid game ID." else: msg = "An unknown error occurred retrieving game %d, sorry." % \ (gameid,) flash(msg) return redirect(url_for('error_page')) if response.is_finished(): return _finished_game(response, gameid) else: return _running_game(response, gameid, None, api)
def unauthenticated_game_page(gameid): """ Game page when user is not authenticated (i.e. the public view) """ api = API() response = api.get_public_game(gameid) if isinstance(response, APIError): if response is api.ERR_NO_SUCH_GAME: msg = "Invalid game ID." else: msg = "An unknown error occurred retrieving game %d, sorry." % \ (gameid,) flash(msg) return redirect(url_for('error_page')) if response.is_finished(): return _finished_game(response, gameid, None) else: return _running_game(response, gameid, None, api)
def unsubscribe(): """ Record that the user does not want to receive any more emails, at least until they log in again and in so doing clear that flag. Note that authentication is not required. """ api = API() identity = request.args.get('identity', None) if identity is None: msg = "Invalid request, sorry." else: response = api.unsubscribe(identity) if response is api.ERR_NO_SUCH_USER: msg = "No record of you on Range vs. Range, sorry." elif isinstance(response, APIError): msg = "An unknown error occurred unsubscribing you, sorry." else: msg = "You have been unsubscribed. If you log in again, you will start receiving emails again." # pylint:disable=C0301 flash(msg) return render_template('web/flash.html', title='Unsubscribe')
def unsubscribe(): """ Record that the user does not want to receive any more emails, at least until they log in again and in so doing clear that flag. Note that authentication is not required. """ api = API() identity = request.args.get('identity', None) if identity is None: msg = "Invalid request, sorry." else: response = api.unsubscribe(identity) if response is api.ERR_NO_SUCH_USER: msg = "No record of you on Range vs. Range, sorry." elif isinstance(response, APIError): msg = "An unknown error occurred." else: msg = "You have been unsubscribed. If you log in again, you will start receiving emails again." # pylint:disable=C0301 flash(msg) return render_template('base.html', title='Unsubscribe')
def _main(): """ Does some initialisation, then runs the website. """ # Initialise database api = API() api.create_db() api.initialise_db() api.ensure_open_games() # Run the app locally APP.run(host=APP.config.get('HOST', '0.0.0.0'), port=APP.config.get('PORT', 80), debug=APP.config['DEBUG'], use_reloader=APP.config.get('RELOADER', False))
def join_game(): """ Join game, flash status, redirect back to /home """ alt = ensure_user() if alt: return alt api = API() gameid = request.args.get('gameid', None) if gameid is None: flash("Invalid game ID.") return redirect(url_for('error_page')) try: gameid = int(gameid) except ValueError: flash("Invalid game ID.") return redirect(url_for('error_page')) userid = session['userid'] response = api.join_game(userid, gameid) if response is api.ERR_JOIN_GAME_ALREADY_IN: msg = "You are already registered in game %s." % (gameid,) elif response is api.ERR_JOIN_GAME_GAME_FULL: msg = "Game %d is full." % (gameid,) elif response is api.ERR_NO_SUCH_OPEN_GAME: msg = "Invalid game ID." elif response is api.ERR_NO_SUCH_USER: msg = "Oddly, your account does not seem to exist. " + \ "Try logging out and logging back in." logging.debug("userid %d can't register for game %d, " + "because user doesn't exist.", userid, gameid) elif isinstance(response, APIError): msg = "An unknown error occurred joining game %d, sorry." % (gameid,) logging.debug("unrecognised error from api.join_game: %s", response) else: msg = "You have joined game %s." % (gameid,) flash(msg) return redirect(url_for('home_page')) flash(msg) return redirect(url_for('error_page'))
def home_page(): """ Generates the unauthenticated landing page. AKA the main or home page. """ if not is_authenticated(): return render_template('web/landing.html') alt = ensure_user() if alt: return alt api = API() userid = session['userid'] screenname = session['screenname'] open_games = api.get_open_games() if isinstance(open_games, APIError): flash("An unknown error occurred retrieving your open games.") return redirect(url_for("error_page")) my_games = api.get_user_running_games(userid) if isinstance(my_games, APIError): flash("An unknown error occurred retrieving your running games.") return redirect(url_for("error_page")) selected_heading = request.cookies.get("selected-heading", "heading-open") my_games.running_details.sort( key=lambda rg: rg.current_user_details.userid != userid) my_open = [og for og in open_games if any([u.userid == userid for u in og.users])] others_open = [og for og in open_games if not any([u.userid == userid for u in og.users])] form = ChangeForm() navbar_items = [('active', url_for('home_page'), 'Home'), ('', url_for('about_page'), 'About'), ('', url_for('faq_page'), 'FAQ')] return render_template('web/home.html', title='Home', screenname=screenname, userid=userid, change_form=form, r_games=my_games, my_open=my_open, others_open=others_open, my_finished_games=my_games.finished_details, navbar_items=navbar_items, selected_heading=selected_heading, is_logged_in=is_logged_in())
def ensure_user(): """ Commit user to database and determine userid """ # TODO: REVISIT: automagically hook this into AUTH.required if not is_authenticated(): # user is not authenticated yet return if 'userid' in session and 'screenname' in session: # user is authenticated and authorised (logged in) return if 'screenname' in session: # user had changed screenname but not yet logged in screenname = session['screenname'] else: # regular login screenname = g.user['name'] api = API() req = LoginRequest(identity=g.user['identity'], # @UndefinedVariable email=g.user['email'], # @UndefinedVariable screenname=screenname) result = api.login(req) if result == API.ERR_DUPLICATE_SCREENNAME: session['screenname'] = g.user['name'] # User is authenticated with OpenID, but not yet authorised (logged # in). We redirect them to a page that allows them to choose a # different screenname. if request.endpoint != 'change_screenname': flash("The screenname '%s' is already taken." % screenname) return redirect(url_for('change_screenname')) elif isinstance(result, APIError): flash("Error registering user details.") logging.debug("login error: %s", result) return redirect(url_for('error_page')) else: session['screenname'] = result.screenname session['userid'] = result.userid flash("You have logged in as '%s'." % (result.screenname, ))
def group_ev(): """ Displays a range viewer type thing with popovers to show EV of each combo. Only for a single game - not for a group! """ groupid = request.args.get('groupid', None) if groupid is None: flash("Invalid groupid.") return redirect(url_for('error_page')) try: groupid = int(groupid) except ValueError: flash("Invalid groupid.") return redirect(url_for('error_page')) screenname = request.args.get('user', None) if screenname is None: flash("Invalid user.") return redirect(url_for('error_page')) api = API() result = api.get_group_tree(groupid) if result == API.ERR_NO_SUCH_GAME: flash("No such group.") return redirect(url_for('error_page')) game_tree = result matches = [user for user in game_tree.users if user.screenname == screenname] if len(matches) != 1: flash("Invalid user.") return redirect(url_for('error_page')) userid = matches[0].userid ev_by_combo = game_tree.root.all_combos_ev(userid, local=False) rank_table = make_ev_rank_table(ev_by_combo) return render_template('web/range_viewer.html', title='EV Viewer', next_map=NEXT_MAP, rank_table=rank_table)
def home_page(): """ Generates the unauthenticated landing page. AKA the main or home page. """ if not is_authenticated(): return render_template('landing.html', title='Welcome') alt = ensure_user() if alt: return alt api = API() userid = session['userid'] screenname = session['screenname'] open_games = api.get_open_games() if isinstance(open_games, APIError): flash("An unknown error occurred retrieving your open games.") return redirect(url_for("error_page")) my_games = api.get_user_running_games(userid) if isinstance(my_games, APIError): flash("An unknown error occurred retrieving your running games.") return redirect(url_for("error_page")) my_open = [og for og in open_games if any([u.userid == userid for u in og.users])] others_open = [og for og in open_games if not any([u.userid == userid for u in og.users])] my_turn_games = [mg for mg in my_games.running_details if mg.current_user_details.userid == userid] others_turn_games = [mg for mg in my_games.running_details if mg.current_user_details.userid != userid] return render_template('home.html', title='Home', screenname=screenname, my_open=my_open, others_open=others_open, my_turn_games=my_turn_games, others_turn_games=others_turn_games, my_finished_games=my_games.finished_details )
def change_screenname(): """ Without the user being logged in, give the user the option to change their screenname from what Google OpenID gave us. """ alt = ensure_user() if alt: return alt form = ChangeForm() if form.validate_on_submit(): new_screenname = form.change.data if 'userid' in session: # Having a userid means they're in the database. req = ChangeScreennameRequest(session['userid'], new_screenname) resp = API().change_screenname(req) if resp == API.ERR_DUPLICATE_SCREENNAME: flash("That screenname is already taken.") elif isinstance(resp, APIError): logging.debug("change_screenname error: %s", resp) flash("An error occurred.") else: session['screenname'] = new_screenname flash("Your screenname has been changed to '%s'." % (new_screenname, )) return redirect(url_for('home_page')) else: # User is not logged in. Changing screenname in session is enough. # Now when they go to the home page, ensure_user() will create their # account. session['screenname'] = new_screenname return redirect(url_for('home_page')) current = session['screenname'] if 'screenname' in session \ else g.user['name'] navbar_items = [('', url_for('home_page'), 'Home'), ('', url_for('about_page'), 'About'), ('', url_for('faq_page'), 'FAQ')] return render_template('web/change.html', title='Change Your Screenname', current=current, form=form, navbar_items=navbar_items, is_logged_in=is_logged_in(), is_account=True)
class AdminCmd(Cmd): """ Cmd class to make calls to an API instance """ def __init__(self): Cmd.__init__(self) self.api = API() def do_recreate_timeouts(self, _details): """ Recreate timeouts that were accidentally lost via dump """ # TODO: 0: They could be (somewhat) manually reconstructed # for each game: # gather all hand history items # look for a gap # fill the gap with a timeout def do_createdb(self, _details): """ Create the database """ result = self.api.create_db() if result: print "Error:", result else: print "Database created" def do_initialise(self, _details): """ Initialise a (created but empty) database """ result = self.api.initialise_db() if result: print "Error:", result else: print "Database initialised" result = self.api.ensure_open_games() if result: print "Error:", result else: print "Open games refreshed." def do_login(self, params): """ Calls the login API function login(identity, email, screenname) """ params = params.split(None, 2) if len(params) == 3: request = LoginRequest(identity=params[0], email=params[1], screenname=params[2]) else: print "Need exactly 3 parameters." print "For more info, help login" return response = self.api.login(request) if isinstance(response, APIError): print "Error:", response return print "User is logged in, response %r" % \ (response) def do_getuserid(self, details): """ getuserid <screenname> shows userid by screenname """ user = self.api.get_user_by_screenname(details) if user is None: print "No such user" else: print "'%s' has userid %s" % (user.screenname, user.userid) def do_rmuser(self, details): """ rmuser <userid> deletes user <userid> """ userid = int(details) response = self.api.delete_user(userid) if isinstance(response, APIError): print "Error:", response elif response: print "Deleted." else: print "Nothing happened." def do_getuser(self, details): """ getuser <userid> shows user's login details """ userid = int(details) response = self.api.get_user(userid) if isinstance(response, APIError): print "Error:", response else: print "userid='%s'\nidentity='%s'\nemail='%s'\nscreenname='%s'" % \ (response.userid, response.identity, response.email, response.screenname) def do_changesn(self, details): """ changesn <userid> <newname> changes user's screenname to <newname> """ args = details.split(None, 1) userid = int(args[0]) newname = args[1] req = ChangeScreennameRequest(userid, newname) result = self.api.change_screenname(req) if isinstance(result, APIError): print "Error:", result.description else: print "Changed userid %d's screenname to '%s'" % \ (userid, newname) def do_opengames(self, _details): """ Display open games, their descriptions, and their registered users. """ response = self.api.get_open_games() if isinstance(response, APIError): print response return print "Open games:" for details in response: print details def do_runninggames(self, _details): """ Display running games, their descriptions, and their users. """ result = self.api.get_running_games() if isinstance(result, APIError): print "Error:", result.description return print "Running games:" for details in result: print details def do_joingame(self, params): """ joingame <userid> <gameid> registers <userid> in open game <gameid> """ args = params.split() userid = int(args[0]) gameid = int(args[1]) result = self.api.join_game(userid, gameid) if isinstance(result, APIError): print "Error:", result.description elif result is None: print "Registered userid %d in open game %d" % (userid, gameid) else: print "Registered userid %d in open game %d" % (userid, gameid) print "Started running game %d" % (result,) def do_leavegame(self, params): """ leavegame <userid> <gameid> unregisters <userid> from open game <gameid> """ args = params.split() userid = int(args[0]) gameid = int(args[1]) result = self.api.leave_game(userid, gameid) if isinstance(result, APIError): print "Error:", result.description else: print "Unregistered userid %d from open game %d" % (userid, gameid) def do_usersgames(self, params): """ usersgames <userid> show details of all games associated with userid """ userid = int(params) result = self.api.get_user_running_games(userid) if isinstance(result, APIError): print "Error:", result.description # pylint:disable=E1101 return print "Running games:" for game in result.running_details: print game print "Finished games:" for game in result.finished_details: print game def do_act(self, params): """ act <gameid> <userid> <fold> <passive> <aggressive> <total> perform an action in a game as a user """ params = params.split() gameid = int(params[0]) userid = int(params[1]) fold = params[2] passive = params[3] aggressive = params[4] total = int(params[5]) range_action = dtos.ActionDetails( fold_raw=fold, passive_raw=passive, aggressive_raw=aggressive, raise_total=total) response = self.api.perform_action(gameid, userid, range_action) if isinstance(response, APIError): print "Error:", response.description # pylint:disable=E1101 return # pylint:disable=E1103 if response.is_fold: print "You folded." elif response.is_passive: print "You called." elif response.is_aggressive: print "You raised to %d." % (response.raise_total,) else: print "Oops. This isn't right." def do_update(self, _details): """ The kind of updates a background process would normally do. Currently includes: - ensure there's exactly one empty open game of each situation. """ result = self.api.ensure_open_games() if result: print "Error:", result else: print "Open games refreshed." def do_handhistory(self, params): """ handhistory <gameid> [<userid>] Display hand history for given game, from given user's perspective, if specified. """ args = params.split(None, 1) gameid = int(args[0]) if len(args) > 1: userid = int(args[1]) result = self.api.get_private_game(gameid, userid) else: userid = None result = self.api.get_public_game(gameid) if isinstance(result, APIError): print "Error:", result.description # pylint:disable=E1101 return print result def do_analyse(self, details): """ analyse [refresh] Run all analysis. If refresh, reanalyse everything. """ if details == "": result = self.api.run_pending_analysis() elif details == "refresh": result = self.api.reanalyse_all() else: print "Bad syntax. See 'help analyse'." return if isinstance(result, APIError): print "Error:", result.description else: print "Analysis run." def do_timeout(self, _details): """ timeout Fold any players who have timed out. """ result = self.api.process_timeouts() if isinstance(result, APIError): print "Error:", result.description else: print result, "timeouts processed." def do_dump(self, params): """ dump { out | in } dump out pickles the database to db.pkl dump in unpickles it To restore a database from a db.pkl file: 1. delete the database file (rvr.db) 2. "createdb" 3. "dump in" 4. "initialise" The "initiialise" does things like refreshing open games, because open games are not dumped out by "dump out". """ filename = 'db.pkl' if params == 'out': dump(filename) print "Successfully exported database to %s." % (filename,) elif params == 'in': try: load(filename) except IntegrityError as err: print "IntegrityError. Is the database empty?" print "Perhaps delete the database and try the 'createdb' command." # pylint:disable=C0301 print "Details:", err return except OperationalError as err: print "OperationalError. Does the database exist?" print "Perhaps try the 'createdb' command." print "Details:", err return print "Successfully read %s into database." % (filename,) else: print "Bad syntax. See 'help dump'." def do_exit(self, _details): """ Close the admin interface """ print "Goodbye." return True
def __init__(self): Cmd.__init__(self) self.api = API()
class AdminCmd(Cmd): """ Cmd class to make calls to an API instance """ def __init__(self): Cmd.__init__(self) self.api = API() def do_createdb(self, _details): """ Create the database """ result = self.api.create_db() if result: print "Error:", result else: print "Database created" def do_deletedb(self, details): """ Delete the database, and everything in it """ if details != "048250": print "Let me think about it..." time.sleep(3) print "No." return result = self.api.delete_db() if result: print "Error:", result else: print "Database deleted" def do_initialise(self, _details): """ Initialise a (created but empty) database """ result = self.api.initialise_db() if result: print "Error:", result else: print "Database initialised" result = self.api.ensure_open_games() if result: print "Error:", result else: print "Open games refreshed." def do_dump(self, params): """ dump { out | in } dump out pickles the database to db.pkl dump in unpickles it To restore a database from a db.pkl file: 1. delete the database file (rvr.db) 2. "createdb" 3. "dump in" 4. "initialise" The "initialise" does things like refreshing open games, because open games are not dumped out by "dump out". """ filename = 'db.pkl' if params == 'out': dump(filename) print "Successfully exported database to %s." % (filename,) elif params == 'in': try: load(filename) except IntegrityError as err: print "IntegrityError. Is the database empty?" print "Perhaps delete the database and try the 'createdb' command." # pylint:disable=C0301 print "Details:", err return except OperationalError as err: print "OperationalError. Does the database exist?" print "Perhaps try the 'createdb' command." print "Details:", err return print "Successfully read %s into database." % (filename,) else: print "Bad syntax. See 'help dump'." def do_situation(self, details): """ Load situation from given python module and add it to the DB. E.g.: situation hu.py will load a situation from a method called reate_situation defined in hu.py, and add it to the database. You can also load directly from the command line: python console.py situation hu.py """ # TODO: REVISIT: this can't be used to accept user-defined situations. with open(details, 'r') as handle: source = handle.read() exec(source) # pylint:disable=exec-used # pylint:disable=undefined-variable situation = create_situation() # @UndefinedVariable result = self.api.add_situation(situation) print "added situation, result", result def do_login(self, params): """ Calls the login API function login(identity, email) """ params = params.split(None, 1) if len(params) == 2: request = LoginRequest(identity=params[0], email=params[1]) else: print "Need exactly 2 parameters." print "For more info, help login" return response = self.api.login(request) if isinstance(response, APIError): print "Error:", response return print "User is logged in, response %r" % \ (response) def do_getuserid(self, details): """ getuserid <screenname> shows userid by screenname """ user = self.api.get_user_by_screenname(details) if user is None: print "No such user" else: print "'%s' has userid %s" % (user.screenname, user.userid) def do_rmuser(self, details): """ rmuser <userid> deletes user <userid> """ userid = int(details) response = self.api.delete_user(userid) if isinstance(response, APIError): print "Error:", response elif response: print "Deleted." else: print "Nothing happened." def do_getuser(self, details): """ getuser <userid> shows user's login details """ userid = int(details) response = self.api.get_user(userid) if isinstance(response, APIError): print "Error:", response else: print "userid='%s'\nidentity='%s'\nemail='%s'\nscreenname='%s'" % \ (response.userid, response.identity, response.email, response.screenname) def do_changesn(self, details): """ changesn <userid> <newname> changes user's screenname to <newname> """ args = details.split(None, 1) userid = int(args[0]) newname = args[1] req = ChangeScreennameRequest(userid, newname) result = self.api.change_screenname(req) if isinstance(result, APIError): print "Error:", result.description else: print "Changed userid %d's screenname to '%s'" % \ (userid, newname) def do_opengames(self, _details): """ Display open games, their descriptions, and their registered users. """ response = self.api.get_open_games() if isinstance(response, APIError): print response return print "Open games:" for details in response: print details def do_joingame(self, params): """ joingame <userid> <gameid> registers <userid> in open game <gameid> """ args = params.split() userid = int(args[0]) gameid = int(args[1]) result = self.api.join_game(userid, gameid) if isinstance(result, APIError): print "Error:", result.description elif result is None: print "Registered userid %d in open game %d" % (userid, gameid) else: print "Registered userid %d in open game %d" % (userid, gameid) print "Started running game %d" % (result,) def do_leavegame(self, params): """ leavegame <userid> <gameid> unregisters <userid> from open game <gameid> """ args = params.split() userid = int(args[0]) gameid = int(args[1]) result = self.api.leave_game(userid, gameid) if isinstance(result, APIError): print "Error:", result.description else: print "Unregistered userid %d from open game %d" % (userid, gameid) def do_usersgames(self, params): """ usersgames <userid> show details of all games associated with userid """ userid = int(params) result = self.api.get_user_running_games(userid) if isinstance(result, APIError): print "Error:", result.description # pylint:disable=E1101 return print "Running games:" for game in result.running_details: print game print "Finished games:" for game in result.finished_details: print game def do_act(self, params): """ act <gameid> <userid> <fold> <passive> <aggressive> <total> perform an action in a game as a user """ params = params.split() gameid = int(params[0]) userid = int(params[1]) fold = params[2] passive = params[3] aggressive = params[4] total = int(params[5]) range_action = dtos.ActionDetails( fold_raw=fold, passive_raw=passive, aggressive_raw=aggressive, raise_total=total) response = self.api.perform_action(gameid, userid, range_action) if isinstance(response, APIError): print "Error:", response.description # pylint:disable=E1101 return action, spawned, is_first_action = response # pylint:disable=unpacking-non-sequence,line-too-long # pylint:disable=E1103 if action.is_fold: print "You folded." elif action.is_passive: print "You called." elif action.is_aggressive: print "You raised to %d." % (action.raise_total,) else: print "Action:", action print "Spawned:", spawned print "Is first action:", is_first_action def do_chat(self, details): """ chat <gameid> <userid> <message> """ params = details.split(None, 2) if len(params) != 3: print "Need exactly 3 parameters." print "For more info: help chat" return gameid = int(params[0]) userid = int(params[1]) message = params[2] response = self.api.chat(gameid, userid, message) if isinstance(response, APIError): print "Error:", response.description # pylint:disable=E1101 return print "Chatted in game %d, for userid %d, message %r" % \ (gameid, userid, message) def do_update(self, _details): """ The kind of updates a background process would normally do. Currently includes: - ensure there's exactly one empty open game of each situation. """ result = self.api.ensure_open_games() if result: print "Error:", result else: print "Open games refreshed." def do_handhistory(self, params): """ handhistory <gameid> [<userid>] Display hand history for given game, from given user's perspective, if specified. """ args = params.split(None, 1) gameid = int(args[0]) if len(args) > 1: userid = int(args[1]) result = self.api.get_private_game(gameid, userid) else: userid = None result = self.api.get_public_game(gameid) if isinstance(result, APIError): print "Error:", result.description # pylint:disable=E1101 return print "(suppressing analysis)" # There's just so damn much of it, the best way to see it is on the web. result.analysis = None print result def do_statistics(self, params): """ statistics <username> <is_competition> Display user's statistics """ args = params.split(None, 1) screenname = args[0] is_competition = len(args) == 1 or args[1] == 'True' result = self.api.get_user_by_screenname(screenname) if isinstance(result, APIError): print "Error:", result.description return result = self.api.get_user_statistics(result.userid, min_hands=1, is_competition=is_competition) if isinstance(result, APIError): print "Error:", result.description return for situation in result: print situation def do_analyse(self, details): """ analyse Run pending analysis analyse <gameid> Re/analyse gameid analyse [refresh] Reanalyse everything. """ if details == "": result = self.api.run_pending_analysis() elif details == "refresh": print "This may re-email everyone for games they have already" \ " had analysis for. If you don't want to do that, turn off" \ " email in local_settings.py first. If you are okay with" \ " re-emailing everyone, or you have checked local_settings.py" \ " the command is 'analyse refresh confirm'." return elif details == "refresh confirm": result = self.api.reanalyse_all() else: try: gameid = int(details) except ValueError: print "Bad syntax. See 'help analyse'." return result = self.api.reanalyse(gameid) if isinstance(result, APIError): print "Error:", result.description else: print "Analysis run." if local_settings.SUPPRESS_EMAIL: print "Email is turned off in local_settings.py. You may now" \ " want to turn it back on." def do_timeout(self, _details): """ timeout Fold any players who have timed out. """ result = self.api.process_timeouts() if isinstance(result, APIError): print "Error:", result.description else: print result, "timeouts processed." def do_tree(self, params): """ tree game <gameid> tree group <groupid> Print game tree for game or group. """ params = params.split(None, 2) if len(params) == 3 and params[2] == 'local': local = True del params[2] else: local = False if len(params) != 2: print "Need exactly 2 parameters." print "For more info, help tree" return if params[0] not in ['game', 'group']: print "Bad syntax. See 'help tree'. (1)" return try: gameid = int(params[1]) except ValueError: print "Bad syntax. See 'help tree'. (2)" if params[0] == 'game': result = self.api.get_game_tree(gameid) else: result = self.api.get_group_tree(gameid) if isinstance(result, APIError): print "Error:", result.description else: print result display_game_tree(result.root, local) def do_volume(self, _details): """ List the players who have completed the most games. """ result = self.api.get_player_volumes() if isinstance(result, APIError): print "Error:", result.description print result def do_exit(self, _details): """ Close the admin interface """ print "Goodbye." return True
class AdminCmd(Cmd): """ Cmd class to make calls to an API instance """ def __init__(self): Cmd.__init__(self) self.api = API() def do_recreate_timeouts(self, _details): """ Recreate timeouts that were accidentally lost via dump """ # TODO: 0: They could be (somewhat) manually reconstructed # for each game: # gather all hand history items # look for a gap # fill the gap with a timeout def do_createdb(self, _details): """ Create the database """ result = self.api.create_db() if result: print "Error:", result else: print "Database created" def do_initialise(self, _details): """ Initialise a (created but empty) database """ result = self.api.initialise_db() if result: print "Error:", result else: print "Database initialised" result = self.api.ensure_open_games() if result: print "Error:", result else: print "Open games refreshed." def do_login(self, params): """ Calls the login API function login(identity, email, screenname) """ params = params.split(None, 2) if len(params) == 3: request = LoginRequest(identity=params[0], email=params[1], screenname=params[2]) else: print "Need exactly 3 parameters." print "For more info, help login" return response = self.api.login(request) if isinstance(response, APIError): print "Error:", response return print "User is logged in, response %r" % \ (response) def do_getuserid(self, details): """ getuserid <screenname> shows userid by screenname """ user = self.api.get_user_by_screenname(details) if user is None: print "No such user" else: print "'%s' has userid %s" % (user.screenname, user.userid) def do_rmuser(self, details): """ rmuser <userid> deletes user <userid> """ userid = int(details) response = self.api.delete_user(userid) if isinstance(response, APIError): print "Error:", response elif response: print "Deleted." else: print "Nothing happened." def do_getuser(self, details): """ getuser <userid> shows user's login details """ userid = int(details) response = self.api.get_user(userid) if isinstance(response, APIError): print "Error:", response else: print "userid='%s'\nidentity='%s'\nemail='%s'\nscreenname='%s'" % \ (response.userid, response.identity, response.email, response.screenname) def do_changesn(self, details): """ changesn <userid> <newname> changes user's screenname to <newname> """ args = details.split(None, 1) userid = int(args[0]) newname = args[1] req = ChangeScreennameRequest(userid, newname) result = self.api.change_screenname(req) if isinstance(result, APIError): print "Error:", result.description else: print "Changed userid %d's screenname to '%s'" % \ (userid, newname) def do_opengames(self, _details): """ Display open games, their descriptions, and their registered users. """ response = self.api.get_open_games() if isinstance(response, APIError): print response return print "Open games:" for details in response: print details def do_runninggames(self, _details): """ Display running games, their descriptions, and their users. """ result = self.api.get_running_games() if isinstance(result, APIError): print "Error:", result.description return print "Running games:" for details in result: print details def do_joingame(self, params): """ joingame <userid> <gameid> registers <userid> in open game <gameid> """ args = params.split() userid = int(args[0]) gameid = int(args[1]) result = self.api.join_game(userid, gameid) if isinstance(result, APIError): print "Error:", result.description elif result is None: print "Registered userid %d in open game %d" % (userid, gameid) else: print "Registered userid %d in open game %d" % (userid, gameid) print "Started running game %d" % (result, ) def do_leavegame(self, params): """ leavegame <userid> <gameid> unregisters <userid> from open game <gameid> """ args = params.split() userid = int(args[0]) gameid = int(args[1]) result = self.api.leave_game(userid, gameid) if isinstance(result, APIError): print "Error:", result.description else: print "Unregistered userid %d from open game %d" % (userid, gameid) def do_usersgames(self, params): """ usersgames <userid> show details of all games associated with userid """ userid = int(params) result = self.api.get_user_running_games(userid) if isinstance(result, APIError): print "Error:", result.description # pylint:disable=E1101 return print "Running games:" for game in result.running_details: print game print "Finished games:" for game in result.finished_details: print game def do_act(self, params): """ act <gameid> <userid> <fold> <passive> <aggressive> <total> perform an action in a game as a user """ params = params.split() gameid = int(params[0]) userid = int(params[1]) fold = params[2] passive = params[3] aggressive = params[4] total = int(params[5]) range_action = dtos.ActionDetails(fold_raw=fold, passive_raw=passive, aggressive_raw=aggressive, raise_total=total) response = self.api.perform_action(gameid, userid, range_action) if isinstance(response, APIError): print "Error:", response.description # pylint:disable=E1101 return # pylint:disable=E1103 if response.is_fold: print "You folded." elif response.is_passive: print "You called." elif response.is_aggressive: print "You raised to %d." % (response.raise_total, ) else: print "Oops. This isn't right." def do_update(self, _details): """ The kind of updates a background process would normally do. Currently includes: - ensure there's exactly one empty open game of each situation. """ result = self.api.ensure_open_games() if result: print "Error:", result else: print "Open games refreshed." def do_handhistory(self, params): """ handhistory <gameid> [<userid>] Display hand history for given game, from given user's perspective, if specified. """ args = params.split(None, 1) gameid = int(args[0]) if len(args) > 1: userid = int(args[1]) result = self.api.get_private_game(gameid, userid) else: userid = None result = self.api.get_public_game(gameid) if isinstance(result, APIError): print "Error:", result.description # pylint:disable=E1101 return print result def do_analyse(self, details): """ analyse [refresh] Run all analysis. If refresh, reanalyse everything. """ if details == "": result = self.api.run_pending_analysis() elif details == "refresh": result = self.api.reanalyse_all() else: print "Bad syntax. See 'help analyse'." return if isinstance(result, APIError): print "Error:", result.description else: print "Analysis run." def do_timeout(self, _details): """ timeout Fold any players who have timed out. """ result = self.api.process_timeouts() if isinstance(result, APIError): print "Error:", result.description else: print result, "timeouts processed." def do_dump(self, params): """ dump { out | in } dump out pickles the database to db.pkl dump in unpickles it To restore a database from a db.pkl file: 1. delete the database file (rvr.db) 2. "createdb" 3. "dump in" 4. "initialise" The "initiialise" does things like refreshing open games, because open games are not dumped out by "dump out". """ filename = 'db.pkl' if params == 'out': dump(filename) print "Successfully exported database to %s." % (filename, ) elif params == 'in': try: load(filename) except IntegrityError as err: print "IntegrityError. Is the database empty?" print "Perhaps delete the database and try the 'createdb' command." # pylint:disable=C0301 print "Details:", err return except OperationalError as err: print "OperationalError. Does the database exist?" print "Perhaps try the 'createdb' command." print "Details:", err return print "Successfully read %s into database." % (filename, ) else: print "Bad syntax. See 'help dump'." def do_exit(self, _details): """ Close the admin interface """ print "Goodbye." return True
def analysis_page(): """ Analysis of a particular hand history item. """ # TODO: 5: the range viewer on the analysis page # This can simply display each rank combo and allow selection of suit # combos. Then it can display, for each rank combo, the average EV of the # selected suit combos, as hover text. I'm not sure about colours. # Or more simply, display EV of each combo in hover text, and colour squares # like a normal range viewer (green / yellow / red / blue). gameid = request.args.get('gameid', None) if gameid is None: return error("Invalid game ID.") try: gameid = int(gameid) except ValueError: return error("Invalid game ID (not a number).") api = API() response = api.get_public_game(gameid) if isinstance(response, APIError): if response is api.ERR_NO_SUCH_GAME: msg = "Invalid game ID." else: msg = "An unknown error occurred retrieving game %d, sorry." % \ (gameid,) return error(msg) game = response order = request.args.get('order', None) if order is None: return error("Invalid order.") try: order = int(order) except ValueError: return error("Invalid order (not a number).") item = None for item in game.history: if item.order == order: break else: return error("Invalid order (not in game).") if not isinstance(item, dtos.GameItemActionResult): return error("Invalid order (not a bet or raise).") if not item.action_result.is_aggressive: return error("Analysis only for bets right now, sorry.") try: aife = game.analysis[order] except KeyError: return error("Analysis for this action is not ready yet.") street_text = aife.STREET_DESCRIPTIONS[aife.street] if item.action_result.is_raise: action_text = "raises to %d" % (item.action_result.raise_total,) else: action_text = "bets %d" % (item.action_result.raise_total,) navbar_items = default_navbar_items() items_aggressive = [i for i in aife.items if i.is_aggressive] items_passive = [i for i in aife.items if i.is_passive] items_fold = [i for i in aife.items if i.is_fold] items_aggressive.sort(key=lambda i: i.immediate_result) # most negative 1st items_passive.sort(key=lambda i: i.immediate_result, reverse=True) items_fold.sort(key=lambda i: i.immediate_result, reverse=True) # Could also have a status column or popover or similar: # "good bluff" = +EV # "bad bluff" = -EV on river # "possible semibluff" = -EV not on river return render_template('web/analysis.html', gameid=gameid, screenname=item.user.screenname, street_text=street_text, action_text=action_text, items_aggressive=items_aggressive, items_passive=items_passive, items_fold=items_fold, is_raise=aife.is_raise, is_check=aife.is_check, navbar_items=navbar_items, is_logged_in=is_logged_in(), url=request.url, my_screenname=get_my_screenname())
def home_page(): """ Generates the authenticated landing page. AKA the main or home page. """ # TODO: 3: 'updated!' indicator on finished games (on the RGPs) # TODO: 3: account page with email preferences, screenname, and spawn if not is_authenticated(): if local_settings.ALLOW_BACKDOOR: return redirect(url_for('backdoor_page')) else: return render_template('web/landing.html') alternate_response = ensure_user() if alternate_response: return alternate_response cfp = request.args.get('cfp', '1') ofp = request.args.get('ofp', '1') try: cfp = int(cfp) except ValueError: cfp = 1 try: ofp = int(ofp) except ValueError: ofp = 1 cstart = (cfp - 1) * 20 ostart = (ofp - 1) * 20 api = API() userid = session['userid'] screenname = session['screenname'] open_games = api.get_open_games() if isinstance(open_games, APIError): flash("An unknown error occurred retrieving your open games.") return redirect(url_for("error_page")) my_games = api.get_user_running_games(userid, cstart, ostart, 20) if isinstance(my_games, APIError): flash("An unknown error occurred retrieving your running games.") return redirect(url_for("error_page")) selected_heading = request.cookies.get("selected-heading", "heading-open") selected_mode = request.cookies.get("selected-mode", "mode-competition") my_running_games = sorted(my_games.running_details, key=lambda g: not g.is_on_me) my_finished_games = my_games.finished_details my_running_groups = sorted(my_games.running_groups, key=lambda g: not g.is_on_me) my_finished_groups = my_games.finished_groups my_open = [og for og in open_games if any([u.userid == userid for u in og.users])] others_open = [og for og in open_games if not any([u.userid == userid for u in og.users])] # if they have *any* running games, we will show them, regardless if selected_heading == "heading-my": has_games = my_running_games and my_running_games[0].is_on_me has_groups = my_running_groups and my_running_groups[0].is_on_me if has_groups and not has_games: selected_mode = "mode-public" if has_games and not has_groups: selected_mode = "mode-competition" form = ChangeForm() navbar_items = default_navbar_items('Home') response = make_response(render_template('web/home.html', title='Home', screenname=screenname, userid=userid, change_form=form, my_running_games=my_running_games, my_finished_games=my_finished_games, my_running_groups=my_running_groups, my_finished_groups=my_finished_groups, my_open=my_open, others_open=others_open, cfp=cfp, cfp_less=my_games.c_less, cfp_more=my_games.c_more, ofp=ofp, ofp_less=my_games.o_less, ofp_more=my_games.o_more, navbar_items=navbar_items, selected_heading=selected_heading, selected_mode=selected_mode, is_logged_in=is_logged_in(), is_first_action=is_what_now(), my_screenname=get_my_screenname())) response.set_cookie('selected-mode', selected_mode) # may have changed return response
class AdminCmd(Cmd): """ Cmd class to make calls to an API instance """ def __init__(self): Cmd.__init__(self) self.api = API() def do_createdb(self, _details): """ Create the database """ result = self.api.create_db() if result: print "Error:", result else: print "Database created" result = self.api.initialise_db() if result: print "Error:", result else: print "Database initialised" result = self.api.ensure_open_games() if result: print "Error:", result else: print "Open games refreshed." def do_login(self, params): """ Calls the login API function login(identity, email, screenname) """ params = params.split(None, 2) if len(params) == 3: request = LoginRequest(identity=params[0], email=params[1], screenname=params[2]) else: print "Need exactly 3 parameters." print "For more info, help login" return response = self.api.login(request) if isinstance(response, APIError): print "Error:", response return print "User is logged in with userid='%s', screenname='%s'" % \ (response.userid, response.screenname) def do_getuserid(self, details): """ getuserid <screenname> shows userid by screenname """ user = self.api.get_user_by_screenname(details) if user is None: print "No such user" else: print "'%s' has userid %s" % (user.screenname, user.userid) def do_rmuser(self, details): """ rmuser <userid> deletes user <userid> """ userid = int(details) response = self.api.delete_user(userid) if isinstance(response, APIError): print "Error:", response elif response: print "Deleted." else: print "Nothing happened." def do_getuser(self, details): """ getuser <userid> shows user's login details """ userid = int(details) response = self.api.get_user(userid) if isinstance(response, APIError): print "Error:", response else: print "userid='%s'\nidentity='%s'\nemail='%s'\nscreenname='%s'" % \ (response.userid, response.identity, response.email, response.screenname) def do_changesn(self, details): """ changesn <userid> <newname> changes user's screenname to <newname> """ args = details.split(None, 1) userid = int(args[0]) newname = args[1] req = ChangeScreennameRequest(userid, newname) result = self.api.change_screenname(req) if isinstance(result, APIError): print "Error:", result.description else: print "Changed userid %d's screenname to '%s'" % \ (userid, newname) def do_opengames(self, _details): """ Display open games, their descriptions, and their registered users. """ response = self.api.get_open_games() if isinstance(response, APIError): print response return print "Open games:" for details in response: print details def do_runninggames(self, _details): """ Display running games, their descriptions, and their users. """ result = self.api.get_running_games() if isinstance(result, APIError): print "Error:", result.description return print "Running games:" for details in result: print details def do_joingame(self, params): """ joingame <userid> <gameid> registers <userid> in open game <gameid> """ args = params.split() userid = int(args[0]) gameid = int(args[1]) result = self.api.join_game(userid, gameid) if isinstance(result, APIError): print "Error:", result.description elif result is None: print "Registered userid %d in open game %d" % (userid, gameid) else: print "Registered userid %d in open game %d" % (userid, gameid) print "Started running game %d" % (result,) def do_leavegame(self, params): """ leavegame <userid> <gameid> unregisters <userid> from open game <gameid> """ args = params.split() userid = int(args[0]) gameid = int(args[1]) result = self.api.leave_game(userid, gameid) if isinstance(result, APIError): print "Error:", result.description else: print "Unregistered userid %d from open game %d" % (userid, gameid) def do_usersgames(self, params): """ usersgames <userid> show details of all games associated with userid """ userid = int(params) result = self.api.get_user_running_games(userid) if isinstance(result, APIError): print "Error:", result.description # pylint:disable=E1101 return print "Running games:" for game in result.running_details: print game print "Finished games:" for game in result.finished_details: print game def do_act(self, params): """ act <gameid> <userid> <fold> <passive> <aggressive> <total> perform an action in a game as a user """ params = params.split() gameid = int(params[0]) userid = int(params[1]) fold = params[2] passive = params[3] aggressive = params[4] total = int(params[5]) range_action = dtos.ActionDetails( fold_raw=fold, passive_raw=passive, aggressive_raw=aggressive, raise_total=total) response = self.api.perform_action(gameid, userid, range_action) if isinstance(response, APIError): print "Error:", response.description # pylint:disable=E1101 return # pylint:disable=E1103 if response.is_fold: print "You folded." elif response.is_passive: print "You called." elif response.is_aggressive: print "You raised to %d." % (response.raise_total,) else: print "Oops. This isn't right." def do_update(self, _details): """ The kind of updates a background process would normally do. Currently includes: - ensure there's exactly one empty open game of each situation. """ result = self.api.ensure_open_games() if result: print "Error:", result else: print "Open games refreshed." def do_handhistory(self, params): """ handhistory <gameid> [<userid>] display hand history for given game, from given user's perspective, if specified. """ args = params.split(None, 1) gameid = int(args[0]) if len(args) > 1: userid = int(args[1]) result = self.api.get_private_game(gameid, userid) else: userid = None result = self.api.get_public_game(gameid) if isinstance(result, APIError): print "Error:", result.description # pylint:disable=E1101 return print result def do_exit(self, _details): """ Close the admin interface """ print "Goodbye." return True
def analysis_page(): """ Analysis of a particular hand history item. """ gameid = request.args.get('gameid', None) if gameid is None: return error("Invalid game ID.") try: gameid = int(gameid) except ValueError: return error("Invalid game ID (not a number).") api = API() response = api.get_public_game(gameid) if isinstance(response, APIError): if response is api.ERR_NO_SUCH_RUNNING_GAME: msg = "Invalid game ID." else: msg = "An unknown error occurred retrieving game %d, sorry." % \ (gameid,) return error(msg) game = response order = request.args.get('order', None) if order is None: return error("Invalid order.") try: order = int(order) except ValueError: return error("Invalid order (not a number).") try: item = game.history[order] except IndexError: return error("Invalid order (not in game).") if not isinstance(item, dtos.GameItemActionResult): return error("Invalid order (not a bet or raise).") if not item.action_result.is_aggressive: return error("Analysis only for bets right now, sorry.") try: aife = game.analysis[order] except KeyError: return error("Analysis for this action is not ready yet.") street = game.game_details.situation.current_round street_text = aife.STREET_DESCRIPTIONS[street] if item.action_result.is_raise: action_text = "raises to %d" % (item.action_result.raise_total,) else: action_text = "bets %d" % (item.action_result.raise_total,) navbar_items = [('', url_for('home_page'), 'Home'), ('', url_for('about_page'), 'About'), ('', url_for('faq_page'), 'FAQ')] items_aggressive = [i for i in reversed(aife.items) if i.is_aggressive] items_passive = [i for i in aife.items if i.is_passive] items_fold = [i for i in aife.items if i.is_fold] # Could also have a status column or popover or similar: # "good bluff" = +EV # "bad bluff" = -EV on river # "possible semibluff" = -EV on river return render_template('web/analysis.html', gameid=gameid, street_text=street_text, screenname=item.user.screenname, action_text=action_text, items_aggressive=items_aggressive, items_passive=items_passive, items_fold=items_fold, is_raise=aife.is_raise, is_check=aife.is_check, navbar_items=navbar_items, is_logged_in=is_logged_in())