Skip to content

Commit

Permalink
init tutorial tool
Browse files Browse the repository at this point in the history
  • Loading branch information
riknoll committed Mar 26, 2024
1 parent 31f091f commit 62f6172
Show file tree
Hide file tree
Showing 43 changed files with 32,978 additions and 4 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ webapp/public/authcode.html
webapp/public/multiplayer.html
webapp/public/kiosk.html
webapp/public/teachertool.html
webapp/public/tutorialtool.html
localtypings/blockly.d.ts
node_modules
*.sw?
Expand Down
6 changes: 6 additions & 0 deletions cli/webapps-config.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,12 @@
"localServeWebConfigUrl": false,
"localServeEndpoint": "eval"
},
{
"name": "tutorialtool",
"buildCss": false,
"localServeWebConfigUrl": false,
"localServeEndpoint": "tt"
},
{
"name": "skillmap",
"buildCss": true,
Expand Down
14 changes: 11 additions & 3 deletions gulpfile.js
Original file line number Diff line number Diff line change
Expand Up @@ -587,6 +587,12 @@ const kiosk = createWebappTasks("kiosk");

const teacherTool = createWebappTasks("teachertool");

/********************************************************
Tutorial Tool
*********************************************************/

const tutorialTool = createWebappTasks("tutorialtool");

/********************************************************
Webapp build wrappers
*********************************************************/
Expand All @@ -601,7 +607,7 @@ const maybeUpdateWebappStrings = () => {

const maybeBuildWebapps = () => {
if (!shouldBuildWebapps()) return noop;
return gulp.parallel(skillmap, authcode, multiplayer, kiosk, teacherTool);
return gulp.parallel(skillmap, authcode, multiplayer, kiosk, teacherTool, tutorialTool);
}

/********************************************************
Expand All @@ -610,8 +616,10 @@ const maybeBuildWebapps = () => {

const lintWithEslint = () => Promise.all(
["cli", "pxtblocks", "pxteditor", "pxtlib", "pxtcompiler",
"pxtpy", "pxtrunner", "pxtsim", "webapp",
"docfiles/pxtweb", "skillmap", "authcode", "multiplayer"/*, "kiosk"*/, "teachertool", "docs/static/streamer"].map(dirname =>
"pxtpy", "pxtrunner", "pxtsim", "webapp", "pxtservices",
"docfiles/pxtweb", "skillmap", "authcode",
"multiplayer"/*, "kiosk"*/, "teachertool",
"tutorialTool", "docs/static/streamer"].map(dirname =>
exec(`node node_modules/eslint/bin/eslint.js -c .eslintrc.js --ext .ts,.tsx ./${dirname}/`, true)))
.then(() => console.log("linted"))
const lint = lintWithEslint
Expand Down
56 changes: 56 additions & 0 deletions pxtservices/backendRequests.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import { ErrorCode } from "./constants";
import { logError } from "./loggingService";

export async function fetchJsonDocAsync<T = any>(url: string): Promise<T | undefined> {
try {
const response = await fetch(url, {
cache: "no-cache",
});
if (!response.ok) {
throw new Error("Unable to fetch the json file");
} else {
const json = await response.json();
return json;
}
} catch (e) {
logError(ErrorCode.fetchJsonDocAsync, e);
}
}

export async function getProjectTextAsync(projectId: string): Promise<pxt.Cloud.JsonText | undefined> {
try {
const projectTextUrl = `${pxt.Cloud.apiRoot}/${projectId}/text`;
const response = await fetch(projectTextUrl);
if (!response.ok) {
throw new Error("Unable to fetch the project details");
} else {
const projectText = await response.json();
return projectText;
}
} catch (e) {
logError(ErrorCode.getProjectTextAsync, e);
}
}

export async function getProjectMetaAsync(projectId: string): Promise<pxt.Cloud.JsonScript | undefined> {
try {
const projectMetaUrl = `${pxt.Cloud.apiRoot}/${projectId}`;
const response = await fetch(projectMetaUrl);
if (!response.ok) {
throw new Error("Unable to fetch the project meta information");
} else {
const projectMeta = await response.json();
return projectMeta;
}
} catch (e) {
logError(ErrorCode.getProjectMetaAsync, e);
}
}

export async function downloadTargetConfigAsync(): Promise<pxt.TargetConfig | undefined> {
try {
return await pxt.targetConfigAsync();
} catch (e) {
logError(ErrorCode.downloadTargetConfigAsync, e);
}
}
6 changes: 6 additions & 0 deletions pxtservices/constants.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
export enum ErrorCode {
downloadTargetConfigAsync = "downloadTargetConfigAsync",
fetchJsonDocAsync = "fetchJsonDocAsync",
getProjectTextAsync = "getProjectTextAsync",
getProjectMetaAsync = "getProjectMetaAsync",
}
1 change: 0 additions & 1 deletion pxtservices/iframeDriver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -402,7 +402,6 @@ export class IframeDriver {
addEventListener(event: "event", handler: (ev: pxt.editor.EditorMessageEventRequest) => void): void;
addEventListener(event: "simevent", handler: (ev: pxt.editor.EditorSimulatorEvent) => void): void;
addEventListener(event: "tutorialevent", handler: (ev: pxt.editor.EditorMessageTutorialEventRequest) => void): void;
addEventListener(event: "editorcontentloaded", handler: (ev: pxt.editor.EditorContentLoadedRequest) => void): void;
addEventListener(event: "workspacesave", handler: (ev: pxt.editor.EditorWorkspaceSaveRequest) => void): void;
addEventListener(event: "workspaceevent", handler: (ev: pxt.editor.EditorWorkspaceEvent) => void): void;
addEventListener(event: "workspacereset", handler: (ev: pxt.editor.EditorWorkspaceSyncRequest) => void): void;
Expand Down
49 changes: 49 additions & 0 deletions pxtservices/loggingService.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
let tickEvent = "pxt.error";

const timestamp = () => {
const time = new Date();
const hours = padTime(time.getHours());
const minutes = padTime(time.getMinutes());
const seconds = padTime(time.getSeconds());

return `[${hours}:${minutes}:${seconds}]`;
};

const padTime = (time: number) => ("0" + time).slice(-2);

export const logError = (errorCode: string, message?: any, data: pxt.Map<string | number> = {}) => {
let dataObj = { ...data };
if (message) {
if (typeof message === "object") {
dataObj = { ...dataObj, ...message };
// Look for non-enumerable properties found on Error objects
["message", "stack", "name"].forEach(key => {
if (message[key]) {
dataObj[key] = message[key];
}
});
} else {
dataObj.message = message;
}
}
pxt.tickEvent(tickEvent, {
...dataObj,
errorCode,
});
console.error(timestamp(), errorCode, dataObj);
};

export const logInfo = (message: any) => {
console.log(timestamp(), message);
};

export const logDebug = (message: any) => {
if (pxt.BrowserUtils.isLocalHost() || pxt.options.debug) {
console.log(timestamp(), message);
}
};


export const setTickEvent = (event: string) => {
tickEvent = event;
}
3 changes: 3 additions & 0 deletions tutorialtool/.env
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
DISABLE_ESLINT_PLUGIN=true
GENERATE_SOURCEMAP=false
BROWSER=none
6 changes: 6 additions & 0 deletions tutorialtool/.eslintrc.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
module.exports = {
"parserOptions": {
"project": "tutorialtool/tsconfig.json",
},
"ignorePatterns": ["tests/**/*.spec.ts", "public/**/*", "build/**/*"]
}
3 changes: 3 additions & 0 deletions tutorialtool/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
.vscode
build
!package-lock.json
6 changes: 6 additions & 0 deletions tutorialtool/.prettierrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"arrowParens": "avoid",
"semi": true,
"tabWidth": 4,
"printWidth":120
}
30 changes: 30 additions & 0 deletions tutorialtool/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# Teacher Tool

## Localhost testing

To test the Teacher Tool locally:

1. Ensure your pxt repo is up to date and has been built recently.
2. In a command shell, in the `pxt` repo, cd into the `tutorialtool` folder and start the Teacher Tool dev server: `npm run start`. This will *not* open a browser window.
3. In another command shell, in the target repo (e.g. `pxt-arcade` or `pxt-microbit`), start the pxt dev server: `pxt serve --rebundle --noauth`. This will open the editor webapp in a browser.
1. **Note the `--noauth` parameter.** It is important to include this option when running on localhost in order to download certain required startup files from the localhost pxt server.

Requests to the `/eval` endpoint will be routed to the Teacher Tool dev server.

Debug and step through Teacher Tool code using the browser dev tools (F12 to open).


## Test in staging environment

1. In the pxt repo, run `gulp` to ensure production teacher tool is built.
2. In a browser, go to `https://staging.pxt.io/oauth/gettoken`. This should return a url with an auth token embedded. Copy the entire url value to your clipboard.
- It should look something like `https://staging.pxt.io/?access_token=X.XXXXXXXX`
- If you get access denied, contact your manager to help you.
3. In a command shell, set environment variable `PXT_ACCESS_TOKEN` with the copied value.
4. In the same shell, in the pxt-arcade repo, run `pxt uploadtrg --rebundle`. This should return a url to your private build.
- It should look something like `https://arcade.staging.pxt.io/app/XXXXXX-XXXXX`
- Paste in a browser and append "/tutorialtool". This should take you to your teacher tool build in staging.

## Test in production environment

Follow the "Test in staging environment" instructions, but get your auth token from `https://makecode.com/oauth/gettoken`.
41 changes: 41 additions & 0 deletions tutorialtool/config-overrides.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
const { aliasWebpack } = require("react-app-alias-ex");
const NodePolyfillPlugin = require("node-polyfill-webpack-plugin");
const HtmlWebpackPlugin = require("html-webpack-plugin");

module.exports = function (config, env) {
const isEnvProduction = env === "production";
const aliasFn = aliasWebpack({});
config = {
...aliasFn(config),
plugins: [
...config.plugins.filter((p) => !(p instanceof HtmlWebpackPlugin)),
new NodePolyfillPlugin(),
new HtmlWebpackPlugin(
Object.assign(
{},
{
inject: true,
template: "./public/index.html",
},
isEnvProduction
? {
minify: {
removeComments: false,
collapseWhitespace: false,
removeRedundantAttributes: true,
useShortDoctype: true,
removeEmptyAttributes: true,
removeStyleLinkTypeAttributes: true,
keepClosingSlash: true,
minifyJS: true,
minifyCSS: true,
minifyURLs: true,
},
}
: undefined
)
),
],
};
return config;
};
Loading

0 comments on commit 62f6172

Please sign in to comment.