Skip to content

Instantly share code, notes, and snippets.

@krooluang
Last active June 19, 2024 05:06
Show Gist options
  • Save krooluang/e28d09de7941b4a307bb1cbef6ce348d to your computer and use it in GitHub Desktop.
Save krooluang/e28d09de7941b4a307bb1cbef6ce348d to your computer and use it in GitHub Desktop.
TestOnline Examblog
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 };
}
<!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