diff --git a/backend/app/controllers/solving_quiz_controller.rb b/backend/app/controllers/solving_quiz_controller.rb new file mode 100644 index 0000000..43b24b2 --- /dev/null +++ b/backend/app/controllers/solving_quiz_controller.rb @@ -0,0 +1,39 @@ +class SolvingQuizController < ApplicationController + before_action :authenticate_user! + + def start_quiz + quiz = Quiz.find(params[:quiz_id]) + quiz_attempt = quiz.quiz_attempts.build(quiz_attempt_params) + if quiz_attempt.save + render json: quiz_attempt + else + render json: {error: quiz_attempt.errors.full_messages}, status: :unprocessable_entity + end + end + + def get_info_with_questions + #quiz with questions and answers but without info about correct answers + quiz = Quiz.find(params[:quiz_id]) + render json: quiz, include: [:questions, :answers.except(:is_correct)] + end + + def submit_answer + quiz_attempt = QuizAttempt.find(params[:quiz_attempt_id]) + answer = Answer.find(params[:answer_id]) + user_answer_params = {quiz_attempt_id: quiz_attempt.id, answer_id: answer.id} + user_answer = quiz_attempt.user_answers.build(user_answer_params) + if user_answer.save + render json: user_answer + else + render json: {error: user_answer.errors.full_messages}, status: :unprocessable_entity + end + end + + def finish_quiz + quiz_attempt = QuizAttempt.find(params[:quiz_attempt_id]) + quiz_attempt_params = {finished: true} + quiz_attempt.update(quiz_attempt_params) + render json: quiz_attempt + end + +end diff --git a/backend/app/models/quiz.rb b/backend/app/models/quiz.rb index d0d46ef..edcb49e 100644 --- a/backend/app/models/quiz.rb +++ b/backend/app/models/quiz.rb @@ -1,6 +1,8 @@ class Quiz < ApplicationRecord belongs_to :user has_many :questions, dependent: :destroy + has_many :quiz_attempts, dependent: :destroy validates :name, presence: true validates :description, presence: true + end diff --git a/backend/app/models/quiz_attempt.rb b/backend/app/models/quiz_attempt.rb new file mode 100644 index 0000000..656b372 --- /dev/null +++ b/backend/app/models/quiz_attempt.rb @@ -0,0 +1,5 @@ +class QuizAttempt < ApplicationRecord + belongs_to :user + belongs_to :quiz + has_many :user_answers, dependent: :destroy +end \ No newline at end of file diff --git a/backend/app/models/user.rb b/backend/app/models/user.rb index 1d2c71f..767d154 100644 --- a/backend/app/models/user.rb +++ b/backend/app/models/user.rb @@ -4,4 +4,5 @@ class User < ApplicationRecord devise :database_authenticatable, :registerable, :recoverable, :validatable, :jwt_authenticatable, jwt_revocation_strategy: self has_many :quizzes, dependent: :destroy + has_many :quiz_attempts, dependent: :destroy end \ No newline at end of file diff --git a/backend/app/models/user_answer.rb b/backend/app/models/user_answer.rb index ce28faf..4d8f2c4 100644 --- a/backend/app/models/user_answer.rb +++ b/backend/app/models/user_answer.rb @@ -1,4 +1,4 @@ class UserAnswer < ApplicationRecord - belongs_to :user + belongs_to :quiz_attempt belongs_to :answer end diff --git a/backend/db/migrate/20230923163539_create_quiz_attempts.rb b/backend/db/migrate/20230923163539_create_quiz_attempts.rb new file mode 100644 index 0000000..bdf8904 --- /dev/null +++ b/backend/db/migrate/20230923163539_create_quiz_attempts.rb @@ -0,0 +1,16 @@ +class CreateQuizAttempts < ActiveRecord::Migration[7.0] + def change + create_table :quiz_attempts do |t| + t.integer :user_id, null: false + t.integer :quiz_id, null: false + t.datetime :started_at + t.datetime :finished_at + t.boolean :is_active, default: true, null: false + + t.timestamps + end + + add_foreign_key :quiz_attempts, :users + add_foreign_key :quiz_attempts, :quizzes + end +end diff --git a/backend/db/migrate/20230923163558_change_user_answers_user_id_to_quiz_attempt_id.rb b/backend/db/migrate/20230923163558_change_user_answers_user_id_to_quiz_attempt_id.rb new file mode 100644 index 0000000..82a57a3 --- /dev/null +++ b/backend/db/migrate/20230923163558_change_user_answers_user_id_to_quiz_attempt_id.rb @@ -0,0 +1,6 @@ +class ChangeUserAnswersUserIdToQuizAttemptId < ActiveRecord::Migration[7.0] + def change + rename_column :user_answers, :user_id, :quiz_attempt_id + add_foreign_key :user_answers, :quiz_attempts + end +end \ No newline at end of file diff --git a/backend/db/schema.rb b/backend/db/schema.rb index 1a6f5e3..6a739f6 100644 --- a/backend/db/schema.rb +++ b/backend/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema[7.0].define(version: 2023_09_20_154155) do +ActiveRecord::Schema[7.0].define(version: 2023_09_23_163558) do create_table "answers", force: :cascade do |t| t.string "text" t.boolean "is_correct" @@ -28,6 +28,16 @@ t.index ["quiz_id"], name: "index_questions_on_quiz_id" end + create_table "quiz_attempts", force: :cascade do |t| + t.integer "user_id", null: false + t.integer "quiz_id", null: false + t.datetime "started_at" + t.datetime "finished_at" + t.boolean "is_active", default: true, null: false + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + end + create_table "quizzes", force: :cascade do |t| t.string "name" t.integer "user_id", null: false @@ -39,12 +49,12 @@ end create_table "user_answers", force: :cascade do |t| - t.integer "user_id", null: false + t.integer "quiz_attempt_id", null: false t.integer "answer_id", null: false t.datetime "created_at", null: false t.datetime "updated_at", null: false t.index ["answer_id"], name: "index_user_answers_on_answer_id" - t.index ["user_id"], name: "index_user_answers_on_user_id" + t.index ["quiz_attempt_id"], name: "index_user_answers_on_quiz_attempt_id" end create_table "users", force: :cascade do |t| @@ -64,7 +74,10 @@ add_foreign_key "answers", "questions" add_foreign_key "questions", "quizzes" + add_foreign_key "quiz_attempts", "quizzes" + add_foreign_key "quiz_attempts", "users" add_foreign_key "quizzes", "users" add_foreign_key "user_answers", "answers" - add_foreign_key "user_answers", "users" + add_foreign_key "user_answers", "quiz_attempts" + add_foreign_key "user_answers", "users", column: "quiz_attempt_id" end diff --git a/frontend/src/Components/Forms/EditQuizInfoForm.tsx b/frontend/src/Components/Forms/EditQuizInfoForm.tsx index 68bde05..ab09ea0 100644 --- a/frontend/src/Components/Forms/EditQuizInfoForm.tsx +++ b/frontend/src/Components/Forms/EditQuizInfoForm.tsx @@ -1,11 +1,4 @@ -import { - Box, - Button, - Checkbox, - FormControlLabel, - TextField, - Typography, -} from "@mui/material"; +import { Box, Button, TextField, Typography } from "@mui/material"; import { Answer, Question, Quiz } from "../../logic/interfaces"; import EditIcon from "@mui/icons-material/Edit"; import DeleteIcon from "@mui/icons-material/Delete"; @@ -58,7 +51,27 @@ const EditQuizInfoForm = (props: { } }) .catch(() => { - enqueueSnackbar("Quiz not deleted!", { variant: "error" }); + enqueueSnackbar("Quiz not deleted!", { variant: "info" }); + }); + }; + + const handlePublish = async () => { + confirm({ + description: + "Are you sure you want to publish this quiz? Once published, it cannot be edited.", + }) + .then(async () => { + props.updateQuiz({ ...props.quiz, is_public: true }); + const response = await updateQuiz({ ...props.quiz, is_public: true }); + if (response.status === "updated") { + enqueueSnackbar("Quiz published!", { variant: "success" }); + } + if (response.error) { + enqueueSnackbar("Something went wrong!", { variant: "error" }); + } + }) + .catch(() => { + enqueueSnackbar("Quiz not published!", { variant: "info" }); }); }; @@ -117,28 +130,21 @@ const EditQuizInfoForm = (props: { }) } /> - ) => - props.updateQuiz({ - ...props.quiz, - is_public: event.target.checked, - }) - } - disabled={!canBePublished} - /> - } - label="Public" - /> +