File size: 7,575 Bytes
3d015cd
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
"""Creative/Design Domain Plugin

Scores creative competency based on:
- Portfolio links (Behance, Dribbble, personal site)
- Project diversity and quality
- Design tool proficiency
- Visual content analysis
"""
import re
import time
import logging
import requests
from typing import Dict, List
from .base_plugin import BaseDomainPlugin, DomainScore
from .plugin_factory import register_plugin

logger = logging.getLogger(__name__)


@register_plugin('creative')
class CreativePlugin(BaseDomainPlugin):
    """Creative/Design domain scoring plugin"""
    
    def __init__(self):
        super().__init__()
        # Design tools and platforms
        self.design_tools = [
            'figma', 'sketch', 'adobe xd', 'photoshop', 'illustrator',
            'after effects', 'premiere pro', 'blender', 'cinema 4d'
        ]
        self.portfolio_platforms = ['behance', 'dribbble', 'artstation', 'deviantart']
    
    def _get_domain_type(self) -> str:
        return 'creative'
    
    def _get_feature_weights(self) -> Dict[str, float]:
        return {
            'portfolio_quality': 0.35,
            'project_diversity': 0.25,
            'tool_proficiency': 0.20,
            'platform_presence': 0.15,
            'description_depth': 0.05
        }
    
    def get_required_fields(self) -> List[str]:
        return ['portfolio_url']
    
    def get_optional_fields(self) -> List[str]:
        return ['behance_url', 'dribbble_url', 'design_tools_text', 'project_description']
    
    def score(self, evidence_data: Dict) -> DomainScore:
        """Calculate creative domain score"""
        start_time = time.time()
        features = {}
        
        # Portfolio analysis
        portfolio_url = evidence_data.get('portfolio_url', '')
        if portfolio_url:
            features['portfolio_quality'] = self._analyze_portfolio_quality(portfolio_url)
        else:
            features['portfolio_quality'] = 0.0
        
        # Platform presence
        behance_url = evidence_data.get('behance_url', '')
        dribbble_url = evidence_data.get('dribbble_url', '')
        features['platform_presence'] = self._check_platform_presence(behance_url, dribbble_url)
        
        # Tool proficiency
        tools_text = evidence_data.get('design_tools_text', '')
        features['tool_proficiency'] = self._assess_tool_proficiency(tools_text)
        
        # Project diversity and description
        project_desc = evidence_data.get('project_description', '')
        features['project_diversity'] = self._assess_project_diversity(project_desc)
        features['description_depth'] = self._assess_description_depth(project_desc)
        
        # Calculate weighted score
        score = sum(features[k] * self.feature_weights[k] for k in features.keys())
        
        # Calculate confidence
        confidence = self.calculate_confidence(evidence_data)
        
        processing_time = (time.time() - start_time) * 1000
        
        return DomainScore(
            domain_type='creative',
            score=min(score, 1.0),
            confidence=confidence,
            raw_features=features,
            processing_time_ms=processing_time
        )
    
    def _analyze_portfolio_quality(self, portfolio_url: str) -> float:
        """
        Analyze portfolio website quality
        Returns: 0-1 score based on accessibility and professionalism
        """
        try:
            if not portfolio_url.startswith(('http://', 'https://')):
                portfolio_url = 'https://' + portfolio_url
            
            response = requests.head(portfolio_url, timeout=5, allow_redirects=True)
            
            if response.status_code == 200:
                score = 0.6  # Base score for accessible portfolio
                
                # Bonus for professional platforms
                if any(platform in portfolio_url for platform in self.portfolio_platforms):
                    score += 0.2
                
                # Bonus for custom domain
                if not any(free in portfolio_url for free in ['github.io', 'wixsite', 'wordpress.com']):
                    score += 0.2
                
                logger.info(f"Portfolio quality: {score:.2f}")
                return min(score, 1.0)
            else:
                return 0.2
                
        except Exception as e:
            logger.error(f"Error analyzing portfolio: {e}")
            return 0.2
    
    def _check_platform_presence(self, behance_url: str, dribbble_url: str) -> float:
        """
        Check presence on design platforms
        Returns: 0-1 score based on platform profiles
        """
        score = 0.0
        
        # Behance presence
        if behance_url and 'behance.net' in behance_url:
            try:
                response = requests.head(behance_url, timeout=5, allow_redirects=True)
                if response.status_code == 200:
                    score += 0.5
            except:
                score += 0.2  # Partial credit for providing URL
        
        # Dribbble presence
        if dribbble_url and 'dribbble.com' in dribbble_url:
            try:
                response = requests.head(dribbble_url, timeout=5, allow_redirects=True)
                if response.status_code == 200:
                    score += 0.5
            except:
                score += 0.2
        
        logger.info(f"Platform presence: {score:.2f}")
        return min(score, 1.0)
    
    def _assess_tool_proficiency(self, tools_text: str) -> float:
        """
        Assess design tool proficiency
        Returns: 0-1 score based on tool mentions
        """
        if not tools_text:
            return 0.0
        
        text_lower = tools_text.lower()
        
        # Count tool mentions
        tool_count = sum(1 for tool in self.design_tools if tool in text_lower)
        
        # Score based on tool diversity
        score = min(tool_count / 5, 1.0)  # 5+ tools = max
        
        # Bonus for professional tools (Adobe, Figma)
        pro_tools = ['figma', 'adobe', 'sketch']
        if any(tool in text_lower for tool in pro_tools):
            score = min(score + 0.2, 1.0)
        
        logger.info(f"Tool proficiency: {score:.2f} ({tool_count} tools)")
        return score
    
    def _assess_project_diversity(self, project_desc: str) -> float:
        """
        Assess project type diversity
        Returns: 0-1 score based on project variety
        """
        if not project_desc:
            return 0.0
        
        text_lower = project_desc.lower()
        
        # Project type categories
        project_types = [
            'ui design', 'ux design', 'branding', 'logo', 'illustration',
            'animation', '3d', 'web design', 'mobile app', 'poster',
            'packaging', 'typography', 'infographic', 'video editing'
        ]
        
        type_count = sum(1 for ptype in project_types if ptype in text_lower)
        score = min(type_count / 6, 1.0)  # 6+ types = max
        
        logger.info(f"Project diversity: {score:.2f} ({type_count} types)")
        return score
    
    def _assess_description_depth(self, project_desc: str) -> float:
        """
        Assess depth of project descriptions
        Returns: 0-1 score based on detail level
        """
        if not project_desc or len(project_desc) < 50:
            return 0.0
        
        score = min(len(project_desc) / 1000, 1.0)  # 1000+ chars = max
        
        logger.info(f"Description depth: {score:.2f}")
        return score