From 18e4477d464142cef0da32e0112be8eb4665ab12 Mon Sep 17 00:00:00 2001 From: chensation Date: Wed, 23 Mar 2022 19:05:58 -0400 Subject: [PATCH 01/19] WIP added scroll function to timeline --- client/src/views/Replay/Replay.js | 77 +++++++++++++++++-------------- 1 file changed, 43 insertions(+), 34 deletions(-) diff --git a/client/src/views/Replay/Replay.js b/client/src/views/Replay/Replay.js index 8d6360fb..7434d458 100644 --- a/client/src/views/Replay/Replay.js +++ b/client/src/views/Replay/Replay.js @@ -17,6 +17,9 @@ const Replay = () => { const { saveID } = useParams(); const workspaceRef = useRef(null); const [replay, setReplay] = useState([]); + const [startIndex, setStartIndex] = useState(0); + const [endIndex, setEndIndex] = useState(TIME_LINE_SIZE); + const [blocksData, setBlocksData] = useState([]); const [isPlaying, setIsPlaying] = useState(false); const [playbackRef, setPlaybackRef] = useState(null); const [playSpeed, setPlaySpeed] = useState(500); @@ -130,11 +133,25 @@ const Replay = () => { ]; const goBack = () => { - dispatchTimelineReducer({ type: 'DecrementStep' }); + dispatch({ type: 'Decrement' }); + + if(step < startIndex){ + let newStart = Math.max(0, step-5); + setStartIndex(newStart); + setEndIndex(newStart + TIME_LINE_SIZE); + } + }; const goForward = () => { - dispatchTimelineReducer({ type: 'IncrementStep' }); + dispatch({ type: 'Increment' }); + + if(step >= endIndex){ + let newEnd = Math.min(replay.length, step+5); + setEndIndex(newEnd); + setStartIndex(newEnd-TIME_LINE_SIZE); + } + }; const setStep = (value) => { @@ -193,15 +210,18 @@ const Replay = () => { }; const scrollTimelineForward = () => { - dispatchTimelineReducer({ type: 'IncrementTimeline' }); - }; + if(endIndex < replay.length){ + setStartIndex(startIndex + 5); + setEndIndex(Math.min(endIndex + 5, replay.length)); + } + } const scrollTimelineBackward = () => { - dispatchTimelineReducer({ type: 'DecrementTimeline' }); - }; - const handleGoBack = () => { - if (window.confirm('Comfirm going back')) navigate(-1); - }; + if(startIndex > 0){ + setStartIndex(Math.max(startIndex - 5, 0)); + setEndIndex(endIndex - 5); + } + } return (
@@ -265,35 +285,24 @@ const Replay = () => {
+ > ‹
- {replay - .map((item, index) => ( -
setStep(index)} - > - {formatMyDate(item.timestamp)} -
- )) - .slice(timelineStates.startIndex, timelineStates.endIndex)} + {replay.map((item, index) => ( +
setStep(index)} + > + {formatMyDate(item.timestamp)} +
+ )).slice(startIndex, endIndex)}
+ disabled={endIndex >= replay.length} + onClick={scrollTimelineForward} + >›

{`[${timelineStates.step + 1}/${ From c8bb27022f29822f109a2d4ecde3ca214c7faacf Mon Sep 17 00:00:00 2001 From: chensation Date: Wed, 23 Mar 2022 19:42:26 -0400 Subject: [PATCH 02/19] added log --- client/src/views/Replay/Replay.js | 45 +++++++++---------------------- 1 file changed, 12 insertions(+), 33 deletions(-) diff --git a/client/src/views/Replay/Replay.js b/client/src/views/Replay/Replay.js index 7434d458..d01fc500 100644 --- a/client/src/views/Replay/Replay.js +++ b/client/src/views/Replay/Replay.js @@ -19,7 +19,6 @@ const Replay = () => { const [replay, setReplay] = useState([]); const [startIndex, setStartIndex] = useState(0); const [endIndex, setEndIndex] = useState(TIME_LINE_SIZE); - const [blocksData, setBlocksData] = useState([]); const [isPlaying, setIsPlaying] = useState(false); const [playbackRef, setPlaybackRef] = useState(null); const [playSpeed, setPlaySpeed] = useState(500); @@ -27,19 +26,6 @@ const Replay = () => { const [action, setAction] = useState(''); const [logData, setLogData] = useState([]); - const timelineReducer = (timeline, action) => { - const checkTimelineStepInBound = () => { - if (timeline.step >= timeline.endIndex) { - let newEnd = Math.min(replay.length, timeline.step + 7); - timeline.endIndex = newEnd; - timeline.startIndex = newEnd - TIME_LINE_SIZE; - } else if (timeline.step < timeline.startIndex) { - let newStart = Math.max(0, timeline.step - 6); - timeline.startIndex = newStart; - timeline.endIndex = newStart + TIME_LINE_SIZE; - } - }; - switch (action.type) { case 'IncrementStep': timeline.step += 1; @@ -114,7 +100,7 @@ const Replay = () => { sorter: { compare: (a, b) => (a < b ? -1 : 1), }, - render: (timestamp) => formatMyDate(timestamp), + render: (timestamp) => formatMyDate(timestamp) }, { title: 'Block ID', @@ -184,23 +170,17 @@ const Replay = () => { workspaceRef.current ? workspaceRef.current.clear() : setWorkspace(); const xml = window.Blockly.Xml.textToDom(replay[timelineStates.step].xml); window.Blockly.Xml.domToWorkspace(xml, workspaceRef.current); - setAction(replay[timelineStates.step].action); - - if (replay[timelineStates.step].blockId) - window.Blockly.mainWorkspace - .getBlockById(replay[timelineStates.step].blockId) - ?.select(); - + setAction(replay[step].action); + //update log - let data = replay.slice(0, timelineStates.step + 1).map((item, index) => { - return { - key: index, - blockId: item.blockId, - timestamp: item.timestamp, - action: item.action, - }; - }); - + let data = replay.slice(0, step+1).map((item, index) => + {return { + key: index, + blockName: "", + timestamp: item.timestamp, + action: item.action + }}); + setLogData(data); } }, [replay, timelineStates, isPlaying, handlePause]); @@ -333,8 +313,7 @@ const Replay = () => { >

Logs

From 1ae1e4f3386b5d1b532a9796fed0e7767891aa97 Mon Sep 17 00:00:00 2001 From: chensation Date: Thu, 24 Mar 2022 14:44:47 -0400 Subject: [PATCH 03/19] changed title and added block id to log --- client/src/views/Replay/Replay.js | 11 ++--------- client/src/views/Replay/Replay.less | 3 +-- 2 files changed, 3 insertions(+), 11 deletions(-) diff --git a/client/src/views/Replay/Replay.js b/client/src/views/Replay/Replay.js index d01fc500..ae99d7ae 100644 --- a/client/src/views/Replay/Replay.js +++ b/client/src/views/Replay/Replay.js @@ -176,7 +176,7 @@ const Replay = () => { let data = replay.slice(0, step+1).map((item, index) => {return { key: index, - blockName: "", + blockId: item.blockId, timestamp: item.timestamp, action: item.action }}); @@ -285,14 +285,7 @@ const Replay = () => { >› -

{`[${timelineStates.step + 1}/${ - replay.length - }] Action: ${action}`}

- {replay[timelineStates.step]?.blockType !== '' && ( -

{`Block Type: ${ - replay[timelineStates.step]?.blockType - }`}

- )} +

{`Action ${step+1}/${replay.length}: ${action}`}

Date: Mon, 4 Apr 2022 21:54:53 -0400 Subject: [PATCH 04/19] allow timeline pagination to interact with timeline moving --- client/src/views/Replay/Replay.js | 85 ++++++++++++++----------------- 1 file changed, 38 insertions(+), 47 deletions(-) diff --git a/client/src/views/Replay/Replay.js b/client/src/views/Replay/Replay.js index ae99d7ae..153a7b47 100644 --- a/client/src/views/Replay/Replay.js +++ b/client/src/views/Replay/Replay.js @@ -17,8 +17,6 @@ const Replay = () => { const { saveID } = useParams(); const workspaceRef = useRef(null); const [replay, setReplay] = useState([]); - const [startIndex, setStartIndex] = useState(0); - const [endIndex, setEndIndex] = useState(TIME_LINE_SIZE); const [isPlaying, setIsPlaying] = useState(false); const [playbackRef, setPlaybackRef] = useState(null); const [playSpeed, setPlaySpeed] = useState(500); @@ -26,34 +24,48 @@ const Replay = () => { const [action, setAction] = useState(''); const [logData, setLogData] = useState([]); + const timelineReducer = (timeline, action) => { + const checkTimelineStepInBound = () =>{ + if(timeline.step >= timeline.endIndex){ + let newEnd = Math.min(replay.length, timeline.step+7); + timeline.endIndex = newEnd; + timeline.startIndex = newEnd-TIME_LINE_SIZE; + } + else if(timeline.step < timeline.startIndex){ + let newStart = Math.max(0, timeline.step-6); + timeline.startIndex = newStart; + timeline.endIndex = newStart+TIME_LINE_SIZE; + } + } + switch (action.type) { case 'IncrementStep': - timeline.step += 1; + timeline.step += 1; checkTimelineStepInBound(); - return { ...timeline }; + return {...timeline}; case 'DecrementStep': - timeline.step -= 1; + timeline.step -= 1; checkTimelineStepInBound(); - return { ...timeline }; + return {...timeline}; case 'SetStepValue': timeline.step = action.value; - return { ...timeline }; + return {...timeline}; case 'IncrementTimeline': - if (timeline.endIndex <= replay.length) { + if(timeline.endIndex <= replay.length){ timeline.startIndex += 5; timeline.endIndex = Math.min(timeline.endIndex + 5, replay.length); } - return { ...timeline }; + return {...timeline}; case 'DecrementTimeline': - if (timeline.startIndex >= 0) { + if(timeline.startIndex >= 0){ timeline.startIndex = Math.max(timeline.startIndex - 5, 0); timeline.endIndex -= 5; } - return { ...timeline }; + return {...timeline}; default: return timeline; @@ -61,13 +73,12 @@ const Replay = () => { }; const [timelineStates, dispatchTimelineReducer] = useReducer( - timelineReducer, + timelineReducer, { step: 0, startIndex: 0, - endIndex: TIME_LINE_SIZE, - } - ); + endIndex: TIME_LINE_SIZE + }); const setWorkspace = () => { workspaceRef.current = window.Blockly.inject('blockly-canvas', { @@ -119,25 +130,11 @@ const Replay = () => { ]; const goBack = () => { - dispatch({ type: 'Decrement' }); - - if(step < startIndex){ - let newStart = Math.max(0, step-5); - setStartIndex(newStart); - setEndIndex(newStart + TIME_LINE_SIZE); - } - + dispatchTimelineReducer({ type: 'DecrementStep' }); }; const goForward = () => { - dispatch({ type: 'Increment' }); - - if(step >= endIndex){ - let newEnd = Math.min(replay.length, step+5); - setEndIndex(newEnd); - setStartIndex(newEnd-TIME_LINE_SIZE); - } - + dispatchTimelineReducer({ type: 'IncrementStep' }); }; const setStep = (value) => { @@ -165,15 +162,15 @@ const Replay = () => { //handle dynamic playback changes useEffect(() => { if (replay.length) { - if (timelineStates.step >= replay.length - 1 && isPlaying) handlePause(); + if (timelineStates.step === replay.length - 1 && isPlaying) handlePause(); workspaceRef.current ? workspaceRef.current.clear() : setWorkspace(); const xml = window.Blockly.Xml.textToDom(replay[timelineStates.step].xml); window.Blockly.Xml.domToWorkspace(xml, workspaceRef.current); - setAction(replay[step].action); + setAction(replay[timelineStates.step].action); //update log - let data = replay.slice(0, step+1).map((item, index) => + let data = replay.slice(0, timelineStates.step+1).map((item, index) => {return { key: index, blockId: item.blockId, @@ -190,17 +187,11 @@ const Replay = () => { }; const scrollTimelineForward = () => { - if(endIndex < replay.length){ - setStartIndex(startIndex + 5); - setEndIndex(Math.min(endIndex + 5, replay.length)); - } + dispatchTimelineReducer({type: 'IncrementTimeline'}); } const scrollTimelineBackward = () => { - if(startIndex > 0){ - setStartIndex(Math.max(startIndex - 5, 0)); - setEndIndex(endIndex - 5); - } + dispatchTimelineReducer({type: 'DecrementTimeline'}); } return ( @@ -265,27 +256,27 @@ const Replay = () => {
{replay.map((item, index) => (
setStep(index)} > {formatMyDate(item.timestamp)}
- )).slice(startIndex, endIndex)} + )).slice(timelineStates.startIndex, timelineStates.endIndex)}
-

{`Action ${step+1}/${replay.length}: ${action}`}

+

{`Action ${timelineStates.step+1}/${replay.length}: ${action}`}

Date: Thu, 7 Apr 2022 17:16:36 -0400 Subject: [PATCH 05/19] display all logs, highlight current log entry --- client/src/views/Replay/Replay.js | 26 +++++++++++++++----------- client/src/views/Replay/Replay.less | 15 +++++++++++++++ 2 files changed, 30 insertions(+), 11 deletions(-) diff --git a/client/src/views/Replay/Replay.js b/client/src/views/Replay/Replay.js index 153a7b47..0d8531e2 100644 --- a/client/src/views/Replay/Replay.js +++ b/client/src/views/Replay/Replay.js @@ -96,6 +96,17 @@ const Replay = () => { const save = await getSave(saveID); console.log(save.data.replay); setReplay(save.data.replay); + + //set log + let data = save.data.replay.map((item, index) => + {return { + key: index, + blockId: item.blockId, + timestamp: item.timestamp, + action: item.action + }}); + + setLogData(data); }; getReplay(); }, []); @@ -169,16 +180,6 @@ const Replay = () => { window.Blockly.Xml.domToWorkspace(xml, workspaceRef.current); setAction(replay[timelineStates.step].action); - //update log - let data = replay.slice(0, timelineStates.step+1).map((item, index) => - {return { - key: index, - blockId: item.blockId, - timestamp: item.timestamp, - action: item.action - }}); - - setLogData(data); } }, [replay, timelineStates, isPlaying, handlePause]); @@ -297,7 +298,10 @@ const Replay = () => { >

Logs

record.key === timelineStates.step ? 'table-row-dark' : 'table-row-light'} + scroll={{y:300}} + pagination={false} columns={columns} dataSource={logData} /> diff --git a/client/src/views/Replay/Replay.less b/client/src/views/Replay/Replay.less index 5e5eb98c..3e4f8f88 100644 --- a/client/src/views/Replay/Replay.less +++ b/client/src/views/Replay/Replay.less @@ -61,3 +61,18 @@ #action-title{ color: white; } + +#replay-log{ + + tbody > tr > td { + background: none; + } + + .table-row-light { + background-color: #ffffff; + } + .table-row-dark { + background-color: #cecece; + } + +} From 77574a055d4c33c7ee5aa7e9006edb7e485f7f83 Mon Sep 17 00:00:00 2001 From: chensation Date: Thu, 7 Apr 2022 17:29:38 -0400 Subject: [PATCH 06/19] click on the row to navigative to action --- client/src/views/Replay/Replay.js | 8 +++++++- client/src/views/Replay/Replay.less | 4 +++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/client/src/views/Replay/Replay.js b/client/src/views/Replay/Replay.js index 0d8531e2..d474e46f 100644 --- a/client/src/views/Replay/Replay.js +++ b/client/src/views/Replay/Replay.js @@ -51,6 +51,7 @@ const Replay = () => { case 'SetStepValue': timeline.step = action.value; + checkTimelineStepInBound(); return {...timeline}; case 'IncrementTimeline': @@ -299,7 +300,12 @@ const Replay = () => {

Logs

record.key === timelineStates.step ? 'table-row-dark' : 'table-row-light'} + rowClassName={(record, index) => "table-row " + (record.key === timelineStates.step ? 'table-row-dark' : 'table-row-light')} + onRow={(record, index)=> { + return { + onClick: () => setStep(record.key) + } + }} scroll={{y:300}} pagination={false} columns={columns} diff --git a/client/src/views/Replay/Replay.less b/client/src/views/Replay/Replay.less index 3e4f8f88..83358a64 100644 --- a/client/src/views/Replay/Replay.less +++ b/client/src/views/Replay/Replay.less @@ -67,7 +67,9 @@ tbody > tr > td { background: none; } - + .table-row { + cursor: pointer; + } .table-row-light { background-color: #ffffff; } From 23b8354d8860a183577b6675ed86588b17198d5c Mon Sep 17 00:00:00 2001 From: chensation Date: Thu, 7 Apr 2022 21:04:33 -0400 Subject: [PATCH 07/19] allow log to be downloaded --- client/src/views/Replay/Replay.js | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/client/src/views/Replay/Replay.js b/client/src/views/Replay/Replay.js index d474e46f..62985b85 100644 --- a/client/src/views/Replay/Replay.js +++ b/client/src/views/Replay/Replay.js @@ -11,6 +11,7 @@ import { useParams, useNavigate } from 'react-router-dom'; import NavBar from '../../components/NavBar/NavBar'; import { Table } from 'antd'; import { getSave } from '../../Utils/requests'; +import { CSVDownloader } from "react-papaparse"; const Replay = () => { const TIME_LINE_SIZE = 25; @@ -23,6 +24,7 @@ const Replay = () => { const navigate = useNavigate(); const [action, setAction] = useState(''); const [logData, setLogData] = useState([]); + const [logFilename, setLogFilename] = useState('code_replay'); const timelineReducer = (timeline, action) => { const checkTimelineStepInBound = () =>{ @@ -108,6 +110,7 @@ const Replay = () => { }}); setLogData(data); + setLogFilename(`${save.data.student.name}_${save.data.created_at}_code_replay`); }; getReplay(); }, []); @@ -297,7 +300,27 @@ const Replay = () => { id='bottom-container' className='flex flex-column vertical-container overflow-visible' > -

Logs

+
+

Logs

+ { + return logData.map(log => { + return { + "key": log.key, + "timestamp": formatMyDate(log.timestamp), + "block id": log.blockId, + "action": log.action + } + }); + }} + > + Download to CSV + +
+
"table-row " + (record.key === timelineStates.step ? 'table-row-dark' : 'table-row-light')} From cbaea97543360bea4026ddaa133e6c9c2a69a5f6 Mon Sep 17 00:00:00 2001 From: chensation Date: Thu, 14 Apr 2022 13:50:49 -0400 Subject: [PATCH 08/19] fixed merge error; added block type to log --- client/src/views/Replay/Replay.js | 29 ++++++++++++++++++++++++++--- 1 file changed, 26 insertions(+), 3 deletions(-) diff --git a/client/src/views/Replay/Replay.js b/client/src/views/Replay/Replay.js index 62985b85..a340c62e 100644 --- a/client/src/views/Replay/Replay.js +++ b/client/src/views/Replay/Replay.js @@ -105,6 +105,7 @@ const Replay = () => { {return { key: index, blockId: item.blockId, + blockType: item.blockType, timestamp: item.timestamp, action: item.action }}); @@ -135,6 +136,13 @@ const Replay = () => { width: '3%', align: 'center', }, + { + title: 'Block Type', + dataIndex: 'blockType', + key: 'blockId', + width: '3%', + align: 'center', + }, { title: 'Action', dataIndex: 'action', @@ -177,7 +185,7 @@ const Replay = () => { //handle dynamic playback changes useEffect(() => { if (replay.length) { - if (timelineStates.step === replay.length - 1 && isPlaying) handlePause(); + if (timelineStates.step >= replay.length - 1 && isPlaying) handlePause(); workspaceRef.current ? workspaceRef.current.clear() : setWorkspace(); const xml = window.Blockly.Xml.textToDom(replay[timelineStates.step].xml); @@ -199,6 +207,10 @@ const Replay = () => { dispatchTimelineReducer({type: 'DecrementTimeline'}); } + const handleGoBack = () => { + if (window.confirm('Comfirm going back')) navigate(-1); + }; + return (
@@ -263,7 +275,10 @@ const Replay = () => { + > + {' '} + ‹{' '} +
{replay.map((item, index) => (
{ >›
-

{`Action ${timelineStates.step+1}/${replay.length}: ${action}`}

+

{`[${timelineStates.step + 1}/${ + replay.length + }] Action: ${action}`}

+ {replay[timelineStates.step]?.blockType !== '' && ( +

{`Block Type: ${ + replay[timelineStates.step]?.blockType + }`}

+ )}
{ "key": log.key, "timestamp": formatMyDate(log.timestamp), "block id": log.blockId, + "block type": log.blockType, "action": log.action } }); From 9090321341aa4862c2c405c32e64e656308c1139 Mon Sep 17 00:00:00 2001 From: chensation Date: Thu, 14 Apr 2022 15:18:46 -0400 Subject: [PATCH 09/19] reordered log and csv --- .../canvas/StudentCanvas.js | 6 ++-- client/src/views/Replay/Replay.js | 31 ++++++++++++------- 2 files changed, 23 insertions(+), 14 deletions(-) diff --git a/client/src/components/DayPanels/BlocklyCanvasPanel/canvas/StudentCanvas.js b/client/src/components/DayPanels/BlocklyCanvasPanel/canvas/StudentCanvas.js index 7fa98477..63030383 100644 --- a/client/src/components/DayPanels/BlocklyCanvasPanel/canvas/StudentCanvas.js +++ b/client/src/components/DayPanels/BlocklyCanvasPanel/canvas/StudentCanvas.js @@ -82,9 +82,9 @@ export default function StudentCanvas({ day }) { }; const pushEvent = (type, blockId = '') => { - let bloackType = ''; + let blockType = ''; if (blockId !== '') { - bloackType = window.Blockly.mainWorkspace.getBlockById(blockId)?.type; + blockType = window.Blockly.mainWorkspace.getBlockById(blockId)?.type; } let xml = window.Blockly.Xml.workspaceToDom(workspaceRef.current); @@ -93,7 +93,7 @@ export default function StudentCanvas({ day }) { xml: xml_text, action: type, blockId: blockId, - blockType: bloackType, + blockType: blockType, timestamp: Date.now(), clicks: clicks.current, }); diff --git a/client/src/views/Replay/Replay.js b/client/src/views/Replay/Replay.js index a340c62e..ed003730 100644 --- a/client/src/views/Replay/Replay.js +++ b/client/src/views/Replay/Replay.js @@ -26,6 +26,7 @@ const Replay = () => { const [logData, setLogData] = useState([]); const [logFilename, setLogFilename] = useState('code_replay'); + const timelineReducer = (timeline, action) => { const checkTimelineStepInBound = () =>{ if(timeline.step >= timeline.endIndex){ @@ -97,7 +98,7 @@ const Replay = () => { useEffect(() => { const getReplay = async () => { const save = await getSave(saveID); - console.log(save.data.replay); + console.log(save.data); setReplay(save.data.replay); //set log @@ -117,6 +118,13 @@ const Replay = () => { }, []); const columns = [ + { + title: 'No.', + dataIndex: 'key', + key: 'blockId', + width: '3%', + align: 'center', + }, { title: 'Timestamp', dataIndex: 'timestamp', @@ -130,9 +138,9 @@ const Replay = () => { render: (timestamp) => formatMyDate(timestamp) }, { - title: 'Block ID', - dataIndex: 'blockId', - key: 'blockId', + title: 'Action', + dataIndex: 'action', + key: 'action', width: '3%', align: 'center', }, @@ -144,9 +152,9 @@ const Replay = () => { align: 'center', }, { - title: 'Action', - dataIndex: 'action', - key: 'action', + title: 'Block ID', + dataIndex: 'blockId', + key: 'blockId', width: '3%', align: 'center', }, @@ -200,7 +208,7 @@ const Replay = () => { }; const scrollTimelineForward = () => { - dispatchTimelineReducer({type: 'IncrementTimeline'}); + dispatchTimelineReducer({type: 'IncrementTimeline'}); } const scrollTimelineBackward = () => { @@ -241,6 +249,7 @@ const Replay = () => { onAfterChange={changePlaySpeed} disabled={isPlaying} reverse={true} + tooltipVisible={false} /> 🐇
@@ -331,11 +340,11 @@ const Replay = () => { data={()=>{ return logData.map(log => { return { - "key": log.key, + "No.": log.key, "timestamp": formatMyDate(log.timestamp), - "block id": log.blockId, + "action": log.action, "block type": log.blockType, - "action": log.action + "block id": log.blockId, } }); }} From 3d84b127af70840e212637e2f92d9de78ab59261 Mon Sep 17 00:00:00 2001 From: chensation Date: Thu, 14 Apr 2022 15:24:41 -0400 Subject: [PATCH 10/19] fixed delete cause undefined block type bug --- .../DayPanels/BlocklyCanvasPanel/canvas/StudentCanvas.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/client/src/components/DayPanels/BlocklyCanvasPanel/canvas/StudentCanvas.js b/client/src/components/DayPanels/BlocklyCanvasPanel/canvas/StudentCanvas.js index 63030383..0cfb34c6 100644 --- a/client/src/components/DayPanels/BlocklyCanvasPanel/canvas/StudentCanvas.js +++ b/client/src/components/DayPanels/BlocklyCanvasPanel/canvas/StudentCanvas.js @@ -84,7 +84,8 @@ export default function StudentCanvas({ day }) { const pushEvent = (type, blockId = '') => { let blockType = ''; if (blockId !== '') { - blockType = window.Blockly.mainWorkspace.getBlockById(blockId)?.type; + let type = window.Blockly.mainWorkspace.getBlockById(blockId)?.type; + type ? blockType = type : blockType = ''; } let xml = window.Blockly.Xml.workspaceToDom(workspaceRef.current); From 92296e5d622762194c4988171fa8dc878fb62e1a Mon Sep 17 00:00:00 2001 From: chensation Date: Thu, 14 Apr 2022 15:52:23 -0400 Subject: [PATCH 11/19] fixed timeline dispatch called twice bug --- client/src/views/Replay/Replay.js | 115 ++++++++++++++++-------------- 1 file changed, 61 insertions(+), 54 deletions(-) diff --git a/client/src/views/Replay/Replay.js b/client/src/views/Replay/Replay.js index ed003730..6e27de32 100644 --- a/client/src/views/Replay/Replay.js +++ b/client/src/views/Replay/Replay.js @@ -12,9 +12,62 @@ import NavBar from '../../components/NavBar/NavBar'; import { Table } from 'antd'; import { getSave } from '../../Utils/requests'; import { CSVDownloader } from "react-papaparse"; +const TIME_LINE_SIZE = 25; + +const timelineReducer = (timeline, action) => { + const checkTimelineStepInBound = () =>{ + if(timeline.step >= timeline.endIndex){ + let newEnd = Math.min(timeline.length, timeline.step+7); + timeline.endIndex = newEnd; + timeline.startIndex = newEnd-TIME_LINE_SIZE; + } + else if(timeline.step < timeline.startIndex){ + let newStart = Math.max(0, timeline.step-6); + timeline.startIndex = newStart; + timeline.endIndex = newStart+TIME_LINE_SIZE; + } + } + + switch (action.type) { + case 'IncrementStep': + timeline.step += 1; + checkTimelineStepInBound(); + return {...timeline}; + + case 'DecrementStep': + timeline.step -= 1; + checkTimelineStepInBound(); + return {...timeline}; + + case 'SetStepValue': + timeline.step = action.value; + checkTimelineStepInBound(); + return {...timeline}; + + case 'IncrementTimeline': + if(timeline.endIndex <= timeline.length){ + timeline.startIndex += 5; + timeline.endIndex = Math.min(timeline.endIndex + 5, timeline.length); + } + return {...timeline}; + + case 'DecrementTimeline': + if(timeline.startIndex >= 0){ + timeline.startIndex = Math.max(timeline.startIndex - 5, 0); + timeline.endIndex -= 5; + } + return {...timeline}; + + case 'FetchReplayLength': + timeline.length = action.value; + return {...timeline}; + + default: + return timeline; + } +}; const Replay = () => { - const TIME_LINE_SIZE = 25; const { saveID } = useParams(); const workspaceRef = useRef(null); const [replay, setReplay] = useState([]); @@ -26,63 +79,14 @@ const Replay = () => { const [logData, setLogData] = useState([]); const [logFilename, setLogFilename] = useState('code_replay'); - - const timelineReducer = (timeline, action) => { - const checkTimelineStepInBound = () =>{ - if(timeline.step >= timeline.endIndex){ - let newEnd = Math.min(replay.length, timeline.step+7); - timeline.endIndex = newEnd; - timeline.startIndex = newEnd-TIME_LINE_SIZE; - } - else if(timeline.step < timeline.startIndex){ - let newStart = Math.max(0, timeline.step-6); - timeline.startIndex = newStart; - timeline.endIndex = newStart+TIME_LINE_SIZE; - } - } - - switch (action.type) { - case 'IncrementStep': - timeline.step += 1; - checkTimelineStepInBound(); - return {...timeline}; - - case 'DecrementStep': - timeline.step -= 1; - checkTimelineStepInBound(); - return {...timeline}; - - case 'SetStepValue': - timeline.step = action.value; - checkTimelineStepInBound(); - return {...timeline}; - - case 'IncrementTimeline': - if(timeline.endIndex <= replay.length){ - timeline.startIndex += 5; - timeline.endIndex = Math.min(timeline.endIndex + 5, replay.length); - } - return {...timeline}; - - case 'DecrementTimeline': - if(timeline.startIndex >= 0){ - timeline.startIndex = Math.max(timeline.startIndex - 5, 0); - timeline.endIndex -= 5; - } - return {...timeline}; - - default: - return timeline; - } - }; - const [timelineStates, dispatchTimelineReducer] = useReducer( timelineReducer, { step: 0, startIndex: 0, - endIndex: TIME_LINE_SIZE - }); + endIndex: TIME_LINE_SIZE, + length: 0 + } ); const setWorkspace = () => { workspaceRef.current = window.Blockly.inject('blockly-canvas', { @@ -98,9 +102,12 @@ const Replay = () => { useEffect(() => { const getReplay = async () => { const save = await getSave(saveID); - console.log(save.data); + console.log(save.data.replay); setReplay(save.data.replay); + dispatchTimelineReducer({ + type: 'FetchReplayLength', + value: save.data.replay.length}); //set log let data = save.data.replay.map((item, index) => {return { From a1dbb774a929db67582a4dc1f933ddca329002c9 Mon Sep 17 00:00:00 2001 From: chensation Date: Thu, 14 Apr 2022 16:47:35 -0400 Subject: [PATCH 12/19] added filter to log --- client/src/views/Replay/Replay.js | 32 +++++++++++++++++++++++++++++-- 1 file changed, 30 insertions(+), 2 deletions(-) diff --git a/client/src/views/Replay/Replay.js b/client/src/views/Replay/Replay.js index 6e27de32..8eea44e5 100644 --- a/client/src/views/Replay/Replay.js +++ b/client/src/views/Replay/Replay.js @@ -78,6 +78,9 @@ const Replay = () => { const [action, setAction] = useState(''); const [logData, setLogData] = useState([]); const [logFilename, setLogFilename] = useState('code_replay'); + const [actionFilter, setActionFilter] = useState([]); + const [blockTypeFilter, setBlockTypeFilter] = useState([]); + const [blockIdFilter, setBlockIdFilter] = useState([]); const [timelineStates, dispatchTimelineReducer] = useReducer( timelineReducer, @@ -99,6 +102,20 @@ const Replay = () => { return new Date(timestamp).toLocaleTimeString(locale); }; + const makeFilter = (data, category) => { + let filter = []; + const map = new Map(); + + data.forEach((element) => { + const name = element[category]; + if (name && !map.has(name)) { + filter.push({ text: name, value: name }); + map.set(name, true); + } + }); + return filter; + }; + useEffect(() => { const getReplay = async () => { const save = await getSave(saveID); @@ -120,6 +137,11 @@ const Replay = () => { setLogData(data); setLogFilename(`${save.data.student.name}_${save.data.created_at}_code_replay`); + + setActionFilter(makeFilter(data, 'action')); + setBlockTypeFilter(makeFilter(data, 'blockType')); + setBlockIdFilter(makeFilter(data, 'blockId')); + }; getReplay(); }, []); @@ -128,7 +150,7 @@ const Replay = () => { { title: 'No.', dataIndex: 'key', - key: 'blockId', + key: 'key', width: '3%', align: 'center', }, @@ -150,13 +172,17 @@ const Replay = () => { key: 'action', width: '3%', align: 'center', + filters: actionFilter, + onFilter: (value, record) => record.action.indexOf(value) === 0 }, { title: 'Block Type', dataIndex: 'blockType', - key: 'blockId', + key: 'blockType', width: '3%', align: 'center', + filters: blockTypeFilter, + onFilter: (value, record) => record.blockType.indexOf(value) === 0 }, { title: 'Block ID', @@ -164,6 +190,8 @@ const Replay = () => { key: 'blockId', width: '3%', align: 'center', + filters: blockIdFilter, + onFilter: (value, record) => record.blockId.indexOf(value) === 0 }, ]; From c5022060d44a2b325019e49a8cfebb6fad216594 Mon Sep 17 00:00:00 2001 From: chensation Date: Thu, 14 Apr 2022 17:09:35 -0400 Subject: [PATCH 13/19] change filter comparison; change download to obey filter --- client/src/views/Replay/Replay.js | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/client/src/views/Replay/Replay.js b/client/src/views/Replay/Replay.js index 8eea44e5..c3635e12 100644 --- a/client/src/views/Replay/Replay.js +++ b/client/src/views/Replay/Replay.js @@ -77,7 +77,8 @@ const Replay = () => { const navigate = useNavigate(); const [action, setAction] = useState(''); const [logData, setLogData] = useState([]); - const [logFilename, setLogFilename] = useState('code_replay'); + const [csvData, setCsvData] = useState([]); + const [csvFilename, setCsvFilename] = useState('code_replay'); const [actionFilter, setActionFilter] = useState([]); const [blockTypeFilter, setBlockTypeFilter] = useState([]); const [blockIdFilter, setBlockIdFilter] = useState([]); @@ -136,7 +137,8 @@ const Replay = () => { }}); setLogData(data); - setLogFilename(`${save.data.student.name}_${save.data.created_at}_code_replay`); + setCsvData(data.slice(0).reverse()); + setCsvFilename(`${save.data.student.name}_${save.data.created_at}_code_replay`); setActionFilter(makeFilter(data, 'action')); setBlockTypeFilter(makeFilter(data, 'blockType')); @@ -153,6 +155,7 @@ const Replay = () => { key: 'key', width: '3%', align: 'center', + render: (key) => key+1 }, { title: 'Timestamp', @@ -173,7 +176,7 @@ const Replay = () => { width: '3%', align: 'center', filters: actionFilter, - onFilter: (value, record) => record.action.indexOf(value) === 0 + onFilter: (value, record) => record.action === value }, { title: 'Block Type', @@ -182,7 +185,7 @@ const Replay = () => { width: '3%', align: 'center', filters: blockTypeFilter, - onFilter: (value, record) => record.blockType.indexOf(value) === 0 + onFilter: (value, record) => record.blockType === value }, { title: 'Block ID', @@ -191,7 +194,7 @@ const Replay = () => { width: '3%', align: 'center', filters: blockIdFilter, - onFilter: (value, record) => record.blockId.indexOf(value) === 0 + onFilter: (value, record) => record.blockId === value }, ]; @@ -369,13 +372,13 @@ const Replay = () => {

Logs

{ - return logData.map(log => { + return csvData.map(log => { return { - "No.": log.key, + "No.": log.key+1, "timestamp": formatMyDate(log.timestamp), "action": log.action, "block type": log.blockType, @@ -400,6 +403,9 @@ const Replay = () => { pagination={false} columns={columns} dataSource={logData} + onChange={(pagination, filter, sorter, extra) =>{ + setCsvData(extra.currentDataSource); + }} />
From 5fc7c331627097f8577f2eb6d5be27238478e23f Mon Sep 17 00:00:00 2001 From: chensation <32966177+chensation@users.noreply.github.com> Date: Thu, 14 Apr 2022 15:44:27 -0700 Subject: [PATCH 14/19] disable play when replay length is 0 --- client/src/views/Replay/Replay.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/src/views/Replay/Replay.js b/client/src/views/Replay/Replay.js index c3635e12..ac4ca357 100644 --- a/client/src/views/Replay/Replay.js +++ b/client/src/views/Replay/Replay.js @@ -301,7 +301,7 @@ const Replay = () => { @@ -312,7 +312,7 @@ const Replay = () => { From 85fb15cfe43d7e9b71ff0e773d56773420021621 Mon Sep 17 00:00:00 2001 From: Siyu Chen Date: Thu, 14 Apr 2022 23:21:38 -0400 Subject: [PATCH 16/19] added select block --- client/src/views/Replay/Replay.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/client/src/views/Replay/Replay.js b/client/src/views/Replay/Replay.js index eb1b81c4..75886d94 100644 --- a/client/src/views/Replay/Replay.js +++ b/client/src/views/Replay/Replay.js @@ -236,6 +236,10 @@ const Replay = () => { workspaceRef.current ? workspaceRef.current.clear() : setWorkspace(); const xml = window.Blockly.Xml.textToDom(replay[timelineStates.step].xml); window.Blockly.Xml.domToWorkspace(xml, workspaceRef.current); + if (replay[timelineStates.step].blockId) + window.Blockly.mainWorkspace + .getBlockById(replay[timelineStates.step].blockId) + ?.select(); setAction(replay[timelineStates.step].action); } From c03277325e3204cc04390c6a435cac6ed199b2c2 Mon Sep 17 00:00:00 2001 From: Siyu Chen Date: Thu, 14 Apr 2022 23:33:05 -0400 Subject: [PATCH 17/19] remove excessive files & console log --- client/src/Utils/analytics.js | 61 ------ .../DayPanels/Utils/consoleHelpers.js | 3 - .../components/DayPanels/consoleHelpers.js | 123 ----------- client/src/components/DayPanels/helpers.js | 197 ------------------ .../components/DropdownMenu/ReportDropdown.js | 29 --- .../DropdownMenu/ReportDropdown.less | 67 ------ .../src/components/Tabs/SavedWorkspaceTab.js | 1 - .../LearningStandardCreator.js | 1 - .../LearningStandardDayCreator/DayEditor.js | 1 - .../ContentCreator/UnitCreator/UnitCreator.js | 2 - client/src/views/Dashboard/Dashboard.js | 1 - .../src/views/Mentor/Classroom/Home/Home.js | 1 - .../views/Mentor/Classroom/Roster/CardView.js | 2 +- .../views/Mentor/Classroom/Roster/ListView.js | 1 - client/src/views/Replay/Replay.js | 1 - .../src/views/TeacherLogin/ForgetPassword.js | 1 - client/src/views/TeacherLogin/TeacherLogin.js | 1 - client/src/views/Workspace/Workspace.js | 1 - 18 files changed, 1 insertion(+), 493 deletions(-) delete mode 100644 client/src/Utils/analytics.js delete mode 100644 client/src/components/DayPanels/consoleHelpers.js delete mode 100644 client/src/components/DayPanels/helpers.js delete mode 100644 client/src/components/DropdownMenu/ReportDropdown.js delete mode 100644 client/src/components/DropdownMenu/ReportDropdown.less diff --git a/client/src/Utils/analytics.js b/client/src/Utils/analytics.js deleted file mode 100644 index ab461aa5..00000000 --- a/client/src/Utils/analytics.js +++ /dev/null @@ -1,61 +0,0 @@ -export const parseXML = xml => { - const parser = new DOMParser(); - const xmlData = { - blocks: {}, - categories: {} - }; - let xmlDoc = parser.parseFromString(xml, "text/xml"); - const xmlBlocks = xmlDoc.querySelectorAll('block'); - for (const block of xmlBlocks) { - const blockType = block.getAttribute('type'); - if(xmlData.blocks[blockType]) { - xmlData.blocks[blockType].count++; - } else { - xmlData.blocks[blockType] = { - count: 1, - deleted: 0 - }; - } - } - return xmlData; -} - -export const compareXML = ({blocks: currentBlocks}, {blocks: previousBlocks}) => { - const blocks = diffObjects(currentBlocks, previousBlocks); - return { - blocks - } -} - -const diffObjects = (currentObj, previousObj) => { - const currentKeys = Object.keys(currentObj) - const prevKeys = Object.keys(previousObj) - // deleted all of one type of block - if (prevKeys.length > currentKeys.length) { - for(let key of prevKeys) { - if(currentKeys.indexOf(key) === -1) { - console.log('deleted all of one block'); - currentObj[key] = { - count: 0, - deleted: previousObj[key].deleted + 1 - } - } - } - } - for (let key of currentKeys) { - if (key in previousObj) { - if (currentObj[key].count < previousObj[key].count) { - currentObj[key].deleted = previousObj[key].deleted + 1; - console.log('a block was deleted'); - } - if(currentObj[key].deleted < previousObj[key].deleted) { - console.log('a block was reintroduced'); - currentObj[key] = { - count: 1, - deleted: previousObj[key].deleted - } - } - } - } - return currentObj -} \ No newline at end of file diff --git a/client/src/components/DayPanels/Utils/consoleHelpers.js b/client/src/components/DayPanels/Utils/consoleHelpers.js index fe8a3018..285f5024 100644 --- a/client/src/components/DayPanels/Utils/consoleHelpers.js +++ b/client/src/components/DayPanels/Utils/consoleHelpers.js @@ -59,7 +59,6 @@ const readUntilClose = async ( .pipeThrough(new window.TransformStream(new LineBreakTransformer())) .getReader(); - console.log('reader opened'); let string = ''; plotData = []; @@ -70,7 +69,6 @@ const readUntilClose = async ( reader.releaseLock(); break; } - console.log(value); if (type === 'notNewLine') { string += value; document.getElementById('console-content').innerHTML = string; @@ -100,7 +98,6 @@ export const writeToPort = async (data) => { writer = port.writable.getWriter(); data += '\n'; await writer.write(textEncoder.encode(data)); - console.log(textEncoder.encode(data)); writer.releaseLock(); }; diff --git a/client/src/components/DayPanels/consoleHelpers.js b/client/src/components/DayPanels/consoleHelpers.js deleted file mode 100644 index 10081017..00000000 --- a/client/src/components/DayPanels/consoleHelpers.js +++ /dev/null @@ -1,123 +0,0 @@ -let port; -let reader; -let writer; -let readableStreamClosed; - -class LineBreakTransformer { - constructor() { - this.container = ''; - } - - transform(chunk, controller) { - this.container += chunk; - const lines = this.container.split('\r\n'); - this.container = lines.pop(); - lines.forEach((line) => controller.enqueue(line)); - } - - flush(controller) { - controller.enqueue(this.container); - } -} - -export const openConnection = async (baudRate_, newLine) => { - //requesting port on the pop up window. - port = window['port']; - - var options = { - baudRate: baudRate_, - parity: 'none', - dataBits: 8, - stopBits: 1, - bufferSize: 1024, - }; - - // connect to port on baudRate 9600. - await port.open(options); - console.log(`port opened at baud rate: ${baudRate_} `); - document.getElementById('console-content').innerHTML = ''; - readUntilClose(newLine); -}; - -const readUntilClose = async (newLine) => { - const textDecoder = new window.TextDecoderStream(); - readableStreamClosed = port.readable.pipeTo(textDecoder.writable); - // reader = textDecoder.readable.getReader(); - reader = textDecoder.readable - .pipeThrough(new window.TransformStream(new LineBreakTransformer())) - .getReader(); - - console.log('reader opened'); - let string = ''; - while (true) { - const { value, done } = await reader.read(); - if (done) { - // Allow the serial port to be closed later. - reader.releaseLock(); - break; - } - console.log(value); - if (!newLine) { - string += value; - document.getElementById('console-content').innerHTML = string; - } else { - let newP = document.createElement('p'); - newP.innerHTML = value; - newP.style.margin = 0; - document.getElementById('console-content').appendChild(newP); - newP.scrollIntoView(); - } - } -}; - -export const writeToPort = async (data) => { - const textEncoder = new window.TextEncoder(); - writer = port.writable.getWriter(); - data += '\n'; - await writer.write(textEncoder.encode(data)); - console.log(textEncoder.encode(data)); - writer.releaseLock(); -}; - -export const disconnect = async () => { - reader.cancel(); - await readableStreamClosed.catch(() => { - /* Ignore the error */ - }); - if (typeof writer !== 'undefined') { - const textEncoder = new window.TextEncoder(); - writer = port.writable.getWriter(); - await writer.write(textEncoder.encode('')); - await writer.close(); - } - await port.close(); -}; - -export const connectToPort = async () => { - const filters = [ - { usbVendorId: 0x2341, usbProductId: 0x0043 }, - { usbVendorId: 0x2341, usbProductId: 0x0001 }, - ]; - try { - port = await navigator.serial.requestPort({ filters }); - } catch (e) { - console.error(e); - return; - } - window['port'] = port; -}; - -export const handleOpenConnection = async (baudRate, newLine) => { - if (typeof window['port'] === 'undefined') { - await connectToPort(); - if (typeof window['port'] === 'undefined') { - return; - } - } - await openConnection(baudRate, newLine); -}; - -export const handleCloseConnection = async () => { - console.log('Close connection'); - disconnect(); -}; diff --git a/client/src/components/DayPanels/helpers.js b/client/src/components/DayPanels/helpers.js deleted file mode 100644 index 3bc83881..00000000 --- a/client/src/components/DayPanels/helpers.js +++ /dev/null @@ -1,197 +0,0 @@ -import { - createSubmission, - getSubmission, - saveWorkspace, - updateDayTemplate, -} from '../../Utils/requests'; -import { message } from 'antd'; - -const AvrboyArduino = window.AvrgirlArduino; - -export const setLocalSandbox = (workspaceRef) => { - let workspaceDom = window.Blockly.Xml.workspaceToDom(workspaceRef); - let workspaceText = window.Blockly.Xml.domToText(workspaceDom); - const localActivity = JSON.parse(localStorage.getItem('sandbox-day')); - - let lastActivity = { ...localActivity, template: workspaceText }; - localStorage.setItem('sandbox-day', JSON.stringify(lastActivity)); -}; - -// Generates xml from blockly canvas -export const getXml = (workspaceRef, shouldAlert = true) => { - const { Blockly } = window; - - let xml = Blockly.Xml.workspaceToDom(workspaceRef); - let xml_text = Blockly.Xml.domToText(xml); - if (shouldAlert) alert(xml_text); - return xml_text; -}; - -// Generates javascript code from blockly canvas -export const getJS = (workspaceRef) => { - window.Blockly.JavaScript.INFINITE_LOOP_TRAP = null; - let code = window.Blockly.JavaScript.workspaceToCode(workspaceRef); - alert(code); - return code; -}; - -// Generates Arduino code from blockly canvas -export const getArduino = (workspaceRef, shouldAlert = true) => { - window.Blockly.Arduino.INFINITE_LOOP_TRAP = null; - let code = window.Blockly.Arduino.workspaceToCode(workspaceRef); - if (shouldAlert) alert(code); - return code; -}; - -let intervalId; -const compileFail = (setSelectedCompile, setCompileError, msg) => { - setSelectedCompile(false); - message.error('Compile Fail', 3); - setCompileError(msg); -}; -// Sends compiled arduino code to server and returns hex to flash board with -export const compileArduinoCode = async ( - workspaceRef, - setSelectedCompile, - setCompileError, - day, - isStudent -) => { - setSelectedCompile(true); - const sketch = getArduino(workspaceRef, false); - let workspaceDom = window.Blockly.Xml.workspaceToDom(workspaceRef); - let workspaceText = window.Blockly.Xml.domToText(workspaceDom); - let path; - isStudent ? (path = '/submissions') : (path = '/sandbox/submission'); - let id = isStudent ? day.id : undefined; - - // create an initial submission - const initialSubmission = await createSubmission( - id, - workspaceText, - sketch, - path, - isStudent - ); - - // if we fail to create submission - if (!initialSubmission.data) { - compileFail( - setSelectedCompile, - setCompileError, - 'Oops. Something went wrong, please check your internet connection.' - ); - return; - } - // Get the submission Id and send a request to get the submission every - // 0.25 second until the submission status equal to COMPLETE. - intervalId = setInterval( - () => - getAndFlashSubmission( - initialSubmission.data.id, - path, - isStudent, - setSelectedCompile, - setCompileError - ), - 250 - ); - - // Set a timeout of 20 second. If the submission status fail to update to - // COMPLETE, show error. - setTimeout(() => { - if (intervalId) { - clearInterval(intervalId); - intervalId = undefined; - compileFail( - setSelectedCompile, - setCompileError, - 'Oops. Something went wrong, please try again.' - ); - } - }, 20000); -}; - -const getAndFlashSubmission = async ( - id, - path, - isStudent, - setSelectedCompile, - setCompileError -) => { - // get the submission - const response = await getSubmission(id, path, isStudent); - // If we fail to retrive submission - if (!response.data) { - if (intervalId) { - clearInterval(intervalId); - intervalId = undefined; - } - compileFail( - setSelectedCompile, - setCompileError, - 'Oops. Something went wrong, please check your internet connection.' - ); - return; - } - - // if the submission is not complete, try again later - if (response.data.status !== 'COMPLETED') { - return; - } - - // If the submission is ready - if (intervalId) { - clearInterval(intervalId); - intervalId = undefined; - } - // flash the board with the output - await flashArduino(response, setSelectedCompile, setCompileError); -}; - -const flashArduino = async (response, setSelectedCompile, setCompileError) => { - if (response.data) { - // if we get a success status from the submission, send it to arduino - if (response.data.success) { - // converting base 64 to hex - let Hex = atob(response.data.hex).toString(); - - const avrgirl = new AvrboyArduino({ - board: 'uno', - debug: true, - }); - - avrgirl.flash(Hex, (err) => { - if (err) { - console.log(err); - } else { - console.log('done correctly.'); - message.success('Compile Success', 3); - setSelectedCompile(false); - } - }); - } - // else if there is error on the Arduino code, show error - else if (response.data.stderr) { - message.error('Compile Fail', 3); - setSelectedCompile(false); - setCompileError(response.data.stderr); - } - } else { - message.error(response.err); - } -}; - -// save current workspace -export const handleSave = async (dayId, workspaceRef, replay) => { - let xml = window.Blockly.Xml.workspaceToDom(workspaceRef.current); - let xml_text = window.Blockly.Xml.domToText(xml); - return await saveWorkspace(dayId, xml_text, replay); -}; - -export const handleCreatorSaveDay = async (dayId, workspaceRef, blocksList) => { - let xml = window.Blockly.Xml.workspaceToDom(workspaceRef.current); - let xml_text = window.Blockly.Xml.domToText(xml); - - return await updateDayTemplate(dayId, xml_text, blocksList); -}; diff --git a/client/src/components/DropdownMenu/ReportDropdown.js b/client/src/components/DropdownMenu/ReportDropdown.js deleted file mode 100644 index e8b5b812..00000000 --- a/client/src/components/DropdownMenu/ReportDropdown.js +++ /dev/null @@ -1,29 +0,0 @@ -import React from 'react'; -import './ReportDropdown.less'; -import { Form, Select } from 'antd'; -const { Option } = Select; - - -function handleChange(value) { - console.log(`selected ${value}`); -} - -export default function ReportDropdown({label, menuName, menuItems}) { - const menus = Object.entries(menuItems).map((key) => { - return ( - - ) - }) - - return ( -
- - - -
- ) -} diff --git a/client/src/components/DropdownMenu/ReportDropdown.less b/client/src/components/DropdownMenu/ReportDropdown.less deleted file mode 100644 index 24c6d791..00000000 --- a/client/src/components/DropdownMenu/ReportDropdown.less +++ /dev/null @@ -1,67 +0,0 @@ -@import "../../assets/style.less"; - -// .report-menu-item { -// display: flex; -// justify-content: space-between; -// align-items: center; -// flex-wrap: nowrap; -// width: 100%; -// position: -webkit-sticky; -// position: fixed; -// top: 0; -// z-index: 2; -// height: 8vh; -// background-color: #colors[primary]; -// padding-right: 2vw; - -// -moz-box-shadow: 0 1px 5px -1px darken(#colors[primary], 90%); -// -webkit-box-shadow: 0 1px 5px -1px darken(#colors[primary], 90%); -// box-shadow: 0 1px 5px -1px darken(#colors[primary], 90%); - -// #link { -// #casmm-logo { -// position: relative; -// bottom: 0; -// height: 6vh; -// width: auto; -// } -// } -// } - -// #menu-link { -// background: none !important; -// border: none; -// padding: 0 !important; -// display: inline-block; - -// &:focus { -// outline: none; -// } - -// &:hover { -// cursor: pointer; -// } -// } - -.ant-select-selection-item { - color: black; - border: none; - // padding: 1rem !important; - font-size: 1rem; - font-weight: bold; - - &:focus { - outline: none; - } - - &:hover { - color: #colors[secondary]; - cursor: pointer; - } -} - -.ant-form-item-label > label { - color: black; - font-size: 1rem; - font-weight: bold; -} \ No newline at end of file diff --git a/client/src/components/Tabs/SavedWorkspaceTab.js b/client/src/components/Tabs/SavedWorkspaceTab.js index a83194b4..a0c9f535 100644 --- a/client/src/components/Tabs/SavedWorkspaceTab.js +++ b/client/src/components/Tabs/SavedWorkspaceTab.js @@ -28,7 +28,6 @@ export default function SavedWorkSpaceTab({searchParams, setSearchParams, classr } setWorkspaceList(wsResponse.data); - console.log(wsResponse.data); }; fetchData(); }, [classroomId]); diff --git a/client/src/views/ContentCreator/LearningStandardCreator/LearningStandardCreator.js b/client/src/views/ContentCreator/LearningStandardCreator/LearningStandardCreator.js index 0577318d..70849e5b 100644 --- a/client/src/views/ContentCreator/LearningStandardCreator/LearningStandardCreator.js +++ b/client/src/views/ContentCreator/LearningStandardCreator/LearningStandardCreator.js @@ -99,7 +99,6 @@ export default function LearningStandardCreator({ }; const checkURL = (n) => { - console.log(n); const regex = /https?:\/\/(www\.)?[-a-zA-Z0-9@:%._+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_+.~#?&//=]*)/g; if (n.search(regex) === -1) { diff --git a/client/src/views/ContentCreator/LearningStandardDayCreator/DayEditor.js b/client/src/views/ContentCreator/LearningStandardDayCreator/DayEditor.js index 5e79c6a5..2f17ffb2 100644 --- a/client/src/views/ContentCreator/LearningStandardDayCreator/DayEditor.js +++ b/client/src/views/ContentCreator/LearningStandardDayCreator/DayEditor.js @@ -39,7 +39,6 @@ export default function ContentCreator({ learningStandard }) { message.error(response.err); } setDay([...days, response.data]); - console.log(response); }; const removeBasicDay = async (currDay) => { diff --git a/client/src/views/ContentCreator/UnitCreator/UnitCreator.js b/client/src/views/ContentCreator/UnitCreator/UnitCreator.js index b6bc47da..c1d30d88 100644 --- a/client/src/views/ContentCreator/UnitCreator/UnitCreator.js +++ b/client/src/views/ContentCreator/UnitCreator/UnitCreator.js @@ -24,9 +24,7 @@ export default function UnitCreator({ gradeList }) { }; const handleSubmit = async (e) => { - console.log(e); const res = await createUnit(number, name, tek, description, grade); - console.log(res); if (res.err) { message.error('Fail to create a new unit'); } else { diff --git a/client/src/views/Dashboard/Dashboard.js b/client/src/views/Dashboard/Dashboard.js index 99ffbb21..91e3ca7d 100644 --- a/client/src/views/Dashboard/Dashboard.js +++ b/client/src/views/Dashboard/Dashboard.js @@ -17,7 +17,6 @@ export default function Dashboard() { let classroomIds = []; getMentor().then((res) => { if (res.data) { - console.log(res.data); res.data.classrooms.forEach((classroom) => { classroomIds.push(classroom.id); }); diff --git a/client/src/views/Mentor/Classroom/Home/Home.js b/client/src/views/Mentor/Classroom/Home/Home.js index 36a02a32..9665c953 100644 --- a/client/src/views/Mentor/Classroom/Home/Home.js +++ b/client/src/views/Mentor/Classroom/Home/Home.js @@ -44,7 +44,6 @@ export default function Home({ classroomId, viewing }) { else { message.error(daysRes.err); } - console.log(daysRes.data); } }); } else { diff --git a/client/src/views/Mentor/Classroom/Roster/CardView.js b/client/src/views/Mentor/Classroom/Roster/CardView.js index f99d19df..865b9f69 100644 --- a/client/src/views/Mentor/Classroom/Roster/CardView.js +++ b/client/src/views/Mentor/Classroom/Roster/CardView.js @@ -24,7 +24,7 @@ export default function CardView(props) {
- +
- {replay.map((item, index) => ( -
setStep(index)} - > - {formatMyDate(item.timestamp)} -
- )).slice(timelineStates.startIndex, timelineStates.endIndex)} + {replay + .map((item, index) => ( +
setStep(index)} + > + {formatMyDate(item.timestamp)} +
+ )) + .slice(timelineStates.startIndex, timelineStates.endIndex)}
+ disabled={timelineStates.endIndex >= replay.length} + onClick={scrollTimelineForward} + > + › +
-

{`[${timelineStates.step + 1}/${ - replay.length - }] Action: ${action}`}

- {replay[timelineStates.step]?.blockType !== '' && ( -

{`Block Type: ${ - replay[timelineStates.step]?.blockType - }`}

- )}
-

Code Replay

+ +
+ Code Replay + + + {`[${timelineStates.step + 1}/${ + replay.length + }] Action: ${action}`} + {replay[timelineStates.step]?.blockType !== '' && ( + <>{`, Block Type: ${ + replay[timelineStates.step]?.blockType + }`} + )} + +
- {/*
- { replay.map((item, index) =>
{timeConverter(item.timestamp)}
)} -
*/}
@@ -376,37 +389,42 @@ const Replay = () => {

Logs

{ - return csvData.map(log => { + data={() => { + return csvData.map((log) => { return { - "No.": log.key+1, - "timestamp": formatMyDate(log.timestamp), - "action": log.action, - "block type": log.blockType, - "block id": log.blockId, - } + 'No.': log.key + 1, + timestamp: formatMyDate(log.timestamp), + action: log.action, + 'block type': log.blockType, + 'block id': log.blockId, + }; }); }} > Download to CSV
- +
"table-row " + (record.key === timelineStates.step ? 'table-row-dark' : 'table-row-light')} - onRow={(record, index)=> { + rowClassName={(record, index) => + 'table-row ' + + (record.key === timelineStates.step + ? 'table-row-dark' + : 'table-row-light') + } + onRow={(record, index) => { return { - onClick: () => setStep(record.key) - } + onClick: () => setStep(record.key), + }; }} - scroll={{y:300}} + scroll={{ y: 300 }} pagination={false} columns={columns} dataSource={logData} - onChange={(pagination, filter, sorter, extra) =>{ + onChange={(pagination, filter, sorter, extra) => { setCsvData(extra.currentDataSource); }} /> diff --git a/client/src/views/Replay/Replay.less b/client/src/views/Replay/Replay.less index 83358a64..7848a11e 100644 --- a/client/src/views/Replay/Replay.less +++ b/client/src/views/Replay/Replay.less @@ -59,7 +59,9 @@ } #action-title{ - color: white; + color: rgb(19, 18, 18); + font-size: larger; + font-weight: 600; } #replay-log{ From 70a16b5eea659b9beda6ccb3f607efa265ce7fd5 Mon Sep 17 00:00:00 2001 From: chensation Date: Wed, 4 May 2022 23:31:08 -0400 Subject: [PATCH 19/19] add csv download to day level report --- client/src/views/Researcher/DayLevelReport.js | 60 ++++++++++++++++--- .../src/views/Researcher/DayLevelReport.less | 7 ++- 2 files changed, 56 insertions(+), 11 deletions(-) diff --git a/client/src/views/Researcher/DayLevelReport.js b/client/src/views/Researcher/DayLevelReport.js index 7d3339a4..d016244d 100644 --- a/client/src/views/Researcher/DayLevelReport.js +++ b/client/src/views/Researcher/DayLevelReport.js @@ -4,6 +4,8 @@ import { Table, Button, Tag } from 'antd'; import './DayLevelReport.less'; import { useSearchParam } from '../../Utils/useSearchParam'; import NavBar from '../../components/NavBar/NavBar'; +import { CSVDownloader } from 'react-papaparse'; + import { getSessionsWithFilter, @@ -17,6 +19,7 @@ import Form from 'antd/lib/form/Form'; const DayLevelReport = () => { const [sessions, setSessions] = useState([]); + const [csvData, setCsvData] = useState([]); const [sessionCount, setSessionCount] = useState(0); const navigate = useNavigate(); const { paramObj, setSearchParam } = useSearchParam(); @@ -56,6 +59,20 @@ const DayLevelReport = () => { setSessions(sessionRes.data); setSessionCount(sessionCountRes.data); + let csvData = sessionRes.data; + + if(tbPrevFilter != null){ + // console.log(Object.entries(tbPrevFilter)); + Object.entries(tbPrevFilter).forEach((property)=> { + if(property[1] != null){ + let set = new Set(property[1]); + csvData = csvData.filter(item => set.has(item[property[0]]?.name)) + } + }) + } + console.log(csvData); + setCsvData(csvData); + // set table head filter data makeTbNameFilter(sessionRes.data); setTbClassroomFilter(makeFilter(sessionRes.data, 'classroom')); @@ -64,7 +81,7 @@ const DayLevelReport = () => { setTbLessonFilter(makeFilter(sessionRes.data, 'learning_standard')); }; if (paramObj['_sort']) fetchData(); - }, [paramObj]); + }, [paramObj, tbPrevFilter]); const makeTbNameFilter = (data) => { let filter = []; @@ -150,7 +167,7 @@ const DayLevelReport = () => { { title: 'Lesson', dataIndex: ['learning_standard', 'name'], - key: 'unit', + key: 'learning_standard', width: '3%', align: 'left', filters: tbLessonFilter, @@ -215,13 +232,36 @@ const DayLevelReport = () => {
Day Level - Student Report
+
- + { + return csvData.map((session) => { + return { + 'Student': session.students[0].name, + 'Classroom': session.classroom?.name, + 'Grade': session.grade?.name, + 'Unit': session.unit?.name, + 'Lesson': session.learning_standard?.name, + 'Session Started': formatMyDate(session.created_at), + 'Partners': session.students.slice(1).map((student) => student.name).join(', ') + }; + }); + }} + > + Download to CSV + + +