1313@dataclass
1414class BrandGuidelines :
1515 """Brand guidelines configuration"""
16+
1617 brand_name : str
1718 primary_colors : List [str ]
1819 secondary_colors : List [str ]
@@ -26,6 +27,7 @@ class BrandGuidelines:
2627@dataclass
2728class ValidationResult :
2829 """Result of brand validation"""
30+
2931 passed : bool
3032 score : float
3133 violations : List [str ]
@@ -35,179 +37,174 @@ class ValidationResult:
3537
3638class BrandValidator :
3739 """Validates content against brand guidelines"""
38-
40+
3941 def __init__ (self , guidelines : BrandGuidelines ):
4042 self .guidelines = guidelines
41-
43+
4244 def validate_colors (self , content : str ) -> Tuple [List [str ], List [str ]]:
4345 """
4446 Validate color usage in content (hex codes, RGB, color names)
4547 Returns: (violations, warnings)
4648 """
4749 violations = []
4850 warnings = []
49-
51+
5052 # Find hex colors
51- hex_pattern = r' #[0-9A-Fa-f]{6}|#[0-9A-Fa-f]{3}'
53+ hex_pattern = r" #[0-9A-Fa-f]{6}|#[0-9A-Fa-f]{3}"
5254 found_colors = re .findall (hex_pattern , content )
53-
55+
5456 # Find RGB colors
55- rgb_pattern = r' rgb\s*\(\s*\d{1,3}\s*,\s*\d{1,3}\s*,\s*\d{1,3}\s*\)'
57+ rgb_pattern = r" rgb\s*\(\s*\d{1,3}\s*,\s*\d{1,3}\s*,\s*\d{1,3}\s*\)"
5658 found_colors .extend (re .findall (rgb_pattern , content , re .IGNORECASE ))
57-
59+
5860 approved_colors = self .guidelines .primary_colors + self .guidelines .secondary_colors
59-
61+
6062 for color in found_colors :
6163 if color .upper () not in [c .upper () for c in approved_colors ]:
6264 violations .append (f"Unapproved color used: { color } " )
63-
65+
6466 return violations , warnings
65-
67+
6668 def validate_fonts (self , content : str ) -> Tuple [List [str ], List [str ]]:
6769 """
6870 Validate font usage in content
6971 Returns: (violations, warnings)
7072 """
7173 violations = []
7274 warnings = []
73-
75+
7476 # Common font specification patterns
7577 font_patterns = [
7678 r'font-family\s*:\s*["\']?([^;"\']+)["\']?' ,
77- r' font:\s*[^;]*\s+([A-Za-z][A-Za-z\s]+)(?:,|;|\s+\d)' ,
79+ r" font:\s*[^;]*\s+([A-Za-z][A-Za-z\s]+)(?:,|;|\s+\d)" ,
7880 ]
79-
81+
8082 found_fonts = []
8183 for pattern in font_patterns :
8284 matches = re .findall (pattern , content , re .IGNORECASE )
8385 found_fonts .extend (matches )
84-
85- approved_fonts_lower = [f .lower () for f in self .guidelines .fonts ]
86-
86+
8787 for font in found_fonts :
8888 font_clean = font .strip ().lower ()
8989 # Check if any approved font is in the found font string
9090 if not any (approved .lower () in font_clean for approved in self .guidelines .fonts ):
9191 violations .append (f"Unapproved font used: { font } " )
92-
92+
9393 return violations , warnings
94-
94+
9595 def validate_tone (self , content : str ) -> Tuple [List [str ], List [str ]]:
9696 """
9797 Validate tone and messaging
9898 Returns: (violations, warnings)
9999 """
100100 violations = []
101101 warnings = []
102-
102+
103103 # Check for prohibited words
104104 content_lower = content .lower ()
105105 for word in self .guidelines .prohibited_words :
106106 if word .lower () in content_lower :
107107 violations .append (f"Prohibited word/phrase used: '{ word } '" )
108-
108+
109109 # Check for tone keywords (should have at least some)
110- tone_matches = sum (1 for keyword in self .guidelines .tone_keywords
111- if keyword .lower () in content_lower )
112-
110+ tone_matches = sum (
111+ 1 for keyword in self .guidelines .tone_keywords if keyword .lower () in content_lower
112+ )
113+
113114 if tone_matches == 0 and len (content ) > 100 :
114115 warnings .append (
115116 f"Content may not align with brand tone. "
116117 f"Consider using terms like: { ', ' .join (self .guidelines .tone_keywords [:5 ])} "
117118 )
118-
119+
119120 return violations , warnings
120-
121+
121122 def validate_brand_name (self , content : str ) -> Tuple [List [str ], List [str ]]:
122123 """
123124 Validate brand name usage and capitalization
124125 Returns: (violations, warnings)
125126 """
126127 violations = []
127128 warnings = []
128-
129+
129130 # Find all variations of the brand name
130131 brand_pattern = re .compile (re .escape (self .guidelines .brand_name ), re .IGNORECASE )
131132 matches = brand_pattern .findall (content )
132-
133+
133134 for match in matches :
134135 if match != self .guidelines .brand_name :
135136 violations .append (
136137 f"Incorrect brand name capitalization: '{ match } ' "
137138 f"should be '{ self .guidelines .brand_name } '"
138139 )
139-
140+
140141 return violations , warnings
141-
142+
142143 def calculate_score (self , violations : List [str ], warnings : List [str ]) -> float :
143144 """Calculate compliance score (0-100)"""
144145 violation_penalty = len (violations ) * 10
145146 warning_penalty = len (warnings ) * 3
146-
147+
147148 score = max (0 , 100 - violation_penalty - warning_penalty )
148149 return round (score , 2 )
149-
150+
150151 def generate_suggestions (self , violations : List [str ], warnings : List [str ]) -> List [str ]:
151152 """Generate helpful suggestions based on violations and warnings"""
152153 suggestions = []
153-
154+
154155 if any ("color" in v .lower () for v in violations ):
155156 suggestions .append (
156157 f"Use approved colors: Primary: { ', ' .join (self .guidelines .primary_colors [:3 ])} "
157158 )
158-
159+
159160 if any ("font" in v .lower () for v in violations ):
160- suggestions .append (
161- f"Use approved fonts: { ', ' .join (self .guidelines .fonts )} "
162- )
163-
161+ suggestions .append (f"Use approved fonts: { ', ' .join (self .guidelines .fonts )} " )
162+
164163 if any ("tone" in w .lower () for w in warnings ):
165164 suggestions .append (
166165 f"Incorporate brand tone keywords: { ', ' .join (self .guidelines .tone_keywords [:5 ])} "
167166 )
168-
167+
169168 if any ("brand name" in v .lower () for v in violations ):
170- suggestions .append (
171- f"Always capitalize brand name as: { self .guidelines .brand_name } "
172- )
173-
169+ suggestions .append (f"Always capitalize brand name as: { self .guidelines .brand_name } " )
170+
174171 return suggestions
175-
172+
176173 def validate (self , content : str ) -> ValidationResult :
177174 """
178175 Perform complete brand validation
179176 Returns: ValidationResult
180177 """
181178 all_violations = []
182179 all_warnings = []
183-
180+
184181 # Run all validation checks
185182 color_v , color_w = self .validate_colors (content )
186183 all_violations .extend (color_v )
187184 all_warnings .extend (color_w )
188-
185+
189186 font_v , font_w = self .validate_fonts (content )
190187 all_violations .extend (font_v )
191188 all_warnings .extend (font_w )
192-
189+
193190 tone_v , tone_w = self .validate_tone (content )
194191 all_violations .extend (tone_v )
195192 all_warnings .extend (tone_w )
196-
193+
197194 brand_v , brand_w = self .validate_brand_name (content )
198195 all_violations .extend (brand_v )
199196 all_warnings .extend (brand_w )
200-
197+
201198 # Calculate score and generate suggestions
202199 score = self .calculate_score (all_violations , all_warnings )
203200 suggestions = self .generate_suggestions (all_violations , all_warnings )
204-
201+
205202 return ValidationResult (
206203 passed = len (all_violations ) == 0 ,
207204 score = score ,
208205 violations = all_violations ,
209206 warnings = all_warnings ,
210- suggestions = suggestions
207+ suggestions = suggestions ,
211208 )
212209
213210
@@ -227,15 +224,13 @@ def load_guidelines_from_json(filepath: str) -> BrandGuidelines:
227224 TypeError: If required fields are missing
228225 """
229226 try :
230- with open (filepath , 'r' ) as f :
227+ with open (filepath , "r" ) as f :
231228 data = json .load (f )
232229 return BrandGuidelines (** data )
233230 except FileNotFoundError :
234231 raise FileNotFoundError (f"Brand guidelines file not found: { filepath } " )
235232 except json .JSONDecodeError as e :
236- raise json .JSONDecodeError (
237- f"Invalid JSON in brand guidelines file: { e .msg } " , e .doc , e .pos
238- )
233+ raise json .JSONDecodeError (f"Invalid JSON in brand guidelines file: { e .msg } " , e .doc , e .pos )
239234 except TypeError as e :
240235 raise TypeError (f"Missing required fields in brand guidelines: { e } " )
241236
@@ -253,11 +248,24 @@ def get_acme_corporation_guidelines() -> BrandGuidelines:
253248 return BrandGuidelines (
254249 brand_name = "Acme Corporation" ,
255250 primary_colors = ["#0066CC" , "#003366" , "#FFFFFF" ], # Acme Blue, Acme Navy, White
256- secondary_colors = ["#28A745" , "#FFC107" , "#DC3545" , "#6C757D" , "#F8F9FA" ], # Success Green, Warning Amber, Error Red, Neutral Gray, Light Gray
251+ secondary_colors = [
252+ "#28A745" ,
253+ "#FFC107" ,
254+ "#DC3545" ,
255+ "#6C757D" ,
256+ "#F8F9FA" ,
257+ ], # Success Green, Warning Amber, Error Red, Neutral Gray, Light Gray
257258 fonts = ["Segoe UI" , "system-ui" , "-apple-system" , "sans-serif" ],
258- tone_keywords = ["innovation" , "excellence" , "professional" , "solutions" , "trusted" , "reliable" ],
259+ tone_keywords = [
260+ "innovation" ,
261+ "excellence" ,
262+ "professional" ,
263+ "solutions" ,
264+ "trusted" ,
265+ "reliable" ,
266+ ],
259267 prohibited_words = ["cheap" , "outdated" , "inferior" , "unprofessional" , "sloppy" ],
260- tagline = "Innovation Through Excellence"
268+ tagline = "Innovation Through Excellence" ,
261269 )
262270
263271
@@ -279,35 +287,35 @@ def main():
279287 Color scheme: #FF0000
280288 Background: rgb(255, 0, 0)
281289 """
282-
290+
283291 # Validate
284292 validator = BrandValidator (guidelines )
285293 result = validator .validate (test_content )
286-
294+
287295 # Print results
288296 print ("=" * 60 )
289- print (f "BRAND VALIDATION REPORT" )
297+ print ("BRAND VALIDATION REPORT" )
290298 print ("=" * 60 )
291299 print (f"\n Overall Status: { '✓ PASSED' if result .passed else '✗ FAILED' } " )
292300 print (f"Compliance Score: { result .score } /100" )
293-
301+
294302 if result .violations :
295303 print (f"\n ❌ VIOLATIONS ({ len (result .violations )} ):" )
296304 for i , violation in enumerate (result .violations , 1 ):
297305 print (f" { i } . { violation } " )
298-
306+
299307 if result .warnings :
300308 print (f"\n ⚠️ WARNINGS ({ len (result .warnings )} ):" )
301309 for i , warning in enumerate (result .warnings , 1 ):
302310 print (f" { i } . { warning } " )
303-
311+
304312 if result .suggestions :
305- print (f "\n 💡 SUGGESTIONS:" )
313+ print ("\n 💡 SUGGESTIONS:" )
306314 for i , suggestion in enumerate (result .suggestions , 1 ):
307315 print (f" { i } . { suggestion } " )
308-
316+
309317 print ("\n " + "=" * 60 )
310-
318+
311319 # Return JSON for programmatic use
312320 return asdict (result )
313321
0 commit comments