A Template is a named batch of pre-filled entries bound to a Trackable: a recipe on Diet, a circuit on the Set. A trackable’s templates are made, edited and archived in the Templates section of its page, since the trackable is what gives their lines meaning. A template is archived, never deleted (template.archived_at): an entry made from it is named live from it, so an archived one leaves every picker and list of templates (templates in useTemplates) and still names every bowl logged from it (all, which useTemplateNames reads). Archived ones are listed under Archived in the section, each with Restore. Picked in the trackable’s form, a template of one line fills the form, the start and stop, time and note staying the form’s own; a template of several lines stamps out one editable row per line, which are tweaked or skipped and then logged inside one entry that stands in for them: it names the template, holds no value of its own, and reads as the template’s name wherever entries are listed (Egg & Cheese, holding the egg and the cheddar). Each row is an entry of its own and counts as one (Entry, Reading). Opened from any list, the entry standing in shows what it holds instead of a value form: for a Trackable that logs items with an amount, its totals and its items as the day’s Diet shows them; otherwise its parts in a list. A part opens to be changed or removed, another is added into it (a slice of tomato), and the whole is deleted, asking whether what it holds goes too. None of this changes the template. Issues skip it: it has nothing of its own to fill in, and its parts are checked as entries of their own.

A template is not a catalog item: its lines carry the trackable’s fields, reps and weight or a food and its amount. A recipe is a template. Chad’s Egg & Cheese is one on Diet, and logged it is a Diet entry holding his egg and his cheddar, so each counts as itself in every total, which a food computed from its ingredients never could.

Definition

tablecolumns
templatename, trackable_id (its trackable), weighed (what the whole weighs once made, optional), serving (what a serving weighs, optional), based_on (the template it was duplicated from, optional), archived_at (when it was put away; null while in use)
template_linetemplate_id, entity_id (the item it names, optional), field_values, sort_order, trackable_id (optional), depth, stands_for (the template a line stands in for, its lines beneath it; optional)

A line is a pre-filled entry. entity_id is a real foreign key to the item the line names, so “which templates use Zottman Curl?” is a query rather than a scan of JSON, and deleting the item deletes the lines that name it; it is empty for a trackable with no item field. A line’s item is one its trackable can log, from the catalog its item field names: writing a template’s lines refuses an exercise as a line of a Diet template, and any item on a trackable that logs none, before anything is written (setLines in packages/core/src/template-repository.ts, the one write path: the form and the seeds write through it, and a line that arrives by sync was checked on the device that wrote it, since sync applies rows as they were written). field_values holds the entry’s other fields. Deleting the trackable deletes its templates.

  • Egg & Cheese on Diet: lines (Egg, quantity 100) and (Cheddar, quantity 30).
  • Arm Circuit on the Set: lines of its own, (Zottman Curl, reps 10, weight 16) and three more, three rounds.

One path, no domain code

Which field a line’s item lands in is read from the trackable’s value schema (referenceSlotOf): the item field that carries the amount, else the first item field. So (exercise, reps, weight) and (food, quantity, unit) are the same code reading two schemas, and a template over a new catalog needs no new code. A line that leaves out what its trackable fills from its item, such as a push-up’s weight from bodyweight, is filled the way the form fills it (Capture Model).

An Event’s template

A Event’s template holds other trackables’ entries, so each line names its own trackable_id; a line naming a trackable that does not exist is skipped. A line’s depth nests it: one deeper than the line before it, it goes inside that line’s Event. Saving an Event’s entry as a template keeps what its Events held the same way. Chad’s Push, Pull and Legs on the Workout are a stretch and then each lift’s sets in a row.

  • Chosen, then logged. In an Event’s form a template is chosen, not acted on: the form’s own Log or Open logs the Event and then applies the template. With no end yet the choice reads Start with; with an end, Log with.
  • Started, it becomes the Event’s plan. An Event started from a template logs nothing yet: its lines, each given an id, are the Event’s plan (the setting routine:plan, per entry, kept while the Event runs and for two days after it ends), and its page shows them as rows to tick (Events). A row keeps the values it was planned with apart from those it holds now, so a set done at 70 kg where 75 was planned starts the next one planned the same at 70. A template added while it runs adds its rows to the plan.
  • Logged after the fact, it holds them all, done in order: an interval starts where the one before it in the same Event ended, and an Event’s lines start with it.
  • The Event’s entry names its template (entry.template_id), whether started with it, logged with it, or saved as it. The link has no foreign key, as part_of has none; a template is archived rather than deleted, so the entry keeps its name, and only deleting the trackable takes its templates.

Found from anywhere

Log or search finds a template by its name once something is typed, beside the trackables, its trackable named beside it (Frictionless Capture). Choosing it opens its trackable’s log form started with it, as picking it there would (StartTemplate in apps/renderer/src/components/entries/start-template.ts): Egg & Cheese opens on its lines to log, a one-line template fills the form, and Push opens the Workout’s form with Push chosen, so Open starts a Workout with its lines planned. In a trackable’s own log form, its item picker offers its templates beside its items, and an Event’s form offers its kind’s as Start with.

Editing one can fix the past

What is logged from a template is an instance: changing the template leaves it alone, so two slices of cheese today as a treat is an ordinary edit of that day’s sandwich. Saving a template of a thing made of parts with new lines offers to fix the past (FixPast in apps/renderer/src/components/templates/). Each entry made from it is compared with what its lines as they were before the edit would have logged (templatePast in packages/core/src/template-past.ts, stillStamped the comparison): one still as stamped takes the new lines; one changed since, by hand or as a portion, is listed to tick or open; one already as the new lines needs nothing. Updating replaces what each holds in one batch (replaceParts), each new part checked as any entry is, and one Undo puts the old parts back. An Event’s template does not offer it: a Workout holds what was done, not its plan.

A portion of the whole

A template of amounts of items is a recipe, logged as a share of the whole: batch cooking. The whole weighs what its lines do, in their items’ basis (linesWeight in packages/core/src/portion.ts), until the template’s form says what it weighed once made (template.weighed), since cooking adds or loses water. A serving is a weight of the whole (template.serving, a 400 g bowl of a 2,400 g pot), the whole by default (servingWeight). Logging it asks for the Amount, in servings or the basis, 1 serving by default (amountWeight, weightAmount); 2 servings of that pot take 800/2,400 of every line in the line’s own unit (portionLines), each row’s quantity its line’s share, its base frozen from that as any amount’s is. A row’s other values, and a row skipped, stay as they were changed. A thing made of parts weighs what its parts do, worked out on read and never stored (holderWeight in packages/core/src/template-past.ts), so a part changed by hand, that day’s extra cheese, makes its total follow. Its entry, opened from any list, carries that amount in servings or the basis; changing it scales every part by the same share (rescaledParts), which keeps a part changed by hand in proportion, and Reset to the recipe, offered once its parts differ from the recipe’s at that amount, writes them again as the recipe makes it (recipeParts). Each is one write with an Undo, the parts changed in place where they are the same items, so an Event’s rows logged as them stay logged. Planned in an Event and not yet logged, its heading carries the same amount, which scales the rows beneath it (scaledBeneath in the Event page’s session-plan.ts).

A variant is a duplicate based on its recipe. A template’s page offers Duplicate, which makes a copy with its lines, Weighed and serving, named with “(copy)” and based on it (template.based_on, written by duplicate in packages/core/src/template-repository.ts), and opens it to change: Turkey Chili from Chili. Each is independent once made; the link only says where it came from, “Based on Chili” on its page and its card. Renaming a template relabels everything logged from it, since an entry is named live from it, so to fork a recipe, duplicate first, then rename. Its variants do not inherit from it (jeliroco-multimedia/datler#434). Each pot made is not tracked: a recipe is one representative pot, and every logging is arithmetic on it.

The card reads its lines as the entries they log

A template’s card shows what its lines come to, read as the entries logging it would write (entriesFromLines), so the card and the log agree: an amount of an item with its base frozen from the item’s units, an interval as long as its line keeps. Each Trackable’s share is read by that Trackable’s own rules, written once on it and never per template: Diet’s food totals as its Today strip has them, the exercise Trackable’s summary strip (sets, volume, top set, and the muscles worked from each exercise’s own muscles), any other Trackable its Reading. Egg & Cheese reads its calories; Arm Circuit its sets, volume and muscles. An entry made from a template and an Event’s page show the same figures over what they hold. A template has no rules of its own for this: two templates of one Trackable read alike.

Code

  • packages/core/src/template-repository.ts; the tables in packages/core/src/schema.ts.
  • apps/renderer/src/components/templates/: TemplateForm.tsx, TemplateExpander.tsx, TemplatesSection.tsx; the card’s figures in apps/renderer/src/components/entries/PartsFigures.tsx.
  • packages/core/src/routines.ts (entriesFromLines) and entity-units.ts (withBase): the entries a template’s lines log.
  • apps/renderer/src/components/entries/RoutineField.tsx and apps/renderer/src/hooks/useRoutinePlan.ts for an Event’s template.

Catalog Entities · Events · Entry · Trackable · Value Types · Exercise & Strength · Diet and Nutrition · Capture Model