Coverage for nexusLIMS/tui/apps/instruments/validators.py: 100%
35 statements
« prev ^ index » next coverage.py v7.11.3, created at 2026-03-24 05:23 +0000
« prev ^ index » next coverage.py v7.11.3, created at 2026-03-24 05:23 +0000
1"""
2Instrument-specific validation functions.
4Provides validation for instrument database fields including uniqueness
5checks and instrument-specific constraints.
6"""
8from sqlmodel import Session
10from nexusLIMS.tui.common.db_utils import find_conflicting_instrument
11from nexusLIMS.tui.common.validators import (
12 validate_max_length,
13 validate_required,
14 validate_url,
15)
18def validate_instrument_pid(
19 value: str | None,
20) -> tuple[bool, str]:
21 """
22 Validate instrument_pid field.
24 Parameters
25 ----------
26 value : str | None
27 Instrument PID to validate
29 Returns
30 -------
31 tuple[bool, str]
32 (is_valid, error_message)
33 """
34 # Required check
35 is_valid, error = validate_required(value, "Instrument PID")
36 if not is_valid:
37 return False, error
39 # Max length check
40 return validate_max_length(value, 100, "Instrument PID")
43def validate_api_url_unique(
44 session: Session,
45 value: str | None,
46 exclude_pid: str | None = None,
47) -> tuple[bool, str]:
48 """
49 Validate api_url field with uniqueness check.
51 Parameters
52 ----------
53 session : Session
54 Database session
55 value : str | None
56 API URL to validate
57 exclude_pid : str | None
58 Instrument PID to exclude (for edit operations)
60 Returns
61 -------
62 tuple[bool, str]
63 (is_valid, error_message)
64 """
65 # Required and URL format check
66 is_valid, error = validate_url(value, "API URL")
67 if not is_valid:
68 return False, error
70 # Uniqueness check
71 conflict = find_conflicting_instrument(session, "api_url", value, exclude_pid)
72 if conflict:
73 return (
74 False,
75 f'API URL "{value}" already used by instrument "{conflict.instrument_pid}"',
76 )
78 return True, ""
81def validate_location(
82 value: str | None,
83) -> tuple[bool, str]:
84 """
85 Validate location field.
87 Parameters
88 ----------
89 value : str | None
90 Location to validate
92 Returns
93 -------
94 tuple[bool, str]
95 (is_valid, error_message)
96 """
97 # Required check
98 is_valid, error = validate_required(value, "Location")
99 if not is_valid:
100 return False, error
102 # Max length check
103 return validate_max_length(value, 100, "Location")
106def validate_property_tag(
107 value: str | None,
108) -> tuple[bool, str]:
109 """
110 Validate property_tag field.
112 Parameters
113 ----------
114 value : str | None
115 Property tag to validate
117 Returns
118 -------
119 tuple[bool, str]
120 (is_valid, error_message)
121 """
122 # Required check
123 is_valid, error = validate_required(value, "Property Tag")
124 if not is_valid:
125 return False, error
127 # Max length check
128 return validate_max_length(value, 20, "Property Tag")
131def validate_harvester(
132 value: str | None,
133) -> tuple[bool, str]:
134 """
135 Validate harvester field.
137 Parameters
138 ----------
139 value : str | None
140 Harvester name to validate
142 Returns
143 -------
144 tuple[bool, str]
145 (is_valid, error_message)
146 """
147 # Required check
148 is_valid, error = validate_required(value, "Harvester")
149 if not is_valid:
150 return False, error
152 # Must be "nemo" or "sharepoint"
153 if value not in ("nemo", "sharepoint"):
154 return False, 'Harvester must be "nemo" or "sharepoint"'
156 return True, ""
159def get_example_values() -> dict[str, str]:
160 """
161 Get example values for instrument fields (for placeholders).
163 Returns
164 -------
165 dict[str, str]
166 Mapping of field names to example values
167 """
168 return {
169 "instrument_pid": "FEI-Quanta-650-FEG-123456",
170 "api_url": "https://nemo.example.com/api/tools/?id=42",
171 "calendar_url": "https://nemo.example.com/calendar/",
172 "location": "Building 223 Room 101",
173 "display_name": "Quanta 650 FEG SEM",
174 "property_tag": "123456",
175 "filestore_path": "./Quanta_650_FEG",
176 "harvester": "nemo",
177 "timezone_str": "America/New_York",
178 }