diff --git a/accounts/locale/fr/LC_MESSAGES/django.po b/accounts/locale/fr/LC_MESSAGES/django.po
index eed9cf2..73661a4 100644
--- a/accounts/locale/fr/LC_MESSAGES/django.po
+++ b/accounts/locale/fr/LC_MESSAGES/django.po
@@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2024-02-01 13:59+0100\n"
+"POT-Creation-Date: 2024-03-11 11:02+0100\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME \n"
"Language-Team: LANGUAGE \n"
@@ -17,6 +17,11 @@ msgstr ""
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n > 1);\n"
+
+#: serializers/update_user.py:15
+msgid "You dont have permission for this user."
+msgstr "Vous n'avez pas de permissions pour cet utilisateur."
+
#: views/login.py:22
msgid "Invalid username or password."
msgstr "Nom d'utilisateur ou mot de passe incorect."
diff --git a/accounts/serializers/update_user.py b/accounts/serializers/update_user.py
new file mode 100644
index 0000000..25c91f5
--- /dev/null
+++ b/accounts/serializers/update_user.py
@@ -0,0 +1,20 @@
+from rest_framework.serializers import ModelSerializer, ValidationError
+from django.contrib.auth.models import User
+from django.utils.translation import gettext as _
+
+
+class UpdateUserSerializer(ModelSerializer):
+ class Meta:
+ model = User
+ fields = ['username']
+
+ def update(self, instance, validated_data):
+ user = self.context['request'].user
+
+ if user.pk != instance.pk:
+ raise ValidationError({'authorize': _('You dont have permission for this user.')})
+
+ instance.username = validated_data.get('username', instance.username)
+
+ instance.save()
+ return instance
diff --git a/accounts/urls.py b/accounts/urls.py
index 73ae6d7..dcac548 100644
--- a/accounts/urls.py
+++ b/accounts/urls.py
@@ -1,6 +1,6 @@
from django.urls import path
-from .views import register, login, logout, delete, edit, logged
+from .views import register, login, logout, delete, logged, update_profile
urlpatterns = [
path("register", register.RegisterView.as_view(), name="register"),
@@ -8,6 +8,5 @@ urlpatterns = [
path("logout", logout.LogoutView.as_view(), name="logout"),
path("logged", logged.LoggedView.as_view(), name="logged"),
path("delete", delete.DeleteView.as_view(), name="delete"),
- path("edit", edit.EditView.as_view(), name="change_password")
-
-]
\ No newline at end of file
+ path('update_profile', update_profile.UpdateProfileView.as_view(), name='update_profile')
+]
diff --git a/accounts/views/delete.py b/accounts/views/delete.py
index 1333f7e..66fd084 100644
--- a/accounts/views/delete.py
+++ b/accounts/views/delete.py
@@ -5,17 +5,18 @@ from django.contrib.auth import logout
from django.http import HttpRequest
from rest_framework.authentication import SessionAuthentication
+
class DeleteView(APIView):
permission_classes = (permissions.IsAuthenticated,)
authentication_classes = (SessionAuthentication,)
+
def delete(self, request: HttpRequest):
data: dict = request.data
password: str = data["password"]
- if (password is None):
- return Response({"password": ["This field may not be blank."]})
- if (request.user.check_password(password) == False):
- return Response({"password": ["Password wrong."]})
+ if (request.user.check_password(password) is False):
+ return Response({"password": ["Password incorrect."]},
+ status.HTTP_401_UNAUTHORIZED)
request.user.delete()
logout(request)
- return Response("user deleted", status=status.HTTP_200_OK)
\ No newline at end of file
+ return Response(status=status.HTTP_200_OK)
diff --git a/accounts/views/edit.py b/accounts/views/edit.py
deleted file mode 100644
index c62a201..0000000
--- a/accounts/views/edit.py
+++ /dev/null
@@ -1,45 +0,0 @@
-from rest_framework.views import APIView
-from rest_framework.response import Response
-from rest_framework import permissions, status
-from django.http import HttpRequest
-from django.contrib.auth import login
-from rest_framework.authentication import SessionAuthentication
-from django.contrib.auth.models import User
-import re
-
-class EditView(APIView):
-
- permission_classes = (permissions.IsAuthenticated,)
- authentication_classes = (SessionAuthentication,)
-
- def get(self, request: HttpRequest):
- return Response({"username": request.user.username, "id": request.user.pk})
-
- def patch(self, request: HttpRequest):
- data: dict = request.data
-
- current_password: str = data.get("current_password")
- if (current_password is None):
- return Response({"current_password": ["This field may not be blank."]})
-
- user_object = request.user
-
- if (user_object.check_password(current_password) == False):
- return Response({"current_password": ["Password is wrong."]})
-
- new_username = data.get("username", user_object.username)
- if (new_username != user_object.username):
- if (User.objects.filter(username=new_username).exists()):
- return Response({"username": ["A user with that username already exists."]})
- if (set(new_username) == {' '}):
- return Response({"username": ["This field may not be blank."]})
- if (re.search('^([a-z]||\@||\+||\-||\_)+$', new_username) is None):
- return Response({"username":["Enter a valid username. This value may contain only letters, numbers, and @/./+/-/_ characters."]})
-
- new_password: str = data.get("password")
- if (new_password is not None):
- user_object.set_password(new_password)
-
- user_object.save()
-
- return Response("data has been alterate")
\ No newline at end of file
diff --git a/accounts/views/logged.py b/accounts/views/logged.py
index b67abe4..ff7f54d 100644
--- a/accounts/views/logged.py
+++ b/accounts/views/logged.py
@@ -2,15 +2,13 @@ from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import permissions, status
from django.http import HttpRequest
-from django.contrib.auth import login
from rest_framework.authentication import SessionAuthentication
-from ..serializers.login import LoginSerializer
class LoggedView(APIView):
-
+
permission_classes = (permissions.AllowAny,)
authentication_classes = (SessionAuthentication,)
def get(self, request: HttpRequest):
- return Response(status = (status.HTTP_200_OK if request.user.is_authenticated else status.HTTP_400_BAD_REQUEST))
+ return Response(status=status.HTTP_200_OK if request.user.is_authenticated else status.HTTP_400_BAD_REQUEST)
diff --git a/accounts/views/login.py b/accounts/views/login.py
index d981f41..0f10c7a 100644
--- a/accounts/views/login.py
+++ b/accounts/views/login.py
@@ -8,8 +8,9 @@ from django.utils.translation import gettext as _
from ..serializers.login import LoginSerializer
+
class LoginView(APIView):
-
+
permission_classes = (permissions.AllowAny,)
authentication_classes = (SessionAuthentication,)
diff --git a/accounts/views/logout.py b/accounts/views/logout.py
index cee8868..f4d7ad1 100644
--- a/accounts/views/logout.py
+++ b/accounts/views/logout.py
@@ -5,9 +5,11 @@ from rest_framework.response import Response
from django.http import HttpRequest
from rest_framework.authentication import SessionAuthentication
+
class LogoutView(APIView):
permission_classes = (permissions.IsAuthenticated,)
authentication_classes = (SessionAuthentication,)
+
def get(self, request: HttpRequest):
logout(request)
- return Response("user unlogged", status=status.HTTP_200_OK)
\ No newline at end of file
+ return Response("user logged out", status.HTTP_200_OK)
diff --git a/accounts/views/register.py b/accounts/views/register.py
index 903609a..926a560 100644
--- a/accounts/views/register.py
+++ b/accounts/views/register.py
@@ -5,8 +5,10 @@ from rest_framework.response import Response
from django.http import HttpRequest
from django.contrib.auth import login
+
class RegisterView(APIView):
permission_classes = (permissions.AllowAny,)
+
def post(self, request: HttpRequest):
data = request.data
serializer = RegisterSerialiser(data=data)
diff --git a/accounts/views/update_profile.py b/accounts/views/update_profile.py
new file mode 100644
index 0000000..eca35f0
--- /dev/null
+++ b/accounts/views/update_profile.py
@@ -0,0 +1,14 @@
+from ..serializers.update_user import UpdateUserSerializer
+from rest_framework.generics import UpdateAPIView
+from rest_framework.permissions import IsAuthenticated
+from django.contrib.auth.models import User
+
+
+class UpdateProfileView(UpdateAPIView):
+
+ queryset = User.objects.all()
+ permission_classes = (IsAuthenticated,)
+ serializer_class = UpdateUserSerializer
+
+ def get_object(self):
+ return self.queryset.get(pk=self.request.user.pk)
diff --git a/frontend/static/css/index.css b/frontend/static/css/index.css
index 6c04bb5..816eaec 100644
--- a/frontend/static/css/index.css
+++ b/frontend/static/css/index.css
@@ -1,11 +1,3 @@
-#app #avatar {
- max-height: 10em;
- max-width: 10em;
- min-height: 6em;
- min-width: 6em;
-}
-
-
#popup {
position: fixed;
font-size: 1.2em;
diff --git a/frontend/static/css/settings.css b/frontend/static/css/settings.css
deleted file mode 100644
index 6879d41..0000000
--- a/frontend/static/css/settings.css
+++ /dev/null
@@ -1,11 +0,0 @@
-#app * {
- font-size: 30px;
-}
-
-
-#app #main
-{
- width: 60%;
- display: flex;
- flex-direction: column;
-}
diff --git a/frontend/static/js/api/Account.js b/frontend/static/js/api/Account.js
index 66c8e0c..b3604a2 100644
--- a/frontend/static/js/api/Account.js
+++ b/frontend/static/js/api/Account.js
@@ -48,41 +48,24 @@ class Account
}
/**
- * Get account data (username)
- * @returns {?Promise
- Akel Engine.
+ Akel Engine.
View recent posts.
`;
diff --git a/frontend/static/js/views/MatchMakingView.js b/frontend/static/js/views/MatchMakingView.js
index 40f9922..c6c1404 100644
--- a/frontend/static/js/views/MatchMakingView.js
+++ b/frontend/static/js/views/MatchMakingView.js
@@ -1,4 +1,4 @@
-import { client, navigateTo } from "../index.js";
+import { client, lang, navigateTo } from "../index.js";
import { clear, fill_errors } from "../utils/formUtils.js";
import AbstractAuthenticatedView from "./abstracts/AbstractAuthenticatedView.js";
@@ -7,56 +7,36 @@ export default class extends AbstractAuthenticatedView {
constructor(params)
{
super(params, "Matchmaking");
- this.game_mode = 0; // 0 -> 2D; 1 -> 3D
}
- async press_button()
+ async toggle_search()
{
clear("innerText", ["detail"]);
if (client.matchmaking.searching)
{
client.matchmaking.stop();
- document.getElementById("button").value = "Find a game";
+ this.button.innerHTML = lang.get("matchmakingStartSearch");
}
else
{
- let nb_players = document.getElementById("nb_players-input").value;
+ let nb_players = this.input.value;
await client.matchmaking.start(this.onreceive.bind(this), this.ondisconnect.bind(this), nb_players);
- document.getElementById("button").value = "Stop matchmaking";
+ this.button.innerHTML = lang.get("matchmakingStopSearch");
}
}
- async press_button_game_mode()
- {
- if(this.game_mode === 0)
- {
- document.getElementById("game-mode").value = "3D";
- this.game_mode = 1;
- }
- else
- {
- document.getElementById("game-mode").value = "2D";
- this.game_mode = 0;
- }
- }
-
ondisconnect(event)
{
- let button = document.getElementById("button")
-
- if (button === null)
- return
-
- button.value = "Find a game";
+ this.button.innerHTML = lang.get("matchmakingStartSearch");
}
onreceive(data)
{
if (data.detail === "game_found")
{
- navigateTo(`/games/${data.game_id}/${this.game_mode}`);
+ navigateTo(`/games/${data.game_id}`);
return;
}
this.display_data(data);
@@ -70,46 +50,44 @@ export default class extends AbstractAuthenticatedView {
async postInit()
{
- let button = document.getElementById("button");
+ this.button = document.getElementById("toggle-search");
+ this.input = document.getElementById("nb-players-input");
- button.onclick = this.press_button.bind(this);
+ this.button.onclick = this.toggle_search.bind(this);
- let input = document.getElementById("nb_players-input");
+ this.input.addEventListener('keydown', async ev => {
- input.addEventListener('keydown', async ev => {
if (ev.key !== 'Enter')
return;
- if (client.matchmaking.searching)
- client.matchmaking.stop();
-
- let nb_players = document.getElementById("nb_players-input").value;
-
- await client.matchmaking.start(this.onreceive.bind(this), this.ondisconnect.bind(this), nb_players);
-
- document.getElementById("button").value = "Stop matchmaking";
+ await this.toggle_search.bind(this);
});
let update = () => {
- if (input.value < 2 || input.value > 4)
- button.disabled = true;
- else
- button.disabled = false;
+ this.button.disabled = (this.input.value < 2 || this.input.value > 4);
};
["change", "oninput"].forEach((event_name) => {
- input.addEventListener(event_name, update);
+ this.input.addEventListener(event_name, update);
});
- document.getElementById("game-mode").onclick = this.press_button_game_mode.bind(this);
}
async getHtml() {
- return `
- Select mode
-
-
-
-
+ return /* HTML */ `
+
+
+
${lang.get("matchmakingTitle")}
+
+
+
+
+
+
+
+
+
+
+
`;
}
diff --git a/frontend/static/js/views/SettingsView.js b/frontend/static/js/views/SettingsView.js
index ffb7662..387cf80 100644
--- a/frontend/static/js/views/SettingsView.js
+++ b/frontend/static/js/views/SettingsView.js
@@ -1,55 +1,82 @@
-import { client, navigateTo } from "../index.js";
-import { clear, fill_errors } from "../utils/formUtils.js";
-import AbstractAuthenticatedView from "./abstracts/AbstractAuthenticatedView.js";
+import { client, navigateTo } from '../index.js';
+import { clear, fill_errors } from '../utils/formUtils.js';
+import AbstractAuthenticatedView from './abstracts/AbstractAuthenticatedView.js';
export default class extends AbstractAuthenticatedView
{
constructor(params)
{
- super(params, "Settings");
+ super(params, 'Settings');
this.PROFILE_PICTURE_MAX_SIZE = 2 * 1024 * 1024; // 2MB
}
async postInit()
{
- this.display_avatar();
- document.getElementById("save-account-button").onclick = () => this.save_account();
- document.getElementById("delete-account-button").onclick = () => this.delete_account();
- document.getElementById("save-profile-button").onclick = () => this.save_profile();
+ this.avatarInit();
+ this.usernameInit();
+
+ // document.getElementById('delete-account-button').onclick = () => this.delete_account();
}
- async display_avatar() {
- let profile = await client.profiles.getProfile(client.me.username);
- if (profile !== undefined || profile !== null) {
- if (document.getElementById("avatar") != undefined)
- document.getElementById("avatar").remove();
- let avatar = document.createElement("img");
- avatar.id = "avatar";
- avatar.src = profile.avatar_url + '?t=' +new Date().getTime();
- document.getElementsByClassName("avatar")[0].appendChild(avatar);
+ usernameInit() {
+ const usernameInput = document.getElementById('usernameInput');
+ const usernameSave = document.getElementById('usernameSave');
+
+ usernameInput.oninput = e => {
+ const value = e.target.value;
+ if (value != client.me.username && value.length)
+ usernameSave.classList.remove('disabled');
+ else
+ usernameSave.classList.add('disabled');
}
+ usernameSave.onclick = _ => this.saveUsername();
+ }
+
+ avatarInit() {
+ const avatar = document.getElementById('avatar');
+ const avatarInput = document.getElementById('avatarInput');
+ const avatarUpload = document.getElementById('avatarUpload');
+ const avatarDelete = document.getElementById('avatarDelete');
+
+ avatar.onclick = _ => avatarInput.click();
+ avatarInput.onchange = function () {
+ const selectedFile = this.files[0];
+ if (!selectedFile)
+ return;
+
+ avatar.src = URL.createObjectURL(selectedFile);
+ avatarUpload.classList.remove('d-none');
+ }
+ avatarUpload.onclick = _ => this.saveAvatar();
+ avatarDelete.onclick = _ => this.deleteAvatar();
+ }
+
+ async displayAvatar() {
+ let avatar = document.getElementById('avatar');
+ avatar.src = client.me.avatar_url + '?t=' + new Date().getTime();
+ console.log(avatar.src);
}
async delete_account()
{
- let current_password = document.getElementById("current_password-input").value;
+ let current_password = document.getElementById('current_password-input').value;
let response_data = await client.account.delete(current_password);
- if (response_data === null || response_data === "user deleted")
+ if (response_data === null || response_data === 'user deleted')
{
- navigateTo("/login");
+ navigateTo('/login');
return;
}
- clear("innerHTML", ["current_password-input"]);
- fill_errors({"current_password-input": response_data.password}, "innerHTML");
+ clear('innerHTML', ['current_password-input']);
+ fill_errors({'current_password-input': response_data.password}, 'innerHTML');
}
async save_account()
{
- let username = document.getElementById("username-input").value;
- let new_password = document.getElementById("new_password-input").value;
- let current_password = document.getElementById("current_password-input").value;
+ let username = document.getElementById('username-input').value;
+ let new_password = document.getElementById('new_password-input').value;
+ let current_password = document.getElementById('current_password-input').value;
let data = {};
@@ -61,67 +88,139 @@ export default class extends AbstractAuthenticatedView
if (response_data === null)
{
- navigateTo("/login");
+ navigateTo('/login');
return;
}
- if (response_data === "data has been alterate")
- response_data = {"save-account": "saved"};
+ if (response_data === 'data has been alterate')
+ response_data = {'save-account': 'saved'};
- clear("innerHTML", ["username", "new_password", "current_password", "save-account", "delete-account"]);
- fill_errors(response_data, "innerHTML");
- }
-
- async save_profile()
- {
- let avatar = document.getElementById("avatar-input");
-
- if (avatar.files[0] !== undefined)
- {
- if (avatar.files[0].size > this.PROFILE_PICTURE_MAX_SIZE) {
- document.getElementById("save-profile").classList.add('text-danger');
- document.getElementById("save-profile").innerHTML = "Image too large :/";
- return;
- }
- let form_data = new FormData();
- form_data.append("avatar", avatar.files[0]);
- await client.me.change_avatar(form_data);
- this.display_avatar();
- }
- document.getElementById("save-profile").classList.remove('text-danger');
- document.getElementById("save-profile").innerHTML = "Saved";
+ clear('innerHTML', ['username', 'new_password', 'current_password', 'save-account', 'delete-account']);
+ fill_errors(response_data, 'innerHTML');
}
- async getHtml()
- {
- return /* HTML */ `
-
- ME
-
- `;
- }
+ if (!username.length || username === client.me.username)
+ return;
+
+ const error = await client.account.updateUsername(username);
+ if (!error) {
+ usernameDetail.classList.remove('text-danger');
+ usernameDetail.classList.add('text-success');
+ usernameDetail.innerHTML = 'Username Saved.';
+ setTimeout(_ => usernameDetail.innerHTML = '', 2000);
+ document.getElementById('usernameSave').classList.add('disabled');
+ } else {
+ usernameDetail.classList.remove('text-success');
+ usernameDetail.classList.add('text-danger');
+ usernameDetail.innerHTML = error;
+ document.getElementById('usernameSave').classList.add('disabled');
+ console.log(error);
+ }
+ }
+
+ async saveAvatar()
+ {
+ const avatarInput = document.getElementById('avatarInput');
+ const selectedFile = avatarInput.files[0];
+ const avatarDetail = document.getElementById('avatarDetail');
+
+ if (!selectedFile)
+ return;
+
+ if (selectedFile.size > this.PROFILE_PICTURE_MAX_SIZE) {
+ avatarDetail.classList.remove('text-success');
+ avatarDetail.classList.add('text-danger');
+ avatarDetail.innerHTML = 'Image is too large.'; //to translate
+ return;
+ }
+
+ const error = await client.me.changeAvatar(selectedFile);
+ if (!error) {
+ avatarDetail.classList.remove('text-danger');
+ avatarDetail.classList.add('text-success');
+ avatarDetail.innerHTML = 'Avatar saved.'; //to translate
+ setTimeout(_ => avatarDetail.innerHTML = '', 2000);
+ document.getElementById('avatarDelete').classList.remove('d-none');
+ document.getElementById('avatarUpload').classList.add('d-none');
+ avatarInput.value = null;
+ } else {
+ avatarDetail.classList.remove('text-success');
+ avatarDetail.classList.add('text-danger');
+ avatarDetail.innerHTML = error.avatar[0];
+ document.getElementById('avatarUpload').classList.add('d-none');
+ avatarInput.value = null;
+ console.log(error);
+ }
+ this.displayAvatar();
+ }
+
+ async deleteAvatar() {
+ const avatarDetail = document.getElementById('avatarDetail');
+
+ const error = await client.me.deleteAvatar();
+ if (!error) {
+ avatarDetail.classList.remove('text-danger');
+ avatarDetail.classList.add('text-success');
+ avatarDetail.innerHTML = 'Avatar deleted.'; //to translate
+ setTimeout(_ => avatarDetail.innerHTML = '', 2000);
+ document.getElementById('avatarDelete').classList.add('d-none');
+ } else {
+ avatarDetail.classList.remove('text-success');
+ avatarDetail.classList.add('text-danger');
+ avatarDetail.innerHTML = 'Something went wrong.'; //to translate
+ }
+ this.displayAvatar();
+ }
+
+ async getHtml()
+ {
+ const avatarUnchanged = client.me.avatar_url === '/static/avatars/default.avif';
+
+ return /* HTML */ `
+
+
+
Avatar
+

+
+
+
+
+
+
+
+
+
+ `;
+ //
+ // Settings
+ //
+ //
+ //
+ //
+ //
+ //
+ //
+ //
+ //
+ //
+ //
+ }
}
diff --git a/frontend/static/js/views/tournament/TournamentCreateView.js b/frontend/static/js/views/tournament/TournamentCreateView.js
index ffd7690..58e2bb1 100644
--- a/frontend/static/js/views/tournament/TournamentCreateView.js
+++ b/frontend/static/js/views/tournament/TournamentCreateView.js
@@ -1,4 +1,4 @@
-import {client, navigateTo} from "../../index.js";
+import {client, lang, navigateTo} from "../../index.js";
import { clear, fill_errors } from "../../utils/formUtils.js";
import AbstractAuthenticatedView from "../abstracts/AbstractAuthenticatedView.js";
@@ -13,13 +13,11 @@ export default class extends AbstractAuthenticatedView
async create()
{
let name = document.getElementById("name-input").value;
- let nb_players = document.getElementById("nb_players-input").value;
- let nb_players_by_game = document.getElementById("nb_players_by_game-input").value;
+ let nb_players = document.getElementById("nb-players-input").value;
+ let nb_players_by_game = document.getElementById("nb-players-by-game-input").value;
- let response_data = await client.tournaments.createTournament(nb_players, nb_players_by_game, name);
-
- if (response_data === null)
- return;
+ let response = await client.tournaments.createTournament(nb_players, nb_players_by_game, name);
+ let response_data = await response.json();
let id = response_data.id;
if (id !== undefined)
@@ -37,16 +35,33 @@ export default class extends AbstractAuthenticatedView
document.getElementById("create-button").onclick = this.create;
}
- async getHtml()
- {
- return `
-
-
-
-
-
-
-
+ async getHtml() {
+
+ return /* HTML */ `
+
+
+
${lang.get("TournamentCreateTitle")}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
`;
}
}
diff --git a/frontend/static/js/views/tournament/TournamentPageView.js b/frontend/static/js/views/tournament/TournamentPageView.js
index 9081c48..d36fd3d 100644
--- a/frontend/static/js/views/tournament/TournamentPageView.js
+++ b/frontend/static/js/views/tournament/TournamentPageView.js
@@ -55,7 +55,7 @@ export default class extends AbstractAuthenticatedView
document.getElementById("level").innerText = this.tournament.level;
document.getElementById("state").innerText = this.tournament.state;
- if (this.tournament.state === "waiting")
+ if (this.tournament.started === false)
button.disabled = false;
}
diff --git a/profiles/models.py b/profiles/models.py
index cd7d859..566e664 100644
--- a/profiles/models.py
+++ b/profiles/models.py
@@ -3,13 +3,16 @@ from django.db import models
from django.contrib.auth.models import User
from django.db.models.signals import post_save, pre_delete
from django.dispatch import receiver
-from django.conf import settings
from django.db.models import IntegerField
from games.consumers import game_manager
+from os.path import splitext
+
+
def upload_to(instance, filename: str):
- return f"./profiles/static/avatars/{instance.pk}.{filename.split('.')[1]}"
+ return f"./profiles/static/avatars/{instance.pk}{splitext(filename)[1]}"
+
# Create your models here.
class ProfileModel(models.Model):
diff --git a/profiles/serializers.py b/profiles/serializers.py
index 893093b..7a9fe97 100644
--- a/profiles/serializers.py
+++ b/profiles/serializers.py
@@ -1,6 +1,8 @@
from rest_framework import serializers
from .models import ProfileModel
from django.conf import settings
+from django.utils.translation import gettext as _
+
class ProfileSerializer(serializers.ModelSerializer):
@@ -16,5 +18,5 @@ class ProfileSerializer(serializers.ModelSerializer):
Check that the image is not too large
'''
if value.size > settings.PROFILE_PICTURE_MAX_SIZE:
- raise serializers.ValidationError('Image is too large.');
- return value;
+ raise serializers.ValidationError(_('Image is too large.'))
+ return value
diff --git a/profiles/urls.py b/profiles/urls.py
index c7e9236..1335989 100644
--- a/profiles/urls.py
+++ b/profiles/urls.py
@@ -4,7 +4,7 @@ from . import viewsets
from . import views
urlpatterns = [
- path("settings", viewsets.MyProfileViewSet.as_view({'patch': 'partial_update'}), name="my_profile_page"),
+ path("settings", viewsets.MyProfileViewSet.as_view({'patch': 'partial_update', 'delete': 'delete_avatar'}), name="my_profile_page"),
path("me", viewsets.MyProfileViewSet.as_view({'get': 'retrieve'}), name="my_profile_page"),
path("", viewsets.ProfileViewSet.as_view({'get': 'list'}), name="profiles_list"),
path("block", views.BlocksView.as_view(), name="block_page"),
@@ -12,5 +12,4 @@ urlpatterns = [
path("friend", views.FriendsView.as_view(), name="friend_page"),
path("user/", viewsets.ProfileViewSet.as_view({'get': 'retrieve'}), name="profile_page"),
path("id/", viewsets.ProfileViewSet.as_view({'get': 'retrieve_id'}), name="profile_page"),
-
]
diff --git a/profiles/viewsets.py b/profiles/viewsets.py
index 38e9755..ad8078e 100644
--- a/profiles/viewsets.py
+++ b/profiles/viewsets.py
@@ -1,4 +1,3 @@
-from rest_framework import permissions
from rest_framework.parsers import MultiPartParser, FormParser
from rest_framework import permissions, status
from rest_framework import viewsets
@@ -42,28 +41,35 @@ class ProfileViewSet(viewsets.ModelViewSet):
profile["avatar"] = profile["avatar"][profile["avatar"].find("static") - 1:]
return Response(serializer.data)
-class MyProfileViewSet(viewsets.ModelViewSet):
+class MyProfileViewSet(viewsets.ModelViewSet):
permission_classes = (permissions.IsAuthenticated,)
authentication_classes = (SessionAuthentication,)
serializer_class = ProfileSerializer
- queryset = ProfileModel.objects.all
+ queryset = ProfileModel.objects.all()
def get_object(self):
- obj = self.queryset().get(pk=self.request.user.pk)
+ obj = self.queryset.get(pk=self.request.user.pk)
return obj
- def perform_update(self, serializer, pk=None):
- serializer.is_valid(raise_exception=True);
- profile: ProfileModel = self.get_object();
- avatar = serializer.validated_data.get('avatar');
+ def perform_update(self, serializer: ProfileSerializer, pk=None):
+ serializer.is_valid(raise_exception=True)
+ avatar = serializer.validated_data.get('avatar')
+ profile: ProfileModel = self.get_object()
if (avatar is not None):
if (profile.avatar.name != "./profiles/static/avatars/default.avif"):
profile.avatar.storage.delete(profile.avatar.name)
- profile.avatar = avatar
- profile.save()
-
+ serializer.save()
+
+ def delete_avatar(self, request, pk=None):
+ profile = self.get_object()
+ if (profile.avatar.name != './profiles/static/avatars/default.avif'):
+ profile.avatar.storage.delete(profile.avatar.name)
+ profile.avatar.name = './profiles/static/avatars/default.avif'
+ profile.save()
+ return Response(ProfileSerializer(profile).data)
+
def retrieve(self, request: HttpRequest, pk=None):
instance: ProfileModel = self.get_object()
instance.avatar.name = instance.avatar.name[instance.avatar.name.find("static") - 1:]
diff --git a/tournament/serializers.py b/tournament/serializers.py
index 6f29235..9308e08 100644
--- a/tournament/serializers.py
+++ b/tournament/serializers.py
@@ -5,6 +5,7 @@ from games.serializers import GameSerializer
class TournamentSerializer(serializers.ModelSerializer):
levels = serializers.SerializerMethodField(read_only=True, required=False)
+ state = serializers.SerializerMethodField(read_only=True, required=False)
level = serializers.ReadOnlyField()
started = serializers.ReadOnlyField()
finished = serializers.ReadOnlyField()
@@ -12,7 +13,10 @@ class TournamentSerializer(serializers.ModelSerializer):
class Meta:
model = TournamentModel
- fields = ["name", "nb_players", "nb_players_by_game", "level", "started", "finished", "levels", "id"]
+ fields = ["name", "nb_players", "nb_players_by_game", "level", "started", "finished", "levels", "id", "state"]
+
+ def get_state(self, instance: TournamentModel):
+ return ["waiting", "started", "finished"][instance.started + instance.finished]
def get_levels(self, instance: TournamentModel):
levels: list[list[int]] = []
diff --git a/tournament/viewset.py b/tournament/viewset.py
index 0ffd2cd..5106928 100644
--- a/tournament/viewset.py
+++ b/tournament/viewset.py
@@ -43,7 +43,7 @@ class TournamentViewSet(viewsets.ModelViewSet):
query = TournamentModel.objects.filter(started=False, finished=False)
case _:
query = TournamentModel.objects.all()
- serializer = TournamentSerializer(query, many=True)
+ serializer = self.serializer_class(query, many=True)
return Response(serializer.data)
def retrieve(self, request: HttpRequest, pk):