Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

124 network visuals #151

Merged
merged 30 commits into from
Feb 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
721bc7c
#41 add students
anisfeld Dec 23, 2023
783832d
Merge branch 'main' of https://github.com/prijatelilab/PrijateliTree
anisfeld Dec 26, 2023
88de4f5
Merge branch 'main' of https://github.com/prijatelilab/PrijateliTree
anisfeld Dec 26, 2023
0a65c19
Merge branch 'main' of https://github.com/prijatelilab/PrijateliTree
anisfeld Dec 27, 2023
cc26dab
Merge branch 'main' of https://github.com/prijatelilab/PrijateliTree
anisfeld Dec 27, 2023
0b95ff4
Merge branch 'main' of https://github.com/prijatelilab/PrijateliTree
anisfeld Jan 3, 2024
7051aeb
Merge branch 'main' of https://github.com/prijatelilab/PrijateliTree
anisfeld Jan 4, 2024
77d1379
Merge branch 'main' of https://github.com/prijatelilab/PrijateliTree
anisfeld Jan 4, 2024
7eb8fe2
Merge branch 'main' of https://github.com/prijatelilab/PrijateliTree
anisfeld Jan 7, 2024
8db9f14
Merge branch 'main' of https://github.com/prijatelilab/PrijateliTree
anisfeld Jan 9, 2024
97c00ba
Merge branch 'main' of https://github.com/prijatelilab/PrijateliTree
anisfeld Jan 12, 2024
7550672
new route with network data (#124)
anisfeld Jan 17, 2024
f5255cd
adding visual html/css/js (#124)
anisfeld Jan 17, 2024
77e918f
tooltip works, randomly flip graph (#124)
anisfeld Jan 18, 2024
2a13a19
Merge branch '124-network-visuals' of github.com:prijatelilab/Prijate…
FedericoDM Feb 3, 2024
692ec1e
Merge change
FedericoDM Feb 3, 2024
84f4914
Linting
FedericoDM Feb 3, 2024
d3c59e2
Adding migration for new column on Games
FedericoDM Feb 4, 2024
718ea1f
Adding new script for admin utils
FedericoDM Feb 4, 2024
af71ea7
Linting edits
FedericoDM Feb 4, 2024
8ce70cc
Add network visibility
FedericoDM Feb 5, 2024
fc40783
Changing script
FedericoDM Feb 7, 2024
527c437
set practice games
anisfeld Feb 8, 2024
5fb8640
requirements update during make
anisfeld Feb 8, 2024
61f0eaa
extend network diagrams (#124)
anisfeld Feb 9, 2024
de79e87
Adding new translations
FedericoDM Feb 9, 2024
6ec9dfe
Processed new translations
FedericoDM Feb 9, 2024
7ad4727
Adding network text and margins
FedericoDM Feb 9, 2024
2fc74d3
Linting and formatting
FedericoDM Feb 9, 2024
5894fea
#124 pass network language to network
anisfeld Feb 10, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions prijateli_tree/app/database.py
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,7 @@ class Game(Base):
"GameType", foreign_keys="Game.game_type_id", back_populates="games"
)
winning_score = Column(Integer, nullable=False)
is_network_visible = Column(Boolean, nullable=True, server_default="false")
players = relationship("GamePlayer", back_populates="game")
session = relationship("GameSession", back_populates="games")

Expand Down
5 changes: 4 additions & 1 deletion prijateli_tree/app/languages/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,10 @@
},
"different_game_intro": {
"different_version": "Now, we will play a different version of the game.",
"instead_of": "Instead of assigning you the peers you observe randomly, we will let you choose the players you observe."
"each_circle": "Each circle is a player: you are the green circle.",
"instead_of": "Instead of assigning you the peers you observe randomly, we will let you choose the players you observe.",
"players_connected": "Players connected by a line can see each other's guesses.",
"this_diagram": "This diagram shows who can see who in this game."
},
"final_score": {
"ask_your_teachers": "Ask your teachers about getting your reward!",
Expand Down
Binary file modified prijateli_tree/app/languages/language_6person 12242023.xlsx
Binary file not shown.
5 changes: 4 additions & 1 deletion prijateli_tree/app/languages/mk.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,10 @@
},
"different_game_intro": {
"different_version": "\u0421\u0435\u0433\u0430 \u045c\u0435 \u0438\u0433\u0440\u0430\u043c\u0435 \u0434\u0440\u0443\u0433\u0430 \u0432\u0435\u0440\u0437\u0438\u0458\u0430 \u043d\u0430 \u0438\u0433\u0440\u0430\u0442\u0430.",
"instead_of": "\u041d\u0430\u043c\u0435\u0441\u0442\u043e \u0434\u0430 \u0432\u0438 \u0433\u0438 \u0434\u043e\u0434\u0435\u043b\u0438\u043c\u0435 \u0432\u0440\u0441\u043d\u0438\u0446\u0438\u0442\u0435 \u0448\u0442\u043e \u0433\u0438 \u043d\u0430\u0431\u0459\u0443\u0434\u0443\u0432\u0430\u0442\u0435 \u043f\u043e \u0441\u043b\u0443\u0447\u0430\u0435\u043d \u0438\u0437\u0431\u043e\u0440, \u045c\u0435 \u0432\u0438 \u0434\u043e\u0437\u0432\u043e\u043b\u0438\u043c\u0435 \u0434\u0430 \u0433\u0438 \u0438\u0437\u0431\u0435\u0440\u0435\u0442\u0435 \u0438\u0433\u0440\u0430\u0447\u0438\u0442\u0435 \u0448\u0442\u043e \u0433\u0438 \u043d\u0430\u0431\u0459\u0443\u0434\u0443\u0432\u0430\u0442\u0435."
"each_circle": "\u0421\u0435\u043a\u043e\u0458 \u043a\u0440\u0443\u0433 \u0435 \u0438\u0433\u0440\u0430\u0447, \u0430 \u0442\u0438 \u0441\u0438 \u0437\u0435\u043b\u0435\u043d\u0438\u043e\u0442 \u043a\u0440\u0443\u0433.",
"instead_of": "\u041d\u0430\u043c\u0435\u0441\u0442\u043e \u0434\u0430 \u0432\u0438 \u0433\u0438 \u0434\u043e\u0434\u0435\u043b\u0438\u043c\u0435 \u0432\u0440\u0441\u043d\u0438\u0446\u0438\u0442\u0435 \u0448\u0442\u043e \u0433\u0438 \u043d\u0430\u0431\u0459\u0443\u0434\u0443\u0432\u0430\u0442\u0435 \u043f\u043e \u0441\u043b\u0443\u0447\u0430\u0435\u043d \u0438\u0437\u0431\u043e\u0440, \u045c\u0435 \u0432\u0438 \u0434\u043e\u0437\u0432\u043e\u043b\u0438\u043c\u0435 \u0434\u0430 \u0433\u0438 \u0438\u0437\u0431\u0435\u0440\u0435\u0442\u0435 \u0438\u0433\u0440\u0430\u0447\u0438\u0442\u0435 \u0448\u0442\u043e \u0433\u0438 \u043d\u0430\u0431\u0459\u0443\u0434\u0443\u0432\u0430\u0442\u0435.",
"players_connected": "\u0418\u0433\u0440\u0430\u0447\u0438\u0442\u0435 \u043f\u043e\u0432\u0440\u0437\u0430\u043d\u0438 \u0441\u043e \u043b\u0438\u043d\u0438\u0458\u0430 \u0441\u0438 \u0433\u0438 \u0433\u043b\u0435\u0434\u0430\u0430\u0442 \u0441\u0432\u043e\u0438\u0442\u0435 \u043f\u0440\u0435\u0442\u043f\u043e\u0441\u0442\u0430\u0432\u043a\u0438 \u043c\u0435\u0453\u0443 \u0441\u0435\u0431\u0435.",
"this_diagram": "\u041d\u0430 \u043e\u0432\u043e\u0458 \u0434\u0438\u0458\u0430\u0433\u0440\u0430\u043c \u043c\u043e\u0436\u0435 \u0434\u0430 \u0432\u0438\u0434\u0438\u0448 \u043a\u043e\u0458 \u043a\u043e\u0433\u043e \u0433\u043b\u0435\u0434\u0430 \u0432\u043e \u0438\u0433\u0440\u0430\u0442\u0430."
},
"final_score": {
"ask_your_teachers": "\u041f\u0440\u0430\u0448\u0430\u0458\u0442\u0435 \u0433\u0438 \u0432\u0430\u0448\u0438\u0442\u0435 \u043d\u0430\u0441\u0442\u0430\u0432\u043d\u0438\u0446\u0438 \u043e\u043a\u043e\u043b\u0443 \u0434\u043e\u0431\u0438\u0432\u0430\u045a\u0435\u0442\u043e \u043d\u0430 \u0432\u0430\u0448\u0430\u0442\u0430 \u043d\u0430\u0433\u0440\u0430\u0434\u0430!",
Expand Down
5 changes: 4 additions & 1 deletion prijateli_tree/app/languages/sq.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,10 @@
},
"different_game_intro": {
"different_version": "Tani do t\u00eb luajm\u00eb nj\u00eb version tjet\u00ebr t\u00eb loj\u00ebs",
"instead_of": "N\u00eb vend q\u00eb t'ju caktojm\u00eb n\u00eb m\u00ebnyr\u00eb t\u00eb rast\u00ebsishme bashk\u00ebmoshatar\u00ebt q\u00eb v\u00ebzhgoni, ne do t'ju lejojm\u00eb t\u00eb zgjidhni lojtar\u00ebt q\u00eb v\u00ebzhgoni."
"each_circle": "\u00c7do rreth \u00ebsht\u00eb nj\u00eb lojtar: ju jeni rrethi i gjelb\u00ebr.",
"instead_of": "N\u00eb vend q\u00eb t'ju caktojm\u00eb n\u00eb m\u00ebnyr\u00eb t\u00eb rast\u00ebsishme bashk\u00ebmoshatar\u00ebt q\u00eb v\u00ebzhgoni, ne do t'ju lejojm\u00eb t\u00eb zgjidhni lojtar\u00ebt q\u00eb v\u00ebzhgoni.",
"players_connected": "Lojtar\u00ebt e lidhur me nj\u00eb linj\u00eb mund t\u00eb shohin supozimet e nj\u00ebri-tjetrit.",
"this_diagram": "Ky diagram tregon se kush cilin mund t\u00eb shoh\u00eb n\u00eb k\u00ebt\u00eb loj\u00eb."
},
"final_score": {
"ask_your_teachers": "Pyetni m\u00ebsimdh\u00ebn\u00ebsin tuaj p\u00ebr marrjen e shp\u00ebrblimit tuaj!",
Expand Down
5 changes: 4 additions & 1 deletion prijateli_tree/app/languages/tr.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,10 @@
},
"different_game_intro": {
"different_version": "\u015eimdi oyunun farkl\u0131 bir versiyonunu oynayaca\u011f\u0131z.",
"instead_of": "G\u00f6zlemledi\u011finiz oyuncular\u0131 size rastgele atamak yerine, g\u00f6zlemledi\u011finiz oyuncular\u0131 se\u00e7menize izin verece\u011fiz."
"each_circle": "Her \u00e7ember bir oyuncudur: sen ye\u015fil \u00e7embersin.",
"instead_of": "G\u00f6zlemledi\u011finiz oyuncular\u0131 size rastgele atamak yerine, g\u00f6zlemledi\u011finiz oyuncular\u0131 se\u00e7menize izin verece\u011fiz.",
"players_connected": "Bir \u00e7izgiyle ba\u011flanan oyuncular birbirlerinin tahminlerini g\u00f6rebilir.",
"this_diagram": "Bu diyagram, bu oyunda kimin kimi g\u00f6rebildi\u011fini g\u00f6steriyor."
},
"final_score": {
"ask_your_teachers": "\u00d6\u011fretmenlerinize \u00f6d\u00fcl\u00fcn\u00fcz\u00fc nas\u0131l alaca\u011f\u0131n\u0131z\u0131 sorun!",
Expand Down
29 changes: 20 additions & 9 deletions prijateli_tree/app/routers/administration.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
SessionLocal,
User,
)
from prijateli_tree.app.utils.administration import show_network
from prijateli_tree.app.utils.constants import (
DENAR_FACTOR,
KEY_LOGIN_SECRET,
Expand Down Expand Up @@ -275,23 +276,29 @@ def create_session(

game = None
previous_game = None
network_type = [NETWORK_TYPE_INTEGRATED, NETWORK_TYPE_SEGREGATED]
random_score = random.choices(WINNING_SCORES, weights=WINNING_WEIGHTS)[0]

for i in range(NUMBER_OF_PRACTICE_GAMES):
if game:
previous_game = game

game_type_id = (
db.query(GameType)
.filter_by(
network=[NETWORK_TYPE_INTEGRATED, NETWORK_TYPE_SEGREGATED][i],
names_hidden=[True, False][i],
)
.first()
.id
)

game = Game(
created_by=user.id,
game_session_id=session.id,
game_type_id=db.query(GameType)
.filter_by(network=network_type[i], names_hidden=False)
.first()
.id,
game_type_id=game_type_id,
rounds=NUMBER_OF_ROUNDS,
practice=True,
winning_score=random_score,
winning_score=0,
is_network_visible=False,
)

db.add(game)
Expand All @@ -302,7 +309,7 @@ def create_session(
previous_game.next_game_id = game.id
db.commit()

add_players_to_first_game(lang_dict, game, session, db)
add_players_to_practice_game(lang_dict, game, session, db)

db.commit()
db.refresh(game)
Expand Down Expand Up @@ -340,6 +347,8 @@ def create_session_games(
)
.all()
)
is_network_visible = show_network()

else:
game_types = (
db.query(GameType)
Expand All @@ -349,6 +358,7 @@ def create_session_games(
)
.all()
)
is_network_visible = False

game_type = random.choice(game_types)
n_rounds = random.choice(ROUNDS_ARRAY)
Expand All @@ -364,6 +374,7 @@ def create_session_games(
game_type_id=game_type.id,
rounds=n_rounds,
winning_score=random_score,
is_network_visible=is_network_visible,
)

db.add(game)
Expand All @@ -376,7 +387,7 @@ def create_session_games(
add_players_to_game(previous_game, game, db)


def add_players_to_first_game(lang_dict, game, session, db):
def add_players_to_practice_game(lang_dict, game, session, db):
user_ids = []
session_players = []
user_lists = lang_dict.values()
Expand Down
50 changes: 48 additions & 2 deletions prijateli_tree/app/routers/games.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import glob
import json
import logging
import random
from http import HTTPStatus
from pathlib import Path
from typing import Annotated
Expand Down Expand Up @@ -135,15 +136,60 @@ def start_of_game(
"request": request,
"player_id": player_id,
"game_id": game_id,
"points": game.winning_score,
"text": template_text,
"practice_game": game.practice,
"game": {
key: game.__dict__[key]
for key in ["practice", "winning_score", "is_network_visible"]
},
"game_type": game.game_type.network,
}

return templates.TemplateResponse("start_of_game.html", result)


@router.get(
"/{game_id}/player/{player_id}/network", response_class=JSONResponse
)
def get_data_for_network(
game_id: int,
player_id: int,
db: Session = Depends(get_db),
) -> Response:
game, player = get_game_and_player(game_id, player_id, db)
template_text = languages[player.language.abbr]
is_integrated = game.game_type.network == NETWORK_TYPE_INTEGRATED

if not game.game_type.names_hidden:
players = [
{
"position": p.position,
"this_player": p.id == player_id,
"name": f"{p.user.first_name} {p.user.last_name}",
}
for p in game.players
]
else:
players = [
{
"position": p.position,
"this_player": p.id == player_id,
"name": f"{p.position}",
}
for p in game.players
]
# ensures consistent diagram on refresh
random.seed(game_id * player_id)
reverse = random.random() > 0.5
reflect = random.random() > 0.5
return {
"text": template_text,
"data": players,
"is_integrated": is_integrated,
"reverse": reverse,
"reflect": reflect,
}


@router.get("/{game_id}/player/{player_id}/round")
def view_round(
request: Request,
Expand Down
8 changes: 8 additions & 0 deletions prijateli_tree/app/static/css/custom.css
Original file line number Diff line number Diff line change
Expand Up @@ -96,3 +96,11 @@ tr.empty-table {
transform: rotate(360deg);
}
}


.tooltip {
position: absolute;
pointer-events: none;
color: #fff;

}
139 changes: 139 additions & 0 deletions prijateli_tree/app/templates/networks.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
<div id="network"></div>
<div id="network_legend">
<h3 style="margin-bottom: 20px; margin-top: 25px;">{{ text.different_game_intro.this_diagram }}</h3>
<h3 style="margin-bottom: 20px;">{{ text.different_game_intro.each_circle }}</h3>
<h3>{{ text.different_game_intro.players_connected }}</h3>
</div>
<script src="https://cdn.jsdelivr.net/npm/d3@7"></script>
<script>
// https://observablehq.com/@harrylove/draw-lines-between-circles-nodes-using-d3-links
async function build_network() {
const response = await fetch("/games/{{game_id}}/player/{{player_id}}/network");
const data = await response.json();
const width = 1000
const height = 400
const offset = 175
const v_offset = 50
const width_inner = width - 2 * offset

const svg = d3.select('#network')
.append('svg')
.attr('width', width)
.attr('height', height);


let nodes = [];


if (data.reflect) {
nodes.push([offset, v_offset]); // 0 (1)
nodes.push([offset, height - v_offset]); // 1 (2)
nodes.push([(width_inner) / 3 + offset, height / 2]); // 2 (3)
nodes.push([2 * (width_inner) / 3 + offset, height / 2 ]); // 3 (4)
nodes.push([width - offset, v_offset]); // 4 (5)
nodes.push([width - offset, (height - v_offset) ]); // 5 (6)

} else {
// reflection across 45 degree line
nodes.push([offset, v_offset]); // 0 (1)
nodes.push([width - offset, v_offset]); // 1 (2) (was 4 (5))
nodes.push([(width_inner) / 2 + offset, height / 3]); // 2 (3)
nodes.push([(width_inner) / 2 + offset, 2 * height / 3 ]); // 3 (4)
nodes.push([offset, height - v_offset]); // 4 (5) (was 1 (2))
nodes.push([width - offset, (height - v_offset) ]); // 5 (6)
}

if (data.reverse) {
nodes.reverse()
}

let connections = [[0,2], [1,2], [2,3], [3,4], [3,5]];
if (data.is_integrated) {
connections.push([1,5], [0, 4])
} else {
connections.push([1, 0], [5, 4])
}

let links = [];
for (i in connections) {
links.push(
d3.linkHorizontal()({
source: nodes[connections[i][0]],
target: nodes[connections[i][1]]
})
);
}

for (let i = 0; i < links.length; i++) {
svg
.append('path')
.attr('d', links[i])
.attr('stroke', 'white')
.attr('stroke-width', 3)
.attr('fill', 'none');
}



const Tooltip = d3.select("#network")
.append("div")
.attr("class", "tooltip")
.style("opacity", 0)
.style("background-color", "white")
.style("border", "solid")
.style("border-width", "3px")
.style("border-radius", "3px")
.style("padding", "5px")

// Three function that change the tooltip when user hover / move / leave a cell
const mouseover = function(event, d) {
Tooltip.style("opacity", 1)
}
var mousemove = function(event, d) {
Tooltip
.html(d.name )
.style('font', "16pt sans-serif")
.style('color', "black")
.style("left", (event.x) + "px")
.style("top", (event.y) - 20 + "px")
}
var mouseleave = function(event, d) {
Tooltip.style("opacity", 0)
}


data.data.forEach(element => {
i = element['position'] - 1
element['cx'] = nodes[i][0]
element['cy'] = nodes[i][1]
});

svg.selectAll('g')
.data(data.data)
.join('circle')
.attr('r', 40)
.attr('cx', function(d){
return d.cx
})
.attr('cy', function(d){
return d.cy
})
.style('fill', function(d){
if (d.this_player) {
return "lightgreen"
} else {
return "grey"
}
})
.on("mouseover", mouseover)
.on("mousemove", mousemove)
.on("mouseleave", mouseleave)



}

$(document).ready( function () {
build_network();
});
</script>
11 changes: 7 additions & 4 deletions prijateli_tree/app/templates/start_of_game.html
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
<div class="container-sm general-container col-md-8 offset-md-2">
<div class="pb-4">
<h1>
{% if practice_game %}
{% if game.practice %}
{{ text.intro_practice.now_you_will_play }}
{% elif game_type == "self-selected" %}
{{ text.different_game_intro.different_version }}
Expand All @@ -16,13 +16,16 @@ <h1>
{% endif %}
</h1>
</div>
<h3>
{% if practice_game %}
<h3 style="margin-bottom: 20px;">
{% if game.practice %}
{{ text.intro_practice.since_these_rules }}
{% else %}
{{ text.real_game_intro.added_points | replace('{X}', points) }}
{{ text.real_game_intro.added_points | replace('{X}', game.winning_score) }}
{% endif %}
</h3>
{% if game.is_network_visible %}
{% include "networks.html" %}
{% endif %}
<div class="p-5">
<form method="get"
action="{{ url_for('view_round', player_id=player_id, game_id=game_id) }}">
Expand Down
Loading
Loading