Skip to content
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
31 changes: 21 additions & 10 deletions docs/chefops.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,31 +9,42 @@ Ricecooker CLI
--------------
This listing shows the `ricecooker` command line interface (CLI) arguments:

usage: sushichef.py [-h] [--token TOKEN] [-u] [-v] [--quiet] [--warn]
[--debug] [--compress] [--thumbnails]
[--resume] [--step {CONSTRUCT_CHANNEL, CREATE_TREE,
DOWNLOAD_FILES, GET_FILE_DIFF,
START_UPLOAD, UPLOAD_CHANNEL}]
[--deploy] [--publish]
usage: sushichef.py [-h] [--token TOKEN] [-u] [--debug] [-v] [--warn]
[--quiet] [--compress] [--thumbnails]
[--download-attempts DOWNLOAD_ATTEMPTS] [--resume]
[--step {INIT, CONSTRUCT_CHANNEL, CREATE_TREE,
DOWNLOAD_FILES, GET_FILE_DIFF, START_UPLOAD,
UPLOADING_FILES, UPLOAD_CHANNEL, PUBLISH_CHANNEL,
DONE, LAST}]
[--prompt] [--deploy] [--publish] [--sample SIZE]

required arguments:
--token TOKEN Studio API Access Token (specify wither the token
value or the path of a file that contains the token).

optional arguments:
-h, --help show this help message and exit
-u, --update Force file re-download (skip .ricecookerfilecache/).
--debug Print extra debugging infomation.
-v, --verbose Verbose mode (default).
--warn Print errors and warnings.
--quiet Print only errors.
--compress Compress videos using ffmpeg -crf=32 -b:a 32k mono.
--thumbnails Automatically generate thumbnails for content nodes.
--download-attempts N Maximum number of times to retry downloading files (default: 3).
--resume Resume chef session from a specified step.
--step {INIT, ... Step to resume progress from (must be used with --resume flag)
--update Force re-download of files (skip .ricecookerfilecache/ check)
--sample SIZE Upload a sample of SIZE nodes from the channel.
--step {INIT, CONSTRUCT_CHANNEL, CREATE_TREE, DOWNLOAD_FILES,
GET_FILE_DIFF, START_UPLOAD, UPLOADING_FILES, UPLOAD_CHANNEL,
PUBLISH_CHANNEL, DONE, LAST}
Step to resume progress from (use with --resume).
--prompt Prompt user to open the channel after the chef run.
--deploy Immediately deploy changes to channel's main tree.
This operation will overwrite the previous channel
content. Use only during development.
content. Use only during development. Staging is
the default, so omit this flag to get a safe,
reviewable draft upload instead.
--publish Publish newly uploaded version of the channel.
--sample SIZE Upload a sample of SIZE nodes from the channel.

As you can tell, there are lot of arguments to choose from, and this is not even
the complete list: you'll have to run `./sushichef.py -h` to see the latest version.
Expand Down
2 changes: 1 addition & 1 deletion docs/concepts/developer_workflows.md
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ Use the following rubric as a checklist to know when a sushi chef script is done
### Metadata checks
1. Do all nodes have appropriate titles?
2. Do all nodes have appropriate descriptions (when available in the source)?
3. Is the correct [language code](https://github.com/learningequality/le-utils/blob/master/le_utils/resources/languagelookup.json)
3. Is the correct [language code](https://github.com/learningequality/le-utils/blob/main/le_utils/resources/languagelookup.json)
set on all nodes and files?
4. Is the `license` field set to the correct value for all nodes?
5. Is the `source_id` field set consistently for all content nodes?
Expand Down
5 changes: 5 additions & 0 deletions docs/concepts/introduction.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,11 @@ Kolibri channels are tree-like structures that consist of the following types of
- Slideshow (a sequence of `jpg` and `png` slide images)
- Exercises containing questions like multiple choice, multiple selection, and numeric inputs

Chef scripts create each content item by passing a `uri` (a local path or URL) to
`ContentNode` — the file pipeline downloads the resource and infers which of the
kinds above it is from the file extension, so you rarely need to specify the kind
explicitly.


Further reading
---------------
Expand Down
2 changes: 1 addition & 1 deletion docs/csv_metadata/csv_exercises.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ Individual questions
- `Source ID *`: This field is the link (foreign key) to the an exercise node, e.g. `exrc1`
- `Question ID *`: A unique identifier for this question within the exercise, e.g. q1
- `Question type *`: (str) Question types are defined in
[le-utils](https://github.com/learningequality/le-utils/blob/master/le_utils/constants/exercises.py#L34).
[le-utils](https://github.com/learningequality/le-utils/blob/main/le_utils/constants/exercises.py#L34).
The currently supported question types for the CSV workflow are:
- `input_question`: Numeric input question, e.g. What is 2+2?
- `single_selection`: Multiple choice questions where a single correct answer.
Expand Down
2 changes: 1 addition & 1 deletion docs/downloader.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ pages that correctly include all necessary resources without extra code.
* If you need to make modifications, call `create_zip_dir_for_page`, then modify
the files in the directory it returns as needed. (Not modifying the original
sources allows you to keep a clean copy at all times.) Finally, create a ZIP by
calling `ricecooker.utils.create_predictable_zip` and use the zip created
calling `ricecooker.utils.zip.create_predictable_zip` and use the zip created
as a file for an HTML5 app node.

Usage example:
Expand Down
4 changes: 2 additions & 2 deletions docs/examples/document_conversion.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@
"cell_type": "markdown",
"metadata": {},
"source": [
"You should now be able to see the file `document.pdf` in the current directory, and add this PDF file to a Kolibri channel using the `DocumentFile` class as part of a `DocumentNode`."
"You should now be able to see the file `document.pdf` in the current directory, and add this PDF file to a Kolibri channel using `ContentNode(uri=...)`."
]
},
{
Expand Down Expand Up @@ -116,7 +116,7 @@
"cell_type": "markdown",
"metadata": {},
"source": [
"You should now be able to see the file `infographic.pdf` in the current directory, and add this PDF file to a Kolibri channel using the `DocumentFile` class as part of a `DocumentNode`."
"You should now be able to see the file `infographic.pdf` in the current directory, and add this PDF file to a Kolibri channel using `ContentNode(uri=...)`."
]
},
{
Expand Down
181 changes: 156 additions & 25 deletions docs/examples/exercises.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,23 @@
{
"cell_type": "code",
"execution_count": 1,
"metadata": {},
"outputs": [],
"metadata": {
"execution": {
"iopub.execute_input": "2026-07-05T01:53:16.396675Z",
"iopub.status.busy": "2026-07-05T01:53:16.396370Z",
"iopub.status.idle": "2026-07-05T01:53:16.708201Z",
"shell.execute_reply": "2026-07-05T01:53:16.706857Z"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Unable to load pyppeteer, using phantomjs for JS loading.\n"
]
}
],
"source": [
"from ricecooker.chefs import SushiChef\n",
"from ricecooker.classes.nodes import TopicNode, ExerciseNode\n",
Expand All @@ -40,7 +55,7 @@
"class ExercisesChef(SushiChef):\n",
" channel_info = {\n",
" 'CHANNEL_TITLE': 'Sample Exercises',\n",
" 'CHANNEL_SOURCE_DOMAIN': '<yourdomain.org>', # where you got the content\n",
" 'CHANNEL_SOURCE_DOMAIN': 'ricecooker-docs-example.org', # where you got the content\n",
" 'CHANNEL_SOURCE_ID': '<unique id for channel>', # channel's unique id CHANGE ME\n",
" 'CHANNEL_LANGUAGE': 'en', # le_utils language code\n",
" 'CHANNEL_DESCRIPTION': 'A test channel with different types of exercise questions', # (optional)\n",
Expand Down Expand Up @@ -92,7 +107,7 @@
" topic.add_child(exercise_node)\n",
"\n",
" # LOAD JSON DATA (as string) FOR PERSEUS QUESTIONS \n",
" RAW_PERSEUS_JSON_STR = open('../../examples/exercises/chefdata/perseus_graph_question.json', 'r').read()\n",
" RAW_PERSEUS_JSON_STR = open('../../examples/oldexamples/data/perseus_graph_question.json', 'r').read()\n",
" # or\n",
" # import requests\n",
" # RAW_PERSEUS_JSON_STR = requests.get('https://raw.githubusercontent.com/learningequality/sample-channels/master/contentnodes/exercise/perseus_graph_question.json').text\n",
Expand All @@ -113,6 +128,7 @@
" PerseusQuestion(\n",
" id='ex2bQ4',\n",
" raw_data=RAW_PERSEUS_JSON_STR,\n",
" ka_language='en',\n",
" source_url='https://github.com/learningequality/sample-channels/blob/master/contentnodes/exercise/perseus_graph_question.json'\n",
" ),\n",
" ]\n",
Expand Down Expand Up @@ -141,35 +157,150 @@
{
"cell_type": "code",
"execution_count": 2,
"metadata": {},
"metadata": {
"execution": {
"iopub.execute_input": "2026-07-05T01:53:16.770646Z",
"iopub.status.busy": "2026-07-05T01:53:16.770163Z",
"iopub.status.idle": "2026-07-05T01:53:16.829779Z",
"shell.execute_reply": "2026-07-05T01:53:16.828264Z"
}
},
"outputs": [
{
"name": "stderr",
"output_type": "stream",
"text": [
"\u001b[32mINFO \u001b[0m \u001b[34mIn SushiChef.run method. args={'command': 'dryrun', 'reset': True, 'verbose': True, 'token': 'YOURTO...'} options={}\u001b[0m\n",
"\u001b[32mINFO \u001b[0m \u001b[34mIn SushiChef.run method. args={'command': 'dryrun', 'verbose': True, 'token': 'YOURTO...'} options={}\u001b[0m\n"
]
},
{
"name": "stderr",
"output_type": "stream",
"text": [
"\u001b[32mINFO \u001b[0m \u001b[34m\n",
"\n",
"***** Starting channel build process *****\n",
"\n",
"\u001b[0m\n",
"\u001b[32mINFO \u001b[0m \u001b[34mCalling construct_channel... \u001b[0m\n",
"\u001b[32mINFO \u001b[0m \u001b[34m Setting up initial channel structure... \u001b[0m\n",
"\u001b[32mINFO \u001b[0m \u001b[34m Validating channel structure...\u001b[0m\n",
"\u001b[32mINFO \u001b[0m \u001b[34m Sample Exercises (ChannelNode): 3 descendants\u001b[0m\n",
"\u001b[32mINFO \u001b[0m \u001b[34m Math Exercises (TopicNode): 2 descendants\u001b[0m\n",
"\u001b[32mINFO \u001b[0m \u001b[34m Basic questions (ExerciseNode): 3 questions\u001b[0m\n",
"\u001b[32mINFO \u001b[0m \u001b[34m An exercise containing a perseus question (ExerciseNode): 1 question\u001b[0m\n",
"\u001b[32mINFO \u001b[0m \u001b[34m Tree is valid\n",
"\u001b[0m\n",
"\u001b[32mINFO \u001b[0m \u001b[34mDownloading files...\u001b[0m\n",
"\u001b[32mINFO \u001b[0m \u001b[34mProcessing content...\u001b[0m\n",
"\u001b[32mINFO \u001b[0m \u001b[34m\t*** Processing images for exercise: Basic questions\u001b[0m\n",
"\u001b[32mINFO \u001b[0m \u001b[34m\t*** Images for Basic questions have been processed\u001b[0m\n",
"\u001b[32mINFO \u001b[0m \u001b[34m\t*** Processing images for exercise: An exercise containing a perseus question\u001b[0m\n",
"\u001b[32mINFO \u001b[0m \u001b[34m\t*** Images for An exercise containing a perseus question have been processed\u001b[0m\n",
"\u001b[32mINFO \u001b[0m \u001b[34m All files were successfully downloaded\u001b[0m\n",
"\u001b[32mINFO \u001b[0m \u001b[34mCommand is dryrun so we are not uploading chanel.\u001b[0m\n"
"\u001b[0m\n"
]
},
{
"name": "stderr",
"output_type": "stream",
"text": [
"\u001b[32mINFO \u001b[0m \u001b[34mCalling construct_channel... \u001b[0m\n"
]
},
{
"name": "stderr",
"output_type": "stream",
"text": [
"\u001b[32mINFO \u001b[0m \u001b[34m Setting up initial channel structure... \u001b[0m\n"
]
},
{
"name": "stderr",
"output_type": "stream",
"text": [
"\u001b[32mINFO \u001b[0m \u001b[34m Validating channel structure...\u001b[0m\n"
]
},
{
"name": "stderr",
"output_type": "stream",
"text": [
"\u001b[32mINFO \u001b[0m \u001b[34m Sample Exercises (ChannelNode) (<unique id for channel>): 3 descendants\u001b[0m\n"
]
},
{
"name": "stderr",
"output_type": "stream",
"text": [
"\u001b[32mINFO \u001b[0m \u001b[34m Math Exercises (TopicNode) (folder-id): 2 descendants\u001b[0m\n"
]
},
{
"name": "stderr",
"output_type": "stream",
"text": [
"\u001b[32mINFO \u001b[0m \u001b[34m Basic questions (ExerciseNode): 3 questions\u001b[0m\n"
]
},
{
"name": "stderr",
"output_type": "stream",
"text": [
"\u001b[32mINFO \u001b[0m \u001b[34m An exercise containing a perseus question (ExerciseNode): 1 question\u001b[0m\n"
]
},
{
"name": "stderr",
"output_type": "stream",
"text": [
"\u001b[32mINFO \u001b[0m \u001b[34m Tree is valid\u001b[0m\n"
]
},
{
"name": "stderr",
"output_type": "stream",
"text": [
"\u001b[32mINFO \u001b[0m \u001b[34m\u001b[0m\n"
]
},
{
"name": "stderr",
"output_type": "stream",
"text": [
"\u001b[32mINFO \u001b[0m \u001b[34mDownloading files...\u001b[0m\n"
]
},
{
"name": "stderr",
"output_type": "stream",
"text": [
"\u001b[32mINFO \u001b[0m \u001b[34mProcessing content...\u001b[0m\n"
]
},
{
"name": "stderr",
"output_type": "stream",
"text": [
"\u001b[32mINFO \u001b[0m \u001b[34m\t*** Processing images for exercise: Basic questions\u001b[0m\n"
]
},
{
"name": "stderr",
"output_type": "stream",
"text": [
"\u001b[32mINFO \u001b[0m \u001b[34m\t*** Images for Basic questions have been processed\u001b[0m\n"
]
},
{
"name": "stderr",
"output_type": "stream",
"text": [
"\u001b[32mINFO \u001b[0m \u001b[34m\t*** Processing images for exercise: An exercise containing a perseus question\u001b[0m\n"
]
},
{
"name": "stderr",
"output_type": "stream",
"text": [
"\u001b[32mINFO \u001b[0m \u001b[34m\t*** Images for An exercise containing a perseus question have been processed\u001b[0m\n"
]
},
{
"name": "stderr",
"output_type": "stream",
"text": [
"\u001b[32mINFO \u001b[0m \u001b[34m All files were successfully downloaded\u001b[0m\n"
]
},
{
"name": "stderr",
"output_type": "stream",
"text": [
"\u001b[32mINFO \u001b[0m \u001b[34mCommand is dryrun so we are not uploading channel.\u001b[0m\n"
]
}
],
Expand Down Expand Up @@ -227,7 +358,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.7.4"
"version": "3.13.12"
}
},
"nbformat": 4,
Expand Down
Loading
Loading