Add filtering on graph

This commit is contained in:
Jack Jackson 2024-07-29 20:16:01 -07:00
parent dee0c26260
commit 3b1c3d7eb3
5 changed files with 86 additions and 4 deletions

View File

@ -1,3 +1,5 @@
from collections import defaultdict
from fastapi import APIRouter, Depends, HTTPException, Request
from fastapi.responses import HTMLResponse
from sqlalchemy import and_, or_, func
@ -9,7 +11,7 @@ from ..templates import jinja_templates
from ..sql import crud, schemas
from ..sql.database import get_db
from .players import list_players
from .players import read_player, list_players
from .games import _render_game_participants
api_router = APIRouter(prefix="/deck", tags=["deck"])
@ -23,6 +25,19 @@ html_router = APIRouter(
########
@api_router.get("/by_player")
def decks_by_player(db=Depends(get_db)):
decks = crud.get_decks(db, skip=0, limit=-1)
# TODO - could probably do this directly in-db with some fancy use of `group_concat`
return_value = defaultdict(list)
for deck in decks:
# TODO - if we cared about latency, could cache this
return_value[read_player(deck.owner_id, db).name].append(
{"id": deck.id, "name": deck.name}
)
return return_value
@api_router.post("/", response_model=schemas.Deck, status_code=201)
def create_deck(deck: schemas.DeckCreate, db: Session = Depends(get_db)):
db_player = crud.get_player_by_id(db, deck.owner_id)

View File

@ -1,4 +1,5 @@
from collections import defaultdict
from datetime import datetime, MINYEAR
from typing import Optional
from fastapi import APIRouter, Depends, Request
@ -17,7 +18,11 @@ html_router = APIRouter(
@api_router.get("/graph")
def stats_graph_api(deck_ids: Optional[str] = None, db=Depends(get_db)):
def stats_graph_api(
deck_ids: Optional[str] = None,
normalize_final_datapoint: bool = False,
db=Depends(get_db),
):
# TODO - parallelize? (Probably not worth it :P )
# SO Answer on row_number: https://stackoverflow.com/a/38160409/1040915
@ -44,12 +49,26 @@ def stats_graph_api(deck_ids: Optional[str] = None, db=Depends(get_db)):
results = query.all()
data_grouped_by_deck = defaultdict(list)
latest_date_so_far = datetime(MINYEAR, 1, 1, 0, 0, 0, 0)
for result in results:
# TODO - how to index results by name instead of tuple-number
date = result[2]
latest_date_so_far = max(latest_date_so_far, date)
data_grouped_by_deck[result[0]].append(
{"score": result[1], "date": result[2].strftime("%Y-%m-%d")}
{"score": result[1], "date": date.strftime("%Y-%m-%d")}
)
if normalize_final_datapoint:
# Add a fake final datapoint to the series for any decks that weren't played in the latest game, so that lines
# continue all the way to the end of the graph
latest_date_formatted = latest_date_so_far.strftime("%Y-%m-%d")
print(f"DEBUG = {latest_date_formatted=}")
for games in data_grouped_by_deck.values():
if games[-1]["date"] != latest_date_formatted:
games.append(
{"score": games[-1]["score"], "date": latest_date_formatted}
)
return {
"datasets": [
{"label": key, "data": data_grouped_by_deck[key]}

5
app/static/css/graph.css Normal file
View File

@ -0,0 +1,5 @@
#options div {
float: left;
margin: 2px;
background-color: lightblue;
}

View File

@ -1,9 +1,35 @@
function updateGraphWithFilter() {
filterString = $('#options input[type=checkbox]:checked').map((idx, elem) => {
console.log('Adding ' + elem.id + ' to return string')
return elem.id
}).get().join(',')
console.log(filterString);
fetch('/api/stats/graph?deck_ids=' + filterString)
.then(response => response.json())
.then(response => {
window.chart.data.datasets = response.datasets
window.chart.update()
})
}
$(document).ready(function() {
$('#filter_button').click(updateGraphWithFilter)
fetch('/api/deck/by_player')
.then(response => response.json())
.then(response => {
console.log(response);
for (playerName in response) {
buildPlayerDecksDiv($('#options'), playerName, response[playerName])
}
})
fetch('/api/stats/graph')
.then(response => response.json())
.then(response => {
console.log(response.datasets);
new Chart(
window.chart = new Chart(
document.getElementById('graph_canvas'),
{
type: 'line',
@ -27,3 +53,14 @@ $(document).ready(function() {
});
});
function buildPlayerDecksDiv(parentDiv, playerName, playerDecks) {
div = $('<div>',
id='player_div_for_' + playerName
)
div.append('<p>' + playerName + '</p>')
for (deck of playerDecks) {
div.append('<input type="checkbox" id="' + deck["id"] + '" name="' + deck["name"] + '" value="' + deck["name"] + '"><label for="' + deck["name"] + '">' + deck["name"] + '</label><br/>')
}
div.appendTo(parentDiv)
}

View File

@ -8,8 +8,14 @@
<script src="https://cdn.jsdelivr.net/npm/moment@2.27.0"></script>
<script src="https://cdn.jsdelivr.net/npm/chartjs-adapter-moment@0.1.1"></script>
<script src="/static/js/stats/graph.js"></script>
<link rel="stylesheet" href="/static/css/graph.css"/>
{% endblock %}
{% block content %}
<div id="wrapper" style="width:95%;margin-left:10px;"><canvas id="graph_canvas"></canvas></div>
<div id="options">
<h2>Filter</h2>
<button id="filter_button">Go!</button><br/>
</div>
{% endblock %}