Spaces:
Sleeping
Sleeping
File size: 7,527 Bytes
dddd6c6 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 | """
Field Validation Defaults - Smart defaults for activation requirement types
Provides sensible validation rules, placeholders, and help text based on field type.
Managers only specify the type, system fills in the rest automatically.
"""
from typing import Dict, Any, Optional
# Smart defaults by field type
FIELD_TYPE_DEFAULTS = {
"phone": {
"min_length": 10,
"max_length": 15,
"pattern": r"^\+?[0-9]{10,15}$",
"placeholder": "+254XXXXXXXXX",
"help_text": "Enter phone number with country code"
},
"email": {
"min_length": 5,
"max_length": 100,
"pattern": r"^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$",
"placeholder": "user@example.com",
"help_text": "Enter valid email address"
},
"mac_address": {
"min_length": 17,
"max_length": 17,
"pattern": r"^([0-9A-Fa-f]{2}[:-]){5}([0-9A-Fa-f]{2})$",
"placeholder": "AA:BB:CC:DD:EE:FF",
"help_text": "Enter MAC address (format: AA:BB:CC:DD:EE:FF)"
},
"serial_number": {
"min_length": 6,
"max_length": 30,
"pattern": r"^[A-Z0-9\-]+$",
"placeholder": "ABC123XYZ",
"help_text": "Enter equipment serial number"
},
"ip_address": {
"min_length": 7,
"max_length": 15,
"pattern": r"^((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$",
"placeholder": "192.168.1.1",
"help_text": "Enter IPv4 address"
},
"url": {
"min_length": 10,
"max_length": 500,
"pattern": r"^https?://[^\s/$.?#].[^\s]*$",
"placeholder": "https://example.com",
"help_text": "Enter valid URL starting with http:// or https://"
},
"text": {
"min_length": 1,
"max_length": 255,
"pattern": None,
"placeholder": None, # Will use field label
"help_text": None
},
"number": {
"min_length": None,
"max_length": None,
"pattern": r"^-?[0-9]+(\.[0-9]+)?$",
"placeholder": "0",
"help_text": "Enter numeric value"
},
"date": {
"min_length": None,
"max_length": None,
"pattern": r"^\d{4}-\d{2}-\d{2}$",
"placeholder": "YYYY-MM-DD",
"help_text": "Select date"
},
"boolean": {
"min_length": None,
"max_length": None,
"pattern": None,
"placeholder": None,
"help_text": None
},
"select": {
"min_length": None,
"max_length": None,
"pattern": None,
"placeholder": "Select an option",
"help_text": None
}
}
def apply_field_defaults(field_config: Dict[str, Any]) -> Dict[str, Any]:
"""
Apply smart defaults to field configuration based on type
Args:
field_config: Field configuration dict (from ActivationRequirement)
Returns:
Enhanced field config with defaults applied (non-destructive)
"""
field_type = field_config.get("type", "text")
defaults = FIELD_TYPE_DEFAULTS.get(field_type, FIELD_TYPE_DEFAULTS["text"])
# Create enhanced config (don't modify original)
enhanced = field_config.copy()
# Apply defaults only if not already specified
if enhanced.get("min_length") is None and defaults["min_length"] is not None:
enhanced["min_length"] = defaults["min_length"]
if enhanced.get("max_length") is None and defaults["max_length"] is not None:
enhanced["max_length"] = defaults["max_length"]
# Handle pattern (check both 'pattern' and deprecated 'validation_regex')
if not enhanced.get("pattern") and not enhanced.get("validation_regex"):
if defaults["pattern"]:
enhanced["pattern"] = defaults["pattern"]
elif enhanced.get("validation_regex") and not enhanced.get("pattern"):
# Migrate deprecated field
enhanced["pattern"] = enhanced["validation_regex"]
if enhanced.get("placeholder") is None:
if defaults["placeholder"]:
enhanced["placeholder"] = defaults["placeholder"]
elif field_type == "text":
# For text fields, use label as placeholder
enhanced["placeholder"] = f"Enter {enhanced.get('label', 'value').lower()}"
if enhanced.get("help_text") is None and defaults["help_text"]:
enhanced["help_text"] = defaults["help_text"]
return enhanced
def get_validation_rules(field_config: Dict[str, Any]) -> Dict[str, Any]:
"""
Get complete validation rules for frontend
Args:
field_config: Field configuration with defaults applied
Returns:
Validation rules dict ready for frontend consumption
"""
return {
"min_length": field_config.get("min_length"),
"max_length": field_config.get("max_length"),
"pattern": field_config.get("pattern"),
"placeholder": field_config.get("placeholder"),
"help_text": field_config.get("help_text"),
"required": field_config.get("required", True)
}
def validate_field_value(value: Any, field_config: Dict[str, Any]) -> tuple[bool, Optional[str]]:
"""
Validate a field value against its configuration
Args:
value: Value to validate
field_config: Field configuration with validation rules
Returns:
Tuple of (is_valid, error_message)
"""
import re
field_type = field_config.get("type", "text")
required = field_config.get("required", True)
# Check required
if required and (value is None or value == ""):
return False, f"{field_config.get('label', 'Field')} is required"
# If not required and empty, it's valid
if not required and (value is None or value == ""):
return True, None
# Convert value to string for validation
str_value = str(value)
# Check min_length
min_length = field_config.get("min_length")
if min_length is not None and len(str_value) < min_length:
return False, f"{field_config.get('label', 'Field')} must be at least {min_length} characters"
# Check max_length
max_length = field_config.get("max_length")
if max_length is not None and len(str_value) > max_length:
return False, f"{field_config.get('label', 'Field')} must not exceed {max_length} characters"
# Check pattern
pattern = field_config.get("pattern")
if pattern:
try:
if not re.match(pattern, str_value):
help_text = field_config.get("help_text", "Invalid format")
return False, f"{field_config.get('label', 'Field')}: {help_text}"
except re.error:
# Invalid regex pattern - log but don't fail validation
return True, None
# Type-specific validation
if field_type == "number":
try:
float(str_value)
except ValueError:
return False, f"{field_config.get('label', 'Field')} must be a valid number"
elif field_type == "boolean":
if str_value.lower() not in ["true", "false", "1", "0", "yes", "no"]:
return False, f"{field_config.get('label', 'Field')} must be true or false"
elif field_type == "select":
options = field_config.get("options", [])
if options and str_value not in options:
return False, f"{field_config.get('label', 'Field')} must be one of: {', '.join(options)}"
return True, None
|