Flatten skill category directory structure
This commit is contained in:
@@ -0,0 +1,116 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Dynamic poster brief validator.
|
||||
Validates the format and content of a poster brief file.
|
||||
|
||||
Usage:
|
||||
python3 validate_poster_brief.py <brief_path>
|
||||
"""
|
||||
|
||||
import argparse
|
||||
import re
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
def validate_brief(brief_path: str) -> tuple[bool, list[str]]:
|
||||
"""Validate a dynamic poster brief file. Returns (passed, errors)."""
|
||||
errors = []
|
||||
warnings = []
|
||||
path = Path(brief_path)
|
||||
|
||||
if not path.exists():
|
||||
return False, [f"File not found: {brief_path}"]
|
||||
|
||||
content = path.read_text(encoding="utf-8")
|
||||
|
||||
# --- Required fields ---
|
||||
required_fields = {
|
||||
"Brand": r"Brand\s*[::]\s*\S+",
|
||||
"Product Category": r"Product Category\s*[::]\s*\S+",
|
||||
"Brand Colors": r"Brand Colors\s*[::]\s*\S+",
|
||||
"Product Texture": r"Product Texture\s*[::]\s*\S+",
|
||||
"Key Visual Elements": r"Key Visual Elements\s*[::]\s*\S+",
|
||||
}
|
||||
|
||||
for field, pattern in required_fields.items():
|
||||
if not re.search(pattern, content):
|
||||
errors.append(f"Missing or empty required field: {field}")
|
||||
|
||||
# --- Brand Colors validation ---
|
||||
color_match = re.search(r"Brand Colors\s*[::]\s*(.+)", content)
|
||||
if color_match:
|
||||
color_line = color_match.group(1)
|
||||
hex_colors = re.findall(r"#[0-9A-Fa-f]{6}", color_line)
|
||||
if not hex_colors:
|
||||
errors.append(
|
||||
"Brand Colors must include at least one hex color value (e.g., #2D5A3D)"
|
||||
)
|
||||
|
||||
# --- Aspect Ratio validation ---
|
||||
ratio_match = re.search(r"Aspect Ratio\s*[::]\s*(.+)", content)
|
||||
if ratio_match:
|
||||
ratio = ratio_match.group(1).strip()
|
||||
valid_ratios = {"9:16", "16:9", "4:5", "1:1"}
|
||||
if ratio not in valid_ratios:
|
||||
warnings.append(
|
||||
f"Aspect Ratio '{ratio}' not standard. Expected: {', '.join(sorted(valid_ratios))}"
|
||||
)
|
||||
|
||||
# --- Logo fields (optional but validate if present) ---
|
||||
logo_match = re.search(r"Logo Type\s*[::]\s*(.+)", content)
|
||||
if logo_match:
|
||||
logo_type = logo_match.group(1).strip().lower()
|
||||
valid_logo_types = {"wordmark", "icon", "combo", "none"}
|
||||
if not any(lt in logo_type for lt in valid_logo_types):
|
||||
warnings.append(
|
||||
f"Logo Type '{logo_match.group(1).strip()}' not recognized. "
|
||||
f"Expected: {', '.join(sorted(valid_logo_types))}"
|
||||
)
|
||||
|
||||
# --- Creative Concept validation (if present in same file) ---
|
||||
has_concept = "# Creative Concept" in content or "# Selected Concept" in content
|
||||
if has_concept:
|
||||
if not re.search(r"Creative Dimension\s*[::]\s*\S+", content):
|
||||
errors.append("Creative Concept found but missing 'Creative Dimension'")
|
||||
if not re.search(r"The Impossible Thing\s*[::]\s*\S+", content):
|
||||
errors.append("Creative Concept found but missing 'The Impossible Thing'")
|
||||
if not re.search(r"Conflict", content):
|
||||
warnings.append("Creative Concept missing conflict arc (Setup → Escalation → Payoff)")
|
||||
|
||||
passed = len(errors) == 0
|
||||
return passed, errors + [f"WARNING: {w}" for w in warnings]
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description="Validate dynamic poster brief format")
|
||||
parser.add_argument("brief_path", help="Path to the brief.md file")
|
||||
args = parser.parse_args()
|
||||
|
||||
passed, messages = validate_brief(args.brief_path)
|
||||
|
||||
errors = [m for m in messages if not m.startswith("WARNING:")]
|
||||
warnings = [m for m in messages if m.startswith("WARNING:")]
|
||||
|
||||
if passed:
|
||||
print("PASSED: Brief validation successful")
|
||||
print(f" File: {args.brief_path}")
|
||||
if warnings:
|
||||
print(f" Warnings: {len(warnings)}")
|
||||
for w in warnings:
|
||||
print(f" - {w}")
|
||||
sys.exit(0)
|
||||
else:
|
||||
print(f"FAILED: Brief validation found {len(errors)} error(s)")
|
||||
print(f" File: {args.brief_path}")
|
||||
for i, err in enumerate(errors, 1):
|
||||
print(f" {i}. {err}")
|
||||
if warnings:
|
||||
print(f" Warnings:")
|
||||
for w in warnings:
|
||||
print(f" - {w}")
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,116 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Dynamic poster brief validator.
|
||||
Validates the format and content of a poster brief file.
|
||||
|
||||
Usage:
|
||||
python3 validate_poster_brief.py <brief_path>
|
||||
"""
|
||||
|
||||
import argparse
|
||||
import re
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
def validate_brief(brief_path: str) -> tuple[bool, list[str]]:
|
||||
"""Validate a dynamic poster brief file. Returns (passed, errors)."""
|
||||
errors = []
|
||||
warnings = []
|
||||
path = Path(brief_path)
|
||||
|
||||
if not path.exists():
|
||||
return False, [f"File not found: {brief_path}"]
|
||||
|
||||
content = path.read_text(encoding="utf-8")
|
||||
|
||||
# --- Required fields ---
|
||||
required_fields = {
|
||||
"Brand": r"Brand\s*[::]\s*\S+",
|
||||
"Product Category": r"Product Category\s*[::]\s*\S+",
|
||||
"Brand Colors": r"Brand Colors\s*[::]\s*\S+",
|
||||
"Product Texture": r"Product Texture\s*[::]\s*\S+",
|
||||
"Key Visual Elements": r"Key Visual Elements\s*[::]\s*\S+",
|
||||
}
|
||||
|
||||
for field, pattern in required_fields.items():
|
||||
if not re.search(pattern, content):
|
||||
errors.append(f"Missing or empty required field: {field}")
|
||||
|
||||
# --- Brand Colors validation ---
|
||||
color_match = re.search(r"Brand Colors\s*[::]\s*(.+)", content)
|
||||
if color_match:
|
||||
color_line = color_match.group(1)
|
||||
hex_colors = re.findall(r"#[0-9A-Fa-f]{6}", color_line)
|
||||
if not hex_colors:
|
||||
errors.append(
|
||||
"Brand Colors must include at least one hex color value (e.g., #2D5A3D)"
|
||||
)
|
||||
|
||||
# --- Aspect Ratio validation ---
|
||||
ratio_match = re.search(r"Aspect Ratio\s*[::]\s*(.+)", content)
|
||||
if ratio_match:
|
||||
ratio = ratio_match.group(1).strip()
|
||||
valid_ratios = {"9:16", "16:9", "4:5", "1:1"}
|
||||
if ratio not in valid_ratios:
|
||||
warnings.append(
|
||||
f"Aspect Ratio '{ratio}' not standard. Expected: {', '.join(sorted(valid_ratios))}"
|
||||
)
|
||||
|
||||
# --- Logo fields (optional but validate if present) ---
|
||||
logo_match = re.search(r"Logo Type\s*[::]\s*(.+)", content)
|
||||
if logo_match:
|
||||
logo_type = logo_match.group(1).strip().lower()
|
||||
valid_logo_types = {"wordmark", "icon", "combo", "none"}
|
||||
if not any(lt in logo_type for lt in valid_logo_types):
|
||||
warnings.append(
|
||||
f"Logo Type '{logo_match.group(1).strip()}' not recognized. "
|
||||
f"Expected: {', '.join(sorted(valid_logo_types))}"
|
||||
)
|
||||
|
||||
# --- Creative Concept validation (if present in same file) ---
|
||||
has_concept = "# Creative Concept" in content or "# Selected Concept" in content
|
||||
if has_concept:
|
||||
if not re.search(r"Creative Dimension\s*[::]\s*\S+", content):
|
||||
errors.append("Creative Concept found but missing 'Creative Dimension'")
|
||||
if not re.search(r"The Impossible Thing\s*[::]\s*\S+", content):
|
||||
errors.append("Creative Concept found but missing 'The Impossible Thing'")
|
||||
if not re.search(r"Conflict", content):
|
||||
warnings.append("Creative Concept missing conflict arc (Setup → Escalation → Payoff)")
|
||||
|
||||
passed = len(errors) == 0
|
||||
return passed, errors + [f"WARNING: {w}" for w in warnings]
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description="Validate dynamic poster brief format")
|
||||
parser.add_argument("brief_path", help="Path to the brief.md file")
|
||||
args = parser.parse_args()
|
||||
|
||||
passed, messages = validate_brief(args.brief_path)
|
||||
|
||||
errors = [m for m in messages if not m.startswith("WARNING:")]
|
||||
warnings = [m for m in messages if m.startswith("WARNING:")]
|
||||
|
||||
if passed:
|
||||
print("PASSED: Brief validation successful")
|
||||
print(f" File: {args.brief_path}")
|
||||
if warnings:
|
||||
print(f" Warnings: {len(warnings)}")
|
||||
for w in warnings:
|
||||
print(f" - {w}")
|
||||
sys.exit(0)
|
||||
else:
|
||||
print(f"FAILED: Brief validation found {len(errors)} error(s)")
|
||||
print(f" File: {args.brief_path}")
|
||||
for i, err in enumerate(errors, 1):
|
||||
print(f" {i}. {err}")
|
||||
if warnings:
|
||||
print(f" Warnings:")
|
||||
for w in warnings:
|
||||
print(f" - {w}")
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user