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_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>
158 lines
5.1 KiB
Python
158 lines
5.1 KiB
Python
"""
|
|
Admin Competitor Monitoring Routes
|
|
===================================
|
|
|
|
Dashboard for viewing and managing competitor tracking.
|
|
"""
|
|
|
|
import logging
|
|
from datetime import datetime, timedelta
|
|
|
|
from flask import render_template, request, jsonify, redirect, url_for
|
|
from flask_login import login_required, current_user
|
|
|
|
from . import bp
|
|
from database import (
|
|
SessionLocal, Company, CompanyCompetitor, CompetitorSnapshot,
|
|
GBPAudit, SystemRole
|
|
)
|
|
from utils.decorators import role_required
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
@bp.route('/competitors')
|
|
@login_required
|
|
@role_required(SystemRole.OFFICE_MANAGER)
|
|
def admin_competitors():
|
|
"""
|
|
Admin dashboard for competitor monitoring overview.
|
|
|
|
Displays:
|
|
- List of companies with active competitor tracking
|
|
- Competitor count per company
|
|
- Recent changes detected
|
|
"""
|
|
db = SessionLocal()
|
|
try:
|
|
# Get companies that have competitors tracked
|
|
companies_with_competitors = (
|
|
db.query(Company, db.query(CompanyCompetitor)
|
|
.filter(CompanyCompetitor.company_id == Company.id,
|
|
CompanyCompetitor.is_active == True)
|
|
.count().label('competitor_count'))
|
|
.filter(Company.status == 'active')
|
|
.all()
|
|
)
|
|
|
|
# Simpler approach: get all active competitors grouped by company
|
|
competitors = db.query(CompanyCompetitor).filter(
|
|
CompanyCompetitor.is_active == True
|
|
).all()
|
|
|
|
# Group by company
|
|
company_ids = set(c.company_id for c in competitors)
|
|
companies = db.query(Company).filter(
|
|
Company.id.in_(company_ids),
|
|
Company.status == 'active'
|
|
).all() if company_ids else []
|
|
|
|
company_data = []
|
|
for company in companies:
|
|
company_competitors = [c for c in competitors if c.company_id == company.id]
|
|
|
|
# Get GBP audit for this company (for comparison)
|
|
gbp_audit = db.query(GBPAudit).filter(
|
|
GBPAudit.company_id == company.id
|
|
).order_by(GBPAudit.audit_date.desc()).first()
|
|
|
|
company_data.append({
|
|
'company': company,
|
|
'competitor_count': len(company_competitors),
|
|
'competitors': company_competitors,
|
|
'gbp_audit': gbp_audit,
|
|
})
|
|
|
|
# Get recent snapshots with changes (last 30 days)
|
|
thirty_days_ago = datetime.now() - timedelta(days=30)
|
|
recent_changes = db.query(CompetitorSnapshot).filter(
|
|
CompetitorSnapshot.snapshot_date >= thirty_days_ago.date(),
|
|
CompetitorSnapshot.changes != None
|
|
).order_by(CompetitorSnapshot.snapshot_date.desc()).limit(20).all()
|
|
|
|
return render_template('admin/competitor_dashboard.html',
|
|
company_data=company_data,
|
|
recent_changes=recent_changes,
|
|
total_companies=len(company_data),
|
|
total_competitors=len(competitors),
|
|
)
|
|
|
|
finally:
|
|
db.close()
|
|
|
|
|
|
@bp.route('/competitors/<int:company_id>')
|
|
@login_required
|
|
@role_required(SystemRole.OFFICE_MANAGER)
|
|
def admin_competitor_detail(company_id):
|
|
"""
|
|
Detailed competitor view for a specific company.
|
|
|
|
Shows:
|
|
- Company's GBP metrics vs competitors
|
|
- Competitor list with current metrics
|
|
- Timeline of changes
|
|
"""
|
|
db = SessionLocal()
|
|
try:
|
|
company = db.query(Company).get(company_id)
|
|
if not company:
|
|
return redirect(url_for('admin.admin_competitors'))
|
|
|
|
# Get company's own GBP audit
|
|
gbp_audit = db.query(GBPAudit).filter(
|
|
GBPAudit.company_id == company.id
|
|
).order_by(GBPAudit.audit_date.desc()).first()
|
|
|
|
# Get competitors
|
|
competitors = db.query(CompanyCompetitor).filter(
|
|
CompanyCompetitor.company_id == company.id,
|
|
CompanyCompetitor.is_active == True
|
|
).all()
|
|
|
|
# Get latest snapshots for each competitor
|
|
competitor_data = []
|
|
for comp in competitors:
|
|
latest_snapshot = db.query(CompetitorSnapshot).filter(
|
|
CompetitorSnapshot.competitor_id == comp.id
|
|
).order_by(CompetitorSnapshot.snapshot_date.desc()).first()
|
|
|
|
competitor_data.append({
|
|
'competitor': comp,
|
|
'latest_snapshot': latest_snapshot,
|
|
})
|
|
|
|
# Get all snapshots with changes (for timeline)
|
|
competitor_ids = [c.id for c in competitors]
|
|
timeline = []
|
|
if competitor_ids:
|
|
timeline = db.query(CompetitorSnapshot).filter(
|
|
CompetitorSnapshot.competitor_id.in_(competitor_ids),
|
|
CompetitorSnapshot.changes != None
|
|
).order_by(CompetitorSnapshot.snapshot_date.desc()).limit(30).all()
|
|
|
|
return render_template('admin/competitor_dashboard.html',
|
|
company=company,
|
|
gbp_audit=gbp_audit,
|
|
competitor_data=competitor_data,
|
|
timeline=timeline,
|
|
detail_view=True,
|
|
total_companies=0,
|
|
total_competitors=len(competitors),
|
|
company_data=[],
|
|
recent_changes=[],
|
|
)
|
|
|
|
finally:
|
|
db.close()
|