diff --git a/Pipfile b/Pipfile index cd3061af..5bec773d 100644 --- a/Pipfile +++ b/Pipfile @@ -18,6 +18,7 @@ python-decouple = "*" raven = "*" Pillow = "*" ipython = "*" +django-sitemaps = "*" [dev-packages] faker = "*" @@ -30,4 +31,4 @@ pytest-freezegun = "*" pytest-mock = "*" [requires] -python_version = "3.7" \ No newline at end of file +python_version = "3.7" diff --git a/Pipfile.lock b/Pipfile.lock index 1cd8f40b..8a888270 100644 --- a/Pipfile.lock +++ b/Pipfile.lock @@ -1,7 +1,7 @@ { "_meta": { "hash": { - "sha256": "66da79ee800cdd4fd7245732cd81ddf4fc9099985e644d1f53dab664edcf2f7d" + "sha256": "55c93238c7921b0d7d84b43e3dcf1ecb31c2d7f7ca27c580a82e87b5e13b9cf8" }, "pipfile-spec": 6, "requires": { @@ -33,18 +33,18 @@ }, "boto3": { "hashes": [ - "sha256:c2816b25fdd792d759ef4d19b6dd551ef93232992d55fbf87307a71ab62ee159", - "sha256:f6f58cf19cc99bc90f1c57dcddf6fdf97d63e152263fc32bfa1d45ee4b278009" + "sha256:35e23af3fcb0d38def987e1e4fc0652dd654b3eb0e4c9c8b2869cdaf289fbfa7", + "sha256:603572f3824be5efc683b4c2327e2ef871d52158790b16ff754cb76b356ec3b5" ], "index": "pypi", - "version": "==1.9.131" + "version": "==1.9.132" }, "botocore": { "hashes": [ - "sha256:691a6fb8ef0edbe2aebe2c1b2707cac9b3f120a87f1c70af014e02542cf398a0", - "sha256:a0275f9b7cab4cab3a701ce53c8711c1a45b77bf1d719b9af9ed232f4abee99a" + "sha256:35c46cf79cbd7fa9b25bee74972e77756e6a584beab10f7ff52abba308e5149e", + "sha256:f7200836d7dd77feae3af9c2e9d7769f69fb8d0ef760da635f3cb5c2ddcb8ade" ], - "version": "==1.12.131" + "version": "==1.12.132" }, "collectfast": { "hashes": [ @@ -99,6 +99,14 @@ "index": "pypi", "version": "==0.5" }, + "django-sitemaps": { + "hashes": [ + "sha256:312f1a9f2c9fbce94f3e8fe08b8665b6506b746d05528c3e7d7fed19b3ad341c", + "sha256:6ba9b2f55697fd42cead7615fd41100b7d6829102d7f8cdb980ea4e807d8382d" + ], + "index": "pypi", + "version": "==1.1" + }, "django-storages": { "hashes": [ "sha256:8e35d2c7baeda5dc6f0b4f9a0fc142d25f9a1bf72b8cebfcbc5db4863abc552d", @@ -152,6 +160,37 @@ ], "version": "==0.9.4" }, + "lxml": { + "hashes": [ + "sha256:03984196d00670b2ab14ae0ea83d5cc0cfa4f5a42558afa9ab5fa745995328f5", + "sha256:0815b0c9f897468de6a386dc15917a0becf48cc92425613aa8bbfc7f0f82951f", + "sha256:175f3825f075cf02d15099eb52658457cf0ff103dcf11512b5d2583e1d40f58b", + "sha256:30e14c62d88d1e01a26936ecd1c6e784d4afc9aa002bba4321c5897937112616", + "sha256:3210da6f36cf4b835ff1be853962b22cc354d506f493b67a4303c88bbb40d57b", + "sha256:40f60819fbd5bad6e191ba1329bfafa09ab7f3f174b3d034d413ef5266963294", + "sha256:43b26a865a61549919f8a42e094dfdb62847113cf776d84bd6b60e4e3fc20ea3", + "sha256:4a03dd682f8e35a10234904e0b9508d705ff98cf962c5851ed052e9340df3d90", + "sha256:62f382cddf3d2e52cf266e161aa522d54fd624b8cc567bc18f573d9d50d40e8e", + "sha256:7b98f0325be8450da70aa4a796c4f06852949fe031878b4aa1d6c417a412f314", + "sha256:846a0739e595871041385d86d12af4b6999f921359b38affb99cdd6b54219a8f", + "sha256:a3080470559938a09a5d0ec558c005282e99ac77bf8211fb7b9a5c66390acd8d", + "sha256:ad841b78a476623955da270ab8d207c3c694aa5eba71f4792f65926dc46c6ee8", + "sha256:afdd75d9735e44c639ffd6258ce04a2de3b208f148072c02478162d0944d9da3", + "sha256:b4fbf9b552faff54742bcd0791ab1da5863363fb19047e68f6592be1ac2dab33", + "sha256:b90c4e32d6ec089d3fa3518436bdf5ce4d902a0787dbd9bb09f37afe8b994317", + "sha256:b91cfe4438c741aeff662d413fd2808ac901cc6229c838236840d11de4586d63", + "sha256:bdb0593a42070b0a5f138b79b872289ee73c8e25b3f0bea6564e795b55b6bcdd", + "sha256:c4e4bca2bb68ce22320297dfa1a7bf070a5b20bcbaec4ee023f83d2f6e76496f", + "sha256:cec4ab14af9eae8501be3266ff50c3c2aecc017ba1e86c160209bb4f0423df6a", + "sha256:e83b4b2bf029f5104bc1227dbb7bf5ace6fd8fabaebffcd4f8106fafc69fc45f", + "sha256:e995b3734a46d41ae60b6097f7c51ba9958648c6d1e0935b7e0ee446ee4abe22", + "sha256:f679d93dec7f7210575c85379a31322df4c46496f184ef650d3aba1484b38a2d", + "sha256:fd213bb5166e46974f113c8228daaef1732abc47cb561ce9c4c8eaed4bd3b09b", + "sha256:fdcb57b906dbc1f80666e6290e794ab8fb959a2e17aa5aee1758a85d1da4533f", + "sha256:ff424b01d090ffe1947ec7432b07f536912e0300458f9a7f48ea217dd8362b86" + ], + "version": "==4.3.3" + }, "parso": { "hashes": [ "sha256:17cc2d7a945eb42c3569d4564cdf49bde221bc2b552af3eca9c1aad517dcdd33", diff --git a/pythonpro/core/tests/test_sitemaps_and_robots.py b/pythonpro/core/tests/test_sitemaps_and_robots.py new file mode 100644 index 00000000..e8fad532 --- /dev/null +++ b/pythonpro/core/tests/test_sitemaps_and_robots.py @@ -0,0 +1,21 @@ +import pytest +from django.test import Client +from django.urls import reverse + + +@pytest.fixture +def resp_sitemap(client: Client, db): + return client.get(reverse('core:sitemap'), secure=True) + + +def test_sitemap_status_code(resp_sitemap): + assert resp_sitemap.status_code == 200 + + +@pytest.fixture +def resp_robots(client: Client, db): + return client.get(reverse('core:robots'), secure=True) + + +def test_robots_status_code(resp_robots): + assert resp_robots.status_code == 200 diff --git a/pythonpro/core/urls.py b/pythonpro/core/urls.py index 1b456eae..898fb6a0 100644 --- a/pythonpro/core/urls.py +++ b/pythonpro/core/urls.py @@ -1,10 +1,13 @@ from django.urls import path +from django_sitemaps import robots_txt from . import views app_name = 'core' urlpatterns = [ path('', views.index, name='index'), + path('sitemap.xml', views.sitemap, name='sitemap'), + path('robots.txt', robots_txt(timeout=86400), name='robots'), path('tech-talks', views.teck_talks, name='tech_talks'), path('podcast', views.podcast, name='podcast'), path('obrigado', views.thanks, name='thanks'), diff --git a/pythonpro/core/views.py b/pythonpro/core/views.py index 0ae1762f..b3eaeab9 100644 --- a/pythonpro/core/views.py +++ b/pythonpro/core/views.py @@ -1,11 +1,14 @@ +from django.conf import settings from django.contrib.auth.decorators import login_required from django.contrib.auth.views import PasswordChangeView from django.shortcuts import render -from django.urls import reverse_lazy +from django.urls import reverse_lazy, reverse from django.views.generic import UpdateView +from django_sitemaps import Sitemap from pythonpro.core.forms import UserEmailForm from pythonpro.core.models import User +from pythonpro.promos.facade import find_all_videos def index(request): @@ -29,6 +32,22 @@ def profile(request): return render(request, 'core/profile_detail.html', {}) +def sitemap(request): + map = Sitemap( + build_absolute_uri=request.build_absolute_uri, + ) + + for section in 'core:index core:podcast core:tech_talks modules:index'.split(): + map.add(reverse(section), changefreq='weekly') + + for video in find_all_videos(): + map.add(video.get_absolute_url(), changefreq='monthly') + + return map.response( + pretty_print=settings.DEBUG, + ) + + class _ProfileUpdateName(UpdateView): model = User fields = ('first_name',) diff --git a/pythonpro/promos/facade.py b/pythonpro/promos/facade.py new file mode 100644 index 00000000..cdfbca63 --- /dev/null +++ b/pythonpro/promos/facade.py @@ -0,0 +1,5 @@ +from pythonpro.promos.models import Video as _Video + + +def find_all_videos(): + return _Video.objects.all() diff --git a/pythonpro/promos/models.py b/pythonpro/promos/models.py index 017c4f65..d37e85a9 100644 --- a/pythonpro/promos/models.py +++ b/pythonpro/promos/models.py @@ -1,7 +1,11 @@ from django.db import models +from django.urls import reverse class Video(models.Model): title = models.CharField(max_length=100) vimeo_id = models.CharField(max_length=100) slug = models.SlugField(max_length=100, unique=True) + + def get_absolute_url(self): + return reverse('promos:video', kwargs={'slug': self.slug})