06/15 Phase 1 + 2 codes
This commit is contained in:
@@ -0,0 +1,75 @@
|
||||
"""Validate listing `attributes` against a category's `field_schema`.
|
||||
|
||||
Returns (cleaned_attributes, errors). Caller rejects on any errors.
|
||||
Supported field types: text, number, select, bool.
|
||||
"""
|
||||
|
||||
_HOT_COLUMNS = {"condition", "job_type", "salary_min", "salary_max"}
|
||||
|
||||
|
||||
class SchemaError(ValueError):
|
||||
pass
|
||||
|
||||
|
||||
def validate_attributes(category, raw):
|
||||
raw = raw or {}
|
||||
cleaned, errors = {}, {}
|
||||
|
||||
for field in category.fields():
|
||||
name = field["name"]
|
||||
ftype = field.get("type", "text")
|
||||
required = field.get("required", False)
|
||||
val = raw.get(name)
|
||||
|
||||
if val in (None, "", []):
|
||||
if required:
|
||||
errors[name] = "required"
|
||||
continue
|
||||
|
||||
try:
|
||||
cleaned[name] = _coerce(field, ftype, val)
|
||||
except SchemaError as e:
|
||||
errors[name] = str(e)
|
||||
|
||||
return cleaned, errors
|
||||
|
||||
|
||||
def _coerce(field, ftype, val):
|
||||
if ftype == "text":
|
||||
s = str(val).strip()
|
||||
mx = field.get("max", 255)
|
||||
if len(s) > mx:
|
||||
raise SchemaError(f"max length {mx}")
|
||||
return s
|
||||
|
||||
if ftype == "number":
|
||||
try:
|
||||
n = int(val) if str(val).lstrip("-").isdigit() else float(val)
|
||||
except (TypeError, ValueError):
|
||||
raise SchemaError("must be a number")
|
||||
if "min" in field and n < field["min"]:
|
||||
raise SchemaError(f"min {field['min']}")
|
||||
if "max" in field and n > field["max"]:
|
||||
raise SchemaError(f"max {field['max']}")
|
||||
return n
|
||||
|
||||
if ftype == "select":
|
||||
opts = field.get("options", [])
|
||||
if val not in opts:
|
||||
raise SchemaError("invalid option")
|
||||
return val
|
||||
|
||||
if ftype == "bool":
|
||||
return bool(val) if isinstance(val, bool) else str(val).lower() in ("1", "true", "on", "yes")
|
||||
|
||||
raise SchemaError("unknown field type")
|
||||
|
||||
|
||||
def hot_values(cleaned):
|
||||
"""Map cleaned attributes -> denormalized Listing hot columns."""
|
||||
return {
|
||||
"attr_condition": cleaned.get("condition"),
|
||||
"attr_job_type": cleaned.get("job_type"),
|
||||
"attr_salary_min": cleaned.get("salary_min"),
|
||||
"attr_salary_max": cleaned.get("salary_max"),
|
||||
}
|
||||
Reference in New Issue
Block a user