diff --git a/M02_MasterTemplete/M02_Hopyo_Marks.py b/M02_MasterTemplete/M02_Hopyo_Marks.py index 82ffd9ecf..1cb02b336 100644 --- a/M02_MasterTemplete/M02_Hopyo_Marks.py +++ b/M02_MasterTemplete/M02_Hopyo_Marks.py @@ -1,7 +1,7 @@ """M02 불러오기 엑셀 보기 하이라이트 재료(PLAN 779-2 · 요구서 621 3장 5) — 읽기만(파일 · storage 변화 0). 블록 `[{번, 판정, 고를수, SK, 갈래, 못옮김, 부름}]` = 단가산출 만들기 결과(`Sangeun.made`) + 블록 판정을 읽기만. -- `판정` = 새 블록 판정(785-6 · `M02_Hopyo_Make.judged` 파생 저장본 = 기초로 풂 · 기초+덧 · 줄 조합 · 고를 것 · +- `판정` = 새 블록 판정(785-6 · `M02_Hopyo_Judge.judged` 파생 저장본 = 기초로 풂 · 기초+덧 · 줄 조합 · 고를 것 · `고를수`) · 그 저장본이 없으면(785-5 전 · 셈 전) 옛 판정 = 부르는 첫 줄의 맞춤셈 상태(맞음 · 다른 점 · 후보 …) · 안 불리거나 셈 전이면 None - `부름` = 부르는 호표 번 [] = 일위대가 밖(780-1) · `어긋남` · `줄` = 모든 블록(밖 포함) @@ -33,6 +33,7 @@ from pathlib import Path from typing import Any from M02_MasterTemplete import M02_Hopyo_Fit as fit +from M02_MasterTemplete import M02_Hopyo_Judge as hjudge from M02_MasterTemplete import M02_Hopyo_Sangeun as sangeun from M02_MasterTemplete import M02_Hopyo_Store as hopyo_store from M02_MasterTemplete import M02_Hopyo_UnitLine as unit_line @@ -123,13 +124,8 @@ def lines(block: dict[str, Any], units: hunits.Units) -> list[list[Any]]: def _judged(folder: Path, key: str) -> dict[int, dict[str, Any]] | None: - """새 블록 판정 `{번: {판정, 고를수}}`(785-5 파생 저장본) · 없으면 None(셈 전 · 785-5 전).""" - try: - from M02_MasterTemplete import M02_Hopyo_Make as make - except ImportError: # ponytail: 785-5 Make 오기 전 = 옛 맞춤셈 판정 · 785-7 에서 걷음 - return None - got = make.judged(folder, key) - return None if got is None else {int(k): v for k, v in got.items()} + """새 블록 판정 `{번: {판정, 고를수}}`(785-5 파생 저장본) · 셈 중 = None(옛 판정 · 785-7 걷음).""" + return hjudge.judged(folder, key) def blocks(folder: Path, key: str, to: Path | None = None) -> list[dict[str, Any]]: diff --git a/M02_MasterTemplete/M02_MasterTemplete_Hopyo_JudgeApi.ts b/M02_MasterTemplete/M02_MasterTemplete_Hopyo_JudgeApi.ts index f0b8f66e8..7306e0bd4 100644 --- a/M02_MasterTemplete/M02_MasterTemplete_Hopyo_JudgeApi.ts +++ b/M02_MasterTemplete/M02_MasterTemplete_Hopyo_JudgeApi.ts @@ -82,7 +82,8 @@ export interface Section { 판정: "기초 풂" | "후보 여럿" | "기초 없음"; 고름: string | null; 후보: BaseCand[]; - 몫?: { 글?: string; 값: number | null; 행?: number | null } | null; + /** 절 몫(곱수) — 수 · 옛 꼴 {값} */ + 몫?: number | { 값: number | null } | null; } /** 덧 줄(엑셀만 줄 → 덧 줄 후보 · `Units` 잇기) */ @@ -167,6 +168,8 @@ export interface JudgeBlockBrief { export interface JudgeList { 키: string; 파일: string; + /** 블록 풀이 뒤 셈 중(블록 판정 · 고를수 None) */ + 셈중?: boolean; 호표: JudgeBrief[]; 블록: JudgeBlockBrief[]; } @@ -208,6 +211,8 @@ export interface MoveBody { 주인?: string | null; 호표: Record>; 블록: Record; + /** 이미 옮긴 호표 · 블록 덮기(아니면 409) */ + 덮음?: boolean; } export interface MoveBlocked { diff --git a/M02_MasterTemplete/M02_MasterTemplete_Hopyo_Rows.ts b/M02_MasterTemplete/M02_MasterTemplete_Hopyo_Rows.ts index fabccaaf5..c5e1adb80 100644 --- a/M02_MasterTemplete/M02_MasterTemplete_Hopyo_Rows.ts +++ b/M02_MasterTemplete/M02_MasterTemplete_Hopyo_Rows.ts @@ -6,17 +6,25 @@ * 수량 · 단위 환산 칸(`logic/Hopyo_Unit` · 같은 단위 = 숨김) · [보기] = 엑셀 그 행 · [확인] * · 단산 줄 = [블록 풀이] 펼침(`Hopyo_Block.solvedBody` · 절마다 기초 · 입력값 칸) + 「단가산출 통째」 칩(단산N → SK) * · 머리 [호표 제안 모두 확인](사용자 누름 · 다 풀린 제안만) · 아래 [옮기기](`createJudgeMove` · 오른쪽 판 아래) + * · 이미 옮긴 것(409) = 물음 한 번 뒤 덮음 * · 고름 · 확인 = 캐시(`createJudgeState`) · 자동 확인 · 자동 옮김 없음 · 옮김 = 서버가 정본으로 다시 셈 * ========================================================================== */ import { createBadge, type BadgeTone } from "@ui/ui_template_badge"; -import { createButton, createInputField, el, showToast } from "@ui/ui_template_elements"; +import { + askConfirm, + createButton, + createInputField, + el, + showToast, +} from "@ui/ui_template_elements"; import { t as L } from "@ui/ui_template_locale"; import { createPanelHead } from "@ui/ui_template_panel_head"; import { tableRow, tableShell, type Cell, type Column } from "@ui/ui_template_table"; import { createText } from "@ui/ui_template_text"; import { createToggle } from "@ui/ui_template_toggle"; import { formatNumber } from "../common_util/common_util_money"; +import { StaleError } from "./M02_MasterTemplete_Api_Fetch"; import type { HopyoScope } from "./M02_MasterTemplete_Hopyo_Api"; import { createJudgeState, solvedBody, type JudgeState } from "./M02_MasterTemplete_Hopyo_Block"; import { @@ -300,7 +308,11 @@ export function createJudgeMove(opt: JudgeMoveOptions): { root: HTMLElement; des } btn.disabled = true; try { - const r = await moveJudge(opt.target, opt.key, body); + const r = await moveJudge(opt.target, opt.key, body).catch(async (e: unknown) => { + // 이미 옮긴 호표 · 블록 — 물음 한 번 뒤 덮음(아니면 그만) + if (!(e instanceof StaleError) || !(await askConfirm({ message: e.message }))) throw e; + return moveJudge(opt.target, opt.key, { ...body, 덮음: true }); + }); const stuck = new Set(r.막힘.map((x) => `${x.호표 ?? "b" + x.블록}:${x.차례 ?? ""}`)); let d = opt.state.get(); for (const [h, lines] of Object.entries(body.호표)) diff --git a/M02_MasterTemplete/M02_MasterTemplete_Hopyo_SheetSk.ts b/M02_MasterTemplete/M02_MasterTemplete_Hopyo_SheetSk.ts index 8c298bd6a..bfd52c09b 100644 --- a/M02_MasterTemplete/M02_MasterTemplete_Hopyo_SheetSk.ts +++ b/M02_MasterTemplete/M02_MasterTemplete_Hopyo_SheetSk.ts @@ -17,6 +17,7 @@ import { createText } from "@ui/ui_template_text"; import type { HopyoScope } from "./M02_MasterTemplete_Hopyo_Api"; import { createJudgeBlock, type JudgeState } from "./M02_MasterTemplete_Hopyo_Block"; import { readJudge, type JudgeBlockBrief } from "./M02_MasterTemplete_Hopyo_JudgeApi"; +import { POLL_MS } from "./M02_MasterTemplete_Hopyo_Check"; import type { SheetNav } from "./M02_MasterTemplete_Hopyo_SheetNav"; export interface SheetSkOptions { @@ -94,12 +95,14 @@ export function createSheetSk(opt: SheetSkOptions) { function ensure(): void { if (asked) return; asked = true; - list.replaceChildren(createText(L("M02_JudgeLoading"), "hint")); + if (!blocks) list.replaceChildren(createText(L("M02_JudgeLoading"), "hint")); void readJudge(opt.scope, opt.key, opt.target) .then((got) => { blocks = got.블록; draw(); opt.onChange?.(); + // 블록 풀이 뒤 셈 중 — 끝나면 판정 · 고를 것이 실림(3 초마다 · 판이 붙어 있는 동안) + if (got.셈중) setTimeout(() => list.isConnected && ((asked = false), ensure()), POLL_MS); }) .catch((e: Error) => list.replaceChildren(createText(e.message, "error"))); } diff --git a/M02_MasterTemplete/logic/M02_MasterTemplete_Hopyo_Picks.ts b/M02_MasterTemplete/logic/M02_MasterTemplete_Hopyo_Picks.ts index 35b07e402..19cfc4cce 100644 --- a/M02_MasterTemplete/logic/M02_MasterTemplete_Hopyo_Picks.ts +++ b/M02_MasterTemplete/logic/M02_MasterTemplete_Hopyo_Picks.ts @@ -62,7 +62,7 @@ export function basePick(cand: BaseCand, sec: Section): DraftPick { 이름: cand.이름, 입력값: { ...b.입력값 }, 출처: { ...b.출처 }, - 몫: sec.몫?.값 ?? null, + 몫: typeof sec.몫 === "number" ? sec.몫 : (sec.몫?.값 ?? null), 필요: [...new Set([...b.입력필요, ...Object.keys(b.엇갈림)])], }; } diff --git a/resources/tester/helper_778_5_sk_pick.cjs b/resources/tester/helper_778_5_sk_pick.cjs index 21d27f8d3..e36973294 100644 --- a/resources/tester/helper_778_5_sk_pick.cjs +++ b/resources/tester/helper_778_5_sk_pick.cjs @@ -64,6 +64,7 @@ const STUBS = { }, }, "./M02_MasterTemplete_Hopyo_Block": { createJudgeBlock: (o) => ({ ...o, destroy() {} }) }, + "./M02_MasterTemplete_Hopyo_Check": { POLL_MS: 3000 }, "@config/config_frontend": { API_BASE_URL: "/api", ROUTES: {} }, "@ui/ui_template_elements": { el: (tag, o = {}) => ({ diff --git a/resources/tester/test_642_6_screen.py b/resources/tester/test_642_6_screen.py index 6eece390d..d4a92398b 100644 --- a/resources/tester/test_642_6_screen.py +++ b/resources/tester/test_642_6_screen.py @@ -227,7 +227,7 @@ def test_고름바꾸면_확인풀림_칠(judged): def test_블록칠_새판정(monkeypatch): - """SheetMap 블록 칠 = 새 판정(제안 칠) · Marks = `Make.judged` 저장본 › 없으면 옛 판정.""" + """SheetMap 블록 칠 = 새 판정(제안 칠) · Marks = `Judge.judged` 저장본 › 셈 중이면 옛 판정.""" from M02_MasterTemplete import M02_Hopyo_Marks as mk from resources.tester.test_779_4_sheet_map import _node @@ -255,17 +255,9 @@ def test_블록칠_새판정(monkeypatch): )["blocks"] assert [b["tone"] for b in got] == ["success", "info", "info", "success", "success"] assert got[1]["글"] == "제안 — 고를 것 3" and got[2]["글"] == "제안 — 줄 조합 · 일위대가 밖" - assert mk._judged(Path("."), "x") is None # 785-5 Make 전 = 옛 길 - - class Make: - @staticmethod - def judged(folder, key): - return {"10": {"판정": "기초로 풂", "고를수": 0}} - - import sys - - monkeypatch.setitem(sys.modules, "M02_MasterTemplete.M02_Hopyo_Make", Make) - monkeypatch.setattr(sys.modules["M02_MasterTemplete"], "M02_Hopyo_Make", Make, raising=False) + monkeypatch.setattr( + mk.hjudge, "judged", lambda folder, key: {10: {"판정": "기초로 풂", "고를수": 0}} + ) assert mk._judged(Path("."), "x") == {10: {"판정": "기초로 풂", "고를수": 0}}