diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 890a4ec08aaf7ec7d6d5edb34373c054f93a50bc..2c4187feb882445df4a598dca816165034828961 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -10,6 +10,7 @@ test: - cd backend/secfit - apt-get update -qy - pip install -r requirements.txt + - python manage.py test staging: type: deploy diff --git a/backend/secfit/requirements.txt b/backend/secfit/requirements.txt index 9feb375bde1e8fb7befe6c102dd29beeee7c6940..2dc56eb1e294d223a3effeabfa1919856317398a 100644 Binary files a/backend/secfit/requirements.txt and b/backend/secfit/requirements.txt differ diff --git a/backend/secfit/secfit/settings.py b/backend/secfit/secfit/settings.py index a76414ade90ef7dd625663be6dad7e2945fcb6a7..e0750a2763772e360d28388066db37e75a4639de 100644 --- a/backend/secfit/secfit/settings.py +++ b/backend/secfit/secfit/settings.py @@ -66,9 +66,11 @@ INSTALLED_APPS = [ "meals.apps.MealsConfig", "users.apps.UsersConfig", "comments.apps.CommentsConfig", + "django_nose", "corsheaders", ] + MIDDLEWARE = [ "corsheaders.middleware.CorsMiddleware", "django.middleware.security.SecurityMiddleware", @@ -96,6 +98,13 @@ TEMPLATES = [ }, }, ] +TEST_RUNNER = "django_nose.NoseTestSuiteRunner" + +NOSE_ARGS = [ + '--with-coverage', + '--cover-package=workouts,users', + '--cover-erase', +] WSGI_APPLICATION = "secfit.wsgi.application" diff --git a/backend/secfit/users/tests.py b/backend/secfit/users/tests.py deleted file mode 100644 index 7ce503c2dd97ba78597f6ff6e4393132753573f6..0000000000000000000000000000000000000000 --- a/backend/secfit/users/tests.py +++ /dev/null @@ -1,3 +0,0 @@ -from django.test import TestCase - -# Create your tests here. diff --git a/backend/secfit/users/tests/__init__.py b/backend/secfit/users/tests/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/backend/secfit/users/tests/test_serializers.py b/backend/secfit/users/tests/test_serializers.py new file mode 100644 index 0000000000000000000000000000000000000000..0f5b2d17fc2319a048bdcca78570800e0339460f --- /dev/null +++ b/backend/secfit/users/tests/test_serializers.py @@ -0,0 +1,59 @@ +from threading import local +from django.test import TestCase, RequestFactory, Client +from users.models import User +from users.serializers import UserSerializer + +# Create your tests here. + +class UserSerializerTestCase(TestCase): + # def setUp(self): + #anything? + + def test_validate_password(self): + print("User tests") + my_pass = "pw" + local_seri = UserSerializer(data={"password": my_pass, "password1": my_pass}) + self.assertEqual(local_seri.validate_password(my_pass), my_pass) + + def test_create(self): + username = "jorsi" + email = "some@mail.com" + password = "pw" + phone_number = "12345678" + city ="Ttown" + country="Norwei" + street_address = "street 3" + favourite_exercise = "curl" + main_gym ="SIT" + + local_seri = UserSerializer(data={ + "username":username, + "email":email, + "password":password, + "phone_number":phone_number, + "country":country, + "city":city, + "street_address":street_address, + "favourite_exercise":favourite_exercise, + "main_gym":main_gym + }) + user_one=local_seri.create({ + "username":username, + "email":email, + "password":password, + "phone_number":phone_number, + "country":country, + "city":city, + "street_address":street_address, + "favourite_exercise":favourite_exercise, + "main_gym":main_gym + }) + #Can not check password to our variable, as the password is hashed. + self.assertEqual(user_one.username, username) + self.assertEqual(user_one.email, email) + self.assertEqual(user_one.phone_number, phone_number) + self.assertEqual(user_one.country, country) + self.assertEqual(user_one.city, city) + self.assertEqual(user_one.street_address, street_address) + self.assertEqual(user_one.favourite_exercise, favourite_exercise) + self.assertEqual(user_one.main_gym, main_gym) \ No newline at end of file diff --git a/backend/secfit/users/tests/test_twoway.py b/backend/secfit/users/tests/test_twoway.py new file mode 100644 index 0000000000000000000000000000000000000000..dd9de1668bfa86a388ecafa26179fb39f1c80e77 --- /dev/null +++ b/backend/secfit/users/tests/test_twoway.py @@ -0,0 +1,92 @@ +from threading import local +from django.test import TestCase, Client + + +class TwoWayDomainTest(TestCase): + def setUp(self): + self.client = Client() + self.test_cases_data =[ + #Valid + { + "username" : "jorsi", + "email" : "some@mail.com", + "password" : "pw", + "password1": "pw", + "phone_number" : "12345678", + "city" : "Ttown", + "country":"Norwei", + "street_address" : "street 3", + "favourite_exercise" : "curl", + "main_gym" :"SIT", + }, + #Rest are invalid, first with several(username, email, password and password1) invalid inputs, then changing one by one + { + "username" : "//", + "email" : "/&", + "password" : "", + "password1": "!", + "phone_number" : "12345678", + "city" : "Ttown", + "country":"Norwei", + "street_address" : "street 3", + "favourite_exercise" : "curl", + "main_gym" :"SIT", + }, + { + "username" : "//", + "email" : "some@mail.com", + "password" : "pw", + "password1": "pw", + "phone_number" : "12345678", + "city" : "Ttown", + "country":"Norwei", + "street_address" : "street 3", + "favourite_exercise" : "curl", + "main_gym" :"SIT", + }, + { + "username" : "jorsi", + "email" : "/&", + "password" : "pw", + "password1": "pw", + "phone_number" : "12345678", + "city" : "Ttown", + "country":"Norwei", + "street_address" : "street 3", + "favourite_exercise" : "curl", + "main_gym" :"SIT", + }, + { + "username" : "jorsi", + "email" : "some@mail.com", + "password" : "", + "password1": "pw", + "phone_number" : "12345678", + "city" : "Ttown", + "country":"Norwei", + "street_address" : "street 3", + "favourite_exercise" : "curl", + "main_gym" :"SIT", + }, + { + "username" : "jorsi", + "email" : "some@mail.com", + "password" : "pw", + "password1": "", + "phone_number" : "12345678", + "city" : "Ttown", + "country":"Norwei", + "street_address" : "street 3", + "favourite_exercise" : "curl", + "main_gym" :"SIT", + }, + ] + + def test_twoway_domain_test(self): + + for case in self.test_cases_data: + response = self.client.post('/api/users/', case) + if case == self.test_cases_data[0]: + self.assertEqual(response.status_code, 201) + else: + self.assertEqual(response.status_code, 400) \ No newline at end of file diff --git a/backend/secfit/workouts/tests.py b/backend/secfit/workouts/tests.py deleted file mode 100644 index 7fbbf7847f5b0f201d408d4017cc865d614e2615..0000000000000000000000000000000000000000 --- a/backend/secfit/workouts/tests.py +++ /dev/null @@ -1,6 +0,0 @@ -""" -Tests for the workouts application. -""" -from django.test import TestCase - -# Create your tests here. diff --git a/backend/secfit/workouts/tests/__init__.py b/backend/secfit/workouts/tests/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/backend/secfit/workouts/tests/test_permissions.py b/backend/secfit/workouts/tests/test_permissions.py new file mode 100644 index 0000000000000000000000000000000000000000..6a85997288b8a08030687cd6c758213bd370511a --- /dev/null +++ b/backend/secfit/workouts/tests/test_permissions.py @@ -0,0 +1,224 @@ +""" +Tests for the workouts application. +Sources: +https://docs.djangoproject.com/en/4.0/topics/testing/overview/ +https://docs.djangoproject.com/en/4.0/topics/testing/tools/ +https://docs.djangoproject.com/en/4.0/topics/testing/advanced/ +""" +from urllib.request import Request +from django.test import TestCase, Client, RequestFactory + +from django.urls import reverse, resolve + +#from workouts.views import Workout, WorkoutDetail, WorkoutList +from users.models import User +from workouts.models import Workout, Exercise, ExerciseInstance +from workouts.permissions import IsOwner, IsOwnerOfWorkout, IsCoachAndVisibleToCoach, IsCoachOfWorkoutAndVisibleToCoach, IsPublic, IsWorkoutPublic, IsReadOnly + +# Create your tests here. + +class IsOwnerTestCase(TestCase): + def setUp(self): + #User setup + self.user_one = User.objects.create(username = "jorsi", phone_number="12345678", country="Norway", + city="Trondheim", street_address="street 3", favourite_exercise="Biceps", main_gym="SIT") + self.user_one.save() + + #Workout setup + self.workout_one = Workout.objects.create(name="Curl", date="2022-03-03", notes="nice", + owner=self.user_one, visibility="PU") + + #Factory setup + self.factory= RequestFactory() + + def test_is_owner(self): + test_request = self.factory.get("/") + test_request.user = self.user_one + workout = self.workout_one + is_owner = IsOwner().has_object_permission(test_request, None, workout) + self.assertTrue(is_owner) + +class IsOwnerOfWorkoutTestCase(TestCase): + """One workout is associated with 1 or more exercises (instances) + Each exercise instance must have an exercise type + So: Create Workout, then exercise, then instance of ex and associated with workout + User1 owns: workout1, + Coach1 Coaches: User1 + ExerciseInstance1 associated with workout1, and is instance of exercise_one + """ + def setUp(self): + #Initiate User, Workout, Exercise, ExerciseInstance and RequestFactory + self.user_one = User.objects.create(username = "jorsi", phone_number="12345678", country="Norway", + city="Trondheim", street_address="street 3", favourite_exercise="Biceps", main_gym="SIT") + + self.workout_one = Workout.objects.create(name="Chest", date="2022-03-03", notes="nice", + owner=self.user_one, visibility="PU") + + self.exercise_one = Exercise.objects.create(name="Pushups", description="push from ground", duration=30, + calories=20, muscleGroup="Chest", unit="Amount") + + self.ex_instance_one = ExerciseInstance.objects.create(workout=self.workout_one, exercise=self.exercise_one, + sets=3, number=10) + + self.factory= RequestFactory() + + def test_has_object_permission(self): + #takes self, request, view obj. + test_request = self.factory.get("/") + test_request.user = self.user_one + ex_instance = self.ex_instance_one + is_owner = IsOwnerOfWorkout().has_object_permission(test_request, None, ex_instance) + self.assertTrue(is_owner) + + """ + Has permissions has several outcomes: + Something else than POST req. + Post without workout false + Posting returns true + """ + def test_has_permission(self): + #takes self, request, view. + test_request = self.factory.get("/") + test_request.user = self.user_one + test_request.data = {"workout" : "/api/workouts/1/"} + is_owner= IsOwnerOfWorkout().has_permission(test_request, None) + self.assertTrue(is_owner) + + def test_has_permission_without_workout(self): + #takes self, request, view. + test_request = self.factory.post("/") + test_request.user = self.user_one + test_request.data = { "workout" : ""} + is_owner= IsOwnerOfWorkout().has_permission(test_request, None) + self.assertFalse(is_owner) + + def test_has_permission_post(self): + #takes self, request, view. + test_request = self.factory.post("/") + test_request.user = self.user_one + test_request.data = {"workout" : "/api/workouts/1/"} + is_owner = IsOwnerOfWorkout().has_permission(test_request, None) + self.assertTrue(is_owner) + + +class IsCoachAndVisibleToCoachTestCase(TestCase): + """ + Coach1 Coaches: User1, owns workout1 + """ + def setUp(self): + #Initiate User, Workout, Coach and RequestFactory + self.user_one = User.objects.create(username = "jorsi", phone_number="12345678", country="Norway", + city="Trondheim", street_address="street 3", favourite_exercise="Biceps", main_gym="SIT") + + self.coach_one = User.objects.create(username = "sijor", phone_number="23456789", country="Norway", + city="Trondheim", street_address="street 4", favourite_exercise="Biceps", main_gym="SIT", coach=self.user_one) + + self.workout_one = Workout.objects.create(name="Chest", date="2022-03-03", notes="nice", + owner=self.coach_one, visibility="PU") + + self.factory= RequestFactory() + + def test_has_object_permission(self): + test_request = self.factory.get("/") + test_request.user = self.user_one + workout = self.workout_one + is_coach = IsCoachAndVisibleToCoach().has_object_permission(test_request, None, workout) + self.assertTrue(is_coach) + +class IsCoachOfWorkoutAndVisibleToCoachTestCase(TestCase): + """ + Coach1 Coaches User1, & owns workout1 + ExerciseInstance1 associated with workout1, and is instance of exercise_one + """ + def setUp(self): + #Initiate User, Coach, Workout, Exercise, ExerciseInstance and RequestFactory + self.user_one = User.objects.create(username = "jorsi", phone_number="12345678", country="Norway", + city="Trondheim", street_address="street 3", favourite_exercise="Biceps", main_gym="SIT") + + self.coach_one = User.objects.create(username = "sijor", phone_number="23456789", country="Norway", + city="Trondheim", street_address="street 4", favourite_exercise="Biceps", main_gym="SIT", coach=self.user_one) + + self.workout_one = Workout.objects.create(name="Chest", date="2022-03-03", notes="nice", + owner=self.coach_one, visibility="PU") + + self.exercise_one = Exercise.objects.create(name="Pushups", description="push from ground", duration=30, + calories=20, muscleGroup="Chest", unit="Amount") + + self.exercise_instance_one = ExerciseInstance.objects.create(workout=self.workout_one, exercise=self.exercise_one, + sets=3, number=10) + + self.factory= RequestFactory() + + def test_has_object_permission(self): + test_request = self.factory.get("/") + test_request.user = self.user_one + ins = self.exercise_instance_one + + is_coach = IsCoachOfWorkoutAndVisibleToCoach().has_object_permission(test_request, None, ins) + self.assertTrue(is_coach) + +class IsPublicTestCase(TestCase): + + def setUp(self): + #Initiate User, Workout, and RequestFactory + self.user_one = User.objects.create(username = "jorsi", phone_number="12345678", country="Norway", + city="Trondheim", street_address="street 3", favourite_exercise="Biceps", main_gym="SIT") + + self.workout_one = Workout.objects.create(name="Chest", date="2022-03-03", notes="nice", + owner=self.user_one, visibility="PU") + + self.factory= RequestFactory() + + def test_has_object_permission(self): + test_request = self.factory.get("/") + test_request.user = self.user_one + workout = self.workout_one + + is_visible = IsPublic().has_object_permission(test_request, None, workout) + self.assertTrue(is_visible) + +class IsWorkoutPublicTestCase(TestCase): + + def setUp(self): + #Initiate User, Workout, Exercise, ExerciseInstance and RequestFactory + self.user_one = User.objects.create(username = "jorsi", phone_number="12345678", country="Norway", + city="Trondheim", street_address="street 3", favourite_exercise="Biceps", main_gym="SIT") + + self.workout_one = Workout.objects.create(name="Chest", date="2022-03-03", notes="nice", + owner=self.user_one, visibility="PU") + + self.exercise_one = Exercise.objects.create(name="Pushups", description="push from ground", duration=30, + calories=20, muscleGroup="Chest", unit="Amount") + + self.exercise_instance_one = ExerciseInstance.objects.create(workout=self.workout_one, exercise=self.exercise_one, + sets=3, number=10) + + self.factory= RequestFactory() + + def test_has_object_permission(self): + test_request = self.factory.get("/") + test_request.user = self.user_one + ins = self.exercise_instance_one + + is_visible = IsWorkoutPublic().has_object_permission(test_request, None, ins) + self.assertTrue(is_visible) + +class IsReadOnlyTestCase(TestCase): + + def setUp(self): + #Initiate User, Workout, and RequestFactory + self.user_one = User.objects.create(username = "jorsi", phone_number="12345678", country="Norway", + city="Trondheim", street_address="street 3", favourite_exercise="Biceps", main_gym="SIT") + + self.workout_one = Workout.objects.create(name="Chest", date="2022-03-03", notes="nice", + owner=self.user_one, visibility="PU") + + self.factory= RequestFactory() + + def test_has_object_permission(self): + test_request = self.factory.get("/") + test_request.user = self.user_one + workout = self.workout_one + + is_read_only = IsReadOnly().has_object_permission(test_request, None, workout) + self.assertTrue(is_read_only) \ No newline at end of file diff --git a/frontend/www/profile.html b/frontend/www/profile.html index f673d456acbeb9f1fc66234092ed99af460d69ad..3349d8bef2d3ae0b0b4015b0ff9c45005de492bc 100644 --- a/frontend/www/profile.html +++ b/frontend/www/profile.html @@ -12,7 +12,7 @@ </head> <body> -<navbar-el></navbar-el> + <navbar-el></navbar-el> <!-- Adapted from https://bootsnipp.com/snippets/z8699 START --> <div class="container"> @@ -111,4 +111,4 @@ <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.0-beta1/dist/js/bootstrap.bundle.min.js" integrity="sha384-ygbV9kiqUc6oa4msXn9868pTtWMgiQaeYH7/t7LECLbyPA2x65Kgf80OJFdroafW" crossorigin="anonymous"></script> </body> -</html> +</html> \ No newline at end of file diff --git a/frontend/www/profilesettings.html b/frontend/www/profilesettings.html new file mode 100644 index 0000000000000000000000000000000000000000..1a390499703dd24bf9740d94cf9d9ba39259abcd --- /dev/null +++ b/frontend/www/profilesettings.html @@ -0,0 +1,67 @@ +<!DOCTYPE html> +<html lang="en"> +<head> + <meta charset="UTF-8"> + <meta name="viewport" content="width=device-width, initial-scale=1.0"> + <title>Profile</title> + <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.0-beta1/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-giJF6kkoqNQ00vy+HMDP7azOuL0xtbfIcaT9wjKHr8RbDVddVHyTfAAsrekwKmP1" crossorigin="anonymous"> + + <script src="https://kit.fontawesome.com/0ce6c392ca.js" crossorigin="anonymous"></script> + <link rel="stylesheet" href="styles/style.css"> + <script src="scripts/navbar.js" type="text/javascript" defer></script> +</head> +<body> +<navbar-el></navbar-el> + +<div class="container"> + <div class="row"> + <div class="col-lg"> + <h3 class="mt-3">Profile settings</h3> + </div> + </div> + <form class="row g-3" id="form-exercise"> + <div class="col-lg-6 "> + <label for="inputName" class="form-label">Name</label> + <input type="text" class="form-control" id="inputName" name="name" readonly> + </div> + <div class="col-lg-6"></div> + <div class="col-lg-6"> + </div> + <div class="col-lg-6"></div> + <div class="col-lg-6 "> + <label for="inputName" class="form-label">Favourite Exercise</label> + <input type="text" class="form-control" id="inputFavouriteExercise" name="favouriteExercise" readonly> + </div> + + <div class="col-lg-6"></div> + <div class="col-lg-6"> + + </div> + <div class="col-lg-6"></div> + <div class="col-lg-6"> + <label for="inputMuscles" class="form-label">Main gym</label> + <select class="form-select" name="gymGroup" disabled="true"> + <option value="Legs">SIT</option> + <option value="Chest">IMPULS</option> + <option value="Back">3T</option> + <option value="Arms">STAMINA</option> + </select> + </div> + <div class="col-lg-6"></div> + <div class="col-lg-6"> + <input type="button" class="btn btn-primary hide" id="btn-ok-exercise" value=" OK "> + <input type="button" class="btn btn-primary" id="btn-edit-exercise" value=" Edit "> + <input type="button" class="btn btn-secondary hide" id="btn-cancel-exercise" value="Cancel"> + <input type="button" class="btn btn-danger float-end hide" id="btn-delete-exercise" value="Delete"> + </div> + <div class="col-lg-6"> + + </div> + </form> +</div> +<script src="scripts/defaults.js"></script> +<script src="scripts/scripts.js"></script> +<script src="scripts/profilesettings.js"></script> +<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.0-beta1/dist/js/bootstrap.bundle.min.js" integrity="sha384-ygbV9kiqUc6oa4msXn9868pTtWMgiQaeYH7/t7LECLbyPA2x65Kgf80OJFdroafW" crossorigin="anonymous"></script> +</body> +</html> diff --git a/frontend/www/scripts/exercise.js b/frontend/www/scripts/exercise.js index f845fe1844b633cf1b0bf1365eee4323c4c84bcc..24ef1862893160cb60522c397b57e83eaa214efe 100644 --- a/frontend/www/scripts/exercise.js +++ b/frontend/www/scripts/exercise.js @@ -10,7 +10,7 @@ class MuscleGroup { this.validTypes = ["Legs", "Chest", "Back", "Arms", "Abdomen", "Shoulders"] this.type = this.validTypes.includes(type) ? type : undefined; - }; + } setMuscleGroupType = (newType) => { this.isValidType = false; @@ -23,7 +23,7 @@ class MuscleGroup { alert("Invalid muscle group!"); } - }; + } getMuscleGroupType = () => { console.log(this.type, "SWIOEFIWEUFH") @@ -41,7 +41,7 @@ function handleCancelButtonDuringEdit() { cancelButton.removeEventListener("click", handleCancelButtonDuringEdit); - let form = document.querySelector("#form-exercise"); + const form = document.querySelector("#form-exercise"); if (oldFormData.has("name")) form.name.value = oldFormData.get("name"); if (oldFormData.has("description")) form.description.value = oldFormData.get("description"); if (oldFormData.has("duration")) form.duration.value = oldFormData.get("duration"); @@ -64,24 +64,24 @@ function handleCancelButtonDuringCreate() { async function createExercise() { document.querySelector("select").removeAttribute("disabled") - let form = document.querySelector("#form-exercise"); - let formData = new FormData(form); - let body = {"name": formData.get("name"), + const form = document.querySelector("#form-exercise"); + const formData = new FormData(form); + const body = {"name": formData.get("name"), "description": formData.get("description"), "duration": formData.get("duration"), "calories": formData.get("calories"), "muscleGroup": formData.get("muscleGroup"), "unit": formData.get("unit")}; - let response = await sendRequest("POST", `${HOST}/api/exercises/`, body); + const response = await sendRequest("POST", `${HOST}/api/exercises/`, body); if (response.ok) { window.location.replace("exercises.html"); - } else { - let data = await response.json(); - let alert = createAlert("Could not create new exercise!", data); - document.body.prepend(alert); + return; } + const data = await response.json(); + const alert = createAlert("Could not create new exercise!", data); + document.body.prepend(alert); } function handleEditExerciseButtonClick() { @@ -96,15 +96,15 @@ function handleEditExerciseButtonClick() { cancelButton.addEventListener("click", handleCancelButtonDuringEdit); - let form = document.querySelector("#form-exercise"); + const form = document.querySelector("#form-exercise"); oldFormData = new FormData(form); } async function deleteExercise(id) { - let response = await sendRequest("DELETE", `${HOST}/api/exercises/${id}/`); + const response = await sendRequest("DELETE", `${HOST}/api/exercises/${id}/`); if (!response.ok) { - let data = await response.json(); - let alert = createAlert(`Could not delete exercise ${id}`, data); + const data = await response.json(); + const alert = createAlert(`Could not delete exercise ${id}`, data); document.body.prepend(alert); } else { window.location.replace("exercises.html"); @@ -112,71 +112,70 @@ async function deleteExercise(id) { } async function retrieveExercise(id) { - let response = await sendRequest("GET", `${HOST}/api/exercises/${id}/`); + const response = await sendRequest("GET", `${HOST}/api/exercises/${id}/`); console.log(response.ok) if (!response.ok) { - let data = await response.json(); - let alert = createAlert("Could not retrieve exercise data!", data); + const data = await response.json(); + const alert = createAlert("Could not retrieve exercise data!", data); document.body.prepend(alert); - } else { - document.querySelector("select").removeAttribute("disabled") - let exerciseData = await response.json(); - let form = document.querySelector("#form-exercise"); - let formData = new FormData(form); - - for (let key of formData.keys()) { - let selector - key !== "muscleGroup" ? selector = `input[name="${key}"], textarea[name="${key}"]` : selector = `select[name=${key}]` - let input = form.querySelector(selector); - let newVal = exerciseData[key]; - input.value = newVal; - } - document.querySelector("select").setAttribute("disabled", "") + return; + } + document.querySelector("select").removeAttribute("disabled") + const exerciseData = await response.json(); + const form = document.querySelector("#form-exercise"); + const formData = new FormData(form); + + for (const key of formData.keys()) { + const selector = key !== "muscleGroup" ? `input[name="${key}"], textarea[name="${key}"]` : `select[name=${key}]` + const input = form.querySelector(selector); + const newVal = exerciseData[key]; + input.value = newVal; } + document.querySelector("select").setAttribute("disabled", "") } async function updateExercise(id) { - let form = document.querySelector("#form-exercise"); - let formData = new FormData(form); + const form = document.querySelector("#form-exercise"); + const formData = new FormData(form); - let muscleGroupSelector = document.querySelector("select") + const muscleGroupSelector = document.querySelector("select") muscleGroupSelector.removeAttribute("disabled") - let selectedMuscleGroup = new MuscleGroup(formData.get("muscleGroup")); + const selectedMuscleGroup = new MuscleGroup(formData.get("muscleGroup")); - let body = {"name": formData.get("name"), + const body = {"name": formData.get("name"), "description": formData.get("description"), "duration": formData.get("duration"), "calories": formData.get("calories"), "muscleGroup": selectedMuscleGroup.getMuscleGroupType(), "unit": formData.get("unit")}; - let response = await sendRequest("PUT", `${HOST}/api/exercises/${id}/`, body); + const response = await sendRequest("PUT", `${HOST}/api/exercises/${id}/`, body); if (!response.ok) { - let data = await response.json(); - let alert = createAlert(`Could not update exercise ${id}`, data); + const data = await response.json(); + const alert = createAlert(`Could not update exercise ${id}`, data); document.body.prepend(alert); - } else { - muscleGroupSelector.setAttribute("disabled", "") - // duplicate code from handleCancelButtonDuringEdit - // you should refactor this - setReadOnly(true, "#form-exercise"); - okButton.className += " hide"; - deleteButton.className += " hide"; - cancelButton.className += " hide"; - editButton.className = editButton.className.replace(" hide", ""); + return; + } + muscleGroupSelector.setAttribute("disabled", "") + // duplicate code from handleCancelButtonDuringEdit + // you should refactor this + setReadOnly(true, "#form-exercise"); + okButton.className += " hide"; + deleteButton.className += " hide"; + cancelButton.className += " hide"; + editButton.className = editButton.className.replace(" hide", ""); - cancelButton.removeEventListener("click", handleCancelButtonDuringEdit); + cancelButton.removeEventListener("click", handleCancelButtonDuringEdit); - oldFormData.delete("name"); - oldFormData.delete("description"); - oldFormData.delete("duration"); - oldFormData.delete("calories"); - oldFormData.delete("muscleGroup"); - oldFormData.delete("unit"); - } + oldFormData.delete("name"); + oldFormData.delete("description"); + oldFormData.delete("duration"); + oldFormData.delete("calories"); + oldFormData.delete("muscleGroup"); + oldFormData.delete("unit"); } window.addEventListener("DOMContentLoaded", async () => { @@ -194,18 +193,17 @@ window.addEventListener("DOMContentLoaded", async () => { await retrieveExercise(exerciseId); editButton.addEventListener("click", handleEditExerciseButtonClick); - deleteButton.addEventListener("click", (async (id) => await deleteExercise(id)).bind(undefined, exerciseId)); - okButton.addEventListener("click", (async (id) => await updateExercise(id)).bind(undefined, exerciseId)); + deleteButton.addEventListener("click", (async (id) => deleteExercise(id)).bind(undefined, exerciseId)); + okButton.addEventListener("click", (async (id) => updateExercise(id)).bind(undefined, exerciseId)); + return; } //create - else { - setReadOnly(false, "#form-exercise"); + setReadOnly(false, "#form-exercise"); - editButton.className += " hide"; - okButton.className = okButton.className.replace(" hide", ""); - cancelButton.className = cancelButton.className.replace(" hide", ""); + editButton.className += " hide"; + okButton.className = okButton.className.replace(" hide", ""); + cancelButton.className = cancelButton.className.replace(" hide", ""); - okButton.addEventListener("click", async () => await createExercise()); - cancelButton.addEventListener("click", handleCancelButtonDuringCreate); - } + okButton.addEventListener("click", async () => createExercise()); + cancelButton.addEventListener("click", handleCancelButtonDuringCreate); }); \ No newline at end of file diff --git a/frontend/www/scripts/gallery.js b/frontend/www/scripts/gallery.js index f9c07b449947470c8df29c8f51894758cf38c025..46b208dfe1650ad8187ad742234ffd7f274f8288 100644 --- a/frontend/www/scripts/gallery.js +++ b/frontend/www/scripts/gallery.js @@ -1,21 +1,20 @@ let goBackButton; -let submitNewFileButton; async function retrieveWorkoutImages(id) { let workoutData = null; - let response = await sendRequest("GET", `${HOST}/api/workouts/${id}/`); + const response = await sendRequest("GET", `${HOST}/api/workouts/${id}/`); if (!response.ok) { - let data = await response.json(); - let alert = createAlert("Could not retrieve workout data!", data); + const data = await response.json(); + const alert = createAlert("Could not retrieve workout data!", data); document.body.prepend(alert); } else { workoutData = await response.json(); - document.getElementById("workout-title").innerHTML = "Workout name: " + workoutData["name"]; - document.getElementById("workout-owner").innerHTML = "Owner: " + workoutData["owner_username"]; + document.getElementById("workout-title").innerHTML = `Workout name: ${workoutData.name}`; + document.getElementById("workout-owner").innerHTML = `Owner: ${workoutData.owner_username}`; - let hasNoImages = workoutData.files.length == 0; - let noImageText = document.querySelector("#no-images-text"); + const hasNoImages = workoutData.files.length == 0; + const noImageText = document.querySelector("#no-images-text"); if(hasNoImages){ noImageText.classList.remove("hide"); @@ -25,34 +24,34 @@ async function retrieveWorkoutImages(id) { noImageText.classList.add("hide"); - let filesDiv = document.getElementById("img-collection"); - let filesDeleteDiv = document.getElementById("img-collection-delete"); + const filesDiv = document.getElementById("img-collection"); + const filesDeleteDiv = document.getElementById("img-collection-delete"); const currentImageFileElement = document.querySelector("#current"); let isFirstImg = true; let fileCounter = 0; - for (let file of workoutData.files) { - let a = document.createElement("a"); + for (const file of workoutData.files) { + const a = document.createElement("a"); a.href = file.file; - let pathArray = file.file.split("/"); + const pathArray = file.file.split("/"); a.text = pathArray[pathArray.length - 1]; a.className = "me-2"; - let isImage = ["jpg", "png", "gif", "jpeg", "JPG", "PNG", "GIF", "JPEG"].includes(a.text.split(".")[1]); + const isImage = ["jpg", "png", "gif", "jpeg", "JPG", "PNG", "GIF", "JPEG"].includes(a.text.split(".")[1]); if(isImage){ - let deleteImgButton = document.createElement("input"); + const deleteImgButton = document.createElement("input"); deleteImgButton.type = "button"; deleteImgButton.className = "btn btn-close"; deleteImgButton.id = file.url.split("/")[file.url.split("/").length - 2]; deleteImgButton.addEventListener('click', () => handleDeleteImgClick(deleteImgButton.id, "DELETE", `Could not delete workout ${deleteImgButton.id}!`, HOST, ["jpg", "png", "gif", "jpeg", "JPG", "PNG", "GIF", "JPEG"])); filesDeleteDiv.appendChild(deleteImgButton); - let img = document.createElement("img"); + const img = document.createElement("img"); img.src = file.file; filesDiv.appendChild(img); @@ -89,9 +88,9 @@ async function retrieveWorkoutImages(id) { } async function validateImgFileType(id, host_variable, acceptedFileTypes) { - let file = await sendRequest("GET", `${host_variable}/api/workout-files/${id}/`); - let fileData = await file.json(); - let fileType = fileData.file.split("/")[fileData.file.split("/").length - 1].split(".")[1]; + const file = await sendRequest("GET", `${host_variable}/api/workout-files/${id}/`); + const fileData = await file.json(); + const fileType = fileData.file.split("/")[fileData.file.split("/").length - 1].split(".")[1]; return acceptedFileTypes.includes(fileType); } @@ -102,11 +101,11 @@ async function handleDeleteImgClick (id, http_keyword, fail_alert_text, host_var return } - let response = await sendRequest(http_keyword, `${host_variable}/api/workout-files/${id}/`); + const response = await sendRequest(http_keyword, `${host_variable}/api/workout-files/${id}/`); if (!response.ok) { - let data = await response.json(); - let alert = createAlert(fail_alert_text, data); + const data = await response.json(); + const alert = createAlert(fail_alert_text, data); document.body.prepend(alert); } else { location.reload(); @@ -122,10 +121,6 @@ function handleGoBackToWorkoutClick() { window.addEventListener("DOMContentLoaded", async () => { goBackButton = document.querySelector("#btn-back-workout"); - goBackButton.addEventListener('click', handleGoBackToWorkoutClick); - - const urlParams = new URLSearchParams(window.location.search); - const id = urlParams.get('id'); - let workoutData = await retrieveWorkoutImages(id); + goBackButton.addEventListener('click', handleGoBackToWorkoutClick); }); \ No newline at end of file diff --git a/frontend/www/scripts/profilesettings.js b/frontend/www/scripts/profilesettings.js new file mode 100644 index 0000000000000000000000000000000000000000..d4422ac8001afd534a48ab22f26a7d2f318f86bc --- /dev/null +++ b/frontend/www/scripts/profilesettings.js @@ -0,0 +1,196 @@ +let cancelButton; +let okButton; +let deleteButton; +let editButton; +let oldFormData; + +//TODO change variablenames and check +class gym { + constructor(type) { + this.isValidType = false; + this.validTypes = ["SIT", "IMPULS", "3T", "STAMINA"]; + + this.type = this.validTypes.includes(type) ? type : undefined; + }; + + setMuscleGroupType = (newType) => { + this.isValidType = false; + + if(this.validTypes.includes(newType)){ + this.isValidType = true; + this.type = newType; + } + else{ + alert("Invalid gym!"); + } + + }; + + getMuscleGroupType = () => { + console.log(this.type, "SWIOEFIWEUFH") + return this.type; + } +} + +function handleCancelButtonDuringEdit() { + console.log("fjpfjhpisdfhøidksfho") + /* + setReadOnly(true, "#form-exercise"); + document.querySelector("select").setAttribute("disabled", "") + okButton.className += " hide"; + deleteButton.className += " hide"; + cancelButton.className += " hide"; + editButton.className = editButton.className.replace(" hide", ""); + + cancelButton.removeEventListener("click", handleCancelButtonDuringEdit); + + let form = document.querySelector("#form-exercise"); + if (oldFormData.has("name")) form.name.value = oldFormData.get("name"); + if (oldFormData.has("favouriteExercise")) form.name.value = oldFormData.get("favouriteExercise"); + if (oldFormData.has("gymGroup")) form.muscleGroup.value = oldFormData.get("gymGroup"); + + oldFormData.delete("name"); + oldFormData.delete("favouriteExercise"); + oldFormData.delete("gymGroup"); + */ + +} + +function handleCancelButtonDuringCreate() { + window.location.replace("profile.html"); +} + + + async function createExercise() { + document.querySelector("select").removeAttribute("disabled") + console.log("dasdadasda") + let form = document.querySelector("#gym"); + let formData = new FormData(form); + let body = {"name": formData.get("name"), + "description": formData.get("description"), + "duration": formData.get("duration"), + "calories": formData.get("calories"), + "muscleGroup": formData.get("gym"), + "unit": formData.get("unit")}; + + let response = await sendRequest("POST", `${HOST}/api/exercises/`, body); + + if (response.ok) { + window.location.replace("exercises.html"); + } else { + let data = await response.json(); + let alert = createAlert("Coulds not create new exercise!", data); + document.body.prepend(alert); + } + + console.log("TEST*IJG") +} + +function handleEditExerciseButtonClick() { + setReadOnly(false, "#form-exercise"); + + document.querySelector("select").removeAttribute("disabled") + + editButton.className += " hide"; + okButton.className = okButton.className.replace(" hide", ""); + cancelButton.className = cancelButton.className.replace(" hide", ""); + deleteButton.className = deleteButton.className.replace(" hide", ""); + + cancelButton.addEventListener("click", handleCancelButtonDuringEdit); + + let form = document.querySelector("#form-exercise"); + oldFormData = new FormData(form); +} + +async function deleteExercise(id) { + let response = await sendRequest("DELETE", `${HOST}/api/exercises/${id}/`); + if (!response.ok) { + let data = await response.json(); + let alert = createAlert(`Could not delete exercise ${id}`, data); + document.body.prepend(alert); + } else { + window.location.replace("exercises.html"); + } +} + +//Testing +async function retrieveGym(id) { + let response = await sendRequest("GET", `${HOST}/api/users/${id}/`); + + console.log(response.ok) + + if (!response.ok) { + let data = await response.json(); + let alert = createAlert("Could not retrieve exercise data!", data); + document.body.prepend(alert); + } else { + document.querySelector("select").removeAttribute("disabled") + let exerciseData = await response.json(); + let form = document.querySelector("#gym"); + let formData = new FormData(form); + + for (let key of formData.keys()) { + let selector + key !== "gym" ? selector = `input[name="${key}"], textarea[name="${key}"]` : selector = `select[name=${key}]` + let input = form.querySelector(selector); + let newVal = exerciseData[key]; + input.value = newVal; + } + document.querySelector("select").setAttribute("disabled", "") + } +} + +async function updateGym(id) { + let form = document.querySelector("#gym"); + let formData = new FormData(form); + + let muscleGroupSelector = document.querySelector("select") + muscleGroupSelector.removeAttribute("disabled") + + let selectedGym = new gym(formData.get("gym")); + + let body = { + "gym": selectedGym.getMuscleGroupType(),}; + let response = await sendRequest("PUT", `${HOST}/api/users/${id}/`, body); + + if (!response.ok) { + let data = await response.json(); + let alert = createAlert(`Could not update gym ${id}`, data); + document.body.prepend(alert); + } else { + console.log(response); + window.location.replace("exercises.html"); + + } +} + +window.addEventListener("DOMContentLoaded", async () => { + cancelButton = document.querySelector("#btn-cancel-exercise"); + okButton = document.querySelector("#btn-ok-exercise"); + deleteButton = document.querySelector("#btn-delete-exercise"); + editButton = document.querySelector("#btn-edit-exercise"); + oldFormData = null; + + const urlParams = new URLSearchParams(window.location.search); + + // view/edit + if (urlParams.has('id')) { + const exerciseId = urlParams.get('id'); + await retrieveExercise(exerciseId); + + editButton.addEventListener("click", handleEditExerciseButtonClick); + deleteButton.addEventListener("click", (async (id) => await deleteExercise(id)).bind(undefined, exerciseId)); + okButton.addEventListener("click", (async (id) => await updateGym(id)).bind(undefined, exerciseId)); + } + //create + else { + setReadOnly(false, "#form-exercise"); + + editButton.className += " hide"; + okButton.className = okButton.className.replace(" hide", ""); + cancelButton.className = cancelButton.className.replace(" hide", ""); + + okButton.addEventListener("click", async () => await updateGym()); + cancelButton.addEventListener("click", handleCancelButtonDuringCreate); + } +}); diff --git a/requirements.txt b/requirements.txt index 9feb375bde1e8fb7befe6c102dd29beeee7c6940..2dc56eb1e294d223a3effeabfa1919856317398a 100644 Binary files a/requirements.txt and b/requirements.txt differ