{"version":3,"file":"diagnostic-Dq2UECPH.js","sources":["../../../client/app/bundles/Diagnostic/actions/concepts.js","../../../client/app/bundles/Diagnostic/components/questions/questionForm.jsx","../../../client/app/bundles/Diagnostic/components/concepts/concept.jsx","../../../client/app/bundles/Diagnostic/components/concepts/concepts.jsx","../../../client/app/bundles/Diagnostic/actions/massEdit.js","../../../client/app/bundles/Diagnostic/components/questions/conceptResultList.jsx","../../../client/app/bundles/Diagnostic/actions/display.js","../../../client/app/bundles/Diagnostic/components/questions/massEditContainer.jsx","../../../client/app/bundles/Diagnostic/actions/filters.js","../../../client/app/bundles/Diagnostic/libs/partsOfSpeechTagging.js","../../../client/app/bundles/Diagnostic/libs/algorithms/changeObjects.js","../../../client/app/bundles/Diagnostic/libs/algorithms/joiningWords.js","../../../client/app/bundles/Diagnostic/libs/algorithms/spacingBeforePunctuation.js","../../../client/app/bundles/Diagnostic/libs/quillNormalizer.js","../../../client/app/bundles/Diagnostic/libs/requiredWords.js","../../../client/app/bundles/Diagnostic/libs/sharedResponseFunctions.js","../../../client/app/bundles/Diagnostic/libs/question.js","../../../client/app/bundles/Diagnostic/libs/validEndingPunctuation.js","../../../client/app/bundles/Diagnostic/libs/sentenceFragment.js","../../../client/app/bundles/Diagnostic/components/questions/POSForResponse.jsx","../../../client/app/bundles/Diagnostic/components/questions/POSIndex.jsx","../../../client/app/bundles/Diagnostic/components/questions/POSForResponsesList.jsx","../../../client/app/bundles/Diagnostic/components/questions/responseComponent.jsx","../../../client/app/bundles/Diagnostic/components/questions/responseRouteWrapper.jsx","../../../client/app/bundles/Diagnostic/components/fillInBlank/fillInBlankForm.jsx","../../../client/app/bundles/Diagnostic/components/fillInBlank/editFillInBlank.jsx","../../../client/app/bundles/Diagnostic/components/renderForQuestions/submitResponse.js","../../../client/app/bundles/Diagnostic/components/renderForQuestions/updateResponseResource.js","../../../client/app/bundles/Diagnostic/components/fillInBlank/testFillInBlankQuestionContainer.jsx","../../../client/app/bundles/Diagnostic/components/fillInBlank/fillInBlankQuestion.jsx","../../../client/app/bundles/Diagnostic/components/fillInBlank/fillInBlankQuestions.jsx","../../../client/app/bundles/Diagnostic/components/fillInBlank/newFillInBlank.jsx","../../../client/app/bundles/Diagnostic/components/lessons/lesson.jsx","../../../client/app/bundles/Diagnostic/components/lessons/lessons.jsx","../../../client/app/bundles/Diagnostic/components/misc/diffedResponse.jsx","../../../client/app/bundles/Diagnostic/components/misc/answerVisualizer.jsx","../../../client/app/bundles/Diagnostic/components/focusPoints/editFocusPointsContainer.jsx","../../../client/app/bundles/Diagnostic/components/focusPoints/focusPointsContainer.jsx","../../../client/app/bundles/Diagnostic/components/focusPoints/newFocusPointsContainer.jsx","../../../client/app/bundles/Diagnostic/components/incorrectSequence/editIncorrectSequenceContainer.jsx","../../../client/app/bundles/Diagnostic/components/incorrectSequence/incorrectSequenceContainer.jsx","../../../client/app/bundles/Diagnostic/components/incorrectSequence/newIncorrectSequenceContainer.jsx","../../../client/app/bundles/Diagnostic/components/questions/boilerplateFeedback.jsx","../../../client/app/bundles/Diagnostic/components/questions/chooseModelContainer.jsx","../../../client/app/bundles/Diagnostic/actions.js","../../../client/app/bundles/Diagnostic/components/renderForQuestions/feedbackStatements.jsx","../../../client/app/bundles/Diagnostic/libs/markupUserResponses.js","../../../client/app/bundles/Diagnostic/components/renderForQuestions/renderTextEditor.jsx","../../../client/app/bundles/Diagnostic/components/diagnostics/sentenceCombining.jsx","../../../client/app/bundles/Diagnostic/components/questions/testQuestion.jsx","../../../client/app/bundles/Diagnostic/components/questions/question.jsx","../../../client/app/bundles/Diagnostic/components/questions/questions.jsx","../../../client/app/bundles/Diagnostic/components/sentenceFragments/chooseModelContainer.jsx","../../../client/app/bundles/Diagnostic/components/sentenceFragments/sentenceFragmentForm.jsx","../../../client/app/bundles/Diagnostic/components/sentenceFragments/newSentenceFragment.jsx","../../../client/app/bundles/Diagnostic/components/diagnostics/sentenceFragment.jsx","../../../client/app/bundles/Diagnostic/components/sentenceFragments/testSentenceFragmentContainer.jsx","../../../client/app/bundles/Diagnostic/components/sentenceFragments/sentenceFragment.jsx","../../../client/app/bundles/Diagnostic/components/sentenceFragments/sentenceFragments.jsx","../../../client/app/bundles/Diagnostic/components/diagnostics/finishedDiagnostic.jsx","../../../client/app/bundles/Diagnostic/components/diagnostics/landing.jsx","../../../client/app/bundles/Diagnostic/components/turk/finishedDiagnostic.jsx","../../../client/app/bundles/Diagnostic/libs/formattedCues.js","../../../client/app/bundles/Diagnostic/libs/conceptResults/fillInTheBlanks.js","../../../client/app/bundles/Diagnostic/libs/conceptResults/sentenceCombining.js","../../../client/app/bundles/Diagnostic/libs/conceptResults/sharedConceptResultsFunctions.js","../../../client/app/bundles/Diagnostic/libs/conceptResults/sentenceFragment.js","../../../client/app/bundles/Diagnostic/libs/conceptResults/diagnostic.js","../../../client/app/bundles/Diagnostic/components/diagnostics/studentDiagnostic.jsx","../../../client/app/bundles/Diagnostic/components/eslDiagnostic/finishedDiagnostic.jsx","../../../client/app/bundles/Shared/libs/translations/modules/languagePageInfo.js","../../../client/app/bundles/Diagnostic/components/eslDiagnostic/landingPage.jsx","../../../client/app/bundles/Diagnostic/components/eslDiagnostic/sentenceCombining.jsx","../../../client/app/bundles/Diagnostic/components/eslDiagnostic/sentenceFragment.jsx","../../../client/app/bundles/Shared/libs/translations/modules/commonText.js","../../../client/app/bundles/Diagnostic/components/eslDiagnostic/studentDiagnostic.jsx","../../../client/app/bundles/Diagnostic/components/admin/admin.jsx","../../../client/app/bundles/Diagnostic/utils/backOff.js","../../../client/app/bundles/Diagnostic/reducers/classroomLesson.js","../../../client/app/bundles/Diagnostic/reducers/classroomLessons.js","../../../client/app/bundles/Diagnostic/reducers/classroomLessonsReviews.js","../../../client/app/bundles/Diagnostic/reducers/classroomSessions.js","../../../client/app/bundles/Diagnostic/reducers/customize.js","../../../client/app/bundles/Diagnostic/reducers/diagnostics.js","../../../client/app/bundles/Diagnostic/reducers/display.js","../../../client/app/bundles/Diagnostic/reducers/filtersReducer.js","../../../client/app/bundles/Diagnostic/reducers/generatedIncorrectSequences.js","../../../client/app/bundles/Diagnostic/reducers/massEdit.js","../../../client/app/bundles/Diagnostic/reducers/questionReducer.js","../../../client/app/bundles/Diagnostic/reducers/questionReducerV2.js","../../../client/app/bundles/Diagnostic/reducers/responsesReducer.js","../../../client/app/bundles/Diagnostic/reducers/sessions.js","../../../client/app/bundles/Diagnostic/reducers/combined.js","../../../client/app/bundles/Diagnostic/utils/configureStore.js","../../../client/app/bundles/Diagnostic/clientRegistration.js"],"sourcesContent":["import _ from 'underscore';\n\nimport { requestGet, } from '../../../modules/request/index';\n\nimport C from '../constants';\n\nconst conceptsEndpoint = `${process.env.DEFAULT_URL}/api/v1/concepts.json`;\n\nfunction splitInLevels(concepts) {\n return _.groupBy(concepts, 'level');\n}\n\nfunction getParentName(concept, concepts) {\n const parent = _.find(concepts['1'], { id: concept.parent_id, });\n const grandParent = _.find(concepts['2'], { id: parent.parent_id, });\n return `${grandParent.name} | ${parent.name}`;\n}\n\nconst actions = {\n startListeningToConcepts() {\n return (dispatch) => {\n requestGet(conceptsEndpoint, (body) => {\n const concepts = splitInLevels(body.concepts);\n concepts['0'] = concepts['0'].map((concept) => {\n concept.displayName = `${getParentName(concept, concepts)} | ${concept.name}`;\n return concept;\n });\n dispatch({ type: C.RECEIVE_CONCEPTS_DATA, data: concepts, });\n });\n\n };\n },\n};\n\nexport default actions;\n","import React from 'react'\nimport {\n FlagDropdown,\n TextEditor,\n ConceptSelector,\n} from '../../../Shared/index'\n\nexport default class extends React.Component {\n state = {\n prompt: \"\",\n concept: this.props.question.conceptID,\n instructions: this.props.question.instructions ? this.props.question.instructions : \"\",\n flag: this.props.question.flag ? this.props.question.flag : \"alpha\",\n cuesLabel: this.props.question.cuesLabel ? this.props.question.cuesLabel : '',\n optimalResponseText: ''\n };\n\n submit = () => {\n const questionObj = {\n conceptUID: this.props.question.conceptUID,\n cuesLabel: this.props.question.cuesLabel,\n focusPoints: this.props.question.focusPoints,\n incorrectSequences: this.props.question.incorrectSequences,\n modelConceptUID: this.props.question.modelConceptUID,\n prompt: this.state.prompt,\n cues: this.refs.cues.value.split(','),\n instructions: this.state.instructions,\n flag: this.state.flag,\n cuesLabel: this.state.cuesLabel\n }\n if (this.props.new) {\n const optimalResponseObj = {text: this.state.optimalResponseText.trim(), optimal: true, count: 0, feedback: \"That's a strong sentence!\"}\n this.props.submit(questionObj, optimalResponseObj)\n } else {\n questionObj.conceptID = this.state.concept\n this.props.submit(questionObj)\n }\n };\n\n handleOptimalResponseText = (e) => {\n this.setState({ optimalResponseText: e.target.value });\n };\n\n handlePromptChange = (e) => {\n this.setState({prompt: e})\n };\n\n handleInstructionsChange = (e) => {\n this.setState({instructions: e.target.value})\n };\n\n renderConceptSelector = () => {\n if (!this.props.new) {\n return (\n
\n \n
\n\n \n
\n \n\n \n
\n \n\n \n
\n {this.renderOptimalResponse()}\n\nLoading...
)\n } else {\n return (\n404: No Concept Found
\n )\n }\n\n }\n}\n\nfunction select(state) {\n return {\n concepts: state.concepts,\n questions: state.questions,\n routing: state.routing\n }\n}\n\nexport default connect(select)(Concept)\n","import React from 'react';\nimport { connect } from 'react-redux';\nimport actions from '../../actions/concepts';\nimport { LinkListItem } from '../shared/linkListItem';\n\nclass Concepts extends React.Component {\n\n submitNewConcept = () => {\n const { dispatch } = this.props;\n const { newConceptName } = this.refs;\n const { value } = newConceptName;\n const newConcept = {name: value}\n dispatch(actions.submitNewConcept(newConcept))\n this.refs.newConceptName.value = \"\"\n };\n\n renderConcepts = () => {\n const { concepts } = this.props;\n const { data } = concepts;\n const dataRow = data[\"0\"];\n if (dataRow) {\n dataRow.sort((a, b) => a.displayName.localeCompare(b.displayName))\n return dataRow.map((concept) => {\n const { uid, displayName } = concept;\n return (\nthis.removeResponseFromMassEditArray(response)} style={{ marginRight: '0.5em', }} type=\"checkbox\" />{responses[response].text}
\n );\n }\n\n renderMassEditSummaryList() {\n const { massEdit } = this.props;\n const { selectedResponses } = massEdit;\n const summaryResponses = selectedResponses.map(response => this.renderMassEditSummaryListResponse(response));\n return (Revise your sentence. You don't need to have a space before a ${fb}.
`;\n}\n\nexport function checkForSpacingError(userString) {\n return _.find(subStrings, subString => userString.indexOf(subString) !== -1);\n}\n\nexport function spacingBeforePunctuation(userString) {\n const match = checkForSpacingError(userString);\n return (match ? { feedback: getFeedbackForPunc(match), } : undefined);\n}\n","import { stringNormalize } from 'quill-string-normalizer'\n\nexport default function() {\n return stringNormalize(this)\n}\n","import _ from 'underscore';\nimport {\n getPartsOfSpeechWordsWithTags\n} from './partsOfSpeechTagging';\n\nconst posTranslations = {\n JJ: 'Adjective',\n JJR: 'Adjective',\n JJS: 'Adjective',\n\n RB: 'Adverb',\n RBR: 'Adverb',\n RBS: 'Adverb',\n WRB: 'Adverb',\n\n CC: 'Conjunction',\n\n NN: 'Noun',\n NNP: 'Noun',\n NNPS: 'Noun',\n NNS: 'Noun',\n\n CD: 'Number',\n LS: 'Number',\n\n IN: 'Preposition',\n\n PP$: 'Pronoun',\n PRP: 'Pronoun',\n WP: 'Pronoun',\n WP$: 'Pronoun',\n\n VB: 'Verb',\n VBD: 'Verb',\n VBG: 'Verb',\n VBN: 'Verb',\n VBP: 'Verb',\n VBZ: 'Verb',\n};\n\nconst posConceptResults = {\n Adjective: 'placeholder',\n Adverb: 'placeholder',\n Conjunction: 'placeholder',\n Noun: 'placeholder',\n Number: 'placeholder',\n Preposition: 'placeholder',\n Pronoun: 'placeholder',\n Verb: 'placeholder',\n};\n\nexport function getCommonWords(sentences) {\n const words = _.map(sentences, (sentence) => normalizeString(sentence).split(' '));\n return _.intersection(...words);\n}\n\nexport function getCommonWordsWithImportantPOS(sentences) {\n const allCommonWords = getCommonWords(sentences);\n return _.reject(allCommonWords, (word) => {\n if (getPartsOfSpeechWordsWithTags(word) && getPartsOfSpeechWordsWithTags(word)[0]) {\n const tag = getPartsOfSpeechWordsWithTags(word)[0][1];\n return !posTranslations[tag];\n }\n return true;\n });\n}\n\nexport function getMissingWords(userString, sentences) {\n const commonWords = getCommonWordsWithImportantPOS(sentences);\n const wordsFromUser = normalizeString(userString).split(' ');\n return _.reject(commonWords, commonWord => _.contains(wordsFromUser, commonWord));\n}\n\nexport function getPOSForWord(word) {\n const tag = getPartsOfSpeechWordsWithTags(word)[0][1];\n return posTranslations[tag];\n}\n\nfunction _getCaseSensitiveWord(word, optimalSentence) {\n const normalizedString = removePunctuation(optimalSentence);\n const normalizedStringPlusLower = normalizeString(optimalSentence);\n const startIndex = normalizedStringPlusLower.indexOf(word);\n return normalizedString.substring(startIndex, word.length + startIndex);\n}\n\nexport function getFeedbackForWord(word, sentences, isSentenceFragment) {\n // const tag = getPOSForWord(word).toLowerCase();\n if (isSentenceFragment) {\n return 'Revise your work. Use all the words from the prompt, and make it complete by adding to it.
';\n }\n const caseSensitiveWord = _getCaseSensitiveWord(word, sentences[0]);\n return `Revise your sentence to include the word ${caseSensitiveWord}. You may have misspelled it.
`;\n}\n\nexport function extractSentencesFromResponses(responses) {\n return _.map(responses, response => response.text);\n}\n\nexport function getMissingWordsFromResponses(userString, sentences) {\n const missingWords = getMissingWords(userString, sentences);\n return _.sortBy(missingWords, word => word.length).reverse();\n}\n\nexport function checkForMissingWords(userString, responses, isSentenceFragment = false) {\n const sentences = extractSentencesFromResponses(responses);\n const missingWords = getMissingWordsFromResponses(userString, sentences);\n if (missingWords.length > 0) {\n return { feedback: getFeedbackForWord(missingWords[0], sentences, isSentenceFragment), };\n }\n}\n\nfunction normalizeString(string = '') {\n return string.replace(/[.,?!;]/g, '').toLowerCase();\n}\n\nfunction removePunctuation(string = '') {\n return string.replace(/[.,?!;\"()]/g, '');\n}\n","import _ from 'underscore';\n\nexport function getOptimalResponses(responses) {\n return _.where(responses, { optimal: true, });\n}\n\nexport function getSubOptimalResponses(responses) {\n return _.filter(responses, resp => resp.parentID === undefined && resp.feedback !== undefined && resp.optimal !== true);\n}\n\nexport function getTopOptimalResponse(responses) {\n return _.sortBy(getOptimalResponses(responses), r => r.count).reverse(responses)[0];\n}\n\nfunction getWeakResponses(responses) {\n return _.filter(responses, resp => resp.weak === true);\n}\n\nfunction getCommonUnmatchedResponses(responses) {\n return _.filter(responses, resp => resp.feedback === undefined && resp.count > 2);\n}\n\nfunction getSumOfWeakAndCommonUnmatchedResponses(responses) {\n return getWeakResponses(responses).length + getCommonUnmatchedResponses(responses).length;\n}\n\nexport function getPercentageWeakResponses(responses) {\n return (getSumOfWeakAndCommonUnmatchedResponses(responses) / responses.length * 100).toPrecision(4);\n}\n\nexport function getGradedResponses(responses) {\n // Returns sorted collection optimal first followed by suboptimal\n const gradedResponses = _.reject(responses, response =>\n (response.optimal === undefined) || (response.parentID)\n );\n return _.sortBy(gradedResponses, 'optimal').reverse();\n}\n","import fuzzy from 'fuzzyset.js';\nimport _ from 'underscore';\nimport constants from '../constants';\nimport {\n checkChangeObjectMatch\n} from './algorithms/changeObjects';\nimport {\n getFeedbackForMissingWord\n} from './algorithms/joiningWords';\nimport {\n spacingBeforePunctuation\n} from './algorithms/spacingBeforePunctuation';\nimport quillNormalize from './quillNormalizer';\nimport { checkForMissingWords } from './requiredWords';\nimport { getOptimalResponses, getTopOptimalResponse } from './sharedResponseFunctions';\n\nimport * as jsDiff from 'diff';\n\nconst ERROR_TYPES = {\n NO_ERROR: 'NO_ERROR',\n MISSING_WORD: 'MISSING_WORD',\n ADDITIONAL_WORD: 'ADDITIONAL_WORD',\n INCORRECT_WORD: 'INCORRECT_WORD',\n};\n\nconst conceptResultTemplate = (conceptUID, correct = false) => ({\n conceptUID,\n correct,\n});\n\nexport function removePunctuation(string) {\n return string.replace(/[^A-Za-z0-9\\s]/g, '');\n}\n\nconst downcasedFocusPoints = (focusPointsArr = []) => focusPointsArr.map((fp) => {\n fp.text = fp.text.toLowerCase();\n return fp;\n});\n\nconst removeSpaces = string => string.replace(/\\s+/g, '');\n\n// check number of chars added.\n\nconst getLowAdditionCount = (newString, oldString) => {\n const diff = jsDiff.diffChars(newString, oldString);\n const additions = _.where(diff, { added: true, });\n if (additions.length > 1) {\n return false;\n }\n const count = _.reduce(additions, (memo, num) => memo + num.count, 0);\n if (count < 3) {\n return true;\n }\n return false;\n};\n\nString.prototype.quillNormalize = quillNormalize\n\nexport default class Question {\n\n constructor(data) {\n this.prompt = data.prompt;\n this.sentences = data.sentences;\n this.responses = data.responses;\n this.questionUID = data.questionUID;\n this.focusPoints = downcasedFocusPoints(data.focusPoints) || [];\n this.incorrectSequences = data.incorrectSequences || [];\n }\n\n checkMatch(response) {\n // remove leading and trailing whitespace, then make sure all words are single spaced\n response = response.trim().replace(/\\s{2,}/g, ' ');\n const returnValue = {\n found: true,\n submitted: response,\n response: {\n text: response,\n questionUID: this.questionUID,\n gradeIndex: `nonhuman${this.questionUID}`,\n count: 1,\n },\n };\n\n const res = returnValue.response;\n const exactMatch = this.checkExactMatch(response);\n if (exactMatch !== undefined) {\n returnValue.response = exactMatch;\n return returnValue;\n }\n const focusPointMatch = this.checkFocusPointMatch(response);\n if (focusPointMatch !== undefined) {\n res.feedback = focusPointMatch.feedback;\n res.author = 'Focus Point Hint';\n res.parentID = getTopOptimalResponse(this.responses).key;\n if (focusPointMatch.conceptUID) {\n res.conceptResults = [\n conceptResultTemplate(focusPointMatch.conceptUID)\n ];\n }\n if (focusPointMatch.conceptResults) {\n res.conceptResults = focusPointMatch.conceptResults;\n }\n return returnValue;\n }\n const incorrectSequenceMatch = this.checkIncorrectSequenceMatch(response);\n if (incorrectSequenceMatch !== undefined) {\n res.feedback = incorrectSequenceMatch.feedback;\n res.author = 'Incorrect Sequence Hint';\n res.parentID = getTopOptimalResponse(this.responses).key;\n if (incorrectSequenceMatch.conceptResults) {\n res.conceptResults = incorrectSequenceMatch.conceptResults;\n }\n return returnValue;\n }\n const lowerCaseMatch = this.checkCaseInsensitiveMatch(response);\n if (lowerCaseMatch !== undefined) {\n res.feedback = constants.FEEDBACK_STRINGS.caseError;\n res.author = 'Capitalization Hint';\n res.parentID = lowerCaseMatch.key;\n res.conceptResults = [\n conceptResultTemplate('66upe3S5uvqxuHoHOt4PcQ')\n ];\n return returnValue;\n }\n const punctuationMatch = this.checkPunctuationInsensitiveMatch(response);\n if (punctuationMatch !== undefined) {\n res.feedback = constants.FEEDBACK_STRINGS.punctuationError;\n res.author = 'Punctuation Hint';\n res.parentID = punctuationMatch.key;\n res.conceptResults = [\n conceptResultTemplate('mdFUuuNR7N352bbMw4Mj9Q')\n ];\n return returnValue;\n }\n const punctuationAndCaseMatch = this.checkPunctuationAndCaseInsensitiveMatch(response);\n if (punctuationAndCaseMatch !== undefined) {\n res.feedback = constants.FEEDBACK_STRINGS.punctuationAndCaseError;\n res.author = 'Punctuation and Case Hint';\n res.parentID = punctuationAndCaseMatch.key;\n res.conceptResults = [\n conceptResultTemplate('66upe3S5uvqxuHoHOt4PcQ'),\n conceptResultTemplate('mdFUuuNR7N352bbMw4Mj9Q')\n ];\n return returnValue;\n }\n const spacingBeforePunctuationMatch = this.checkSpacingBeforePunctuationMatch(response);\n if (spacingBeforePunctuationMatch !== undefined) {\n res.feedback = spacingBeforePunctuationMatch.feedback;\n res.author = 'Punctuation Hint';\n res.parentID = getTopOptimalResponse(this.responses) ? getTopOptimalResponse(this.responses).key : undefined;\n res.conceptResults = [\n conceptResultTemplate('mdFUuuNR7N352bbMw4Mj9Q')\n ];\n return returnValue;\n }\n const spacingAfterCommaMatch = this.checkSpacingAfterCommaMatch(response);\n if (spacingAfterCommaMatch !== undefined) {\n res.feedback = spacingAfterCommaMatch.feedback;\n res.author = 'Punctuation Hint';\n res.parentID = getTopOptimalResponse(this.responses) ? getTopOptimalResponse(this.responses).key : undefined;\n res.conceptResults = [\n conceptResultTemplate('mdFUuuNR7N352bbMw4Mj9Q')\n ];\n return returnValue;\n }\n const whitespaceMatch = this.checkWhiteSpaceMatch(response);\n if (whitespaceMatch !== undefined) {\n res.feedback = constants.FEEDBACK_STRINGS.whitespaceError;\n res.author = 'Whitespace Hint';\n res.parentID = whitespaceMatch.key;\n res.conceptResults = [\n conceptResultTemplate('5Yv4-kNHwwCO2p8HI90oqQ')\n ];\n return returnValue;\n }\n const changeObjectMatch = this.checkChangeObjectRigidMatch(response);\n if (changeObjectMatch !== undefined) {\n switch (changeObjectMatch.errorType) {\n case ERROR_TYPES.INCORRECT_WORD:\n const missingWord = changeObjectMatch.missingText;\n const missingTextFeedback = getFeedbackForMissingWord(missingWord);\n res.feedback = missingTextFeedback || constants.FEEDBACK_STRINGS.modifiedWordError;\n res.author = 'Modified Word Hint';\n res.parentID = changeObjectMatch.response.key;\n res.conceptResults = [\n conceptResultTemplate('H-2lrblngQAQ8_s-ctye4g')\n ];\n return returnValue;\n case ERROR_TYPES.ADDITIONAL_WORD:\n res.feedback = constants.FEEDBACK_STRINGS.additionalWordError;\n res.author = 'Additional Word Hint';\n res.parentID = changeObjectMatch.response.key;\n res.conceptResults = [\n conceptResultTemplate('QYHg1tpDghy5AHWpsIodAg')\n ];\n return returnValue;\n case ERROR_TYPES.MISSING_WORD:\n\n res.feedback = constants.FEEDBACK_STRINGS.missingWordError;\n res.author = 'Missing Word Hint';\n res.parentID = changeObjectMatch.response.key;\n res.conceptResults = [\n conceptResultTemplate('N5VXCdTAs91gP46gATuvPQ')\n ];\n return returnValue;\n default:\n return;\n }\n }\n const changeObjectFlexMatch = this.checkChangeObjectFlexibleMatch(response);\n if (changeObjectFlexMatch !== undefined) {\n switch (changeObjectFlexMatch.errorType) {\n case ERROR_TYPES.INCORRECT_WORD:\n const missingWord = changeObjectFlexMatch.missingText;\n const missingTextFeedback = getFeedbackForMissingWord(missingWord);\n res.feedback = missingTextFeedback || constants.FEEDBACK_STRINGS.modifiedWordError;\n res.author = 'Flexible Modified Word Hint';\n res.parentID = changeObjectFlexMatch.response.key;\n res.conceptResults = [\n conceptResultTemplate('H-2lrblngQAQ8_s-ctye4g')\n ];\n return returnValue;\n case ERROR_TYPES.ADDITIONAL_WORD:\n\n res.feedback = constants.FEEDBACK_STRINGS.additionalWordError;\n res.author = 'Flexible Additional Word Hint';\n res.parentID = changeObjectFlexMatch.response.key;\n res.conceptResults = [\n conceptResultTemplate('QYHg1tpDghy5AHWpsIodAg')\n ];\n return returnValue;\n case ERROR_TYPES.MISSING_WORD:\n\n res.feedback = constants.FEEDBACK_STRINGS.missingWordError;\n res.author = 'Flexible Missing Word Hint';\n res.parentID = changeObjectFlexMatch.response.key;\n res.conceptResults = [\n conceptResultTemplate('N5VXCdTAs91gP46gATuvPQ')\n ];\n return returnValue;\n default:\n return;\n }\n }\n const requiredWordsMatch = this.checkRequiredWordsMatch(response);\n if (requiredWordsMatch !== undefined) {\n res.feedback = requiredWordsMatch.feedback;\n res.author = 'Required Words Hint';\n res.parentID = getTopOptimalResponse(this.responses).key;\n res.conceptResults = [\n conceptResultTemplate('N5VXCdTAs91gP46gATuvPQ')\n ];\n return returnValue;\n }\n const minLengthMatch = this.checkMinLengthMatch(response);\n if (minLengthMatch !== undefined) {\n res.feedback = constants.FEEDBACK_STRINGS.minLengthError;\n res.author = 'Missing Details Hint';\n res.parentID = minLengthMatch.key;\n res.conceptResults = [\n conceptResultTemplate('N5VXCdTAs91gP46gATuvPQ')\n ];\n return returnValue;\n }\n const maxLengthMatch = this.checkMaxLengthMatch(response);\n if (maxLengthMatch !== undefined) {\n res.feedback = constants.FEEDBACK_STRINGS.maxLengthError;\n res.author = 'Not Concise Hint';\n res.parentID = maxLengthMatch.key;\n res.conceptResults = [\n conceptResultTemplate('QYHg1tpDghy5AHWpsIodAg')\n ];\n return returnValue;\n }\n const lowerCaseStartMatch = this.checkCaseStartMatch(response);\n if (lowerCaseStartMatch !== undefined) {\n res.feedback = constants.FEEDBACK_STRINGS.caseError;\n res.author = 'Capitalization Hint';\n res.parentID = lowerCaseStartMatch.key;\n res.conceptResults = [\n conceptResultTemplate('S76ceOpAWR-5m-k47nu6KQ')\n ];\n return returnValue;\n }\n const punctuationEndMatch = this.checkPunctuationEndMatch(response);\n if (punctuationEndMatch !== undefined) {\n res.feedback = constants.FEEDBACK_STRINGS.punctuationError;\n res.author = 'Punctuation End Hint';\n res.parentID = punctuationEndMatch.key;\n res.conceptResults = [\n conceptResultTemplate('JVJhNIHGZLbHF6LYw605XA')\n ];\n return returnValue;\n }\n returnValue.found = false;\n returnValue.response.gradeIndex = `unmarked${this.questionUID}`;\n return returnValue;\n }\n\n nonChildResponses() {\n return _.filter(this.responses,\n resp => resp.parentID === undefined && resp.feedback !== undefined\n );\n }\n\n checkExactMatch(response) {\n return _.find(this.responses,\n resp => resp.text.quillNormalize() === response.quillNormalize()\n );\n }\n\n checkCaseInsensitiveMatch(response) {\n return _.find(getOptimalResponses(this.responses),\n resp => resp.text.quillNormalize().toLowerCase() === response.quillNormalize().toLowerCase()\n );\n }\n\n checkCaseStartMatch(response) {\n if ((/^[a-z]/).test(response)) {\n return getTopOptimalResponse(this.responses);\n }\n }\n\n checkPunctuationEndMatch(response) {\n const lastChar = response[response.length - 1];\n if (lastChar && lastChar.match(/[a-z]/i)) {\n return getTopOptimalResponse(this.responses);\n }\n }\n\n checkPunctuationInsensitiveMatch(response) {\n return _.find(getOptimalResponses(this.responses),\n resp => removePunctuation(resp.text.quillNormalize()) === removePunctuation(response.quillNormalize())\n );\n }\n\n checkPunctuationAndCaseInsensitiveMatch(response) {\n return _.find(getOptimalResponses(this.responses), (resp) => {\n const supplied = removePunctuation(response.quillNormalize()).toLowerCase();\n const target = removePunctuation(resp.text.quillNormalize()).toLowerCase();\n return supplied === target;\n });\n }\n\n checkWhiteSpaceMatch(response) {\n return _.find(getOptimalResponses(this.responses),\n resp => removeSpaces(response.quillNormalize()) === removeSpaces(resp.text.quillNormalize())\n );\n }\n\n checkSmallTypoMatch(response) {\n return _.find(this.nonChildResponses(this.responses),\n resp => getLowAdditionCount(response.quillNormalize(), resp.text.quillNormalize())\n );\n }\n\n checkChangeObjectRigidMatch(response) {\n const fn = string => string.quillNormalize();\n return checkChangeObjectMatch(response, getOptimalResponses(this.responses), fn);\n }\n\n checkChangeObjectFlexibleMatch(response) {\n const fn = string => removePunctuation(string.quillNormalize()).toLowerCase();\n return checkChangeObjectMatch(response, getOptimalResponses(this.responses), fn);\n }\n\n checkFuzzyMatch(response) {\n const set = fuzzy(_.pluck(this.responses, 'text'));\n const matches = set.get(response, []);\n let foundResponse;\n let text;\n if (matches.length > 0) {\n const threshold = (matches[0][1].length - 3) / matches[0][1].length;\n text = (matches[0][0] > threshold) && (response.split(' ').length <= matches[0][1].split(' ').length) ? matches[0][1] : null;\n }\n if (text) {\n foundResponse = _.findWhere(this.responses, { text, });\n }\n return foundResponse;\n }\n\n checkRequiredWordsMatch(response) {\n return checkForMissingWords(response, getOptimalResponses(this.responses));\n }\n\n checkMinLengthMatch(response) {\n const optimalResponses = getOptimalResponses(this.responses);\n if (optimalResponses.length < 2) {\n return undefined;\n }\n const lengthsOfResponses = optimalResponses.map(resp => resp.text.quillNormalize().split(' ').length);\n const minLength = _.min(lengthsOfResponses) - 1;\n if (response.split(' ').length < minLength) {\n return _.sortBy(optimalResponses, resp => resp.text.quillNormalize().length)[0];\n }\n return undefined;\n }\n\n checkMaxLengthMatch(response) {\n const optimalResponses = getOptimalResponses(this.responses);\n if (optimalResponses.length < 2) {\n return undefined;\n }\n const lengthsOfResponses = optimalResponses.map(resp => resp.text.quillNormalize().split(' ').length);\n const maxLength = _.max(lengthsOfResponses) + 1;\n if (response.split(' ').length > maxLength) {\n return _.sortBy(optimalResponses, resp => resp.text.quillNormalize().length).reverse()[0];\n }\n return undefined;\n }\n\n checkFocusPointMatch(response) {\n return _.find(this.focusPoints, (fp) => {\n const options = fp.text.split('|||');\n const anyMatches = _.any(options, opt => response.toLowerCase().indexOf(opt) !== -1);\n return !anyMatches;\n });\n }\n\n checkIncorrectSequenceMatch(response) {\n return _.find(this.incorrectSequences, (incSeq) => {\n const options = incSeq.text.split('|||');\n const anyMatches = _.any(options, opt => new RegExp(opt).test(response));\n return anyMatches;\n });\n }\n\n checkSpacingBeforePunctuationMatch(response) {\n return spacingBeforePunctuation(response);\n }\n\n checkSpacingAfterCommaMatch(response) {\n for (let i = 0; i < response.length; i++) {\n if (response[i] === ',' && (i + 1 < response.length)) {\n if (response[i + 1] !== ' ') {\n return {\n feedback: 'Revise your work. Always put a space after a comma.
',\n };\n }\n }\n }\n return undefined;\n }\n}\n","export default ['\"', \"'\", '!', '.', '?'];\n","import _ from 'underscore';\nimport constants from '../constants';\nimport validEndingPunctuation from '../libs/validEndingPunctuation.js';\nimport {\n spacingBeforePunctuation\n} from './algorithms/spacingBeforePunctuation';\nimport * as qpos from './partsOfSpeechTagging';\nimport { checkForMissingWords } from './requiredWords';\n\nconst conceptResultTemplate = (conceptUID, correct = false) => ({\n conceptUID,\n correct,\n});\n\nexport function wordLengthCount(str) {\n const strNoPunctuation = str.replace(/[^0-9a-z\\s]/gi, '').replace(/\\s{2,}/g, ' ').split(' ');\n return strNoPunctuation.length;\n}\n\nfunction sortbyCount(responses) {\n return _.sortBy(responses, r => r.count).reverse();\n}\n\nfunction removePunctuation(string) {\n return string.replace(/[^A-Za-z0-9\\s]/g, '');\n}\n\nexport default class POSMatcher {\n\n constructor(data) {\n this.prompt = data.prompt;\n this.responses = sortbyCount(data.responses);\n this.questionUID = data.questionUID;\n this.wordCountChange = data.wordCountChange || {};\n this.ignoreCaseAndPunc = data.ignoreCaseAndPunc;\n this.incorrectSequences = data.incorrectSequences || [];\n }\n\n getOptimalResponses() {\n return _.reject(this.responses, response =>\n (response.optimal !== true) || (response.parentID)\n );\n }\n\n getTopOptimalResponse() {\n return _.sortBy(this.getOptimalResponses(), r => r.count).reverse()[0];\n }\n\n getGradedResponses() {\n // returns sorted collection optimal first followed by suboptimal\n const gradedResponses = _.reject(this.responses, response =>\n (response.optimal === undefined) || (response.parentID)\n );\n return _.sortBy(gradedResponses, 'optimal').reverse();\n }\n\n checkMatch(userSubmission) {\n const formattedResponse = userSubmission.trim().replace(/\\s{2,}/g, ' ');\n const returnValue = {\n found: true,\n submitted: formattedResponse,\n response: {\n text: formattedResponse,\n questionUID: this.questionUID,\n gradeIndex: `nonhuman${this.questionUID}`,\n count: 1,\n },\n };\n const res = returnValue.response;\n\n const exactMatch = this.checkExactMatch(userSubmission);\n if (exactMatch !== undefined) {\n returnValue.response = exactMatch;\n return returnValue;\n }\n const incorrectSequenceMatch = this.checkIncorrectSequenceMatch(userSubmission);\n if (incorrectSequenceMatch !== undefined) {\n returnValue.response = Object.assign({}, res, incorrectSequenceMatch);\n return returnValue;\n }\n const lengthMatch = this.checkLengthMatch(userSubmission);\n if (lengthMatch !== undefined) {\n returnValue.response = Object.assign({}, res, lengthMatch);\n return returnValue;\n }\n\n const endingPunctuationMatch = this.checkEndingPunctuationMatch(userSubmission);\n if (endingPunctuationMatch !== undefined) {\n returnValue.response = Object.assign({}, res, endingPunctuationMatch);\n return returnValue;\n }\n\n const startingCapitalizationMatch = this.checkStartingCapitalization(userSubmission);\n if (startingCapitalizationMatch !== undefined) {\n returnValue.response = Object.assign({}, res, startingCapitalizationMatch);\n return returnValue;\n }\n\n const optimalCapitalizationMatch = this.checkOptimalCapitalizationMatch(userSubmission);\n if (optimalCapitalizationMatch !== undefined) {\n returnValue.response = Object.assign({}, res, optimalCapitalizationMatch);\n return returnValue;\n }\n\n const optimalPunctuationMatch = this.checkOptimalPunctuationMatch(userSubmission);\n if (optimalPunctuationMatch !== undefined) {\n returnValue.response = Object.assign({}, res, optimalPunctuationMatch);\n return returnValue;\n }\n\n const punctuationAndCaseMatch = this.checkPunctuationAndCaseInsensitiveMatch(userSubmission);\n if (punctuationAndCaseMatch !== undefined) {\n returnValue.response = Object.assign({}, res, punctuationAndCaseMatch);\n return returnValue;\n }\n\n const spacingBeforePunctuationMatch = this.checkSpacingBeforePunctuationMatch(userSubmission);\n if (spacingBeforePunctuationMatch !== undefined) {\n returnValue.response = Object.assign({}, res, spacingBeforePunctuationMatch);\n return returnValue;\n }\n\n const spacingAfterCommaMatch = this.checkSpacingAfterCommaMatch(userSubmission);\n if (spacingAfterCommaMatch !== undefined) {\n returnValue.response = Object.assign({}, res, spacingAfterCommaMatch);\n return returnValue;\n }\n\n const requiredWordsMatch = this.checkRequiredWordsMatch(userSubmission);\n if (requiredWordsMatch !== undefined) {\n returnValue.response = Object.assign({}, res, requiredWordsMatch);\n return returnValue;\n }\n\n const posMatch = this.checkPOSMatch(userSubmission);\n if (posMatch !== undefined) {\n returnValue.response = Object.assign({}, res, posMatch);\n return returnValue;\n }\n\n returnValue.found = false;\n returnValue.response.gradeIndex = `unmarked${this.questionUID}`;\n return returnValue;\n }\n\n checkExactMatch(userSubmission) {\n return _.find(this.responses, response => response.text === userSubmission);\n }\n\n checkIncorrectSequenceMatch(userSubmission) {\n const match = _.find(this.incorrectSequences, (incSeq) => {\n const options = incSeq.text.split('|||');\n const anyMatches = _.any(options, opt => userSubmission.toLowerCase().indexOf(opt) !== -1);\n return anyMatches;\n });\n if (match !== undefined) {\n return {\n optimal: false,\n parentID: this.getTopOptimalResponse().key,\n author: 'Incorrect Sequence Hint',\n feedback: match.feedback,\n conceptResults: match.conceptResults ? match.conceptResults : undefined,\n };\n }\n }\n\n checkLengthMatch(userSubmission) {\n if (this.ignoreCaseAndPunc) {\n return;\n }\n const userWordCount = wordLengthCount(userSubmission);\n const promptWordCount = wordLengthCount(this.prompt);\n const maxWordCount = promptWordCount + this.wordCountChange.max;\n const minWordCount = promptWordCount + this.wordCountChange.min;\n const templateResponse = {\n optimal: false,\n parentID: this.getTopOptimalResponse().key,\n };\n const feedback = getMinMaxFeedback(this.wordCountChange.min, this.wordCountChange.max);\n if (this.wordCountChange.min && (userWordCount < minWordCount)) {\n if (this.wordCountChange.min === 1) {\n return Object.assign({}, templateResponse, {\n feedback,\n author: 'Too Short Hint',\n });\n } else if (this.wordCountChange.min === this.wordCountChange.max) {\n return Object.assign({}, templateResponse, {\n feedback,\n author: 'Too Short Hint',\n });\n }\n return Object.assign({}, templateResponse, {\n feedback,\n author: 'Too Short Hint',\n });\n } else if (this.wordCountChange.max && (userWordCount > maxWordCount)) {\n if (this.wordCountChange.max === 1) {\n return Object.assign({}, templateResponse, {\n feedback,\n author: 'Too Long Hint',\n });\n }\n return Object.assign({}, templateResponse, {\n feedback,\n author: 'Too Long Hint',\n });\n }\n }\n\n checkEndingPunctuationMatch(userSubmission) {\n if (this.ignoreCaseAndPunc) {\n return;\n }\n const lastChar = _.last(userSubmission);\n if (!_.includes(validEndingPunctuation, lastChar)) {\n return {\n optimal: false,\n parentID: this.getTopOptimalResponse().key,\n author: 'Punctuation End Hint',\n feedback: 'Proofread your sentence for missing punctuation.',\n conceptResults: [\n conceptResultTemplate('JVJhNIHGZLbHF6LYw605XA')\n ],\n };\n }\n }\n\n checkStartingCapitalization(userSubmission) {\n if (this.ignoreCaseAndPunc) {\n return;\n }\n // only trigger if sentence begins with a lower case letter\n if ((/^[a-z]/).test(userSubmission)) {\n return {\n optimal: false,\n parentID: this.getTopOptimalResponse().key,\n author: 'Starting Capitalization Hint',\n feedback: 'Proofread your sentence for correct capitalization.',\n conceptResults: [\n conceptResultTemplate('S76ceOpAWR-5m-k47nu6KQ')\n ],\n };\n }\n }\n\n checkOptimalCapitalizationMatch(userSubmission) {\n if (this.ignoreCaseAndPunc) {\n return;\n }\n const optimals = this.getOptimalResponses();\n for (let i = 0; i < optimals.length; i++) {\n const optimal = optimals[i];\n if (userSubmission.toLowerCase() === optimal.text.toLowerCase()) {\n if (userSubmission !== optimal) {\n return {\n optimal: false,\n parentID: optimal.key,\n author: 'Capitalization Hint',\n feedback: 'Proofread your sentence for correct capitalization.',\n conceptResults: [\n conceptResultTemplate('66upe3S5uvqxuHoHOt4PcQ')\n ],\n };\n }\n }\n }\n }\n\n checkOptimalPunctuationMatch(userSubmission) {\n if (this.ignoreCaseAndPunc) {\n return;\n }\n const optimals = this.getOptimalResponses();\n for (let i = 0; i < optimals.length; i++) {\n const optimal = optimals[i];\n if (removePunctuation(userSubmission) === removePunctuation(optimal.text)) {\n if (userSubmission !== optimal) {\n return {\n optimal: false,\n parentID: optimal.key,\n author: 'Punctuation Hint',\n feedback: 'Proofread your sentence for correct punctuation.',\n conceptResults: [\n conceptResultTemplate('mdFUuuNR7N352bbMw4Mj9Q')\n ],\n };\n }\n }\n }\n }\n\n checkPunctuationAndCaseInsensitiveMatch(userSubmission) {\n if (this.ignoreCaseAndPunc) {\n return;\n }\n const optimals = this.getOptimalResponses();\n for (let i = 0; i < optimals.length; i++) {\n const optimal = optimals[i];\n if (removePunctuation(userSubmission).toLowerCase() === removePunctuation(optimal.text).toLowerCase()) {\n if (userSubmission !== optimal) {\n return {\n optimal: false,\n parentID: optimal.key,\n author: 'Punctuation and Case Hint',\n feedback: 'Proofread your sentence for correct capitalization and punctuation.',\n conceptResults: [\n conceptResultTemplate('66upe3S5uvqxuHoHOt4PcQ'),\n conceptResultTemplate('mdFUuuNR7N352bbMw4Mj9Q')\n ],\n };\n }\n }\n }\n }\n\n checkSpacingBeforePunctuationMatch(userSubmission) {\n if (this.ignoreCaseAndPunc) {\n return;\n }\n const spacingBeforePunctuationMatch = spacingBeforePunctuation(userSubmission);\n if (spacingBeforePunctuationMatch) {\n return {\n optimal: false,\n feedback: spacingBeforePunctuationMatch.feedback,\n author: 'Punctuation Hint',\n parentID: this.getTopOptimalResponse().key,\n conceptResults: [\n conceptResultTemplate('mdFUuuNR7N352bbMw4Mj9Q')\n ],\n };\n }\n }\n\n checkSpacingAfterCommaMatch(userSubmission) {\n if (this.ignoreCaseAndPunc) {\n return;\n }\n for (let i = 0; i < userSubmission.length; i++) {\n if (userSubmission[i] === ',' && (i + 1 < userSubmission.length)) {\n if (userSubmission[i + 1] !== ' ') {\n return {\n optimal: false,\n feedback: 'Revise your work. Always put a space after a comma.
',\n author: 'Punctuation Hint',\n parentID: this.getTopOptimalResponse().key,\n conceptResults: [\n conceptResultTemplate('mdFUuuNR7N352bbMw4Mj9Q')\n ],\n };\n }\n }\n }\n }\n\n checkRequiredWordsMatch(userSubmission) {\n if (this.ignoreCaseAndPunc) {\n return;\n } else if (this.getOptimalResponses().length < 3) {\n return;\n }\n return checkForMissingWords(userSubmission, this.getOptimalResponses(), true);\n }\n\n checkPOSMatch(userSubmission) {\n // get graded responses and convert to POS strings\n const correctPOSTags = this.getGradedResponses().map(\n optimalResponse => qpos.getPartsOfSpeechTags(optimalResponse.text)\n );\n // convert user submission to POS string\n const userPOSTags = qpos.getPartsOfSpeechTags(userSubmission);\n // if user string could be converted to POS tags find response that has the same POS tags\n if (userPOSTags) {\n const matchedResponse = _.find(this.getGradedResponses(), (optimalResponse, index) => {\n if (optimalResponse.parentID) {\n return false;\n } else if (correctPOSTags[index]) {\n if (JSON.stringify(correctPOSTags[index]) === JSON.stringify(userPOSTags)) {\n // this will return the response object\n return true;\n }\n }\n });\n if (matchedResponse) {\n const returnValue = {\n optimal: matchedResponse.optimal,\n parentID: matchedResponse.key,\n feedback: matchedResponse.feedback,\n author: 'Parts of Speech',\n };\n if (matchedResponse.conceptResults) {\n returnValue.conceptResults = matchedResponse.conceptResults;\n }\n return returnValue;\n }\n }\n }\n}\n\nfunction getMinMaxFeedback(min, max) {\n if (min === max) {\n if (min === 1) {\n return 'Revise your work. Add one word to the prompt to make the sentence complete.';\n }\n return `Revise your work. Add ${constants.NUMBERS_AS_WORDS[min]} words to the prompt to make the sentence complete.`;\n }\n return `Revise your work. Add ${constants.NUMBERS_AS_WORDS[min]} to ${constants.NUMBERS_AS_WORDS[max]} words to the prompt to make the sentence complete.`;\n}\n","import React from 'react';\n\nexport default class extends React.Component {\n state = {\n isExpanded: false,\n };\n\n toggleExpandSinglePOS = () => {\n this.setState({\n isExpanded: !this.state.isExpanded,\n });\n };\n\n renderExpandedPOSListText = () => {\n if (this.state.isExpanded) {\n const tag = this.props.tag;\n const additionalResponses = tag.responses.slice(1); // first response has already been rendered in unexpanded view\n if (additionalResponses.length === 0) {\n return (***No more responses match this pattern***
);\n }\n return additionalResponses.map(response => (\n{response.text}
\n ));\n }\n };\n\n renderExpandedPOSListCount = () => {\n if (this.state.isExpanded) {\n const tag = this.props.tag;\n const additionalResponses = tag.responses.slice(1); // first response has already been rendered in unexpanded view\n return additionalResponses.map(response => (\n{response.count}
\n ));\n }\n };\n\n render() {\n const { headerStyle, } = this.props;\n if (this.state.isExpanded) {\n headerStyle.marginTop = '20px';\n headerStyle.marginBottom = '20px';\n } else {\n headerStyle.marginTop = '0px';\n headerStyle.marginBottom = '0px';\n }\n return (\n{this.props.tagsToRender.join('---')}
\n{this.props.tag.responses[0].text}
\n {this.renderExpandedPOSListText()}\n{this.props.icon} {this.props.tag.count === undefined ? 0 : this.props.tag.count}
\n{this.props.tag.responses[0].count === undefined ? 0 : this.props.tag.responses[0].count}
\n {this.renderExpandedPOSListCount()}\n{message}
;\n };\n\n renderPageNumbers = () => {\n // var array\n // if(this.state.viewingResponses) {\n // array = this.gatherVisibleResponses()\n // } else {\n // array = this.getPOSTagsList()\n // }\n\n const pageNumbers = _.range(1, this.props.filters.numberOfPages + 1);\n\n let pageNumberStyle = {};\n const numbersToRender = pageNumbers.map((pageNumber, i) => {\n if (this.props.filters.responsePageNumber === pageNumber) {\n pageNumberStyle = {\n backgroundColor: 'lightblue',\n };\n } else {\n pageNumberStyle = {};\n }\n return (\n\n \n
\nAuthor: {author}
\nFeedback: {feedback}
\nLoading...
\n );\n }\n }\n}\n\nfunction select(props) {\n return {\n fillInBlank: props.fillInBlank,\n playDiagnostic: props.playDiagnostic,\n };\n}\n\nexport default connect(select)(TestQuestion);\n","import React, { Component } from 'react';\nimport { connect } from 'react-redux';\nimport { Link, NavLink, Route, Switch, withRouter } from 'react-router-dom';\nimport MassEditContainer from '../questions/massEditContainer.jsx';\nimport ResponseComponentWrapper from '../questions/responseRouteWrapper.jsx';\nimport Cues from '../renderForQuestions/cues.tsx';\nimport EditFillInBlank from './editFillInBlank.jsx';\nimport TestFillInBlankQuestionContainer from './testFillInBlankQuestionContainer.jsx';\n\nconst icon = `${process.env.CDN_URL}/images/icons/direction.svg`\n\nclass FillInBlankQuestion extends Component {\n\n getQuestion = () => {\n const { match, fillInBlank, } = this.props\n const { params } = match\n const { questionID, } = params;\n return fillInBlank ? fillInBlank.data[questionID] : null;\n }\n\n isLoading = () => {\n const { fillInBlank, } = this.props\n return !fillInBlank.hasreceiveddata\n }\n\n render() {\n const { match, massEdit, fillInBlank } = this.props\n const { params } = match\n const { questionID} = params;\n const question = this.getQuestion();\n if (this.isLoading()) {\n return (Loading...
);\n } else if (question) {\n const activeLink = massEdit.numSelectedResponses > 1\n ?{question.instructions || 'Fill in the blank with the correct option.'}
\n\n Edit Question\n
\n404: No Question Found
\n );\n }\n }\n}\n\nfunction select(props) {\n return {\n fillInBlank: props.fillInBlank,\n massEdit: props.massEdit\n };\n}\n\nexport default withRouter(connect(select)(FillInBlankQuestion));\n","import _ from 'lodash';\nimport React, { Component } from 'react';\nimport { connect } from 'react-redux';\nimport { Link } from 'react-router-dom';\nimport { ArchivedButton, hashToCollection } from '../../../Shared/index';\nimport { QuestionList } from '../shared/questionList.tsx';\n\nclass FillInBlankQuestions extends Component {\n constructor(props) {\n super(props)\n\n const { fillInBlank } = props\n\n this.state = {\n diagnosticQuestions: fillInBlank.data || null\n }\n }\n\n UNSAFE_componentWillReceiveProps(nextProps) {\n const { fillInBlank, lessons } = nextProps\n const { diagnosticQuestions } = this.state;\n if (fillInBlank.hasreceiveddata && lessons.hasreceiveddata) {\n if (Object.keys(diagnosticQuestions).length === 0 || !_.isEqual(this.props.fillInBlank.data, fillInBlank.data) || (!_.isEqual(this.props.lessons.data, lessons.data))) {\n this.setState({ diagnosticQuestions: fillInBlank.data })\n }\n }\n }\n\n toggleShowArchived = () => {\n const { showOnlyArchived } = this.state;\n this.setState({\n showOnlyArchived: !showOnlyArchived,\n });\n };\n\n render() {\n const { diagnosticQuestions, showOnlyArchived } = this.state;\n const sortedDiagnosticQuestions = hashToCollection(diagnosticQuestions).sort((a, b) => a.prompt.localeCompare(b.prompt))\n return (\nFill In The Blank
\n\n \n
\n {this.renderQuestionsForLesson()}\nLoading...
);\n }\n return (\n404: No Concept Found
\n );\n }\n}\n\nfunction select(state) {\n return {\n lessons: state.lessons,\n questions: state.questions,\n routing: state.routing,\n sentenceFragments: state.sentenceFragments,\n fillInBlank: state.fillInBlank,\n titleCards: state.titleCards\n };\n}\n\nexport default connect(select)(Lesson);\n","import React from 'react';\nimport { connect } from 'react-redux';\nimport _ from 'underscore';\nimport {\n ArchivedButton,\n FlagDropdown,\n Modal\n} from '../../../Shared/index';\nimport actions from '../../actions/lessons.ts';\nimport { LinkListItem } from '../shared/linkListItem';\nimport EditLessonForm from './lessonForm.tsx';\n\nclass Lessons extends React.Component {\n constructor(props) {\n super(props)\n\n this.state = {\n lessonFlags: 'production',\n showOnlyArchived: false,\n }\n }\n\n createNew = () => {\n this.props.dispatch(actions.toggleNewLessonModal());\n };\n\n submitNewLesson = data => {\n // TODO: fix add new lesson action to show new lessons without refreshing\n this.props.dispatch(actions.submitNewLesson(data));\n this.props.dispatch(actions.toggleNewLessonModal())\n };\n\n toggleShowArchived = () => {\n this.setState({\n showOnlyArchived: !this.state.showOnlyArchived,\n });\n };\n\n renderLessons = () => {\n const { data, } = this.props.lessons;\n let keys = _.keys(data);\n if (this.state.lessonFlags !== 'All Flags') {\n keys = _.filter(keys, key => data[key].flag === this.state.lessonFlags);\n }\n if (this.state.showOnlyArchived) {\n keys = keys.filter(key => data[key].questions && data[key].questions.some(q => q.flag === 'archived'))\n }\n keys.sort((a, b) => data[a].name.localeCompare(data[b].name))\n return keys.map(key => (\n{val.name}\n {val.correct ? Correct\n : Incorrect }\n this.deleteConceptResult(key, focusPointKey)} style={{ cursor: 'pointer', marginLeft: 5, }}>Delete\n
\n )\n );\n return _.values(components);\n }\n }\n\n renderFocusPointsList = () => {\n const { questionTypeLink } = this.state;\n const { match } = this.props;\n const { params } = match;\n const { questionID } = params;\n const components = this.fPsortedByOrder().map((fp) => {\n const { conceptResults, feedback, key, order, text } = fp;\n return (\n\n {this.renderTextInputFields(text, key)}\n \n
\n\n {order}\n
\n{val.name}\n {val.correct ? Correct\n : Incorrect }\n this.deleteConceptResult(key, sequenceKey)} style={{ cursor: 'pointer', marginLeft: 5, }}>Delete\n
\n )\n );\n return _.values(components);\n }\n }\n\n renderSaveButton() {\n return (\n \n );\n }\n\n renderSequenceList() {\n const { match } = this.props;\n const { params, url } = match;\n const { questionID } = params;\n const path = url.includes('sentence-fragments') ? 'sentence-fragments' : 'questions'\n const components = this.sequencesSortedByOrder().map((seq) => {\n return (\n\n {this.renderTextInputFields(seq.text, seq.key)}\n \n
\n\n {seq.order}\n
\n\n \n \n \n
\n )\n }\n\n renderLessonModelNote = () => {\n const { lessonModelConceptUID, modelConceptUID } = this.state\n const { concepts } = this.props\n const { data } = concepts\n if (lessonModelConceptUID && lessonModelConceptUID !== modelConceptUID) {\n const concept = data['0'].find(c => c.uid === lessonModelConceptUID)\n if (concept) {\n return (\nThe activity that this question belongs to has the following Model Concept:
\n\"{concept.displayName}\"
\nLoading...
);\n\n }\n}\n\nexport default PlayDiagnosticQuestion;\n","import React, { Component } from 'react';\nimport { connect } from 'react-redux';\nimport { clearData, loadData, nextQuestion } from '../../actions.js';\nimport PlayLessonQuestion from '../diagnostics/sentenceCombining';\n\nclass TestQuestion extends Component {\n constructor(props) {\n super(props);\n\n this.state = {\n responsesForGrading: [],\n allResponses: [],\n key: 0,\n };\n }\n\n componentDidMount() {\n this.reset();\n }\n\n reset = () => {\n const { dispatch, } = this.props\n dispatch(clearData());\n this.startActivity();\n this.setState(prevState => ({ key: prevState.key + 1, }));\n }\n\n questionsForLesson = () => {\n const { match } = this.props\n const { params } = match\n const question = this.getQuestion();\n question.key = params.questionID;\n return [\n {\n type: 'SC',\n question,\n }\n ];\n }\n\n startActivity = () => {\n const { dispatch, } = this.props\n const action = loadData(this.questionsForLesson());\n dispatch(action);\n const next = nextQuestion();\n dispatch(next);\n }\n\n getQuestion = () => {\n const { questions, match } = this.props\n const { data } = questions\n const { params } = match\n const { questionID } = params\n return data[questionID];\n }\n\n render() {\n const { playLesson, dispatch, } = this.props\n const { key, } = this.state\n\n if (playLesson.currentQuestion) {\n const { question, } = playLesson.currentQuestion;\n return (\nLoading...
\n );\n }\n }\n\n}\n\nfunction select(props) {\n return {\n questions: props.questions,\n playLesson: props.playLesson,\n };\n}\n\nexport default connect(select)(TestQuestion);\n","import React from 'react';\nimport { connect } from 'react-redux';\nimport { NavLink, Route, Switch, withRouter } from 'react-router-dom';\nimport _ from 'underscore';\nimport { Modal, UploadOptimalResponses } from '../../../Shared/index';\nimport questionActions from '../../actions/questions';\nimport {\n submitOptimalResponses,\n submitResponse\n} from '../../actions/responses';\nimport C from '../../constants';\nimport EditFocusPointsContainer from '../focusPoints/editFocusPointsContainer.jsx';\nimport FocusPointsContainer from '../focusPoints/focusPointsContainer.jsx';\nimport NewFocusPointsContainer from '../focusPoints/newFocusPointsContainer.jsx';\nimport EditIncorrectSequenceContainer from '../incorrectSequence/editIncorrectSequenceContainer.jsx';\nimport IncorrectSequenceContainer from '../incorrectSequence/incorrectSequenceContainer.jsx';\nimport NewIncorrectSequenceContainer from '../incorrectSequence/newIncorrectSequenceContainer.jsx';\nimport Cues from '../renderForQuestions/cues.tsx';\nimport getBoilerplateFeedback from './boilerplateFeedback.jsx';\nimport ChooseModelContainer from './chooseModelContainer.jsx';\nimport MassEditContainer from './massEditContainer.jsx';\nimport EditForm from './questionForm.jsx';\nimport ResponseComponentWrapper from './responseRouteWrapper.jsx';\nimport TestQuestionContainer from './testQuestion';\n\nconst icon = `${process.env.CDN_URL}/images/icons/direction.svg`\n\nclass Question extends React.Component {\n constructor(props) {\n super(props)\n\n this.state = {\n selectedBoilerplateCategory: '',\n responses: [],\n loadedResponses: false,\n addingNewResponse: false,\n uploadingNewOptimalResponses: false\n }\n }\n\n handleClickStartEditingQuestion = () => {\n const { dispatch, match } = this.props\n const { params } = match\n const { questionID } = params;\n dispatch(questionActions.startQuestionEdit(questionID));\n }\n\n cancelEditingQuestion = () => {\n const { dispatch, match } = this.props\n const { params } = match\n const { questionID } = params;\n dispatch(questionActions.cancelQuestionEdit(questionID));\n }\n\n saveQuestionEdits = (vals) => {\n const { dispatch, match } = this.props\n const { params } = match\n const { questionID } = params;\n dispatch(questionActions.submitQuestionEdit(questionID, vals));\n }\n\n\n submitOptimalResponses = (responses) => {\n const { dispatch, match, concepts, } = this.props\n const { params } = match\n const { questionID } = params;\n const conceptUID = this.getQuestion().conceptID\n dispatch(\n submitOptimalResponses(questionID, conceptUID, responses, concepts)\n )\n this.setState({ uploadingNewOptimalResponses: false, })\n }\n\n getQuestion = () => {\n const { match, questions, } = this.props\n const { params } = match\n const { data, } = questions;\n const { questionID, } = params;\n return data[questionID];\n }\n\n handleClickAddNewResponse = () => {\n this.setState({ addingNewResponse: true, });\n }\n\n handleClickUploadOptimalResponses = () => {\n this.setState({ uploadingNewOptimalResponses: true, });\n }\n\n handleSubmitResponse = () => {\n const { match, dispatch, } = this.props\n const { params } = match\n const { questionID } = params\n\n const newResp = {\n vals: {\n text: this.refs.newResponseText.value,\n feedback: this.refs.newResponseFeedback.value,\n optimal: this.refs.newResponseOptimal.checked,\n questionUID: questionID,\n gradeIndex: `human${questionID}`,\n count: 1,\n }\n };\n this.refs.newResponseText.value = null;\n this.refs.newResponseFeedback.value = null;\n this.refs.newResponseOptimal.checked = false;\n dispatch(submitResponse(newResp.vals));\n this.setState({ addingNewResponse: false, });\n }\n\n boilerplateCategoriesToOptions = () => {\n return getBoilerplateFeedback().map(category => (\n \n ));\n }\n\n boilerplateSpecificFeedbackToOptions = (selectedCategory) => {\n return selectedCategory.children.map(childFeedback => (\n \n ));\n }\n\n chooseBoilerplateCategory = (e) => {\n this.setState({ selectedBoilerplateCategory: e.target.value, });\n }\n\n chooseSpecificBoilerplateFeedback = (e) => {\n if (e.target.value === 'Select specific boilerplate feedback') {\n this.refs.newResponseFeedback.value = '';\n } else {\n this.refs.newResponseFeedback.value = e.target.value;\n }\n }\n\n onCloseUploadOptimalResponsesModal = () => this.setState({ uploadingNewOptimalResponses: false })\n\n onCloseNewResponseModal = () => this.setState({ addingNewResponse: false, })\n\n renderBoilerplateCategoryDropdown = (onChangeEvent) => {\n const style = { marginRight: '20px', };\n return (\n \n \n \n );\n }\n\n renderBoilerplateCategoryOptionsDropdown = (onChangeEvent, description) => {\n const selectedCategory = _.find(getBoilerplateFeedback(), { description, });\n if (selectedCategory) {\n return (\n \n \n \n );\n } else {\n return ();\n }\n }\n\n renderNewResponseForm = () => {\n const { addingNewResponse, selectedBoilerplateCategory, } = this.state\n if (!addingNewResponse) { return }\n\n return (\n\n \n
\n \n\n \n
\n \n\n \n
\n \nLoading...
);\n } else if (data[questionID]) {\n const activeLink = massEdit.numSelectedResponses > 1\n ?{data[questionID].instructions || 'Combine the sentences into one sentence.'}
\n\n Edit Question\n Add New Response\n Upload Optimal Responses\n
\n404: No Question Found
\n );\n }\n }\n}\n\nfunction select(state) {\n return {\n concepts: state.concepts,\n questions: state.questions,\n routing: state.routing,\n massEdit: state.massEdit\n };\n}\n\nexport default withRouter(connect(select)(Question));\n","import React from 'react';\nimport { connect } from 'react-redux';\nimport { push } from 'react-router-redux';\nimport SelectSearch from 'react-select-search';\nimport _ from 'underscore';\nimport {\n ArchivedButton,\n Modal,\n hashToCollection,\n responsesWithStatus\n} from '../../../Shared/index';\nimport actions from '../../actions/questions';\nimport { deleteResponse, submitResponseEdit } from '../../actions/responses';\nimport Question from '../../libs/question';\nimport { QuestionListByConcept } from '../shared/questionListByConcept';\n\nfunction sleep(milliseconds) {\n const start = new Date().getTime();\n for (let i = 0; i < 1e7; i++) {\n if ((new Date().getTime() - start) > milliseconds) {\n break;\n }\n }\n}\n\nclass Questions extends React.Component {\n constructor(props) {\n super(props)\n\n const { questions } = props\n\n this.state = {\n diagnosticQuestions: questions.data ? questions.data : null,\n showOnlyArchived: false\n }\n }\n\n UNSAFE_componentWillReceiveProps(nextProps) {\n const { questions, lessons } = nextProps\n const { diagnosticQuestions } = this.state;\n if (questions.hasreceiveddata && lessons.hasreceiveddata) {\n if (Object.keys(diagnosticQuestions).length === 0 || !_.isEqual(this.props.questions.data, questions.data) || (!_.isEqual(this.props.lessons.data, lessons.data))) {\n this.setState({ diagnosticQuestions: questions.data })\n }\n }\n }\n\n createNew = () => {\n const { dispatch } = this.props;\n dispatch(actions.toggleNewQuestionModal());\n };\n\n submitNewQuestion = () => {\n const { dispatch } = this.props;\n const newQuestion = { name: this.refs.newQuestionName.value, };\n dispatch(actions.submitNewQuestion(newQuestion));\n this.refs.newQuestionName.value = '';\n };\n\n updateRematchedResponse = (rid, vals) => {\n const { dispatch } = this.props;\n dispatch(submitResponseEdit(rid, vals));\n };\n\n getErrorsForAttempt(attempt) {\n return attempt.feedback;\n }\n\n generateFeedbackString(attempt) {\n const errors = this.getErrorsForAttempt(attempt);\n return errors;\n }\n\n getMatchingResponse(quest, response, responses) {\n const fields = {\n questionUID: quest.key,\n responses: _.filter(responses, resp => resp.statusCode < 2),\n focusPoints: quest.focusPoints ? hashToCollection(quest.focusPoints) : [],\n };\n const question = new Question(fields);\n return question.checkMatch(response.text);\n }\n\n // functions for rematching all Responses\n mapConceptsToList = () => {\n let { concepts, questions } = this.props;\n concepts = hashToCollection(concepts.data['0']);\n questions = hashToCollection(questions.data);\n const conceptsWithQuestions = concepts.map(concept => _.where(questions, { conceptID: concept.uid, }));\n return _.flatten(conceptsWithQuestions);\n };\n\n responsesWithStatusForQuestion = questionUID => {\n let { responses } = this.props;\n const { data } = responses;\n responses = data[questionUID];\n return hashToCollection(responsesWithStatus(responses));\n };\n\n rematchAllResponses = (question) => {\n const responsesWithStat = this.responsesWithStatusForQuestion(question.key);\n const weak = _.filter(responsesWithStat, resp => resp.statusCode > 1);\n weak.forEach((resp, index) => {\n const percentage = index / weak.length * 100;\n this.rematchResponse(question, resp, responsesWithStat);\n });\n };\n\n rematchResponse = (question, response, responses) => {\n if (!response.questionUID || !response.text) {\n return;\n }\n const newMatchedResponse = this.getMatchingResponse(question, response, responses);\n const changed =\n (newMatchedResponse.response.parentID !== response.parentID) ||\n (newMatchedResponse.response.author !== response.author) ||\n (newMatchedResponse.response.feedback !== response.feedback) ||\n (newMatchedResponse.response.conceptResults !== response.conceptResults);\n const unmatched = (newMatchedResponse.found === false);\n if (changed) {\n if (unmatched) {\n const newValues = {\n weak: false,\n text: response.text,\n count: response.count || 1,\n questionUID: response.questionUID,\n gradeIndex: `unmatched${response.questionUID}`,\n };\n } else if (newMatchedResponse.response.parentID === undefined) {\n this.props.dispatch(\n deleteResponse(question.key, response.key)\n );\n } else {\n const newValues = {\n weak: false,\n parentID: newMatchedResponse.response.parentID,\n author: newMatchedResponse.response.author,\n feedback: newMatchedResponse.response.feedback,\n gradeIndex: `nonhuman${response.questionUID}`,\n };\n if (newMatchedResponse.response.conceptResults) {\n newValues.conceptResults = newMatchedResponse.response.conceptResults;\n }\n sleep(150);\n this.updateRematchedResponse(response.key, newValues);\n }\n }\n };\n\n renderModal = () => {\n const { questions } = this.props;\n const { newQuestionModalOpen, submittingnew } = questions;\n const stateSpecificClass = submittingnew ? 'is-loading' : '';\n if (newQuestionModalOpen) {\n return (\n\n \n \n
\n\n \n \n \n
\n\n \n
\n\n \n \n \n
\n )\n }\n\n render() {\n const { conceptsFeedback } = this.props;\n const { data } = conceptsFeedback;\n return(\n\n this.handleChange('optimalResponseText', e)} type=\"text\" value={this.state.optimalResponseText} />\n
)\n ]\n );\n };\n\n wordCountInfo = (minOrMax) => {\n if (this.state.wordCountChange && this.state.wordCountChange[minOrMax]) {\n return this.state.wordCountChange[minOrMax];\n }\n };\n\n render() {\n const { conceptID, flag, isFragment, instructions, needsIdentification, prompt } = this.state\n return (\n\n this.handleChange('prompt', e)} type=\"text\" value={prompt} />\n
\n \n\n
\n\n\n \n
\n\n \n
\n \n
\n \n
\n {this.renderOptimalResponseTextInput()}\n\n \n
Loading...
\n );\n }\n }\n\n}\n\nfunction select(props) {\n return {\n sentenceFragments: props.sentenceFragments,\n playDiagnostic: props.playDiagnostic,\n };\n}\n\nexport default connect(select)(TestQuestion);\n","import React from 'react';\nimport { connect } from 'react-redux';\nimport { NavLink, Route, Switch, withRouter } from 'react-router-dom';\n\nimport {\n Modal,\n UploadOptimalResponses,\n} from '../../../Shared/index';\nimport {\n listenToResponsesWithCallback,\n submitOptimalResponses\n} from '../../actions/responses';\nimport fragmentActions from '../../actions/sentenceFragments.ts';\nimport C from '../../constants';\nimport EditFocusPointsContainer from '../focusPoints/editFocusPointsContainer.jsx';\nimport FocusPointsContainer from '../focusPoints/focusPointsContainer.jsx';\nimport NewFocusPointsContainer from '../focusPoints/newFocusPointsContainer.jsx';\nimport EditIncorrectSequenceContainer from '../incorrectSequence/editIncorrectSequenceContainer.jsx';\nimport IncorrectSequenceContainer from '../incorrectSequence/incorrectSequenceContainer.jsx';\nimport NewIncorrectSequenceContainer from '../incorrectSequence/newIncorrectSequenceContainer.jsx';\nimport MassEditContainer from '../questions/massEditContainer.jsx';\nimport ResponseComponent from '../questions/responseComponent.jsx';\nimport ResponseComponentWrapper from '../questions/responseRouteWrapper.jsx';\nimport ChooseModelContainer from './chooseModelContainer.jsx';\nimport EditForm from './sentenceFragmentForm.jsx';\nimport TestQuestionContainer from './testSentenceFragmentContainer';\n\nconst icon = `${process.env.CDN_URL}/images/icons/direction.svg`\n\n\nclass SentenceFragment extends React.Component {\n constructor(props) {\n super(props)\n\n this.state = {\n selectedBoilerplateCategory: '',\n responses: [],\n loadedResponses: false,\n uploadingNewOptimalResponses: false\n }\n }\n\n UNSAFE_componentWillMount() {\n const { match } = this.props\n const { params } = match\n const { questionID } = params\n listenToResponsesWithCallback(\n questionID,\n (data) => {\n this.setState({\n responses: data,\n loadedResponses: true,\n });\n }\n );\n }\n\n getQuestion = () => {\n const { sentenceFragments, match, } = this.props;\n const { params } = match\n const { data } = sentenceFragments;\n const { questionID } = params;\n return data[questionID];\n }\n\n getResponses = () => {\n const { responses, } = this.state\n return responses;\n }\n\n handleUploadOptimalResponsesClick = () => {\n this.setState({ uploadingNewOptimalResponses: true, });\n }\n\n cancelEditingSentenceFragment = () => {\n const { dispatch, match, } = this.props\n const { params } = match\n const { questionID } = params;\n dispatch(fragmentActions.cancelSentenceFragmentEdit(questionID));\n }\n\n handleEditFragmentClick = () => {\n const { dispatch, match, } = this.props;\n const { params } = match\n const { questionID } = params;\n dispatch(fragmentActions.startSentenceFragmentEdit(questionID));\n }\n\n saveSentenceFragmentEdits = (data) => {\n const { dispatch, match, } = this.props;\n const { params } = match\n const { questionID } = params;\n dispatch(fragmentActions.submitSentenceFragmentEdit(questionID, data));\n }\n\n submitOptimalResponses = (responses) => {\n const { dispatch, match, concepts, } = this.props;\n const { params } = match\n const { questionID } = params;\n\n const conceptUID = this.getQuestion().conceptID\n dispatch(submitOptimalResponses(questionID, conceptUID, responses, concepts))\n this.setState({ uploadingNewOptimalResponses: false, })\n }\n\n closeModal = () => {\n this.setState({ uploadingNewOptimalResponses: false, })\n }\n\n renderEditForm = () => {\n const { sentenceFragments, match, concepts, } = this.props;\n const { data, states } = sentenceFragments;\n const { params } = match\n const { questionID } = params;\n if (states[questionID] === C.EDITING_SENTENCE_FRAGMENT) {\n return (\n{data[questionID].instructions || 'Combine the sentences into one sentence.'}
\nSentence Fragments
\n\n The activity session with this unique identifier has already been completed.
\n In order to redo this activity, you must return to your dashboard and click \"Replay Activity\".
\n If you believe that you have received this message in error, ask your teacher to contact Quill.
\n Please provide the following URL to help us solve the problem.\n
Your results could not be saved.
\n Make sure you are connected to the internet.
\n You can attempt to save again using the button below.
\n If the issue persists, ask your teacher to contact Quill.
\n Please provide the following URL to help us solve the problem.\n
\n {window.location.href}\n
\n Your results are being saved now.\n You'll be redirected automatically once they are saved.\n
\n\n You're about to answer {questionCount || '22'} questions about writing sentences.\n Don't worry, it's not a test. It's just to figure out what you know.\n
\n\n Some of the questions might be about things you haven't learned yet—that's okay!\n Just answer them as best as you can.\n Once you're finished, Quill will create a learning plan just for you!\n
\n\n Your unique code is ij3982rmf9.\n
\n\n The activity session with this unique identifier has already been completed.
\n In order to redo this activity, you must return to your dashboard and click "Replay Activity".
\n If you believe that you have received this message in error, ask your teacher to contact Quill.
\n Please provide the following URL to help us solve the problem.\n
Your results could not be saved.
\n Make sure you are connected to the internet.
\n You can attempt to save again using the button below.
\n If the issue persists, ask your teacher to contact Quill.
\n Please provide the following URL to help us solve the problem.\n
\n {window.location.href}\n
Your results are being saved now. You'll be redirected automatically once they are saved.
\n{translate('completedDiagnostic^text')}
\n{firstLine}
\n {isLeftToRight &&{secondLine}
}\n {isLeftToRight &&{thirdLine}
}\n{instructions}
\n {translationPresent &&{translate(text)}
}\nLoading...
);\n }\n}\n\nfunction select(state) {\n return {\n concepts: state.concepts,\n questions: state.questions,\n routing: state.routing,\n };\n}\n\nexport default connect(select)(ELLSentenceCombining);\n","import React from 'react';\nimport ReactTransition from 'react-addons-css-transition-group';\nimport { connect } from 'react-redux';\n\nimport { Feedback, getDisplayedText, getLatestAttempt, hashToCollection, renderPreviewFeedback } from '../../../Shared/index';\nimport {\n getGradedResponsesWithCallback\n} from '../../actions/responses.js';\nimport POSMatcher from '../../libs/sentenceFragment.js';\nimport TextEditor from '../renderForQuestions/renderTextEditor.jsx';\nimport updateResponseResource from '../renderForQuestions/updateResponseResource.js';\nconst icon = `${process.env.CDN_URL}/images/icons/direction.svg`\n\nconst key = ''; // enables this component to be used by both play/sentence-fragments and play/diagnostic\n\nclass PlaySentenceFragment extends React.Component {\n constructor(props) {\n super(props)\n\n this.state = {\n response: props.question.prompt,\n checkAnswerEnabled: true,\n submitted: false,\n }\n }\n componentDidMount = () => {\n getGradedResponsesWithCallback(\n this.getQuestion().key,\n (data) => {\n this.setState({ responses: data, });\n }\n );\n }\n\n getInstructionText = () => {\n const { question } = this.props;\n const { instructions } = question;\n return ;\n }\n\n choosingSentenceOrFragment = () => {\n const { question, } = this.props;\n return question.identified === undefined && (question.needsIdentification === undefined || question.needsIdentification === true);\n // the case for question.needsIdentification===undefined is for sentenceFragments that were created before the needsIdentification field was put in\n }\n\n showNextQuestionButton = () => {\n const { question, } = this.props;\n const attempted = question.attempts.length > 0;\n if (attempted) {\n return true;\n }\n return false;\n }\n\n getQuestion = () => {\n const { question, } = this.props;\n if (question.key.endsWith('-esp')) {\n question.key = question.key.slice(0, -4);\n }\n return question;\n }\n\n getResponses = () => {\n const { responses, } = this.state\n return responses;\n }\n\n handleClickCompleteSentence = () => this.checkChoice('Sentence')\n\n handleClickIncompleteSentence = () => this.checkChoice('Fragment')\n\n checkChoice = (choice) => {\n const { question, markIdentify, } = this.props\n const questionType = question.isFragment ? 'Fragment' : 'Sentence';\n markIdentify(choice === questionType);\n }\n\n getSentenceOrFragmentButtons = () => {\n return (\nIs this a complete or an incomplete sentence?
\n{this.getQuestion().prompt}
\n