Skip to content
orticPublic

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Repository files navigation

lcf-go

A Go library for the Learning Content Format (LCF) — an open, plain-text format for authoring learning content of any subject (languages, mathematics, chemistry, history, music, …) as Markdown files that are human-authorable, git-friendly, and machine-scorable. See the format specification at github.com/ortic/lcf.

lcf-go gives you everything needed to build a web viewer (or any tool) on top of LCF documents:

Package Responsibility
lcf Parse a .lcf.md file into typed front-matter + a body AST; extract the assessable units as typed interactions.
lcf/render Render a document body to interactive, framework-agnostic HTML (prose via CommonMark, math preserved for KaTeX/MathJax).
lcf/score Grade learner responses against the answer model, with pluggable domain checkers.
lcf/catalog Index a directory tree of documents into a queryable, faceted catalog for browse/list pages.

It is validated against the full ortic/knowledge corpus (100 exercises across 10 subjects in English and German).

Install

go get github.com/ortic/lcf-go

Requires Go 1.24+. Dependencies: goldmark (CommonMark) and gopkg.in/yaml.v3.

Quick start

Parse a document

doc, err := lcf.ParseFile("exercises/en/mathematics/fractions.lcf.md")
if err != nil {
    log.Fatal(err)
}
fmt.Println(doc.FrontMatter.Title)          // "Fractions: Addition, …"
fmt.Println(doc.FrontMatter.Domain.First()) // "math"

for _, item := range doc.Items() {
    fmt.Printf("%s is a %s worth %.0f points\n", item.ID, item.Primitive, item.Points)
}

Document.Items() returns typed Item values with the answer model populated per primitive — choice, text, cloze, order, match, numeric, formula — resolving domain-pack aliases (e.g. the language pack's [translate] → text, the chemistry pack's [reaction] → formula).

Render to HTML

r := render.New(render.Options{Interactive: true})
html, _ := r.Document(doc)
// html now contains <div class="lcf-interaction lcf-choice" data-lcf-id="q1" …>

The output is semantic HTML carrying data-lcf-* attributes and stable CSS classes so your front-end can style it and wire up answering. Inline $…$ and display $$…$$ math is passed through verbatim for client-side KaTeX/MathJax. Set ShowAnswers: true for author previews and answer keys.

Grade responses

g := score.NewGrader()
result := g.Grade(doc, map[string]score.Response{
    "n1": {Text: "0.875"},
    "q1": {Selected: []string{"opt-dfrac-3-4"}},
    "c1": {Blanks: map[string]string{"b1": "3", "b2": "4"}},
})
fmt.Printf("%.1f / %.1f\n", result.Awarded, result.Max)

Simple items score with sane defaults (declared/exact match ⇒ full points). Normalisation (case, whitespace, accents, punctuation), match modes (exact/contains/regex), numeric tolerances (absolute or %), and partial credit (partial="proportional") are all honoured. Domain grading is delegated to named Checker hooks — a chem.balanced checker for balanced chemical equations ships built in; register your own for e.g. math.symbolic-equiv:

g.RegisterChecker("math.symbolic-equiv", func(item lcf.Item, response string) (correct, handled bool) {
    // … call a CAS, return (equivalent, true) …
})

Index a corpus

cat, _ := catalog.LoadDir("./exercises")
fmt.Println(cat.Len())        // 100
fmt.Println(cat.Domains())    // [{math 10} {chemistry 10} …] — faceted counts
fmt.Println(cat.Locales())    // [{de 50} {en 50}]

for _, e := range cat.ByLocale("en") {
    fmt.Println(e.Title, e.Subject, e.ItemCount)
}

doc, _ := cat.OpenID("mathematics.en.fractions") // parse the full body on demand

catalog indexes lightweight metadata per document (no bodies) so a large corpus loads cheaply, and offers filtering (ByDomain, BySubject, ByLocale, ByKind, ByTag, Filter) plus faceted navigation (Domains, Subjects, Locales, Kinds, Tags).

Design notes

  • Graceful degradation (spec §8.4). Parsing never fails on malformed shortcodes: an unknown or unbalanced block degrades to literal prose, and a Markdown link like [text](url) is never mistaken for a [text] interaction. Parsing only errors when the YAML front-matter cannot be decoded.
  • Stable identity (spec §6). Every interaction carries an id; the pair (document.id, item.id) is the key you use for attempts, analytics, and spaced-repetition state.
  • Extensible. The Registry classifies block names and maps domain-pack blocks to core primitives; register new blocks without touching the parser.
  • Round-trippable. Every node retains its original source (Node.Raw()).

A web viewer

A complete, server-rendered web viewer built on this library — faceted browsing by language and subject, interactive exercise pages, and answer grading — lives in the ortic/knowledge repository under web/.

Conformance

Targets LCF v0.1. Supports conformance levels L1 (structure/rendering), L2 (assessment/scoring), and L3 (domain packs) via the registry and checker extension points.

Licence

MIT © 2026 Ortic Solutions GmbH. See LICENSE.

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages