diff --git a/blueprints/admin/routes_audits.py b/blueprints/admin/routes_audits.py index 1869ca1..530fc1c 100644 --- a/blueprints/admin/routes_audits.py +++ b/blueprints/admin/routes_audits.py @@ -1059,6 +1059,143 @@ def admin_gbp_audit_batch_discard(): }) +# ============================================================ +# GBP PLACE ID MATCHING (manual review) +# ============================================================ + +@bp.route('/gbp-audit/match-places') +@login_required +@role_required(SystemRole.ADMIN) +def admin_gbp_match_places(): + """Show companies without google_place_id for manual matching.""" + if not is_audit_owner(): + abort(404) + + db = SessionLocal() + try: + # Get companies without place_id + subq = db.query(CompanyWebsiteAnalysis.company_id).filter( + CompanyWebsiteAnalysis.google_place_id.isnot(None) + ).subquery() + + companies = db.query( + Company.id, Company.name, Company.address_city, Company.website + ).filter( + Company.status == 'active', + ~Company.id.in_(subq) + ).order_by(Company.name).all() + + return render_template('admin/gbp_match_places.html', companies=companies) + finally: + db.close() + + +@bp.route('/gbp-audit/search-place', methods=['POST']) +@login_required +@role_required(SystemRole.ADMIN) +def admin_gbp_search_place(): + """Search Google Places for a company (no name filter — raw results).""" + if not is_audit_owner(): + return jsonify({'error': 'Brak uprawnień'}), 403 + + company_id = request.form.get('company_id', type=int) + if not company_id: + return jsonify({'error': 'Brak company_id'}), 400 + + db = SessionLocal() + try: + company = db.get(Company, company_id) + if not company: + return jsonify({'error': 'Firma nie znaleziona'}), 404 + + try: + from google_places_service import GooglePlacesService + places_service = GooglePlacesService() + except (ImportError, ValueError) as e: + return jsonify({'error': f'Places API niedostępne: {e}'}), 500 + + city = company.address_city or 'Wejherowo' + query = f'{company.name} {city}' + location_bias = {'latitude': 54.6059, 'longitude': 18.2350, 'radius': 50000.0} + + # Search via raw API call — return ALL results for manual review + results = places_service.search_places_raw(query, location_bias=location_bias) + + if not results: + # Try broader search with just company name + results = places_service.search_places_raw(company.name, location_bias=location_bias) + + if not results: + return jsonify({'results': [], 'query': query}) + + places = [] + for p in results: + place_id = p.get('id', '') + if place_id.startswith('places/'): + place_id = place_id.replace('places/', '') + places.append({ + 'place_id': place_id, + 'name': p.get('displayName', {}).get('text', ''), + 'address': p.get('formattedAddress', ''), + 'types': ', '.join((p.get('types') or [])[:3]), + 'rating': p.get('rating'), + 'reviews_count': p.get('userRatingCount'), + }) + + return jsonify({'results': places, 'query': query}) + finally: + db.close() + + +@bp.route('/gbp-audit/confirm-place', methods=['POST']) +@login_required +@role_required(SystemRole.ADMIN) +def admin_gbp_confirm_place(): + """Save confirmed google_place_id for a company.""" + if not is_audit_owner(): + return jsonify({'error': 'Brak uprawnień'}), 403 + + company_id = request.form.get('company_id', type=int) + place_id = request.form.get('place_id', '').strip() + google_name = request.form.get('google_name', '').strip() + + if not company_id or not place_id: + return jsonify({'error': 'Brak company_id lub place_id'}), 400 + + db = SessionLocal() + try: + company = db.get(Company, company_id) + if not company: + return jsonify({'error': 'Firma nie znaleziona'}), 404 + + analysis = db.query(CompanyWebsiteAnalysis).filter( + CompanyWebsiteAnalysis.company_id == company_id + ).first() + + if not analysis: + analysis = CompanyWebsiteAnalysis( + company_id=company_id, + url=company.website, + analyzed_at=datetime.now() + ) + db.add(analysis) + + analysis.google_place_id = place_id + if google_name: + analysis.google_name = google_name + analysis.analyzed_at = datetime.now() + db.commit() + + logger.info(f"Place ID confirmed for company {company_id} ({company.name}): {place_id}") + return jsonify({'status': 'ok', 'message': f'Place ID zapisany dla {company.name}'}) + except Exception as e: + db.rollback() + logger.error(f"Failed to confirm place_id for company {company_id}: {e}") + return jsonify({'error': str(e)[:100]}), 500 + finally: + db.close() + + # ============================================================ # DIGITAL MATURITY DASHBOARD # ============================================================ diff --git a/google_places_service.py b/google_places_service.py index 72fa6c1..97a263f 100644 --- a/google_places_service.py +++ b/google_places_service.py @@ -280,6 +280,41 @@ class GooglePlacesService: logger.error(f"Places search error for '{query}': {e}") return None + def search_places_raw(self, query: str, location_bias: Dict = None) -> List[Dict[str, Any]]: + """ + Search for places and return ALL results (no name filtering). + Used for manual review/matching in admin panel. + """ + body = { + "textQuery": query, + "languageCode": "pl", + "maxResultCount": 5 + } + if location_bias: + body["locationBias"] = { + "circle": { + "center": { + "latitude": location_bias["latitude"], + "longitude": location_bias["longitude"] + }, + "radius": location_bias.get("radius", 5000.0) + } + } + + field_mask = ','.join(f'places.{f}' for f in [ + 'id', 'displayName', 'formattedAddress', 'types', + 'rating', 'userRatingCount', 'googleMapsUri' + ]) + headers = {'X-Goog-FieldMask': field_mask} + + try: + response = self.session.post(PLACES_SEARCH_URL, json=body, headers=headers, timeout=15) + response.raise_for_status() + return response.json().get('places', []) + except requests.exceptions.RequestException as e: + logger.error(f"Places raw search error for '{query}': {e}") + return [] + def search_nearby(self, latitude: float, longitude: float, radius: float = 5000.0, included_types: List[str] = None, diff --git a/templates/admin/gbp_audit_dashboard.html b/templates/admin/gbp_audit_dashboard.html index ee23a9d..1334ebe 100644 --- a/templates/admin/gbp_audit_dashboard.html +++ b/templates/admin/gbp_audit_dashboard.html @@ -450,6 +450,13 @@