Projects have their own name, description and goal; the template is shown only as origin
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 52s
Work Records / validate (push) Successful in 12s

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

Assistant: claude-code
Assistant-Model: sonnet
Assistant-Process: 295952@bnt-lap001
Assistant-Session: e93f64ad-516c-46eb-9666-aad8d300c477
This commit is contained in:
tegwick 2026-09-29 14:12:21 +02:00
parent 334e0d3eb6
commit f8ce4717a8
8 changed files with 125 additions and 24 deletions

View file

@ -68,8 +68,10 @@ def index_sections(app) -> str:
continue
errs = J.validate(reg, d)
st = pill("invalid") if errs else pill(J.state(reg, d)["state"])
prj.append(f"<li><a href='/project/{e(pid)}'>{e(d.data['title'])}</a> {pill(d.data['environment'])} {st} "
f"<span class=note>{e(d.data.get('goal', ''))}</span></li>")
origin = (f" <span class=note>from template <a href='/template/{e(d.data['template'])}'>{e(d.data['template'])}</a></span>"
if d.data.get("template") else "")
desc = f"<br><span class=note>{e(d.data['description'])}</span>" if d.data.get("description") else ""
prj.append(f"<li><a href='/project/{e(pid)}'>{e(d.data['title'])}</a> {pill(d.data['environment'])} {st}{origin}{desc}</li>")
tpl = [f"<li><a href='/template/{e(tid)}'>{e(d.data['title'])}</a> "
f"<span class=note>{e(d.data.get('goal', ''))}</span></li>" if d.ok else f"<li class=bad>{e(tid)}: invalid</li>"
for tid, d in reg.templates.items()]
@ -113,9 +115,11 @@ def project_page(app, pid: str, msg: str = "") -> bytes | None:
return page(d.get("title", pid), "".join(out))
st = J.state(reg, doc)
out.append(f"<p>{pill(d['environment'])} {pill(st['state'])}"
f"{' <span class=note>from template ' + e(d['template']) + '</span>' if d.get('template') else ''}</p>")
f"{' <span class=note>from template <a href=' + chr(39) + '/template/' + e(d['template']) + chr(39) + '>' + e(d['template']) + '</a></span>' if d.get('template') else ''}</p>")
if d.get("description"):
out.append(f"<p>{e(d['description'])}</p>")
if d.get("goal"):
out.append(f"<p>{e(d['goal'])}</p>")
out.append(f"<p><b>Goal:</b> {e(d['goal'])}</p>")
# what is still needed
needs: dict[str, list[str]] = {}
for n in st["needs"]:
@ -204,6 +208,12 @@ def template_page(app, tid: str, msg: str = "") -> bytes | None:
envs = "".join(f"<option>{e(x)}</option>" for x in R.ENVIRONMENTS)
out.append("<h2>Start a project from this template</h2>"
f"<form method=post action='/template/{e(tid)}/new'>{app.hidden()}"
"<p><label>Project name<br><input name=title class=grow style='width:100%' minlength=3 required "
"placeholder='what you call this project'></label></p>"
"<p><label>Description<br><textarea name=description rows=2 style='width:100%' "
"placeholder='what this project is (optional)'></textarea></label></p>"
"<p><label>Goal<br><input name=goal class=grow style='width:100%' "
"placeholder='what should be true when it is done (optional)'></label></p>"
"<p><label>Project id<br><input name=project_id pattern='[a-z0-9]+([.-][a-z0-9]+)*' required "
"placeholder='lower-case, digits, dots, hyphens'></label></p>"
f"<p><label>Environment<br><select name=environment>{envs}</select></label></p>"
@ -238,7 +248,10 @@ def create_from_template(app, tid: str, form: dict) -> str:
if SECRET_RE.search(v):
raise ValueError(f"{name}: value looks like a secret; attributes are never secret")
pid = form.get("project_id", "").strip()
J.instantiate(reg, tid, pid, form.get("environment", ""), values, root=doc.path.parent.parent)
if len(form.get("title", "").strip()) < 3:
raise ValueError("give the project a name of at least 3 characters")
J.instantiate(reg, tid, pid, form.get("environment", ""), values, title=form.get("title", ""),
root=doc.path.parent.parent, description=form.get("description", ""), goal=form.get("goal", ""))
reg = app.registry()
created = reg.projects[pid]
errs = J.validate(reg, created)