-
-
Save krooluang/e28d09de7941b4a307bb1cbef6ce348d to your computer and use it in GitHub Desktop.
TestOnline Examblog
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
function doGet() { | |
return HtmlService.createTemplateFromFile('index') | |
.evaluate() | |
.setTitle('TestOnline Examblog') | |
.addMetaTag('viewport', 'width=device-width, initial-scale=1') | |
.setXFrameOptionsMode(HtmlService.XFrameOptionsMode.ALLOWALL) | |
} | |
// This function retrieves questions from 'Sheet1' in the active Google Sheets document. | |
function getQuestions() { | |
// Get the 'Sheet1' sheet from the active spreadsheet. | |
const sheet = SpreadsheetApp.getActive().getSheetByName('Sheet1'); | |
// Get all values from the data range of the sheet. | |
const data = sheet.getDataRange().getValues(); | |
// Initialize an empty array to store questions. | |
let questions = []; | |
// Iterate through each row of data (excluding the header row at index 0). | |
for (let i = 1; i < data.length; i++) { | |
// Create an object for each question with its choices and correct answer. | |
questions.push({ | |
question: data[i][0], // Question is in column A (index 0). | |
choices: [data[i][1], data[i][2], data[i][3], data[i][4]], // Choices are in columns B to E. | |
answer: data[i][5] // Correct answer is in column F. | |
}); | |
} | |
// Return the array of questions. | |
return questions; | |
} | |
// This function submits answers and calculates scores. | |
function submitAnswers(name, classroom, num, answers) { | |
// Get the 'Sheet1' sheet from the active spreadsheet. | |
const sheet = SpreadsheetApp.getActive().getSheetByName('Sheet1'); | |
// Get all values from the data range of the sheet. | |
const data = sheet.getDataRange().getValues(); | |
// Initialize an empty array to store correct answers. | |
let correctAnswers = []; | |
// Extract correct answers from the data (assuming they are in column F). | |
for (let i = 1; i < data.length; i++) { | |
correctAnswers.push(data[i][5]); // Correct answers are in column F. | |
} | |
let score = 0; | |
// Calculate the score by comparing submitted answers with correct answers. | |
for (let j = 0; j < answers.length; j++) { | |
if (answers[j] === correctAnswers[j]) { | |
score++; | |
} | |
} | |
// Calculate percentage score. | |
const totalQuestions = correctAnswers.length; | |
const percentage = (score / totalQuestions) * 100; | |
// Determine pass status based on percentage score. | |
const passStatus = percentage >= 60 ? 'Passed' : 'Failed'; | |
// Get the 'Responses' sheet from the active spreadsheet. | |
const responseSheet = SpreadsheetApp.getActive().getSheetByName('Responses'); | |
// Append a new row with submission details (date, name, classroom, num, answers, score, total questions, percentage, pass status). | |
responseSheet.appendRow([new Date(), name, classroom, num, score, totalQuestions, percentage.toFixed(2), passStatus, ...answers]); | |
// Return an object with submission information. | |
return { message: "Submitted successfully!", name: name, score: score, total: totalQuestions, percentage: percentage, passStatus: passStatus }; | |
} | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE html> | |
<html> | |
<head> | |
<!-- Define base target and viewport --> | |
<base target="_top"> | |
<meta name="viewport" content="width=device-width, initial-scale=1"> | |
<!-- Include Bootstrap CSS and jQuery --> | |
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet"> | |
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script> | |
<!-- Include SweetAlert2 for alerts --> | |
<script src="https://cdn.jsdelivr.net/npm/sweetalert2@11"></script> | |
<!-- Include Google Fonts --> | |
<link href="https://fonts.googleapis.com/css2?family=Sarabun:wght@100;200;300;400;500;600;700;800&display=swap" | |
rel="stylesheet"> | |
<!-- Custom CSS --> | |
<style> | |
body { | |
font-family: 'Sarabun', sans-serif; | |
} | |
.container { | |
margin-top: 0px; | |
} | |
.question { | |
margin-bottom: 20px; | |
} | |
.spinner-border { | |
display: none; /* Ensure spinner is hidden initially */ | |
margin-left: 5px; | |
} | |
</style> | |
</head> | |
<body> | |
<!-- Main container --> | |
<div class="container my-5"> | |
<h2>Sample exam</h2> | |
<!-- Form for exam submission --> | |
<form id="myForm"> | |
<div class="row"> | |
<!-- Inputs for name, classroom, and number --> | |
<div class="col-md-4 mb-2"> | |
<label for="name" class="form-label">FullName</label> | |
<input type="text" class="form-control" id="name" required> | |
</div> | |
<div class="col-md-4 mb-2"> | |
<label for="classroom" class="form-label">Class</label> | |
<input type="text" class="form-control" id="classroom" required> | |
</div> | |
<div class="col-md-4 mb-2"> | |
<label for="num" class="form-label">Number</label> | |
<input type="number" class="form-control" id="num" required> | |
</div> | |
</div> | |
<!-- Container for questions --> | |
<div id="questions"></div> | |
<!-- Submit button --> | |
<button type="submit" class="btn btn-primary" id="submitBtn"> | |
Submit | |
<span class="spinner-border spinner-border-sm" role="status" aria-hidden="true"></span> <!-- Spinner for loading indication --> | |
</button> | |
</form> | |
<!-- Container for result display --> | |
<div id="result"></div> | |
</div> | |
<!-- JavaScript for form functionality --> | |
<script> | |
$(document).ready(function() { | |
// Load questions from Google Sheets when the document is ready | |
google.script.run.withSuccessHandler(loadQuestions).getQuestions(); | |
// Function to load questions into the form | |
function loadQuestions(questions) { | |
let questionsHtml = ''; | |
questions.forEach((question, index) => { | |
questionsHtml += ` | |
<div class="question"> | |
${index + 1}. ${question.question} | |
${question.choices.map((choice, i) => ` | |
<div class="form-check"> | |
<input class="form-check-input" type="radio" name="question${index}" id="question${index}choice${i}" value="${choice}" required> | |
<label class="form-check-label" for="question${index}choice${i}"> | |
${choice} | |
</label> | |
</div> | |
`).join('')} | |
</div> | |
`; | |
}); | |
$('#questions').html(questionsHtml); // Populate questions into the form | |
} | |
// Form submission handling | |
$('#myForm').submit(function(event) { | |
event.preventDefault(); // Prevent default form submission | |
// Get user inputs | |
let name = $('#name').val(); | |
let classroom = $('#classroom').val(); | |
let num = $('#num').val(); | |
let allAnswered = true; | |
// Check if all questions have been answered | |
$('#questions .question').each(function(index) { | |
if (!$(`input[name="question${index}"]:checked`).val()) { | |
allAnswered = false; | |
} | |
}); | |
if (!allAnswered) { | |
// Show error message if not all questions are answered | |
Swal.fire({ | |
icon: 'error', | |
title: 'Error', | |
text: 'Please answer all questions before submitting the form.', | |
confirmButtonText: 'OK' | |
}); | |
} else { | |
// Get answers from the form | |
let answers = []; | |
$('#questions .question').each(function(index) { | |
const answer = $(`input[name="question${index}"]:checked`).val(); | |
answers.push(answer); | |
}); | |
// Show spinner and disable submit button during submission | |
$('#submitBtn .spinner-border').css('display', 'inline-block'); | |
$('#submitBtn').prop('disabled', true); | |
// Call server-side function to submit answers and handle response | |
google.script.run.withSuccessHandler(function(response) { | |
// Hide spinner and enable submit button after response | |
$('#submitBtn .spinner-border').hide(); | |
$('#submitBtn').prop('disabled', false); | |
$('#myForm')[0].reset(); // Reset form fields | |
// Show exam results using SweetAlert2 | |
Swal.fire({ | |
title: 'Exam results', | |
html: `You answered correctly: ${response.score}/${response.total} <br>Percent: ${response.percentage.toFixed(2)}% <br> Exam results: ${response.passStatus}`, | |
icon: response.passStatus === 'Passed' ? 'success' : 'error', | |
confirmButtonText: 'OK' | |
}); | |
}).submitAnswers(name, classroom, num, answers); // Call server-side function to submit answers | |
} | |
}); | |
}); | |
</script> | |
</body> | |
</html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment