Make a partly-undone import retryable, and refuse a plan it cannot deliver
This commit is contained in:
@@ -57,7 +57,7 @@ def plan_hash(plan):
|
||||
return hasher.hexdigest()
|
||||
|
||||
|
||||
def build_packet(kind, plan, counts, groups):
|
||||
def build_packet(kind, plan, counts, groups, creatable=None):
|
||||
"""Build an approval packet from a plan.
|
||||
|
||||
kind a short label identifying the importer, stored on the packet.
|
||||
@@ -69,6 +69,12 @@ def build_packet(kind, plan, counts, groups):
|
||||
column is what a human would act on - the raw export value for
|
||||
UNRESOLVED (so they know what to alias) and the canonical name for
|
||||
NEEDS_CREATE (so they know what would be created).
|
||||
creatable
|
||||
the group names the caller's writer can actually create, or None
|
||||
for "no opinion". A needs-create group outside this set is reported
|
||||
under `uncreatable` and makes the packet blocking, because a plan
|
||||
that promises rows nothing will create is as unrunnable as one with
|
||||
unresolved values.
|
||||
|
||||
needscreate is keyed on the canonical name column when present; callers pass
|
||||
the *_name column for that via the fourth tuple element when it differs from
|
||||
@@ -86,6 +92,26 @@ def build_packet(kind, plan, counts, groups):
|
||||
if needs_hits:
|
||||
needscreate[group_name] = needs_hits
|
||||
|
||||
# A needs-create group the writer cannot actually create BLOCKS the plan,
|
||||
# the same as an unresolved value. It is the same failure either way: the
|
||||
# import cannot do what the preview says it would.
|
||||
#
|
||||
# It has to be decided HERE rather than at write time. The writer's own
|
||||
# refusal sits behind the acknowledgement check, and the UI only ever
|
||||
# acknowledges the groups it renders a control for - so a plan needing an
|
||||
# uncreatable group reached Commit looking clean, and died on a message
|
||||
# about acknowledging something the screen had no checkbox for. Blocking at
|
||||
# plan time puts it in the preview, where the admin can act on it.
|
||||
#
|
||||
# creatable=None means "no opinion" - the CLI callers that do not pass it
|
||||
# keep the old behaviour.
|
||||
uncreatable = OrderedDict()
|
||||
if creatable is not None:
|
||||
allowed = set(creatable)
|
||||
for group_name, hits in needscreate.items():
|
||||
if group_name not in allowed:
|
||||
uncreatable[group_name] = hits
|
||||
|
||||
return OrderedDict([
|
||||
('kind', kind),
|
||||
('batchid', str(uuid.uuid4())),
|
||||
@@ -93,8 +119,9 @@ def build_packet(kind, plan, counts, groups):
|
||||
('rowcount', len(plan)),
|
||||
('counts', OrderedDict(counts)),
|
||||
('needscreate', needscreate),
|
||||
('uncreatable', uncreatable),
|
||||
('unresolved', unresolved),
|
||||
('blocking', bool(unresolved)),
|
||||
('blocking', bool(unresolved) or bool(uncreatable)),
|
||||
])
|
||||
|
||||
|
||||
@@ -123,6 +150,20 @@ def format_packet(packet):
|
||||
lines.append(' + %-28s (%d rows)' % (name, count))
|
||||
lines.append(' total new %s rows: %d' % (group, total))
|
||||
|
||||
# An uncreatable group blocks, so it has to be ON SCREEN. Setting
|
||||
# blocking without printing the reason left "BLOCKED (resolve the values
|
||||
# above)" pointing at nothing.
|
||||
uncreatable = packet.get('uncreatable') or {}
|
||||
if uncreatable:
|
||||
for group, items in uncreatable.items():
|
||||
lines.append('')
|
||||
lines.append(' BLOCKING: this import cannot create %s rows, but '
|
||||
'%d are needed - create them first, or alias the '
|
||||
'export values to rows that exist:'
|
||||
% (group, len(items)))
|
||||
for name, count in items.items():
|
||||
lines.append(' x %-28s (%d rows)' % (name, count))
|
||||
|
||||
unresolved = packet.get('unresolved') or {}
|
||||
if unresolved:
|
||||
for group, items in unresolved.items():
|
||||
@@ -134,7 +175,7 @@ def format_packet(packet):
|
||||
|
||||
lines.append('')
|
||||
lines.append(' status : %s' % (
|
||||
'BLOCKED (resolve the values above)' if packet.get('blocking')
|
||||
'BLOCKED (resolve the problems above)' if packet.get('blocking')
|
||||
else 'READY for acknowledgment'))
|
||||
return '\n'.join(lines)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user