import asyncio, time, datetime, games, json, threading, jinja2, leagues, os, leagues, gametext, logging from leagues import league_structure from league_storage import league_exists from flask import Flask, url_for, Response, render_template, request, jsonify, send_from_directory, abort from flask_socketio import SocketIO, emit import database as db app = Flask("the-prestige", static_folder='simmadome/build/', subdomain_matching=True) app.config['SECRET KEY'] = 'dev' #url = "sakimori.space:5000" #app.config['SERVER_NAME'] = url socketio = SocketIO(app) socket_thread = None log = logging.getLogger('werkzeug') log.disabled = True app.logger.disabled = True # Serve React App @app.route('/', defaults={'path': ''}) @app.route('/') def serve(path): if path != "" and os.path.exists(app.static_folder + '/' + path): return send_from_directory(app.static_folder, path) else: return send_from_directory(app.static_folder, 'index.html') ### API @app.route('/api/teams/search') def search_teams(): query = request.args.get('query') page_len = int(request.args.get('page_len')) page_num = int(request.args.get('page_num')) if query is None: abort(400, "A query term is required") result = db.search_teams(query) if page_len is not None: #pagination should probably be done in the sqlite query but this will do for now if page_num is None: abort(400, "A page_len argument must be accompanied by a page_num argument") result = result[page_num*page_len : (page_num + 1)*page_len] return jsonify([json.loads(x[0])['name'] for x in result]) #currently all we need is the name but that can change MAX_SUBLEAGUE_DIVISION_TOTAL = 22; MAX_TEAMS_PER_DIVISION = 12; @app.route('/api/leagues', methods=['POST']) def create_league(): config = json.loads(request.data) if league_exists(config['name']): return jsonify({'status':'err_league_exists'}), 400 num_subleagues = len(config['structure']['subleagues']) if num_subleagues < 1: return jsonify({'status':'err_invalid_subleague_count'}), 400 num_divisions = len(config['structure']['subleagues'][0]['divisions']) if num_subleagues * (num_divisions + 1) > MAX_SUBLEAGUE_DIVISION_TOTAL: return jsonify({'status':'err_invalid_subleague_division_total'}), 400 league_dic = {} all_teams = set() err_teams = [] for subleague in config['structure']['subleagues']: if subleague['name'] in league_dic: return jsonify({'status':'err_duplicate_name', 'cause':subleague['name']}) subleague_dic = {} for division in subleague['divisions']: if division['name'] in subleague_dic: return jsonify({'status':'err_duplicate_name', 'cause':f"{subleague['name']}/{division['name']}"}), 400 elif len(division['teams']) > MAX_TEAMS_PER_DIVISION: return jsonify({'status':'err_too_many_teams', 'cause':f"{subleague['name']}/{division['name']}"}), 400 teams = [] for team_name in division['teams']: if team_name in all_teams: return jsonify({'status':'err_duplicate_team', 'cause':team_name}), 400 all_teams.add(team_name) team = games.get_team(team_name) if team is None: err_teams.append(team_name) else: teams.append(team) subleague_dic[division['name']] = teams league_dic[subleague['name']] = subleague_dic if len(err_teams) > 0: return jsonify({'status':'err_no_such_team', 'cause': err_teams}), 400 for (key, min_val) in [ ('division_series', 1), ('inter_division_series', 0), ('inter_league_series', 0) ]: if config[key] < min_val: return jsonify({'status':'err_invalid_option_value', 'cause':key}), 400 new_league = league_structure(config['name']) new_league.setup( league_dic, division_games=config['division_series'], inter_division_games=config['inter_division_series'], inter_league_games=config['inter_league_series'], ) new_league.constraints["division_leaders"] = config["top_postseason"] new_league.constraints["wild_cards"] = config["wildcards"] new_league.generate_schedule() leagues.save_league(new_league) return jsonify({'status':'success_league_created'}) ### SOCKETS thread2 = threading.Thread(target=socketio.run,args=(app,"127.0.0.1", "8080")) thread2.start() master_games_dic = {} #key timestamp : (game game, {} state) game_states = [] @socketio.on("recieved") def handle_new_conn(data): socketio.emit("states_update", game_states, room=request.sid) def update_loop(): global game_states global socket_thread while True: if socket_thread is not None: socket_thread.join() game_states = [] game_ids = iter(master_games_dic.copy().keys()) for game_id in game_ids: this_game, state, discrim_string = master_games_dic[game_id] test_string = this_game.gamestate_display_full() state["leagueoruser"] = discrim_string state["display_inning"] = this_game.inning #games need to be initialized with the following keys in state: #is_league, bool state["outs"] = this_game.outs #away_name state["pitcher"] = this_game.get_pitcher().name #home_name state["batter"] = this_game.get_batter().name #max_innings state["away_score"] = this_game.teams["away"].score #top_of_inning = True state["home_score"] = this_game.teams["home"].score #update_pause = 0 #victory_lap = False if not this_game.play_has_begun: #weather_emoji if state["start_delay"] - this_game.voice.intro_counter > -1: state["update_emoji"] = "🪑" #weather_text state["update_text"] = "The broadcast booth is being prepared..." #they also need a timestamp else: state["update_emoji"] = this_game.voice.intro[-this_game.voice.intro_counter][0] state["update_text"] = this_game.voice.intro[-this_game.voice.intro_counter][1] this_game.voice.intro_counter -= 1 state["start_delay"] -= 1 state["display_top_of_inning"] = state["top_of_inning"] this_game.weather.modify_gamestate(this_game, state) if state["start_delay"] <= 0: if this_game.top_of_inning != state["top_of_inning"]: state["update_pause"] = 2 state["pitcher"] = "-" state["batter"] = "-" if not state["top_of_inning"]: state["display_inning"] -= 1 state["display_top_of_inning"] = False if state["update_pause"] == 1: #generate the top of the inning message before displaying the at bat result state["update_emoji"] = "🍿" if this_game.over: # game over message state["display_inning"] -= 1 state["display_top_of_inning"] = False winning_team = this_game.teams['home'].name if this_game.teams['home'].score > this_game.teams['away'].score else this_game.teams['away'].name if this_game.victory_lap and winning_team == this_game.teams['home'].name: state["update_text"] = f"{winning_team} wins with a victory lap!" elif winning_team == this_game.teams['home'].name: state["update_text"] = f"{winning_team} wins with a partial victory lap!" else: state["update_text"] = f"{winning_team} wins on the road!" state["pitcher"] = "-" state["batter"] = "-" this_game.weather.modify_game_end_message(this_game, state) else: if this_game.top_of_inning: state["update_text"] = f"Top of {this_game.inning}. {this_game.teams['away'].name} batting!" this_game.weather.modify_top_of_inning_message(this_game, state) if this_game.random_weather_flag: this_game.weather.weather_report(this_game, state) else: if this_game.inning >= this_game.max_innings: if this_game.teams["home"].score > this_game.teams["away"].score: this_game.victory_lap = True state["update_text"] = f"Bottom of {this_game.inning}. {this_game.teams['home'].name} batting!" this_game.weather.modify_top_of_inning_message(this_game, state) elif state["update_pause"] != 1 and this_game.play_has_begun: if "twopart" in this_game.last_update[0].keys(): state["update_emoji"] = "💬" elif "weather_message" in this_game.last_update[0].keys(): state["update_emoji"] = this_game.weather.emoji elif "ishit" in this_game.last_update[0].keys() and this_game.last_update[0]["ishit"]: state["update_emoji"] = "🏏" elif "outcome" in this_game.last_update[0].keys() and this_game.last_update[0]["outcome"] == gametext.appearance_outcomes.walk: state["update_emoji"] = "👟" else: state["update_emoji"] = "🗞" if "steals" in this_game.last_update[0].keys(): updatestring = "" for attempt in this_game.last_update[0]["steals"]: updatestring += attempt + "\n" state["update_emoji"] = "💎" state["update_text"] = updatestring elif "text_only" in this_game.last_update[0].keys(): #this handles many weather messages state["update_text"] = this_game.last_update[0]["text"] else: updatestring = this_game.last_update[0]["displaytext"] if this_game.last_update[1] > 0: updatestring += f" {this_game.last_update[1]} runs scored!" state["update_text"] = f"{updatestring}" if "twopart" not in this_game.last_update[0].keys(): this_game.weather.modify_atbat_message(this_game, state) state["bases"] = this_game.named_bases() state["top_of_inning"] = this_game.top_of_inning game_states.append([game_id, state]) if state["update_pause"] <= 1 and state["start_delay"] < 0: if this_game.over: state["update_pause"] = 2 if state["end_delay"] < 0: master_games_dic.pop(game_id) else: state["end_delay"] -= 1 master_games_dic[game_id][1]["end_delay"] -= 1 else: this_game.gamestate_update_full() state["update_pause"] -= 1 socket_thread = threading.Thread(target=socketio.emit, args=("states_update", game_states)) socket_thread.start() #socketio.emit("states_update", game_states) time.sleep(8)