Compare commits

...

24 commits

Author SHA1 Message Date
Yarne Coppens
1239995080 Added navbar 2024-08-14 10:55:43 +02:00
Yarne Coppens
8e10d5fa1d Added loading spinner on plays page 2024-08-14 10:55:31 +02:00
Yarne Coppens
26a1c0422f Split JS in files per page 2024-08-14 10:16:54 +02:00
Yarne Coppens
e0443811b1 Added a 'plays' page to list all plays 2024-08-14 08:49:46 +02:00
Yarne Coppens
41441db75d Created line and bar multi chart for games over time 2024-08-12 09:17:32 +02:00
Yarne Coppens
06e1599461 Filtered out expansions in owned game list. Owned game statistic uses day steps 2024-08-12 09:09:39 +02:00
Yarne Coppens
8036276187 Self hosting chart.js 2024-08-12 08:27:10 +02:00
Yarne Coppens
141f533af6 Implemented chart.js 2024-08-11 23:04:30 +02:00
Yarne Coppens
a3af3aea05 Added bootstrap map 2024-08-11 22:52:46 +02:00
Yarne Coppens
af9196bca0 Made game thumbnails fluid 2024-08-11 17:48:50 +02:00
Yarne Coppens
d88ca6d216 Created more board game table columns 2024-08-11 16:20:53 +02:00
Yarne Coppens
49b97ef0c2 Conformed with the API change of the boardgame ID path -> GET 2024-08-11 16:03:22 +02:00
Yarne Coppens
76497e21c9 Self hosting all css & javascript 2024-08-11 15:17:18 +02:00
Yarne Coppens
51de22aa67 Added wishlist page 2024-08-11 12:07:16 +02:00
Yarne Coppens
d3fd938da3 Added board game card 2024-08-11 10:49:59 +02:00
Yarne Coppens
d9fd74f422 Made datatable rows clickable 2024-08-11 10:29:40 +02:00
Yarne Coppens
ecbc5f9162 Turned table into a bootstrap datatable 2024-08-11 10:06:27 +02:00
Yarne Coppens
08e6bd431d Added route to request specific board game 2024-08-10 22:45:08 +02:00
Yarne Coppens
2fabfb60f0 Created base for Flask application 2024-08-10 22:07:47 +02:00
Yarne Coppens
5a9360d10a Make board games clickable 2024-08-09 15:29:29 +02:00
Yarne Coppens
581adb64a3 Added weight column 2024-08-09 14:42:52 +02:00
Yarne Coppens
bc494a6133 Table now loads boardgame thumbnails 2024-08-08 16:32:05 +02:00
Yarne Coppens
f944e320ef Table now loads owned board games 2024-08-08 16:14:55 +02:00
Yarne Coppens
204f8a4f22 Created base files 2024-08-08 15:43:49 +02:00
21 changed files with 599 additions and 0 deletions

2
.gitignore vendored Normal file
View file

@ -0,0 +1,2 @@
__pycache__/
venv/

24
app.py Normal file
View file

@ -0,0 +1,24 @@
from flask import Flask, render_template, request
app = Flask(__name__)
@app.get("/")
def get_owned():
return render_template('owned.jinja')
@app.get("/wishlist")
def get_wishlist():
return render_template('wishlist.jinja')
@app.get("/boardgame")
def get_boardgame():
return render_template('boardgame.jinja')
@app.get('/statistics')
def get_statistics():
return render_template('statistics.jinja')
@app.get('/plays')
def get_plays():
return render_template('plays.jinja')

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

0
static/css/main.css Normal file
View file

View file

@ -0,0 +1,20 @@
document.body.onload=loadGame()
async function loadGame() {
let params = new URLSearchParams(document.location.search);
let boardgame_id = params.get("id");
var loadGameURL = api_url + '/boardgame?id=' + boardgame_id
var requested_game = await makeRequest(loadGameURL)
$('#boardgame_image').attr('src', requested_game.image_url)
$('#boardgame_name').text(requested_game.name)
$('#boardgame_weight').text(requested_game.weight)
$('#boardgame_description').text(requested_game.description)
$('#boardgame_link').attr('href', 'https://boardgamegeek.com/boardgame/' + boardgame_id)
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

14
static/javascript/main.js Normal file
View file

@ -0,0 +1,14 @@
const api_url = "http://127.0.0.1:8000"
var all_owned_games
async function makeRequest(url) {
try {
const url_request = new Request(url)
const response = await fetch(url_request);
const result = await response.json();
return result
} catch (error) {
console.error("Error:", error);
}
}

View file

@ -0,0 +1,57 @@
document.body.onload=loadOwnedGames()
async function loadOwnedGames() {
var boardgame_datatable = new DataTable('.boardgame_table', {
ajax: {
url: api_url + '/owned?filter_expansions_out=true',
dataSrc: ''
},
columns: [
{
data: 'thumbnail_url',
render: function (data,type){
return '<img src="' + data + '" class="img-fluid" />'
}
},
{
data: 'name'
},
{
data: 'min_players',
render: function(data,type,row){
if (row.min_players != row.max_players){
return row.min_players + '-' + row.max_players
}else{
return row.min_players
}
}
},
{
data: 'min_playing_time',
render: function(data,type,row){
if (row.min_playing_time != row.max_playing_time){
return row.min_playing_time + '-' + row.max_playing_time
}else{
return row.min_playing_time
}
}
},
{
data: 'weight'
}
],
columnDefs: [
{ targets: 'no-sort', orderable: false }
],
order: [[1, 'asc']]
});
$('.boardgame_table').on('click', 'tbody tr', function() {
var boardgame_id = boardgame_datatable.row(this).data().id;
window.location.href = "/boardgame?id=" + boardgame_id
})
}

View file

@ -0,0 +1,67 @@
document.body.onload=loadPlays()
async function loadPlays() {
function createPlayCard(play){
const card_div = document.createElement('div')
card_div.className = "card"
const card_image = document.createElement('img')
card_image.src = play.boardgame.image_url
const card_body = document.createElement('div')
card_body.className = 'card-body'
const card_title = document.createElement('h4')
card_title.innerHTML = play.boardgame.name
const player_names = document.createElement('div')
for (let player_index in play.players){
const player = play.players[player_index]
const player_div = document.createElement('div')
player_div.innerHTML = player.name
if (player.has_won) {
player_div.style.color = "green"
}
player_names.appendChild(player_div)
}
card_body.appendChild(card_title)
card_body.appendChild(player_names)
card_div.appendChild(card_image)
card_div.appendChild(card_body)
return card_div
}
const all_plays = await makeRequest(api_url + '/plays?filter_expansions_out=true')
const MAX_COLUMNS = 4
const column_width = 12 / MAX_COLUMNS
const rows_needed = Math.ceil(all_plays.length / MAX_COLUMNS)
for (let row_number = 0; row_number < rows_needed; row_number++){
const row_div = document.createElement('div')
row_div.className = 'row'
for (let column_number = 0; column_number < MAX_COLUMNS; column_number++) {
const column_div = document.createElement('div')
column_div.className = "col-sm-" + column_width
const current_play = all_plays[(row_number * MAX_COLUMNS) + column_number]
const play_card = createPlayCard(current_play)
column_div.appendChild(play_card)
row_div.appendChild(column_div)
}
document.body.appendChild(row_div)
}
document.getElementById('plays_loading_spinner').remove()
}

View file

@ -0,0 +1,43 @@
document.body.onload=loadStatistics()
async function loadStatistics(){
const overtimechart = document.getElementById("overtimechart")
games_over_time_statistic = await makeRequest(api_url + '/statistics/amount_of_games_over_time?day_step=30')
games_over_time_statistic_no_expanions = await makeRequest(api_url + '/statistics/amount_of_games_over_time?day_step=30&filter_expansions_out=true')
games_over_time_statistic_only_expanions = await makeRequest(api_url + '/statistics/amount_of_games_over_time?day_step=30&only_expansions=true')
new Chart(overtimechart, {
type: 'bar',
data: {
labels: Object.keys(games_over_time_statistic.result),
datasets: [{
label: games_over_time_statistic.name,
data: Object.values(games_over_time_statistic.result),
borderWidth: 1,
type: 'line'
},
{
label: "Base games",
data: Object.values(games_over_time_statistic_no_expanions.result),
borderWidth: 1
},
{
label: "Expansions",
data: Object.values(games_over_time_statistic_only_expanions.result),
borderWidth: 1
}
]
},
options: {
scales: {
y: {
beginAtZero: true
}
}
}
});
$("#overtimechartname").text(games_over_time_statistic.name)
}

View file

@ -0,0 +1,69 @@
document.body.onload=loadWishlistedGames()
async function loadWishlistedGames() {
var wishlist_priorities = [1,2,3,4]
jQuery.each(wishlist_priorities, function(index, item){
var boardgame_datatable = new DataTable('#wishlist_table'+item, {
ajax: {
url: api_url + '/wishlist?priority='+item,
dataSrc: ''
},
columns: [
{
data: 'wishlist_priority'
},
{
data: 'thumbnail_url',
render: function (data,type){
return '<img src="' + data + '" class="img-fluid" />'
}
},
{
data: 'name'
},
{
data: 'min_players',
render: function(data,type,row){
if (row.min_players != row.max_players){
return row.min_players + '-' + row.max_players
}else{
return row.min_players
}
}
},
{
data: 'min_playing_time',
render: function(data,type,row){
if (row.min_playing_time != row.max_playing_time){
return row.min_playing_time + '-' + row.max_playing_time
}else{
return row.min_playing_time
}
}
},
{
data: 'weight'
}
],
columnDefs: [
{
target: 0,
visible: false
},
{ targets: 'no-sort', orderable: false }
],
order: [[2, 'asc']]
});
$('#wishlist_table'+item).on('click', 'tbody tr', function() {
var boardgame_id = boardgame_datatable.row(this).data().id;
window.location.href = "/boardgame?id=" + boardgame_id
})
});
}

51
templates/base.jinja Normal file
View file

@ -0,0 +1,51 @@
<!DOCTYPE html>
<html data-theme="light">
<head>
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>
Boardgame Site
</title>
<link href="{{ url_for('static', filename='css/bootstrap/bootstrap.min.css') }}" rel="stylesheet" integrity="sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6hW+ALEwIH" crossorigin="anonymous">
<link href="{{ url_for('static', filename='css/datatables/datatables.min.css') }}" rel="stylesheet">
<link rel="stylesheet" href="{{ url_for('static', filename='css/main.css') }}">
<script src="{{ url_for('static', filename='javascript/jquery/jquery-3.7.1.min.js') }}" defer></script>
<script src="{{ url_for('static', filename='javascript/datatables/datatables.min.js') }}" defer></script>
<script src="{{ url_for('static', filename='javascript/chart/chart.js') }}"></script>
</head>
<body>
<ul class="nav nav-tabs">
<li class="nav-item">
<a class="nav-link active" href="{{ url_for('get_owned') }}">Collectie</a>
</li>
<li class="nav-item">
<a class="nav-link" href="{{ url_for('get_wishlist') }}">Wishlist</a>
</li>
<li class="nav-item">
<a class="nav-link" href="{{ url_for('get_statistics') }}">Statistieken</a>
</li>
<li class="nav-item">
<a class="nav-link" href="{{ url_for('get_plays') }}">Gespeeld</a>
</li>
<li class="nav-item">
<a class="nav-link disabled" href="#">Disabled</a>
</li>
</ul>
{% block body_block %}
{% endblock body_block %}
<script src="{{ url_for('static', filename='javascript/main.js') }}" defer></script>
{% block extra_js_files %}
{% endblock extra_js_files%}
</body>
</html>

25
templates/boardgame.jinja Normal file
View file

@ -0,0 +1,25 @@
{% extends "base.jinja" %}
{% block body_block %}
<div class="row">
<div class="col-sm-2">
<div class="card">
<img id="boardgame_image">
<div class="card-body">
<h4 class="card-title" id="boardgame_name">John Doe</h4>
<p class="card-text" id="boardgame_description">Some example text.</p>
<a href="#" id="boardgame_link" class="btn btn-primary">Bekijk op BGG</a>
</div>
</div>
</div>
<div class="col-sm-10"><p id="boardgame_weight"></p></div>
</div>
{% endblock body_block %}
{% block extra_js_files %}
<script src="{{ url_for('static', filename='javascript/boardgame.js') }}" defer></script>
{% endblock extra_js_files %}

26
templates/owned.jinja Normal file
View file

@ -0,0 +1,26 @@
{% extends "base.jinja" %}
{% block body_block %}
<div class="table-responsive">
<table class="table table-striped boardgame_table">
<thead>
<tr>
<th scope="col" class="no-sort">Thumbnail</th>
<th scope="col">Naam</th>
<th scope="col">Spelers</th>
<th scope="col">Duratie</th>
<th scope="col">Moeilijkheid</th>
</tr>
</thead>
</table>
</div>
{% endblock body_block %}
{% block extra_js_files %}
<script src="{{ url_for('static', filename='javascript/owned.js') }}" defer></script>
{% endblock extra_js_files %}

11
templates/plays.jinja Normal file
View file

@ -0,0 +1,11 @@
{% extends "base.jinja" %}
{% block body_block %}
<div id="plays_loading_spinner" class="spinner-border"></div>
{% endblock body_block %}
{% block extra_js_files %}
<script src="{{ url_for('static', filename='javascript/plays.js') }}" defer></script>
{% endblock extra_js_files %}

View file

@ -0,0 +1,39 @@
{% extends "base.jinja" %}
{% block body_block %}
<div class="row">
<div class="col">
<div class="card">
<canvas id="overtimechart"></canvas>
<div class="card-body">
<h4 class="card-title" id="overtimechartname"></h4>
</div>
</div>
</div>
<div class="col">
<div class="card">
<canvas id="overtimechart"></canvas>
<div class="card-body">
<h4 class="card-title" id="overtimechartname"></h4>
</div>
</div>
</div>
<div class="col">
<div class="card">
<canvas id="overtimechart"></canvas>
<div class="card-body">
<h4 class="card-title" id="overtimechartname"></h4>
</div>
</div>
</div>
</div>
{% endblock body_block %}
{% block extra_js_files %}
<script src="{{ url_for('static', filename='javascript/statistics.js') }}" defer></script>
{% endblock extra_js_files %}

81
templates/wishlist.jinja Normal file
View file

@ -0,0 +1,81 @@
{% extends "base.jinja" %}
{% block body_block %}
<body onload="loadWishlistedGames()">
<div class="table-responsive">
<table id="wishlist_table1" class="table table-striped boardgame_table">
<thead>
<tr>
<th scope="col">wishlist_priority</th>
<th scope="col" class="no-sort">Thumbnail</th>
<th scope="col">Naam</th>
<th scope="col">Spelers</th>
<th scope="col">Duratie</th>
<th scope="col">Moeilijkheid</th>
</tr>
</thead>
</table>
</div>
<div class="table-responsive">
<table id="wishlist_table2" class="table table-striped boardgame_table">
<thead>
<tr>
<th scope="col">wishlist_priority</th>
<th scope="col" class="no-sort">Thumbnail</th>
<th scope="col">Naam</th>
<th scope="col">Spelers</th>
<th scope="col">Duratie</th>
<th scope="col">Moeilijkheid</th>
</tr>
</tr>
</thead>
</table>
</div>
<div class="table-responsive">
<table id="wishlist_table3" class="table table-striped boardgame_table">
<thead>
<tr>
<th scope="col">wishlist_priority</th>
<th scope="col" class="no-sort">Thumbnail</th>
<th scope="col">Naam</th>
<th scope="col">Spelers</th>
<th scope="col">Duratie</th>
<th scope="col">Moeilijkheid</th>
</tr>
</tr>
</thead>
</table>
</div>
<div class="table-responsive">
<table id="wishlist_table4" class="table table-striped boardgame_table">
<thead>
<tr>
<th scope="col">wishlist_priority</th>
<th scope="col" class="no-sort">Thumbnail</th>
<th scope="col">Naam</th>
<th scope="col">Spelers</th>
<th scope="col">Duratie</th>
<th scope="col">Moeilijkheid</th>
</tr>
</tr>
</thead>
</table>
</div>
</body>
{% endblock body_block %}
{% block extra_js_files %}
<script src="{{ url_for('static', filename='javascript/wishlist.js') }}" defer></script>
{% endblock extra_js_files %}