Files
our-claude-skills/custom-skills/40-jamie-brand-editor/code/scripts/compliance_checker.py
Andrew Yim 236be6c580 directory changes and restructuring
🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-22 02:01:41 +09:00

274 lines
12 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""
Jamie Marketing Brand Editor - Compliance Checker
==================================================
This script automatically scans marketing content for Korean medical advertising
law violations (의료법 제56조) and flags problematic content.
Usage:
python compliance_checker.py --input content.txt --output report.json
Or import as module:
from compliance_checker import ComplianceChecker
checker = ComplianceChecker()
results = checker.check_content(content_text)
"""
import re
import json
from typing import Dict, List, Tuple
from dataclasses import dataclass, asdict
@dataclass
class ComplianceViolation:
"""Represents a single compliance violation found in content"""
violation_type: str
severity: str # 'critical', 'high', 'medium', 'low'
location: Tuple[int, int] # (start_pos, end_pos)
matched_text: str
explanation_korean: str
suggestion: str
legal_reference: str
class ComplianceChecker:
"""
Checks marketing content for violations of Korean medical advertising law.
"""
def __init__(self):
self.prohibited_patterns = self._load_prohibited_patterns()
self.required_disclaimers = self._load_required_disclaimers()
def _load_prohibited_patterns(self) -> Dict[str, List[str]]:
"""
Load regex patterns for prohibited content types.
"""
return {
'effect_guarantee': [
r'100[%]\s*(?:만족|효과|성공)',
r'반드시\s+(?:효과|개선|만족)',
r'완벽한?\s+결과',
r'보장합니다',
r'확실한?\s+효과',
],
'comparative_superiority': [
r'최고의?',
r'1위',
r'(?:압구정|강남|서울|국내)\s*(?:최고|1위)',
r'\s*병원보다',
r'다른\s*(?:병원|의원)보다\s*우수',
],
'safety_guarantee': [
r'부작용\s*(?:없|無)',
r'(?:100[%]|완전히?|절대)\s*안전',
r'위험\s*(?:없|無)',
],
'patient_testimonial': [
r'(?:환자|고객)\s*[A-Z가-힣]+\s*(?:씨|님)의?\s*(?:후기|경험)',
r'실제\s*(?:환자|고객)\s*(?:후기|리뷰|경험담)',
r'[""]\s*(?:정말|너무|진짜)\s+(?:만족|좋아요|감사)', # Quoted testimonials
r'수술\s*후\s*[0-9]+\s*(?:개월|주일|년)\s*(?:만족|경과)',
],
'exaggeration': [
r'(?:놀라운|대박|극적인)\s*(?:변화|효과|결과)',
r'마법같은?',
r'기적적인?',
],
}
def _load_required_disclaimers(self) -> Dict[str, str]:
"""
Load templates for required disclaimers.
"""
return {
'general_surgery': '※ 모든 수술 및 시술은 개인에 따라 붓기, 멍, 염증 등의 부작용이 발생할 수 있습니다.',
'individual_variation': '※ 수술 결과는 개인의 특성에 따라 차이가 있을 수 있습니다.',
'consultation_required': '※ 수술 전 반드시 전문의와 충분한 상담을 통해 결정하시기 바랍니다.',
}
def check_content(self, content: str) -> Dict:
"""
Main method to check content for compliance violations.
Args:
content: Korean text content to check
Returns:
Dictionary containing violations and recommendations
"""
violations = []
# Check for prohibited patterns
for violation_type, patterns in self.prohibited_patterns.items():
for pattern in patterns:
for match in re.finditer(pattern, content, re.IGNORECASE):
violation = self._create_violation(
violation_type=violation_type,
match=match,
content=content
)
violations.append(violation)
# Check for missing required disclaimers
disclaimer_issues = self._check_disclaimers(content)
violations.extend(disclaimer_issues)
# Generate compliance report
report = {
'is_compliant': len(violations) == 0,
'total_violations': len(violations),
'violations_by_severity': self._count_by_severity(violations),
'violations': [asdict(v) for v in violations],
'recommendations': self._generate_recommendations(violations),
'required_disclaimers': list(self.required_disclaimers.values())
}
return report
def _create_violation(self, violation_type: str, match, content: str) -> ComplianceViolation:
"""
Create a ComplianceViolation object from a regex match.
"""
explanations = {
'effect_guarantee': '효과를 보장하는 표현은 의료법 제56조 제2항 제2호 위반입니다.',
'comparative_superiority': '타 의료기관과의 비교 우위 주장은 의료법 제56조 제2항 제4호 위반입니다.',
'safety_guarantee': '안전성을 보장하거나 부작용이 없다는 표현은 의료법 제56조 제2항 제7호 위반입니다.',
'patient_testimonial': '환자 치료경험담은 의료법 제56조 제2항 제2호 위반입니다.',
'exaggeration': '과장된 표현은 객관적 사실과 다른 내용으로 의료법 제56조 제2항 제3호 위반 가능성이 있습니다.',
}
suggestions = {
'effect_guarantee': '"개선에 도움을 줄 수 있습니다" 또는 "개인에 따라 결과가 다를 수 있습니다"와 같은 표현으로 변경하세요.',
'comparative_superiority': '"풍부한 경험을 보유한" 또는 "전문적인"과 같은 객관적 표현으로 변경하세요.',
'safety_guarantee': '부작용 가능성을 명시하고 "안전한 수술을 위해 최선을 다합니다"와 같은 표현으로 변경하세요.',
'patient_testimonial': '개인 환자 경험담 대신 통계적 데이터나 일반적인 수술 과정 설명으로 대체하세요.',
'exaggeration': '객관적이고 절제된 표현으로 변경하세요. 예: "자연스러운 개선", "점진적인 효과"',
}
severity_map = {
'effect_guarantee': 'critical',
'comparative_superiority': 'critical',
'safety_guarantee': 'critical',
'patient_testimonial': 'critical',
'exaggeration': 'high',
}
return ComplianceViolation(
violation_type=violation_type,
severity=severity_map.get(violation_type, 'medium'),
location=(match.start(), match.end()),
matched_text=match.group(),
explanation_korean=explanations.get(violation_type, ''),
suggestion=suggestions.get(violation_type, ''),
legal_reference='의료법 제56조'
)
def _check_disclaimers(self, content: str) -> List[ComplianceViolation]:
"""
Check if required disclaimers are present in content.
"""
violations = []
# Check if content discusses surgery/procedures
procedure_keywords = ['수술', '시술', '이마거상', '쌍꺼풀', '리프팅', '보톡스', '필러']
has_procedure_content = any(keyword in content for keyword in procedure_keywords)
if has_procedure_content:
# Check for required disclaimers
has_side_effect_notice = any(term in content for term in ['부작용', '합병증', '붓기', ''])
has_individual_variation = '개인' in content and any(term in content for term in ['차이', '다를 수'])
if not has_side_effect_notice:
violations.append(ComplianceViolation(
violation_type='missing_disclaimer',
severity='high',
location=(-1, -1),
matched_text='',
explanation_korean='부작용 가능성에 대한 고지가 누락되었습니다.',
suggestion='페이지 하단에 "※ 모든 수술 및 시술은 개인에 따라 붓기, 멍, 염증 등의 부작용이 발생할 수 있습니다." 문구를 추가하세요.',
legal_reference='의료법 제56조 제2항 제7호'
))
if not has_individual_variation:
violations.append(ComplianceViolation(
violation_type='missing_disclaimer',
severity='medium',
location=(-1, -1),
matched_text='',
explanation_korean='개인차에 대한 고지가 누락되었습니다.',
suggestion='"개인에 따라 결과가 다를 수 있습니다" 문구를 추가하세요.',
legal_reference='의료법 시행령 제23조'
))
return violations
def _count_by_severity(self, violations: List[ComplianceViolation]) -> Dict[str, int]:
"""Count violations by severity level."""
counts = {'critical': 0, 'high': 0, 'medium': 0, 'low': 0}
for v in violations:
counts[v.severity] += 1
return counts
def _generate_recommendations(self, violations: List[ComplianceViolation]) -> List[str]:
"""Generate actionable recommendations based on violations found."""
recommendations = []
if any(v.violation_type == 'patient_testimonial' for v in violations):
recommendations.append('환자 후기를 제거하고 통계적 만족도 데이터로 대체하세요.')
if any(v.violation_type in ['effect_guarantee', 'safety_guarantee'] for v in violations):
recommendations.append('절대적인 보장 표현을 가능성 표현으로 변경하세요. 예: "도움을 줄 수 있습니다"')
if any(v.violation_type == 'comparative_superiority' for v in violations):
recommendations.append('비교 우위 표현을 제거하고 객관적 사실(경력, 경험)로 대체하세요.')
if any(v.violation_type == 'missing_disclaimer' for v in violations):
recommendations.append('페이지 하단에 필수 고지사항을 추가하세요.')
return recommendations
def main():
"""Command-line interface for compliance checker."""
import argparse
parser = argparse.ArgumentParser(description='Check medical marketing content for compliance')
parser.add_argument('--input', '-i', required=True, help='Input content file')
parser.add_argument('--output', '-o', default='compliance_report.json', help='Output report file')
parser.add_argument('--verbose', '-v', action='store_true', help='Print detailed output')
args = parser.parse_args()
# Read input content
with open(args.input, 'r', encoding='utf-8') as f:
content = f.read()
# Run compliance check
checker = ComplianceChecker()
report = checker.check_content(content)
# Save report
with open(args.output, 'w', encoding='utf-8') as f:
json.dump(report, f, ensure_ascii=False, indent=2)
# Print summary
print(f"Compliance Check Complete")
print(f"==========================================")
print(f"Compliant: {'YES ✓' if report['is_compliant'] else 'NO ✗'}")
print(f"Total Violations: {report['total_violations']}")
print(f" - Critical: {report['violations_by_severity']['critical']}")
print(f" - High: {report['violations_by_severity']['high']}")
print(f" - Medium: {report['violations_by_severity']['medium']}")
print(f" - Low: {report['violations_by_severity']['low']}")
print(f"\nReport saved to: {args.output}")
if args.verbose and report['violations']:
print(f"\nViolations Found:")
for v in report['violations']:
print(f"\n Type: {v['violation_type']}")
print(f" Severity: {v['severity']}")
print(f" Text: '{v['matched_text']}'")
print(f" Explanation: {v['explanation_korean']}")
if __name__ == '__main__':
main()