Skip to content
Snippets Groups Projects
Commit b10290a9 authored by florimondmanca's avatar florimondmanca
Browse files

add key figure model, api and tests

parent 18376add
No related branches found
No related tags found
No related merge requests found
...@@ -28,5 +28,6 @@ router.register(r'tutoring/sessions', tutoring_views.TutoringSessionViewSet, ...@@ -28,5 +28,6 @@ router.register(r'tutoring/sessions', tutoring_views.TutoringSessionViewSet,
router.register(r'articles', showcase_site_views.ArticleViewSet) router.register(r'articles', showcase_site_views.ArticleViewSet)
router.register(r'categories', showcase_site_views.CategoryViewSet) router.register(r'categories', showcase_site_views.CategoryViewSet)
router.register(r'testimonies', showcase_site_views.TestimonyViewSet) router.register(r'testimonies', showcase_site_views.TestimonyViewSet)
router.register(r'keyfigures', showcase_site_views.KeyFigureViewSet)
urlpatterns = router.urls urlpatterns = router.urls
...@@ -42,6 +42,8 @@ THIRD_PARTY_APPS = [ ...@@ -42,6 +42,8 @@ THIRD_PARTY_APPS = [
'dry_rest_permissions', 'dry_rest_permissions',
# Frontend integration # Frontend integration
'corsheaders', 'corsheaders',
# Sortable models in Admin
'adminsortable2',
] ]
PROJECT_APPS = [ PROJECT_APPS = [
'users.apps.UsersConfig', 'users.apps.UsersConfig',
......
"""Showcase site admin panel configuration.""" """Showcase site admin panel configuration."""
from django.contrib import admin from django.contrib import admin
from adminsortable2.admin import SortableAdminMixin
from .models import Article from .models import Article
from .models import Category from .models import Category
from .models import Testimony from .models import Testimony
from .models import KeyFigure
# Register your models here. # Register your models here.
...@@ -19,9 +21,6 @@ class ArticleAdmin(admin.ModelAdmin): ...@@ -19,9 +21,6 @@ class ArticleAdmin(admin.ModelAdmin):
# reorganize fields # reorganize fields
fields = ('title', 'slug', 'categories', 'pinned', 'image', 'content',) fields = ('title', 'slug', 'categories', 'pinned', 'image', 'content',)
class Meta: # noqa
model = Article
@admin.register(Category) @admin.register(Category)
class CategoryAdmin(admin.ModelAdmin): class CategoryAdmin(admin.ModelAdmin):
...@@ -33,9 +32,6 @@ class CategoryAdmin(admin.ModelAdmin): ...@@ -33,9 +32,6 @@ class CategoryAdmin(admin.ModelAdmin):
return obj.article_set.count() return obj.article_set.count()
get_num_articles.short_description = "Nombre d'articles" get_num_articles.short_description = "Nombre d'articles"
class Meta: # noqa
model = Category
@admin.register(Testimony) @admin.register(Testimony)
class TestimonyAdmin(admin.ModelAdmin): class TestimonyAdmin(admin.ModelAdmin):
...@@ -49,3 +45,8 @@ class TestimonyAdmin(admin.ModelAdmin): ...@@ -49,3 +45,8 @@ class TestimonyAdmin(admin.ModelAdmin):
return obj.content[:100] + ' […]' return obj.content[:100] + ' […]'
return obj.content return obj.content
get_preview.short_description = 'Aperçu' get_preview.short_description = 'Aperçu'
@admin.register(KeyFigure)
class KeyFigureAdmin(SortableAdminMixin, admin.ModelAdmin):
pass
...@@ -3,6 +3,7 @@ ...@@ -3,6 +3,7 @@
from django.utils.text import slugify from django.utils.text import slugify
from django.db import models from django.db import models
from django.shortcuts import reverse from django.shortcuts import reverse
from django.core.validators import integer_validator
from dry_rest_permissions.generics import authenticated_users from dry_rest_permissions.generics import authenticated_users
from markdownx.models import MarkdownxField from markdownx.models import MarkdownxField
...@@ -120,3 +121,31 @@ class Testimony(models.Model): ...@@ -120,3 +121,31 @@ class Testimony(models.Model):
def __str__(self): def __str__(self):
return self.author return self.author
class KeyFigure(models.Model):
"""A key figure about the association."""
figure = models.PositiveIntegerField(
'chiffre',
help_text='Un nombre entier positif. Exemple : 60')
description = models.CharField(
max_length=100,
help_text=(
"Une courte description du chiffre "
"(sera convertie en minuscules). "
"Exemple : millions d'amis."))
order = models.PositiveIntegerField('ordre', default=0)
class Meta: # noqa
verbose_name = 'chiffre clé'
verbose_name_plural = 'chiffres clés'
ordering = ('order',)
def save(self, *args, **kwargs):
# Always save description as lowercase
self.description = self.description.lower()
super().save(*args, **kwargs)
def __str__(self):
return '{} {}'.format(self.figure, self.description.lower())
"""Showcase site API serializers.""" """Showcase site API serializers."""
from rest_framework import serializers from rest_framework import serializers
from .models import Article, Category, Testimony from .models import Article, Category, Testimony, KeyFigure
class CategoryField(serializers.RelatedField): class CategoryField(serializers.RelatedField):
...@@ -62,3 +62,11 @@ class TestimonySerializer(serializers.ModelSerializer): ...@@ -62,3 +62,11 @@ class TestimonySerializer(serializers.ModelSerializer):
class Meta: # noqa class Meta: # noqa
model = Testimony model = Testimony
fields = ('id', 'author', 'created', 'content',) fields = ('id', 'author', 'created', 'content',)
class KeyFigureSerializer(serializers.ModelSerializer):
"""Serializer for KeyFigure."""
class Meta: # noqa
model = KeyFigure
fields = ('order', 'figure', 'description',)
...@@ -5,9 +5,11 @@ from dry_rest_permissions.generics import DRYPermissions ...@@ -5,9 +5,11 @@ from dry_rest_permissions.generics import DRYPermissions
from .serializers import ArticleSerializer from .serializers import ArticleSerializer
from .serializers import CategorySerializer from .serializers import CategorySerializer
from .serializers import TestimonySerializer from .serializers import TestimonySerializer
from .serializers import KeyFigureSerializer
from .models import Article from .models import Article
from .models import Category from .models import Category
from .models import Testimony from .models import Testimony
from .models import KeyFigure
# Create your views here. # Create your views here.
...@@ -41,3 +43,13 @@ class TestimonyViewSet(viewsets.ReadOnlyModelViewSet): ...@@ -41,3 +43,13 @@ class TestimonyViewSet(viewsets.ReadOnlyModelViewSet):
serializer_class = TestimonySerializer serializer_class = TestimonySerializer
queryset = Testimony.objects.all() queryset = Testimony.objects.all()
class KeyFigureViewSet(viewsets.ReadOnlyModelViewSet):
"""API endpoint to view key figures.
Actions: list, retrieve
"""
serializer_class = KeyFigureSerializer
queryset = KeyFigure.objects.all()
...@@ -4,7 +4,8 @@ FactoryBoy docs: http://factoryboy.readthedocs.io/en/latest/index.html ...@@ -4,7 +4,8 @@ FactoryBoy docs: http://factoryboy.readthedocs.io/en/latest/index.html
""" """
import datetime import datetime
import random
from string import printable
import factory import factory
import factory.django import factory.django
...@@ -49,12 +50,19 @@ class UserFactory(factory.DjangoModelFactory): ...@@ -49,12 +50,19 @@ class UserFactory(factory.DjangoModelFactory):
last_name = factory.Faker('last_name', locale='fr') last_name = factory.Faker('last_name', locale='fr')
# email built after first_name and last_name # email built after first_name and last_name
uid = factory.Sequence(lambda n: n) uid = factory.Sequence(lambda n: n)
email = factory.LazyAttribute(
lambda o: @factory.lazy_attribute
'{}.{}-{}@exmaple.net'.format( def email(self):
o.first_name.lower(), # email can only contain printable characters,
o.last_name.lower(), # i.e. not "ç", no "é", ...
o.uid)) def printable_only(s):
return ''.join(c for c in filter(lambda x: x in printable, s))
return '{}.{}-{}@example.net'.format(
printable_only(self.first_name.lower()),
printable_only(self.last_name.lower()),
self.uid)
# this is a default, override by passing `profile_type='...'` in create() # this is a default, override by passing `profile_type='...'` in create()
profile_type = 'student' profile_type = 'student'
date_of_birth = factory.Faker('date_this_century', date_of_birth = factory.Faker('date_this_century',
...@@ -176,6 +184,7 @@ class StudentFactory(ProfileFactory): ...@@ -176,6 +184,7 @@ class StudentFactory(ProfileFactory):
class ArticleFactory(factory.DjangoModelFactory): class ArticleFactory(factory.DjangoModelFactory):
"""Article object factory."""
class Meta: # noqa class Meta: # noqa
model = showcase_site.models.Article model = showcase_site.models.Article
...@@ -187,6 +196,7 @@ class ArticleFactory(factory.DjangoModelFactory): ...@@ -187,6 +196,7 @@ class ArticleFactory(factory.DjangoModelFactory):
class CategoryFactory(factory.DjangoModelFactory): class CategoryFactory(factory.DjangoModelFactory):
"""Category object factory."""
class Meta: # noqa class Meta: # noqa
model = showcase_site.models.Category model = showcase_site.models.Category
...@@ -195,6 +205,7 @@ class CategoryFactory(factory.DjangoModelFactory): ...@@ -195,6 +205,7 @@ class CategoryFactory(factory.DjangoModelFactory):
class TestimonyFactory(factory.DjangoModelFactory): class TestimonyFactory(factory.DjangoModelFactory):
"""Testimony object factory."""
class Meta: # noqa class Meta: # noqa
model = showcase_site.models.Testimony model = showcase_site.models.Testimony
...@@ -202,3 +213,14 @@ class TestimonyFactory(factory.DjangoModelFactory): ...@@ -202,3 +213,14 @@ class TestimonyFactory(factory.DjangoModelFactory):
author_name = factory.Faker('name', locale='fr') author_name = factory.Faker('name', locale='fr')
author_position = factory.Faker('job', locale='fr') author_position = factory.Faker('job', locale='fr')
content = factory.Faker('text', max_nb_chars=200, locale='fr') content = factory.Faker('text', max_nb_chars=200, locale='fr')
class KeyFigureFactory(factory.DjangoModelFactory):
"""Key figure object factory."""
class Meta: # noqa
model = showcase_site.models.KeyFigure
figure = factory.LazyFunction(lambda: random.randint(10, 500))
description = factory.Faker('text', max_nb_chars=100)
order = factory.Sequence(lambda n: n)
"""KeyFigure API tests."""
from rest_framework import status
from tests.factory import KeyFigureFactory
from tests.utils import HyperlinkedAPITestCase
from showcase_site.serializers import KeyFigureSerializer
class KeyFigureEndpointsTest(HyperlinkedAPITestCase):
"""Test access to the key figures endpoints."""
factory = KeyFigureFactory
serializer_class = KeyFigureSerializer
def perform_list(self):
url = '/api/keyfigures/'
response = self.client.get(url)
return response
def test_list_no_authentication_required(self):
self.assertRequestResponse(
self.perform_list,
user=None,
expected_status_code=status.HTTP_200_OK)
def perform_retrieve(self):
obj = self.factory.create()
url = '/api/keyfigures/{obj.pk}/'.format(obj=obj)
response = self.client.get(url)
return response
def test_retrieve_no_authentication_required(self):
self.assertRequestResponse(
self.perform_retrieve,
user=None,
expected_status_code=status.HTTP_200_OK)
"""KeyFigure model tests."""
from tests.factory import KeyFigureFactory
from tests.utils import ModelTestCase
import showcase_site.models
class KeyFigureTest(ModelTestCase):
"""Test the KeyFigure model."""
model = showcase_site.models.KeyFigure
field_tests = {
'figure': {
'verbose_name': 'chiffre',
},
'description': {
'max_length': 100,
},
'order': {
'verbose_name': 'ordre',
# v required by adminsortable
'default': 0,
'editable': True,
# ^
},
}
model_tests = {
'verbose_name': 'chiffre clé',
'verbose_name_plural': 'chiffres clés',
'ordering': ('order',),
}
def test_description_is_saved_as_lowercase(self):
obj = KeyFigureFactory.create(description='TO LOWERCASE')
self.assertEqual(obj.description, 'to lowercase')
...@@ -65,7 +65,8 @@ class HyperlinkedAPITestCase(APITestCase): ...@@ -65,7 +65,8 @@ class HyperlinkedAPITestCase(APITestCase):
self.client.force_login(user) self.client.force_login(user)
response = perform_request() response = perform_request()
self._check_response(perform_request, response) self._check_response(perform_request, response)
self.assertEqual(response.status_code, expected_status_code) self.assertEqual(response.status_code, expected_status_code,
response.data)
def assertUserRequestResponse(self, perform_request, def assertUserRequestResponse(self, perform_request,
expected_status_code): expected_status_code):
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment