diff --git a/.idea/charasheet.iml b/.idea/charasheet.iml
index 391aeba..b15df4a 100644
--- a/.idea/charasheet.iml
+++ b/.idea/charasheet.iml
@@ -29,6 +29,7 @@
+
diff --git a/src/character/admin.py b/src/character/admin.py
index 9d0888e..fd451f5 100644
--- a/src/character/admin.py
+++ b/src/character/admin.py
@@ -210,9 +210,3 @@ class WeaponAdmin(admin.ModelAdmin):
class HarmfulStateAdmin(admin.ModelAdmin):
list_display = ["name", "description"]
search_fields = ["name"]
-
-
-@admin.register(models.Party)
-class PartyAdmin(admin.ModelAdmin):
- list_display = ["name", "game_master"]
- search_fields = ["name"]
diff --git a/src/character/migrations/0036_delete_party.py b/src/character/migrations/0036_delete_party.py
new file mode 100644
index 0000000..984b110
--- /dev/null
+++ b/src/character/migrations/0036_delete_party.py
@@ -0,0 +1,19 @@
+# Generated by Django 4.1.2 on 2022-11-02 22:28
+
+from django.db import migrations
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ (
+ "character",
+ "0035_alter_capability_options_alter_harmfulstate_options_and_more",
+ ),
+ ]
+
+ operations = [
+ migrations.DeleteModel(
+ name="Party",
+ ),
+ ]
diff --git a/src/character/migrations/max_migration.txt b/src/character/migrations/max_migration.txt
index f9e6e2f..208836e 100644
--- a/src/character/migrations/max_migration.txt
+++ b/src/character/migrations/max_migration.txt
@@ -1 +1 @@
-0035_alter_capability_options_alter_harmfulstate_options_and_more
+0036_delete_party
diff --git a/src/character/models/__init__.py b/src/character/models/__init__.py
index db3967e..c945787 100644
--- a/src/character/models/__init__.py
+++ b/src/character/models/__init__.py
@@ -1,7 +1,6 @@
from .capabilities import Capability, Path, RacialCapability
from .character import Character, HarmfulState, Profile, Race
from .equipment import Weapon
-from .party import Party
__all__ = [
"Capability",
@@ -12,5 +11,4 @@ __all__ = [
"Profile",
"Race",
"Weapon",
- "Party",
]
diff --git a/src/charasheet/settings.py b/src/charasheet/settings.py
index 3490f5a..e2a67c7 100644
--- a/src/charasheet/settings.py
+++ b/src/charasheet/settings.py
@@ -75,6 +75,7 @@ CUSTOM_APPS = [
"whitenoise.runserver_nostatic", # should be first
"common",
"character",
+ "party",
]
INSTALLED_APPS = CUSTOM_APPS + DJANGO_APPS + EXTERNAL_APPS
diff --git a/src/charasheet/urls.py b/src/charasheet/urls.py
index 5c5f2a1..101fa47 100644
--- a/src/charasheet/urls.py
+++ b/src/charasheet/urls.py
@@ -48,6 +48,7 @@ urlpatterns = [
path("admin/", admin.site.urls),
path("", hello_world, name="hello_world"),
path("character/", include("character.urls", namespace="character")),
+ path("party/", include("party.urls", namespace="party")),
]
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
diff --git a/src/common/templates/common/navbar.html b/src/common/templates/common/navbar.html
index 48b33e7..0142e7f 100644
--- a/src/common/templates/common/navbar.html
+++ b/src/common/templates/common/navbar.html
@@ -7,16 +7,21 @@
diff --git a/src/party/__init__.py b/src/party/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/src/party/admin.py b/src/party/admin.py
new file mode 100644
index 0000000..8810460
--- /dev/null
+++ b/src/party/admin.py
@@ -0,0 +1,9 @@
+from django.contrib import admin
+
+from party import models
+
+
+@admin.register(models.Party)
+class PartyAdmin(admin.ModelAdmin):
+ list_display = ["name", "game_master"]
+ search_fields = ["name"]
diff --git a/src/party/apps.py b/src/party/apps.py
new file mode 100644
index 0000000..347db05
--- /dev/null
+++ b/src/party/apps.py
@@ -0,0 +1,6 @@
+from django.apps import AppConfig
+
+
+class PartyConfig(AppConfig):
+ default_auto_field = "django.db.models.BigAutoField"
+ name = "party"
diff --git a/src/party/forms.py b/src/party/forms.py
new file mode 100644
index 0000000..9970d96
--- /dev/null
+++ b/src/party/forms.py
@@ -0,0 +1,9 @@
+from django import forms
+
+from party.models import Party
+
+
+class PartyForm(forms.ModelForm):
+ class Meta:
+ model = Party
+ fields = ["name", "characters"]
diff --git a/src/party/migrations/0001_initial.py b/src/party/migrations/0001_initial.py
new file mode 100644
index 0000000..718b234
--- /dev/null
+++ b/src/party/migrations/0001_initial.py
@@ -0,0 +1,74 @@
+# Generated by Django 4.1.2 on 2022-11-02 22:28
+
+import django.db.models.deletion
+import django_extensions.db.fields
+from django.conf import settings
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ initial = True
+
+ dependencies = [
+ ("character", "0036_delete_party"),
+ migrations.swappable_dependency(settings.AUTH_USER_MODEL),
+ ]
+
+ operations = [
+ migrations.CreateModel(
+ name="Party",
+ fields=[
+ (
+ "id",
+ models.BigAutoField(
+ auto_created=True,
+ primary_key=True,
+ serialize=False,
+ verbose_name="ID",
+ ),
+ ),
+ (
+ "name",
+ models.CharField(max_length=100, unique=True, verbose_name="nom"),
+ ),
+ (
+ "created",
+ django_extensions.db.fields.CreationDateTimeField(
+ auto_now_add=True, verbose_name="created"
+ ),
+ ),
+ (
+ "modified",
+ django_extensions.db.fields.ModificationDateTimeField(
+ auto_now=True, verbose_name="modified"
+ ),
+ ),
+ (
+ "characters",
+ models.ManyToManyField(
+ blank=True,
+ related_name="parties",
+ to="character.character",
+ verbose_name="personnages",
+ ),
+ ),
+ (
+ "game_master",
+ models.ForeignKey(
+ on_delete=django.db.models.deletion.PROTECT,
+ related_name="parties",
+ to=settings.AUTH_USER_MODEL,
+ verbose_name="meneur de jeu",
+ ),
+ ),
+ ],
+ options={
+ "verbose_name": "Groupe",
+ "verbose_name_plural": "Groupes",
+ "ordering": ["name"],
+ "get_latest_by": "modified",
+ "abstract": False,
+ },
+ ),
+ ]
diff --git a/src/party/migrations/__init__.py b/src/party/migrations/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/src/party/migrations/max_migration.txt b/src/party/migrations/max_migration.txt
new file mode 100644
index 0000000..cbab66d
--- /dev/null
+++ b/src/party/migrations/max_migration.txt
@@ -0,0 +1 @@
+0001_initial
diff --git a/src/character/models/party.py b/src/party/models.py
similarity index 57%
rename from src/character/models/party.py
rename to src/party/models.py
index 2ce6651..7e5d5e9 100644
--- a/src/character/models/party.py
+++ b/src/party/models.py
@@ -1,7 +1,20 @@
from django.db import models
from django_extensions.db.models import TimeStampedModel
-from common.models import UniquelyNamedModel
+from character.models import Character
+from common.models import UniquelyNamedModel, UniquelyNamedModelManager
+
+
+class PartyQuerySet(models.QuerySet):
+ def managed_by(self, user):
+ return self.filter(game_master=user)
+
+ def played_by(self, user):
+ return self.filter(characters__in=Character.objects.filter(player=user))
+
+
+class PartyManager(UniquelyNamedModelManager):
+ pass
class Party(UniquelyNamedModel, TimeStampedModel, models.Model):
@@ -18,6 +31,8 @@ class Party(UniquelyNamedModel, TimeStampedModel, models.Model):
verbose_name="personnages",
)
+ objects = PartyManager.from_queryset(PartyQuerySet)()
+
class Meta(UniquelyNamedModel.Meta, TimeStampedModel.Meta):
verbose_name = "Groupe"
verbose_name_plural = "Groupes"
diff --git a/src/party/templates/party/parties_list.html b/src/party/templates/party/parties_list.html
new file mode 100644
index 0000000..d19471b
--- /dev/null
+++ b/src/party/templates/party/parties_list.html
@@ -0,0 +1,27 @@
+{% extends "common/base.html" %}
+{% load character_extras %}
+
+{% block title %}Groupes{% endblock %}
+
+{% block content %}
+
Groupes
+
+ Nouveau groupe
+
+
Groupes que vous gérez
+
+ {% for party in managed_parties %}
+ {% include "party/snippets/party_card.html" %}
+ {% empty %}
+
Aucun
+ {% endfor %}
+
+
Groupes dont vous êtes membre
+
+ {% for party in played_parties %}
+ {% include "party/snippets/party_card.html" %}
+ {% empty %}
+
Aucun
+ {% endfor %}
+
+{% endblock %}
diff --git a/src/party/templates/party/party_create.html b/src/party/templates/party/party_create.html
new file mode 100644
index 0000000..280ff4a
--- /dev/null
+++ b/src/party/templates/party/party_create.html
@@ -0,0 +1,14 @@
+{% extends "common/base.html" %}
+{% load django_bootstrap5 %}
+{% load character_extras %}
+
+{% block title %}Créer groupe{% endblock %}
+
+{% block content %}
+
Créer un groupe
+
+{% endblock %}
diff --git a/src/party/templates/party/snippets/party_card.html b/src/party/templates/party/snippets/party_card.html
new file mode 100644
index 0000000..fd5da46
--- /dev/null
+++ b/src/party/templates/party/snippets/party_card.html
@@ -0,0 +1,12 @@
+
diff --git a/src/party/tests/__init__.py b/src/party/tests/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/src/party/tests/test_models.py b/src/party/tests/test_models.py
new file mode 100644
index 0000000..50b0e02
--- /dev/null
+++ b/src/party/tests/test_models.py
@@ -0,0 +1,30 @@
+from model_bakery import baker
+
+from character.models import Character
+from common.models import User
+from party.models import Party
+
+
+def test_party_managed_by(db):
+ game_master = User.objects.create_user("game_master")
+ other_user = User.objects.create_user("other")
+ expected = Party.objects.create(name="some name", game_master=game_master)
+ Party.objects.create(name="some other name", game_master=other_user)
+ related_to = Party.objects.managed_by(game_master)
+ assert len(related_to) == 1
+ assert related_to[0] == expected
+
+
+def test_party_played_by(db):
+ player = User.objects.create_user("game_master")
+ player_character = baker.make(Character, player=player)
+ other_user = User.objects.create_user("other")
+ other_character = baker.make(Character, player=other_user)
+
+ expected = Party.objects.create(name="some name", game_master=other_user)
+ expected.characters.add(player_character)
+ other_party = Party.objects.create(name="some other name", game_master=other_user)
+ other_party.characters.add(other_character)
+ related_to = Party.objects.played_by(player)
+ assert len(related_to) == 1
+ assert related_to[0] == expected
diff --git a/src/party/urls.py b/src/party/urls.py
new file mode 100644
index 0000000..310cff0
--- /dev/null
+++ b/src/party/urls.py
@@ -0,0 +1,9 @@
+from django.urls import path
+
+from party import views
+
+app_name = "party"
+urlpatterns = [
+ path("", views.parties_list, name="list"),
+ path("create/", views.party_create, name="create"),
+]
diff --git a/src/party/views.py b/src/party/views.py
new file mode 100644
index 0000000..591646e
--- /dev/null
+++ b/src/party/views.py
@@ -0,0 +1,30 @@
+from django.contrib.auth.decorators import login_required
+from django.shortcuts import redirect, render
+
+from party.forms import PartyForm
+from party.models import Party
+
+
+@login_required
+def parties_list(request):
+ context = {
+ "managed_parties": Party.objects.managed_by(request.user),
+ "played_parties": Party.objects.played_by(request.user),
+ }
+ return render(request, "party/parties_list.html", context)
+
+
+@login_required
+def party_create(request):
+ if request.method == "GET":
+ form = PartyForm()
+ else:
+ form = PartyForm(request.POST)
+ if form.is_valid():
+ party = form.save(commit=False)
+ party.game_master = request.user
+ party.save()
+ form.save_m2m()
+ return redirect("party:list")
+ context = {"form": form}
+ return render(request, "party/party_create.html", context)