From 44e1fc0556fd7f2a9dc7b09f3570df73bb4b9e71 Mon Sep 17 00:00:00 2001 From: Kim Ying <15070078+kimprice@users.noreply.github.com> Date: Thu, 11 Jan 2024 13:37:02 -0800 Subject: [PATCH 1/4] set up evaluation results --- pxtblocks/code-validation/evaluationResult.ts | 2 +- pxtblocks/code-validation/rubricCriteria.ts | 49 +++++++++++++++++++ pxteditor/editor.ts | 1 + pxteditor/editorcontroller.ts | 6 +-- teachertool/src/App.tsx | 8 +-- .../src/components/EvalResultDisplay.tsx | 11 ++++- teachertool/src/teacherTool.css | 23 +++++++++ webapp/src/app.tsx | 8 +++ 8 files changed, 99 insertions(+), 9 deletions(-) diff --git a/pxtblocks/code-validation/evaluationResult.ts b/pxtblocks/code-validation/evaluationResult.ts index 982d4bc62ab8..00e438ed6e53 100644 --- a/pxtblocks/code-validation/evaluationResult.ts +++ b/pxtblocks/code-validation/evaluationResult.ts @@ -1,5 +1,5 @@ namespace pxt.blocks { export interface EvaluationResult { - passed: boolean; + blockIdResults: pxt.Map; } } diff --git a/pxtblocks/code-validation/rubricCriteria.ts b/pxtblocks/code-validation/rubricCriteria.ts index 7459794b7017..7bbe02550ba9 100644 --- a/pxtblocks/code-validation/rubricCriteria.ts +++ b/pxtblocks/code-validation/rubricCriteria.ts @@ -23,6 +23,55 @@ namespace pxt.blocks { } } + function blockSetToRequiredBlockCounts(blockSet: BlockSet): pxt.Map { + const requiredBlockCounts: pxt.Map = {}; + blockSet.blocks.forEach((block) => { + requiredBlockCounts[block] = blockSet.count; + }); + return requiredBlockCounts; + } + + export function validateProject(usedBlocks: Blockly.Block[], rubric: string): EvaluationResult { + const rubricData = parseRubric(rubric); + const finalResult: pxt.Map = {}; + rubricData.criteria.forEach((criteria: RubricCriteria) => { + (criteria as BlockCheckCriteria).blockRequirements.forEach((blockSet) => { + const result = validateBlockSet(usedBlocks, blockSet); + Object.keys(result).forEach((blockId) => { + finalResult[blockId] = result[blockId]; + }); + }); + }); + return { blockIdResults: finalResult} as EvaluationResult; + } + + + function validateBlockSet(usedBlocks: Blockly.Block[], blockSet: BlockSet): pxt.Map { + const requiredBlockCounts = blockSetToRequiredBlockCounts(blockSet); + const blockResults: pxt.Map = {}; + Object.keys(requiredBlockCounts).forEach((blockId) => { + blockResults[blockId] = true; + }); + const { + missingBlocks, + disabledBlocks, + insufficientBlocks + } = pxt.blocks.validateBlocksExist({ + usedBlocks: usedBlocks, + requiredBlockCounts: requiredBlockCounts, + }); + missingBlocks.forEach((blockId) => { + blockResults[blockId] = false; + }); + disabledBlocks.forEach((blockId) => { + blockResults[blockId] = false; + }); + insufficientBlocks.forEach((blockId) => { + blockResults[blockId] = false; + }); + return blockResults; + } + export abstract class RubricCriteria { displayText: string; abstract criteriaId: string; diff --git a/pxteditor/editor.ts b/pxteditor/editor.ts index 11c5b373d426..c0811be52f29 100644 --- a/pxteditor/editor.ts +++ b/pxteditor/editor.ts @@ -335,6 +335,7 @@ namespace pxt.editor { blocksScreenshotAsync(pixelDensity?: number, encodeBlocks?: boolean): Promise; renderBlocksAsync(req: EditorMessageRenderBlocksRequest): Promise; renderPythonAsync(req: EditorMessageRenderPythonRequest): Promise; + getBlocksAsync(): Promise; toggleHighContrast(): void; setHighContrast(on: boolean): void; diff --git a/pxteditor/editorcontroller.ts b/pxteditor/editorcontroller.ts index 55e64e32576e..2495790897c9 100644 --- a/pxteditor/editorcontroller.ts +++ b/pxteditor/editorcontroller.ts @@ -548,9 +548,9 @@ namespace pxt.editor { const evalmsg = data as EditorMessageRunEvalRequest; const rubric = evalmsg.rubric; return Promise.resolve() - .then(() => ( - // TODO : call into evaluation function. - { passed: true } as pxt.blocks.EvaluationResult)) + .then(() => projectView.getBlocksAsync()) + .then((blocks) => ( + pxt.blocks.validateProject(blocks, rubric))) .then (results => { resp = results; }); diff --git a/teachertool/src/App.tsx b/teachertool/src/App.tsx index 8903bfd32626..2635ba2cc701 100644 --- a/teachertool/src/App.tsx +++ b/teachertool/src/App.tsx @@ -35,9 +35,11 @@ function App() { return (
- - - +
+ + + +
); diff --git a/teachertool/src/components/EvalResultDisplay.tsx b/teachertool/src/components/EvalResultDisplay.tsx index 6e7ac7162c6e..08e76ecc1d55 100644 --- a/teachertool/src/components/EvalResultDisplay.tsx +++ b/teachertool/src/components/EvalResultDisplay.tsx @@ -14,8 +14,15 @@ const EvalResultDisplay: React.FC = ({}) => {

{lf("Project: {0}", teacherTool.projectMetadata.name)}

{teacherTool.currentEvalResult === undefined &&
} - {teacherTool.currentEvalResult?.passed === true &&

Passed!

} - {teacherTool.currentEvalResult?.passed === false &&

Failed!

} + {Object.keys(teacherTool.currentEvalResult?.blockIdResults??{}).map((id) => { + const result = teacherTool.currentEvalResult?.blockIdResults[id]; + return ( +
+

{id}:

+

{result?"passed":"failed"}

+
+ ); + })}
)} diff --git a/teachertool/src/teacherTool.css b/teachertool/src/teacherTool.css index 7d4e6fff27c1..e6adffeec87f 100644 --- a/teachertool/src/teacherTool.css +++ b/teachertool/src/teacherTool.css @@ -76,6 +76,13 @@ code { overflow: auto; } +.inner-app-container { + display: flex; + flex-direction: column; + min-height: 100%; + width: 100%; +} + .noclick { pointer-events: none; cursor: default; @@ -397,6 +404,22 @@ code { border-bottom: 1px solid #ddd; margin: 1rem 0; padding: 1rem; + overflow: auto; +} + +.result-block-id { + display: flex; + padding-left: .5rem; + margin: .2rem 0; +} + +.result-block-id p { + margin: 0; +} + +.block-id-label { + font-weight: 700; + padding-right: .5rem; } .positive-text { diff --git a/webapp/src/app.tsx b/webapp/src/app.tsx index abbb5bc048d3..bd2594d2a4a4 100644 --- a/webapp/src/app.tsx +++ b/webapp/src/app.tsx @@ -4038,6 +4038,14 @@ export class ProjectView }); } + async getBlocksAsync(): Promise { + if (!this.isBlocksActive()) { + await this.openBlocksAsync(); + } + + return this.blocksEditor.editor.getAllBlocks(false); + } + launchFullEditor() { Util.assert(pxt.shell.isSandboxMode()); From 54aabaf666fa2f112b337b01e87c97e927ae25e8 Mon Sep 17 00:00:00 2001 From: Kim Ying <15070078+kimprice@users.noreply.github.com> Date: Thu, 11 Jan 2024 15:22:19 -0800 Subject: [PATCH 2/4] whitespace fixes Co-authored-by: Eric Anderson --- pxtblocks/code-validation/rubricCriteria.ts | 2 +- teachertool/src/components/EvalResultDisplay.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pxtblocks/code-validation/rubricCriteria.ts b/pxtblocks/code-validation/rubricCriteria.ts index 7bbe02550ba9..9e3b335d0e3d 100644 --- a/pxtblocks/code-validation/rubricCriteria.ts +++ b/pxtblocks/code-validation/rubricCriteria.ts @@ -42,7 +42,7 @@ namespace pxt.blocks { }); }); }); - return { blockIdResults: finalResult} as EvaluationResult; + return { blockIdResults: finalResult } as EvaluationResult; } diff --git a/teachertool/src/components/EvalResultDisplay.tsx b/teachertool/src/components/EvalResultDisplay.tsx index 08e76ecc1d55..c7a1d30ce8de 100644 --- a/teachertool/src/components/EvalResultDisplay.tsx +++ b/teachertool/src/components/EvalResultDisplay.tsx @@ -14,7 +14,7 @@ const EvalResultDisplay: React.FC = ({}) => {

{lf("Project: {0}", teacherTool.projectMetadata.name)}

{teacherTool.currentEvalResult === undefined &&
} - {Object.keys(teacherTool.currentEvalResult?.blockIdResults??{}).map((id) => { + {Object.keys(teacherTool.currentEvalResult?.blockIdResults ?? {}).map((id) => { const result = teacherTool.currentEvalResult?.blockIdResults[id]; return (
From 4a34ccefb30c0af0f84b09034ea6c7de89cf9d1d Mon Sep 17 00:00:00 2001 From: Kim Ying <15070078+kimprice@users.noreply.github.com> Date: Thu, 11 Jan 2024 15:38:47 -0800 Subject: [PATCH 3/4] whitespace fixes Co-authored-by: Thomas Sparks <69657545+thsparks@users.noreply.github.com> --- teachertool/src/components/EvalResultDisplay.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/teachertool/src/components/EvalResultDisplay.tsx b/teachertool/src/components/EvalResultDisplay.tsx index c7a1d30ce8de..fad4eef52d18 100644 --- a/teachertool/src/components/EvalResultDisplay.tsx +++ b/teachertool/src/components/EvalResultDisplay.tsx @@ -19,7 +19,7 @@ const EvalResultDisplay: React.FC = ({}) => { return (

{id}:

-

{result?"passed":"failed"}

+

{result ? "passed" : "failed"}

); })} From f6cf80d67e848d80815a64eb8921303110b9c8b3 Mon Sep 17 00:00:00 2001 From: Kim Ying <15070078+kimprice@users.noreply.github.com> Date: Thu, 11 Jan 2024 16:32:37 -0800 Subject: [PATCH 4/4] throw error for non-blocks project instead of converting to blocks --- pxteditor/editor.ts | 2 +- pxteditor/editorcontroller.ts | 6 +++--- webapp/src/app.tsx | 5 +++-- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/pxteditor/editor.ts b/pxteditor/editor.ts index c0811be52f29..07ca98a9c95d 100644 --- a/pxteditor/editor.ts +++ b/pxteditor/editor.ts @@ -335,7 +335,7 @@ namespace pxt.editor { blocksScreenshotAsync(pixelDensity?: number, encodeBlocks?: boolean): Promise; renderBlocksAsync(req: EditorMessageRenderBlocksRequest): Promise; renderPythonAsync(req: EditorMessageRenderPythonRequest): Promise; - getBlocksAsync(): Promise; + getBlocks(): Blockly.Block[]; toggleHighContrast(): void; setHighContrast(on: boolean): void; diff --git a/pxteditor/editorcontroller.ts b/pxteditor/editorcontroller.ts index 2495790897c9..98da38ca339e 100644 --- a/pxteditor/editorcontroller.ts +++ b/pxteditor/editorcontroller.ts @@ -548,9 +548,9 @@ namespace pxt.editor { const evalmsg = data as EditorMessageRunEvalRequest; const rubric = evalmsg.rubric; return Promise.resolve() - .then(() => projectView.getBlocksAsync()) - .then((blocks) => ( - pxt.blocks.validateProject(blocks, rubric))) + .then(() => { + const blocks = projectView.getBlocks(); + return pxt.blocks.validateProject(blocks, rubric)}) .then (results => { resp = results; }); diff --git a/webapp/src/app.tsx b/webapp/src/app.tsx index bd2594d2a4a4..94bf1cb9f79e 100644 --- a/webapp/src/app.tsx +++ b/webapp/src/app.tsx @@ -4038,9 +4038,10 @@ export class ProjectView }); } - async getBlocksAsync(): Promise { + getBlocks(): Blockly.Block[] { if (!this.isBlocksActive()) { - await this.openBlocksAsync(); + console.error("Trying to get blocks from a non-blocks editor."); + throw new Error("Trying to get blocks from a non-blocks editor."); } return this.blocksEditor.editor.getAllBlocks(false);