furry/products/seo.py

341 lines
12 KiB
Python

"""
SEO Module für Kasico Fursuit Shop
Meta Tags, Structured Data, Sitemap Generation
"""
from django.utils.html import strip_tags
from django.template.loader import render_to_string
from django.contrib.sites.models import Site
from django.urls import reverse
from django.utils import timezone
import json
class SEOManager:
"""SEO Manager für Meta Tags und Structured Data"""
def __init__(self, request=None):
self.request = request
self.site = Site.objects.get_current()
self.base_url = f"https://{self.site.domain}"
def get_product_meta_tags(self, product):
"""Meta Tags für Produkt-Seiten"""
meta_tags = {
'title': f"{product.name} - Kasico Fursuit Shop",
'description': self._clean_description(product.description),
'keywords': f"fursuit, {product.fursuit_type}, {product.style}, custom fursuit, furry",
'og_title': product.name,
'og_description': self._clean_description(product.description),
'og_type': 'product',
'og_image': self._get_product_image_url(product),
'og_url': f"{self.base_url}{product.get_absolute_url()}",
'twitter_card': 'summary_large_image',
'twitter_title': product.name,
'twitter_description': self._clean_description(product.description),
'twitter_image': self._get_product_image_url(product),
'canonical_url': f"{self.base_url}{product.get_absolute_url()}",
}
# Preis-Informationen für E-Commerce
if product.on_sale:
meta_tags['price'] = str(product.sale_price)
meta_tags['original_price'] = str(product.base_price)
else:
meta_tags['price'] = str(product.base_price)
return meta_tags
def get_category_meta_tags(self, category):
"""Meta Tags für Kategorie-Seiten"""
meta_tags = {
'title': f"{category.name} Fursuits - Kasico Shop",
'description': f"Entdecke unsere {category.name} Fursuits. Handgefertigte, hochwertige Fursuits in verschiedenen Stilen und Größen.",
'keywords': f"{category.name}, fursuit, furry, custom fursuit",
'og_title': f"{category.name} Fursuits",
'og_description': f"Entdecke unsere {category.name} Fursuits bei Kasico",
'og_type': 'website',
'og_url': f"{self.base_url}{category.get_absolute_url()}",
'canonical_url': f"{self.base_url}{category.get_absolute_url()}",
}
return meta_tags
def get_homepage_meta_tags(self):
"""Meta Tags für Homepage"""
meta_tags = {
'title': 'Kasico - Premium Fursuit Shop | Handgefertigte Fursuits',
'description': 'Entdecke handgefertigte, hochwertige Fursuits bei Kasico. Custom Fursuits, Fullsuits, Headsets und mehr. Made in Germany.',
'keywords': 'fursuit, furry, custom fursuit, fullsuit, headset, handgefertigt, germany',
'og_title': 'Kasico - Premium Fursuit Shop',
'og_description': 'Handgefertigte, hochwertige Fursuits made in Germany',
'og_type': 'website',
'og_url': self.base_url,
'canonical_url': self.base_url,
}
return meta_tags
def get_structured_data_product(self, product):
"""Structured Data für Produkte (JSON-LD)"""
structured_data = {
"@context": "https://schema.org/",
"@type": "Product",
"name": product.name,
"description": self._clean_description(product.description),
"image": self._get_product_image_url(product),
"url": f"{self.base_url}{product.get_absolute_url()}",
"brand": {
"@type": "Brand",
"name": "Kasico"
},
"offers": {
"@type": "Offer",
"price": str(product.sale_price if product.on_sale else product.base_price),
"priceCurrency": "EUR",
"availability": "https://schema.org/InStock" if product.stock > 0 else "https://schema.org/OutOfStock",
"seller": {
"@type": "Organization",
"name": "Kasico"
}
},
"aggregateRating": {
"@type": "AggregateRating",
"ratingValue": product.average_rating,
"reviewCount": product.reviews.count()
}
}
if product.on_sale:
structured_data["offers"]["priceSpecification"] = {
"@type": "PriceSpecification",
"price": str(product.sale_price),
"priceCurrency": "EUR",
"valueAddedTaxIncluded": True
}
return structured_data
def get_structured_data_organization(self):
"""Structured Data für Organisation"""
structured_data = {
"@context": "https://schema.org",
"@type": "Organization",
"name": "Kasico",
"url": self.base_url,
"logo": f"{self.base_url}/static/images/kasico-logo.svg",
"description": "Premium Fursuit Shop - Handgefertigte Fursuits made in Germany",
"address": {
"@type": "PostalAddress",
"addressCountry": "DE",
"addressLocality": "Germany"
},
"contactPoint": {
"@type": "ContactPoint",
"contactType": "customer service",
"email": "info@kasico.de"
},
"sameAs": [
"https://twitter.com/kasico_fursuits",
"https://www.instagram.com/kasico_fursuits",
"https://www.facebook.com/kasico.fursuits"
]
}
return structured_data
def get_structured_data_website(self):
"""Structured Data für Website"""
structured_data = {
"@context": "https://schema.org",
"@type": "WebSite",
"name": "Kasico Fursuit Shop",
"url": self.base_url,
"description": "Premium Fursuit Shop - Handgefertigte Fursuits made in Germany",
"potentialAction": {
"@type": "SearchAction",
"target": f"{self.base_url}/products/search/?q={{search_term_string}}",
"query-input": "required name=search_term_string"
}
}
return structured_data
def get_structured_data_breadcrumb(self, breadcrumbs):
"""Structured Data für Breadcrumbs"""
structured_data = {
"@context": "https://schema.org",
"@type": "BreadcrumbList",
"itemListElement": []
}
for i, (name, url) in enumerate(breadcrumbs):
structured_data["itemListElement"].append({
"@type": "ListItem",
"position": i + 1,
"name": name,
"item": f"{self.base_url}{url}"
})
return structured_data
def generate_sitemap_xml(self):
"""XML Sitemap generieren"""
from products.models import Product, Category
from django.contrib.sitemaps import Sitemap
sitemap_data = {
'urlset': {
'@xmlns': 'http://www.sitemaps.org/schemas/sitemap/0.9',
'url': []
}
}
# Homepage
sitemap_data['urlset']['url'].append({
'loc': self.base_url,
'lastmod': timezone.now().strftime('%Y-%m-%d'),
'changefreq': 'daily',
'priority': '1.0'
})
# Produkt-Seiten
for product in Product.objects.filter(is_active=True):
sitemap_data['urlset']['url'].append({
'loc': f"{self.base_url}{product.get_absolute_url()}",
'lastmod': product.updated_at.strftime('%Y-%m-%d'),
'changefreq': 'weekly',
'priority': '0.8'
})
# Kategorie-Seiten
for category in Category.objects.all():
sitemap_data['urlset']['url'].append({
'loc': f"{self.base_url}{category.get_absolute_url()}",
'lastmod': timezone.now().strftime('%Y-%m-%d'),
'changefreq': 'weekly',
'priority': '0.7'
})
# Statische Seiten
static_pages = [
('/products/', 'weekly', '0.8'),
('/gallery/', 'weekly', '0.7'),
('/contact/', 'monthly', '0.5'),
('/about/', 'monthly', '0.5'),
('/faq/', 'monthly', '0.6'),
]
for url, changefreq, priority in static_pages:
sitemap_data['urlset']['url'].append({
'loc': f"{self.base_url}{url}",
'lastmod': timezone.now().strftime('%Y-%m-%d'),
'changefreq': changefreq,
'priority': priority
})
return sitemap_data
def generate_robots_txt(self):
"""Robots.txt generieren"""
robots_content = f"""User-agent: *
Allow: /
# Sitemap
Sitemap: {self.base_url}/sitemap.xml
# Disallow admin and private areas
Disallow: /admin/
Disallow: /api/
Disallow: /static/admin/
Disallow: /media/admin/
# Allow important pages
Allow: /products/
Allow: /gallery/
Allow: /contact/
Allow: /about/
Allow: /faq/
# Crawl delay
Crawl-delay: 1
"""
return robots_content
def _clean_description(self, text, max_length=160):
"""Beschreibung für Meta Tags bereinigen"""
if not text:
return ""
# HTML Tags entfernen
clean_text = strip_tags(text)
# Zu lang kürzen
if len(clean_text) > max_length:
clean_text = clean_text[:max_length-3] + "..."
return clean_text
def _get_product_image_url(self, product):
"""Produkt-Bild URL generieren"""
if product.image:
return f"{self.base_url}{product.image.url}"
return f"{self.base_url}/static/images/default-product.jpg"
class SEOTemplateTags:
"""Template Tags für SEO"""
@staticmethod
def render_meta_tags(meta_tags):
"""Meta Tags als HTML rendern"""
return render_to_string('seo/meta_tags.html', {'meta_tags': meta_tags})
@staticmethod
def render_structured_data(structured_data):
"""Structured Data als JSON-LD rendern"""
return render_to_string('seo/structured_data.html', {
'structured_data': json.dumps(structured_data, indent=2)
})
@staticmethod
def render_social_meta_tags(meta_tags):
"""Social Media Meta Tags rendern"""
return render_to_string('seo/social_meta_tags.html', {'meta_tags': meta_tags})
# SEO Context Processor
def seo_context_processor(request):
"""SEO Context für alle Templates"""
seo_manager = SEOManager(request)
# Standard SEO für alle Seiten
context = {
'seo_manager': seo_manager,
'site_name': 'Kasico',
'site_description': 'Premium Fursuit Shop - Handgefertigte Fursuits made in Germany',
'site_keywords': 'fursuit, furry, custom fursuit, fullsuit, headset, handgefertigt, germany',
}
# Page-spezifische SEO
if hasattr(request, 'resolver_match'):
view_name = request.resolver_match.view_name
if 'product_detail' in view_name:
product = getattr(request, 'product', None)
if product:
context.update({
'meta_tags': seo_manager.get_product_meta_tags(product),
'structured_data': seo_manager.get_structured_data_product(product)
})
elif 'category_detail' in view_name:
category = getattr(request, 'category', None)
if category:
context.update({
'meta_tags': seo_manager.get_category_meta_tags(category)
})
elif 'home' in view_name:
context.update({
'meta_tags': seo_manager.get_homepage_meta_tags(),
'structured_data': seo_manager.get_structured_data_website()
})
return context