Implement incremental updating
When `seed/all_in_one` is now called, it will update with only that data that exists later than the most-recently-played* game, allowing the upload to be used repeatedly without having to clear the database. \* Actually, "highest-ID game", as we haven't implemented `list_games_by_date`, yet
This commit is contained in:
parent
1c9aa30721
commit
2fb5a291e5
@ -16,3 +16,9 @@ alter sequence games_id_seq restart with 1;
|
|||||||
alter sequence players_id_seq restart with 1;
|
alter sequence players_id_seq restart with 1;
|
||||||
alter sequence elo_scores_id_seq restart with 1;
|
alter sequence elo_scores_id_seq restart with 1;
|
||||||
```
|
```
|
||||||
|
|
||||||
|
# To copy the database out to local
|
||||||
|
|
||||||
|
```
|
||||||
|
$ docker cp edh-elo-server-1:/app/database/<name>
|
||||||
|
```
|
||||||
|
@ -61,11 +61,30 @@ def create_game(game: schemas.GameCreate, db: Session = Depends(get_db)):
|
|||||||
return created_game
|
return created_game
|
||||||
|
|
||||||
|
|
||||||
|
# TODO - when this is updated to support sorting, also update `app/routers/seed.py:all_in_one` to take advantage of it
|
||||||
|
# TODO - and `latest_game`
|
||||||
@api_router.get("/list", response_model=list[schemas.Game])
|
@api_router.get("/list", response_model=list[schemas.Game])
|
||||||
def list_games(skip: int = 0, limit: int = 100, db=Depends(get_db)):
|
def list_games(skip: int = 0, limit: int = 100, db=Depends(get_db)):
|
||||||
return crud.get_games(db, skip=skip, limit=limit)
|
return crud.get_games(db, skip=skip, limit=limit)
|
||||||
|
|
||||||
|
|
||||||
|
# This is helpful for at least the `all_in_one` data-seed path, but it could conceivably also be useful for a
|
||||||
|
# "Frontpage/dashboard" widget
|
||||||
|
@api_router.get("/latest_game", response_model=schemas.Game)
|
||||||
|
def latest_game(db=Depends(get_db)):
|
||||||
|
# Limit gives me a bit of time to allow natural growth before being forced into updating to basing on sorted-response
|
||||||
|
# Will error out if called on a database with no games
|
||||||
|
# TODO - logging does not seem to be coming up during testing
|
||||||
|
latest_game = db.query(models.Game).order_by(models.Game.date.desc()).first()
|
||||||
|
if latest_game is None:
|
||||||
|
# I.e. database is empty
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=404, detail="Cannot read latest_game of an empty database"
|
||||||
|
)
|
||||||
|
return latest_game
|
||||||
|
|
||||||
|
|
||||||
|
# Note that this must be after all the "static" routes, lest it take precedence
|
||||||
@api_router.get("/{game_id}", response_model=schemas.Game)
|
@api_router.get("/{game_id}", response_model=schemas.Game)
|
||||||
def read_game(game_id: int, db=Depends(get_db)):
|
def read_game(game_id: int, db=Depends(get_db)):
|
||||||
db_game = crud.get_game_by_id(db, game_id)
|
db_game = crud.get_game_by_id(db, game_id)
|
||||||
@ -79,6 +98,9 @@ def delete_game(game_id: str, db=Depends(get_db)):
|
|||||||
crud.delete_game_by_id(db, int(game_id))
|
crud.delete_game_by_id(db, int(game_id))
|
||||||
|
|
||||||
|
|
||||||
|
# Do not add more api routes under here! See the comment above `read_game`
|
||||||
|
|
||||||
|
|
||||||
########
|
########
|
||||||
# HTML Routes
|
# HTML Routes
|
||||||
########
|
########
|
||||||
|
@ -5,11 +5,13 @@ import logging
|
|||||||
from collections import defaultdict
|
from collections import defaultdict
|
||||||
|
|
||||||
|
|
||||||
from fastapi import APIRouter, Depends, Request, UploadFile
|
from fastapi import APIRouter, Depends, HTTPException, Request, UploadFile
|
||||||
from fastapi.responses import HTMLResponse
|
from fastapi.responses import HTMLResponse
|
||||||
from sqlalchemy.orm import Session
|
from sqlalchemy.orm import Session
|
||||||
|
|
||||||
from .games import create_game
|
from .decks import list_decks
|
||||||
|
from .games import create_game, latest_game, list_games
|
||||||
|
from .players import list_players
|
||||||
from ..templates import jinja_templates
|
from ..templates import jinja_templates
|
||||||
from ..sql import crud, schemas
|
from ..sql import crud, schemas
|
||||||
from ..sql.database import get_db
|
from ..sql.database import get_db
|
||||||
@ -89,6 +91,29 @@ def all_in_one(file: UploadFile, db: Session = Depends(get_db)):
|
|||||||
file_contents = file.file.read().decode("utf-8").split("\n")
|
file_contents = file.file.read().decode("utf-8").split("\n")
|
||||||
reader = csv.DictReader(file_contents, delimiter=",")
|
reader = csv.DictReader(file_contents, delimiter=",")
|
||||||
|
|
||||||
|
# Fetch the currently-known-information so that we can avoid recreating existing data
|
||||||
|
current_player_ids_by_name = {
|
||||||
|
player.name: player.id for player in list_players(db=db)
|
||||||
|
}
|
||||||
|
current_deck_ids_by_name = {deck.name: deck.id for deck in list_decks(db=db)}
|
||||||
|
try:
|
||||||
|
latest_recorded_game = latest_game(db=db)
|
||||||
|
date_of_latest_game = latest_recorded_game.date
|
||||||
|
except HTTPException:
|
||||||
|
# No games have been returned from the db, thus no games should be skipped for downloading
|
||||||
|
date_of_latest_game = datetime.datetime(datetime.MINYEAR, 1, 1, 0, 0, 0, 0)
|
||||||
|
current_games = list_games(db=db)
|
||||||
|
print(f"{current_games}")
|
||||||
|
# Depends on being sorted by date - which is currently _coincidentally_ true of our source data as games have thus
|
||||||
|
# far only been added in date order, but is not necessarily the case.
|
||||||
|
# TODO - implement sorting (and pagination) of returned data, then update this to take advantage of it.
|
||||||
|
if current_games:
|
||||||
|
# I.e. if any games have been returned from the db
|
||||||
|
date_of_latest_game = current_games[-1].date
|
||||||
|
else:
|
||||||
|
# No games have been returned from the db, thus no games should be skipped for downloading
|
||||||
|
date_of_latest_game = datetime.datetime(datetime.MINYEAR, 1, 1, 0, 0, 0, 0)
|
||||||
|
|
||||||
# Mapping from name to set-of-owned-decks
|
# Mapping from name to set-of-owned-decks
|
||||||
# (Set rather than list so that we can blindly `.add`)
|
# (Set rather than list so that we can blindly `.add`)
|
||||||
player_decks = defaultdict(set)
|
player_decks = defaultdict(set)
|
||||||
@ -116,13 +141,25 @@ def all_in_one(file: UploadFile, db: Session = Depends(get_db)):
|
|||||||
deck_id_lookup = {}
|
deck_id_lookup = {}
|
||||||
|
|
||||||
for player_name, decks in player_decks.items():
|
for player_name, decks in player_decks.items():
|
||||||
|
if player_name in current_player_ids_by_name:
|
||||||
|
LOGGER.info(f"Looked up {player_name=} from existing database")
|
||||||
|
player_id = current_player_ids_by_name[player_name]
|
||||||
|
else:
|
||||||
player = crud.create_player(
|
player = crud.create_player(
|
||||||
db=db, player=schemas.PlayerCreate(name=player_name)
|
db=db, player=schemas.PlayerCreate(name=player_name)
|
||||||
)
|
)
|
||||||
LOGGER.info(f"Seeded {player=}")
|
LOGGER.info(f"Seeded {player=}")
|
||||||
player_id = player.id
|
player_id = player.id
|
||||||
|
|
||||||
player_id_lookup[player_name] = player_id
|
player_id_lookup[player_name] = player_id
|
||||||
|
|
||||||
for deck_name in decks:
|
for deck_name in decks:
|
||||||
|
if deck_name in current_deck_ids_by_name:
|
||||||
|
LOGGER.info(f"Looked up {deck_name=} from existing database")
|
||||||
|
deck_id_lookup[f"{player_name}:{deck_name}"] = current_deck_ids_by_name[
|
||||||
|
deck_name
|
||||||
|
]
|
||||||
|
else:
|
||||||
deck = crud.create_deck(
|
deck = crud.create_deck(
|
||||||
db=db,
|
db=db,
|
||||||
deck=schemas.DeckCreate(
|
deck=schemas.DeckCreate(
|
||||||
@ -149,6 +186,21 @@ def all_in_one(file: UploadFile, db: Session = Depends(get_db)):
|
|||||||
# ((Yes, I know that's an abuse of Big-O notation, shut up - you knew what I meant :P ))
|
# ((Yes, I know that's an abuse of Big-O notation, shut up - you knew what I meant :P ))
|
||||||
reader = csv.DictReader(file_contents, delimiter=",")
|
reader = csv.DictReader(file_contents, delimiter=",")
|
||||||
for row in reader:
|
for row in reader:
|
||||||
|
# Skip any games created before the date of the latest current game
|
||||||
|
# (Note that this means that the `all_in_one` method cannot be used to backfill any previously-played games. If
|
||||||
|
# there arises a desire for that, instead will have to check each potentially-uploaded game against _every_
|
||||||
|
# currently-uploaded one to check for pre-existence (or, make the "create" option idempotent...though that
|
||||||
|
# probably shouldn't be the case, as attempting to upload the same game twice is _probably_ an indication of an
|
||||||
|
# automated script or summarization going rogue, which should be flagged up _as_ an error rather than blindly
|
||||||
|
# continued. For the User-facing UI, just present a "whoops! You submitted a duplicate" screen))
|
||||||
|
date_of_current_row = parse_date(row["Date"])
|
||||||
|
if date_of_current_row <= date_of_latest_game:
|
||||||
|
message = f"Skipped a game on {date_of_current_row} because it is not later than {date_of_latest_game}"
|
||||||
|
LOGGER.info(message)
|
||||||
|
# TBD - logging does not seem to be showing up as-expected
|
||||||
|
print(message)
|
||||||
|
continue
|
||||||
|
|
||||||
# Note that we intentionally create via the API, not via direct `crud.create_game`, to trigger ELO calculation.
|
# Note that we intentionally create via the API, not via direct `crud.create_game`, to trigger ELO calculation.
|
||||||
|
|
||||||
index_of_winning_deck = [
|
index_of_winning_deck = [
|
||||||
@ -157,7 +209,7 @@ def all_in_one(file: UploadFile, db: Session = Depends(get_db)):
|
|||||||
print(f"DEBUG - checking row {row}")
|
print(f"DEBUG - checking row {row}")
|
||||||
created_game = create_game(
|
created_game = create_game(
|
||||||
schemas.GameCreate(
|
schemas.GameCreate(
|
||||||
date=parse_date(row["Date"]),
|
date=date_of_current_row,
|
||||||
**{
|
**{
|
||||||
f"deck_id_{i+1}": deck_id_lookup[
|
f"deck_id_{i+1}": deck_id_lookup[
|
||||||
row[f"Player {i+1}"] + ":" + row[f"Deck {i+1}"]
|
row[f"Player {i+1}"] + ":" + row[f"Deck {i+1}"]
|
||||||
|
96
seed-data/all-in-one-updated-for-incremental-add-testing.csv
Normal file
96
seed-data/all-in-one-updated-for-incremental-add-testing.csv
Normal file
@ -0,0 +1,96 @@
|
|||||||
|
Date,Player 1,Deck 1,Player 2,Deck 2,Player 3,Deck 3,Player 4,Deck 4,Player 5,Deck 5,Player 6,Deck 6,Winning Player,Winning Deck,# turns,turn 1st player out,Type of win,Format,Notes
|
||||||
|
1/13/24,Evan,Kelsien the Plague,Terence,Ravos/Rebbec,Patrick,Duke Ulder Ravengard,Jeff,Mondrak,,,,,Terence,Ravos/Rebbec,15,12,combat damage,FFA,3-4 board wipes; Biotransference
|
||||||
|
1/13/24,Terence,Wernog/Cecily,Patrick,Duke Ulder Ravengard,Ryan,Don Andres,Jeff,Mondrak,Evan,Jasmine Boreal of the Seven,,,Patrick,Duke Ulder Ravengard,12,10,combat damage,FFA,Possessed Portal scary!
|
||||||
|
1/13/24,Jeff,Go-Shintai of Life's Origin,Evan,Kethis the Hidden Hand,Terence,Rograkh/Silas ninjas,Patrick,Tekuthal,Ryan,Gitrog,,,Patrick,Tekuthal,8,8,poison,FFA,Flux Channeler + Tekuthal
|
||||||
|
1/13/24,Patrick,Tekuthal,Jeff,Illuna,Evan,"Atraxa, Praetor's Voice",Terence,Muldrotha,,,,,Patrick,Tekuthal,20,5,combat damage,FFA,3 Hullbreaker Horrors simultaneously (Evan forfeited ~t5; game took ~2hrs)
|
||||||
|
1/17/24,Patrick,Duke Ulder Ravengard,Ryan,Grist,Terence,Ravos/Rebbec,,,,,,,Patrick,Duke Ulder Ravengard,12,12,combat damage,FFA,"Eternal Wanderer wipe, Angel Serenity + Sun Titan followup"
|
||||||
|
1/17/24,Terence,Ravos/Rebbec,Patrick,Duke Ulder Ravengard,Ryan,Grist,,,,,,,Terence,Ravos/Rebbec,13,11,combat damage,FFA,Verge Rangers + Conjurer's Mantle
|
||||||
|
1/23/24,Ryan,Goose Mother,Patrick,Maarika,Terence,Wilson/Cultist,,,,,,,Patrick,Maarika,8,7,21+ commander,FFA,Maarika + Runes of the Deus
|
||||||
|
1/23/24,Ryan,Goose Mother,Patrick,Maarika,Terence,Wilson/Cultist,,,,,,,Terence,Wilson/Cultist,10,8,combat damage,FFA,Scepter of Celebration brings an army
|
||||||
|
1/23/24,Terence,Abdel Adrian/Far Traveler,Ryan,Obeka,Patrick,Laelia,,,,,,,Patrick,Laelia,9,8,21+ commander,FFA,Nalfeshnee copying Storm's Wrath cleared the way; Ryan mana screwed
|
||||||
|
1/23/24,Terence,Jan Jansen,Ryan,Don Andres,Patrick,Laelia,,,,,,,Terence,Jan Jansen,10,10,aristocrats/burn,FFA,Mirkwood Bats + Thornbite Staff ftw; Ryan mana screwed
|
||||||
|
1/30/24,Patrick,Urza,Terence,"Me, the Immortal",Ryan,Gitrog,,,,,,,Ryan,Gitrog,8,7,combat damage,FFA,Mending of Dominaria + Lotus Cobra (10 lands)
|
||||||
|
1/30/24,Patrick,Kiora,Terence,Raffine,Ryan,Kozilek,,,,,,,Patrick,Kiora,11,9,combat damage,FFA,"Raffine does all the work, then Kederekt Leviathan cleans up"
|
||||||
|
2/4/24,Ajit,Jhoira of the Ghitu,Brandon,Anikthea,Patrick,Oops all Kayas,,,,,,,Brandon,Anikthea,12,11,combat damage,FFA,Unopposed enchantress with card draw and Nature's Will
|
||||||
|
2/4/24,Brandon,Silvar/Trynn,Patrick,Oops all Kayas,Ryan,"Purphoros, God of the Forge",Ajit,Jhoira of the Ghitu,,,,,Brandon,Silvar/Trynn,12,10,aristocrats/burn,FFA,Bastion of Remembrance overcame Emrakul + It That Betrays
|
||||||
|
2/4/24,Ajit,Brago,Brandon,Marneus Calgar,Patrick,Laelia,Ryan,Gitrog,,,,,Ryan,Gitrog,15,12,combat damage,FFA,Gitrog draws a zillion cards and closes w/ Multani
|
||||||
|
2/4/24,Terence,Slimefoot and Squee,Patrick,Ayara,Ryan,Omnath,,,,,,,Ryan,Omnath,8,8,combat damage,FFA,"Vorinclex, Voice of Hunger enables Crackle with Power (x=4) to overcome Army of the Damned"
|
||||||
|
2/20/24,Terence,Wilson/Cultist,Ryan,Gitrog,Patrick,Tekuthal,,,,,,,Terence,Wilson/Cultist,15,12,combat damage,FFA,Weatherlight and Ormendahl collect many tithes
|
||||||
|
2/20/24,Ryan,Myrel,Patrick,Tekuthal,Terence,Ravos/Rebbec,,,,,,,Patrick,Tekuthal,10,9,combat damage,FFA,Arcbound Crusher got chonky (w/ Sword of Truth & Justice)
|
||||||
|
2/20/24,Terence,Ravos/Rebbec,Ryan,Myrel,Patrick,Tekuthal,,,,,,,Terence,Ravos/Rebbec,10,9,combat damage,FFA,Ryan's Coat of Arms + Terence's Door of Destinies and Haunted One = math!
|
||||||
|
2/25/24,Patrick,Elmar storm,Jack J,Gale/Scion of Halaster,Terence,Kefnet the Mindful,Jeff,Mondrak,Ryan,Don Andres,,,Jeff,Mondrak,12,9,combat damage,FFA,Elesh Norn is a good finisher; Ryan achievement unlocked: control all other commanders at once
|
||||||
|
2/25/24,Terence,Rograkh/Silas ninjas,Jeff,Illuna,Ryan,Emiel,Patrick,Myrkul planeswalkers,Jack J,Syr Ginger,,,Terence,Rograkh/Silas ninjas,16,15,combat damage,FFA,Yuriko + ninja informant = overwhelming card advantage
|
||||||
|
2/25/24,Ryan,Omnath,Patrick,Rafiq,Terence,Yoshimaru/Reyhan,Jeff,Go-Shintai of Life's Origin,,,,,Jeff,Go-Shintai of Life's Origin,8,5,combat damage,FFA,Shrine of tapping stuff down bought just enough time
|
||||||
|
2/28/24,Terence,Dargo/Nadier,Ryan,Pantlaza,Patrick,Niv-Mizzet,,,,,,,Ryan,Pantlaza,10,9,combat damage,FFA,Hasty dinosaurs post-wrath with a Savage Order for Gishath chonks life totals
|
||||||
|
2/28/24,Ryan,Gitrog,Patrick,Niv-Mizzet,Terence,Dargo/Nadier,,,,,,,Patrick,Niv-Mizzet,9,9,combat damage,FFA,Faeburrow Elder enables quick acceleration; Case of the Shattered Pact is quick damage
|
||||||
|
2/28/24,Ryan,Gitrog,Patrick,Niv-Mizzet,Terence,Dargo/Nadier,,,,,,,Terence,Dargo/Nadier,8,5,combat damage,FFA,Dargo with Jeska 1-shot Ryan (and earned 27 impulse draws via Fire Giant's Fury - into Burnt Offering); Skull Storm precisely lethal
|
||||||
|
2/28/24,Ryan,"Liesa, Shroud of Dusk",Patrick,Duke Ulder Ravengard,Terence,Malcolm/Ich-Tekik,,,,,,,Patrick,Duke Ulder Ravengard,13,11,combat damage,FFA,Stealing Angel of Destiny and giving myriad 1-shot Ryan (while earning 60 life)
|
||||||
|
3/10/24,stranger,"Krenko, Tin Street Kingpin",Patrick,Tekuthal,stranger,Gishath,stranger,Ur-Dragon,,,,,stranger,Ur-Dragon,7,7,combat damage,FFA,"Master Warcraft with Atarka, Miirym, and 2 Ur-Dragons; poor threat analysis (2 friends didn't target each other)"
|
||||||
|
3/10/24,stranger,Gishath,stranger,Ur-Dragon,stranger,Korvold,Patrick,Dihada,,,,,Patrick,Dihada,10,8,combat damage,FFA,Heroes' Podium + Day of Destiny stacks quickly; Malik gets around Lightning Greaves (same 2 friends)
|
||||||
|
3/10/24,stranger,"Lazav, the Multifarious",stranger,"Mirri, Weatherlight Duelist",Patrick,Duke Ulder Ravengard,stranger,Anzrag,,,,,Patrick,Duke Ulder Ravengard,11,9,combat damage,FFA,Lazav cast an honest Eater of Days to block Anzrag buffed by Xenagos; Knight-Captain of Eos perma-fogged Anzrag to victory
|
||||||
|
3/10/24,stranger,"Brimaz, Blight of Oreskos",stranger,"The Master, Multiplied",stranger,"Lonis, Cryptozoologist",stranger,"Mirri, Weatherlight Duelist",Patrick,Tekuthal,,,stranger,"The Master, Multiplied",7,7,combat damage,FFA,unchecked Master Multiplied with ramp = 13 Masters attacking everyone; Brimaz + Tekuthal both mana screwed
|
||||||
|
3/10/24,stranger,"Brimaz, Blight of Oreskos",stranger,"The Master, Multiplied",stranger,"Lonis, Cryptozoologist",stranger,"Mirri, Weatherlight Duelist",Patrick,Tekuthal,,,stranger,"Brimaz, Blight of Oreskos",12,12,quality of life concede,FFA,Several board wipes + removal with life totals largely intact; dealt with Koma; Myojin of Seeing Winds drew 13 and could keep going but conceded
|
||||||
|
3/15/24,Terence,Atraxa win-cons,Ryan,Emiel,Patrick,Duke Ulder Ravengard,,,,,,,Patrick,Duke Ulder Ravengard,16,13,combat damage,FFA,Ghostway + wrath to slow down Emiel+Seedborn
|
||||||
|
3/15/24,Patrick,Niv-Mizzet,Terence,Yidris dredge,Ryan,Pantlaza,,,,,,,Terence,Yidris dredge,10,9,combat damage,FFA,"Living End claims another victim, even when scripted a turn ahead"
|
||||||
|
3/15/24,Ryan,Pantlaza,Patrick,Niv-Mizzet,Terence,Yidris dredge,,,,,,,Ryan,Pantlaza,12,12,combat damage,FFA,Ghalta discover trigger into Portal to Phyrexia (with Skullspore Nexus)
|
||||||
|
3/15/24,Ryan,Reyav,Patrick,Laelia,Terence,Yidris dredge,,,,,,,Terence,Yidris dredge,9,7,combat damage,FFA,Echoing Equation on Hogaak takes out Patrick; Narcomoeba attacking was the final damage to Ryan
|
||||||
|
3/30/24,Jack F,"Brenard, Ginger Sculptor",Terence,Ravos/Rebbec,Patrick,Duke Ulder Ravengard,Jeff,Mondrak,Brandon,The Wise Mothman,,,Jeff,Mondrak,11,8,combat damage,FFA,Anointed Procession + (destroyed: Cathar's Crusade + Starlight Spectacular) with multiple wraths overcame a LARGE Mothman and Brenard's army
|
||||||
|
3/30/24,Ajit,Brago,Patrick,Laelia,Jeff,Go-Shintai of Life's Origin,Brandon,"Liberty Prime, Recharged",Jack F,"Astor, Bearer of Blades",,,Ajit,Brago,14,9,combat damage,FFA,"Brago + Medomai turns ft. 21/21 Astor (RIP Patrick), 22/22 Danitha; 31/31 Laelia; Goblin Welder loops of Synth-Reflector Mages; 7+ shrines threatening w/ Zur"
|
||||||
|
4/10/24,Terence,Ishai/Tana,Ryan,Don Andres,Patrick,Tekuthal,,,,,,,Terence,Ishai/Tana,9,9,combat damage,FFA,Assemble the Legion + Felidar Retreat
|
||||||
|
4/10/24,Patrick,Tekuthal,Terence,Ishai/Tana,Ryan,Don Andres,,,,,,,Patrick,Tekuthal,10,10,poison,FFA,Don Andres cast Eternal Dominion
|
||||||
|
4/16/24,Ryan,Goose Mother,Terence,"Vorinclex, Monstrous Raider",Patrick,Maarika,,,,,,,Terence,"Vorinclex, Monstrous Raider",10,9,21+ commander,FFA,Vorinclex + Bone Saws backed up by hexproof
|
||||||
|
4/16/24,Terence,Kefnet the Mindful,Patrick,Maarika,Ryan,Goose Mother,,,,,,,Terence,Kefnet the Mindful,10,9,21+ commander,FFA,Kefnet wears a Robe of the Archmagi and survives Phasing of Zhalfir; Ryan Mana Drained a t4 Harmonize into a large Goose
|
||||||
|
4/16/24,Terence,Kefnet the Mindful,Patrick,Dihada,Ryan,Emiel,,,,,,,Terence,Kefnet the Mindful,13,12,21+ commander,FFA,Kefnet w/ Empyrial Plate
|
||||||
|
4/20/24,Jack J,Syr Ginger,Patrick,Kamahl/Prava,Terence,Atraxa win-cons,,,,,,,Patrick,Kamahl/Prava,9,8,combat damage,FFA,Inspiring Leader + both commanders
|
||||||
|
4/20/24,Terence,Atraxa win-cons,Jack J,Syr Ginger,Ryan,Emiel,Patrick,Kamahl/Prava,,,,,Jack J,Syr Ginger,15,12,combat damage,FFA,Ghalta/Mavren made 20 vampires (w/ Doubling Season); Millenium Calendar hit 50; Spine of Ish Sah recurred each turn
|
||||||
|
4/20/24,Ryan,Gitrog,Patrick,Laelia,Terence,Amber/Veteran Soldier,Jack J,Ghyrson Starn,,,,,Patrick,Laelia,10,9,21+ commander,FFA,"Throne of Eldraine into Etali snowballed, then Laelia cascaded for +50/+50. Slicer *nearly* took Laelia out. Terence Generous Gifted Ryan's only land"
|
||||||
|
4/20/24,Terence,Vial Smasher/Sidar Kondo,Ryan,"Liesa, Shroud of Dusk",Patrick,Dihada,,,,,,,Ryan,"Liesa, Shroud of Dusk",11,10,combat damage,FFA,Austere Command
|
||||||
|
4/20/24,Patrick,Dihada,Terence,Vial Smasher/Sidar Kondo,Ryan,"Liesa, Shroud of Dusk",,,,,,,Terence,Vial Smasher/Sidar Kondo,10,8,combat damage,FFA,Authority of the Consul
|
||||||
|
4/23/24,Patrick,Ashad,Terence,Phabine,Ryan,Zaxara,,,,,,,Terence,Phabine,8,7,combat damage,FFA,"Adeline, Port Razer, Halana and Alena into Phabine"
|
||||||
|
4/23/24,Ryan,Zaxara,Patrick,Ashad,Terence,(Borrowed) Ryan's Jon Irenicus,,,,,,,Ryan,Zaxara,9,7,combat damage,FFA,Genesis Wave for 7; Exponential Growth for 3; Villainous Wealth for 19; Biomass Mutation for 32 (477 trample damage)
|
||||||
|
4/23/24,Ryan,Reyav,Patrick,Gwenna,Terence,Yidris dredge,,,,,,,Patrick,Gwenna,11,7,combat damage,FFA,Living Death wiped out Reyav early; Ram Through post Zopandrel for lethal
|
||||||
|
4/23/24,Ryan,Gitrog,Patrick,Rafiq,Terence,Jan Jansen,,,,,,,Terence,Jan Jansen,9,8,aristocrats/burn,FFA,Mirkwood Bats + Thornbite Staff + Mayhem Devil + Jan Jansen ftw; Rafiq flooded after a terrifying start
|
||||||
|
5/1/24,Brandon,"Gavi, Nest Warden",Ryan,Ziatora,Patrick,Ashad,Terence,"Izzet (Jori En, Ruin Diver)",,,,,Terence,"Izzet (Jori En, Ruin Diver)",9,8,combat damage,FFA,"Arcane Bombardment, Stormkiln Artist, Haughty Djinn should've been stopped sooner. Djinn Illuminatus replicated Lightning Bolt x8 to take out Ashad"
|
||||||
|
5/1/24,Patrick,Ashad,Terence,"Izzet (Bilbo, Retired Burglar)",Brandon,"Dogmeat, Ever Loyal",Ryan,Ziatora,,,,,Patrick,Ashad,11,6,combat damage,FFA,Ziatora+GE-Rhonas+Zopandrel KO'ed Brandon on t6; Ashad and 8 Mishra's Self-Replicators went wide enough
|
||||||
|
5/7/24,Terence,Tevesh Szat/Kraum,Ryan,Ur-Dragon,Patrick,Kamahl/Prava,,,,,,,Ryan,Ur-Dragon,11,10,combat damage,FFA,"Elminster's Simulacrum (copied) created 2 dragons and a Kamahl, but didn't live to untap"
|
||||||
|
5/7/24,Terence,Wernog/Cecily,Ryan,Ur-Dragon,Patrick,Kamahl/Prava,,,,,,,Terence,Wernog/Cecily,11,11,alt win-con,FFA,Hellkite Tyrant win-con enabled by Storm the Vault and Wernog flickers (equalled 32 life!)
|
||||||
|
5/7/24,Terence,Ravos/Rebbec,Ryan,Zaxara,Patrick,Kamahl/Prava,,,,,,,Terence,Ravos/Rebbec,11,10,combat damage,FFA,"Rammas Echor, Door of Destinies, Metallic Mimic, Hero of Bladehold took out Ryan 2 turns before Simic Ascendancy, then Anduril, Narsil Reforged + Biotransference closed it out after a mana-screwed Patrick dropped 8 lands (Harvest Season) w/ Felidar Retreat and Doubling Season"
|
||||||
|
5/15/24,Ryan,"Tinybones, the Pickpocket",Patrick,"Smeagol, Helpful Guide",Terence,Atraxa win-cons,,,,,,,Terence,Atraxa win-cons,10,10,alt win-con,FFA,Accidental Millennium Calendar win; Smeagol milled both opponents out but forgot Calendar would win on upkeep (and had artifact removal in hand)
|
||||||
|
5/15/24,Patrick,"Smeagol, Helpful Guide",Terence,Atraxa win-cons,Ryan,"Tinybones, the Pickpocket",,,,,,,Patrick,"Smeagol, Helpful Guide",11,10,combat damage,FFA,Awakening Zone triggered Smeagol every turn; Rampaging Baloths + Invasion of Lorwyn closed out
|
||||||
|
5/15/24,Terence,Emry,Ryan,Omnath,Patrick,Niv-Mizzet,,,,,,,Terence,Emry,9,9,combat damage,FFA,"T3 Kappa Cannoneer survived 2 board wipes, then Echo Storm created 4 more"
|
||||||
|
5/15/24,Patrick,Niv-Mizzet,Terence,Emry,Ryan,Omnath,,,,,,,Ryan,Omnath,8,8,aristocrats/burn,FFA,Nissa + Ancient Greenwarden into Crackle With Power
|
||||||
|
5/18/24,Jack J,Syr Ginger,Patrick,Maarika,Jack F,"Brenard, Ginger Sculptor",Brandon,"Dogmeat, Ever Loyal",,,,,Brandon,"Dogmeat, Ever Loyal",9,6,combat damage,FFA,31-power Dogmeat (Strong Back+Mantle of the Ancients) archenemy'ed the table
|
||||||
|
5/18/24,Patrick,Maarika,Jack F,Jeskai (The War Doctor/Clara Oswald (blue)),Brandon,"Caesar, Legion's Emperor",Jack J,Ghyrson Starn,,,,,Patrick,Maarika,12,7,combat damage,FFA,Maarika/Tergrid stole Fervent Charge and Iroas to lethal Brandon
|
||||||
|
5/18/24,Jack F,Jeskai (Kate Stewart),Brandon,"Morska, Undersea Sleuth",Jack J,Ghyrson Starn,Patrick,Kamahl/Prava,,,,,Jack J,Ghyrson Starn,7,7,aristocrats/burn,FFA,"Niv-Mizzet, Ghyrson Starn, Ophidian Eye"
|
||||||
|
5/18/24,Jack F,"Astor, Bearer of Blades",Brandon,Commodore Guff,Ryan,Don Andres,Jack J,Jon Irenicus,Patrick,Kamahl/Prava,,,Jack F,"Astor, Bearer of Blades",15,9,combat damage,FFA,Lae'Zel and 5 planeswalkers (unanswered for 5 turns) played archenemy until Holy Day and 2 Bruenor swings to the face
|
||||||
|
5/21/24,Terence,Wilson/Cultist,Patrick,Tekuthal,Ryan,Emiel,,,,,,,Terence,Wilson/Cultist,16,14,combat damage,FFA,Wilson's card-advantage engines never stopped; Jace TMS ult'ed (after a 5*2 proliferate turn) on Emiel; Emiel's White Dragon tapped down Tekuthal's blockers
|
||||||
|
5/21/24,Ryan,Toxrill,Terence,Rigo,Patrick,Jared [Jegantha],,,,,,,Terence,Rigo,10,8,poison,FFA,"Norn's Decree: incentivized Jared to only attack Toxrill (which halted the 1/1 swarm), then was lethal to a first strike + normal strike would-be attack"
|
||||||
|
5/21/24,Terence,Vial Smasher/Sidar Kondo,Patrick,Ayara,Ryan,Gitrog,,,,,,,Terence,Vial Smasher/Sidar Kondo,9,8,combat damage,FFA,"Vial Smasher literally only hit Ayara (~20 damage); Ayara jumped from 2 to 20 (taking out mana-screwed Gitrog) w/ Gary, but unblockable + Mercadia's Downfall was exactly lethal"
|
||||||
|
5/25/24,Jeff,Mondrak,Terence,Viconia/Cultist,Patrick,Kiora,,,,,,,Terence,Viconia/Cultist,9,9,aristocrats/burn,FFA,Living Death: Ayara + Abhorrent Overlord devotion 32 insta lethal
|
||||||
|
5/25/24,Patrick,Kiora,Jeff,Mondrak,Terence,Viconia/Cultist,,,,,,,Terence,Viconia/Cultist,16,15,combat damage,FFA,Slow one: multiple board bounces and one wipe; bestowed Nighthowler for 19 closed it out
|
||||||
|
5/26/24,stranger,Jeska/Vial Smasher,stranger,Riku of Many Paths,stranger,Sovereign Okinec Ahau,Patrick,Rafiq,,,,,Patrick,Rafiq,10,5,21+ commander,FFA,"T3 Rafiq w/ mom protection took out Riku, then rebuilt post board wipe (mom survived) and 3/3 commander lethal"
|
||||||
|
5/26/24,Patrick,Ayara,stranger,"Kellan, the Fae-Blooded",stranger,Sovereign Okinec Ahau,,,,,,,stranger,"Kellan, the Fae-Blooded",13,8,combat damage,FFA,(opted not to play Bolas' Citadel b/c had just won previous game); Kellan won due to fog effect stopping Sovereign from hitting for 100+
|
||||||
|
5/26/24,stranger,The Swarmlord,stranger,Tergrid,Patrick,Ashad,stranger,"Rocco, Street Chef",,,,,stranger,"Rocco, Street Chef",10,9,combat damage,FFA,Rocco and Tergrid emerged as heavyweights; Rocco Final Showdown'ed in response to Tergrid's Myojin of Night's Reach; Tergrid had to leave and Rocco cleaned up fast
|
||||||
|
6/2/24,Brandon,Sliver Gravemother,Ajit,Brago,Terence,Umori,Jeff,Go-Shintai of Life's Origin,Patrick,"Smeagol, Helpful Guide",,,Terence,Umori,12,8,combat damage,Star,"Smeagol was a punching bag; Aetherspouts stopped a lethal Rumbleweed, but Sanctum of Stone Fangs inadvertently took out Brandon before Ajit could be taken out"
|
||||||
|
6/2/24,Patrick,"Smeagol, Helpful Guide",Brandon,Abaddon,Ajit,Jhoira of the Ghitu,Terence,Borborygmos Enraged,Jeff,Mondrak,,,Patrick,"Smeagol, Helpful Guide",6,6,mill,Star,Naturally drew the mill combo
|
||||||
|
6/2/24,Terence,Borborygmos Enraged,Jeff,Mondrak,Patrick,Duke Ulder Ravengard,Brandon,"Morska, Undersea Sleuth",Ajit,Jhoira of the Ghitu,,,Terence,Borborygmos Enraged,10,8,combat damage,Star,"Windshaper Planetar rerouted 21 Mondrak damage from Brandon to Terence; Mondrak's Starlight Spectacular (would've won if we'd done math precombat to play 1 more creature) was short of killing Brandon so took out Patrick; Morska's Kappa Cannoneer ended Jeff, leaving Terence last one standing"
|
||||||
|
6/2/24,Brandon,"Atraxa, Grand Unifier",Patrick,Talion,Jeff,Illuna,Ajit,Kadena,Terence,Rograkh/Silas ninjas,,,Patrick,Talion,11,9,combat damage,Star,"Kadena early Tempt for Discovery and stole Talion; Atraxa early Breach the Multiverse (Mommy Norn, Oko, Apex Altisaur, Elspeth Knight Errant) became archenemy; loads of interaction; eventually Talion+Sakashima + Sheoldred closed the door"
|
||||||
|
6/11/24,Ryan,Reyav,Patrick,Talion,Terence,Umori,,,,,,,Patrick,Talion,13,11,21+ commander,FFA,T2 Hunted Horror centaurs nearly solo'ed Patrick; Sword of W&P stabilized from 5 life
|
||||||
|
6/11/24,Patrick,Duke Ulder Ravengard,Terence,Ardenn/Esior,Ryan,Emiel,,,,,,,Ryan,Emiel,11,9,combat damage,FFA,Esior w/ Sword of F&F and double strike was dominant until double Manglehorn
|
||||||
|
6/11/24,Terence,Slimefoot and Squee,Ryan,Chatterfang,Patrick,Ayara,,,,,,,Patrick,Ayara,9,8,aristocrats/burn,FFA,Bolas' Citadel activation lethaled Terence; Muranda Petroglyphs buffed more zombies than squirrels (thus keeping big Liliana alive)
|
||||||
|
7/5/24,Jeff,Illuna,Natalie,Anikthea,Jack J,Gale/Scion of Halaster,,,,,,,Jeff,Illuna,15,15,aristocrats/burn,FFA,A large Setessan Champion provided ample fuel for a triple Brash Taunter activation
|
||||||
|
7/5/24,Jeff,Ulalek,Natalie,(Borrowed) Jeff's Go-Shintai,Jack J,Slivers,,,,,,,Jack J,Slivers,8,8,combat damage,FFA,Jack snookered himself as Crystalline Sliver blocked Magma Sliver from going off. Magma Sliver + Sliver Queen + Heart Sliver + Ashnod's Altar is nuts (as is Mana Echoes)
|
||||||
|
7/5/24,Natalie,(Borrowed) Jack's Ghyrson Starn,Jack J,Evra,Jeff,Ulalek,,,,,,,Natalie,(Borrowed) Jack's Ghyrson Starn,9,9,aristocrats/burn,FFA,"Natalie won by playing a pointless Mana Geyser which triggered a bunch of ""on instant/sorcery"" effects"
|
||||||
|
7/5/24,Jeff,Mondrak,Natalie,(Borrowed) Jack's Melek,Jack J,Evra,,,,,,,Jack J,Evra,6,6,alt win-con,FFA,Jack wins with Felidar Sovereign after a slow start and with Aetherflux Reservoir
|
||||||
|
7/5/24,Jack J,Evra,Jeff,Mondrak,Natalie,(Borrowed) Jack's Melek,,,,,,,Jeff,Mondrak,11,11,combat damage,FFA,Guardian of Faith whiffed a huge Evra swing
|
||||||
|
7/5/24,Natalie,Anikthea,Jack J,Syr Ginger,Jeff,"Liesa, Forgotten Archangel",,,,,,,Natalie,Anikthea,15,15,combat damage,FFA,Enchantments are oppressive!
|
||||||
|
7/13/24,Ryan,Ulalek,Patrick,Oops all Kayas,Jeff,Ulalek,,,,,,,Jeff,Ulalek,9,7,combat damage,FFA,MH3 Ulamog as a 19/19 w/ Annihilator 12 > two It That Betrays; Patrick mana screwed
|
||||||
|
7/13/24,Patrick,Oops all Kayas,Jeff,Ulalek,Ryan,Ulalek,,,,,,,Patrick,Oops all Kayas,11,10,combat damage,FFA,13 spirit and bird tokens ftw! Buffed by Intangible Virtue
|
||||||
|
7/13/24,Patrick,Ashad,Jeff,"Liesa, Forgotten Archangel",Ryan,Rin and Seri,,,,,,,Ryan,Rin and Seri,11,10,combat damage,FFA,"Liesa + Luminous Broodmoth + Vito = redundancy and fast drains! But, double Combustible Gearhulk dropped Liesa from 39 to 4 before dying; Rin and Seri had exactly enough creatures for lethal (upon noting Dauthi Voidwalker has shadow and couldn't block!)"
|
||||||
|
7/13/24,Patrick,Tekuthal,Jeff,Mondrak,Ryan,Gitrog,,,,,,,Jeff,Mondrak,8,7,combat damage,FFA,"Sol Ring -> Throne of Eldraine -> Cathar's Crusade -> Elesh Norn, GC took out Tekuthal + Flux Channeler + Danny Pink w/ Sword of Truth and Justice one turn before lethal; Gitrog mana screwed"
|
||||||
|
7/13/24,Jeff,Go-Shintai of Life's Origin,Ryan,Breya,Patrick,Gwenna,,,,,,,Patrick,Gwenna,12,12,combat damage,FFA,Heavyweight battle! KO turn: topdecked Return of the Wildspeaker for 10 cards -> Old Gnawbone + Pest Infestation to clear out leftovers after exiling Darksteel Forge. Double Portal to Phyrexia ate enough Shrines; red Shrine took Daretti and 1 Garruk
|
||||||
|
7/13/24,Ryan,Reyav,Patrick,Dihada,Jeff,Mondrak,,,,,,,Ryan,Reyav,5,4,21+ commander,FFA,Sol Ring -> Reyav + Shadowspear + 3 more equipment
|
||||||
|
7/13/24,Patrick,Dihada,Jeff,Mondrak,Ryan,Reyav,,,,,,,Patrick,Dihada,8,7,combat damage,FFA,Reaver Cleaver on Mogis into Gallifrey Falls+No More to (mostly) 1-sided wipe
|
||||||
|
7/25/24,Ryan,Ulalek,Terence,Ardenn/Esior,Patrick,Ashad,,,,,,,Terence,Ardenn/Esior,12,7,21+ commander,FFA,Ardenn w/ Colossus Hammer for 22; Ugin's Binding bought Ryan a couple turns before the inevitable
|
||||||
|
7/25/24,Terence,Ardenn/Esior,Patrick,Ashad,Ryan,Goose Mother,,,,,,,Ryan,Goose Mother,12,12,combat damage,FFA,Esior stalled big time; 2x Blinkmoth Urn accelerated large Goose Mothers; critical Silence stopped Ashad; Momentous Fall for 12 into 2x counterspell + Questing Beast lethal
|
||||||
|
7/25/24,Terence,Vial Smasher/Sidar Kondo,Patrick,Niv-Mizzet,Ryan,Emiel,,,,,,,Terence,Vial Smasher/Sidar Kondo,11,7,combat damage,FFA,Mercadia's Downfall adds 20+ damage to oust Niv-Mizzet
|
|
@ -62,6 +62,36 @@ def test_add_and_retrieve_deck(test_client: TestClient, cleanups):
|
|||||||
cleanups.add_success(success_cleanup)
|
cleanups.add_success(success_cleanup)
|
||||||
|
|
||||||
|
|
||||||
|
def test_incremental_add_of_games(test_client: TestClient, cleanups):
|
||||||
|
latest_deck_response = _json_get(test_client, "/game/latest_game")
|
||||||
|
assert latest_deck_response.status_code == 404
|
||||||
|
|
||||||
|
# https://github.com/tiangolo/fastapi/issues/1536#issuecomment-640781718
|
||||||
|
with open("seed-data/all-in-one.csv", "rb") as f:
|
||||||
|
test_client.post(
|
||||||
|
"/api/seed/all_in_one",
|
||||||
|
files={"file": ("fake_all_in_one_filename.csv", f, "text/csv")},
|
||||||
|
)
|
||||||
|
|
||||||
|
latest_deck_response = _json_get(test_client, "/game/latest_game")
|
||||||
|
assert latest_deck_response.status_code == 200
|
||||||
|
print(latest_deck_response.json())
|
||||||
|
assert latest_deck_response.json()["date"] == "2024-07-05T00:00:00"
|
||||||
|
|
||||||
|
# then seed again, and check that it successfully gets the expected latest
|
||||||
|
with open(
|
||||||
|
"seed-data/all-in-one-updated-for-incremental-add-testing.csv", "rb"
|
||||||
|
) as f:
|
||||||
|
test_client.post(
|
||||||
|
"/api/seed/all_in_one",
|
||||||
|
files={"file": ("fake_all_in_one_filename.csv", f, "text/csv")},
|
||||||
|
)
|
||||||
|
|
||||||
|
latest_deck_response = _json_get(test_client, "/game/latest_game")
|
||||||
|
assert latest_deck_response.status_code == 200
|
||||||
|
assert latest_deck_response.json()["date"] == "2024-07-25T00:00:00"
|
||||||
|
|
||||||
|
|
||||||
def _json_get(c: TestClient, path: str) -> httpx.Response:
|
def _json_get(c: TestClient, path: str) -> httpx.Response:
|
||||||
return c.get(f"/api{path}", headers={"Content-Type": "application/json"})
|
return c.get(f"/api{path}", headers={"Content-Type": "application/json"})
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user