Add the dualpath-as-single-machine site toggle
Most facilities consider a Dualpath pair one physical dual-bay machine. New site setting dualpath_single_machine (default on): the machines list, dashboard counts, machines-by-type report, and floor map collapse each pair to its primary bay (lower assetnumber), with combined 2007 / 2008 labels; pagination totals stay honest. Detail pages remain per-bay and always show a dual-bay sibling banner linking the partner. Pair resolution lives in core services and joins the plugin contract surface (0.8.0 -> 0.9.0). On the WJ dataset: 31 pairs collapse, machine counts 262 -> 231, map 470 assets. Toggle verified live in both states, left on. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -247,3 +247,49 @@ def test_cli_backfill_propagates_and_is_idempotent(client, db, auth_headers, run
|
||||
sourceassetid=pc.assetid, targetassetid=bayb.assetid,
|
||||
relationshiptypeid=controls.relationshiptypeid).count()
|
||||
assert count == 1
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# CLI fix-controls-direction flips reversed legacy rows to PC -> machine
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
def test_cli_fix_controls_direction(client, db, auth_headers, runner):
|
||||
atype, controls, dualpath, partof = _setup_types()
|
||||
ctype = AssetType(assettype='computer')
|
||||
db.session.add(ctype)
|
||||
db.session.flush()
|
||||
|
||||
pc = _make_asset('PC-7', ctype.assettypeid)
|
||||
pc2 = _make_asset('PC-8', ctype.assettypeid)
|
||||
bay = _make_asset('BAY-7', atype.assettypeid)
|
||||
bay2 = _make_asset('BAY-8', atype.assettypeid)
|
||||
|
||||
# reversed legacy row: machine -> PC
|
||||
db.session.add(AssetRelationship(sourceassetid=bay.assetid, targetassetid=pc.assetid,
|
||||
relationshiptypeid=controls.relationshiptypeid))
|
||||
# reversed row whose flip already exists -> reversed one gets deactivated
|
||||
db.session.add(AssetRelationship(sourceassetid=bay2.assetid, targetassetid=pc2.assetid,
|
||||
relationshiptypeid=controls.relationshiptypeid))
|
||||
db.session.add(AssetRelationship(sourceassetid=pc2.assetid, targetassetid=bay2.assetid,
|
||||
relationshiptypeid=controls.relationshiptypeid))
|
||||
db.session.commit()
|
||||
|
||||
result = runner.invoke(args=['relationships', 'fix-controls-direction'])
|
||||
assert result.exit_code == 0, result.output
|
||||
assert 'Flipped 1' in result.output
|
||||
assert '1 reversed duplicate(s) deactivated' in result.output
|
||||
|
||||
# first row now reads PC -> machine
|
||||
assert _rel_exists(pc.assetid, bay.assetid, controls.relationshiptypeid)
|
||||
assert not _rel_exists(bay.assetid, pc.assetid, controls.relationshiptypeid)
|
||||
# duplicate pair: canonical row stays active, reversed one deactivated
|
||||
rev = AssetRelationship.query.filter_by(
|
||||
sourceassetid=bay2.assetid, targetassetid=pc2.assetid,
|
||||
relationshiptypeid=controls.relationshiptypeid).first()
|
||||
assert rev is not None and rev.isactive is False
|
||||
|
||||
# idempotent: second run finds nothing
|
||||
result2 = runner.invoke(args=['relationships', 'fix-controls-direction'])
|
||||
assert result2.exit_code == 0, result2.output
|
||||
assert 'Flipped 0' in result2.output
|
||||
assert '0 reversed duplicate(s) deactivated' in result2.output
|
||||
|
||||
Reference in New Issue
Block a user