1
0

seo_utils.py 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156
  1. """
  2. Utilitaires SEO pour le blog
  3. Génère les métadonnées structurées et optimise le référencement
  4. """
  5. from django.conf import settings
  6. from django.urls import reverse
  7. from datetime import datetime
  8. import json
  9. def generate_article_schema(article, request):
  10. """
  11. Génère le schema.org JSON-LD pour un article de blog
  12. Format Article pour Google Rich Results
  13. """
  14. # URL absolue de l'article
  15. article_url = request.build_absolute_uri(
  16. reverse('blog_play', args=[article.b_titre_slugify])
  17. )
  18. # Image de l'article ou image par défaut
  19. image_url = article.b_description_img if article.b_description_img else \
  20. request.build_absolute_uri(settings.STATIC_URL + 'logo-txt-Mrduhaz.png')
  21. schema = {
  22. "@context": "https://schema.org",
  23. "@type": "BlogPosting",
  24. "headline": article.b_titre,
  25. "description": article.b_description[:160] if article.b_description else "",
  26. "image": image_url,
  27. "datePublished": article.b_publdate.isoformat() if article.b_publdate else "",
  28. "dateModified": article.b_publdate.isoformat() if article.b_publdate else "",
  29. "author": {
  30. "@type": "Person",
  31. "name": "Mr Duhaz",
  32. "url": request.build_absolute_uri('/')
  33. },
  34. "publisher": {
  35. "@type": "Organization",
  36. "name": "Duhaz Blog",
  37. "url": request.build_absolute_uri('/'),
  38. "logo": {
  39. "@type": "ImageObject",
  40. "url": request.build_absolute_uri(settings.STATIC_URL + 'logo-txt-Mrduhaz.png')
  41. }
  42. },
  43. "mainEntityOfPage": {
  44. "@type": "WebPage",
  45. "@id": article_url
  46. },
  47. "wordCount": len(article.b_contenu.split()) if article.b_contenu else 0,
  48. "articleSection": [cat.cb_titre for cat in article.b_cat.all()],
  49. "keywords": article.b_mots_clefs,
  50. "inLanguage": "fr-FR"
  51. }
  52. return json.dumps(schema, ensure_ascii=False)
  53. def generate_breadcrumb_schema(items, request):
  54. """
  55. Génère le schema.org pour le fil d'Ariane (breadcrumb)
  56. items: liste de tuples (nom, url)
  57. """
  58. schema = {
  59. "@context": "https://schema.org",
  60. "@type": "BreadcrumbList",
  61. "itemListElement": []
  62. }
  63. for position, (name, url) in enumerate(items, start=1):
  64. schema["itemListElement"].append({
  65. "@type": "ListItem",
  66. "position": position,
  67. "name": name,
  68. "item": request.build_absolute_uri(url) if url else None
  69. })
  70. return json.dumps(schema, ensure_ascii=False)
  71. def generate_website_schema(request):
  72. """
  73. Génère le schema.org pour le site web principal
  74. """
  75. schema = {
  76. "@context": "https://schema.org",
  77. "@type": "WebSite",
  78. "name": "Duhaz Blog",
  79. "url": request.build_absolute_uri('/'),
  80. "description": "Blog tech et développement par Mr Duhaz",
  81. "inLanguage": "fr-FR",
  82. "potentialAction": {
  83. "@type": "SearchAction",
  84. "target": {
  85. "@type": "EntryPoint",
  86. "urlTemplate": request.build_absolute_uri(reverse('blog_index')) + "?b_search={search_term_string}"
  87. },
  88. "query-input": "required name=search_term_string"
  89. }
  90. }
  91. return json.dumps(schema, ensure_ascii=False)
  92. def generate_og_tags(article, request):
  93. """
  94. Génère les balises Open Graph pour le partage sur réseaux sociaux
  95. """
  96. article_url = request.build_absolute_uri(
  97. reverse('blog_play', args=[article.b_titre_slugify])
  98. )
  99. image_url = article.b_description_img if article.b_description_img else \
  100. request.build_absolute_uri(settings.STATIC_URL + 'logo-txt-Mrduhaz.png')
  101. return {
  102. 'og:type': 'article',
  103. 'og:title': article.b_titre,
  104. 'og:description': article.b_description[:160] if article.b_description else "",
  105. 'og:url': article_url,
  106. 'og:image': image_url,
  107. 'og:site_name': 'Duhaz Blog',
  108. 'og:locale': 'fr_FR',
  109. 'article:published_time': article.b_publdate.isoformat() if article.b_publdate else "",
  110. 'article:author': 'Mr Duhaz',
  111. 'article:section': ', '.join([cat.cb_titre for cat in article.b_cat.all()]),
  112. 'article:tag': article.b_mots_clefs
  113. }
  114. def generate_twitter_cards(article, request):
  115. """
  116. Génère les balises Twitter Card pour le partage sur Twitter
  117. """
  118. article_url = request.build_absolute_uri(
  119. reverse('blog_play', args=[article.b_titre_slugify])
  120. )
  121. image_url = article.b_description_img if article.b_description_img else \
  122. request.build_absolute_uri(settings.STATIC_URL + 'logo-txt-Mrduhaz.png')
  123. return {
  124. 'twitter:card': 'summary_large_image',
  125. 'twitter:title': article.b_titre,
  126. 'twitter:description': article.b_description[:160] if article.b_description else "",
  127. 'twitter:image': image_url,
  128. 'twitter:url': article_url
  129. }
  130. def get_canonical_url(request):
  131. """
  132. Retourne l'URL canonical pour éviter le duplicate content
  133. """
  134. return request.build_absolute_uri(request.path)