Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

#27/automate toolbox creation #78

Open
wants to merge 4 commits into
base: react
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
node_modules
package-lock.json
.DS_Store
client/src/Exercises
src/Exercises
5 changes: 5 additions & 0 deletions client/exercises/01-stuff.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
## Some stuff

This lesson is about some stuff™️.

I'm writing some <!--block-->things<!--/block--> <!--block-->here<!--/block--> <!--block-->things<!--/block-->, blah, blah, blah.
5 changes: 5 additions & 0 deletions client/exercises/02-more-stuff.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
## Some more stuff

This lesson is about some more stuff™️.

I'm writing some more things here, <!--block-->blah<!--/block-->, blah, blah.
7 changes: 7 additions & 0 deletions client/exercises/03-test-content.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
## Test lesson

This lesson is about some more stuff™️.

I'm writing some more things here, <!--block-->with element by ID<!--/block-->, blah, blah.

Something something <!--block-->set content<!--/block--> something else
7 changes: 4 additions & 3 deletions client/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,10 @@
"web-vitals": "^2.1.4"
},
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test",
"gen-exercises": "node ./scripts/generate-exercises.js ",
"start": "npm run gen-exercises && react-scripts start",
"build": "npm run gen-exercises && react-scripts build",
"test": "npm run gen-exercises && react-scripts test",
"eject": "react-scripts eject"
},
"eslintConfig": {
Expand Down
23 changes: 23 additions & 0 deletions client/scripts/blocks-to-categories.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
{
"things": {
"categoryType": "Values",
"blockType": "get_randomWord"
},
"here": {
"categoryType": "HTML",
"blockType": "on_start"
},
"blah": {
"categoryType": "Values",
"blockType": "text"
},
"with element by ID": {
"categoryType": "HTML",
"blockType": "with_element_by_id"
},
"set content": {
"categoryType": "HTML",
"blockType": "set_content",
"blockxml": "<block type='set_content'><value name='VALUE'><shadow type='text'> </shadow></value></block>"
}
}
122 changes: 122 additions & 0 deletions client/scripts/generate-exercises.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
const fs = require('fs')
const path = require('path');

const btcContents = fs.readFileSync(path.join(__dirname, 'blocks-to-categories.json'))
const btcJson = JSON.parse(btcContents)

function parseToolbox(text) {
const blockPattern = /<!--block-->(\w+)<!--\/block-->/g

let blocks = [...text.matchAll(blockPattern)]
.map((block) => block[1])

blocks = [...new Set(blocks)]

let categories = {}
let toolboxContents = []

for (let blockName of blocks) {
if (!(blockName in btcJson)) {
console.warn(`${blockName} not defined in blocks-to-categories.json`)
continue
}

const blockDetails = btcJson[blockName]
const categoryName = blockDetails.categoryType

const blockEntry = {
kind: 'block',
type: blockDetails.blockType
}

if ('blockxml' in blockDetails) { blockEntry.blockxml = blockDetails.blockxml }

if (categoryName in categories) {
categories[categoryName].push(blockEntry)
} else {
categories[categoryName] = [blockEntry]
}
}

for (let category in categories) {
toolboxContents.push({
kind: "category",
name: category,
contents: categories[category]
})
}

return JSON.stringify({
kind: "categoryToolbox",
contents: toolboxContents
}, null, 2)
}

function writeExercisesIndexJs(exerciseInputDir) {
let exercisesFileNames = fs.readdirSync(exerciseInputDir).filter((filename) => filename.endsWith('.md'))

exercisesFileNames.forEach((filename) => {
const filePath = path.join(exerciseInputDir, filename)
const fileText = fs.readFileSync(filePath, 'utf8')
const filenameNoExt = filename.slice(0, -3)

const exerciseOutputDir = path.join(__dirname, '..', 'src', 'Exercises', filenameNoExt)
const exerciseMd = path.join(exerciseOutputDir, 'lesson.md')
const exerciseIndexJs = path.join(exerciseOutputDir, 'index.js')

fs.mkdirSync(exerciseOutputDir, { recursive: true })
fs.copyFileSync(filePath, exerciseMd)

const exerciseIndexJsContent = `
import LessonMarkdown from "../../LessonMarkdown";
import markdownUrl from "./lesson.md";

export function Lesson() {
return <LessonMarkdown url={markdownUrl} />;
}

export const toolbox = ${parseToolbox(fileText)}`

fs.writeFileSync(exerciseIndexJs, exerciseIndexJsContent)
})
}

function writeRootExercisesIndexJs(exerciseInputDir) {
let exercisesFileNames = fs.readdirSync(exerciseInputDir).filter((filename) => filename.endsWith('.md'))
const exerciseAliases = []

let rootIndexJsContent = ""
const rootIndexJs = path.join(__dirname, '..', 'src', 'Exercises', 'index.js')

exercisesFileNames.forEach((filename) => {
let exerciseNoExt = filename.slice(0,-3)

// Starting with _ so that the alias is ES6 compatible, in case file name starts with number
let exerciseAlias = `_${exerciseNoExt.replaceAll('-', '_')}`

exerciseAliases.push(exerciseAlias)

rootIndexJsContent += `import * as ${exerciseAlias} from "./${exerciseNoExt}"\n`
})

rootIndexJsContent += `\n\nexport default ${JSON
.stringify(exerciseAliases, null, 2)
.replaceAll('"', '')}`

fs.writeFileSync(rootIndexJs, rootIndexJsContent)
}

function generateExercises() {
const rootDir = path.join(__dirname, '..')
const relExerciseInputDir = './exercises'

const absExerciseInputDir = path.join(rootDir, './exercises')

// Create exercises folders and index
writeExercisesIndexJs(absExerciseInputDir)

// Create root exercises index
writeRootExercisesIndexJs(absExerciseInputDir)
}

generateExercises()
7 changes: 1 addition & 6 deletions client/src/App.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,25 +7,20 @@ import "./Blocks/dom";
import "./Blocks/cyf";
import useBlockly from "./Blockly/useBlockly";

import * as Exercise1 from "./Exercises/01-stuff";
import * as Exercise2 from "./Exercises/02-more-stuff";
import exercises from "./Exercises"

import Split from "react-split-grid";
import TextPanel from "./TextPanel/TextPanel";
import Output from "./Output/Output";
import Header from "./Layout/Header/Header";
import Menu from "./Layout/Menu/Menu";
import Footer from "./Layout/Footer/Footer";
import Button from "./Button/Button";
import "./App.scss";

import { ReactComponent as Background } from "../src/svgs/Humaaans-Phone.svg";

Blockly.setLocale(locale);

// just left all this and presumed you will pass whatever you decide to do into the text panel
const exercises = [Exercise1, Exercise2];

function useExercise() {
const [exerciseIndex, setExerciseIndex] = useState(0);

Expand Down
46 changes: 0 additions & 46 deletions client/src/Exercises/01-stuff/index.js

This file was deleted.

5 changes: 0 additions & 5 deletions client/src/Exercises/01-stuff/lesson.md

This file was deleted.

58 changes: 0 additions & 58 deletions client/src/Exercises/02-more-stuff/index.js

This file was deleted.

5 changes: 0 additions & 5 deletions client/src/Exercises/02-more-stuff/lesson.md

This file was deleted.