242 lines
No EOL
11 KiB
Python
242 lines
No EOL
11 KiB
Python
import os
|
|
import json
|
|
import logging
|
|
import re
|
|
import traceback
|
|
from typing import Dict, Any, Set, Tuple, Optional
|
|
from utils.check_helpers import record_skipped_type, prepare_skipped_result
|
|
|
|
def run_check(config):
|
|
"""
|
|
Check that the layer property value matches the layer depth in the filename for layeroptint and layeroptext image types.
|
|
Also ensures base assets have layer=0.
|
|
|
|
Expected config:
|
|
- working_dir (str): Directory where linkingrecord.json and images reside.
|
|
- linkingrecord_filename (str): Name of the linking record file. Default: linkingrecord.json
|
|
|
|
Returns:
|
|
- "passed" if all layer values match their filename depth indicators
|
|
- "passed" with skipped_types details if unknown type combinations were encountered
|
|
- "failed" if any discrepancies are found, listing each that failed
|
|
- "error" if linkingrecord is missing or invalid
|
|
"""
|
|
try:
|
|
working_dir = config.get("working_dir", "working")
|
|
linkingrecord_filename = config.get("linkingrecord_filename", "linkingrecord.json")
|
|
linkingrecord_path = os.path.join(working_dir, linkingrecord_filename)
|
|
|
|
# Track skipped viewtype/imagetype combinations
|
|
skipped_types: Dict[str, Set[Tuple[str, Optional[str]]]] = {"layer_depth_check": set()}
|
|
|
|
if not os.path.exists(linkingrecord_path):
|
|
error_msg = f"Linking record '{linkingrecord_filename}' not found in {working_dir}."
|
|
logging.error(error_msg)
|
|
return {
|
|
"status": "error",
|
|
"error_message": error_msg,
|
|
"details": {
|
|
"message": error_msg,
|
|
"working_dir_exists": os.path.exists(working_dir),
|
|
"working_dir_content": os.listdir(working_dir) if os.path.exists(working_dir) else []
|
|
}
|
|
}
|
|
|
|
# Load the linking record
|
|
try:
|
|
with open(linkingrecord_path, 'r', encoding='utf-8') as f:
|
|
linkingrecord = json.load(f)
|
|
except json.JSONDecodeError as e:
|
|
error_msg = f"Invalid JSON in '{linkingrecord_filename}': {str(e)}"
|
|
logging.error(error_msg)
|
|
return {
|
|
"status": "error",
|
|
"error_message": error_msg,
|
|
"details": {
|
|
"message": error_msg,
|
|
"file_path": linkingrecord_path,
|
|
"error_details": str(e)
|
|
}
|
|
}
|
|
except Exception as e:
|
|
error_msg = f"Error reading '{linkingrecord_filename}': {str(e)}"
|
|
logging.error(error_msg)
|
|
return {
|
|
"status": "error",
|
|
"error_message": error_msg,
|
|
"details": {
|
|
"message": error_msg,
|
|
"file_path": linkingrecord_path,
|
|
"error_details": str(e),
|
|
"traceback": traceback.format_exc()
|
|
}
|
|
}
|
|
|
|
if "items" not in linkingrecord or not isinstance(linkingrecord["items"], list):
|
|
error_msg = "Invalid linkingrecord.json: 'items' missing or not a list."
|
|
logging.error(error_msg)
|
|
return {
|
|
"status": "error",
|
|
"error_message": error_msg,
|
|
"details": {
|
|
"message": error_msg,
|
|
"linkingrecord_keys": list(linkingrecord.keys()),
|
|
"items_type": type(linkingrecord.get("items", None)).__name__
|
|
}
|
|
}
|
|
|
|
# Use a dictionary to avoid duplicates (keyed by filename)
|
|
failed_layers_dict = {}
|
|
|
|
# Function to extract layer number from filename
|
|
def extract_layer_from_filename(filename):
|
|
# Try to find a pattern like "_XX_" at the end of the filename where XX is a number
|
|
# Example: "deu/cfu08/yyy/images/deucfu08yyyacmaabs-savs-d5layeroptexta1gan_21_20.png"
|
|
# The layer number would be 20 in this case
|
|
match = re.search(r"_(\d+)\.(?:png|jpg|jpeg)$", filename, re.IGNORECASE)
|
|
if match:
|
|
return int(match.group(1))
|
|
return None
|
|
|
|
# Iterate over items in the linking record
|
|
for item_index, item in enumerate(linkingrecord["items"]):
|
|
conditions = item.get("conditions", {})
|
|
viewtype = conditions.get("viewtype")
|
|
imagetype = conditions.get("imagetype")
|
|
|
|
# Skip items with missing viewtype
|
|
if not viewtype:
|
|
logging.debug(f"Skipping item with missing viewtype: {conditions}")
|
|
continue
|
|
|
|
# Record unknown combinations
|
|
if viewtype not in ["carousel", "exterior", "interior", "vehicleselector", "lifestyle", "inventory"]:
|
|
record_skipped_type(skipped_types, "layer_depth_check", viewtype, imagetype)
|
|
continue
|
|
elif viewtype in ["exterior", "interior"] and imagetype not in [None, "layeroptint", "layeroptext", "showroom"]:
|
|
record_skipped_type(skipped_types, "layer_depth_check", viewtype, imagetype)
|
|
continue
|
|
elif viewtype == "carousel" and imagetype not in ["powertrain", "extra", "colour", "bodystyle", "series", "trim"]:
|
|
record_skipped_type(skipped_types, "layer_depth_check", viewtype, imagetype)
|
|
continue
|
|
elif viewtype == "vehicleselector" and imagetype not in ["desktop", "mobile"]:
|
|
record_skipped_type(skipped_types, "layer_depth_check", viewtype, imagetype)
|
|
continue
|
|
|
|
# Check if this is a layered image type
|
|
is_layered = imagetype in ["layeroptint", "layeroptext"]
|
|
|
|
records = item.get("records", [])
|
|
for record_index, record in enumerate(records):
|
|
assets = record.get("assets", [])
|
|
for asset_index, asset in enumerate(assets):
|
|
filename = asset.get("filename")
|
|
layer_value = asset.get("layer")
|
|
|
|
if not filename:
|
|
continue
|
|
|
|
# For layered image types
|
|
if is_layered:
|
|
# Check if layer property exists and is more than zero
|
|
if layer_value is None:
|
|
if filename not in failed_layers_dict:
|
|
failed_layers_dict[filename] = {
|
|
"filename": filename,
|
|
"imagetype": imagetype,
|
|
"issue": "Missing 'layer' property for layered image type",
|
|
"item_index": item_index,
|
|
"record_index": record_index,
|
|
"asset_index": asset_index
|
|
}
|
|
continue
|
|
|
|
if not isinstance(layer_value, int) or layer_value <= 0:
|
|
if filename not in failed_layers_dict:
|
|
failed_layers_dict[filename] = {
|
|
"filename": filename,
|
|
"imagetype": imagetype,
|
|
"layer_value": layer_value,
|
|
"issue": f"Layer value must be a positive integer greater than zero, found {layer_value}",
|
|
"item_index": item_index,
|
|
"record_index": record_index,
|
|
"asset_index": asset_index
|
|
}
|
|
continue
|
|
|
|
# Check if layer value matches filename
|
|
layer_in_filename = extract_layer_from_filename(filename)
|
|
if layer_in_filename is not None and layer_in_filename != layer_value:
|
|
if filename not in failed_layers_dict:
|
|
failed_layers_dict[filename] = {
|
|
"filename": filename,
|
|
"imagetype": imagetype,
|
|
"layer_value": layer_value,
|
|
"layer_in_filename": layer_in_filename,
|
|
"issue": f"Layer value {layer_value} does not match layer in filename {layer_in_filename}",
|
|
"item_index": item_index,
|
|
"record_index": record_index,
|
|
"asset_index": asset_index
|
|
}
|
|
# For base assets (non-layered image types)
|
|
else:
|
|
# Check if layer property exists and is zero
|
|
if layer_value is not None and layer_value != 0:
|
|
if filename not in failed_layers_dict:
|
|
failed_layers_dict[filename] = {
|
|
"filename": filename,
|
|
"imagetype": imagetype,
|
|
"layer_value": layer_value,
|
|
"issue": f"Base asset should have layer=0 or no layer property, found {layer_value}",
|
|
"item_index": item_index,
|
|
"record_index": record_index,
|
|
"asset_index": asset_index
|
|
}
|
|
|
|
# Convert dict of failures to a list
|
|
if failed_layers_dict:
|
|
result = {
|
|
"status": "failed",
|
|
"details": {
|
|
"message": "Layer depth issues found in the asset pack.",
|
|
"failed_layers": list(failed_layers_dict.values())
|
|
}
|
|
}
|
|
|
|
# Add skipped types to the result if any were found
|
|
if skipped_types["layer_depth_check"]:
|
|
result["details"]["skipped_types_message"] = f"{len(skipped_types['layer_depth_check'])} unknown viewtype/imagetype combinations were skipped."
|
|
result["details"]["skipped_types"] = [
|
|
{"viewtype": vt, "imagetype": it if it is not None else "None"}
|
|
for vt, it in skipped_types["layer_depth_check"]
|
|
]
|
|
|
|
return result
|
|
|
|
# If we skipped any types, add that to the successful result
|
|
if skipped_types["layer_depth_check"]:
|
|
result = prepare_skipped_result(skipped_types, "layer_depth_check")
|
|
return result
|
|
|
|
return {
|
|
"status": "passed",
|
|
"details": {
|
|
"message": "All layer depth values are correct."
|
|
}
|
|
}
|
|
|
|
except Exception as e:
|
|
# Catch any unexpected exceptions and return detailed error information
|
|
error_msg = f"Unexpected error in layer depth check: {str(e)}"
|
|
logging.exception(error_msg)
|
|
return {
|
|
"status": "error",
|
|
"error_message": error_msg,
|
|
"details": {
|
|
"message": error_msg,
|
|
"config": config,
|
|
"error_type": type(e).__name__,
|
|
"error_details": str(e),
|
|
"traceback": traceback.format_exc()
|
|
}
|
|
} |