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

1""" 

2Instrument-specific validation functions. 

3 

4Provides validation for instrument database fields including uniqueness 

5checks and instrument-specific constraints. 

6""" 

7 

8from sqlmodel import Session 

9 

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) 

16 

17 

18def validate_instrument_pid( 

19 value: str | None, 

20) -> tuple[bool, str]: 

21 """ 

22 Validate instrument_pid field. 

23 

24 Parameters 

25 ---------- 

26 value : str | None 

27 Instrument PID to validate 

28 

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 

38 

39 # Max length check 

40 return validate_max_length(value, 100, "Instrument PID") 

41 

42 

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. 

50 

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) 

59 

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 

69 

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 ) 

77 

78 return True, "" 

79 

80 

81def validate_location( 

82 value: str | None, 

83) -> tuple[bool, str]: 

84 """ 

85 Validate location field. 

86 

87 Parameters 

88 ---------- 

89 value : str | None 

90 Location to validate 

91 

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 

101 

102 # Max length check 

103 return validate_max_length(value, 100, "Location") 

104 

105 

106def validate_property_tag( 

107 value: str | None, 

108) -> tuple[bool, str]: 

109 """ 

110 Validate property_tag field. 

111 

112 Parameters 

113 ---------- 

114 value : str | None 

115 Property tag to validate 

116 

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 

126 

127 # Max length check 

128 return validate_max_length(value, 20, "Property Tag") 

129 

130 

131def validate_harvester( 

132 value: str | None, 

133) -> tuple[bool, str]: 

134 """ 

135 Validate harvester field. 

136 

137 Parameters 

138 ---------- 

139 value : str | None 

140 Harvester name to validate 

141 

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 

151 

152 # Must be "nemo" or "sharepoint" 

153 if value not in ("nemo", "sharepoint"): 

154 return False, 'Harvester must be "nemo" or "sharepoint"' 

155 

156 return True, "" 

157 

158 

159def get_example_values() -> dict[str, str]: 

160 """ 

161 Get example values for instrument fields (for placeholders). 

162 

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 }