API Timeline ajoutée !
This commit is contained in:
parent
95d8b30c3c
commit
92ea2f6af7
@ -4,7 +4,7 @@ Current version (0.2) :
|
|||||||
- Renommage des consoles en « plateformes »
|
- Renommage des consoles en « plateformes »
|
||||||
- Activation des requêtes CORS pour permettre à une autre application d'accéder à l'API
|
- Activation des requêtes CORS pour permettre à une autre application d'accéder à l'API
|
||||||
- MàJ vers Django 1.11.5
|
- MàJ vers Django 1.11.5
|
||||||
- Activation d'une API (pour les consoles et les jeux) accessible par l'administrateur (avec documentation)
|
- Activation d'une API accessible par l'administrateur (avec documentation)
|
||||||
- Ajout d'une page d'accueil listant les jeux vidéos en cours, la liste complète et les 5 dernières activités sur ces derniers triées par date
|
- Ajout d'une page d'accueil listant les jeux vidéos en cours, la liste complète et les 5 dernières activités sur ces derniers triées par date
|
||||||
- Nouveau champ 'note' pour la progression dans le jeu
|
- Nouveau champ 'note' pour la progression dans le jeu
|
||||||
- Omission de l'état "Nouveau" pour les jeux
|
- Omission de l'état "Nouveau" pour les jeux
|
||||||
|
5
TODO
5
TODO
@ -1,6 +1,7 @@
|
|||||||
# À faire
|
# À faire
|
||||||
|
|
||||||
* Ajouter des help_text à tous les champs + classes enfants. Les traduire.
|
* Test API sur l'ordre de tri des date pour les Timeline
|
||||||
|
* Bug? : dans la documentation de l'API, les champs "required" sont peu nombreux. Regarder s'il faut faire quelque chose de particulier pour la Timeline (sur date et status) et Game (sur status).
|
||||||
* étudier la possibilité à l'utilisateur, via des variables d'environnement, de configurer un email, une autre BDD, etc.
|
* étudier la possibilité à l'utilisateur, via des variables d'environnement, de configurer un email, une autre BDD, etc.
|
||||||
|
|
||||||
## Dépôt / code
|
## Dépôt / code
|
||||||
@ -11,7 +12,6 @@
|
|||||||
|
|
||||||
* Unplayed ne doit pas s'afficher si on a un état différent de Unfinished
|
* Unplayed ne doit pas s'afficher si on a un état différent de Unfinished
|
||||||
* Gérer les figurines (trouver le nom de l'objet qui permet de les regrouper, par exemple Skylanders, Disney Infinity, etc.)
|
* Gérer les figurines (trouver le nom de l'objet qui permet de les regrouper, par exemple Skylanders, Disney Infinity, etc.)
|
||||||
* API django rest framework
|
|
||||||
* ajouter une date d'obtention du jeu vidéo (par défaut aujourd'hui) => est-ce vraiment utile si on a la Timeline ?
|
* ajouter une date d'obtention du jeu vidéo (par défaut aujourd'hui) => est-ce vraiment utile si on a la Timeline ?
|
||||||
|
|
||||||
## Tests
|
## Tests
|
||||||
@ -30,7 +30,6 @@
|
|||||||
|
|
||||||
## Documentation
|
## Documentation
|
||||||
|
|
||||||
* documentation API
|
|
||||||
* documentation du projet : installation par Docker, utilisation des variables d'environnement pour changer différentes choses, etc.
|
* documentation du projet : installation par Docker, utilisation des variables d'environnement pour changer différentes choses, etc.
|
||||||
|
|
||||||
# Idée
|
# Idée
|
||||||
|
@ -7,7 +7,7 @@ msgid ""
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: 0.1\n"
|
"Project-Id-Version: 0.1\n"
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"POT-Creation-Date: 2017-09-16 15:15+0000\n"
|
"POT-Creation-Date: 2017-09-16 21:58+0000\n"
|
||||||
"PO-Revision-Date: 2017-09-16 17:16+0200\n"
|
"PO-Revision-Date: 2017-09-16 17:16+0200\n"
|
||||||
"Last-Translator: Olivier DOSSMANN <git@dossmann.net>\n"
|
"Last-Translator: Olivier DOSSMANN <git@dossmann.net>\n"
|
||||||
"Language-Team: \n"
|
"Language-Team: \n"
|
||||||
|
@ -16,7 +16,12 @@ Including another URLconf
|
|||||||
from django.conf.urls import url, include
|
from django.conf.urls import url, include
|
||||||
from django.contrib import admin
|
from django.contrib import admin
|
||||||
from collection import __version__ as app_version
|
from collection import __version__ as app_version
|
||||||
from games.views import GameList, GameViewSet, PlatformViewSet
|
from games.views import (
|
||||||
|
GameList,
|
||||||
|
GameViewSet,
|
||||||
|
GameTimelineViewSet,
|
||||||
|
PlatformViewSet,
|
||||||
|
)
|
||||||
from rest_framework import routers
|
from rest_framework import routers
|
||||||
from rest_framework.documentation import include_docs_urls
|
from rest_framework.documentation import include_docs_urls
|
||||||
|
|
||||||
@ -28,6 +33,10 @@ admin.site.site_header = '%s %s' % (admin.site.site_title, app_version)
|
|||||||
router = routers.DefaultRouter()
|
router = routers.DefaultRouter()
|
||||||
router.register(r'games', GameViewSet)
|
router.register(r'games', GameViewSet)
|
||||||
router.register(r'platforms', PlatformViewSet)
|
router.register(r'platforms', PlatformViewSet)
|
||||||
|
router.register(
|
||||||
|
r'game_timelines',
|
||||||
|
GameTimelineViewSet,
|
||||||
|
base_name='game_timeline')
|
||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
url(r'^$', GameList.as_view(), name='homepage'),
|
url(r'^$', GameList.as_view(), name='homepage'),
|
||||||
|
@ -7,8 +7,8 @@ msgid ""
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: 0.1\n"
|
"Project-Id-Version: 0.1\n"
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"POT-Creation-Date: 2017-09-16 15:15+0000\n"
|
"POT-Creation-Date: 2017-09-16 21:58+0000\n"
|
||||||
"PO-Revision-Date: 2017-09-16 17:17+0200\n"
|
"PO-Revision-Date: 2017-09-16 23:59+0200\n"
|
||||||
"Last-Translator: \n"
|
"Last-Translator: \n"
|
||||||
"Language-Team: \n"
|
"Language-Team: \n"
|
||||||
"Language: fr\n"
|
"Language: fr\n"
|
||||||
@ -55,7 +55,7 @@ msgstr "plateformes"
|
|||||||
|
|
||||||
#: collection/games/models.py:20
|
#: collection/games/models.py:20
|
||||||
msgid "Most used platform name."
|
msgid "Most used platform name."
|
||||||
msgstr "Nom de plateforme le plus utilisé."
|
msgstr "Nom de plateforme le plus communément utilisé."
|
||||||
|
|
||||||
#: collection/games/models.py:40
|
#: collection/games/models.py:40
|
||||||
msgid "Beaten"
|
msgid "Beaten"
|
||||||
@ -117,14 +117,38 @@ msgstr "jeu"
|
|||||||
msgid "games"
|
msgid "games"
|
||||||
msgstr "jeux"
|
msgstr "jeux"
|
||||||
|
|
||||||
#: collection/games/models.py:84
|
#: collection/games/models.py:77
|
||||||
|
msgid "Game title"
|
||||||
|
msgstr "Titre du jeu"
|
||||||
|
|
||||||
|
#: collection/games/models.py:78
|
||||||
|
msgid "Game running platform"
|
||||||
|
msgstr "Plateforme sur laquelle se lance le jeu"
|
||||||
|
|
||||||
|
#: collection/games/models.py:79
|
||||||
|
msgid "Game progression"
|
||||||
|
msgstr "Progression du jeu"
|
||||||
|
|
||||||
|
#: collection/games/models.py:90
|
||||||
msgid "Timeline"
|
msgid "Timeline"
|
||||||
msgstr "Chronologie"
|
msgstr "Chronologie"
|
||||||
|
|
||||||
#: collection/games/models.py:85
|
#: collection/games/models.py:91
|
||||||
msgid "Timelines"
|
msgid "Timelines"
|
||||||
msgstr "Chronologies"
|
msgstr "Chronologies"
|
||||||
|
|
||||||
|
#: collection/games/models.py:95
|
||||||
|
msgid "Status change date"
|
||||||
|
msgstr "Date de changement de statut"
|
||||||
|
|
||||||
|
#: collection/games/models.py:96
|
||||||
|
msgid "Which game?"
|
||||||
|
msgstr "Quel jeu ?"
|
||||||
|
|
||||||
|
#: collection/games/models.py:98
|
||||||
|
msgid "New status for this game at the given date"
|
||||||
|
msgstr "Nouveau statut pour ce jeu à la date donnée"
|
||||||
|
|
||||||
#: collection/games/templates/games/index.html:5
|
#: collection/games/templates/games/index.html:5
|
||||||
msgid "Games"
|
msgid "Games"
|
||||||
msgstr "Jeux"
|
msgstr "Jeux"
|
||||||
|
@ -0,0 +1,61 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Generated by Django 1.11.5 on 2017-09-16 21:55
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
import datetime
|
||||||
|
from django.db import migrations, models
|
||||||
|
import django.db.models.deletion
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('games', '0008_rename_console_to_platform'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterModelOptions(
|
||||||
|
name='platform',
|
||||||
|
options={'ordering': ('name',), 'verbose_name': 'plateforme', 'verbose_name_plural': 'plateformes'},
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='game',
|
||||||
|
name='collection',
|
||||||
|
field=models.ForeignKey(help_text='Game running platform', on_delete=django.db.models.deletion.CASCADE, related_name='games', to='games.Platform', verbose_name='plateforme'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='game',
|
||||||
|
name='name',
|
||||||
|
field=models.CharField(help_text='Game title', max_length=255, verbose_name='nom'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='game',
|
||||||
|
name='note',
|
||||||
|
field=models.CharField(blank=True, help_text='Courte note affichée à ceux qui vous suive.', max_length=150, null=True, verbose_name='Note de progression'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='game',
|
||||||
|
name='status',
|
||||||
|
field=models.CharField(choices=[('created', 'Nouveau'), ('beaten', 'Terminé (quête principale)'), ('completed', 'Terminé complètement'), ('excluded', 'Exclu'), ('mastered', 'Usé / Épuisé'), ('unfinished', 'Inachevé')], default='unfinished', help_text='Game progression', max_length=30, verbose_name='état'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='platform',
|
||||||
|
name='name',
|
||||||
|
field=models.CharField(help_text='Nom de plateforme le plus utilisé.', max_length=255, verbose_name='nom'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='timeline',
|
||||||
|
name='date',
|
||||||
|
field=models.DateField(default=datetime.datetime.now, help_text='Status change date', verbose_name='date'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='timeline',
|
||||||
|
name='item',
|
||||||
|
field=models.ForeignKey(help_text='Which game?', on_delete=django.db.models.deletion.CASCADE, to='games.Game', verbose_name='jeu'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='timeline',
|
||||||
|
name='status',
|
||||||
|
field=models.CharField(choices=[('created', 'Nouveau'), ('beaten', 'Terminé (quête principale)'), ('completed', 'Terminé complètement'), ('excluded', 'Exclu'), ('mastered', 'Usé / Épuisé'), ('unfinished', 'Inachevé')], default='created', help_text='New status for this game at the given date', max_length=30, verbose_name='état'),
|
||||||
|
),
|
||||||
|
]
|
@ -73,6 +73,12 @@ class Game(Item):
|
|||||||
verbose_name_plural = _('games')
|
verbose_name_plural = _('games')
|
||||||
|
|
||||||
|
|
||||||
|
# Redefine help_text (for documentation, API, etc.)
|
||||||
|
Game._meta.get_field('name').help_text = _('Game title')
|
||||||
|
Game._meta.get_field('collection').help_text = _('Game running platform')
|
||||||
|
Game._meta.get_field('status').help_text = _('Game progression')
|
||||||
|
|
||||||
|
|
||||||
class Timeline(BaseTimeline):
|
class Timeline(BaseTimeline):
|
||||||
TARGET_MODEL = 'games.Game'
|
TARGET_MODEL = 'games.Game'
|
||||||
TARGET_VERBOSE_NAME = 'game'
|
TARGET_VERBOSE_NAME = 'game'
|
||||||
@ -83,3 +89,10 @@ class Timeline(BaseTimeline):
|
|||||||
ordering = ('-date',)
|
ordering = ('-date',)
|
||||||
verbose_name = _('Timeline')
|
verbose_name = _('Timeline')
|
||||||
verbose_name_plural = _('Timelines')
|
verbose_name_plural = _('Timelines')
|
||||||
|
|
||||||
|
|
||||||
|
# Redefine help_text (for documentation, API, etc.)
|
||||||
|
Timeline._meta.get_field('date').help_text = _('Status change date')
|
||||||
|
Timeline._meta.get_field('item').help_text = _('Which game?')
|
||||||
|
Timeline._meta.get_field('status').help_text = _(
|
||||||
|
'New status for this game at the given date')
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
from games.models import Game, Platform
|
from games.models import Game, Platform, Timeline
|
||||||
from rest_framework import serializers
|
from rest_framework import serializers
|
||||||
|
|
||||||
|
|
||||||
@ -16,6 +16,16 @@ class GameSerializer(serializers.HyperlinkedModelSerializer):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class GameTimelineSerializer(serializers.HyperlinkedModelSerializer):
|
||||||
|
class Meta:
|
||||||
|
model = Timeline
|
||||||
|
fields = (
|
||||||
|
'date',
|
||||||
|
'item',
|
||||||
|
'status',
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class PlatformSerializer(serializers.ModelSerializer):
|
class PlatformSerializer(serializers.ModelSerializer):
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Platform
|
model = Platform
|
||||||
|
@ -1,8 +1,9 @@
|
|||||||
from django.contrib.auth.models import User
|
from django.contrib.auth.models import User
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
from games.models import Game, Platform
|
from games.models import Game, Platform, Timeline
|
||||||
from rest_framework import status
|
from rest_framework import status
|
||||||
from rest_framework.test import APITestCase, force_authenticate
|
from rest_framework.test import APITestCase, force_authenticate
|
||||||
|
from datetime import date
|
||||||
import json
|
import json
|
||||||
|
|
||||||
|
|
||||||
@ -112,3 +113,46 @@ class GameTest(APITestCase):
|
|||||||
response = self.client.post(url, data, format='json')
|
response = self.client.post(url, data, format='json')
|
||||||
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
|
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
|
||||||
self.assertEqual(Game.objects.count(), 0)
|
self.assertEqual(Game.objects.count(), 0)
|
||||||
|
|
||||||
|
|
||||||
|
class TimelineTest(APITestCase):
|
||||||
|
@classmethod
|
||||||
|
def setUpTestData(cls):
|
||||||
|
cls.superuser = User.objects.create_superuser(
|
||||||
|
'admin',
|
||||||
|
'admin@localhost',
|
||||||
|
'admin')
|
||||||
|
|
||||||
|
def test_create_timeline(self):
|
||||||
|
"""
|
||||||
|
Check we can create a timeline object.
|
||||||
|
"""
|
||||||
|
console = Platform.objects.create(name='BestPlatform4Ever')
|
||||||
|
game = Game.objects.create(
|
||||||
|
name='Cherubin',
|
||||||
|
collection=console,
|
||||||
|
status='created')
|
||||||
|
|
||||||
|
# By default a timeline is generated with current datetime.
|
||||||
|
# We need to change this date.
|
||||||
|
timeline = Timeline.objects.first()
|
||||||
|
timeline.date = date(2017, 9, 15)
|
||||||
|
timeline.save()
|
||||||
|
|
||||||
|
url = reverse('game_timeline-list')
|
||||||
|
data = {
|
||||||
|
'item': reverse('game-detail', kwargs={'pk': game.id}),
|
||||||
|
'date': date(2017, 9, 16),
|
||||||
|
'status': 'beaten',
|
||||||
|
}
|
||||||
|
self.client.force_authenticate(user=self.superuser)
|
||||||
|
response = self.client.post(url, data, format='json')
|
||||||
|
self.assertEqual(
|
||||||
|
response.status_code,
|
||||||
|
status.HTTP_201_CREATED,
|
||||||
|
response.content)
|
||||||
|
self.assertEqual(Timeline.objects.count(), 2)
|
||||||
|
|
||||||
|
def test_sorted_game_timeline(self):
|
||||||
|
# TODO: Check descending date order.
|
||||||
|
pass
|
||||||
|
@ -1,10 +1,14 @@
|
|||||||
from django.db.models import Q
|
from django.db.models import Q
|
||||||
from django.views.generic import ListView
|
from django.views.generic import ListView
|
||||||
from rest_framework import viewsets
|
from rest_framework import viewsets
|
||||||
from .serializers import GameSerializer, PlatformSerializer
|
from .serializers import (
|
||||||
|
GameSerializer,
|
||||||
|
GameTimelineSerializer,
|
||||||
|
PlatformSerializer
|
||||||
|
)
|
||||||
from .models import Game, Platform, Timeline
|
from .models import Game, Platform, Timeline
|
||||||
|
|
||||||
|
|
||||||
class PlatformViewSet(viewsets.ModelViewSet):
|
class PlatformViewSet(viewsets.ModelViewSet):
|
||||||
"""
|
"""
|
||||||
API endpoints that allows platforms to be edited or viewed.
|
API endpoints that allows platforms to be edited or viewed.
|
||||||
@ -22,12 +26,32 @@ class PlatformViewSet(viewsets.ModelViewSet):
|
|||||||
serializer_class = PlatformSerializer
|
serializer_class = PlatformSerializer
|
||||||
|
|
||||||
|
|
||||||
|
class GameTimelineViewSet(viewsets.ModelViewSet):
|
||||||
|
"""
|
||||||
|
API endpoints that allows to keep games life.
|
||||||
|
BE CAREFUL. You shouldn't change these entries because they're generated
|
||||||
|
by Game status change. It could be only useful to change the acquisition
|
||||||
|
date. Not more.
|
||||||
|
|
||||||
|
retrieve:
|
||||||
|
Return the given timeline entry.
|
||||||
|
|
||||||
|
list:
|
||||||
|
Return a list of timeline entries sorted by date in descending order.
|
||||||
|
|
||||||
|
create:
|
||||||
|
Create a new timeline instance.
|
||||||
|
"""
|
||||||
|
queryset = Timeline.objects.all().order_by('-date')
|
||||||
|
serializer_class = GameTimelineSerializer
|
||||||
|
|
||||||
|
|
||||||
class GameViewSet(viewsets.ModelViewSet):
|
class GameViewSet(viewsets.ModelViewSet):
|
||||||
"""
|
"""
|
||||||
API endpoints that allows games to be edited or viewed.
|
API endpoints that allows games to be edited or viewed.
|
||||||
|
|
||||||
retrieve:
|
retrieve:
|
||||||
Return the given game
|
Return the given game.
|
||||||
|
|
||||||
list:
|
list:
|
||||||
Return a list of all existing games ordered by name.
|
Return a list of all existing games ordered by name.
|
||||||
|
Loading…
Reference in New Issue
Block a user