Let a proposed record be withdrawn (issue 1081)
A dropped proposal or a stale reservation stayed proposed forever: there was no way to say the operator decided against it. knowledge-base.withdraw takes a reserved or proposed record to withdrawn as a new version naming why, ends a waiting operator's ask and clears a pending review in the same version, and refuses an already accepted, superseded or withdrawn record. The number stays spent either way. The old Withdraw (the background job for reservations never proposed) is renamed WithdrawStale to make room for the new, caller-driven WithdrawRecord.
This commit is contained in:
@@ -606,9 +606,9 @@ func (k *KB) TellFindings() {
|
||||
|
||||
// ---- reservations that were never proposed -------------------------------------------------------------------
|
||||
|
||||
// Withdraw withdraws every number reserved longer ago than the settings allow and never proposed: it stays spent,
|
||||
// with who reserved it and when.
|
||||
func (k *KB) Withdraw() (int, error) {
|
||||
// WithdrawStale withdraws every number reserved longer ago than the settings allow and never proposed: it stays
|
||||
// spent, with who reserved it and when.
|
||||
func (k *KB) WithdrawStale() (int, error) {
|
||||
ctx := context.Background()
|
||||
k.mu.Lock()
|
||||
defer k.mu.Unlock()
|
||||
@@ -638,3 +638,51 @@ func (k *KB) Withdraw() (int, error) {
|
||||
}
|
||||
return n, nil
|
||||
}
|
||||
|
||||
// ---- withdraw, on the operator's decision ------------------------------------------------------------------
|
||||
|
||||
// Withdrawn is the answer to withdraw.
|
||||
type Withdrawn struct {
|
||||
Record string `json:"record"`
|
||||
Status string `json:"status"`
|
||||
Says string `json:"says"`
|
||||
}
|
||||
|
||||
// WithdrawRecord withdraws a reserved or proposed record on the caller's decision, with why in one line: the
|
||||
// number stays spent (novox/hq ADR 0290 rule 5), as a new version by the caller. A pending review is cleared, and
|
||||
// a waiting operator's ask is ended, both as part of that one version. Refused: a record this holds none of, an
|
||||
// empty why, and a record already accepted (a research effort accepted at any status), superseded or withdrawn —
|
||||
// only a reserved or a proposed record takes it.
|
||||
func (k *KB) WithdrawRecord(caller string, key Key, why string) (*Withdrawn, error) {
|
||||
why = strings.TrimSpace(why)
|
||||
if why == "" {
|
||||
return nil, errors.New("withdraw needs why: one line saying why the record is withdrawn; nothing was withdrawn")
|
||||
}
|
||||
ctx := context.Background()
|
||||
k.mu.Lock()
|
||||
defer k.mu.Unlock()
|
||||
e, err := k.Store.Get(ctx, key)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("the knowledge base cannot be read (%v); nothing was withdrawn", err)
|
||||
}
|
||||
if e == nil {
|
||||
return nil, fmt.Errorf("the knowledge base holds no %s; nothing was withdrawn", key)
|
||||
}
|
||||
cur := e.Current
|
||||
if cur.Status != StatusReserved && cur.Status != StatusProposed {
|
||||
return nil, fmt.Errorf("%s is %s and takes no withdraw; only a reserved or a proposed record does. Nothing was withdrawn", key, cur.Status)
|
||||
}
|
||||
r := cur.Clone()
|
||||
r.Status = StatusWithdrawn
|
||||
r.Judging = nil
|
||||
if r.Ask != nil && r.Ask.Said == AskWaiting {
|
||||
r.Ask.Said = "ended: the record was withdrawn"
|
||||
}
|
||||
full := fmt.Sprintf("withdrawn by %s: %s", caller, why)
|
||||
if err := k.Store.Save(ctx, Version{By: caller, At: k.now(), Why: full, Record: r}, true, true, e.Revision); err != nil {
|
||||
return nil, fmt.Errorf("%s could not be written (%v); nothing was withdrawn", key, err)
|
||||
}
|
||||
k.emit(EventChanged, r, caller, full, e.Versions+1)
|
||||
return &Withdrawn{Record: key.String(), Status: StatusWithdrawn,
|
||||
Says: fmt.Sprintf("%s is withdrawn: %s. Its number stays spent, and is never handed out again.", key, why)}, nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user