Skip to content

Instantly share code, notes, and snippets.

@jlewin
Last active May 24, 2023 22:54
Show Gist options
  • Save jlewin/4faef375894d76fc2b592013ae7a159f to your computer and use it in GitHub Desktop.
Save jlewin/4faef375894d76fc2b592013ae7a159f to your computer and use it in GitHub Desktop.
Canvas Quiz to Quizlet Import
// Extracts the content of textNode children, skipping descendant elements
function getTextContent(e) {
return [...e.childNodes] // has childNodes inside, including text ones
.filter(child => child.nodeType === 3) // get only text nodes
.filter(child => child.textContent.trim()) // eliminate empty text
.map(textNode => textNode.textContent) // extract text content
.join('').trim();
}
// Selects either the .answer_html, .answer_text, or textContent of the given element
function getAnswerText(e) {
let html = e.querySelector('label .answer_html')?.textContent.trim();
let text = e.querySelector('label .answer_text')?.textContent.trim();
return html || text || getTextContent(e);
}
// Parses the loaded Canvas Exam/HW for .display_question(s) and extracts content
// used during the Quizlet import process
function extractQuestions() {
// Guard for and drop essay questions
let nodes = document.querySelectorAll('.display_question:not(.essay_question)');
let elems = Array.from(nodes);
// Iterate .display_question elements, extracting a question/answer for each
let items = elems.flatMap(function (e) {
// Collect question text
let questionElem = e.querySelector('.question_text');
let questionText = questionElem.textContent.trim();
if (questionElem.querySelector('input.question_input'))
{
// Clone and replace input elements with ________________ placeholders
const clonedElem = questionElem.cloneNode(true);
let questionInputs = Array.from(clonedElem.querySelectorAll('input.question_input'));
// Replace and inject placeholders
questionInputs.forEach(q => q.replaceWith(q.ownerDocument.createTextNode('________________')));
// Extract new revised text
questionText = clonedElem.textContent.trim();
}
// Determine question type
let questionType = "multipleChoice";
if (e.classList.contains('fill_in_multiple_blanks_question')) {
questionType = 'fillInBlanks';
} else if (e.classList.contains('matching_question')) {
questionType = 'matchQuestionToList'
}
switch (questionType) {
case 'fillInBlanks':
// Multiple answers listed for a single question. Combine with 'and'...
let multiAnswer = e.querySelectorAll('.answers .answer_group');
var answerValues = Array.from(multiAnswer).map(function (answer) {
let selectedCorrect = answer.querySelector('.selected_answer.correct_answer:not(.skipped)');
if (selectedCorrect)
return getTextContent(selectedCorrect);
return getTextContent(answer.querySelector('.correct_answer:not(.skipped)'));
});
return questionText + '\t' + answerValues.join(' and ');
case 'matchQuestionToList':
return Array.from(e.querySelectorAll('.selected_answer')).map(function (a) {
let question = a.querySelector('.answer_match_left').textContent;
let answer = a.querySelector('.answer_match_right select option').textContent;
return question + '\t' + answer;
});
default: // multipleChoice
let correctA = e.querySelector('.correct_answer:not(.skipped)');
// General case
return questionText + '\t' + getAnswerText(correctA);
}
});
// Copy console output to quizlet and use '~~' as 'Between cards' custom separator
console.log(items.join('~~'))
}
extractQuestions();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment