Skip to content

Commit

Permalink
quiz page and ai questions generation route and game and questions mo…
Browse files Browse the repository at this point in the history
…del prisma
  • Loading branch information
krishnathakkar29 committed Jul 16, 2024
1 parent dec48d6 commit 63c21dd
Show file tree
Hide file tree
Showing 8 changed files with 532 additions and 30 deletions.
10 changes: 10 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
},
"dependencies": {
"@auth/prisma-adapter": "^2.4.1",
"@google/generative-ai": "^0.14.1",
"@hookform/resolvers": "^3.9.0",
"@prisma/client": "^5.16.2",
"@radix-ui/react-avatar": "^1.1.0",
Expand Down
41 changes: 41 additions & 0 deletions prisma/migrations/20240714074128_game_question_model/migration.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
-- CreateEnum
CREATE TYPE "GameType" AS ENUM ('mcq', 'open_ended');

-- CreateTable
CREATE TABLE "Game" (
"id" TEXT NOT NULL,
"userId" TEXT NOT NULL,
"timeStarted" TIMESTAMP(3) NOT NULL,
"timeEnded" TIMESTAMP(3),
"topic" TEXT NOT NULL,
"gameType" "GameType" NOT NULL,

CONSTRAINT "Game_pkey" PRIMARY KEY ("id")
);

-- CreateTable
CREATE TABLE "Questions" (
"id" TEXT NOT NULL,
"question" TEXT NOT NULL,
"answer" TEXT NOT NULL,
"gameId" TEXT NOT NULL,
"options" JSONB,
"percentageCorrect" DOUBLE PRECISION,
"isCorrect" BOOLEAN,
"questionType" "GameType" NOT NULL,
"userAnswer" TEXT,

CONSTRAINT "Questions_pkey" PRIMARY KEY ("id")
);

-- CreateIndex
CREATE INDEX "userId" ON "Game"("userId");

-- CreateIndex
CREATE INDEX "gameId" ON "Questions"("gameId");

-- AddForeignKey
ALTER TABLE "Game" ADD CONSTRAINT "Game_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE;

-- AddForeignKey
ALTER TABLE "Questions" ADD CONSTRAINT "Questions_gameId_fkey" FOREIGN KEY ("gameId") REFERENCES "Game"("id") ON DELETE CASCADE ON UPDATE CASCADE;
39 changes: 39 additions & 0 deletions prisma/schema.prisma
Original file line number Diff line number Diff line change
Expand Up @@ -41,4 +41,43 @@ model User {
image String?
accounts Account[]
sessions Session[]
games Game[]
}

enum GameType {
mcq
open_ended
}

//game linked to the user , so one game has one user and one user has many games!
//one to many relationship
model Game {
id String @id @default(cuid())
userId String
timeStarted DateTime
timeEnded DateTime?
topic String
gameType GameType
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
questions Questions[]
@@index([userId], name: "userId")
}

//one to many relationship
//each game can have multiple questions
model Questions {
id String @id @default(cuid())
question String
answer String
gameId String
options Json? //only for mcq type questions
percentageCorrect Float? //similarity between the open ended question's answer and correct answer
isCorrect Boolean? //for mcqs
questionType GameType
userAnswer String?
game Game @relation(fields: [gameId], references: [id], onDelete: Cascade)
@@index([gameId], name: "gameId")
}
127 changes: 127 additions & 0 deletions src/app/api/questions/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
import { auth } from "@/auth";
import { quizCreationSchema } from "@/schemas/form/quiz";
import { NextResponse } from "next/server";
import { ZodError } from "zod";
const {
GoogleGenerativeAI,
HarmCategory,
HarmBlockThreshold,
} = require("@google/generative-ai");

const apiKey = process.env.GEMINI_API_KEY;
const genAI = new GoogleGenerativeAI(apiKey);
const model = genAI.getGenerativeModel({
model: "gemini-1.5-flash",
});
const generationConfig = {
temperature: 1,
topP: 0.95,
topK: 64,
maxOutputTokens: 8192,
responseMimeType: "text/plain",
};
export async function POST(req: Request, res: Response) {
const session = await auth();

const body = await req.json();
try {
const { amount, topic } = quizCreationSchema.parse(body);
const chatSession = model.startChat({
generationConfig,
// history: [
// {
// role: "user",
// parts: [
// {
// text: `You are to generate a random hard mcq question about ${topic} , ${amount} times`,
// },
// ],
// },
// {
// role: "system",
// parts: [
// {
// text:
// "You are a helpful AI that is able to generate mcq questions and answers, the length of each answer should not be more than 15 words, store all answers and questions and options in a JSON array" +
// `
// if is it asked to generate question 3 times then there should be an array of objects where each object is of the following output format -

// {
// question: "question",
// answer: "answer with max length of 15 words",
// option1: "option1 with max length of 15 words",
// option2: "option2 with max length of 15 words",
// option3: "option3 with max length of 15 words",
// }
// `,
// },
// ],
// },
// ],
history: [],
});
const prompt = `
You are a helpful AI that is able to generate mcq questions and answers, the length of each answer should not be more than 15 words, store all answers and questions and options in a JSON array,
You are to generate a random hard mcq question about ${topic} , ${amount} times,
if is it asked to generate question 3 times then there should be an array of objects where each object should be of the object structure give below and strictly give a response which when passed under JSON.parse() , should not give any error:-
{
question: "question",
answer: "answer with max length of 15 words",
option1: "option1 with max length of 15 words",
option2: "option2 with max length of 15 words",
option3: "option3 with max length of 15 words",
}
Make sure to properly escape any characters that could invalidate the JSON format, such as quotes inside strings, and errors are like these SyntaxError: Expected ',' or '}' after property value in JSON do not occur.
`;
const result = await chatSession.sendMessage(prompt);
let resi = result.response.text().replace(/'/g, '"');
resi = resi.replace(/(\w)"(\w)/g, "$1'$2");

resi = resi
.replace(/```json/g, "")
.replace(/```/g, "")
.trim();

// console.log("naya reiessssss\n", resi);

let parsedResponse;
try {
parsedResponse = JSON.parse(resi);
console.log("yeh hai json wala\n", parsedResponse);
} catch (parseError) {
console.error("JSON parse error:", parseError);
return NextResponse.json(
{ error: "An error occurred while parsing the AI response." },
{
status: 500,
}
);
}

return NextResponse.json(
{
questions: parsedResponse,
},
{
status: 200,
}
);
} catch (error) {
if (error instanceof ZodError) {
return NextResponse.json(
{ error: error.issues },
{
status: 400,
}
);
} else {
console.error("elle gpt error", error);
return NextResponse.json(
{ error: "An unexpected error occurred." },
{
status: 500,
}
);
}
}
}
31 changes: 2 additions & 29 deletions src/components/forms/QuizCreation.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,7 @@ const QuizCreation = (props: Props) => {
resolver: zodResolver(quizCreationSchema),
defaultValues: {
amount: 3,
topic: "",
type: "mcq",
topic: ""
},
});

Expand Down Expand Up @@ -97,33 +96,7 @@ const QuizCreation = (props: Props) => {
)}
/>

<div className="flex justify-between">
<Button
variant={
form.getValues("type") === "mcq" ? "default" : "secondary"
}
className="w-1/2 rounded-none rounded-l-lg"
onClick={() => {
form.setValue("type", "mcq");
}}
type="button"
>
<CopyCheck className="w-4 h-4 mr-2" /> Multiple Choice
</Button>
<Separator orientation="vertical" />
<Button
variant={
form.getValues("type") === "open_ended"
? "default"
: "secondary"
}
className="w-1/2 rounded-none rounded-r-lg"
onClick={() => form.setValue("type", "open_ended")}
type="button"
>
<BookOpen className="w-4 h-4 mr-2" /> Open Ended
</Button>
</div>

<Button type="submit">Submit</Button>
</form>
</Form>
Expand Down
Loading

0 comments on commit 63c21dd

Please sign in to comment.