harden CSV import reconciliation and preserve saved model selections
Default fuzzy reconcile threshold to 0 and refuse merges when digit runs differ so numbered labels like Bay 311 never bind to Bay 310. Keep edit forms from clearing saved model fields when category filtering hides untyped rows. Includes in-progress computers policy and endpoints command feature work.
This commit is contained in:
@@ -12,6 +12,7 @@ from shopdb.core.services.csvimport import (
|
||||
IMPORTABLE, ImportError_, Resolver, generate_template, import_csv,
|
||||
importable_columns, required_columns,
|
||||
)
|
||||
from shopdb.core.services.csvimport import digit_runs, normalize_label
|
||||
from shopdb.extensions import db as _db
|
||||
|
||||
|
||||
@@ -280,3 +281,57 @@ def test_template_example_set_imports_cleanly(db):
|
||||
table, [str(p) for p in result.problems])
|
||||
_db.session.rollback()
|
||||
|
||||
|
||||
# -- fuzzy reconciliation safety (review item 1.1) -----------------------------
|
||||
#
|
||||
# A high similarity score is the DANGEROUS case: 'Bay 310' and 'Bay 311' share
|
||||
# every word and score ~0.857, so a naive threshold silently merges two distinct
|
||||
# locations. Reconciliation now ships OFF (threshold 0) and, even when a site
|
||||
# turns it on, refuses any pair whose digit runs differ.
|
||||
|
||||
def test_digit_runs_extracts_number_groups():
|
||||
assert digit_runs(normalize_label('Bay 310')) == ['310']
|
||||
assert digit_runs(normalize_label('Precision 3660')) == ['3660']
|
||||
assert digit_runs(normalize_label('Dell Inc.')) == []
|
||||
|
||||
|
||||
def test_reconciliation_is_off_by_default(db):
|
||||
"""A fresh Resolver does not fuzzy-match: an unknown numbered bay is a row
|
||||
error, not a silent bind onto its neighbour."""
|
||||
resolver = Resolver()
|
||||
seed_reference(resolver)
|
||||
# 'Bay 3' exists from the reference seed; 'Bay 8' does not.
|
||||
result = import_csv(
|
||||
'assets',
|
||||
'assetnumber,assettypeid,locationid\nA-1,Machine,Bay 8\n',
|
||||
resolver=resolver)
|
||||
assert not result.ok
|
||||
assert not resolver.events
|
||||
|
||||
|
||||
def test_reconcile_refuses_when_digit_runs_differ(db):
|
||||
"""Even with fuzzy matching turned on, 'Bay 311' must not bind to the
|
||||
existing 'Bay 3': the digit runs differ, so it is a distinct location."""
|
||||
resolver = Resolver(reconcile_threshold=0.5)
|
||||
seed_reference(resolver)
|
||||
result = import_csv(
|
||||
'assets',
|
||||
'assetnumber,assettypeid,locationid\nB-1,Machine,Bay 311\n',
|
||||
resolver=resolver)
|
||||
assert not result.ok
|
||||
assert not resolver.events
|
||||
|
||||
|
||||
def test_reconcile_still_binds_normalized_exact(db):
|
||||
"""The normalized-exact tier is untouched: 'bay 3' folds to the existing
|
||||
'Bay 3' regardless of threshold, and is reported as an event."""
|
||||
resolver = Resolver(reconcile_threshold=0.9)
|
||||
seed_reference(resolver)
|
||||
result = import_csv(
|
||||
'assets',
|
||||
'assetnumber,assettypeid,locationid\nC-1,Machine,BAY 3\n',
|
||||
resolver=resolver)
|
||||
assert result.ok, [str(p) for p in result.problems]
|
||||
_db.session.rollback()
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user