feat: Add logo recommendation scoring to candidate gallery
Some checks are pending
NordaBiz Tests / E2E Tests (Playwright) (push) Blocked by required conditions
NordaBiz Tests / Smoke Tests (Production) (push) Blocked by required conditions
NordaBiz Tests / Send Failure Notification (push) Blocked by required conditions
NordaBiz Tests / Unit & Integration Tests (push) Waiting to run
Some checks are pending
NordaBiz Tests / E2E Tests (Playwright) (push) Blocked by required conditions
NordaBiz Tests / Smoke Tests (Production) (push) Blocked by required conditions
NordaBiz Tests / Send Failure Notification (push) Blocked by required conditions
NordaBiz Tests / Unit & Integration Tests (push) Waiting to run
Score candidates based on format (SVG > raster), source reliability (img_scan > apple-touch-icon > og:image > favicon), aspect ratio (square-ish preferred), and size. Recommended candidate gets amber border with "rekomendowane" badge in gallery UI. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
4119e44a58
commit
788ac12d30
@ -112,6 +112,12 @@ class LogoFetchService:
|
||||
if not saved:
|
||||
return {'success': False, 'message': 'Nie udało się pobrać żadnego kandydata', 'candidates': [], 'steps': steps}
|
||||
|
||||
# Score candidates and find recommendation
|
||||
recommended_index = None
|
||||
if saved:
|
||||
best = max(saved, key=lambda c: self._score_candidate(c))
|
||||
recommended_index = best['index']
|
||||
|
||||
steps.append({
|
||||
'step': 'save',
|
||||
'status': 'complete',
|
||||
@ -122,6 +128,7 @@ class LogoFetchService:
|
||||
'success': True,
|
||||
'message': f'Znaleziono {len(saved)} kandydatów na logo',
|
||||
'candidates': saved,
|
||||
'recommended_index': recommended_index,
|
||||
'steps': steps
|
||||
}
|
||||
|
||||
@ -408,6 +415,53 @@ class LogoFetchService:
|
||||
else:
|
||||
steps.append({'step': 'scan_images', 'status': 'missing', 'message': 'Brak elementów img z "logo" w atrybutach'})
|
||||
|
||||
@staticmethod
|
||||
def _score_candidate(candidate):
|
||||
"""Score a candidate for recommendation. Higher = better logo choice."""
|
||||
score = 0
|
||||
|
||||
# SVG is ideal for logos (vector, scalable)
|
||||
if candidate['ext'] == 'svg':
|
||||
score += 30
|
||||
|
||||
# Source reliability — how likely this is the actual logo
|
||||
source_scores = {
|
||||
'img_scan': 35,
|
||||
'css_bg': 25,
|
||||
'apple-touch-icon': 15,
|
||||
'og:image': 0,
|
||||
'twitter:image': 0,
|
||||
'favicon': -10,
|
||||
'google_favicon': -20,
|
||||
}
|
||||
score += source_scores.get(candidate['source'], 0)
|
||||
|
||||
# Aspect ratio — square-ish logos are most versatile
|
||||
w, h = candidate.get('width', 0), candidate.get('height', 0)
|
||||
if w > 0 and h > 0:
|
||||
ratio = max(w, h) / min(w, h)
|
||||
if ratio <= 2.0:
|
||||
score += 20
|
||||
elif ratio <= 3.0:
|
||||
score += 5
|
||||
else:
|
||||
score -= 15
|
||||
elif candidate['ext'] == 'svg':
|
||||
score += 15
|
||||
|
||||
# Size — prefer medium-sized, not favicon-tiny or banner-huge
|
||||
max_dim = max(w, h) if w > 0 else 0
|
||||
if max_dim == 0 and candidate['ext'] == 'svg':
|
||||
score += 10
|
||||
elif 64 <= max_dim <= 512:
|
||||
score += 15
|
||||
elif max_dim > 512:
|
||||
score += 5
|
||||
elif max_dim > 0:
|
||||
score -= 10
|
||||
|
||||
return score
|
||||
|
||||
@staticmethod
|
||||
def _parse_size(sizes_str):
|
||||
"""Parse '180x180' to max dimension int."""
|
||||
|
||||
@ -446,6 +446,11 @@
|
||||
background: #d1fae5;
|
||||
color: #065f46;
|
||||
}
|
||||
.logo-gallery-item.recommended { border-color: #f59e0b; }
|
||||
.logo-gallery-item.recommended .gallery-badge.badge-recommended {
|
||||
background: #fef3c7;
|
||||
color: #92400e;
|
||||
}
|
||||
.logo-gallery-item .gallery-size {
|
||||
font-size: 10px;
|
||||
color: #9ca3af;
|
||||
@ -4707,7 +4712,7 @@ function updateLogoStep(stepId, status, message) {
|
||||
return resp.json();
|
||||
}
|
||||
|
||||
function buildGallery(candidates, existingLogoExt) {
|
||||
function buildGallery(candidates, existingLogoExt, recommendedIndex) {
|
||||
const grid = document.getElementById('logoGalleryGrid');
|
||||
grid.innerHTML = '';
|
||||
selectedIndex = null;
|
||||
@ -4731,15 +4736,18 @@ function updateLogoStep(stepId, status, message) {
|
||||
// Show candidates
|
||||
candidates.forEach(c => {
|
||||
const item = document.createElement('div');
|
||||
item.className = 'logo-gallery-item';
|
||||
const isRecommended = (recommendedIndex !== null && c.index === recommendedIndex);
|
||||
item.className = 'logo-gallery-item' + (isRecommended ? ' recommended' : '');
|
||||
item.dataset.index = c.index;
|
||||
const dims = c.width ? `${c.width}x${c.height}` : 'SVG';
|
||||
const recBadge = isRecommended ? '<span class="gallery-badge badge-recommended">rekomendowane</span>' : '';
|
||||
item.innerHTML = `
|
||||
<div class="gallery-img">
|
||||
<img src="/static/img/companies/${c.filename}${cacheBust}" alt="${c.label}">
|
||||
</div>
|
||||
<div class="gallery-label">${c.label}</div>
|
||||
<div class="gallery-size">${dims}</div>
|
||||
${recBadge}
|
||||
`;
|
||||
item.addEventListener('click', () => selectCandidate(item, c.index));
|
||||
grid.appendChild(item);
|
||||
@ -4787,7 +4795,7 @@ function updateLogoStep(stepId, status, message) {
|
||||
}
|
||||
|
||||
// Show gallery with all candidates
|
||||
buildGallery(data.candidates, data.existing_logo_ext);
|
||||
buildGallery(data.candidates, data.existing_logo_ext, data.recommended_index);
|
||||
document.getElementById('logoGalleryOverlay').classList.add('active');
|
||||
|
||||
} catch (error) {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user