@lernapps/tooling 0.0.0-stage → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +22 -0
- package/README.md +67 -2
- package/archetypes/shared/a11y.ts +38 -0
- package/archetypes/shared/hooks/pre-commit +22 -0
- package/archetypes/shared/hooks/pre-push +4 -0
- package/archetypes/shared/i18n.ts +25 -0
- package/archetypes/shared/install-hooks.ts +63 -0
- package/archetypes/shared/playwright.ts +54 -0
- package/archetypes/shared/preset.ts +81 -0
- package/archetypes/shared/storage.ts +167 -0
- package/archetypes/shared/tsconfig.json +27 -0
- package/check/validation-report.v1.schema.json +116 -0
- package/dist/a11y.mjs +30 -0
- package/dist/check/rules/accessible.mjs +18 -0
- package/dist/check/rules/app-content-guide.mjs +69 -0
- package/dist/check/rules/dependency-licence.mjs +82 -0
- package/dist/check/rules/entry-fitness-matches.mjs +22 -0
- package/dist/check/rules/entry-links-resolve.mjs +44 -0
- package/dist/check/rules/explainer-readable-without-js.mjs +66 -0
- package/dist/check/rules/links-resolve.mjs +10 -0
- package/dist/check/rules/lockfile-matches.mjs +57 -0
- package/dist/check/rules/no-request-before-click.mjs +17 -0
- package/dist/check/rules/no-tracking.mjs +17 -0
- package/dist/check/rules/own-data-only.mjs +66 -0
- package/dist/check/rules/pages-reachable.mjs +25 -0
- package/dist/check/rules/usable-at-360px.mjs +19 -0
- package/dist/check-cv4I6CCH.mjs +28 -0
- package/dist/cli.mjs +3118 -0
- package/dist/i18n.mjs +11 -0
- package/dist/lint.mjs +528 -0
- package/dist/plan-DWKayEnY.mjs +75 -0
- package/dist/playwright.mjs +47 -0
- package/dist/preset.mjs +60 -0
- package/dist/skills/lernapps-app/SKILL.md +49 -0
- package/dist/skills/lernapps-cli/SKILL.md +156 -0
- package/dist/skills/lernapps-cli/references/platform-rules.md +258 -0
- package/dist/skills/lernapps-commit/SKILL.md +39 -0
- package/dist/skills/lernapps-content-growth/SKILL.md +33 -0
- package/dist/skills/lernapps-content-questions/SKILL.md +133 -0
- package/dist/skills/lernapps-content-questions/references/question-types.md +69 -0
- package/dist/skills/lernapps-content-research/SKILL.md +50 -0
- package/dist/skills/lernapps-content-research/references/sources.md +16 -0
- package/dist/skills/lernapps-exercise-patterns/SKILL.md +74 -0
- package/dist/skills/lernapps-exercise-patterns/references/biology-geography.md +13 -0
- package/dist/skills/lernapps-exercise-patterns/references/chemistry.md +16 -0
- package/dist/skills/lernapps-exercise-patterns/references/computer-science.md +13 -0
- package/dist/skills/lernapps-exercise-patterns/references/figures.md +10 -0
- package/dist/skills/lernapps-exercise-patterns/references/history.md +13 -0
- package/dist/skills/lernapps-exercise-patterns/references/mathematics.md +19 -0
- package/dist/skills/lernapps-exercise-patterns/references/page-structures.md +8 -0
- package/dist/skills/lernapps-exercise-patterns/references/physics.md +18 -0
- package/dist/skills/lernapps-exercise-patterns/references/success.md +28 -0
- package/dist/skills/lernapps-explainer/SKILL.md +33 -0
- package/dist/skills/lernapps-explainer/references/blocks.md +83 -0
- package/dist/skills/lernapps-explainer/references/components.md +66 -0
- package/dist/skills/lernapps-explainer/references/exercises.md +115 -0
- package/dist/skills/lernapps-explainer/references/rules.md +285 -0
- package/dist/skills/lernapps-explainer/references/runtime.md +79 -0
- package/dist/skills/lernapps-explainer/references/structure.md +24 -0
- package/dist/skills/lernapps-explainer-design/SKILL.md +57 -0
- package/dist/skills/lernapps-explainer-design/references/rules.md +75 -0
- package/dist/skills/lernapps-explainer-self-assessment/SKILL.md +93 -0
- package/dist/skills/lernapps-github/SKILL.md +102 -0
- package/dist/skills/lernapps-interactive/SKILL.md +141 -0
- package/dist/skills/lernapps-interactive/references/frame.md +30 -0
- package/dist/skills/lernapps-interactive-design/SKILL.md +58 -0
- package/dist/skills/lernapps-listing/SKILL.md +44 -0
- package/dist/skills/lernapps-listing/references/keeping-updated.md +33 -0
- package/dist/skills/lernapps-quiz/SKILL.md +100 -0
- package/dist/skills/lernapps-quiz-design/SKILL.md +48 -0
- package/dist/status-CXoQBgfU.mjs +76 -0
- package/dist/storage.mjs +113 -0
- package/generator/app/.agents/skills/app-content/SKILL.md +63 -0
- package/generator/app/.github/ISSUE_TEMPLATE/inhaltsfehler.yml +25 -0
- package/generator/app/.github/workflows/pages.yml +61 -0
- package/generator/app/LICENSE +21 -0
- package/generator/app/renovate.json +4 -0
- package/generator/architecture.ts +212 -0
- package/generator/create.ts +462 -0
- package/generator/init.ts +186 -0
- package/generator/update.ts +429 -0
- package/guidance/AGENTS.md +26 -0
- package/guidance/increment-template.md +194 -0
- package/guidance/plan-front-matter.v1.schema.json +36 -0
- package/guidance/plan-template.md +233 -0
- package/guidance/subagent-brief.md +25 -0
- package/guidance/workflow-rules.md +109 -0
- package/package.json +135 -4
- package/review/verdict.v1.schema.json +134 -0
- package/skills/archetypes/lernapps-explainer/SKILL.md +33 -0
- package/skills/archetypes/lernapps-explainer/references/blocks.md +83 -0
- package/skills/archetypes/lernapps-explainer/references/components.md +66 -0
- package/skills/archetypes/lernapps-explainer/references/exercises.md +115 -0
- package/skills/archetypes/lernapps-explainer/references/rules.md +285 -0
- package/skills/archetypes/lernapps-explainer/references/runtime.md +79 -0
- package/skills/archetypes/lernapps-explainer/references/structure.md +24 -0
- package/skills/archetypes/lernapps-explainer-design/SKILL.md +57 -0
- package/skills/archetypes/lernapps-explainer-design/references/rules.md +75 -0
- package/skills/archetypes/lernapps-explainer-self-assessment/SKILL.md +93 -0
- package/skills/archetypes/lernapps-interactive/SKILL.md +141 -0
- package/skills/archetypes/lernapps-interactive/references/frame.md +30 -0
- package/skills/archetypes/lernapps-interactive-design/SKILL.md +58 -0
- package/skills/archetypes/lernapps-quiz/SKILL.md +100 -0
- package/skills/archetypes/lernapps-quiz-design/SKILL.md +48 -0
- package/skills/content/lernapps-content-questions/SKILL.md +133 -0
- package/skills/content/lernapps-content-questions/references/question-types.md +69 -0
- package/skills/content/lernapps-content-research/SKILL.md +50 -0
- package/skills/content/lernapps-content-research/references/sources.md +16 -0
- package/skills/content/lernapps-exercise-patterns/SKILL.md +74 -0
- package/skills/content/lernapps-exercise-patterns/references/biology-geography.md +13 -0
- package/skills/content/lernapps-exercise-patterns/references/chemistry.md +16 -0
- package/skills/content/lernapps-exercise-patterns/references/computer-science.md +13 -0
- package/skills/content/lernapps-exercise-patterns/references/figures.md +10 -0
- package/skills/content/lernapps-exercise-patterns/references/history.md +13 -0
- package/skills/content/lernapps-exercise-patterns/references/mathematics.md +19 -0
- package/skills/content/lernapps-exercise-patterns/references/page-structures.md +8 -0
- package/skills/content/lernapps-exercise-patterns/references/physics.md +18 -0
- package/skills/content/lernapps-exercise-patterns/references/success.md +28 -0
- package/skills/process/lernapps-app/SKILL.md +49 -0
- package/skills/process/lernapps-commit/SKILL.md +39 -0
- package/skills/process/lernapps-content-growth/SKILL.md +33 -0
- package/skills/process/lernapps-github/SKILL.md +102 -0
- package/skills/process/lernapps-listing/SKILL.md +44 -0
- package/skills/process/lernapps-listing/references/keeping-updated.md +33 -0
- package/skills/tooling/lernapps-cli/SKILL.md +156 -0
- package/skills/tooling/lernapps-cli/references/platform-rules.md +258 -0
package/package.json
CHANGED
|
@@ -1,6 +1,137 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lernapps/tooling",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Shared tooling of the lernapps.net apps and sites: the CLI lernapps, site actions, Renovate preset, guidance for building apps; documentation at https://lernapps.net/tooling/",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"repository": {
|
|
7
|
+
"type": "git",
|
|
8
|
+
"url": "git+https://github.com/lernapps/tooling.git"
|
|
9
|
+
},
|
|
10
|
+
"bin": {
|
|
11
|
+
"lernapps": "./dist/cli.mjs"
|
|
12
|
+
},
|
|
13
|
+
"files": [
|
|
14
|
+
"dist",
|
|
15
|
+
"archetypes",
|
|
16
|
+
"check/validation-report.v1.schema.json",
|
|
17
|
+
"generator",
|
|
18
|
+
"review/verdict.v1.schema.json",
|
|
19
|
+
"guidance",
|
|
20
|
+
"skills"
|
|
21
|
+
],
|
|
22
|
+
"type": "module",
|
|
23
|
+
"exports": {
|
|
24
|
+
"./package.json": "./package.json",
|
|
25
|
+
"./preset": {
|
|
26
|
+
"types": "./archetypes/shared/preset.ts",
|
|
27
|
+
"default": "./dist/preset.mjs"
|
|
28
|
+
},
|
|
29
|
+
"./playwright": {
|
|
30
|
+
"types": "./archetypes/shared/playwright.ts",
|
|
31
|
+
"default": "./dist/playwright.mjs"
|
|
32
|
+
},
|
|
33
|
+
"./storage": {
|
|
34
|
+
"types": "./archetypes/shared/storage.ts",
|
|
35
|
+
"default": "./dist/storage.mjs"
|
|
36
|
+
},
|
|
37
|
+
"./i18n": {
|
|
38
|
+
"types": "./archetypes/shared/i18n.ts",
|
|
39
|
+
"default": "./dist/i18n.mjs"
|
|
40
|
+
},
|
|
41
|
+
"./a11y": {
|
|
42
|
+
"types": "./archetypes/shared/a11y.ts",
|
|
43
|
+
"default": "./dist/a11y.mjs"
|
|
44
|
+
},
|
|
45
|
+
"./lint": {
|
|
46
|
+
"default": "./dist/lint.mjs"
|
|
47
|
+
},
|
|
48
|
+
"./tsconfig.json": "./archetypes/shared/tsconfig.json",
|
|
49
|
+
"./check/validation-report.v1.schema.json": "./check/validation-report.v1.schema.json",
|
|
50
|
+
"./review/verdict.v1.schema.json": "./review/verdict.v1.schema.json",
|
|
51
|
+
"./guidance/AGENTS.md": "./guidance/AGENTS.md",
|
|
52
|
+
"./guidance/plan-template.md": "./guidance/plan-template.md",
|
|
53
|
+
"./guidance/increment-template.md": "./guidance/increment-template.md",
|
|
54
|
+
"./guidance/plan-front-matter.v1.schema.json": "./guidance/plan-front-matter.v1.schema.json",
|
|
55
|
+
"./guidance/workflow-rules.md": "./guidance/workflow-rules.md",
|
|
56
|
+
"./guidance/subagent-brief.md": "./guidance/subagent-brief.md",
|
|
57
|
+
"./skills/archetypes/lernapps-explainer-design/SKILL.md": "./skills/archetypes/lernapps-explainer-design/SKILL.md",
|
|
58
|
+
"./skills/archetypes/lernapps-explainer-design/references/rules.md": "./skills/archetypes/lernapps-explainer-design/references/rules.md",
|
|
59
|
+
"./skills/archetypes/lernapps-explainer-self-assessment/SKILL.md": "./skills/archetypes/lernapps-explainer-self-assessment/SKILL.md",
|
|
60
|
+
"./skills/archetypes/lernapps-explainer/SKILL.md": "./skills/archetypes/lernapps-explainer/SKILL.md",
|
|
61
|
+
"./skills/archetypes/lernapps-explainer/references/blocks.md": "./skills/archetypes/lernapps-explainer/references/blocks.md",
|
|
62
|
+
"./skills/archetypes/lernapps-explainer/references/components.md": "./skills/archetypes/lernapps-explainer/references/components.md",
|
|
63
|
+
"./skills/archetypes/lernapps-explainer/references/exercises.md": "./skills/archetypes/lernapps-explainer/references/exercises.md",
|
|
64
|
+
"./skills/archetypes/lernapps-explainer/references/rules.md": "./skills/archetypes/lernapps-explainer/references/rules.md",
|
|
65
|
+
"./skills/archetypes/lernapps-explainer/references/runtime.md": "./skills/archetypes/lernapps-explainer/references/runtime.md",
|
|
66
|
+
"./skills/archetypes/lernapps-explainer/references/structure.md": "./skills/archetypes/lernapps-explainer/references/structure.md",
|
|
67
|
+
"./skills/archetypes/lernapps-interactive-design/SKILL.md": "./skills/archetypes/lernapps-interactive-design/SKILL.md",
|
|
68
|
+
"./skills/archetypes/lernapps-interactive/SKILL.md": "./skills/archetypes/lernapps-interactive/SKILL.md",
|
|
69
|
+
"./skills/archetypes/lernapps-interactive/references/frame.md": "./skills/archetypes/lernapps-interactive/references/frame.md",
|
|
70
|
+
"./skills/archetypes/lernapps-quiz-design/SKILL.md": "./skills/archetypes/lernapps-quiz-design/SKILL.md",
|
|
71
|
+
"./skills/archetypes/lernapps-quiz/SKILL.md": "./skills/archetypes/lernapps-quiz/SKILL.md",
|
|
72
|
+
"./skills/content/lernapps-content-questions/SKILL.md": "./skills/content/lernapps-content-questions/SKILL.md",
|
|
73
|
+
"./skills/content/lernapps-content-questions/references/question-types.md": "./skills/content/lernapps-content-questions/references/question-types.md",
|
|
74
|
+
"./skills/content/lernapps-content-research/SKILL.md": "./skills/content/lernapps-content-research/SKILL.md",
|
|
75
|
+
"./skills/content/lernapps-content-research/references/sources.md": "./skills/content/lernapps-content-research/references/sources.md",
|
|
76
|
+
"./skills/content/lernapps-exercise-patterns/SKILL.md": "./skills/content/lernapps-exercise-patterns/SKILL.md",
|
|
77
|
+
"./skills/content/lernapps-exercise-patterns/references/biology-geography.md": "./skills/content/lernapps-exercise-patterns/references/biology-geography.md",
|
|
78
|
+
"./skills/content/lernapps-exercise-patterns/references/chemistry.md": "./skills/content/lernapps-exercise-patterns/references/chemistry.md",
|
|
79
|
+
"./skills/content/lernapps-exercise-patterns/references/computer-science.md": "./skills/content/lernapps-exercise-patterns/references/computer-science.md",
|
|
80
|
+
"./skills/content/lernapps-exercise-patterns/references/figures.md": "./skills/content/lernapps-exercise-patterns/references/figures.md",
|
|
81
|
+
"./skills/content/lernapps-exercise-patterns/references/history.md": "./skills/content/lernapps-exercise-patterns/references/history.md",
|
|
82
|
+
"./skills/content/lernapps-exercise-patterns/references/mathematics.md": "./skills/content/lernapps-exercise-patterns/references/mathematics.md",
|
|
83
|
+
"./skills/content/lernapps-exercise-patterns/references/page-structures.md": "./skills/content/lernapps-exercise-patterns/references/page-structures.md",
|
|
84
|
+
"./skills/content/lernapps-exercise-patterns/references/physics.md": "./skills/content/lernapps-exercise-patterns/references/physics.md",
|
|
85
|
+
"./skills/content/lernapps-exercise-patterns/references/success.md": "./skills/content/lernapps-exercise-patterns/references/success.md",
|
|
86
|
+
"./skills/process/lernapps-app/SKILL.md": "./skills/process/lernapps-app/SKILL.md",
|
|
87
|
+
"./skills/process/lernapps-commit/SKILL.md": "./skills/process/lernapps-commit/SKILL.md",
|
|
88
|
+
"./skills/process/lernapps-content-growth/SKILL.md": "./skills/process/lernapps-content-growth/SKILL.md",
|
|
89
|
+
"./skills/process/lernapps-github/SKILL.md": "./skills/process/lernapps-github/SKILL.md",
|
|
90
|
+
"./skills/process/lernapps-listing/SKILL.md": "./skills/process/lernapps-listing/SKILL.md",
|
|
91
|
+
"./skills/process/lernapps-listing/references/keeping-updated.md": "./skills/process/lernapps-listing/references/keeping-updated.md",
|
|
92
|
+
"./skills/tooling/lernapps-cli/SKILL.md": "./skills/tooling/lernapps-cli/SKILL.md",
|
|
93
|
+
"./skills/tooling/lernapps-cli/references/platform-rules.md": "./skills/tooling/lernapps-cli/references/platform-rules.md"
|
|
94
|
+
},
|
|
95
|
+
"publishConfig": {
|
|
96
|
+
"access": "public",
|
|
97
|
+
"provenance": true
|
|
98
|
+
},
|
|
99
|
+
"scripts": {
|
|
100
|
+
"prepare": "vp pack && node scripts/dist-skills.ts && vp config --no-agent",
|
|
101
|
+
"lernapps": "node src/cli.ts",
|
|
102
|
+
"check": "npm run --silent lernapps -- check",
|
|
103
|
+
"rules": "node scripts/rules.ts list",
|
|
104
|
+
"verdict": "node scripts/verdict.ts",
|
|
105
|
+
"build": "./scripts/build.sh",
|
|
106
|
+
"check:site": "lernapps-check --site /tooling/",
|
|
107
|
+
"arc42": "arc42 --dir docs/arc42"
|
|
108
|
+
},
|
|
109
|
+
"dependencies": {
|
|
110
|
+
"@lernapps/app-templates": "0.1.0",
|
|
111
|
+
"axe-core": "4.14.0",
|
|
112
|
+
"playwright": "1.64.1",
|
|
113
|
+
"skills": "1.7.4",
|
|
114
|
+
"yaml": "2.9.1"
|
|
115
|
+
},
|
|
116
|
+
"devDependencies": {
|
|
117
|
+
"@doctc/arc42": "0.38.0",
|
|
118
|
+
"@lernapps/site": "github:lernapps/lernapps.github.io#4c677cc9949b22bcd24b168dfb00098f3ccae624",
|
|
119
|
+
"@types/node": "26.6.5",
|
|
120
|
+
"ajv": "8.20.0",
|
|
121
|
+
"vite-plus": "1.4.0"
|
|
122
|
+
},
|
|
123
|
+
"peerDependencies": {
|
|
124
|
+
"vite-plus": "1.0.0 || 1.4.0"
|
|
125
|
+
},
|
|
126
|
+
"peerDependenciesMeta": {
|
|
127
|
+
"vite-plus": {
|
|
128
|
+
"optional": true
|
|
129
|
+
}
|
|
130
|
+
},
|
|
131
|
+
"overrides": {
|
|
132
|
+
"vite": "npm:@voidzero-dev/vite-plus-core@1.4.0"
|
|
133
|
+
},
|
|
134
|
+
"engines": {
|
|
135
|
+
"node": "^22.18.0 || ^24.11.0 || >=26.0.0"
|
|
136
|
+
}
|
|
137
|
+
}
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"$id": "https://lernapps.net/tooling/schemas/verdict.v1.schema.json",
|
|
4
|
+
"title": "Review verdict of lernapps, version 1",
|
|
5
|
+
"description": "What the review agent judged on an app that asks to be listed, as YAML (verdict.yaml): the reviewed commit, the rules judged, and every finding with its rule id or a proposed new rule. Written by the agent that follows review/prompt.md of @lernapps/tooling, checked with `npm run verdict -- <file>`. A breaking change gets a new version of this schema.",
|
|
6
|
+
"type": "object",
|
|
7
|
+
"properties": {
|
|
8
|
+
"reviewed": {
|
|
9
|
+
"description": "What was reviewed: the commit of the app's repo, and where the app and its listing are.",
|
|
10
|
+
"type": "object",
|
|
11
|
+
"properties": {
|
|
12
|
+
"commit": {
|
|
13
|
+
"type": "string",
|
|
14
|
+
"pattern": "^[0-9a-f]{40}$",
|
|
15
|
+
"description": "The full id of the reviewed commit"
|
|
16
|
+
},
|
|
17
|
+
"repo": { "type": "string", "minLength": 1, "description": "The app's repo: a URL, or a directory" },
|
|
18
|
+
"url": {
|
|
19
|
+
"type": "string",
|
|
20
|
+
"minLength": 1,
|
|
21
|
+
"description": "Where the app is deployed, if it was reviewed there"
|
|
22
|
+
},
|
|
23
|
+
"listing": { "type": "string", "minLength": 1, "description": "The listing pull request, if there is one" }
|
|
24
|
+
},
|
|
25
|
+
"required": ["commit"],
|
|
26
|
+
"additionalProperties": false
|
|
27
|
+
},
|
|
28
|
+
"report": {
|
|
29
|
+
"description": "The validation report of `lernapps check` the review started from: its file, or the URL of the listing comment that holds it.",
|
|
30
|
+
"type": "string",
|
|
31
|
+
"minLength": 1
|
|
32
|
+
},
|
|
33
|
+
"archetype": {
|
|
34
|
+
"description": "The archetype from the plan's front matter; null when the app has no plan file. It selects review/archetypes/<archetype>.md.",
|
|
35
|
+
"enum": ["explainer", "interactive", "quiz", null]
|
|
36
|
+
},
|
|
37
|
+
"outcome": {
|
|
38
|
+
"description": "fail when a finding has severity error, else pass. The owner decides on the listing.",
|
|
39
|
+
"enum": ["pass", "fail"]
|
|
40
|
+
},
|
|
41
|
+
"rules": {
|
|
42
|
+
"description": "The ids of the rubric items judged: those of review/rubric.md and of the archetype's file.",
|
|
43
|
+
"type": "array",
|
|
44
|
+
"items": { "$ref": "#/$defs/id" },
|
|
45
|
+
"uniqueItems": true,
|
|
46
|
+
"minItems": 1
|
|
47
|
+
},
|
|
48
|
+
"findings": {
|
|
49
|
+
"description": "Every finding, sorted by severity and rule id. Each names a rule of the catalog (rule, link), or proposes a new rule (proposal).",
|
|
50
|
+
"type": "array",
|
|
51
|
+
"items": {
|
|
52
|
+
"type": "object",
|
|
53
|
+
"properties": {
|
|
54
|
+
"rule": { "$ref": "#/$defs/id", "description": "The id of the broken rule, as `npm run rules` lists it" },
|
|
55
|
+
"link": {
|
|
56
|
+
"type": "string",
|
|
57
|
+
"pattern": "^https://lernapps\\.net/tooling/rules/#[a-z][a-z0-9]*(-[a-z0-9]+)*$",
|
|
58
|
+
"description": "The rule's entry on the rule page"
|
|
59
|
+
},
|
|
60
|
+
"proposal": {
|
|
61
|
+
"description": "A new rule, when no rule of the catalog covers the finding.",
|
|
62
|
+
"type": "object",
|
|
63
|
+
"properties": {
|
|
64
|
+
"id": { "$ref": "#/$defs/id", "description": "A stable name that is no rule id yet" },
|
|
65
|
+
"title": { "type": "string", "minLength": 1 },
|
|
66
|
+
"text": { "type": "string", "minLength": 1, "description": "The rule, as a skill would tell it" },
|
|
67
|
+
"scope": {
|
|
68
|
+
"type": "array",
|
|
69
|
+
"items": { "type": "string", "pattern": "^(listing|archetype:[a-z][a-z0-9-]*)$" },
|
|
70
|
+
"minItems": 1,
|
|
71
|
+
"uniqueItems": true
|
|
72
|
+
},
|
|
73
|
+
"severity": { "enum": ["error", "warning", "hint"] },
|
|
74
|
+
"actsIn": { "$ref": "#/$defs/artifacts", "description": "Where the rule would act" }
|
|
75
|
+
},
|
|
76
|
+
"required": ["id", "actsIn"],
|
|
77
|
+
"additionalProperties": false
|
|
78
|
+
},
|
|
79
|
+
"severity": { "enum": ["error", "warning", "hint"] },
|
|
80
|
+
"where": {
|
|
81
|
+
"type": "string",
|
|
82
|
+
"minLength": 1,
|
|
83
|
+
"description": "File of the bundle or repo and element, page, or the plan's section"
|
|
84
|
+
},
|
|
85
|
+
"found": { "type": "string", "minLength": 1, "description": "What was found" },
|
|
86
|
+
"fix": { "type": "string", "minLength": 1, "description": "How to fix it" },
|
|
87
|
+
"moveTo": {
|
|
88
|
+
"$ref": "#/$defs/artifacts",
|
|
89
|
+
"description": "For a rule of the catalog: where it could be decided or prevented next time, so that it moves down from the review"
|
|
90
|
+
}
|
|
91
|
+
},
|
|
92
|
+
"required": ["severity", "where", "found", "fix"],
|
|
93
|
+
"additionalProperties": false,
|
|
94
|
+
"dependentRequired": { "rule": ["link"], "link": ["rule"] },
|
|
95
|
+
"oneOf": [
|
|
96
|
+
{ "properties": { "rule": true }, "required": ["rule"] },
|
|
97
|
+
{ "properties": { "proposal": true }, "required": ["proposal"] }
|
|
98
|
+
]
|
|
99
|
+
}
|
|
100
|
+
},
|
|
101
|
+
"at": { "type": "string", "description": "ISO 8601 date of the review" }
|
|
102
|
+
},
|
|
103
|
+
"required": ["reviewed", "report", "archetype", "outcome", "rules", "findings"],
|
|
104
|
+
"additionalProperties": false,
|
|
105
|
+
"if": { "type": "object", "properties": { "outcome": { "const": "pass" } }, "required": ["outcome"] },
|
|
106
|
+
"then": {
|
|
107
|
+
"properties": {
|
|
108
|
+
"findings": {
|
|
109
|
+
"type": "array",
|
|
110
|
+
"not": {
|
|
111
|
+
"type": "array",
|
|
112
|
+
"contains": { "type": "object", "properties": { "severity": { "const": "error" } }, "required": ["severity"] }
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
},
|
|
117
|
+
"else": {
|
|
118
|
+
"properties": {
|
|
119
|
+
"findings": {
|
|
120
|
+
"type": "array",
|
|
121
|
+
"contains": { "type": "object", "properties": { "severity": { "const": "error" } }, "required": ["severity"] }
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
},
|
|
125
|
+
"$defs": {
|
|
126
|
+
"id": { "type": "string", "pattern": "^[a-z][a-z0-9]*(-[a-z0-9]+)*$" },
|
|
127
|
+
"artifacts": {
|
|
128
|
+
"type": "array",
|
|
129
|
+
"items": { "enum": ["skill", "lint rule", "check", "rubric item"] },
|
|
130
|
+
"minItems": 1,
|
|
131
|
+
"uniqueItems": true
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: lernapps-explainer
|
|
3
|
+
description: Use in Code of a lernapps.net explainer whenever you write or change its content in src/ - a chapter, page, block, figure, formula, exercise, data file, language or component - or a build or runtime test of the explainer fails. What you write, the notation of structure, blocks and exercises, components, the runtime, and the explainer's technical rules.
|
|
4
|
+
license: MIT
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Explainer
|
|
8
|
+
|
|
9
|
+
- You write YAML in `src/`; a Vue component only where the plan names one. Rendering, navigation, formulas, figures,
|
|
10
|
+
exercises, accessibility, languages and the no-JS page come from the runtime `@lernapps/app-templates`.
|
|
11
|
+
- `create` writes the example chapter "Prozent": replace it with the planned content.
|
|
12
|
+
- Schema: `node_modules/@lernapps/app-templates/runtime/explainer/explainer.v1.schema.json`. Start every YAML file with
|
|
13
|
+
`# yaml-language-server: $schema=../../node_modules/@lernapps/app-templates/runtime/explainer/explainer.v1.schema.json`
|
|
14
|
+
(one `../` less in `src/app.yaml`).
|
|
15
|
+
- Questions of the quiz's types: also skill `lernapps-content-questions`.
|
|
16
|
+
|
|
17
|
+
## Read when
|
|
18
|
+
|
|
19
|
+
- [references/structure.md](references/structure.md): before you add or move a chapter or page.
|
|
20
|
+
- [references/blocks.md](references/blocks.md): before you write a page.
|
|
21
|
+
- [references/exercises.md](references/exercises.md): before you write an exercise, above all a `generated` one.
|
|
22
|
+
- [references/components.md](references/components.md): only when the plan names a component.
|
|
23
|
+
- [references/runtime.md](references/runtime.md): when the build or a runtime test fails, and before you set up the
|
|
24
|
+
tutor.
|
|
25
|
+
- [references/rules.md](references/rules.md): when a check names an `explainer-*` or `vue-component-*` rule.
|
|
26
|
+
|
|
27
|
+
## Gotchas
|
|
28
|
+
|
|
29
|
+
- A build problem is one line `file:line: place (language): what to do`. Fix where it points.
|
|
30
|
+
- Headings are `section` blocks, never Markdown headings.
|
|
31
|
+
- A new chapter: a folder with `chapter.yaml`, listed in `src/app.yaml`, each page listed in `chapter.yaml`.
|
|
32
|
+
- `selfAssessment` in a `chapter.yaml`: written in Commit, skill
|
|
33
|
+
`lernapps-explainer-self-assessment`. An exercise it names renamed or moved → change the reference too.
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
# Blocks
|
|
2
|
+
|
|
3
|
+
- A page = list of blocks from a closed set. Every text field: text or language map.
|
|
4
|
+
- Prose: restricted Markdown: paragraphs, `**bold**`, `*italics*`, lists, links, line breaks, `$…$`.
|
|
5
|
+
- No headings (use `section`), HTML, images, code, quotes, tables in Markdown: each has its block.
|
|
6
|
+
- App-internal links start with `/` (`/prozent/grundwert/#regel`); the build checks them.
|
|
7
|
+
|
|
8
|
+
| `type` | Fields | Shows |
|
|
9
|
+
|---|---|---|
|
|
10
|
+
| `section` | `id`, `title`, `blocks` | heading and blocks |
|
|
11
|
+
| `text` | `text` | prose |
|
|
12
|
+
| `formula` | `formula` (LaTeX subset, no `$`) | formula on its own line, MathML |
|
|
13
|
+
| `figure` | `draw` (`hundred-grid` with `filled`, `bar` with `part`, `whole`) or `image` (`images/<name>.svg`, `source`, `licence`); `alt`, `caption` | inline SVG or bundled image |
|
|
14
|
+
| `labelled-figure` | `image`, `alt`, `caption`, `labels` (`x`, `y` in %, `text`, optional `marker` of 1 to 3 characters, e.g. the material's `A` to `E`, else the number), `legend` (default `true`; `false` shows only the markers, then `alt` names every marker), `source`, `licence` | image with markers (letters or numbers) and a legend of the texts |
|
|
15
|
+
| `table` | `caption`, `header`, `rowHeaders`, `rows` | table with caption |
|
|
16
|
+
| `timeline` | `events` (`date`, `title`, `label`, `text`) or `data` | dated events, earliest first |
|
|
17
|
+
| `definition` | `term`, `explanation` | term in place, in the glossary |
|
|
18
|
+
| `quote` | `text`, `origin` (`author`, `work`, `year`), `source`, `licence` | quotation with origin |
|
|
19
|
+
| `code` | `language` (bash, c, css, html, java, javascript, json, plaintext, python, sql, typescript), `code`, `caption` | highlighted code |
|
|
20
|
+
| `example`, `note` | `title`, `blocks` | box: worked example, hint |
|
|
21
|
+
| `video` | `youtube` (the 11-character id, not the address), `title`, `channel` (both as on YouTube, plain strings) | YouTube video behind two clicks: link, channel, privacy note; player only after a click |
|
|
22
|
+
| exercises | quiz's six types, `generated` | `exercises.md` |
|
|
23
|
+
| `vue-component` | `component`, `props`, `nojs` | `components.md` |
|
|
24
|
+
|
|
25
|
+
```yaml
|
|
26
|
+
- type: section
|
|
27
|
+
id: regel
|
|
28
|
+
title: Regel
|
|
29
|
+
blocks:
|
|
30
|
+
- type: formula
|
|
31
|
+
formula: 'W = \frac{G \cdot p}{100}'
|
|
32
|
+
- type: definition
|
|
33
|
+
term: Prozentwert
|
|
34
|
+
explanation: Der Prozentwert $W$ ist der Anteil am Ganzen, in der Einheit des Grundwerts.
|
|
35
|
+
- type: figure
|
|
36
|
+
draw: { kind: hundred-grid, filled: 12 }
|
|
37
|
+
alt: Ein Hunderterfeld mit 100 Kästchen, 12 davon sind gefärbt.
|
|
38
|
+
caption: Die 12 gefärbten Kästchen sind 12 % des Ganzen.
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
- Images in `src/images/`: found under a free licence (e.g. Wikimedia Commons) or your own, generated or drawn as
|
|
42
|
+
SVG. Always `source` (URL or citation, e.g. `eigene Zeichnung`, `KI-generiert`) and `licence` (SPDX id, e.g.
|
|
43
|
+
`CC-BY-SA-4.0`, or `gemeinfrei`). Several languages: a citation is a language map; a URL one string.
|
|
44
|
+
|
|
45
|
+
## Videos
|
|
46
|
+
|
|
47
|
+
- YouTube only, explainer only (not in a quiz question's `material`). No thumbnail, no video files in the bundle.
|
|
48
|
+
- `title`, `channel`: as YouTube names them, in the video's language; the page's language does not change them.
|
|
49
|
+
- What the learner should notice in the video: a `text` block before it; the block has no note field.
|
|
50
|
+
- An app with a video loads from another server after a click: its catalog entry says `fitness.thirdParty: on-click`,
|
|
51
|
+
and with the choice "Videos immer direkt laden" also `fitness.storage: device`.
|
|
52
|
+
|
|
53
|
+
```yaml
|
|
54
|
+
- type: video
|
|
55
|
+
youtube: rkLmLaFHodo
|
|
56
|
+
title: einstufiger Zufallsversuch | Wahrscheinlichkeitsrechnung - einfach erklärt
|
|
57
|
+
channel: Lehrerschmidt
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
## Formulas
|
|
61
|
+
|
|
62
|
+
- LaTeX subset → MathML; `formula` block or inline `$…$`.
|
|
63
|
+
- Commands: `\frac`, `\cdot`, `\times`, `\div`, `\%`, `\text`, `^`, `_`, `\sqrt`, `\approx`, `\pm`, `\le`, `\ge`,
|
|
64
|
+
`\neq`, `\,`, parentheses, braces (`\{`, `\}`).
|
|
65
|
+
- Chemistry (mhchem): `\ce{2H2 + O2 -> 2H2O}`, `\ce{CO2 + H2O <=> H2CO3}`, units `\pu{123 kJ/mol}`.
|
|
66
|
+
- Other command or parse error → build fails with the place.
|
|
67
|
+
- YAML formulas in single quotes. Same in every language unless `\text{…}` differs (then a map).
|
|
68
|
+
|
|
69
|
+
## Languages
|
|
70
|
+
|
|
71
|
+
- `languages` in `src/app.yaml`, German first: `[de]` or `[de, en]` (runtime texts: de, en).
|
|
72
|
+
- A string = German, enough only while German is the only language. Else every text a map:
|
|
73
|
+
`title: { de: Die drei Stände, en: The three estates }`.
|
|
74
|
+
- Build fails on a missing translation (`the translation is missing; write the text as a map: { de: …, en: … }`)
|
|
75
|
+
and an undeclared language.
|
|
76
|
+
- Same in every language: ids, addresses, numbers, formulas, drawn figures.
|
|
77
|
+
- Trees: German at root, others under `/<language>/`; `lang`, `hreflang`, plain-link switch.
|
|
78
|
+
|
|
79
|
+
## Data files
|
|
80
|
+
|
|
81
|
+
- `src/data/<name>.yaml`: list of records, same lowercase fields; values number, boolean, text (map if several
|
|
82
|
+
languages).
|
|
83
|
+
- Used by `timeline` (`data: ereignisse`) and `generated` (`from: { data: elemente }`). Each fact once, there.
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
# Components
|
|
2
|
+
|
|
3
|
+
- Only for what no block or `generated` can express (label a figure, drag into place). Blocks are readable without
|
|
4
|
+
JS, accessible, tested by construction.
|
|
5
|
+
|
|
6
|
+
## Files (`src/components/`)
|
|
7
|
+
|
|
8
|
+
- Only `<Name>.vue`, `<Name>.ts`, `<Name>.test.ts`; PascalCase; no subfolders, no other files.
|
|
9
|
+
- Every `.vue`: `<script setup lang="ts">` and its `<Name>.test.ts` (run by `lernapps check`; `.vue` type-checked
|
|
10
|
+
with `vue-tsc`).
|
|
11
|
+
- Exercise: logic in `<Name>.ts`. Shared helpers: a `<Name>.ts` here, imported with extension (`./Circuit.ts`).
|
|
12
|
+
|
|
13
|
+
## Block and props
|
|
14
|
+
|
|
15
|
+
- `type: vue-component`, `component: <Name>`, `props`, `nojs` (not for an exercise).
|
|
16
|
+
- `defineProps<{ … }>()`: `string`, `number`, `boolean`, literals, unions, arrays, object types of them; declared in
|
|
17
|
+
the component or a local type alias. No `null`, no `undefined` union (make it optional), no imported alias.
|
|
18
|
+
- The build checks each block's `props`. A `string` prop is a learner text: a map with several languages.
|
|
19
|
+
- A list prop may be `{ data: <name> }`: the records of `src/data/<name>.yaml`, cut to the fields the item type declares
|
|
20
|
+
(`events: { title: string }[]`). Each record needs them; a missing file or field fails the build. One data file,
|
|
21
|
+
many blocks: never copy records into `props`. A field of a record may be a list or a record of values
|
|
22
|
+
(`compounds: [{ formula, text }]`) for this; exercises and timelines use the flat fields.
|
|
23
|
+
|
|
24
|
+
## `nojs`
|
|
25
|
+
|
|
26
|
+
- Restricted Markdown: what it shows plus its content (diagram described, or task with solution); carries the
|
|
27
|
+
meaning alone.
|
|
28
|
+
- Replaced once the component runs; `defineOptions({ nojs: "caption" })` keeps it folded as a description.
|
|
29
|
+
- Never in an exercise component (it would give the solution; build fails). Its text comes from `describe` for task 1.
|
|
30
|
+
|
|
31
|
+
## Exercise contract
|
|
32
|
+
|
|
33
|
+
- Emits `answer` → exercise. It is only the input: emit the learner's answer on every change (`undefined` while
|
|
34
|
+
none). No own "Prüfen", no own `<form>`, no call of `check`: the explainer draws "Prüfen" under it (Enter in a field
|
|
35
|
+
checks), calls `check`, `reason` and `show`, shows the feedback. Own controls ("Zurücksetzen", tools) stay, as
|
|
36
|
+
`type="button"`. Optional prop `checked: boolean`: true once the current answer is checked.
|
|
37
|
+
- `answered` is gone: the build fails on it.
|
|
38
|
+
- Does not count as the page's required exercise: the page also needs a question or a `generated` exercise.
|
|
39
|
+
- `<Name>.ts`, pure: `generate(seed, props)`, `check(task, answer)` (boolean), `solution(task)`, `wrong(task)`,
|
|
40
|
+
`reason(task, answer, props, lang)` (the app's message for a wrong answer from the props' texts, `undefined` for
|
|
41
|
+
none; what does not fit, never the solution). A component shows nothing after "Prüfen" that gives the solution away. Check the asked property, not equality with `solution`.
|
|
42
|
+
- Build tests 50 fixed seeds per block: same task per seed, solution passes, wrong fails, nothing throws.
|
|
43
|
+
- `about.mistakes` from the same wrong answers as `examples.rejected`; `describe` states the task exactly as shown
|
|
44
|
+
for that task number (no-JS text).
|
|
45
|
+
- For the tutor: `template(props, lang)` (task with `{name}`), `params(task, props, lang)` (inputs by name),
|
|
46
|
+
`mistakes(task, props)` (per `about.mistakes` its one fixed answer, else `undefined`: never an invented example),
|
|
47
|
+
`show(answer, task, props, lang)` (answer as text). An input of `params` that is no placeholder shows as
|
|
48
|
+
"(verdeckt)": the tutor knows it, the child does not. One `template` per block: prefer one kind of task per block;
|
|
49
|
+
else give the sentence that changes as an input (`{Aufgabe}`). `about.answer` names what the child does and "Prüfen". The build checks them against `check` for every number; rule `vue-component-tutor-table`. Model:
|
|
50
|
+
`ProzentFeld` of the template.
|
|
51
|
+
- More: rule `vue-component-exercise-contract`.
|
|
52
|
+
|
|
53
|
+
## Rendering
|
|
54
|
+
|
|
55
|
+
- Server-rendered into the page, hidden until hydrated. First render identical on server and browser:
|
|
56
|
+
`generate(1, props)`; randomness only in `onMounted`.
|
|
57
|
+
- No headings in its markup (also hidden parts). Texts via props, not strings in code.
|
|
58
|
+
|
|
59
|
+
## Limits (build and lint check, same ids)
|
|
60
|
+
|
|
61
|
+
- No network: no `fetch`, XMLHttpRequest, WebSocket, EventSource, `sendBeacon`, URL imports.
|
|
62
|
+
- No `eval`, `new Function`, `innerHTML`, `v-html`.
|
|
63
|
+
- Imports: `vue`, `@lernapps/app-templates`, `@lernapps/tooling/storage`, `i18n`, `a11y`, own folder.
|
|
64
|
+
- Browser globals only in `onMounted` or later unmount hooks.
|
|
65
|
+
- Storage only via `createStorage`.
|
|
66
|
+
- Max 20 kB gzip own code (Vue not counted); more → split, or ask for a block.
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
# Exercises
|
|
2
|
+
|
|
3
|
+
- Every page at least one, best in a last section "Übung".
|
|
4
|
+
- What counts as right and each wrong answer's feedback: from "Content method" of the plan.
|
|
5
|
+
- Wrong answer gets the most specific reason: chosen option's `info`, a matching `mistakes` entry (`generated`;
|
|
6
|
+
`number` with `answer`, `message`), the `hint`, else "look again". Write `info` per wrong option, 2–3 mistakes,
|
|
7
|
+
a `hint`.
|
|
8
|
+
|
|
9
|
+
## Quiz types
|
|
10
|
+
|
|
11
|
+
- `single-choice`, `multiple-choice`, `true-false`, `number`, `ordering`, `matching`: blocks with the quiz fields
|
|
12
|
+
(`id`, `type`, `text`, `explanation`, `links`, type fields). Fields and how to write them: skill
|
|
13
|
+
`lernapps-content-questions`.
|
|
14
|
+
|
|
15
|
+
```yaml
|
|
16
|
+
- type: ordering
|
|
17
|
+
id: staende-ordnen
|
|
18
|
+
text: Bringe die Stände in ihre Rangfolge, den ersten Stand zuerst.
|
|
19
|
+
items: [die Geistlichen, der Adel, alle anderen]
|
|
20
|
+
explanation: Der erste Stand waren die Geistlichen, der zweite der Adel, der dritte alle anderen.
|
|
21
|
+
links:
|
|
22
|
+
- { title: Ständegesellschaft (Wikipedia), url: "https://de.wikipedia.org/wiki/Ständegesellschaft" }
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## `generated`
|
|
26
|
+
|
|
27
|
+
- New task per seed.
|
|
28
|
+
- `given`: `int: [min, max]` + `step`; `decimal: [min, max]` + `step`; `oneOf: [...]`; `pick: <n>` `from:` list or
|
|
29
|
+
`{ data: <name> }` (`distinct: true`; `pick: 1` = one record).
|
|
30
|
+
- `where`: boolean expressions; failing draws are redrawn.
|
|
31
|
+
- `task`: `text`. A figure that changes with the task: an exercise component (`components.md`).
|
|
32
|
+
- `answer`: `value` (expression) and `compare`: `number` or
|
|
33
|
+
`{ number: { format: integer|decimal|money|percent, decimals, tolerance, unit } }`, `{ choice: <options> }`,
|
|
34
|
+
`order`, `pairs` or `{ pairs: { left, right } }`.
|
|
35
|
+
- `mistakes`: `value` + `message` each. `hint`, `steps`: help, the way to the solution.
|
|
36
|
+
- Expressions (no eval): exact fractions, `+ - * /`, parentheses, `== != < <= > >=`, `and`, `or`, `not`, strings,
|
|
37
|
+
fields (`e.year`), index (`xs[0]`), lists, `round(x, n)`, `decimals`, `map(xs, field)`,
|
|
38
|
+
`sortBy(xs, field)`, `reverse`.
|
|
39
|
+
|
|
40
|
+
### Placeholders
|
|
41
|
+
|
|
42
|
+
- Always two braces around a declared name or field: `{{G}}`, `{{e.name}}`, `{{answer}}` in steps. Prose and
|
|
43
|
+
formulas alike.
|
|
44
|
+
- One brace is never a placeholder: `G`, `{G}`, `\frac{W}{G}` are letters. Group around a value stays:
|
|
45
|
+
`$\frac{{{W}}}{{{G}}} \cdot 100 = {{answer}}$`.
|
|
46
|
+
- Build fails on: undeclared name; `{G}` or `{{ G }}` in prose; a `given` variable nothing uses (`where` does not
|
|
47
|
+
count); a placeholder left after filling.
|
|
48
|
+
|
|
49
|
+
```yaml
|
|
50
|
+
- type: generated
|
|
51
|
+
id: prozentwert-ueben
|
|
52
|
+
given:
|
|
53
|
+
G: { int: [150, 1500], step: 5 }
|
|
54
|
+
p: { oneOf: [5, 10, 12, 15, 20, 25, 30, 40] }
|
|
55
|
+
where: ["decimals(G * p / 100) <= 2"]
|
|
56
|
+
task:
|
|
57
|
+
text: "Ein Fahrrad kostet {{G}} €. Im Angebot gibt es {{p}} % Rabatt. Wie viel Euro sparst du?"
|
|
58
|
+
answer: { value: "G * p / 100", compare: { number: { format: money } } }
|
|
59
|
+
mistakes:
|
|
60
|
+
- { value: "G * p", message: "Du hast vergessen, durch 100 zu teilen: {{p}} % sind {{p}} Hundertstel." }
|
|
61
|
+
- { value: "G - G * p / 100", message: "Das ist der neue Preis, nicht der Rabatt." }
|
|
62
|
+
hint: "Rechne erst 1 % aus: {{G}} € geteilt durch 100."
|
|
63
|
+
steps:
|
|
64
|
+
- '$W = \frac{G \cdot p}{100}$'
|
|
65
|
+
- '$W = \frac{{{G}} \cdot {{p}}}{100} = {{answer}}$, also {{answer}} € Rabatt.'
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
- Data-file example: `given: { events: { pick: 3, distinct: true, from: { data: ereignisse } } }`,
|
|
69
|
+
`where: ["events[0].date != events[1].date"]`, `answer: { value: "map(sortBy(events, date), title)", compare: order }`,
|
|
70
|
+
mistake `map(reverse(sortBy(events, date)), title)`.
|
|
71
|
+
|
|
72
|
+
### Property test (200 fixed seeds; first problem with its seed)
|
|
73
|
+
|
|
74
|
+
- `where` holds in at least 1 of 10 draws.
|
|
75
|
+
- Answer defined, in its format, passes its own comparator.
|
|
76
|
+
- No mistake equals the answer (`for seed 17 the mistake equals the answer (20); …`); each mistake is enterable.
|
|
77
|
+
- Same task for the same seed; no placeholder left.
|
|
78
|
+
- Mistake equal to the answer for some values = range too wide (`G - G * p / 100` = `G * p / 100` at 50 %: so `p`
|
|
79
|
+
≤ 40).
|
|
80
|
+
|
|
81
|
+
## `open-task`
|
|
82
|
+
|
|
83
|
+
- For what learners answer in their own words: describe, explain, compare, argue; a short text in German or a foreign
|
|
84
|
+
language. Not for what a quiz type can check.
|
|
85
|
+
- The learner answers first, typed or on paper with a photo (`answer: [text, photo]`, both by default), then compares:
|
|
86
|
+
the points show as tick boxes, each point of the content with its link "Nachlesen", and the model answer opens.
|
|
87
|
+
The ticks give right, partly right or wrong for the self-assessment and the tutor.
|
|
88
|
+
- `task`: what to write and how long ("in zwei bis vier Sätzen").
|
|
89
|
+
- `points`: 2 to 5, each one checkable sentence a good answer has, in its order. A point of the content has `source`,
|
|
90
|
+
the section of the app where its content is, `/<chapter>/<page>/#<section>` (the build checks it like a link); a
|
|
91
|
+
point of form (whole sentences, a thread, the words to use, the tense) has `kind: form` and no source. Only what the
|
|
92
|
+
material teaches.
|
|
93
|
+
- `solution`: the model answer, in the words a learner of the class would write, with every point. `hint` optional.
|
|
94
|
+
|
|
95
|
+
```yaml
|
|
96
|
+
- type: open-task
|
|
97
|
+
id: tauschen-erklaeren
|
|
98
|
+
task: Erkläre in zwei bis vier Sätzen, warum 20 % von 50 € genauso viel sind wie 50 % von 20 €.
|
|
99
|
+
points:
|
|
100
|
+
- text: Der Prozentwert ist Grundwert mal Prozentsatz, geteilt durch 100.
|
|
101
|
+
source: /prozent/prozentwert/#regel
|
|
102
|
+
- text: Beim Malnehmen darf man die Zahlen tauschen, darum ist das Ergebnis gleich.
|
|
103
|
+
source: /prozent/prozentwert/#regel
|
|
104
|
+
- text: Ganze Sätze mit einer Begründung (weil …, darum …).
|
|
105
|
+
kind: form
|
|
106
|
+
solution: |
|
|
107
|
+
Den Prozentwert rechnet man mit $W = \frac{G \cdot p}{100}$. Für 20 % von 50 € ist das
|
|
108
|
+
$\frac{50 \cdot 20}{100} = 10$, für 50 % von 20 € ebenso 10. Weil man beim Malnehmen tauschen darf, kommt beide
|
|
109
|
+
Male 10 € heraus.
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
## Examples
|
|
113
|
+
|
|
114
|
+
- Any exercise: `examples: { accepted, rejected }`, checked by the build, never shown. `generated`: expressions of
|
|
115
|
+
`given` and `answer`, checked per seed (e.g. `reverse(answer)`).
|