refactor(seo): Rename FID to INP across entire codebase
Some checks are pending
NordaBiz Tests / Unit & Integration Tests (push) Waiting to run
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
Some checks are pending
NordaBiz Tests / Unit & Integration Tests (push) Waiting to run
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
Google replaced First Input Delay (FID) with Interaction to Next Paint (INP) as a Core Web Vital in March 2024. This renames the DB column from first_input_delay_ms to interaction_to_next_paint_ms, updates the PageSpeed client to prefer the INP audit key, and fixes all references across routes, services, scripts, and report generators. Updated INP thresholds: good ≤200ms, needs improvement ≤500ms. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
ea3d26282f
commit
ef39ebf8a3
@ -222,7 +222,7 @@ def _collect_seo_data(db, company) -> dict:
|
||||
'nap_on_website': analysis.nap_on_website,
|
||||
# Core Web Vitals
|
||||
'lcp_ms': analysis.largest_contentful_paint_ms,
|
||||
'inp_ms': getattr(analysis, 'interaction_to_next_paint_ms', None), # Replaced FID in March 2024
|
||||
'inp_ms': analysis.interaction_to_next_paint_ms,
|
||||
'cls': float(analysis.cumulative_layout_shift) if analysis.cumulative_layout_shift else None,
|
||||
# Additional performance metrics (10 missing metrics)
|
||||
'fcp_ms': getattr(analysis, 'first_contentful_paint_ms', None),
|
||||
|
||||
@ -192,7 +192,7 @@ def _build_seo_audit_response(company, analysis):
|
||||
},
|
||||
'core_web_vitals': {
|
||||
'largest_contentful_paint_ms': analysis.largest_contentful_paint_ms,
|
||||
'first_input_delay_ms': analysis.first_input_delay_ms,
|
||||
'interaction_to_next_paint_ms': analysis.interaction_to_next_paint_ms,
|
||||
'cumulative_layout_shift': float(analysis.cumulative_layout_shift) if analysis.cumulative_layout_shift else None
|
||||
},
|
||||
'social': {
|
||||
|
||||
@ -105,7 +105,7 @@ def seo_audit_dashboard(slug):
|
||||
'last_modified_date': analysis.last_modified_at,
|
||||
# Core Web Vitals
|
||||
'lcp_ms': analysis.largest_contentful_paint_ms,
|
||||
'inp_ms': analysis.first_input_delay_ms,
|
||||
'inp_ms': analysis.interaction_to_next_paint_ms,
|
||||
'cls': float(analysis.cumulative_layout_shift) if analysis.cumulative_layout_shift is not None else None,
|
||||
# Heading structure
|
||||
'h1_count': analysis.h1_count,
|
||||
|
||||
@ -1111,7 +1111,7 @@ class CompanyWebsiteAnalysis(Base):
|
||||
# Core Web Vitals
|
||||
viewport_configured = Column(Boolean) # Whether viewport meta tag is properly configured
|
||||
largest_contentful_paint_ms = Column(Integer) # Core Web Vital: LCP in milliseconds
|
||||
first_input_delay_ms = Column(Integer) # Core Web Vital: FID in milliseconds
|
||||
interaction_to_next_paint_ms = Column(Integer) # Core Web Vital: INP in milliseconds (replaced FID March 2024)
|
||||
cumulative_layout_shift = Column(Numeric(5, 3)) # Core Web Vital: CLS score
|
||||
|
||||
# Open Graph & Social Meta
|
||||
|
||||
8
database/migrations/060_rename_fid_to_inp.sql
Normal file
8
database/migrations/060_rename_fid_to_inp.sql
Normal file
@ -0,0 +1,8 @@
|
||||
-- Migration 060: Rename first_input_delay_ms to interaction_to_next_paint_ms
|
||||
-- Google replaced FID with INP (Interaction to Next Paint) as a Core Web Vital in March 2024
|
||||
-- The PageSpeed API now returns INP data, so the column name should reflect the actual metric
|
||||
|
||||
ALTER TABLE company_website_analysis RENAME COLUMN first_input_delay_ms TO interaction_to_next_paint_ms;
|
||||
|
||||
-- Grant permissions
|
||||
GRANT ALL ON TABLE company_website_analysis TO nordabiz_app;
|
||||
@ -243,7 +243,7 @@ class AuditReportGenerator:
|
||||
'content_freshness_score': analysis.content_freshness_score,
|
||||
'core_web_vitals': {
|
||||
'lcp_ms': analysis.largest_contentful_paint_ms,
|
||||
'fid_ms': analysis.first_input_delay_ms,
|
||||
'inp_ms': analysis.interaction_to_next_paint_ms,
|
||||
'cls': float(analysis.cumulative_layout_shift) if analysis.cumulative_layout_shift else None,
|
||||
},
|
||||
'seo_issues': analysis.seo_issues,
|
||||
|
||||
@ -87,7 +87,7 @@ class PageSpeedScore:
|
||||
class CoreWebVitals:
|
||||
"""Core Web Vitals metrics from PageSpeed."""
|
||||
lcp_ms: Optional[int] = None # Largest Contentful Paint
|
||||
fid_ms: Optional[int] = None # First Input Delay
|
||||
inp_ms: Optional[int] = None # Interaction to Next Paint (replaced FID March 2024)
|
||||
cls: Optional[float] = None # Cumulative Layout Shift
|
||||
fcp_ms: Optional[int] = None # First Contentful Paint
|
||||
ttfb_ms: Optional[int] = None # Time to First Byte
|
||||
@ -517,7 +517,7 @@ class GooglePageSpeedClient:
|
||||
audits = lighthouse.get('audits', {})
|
||||
core_web_vitals = CoreWebVitals(
|
||||
lcp_ms=self._extract_metric_ms(audits.get('largest-contentful-paint')),
|
||||
fid_ms=self._extract_metric_ms(audits.get('max-potential-fid')),
|
||||
inp_ms=self._extract_metric_ms(audits.get('interaction-to-next-paint') or audits.get('max-potential-fid')),
|
||||
cls=self._extract_cls(audits.get('cumulative-layout-shift')),
|
||||
fcp_ms=self._extract_metric_ms(audits.get('first-contentful-paint')),
|
||||
ttfb_ms=self._extract_metric_ms(audits.get('server-response-time')),
|
||||
|
||||
@ -926,7 +926,7 @@ class SEOAuditor:
|
||||
viewport_configured, is_mobile_friendly,
|
||||
|
||||
-- Core Web Vitals
|
||||
largest_contentful_paint_ms, first_input_delay_ms, cumulative_layout_shift,
|
||||
largest_contentful_paint_ms, interaction_to_next_paint_ms, cumulative_layout_shift,
|
||||
|
||||
-- Open Graph
|
||||
has_og_tags, og_title, og_description, og_image,
|
||||
@ -969,7 +969,7 @@ class SEOAuditor:
|
||||
:has_sitemap, :has_robots_txt,
|
||||
:viewport_configured, :is_mobile_friendly,
|
||||
|
||||
:largest_contentful_paint_ms, :first_input_delay_ms, :cumulative_layout_shift,
|
||||
:largest_contentful_paint_ms, :interaction_to_next_paint_ms, :cumulative_layout_shift,
|
||||
|
||||
:has_og_tags, :og_title, :og_description, :og_image,
|
||||
:has_twitter_cards,
|
||||
@ -1028,7 +1028,7 @@ class SEOAuditor:
|
||||
is_mobile_friendly = EXCLUDED.is_mobile_friendly,
|
||||
|
||||
largest_contentful_paint_ms = EXCLUDED.largest_contentful_paint_ms,
|
||||
first_input_delay_ms = EXCLUDED.first_input_delay_ms,
|
||||
interaction_to_next_paint_ms = EXCLUDED.interaction_to_next_paint_ms,
|
||||
cumulative_layout_shift = EXCLUDED.cumulative_layout_shift,
|
||||
|
||||
has_og_tags = EXCLUDED.has_og_tags,
|
||||
@ -1122,7 +1122,7 @@ class SEOAuditor:
|
||||
|
||||
# Core Web Vitals
|
||||
'largest_contentful_paint_ms': cwv.get('lcp_ms'),
|
||||
'first_input_delay_ms': cwv.get('fid_ms'),
|
||||
'interaction_to_next_paint_ms': cwv.get('inp_ms'),
|
||||
'cumulative_layout_shift': cwv.get('cls'),
|
||||
|
||||
# Open Graph
|
||||
|
||||
@ -104,7 +104,7 @@ class SEOReportGenerator:
|
||||
wa.has_canonical, wa.canonical_url, wa.is_indexable, wa.noindex_reason,
|
||||
wa.has_sitemap, wa.has_robots_txt,
|
||||
wa.viewport_configured, wa.is_mobile_friendly,
|
||||
wa.largest_contentful_paint_ms, wa.first_input_delay_ms, wa.cumulative_layout_shift,
|
||||
wa.largest_contentful_paint_ms, wa.interaction_to_next_paint_ms, wa.cumulative_layout_shift,
|
||||
wa.has_og_tags, wa.og_title, wa.og_description, wa.og_image,
|
||||
wa.has_twitter_cards, wa.html_lang, wa.has_hreflang,
|
||||
wa.word_count_homepage,
|
||||
@ -629,10 +629,10 @@ class SEOReportGenerator:
|
||||
def _core_web_vitals_section(self, company: Dict[str, Any]) -> str:
|
||||
"""Generate Core Web Vitals section HTML."""
|
||||
lcp = company.get('largest_contentful_paint_ms')
|
||||
fid = company.get('first_input_delay_ms')
|
||||
inp = company.get('interaction_to_next_paint_ms')
|
||||
cls = company.get('cumulative_layout_shift')
|
||||
|
||||
if lcp is None and fid is None and cls is None:
|
||||
if lcp is None and inp is None and cls is None:
|
||||
return ''
|
||||
|
||||
def lcp_status(val):
|
||||
@ -644,12 +644,12 @@ class SEOReportGenerator:
|
||||
return (f'{val}ms', 'badge-warning')
|
||||
return (f'{val}ms', 'badge-danger')
|
||||
|
||||
def fid_status(val):
|
||||
def inp_status(val):
|
||||
if val is None:
|
||||
return ('—', 'badge-secondary')
|
||||
if val <= 100:
|
||||
if val <= 200:
|
||||
return (f'{val}ms', 'badge-success')
|
||||
if val <= 300:
|
||||
if val <= 500:
|
||||
return (f'{val}ms', 'badge-warning')
|
||||
return (f'{val}ms', 'badge-danger')
|
||||
|
||||
@ -663,7 +663,7 @@ class SEOReportGenerator:
|
||||
return (f'{val:.3f}', 'badge-danger')
|
||||
|
||||
lcp_val, lcp_class = lcp_status(lcp)
|
||||
fid_val, fid_class = fid_status(fid)
|
||||
inp_val, inp_class = inp_status(inp)
|
||||
cls_val, cls_class = cls_status(cls)
|
||||
|
||||
return f'''
|
||||
@ -682,14 +682,14 @@ class SEOReportGenerator:
|
||||
</div>
|
||||
</div>
|
||||
<div class="info-card">
|
||||
<h3>FID (First Input Delay)</h3>
|
||||
<h3>INP (Interaction to Next Paint)</h3>
|
||||
<div class="item">
|
||||
<span class="label">Wynik</span>
|
||||
<span class="value"><span class="badge {fid_class}">{fid_val}</span></span>
|
||||
<span class="value"><span class="badge {inp_class}">{inp_val}</span></span>
|
||||
</div>
|
||||
<div class="item">
|
||||
<span class="label">Cel</span>
|
||||
<span class="value">< 100ms</span>
|
||||
<span class="value">< 200ms</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="info-card">
|
||||
@ -1078,7 +1078,7 @@ class SEOReportGenerator:
|
||||
}
|
||||
company_data['seo_audit']['core_web_vitals'] = {
|
||||
'lcp_ms': company.get('largest_contentful_paint_ms'),
|
||||
'fid_ms': company.get('first_input_delay_ms'),
|
||||
'inp_ms': company.get('interaction_to_next_paint_ms'),
|
||||
'cls': float(company.get('cumulative_layout_shift')) if company.get('cumulative_layout_shift') else None,
|
||||
}
|
||||
company_data['seo_audit']['social'] = {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user