Skip to content

Commit

Permalink
(documentation): add CategoryCreateView and CategoryUpdateView, their…
Browse files Browse the repository at this point in the history
…s tests, templates and navigation
  • Loading branch information
vincentporte committed Sep 23, 2024
1 parent ea0bd1a commit 11b3a24
Show file tree
Hide file tree
Showing 15 changed files with 393 additions and 6 deletions.
6 changes: 3 additions & 3 deletions lacommunaute/documentation/abstract_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,10 @@ class AbstractPublication(AbstractDatedModel):
slug = models.SlugField(max_length=255, verbose_name=_("Slug"), unique=True)

description = MarkupTextField(verbose_name=_("Description"), null=True, blank=True)
short_description = models.CharField(
max_length=400, blank=True, null=True, verbose_name="Description courte (SEO)"
)
short_description = models.CharField(max_length=400, verbose_name="Description courte (SEO)")
image = models.ImageField(
blank=True,
null=True,
storage=S3Boto3Storage(bucket_name=settings.AWS_STORAGE_BUCKET_NAME, file_overwrite=False),
validators=[validate_image_size],
)
Expand Down
81 changes: 81 additions & 0 deletions lacommunaute/documentation/forms.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
from django import forms
from django.conf import settings
from taggit.models import Tag

from lacommunaute.documentation.models import Category, Document
from lacommunaute.partner.models import Partner
from lacommunaute.utils.iframe import wrap_iframe_in_div_tag


class DocumentationFormMixin:
name = forms.CharField(required=True, label="Titre")
short_description = forms.CharField(
widget=forms.Textarea(attrs={"rows": 3}),
max_length=400,
required=True,
label="Sous-titre (400 caractères pour le SEO)",
)
description = forms.CharField(
widget=forms.Textarea(attrs={"rows": 20}), required=True, label="Contenu (markdown autorisé)"
)
image = forms.ImageField(
label="Banniere de couverture, format 1200 x 630 pixels recommandé",
widget=forms.FileInput(attrs={"accept": settings.SUPPORTED_IMAGE_FILE_TYPES.keys()}),
)

def save(self, commit=True):
instance = super().save(commit=False)
instance.description = wrap_iframe_in_div_tag(self.cleaned_data.get("description"))

if commit:
instance.save()
return instance


class CategoryForm(forms.ModelForm, DocumentationFormMixin):
class Meta:
model = Category
fields = ["name", "short_description", "description", "image"]


class DocumentForm(forms.ModelForm, DocumentationFormMixin):
certified = forms.BooleanField(required=False, label="Certifiée par la communauté de l'inclusion")
partner = forms.ModelChoiceField(
label="Sélectionner un partenaire",
queryset=Partner.objects.all(),
required=False,
)
category = forms.ModelChoiceField(
label="Sélectionner une catégorie documentaire",
queryset=Category.objects.all(),
required=False,
)
tags = forms.ModelMultipleChoiceField(
label="Sélectionner un ou plusieurs tags",
queryset=Tag.objects.all(),
widget=forms.CheckboxSelectMultiple,
required=False,
)
new_tags = forms.CharField(required=False, label="Ajouter un tag ou plusieurs tags (séparés par des virgules)")

def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
if self.instance.pk:
self.fields["tags"].initial = self.instance.tags.all()

def save(self, commit=True):
instance = super().save(commit=False)

if commit:
instance.save()
instance.tags.set(self.cleaned_data["tags"])
(
instance.tags.add(*[tag.strip() for tag in self.cleaned_data["new_tags"].split(",")])
if self.cleaned_data.get("new_tags")
else None
)
return instance

class Meta:
model = Document
fields = ["name", "short_description", "description", "image", "certified", "partner", "category"]
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
# Generated by Django 5.0.9 on 2024-09-19 14:09

import storages.backends.s3
from django.db import migrations, models

import lacommunaute.utils.validators


class Migration(migrations.Migration):

dependencies = [
("documentation", "0001_initial"),
]

operations = [
migrations.AlterField(
model_name="category",
name="image",
field=models.ImageField(
blank=True,
null=True,
storage=storages.backends.s3.S3Storage(bucket_name="private-bucket", file_overwrite=False),
upload_to="",
validators=[lacommunaute.utils.validators.validate_image_size],
),
),
migrations.AlterField(
model_name="category",
name="short_description",
field=models.CharField(default="empty", max_length=400, verbose_name="Description courte (SEO)"),
preserve_default=False,
),
migrations.AlterField(
model_name="document",
name="image",
field=models.ImageField(
blank=True,
null=True,
storage=storages.backends.s3.S3Storage(bucket_name="private-bucket", file_overwrite=False),
upload_to="",
validators=[lacommunaute.utils.validators.validate_image_size],
),
),
migrations.AlterField(
model_name="document",
name="short_description",
field=models.CharField(default="empty", max_length=400, verbose_name="Description courte (SEO)"),
preserve_default=False,
),
]
3 changes: 3 additions & 0 deletions lacommunaute/documentation/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@ def get_absolute_url(self, with_fqdn=False):
return f"{settings.COMMU_PROTOCOL}://{settings.COMMU_FQDN}{absolute_url}"
return absolute_url

def get_update_url(self):
return reverse("documentation:category_update", kwargs={"pk": self.pk, "slug": self.slug})


class Document(AbstractPublication):
category = models.ForeignKey(Category, on_delete=models.CASCADE, related_name="documents")
Expand Down
58 changes: 58 additions & 0 deletions lacommunaute/documentation/tests/tests_category_create_view.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import pytest # noqa

from django.urls import reverse

from lacommunaute.documentation.models import Category
from lacommunaute.users.factories import UserFactory


@pytest.fixture(name="url")
def fixture_url():
return reverse("documentation:category_create")


@pytest.fixture(name="superuser")
def fixture_superuser(db):
return UserFactory(is_superuser=True)


def test_user_pass_test_mixin(client, db, url, superuser):
response = client.get(url)
assert response.status_code == 302

client.force_login(superuser)
response = client.get(url)
assert response.status_code == 200


def test_context(client, db, url, superuser):
client.force_login(superuser)
response = client.get(url)
assert response.status_code == 200
assert response.context["title"] == "Ajouter une catégorie documentaire"
assert response.context["back_url"] == reverse("documentation:category_list")
assert response.context["form"].fields.keys() == {"name", "short_description", "description", "image"}
assert response.context["form"].fields["name"].required
assert response.context["form"].fields["short_description"].required


def test_create_category(client, db, url, superuser):
client.force_login(superuser)
response = client.post(
url,
data={
"name": "Test Name",
"short_description": "Test Short Description",
"description": "Test Description",
},
)
assert response.status_code == 302

category = Category.objects.get()
assert category.name == "Test Name"
assert category.short_description == "Test Short Description"
assert category.description.raw == "Test Description"
assert category.slug == "test-name"


# TODO tester avec image
24 changes: 24 additions & 0 deletions lacommunaute/documentation/tests/tests_category_detail_view.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

from lacommunaute.documentation.factories import CategoryFactory, DocumentFactory
from lacommunaute.utils.testing import parse_response_to_soup
from lacommunaute.users.factories import UserFactory


@pytest.fixture(name="category")
Expand Down Expand Up @@ -32,3 +33,26 @@ def test_category_detail_view_with_tagged_documents(client, db, url, category, a
assert response.status_code == 200
content = parse_response_to_soup(response, selector="main", replace_img_src=True, replace_in_href=[category])
assert str(content) == snapshot(name=snapshot_name)


@pytest.mark.parametrize(
"user_factory,link_is_visible",
[
(None, False),
(UserFactory, False),
(lambda: UserFactory(is_superuser=True), True),
],
)
def test_update_link_is_visible_for_superuser_only(client, db, url, category, user_factory, link_is_visible):
user = user_factory() if user_factory else None
if user:
client.force_login(user)

response = client.get(url)
assert response.status_code == 200

category_update_url = reverse("documentation:category_update", kwargs={"pk": category.pk, "slug": category.slug})
if link_is_visible:
assert category_update_url in str(response.content)
else:
assert category_update_url not in str(response.content)
25 changes: 25 additions & 0 deletions lacommunaute/documentation/tests/tests_category_list_view.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

from lacommunaute.documentation.factories import CategoryFactory
from lacommunaute.utils.testing import parse_response_to_soup
from lacommunaute.users.factories import UserFactory


@pytest.fixture(name="url")
Expand Down Expand Up @@ -30,3 +31,27 @@ def test_category_list_view(client, db, url, objects, status_code, snapshot_name
assert response.status_code == status_code
content = parse_response_to_soup(response, selector="main", replace_img_src=True, replace_in_href=categories)
assert str(content) == snapshot(name=snapshot_name)


@pytest.mark.parametrize(
"user_factory,link_is_visible",
[
(None, False),
(UserFactory, False),
(lambda: UserFactory(is_superuser=True), True),
],
)
def test_create_category_link_for_superuser_only(client, db, url, user_factory, link_is_visible):
user = user_factory() if user_factory else None
if user:
client.force_login(user)

response = client.get(url)
assert response.status_code == 200

category_create_url = reverse("documentation:category_create")

if link_is_visible:
assert category_create_url in str(response.content)
else:
assert category_create_url not in str(response.content)
64 changes: 64 additions & 0 deletions lacommunaute/documentation/tests/tests_category_update_view.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import pytest # noqa

from django.urls import reverse

from lacommunaute.documentation.factories import CategoryFactory
from lacommunaute.documentation.models import Category
from lacommunaute.users.factories import UserFactory


@pytest.fixture(name="category")
def fixture_category(db):
return CategoryFactory(for_snapshot=True)


@pytest.fixture(name="url")
def fixture_url(category):
return reverse("documentation:category_update", kwargs={"pk": category.pk, "slug": category.slug})


@pytest.fixture(name="superuser")
def fixture_superuser(db):
return UserFactory(is_superuser=True)


def test_user_pass_test_mixin(client, db, url, superuser):
response = client.get(url)
assert response.status_code == 302

client.force_login(superuser)
response = client.get(url)
assert response.status_code == 200


def test_context(client, db, url, superuser, category):
client.force_login(superuser)
response = client.get(url)
assert response.status_code == 200
assert response.context["title"] == "Mettre à jour la catégorie Test Category"
assert response.context["back_url"] == category.get_absolute_url()
assert response.context["form"].fields.keys() == {"name", "short_description", "description", "image"}
assert response.context["form"].fields["name"].required
assert response.context["form"].fields["short_description"].required


def test_update_category(client, db, url, superuser, category):
client.force_login(superuser)
response = client.post(
url,
data={
"name": "Updated Name",
"short_description": "Updated Short Description",
"description": "Updated Description",
},
)
assert response.status_code == 302

category = Category.objects.get()
assert category.name == "Updated Name"
assert category.short_description == "Updated Short Description"
assert category.description.raw == "Updated Description"
assert category.slug == "updated-name"


# TODO tester la mise à jour de l'image
4 changes: 4 additions & 0 deletions lacommunaute/documentation/tests/tests_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@ def test_get_absolute_url(self, db):
category = CategoryFactory()
assert category.get_absolute_url() == f"/documentation/{category.slug}-{category.pk}/"

def test_get_update_url(self, db):
category = CategoryFactory()
assert category.get_update_url() == f"/documentation/{category.slug}-{category.pk}/update/"


class TestDocument:
def test_slug(self, db):
Expand Down
10 changes: 9 additions & 1 deletion lacommunaute/documentation/urls.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
from django.urls import path

from lacommunaute.documentation.views import CategoryDetailView, CategoryListView, DocumentDetailView
from lacommunaute.documentation.views import (
CategoryCreateView,
CategoryDetailView,
CategoryListView,
CategoryUpdateView,
CategoryCreateView,
)


app_name = "documentation"
Expand All @@ -9,4 +15,6 @@
urlpatterns = [
path("", CategoryListView.as_view(), name="category_list"),
path("<str:slug>-<int:pk>/", CategoryDetailView.as_view(), name="category_detail"),
path("create/", CategoryCreateView.as_view(), name="category_create"),
path("<str:slug>-<int:pk>/update/", CategoryUpdateView.as_view(), name="category_update"),
]
Loading

0 comments on commit 11b3a24

Please sign in to comment.