Skip to content

Commit

Permalink
Teacher tool: Initial validation logic (#9823)
Browse files Browse the repository at this point in the history
* set up evaluation results

* whitespace fixes

Co-authored-by: Eric Anderson <[email protected]>

* whitespace fixes

Co-authored-by: Thomas Sparks <[email protected]>

* throw error for non-blocks project instead of converting to blocks

---------

Co-authored-by: Eric Anderson <[email protected]>
Co-authored-by: Thomas Sparks <[email protected]>
  • Loading branch information
3 people authored Jan 12, 2024
1 parent 647af57 commit fe3f3b4
Show file tree
Hide file tree
Showing 8 changed files with 100 additions and 9 deletions.
2 changes: 1 addition & 1 deletion pxtblocks/code-validation/evaluationResult.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
namespace pxt.blocks {
export interface EvaluationResult {
passed: boolean;
blockIdResults: pxt.Map<boolean>;
}
}
49 changes: 49 additions & 0 deletions pxtblocks/code-validation/rubricCriteria.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,55 @@ namespace pxt.blocks {
}
}

function blockSetToRequiredBlockCounts(blockSet: BlockSet): pxt.Map<number> {
const requiredBlockCounts: pxt.Map<number> = {};
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<boolean> = {};
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<boolean> {
const requiredBlockCounts = blockSetToRequiredBlockCounts(blockSet);
const blockResults: pxt.Map<boolean> = {};
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;
Expand Down
1 change: 1 addition & 0 deletions pxteditor/editor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -335,6 +335,7 @@ namespace pxt.editor {
blocksScreenshotAsync(pixelDensity?: number, encodeBlocks?: boolean): Promise<string>;
renderBlocksAsync(req: EditorMessageRenderBlocksRequest): Promise<EditorMessageRenderBlocksResponse>;
renderPythonAsync(req: EditorMessageRenderPythonRequest): Promise<EditorMessageRenderPythonResponse>;
getBlocks(): Blockly.Block[];

toggleHighContrast(): void;
setHighContrast(on: boolean): void;
Expand Down
6 changes: 3 additions & 3 deletions pxteditor/editorcontroller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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(() => {
const blocks = projectView.getBlocks();
return pxt.blocks.validateProject(blocks, rubric)})
.then (results => {
resp = results;
});
Expand Down
8 changes: 5 additions & 3 deletions teachertool/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,11 @@ function App() {
return (
<div className="app-container">
<HeaderBar />
<DebugInput />
<EvalResultDisplay />
<MakeCodeFrame />
<div className="inner-app-container">
<DebugInput />
<EvalResultDisplay />
<MakeCodeFrame />
</div>
<Notifications />
</div>
);
Expand Down
11 changes: 9 additions & 2 deletions teachertool/src/components/EvalResultDisplay.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,15 @@ const EvalResultDisplay: React.FC<IProps> = ({}) => {
<div className="eval-results-container">
<h3>{lf("Project: {0}", teacherTool.projectMetadata.name)}</h3>
{teacherTool.currentEvalResult === undefined && <div className="common-spinner" />}
{teacherTool.currentEvalResult?.passed === true && <h4 className="positive-text">Passed!</h4>}
{teacherTool.currentEvalResult?.passed === false && <h4 className="negative-text">Failed!</h4>}
{Object.keys(teacherTool.currentEvalResult?.blockIdResults ?? {}).map((id) => {
const result = teacherTool.currentEvalResult?.blockIdResults[id];
return (
<div className="result-block-id" key={id}>
<p className="block-id-label">{id}:</p>
<p className={result ? "positive-text" : "negative-text"}>{result ? "passed" : "failed"}</p>
</div>
);
})}
</div>
)}
</>
Expand Down
23 changes: 23 additions & 0 deletions teachertool/src/teacherTool.css
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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 {
Expand Down
9 changes: 9 additions & 0 deletions webapp/src/app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4038,6 +4038,15 @@ export class ProjectView
});
}

getBlocks(): Blockly.Block[] {
if (!this.isBlocksActive()) {
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);
}

launchFullEditor() {
Util.assert(pxt.shell.isSandboxMode());

Expand Down

0 comments on commit fe3f3b4

Please sign in to comment.