Use min/max/precisions fields for game duration
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 'à'.
This commit is contained in:
parent
d5d239c3df
commit
1fea637d7a
9 changed files with 206 additions and 37 deletions
77
inventory/migrations/0002_duration_range.py
Normal file
77
inventory/migrations/0002_duration_range.py
Normal file
|
@ -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",
|
||||
),
|
||||
),
|
||||
]
|
|
@ -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,))
|
||||
|
||||
|
|
|
@ -13,9 +13,9 @@
|
|||
<p><i class="fa fa-fw fa-bookmark"></i> <a href="{{ game.category.get_absolute_url }}">{{ game.category }}</a></p>
|
||||
<hr/>
|
||||
<p><i class="fa fa-fw fa-users" aria-hidden="true"></i> {{ game.get_player_range }}</p>
|
||||
<p><i class="fa fa-fw fa-clock-o" aria-hidden="true"></i> {{ game.duration|default:"(Durée de jeu inconnue)" }}
|
||||
<p><i class="fa fa-fw fa-clock-o" aria-hidden="true"></i> {{ game.get_duration_range }}</p>
|
||||
<hr/>
|
||||
<p><i class="fa fa-fw fa-tags" aria-hidden="true"></i>
|
||||
<p><i class="fa fa-fw fa-tags" aria-hidden="true"></i>
|
||||
{% for tag in game.tags.all %}
|
||||
<a href="{{ tag.get_absolute_url }}">{{ tag }}</a>{% if not forloop.last %},{% endif %}
|
||||
{% empty %}
|
||||
|
@ -23,8 +23,8 @@
|
|||
{% endfor %}
|
||||
</p>
|
||||
<hr/>
|
||||
<p><i class="fa fa-fw fa-wrench" aria-hidden="true"></i> {{ game.game_designer|default:"(Game designer inconnu·e)" }}</li>
|
||||
<p><i class="fa fa-fw fa-paint-brush" aria-hidden="true"></i> {{ game.illustrator|default:"(Illustrateur·trice inconnu·e)" }}</li>
|
||||
<p><i class="fa fa-fw fa-wrench" aria-hidden="true"></i> {{ game.game_designer|default:"(Game designer inconnu·e)" }}</p>
|
||||
<p><i class="fa fa-fw fa-paint-brush" aria-hidden="true"></i> {{ game.illustrator|default:"(Illustrateur·trice inconnu·e)" }}</p>
|
||||
<p><i class="fa fa-fw fa-cogs" aria-hidden="true"></i> {{ game.editor|default:"(Éditeur inconnu)" }}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -1,22 +1,20 @@
|
|||
<a class="inventory_item game" href="{{ game.get_absolute_url }}">
|
||||
<span class="title">{{ game.title }}</span>
|
||||
<span class="details">
|
||||
<span><i class="fa fa-fw fa-users" aria-hidden="true"></i> {{ game.get_player_range }}</span>
|
||||
{% if game.duration %}
|
||||
<span><i class="fa fa-fw fa-clock-o" aria-hidden="true"></i> {{ game.duration }}</span>
|
||||
{% endif %}
|
||||
<span><i class="fa fa-fw fa-bookmark"></i> {{ game.category }}</span>
|
||||
<span><i class="fa fa-users" aria-hidden="true"></i> {{ game.get_player_range }}</span>
|
||||
<span><i class="fa fa-clock-o" aria-hidden="true"></i> {{ game.get_duration_range }}</span>
|
||||
<span><i class="fa fa-bookmark"></i> {{ game.category }}</span>
|
||||
{% for tag in game.tags.all %}
|
||||
<span><i class="fa fa-fw fa-tag" aria-hidden="true"></i> {{ tag }}</span>
|
||||
<span><i class="fa fa-tag" aria-hidden="true"></i> {{ tag }}</span>
|
||||
{% endfor %}
|
||||
{% if game.game_designer %}
|
||||
<span><i class="fa fa-fw fa-wrench" aria-hidden="true"></i> {{ game.game_designer }}</span>
|
||||
<span><i class="fa fa-wrench" aria-hidden="true"></i> {{ game.game_designer }}</span>
|
||||
{% endif %}
|
||||
{% if game.illustrator %}
|
||||
<span><i class="fa fa-fw fa-paint-brush" aria-hidden="true"></i> {{ game.illustrator }}</span>
|
||||
<span><i class="fa fa-paint-brush" aria-hidden="true"></i> {{ game.illustrator }}</span>
|
||||
{% endif %}
|
||||
{% if game.editor %}
|
||||
<span><i class="fa fa-fw fa-cogs" aria-hidden="true"></i> {{ game.editor }}</span>
|
||||
<span><i class="fa fa-cogs" aria-hidden="true"></i> {{ game.editor }}</span>
|
||||
{% endif %}
|
||||
</span>
|
||||
</a>
|
||||
|
|
|
@ -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",
|
||||
|
|
34
suggestions/migrations/0002_duration_range.py
Normal file
34
suggestions/migrations/0002_duration_range.py
Normal file
|
@ -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'),
|
||||
),
|
||||
]
|
|
@ -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,))
|
||||
|
||||
|
|
|
@ -1,26 +1,24 @@
|
|||
<a class="inventory_item suggestion" href="{{ suggestion.get_absolute_url }}">
|
||||
<span class="title">{{ suggestion.title }}</span>
|
||||
<span class="details">
|
||||
<span><i class="fa fa-fw fa-thumbs-up" aria-hidden="true"></i> {{ suggestion.upvoting_users.count }}</span>
|
||||
<span><i class="fa fa-fw fa-money" aria-hidden="true"></i> {{ suggestion.price }} €</span>
|
||||
<span><i class="fa fa-fw fa-users" aria-hidden="true"></i> {{ suggestion.get_player_range }}</span>
|
||||
{% if suggestion.duration %}
|
||||
<span><i class="fa fa-fw fa-clock-o" aria-hidden="true"></i> {{ suggestion.duration }}</span>
|
||||
{% endif %}
|
||||
<span><i class="fa fa-thumbs-up" aria-hidden="true"></i> {{ suggestion.upvoting_users.count }}</span>
|
||||
<span><i class="fa fa-money" aria-hidden="true"></i> {{ suggestion.price }} €</span>
|
||||
<span><i class="fa fa-users" aria-hidden="true"></i> {{ suggestion.get_player_range }}</span>
|
||||
<span><i class="fa fa-clock-o" aria-hidden="true"></i> {{ suggestion.get_duration_range }}</span>
|
||||
{% if suggestion.category %}
|
||||
<span><i class="fa fa-fw fa-bookmark"></i> {{ suggestion.category }}</span>
|
||||
<span><i class="fa fa-bookmark"></i> {{ suggestion.category }}</span>
|
||||
{% endif %}
|
||||
{% for tag in suggestion.tags.all %}
|
||||
<span><i class="fa fa-fw fa-tag" aria-hidden="true"></i> {{ tag }}</span>
|
||||
<span><i class="fa fa-tag" aria-hidden="true"></i> {{ tag }}</span>
|
||||
{% endfor %}
|
||||
{% if suggestion.game_designer %}
|
||||
<span><i class="fa fa-fw fa-wrench" aria-hidden="true"></i> {{ suggestion.game_designer }}</span>
|
||||
<span><i class="fa fa-wrench" aria-hidden="true"></i> {{ suggestion.game_designer }}</span>
|
||||
{% endif %}
|
||||
{% if suggestion.illustrator %}
|
||||
<span><i class="fa fa-fw fa-paint-brush" aria-hidden="true"></i> {{ suggestion.illustrator }}</span>
|
||||
<span><i class="fa fa-paint-brush" aria-hidden="true"></i> {{ suggestion.illustrator }}</span>
|
||||
{% endif %}
|
||||
{% if suggestion.editor %}
|
||||
<span><i class="fa fa-fw fa-cogs" aria-hidden="true"></i> {{ suggestion.editor }}</span>
|
||||
<span><i class="fa fa-cogs" aria-hidden="true"></i> {{ suggestion.editor }}</span>
|
||||
{% endif %}
|
||||
</span>
|
||||
</a>
|
||||
|
|
|
@ -13,10 +13,10 @@
|
|||
<p><i class="fa fa-fw fa-money" aria-hidden="true"></i> <a href="{{ suggestion.buy_link }}" rel="nofollow">{{ suggestion.price }} €</a></p>
|
||||
<hr/>
|
||||
<p><i class="fa fa-fw fa-users" aria-hidden="true"></i> {{ suggestion.get_player_range }}</p>
|
||||
<p><i class="fa fa-fw fa-clock-o" aria-hidden="true"></i> {{ suggestion.duration|default:"(Durée de jeu inconnue)" }}
|
||||
<p><i class="fa fa-fw fa-clock-o" aria-hidden="true"></i> {{ suggestion.get_duration_range }}</p>
|
||||
<hr/>
|
||||
<p><i class="fa fa-fw fa-bookmark" aria-hidden="true"></i> {% if suggestion.category %}<a href="{{ suggestion.category.get_absolute_url }}">{{ suggestion.category }}</a>{% else %}(Pas de catégorie){% endif %}</p>
|
||||
<p><i class="fa fa-fw fa-tags" aria-hidden="true"></i>
|
||||
<p><i class="fa fa-fw fa-tags" aria-hidden="true"></i>
|
||||
{% for tag in suggestion.tags.all %}
|
||||
<a href="{{ tag.get_absolute_url }}">{{ tag }}</a>{% if not forloop.last %},{% endif %}
|
||||
{% empty %}
|
||||
|
@ -24,8 +24,8 @@
|
|||
{% endfor %}
|
||||
</p>
|
||||
<hr/>
|
||||
<p><i class="fa fa-fw fa-wrench" aria-hidden="true"></i> {{ suggestion.game_designer|default:"(Game designer inconnu·e)" }}</li>
|
||||
<p><i class="fa fa-fw fa-paint-brush" aria-hidden="true"></i> {{ suggestion.illustrator|default:"(Illustrateur·trice inconnu·e)" }}</li>
|
||||
<p><i class="fa fa-fw fa-wrench" aria-hidden="true"></i> {{ suggestion.game_designer|default:"(Game designer inconnu·e)" }}</p>
|
||||
<p><i class="fa fa-fw fa-paint-brush" aria-hidden="true"></i> {{ suggestion.illustrator|default:"(Illustrateur·trice inconnu·e)" }}</p>
|
||||
<p><i class="fa fa-fw fa-cogs" aria-hidden="true"></i> {{ suggestion.editor|default:"(Éditeur inconnu)" }}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
|
Loading…
Reference in a new issue