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,8 +12,16 @@ from shopdb.plugins.base import BasePlugin, PluginMeta
|
||||
from shopdb.api import db, AssetType
|
||||
|
||||
from .models import Computer, ComputerType, ComputerInstalledApp, AccessProtocol, ComputerAccess
|
||||
from .models import (
|
||||
ConfigurationPolicy,
|
||||
ConfigurationPolicyAction,
|
||||
BusinessPolicy,
|
||||
BusinessPolicyItem,
|
||||
)
|
||||
from .api import computers_bp
|
||||
|
||||
from .policyflag import policy_flag_enabled as _policy_flag_enabled
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
# Marker stamped on PC->measuringtool "controls" links this collector creates
|
||||
@@ -92,7 +100,8 @@ class ComputersPlugin(BasePlugin):
|
||||
|
||||
def get_models(self) -> List[Type]:
|
||||
"""Return list of SQLAlchemy model classes."""
|
||||
return [Computer, ComputerType, ComputerInstalledApp, AccessProtocol, ComputerAccess]
|
||||
return [Computer, ComputerType, ComputerInstalledApp, AccessProtocol, ComputerAccess,
|
||||
ConfigurationPolicy, ConfigurationPolicyAction, BusinessPolicy, BusinessPolicyItem]
|
||||
|
||||
def init_app(self, app: Flask, db_instance) -> None:
|
||||
"""Initialize plugin with Flask app."""
|
||||
@@ -397,6 +406,36 @@ class ComputersPlugin(BasePlugin):
|
||||
'site\'s URL and ranges',
|
||||
'position': 26,
|
||||
},
|
||||
] + self._policy_settings_cards()
|
||||
|
||||
def _policy_settings_cards(self):
|
||||
"""Policy admin cards, shown only while the feature flag is on.
|
||||
|
||||
Gating the cards on the switch keeps the settings menu honest: the
|
||||
Configuration/Business Policy screens appear exactly when the feature is
|
||||
enabled and vanish on rollback, matching the 404 the routes return.
|
||||
"""
|
||||
if not self._configpolicies_enabled_safe():
|
||||
return []
|
||||
return [
|
||||
{
|
||||
'group': 'Computers',
|
||||
'to': '/settings/configuration-policies',
|
||||
'icon': 'shield',
|
||||
'title': 'Configuration policies',
|
||||
'description': 'Reusable rules binding endpoint actions to PC '
|
||||
'types and applications',
|
||||
'position': 27,
|
||||
},
|
||||
{
|
||||
'group': 'Computers',
|
||||
'to': '/settings/business-policies',
|
||||
'icon': 'layers',
|
||||
'title': 'Business policies',
|
||||
'description': 'Ordered collections of configuration policies '
|
||||
'assigned by priority',
|
||||
'position': 28,
|
||||
},
|
||||
]
|
||||
|
||||
def get_settings_defaults(self) -> List[dict]:
|
||||
@@ -438,8 +477,41 @@ class ComputersPlugin(BasePlugin):
|
||||
'Off while machine numbers are shared between '
|
||||
'PCs; the collector response still warns.',
|
||||
},
|
||||
{
|
||||
'key': 'computers_configpolicies_enabled',
|
||||
'value': 'false',
|
||||
'valuetype': 'boolean',
|
||||
'category': 'computers',
|
||||
'description': 'Master switch for RMM-style Configuration and '
|
||||
'Business Policies. Off by default: models exist '
|
||||
'but the CRUD routes, WebUI, and endpoint '
|
||||
'enforcement stay inert so the whole feature can '
|
||||
'be rolled back without a schema change.',
|
||||
},
|
||||
]
|
||||
|
||||
def _configpolicies_enabled(self):
|
||||
"""Whether the Configuration/Business Policy feature is turned on.
|
||||
|
||||
Off by default. This is the single exit gate for the policy scope: the
|
||||
tables and models load regardless (SQLAlchemy needs them), but every
|
||||
route, panel, and enforcement path checks this first and no-ops while it
|
||||
is false, so the feature can be disabled without a migration.
|
||||
"""
|
||||
return _policy_flag_enabled()
|
||||
|
||||
def _configpolicies_enabled_safe(self):
|
||||
"""Flag read that never raises when the settings table is absent.
|
||||
|
||||
get_settings_cards can be called in contexts without a database (the
|
||||
plugin contract tests build hooks against a bare app), so a missing
|
||||
settings table must read as "off" rather than propagate.
|
||||
"""
|
||||
try:
|
||||
return _policy_flag_enabled()
|
||||
except Exception:
|
||||
return False
|
||||
|
||||
def _machinelink_alerts_enabled(self):
|
||||
"""Whether the PC-to-machine alerts may be sent.
|
||||
|
||||
|
||||
Reference in New Issue
Block a user