“Per-method” tutorials¶
We support tutorials for program represented in the “per-method” style (also known internally as “structured” or “Pytch junior”). The underlying source of truth for such a tutorial remains a linear git history for a single file of flat Python code, coupled with a markdown file for the tutorial text. For these tutorials, we also bring in the idea of progressive revelation of help for the user, and a different presentation of changes to the code.
Writing a per-method tutorial¶
The learner will work with the program using the per-method representation and interface. However, the source of truth for the tutorial is a normal flat Pytch program, obeying some constraints to ensure that it has a direct equivalent per-method representation.
Each well-defined change to the user’s program should be a commit in the git history. Such commits can be as short as only one or two lines long for introductory tutorials. Every commit must be of a known and well-defined semantic kind, described below.
Each commit is presented as part of a “learner task” in the front end. The learner tasks are marked up in the tutorial source by means of begin and end shortcodes:
{{< learner-task >}}
to mark the beginning of a learner task{{< /learner-task >}}
to mark the end of a learner task
In between these two shortcode markers, the learner task consists of
introductory text and a sequence of one or more learner-task help
sections. The introductory text starts at the start of the learner
task, and ends just before the first {{< learner-task-help >}}
marker shortcode. The text should describe in fairly broad,
high-level terms the goal of the change.
Each {{< learner-task-help }}
shortcode indicates the start of a
learner help section. The help sections should provide more and more
detail about how to achieve the goal. The last learner help section
should include “the answer”, i.e., tell the learner exactly what
change to their program is needed. This is done by referring to a
tagged commit using a shortcode like
{{< jr-commit COMMIT-SLUG COMMIT-KIND OPTIONAL-COMMIT-ARGS >}}
where:
COMMIT-SLUG is the label identifying this commit, as found in its special tag commit message (e.g., for a commit with message
{#increment-score}
, the commit-slug isincrement-score
);COMMIT-KIND identifies the kind of commit this is, for example one which adds a new script to a sprite; see below for details of the different kinds of commit;
OPTIONAL-COMMIT-ARGS is JSON representing an array of the arguments the particular commit-kind requires; see below for details.
Generating learner-tasks for all code commits¶
The tool pytchbuild-emit-commit-slugs-markdown
will emit, to
stdout, markdown text consisting of a sequence of learner-task blocks,
one per code commit in the history. This can be copied into the
tutorial.md
file as a basis for writing the tutorial text.
Kinds of commit¶
Currently the tutorial author must indicate what kind of change is
being made by each commit, by supplying one of the following
commit-kinds. The add-medialib-appearance
kind takes one
argument; the other kinds take no arguments. If the
optional-commit-args string is absent, the empty array is used.
add-sprite
Add a new sprite to the project. Such a commit should add code text for a new Sprite-derived class whose body is the assignment statement
Costumes = []
add-medialib-appearance
DISPLAY-IDENTIFIER
Add an element to the list of Costumes (for a Sprite) or Backdrops (for the Stage). Such a commit should add a string literal to the appropriate class variable. The DISPLAY-IDENTIFIER is a string shown to the learner to help them find the correct appearance in the media library.
add-medialib-appearances-entry
ENTRY-NAME
Add multiple elements to the list of Costumes (for a Sprite) or Backdrops (for the Stage). Such a commit should add multiple string literals to the appropriate class variable. The ENTRY-NAME is a string shown to the learner to help them find the correct ‘entry’ (group of appearances) in the media library. It is assumed that the named medialib entry does in fact contain all the added images (and no others).
delete-appearance
Remove an element from the list of Costumes or Backdrops. Such a commit should remove exactly one string literal from the appropriate class variable. The deleted string literal is shown to the user so they can find the right appearance to delete.
add-script
Add a script (decorated method) to a sprite. The code added should include the decorator, the method
def
line, and a body. If the body is justpass
, this is treated as empty; otherwise, the displayed help will include the given body code.
edit-script
Change exactly one script (decorated method) body in exactly one Sprite or the Stage.
change-hat-block
Change exactly one script’s decorator in exactly one Sprite or the Stage.
Representation of commits¶
There is almost a one-to-one mapping between the above commit-kind
strings and the values of the kind
slot of the various
CommitKind
types. The exception is that
add-medialib-appearance
and add-medialib-appearances-entry
are
both mapped to add-medialib-appearances-entry
, because they’re
treated the same by the front end.
Excluding chapters from “progress trail”¶
The front end shows a progress trail of circles which are replaced with check-marks as the learner works through the tutorial. Often the last chapter or two are not really part of the tutorial proper; they might be challenges to try, or the credits for the assets used. To indicate that these chapters should not be counted as part of the progress the learner must make to complete the tutorial, the tutorial author can include the shortcode
{{< exclude-from-progress-trail >}}
somewhere in that chapter. Straight after the heading is a good place. Currently, all excluded chapters must be at the end of the tutorial.
Summary theory of operation for compiler¶
An instance of the class StructuredPytchProgram
wraps a
well-formed Pytch program, and provides access to the program’s list
of actors, methods, appearances, etc. An instance of the class
StructuredPytchDiff
can provide a “rich commit” instance
representing the change from an “old” state of the program to a “new”
state. There are different rich-commit classes, one for each kind of
commit (e.g., JrCommitAddSprite
for the "add-sprite"
commit-kind). These form the interface between the compiler and the
front end.
There are fairly extensive assertions when generating these commits that the code has undergone the right kind of change. (E.g., an “edit-script” commit must change exactly one method-body.) If any of these assertions fails, an exception is raised.
A learner task’s flat sequence of div
elements, as produced by the
markdown processor, is converted into a nested form before being
written to the final tutorial output. Each learner-task div
will
contain a “commit div
” with the JSON of the rich commit object as
a data attribute.
Future work¶
It should be possible to determine the commit-kind automatically. One
approach would be to just try asking the StructuredPytchDiff
object for every kind of rich-commit kind. Exactly one such request
should succeed, with all others raising an exception.
As we write more tutorials, other kinds of commit might be needed. For example, one which adds all the images in a group (e.g., all four directions of the player’s character in Qbert).
It would be good to include costume thumbnails in the “task cards”; this might be made easier with help from the compiler.