feat(lessons): the web editor records which rule a lesson is an instance of (milestone 440, #4658)
CI & Build / Python lint (push) Successful in 3s
CI & Build / Plugin hooks (push) Successful in 13s
CI & Build / TypeScript typecheck (push) Successful in 55s
CI & Build / integration (push) Successful in 59s
CI & Build / Python tests (push) Successful in 1m55s
CI & Build / Build & push image (push) Successful in 37s
CI & Build / Python lint (push) Successful in 3s
CI & Build / Plugin hooks (push) Successful in 13s
CI & Build / TypeScript typecheck (push) Successful in 55s
CI & Build / integration (push) Successful in 59s
CI & Build / Python tests (push) Successful in 1m55s
CI & Build / Build & push image (push) Successful in 37s
The lesson editor now asks the question the create tool asks, while the writer still has the situation in mind. It offers three answers: - an instance of a rule, ticked from the rules the lesson resembles; - "No rule fits", with a required reason; - leave it open. The rules are fetched before the save from a new GET /api/lessons/rule-candidates. It runs the same rule_candidates search the create door replies with, and passes "search unavailable" through as null, apart from "nothing resembles it". An edit sends an answer only when it changed. Re-sending the same rules would re-stamp their judgments. Worse, a shared editor who cannot see the owner's rule would reject it by sending a list without it. Leaving a linked lesson open sends rule_ids=[], which is what clears the links. "Leave it open" is not offered once "no rule fits" is on file: the server has no way to take that answer back except by naming a rule. When "no rule fits" completes a convergence group, the editor says so in a toast, since the page it goes to does not recompute it. Guards check that the payload uses the names both routes read, that the three answers are offered, that the candidates route sits above the id route and keeps null apart from [], and that the editor reuses .rule-chip. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -119,6 +119,34 @@ async def lessons_taught_by_route(record_id: int):
|
||||
})
|
||||
|
||||
|
||||
@lessons_bp.route("/rule-candidates", methods=["GET"])
|
||||
@login_required
|
||||
async def lesson_rule_candidates_route():
|
||||
"""The rules a lesson being written resembles, BEFORE it is saved
|
||||
(milestone 440, #4658).
|
||||
|
||||
The create door offers the same list in its reply when neither answer was
|
||||
given. A form needs it earlier, so the writer can name the rule in the
|
||||
same save while the situation is still in mind. Query: `what`,
|
||||
`when_to_apply`, optional `project_id`. Above the `/<int:lesson_id>`
|
||||
routes for the reason `taught-by` is.
|
||||
|
||||
`candidates` is null when the search could not run. That means
|
||||
"unavailable", which is different from "nothing resembles it" (an empty
|
||||
list).
|
||||
"""
|
||||
uid = get_current_user_id()
|
||||
what = (request.args.get("what") or "").strip()
|
||||
when_to_apply = (request.args.get("when_to_apply") or "").strip()
|
||||
if not what and not when_to_apply:
|
||||
return jsonify({"error": "what or when_to_apply is required"}), 400
|
||||
project_id = request.args.get("project_id", type=int) or None
|
||||
candidates = await lesson_rules_svc.rule_candidates(
|
||||
uid, what, when_to_apply, project_id,
|
||||
)
|
||||
return jsonify({"candidates": candidates})
|
||||
|
||||
|
||||
@lessons_bp.route("", methods=["POST"])
|
||||
@login_required
|
||||
async def create_lesson_route():
|
||||
|
||||
Reference in New Issue
Block a user