Skip to content

Commit

Permalink
Merge pull request #93 from Danielsio/profile_page
Browse files Browse the repository at this point in the history
Add Profile-Page
  • Loading branch information
yftacherzog authored May 31, 2023
2 parents 213d7ac + 6b4e676 commit 8b1c394
Show file tree
Hide file tree
Showing 10 changed files with 369 additions and 1 deletion.
24 changes: 24 additions & 0 deletions conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@
from notification.models import Notification, NotificationType
from django.utils import timezone
from decimal import Decimal
from player_rating.models import PlayerRating
from game_event.models import GameEvent
from game_event_player.models import GameEventPlayer
import pytest


Expand All @@ -17,6 +20,11 @@ def player():
return player


@pytest.fixture
def player_rating(player):
return PlayerRating.objects.create(ball_game="Basketball", player=player, rating=5)


@pytest.fixture
def court():
court = Court.objects.create(x=Decimal('11'), y=Decimal('22'),
Expand All @@ -39,3 +47,19 @@ def notification(player):
notification_type=NotificationType.WEBSITE,
is_read=False
)


@pytest.fixture
def game_event(court, court_ball_game):
return GameEvent.objects.create(
id=20,
time=timezone.now(),
level_of_game=5,
min_number_of_players=3,
max_number_of_players=5,
court=court, ball_game="Basketball")


@pytest.fixture
def game_event_player(game_event, player):
return GameEventPlayer.objects.create(game_event=game_event, player=player, ball_responsible=False)
4 changes: 4 additions & 0 deletions meet_balls_app/templates/base.html
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
rel="stylesheet"
/>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.bundle.min.js"></script>
{% block scripts %} {% endblock %}
<link rel="shortcut icon" type="image/x-icon" href="{% static 'images/favicon.ico' %}">
<link href="/static/css/styles.css" rel="stylesheet" />
</head>
Expand Down Expand Up @@ -40,6 +41,9 @@
<a class="nav-link" href="/about">About</a>
</li>
{% if user.is_authenticated %}
<li class="nav-item">
<a class="nav-link" href="/profile/{{ user.id }}">Profile</a>
</li>
<li class="nav-item">
<a class="nav-link" href="/logout">Logout</a>
</li>
Expand Down
50 changes: 50 additions & 0 deletions meet_balls_app/templates/meet_balls_app/edit_profile_form.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
{% extends 'base.html' %}

{% block content %}
<div style="width: 40%; margin: 0 auto;">

<h1>Edit Player Profile</h1>
<form method="post">
{% csrf_token %}

<div class="form-group">
<label for="id_birth_date">Birth Date</label>
<input type="date" class="form-control" id="id_birth_date" name="birth_date" value="{{ player.birth_date|date:'Y-m-d' }}">
</div>

<div class="form-group">
<label for="id_favorite_ball_game">Favorite Ball Game</label>
<select class="form-control" id="id_favorite_ball_game" name="favorite_ball_game">
{% for ball_game in ball_games %}
<option value="{{ ball_game.0 }}" {% if player.favorite_ball_game == ball_game.0 %}selected{% endif %}>{{ ball_game.0 }}</option>
{% endfor %}
</select>
</div>

<h2>Player Ratings</h2>
{% for rating in ratings %}
<div class="form-group">
<label for="id_rating_{{ rating.id }}">Rating for {{ rating.ball_game }}</label>
<input type="number" min="0" max="10" class="form-control" id="id_rating_{{ rating.id }}" name="rating[{{ rating.id }}]" value="{{ rating.rating }}" required>
</div>
{% endfor %}
<button onclick="location.href='{% url 'profile' player.user.id %}'" class="round-button mt-3">Cancel</button>
<button type="submit" class="round-button">Save Changes</button>
</form>

{% if messages %}
<div class="row my-4">
<div class="col">
<div class="alert alert-danger" role="alert">
<ul class="messages">
{% for message in messages %}
<li{% if message.tags %} class="{{ message.tags }}"{% endif %}>{{ message }}</li>
{% endfor %}
</ul>
</div>
</div>
</div>
{% endif %}
</div>

{% endblock %}
105 changes: 105 additions & 0 deletions meet_balls_app/templates/meet_balls_app/profile.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
{% extends 'base.html' %} {% block content %}
{% if player %}
<div class="text-center">
<h2>Profile Information</h2>
<p>Name: {{ player.user.first_name }} {{ player.user.last_name }}</p>
<p>Date of Birth: {{ player.birth_date }}</p>
<p>Favorite Ball Game: {{ player.favorite_ball_game }}</p>
</div>
<h2 class="text-center">Player Ratings</h2>
<div class="container">
<div class="row justify-content-center">
<div class="col-md-5">
<table class="table table-striped table-bordered">
<thead class="thead-dark">
<tr>
<th>Ball Game</th>
<th>Rating</th>
</tr>
</thead>
<tbody>
{% for rating in ratings %}
<tr>
<td>{{ rating.ball_game }}</td>
<td>{{ rating.rating }}</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
</div>

<div class="text-center">
{% if user.id|stringformat:"s" == url_id %}
<div>
<button onclick="location.href='{% url 'edit profile' %}'" class="round-button">Edit Profile</button>
</div>
{% endif %}
</div>

<h2 class="text-center">Game History</h2>
<div>
<canvas id="gameHistoryChart"></canvas>
</div>
{% block scripts %}
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<script>
document.addEventListener("DOMContentLoaded", function() {
var gameHistoryData = {
labels: [
{% for ball_game, count in game_history_data.items %}
"{{ ball_game }}",
{% endfor %}
],
datasets: [{
data: [
{% for ball_game, count in game_history_data.items %}
{{ count }},
{% endfor %}
],
backgroundColor: [
'rgba(255, 99, 132, 0.6)',
'rgba(54, 162, 235, 0.6)',
'rgba(255, 206, 86, 0.6)',
'rgba(75, 192, 192, 0.6)',
'rgba(153, 102, 255, 0.6)',
'rgba(255, 159, 64, 0.6)',
'rgba(255, 99, 132, 0.6)',
'rgba(54, 162, 235, 0.6)',
],
borderWidth: 1
}]
};

var gameHistoryChart = new Chart(document.getElementById("gameHistoryChart"), {
type: 'pie',
data: gameHistoryData,
options: {
responsive: true,
maintainAspectRatio: false,
plugins: {
legend: {
position: 'bottom',
},
},
layout: {
padding: {
left: 0,
right: 0,
top: 20,
bottom: 20,
},
},
},
});
});
</script>
{% endblock %}
{% else %}
<div class="text-center">
<h2>Player Not Found</h2>
<p>The requested player does not exist.</p>
</div>
{% endif %}
{% endblock %}
82 changes: 82 additions & 0 deletions meet_balls_app/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import pytest
from django.urls import reverse
from django.utils import timezone
from player.models import BallGame


class TestUi:
Expand Down Expand Up @@ -195,3 +196,84 @@ def test_login_page_navigation(self, client):
response = client.get(login_url)
assert response.status_code == 200
assert response.request['PATH_INFO'] == '/login/'


@pytest.mark.django_db
class TestProfile:

def test_profile_view(self, client, player, player_rating, court, court_ball_game, game_event, game_event_player):
url = reverse('profile', kwargs={'id': player.user.id})
client.force_login(player.user)
response = client.get(url)
assert response.status_code == 200
assert player.user.username in response.content.decode()
assert player.favorite_ball_game in response.content.decode()
assert '<td>' + str(player_rating.rating) + '</td>' in response.content.decode()
assert '<td>' + str(game_event.ball_game) + '</td>' in response.content.decode()
assert b'<button onclick' in response.content

def test_profile_view_edit_button_not_displayed(self, client, player):
not_player_user_id = 5
url = reverse('profile', kwargs={'id': not_player_user_id})
client.force_login(player.user)
response = client.get(url)
assert response.status_code == 200
assert b'<button onclick' not in response.content

def test_profile_view_player_not_found(self, client, player):
non_existing_player_id = 1234567890
url = reverse('profile', kwargs={'id': non_existing_player_id})
client.force_login(player.user)
response = client.get(url)
assert response.status_code == 200
assert 'Player Not Found' in response.content.decode()
assert 'The requested player does not exist.' in response.content.decode()

def test_display_edit_profile_form(self, client, player):
url = reverse('edit profile')
client.force_login(player.user)
response = client.get(url)
assert response.status_code == 200
assert 'Edit Player Profile' in response.content.decode()

def test_edit_profile_post_invalid(self, client, player):
url = reverse('edit profile')
client.force_login(player.user)
initial_birth_date = player.birth_date
initial_favorite_ball_game = player.favorite_ball_game

data = {
'birth_date': 'invalid_date',
'favorite_ball_game': 'Basketball',
}
assert player.favorite_ball_game != BallGame.Basketball
assert player.birth_date != "invalid_date"
response = client.post(url, data)
assert response.status_code == 302
assert response.url == reverse('edit profile')

player.refresh_from_db()

assert player.birth_date == datetime.datetime.strptime(initial_birth_date, '%Y-%m-%d').date()
assert player.favorite_ball_game == initial_favorite_ball_game

def test_edit_profile_post_valid(self, client, player):
url = reverse('edit profile')
client.force_login(player.user)

data = {
'birth_date': '2000-02-02',
'favorite_ball_game': 'Tennis',
}
expected_birth_date = datetime.datetime.strptime('2000-02-02', '%Y-%m-%d').date()
assert player.birth_date != expected_birth_date
assert player.favorite_ball_game != BallGame.Tennis

response = client.post(url, data)
assert response.status_code == 302
assert response.url == reverse('profile', kwargs={'id': player.user.id})

player.refresh_from_db()

assert player.birth_date == expected_birth_date
assert player.favorite_ball_game == BallGame.Tennis
48 changes: 47 additions & 1 deletion meet_balls_app/views.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
from django.shortcuts import render, redirect
from datetime import datetime
from django.core.exceptions import ValidationError
from django.shortcuts import render, redirect
from django.contrib.auth import authenticate, logout, login
from django.contrib import messages
from player.models import BallGame, Player
from django.contrib.auth.models import User
from player_rating.models import PlayerRating
from game_event_player.models import GameEventPlayer
from django.db.models import Count
from django.contrib.auth.decorators import login_required


def home(request):
Expand Down Expand Up @@ -98,3 +102,45 @@ def validate_register_form(data):
errors.append("Invalid birthdate format.")

return errors


@login_required(login_url="/login/")
def profile(request, id):
try:
player = Player.objects.get(user=id)
except Player.DoesNotExist:
return render(request, 'meet_balls_app/profile.html', {})
ratings = PlayerRating.objects.filter(player=player)

game_counts = GameEventPlayer.objects.filter(player=player).values('game_event__ball_game').annotate(
count=Count('game_event__ball_game'))
game_history_data = {item['game_event__ball_game']: item['count'] for item in game_counts}

return render(request, 'meet_balls_app/profile.html',
{'player': player, 'ratings': ratings, 'game_history_data': game_history_data, 'url_id': id})


@login_required(login_url="/login/")
def edit_profile(request):
player = Player.objects.get(user=request.user.id)
ratings = PlayerRating.objects.filter(player=player)
if request.method == 'POST':
birth_date = request.POST.get('birth_date')
favorite_ball_game = request.POST.get('favorite_ball_game')
try:
player.validate_and_save(birth_date, favorite_ball_game)
for rating in ratings:
rating_value = request.POST.get(f"rating[{rating.id}]")
rating.rating = int(rating_value)
rating.save()
return redirect('profile', id=player.user.id)
except ValidationError as e:
error_messages = e.messages[0].split("\n")
for error in error_messages:
messages.error(request, error)
return redirect("edit profile")
else:
return render(request, 'meet_balls_app/edit_profile_form.html', {
"player": player,
"ratings": ratings,
"ball_games": BallGame.choices})
3 changes: 3 additions & 0 deletions meetballs/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,4 +27,7 @@
path('login/', app_views.loginUser, name='loginUser'),
path('logout/', app_views.logoutUser, name='logoutUser'),
path('register/', app_views.registerUser, name='registerUser'),
path('profile/<id>/', app_views.profile, name='profile'),
path('edit-profile/', app_views.edit_profile, name='edit profile'),

] + static(settings.STATIC_URL, document_root=settings.STATIC_ROOT)
Loading

0 comments on commit 8b1c394

Please sign in to comment.