From d98b4b01a42168bdf0129e255c81ed6497c7399c Mon Sep 17 00:00:00 2001 From: Plumey Simon Date: Tue, 2 Apr 2024 17:07:41 +0200 Subject: [PATCH 1/7] WIP --- frontend/src/components/AnswerForm.vue | 23 +++++++++++++++-------- frontend/src/views/QuizView.vue | 6 ++++-- 2 files changed, 19 insertions(+), 10 deletions(-) diff --git a/frontend/src/components/AnswerForm.vue b/frontend/src/components/AnswerForm.vue index 87873f7..645eb7a 100644 --- a/frontend/src/components/AnswerForm.vue +++ b/frontend/src/components/AnswerForm.vue @@ -41,8 +41,8 @@ const fetchOptions = async () => { } const hasAskedOptions = async () => { - const response = await axios.get(`/api/question/has_asked_options/`); // TODO - return !!response.data.has_asked_options; // TODO + const response = await axios.get(`/api/question/options_asked`); + return !!response.data.options_asked; } const newQuestion = () => { @@ -52,16 +52,23 @@ const newQuestion = () => { emit('newQuestion'); } -onMounted(() => { +const init = async () => { // ask backend if the user has already ask options in this category // if so, show the options form - /* - if (hasAskedOptions()) { + + console.log("request Has asked options") + const response = await hasAskedOptions(); + console.log("response Has asked options") + console.log(response); + if (response) { fetchOptions(); show_text_form.value = false; - }*/ + } // if not, show the text form (default value is true) +} +onMounted(() => { + init(); }); @@ -77,7 +84,7 @@ onMounted(() => {

- Good job! {{ response_to_answer.right_answer }} was the right answer! + Good job! "{{ response_to_answer.right_answer }}" was the right answer!

Unfortunately, your answer "{{ response_to_answer.answer_sent }}" was wrong. The correct answer was "{{ @@ -94,7 +101,7 @@ onMounted(() => {

Unfortunately, your answer "{{ response_to_answer.answer_sent }}" was wrong. The correct answer was "{{ response_to_answer.right_answer }}"

- +
diff --git a/frontend/src/views/QuizView.vue b/frontend/src/views/QuizView.vue index 17d4761..461ac9d 100644 --- a/frontend/src/views/QuizView.vue +++ b/frontend/src/views/QuizView.vue @@ -9,11 +9,13 @@ const route = useRoute(); const id_category = route.params.id_category; // variables specific to this component -const question = ref(""); +const question = ref(null); const fetchNewQuestion = async () => { + console.log("fetch new question"); const response = await axios.get(`/api/question/${id_category}/new`, { }); + console.log("response to new question"); question.value = response.data.text; } @@ -29,7 +31,7 @@ onMounted(() => {

Question

{{ question }}

- + From 8ff03b28c7881a0e3712440afd4c1d74ca6c6066 Mon Sep 17 00:00:00 2001 From: Alessio Comi Date: Fri, 5 Apr 2024 14:54:58 +0200 Subject: [PATCH 2/7] add backend login, logout and signup --- .github/workflows/django_test.yml | 36 ------------------- api/masteriq/urls.py | 4 ++- api/masteriqapp/views/AuthenticationView.py | 35 ++++++++++++++++++ api/masteriqapp/views/IQView.py | 7 ++-- api/masteriqapp/views/__init__.py | 1 + .../views/{ => Authentication}/LoginView.vue | 0 .../src/views/Authentication/LogoutView.vue | 0 .../src/views/Authentication/RegisterView.vue | 36 +++++++++++++++++++ frontend/src/views/RegisterView.vue | 5 --- 9 files changed, 79 insertions(+), 45 deletions(-) delete mode 100644 .github/workflows/django_test.yml create mode 100644 api/masteriqapp/views/AuthenticationView.py rename frontend/src/views/{ => Authentication}/LoginView.vue (100%) create mode 100644 frontend/src/views/Authentication/LogoutView.vue create mode 100644 frontend/src/views/Authentication/RegisterView.vue delete mode 100644 frontend/src/views/RegisterView.vue diff --git a/.github/workflows/django_test.yml b/.github/workflows/django_test.yml deleted file mode 100644 index f4af184..0000000 --- a/.github/workflows/django_test.yml +++ /dev/null @@ -1,36 +0,0 @@ -name: Django CI - -on: - push: - branches: [ "main" ] - pull_request: - branches: [ "main" ] - -jobs: - build: - - runs-on: ubuntu-latest - strategy: - max-parallel: 4 - matrix: - python-version: [3.11] - - steps: - - uses: actions/checkout@v3 - - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v3 - with: - python-version: ${{ matrix.python-version }} - - name: Install Dependencies - run: | - python -m pip install --upgrade pip - cd api - pip install -r requirements.txt - - name: Copy .env - run: | - cd api - cp .env.cicd .env - - name: Run Tests - run: | - cd api - python manage.py test diff --git a/api/masteriq/urls.py b/api/masteriq/urls.py index 2a968fa..b6bb9b9 100644 --- a/api/masteriq/urls.py +++ b/api/masteriq/urls.py @@ -26,8 +26,10 @@ router.register("category", views.IQView, basename="category") router.register("question", views.QuestionView, basename="question") router.register("rank", views.RankView, basename="rank") +router.register("user", views.AuthenticationView, basename="user") + urlpatterns = [ path('admin/', admin.site.urls), - path("api/", include(router.urls)) + path("api/", include(router.urls)), ] diff --git a/api/masteriqapp/views/AuthenticationView.py b/api/masteriqapp/views/AuthenticationView.py new file mode 100644 index 0000000..420df27 --- /dev/null +++ b/api/masteriqapp/views/AuthenticationView.py @@ -0,0 +1,35 @@ +from rest_framework import viewsets, status +from rest_framework.response import Response +from django.contrib.auth import authenticate, login, logout +from django.contrib.auth.models import User +from rest_framework.decorators import action +from rest_framework.permissions import AllowAny + + +class AuthenticationView(viewsets.ViewSet): + @action(detail=False, methods=['POST'], permission_classes=[AllowAny]) + def login(self, request): + username = request.data.get('username') + password = request.data.get('password') + user = authenticate(username=username, password=password) + if user: + login(request, user) + return Response({'message': 'Login successful'}, status=status.HTTP_200_OK) + else: + return Response({'message': 'Invalid credentials'}, status=status.HTTP_401_UNAUTHORIZED) + + @action(detail=False, methods=['POST']) + def logout(self, request): + logout(request) + return Response({'message': 'Logout successful'}, status=status.HTTP_200_OK) + + @action(detail=False, methods=['POST'], permission_classes=[AllowAny]) + def signup(self, request): + username = request.data.get('username') + password = request.data.get('password') + if not User.objects.filter(username=username).exists(): + user = User.objects.create_user(username=username, password=password) + login(request, user) + return Response({'message': 'Signup successful'}, status=status.HTTP_201_CREATED) + else: + return Response({'message': 'Username already exists'}, status=status.HTTP_400_BAD_REQUEST) diff --git a/api/masteriqapp/views/IQView.py b/api/masteriqapp/views/IQView.py index f72b87c..a01bf00 100644 --- a/api/masteriqapp/views/IQView.py +++ b/api/masteriqapp/views/IQView.py @@ -8,6 +8,7 @@ from django.apps import apps from rest_framework import status from rest_framework.response import Response +from django.contrib.auth.decorators import login_required masteriq = apps.get_app_config("masteriqapp") @@ -27,16 +28,16 @@ def category_image(self, request, pk): @action(detail=False, methods=["GET"], url_path="iq") def category_with_iq(self, request): - #TODO: RESTRICT TO CONNECTED USER + # TODO: RESTRICT TO CONNECTED USER answer_dict = {} for category in self.queryset: cat_dict = { "category_name": category.name, - "user_iq": 100 #TODO: REPLACE WITH USER IQ + "user_iq": 100 # TODO: REPLACE WITH USER IQ } answer_dict[category.id] = cat_dict return Response(status=status.HTTP_200_OK, data=answer_dict) @action(detail=True, methods=["GET"]) def user_iq(self, request, pk): - return Response(status=status.HTTP_200_OK, data={"user_iq":random.randint(1,200)}) \ No newline at end of file + return Response(status=status.HTTP_200_OK, data={"user_iq": random.randint(1, 200)}) diff --git a/api/masteriqapp/views/__init__.py b/api/masteriqapp/views/__init__.py index 69ea03e..953cce9 100644 --- a/api/masteriqapp/views/__init__.py +++ b/api/masteriqapp/views/__init__.py @@ -1,3 +1,4 @@ from .IQView import IQView from .QuestionView import QuestionView from .RankView import RankView +from .AuthenticationView import AuthenticationView diff --git a/frontend/src/views/LoginView.vue b/frontend/src/views/Authentication/LoginView.vue similarity index 100% rename from frontend/src/views/LoginView.vue rename to frontend/src/views/Authentication/LoginView.vue diff --git a/frontend/src/views/Authentication/LogoutView.vue b/frontend/src/views/Authentication/LogoutView.vue new file mode 100644 index 0000000..e69de29 diff --git a/frontend/src/views/Authentication/RegisterView.vue b/frontend/src/views/Authentication/RegisterView.vue new file mode 100644 index 0000000..7aaa817 --- /dev/null +++ b/frontend/src/views/Authentication/RegisterView.vue @@ -0,0 +1,36 @@ + + + diff --git a/frontend/src/views/RegisterView.vue b/frontend/src/views/RegisterView.vue deleted file mode 100644 index 951d01e..0000000 --- a/frontend/src/views/RegisterView.vue +++ /dev/null @@ -1,5 +0,0 @@ - \ No newline at end of file From 20b51687ea42892dad74087c748b3ced2db9d9d7 Mon Sep 17 00:00:00 2001 From: Alessio Comi Date: Fri, 5 Apr 2024 15:08:35 +0200 Subject: [PATCH 3/7] preparing folder authentication for authentication views and adapting router index.js --- frontend/src/router/index.js | 15 +++++--- .../src/views/Authentication/RegisterView.vue | 36 ------------------- .../src/views/Authentication/SignUpView.vue | 0 3 files changed, 10 insertions(+), 41 deletions(-) delete mode 100644 frontend/src/views/Authentication/RegisterView.vue create mode 100644 frontend/src/views/Authentication/SignUpView.vue diff --git a/frontend/src/router/index.js b/frontend/src/router/index.js index e4de527..c51e197 100644 --- a/frontend/src/router/index.js +++ b/frontend/src/router/index.js @@ -20,14 +20,19 @@ const router = createRouter({ component: () => import('../views/AddQuestionView.vue') }, { - path: '/login', + path: 'Authentication/login', name: 'Login', - component: () => import('../views/LoginView.vue') + component: () => import('../views/Authentication/LoginView.vue') }, { - path: '/register', - name: 'Register', - component: () => import('../views/RegisterView.vue') + path: 'Authentication/logout', + name: 'Logout', + component: () => import('../views/Authentication/LogoutView.vue') + }, + { + path: 'Authentication/Signup', + name: 'Signup', + component: () => import('../views/Authentication/SignUpView.vue') } ] }) diff --git a/frontend/src/views/Authentication/RegisterView.vue b/frontend/src/views/Authentication/RegisterView.vue deleted file mode 100644 index 7aaa817..0000000 --- a/frontend/src/views/Authentication/RegisterView.vue +++ /dev/null @@ -1,36 +0,0 @@ - - - diff --git a/frontend/src/views/Authentication/SignUpView.vue b/frontend/src/views/Authentication/SignUpView.vue new file mode 100644 index 0000000..e69de29 From 15866cd97492d097263d229941fe12eb6bca38c7 Mon Sep 17 00:00:00 2001 From: Strogator <102219695+Strogator@users.noreply.github.com> Date: Fri, 5 Apr 2024 15:19:53 +0200 Subject: [PATCH 4/7] my bad (#77) --- frontend/src/views/Authentication/LogoutView.vue | 3 +++ frontend/src/views/Authentication/SignUpView.vue | 3 +++ 2 files changed, 6 insertions(+) diff --git a/frontend/src/views/Authentication/LogoutView.vue b/frontend/src/views/Authentication/LogoutView.vue index e69de29..956dc09 100644 --- a/frontend/src/views/Authentication/LogoutView.vue +++ b/frontend/src/views/Authentication/LogoutView.vue @@ -0,0 +1,3 @@ + \ No newline at end of file diff --git a/frontend/src/views/Authentication/SignUpView.vue b/frontend/src/views/Authentication/SignUpView.vue index e69de29..08d003c 100644 --- a/frontend/src/views/Authentication/SignUpView.vue +++ b/frontend/src/views/Authentication/SignUpView.vue @@ -0,0 +1,3 @@ + \ No newline at end of file From be448620f8e07450c316adb42407195f91566b08 Mon Sep 17 00:00:00 2001 From: Plumey Simon Date: Tue, 9 Apr 2024 13:40:52 +0200 Subject: [PATCH 5/7] Creating apiclient for fetching data --- frontend/src/api_client.js | 20 ++++++++++++++++++++ frontend/src/main.js | 5 ----- frontend/src/views/HomeView.vue | 14 +++----------- 3 files changed, 23 insertions(+), 16 deletions(-) create mode 100644 frontend/src/api_client.js diff --git a/frontend/src/api_client.js b/frontend/src/api_client.js new file mode 100644 index 0000000..0a775d6 --- /dev/null +++ b/frontend/src/api_client.js @@ -0,0 +1,20 @@ +import axios from 'axios'; + +// set the default base url for axios requests +const API_SERVER = import.meta.env.VITE_API_SERVER; +axios.defaults.baseURL = API_SERVER; +axios.defaults.withCredentials = true; + +export default +class APIClient { + /** + * Get all the categories with current IQ in each category + * (keys are category id) + * @returns + */ + static async getCategories() + { + const response = await axios.get(`/api/category/iq/`); + return response.data; + } +} \ No newline at end of file diff --git a/frontend/src/main.js b/frontend/src/main.js index 422f8f5..5a5dbdb 100644 --- a/frontend/src/main.js +++ b/frontend/src/main.js @@ -3,11 +3,6 @@ import './assets/main.css' import { createApp } from 'vue' import App from './App.vue' import router from './router' -import axios from 'axios' - -const API_SERVER = import.meta.env.VITE_API_SERVER; -axios.defaults.baseURL = API_SERVER; -axios.defaults.withCredentials = true; const app = createApp(App) diff --git a/frontend/src/views/HomeView.vue b/frontend/src/views/HomeView.vue index 3b6268b..2101ad6 100644 --- a/frontend/src/views/HomeView.vue +++ b/frontend/src/views/HomeView.vue @@ -1,21 +1,13 @@ From a867d67a3dd1c9a22035e0a45304931112a7c42c Mon Sep 17 00:00:00 2001 From: Plumey Simon Date: Tue, 9 Apr 2024 14:07:04 +0200 Subject: [PATCH 6/7] Fixing routes --- frontend/src/router/index.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/frontend/src/router/index.js b/frontend/src/router/index.js index c51e197..ee9531b 100644 --- a/frontend/src/router/index.js +++ b/frontend/src/router/index.js @@ -20,17 +20,17 @@ const router = createRouter({ component: () => import('../views/AddQuestionView.vue') }, { - path: 'Authentication/login', + path: '/login', name: 'Login', component: () => import('../views/Authentication/LoginView.vue') }, { - path: 'Authentication/logout', + path: '/logout', name: 'Logout', component: () => import('../views/Authentication/LogoutView.vue') }, { - path: 'Authentication/Signup', + path: '/signup', name: 'Signup', component: () => import('../views/Authentication/SignUpView.vue') } From cf13415eb5df181067022eaec2a95ef68c57d01b Mon Sep 17 00:00:00 2001 From: Plumey Simon Date: Tue, 9 Apr 2024 16:05:52 +0200 Subject: [PATCH 7/7] Fixing bug after refresh --- frontend/src/components/AnswerForm.vue | 38 ++++++++++---------------- frontend/src/views/QuizView.vue | 13 +++++---- 2 files changed, 23 insertions(+), 28 deletions(-) diff --git a/frontend/src/components/AnswerForm.vue b/frontend/src/components/AnswerForm.vue index 645eb7a..7e0fd9c 100644 --- a/frontend/src/components/AnswerForm.vue +++ b/frontend/src/components/AnswerForm.vue @@ -1,11 +1,6 @@ diff --git a/frontend/src/views/QuizView.vue b/frontend/src/views/QuizView.vue index 461ac9d..a6c81ac 100644 --- a/frontend/src/views/QuizView.vue +++ b/frontend/src/views/QuizView.vue @@ -10,13 +10,16 @@ const id_category = route.params.id_category; // variables specific to this component const question = ref(null); +const hasAskedOptions = ref(false); const fetchNewQuestion = async () => { - console.log("fetch new question"); - const response = await axios.get(`/api/question/${id_category}/new`, { + const responseQuestion = await axios.get(`/api/question/${id_category}/new`, { }); - console.log("response to new question"); - question.value = response.data.text; + question.value = responseQuestion.data.text; + + // wait for the question before checking if the user has asked for options + const responseOptionAsked = await axios.get(`/api/question/options_asked`); + hasAskedOptions.value = !!responseOptionAsked.data.options_asked; } onMounted(() => { @@ -30,7 +33,7 @@ onMounted(() => {

Answer correctly to the question and earn as many IQs as possible!

Question

{{ question }}

- +