Enki Curriculum Compiler JSON
Compiles Enki Curriculum AST into a json object.
See Enki curriculum processors for more details.
How it works
The Insight/Exercise compiler works in the following way:
- Iterates over all
children
of the AST provided
- looks at the
type
value of each top-most node - if type is
"yaml"
=> fires metadata compiler - if type is
"headline"
=> fires headline compiler - if type is
"section"
, it looks at the name
field of the node
- if name is
"Content"
=> returns { content: compileString(node) }
- if name is
"Game Content"
=> returns { gameContent: compileString(node) }
- if name is
"Practice"
=> fires question compiler with "practice" argument - if name is
"Revision"
=> fires question compiler with "revision" argument - if name is
"Quiz"
=> fires quiz compiler - if name is
"Footnotes"
=> fires footnotes compiler - otherwise throws an error
- otherwise throws an error
- combines all JSON properties returned by each sub-compiler into a single object
- returns the final JSON object
The Question compiler does the following:
- Wrap incoming Question AST in a
"section"
node. - Return the output of the Question compiler
Compilers
Most of the compilers make use of the string compiler on top of which they do subsequent modifications.
Each of them will return a simple object { key: value }
Headline
compilers.headline(node)
Given an AST node as input:
{
"type": "headline",
"children": [
{
"type": "text",
"value": "Sample title",
}
}
}
It will compile the node, remove the \n
s, trim the string and return an object:
{
"headline": "Sample title"
}
Metadata
compilers.metadata(node)
Given an AST node as input:
{
"type": "yaml",
"value": "author: catalin\n\nlevels:\n - basic\n - medium\n -...",
"data": {
"parsedData": {
"author": "catalin",
...
}
}
It will return the already parsed data in node.data.parsedValue
:
{
"metadata": {
"author": "catalin",
...
}
}
Question
compilers.question(node, 'key')
This compiler is used for both "Practice" and "Revision" questions. Apart from the ast
, this compiler takes another argument key
:
question(ast, key) { }
Given a question AST node as input and a key
:
{
"type": "section",
"name": "Revision" || "Practice",
"children": [...]
}
It will return:
{
"key": {
"rawText": "question with two gaps ??? ???\n- answer1\n- answer2\n- answer3",
"question": "question with two gaps ??? ???",
"answers": [
{
"text": "answer1",
"correct": true,
"correctIndex": 0
},
{
"text": "answer2",
"correct": true,
"correctIndex": 1
},
{
"text": "answer3",
"correct": false,
"correctIndex": null
}
]
}
}
Each answer will have its text
, a correct
flag indicating if it's one of the correct answers of the question and a correctIndex
integer which indicates the index of the question gap it should match.
Quiz
compilers.quiz(node)
This compiler is similar to the question
compiler. In addition, it returns a headline
field specific to quizzes.
Because quiz question don't have question gaps ???
, the first answer will be marked as correct
(from within the parser).
Given an AST node:
{
"type": "section",
"name": "Quiz",
"children": [ ... ]
}
It returns the following object:
{
"quiz": {
"rawText": "### Quiz name\nQuiz text\n- answer1\n- answer2",
"headline": "Quiz name",
"question": "Quiz text",
"answers": [
{
"text": "answer1",
"correct": true,
"correctIndex": 0
},
{
"text": "answer2",
"correct": false,
"correctIndex": null
}
]
}
}
compilers.footnotes(node)
Given an AST node:
{
"type": "section",
"name": "Footnotes",
"children": [ ... ]
}
It returns the following object:
{
"footnotes": {
"rawText": "fullFootnotesText",
"items": [
{
"number": 1,
"name": "Footnote name",
"text": "Footnote text"
},
{
"number": 2,
"name": "Footnote name 2",
"text": "Footnote text 2"
}
]
}
}
Schema
The final schema of a compiled JSON looks like:
{
"metadata": {
"author": "catalin",
"levels": [
"basic",
"medium",
"advanced"
],
"type": "normal",
"category": "must-know",
"standards": {
"sql.use-dql.0": 10,
"sql.use-ddl.1": 1000
},
"tags": [
"introduction",
"workout"
],
"stub": false,
"links": [
{
"name": "EnkiCool",
"url": "https://enki.com",
"nature": "website"
}
]
},
"headline": "Sample title with `code` within",
"content": "This is a sample paragraph.[1]\n\nThis is a sample list[2]:\n\n- item one\n- item `two`\n\nSample code[3]:\n\n```javascript\nconsole.log('sample code')\n```\n",
"gameContent": "Sample game content\n",
"practice": {
"rawText": "This is a sample question with one gap:\n\n???\n\n- correct\n- incorrect\n- not a chance\n",
"question": "This is a sample question with one gap:\n\n???\n",
"answers": [
{
"text": "correct",
"correct": true,
"correctIndex": 0
},
{
"text": "incorrect",
"correct": true,
"correctIndex": 1
},
{
"text": "not a chance",
"correct": true,
"correctIndex": 2
}
]
},
"revision": {
"rawText": "This is a sample question with two gaps:\n\n???\n\n???\n\n- correct\n- also correct\n- nah bro\n- fam, just no\n",
"question": "This is a sample question with two gaps:\n\n???\n\n???\n",
"answers": [
{
"text": "correct",
"correct": true,
"correctIndex": 0
},
{
"text": "also correct",
"correct": false,
"correctIndex": null
},
{
"text": "nah bro",
"correct": false,
"correctIndex": null
},
{
"text": "fam, just no",
"correct": false,
"correctIndex": null
}
]
},
"quiz": {
"rawText": "### Quiz title\n\n\nSample quiz question\n\n???\n- correct\n- *incorrect*\n- not a `chance`\n- nope\n",
"headline": "Quiz title",
"question": "Sample quiz question\n\n???\n",
"answers": [
{
"text": "correct",
"correct": true,
"correctIndex": 0
},
{
"text": "*incorrect*",
"correct": false,
"correctIndex": null
},
{
"text": "not a `chance`",
"correct": false,
"correctIndex": null
},
{
"text": "nope",
"correct": false,
"correctIndex": null
}
]
},
"footnotes": {
"rawText": "[1: Paragraph]\nSample explanation\n\n[2: List]\nSample list\n\n[3: Code]\nSample code\n\n```javascript\nvar x = 10\n```\n",
"items": [
{
"number": "1",
"name": " Paragraph",
"text": "\nSample explanation\n\n"
},
{
"number": "2",
"name": " List",
"text": "\nSample list\n\n"
},
{
"number": "3",
"name": " Code",
"text": "\nSample code\n\n```javascript\nvar x = 10\n```\n"
}
]
}
}
API
Use the package like:
const {
contentTypes
} = require('@enkidevs/curriculum-helpers')
const {
getCompiler
} = require('@enkidevs/curriculum-compiler-json')
const json = getCompiler(contentTypes.INSIGHT).compileSync(ast)