Last active
May 24, 2023 22:54
-
-
Save jlewin/4faef375894d76fc2b592013ae7a159f to your computer and use it in GitHub Desktop.
Canvas Quiz to Quizlet Import
This file contains hidden or 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
// 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