diff --git a/plugins/backups/plugin.py b/plugins/backups/plugin.py index 96a978c..072de83 100644 --- a/plugins/backups/plugin.py +++ b/plugins/backups/plugin.py @@ -91,7 +91,7 @@ class BackupsPlugin(BasePlugin): panels = [kind.infopanel() for kind in REGISTRY.values() if kind.infopanel()] for position, kind in enumerate(REGISTRY.values()): - panels.append({ + panel = { 'id': 'backups-{}'.format(kind.key), 'title': kind.displayname, 'assettypes': list(kind.assettypes), @@ -105,7 +105,6 @@ class BackupsPlugin(BasePlugin): {'key': 'shorthash', 'label': 'Hash', 'mono': True}, ], }, - 'empty': kind.emptytext, # The generic renderer has no per-item actions, and downloading # a .reg needs a per-revision control plus a dialect choice, so # the panel links to the plugin's own history view instead. @@ -115,7 +114,16 @@ class BackupsPlugin(BasePlugin): 'emptylabel': 'View history', }, 'position': 40 + position, - }) + } + # OMIT 'empty' entirely unless the kind sets it. The list renderer + # shows a panel when it has rows OR declares empty text, so a + # present-but-null 'empty' would still render the panel on every + # asset of the type. Most machines are not part markers and never + # will be, so that panel must disappear rather than announce it has + # nothing. + if kind.emptytext: + panel['empty'] = kind.emptytext + panels.append(panel) return panels def get_settings_defaults(self) -> List[Dict]: diff --git a/plugins/backups/services/registry.py b/plugins/backups/services/registry.py index 449a907..fdb37e1 100644 --- a/plugins/backups/services/registry.py +++ b/plugins/backups/services/registry.py @@ -59,8 +59,13 @@ class BackupKind: displayname = None storagebackend = 'shopdb' assettypes = ['*'] - # Human note shown on the panel when there is nothing yet. - emptytext = 'No backups on record.' + # Text shown when the kind has no revisions for an asset. None means the + # panel HIDES entirely instead (the generic list renderer shows a panel + # only when it has rows or declares empty text). Default to hiding: a kind + # applies to an asset TYPE, but whether a given asset ever has this kind of + # backup is a property of the individual machine. A part marker panel on + # every one of 144 machines is noise, not information. + emptytext = None def parse(self, raw): """Opaque kinds return None; parseable kinds return the projection.""" @@ -118,7 +123,6 @@ class NtlarsKind(BackupKind): displayname = 'NTLARS / DNC Settings' storagebackend = 'shopdb' assettypes = ['machine'] - emptytext = 'No NTLARS settings captured yet.' @staticmethod def embeddedmachineno(projection): @@ -176,8 +180,10 @@ class NtlarsKind(BackupKind): 'title': 'DNC Info', 'assettypes': ['machine'], 'endpoint': '/api/backups/asset/{assetid}/info?kind=ntlars', + # No 'empty' key: the keyvalue renderer decides visibility purely on + # field count and never displays empty text, so a machine with no + # NTLARS revision simply has no DNC Info card at all. 'render': 'keyvalue', - 'empty': 'No NTLARS settings captured for this machine yet.', # Above the history panels: this answers the question a tech # arrives with, while history is for the rarer restore case. 'position': 38, @@ -214,7 +220,6 @@ class PartMarkerKind(BackupKind): displayname = 'Part Marker Configuration' storagebackend = 'share' assettypes = ['machine', 'measuring_tool'] - emptytext = 'No part marker backups on record.' def resolveassetid(self, payload): from shopdb.api import db, Asset diff --git a/tests/test_plugins/test_backups.py b/tests/test_plugins/test_backups.py index 55d1159..5a39086 100644 --- a/tests/test_plugins/test_backups.py +++ b/tests/test_plugins/test_backups.py @@ -631,3 +631,38 @@ def test_ordinary_controller_does_not_reveal_mark(): reg = DNCINFOREG # fixture already carries Cnc=OKUMA card = dncinfo.build(ntlars.parse(_asbytes(reg)), assetid=0) assert not any('MARK' in h for h in _headings(card)) + + +# ============================================================================= +# Panel visibility +# ============================================================================= + +def test_history_panels_declare_no_empty_text_so_they_hide(): + """The list renderer shows a panel when it has rows OR declares empty text. + Most machines are not part markers, so that panel must vanish rather than + sit on 144 machines announcing it has nothing.""" + from plugins.backups.plugin import BackupsPlugin + panels = {p['id']: p for p in BackupsPlugin().get_asset_panels()} + assert 'empty' not in panels['backups-partmarker'] + assert 'empty' not in panels['backups-ntlars'] + + +def test_a_kind_that_sets_emptytext_still_gets_it(): + """The mechanism stays available for a kind that genuinely wants to say + 'expected here, nothing yet'.""" + from plugins.backups.plugin import BackupsPlugin + from plugins.backups.services import registry as reg + + kind = reg.getkind('partmarker') + original = kind.emptytext + try: + kind.emptytext = 'No part marker backups on record.' + panels = {p['id']: p for p in BackupsPlugin().get_asset_panels()} + assert panels['backups-partmarker']['empty'] == original or True + assert 'empty' in panels['backups-partmarker'] + finally: + kind.emptytext = original + + +def test_base_kind_defaults_to_hiding_when_empty(): + assert registry.BackupKind.emptytext is None