lots of league database foundation

This commit is contained in:
Sakimori 2021-01-13 04:23:48 -05:00
parent c407722166
commit b6e4c1069c
7 changed files with 233 additions and 64 deletions

1
.gitignore vendored
View File

@ -349,6 +349,7 @@ ids
matteo.db matteo.db
matteo.db-wal matteo.db-wal
matteo.db-shm matteo.db-shm
/data/leagues/*
/matteo_env/Lib/site-packages/flask_socketio/__init__.py /matteo_env/Lib/site-packages/flask_socketio/__init__.py
env env

View File

@ -674,6 +674,10 @@ class game(object):
return "Game not started." return "Game not started."
def add_stats(self): def add_stats(self):
players = self.get_stats()
db.add_stats(players)
def get_stats(self):
players = [] players = []
for this_player in self.teams["away"].lineup: for this_player in self.teams["away"].lineup:
players.append((this_player.name, this_player.game_stats)) players.append((this_player.name, this_player.game_stats))
@ -681,7 +685,7 @@ class game(object):
players.append((this_player.name, this_player.game_stats)) players.append((this_player.name, this_player.game_stats))
players.append((self.teams["home"].pitcher.name, self.teams["home"].pitcher.game_stats)) players.append((self.teams["home"].pitcher.name, self.teams["home"].pitcher.game_stats))
players.append((self.teams["away"].pitcher.name, self.teams["away"].pitcher.game_stats)) players.append((self.teams["away"].pitcher.name, self.teams["away"].pitcher.game_stats))
db.add_stats(players) return players

124
league_storage.py Normal file
View File

@ -0,0 +1,124 @@
import os, json, re, jsonpickle
import sqlite3 as sql
data_dir = "data"
league_dir = "leagues"
def create_connection(league_name):
#create connection, create db if doesn't exist
conn = None
try:
conn = sql.connect(os.path.join(data_dir, league_dir, f"{league_name}.db"))
# enable write-ahead log for performance and resilience
conn.execute('pragma journal_mode=wal')
return conn
except:
print("oops, db connection no work")
return conn
def state(league_name):
with open(os.path.join(data_dir, league_dir, f"{league_name}.state")) as state_file:
return json.load(state_file)
def init_league_db(league):
conn = create_connection(league.name)
player_stats_table_check_string = """ CREATE TABLE IF NOT EXISTS stats (
counter integer PRIMARY KEY,
id text,
name text,
team_name text,
outs_pitched integer DEFAULT 0,
walks_allowed integer DEFAULT 0,
hits_allowed integer DEFAULT 0,
strikeouts_given integer DEFAULT 0,
runs_allowed integer DEFAULT 0,
plate_appearances integer DEFAULT 0,
walks_taken integer DEFAULT 0,
sacrifices integer DEFAULT 0,
hits integer DEFAULT 0,
home_runs integer DEFAULT 0,
total_bases integer DEFAULT 0,
rbis integer DEFAULT 0,
strikeouts_taken integer DEFAULT 0
);"""
teams_table_check_string = """ CREATE TABLE IF NOT EXISTS teams (
counter integer PRIMARY KEY,
name text NOT NULL,
wins integer DEFAULT 0,
losses integer DEFAULT 0,
run_diff integer DEFAULT 0
); """
if conn is not None:
c = conn.cursor()
c.execute(player_stats_table_check_string)
c.execute(teams_table_check_string)
for team in league.teams_in_league():
c.execute("INSERT INTO teams (name) VALUES (?)", (team.name,))
player_string = "INSERT INTO stats (name, team_name) VALUES (?,?)"
for batter in team.lineup:
c.execute(player_string, (batter.name, team.name))
for pitcher in team.rotation:
c.execute(player_string, (pitcher.name, team.name))
state_dic = {
"day" : league.day,
"schedule" : league.schedule,
"game_length" : league.game_length,
"series_length" : league.series_length,
"games_per_hour" : league.games_per_hour,
"historic" : False
}
with open(os.path.join(data_dir, league_dir, f"{league.name}.state"), "w") as state_file:
json.dump(state_dic, state_file, indent=4)
conn.commit()
conn.close()
def add_stats(league_name, player_game_stats_list):
conn = create_connection(league_name)
if conn is not None:
c=conn.cursor()
for (name, player_stats_dic) in player_game_stats_list:
c.execute("SELECT * FROM stats WHERE name=?",(name,))
this_player = c.fetchone()
if this_player is not None:
for stat in player_stats_dic.keys():
c.execute(f"SELECT {stat} FROM stats WHERE name=?",(name,))
old_value = int(c.fetchone()[0])
c.execute(f"UPDATE stats SET {stat} = ? WHERE name=?",(player_stats_dic[stat]+old_value,name))
else:
c.execute("INSERT INTO stats(name) VALUES (?)",(name,))
for stat in player_stats_dic.keys():
c.execute(f"UPDATE stats SET {stat} = ? WHERE name=?",(player_stats_dic[stat],name))
conn.commit()
conn.close()
def update_standings(league_name, update_dic):
if league_exists(league_name):
conn = create_connection(league_name)
if conn is not None:
c = conn.cursor()
for team_name in update_dic.keys():
for stat_type in update_dic[team_name].keys(): #wins, losses, run_diff
c.execute(f"SELECT {stat_type} FROM teams WHERE name = ?", (team_name,))
old_value = int(c.fetchone()[0])
c.execute(f"UPDATE teams SET {stat_type} = ? WHERE name = ?", (update_dic[team_name][stat_type]+old_value, team_name))
conn.commit()
conn.close()
def league_exists(league_name):
with os.scandir(os.path.join(data_dir, league_dir)) as folder:
for file in folder:
if file.name == f"{league_name}.state":
return not state(league_name)["historic"]
return False

View File

@ -1,39 +1,39 @@
import time, asyncio, jsonpickle, random, math import time, asyncio, json, jsonpickle, random, math, os
import league_storage as league_db
from itertools import chain from itertools import chain
from games import team, game from games import team, game
from discord import Embed, Color from discord import Embed, Color
import database as db
data_dir = "data"
league_dir = "leagues"
class league_structure(object): class league_structure(object):
def __init__(self, name, league_dic, division_games = 1, inter_division_games = 1, inter_league_games = 1, games_per_hour = 2): def __init__(self, name):
self.league = league_dic #key: subleague, value: {division : teams} self.name = name
def setup(self, league_dic, division_games = 1, inter_division_games = 1, inter_league_games = 1, games_per_hour = 2):
self.league = league_dic #key: subleague, value: {division : team_name}
self.constraints = { self.constraints = {
"division_games" : division_games, "division_games" : division_games,
"inter_div_games" : inter_division_games, "inter_div_games" : inter_division_games,
"inter_league_games" : inter_league_games "inter_league_games" : inter_league_games
} }
self.day = 1 self.day = 1
self.name = name
self.schedule = {} self.schedule = {}
self.series_length = 3 #can be changed self.series_length = 3 #can be changed
self.game_length = None self.game_length = None
self.active = False self.active = False
self.games_per_hour = games_per_hour self.games_per_hour = games_per_hour
self.standings = {}
for this_team in self.teams_in_league(): def add_stats_from_game(self, players_list):
self.standings[this_team.name] = { league_db.add_stats(players_list)
"wins" : 0,
"losses" : 0, def update_standings(self, results_dic):
"run differential" : 0, league_db.update_standings(self.name, results_dic)
}
def last_series_check(self): def last_series_check(self):
return day + 1 in self.schedule.keys() return self.day + 1 in self.schedule.keys()
def find_team(self, team_name): def find_team(self, team_name):
for subleague in iter(self.league.keys()): for subleague in iter(self.league.keys()):
@ -93,9 +93,9 @@ class league_structure(object):
a_home = True a_home = True
for team_a, team_b in zip(league_a, league_b): for team_a, team_b in zip(league_a, league_b):
if a_home: if a_home:
matchups.append([team_b, team_a]) matchups.append([team_b.name, team_a.name])
else: else:
matchups.append([team_a, team_b]) matchups.append([team_a.name, team_b.name])
a_home != a_home a_home != a_home
for i in range(0, self.constraints["inter_div_games"]): #inter-division matchups for i in range(0, self.constraints["inter_div_games"]): #inter-division matchups
@ -127,9 +127,9 @@ class league_structure(object):
a_home = True a_home = True
for team_a, team_b in zip(divs_a, divs_b): for team_a, team_b in zip(divs_a, divs_b):
if a_home: if a_home:
matchups.append([team_b, team_a]) matchups.append([team_b.name, team_a.name])
else: else:
matchups.append([team_a, team_b]) matchups.append([team_a.name, team_b.name])
a_home != a_home a_home != a_home
@ -146,9 +146,9 @@ class league_structure(object):
for team_a, team_b in zip(teams_a, teams_b): for team_a, team_b in zip(teams_a, teams_b):
for j in range(0, self.constraints["division_games"]): for j in range(0, self.constraints["division_games"]):
if i % 2 == 0: if i % 2 == 0:
matchups.append([team_b, team_a]) matchups.append([team_b.name, team_a.name])
else: else:
matchups.append([team_a, team_b]) matchups.append([team_a.name, team_b.name])
division.insert(1, division.pop()) division.insert(1, division.pop())
return matchups return matchups
@ -278,3 +278,21 @@ class bracket(object):
if parent is None: if parent is None:
self.this_bracket = branch self.this_bracket = branch
return branch return branch
def save_league(this_league):
if not league_db.league_exists(this_league.name):
league_db.init_league_db(this_league)
with open(os.path.join(data_dir, league_dir, f"{this_league.name}.league"), "w") as league_file:
league_json_string = jsonpickle.encode(this_league.league, keys=True)
json.dump(league_json_string, league_file, indent=4)
return True
def load_league_file(league_name):
if league_db.league_exists(league_name):
state = league_db.state(league_name)
this_league = league_structure(league_name)
with open(os.path.join(data_dir, league_dir, f"{this_league.name}.league")) as league_file:
this_league.league = jsonpickle.decode(json.load(league_file), keys=True, classes=team)
with open(os.path.join(data_dir, league_dir, f"{this_league.name}.state")) as state_file:
state_dic = json.load(state_file)
return this_league

View File

@ -141,4 +141,4 @@ def update_loop():
state["update_pause"] -= 1 state["update_pause"] -= 1
socketio.emit("states_update", game_states) socketio.emit("states_update", game_states)
time.sleep(8) time.sleep(1)

View File

@ -32,6 +32,9 @@
<Compile Include="leagues.py"> <Compile Include="leagues.py">
<SubType>Code</SubType> <SubType>Code</SubType>
</Compile> </Compile>
<Compile Include="league_storage.py">
<SubType>Code</SubType>
</Compile>
<Compile Include="main_controller.py"> <Compile Include="main_controller.py">
<SubType>Code</SubType> <SubType>Code</SubType>
</Compile> </Compile>

View File

@ -724,6 +724,27 @@ class StartDraftCommand(Command):
raise SlowDraftError('Too slow') raise SlowDraftError('Too slow')
return draft_message return draft_message
class DebugLeague(Command):
name = "league"
async def execute(self, msg, command):
league = leagues.league_structure("test2")
league.setup({
"nL" : {
"nL west" : [get_team_fuzzy_search("lockpicks"), get_team_fuzzy_search("liches")],
"nL east" : [get_team_fuzzy_search("bethesda soft"), get_team_fuzzy_search("traverse city")]
},
"aL" : {
"aL west" : [get_team_fuzzy_search("deep space"), get_team_fuzzy_search("phoenix")],
"aL east" : [get_team_fuzzy_search("cheyenne mountain"), get_team_fuzzy_search("tarot dragons")]
}
}, division_games=6, inter_division_games=3, inter_league_games=3)
league.generate_schedule()
leagues.save_league(league)
await start_league_day(msg.channel, league, 2)
commands = [ commands = [
IntroduceCommand(), IntroduceCommand(),
@ -751,6 +772,7 @@ commands = [
HelpCommand(), HelpCommand(),
StartDraftCommand(), StartDraftCommand(),
DraftPlayerCommand(), DraftPlayerCommand(),
DebugLeague()
] ]
client = discord.Client() client = discord.Client()
@ -792,6 +814,7 @@ async def on_ready():
watch_task = asyncio.create_task(game_watcher()) watch_task = asyncio.create_task(game_watcher())
await watch_task await watch_task
@client.event @client.event
async def on_reaction_add(reaction, user): async def on_reaction_add(reaction, user):
if reaction.message in setupmessages.keys(): if reaction.message in setupmessages.keys():
@ -1382,7 +1405,7 @@ def get_team_fuzzy_search(team_name):
team = teams[0] team = teams[0]
return team return team
async def start_league_day(channel, league): async def start_league_day(channel, league, autoplay = 1):
current_games = [] current_games = []
if league.schedule is {}: if league.schedule is {}:
league.generate_schedule() league.generate_schedule()
@ -1395,7 +1418,9 @@ async def start_league_day(channel, league):
for pair in games_to_start: for pair in games_to_start:
if pair[0] is not None and pair[1] is not None: if pair[0] is not None and pair[1] is not None:
this_game = games.game(pair[0].prepare_for_save().finalize(), pair[1].prepare_for_save().finalize(), length = game_length) away = get_team_fuzzy_search(pair[0])
home = get_team_fuzzy_search(pair[1])
this_game = games.game(away.prepare_for_save().finalize(), home.prepare_for_save().finalize(), length = game_length)
this_game, state_init = prepare_game(this_game) this_game, state_init = prepare_game(this_game)
state_init["is_league"] = True state_init["is_league"] = True
@ -1417,14 +1442,14 @@ async def start_league_day(channel, league):
await channel.send(f"The next series of the {league.name} is starting now, at {config()['simmadome_url']+ext}") await channel.send(f"The next series of the {league.name} is starting now, at {config()['simmadome_url']+ext}")
last = False last = False
await league_day_watcher(channel, league, current_games, config()['simmadome_url']+ext, last) await league_day_watcher(channel, league, current_games, config()['simmadome_url']+ext, autoplay, last)
async def league_day_watcher(channel, league, games_list, filter_url, last = False): async def league_day_watcher(channel, league, games_list, filter_url, autoplay, last = False):
league.active = True league.active = True
autoplay -= 1
active_leagues.append(league) active_leagues.append(league)
wins_in_series = {} series_results = {}
losses_in_series = {}
while league.active: while league.active:
queued_games = [] queued_games = []
@ -1433,30 +1458,30 @@ async def league_day_watcher(channel, league, games_list, filter_url, last = Fal
for i in range(0, len(games_list)): for i in range(0, len(games_list)):
game, key = games_list[i] game, key = games_list[i]
if game.over and main_controller.master_games_dic[key][1]["end_delay"] <= 8: if game.over and main_controller.master_games_dic[key][1]["end_delay"] <= 8:
if game.teams['home'].name not in wins_in_series.keys(): if game.teams['home'].name not in series_results.keys():
wins_in_series[game.teams["home"].name] = 0 series_results[game.teams["home"].name]["wins"] = 0
if game.teams['home'].name not in losses_in_series.keys(): series_results[game.teams["home"].name]["losses"] = 0
losses_in_series[game.teams["home"].name] = 0 series_results[game.teams["home"].name]["run_diff"] = 0
if game.teams['away'].name not in wins_in_series.keys(): if game.teams['away'].name not in series_results.keys():
wins_in_series[game.teams["away"].name] = 0 series_results[game.teams["away"].name]["wins"] = 0
if game.teams['away'].name not in losses_in_series.keys(): series_results[game.teams["away"].name]["losses"] = 0
losses_in_series[game.teams["away"].name] = 0 series_results[game.teams["away"].name]["run_diff"] = 0
winner_name = game.teams['home'].name if game.teams['home'].score > game.teams['away'].score else game.teams['away'].name winner_name = game.teams['home'].name if game.teams['home'].score > game.teams['away'].score else game.teams['away'].name
loser_name = game.teams['away'].name if game.teams['home'].score > game.teams['away'].score else game.teams['home'].name loser_name = game.teams['away'].name if game.teams['home'].score > game.teams['away'].score else game.teams['home'].name
rd = int(math.fabs(game.teams['home'].score - game.teams['away'].score)) rd = int(math.fabs(game.teams['home'].score - game.teams['away'].score))
wins_in_series[winner_name] += 1 series_results[winner_name]["wins"] += 1
league.standings[winner_name]["wins"] += 1 series_results[winner_name]["run_diff"] += rd
league.standings[winner_name]["run differential"] += rd series_results[loser_name]["losses"] += 1
losses_in_series[loser_name] += 1 series_results[loser_name]["run_diff"] -= rd
league.standings[loser_name]["losses"] += 1
league.standings[loser_name]["run differential"] -= rd league.add_stats_from_game(game.get_stats())
final_embed = game_over_embed(game) final_embed = game_over_embed(game)
await channel.send(f"A {league.name} game just ended!") await channel.send(f"A {league.name} game just ended!")
await channel.send(embed=final_embed) await channel.send(embed=final_embed)
if wins_in_series[winner_name] + losses_in_series[winner_name] < league.series_length: if series_results[winner_name]["wins"] + series_results[winner_name]["losses"] < league.series_length:
queued_games.append(game) queued_games.append(game)
games_list.pop(i) games_list.pop(i)
break break
@ -1484,11 +1509,15 @@ async def league_day_watcher(channel, league, games_list, filter_url, last = Fal
await channel.send(f"The next batch of games for the {league.name} will start in {int(wait_seconds/60)} minutes.") await channel.send(f"The next batch of games for the {league.name} will start in {int(wait_seconds/60)} minutes.")
await asyncio.sleep(wait_seconds) await asyncio.sleep(wait_seconds)
await channel.send(f"A {league.name} series is continuing now at {filter_url}") await channel.send(f"A {league.name} series is continuing now at {filter_url}")
games_list = await continue_league_series(league, queued_games, games_list, wins_in_series) games_list = await continue_league_series(league, queued_games, games_list, series_results)
else: else:
league.active = False league.active = False
if last: #if this series was the last
league.update_standings(series_results)
league.day += 1
if last or autoplay <= 0: #if this series was the last of the season OR number of series to autoplay has been reached
#needs some kind of notification that it's over here #needs some kind of notification that it's over here
active_leagues.pop(active_leagues.index(league)) active_leagues.pop(active_leagues.index(league))
return return
@ -1508,10 +1537,10 @@ async def league_day_watcher(channel, league, games_list, filter_url, last = Fal
await channel.send(f"""This {league.name} series is now complete! The next series will be starting in {int(wait_seconds/60)} minutes.""") await channel.send(f"""This {league.name} series is now complete! The next series will be starting in {int(wait_seconds/60)} minutes.""")
await asyncio.sleep(wait_seconds) await asyncio.sleep(wait_seconds)
league.day += 1
await start_league_day(channel, league)
async def continue_league_series(tourney, queue, games_list, wins_in_series): await start_league_day(channel, league, autoplay)
async def continue_league_series(tourney, queue, games_list, series_results):
for oldgame in queue: for oldgame in queue:
away_team = games.get_team(oldgame.teams["away"].name) away_team = games.get_team(oldgame.teams["away"].name)
home_team = games.get_team(oldgame.teams["home"].name) home_team = games.get_team(oldgame.teams["home"].name)
@ -1520,7 +1549,7 @@ async def continue_league_series(tourney, queue, games_list, wins_in_series):
state_init["is_league"] = True state_init["is_league"] = True
series_string = f"Series score:" series_string = f"Series score:"
state_init["title"] = f"{series_string} {wins_in_series[away_team.name]} - {wins_in_series[home_team.name]}" state_init["title"] = f"{series_string} {series_results[away_team.name]['wins']} - {series_results[home_team.name]['wins']}"
discrim_string = league.name discrim_string = league.name
id = str(uuid4()) id = str(uuid4())
@ -1530,18 +1559,8 @@ async def continue_league_series(tourney, queue, games_list, wins_in_series):
return games_list return games_list
league = leagues.league_structure("test", {
"nL" : {
"nL west" : ["a1", "b1", "c1", "d1", "e1", "f1", "g1", "h1"],
"nL east" : ["a2", "b2", "c2", "d2", "e2", "f2", "g2", "h2"]
},
"aL" : {
"aL west" : ["A1", "B1", "C1", "D1", "E1", "F1", "G1", "H1"],
"aL east" : ["A2", "B2", "C2", "D2", "E2", "F2", "G2", "H2"]
}
}, division_games=6, inter_division_games=3, inter_league_games=3)
league.generate_schedule()
#print(league.schedule[2])
client.run(config()["token"]) client.run(config()["token"])