Sentiment analysis:
- New analyze_review_sentiment_ai() method in GBPAuditService
- Uses Gemini to analyze review text content (not just ratings)
- Extracts themes, strengths, weaknesses, sentiment score (-1 to 1)
- Review sentiment data passed to GBP AI prompt
Competitor benchmarking:
- New benchmark_service.py with BenchmarkService class
- Calculates category averages across all 150 firms (GBP, SEO, Social)
- Metrics: completeness scores, ratings, reviews, photos, PageSpeed,
load time, follower counts, platform coverage
- Benchmark data injected into all 3 AI prompts (SEO, GBP, Social)
- Excluded from cache hash to avoid unnecessary invalidation
All 4 phases of audit completeness plan now implemented.
Estimated completeness: 52% → ~93%
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
New files:
- oauth_service.py: Shared OAuth 2.0 service supporting Google and Meta
providers with token exchange, refresh, and storage
- database/migrations/058_oauth_tokens.sql: oauth_tokens table with
company/provider/service unique constraint
- blueprints/api/routes_oauth.py: OAuth API endpoints for connect,
callback, status, and disconnect flows
Supports:
- Google OAuth (GBP Business Profile, Search Console)
- Meta OAuth (Facebook Pages, Instagram)
- CSRF state validation, token refresh, expiry tracking
- Per-company token storage with active/inactive status
Requires .env config:
- GOOGLE_OAUTH_CLIENT_ID, GOOGLE_OAUTH_CLIENT_SECRET (Google APIs)
- META_APP_ID, META_APP_SECRET (Facebook/Instagram)
- OAUTH_REDIRECT_BASE_URL (default: https://nordabiznes.pl)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
GBP data fetching migration:
- Replace legacy maps.googleapis.com/maps/api/place/ with GooglePlacesService
- Use Places API (New): places.googleapis.com/v1/places
- Extract 20+ new fields: primaryType, editorialSummary, priceLevel,
paymentOptions, parkingOptions, accessibilityOptions, service options,
amenities, food & drink, detailed photos metadata, review statistics
- Location bias for Wejherowo area in place search
- Backward-compatible return format for existing callers
GBP AI prompt enrichment:
- Add primaryType, editorialSummary, priceLevel to company info section
- Add business attributes section (payment, parking, accessibility,
services, amenities, food & drink) with dynamic rendering
- Use getattr with fallbacks for new DB columns not yet migrated
Completeness: GBP 55% → ~90% (estimated)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
New services:
- youtube_service.py: YouTube Data API v3 integration for channel stats
(subscriber count, view count, video count)
- crux_service.py: Chrome UX Report API for real user field data
(INP, LCP, CLS, FCP, TTFB from actual Chrome users)
SEO audit enrichment:
- Security headers check: HSTS, CSP, X-Frame-Options, X-Content-Type-Options
via live requests.head() during data collection
- Image format analysis: WebP/AVIF/SVG vs legacy JPEG/PNG ratio
- CrUX field data complements existing PageSpeed lab data in AI prompt
- All new metrics passed to Gemini for richer analysis
Social media audit enrichment:
- YouTube API data (video count, views, subscribers) integrated into
social media AI prompt when YouTube profile exists
All APIs use existing GOOGLE_PLACES_API_KEY (free tier, $0 cost).
Completeness: ~68% → ~78% (estimated)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
GBP audit:
- Fix review_response_rate bug: check ownerResponse instead of authorAttribution.displayName
- Mark has_posts/has_products/has_qa as OAuth-dependent in AI prompt
- Add review_keywords and description_keywords to AI prompt
SEO audit:
- Replace deprecated FID with INP (Core Web Vital since March 2024)
- Pass 10 additional metrics to AI prompt: FCP, TTFB, TBT, Speed Index,
meta title/desc length, html lang, Schema.org field details
- Update templates with INP thresholds (200ms/500ms)
Social media audit:
- Calculate engagement_rate from industry base rates × activity multiplier
- Calculate posting_frequency_score (0-10 based on posts_count_30d)
- Enrich AI prompt with page_name, freq_score, engagement, last_post_date
- Add avg engagement rate and brand name consistency check to prompt
Completeness: 52% → ~68% (estimated)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1. Switch to Gemini Structured Output (response_schema) for audit AI analysis
- Enforces valid JSON from API, ~95% → ~99% reliability
- Fallback to manual cleaning if structured output fails
2. Add JSON parse failure rate metric - logs to AIUsageLog for monitoring
3. Add Gemini 3 Pro preview model monitoring warning at service init
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Replace hardcoded logo_present=False with heuristic (photo_count>=1)
- Replace hardcoded cover_photo_present=False with heuristic (>=2)
- Fix has_photos to use photo_count>0 instead of score threshold
- Add descriptive photo_status string for AI context
- Add UWAGA section in Gemini prompt for cautious recommendations
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The API route was explicitly mapping fields and omitting the 'previous'
key from the service result.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Store previous analysis before regeneration and show comparison table
with priority breakdown, new/removed actions diff.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Instead of requiring users to click "Wygeneruj analize AI" every time,
the audit pages now automatically fetch cached analysis on DOMContentLoaded.
If cache exists, results render instantly; if not, the generate button remains.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add per-call model override parameter to generate_text()
- GBP audit, SEO/social audit analysis, and audit content generation
now use gemini-3-pro-preview for highest quality reasoning
- Chat and other features remain on 3-flash (cheaper, faster)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Switch primary model from flash-lite (2.5) to 3-flash (Gemini 3 Flash Preview)
for better reasoning and thinking mode across all AI features
- Add _is_retryable() method to handle 503 UNAVAILABLE (server overload)
in addition to existing 429 rate limit fallback
- Fallback chain: 3-flash → 2.5-flash-lite → 2.5-flash
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add "Users" button and modal in companies table to view/assign/unassign users
- New endpoint POST /admin/companies/<id>/unassign-user to detach user from company
- New endpoint GET /admin/users/list-all for user dropdown in assignment modal
- Modal shows assigned users with "Unpin" button and dropdown for adding new ones
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Move percentage text from inside bars to separate column (always visible)
- Add cost (USD) column to "Wykorzystanie wg typu" section
- Add tokens+cost to type query in backend
- Fix same issues in company detail template
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Increase usage-label width from 120px to 180px for full model/type names
- Remove [:20] truncation on model names in templates
- Add word-break on ranking table cells and usage labels
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add user/company/model/custom date range filters to AI usage dashboard
- Add model breakdown section showing per-model usage and costs
- Add company detail page (/admin/ai-usage/company/<id>) with per-user stats
- Add CSV export endpoint respecting all active filters
- Remove unused model-comparison page from navigation and imports
- Remove 20-item limit on user/company rankings (show all)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Users can now choose between Flash Lite (fastest, 1000 RPD), Flash
(thinking mode, 20 RPD) and Pro (premium). Default changed to Flash Lite.
Badge shows actual model used for full transparency.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Switch primary model to flash-lite (1000 RPD) with automatic fallback
to 3-flash-preview (20 RPD) and flash (20 RPD) on RESOURCE_EXHAUSTED,
giving 1040 req/day on free tier instead of 20.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
company.category is a SQLAlchemy Category object. json.dumps(default=str)
converted it to '<database.Category object at 0x...>' with a different
memory address on each request, causing cache hash to always differ.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Cache: exclude volatile citations fields from hash for better hit rate
- Thinking level: reduce from 'high' to 'low' for faster responses (13-24s → ~5-10s)
- UTF-8: html.unescape() on meta_title, meta_description, h1_text
- SEO score: add "(Google Lighthouse)" label and explanatory note
- Spinner: update text to "15-30 seconds" with live timer counter
- Auto-scroll: smooth scroll to results after AI analysis and content generation
- Cache info: show generation date with "Wygeneruj ponownie" link
- Markdown: render non-code AI content with basic markdown formatting
- Error feedback: inline error display with retry button instead of modal
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
AI analysis generates action_types dynamically (e.g. add_sitemap, add_nap)
that don't always match predefined CONTENT_PROMPTS. Added fallback that
builds a generic prompt using the action title/description from the DB.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
company.services returns CompanyService join table objects, not Service
objects. Access service name via s.service.name relationship.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add Gemini AI integration to SEO, GBP, and Social Media audits that
generates contextual analysis summaries and prioritized action items
with ready-to-use content (Schema.org, meta descriptions, social posts,
GBP descriptions, review responses, content calendars).
New files:
- audit_ai_service.py: Central AI service with caching (7-day TTL)
- blueprints/api/routes_audit_actions.py: 4 API endpoints
- database/migrations/056_audit_actions.sql: 3 new tables
- templates/partials/audit_ai_actions.html: Reusable UI component
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Jinja2 auto-escaping converts HTML entities inside {{ }} to literal text.
Replaced ✓/✗/⚠ with ✓/✗/⚠ in Local SEO and Technical SEO sections.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- google_places_service.py: Google Places API integration
- competitor_monitoring_service.py: Competitor tracking service
- scripts/competitor_monitor_cron.py, scripts/generate_audit_report.py
- blueprints/admin/routes_competitors.py, templates/admin/competitor_dashboard.html
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The admin social audit dashboard (/admin/social-audit) now shows:
- Yellow stat card with count of profiles needing verification
- Dedicated 'Profile do recznej weryfikacji' section listing all items
- Warning icon in recommendations column for verification-needed profiles
- Route now fetches profiles with check_status='needs_verification' alongside valid ones
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Previously the check_status='needs_verification' was only visible in the admin
social media panel (Wszystkie wpisy tab). Now it also shows on:
- Company social audit page (/audit/social/...) as yellow status badge + warning
- Company detail page social media grid as yellow 'Do weryfikacji' badge
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Display yellow 'Do weryfikacji' badge when check_status is
needs_verification, red 'Nieaktywny' when is_valid is false.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add regex pattern for Facebook /p/PageName-ID/ multi-segment URLs
- Add 'p' to Facebook exclusion list (bare /p is always truncated)
- Add minimum length validation for extracted social handles
- Strip Instagram tracking params (?igsh=, &utm_source=) from handles
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Adds user_companies table with BEFORE/AFTER triggers to sync primary
company to users.company_id. Dashboard shows all user's companies with
edit buttons. Company edit routes accept optional company_id parameter.
Admin API endpoints for managing user-company associations.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Simplify all 18 historical release notes for non-technical readers.
Replace jargon with clear Polish descriptions explaining changes
from the user's perspective. Merge duplicate entries describing
the same feature (PWA 3→1, email polling 2→1, blocking 2→1).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Simplify release notes for non-technical readers. Remove jargon
(RBAC, regex, CSRF, SQL injection), use clear Polish descriptions
that explain what changed from the user's perspective.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The class is named AuditResult in gbp_audit_service.py but was imported
as GBPAuditResult. Using alias to maintain compatibility.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The import was looking for 'seo_audit_service' module which no longer
exists. The SEOAuditor class lives in scripts/seo_audit.py. Fixed to
use sys.path like routes_social_audit.py does.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Shows per-company recommendations: missing platforms, Facebook numeric
ID warning, etc. Color-coded by severity in a new "Zalecenia" column.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Show warning on platform card and in recommendations when a company's
Facebook URL uses profile.php?id=XXX instead of a vanity username.
Includes actionable advice on how to set a custom Facebook username.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The regex was capturing 'profile.php' as a username instead of extracting
the numeric ID from profile.php?id=XXX links. Added dedicated pattern for
profile.php URLs and added profile.php to exclusion list.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add empty JSON body to fetch POST requests - Flask cannot parse
Content-Type: application/json with no body.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Adds "Pobierz dane urzędowe" button on company detail page (admin-only)
that fetches data from KRS, Biała Lista VAT, or CEIDG registries.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace selectattr('value', 'search', ...) with simple 'in' check against
known_phones list that was already being built. Fixes 500 error on /company/102.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
New member companies: Termo sp. z o.o. (electrical) and
STUDIO N°33 Premium Body & Mental (fitness/wellness).
Both operate under NIP 5882495040.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add missing sections for services_offered, technologies_used,
operational_area, languages_offered, founding_history, and core_values
to company_detail.html. These fields are editable via /firma/edytuj
but were not visible on the profile page.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>