Add filtering on graph
This commit is contained in:
parent
dee0c26260
commit
3b1c3d7eb3
@ -1,3 +1,5 @@
|
|||||||
|
from collections import defaultdict
|
||||||
|
|
||||||
from fastapi import APIRouter, Depends, HTTPException, Request
|
from fastapi import APIRouter, Depends, HTTPException, Request
|
||||||
from fastapi.responses import HTMLResponse
|
from fastapi.responses import HTMLResponse
|
||||||
from sqlalchemy import and_, or_, func
|
from sqlalchemy import and_, or_, func
|
||||||
@ -9,7 +11,7 @@ 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
|
||||||
|
|
||||||
from .players import list_players
|
from .players import read_player, list_players
|
||||||
from .games import _render_game_participants
|
from .games import _render_game_participants
|
||||||
|
|
||||||
api_router = APIRouter(prefix="/deck", tags=["deck"])
|
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)
|
@api_router.post("/", response_model=schemas.Deck, status_code=201)
|
||||||
def create_deck(deck: schemas.DeckCreate, db: Session = Depends(get_db)):
|
def create_deck(deck: schemas.DeckCreate, db: Session = Depends(get_db)):
|
||||||
db_player = crud.get_player_by_id(db, deck.owner_id)
|
db_player = crud.get_player_by_id(db, deck.owner_id)
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
from collections import defaultdict
|
from collections import defaultdict
|
||||||
|
from datetime import datetime, MINYEAR
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
|
|
||||||
from fastapi import APIRouter, Depends, Request
|
from fastapi import APIRouter, Depends, Request
|
||||||
@ -17,7 +18,11 @@ html_router = APIRouter(
|
|||||||
|
|
||||||
|
|
||||||
@api_router.get("/graph")
|
@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 )
|
# TODO - parallelize? (Probably not worth it :P )
|
||||||
|
|
||||||
# SO Answer on row_number: https://stackoverflow.com/a/38160409/1040915
|
# 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()
|
results = query.all()
|
||||||
|
|
||||||
data_grouped_by_deck = defaultdict(list)
|
data_grouped_by_deck = defaultdict(list)
|
||||||
|
latest_date_so_far = datetime(MINYEAR, 1, 1, 0, 0, 0, 0)
|
||||||
for result in results:
|
for result in results:
|
||||||
# TODO - how to index results by name instead of tuple-number
|
# 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(
|
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 {
|
return {
|
||||||
"datasets": [
|
"datasets": [
|
||||||
{"label": key, "data": data_grouped_by_deck[key]}
|
{"label": key, "data": data_grouped_by_deck[key]}
|
||||||
|
5
app/static/css/graph.css
Normal file
5
app/static/css/graph.css
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
#options div {
|
||||||
|
float: left;
|
||||||
|
margin: 2px;
|
||||||
|
background-color: lightblue;
|
||||||
|
}
|
@ -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() {
|
$(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')
|
fetch('/api/stats/graph')
|
||||||
.then(response => response.json())
|
.then(response => response.json())
|
||||||
.then(response => {
|
.then(response => {
|
||||||
console.log(response.datasets);
|
console.log(response.datasets);
|
||||||
new Chart(
|
window.chart = new Chart(
|
||||||
document.getElementById('graph_canvas'),
|
document.getElementById('graph_canvas'),
|
||||||
{
|
{
|
||||||
type: 'line',
|
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)
|
||||||
|
}
|
||||||
|
@ -8,8 +8,14 @@
|
|||||||
<script src="https://cdn.jsdelivr.net/npm/moment@2.27.0"></script>
|
<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="https://cdn.jsdelivr.net/npm/chartjs-adapter-moment@0.1.1"></script>
|
||||||
<script src="/static/js/stats/graph.js"></script>
|
<script src="/static/js/stats/graph.js"></script>
|
||||||
|
<link rel="stylesheet" href="/static/css/graph.css"/>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<div id="wrapper" style="width:95%;margin-left:10px;"><canvas id="graph_canvas"></canvas></div>
|
<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 %}
|
{% endblock %}
|
Loading…
x
Reference in New Issue
Block a user