"""Measuring-tools plugin main class. The first plugin built on the matured framework scaffold (ADR-005). It owns two tables (measuringtooltypes, measuringtools), a blueprint under /api/measuringtools, a sidebar entry, and a calibration report card. It seeds its own asset type and a set of starter tool types on install. """ import json import logging from pathlib import Path from typing import List, Dict, Optional, Type from flask import Flask, Blueprint from shopdb.plugins.base import BasePlugin, PluginMeta from shopdb.api import db, AssetType from .api import measuringtools_bp from .models import MeasuringTool, MeasuringToolType logger = logging.getLogger(__name__) # Starter tool types seeded on install: (name, description, color). # Colors are drawn from the shared frontend PALETTE (utils/colorStyle.js). STARTER_TYPES = [ ('Caliper', 'Vernier / digital caliper', '#14abef'), ('Micrometer', 'Outside / inside / depth micrometer', '#2dce89'), ('Thread Gage', 'Go / no-go thread gage', '#fb6340'), ('Bore Gage', 'Bore / hole diameter gage', '#7934f3'), ('Height Gage', 'Height / vertical measuring gage', '#ffc107'), ('Indicator', 'Dial / test indicator', '#11cdef'), ('Gage Block Set', 'Reference gage block set', '#e83e8c'), ('Other', 'Other measuring tool', '#6c757d'), ] class MeasuringToolsPlugin(BasePlugin): """Metrology and inspection instruments (calibration lifecycle).""" def __init__(self): self._manifest = self._load_manifest() def _load_manifest(self) -> Dict: manifest_path = Path(__file__).parent / 'manifest.json' if manifest_path.exists(): with open(manifest_path, 'r') as f: return json.load(f) return {} @property def meta(self) -> PluginMeta: return PluginMeta( name=self._manifest.get('name', 'measuringtools'), version=self._manifest.get('version', '1.0.0'), description=self._manifest.get( 'description', 'Metrology and inspection instruments'), author=self._manifest.get('author', 'ShopDB Team'), dependencies=self._manifest.get('dependencies', []), core_version=self._manifest.get('core_version', '>=0.6.0,<1.0.0'), api_prefix=self._manifest.get('api_prefix', '/api/measuringtools'), ) def get_blueprint(self) -> Optional[Blueprint]: return measuringtools_bp def get_models(self) -> List[Type]: return [MeasuringTool, MeasuringToolType] def get_navigation_items(self) -> List[Dict]: return [ { 'name': 'Measuring Tools', 'icon': 'ruler', 'route': '/measuringtools', 'position': 22, }, ] def get_reports(self) -> List[Dict]: return [ { 'id': 'calibration', 'name': 'Calibration Due', 'description': 'Measuring tools bucketed by calibration status', 'category': 'compliance', 'route': '/reports/calibration', }, ] def get_config_schema(self) -> List[Dict]: # No external credentials or endpoints: calibration is tracked by hand, # so the setup wizard shows nothing to configure. Documented in the # plugin guide as the intentional empty-schema case. return [] def init_app(self, app: Flask, db_instance) -> None: logger.info(f"Measuring-tools plugin initialized (v{self.meta.version})") def on_install(self, app: Flask) -> None: with app.app_context(): self._ensure_asset_type() self._ensure_starter_types() db.session.commit() logger.info("Measuring-tools plugin installed") def _ensure_asset_type(self) -> None: existing = AssetType.query.filter_by(assettype='measuring_tool').first() if not existing: db.session.add(AssetType( assettype='measuring_tool', pluginname='measuringtools', tablename='measuringtools', description='Metrology and inspection instruments (gauges, ' 'calipers, thread gages, bore gages, ...)', icon='ruler', )) logger.debug("Created asset type: measuring_tool") db.session.commit() def _ensure_starter_types(self) -> None: for name, description, color in STARTER_TYPES: if not MeasuringToolType.query.filter_by(name=name).first(): db.session.add(MeasuringToolType( name=name, description=description, color=color)) logger.debug(f"Created measuring-tool type: {name}") db.session.commit()