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

Implementing self-selected game #144

Merged
merged 52 commits into from
Jan 30, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
52 commits
Select commit Hold shift + click to select a range
0c24fe3
First attempt at self-selected
FedericoDM Jan 10, 2024
2d68382
Adding players to game
FedericoDM Jan 10, 2024
0ffd0f9
Linting
FedericoDM Jan 13, 2024
9f019d0
Merge branch 'main' of github.com:prijatelilab/PrijateliTree into sel…
FedericoDM Jan 13, 2024
7e2c05b
Adding self selected constant
FedericoDM Jan 14, 2024
fcd2688
Filtering for when names are not hidden
FedericoDM Jan 14, 2024
5bdf1a1
Some changes for new table
FedericoDM Jan 15, 2024
962de79
Adding relationships
FedericoDM Jan 15, 2024
aa84c95
Slight typo
FedericoDM Jan 15, 2024
7051948
Linting
FedericoDM Jan 15, 2024
aafe62b
Migration file added
FedericoDM Jan 15, 2024
dcab547
Linting and adding template for function
FedericoDM Jan 15, 2024
fa0691d
Adding base html template for choosing neighbors
FedericoDM Jan 15, 2024
cd774bb
Progress in form
FedericoDM Jan 17, 2024
6778ef4
Progress in both template and route
FedericoDM Jan 17, 2024
88d1462
If condition in template
FedericoDM Jan 17, 2024
0101ec8
URL typo fix
FedericoDM Jan 17, 2024
a6e5a15
Reformat
FedericoDM Jan 17, 2024
e7c8bee
Adding bottom margin
FedericoDM Jan 17, 2024
13a80a9
Trying to get form to display student names
FedericoDM Jan 17, 2024
75436e8
Games show succesfully
FedericoDM Jan 17, 2024
0438201
Temporary place-holder for route
FedericoDM Jan 17, 2024
7240aa7
Trying to get post to work
FedericoDM Jan 17, 2024
e8f4216
First attempt to add helper script
FedericoDM Jan 17, 2024
e9af871
Post now works
FedericoDM Jan 17, 2024
02ea70d
Testing to see if neighbors are added to the new table
FedericoDM Jan 17, 2024
a7431d5
Testing endpoint
FedericoDM Jan 17, 2024
2b0d246
Filtering by game
FedericoDM Jan 17, 2024
a915389
Adding to DB
FedericoDM Jan 17, 2024
6ad7b61
Post succesful to db
FedericoDM Jan 17, 2024
d5d4b56
When choosing neighbors, redirect to waiting page
FedericoDM Jan 17, 2024
9e7f294
Condition for self-selected game
FedericoDM Jan 17, 2024
cea7b23
Adding template for self-selected-intro
FedericoDM Jan 17, 2024
5e08420
Condition to redirect
FedericoDM Jan 17, 2024
fcdf453
edits
FedericoDM Jan 17, 2024
372ef1d
Aesthetic edits
FedericoDM Jan 17, 2024
b426fc4
Querying network
FedericoDM Jan 18, 2024
17ed5d3
Pending to work on getting answers and names from neighbors
FedericoDM Jan 18, 2024
7210052
Adding mechanism to get previous answers
FedericoDM Jan 19, 2024
92f09f2
Adding javascript for error logging
FedericoDM Jan 19, 2024
e773a2d
Correct redirect to route and linting
FedericoDM Jan 19, 2024
cbab967
Removing own student from possible neighbors
FedericoDM Jan 19, 2024
33caab2
Adding condition to advance to round after choosing neighbors
FedericoDM Jan 19, 2024
4a79641
Neighbors' answers displayed correctly
FedericoDM Jan 19, 2024
56922e8
Merge branch 'main' of github.com:prijatelilab/PrijateliTree into sel…
FedericoDM Jan 19, 2024
0f69aa7
Linting
FedericoDM Jan 19, 2024
c30e8af
Linting
FedericoDM Jan 19, 2024
87dc5f9
Merging main into self-selected-game
FedericoDM Jan 21, 2024
185a5e6
refactor code, change order of self-selected game
anisfeld Jan 28, 2024
476446d
Merge branch 'main' into self-selected-game
anisfeld Jan 28, 2024
5eef785
lint
anisfeld Jan 28, 2024
6c2652f
we need real_game_transition
anisfeld Jan 30, 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
25 changes: 25 additions & 0 deletions prijateli_tree/app/database.py
Original file line number Diff line number Diff line change
Expand Up @@ -392,3 +392,28 @@ class GameSessionPlayer(Base):
def language(self, db: Session = next(get_db())):
user = db.query(User).filter_by(id=self.user_id).one()
return db.query(Language).filter_by(id=user.language_id).one()


class PlayerNetwork(Base):
__tablename__ = "player_networks"
id = Column(Integer, Identity(start=1, cycle=True), primary_key=True)
game_id = Column(
Integer,
ForeignKey("games.id", name="game_players_game_id_fkey"),
nullable=False,
)
player_id = Column(
Integer,
ForeignKey("game_players.id", name="game_players_player_id_fkey"),
nullable=False,
)
neighbor_id = Column(
Integer,
ForeignKey("game_players.id", name="game_players_neighbor_id_fkey"),
nullable=False,
)

player = relationship("GamePlayer", foreign_keys="PlayerNetwork.player_id")
neighbor = relationship(
"GamePlayer", foreign_keys="PlayerNetwork.neighbor_id"
)
56 changes: 56 additions & 0 deletions prijateli_tree/app/routers/administration.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
KEY_LOGIN_SECRET,
NETWORK_TYPE_INTEGRATED,
NETWORK_TYPE_SEGREGATED,
NETWORK_TYPE_SELF_SELECTED,
NUMBER_OF_GAMES,
NUMBER_OF_ROUNDS,
ROLE_ADMIN,
Expand Down Expand Up @@ -384,6 +385,61 @@ def create_session_games(
add_players_to_game(pos_players, game, db)


def create_self_selected_games(
session,
game,
db: Session = Depends(get_db),
) -> None:
"""
Creates a self-selected game
"""
for index in range(session.num_games):
previous_game = game
game_types = (
db.query(GameType)
.filter(
GameType.network.in_([NETWORK_TYPE_SELF_SELECTED]),
GameType.names_hidden.is_(False),
)
.all()
)
game_type = random.choice(game_types)
n_rounds = random.choice(ROUNDS_ARRAY)

# Add score
random_score = random.choices(WINNING_SCORES, weights=WINNING_WEIGHTS)[
0
]

game = Game(
created_by=session.created_by,
game_session_id=session.id,
game_type_id=game_type.id,
rounds=n_rounds,
winning_score=random_score,
)

db.add(game)
db.commit()
db.refresh(game)

previous_game.next_game_id = game.id
db.commit()

# Sommething missing here about the network

# randomize location in network conditional on language
# one group is always in 1-2-3 and the other 4-5-6
group_1 = [p for p in previous_game.players if p.position in (1, 2, 3)]
random.shuffle(group_1)

group_2 = [p for p in previous_game.players if p.position in (4, 5, 6)]
random.shuffle(group_2)

pos_players = group_1 + group_2
add_players_to_game(pos_players, game, db)


def add_players_to_game(pos_players: list[GamePlayer], game, db) -> None:
""" """
rand_bag = random.sample(game.game_type.bag, len(game.game_type.bag))
Expand Down
124 changes: 113 additions & 11 deletions prijateli_tree/app/routers/games.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import logging
from http import HTTPStatus
from pathlib import Path
from typing import Annotated

from fastapi import APIRouter, Depends, Form, HTTPException, Request, Response
from fastapi.responses import HTMLResponse, JSONResponse, RedirectResponse
Expand All @@ -14,16 +15,22 @@
GameAnswer,
GamePlayer,
GameSessionPlayer,
PlayerNetwork,
User,
get_db,
)
from prijateli_tree.app.utils.constants import (
DENAR_FACTOR,
FILE_MODE_READ,
NETWORK_TYPE_INTEGRATED,
NETWORK_TYPE_SELF_SELECTED,
POST_SURVEY_LINK,
PRE_SURVEY_LINK,
STANDARD_ENCODING,
)
from prijateli_tree.app.utils.games import (
GameUtil,
check_if_neighbors,
did_player_win,
get_bag_color,
get_current_round,
Expand Down Expand Up @@ -131,6 +138,7 @@ def start_of_game(
"points": game.winning_score,
"text": template_text,
"practice_game": game.practice,
"game_type": game.game_type.network,
}

return templates.TemplateResponse("start_of_game.html", result)
Expand Down Expand Up @@ -163,6 +171,15 @@ def view_round(
}
# Get current round
if current_round == 1:
if game.game_type.network == NETWORK_TYPE_SELF_SELECTED:
neighbors_exist = check_if_neighbors(player_id, db)
if not neighbors_exist:
redirect_url = request.url_for(
"choose_neighbors", game_id=game_id, player_id=player_id
)
return RedirectResponse(
url=redirect_url, status_code=HTTPStatus.FOUND
)
template_data["ball"] = player.initial_ball
elif current_round > game.rounds:
redirect_url = request.url_for(
Expand All @@ -179,6 +196,100 @@ def view_round(
)


@router.get("/{game_id}/player/{player_id}/choose_neighbors")
def choose_neighbors(
request: Request,
game_id: int,
player_id: int,
db: Session = Depends(get_db),
) -> Response:
"""
Function that will allow each player to choose their neighbors
"""
game, player = get_game_and_player(game_id, player_id, db)
template_text = languages[get_lang_from_player_id(player_id, db)]
header = get_header_data(player, db)

# Get number of neighbors the player has
# We just use the integrated network to get the number of neighbors
game_util = GameUtil(NETWORK_TYPE_INTEGRATED)
num_neighbors = len(game_util.neighbors[player.position])

# Get the users of the players
user_ids = [p.user_id for p in game.players]

# Remove the current player from the list
user_ids.remove(player.user_id)

# Query users
users = db.query(User).filter(User.id.in_(user_ids)).all()

template_data = {
"text": template_text,
"player_id": player_id,
"game_id": game_id,
"num_neighbors": num_neighbors,
"students": users,
**header,
}

return templates.TemplateResponse(
"choose_neighbors.html", {"request": request, **template_data}
)


@router.post("/{game_id}/player/{player_id}/add_neighbors")
def add_neighbors(
request: Request,
game_id: int,
player_id: int,
player_one: Annotated[int, Form()],
player_two: Annotated[int, Form()],
player_three: Annotated[int, Form()] | None = Form(None),
db: Session = Depends(get_db),
) -> RedirectResponse:
"""
Function that will add the neighbors to the database
"""
# Get the players
neighbor_player_ids = []
for user_id in [player_one, player_two, player_three]:
if user_id is not None:
neighbor_player_ids.append(
db.query(GamePlayer)
.filter_by(user_id=user_id, game_id=game_id)
.one_or_none()
.id
)

if len(set(neighbor_player_ids)) != len(neighbor_player_ids):
# Do not let them move forward if neighbor is duplicated
redirect_url = request.url_for(
"choose_neighbors", game_id=game_id, player_id=player_id
)

return RedirectResponse(
url=redirect_url,
status_code=HTTPStatus.FOUND,
)

for neighbor_id in neighbor_player_ids:
new_neighbor = PlayerNetwork(
game_id=game_id,
player_id=player_id,
neighbor_id=neighbor_id,
)
db.add(new_neighbor)
db.commit()
db.refresh(new_neighbor)

redirect_url = request.url_for(
"view_round", game_id=game_id, player_id=player_id
)

return RedirectResponse(url=redirect_url, status_code=HTTPStatus.SEE_OTHER)


@router.post("/{game_id}/player/{player_id}/answer")
def route_add_answer(
request: Request,
Expand Down Expand Up @@ -424,7 +535,6 @@ def go_to_next_game(
game, player = get_game_and_player(game_id, player_id, db)

if game.next_game_id is None:
# TODO: end of session screen
redirect_url = request.url_for(
"get_qualtrics", game_id=game_id, player_id=player_id
)
Expand All @@ -445,7 +555,6 @@ def go_to_next_game(
if game.practice:
# Check if next game is practice
next_game = db.query(Game).filter_by(id=game.next_game_id).one()
# If next game is NOT practice
if not next_game.practice:
# Show end of practice screen
redirect_url = request.url_for(
Expand All @@ -454,12 +563,6 @@ def go_to_next_game(
player_id=next_player_id,
)

return RedirectResponse(
redirect_url,
status_code=HTTPStatus.FOUND,
)

# next_player_id
redirect_url = request.url_for(
"start_of_game",
game_id=game.next_game_id,
Expand All @@ -486,21 +589,20 @@ def real_game_transition(
Function that returns the start of game page and
template.
"""
game, player = get_game_and_player(game_id, player_id, db)
_, player = get_game_and_player(game_id, player_id, db)
header = get_header_data(player, db)
template_text = languages[get_lang_from_player_id(player_id, db)]

result = {
"request": request,
"player_id": player_id,
"game_id": game_id,
"points": game.winning_score,
"text": template_text,
"completed_game": True,
**header,
}

return templates.TemplateResponse("real_game_transition.html", result)
return templates.TemplateResponse("self_selected_intro.html", result)


@router.get(
Expand Down
74 changes: 74 additions & 0 deletions prijateli_tree/app/templates/choose_neighbors.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
{% extends "base.html" %}
{% block title %}
PrijateliTree - Choose Neighbors
{% endblock title %}
{% block helper_script %}
<script>
fetch('{{ url_for("create_session") }}', {
method: 'POST',
body: formData
})
.then(resp => {
window.location.href = resp.url
}) // or, resp.text(), etc
.catch(error => {
console.error(error);
});
function cleanParams() {
// Used to clear the URL of parameters after the banner alert is closed.
window.history.replaceState(null, '', window.location.pathname);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Make a future issue called "Display errors to users during self-selected network". Include notes about the challenges you faced and our goal for the issue.

}
</script>
{% endblock helper_script %}
{% block content %}
{% if error %}
<div class="alert alert-danger alert-dismissible fade show" role="alert">
<strong>ERROR:</strong> {{ error }}
<button type="button"
class="btn-close"
data-bs-dismiss="alert"
aria-label="Close"
onclick="cleanParams()"></button>
</div>
{% endif %}
<div class="container-sm general-container">
<h3 style="margin-bottom: 40px;">
{{ text.different_game_choices.please_choose_players | replace('{X}', num_neighbors) }}
</h3>
<div class="row justify-content-center">
<!-- Added row and justify-content-center classes -->
<div class="col-sm-4">
<form method="post"
action='{{ url_for("add_neighbors", game_id=game_id, player_id=player_id) }}'>
<div class="mb-3 row">
<label for="pos-one" class="col-sm-4 form-label">{{ text.util.player }} 1</label>
<div class="col-sm-8">
<select id="pos-one" class="form-select form-select-sm" name="player_one">
{% for s in students|sort(attribute='last_name') %}<option value="{{ s.id }}">{{ s.name_str }}</option>{% endfor %}
</select>
</div>
</div>
<div class="mb-3 row">
<label for="pos-two" class="col-sm-4 form-label">{{ text.util.player }} 2</label>
<div class="col-sm-8">
<select id="pos-two" class="form-select form-select-sm" name="player_two">
{% for s in students|sort(attribute='last_name') %}<option value="{{ s.id }}">{{ s.name_str }}</option>{% endfor %}
</select>
</div>
</div>
{% if num_neighbors > 2 %}
<div class="mb-3 row">
<label for="pos-three" class="col-sm-4 form-label">{{ text.util.player }} 3</label>
<div class="col-sm-8">
<select id="pos-three" class="form-select form-select-sm" name="player_three">
{% for s in students|sort(attribute='last_name') %}<option value="{{ s.id }}">{{ s.name_str }}</option>{% endfor %}
</select>
</div>
</div>
{% endif %}
<button type="submit" class="btn btn-primary float-end">{{ text.ready_to_play.im_ready }}</button>
</form>
</div>
</div>
</div>
{% endblock content %}
3 changes: 3 additions & 0 deletions prijateli_tree/app/templates/start_of_game.html
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@
<h1>
{% if practice_game %}
{{ text.intro_practice.now_you_will_play }}
{% elif game_type == "self-selected" %}
{{ text.different_game_intro.different_version }}
{{ text.different_game_intro.instead_of }}
{% else %}
{{ text.ready_to_play.get_ready }}
{% endif %}
Expand Down
Loading
Loading