//styles done
(function () {
const styles = `
body {
font-family: "Times New Roman", serif;
margin: 20px;
padding: 0;
background-color: #3c2f2f;
color: #f8f3d4;
background-size: cover;
background-attachment: fixed;
}
h1, h3, h5 {
font-family: "Garamond", serif;
color: #c9aa71;
text-shadow: 1px 1px 2px #1e130e;
}
button {
background-color: #704214;
color: #f8f3d4;
border: 2px solid #c9aa71;
padding: 10px 20px;
cursor: pointer;
border-radius: 5px;
box-shadow: 2px 2px 5px #1e130e;
font-family: "Courier New", monospace;
}
button:hover {
background-color: #c9aa71;
color: #704214;
}
#maindiv {
background-color: rgba(60, 47, 47, 0.9);
border: 2px solid #c9aa71;
padding: 20px;
border-radius: 10px;
box-shadow: 0 0 10px 2px #c9aa71;
}
#output, #answers, #solutions, #answers-to-alt-questions {
margin-top: 20px;
padding: 15px;
border: 2px solid #c9aa71;
border-radius: 10px;
background-color: #423635;
display: none;
}
label {
font-weight: bold;
color: #c9aa71;
}
input[type="password"], input[type="file"] {
background-color: #423635;
color: #f8f3d4;
border: 1px solid #c9aa71;
border-radius: 5px;
padding: 5px;
font-family: "Courier New", monospace;
}
select {
background-color: #423635;
color: #f8f3d4;
border: 1px solid #c9aa71;
border-radius: 5px;
padding: 5px;
font-family: "Courier New", monospace;
}
pre {
font-family: "Courier New", monospace;
color: #f8f3d4;
background-color: #322828;
padding: 10px;
border-radius: 5px;
border: 1px solid #704214;
}
`;
// adds the CSS styles into the page
const styleSheet = document.createElement("style");
styleSheet.type = "text/css";
styleSheet.innerText = styles;
document.head.appendChild(styleSheet);
// adds the html into the page
document.write(`
<div id="maindiv">
<h1>Alternate Exam Question Generator</h1>
<h3>Upload past exam papers to receive an alternate list of questions from the same core topic</h3>
<h3>When you receive your new questions, click the button below to reveal your answers</h3>
<h5>BE PATIENT! I'm fueled by AI, not magic!</h5>
<div id="model-selector">
<label>Select Model:</label>
<select id="model-dropdown">
<option value="CHATGPT" selected>CHATGPT</option>
<option value="GEMINI">GEMINI</option>
</select>
</div>
<br>
<div id="api-key-section">
<label>Enter your API Key:</label>
<input type="password" id="api-key" placeholder="sk-..." style="width: 60%;">
</div>
<br>
<form id="upload-form">
<input type="file" id="file-input" accept="application/pdf" required>
<button type="submit">Upload and Generate Questions</button>
</form>
<div id="output">
<h2>Generated Alternate Questions:</h2>
<div id="questions"></div>
<button id="reveal-questions-button" style="display: none;">Reveal Alternate Questions</button>
<div id="answers" style="display: none;">
<h3>Alternate Questions:</h3>
<pre id="answers-text"></pre>
<button id="reveal-solutions-button" style="margin-top: 10px;">Reveal Solutions</button>
</div>
</div>
<div id="solutions">
<h2>Solutions to Questions:</h2>
<pre id="solutions-text"></pre>
<button id="reveal-answers-to-alt-questions-button" style="margin-top: 10px;">Reveal Answers to Alternate Questions</button>
</div>
<div id="answers-to-alt-questions">
<h2>Answers to Alternate Questions:</h2>
<pre id="alt-answers-text"></pre>
</div>
</div>
`);
// imports PDF.js script for handling past exam paper PDFs
const script = document.createElement("script");
script.src = "https://cdnjs.cloudflare.com/ajax/libs/pdf.js/2.16.105/pdf.min.js";
document.head.appendChild(script);
// Wait until the PDF.js script is loaded
script.onload = function () {
// Event handlers and functionality
let apikey = ""; // Stores the API key entered by the user
// Handles pdf submission for uploading from the client
document.querySelector("#upload-form").onsubmit = async function (event) {
event.preventDefault();
const file = document.getElementById("file-input").files[0];
apikey = document.getElementById("api-key").value.trim();
if (!file || !apikey) {
alert("Please provide all required inputs.");
return;
}
// parses text from the uploaded paper, and then extracts test to be later added to a prompt
const text = await extractTextFromPDF(file);
const content = [
"You are an assistant that generates exam questions.",
`Here are some exam questions: ${text}. Generate alternate related questions, just the questions nothing else.`
];
const alternateQuestions = await model(apikey, content);
if (alternateQuestions) {
document.querySelector("#questions").innerHTML = `<p>Alternate questions are ready. Click the button below to reveal them.</p>`;
document.querySelector("#answers-text").textContent = alternateQuestions;
document.querySelector("#reveal-questions-button").style.display = "block";
document.querySelector("#output").style.display = "block";
} else {
alert("Failed to generate questions.");
}
};
// Reveals alternate questions when the button is clicked
document.querySelector("#reveal-questions-button").onclick = function () {
document.querySelector("#answers").style.display = "block";
this.style.display = "none";
};
// Generates solutions when the button is clicked
document.querySelector("#reveal-solutions-button").onclick = async function () {
const message = document.querySelector("#answers-text").textContent;
const content = [
"You are an assistant that generates exam questions answers.",
`Here are some exam questions: ${message}. Generate the answers of all of them.`
];
const solutions = await model(apikey, content);
if (solutions) {
document.querySelector("#solutions").style.display = "block";
document.querySelector("#solutions-text").textContent = solutions;
} else {
alert("Failed to generate solutions.");
}
};
// Reveals solutions when the button is clicked
document.querySelector("#reveal-answers-to-alt-questions-button").onclick = function () {
document.querySelector("#answers-to-alt-questions").style.display = "block";
};
// Extracts text from the pdf using PDF.js
async function extractTextFromPDF(file) {
const pdf = await pdfjsLib.getDocument(URL.createObjectURL(file)).promise;
let text = "";
for (let i = 1; i <= pdf.numPages; i++) {
const page = await pdf.getPage(i);
const content = await page.getTextContent();
text += content.items.map(item => item.str).join(" ") + "\n";
}
return text;
}
// User Chooses the model for generating the alt qs and answers
async function model(apiKey, content) {
const selectedModel = document.querySelector("#model-dropdown").value;
return selectedModel === "CHATGPT"
? await callGPTAPI(apiKey, content)
: await callGemini(apiKey, content);
}
// Calls the GPT API to generate output
async function callGPTAPI(apiKey, content) {
const response = await fetch("https://api.openai.com/v1/chat/completions", {
method: "POST",
headers: {
"Content-Type": "application/json",
"Authorization": `Bearer ${apiKey}`
},
body: JSON.stringify({
model: "gpt-4",
messages: [
{ role: "system", content: content[0] },
{ role: "user", content: content[1] }
],
max_tokens: 2000
})
});
const data = await response.json();
return response.ok ? data.choices[0].message.content.trim() : null;
}
// Calls the Gemini API to generate output
async function callGemini(apiKey, content) {
const response = await fetch(`https://generativelanguage.googleapis.com/v1beta/models/gemini-1.5-flash-latest:generateContent?key=${apiKey}`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
contents: [
{ parts: [{ text: content[0] }, { text: content[1] }] }
]
})
});
const data = await response.json();
return response.ok ? data.candidates[0].content.parts[0].text.trim() : null;
}
};
})();