auto-claude: 4.3 - Add save_audit_result method with ON CONFLICT DO UPDATE

- Enhanced save_audit_result method with complete column coverage
- Added missing columns to idempotent upsert query:
  - broken_links_count (for future link checking)
  - viewport_configured (derived from meta viewport tag)
  - is_mobile_friendly (derived from viewport content)
  - has_hreflang (for international SEO detection)
- All 45+ SEO columns now properly mapped for database upserts
- ON CONFLICT (company_id) DO UPDATE ensures idempotent operations

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Maciej Pienczyn 2026-01-08 08:00:12 +01:00
parent c8eb0829d9
commit c24c545cfe

View File

@ -501,6 +501,7 @@ class SEOAuditor:
ps_scores = pagespeed.get('scores', {}) if pagespeed else {}
# Upsert query for company_website_analysis
# Uses ON CONFLICT DO UPDATE for idempotent upserts
upsert_query = text("""
INSERT INTO company_website_analysis (
company_id, analyzed_at, website_url, final_url,
@ -515,12 +516,13 @@ class SEOAuditor:
meta_title, meta_description, meta_keywords,
h1_count, h2_count, h3_count, h1_text,
total_images, images_without_alt, images_with_alt,
internal_links_count, external_links_count,
internal_links_count, external_links_count, broken_links_count,
has_structured_data, structured_data_types, structured_data_json,
-- Technical SEO
has_canonical, canonical_url, is_indexable, noindex_reason,
has_sitemap, has_robots_txt,
viewport_configured, is_mobile_friendly,
-- Core Web Vitals
largest_contentful_paint_ms, first_input_delay_ms, cumulative_layout_shift,
@ -529,8 +531,8 @@ class SEOAuditor:
has_og_tags, og_title, og_description, og_image,
has_twitter_cards,
-- Language
html_lang,
-- Language & International
html_lang, has_hreflang,
-- Word count
word_count_homepage,
@ -549,18 +551,19 @@ class SEOAuditor:
:meta_title, :meta_description, :meta_keywords,
:h1_count, :h2_count, :h3_count, :h1_text,
:total_images, :images_without_alt, :images_with_alt,
:internal_links_count, :external_links_count,
:internal_links_count, :external_links_count, :broken_links_count,
:has_structured_data, :structured_data_types, :structured_data_json,
:has_canonical, :canonical_url, :is_indexable, :noindex_reason,
:has_sitemap, :has_robots_txt,
:viewport_configured, :is_mobile_friendly,
:largest_contentful_paint_ms, :first_input_delay_ms, :cumulative_layout_shift,
:has_og_tags, :og_title, :og_description, :og_image,
:has_twitter_cards,
:html_lang,
:html_lang, :has_hreflang,
:word_count_homepage,
@ -592,6 +595,7 @@ class SEOAuditor:
images_with_alt = EXCLUDED.images_with_alt,
internal_links_count = EXCLUDED.internal_links_count,
external_links_count = EXCLUDED.external_links_count,
broken_links_count = EXCLUDED.broken_links_count,
has_structured_data = EXCLUDED.has_structured_data,
structured_data_types = EXCLUDED.structured_data_types,
structured_data_json = EXCLUDED.structured_data_json,
@ -602,6 +606,8 @@ class SEOAuditor:
noindex_reason = EXCLUDED.noindex_reason,
has_sitemap = EXCLUDED.has_sitemap,
has_robots_txt = EXCLUDED.has_robots_txt,
viewport_configured = EXCLUDED.viewport_configured,
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,
@ -614,6 +620,7 @@ class SEOAuditor:
has_twitter_cards = EXCLUDED.has_twitter_cards,
html_lang = EXCLUDED.html_lang,
has_hreflang = EXCLUDED.has_hreflang,
word_count_homepage = EXCLUDED.word_count_homepage,
@ -665,6 +672,7 @@ class SEOAuditor:
'images_with_alt': images.get('images_with_alt'),
'internal_links_count': links.get('internal_links'),
'external_links_count': links.get('external_links'),
'broken_links_count': links.get('broken_links'), # May be None if not checked
'has_structured_data': structured_data.get('has_structured_data', False),
'structured_data_types': structured_data.get('all_types', []),
'structured_data_json': json.dumps(structured_data.get('json_ld_data', [])) if structured_data.get('json_ld_data') else None,
@ -676,6 +684,9 @@ class SEOAuditor:
'noindex_reason': indexability.get('noindex_source'),
'has_sitemap': sitemap.get('exists', False),
'has_robots_txt': robots.get('exists', False),
# Viewport and mobile-friendliness derived from meta_tags
'viewport_configured': bool(meta_tags.get('viewport')),
'is_mobile_friendly': 'width=device-width' in (meta_tags.get('viewport') or '').lower(),
# Core Web Vitals
'largest_contentful_paint_ms': cwv.get('lcp_ms'),
@ -689,8 +700,9 @@ class SEOAuditor:
'og_image': og.get('og_image', '')[:500] if og.get('og_image') else None,
'has_twitter_cards': bool(tc.get('card_type')),
# Language
# Language & International
'html_lang': onpage.get('lang_attribute', '')[:10] if onpage.get('lang_attribute') else None,
'has_hreflang': onpage.get('has_hreflang', False), # Detected by analyzer if present
# Word count
'word_count_homepage': onpage.get('word_count'),