From 1fea637d7a0e3c19912500eb9cac9ea3cd93790d Mon Sep 17 00:00:00 2001 From: Guillaume Bertholon Date: Fri, 29 Jan 2021 12:07:46 +0100 Subject: [PATCH] Use min/max/precisions fields for game duration MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit There is an data migration for Game items but not for Suggestion items. It assumes that all duration fields are filled with a single integer or a range separated by '-' or 'à'. --- inventory/migrations/0002_duration_range.py | 77 +++++++++++++++++++ inventory/models.py | 39 ++++++++-- inventory/templates/inventory/game.html | 8 +- .../inventory/partials/game_item.html | 16 ++-- suggestions/forms.py | 4 +- suggestions/migrations/0002_duration_range.py | 34 ++++++++ suggestions/models.py | 37 ++++++++- .../suggestions/partials/suggestion_item.html | 20 +++-- .../templates/suggestions/suggestion.html | 8 +- 9 files changed, 206 insertions(+), 37 deletions(-) create mode 100644 inventory/migrations/0002_duration_range.py create mode 100644 suggestions/migrations/0002_duration_range.py diff --git a/inventory/migrations/0002_duration_range.py b/inventory/migrations/0002_duration_range.py new file mode 100644 index 0000000..9cafeb8 --- /dev/null +++ b/inventory/migrations/0002_duration_range.py @@ -0,0 +1,77 @@ +# Generated by Django 3.1.5 on 2021-01-28 23:15 + +from django.db import migrations, models + + +def extract_duration_range(apps, schema_editor): + Game = apps.get_model("inventory", "Game") + for game in Game.objects.all(): + sep = None + if game.duration.count("-") == 1: + sep = "-" + elif game.duration.count("à") == 1: + sep = "à" + + if sep: + duration_split = game.duration.split(sep) + game.duration_min = int(duration_split[0]) + game.duration_max = int(duration_split[1]) + else: + single_duration = int(game.duration) + game.duration_min = single_duration + game.duration_max = single_duration + + game.duration = "" + game.save() + + +class Migration(migrations.Migration): + + dependencies = [ + ("inventory", "0001_initial"), + ] + + operations = [ + migrations.AddField( + model_name="game", + name="duration_max", + field=models.PositiveSmallIntegerField( + null=True, + ), + ), + migrations.AddField( + model_name="game", + name="duration_min", + field=models.PositiveSmallIntegerField( + null=True, + ), + ), + migrations.AlterField( + model_name="game", + name="duration", + field=models.CharField( + blank=True, + help_text="Affichage personnalisé pour la durée de la partie", + max_length=256, + verbose_name="durée de partie", + ), + ), + migrations.RunPython(extract_duration_range), + migrations.AlterField( + model_name="game", + name="duration_max", + field=models.PositiveSmallIntegerField( + blank=True, + help_text="En minutes, telle qu'indiquée par l'éditeur, identique à la durée minimale si laissée vide", + verbose_name="durée de partie maximale", + ), + ), + migrations.AlterField( + model_name="game", + name="duration_min", + field=models.PositiveSmallIntegerField( + help_text="En minutes, telle qu'indiquée par l'éditeur", + verbose_name="durée de partie minimale", + ), + ), + ] diff --git a/inventory/models.py b/inventory/models.py index 221822d..853e9a1 100644 --- a/inventory/models.py +++ b/inventory/models.py @@ -51,8 +51,21 @@ class Game(models.Model): help_text="Affichage personnalisé pour le nombre de joueur·se·s", verbose_name="nombre de joueur·se·s", ) + + duration_min = models.PositiveSmallIntegerField( + verbose_name="durée de partie minimale", + help_text="En minutes, telle qu'indiquée par l'éditeur", + ) + duration_max = models.PositiveSmallIntegerField( + verbose_name="durée de partie maximale", + help_text="En minutes, telle qu'indiquée par l'éditeur, identique à la durée minimale si laissée vide", + blank=True, + ) duration = models.CharField( - max_length=256, blank=True, verbose_name="durée de partie" + max_length=256, + blank=True, + help_text="Affichage personnalisé pour la durée de la partie", + verbose_name="durée de partie", ) game_designer = models.CharField( @@ -88,16 +101,22 @@ class Game(models.Model): return self.title def clean(self): - if ( - self.nb_player_min is not None - and self.nb_player_max is not None - and self.nb_player_min > self.nb_player_max - ): + if not self.nb_player_min or not self.nb_player_max or not self.duration_min: + return + if self.nb_player_min > self.nb_player_max: raise ValidationError( { "nb_player_max": "Le nombre de joueur·se·s maximum doit être supérieur au nombre de joueur·se·s minimum" } ) + if self.duration_max is None: + self.duration_max = self.duration_min + if self.duration_min > self.duration_max: + raise ValidationError( + { + "duration_max": "La durée maximale doit être supérieure à la durée minimale" + } + ) def get_player_range(self): if self.player_range: @@ -107,6 +126,14 @@ class Game(models.Model): else: return "{} joueur·se·s".format(self.nb_player_min) + def get_duration_range(self): + if self.duration: + return self.duration + elif self.duration_min != self.duration_max: + return "{} à {} min".format(self.duration_min, self.duration_max) + else: + return "{} min".format(self.duration_min) + def get_absolute_url(self): return reverse("inventory:game", args=(self.slug,)) diff --git a/inventory/templates/inventory/game.html b/inventory/templates/inventory/game.html index 3a49407..86f89f3 100644 --- a/inventory/templates/inventory/game.html +++ b/inventory/templates/inventory/game.html @@ -13,9 +13,9 @@

{{ game.category }}


{{ game.get_player_range }}

-

{{ game.duration|default:"(Durée de jeu inconnue)" }} +

{{ game.get_duration_range }}


-

+

{% for tag in game.tags.all %} {{ tag }}{% if not forloop.last %},{% endif %} {% empty %} @@ -23,8 +23,8 @@ {% endfor %}


-

{{ game.game_designer|default:"(Game designer inconnu·e)" }} -

{{ game.illustrator|default:"(Illustrateur·trice inconnu·e)" }} +

{{ game.game_designer|default:"(Game designer inconnu·e)" }}

+

{{ game.illustrator|default:"(Illustrateur·trice inconnu·e)" }}

{{ game.editor|default:"(Éditeur inconnu)" }}

diff --git a/inventory/templates/inventory/partials/game_item.html b/inventory/templates/inventory/partials/game_item.html index 9340d7e..05d908f 100644 --- a/inventory/templates/inventory/partials/game_item.html +++ b/inventory/templates/inventory/partials/game_item.html @@ -1,22 +1,20 @@ {{ game.title }} - {{ game.get_player_range }} - {% if game.duration %} - {{ game.duration }} - {% endif %} - {{ game.category }} + {{ game.get_player_range }} + {{ game.get_duration_range }} + {{ game.category }} {% for tag in game.tags.all %} - {{ tag }} + {{ tag }} {% endfor %} {% if game.game_designer %} - {{ game.game_designer }} + {{ game.game_designer }} {% endif %} {% if game.illustrator %} - {{ game.illustrator }} + {{ game.illustrator }} {% endif %} {% if game.editor %} - {{ game.editor }} + {{ game.editor }} {% endif %} diff --git a/suggestions/forms.py b/suggestions/forms.py index 31b48b2..22704ed 100644 --- a/suggestions/forms.py +++ b/suggestions/forms.py @@ -15,7 +15,9 @@ class SuggestionForm(forms.ModelForm): "nb_player_min", "nb_player_max", "player_range_precisions", - "duration", + "duration_min", + "duration_max", + "duration_precisions", "category", "tags", "game_designer", diff --git a/suggestions/migrations/0002_duration_range.py b/suggestions/migrations/0002_duration_range.py new file mode 100644 index 0000000..c4f7a01 --- /dev/null +++ b/suggestions/migrations/0002_duration_range.py @@ -0,0 +1,34 @@ +# Generated by Django 3.1.5 on 2021-01-29 00:17 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('suggestions', '0001_initial'), + ] + + operations = [ + migrations.RemoveField( + model_name='suggestion', + name='duration', + ), + migrations.AddField( + model_name='suggestion', + name='duration_max', + field=models.PositiveSmallIntegerField(blank=True, default=60, help_text="En minutes, telle qu'indiquée par l'éditeur, identique à la durée minimale si laissée vide", verbose_name='durée de partie maximale'), + preserve_default=False, + ), + migrations.AddField( + model_name='suggestion', + name='duration_min', + field=models.PositiveSmallIntegerField(default=60, help_text="En minutes, telle qu'indiquée par l'éditeur", verbose_name='durée de partie minimale'), + preserve_default=False, + ), + migrations.AddField( + model_name='suggestion', + name='duration_precisions', + field=models.CharField(blank=True, help_text='Pour indiquer des informations complémentaires sur la durée de la partie (ex. évolution en fonction du nombre de joueur·se·s)', max_length=256, verbose_name='précisions sur la durée de partie'), + ), + ] diff --git a/suggestions/models.py b/suggestions/models.py index 9769778..dff5251 100644 --- a/suggestions/models.py +++ b/suggestions/models.py @@ -34,7 +34,21 @@ class Suggestion(models.Model): help_text="Pour indiquer une éventuelle contrainte (ex. parité) ou information sur le nombre de joueur·se·s", ) - duration = models.CharField(max_length=256, verbose_name="durée de partie") + duration_min = models.PositiveSmallIntegerField( + verbose_name="durée de partie minimale", + help_text="En minutes, telle qu'indiquée par l'éditeur", + ) + duration_max = models.PositiveSmallIntegerField( + verbose_name="durée de partie maximale", + help_text="En minutes, telle qu'indiquée par l'éditeur, identique à la durée minimale si laissée vide", + blank=True, + ) + duration_precisions = models.CharField( + max_length=256, + blank=True, + verbose_name="précisions sur la durée de partie", + help_text="Pour indiquer des informations complémentaires sur la durée de la partie (ex. évolution en fonction du nombre de joueur·se·s)", + ) game_designer = models.CharField( max_length=256, blank=True, verbose_name="game designer" @@ -89,7 +103,7 @@ class Suggestion(models.Model): return self.title def clean(self): - if not self.nb_player_min or not self.nb_player_max: + if not self.nb_player_min or not self.nb_player_max or not self.duration_min: return if self.nb_player_min > self.nb_player_max: raise ValidationError( @@ -97,6 +111,14 @@ class Suggestion(models.Model): "nb_player_max": "Le nombre de joueur·se·s maximum doit être supérieur au nombre de joueur·se·s minimum" } ) + if self.duration_max is None: + self.duration_max = self.duration_min + if self.duration_min > self.duration_max: + raise ValidationError( + { + "duration_max": "La durée maximale doit être supérieure à la durée minimale" + } + ) def get_player_range(self): precisions = "" @@ -109,6 +131,17 @@ class Suggestion(models.Model): else: return "{} joueur·se·s{}".format(self.nb_player_min, precisions) + def get_duration_range(self): + precisions = "" + if self.duration_precisions: + precisions = " ({})".format(self.duration_precisions) + elif self.duration_min != self.duration_max: + return "{} à {} min{}".format( + self.duration_min, self.duration_max, precisions + ) + else: + return "{} min{}".format(self.duration_min, precisions) + def get_absolute_url(self): return reverse("suggestions:suggestion", args=(self.slug,)) diff --git a/suggestions/templates/suggestions/partials/suggestion_item.html b/suggestions/templates/suggestions/partials/suggestion_item.html index 8bd6b3f..d72e833 100644 --- a/suggestions/templates/suggestions/partials/suggestion_item.html +++ b/suggestions/templates/suggestions/partials/suggestion_item.html @@ -1,26 +1,24 @@ {{ suggestion.title }} - {{ suggestion.upvoting_users.count }} - {{ suggestion.price }} € - {{ suggestion.get_player_range }} - {% if suggestion.duration %} - {{ suggestion.duration }} - {% endif %} + {{ suggestion.upvoting_users.count }} + {{ suggestion.price }} € + {{ suggestion.get_player_range }} + {{ suggestion.get_duration_range }} {% if suggestion.category %} - {{ suggestion.category }} + {{ suggestion.category }} {% endif %} {% for tag in suggestion.tags.all %} - {{ tag }} + {{ tag }} {% endfor %} {% if suggestion.game_designer %} - {{ suggestion.game_designer }} + {{ suggestion.game_designer }} {% endif %} {% if suggestion.illustrator %} - {{ suggestion.illustrator }} + {{ suggestion.illustrator }} {% endif %} {% if suggestion.editor %} - {{ suggestion.editor }} + {{ suggestion.editor }} {% endif %} diff --git a/suggestions/templates/suggestions/suggestion.html b/suggestions/templates/suggestions/suggestion.html index a2f2ae8..f1c0e27 100644 --- a/suggestions/templates/suggestions/suggestion.html +++ b/suggestions/templates/suggestions/suggestion.html @@ -13,10 +13,10 @@

{{ suggestion.price }} €


{{ suggestion.get_player_range }}

-

{{ suggestion.duration|default:"(Durée de jeu inconnue)" }} +

{{ suggestion.get_duration_range }}


{% if suggestion.category %}{{ suggestion.category }}{% else %}(Pas de catégorie){% endif %}

-

+

{% for tag in suggestion.tags.all %} {{ tag }}{% if not forloop.last %},{% endif %} {% empty %} @@ -24,8 +24,8 @@ {% endfor %}


-

{{ suggestion.game_designer|default:"(Game designer inconnu·e)" }} -

{{ suggestion.illustrator|default:"(Illustrateur·trice inconnu·e)" }} +

{{ suggestion.game_designer|default:"(Game designer inconnu·e)" }}

+

{{ suggestion.illustrator|default:"(Illustrateur·trice inconnu·e)" }}

{{ suggestion.editor|default:"(Éditeur inconnu)" }}