diff --git a/.gitignore b/.gitignore
index 04d23fc8..78076677 100644
--- a/.gitignore
+++ b/.gitignore
@@ -27,4 +27,5 @@ backend/token.json
backend/service_account_key.json
venv
backend/Eduaid
-.DS_Store
\ No newline at end of file
+.DS_Store
+backend/s2v_reddit_2015_md.tar.gz
diff --git a/eduaid_web/src/App.js b/eduaid_web/src/App.js
index e9eef0a0..49717d34 100644
--- a/eduaid_web/src/App.js
+++ b/eduaid_web/src/App.js
@@ -3,6 +3,7 @@ import { Routes, Route, HashRouter } from "react-router-dom";
import Home from "./pages/Home";
import Question_Type from "./pages/Question_Type";
import Text_Input from "./pages/Text_Input";
+import Review from "./pages/Review";
import Output from "./pages/Output";
import Previous from "./pages/Previous";
import NotFound from "./pages/PageNotFound";
@@ -14,6 +15,7 @@ function App() {
} />
} />
} />
+ } />
} />
} />
} />
diff --git a/eduaid_web/src/pages/Review.jsx b/eduaid_web/src/pages/Review.jsx
new file mode 100644
index 00000000..bee9f252
--- /dev/null
+++ b/eduaid_web/src/pages/Review.jsx
@@ -0,0 +1,233 @@
+import React, { useState, useEffect } from "react";
+import "../index.css";
+import logo_trans from "../assets/aossie_logo_transparent.png";
+import { Link, useNavigate } from "react-router-dom";
+import apiClient from "../utils/apiClient";
+
+const Review = () => {
+ const navigate = useNavigate();
+ const [loading, setLoading] = useState(false);
+ const [reviewData, setReviewData] = useState({
+ text: "",
+ difficulty: "",
+ numQuestions: 0,
+ questionType: "",
+ useWikipedia: false,
+ inputSource: "Text"
+ });
+
+ useEffect(() => {
+ const text = localStorage.getItem("textContent") || "";
+ const difficulty = localStorage.getItem("difficulty") || "Easy Difficulty";
+ const savedNumQuestions = localStorage.getItem("numQuestions");
+ const parsedNumQuestions = Number.parseInt(savedNumQuestions ?? "", 10);
+ const numQuestions = Number.isInteger(parsedNumQuestions) && parsedNumQuestions > 0 ? parsedNumQuestions : 10;
+ const questionType = localStorage.getItem("selectedQuestionType") || "";
+ const useWikipedia = localStorage.getItem("useWikipedia") === "1";
+ const savedInputSource = localStorage.getItem("inputSource");
+
+ let inputSource = savedInputSource || "text";
+ if (!savedInputSource) {
+ if (text.includes("uploaded file") || text.includes("Error uploading")) {
+ inputSource = "file";
+ } else if (text.includes("Google Doc")) {
+ inputSource = "url";
+ }
+ }
+
+ setReviewData({
+ text,
+ difficulty,
+ numQuestions,
+ questionType,
+ useWikipedia,
+ inputSource
+ });
+
+ if (!text || !questionType) {
+ navigate("/input");
+ }
+ }, [navigate]);
+
+ const getInputSourceLabel = (source) => {
+ const labels = {
+ text: "Text",
+ file: "File Upload",
+ url: "Google Doc URL"
+ };
+ return labels[source] || "Text";
+ };
+
+ const getQuestionTypeLabel = (type) => {
+ const types = {
+ get_shortq: "Short-Answer Type Questions",
+ get_mcq: "Multiple Choice Questions",
+ get_boolq: "True/False Questions",
+ get_problems: "All Questions"
+ };
+ return types[type] || type;
+ };
+
+ const getEndpoint = (difficulty, questionType) => {
+ if (difficulty !== "Easy Difficulty") {
+ if (questionType === "get_shortq") {
+ return "get_shortq_hard";
+ } else if (questionType === "get_mcq") {
+ return "get_mcq_hard";
+ }
+ }
+ return questionType;
+ };
+
+ const handleConfirmGenerate = async () => {
+ setLoading(true);
+ const endpoint = getEndpoint(reviewData.difficulty, reviewData.questionType);
+
+ const allowedEndpoints = ["get_shortq", "get_mcq", "get_boolq", "get_problems", "get_shortq_hard", "get_mcq_hard"];
+ if (!allowedEndpoints.includes(endpoint)) {
+ console.error("Invalid endpoint:", endpoint);
+ setLoading(false);
+ return;
+ }
+
+ const trimmedText = reviewData.text.trim();
+ if (!trimmedText || !Number.isInteger(reviewData.numQuestions) || reviewData.numQuestions <= 0) {
+ console.error("Invalid generation payload");
+ setLoading(false);
+ return;
+ }
+
+ try {
+ const requestData = {
+ input_text: trimmedText,
+ max_questions: reviewData.numQuestions,
+ use_mediawiki: reviewData.useWikipedia ? 1 : 0,
+ };
+
+ const responseData = await apiClient.post(`/${endpoint}`, requestData);
+ localStorage.setItem("qaPairs", JSON.stringify(responseData));
+
+ const quizDetails = {
+ difficulty: reviewData.difficulty,
+ numQuestions: reviewData.numQuestions,
+ date: new Date().toLocaleDateString(),
+ qaPair: responseData,
+ };
+
+ let last5Quizzes = [];
+ try {
+ const stored = localStorage.getItem("last5Quizzes");
+ if (stored) {
+ const parsed = JSON.parse(stored);
+ if (Array.isArray(parsed)) {
+ last5Quizzes = parsed;
+ }
+ }
+ } catch (parseError) {
+ console.error("Failed to parse last5Quizzes:", parseError);
+ }
+
+ last5Quizzes.push(quizDetails);
+ if (last5Quizzes.length > 5) {
+ last5Quizzes.shift();
+ }
+ localStorage.setItem("last5Quizzes", JSON.stringify(last5Quizzes));
+
+ navigate("/output");
+ } catch (error) {
+ console.error("Error:", error);
+ } finally {
+ setLoading(false);
+ }
+ };
+
+ return (
+
+ {loading && (
+
+ )}
+
+
+
+
+

+
+ Edu
+ Aid
+
+
+
+
+
+
Review Your Configuration
+
Please confirm the details before generating questions
+
+
+
+
+
+
Input Source
+
{getInputSourceLabel(reviewData.inputSource)}
+
+
+
+
Question Type
+
+ {getQuestionTypeLabel(reviewData.questionType)}
+
+
+
+
+
Number of Questions
+
{reviewData.numQuestions}
+
+
+
+
Difficulty Level
+
{reviewData.difficulty}
+
+
+
+
Use Wikipedia
+
+ {reviewData.useWikipedia ? "Yes" : "No"}
+
+
+
+ {reviewData.text && (
+
+
Content Preview
+
+ {reviewData.text.substring(0, 200)}
+ {reviewData.text.length > 200 && "..."}
+
+
+ )}
+
+
+
+
+
+
+
+
+
+
+
+ );
+};
+
+export default Review;
diff --git a/eduaid_web/src/pages/Text_Input.jsx b/eduaid_web/src/pages/Text_Input.jsx
index e341d331..59ed6302 100644
--- a/eduaid_web/src/pages/Text_Input.jsx
+++ b/eduaid_web/src/pages/Text_Input.jsx
@@ -1,11 +1,11 @@
-import React, { useState, useRef } from "react";
+import React, { useState, useRef, useEffect } from "react";
import "../index.css";
import logo_trans from "../assets/aossie_logo_transparent.png"
import stars from "../assets/stars.png";
import cloud from "../assets/cloud.png";
import { FaClipboard } from "react-icons/fa";
import Switch from "react-switch";
-import { Link,useNavigate } from "react-router-dom";
+import { Link, useNavigate } from "react-router-dom";
import apiClient from "../utils/apiClient";
const Text_Input = () => {
@@ -15,9 +15,38 @@ const Text_Input = () => {
const [numQuestions, setNumQuestions] = useState(10);
const [loading, setLoading] = useState(false);
const fileInputRef = useRef(null);
- const [fileContent, setFileContent] = useState("");
const [docUrl, setDocUrl] = useState("");
const [isToggleOn, setIsToggleOn] = useState(0);
+ const [inputSource, setInputSource] = useState("text");
+ const [error, setError] = useState("");
+
+ useEffect(() => {
+ const savedText = localStorage.getItem("textContent");
+ const savedDifficulty = localStorage.getItem("difficulty");
+ const savedNumQuestions = localStorage.getItem("numQuestions");
+ const savedWikipedia = localStorage.getItem("useWikipedia");
+ const savedInputSource = localStorage.getItem("inputSource");
+
+ if (savedText) setText(savedText);
+ if (savedDifficulty) setDifficulty(savedDifficulty);
+ if (savedNumQuestions !== null) {
+ const parsedNumQuestions = Number.parseInt(savedNumQuestions, 10);
+
+ setNumQuestions(
+ Number.isInteger(parsedNumQuestions) && parsedNumQuestions > 0
+ ? parsedNumQuestions
+ : 10
+ );
+ }
+ if (savedWikipedia !== null) {
+ const normalizedWikipedia =
+ savedWikipedia === "1" || savedWikipedia === "true"
+ ? 1
+ : 0;
+ setIsToggleOn(normalizedWikipedia);
+ }
+ if (savedInputSource) setInputSource(savedInputSource);
+ }, []);
const toggleSwitch = () => {
setIsToggleOn((isToggleOn + 1) % 2);
@@ -31,10 +60,16 @@ const Text_Input = () => {
try {
const data = await apiClient.postFormData("/upload", formData);
- setText(data.content || data.error);
+
+ if (data && typeof data.content === "string" && data.content.trim()) {
+ setText(data.content.trim());
+ setInputSource("file");
+ } else {
+ setError("Invalid file content received.");
+ }
} catch (error) {
console.error("Error uploading file:", error);
- setText("Error uploading file");
+ setError("Error uploading file. Please try again.");
}
}
};
@@ -48,31 +83,49 @@ const Text_Input = () => {
};
const handleSaveToLocalStorage = async () => {
- setLoading(true);
-
- // Check if a Google Doc URL is provided
- if (docUrl) {
+ const trimmedUrl = docUrl.trim();
+ const trimmedText = text.trim();
+ const validQuestionCount = Number.isInteger(numQuestions) && numQuestions > 0;
+ if (!validQuestionCount) {
+ console.error("Number of questions must be a positive integer");
+ setError("Number of questions must be at least 1.");
+ return;
+ }
+ if (trimmedUrl) {
+ setError("");
+ setLoading(true);
try {
- const data = await apiClient.post("/get_content", { document_url: docUrl });
+ const data = await apiClient.post("/get_content", { document_url: trimmedUrl });
+ if (!data || typeof data !== "string" || !data.trim()) {
+ setError("could not retrieve content from the provided URL.");
+ setLoading(false);
+ return;
+ }
+
+ const fetchedText = data.trim();
setDocUrl("");
- setText(data || "Error in retrieving");
+ setText(fetchedText);
+ setInputSource("url");
+
+ localStorage.setItem("textContent", fetchedText);
+ localStorage.setItem("difficulty", difficulty);
+ localStorage.setItem("numQuestions", numQuestions.toString());
+ localStorage.setItem("useWikipedia", isToggleOn.toString());
+ localStorage.setItem("inputSource", "url");
+ navigate("/review");
} catch (error) {
console.error("Error:", error);
- setText("Error retrieving Google Doc content");
+ setError("failed to fetch document content. Please try again.")
} finally {
setLoading(false);
}
- } else if (text) {
- // Proceed with existing functionality for local storage
- localStorage.setItem("textContent", text);
+ } else if (trimmedText) {
+ localStorage.setItem("textContent", trimmedText);
localStorage.setItem("difficulty", difficulty);
- localStorage.setItem("numQuestions", numQuestions);
-
- await sendToBackend(
- text,
- difficulty,
- localStorage.getItem("selectedQuestionType")
- );
+ localStorage.setItem("numQuestions", numQuestions.toString());
+ localStorage.setItem("useWikipedia", isToggleOn.toString());
+ localStorage.setItem("inputSource", inputSource);
+ navigate("/review");
}
};
@@ -88,53 +141,6 @@ const Text_Input = () => {
setNumQuestions((prev) => (prev > 0 ? prev - 1 : 0));
};
- const getEndpoint = (difficulty, questionType) => {
- if (difficulty !== "Easy Difficulty") {
- if (questionType === "get_shortq") {
- return "get_shortq_hard";
- } else if (questionType === "get_mcq") {
- return "get_mcq_hard";
- }
- }
- return questionType;
- };
-
- const sendToBackend = async (data, difficulty, questionType) => {
- const endpoint = getEndpoint(difficulty, questionType);
- try {
- const requestData = {
- input_text: data,
- max_questions: numQuestions,
- use_mediawiki: isToggleOn,
- };
-
- const responseData = await apiClient.post(`/${endpoint}`, requestData);
- localStorage.setItem("qaPairs", JSON.stringify(responseData));
-
- // Save quiz details to local storage
- const quizDetails = {
- difficulty,
- numQuestions,
- date: new Date().toLocaleDateString(),
- qaPair: responseData,
- };
-
- let last5Quizzes =
- JSON.parse(localStorage.getItem("last5Quizzes")) || [];
- last5Quizzes.push(quizDetails);
- if (last5Quizzes.length > 5) {
- last5Quizzes.shift(); // Keep only the last 5 quizzes
- }
- localStorage.setItem("last5Quizzes", JSON.stringify(last5Quizzes));
-
- navigate("/output");
- } catch (error) {
- console.error("Error:", error);
- } finally {
- setLoading(false);
- }
- };
-
return (
{loading && (
@@ -174,7 +180,10 @@ const Text_Input = () => {
className="absolute inset-0 p-8 pt-6 bg-[#83b6cc40] text-lg sm:text-xl rounded-2xl outline-none resize-none h-full overflow-y-auto text-white caret-white"
style={{ scrollbarWidth: "none", msOverflowStyle: "none" }}
value={text}
- onChange={(e) => setText(e.target.value)}
+ onChange={(e) => {
+ setText(e.target.value);
+ setInputSource("text");
+ }}
/>
@@ -202,6 +211,11 @@ const Text_Input = () => {
value={docUrl}
onChange={(e) => setDocUrl(e.target.value)}
/>
+ {error && (
+
+ {error}
+
+ )}
{/* Controls Section */}