diff --git a/media/js/src/app.jsx b/media/js/src/app.jsx index 66ff1e6a..5f8f950c 100644 --- a/media/js/src/app.jsx +++ b/media/js/src/app.jsx @@ -1,9 +1,8 @@ import React, { useState, useEffect } from 'react'; -import { BrowserRouter as Router, Route, Routes } from 'react-router-dom'; +import { BrowserRouter as Router, Route, Routes } from 'react-router-dom'; import { Dashboard } from './containers/dashboard'; -import { SimulationOne} from './simulations/simulation1/simulationOne'; -import {SimulationTwo -} from './simulations/simulation2/components/simulationTwo'; +import { SimulationOne } from './simulations/simulation1/simulationOne'; +import { SimulationTwo } from './simulations/simulation2/simulationTwo'; import { SimulationThree } from './simulations/simulation3/simulationThree'; import { SimulationFour } from './simulations/simulation4/simulationFour'; import { getCoursePk } from './utils/utils'; @@ -31,7 +30,7 @@ export const App = () => { } /> + isFaculty={isFaculty} />} /> {(isSuperUser || isFaculty || coursePk === 6) && ( } /> diff --git a/media/js/src/simulations/simulation2/components/simulationTwo.jsx b/media/js/src/simulations/simulation2/components/simulationTwo.jsx deleted file mode 100644 index e407f659..00000000 --- a/media/js/src/simulations/simulation2/components/simulationTwo.jsx +++ /dev/null @@ -1,245 +0,0 @@ -import React, { useState, useEffect } from 'react'; -import { ScatterPlot2 } from './scatterPlot2'; -import { Variables } from './variables'; -import { ControlVariable } from './controlVariable'; -import { inlineKatex } from '../../../utils/utils'; -import DATASETS from '../datasets.json'; -import { Step } from '../../../step.jsx'; -import { LearningGoals } from './learningGoals'; -import { MultipleChoiceQuestion2 } from './multipleChoiceQuestion2'; -import { OVBTheoryModal } from './modalOVBTheory.jsx'; -import { authedFetch, deleteAnswer, - fetchQuizData -} from '../../../utils/utils.jsx'; -import { dataAttr, labelIndex, takeaways2, sim2TextVariable as varText, - sim2TextControl as controlText, sim2Information as info, dataRange, - dataIndex -} from '../dataAttr'; - -const simContainer = document.querySelector('#react-root'); -const coursePk = - simContainer ? Number(simContainer.dataset.course) : ''; - -const qIndex = ['general', 'income', 'gpa4', 'affairs_sim2', 'campus_sim2']; - -export const SimulationTwo = () => { - const freshComplete = () => Object.keys(DATASETS).reduce((acc, key) => { - acc[key] = false; - return acc; - }, {}); - - const [choice, setChoice] = useState(); - const [data, setData] = useState(); - const [controls, setControls] = useState({}); - const [isSubmitted, setIsSubmitted] = useState(false); - const [isComplete, setIsComplete] = useState(freshComplete()); - const [submissionId, setSubmissionId] = useState(); - const [nextStep, setNextStep] = useState(false); - const [results, setResults] = useState({}); - - const checkComplete = () => Object.values(isComplete) - .reduce((acc, val) => acc + val, 0); - - const [showGeneral, setShowGeneral] = useState( - checkComplete() > 1 && isComplete['general'] != true); - - useEffect(() => { - fetchQuizData(coursePk, 2).then(data => { - setSubmissionId(data.submission_id); - const store = {}; - for (const answer of data.answers) { - store[qIndex[answer.question_number - 1]] = answer.is_correct; - } - setIsComplete(store); - }); - }, []); - - const createSubmission = async(followUp=()=>true) => { - // Define the data to be saved based on the plot type - const data = {}; - - const payload = { - simulation: 2, - data: data - }; - - const url = `/course/${coursePk}/api/create-sub/`; - - authedFetch(url, 'POST', payload) - .then(response => { - if (response.status === 201) { - return response.json(); - } else { - throw `Error (${response.status}) ${response.statusText}`; - } - }) - .then(data => { - setSubmissionId(data.submission_id); - followUp(); - }) - .catch(error => { - console.error('Error saving graph data:', error); - throw error; - }); - }; - - const handleChoice = (e) => { - setResults({}); - setIsSubmitted(false); - setNextStep(false); - setChoice(e.target.value); - }; - - const handleControls = (e) => { - setControls({...controls, [e.target.name]: e.target.checked}); - }; - - /** - * Reset the simulation to the beginning. - */ - const handleStartOver = () => { - setData(); - setControls({}); - for (const topic in isComplete) { - deleteAnswer(submissionId, takeaways2[topic].q_id); - } - setChoice(); - setIsComplete(freshComplete()); - }; - - useEffect(() => { - setData(DATASETS[choice]); - }, [choice]); - - useEffect(() => { - setControls({}); - }, [data]); - - /** - * If the user has completed the first question for the current dataset, - * show the second with a general question. - * @returns {JSX.Element} The question component to render. - */ - const getQuestions = () => { - // If the user has completed the first question - // for the current dataset, show the second with a - // general question. - - const takeaways = showGeneral ? - {[choice]: takeaways2[choice], 'general': takeaways2.general} : - {[choice]: takeaways2[choice]}; - const data = {choice, isSubmitted, handleStartOver, setIsSubmitted, - handleContinue, checkComplete, submissionId, isComplete, - setIsComplete, createSubmission, coursePk, nextStep, results, - setResults, setNextStep, takeaways, showGeneral, setShowGeneral}; - return ; - }; - - const handleContinue = () => { - setChoice(null); - setData(null); - setIsSubmitted(false); - setShowGeneral(checkComplete() > 1 && isComplete['general'] != true); - }; - - return ( -
-
- {[ - { - header: 'Simulation 2', - title: 'Omitted Variable Bias', - body: <> -

This simulation will address one of the - endogeneity problems: omitted variable bias (OVB). - Omitting certain variables can lead to biased - estimates of the sample slope of the variable of - interest in regression analyses. Through - interactive exercises, you’ll learn how to - detect the degree of OVB.

-

In applied research, the goal is to ensure - that {inlineKatex('\\hat{\\beta_1}')}, the - estimated slope for the variable of - interest ({inlineKatex('x_1')}), is an unbiased - estimate of the population slope. If it is biased, - the estimate becomes unreliable.

- - }, - { - title: 'Learning Goals', - body: - }, - ].map((step, i) => - - {step.body} - ) - } - {data && <> - {[ - { - title: 'Variables of Interest', - body: - }, - { - title: 'Control Variables', - body: - }, - ].map((step, i) => - - {step.body} - ) - } -
-
- • -
-
-
-
-
-

- Takeaway Questions -

-
-
- {isComplete[choice] && -

- You’ve analyzed this dataset. - You can revisit it, but - focus on a dataset that you - haven’t examined. -

- } - {getQuestions()} -
-
-
{/* div class=simulation__step-container */} - } -
{/* div class=simulation__workspace */} -
- - {choice &&
-

{info[choice]}

-
} -
{/* div class=simulation__graphspace */} - - - -
// div class=simulation - ); -}; \ No newline at end of file diff --git a/media/js/src/simulations/simulation2/simulationTwo.jsx b/media/js/src/simulations/simulation2/simulationTwo.jsx new file mode 100644 index 00000000..302d603e --- /dev/null +++ b/media/js/src/simulations/simulation2/simulationTwo.jsx @@ -0,0 +1,235 @@ +import React, { useState, useEffect } from 'react'; +import { ScatterPlot2 } from './components/scatterPlot2'; +import { Variables } from './components/variables'; +import { ControlVariable } from './components/controlVariable'; +import { + inlineKatex, authedFetch, deleteAnswer, + fetchQuizData, STATIC_URL, +} from '../../utils/utils'; +import DATASETS from './datasets.json'; +import { LearningGoals } from './components/learningGoals'; +import { MultipleChoiceQuestion2 } from './components/multipleChoiceQuestion2'; +import { OVBTheoryModal } from './components/modalOVBTheory.jsx'; +import { + dataAttr, labelIndex, takeaways2, sim2TextVariable as varText, + sim2TextControl as controlText, sim2Information as info, dataRange, + dataIndex +} from './dataAttr'; +import { SimulationPanel } from '../../SimulationPanel'; + +const simContainer = document.querySelector('#react-root'); +const coursePk = + simContainer ? Number(simContainer.dataset.course) : ''; + +const qIndex = ['general', 'income', 'gpa4', 'affairs_sim2', 'campus_sim2']; + +export const SimulationTwo = () => { + const freshComplete = () => Object.keys(DATASETS).reduce((acc, key) => { + acc[key] = false; + return acc; + }, {}); + + const [choice, setChoice] = useState(); + const [data, setData] = useState(); + const [controls, setControls] = useState({}); + const [isSubmitted, setIsSubmitted] = useState(false); + const [isComplete, setIsComplete] = useState(freshComplete()); + const [submissionId, setSubmissionId] = useState(); + const [nextStep, setNextStep] = useState(false); + const [results, setResults] = useState({}); + + const checkComplete = () => Object.values(isComplete) + .reduce((acc, val) => acc + val, 0); + + const [showGeneral, setShowGeneral] = useState( + checkComplete() > 1 && isComplete['general'] != true); + + useEffect(() => { + fetchQuizData(coursePk, 2).then(data => { + setSubmissionId(data.submission_id); + const store = {}; + for (const answer of data.answers) { + store[qIndex[answer.question_number - 1]] = answer.is_correct; + } + setIsComplete(store); + }); + }, []); + + const createSubmission = async(followUp = () => true) => { + // Define the data to be saved based on the plot type + const data = {}; + + const payload = { + simulation: 2, + data: data + }; + + const url = `/course/${coursePk}/api/create-sub/`; + + authedFetch(url, 'POST', payload) + .then(response => { + if (response.status === 201) { + return response.json(); + } else { + throw `Error (${response.status}) ${response.statusText}`; + } + }) + .then(data => { + setSubmissionId(data.submission_id); + followUp(); + }) + .catch(error => { + console.error('Error saving graph data:', error); + throw error; + }); + }; + + const handleChoice = (e) => { + setResults({}); + setIsSubmitted(false); + setNextStep(false); + setChoice(e.target.value); + }; + + const handleControls = (e) => { + setControls({ ...controls, [e.target.name]: e.target.checked }); + }; + + /** + * Reset the simulation to the beginning. + */ + const handleStartOver = () => { + setData(); + setControls({}); + for (const topic in isComplete) { + deleteAnswer(submissionId, takeaways2[topic].q_id); + } + setChoice(); + setIsComplete(freshComplete()); + }; + + useEffect(() => { + setData(DATASETS[choice]); + }, [choice]); + + useEffect(() => { + setControls({}); + }, [data]); + + /** + * If the user has completed the first question for the current dataset, + * show the second with a general question. + * @returns {JSX.Element} The question component to render. + */ + const getQuestions = () => { + // If the user has completed the first question + // for the current dataset, show the second with a + // general question. + + const takeaways = showGeneral ? + { [choice]: takeaways2[choice], 'general': takeaways2.general } : + { [choice]: takeaways2[choice] }; + const data = { + choice, isSubmitted, handleStartOver, setIsSubmitted, + handleContinue, checkComplete, submissionId, isComplete, + setIsComplete, createSubmission, coursePk, nextStep, results, + setResults, setNextStep, takeaways, showGeneral, setShowGeneral + }; + return ; + }; + + const handleContinue = () => { + setChoice(null); + setData(null); + setIsSubmitted(false); + setShowGeneral(checkComplete() > 1 && isComplete['general'] != true); + }; + + const steps = [ + { + subtitle: 'Simulation 2', + title: 'Omitted Variable Bias', + segment: 'preamble', + content: <> +

This simulation will address one of the + endogeneity problems: omitted variable bias (OVB). + Omitting certain variables can lead to biased + estimates of the sample slope of the variable of + interest in regression analyses. Through + interactive exercises, you’ll learn how to + detect the degree of OVB.

+

In applied research, the goal is to ensure + that {inlineKatex('\\hat{\\beta_1}')}, the + estimated slope for the variable of + interest ({inlineKatex('x_1')}), is an unbiased + estimate of the population slope. If it is biased, + the estimate becomes unreliable.

+ + }, + { + icon: `${STATIC_URL}/img/icon-goal.svg`, + headerId: 'learning-goals', + title: 'Learning Goals', + content: + } + ]; + + if (data) { + steps.push({ + title: 'Variables of Interest', + content: + }); + steps.push({ + title: 'Control Variables', + content: + }); + steps.push({ + title: 'Takeaway Questions', + headerId: 'takeawayQuestions', + content: <> + {isComplete[choice] && +

+ You’ve analyzed this dataset. + You can revisit it, but + focus on a dataset that you + haven’t examined. +

+ } + {getQuestions()} + + }); + } + + const graphContent = ( + <> + + {choice &&
+

{info[choice]}

+
} + + ); + + return ( + ]} + /> + ); +};