forked from DGNum/gestiojeux
style(pre-commit): Add hook
Python: - black - isort (black profile) - ruff Nix: - statix - nixfmt-rfc-style - deadnix
This commit is contained in:
parent
fe0dc46988
commit
c01ed7cb47
53 changed files with 775 additions and 267 deletions
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -68,3 +68,5 @@ public/
|
||||||
|
|
||||||
# Vim recover files
|
# Vim recover files
|
||||||
*~
|
*~
|
||||||
|
|
||||||
|
.pre-commit-config.yaml
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
from django.forms import ModelForm, ValidationError
|
|
||||||
from django.contrib.auth.models import User
|
from django.contrib.auth.models import User
|
||||||
|
from django.forms import ModelForm, ValidationError
|
||||||
|
|
||||||
|
|
||||||
class AccountSettingsForm(ModelForm):
|
class AccountSettingsForm(ModelForm):
|
||||||
|
|
|
@ -8,27 +8,94 @@ class Migration(migrations.Migration):
|
||||||
initial = True
|
initial = True
|
||||||
|
|
||||||
dependencies = [
|
dependencies = [
|
||||||
('auth', '0012_alter_user_first_name_max_length'),
|
("auth", "0012_alter_user_first_name_max_length"),
|
||||||
]
|
]
|
||||||
|
|
||||||
operations = [
|
operations = [
|
||||||
migrations.CreateModel(
|
migrations.CreateModel(
|
||||||
name='User',
|
name="User",
|
||||||
fields=[
|
fields=[
|
||||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
(
|
||||||
('password', models.CharField(max_length=128, verbose_name='password')),
|
"id",
|
||||||
('last_login', models.DateTimeField(blank=True, null=True, verbose_name='last login')),
|
models.AutoField(
|
||||||
('is_superuser', models.BooleanField(default=False, help_text='Designates that this user has all permissions without explicitly assigning them.', verbose_name='superuser status')),
|
auto_created=True,
|
||||||
('email', models.EmailField(max_length=254, unique=True, verbose_name='adresse email')),
|
primary_key=True,
|
||||||
('public_name', models.CharField(help_text='Ce nom est utilisé pour toutes les interactions publiques sur GestioJeux. Il doit être unique.', max_length=150, unique=True, verbose_name='nom ou pseudo')),
|
serialize=False,
|
||||||
('is_staff', models.BooleanField(default=False, help_text="Précise si l’utilisateur peut se connecter à ce site d'administration.", verbose_name='statut équipe')),
|
verbose_name="ID",
|
||||||
('is_active', models.BooleanField(default=True, help_text='Précise si l’utilisateur doit être considéré comme actif. Décochez ceci plutôt que de supprimer le compte.', verbose_name='actif')),
|
),
|
||||||
('groups', models.ManyToManyField(blank=True, help_text='The groups this user belongs to. A user will get all permissions granted to each of their groups.', related_name='user_set', related_query_name='user', to='auth.Group', verbose_name='groups')),
|
),
|
||||||
('user_permissions', models.ManyToManyField(blank=True, help_text='Specific permissions for this user.', related_name='user_set', related_query_name='user', to='auth.Permission', verbose_name='user permissions')),
|
("password", models.CharField(max_length=128, verbose_name="password")),
|
||||||
|
(
|
||||||
|
"last_login",
|
||||||
|
models.DateTimeField(
|
||||||
|
blank=True, null=True, verbose_name="last login"
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"is_superuser",
|
||||||
|
models.BooleanField(
|
||||||
|
default=False,
|
||||||
|
help_text="Designates that this user has all permissions without explicitly assigning them.",
|
||||||
|
verbose_name="superuser status",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"email",
|
||||||
|
models.EmailField(
|
||||||
|
max_length=254, unique=True, verbose_name="adresse email"
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"public_name",
|
||||||
|
models.CharField(
|
||||||
|
help_text="Ce nom est utilisé pour toutes les interactions publiques sur GestioJeux. Il doit être unique.",
|
||||||
|
max_length=150,
|
||||||
|
unique=True,
|
||||||
|
verbose_name="nom ou pseudo",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"is_staff",
|
||||||
|
models.BooleanField(
|
||||||
|
default=False,
|
||||||
|
help_text="Précise si l’utilisateur peut se connecter à ce site d'administration.",
|
||||||
|
verbose_name="statut équipe",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"is_active",
|
||||||
|
models.BooleanField(
|
||||||
|
default=True,
|
||||||
|
help_text="Précise si l’utilisateur doit être considéré comme actif. Décochez ceci plutôt que de supprimer le compte.",
|
||||||
|
verbose_name="actif",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"groups",
|
||||||
|
models.ManyToManyField(
|
||||||
|
blank=True,
|
||||||
|
help_text="The groups this user belongs to. A user will get all permissions granted to each of their groups.",
|
||||||
|
related_name="user_set",
|
||||||
|
related_query_name="user",
|
||||||
|
to="auth.Group",
|
||||||
|
verbose_name="groups",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"user_permissions",
|
||||||
|
models.ManyToManyField(
|
||||||
|
blank=True,
|
||||||
|
help_text="Specific permissions for this user.",
|
||||||
|
related_name="user_set",
|
||||||
|
related_query_name="user",
|
||||||
|
to="auth.Permission",
|
||||||
|
verbose_name="user permissions",
|
||||||
|
),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
options={
|
options={
|
||||||
'verbose_name': 'utilisateur·ice',
|
"verbose_name": "utilisateur·ice",
|
||||||
'verbose_name_plural': 'utilisateur·ice·s',
|
"verbose_name_plural": "utilisateur·ice·s",
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
|
|
|
@ -6,11 +6,11 @@ from django.db import migrations
|
||||||
class Migration(migrations.Migration):
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
dependencies = [
|
dependencies = [
|
||||||
('accounts', '0001_initial'),
|
("accounts", "0001_initial"),
|
||||||
]
|
]
|
||||||
|
|
||||||
operations = [
|
operations = [
|
||||||
migrations.DeleteModel(
|
migrations.DeleteModel(
|
||||||
name='User',
|
name="User",
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
from django.urls import include, path
|
from django.urls import include, path
|
||||||
from .views import PasswordChangeView, AccountSettingsView
|
|
||||||
|
from .views import AccountSettingsView, PasswordChangeView
|
||||||
|
|
||||||
app_name = "accounts"
|
app_name = "accounts"
|
||||||
|
|
||||||
|
|
|
@ -1,15 +1,10 @@
|
||||||
from django.views.generic import TemplateView, RedirectView
|
from django.contrib import messages
|
||||||
from django.views.generic.edit import UpdateView
|
|
||||||
from django.shortcuts import redirect
|
|
||||||
from django.urls import reverse
|
|
||||||
from django.dispatch import receiver
|
|
||||||
from django.contrib.auth import logout as auth_logout
|
|
||||||
from django.contrib.auth import user_logged_in, user_logged_out, user_login_failed
|
from django.contrib.auth import user_logged_in, user_logged_out, user_login_failed
|
||||||
from django.contrib.auth.mixins import LoginRequiredMixin
|
from django.contrib.auth.mixins import LoginRequiredMixin
|
||||||
from django.contrib.auth.views import PasswordChangeView
|
from django.contrib.auth.views import PasswordChangeView
|
||||||
from django.contrib import messages
|
from django.dispatch import receiver
|
||||||
|
from django.urls import reverse
|
||||||
from urllib.parse import quote as urlquote
|
from django.views.generic.edit import UpdateView
|
||||||
|
|
||||||
from .forms import AccountSettingsForm
|
from .forms import AccountSettingsForm
|
||||||
|
|
||||||
|
|
|
@ -2,4 +2,4 @@ from django.apps import AppConfig
|
||||||
|
|
||||||
|
|
||||||
class CommentsConfig(AppConfig):
|
class CommentsConfig(AppConfig):
|
||||||
name = 'comments'
|
name = "comments"
|
||||||
|
|
|
@ -1,3 +1 @@
|
||||||
from django.test import TestCase
|
|
||||||
|
|
||||||
# Create your tests here.
|
# Create your tests here.
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
from django.views.generic import TemplateView, RedirectView
|
|
||||||
from django.views.generic.detail import SingleObjectMixin
|
|
||||||
from django.contrib.auth.mixins import LoginRequiredMixin
|
|
||||||
from django.contrib import messages
|
from django.contrib import messages
|
||||||
from django.http import Http404
|
from django.contrib.auth.mixins import LoginRequiredMixin
|
||||||
from django.core.exceptions import PermissionDenied
|
from django.core.exceptions import PermissionDenied
|
||||||
from django.shortcuts import redirect, get_object_or_404
|
from django.http import Http404
|
||||||
|
from django.shortcuts import get_object_or_404, redirect
|
||||||
|
from django.views.generic import RedirectView, TemplateView
|
||||||
|
from django.views.generic.detail import SingleObjectMixin
|
||||||
|
|
||||||
|
|
||||||
class AddCommentView(LoginRequiredMixin, SingleObjectMixin, RedirectView):
|
class AddCommentView(LoginRequiredMixin, SingleObjectMixin, RedirectView):
|
||||||
|
|
30
default.nix
30
default.nix
|
@ -6,6 +6,31 @@
|
||||||
let
|
let
|
||||||
nix-pkgs = import sources.nix-pkgs { inherit pkgs; };
|
nix-pkgs = import sources.nix-pkgs { inherit pkgs; };
|
||||||
|
|
||||||
|
check = (import sources.git-hooks).run {
|
||||||
|
src = ./.;
|
||||||
|
|
||||||
|
hooks = {
|
||||||
|
# Python hooks
|
||||||
|
ruff.enable = true;
|
||||||
|
black.enable = true;
|
||||||
|
isort.enable = true;
|
||||||
|
|
||||||
|
# Nix Hooks
|
||||||
|
statix.enable = true;
|
||||||
|
deadnix.enable = true;
|
||||||
|
rfc101 = {
|
||||||
|
enable = true;
|
||||||
|
|
||||||
|
name = "RFC-101 formatting";
|
||||||
|
entry = "${pkgs.lib.getExe pkgs.nixfmt-rfc-style}";
|
||||||
|
files = "\\.nix$";
|
||||||
|
};
|
||||||
|
|
||||||
|
# Misc Hooks
|
||||||
|
commitizen.enable = true;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
python3 = pkgs.python3.override {
|
python3 = pkgs.python3.override {
|
||||||
packageOverrides = final: _: {
|
packageOverrides = final: _: {
|
||||||
inherit (nix-pkgs)
|
inherit (nix-pkgs)
|
||||||
|
@ -23,7 +48,7 @@ in
|
||||||
devShell = pkgs.mkShell {
|
devShell = pkgs.mkShell {
|
||||||
name = "gestiojeux.dev";
|
name = "gestiojeux.dev";
|
||||||
|
|
||||||
packages = [
|
packages = check.enabledPackages ++ [
|
||||||
(python3.withPackages (ps: [
|
(python3.withPackages (ps: [
|
||||||
ps.django
|
ps.django
|
||||||
ps.django-types
|
ps.django-types
|
||||||
|
@ -53,5 +78,8 @@ in
|
||||||
|
|
||||||
GESTIOJEUX_DEBUG = builtins.toJSON true;
|
GESTIOJEUX_DEBUG = builtins.toJSON true;
|
||||||
};
|
};
|
||||||
|
shellHook = ''
|
||||||
|
${check.shellHook}
|
||||||
|
'';
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,6 +11,6 @@ import os
|
||||||
|
|
||||||
from django.core.asgi import get_asgi_application
|
from django.core.asgi import get_asgi_application
|
||||||
|
|
||||||
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'gestiojeux.settings')
|
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "gestiojeux.settings")
|
||||||
|
|
||||||
application = get_asgi_application()
|
application = get_asgi_application()
|
||||||
|
|
|
@ -13,10 +13,11 @@ Including another URLconf
|
||||||
1. Import the include() function: from django.urls import include, path
|
1. Import the include() function: from django.urls import include, path
|
||||||
2. Add a URL to urlpatterns: path('blog/', include('blog.urls'))
|
2. Add a URL to urlpatterns: path('blog/', include('blog.urls'))
|
||||||
"""
|
"""
|
||||||
from django.contrib import admin
|
|
||||||
from django.urls import include, path
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.conf.urls.static import static
|
from django.conf.urls.static import static
|
||||||
|
from django.contrib import admin
|
||||||
|
from django.urls import include, path
|
||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
path("admin/", admin.site.urls),
|
path("admin/", admin.site.urls),
|
||||||
|
|
|
@ -11,6 +11,6 @@ import os
|
||||||
|
|
||||||
from django.core.wsgi import get_wsgi_application
|
from django.core.wsgi import get_wsgi_application
|
||||||
|
|
||||||
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'gestiojeux.settings')
|
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "gestiojeux.settings")
|
||||||
|
|
||||||
application = get_wsgi_application()
|
application = get_wsgi_application()
|
||||||
|
|
|
@ -1,7 +1,9 @@
|
||||||
from django.contrib import admin
|
from django.contrib import admin
|
||||||
from .models import Category, Tag, Game, GameComment
|
|
||||||
from comments.admin import CommentAdmin
|
from comments.admin import CommentAdmin
|
||||||
|
|
||||||
|
from .models import Category, Game, GameComment, Tag
|
||||||
|
|
||||||
admin.site.register(Category)
|
admin.site.register(Category)
|
||||||
admin.site.register(Tag)
|
admin.site.register(Tag)
|
||||||
admin.site.register(Game)
|
admin.site.register(Game)
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
from loans.forms import BorrowForm
|
from loans.forms import BorrowForm
|
||||||
|
|
||||||
|
|
||||||
class BorrowGameForm(BorrowForm):
|
class BorrowGameForm(BorrowForm):
|
||||||
error_css_class = "errorfield"
|
error_css_class = "errorfield"
|
||||||
required_css_class = "requiredfield"
|
required_css_class = "requiredfield"
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import markdown
|
import markdown
|
||||||
from django.template.loader import get_template
|
from django.template.loader import get_template
|
||||||
from .models import Category, Tag, Game
|
|
||||||
|
from .models import Category, Game, Tag
|
||||||
|
|
||||||
|
|
||||||
class InventoryLinkProcessor(markdown.inlinepatterns.InlineProcessor):
|
class InventoryLinkProcessor(markdown.inlinepatterns.InlineProcessor):
|
||||||
|
|
|
@ -1,9 +1,10 @@
|
||||||
# Generated by Django 3.1.2 on 2020-12-29 23:21
|
# Generated by Django 3.1.2 on 2020-12-29 23:21
|
||||||
|
|
||||||
import autoslug.fields
|
import autoslug.fields
|
||||||
|
import django.db.models.deletion
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.db import migrations, models
|
from django.db import migrations, models
|
||||||
import django.db.models.deletion
|
|
||||||
import website.validators
|
import website.validators
|
||||||
|
|
||||||
|
|
||||||
|
@ -17,71 +18,216 @@ class Migration(migrations.Migration):
|
||||||
|
|
||||||
operations = [
|
operations = [
|
||||||
migrations.CreateModel(
|
migrations.CreateModel(
|
||||||
name='Category',
|
name="Category",
|
||||||
fields=[
|
fields=[
|
||||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
(
|
||||||
('name', models.CharField(max_length=256, unique=True, verbose_name='nom')),
|
"id",
|
||||||
('slug', autoslug.fields.AutoSlugField(editable=False, populate_from='name', unique=True)),
|
models.AutoField(
|
||||||
|
auto_created=True,
|
||||||
|
primary_key=True,
|
||||||
|
serialize=False,
|
||||||
|
verbose_name="ID",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"name",
|
||||||
|
models.CharField(max_length=256, unique=True, verbose_name="nom"),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"slug",
|
||||||
|
autoslug.fields.AutoSlugField(
|
||||||
|
editable=False, populate_from="name", unique=True
|
||||||
|
),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
options={
|
options={
|
||||||
'verbose_name': 'catégorie',
|
"verbose_name": "catégorie",
|
||||||
'ordering': ['name'],
|
"ordering": ["name"],
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
migrations.CreateModel(
|
migrations.CreateModel(
|
||||||
name='Game',
|
name="Game",
|
||||||
fields=[
|
fields=[
|
||||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
(
|
||||||
('title', models.CharField(max_length=256, unique=True, verbose_name='titre du jeu')),
|
"id",
|
||||||
('slug', autoslug.fields.AutoSlugField(editable=False, populate_from='title', unique=True)),
|
models.AutoField(
|
||||||
('nb_player_min', models.PositiveSmallIntegerField(verbose_name='nombre de joueur·se·s minimum')),
|
auto_created=True,
|
||||||
('nb_player_max', models.PositiveSmallIntegerField(verbose_name='nombre de joueur·se·s maximum')),
|
primary_key=True,
|
||||||
('player_range', models.CharField(blank=True, help_text='Affichage personnalisé pour le nombre de joueur·se·s', max_length=256, verbose_name='nombre de joueur·se·s')),
|
serialize=False,
|
||||||
('duration', models.CharField(blank=True, max_length=256, verbose_name='durée de partie')),
|
verbose_name="ID",
|
||||||
('game_designer', models.CharField(blank=True, max_length=256, verbose_name='game designer')),
|
),
|
||||||
('illustrator', models.CharField(blank=True, max_length=256, verbose_name='illustrateur·trice')),
|
),
|
||||||
('editor', models.CharField(blank=True, max_length=256, verbose_name='éditeur')),
|
(
|
||||||
('description', models.TextField(blank=True, verbose_name='description')),
|
"title",
|
||||||
('image', models.ImageField(blank=True, help_text="L'image doit peser 512 Kio au maximum", upload_to='game_img/', validators=[website.validators.MaxFileSizeValidator(512)], verbose_name='image')),
|
models.CharField(
|
||||||
('missing_elements', models.TextField(blank=True, verbose_name='pièces manquantes')),
|
max_length=256, unique=True, verbose_name="titre du jeu"
|
||||||
('category', models.ForeignKey(on_delete=django.db.models.deletion.RESTRICT, to='inventory.category', verbose_name='catégorie')),
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"slug",
|
||||||
|
autoslug.fields.AutoSlugField(
|
||||||
|
editable=False, populate_from="title", unique=True
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"nb_player_min",
|
||||||
|
models.PositiveSmallIntegerField(
|
||||||
|
verbose_name="nombre de joueur·se·s minimum"
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"nb_player_max",
|
||||||
|
models.PositiveSmallIntegerField(
|
||||||
|
verbose_name="nombre de joueur·se·s maximum"
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"player_range",
|
||||||
|
models.CharField(
|
||||||
|
blank=True,
|
||||||
|
help_text="Affichage personnalisé pour le nombre de joueur·se·s",
|
||||||
|
max_length=256,
|
||||||
|
verbose_name="nombre de joueur·se·s",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"duration",
|
||||||
|
models.CharField(
|
||||||
|
blank=True, max_length=256, verbose_name="durée de partie"
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"game_designer",
|
||||||
|
models.CharField(
|
||||||
|
blank=True, max_length=256, verbose_name="game designer"
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"illustrator",
|
||||||
|
models.CharField(
|
||||||
|
blank=True, max_length=256, verbose_name="illustrateur·trice"
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"editor",
|
||||||
|
models.CharField(
|
||||||
|
blank=True, max_length=256, verbose_name="éditeur"
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"description",
|
||||||
|
models.TextField(blank=True, verbose_name="description"),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"image",
|
||||||
|
models.ImageField(
|
||||||
|
blank=True,
|
||||||
|
help_text="L'image doit peser 512 Kio au maximum",
|
||||||
|
upload_to="game_img/",
|
||||||
|
validators=[website.validators.MaxFileSizeValidator(512)],
|
||||||
|
verbose_name="image",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"missing_elements",
|
||||||
|
models.TextField(blank=True, verbose_name="pièces manquantes"),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"category",
|
||||||
|
models.ForeignKey(
|
||||||
|
on_delete=django.db.models.deletion.RESTRICT,
|
||||||
|
to="inventory.category",
|
||||||
|
verbose_name="catégorie",
|
||||||
|
),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
options={
|
options={
|
||||||
'verbose_name': 'jeu',
|
"verbose_name": "jeu",
|
||||||
'verbose_name_plural': 'jeux',
|
"verbose_name_plural": "jeux",
|
||||||
'ordering': ['title'],
|
"ordering": ["title"],
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
migrations.CreateModel(
|
migrations.CreateModel(
|
||||||
name='Tag',
|
name="Tag",
|
||||||
fields=[
|
fields=[
|
||||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
(
|
||||||
('name', models.CharField(max_length=256, unique=True, verbose_name='nom')),
|
"id",
|
||||||
('slug', autoslug.fields.AutoSlugField(editable=False, populate_from='name', unique=True)),
|
models.AutoField(
|
||||||
|
auto_created=True,
|
||||||
|
primary_key=True,
|
||||||
|
serialize=False,
|
||||||
|
verbose_name="ID",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"name",
|
||||||
|
models.CharField(max_length=256, unique=True, verbose_name="nom"),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"slug",
|
||||||
|
autoslug.fields.AutoSlugField(
|
||||||
|
editable=False, populate_from="name", unique=True
|
||||||
|
),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
options={
|
options={
|
||||||
'ordering': ['name'],
|
"ordering": ["name"],
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
migrations.CreateModel(
|
migrations.CreateModel(
|
||||||
name='GameComment',
|
name="GameComment",
|
||||||
fields=[
|
fields=[
|
||||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
(
|
||||||
('text', models.TextField(verbose_name='texte')),
|
"id",
|
||||||
('created_on', models.DateTimeField(auto_now_add=True, verbose_name='date de publication')),
|
models.AutoField(
|
||||||
('modified_on', models.DateTimeField(auto_now=True, verbose_name='date de modification')),
|
auto_created=True,
|
||||||
('author', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='auteur·ice')),
|
primary_key=True,
|
||||||
('commented_object', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='comments', to='inventory.game', verbose_name='jeu')),
|
serialize=False,
|
||||||
|
verbose_name="ID",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
("text", models.TextField(verbose_name="texte")),
|
||||||
|
(
|
||||||
|
"created_on",
|
||||||
|
models.DateTimeField(
|
||||||
|
auto_now_add=True, verbose_name="date de publication"
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"modified_on",
|
||||||
|
models.DateTimeField(
|
||||||
|
auto_now=True, verbose_name="date de modification"
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"author",
|
||||||
|
models.ForeignKey(
|
||||||
|
on_delete=django.db.models.deletion.CASCADE,
|
||||||
|
to=settings.AUTH_USER_MODEL,
|
||||||
|
verbose_name="auteur·ice",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"commented_object",
|
||||||
|
models.ForeignKey(
|
||||||
|
on_delete=django.db.models.deletion.CASCADE,
|
||||||
|
related_name="comments",
|
||||||
|
to="inventory.game",
|
||||||
|
verbose_name="jeu",
|
||||||
|
),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
options={
|
options={
|
||||||
'verbose_name': 'commentaire sur un jeu',
|
"verbose_name": "commentaire sur un jeu",
|
||||||
'verbose_name_plural': 'commentaires sur des jeux',
|
"verbose_name_plural": "commentaires sur des jeux",
|
||||||
'ordering': ['created_on'],
|
"ordering": ["created_on"],
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
migrations.AddField(
|
migrations.AddField(
|
||||||
model_name='game',
|
model_name="game",
|
||||||
name='tags',
|
name="tags",
|
||||||
field=models.ManyToManyField(blank=True, to='inventory.Tag', verbose_name='tags'),
|
field=models.ManyToManyField(
|
||||||
|
blank=True, to="inventory.Tag", verbose_name="tags"
|
||||||
|
),
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
|
|
|
@ -1,32 +1,52 @@
|
||||||
# Generated by Django 4.2.8 on 2024-05-02 09:30
|
# Generated by Django 4.2.8 on 2024-05-02 09:30
|
||||||
|
|
||||||
import autoslug.fields
|
import autoslug.fields
|
||||||
from django.db import migrations, models
|
|
||||||
import django.db.models.deletion
|
import django.db.models.deletion
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
dependencies = [
|
dependencies = [
|
||||||
('inventory', '0002_duration_range'),
|
("inventory", "0002_duration_range"),
|
||||||
]
|
]
|
||||||
|
|
||||||
operations = [
|
operations = [
|
||||||
migrations.CreateModel(
|
migrations.CreateModel(
|
||||||
name='GameLoan',
|
name="GameLoan",
|
||||||
fields=[
|
fields=[
|
||||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
(
|
||||||
('slug', autoslug.fields.AutoSlugField(editable=False, populate_from='lent_object', unique=True)),
|
"id",
|
||||||
('borrow_date', models.DateTimeField(auto_now_add=True)),
|
models.AutoField(
|
||||||
('return_date', models.DateTimeField(null=True)),
|
auto_created=True,
|
||||||
('mail', models.EmailField(max_length=254)),
|
primary_key=True,
|
||||||
('lent_object', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='inventory.game', verbose_name='jeu emprunté')),
|
serialize=False,
|
||||||
|
verbose_name="ID",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"slug",
|
||||||
|
autoslug.fields.AutoSlugField(
|
||||||
|
editable=False, populate_from="lent_object", unique=True
|
||||||
|
),
|
||||||
|
),
|
||||||
|
("borrow_date", models.DateTimeField(auto_now_add=True)),
|
||||||
|
("return_date", models.DateTimeField(null=True)),
|
||||||
|
("mail", models.EmailField(max_length=254)),
|
||||||
|
(
|
||||||
|
"lent_object",
|
||||||
|
models.ForeignKey(
|
||||||
|
on_delete=django.db.models.deletion.CASCADE,
|
||||||
|
to="inventory.game",
|
||||||
|
verbose_name="jeu emprunté",
|
||||||
|
),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
options={
|
options={
|
||||||
'verbose_name': 'emprunt',
|
"verbose_name": "emprunt",
|
||||||
'verbose_name_plural': 'emprunts',
|
"verbose_name_plural": "emprunts",
|
||||||
'ordering': ['borrow_date'],
|
"ordering": ["borrow_date"],
|
||||||
'abstract': False,
|
"abstract": False,
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
|
|
|
@ -6,26 +6,33 @@ from django.db import migrations, models
|
||||||
class Migration(migrations.Migration):
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
dependencies = [
|
dependencies = [
|
||||||
('inventory', '0003_gameloan'),
|
("inventory", "0003_gameloan"),
|
||||||
]
|
]
|
||||||
|
|
||||||
operations = [
|
operations = [
|
||||||
migrations.AlterModelOptions(
|
migrations.AlterModelOptions(
|
||||||
name='category',
|
name="category",
|
||||||
options={'ordering': ['name'], 'verbose_name': 'étagère'},
|
options={"ordering": ["name"], "verbose_name": "étagère"},
|
||||||
),
|
),
|
||||||
migrations.AlterModelOptions(
|
migrations.AlterModelOptions(
|
||||||
name='gameloan',
|
name="gameloan",
|
||||||
options={'ordering': ['borrow_date'], 'permissions': [('can_see_loan_details', 'Can see loan details')], 'verbose_name': 'emprunt', 'verbose_name_plural': 'emprunts'},
|
options={
|
||||||
|
"ordering": ["borrow_date"],
|
||||||
|
"permissions": [("can_see_loan_details", "Can see loan details")],
|
||||||
|
"verbose_name": "emprunt",
|
||||||
|
"verbose_name_plural": "emprunts",
|
||||||
|
},
|
||||||
),
|
),
|
||||||
migrations.AlterField(
|
migrations.AlterField(
|
||||||
model_name='gameloan',
|
model_name="gameloan",
|
||||||
name='borrow_date',
|
name="borrow_date",
|
||||||
field=models.DateTimeField(auto_now_add=True, verbose_name='Date d’emprunt'),
|
field=models.DateTimeField(
|
||||||
|
auto_now_add=True, verbose_name="Date d’emprunt"
|
||||||
|
),
|
||||||
),
|
),
|
||||||
migrations.AlterField(
|
migrations.AlterField(
|
||||||
model_name='gameloan',
|
model_name="gameloan",
|
||||||
name='return_date',
|
name="return_date",
|
||||||
field=models.DateTimeField(null=True, verbose_name='Date de retour'),
|
field=models.DateTimeField(null=True, verbose_name="Date de retour"),
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
# Generated by Django 4.2.11 on 2024-07-02 22:59
|
# Generated by Django 4.2.11 on 2024-07-02 22:59
|
||||||
|
|
||||||
from django.db import migrations, models
|
|
||||||
import django.db.models.deletion
|
import django.db.models.deletion
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
class Migration(migrations.Migration):
|
||||||
|
|
|
@ -1,13 +1,13 @@
|
||||||
from django.db import models
|
|
||||||
from django.urls import reverse
|
|
||||||
from django.core.exceptions import ValidationError
|
|
||||||
import os
|
import os
|
||||||
import uuid
|
import uuid
|
||||||
|
|
||||||
from autoslug import AutoSlugField
|
from autoslug import AutoSlugField
|
||||||
from website.validators import MaxFileSizeValidator
|
from django.db import models
|
||||||
|
from django.urls import reverse
|
||||||
|
|
||||||
from comments.models import AbstractComment
|
from comments.models import AbstractComment
|
||||||
from loans.models import AbstractLoan
|
from loans.models import AbstractLoan
|
||||||
|
from website.validators import MaxFileSizeValidator
|
||||||
|
|
||||||
|
|
||||||
class Category(models.Model):
|
class Category(models.Model):
|
||||||
|
@ -78,7 +78,6 @@ class Game(models.Model):
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return self.title
|
return self.title
|
||||||
|
|
||||||
|
|
||||||
def get_player_range(self):
|
def get_player_range(self):
|
||||||
return ""
|
return ""
|
||||||
|
|
||||||
|
@ -104,13 +103,12 @@ class GameComment(AbstractComment):
|
||||||
"inventory:modify_game_comment", args=(self.commented_object.slug, self.id)
|
"inventory:modify_game_comment", args=(self.commented_object.slug, self.id)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class GameLoan(AbstractLoan):
|
class GameLoan(AbstractLoan):
|
||||||
lent_object = models.ForeignKey(
|
lent_object = models.ForeignKey(
|
||||||
Game, on_delete=models.CASCADE,
|
Game, on_delete=models.CASCADE, verbose_name="outil emprunté"
|
||||||
verbose_name="outil emprunté"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
class Meta(AbstractLoan.Meta):
|
class Meta(AbstractLoan.Meta):
|
||||||
abstract = False
|
abstract = False
|
||||||
permissions = [("can_see_loan_details", "Can see loan details")]
|
permissions = [("can_see_loan_details", "Can see loan details")]
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
from haystack import indexes
|
from haystack import indexes
|
||||||
from .models import Category, Tag, Game
|
|
||||||
|
from .models import Category, Game, Tag
|
||||||
|
|
||||||
|
|
||||||
class CategoryIndex(indexes.SearchIndex, indexes.Indexable):
|
class CategoryIndex(indexes.SearchIndex, indexes.Indexable):
|
||||||
|
|
|
@ -24,7 +24,7 @@ class LoanTable(tables.Table):
|
||||||
|
|
||||||
def render_slug(self, value, record):
|
def render_slug(self, value, record):
|
||||||
res = ""
|
res = ""
|
||||||
if record.return_date == None:
|
if record.return_date is None:
|
||||||
res = format_html(
|
res = format_html(
|
||||||
"<a class='button' href='{}?next={}'>Rendre l'outil</a>",
|
"<a class='button' href='{}?next={}'>Rendre l'outil</a>",
|
||||||
reverse("inventory:return_game", args=[value]),
|
reverse("inventory:return_game", args=[value]),
|
||||||
|
|
|
@ -1,3 +1 @@
|
||||||
from django.test import TestCase
|
|
||||||
|
|
||||||
# Create your tests here.
|
# Create your tests here.
|
||||||
|
|
|
@ -1,10 +1,23 @@
|
||||||
from django.urls import path
|
from django.urls import path
|
||||||
|
|
||||||
from .views import (AddGameCommentView, BorrowGameView, CategoryListView,
|
from .views import (
|
||||||
CategoryView, DetailLoanView, GameListView, GameLoanView,
|
AddGameCommentView,
|
||||||
GameView, InventorySearchView, InventoryView,
|
BorrowGameView,
|
||||||
ModifyGameCommentView, OngoingLoansView, QrCodeView,
|
CategoryListView,
|
||||||
ReturnGameView, TagListView, TagView)
|
CategoryView,
|
||||||
|
DetailLoanView,
|
||||||
|
GameListView,
|
||||||
|
GameLoanView,
|
||||||
|
GameView,
|
||||||
|
InventorySearchView,
|
||||||
|
InventoryView,
|
||||||
|
ModifyGameCommentView,
|
||||||
|
OngoingLoansView,
|
||||||
|
QrCodeView,
|
||||||
|
ReturnGameView,
|
||||||
|
TagListView,
|
||||||
|
TagView,
|
||||||
|
)
|
||||||
|
|
||||||
app_name = "inventory"
|
app_name = "inventory"
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
import qrcode
|
import qrcode
|
||||||
from comments.views import AddCommentView, ModifyCommentView
|
|
||||||
from django.contrib.auth.mixins import PermissionRequiredMixin
|
from django.contrib.auth.mixins import PermissionRequiredMixin
|
||||||
from django.http import HttpResponse
|
from django.http import HttpResponse
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
|
@ -8,6 +7,8 @@ from django_tables2.views import SingleTableView
|
||||||
from haystack.forms import SearchForm
|
from haystack.forms import SearchForm
|
||||||
from haystack.generic_views import SearchView
|
from haystack.generic_views import SearchView
|
||||||
from haystack.query import SearchQuerySet
|
from haystack.query import SearchQuerySet
|
||||||
|
|
||||||
|
from comments.views import AddCommentView, ModifyCommentView
|
||||||
from loans.views import BorrowView, DetailLoanView, ReturnView
|
from loans.views import BorrowView, DetailLoanView, ReturnView
|
||||||
|
|
||||||
from .forms import BorrowGameForm
|
from .forms import BorrowGameForm
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
from django.contrib import admin
|
from django.contrib import admin
|
||||||
|
|
||||||
|
|
||||||
class LoanAdmin(admin.ModelAdmin):
|
class LoanAdmin(admin.ModelAdmin):
|
||||||
list_display = ("lent_object", "borrow_date", "return_date")
|
list_display = ("lent_object", "borrow_date", "return_date")
|
||||||
ordering = ("-borrow_date",)
|
ordering = ("-borrow_date",)
|
||||||
|
|
|
@ -2,5 +2,5 @@ from django.apps import AppConfig
|
||||||
|
|
||||||
|
|
||||||
class LoansConfig(AppConfig):
|
class LoansConfig(AppConfig):
|
||||||
default_auto_field = 'django.db.models.BigAutoField'
|
default_auto_field = "django.db.models.BigAutoField"
|
||||||
name = 'loans'
|
name = "loans"
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
from django import forms
|
from django import forms
|
||||||
|
|
||||||
|
|
||||||
class BorrowForm(forms.Form):
|
class BorrowForm(forms.Form):
|
||||||
mail = forms.EmailField(label="Mail")
|
mail = forms.EmailField(label="Mail")
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
# Generated by Django 4.2.8 on 2024-04-23 16:45
|
# Generated by Django 4.2.8 on 2024-04-23 16:45
|
||||||
|
|
||||||
import autoslug.fields
|
import autoslug.fields
|
||||||
from django.db import migrations, models
|
|
||||||
import django.db.models.deletion
|
import django.db.models.deletion
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
class Migration(migrations.Migration):
|
||||||
|
@ -10,24 +10,45 @@ class Migration(migrations.Migration):
|
||||||
initial = True
|
initial = True
|
||||||
|
|
||||||
dependencies = [
|
dependencies = [
|
||||||
('inventory', '0002_duration_range'),
|
("inventory", "0002_duration_range"),
|
||||||
]
|
]
|
||||||
|
|
||||||
operations = [
|
operations = [
|
||||||
migrations.CreateModel(
|
migrations.CreateModel(
|
||||||
name='Loan',
|
name="Loan",
|
||||||
fields=[
|
fields=[
|
||||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
(
|
||||||
('slug', autoslug.fields.AutoSlugField(editable=False, populate_from='game', unique=True)),
|
"id",
|
||||||
('borrow_date', models.DateTimeField(auto_now_add=True)),
|
models.BigAutoField(
|
||||||
('return_date', models.DateTimeField(null=True)),
|
auto_created=True,
|
||||||
('mail', models.EmailField(max_length=254)),
|
primary_key=True,
|
||||||
('game', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='loans', to='inventory.game', verbose_name='jeu emprunté')),
|
serialize=False,
|
||||||
|
verbose_name="ID",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"slug",
|
||||||
|
autoslug.fields.AutoSlugField(
|
||||||
|
editable=False, populate_from="game", unique=True
|
||||||
|
),
|
||||||
|
),
|
||||||
|
("borrow_date", models.DateTimeField(auto_now_add=True)),
|
||||||
|
("return_date", models.DateTimeField(null=True)),
|
||||||
|
("mail", models.EmailField(max_length=254)),
|
||||||
|
(
|
||||||
|
"game",
|
||||||
|
models.ForeignKey(
|
||||||
|
on_delete=django.db.models.deletion.CASCADE,
|
||||||
|
related_name="loans",
|
||||||
|
to="inventory.game",
|
||||||
|
verbose_name="jeu emprunté",
|
||||||
|
),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
options={
|
options={
|
||||||
'verbose_name': 'emprunt',
|
"verbose_name": "emprunt",
|
||||||
'verbose_name_plural': 'emprunts',
|
"verbose_name_plural": "emprunts",
|
||||||
'ordering': ['borrow_date'],
|
"ordering": ["borrow_date"],
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
|
|
|
@ -6,11 +6,11 @@ from django.db import migrations
|
||||||
class Migration(migrations.Migration):
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
dependencies = [
|
dependencies = [
|
||||||
('loans', '0001_initial'),
|
("loans", "0001_initial"),
|
||||||
]
|
]
|
||||||
|
|
||||||
operations = [
|
operations = [
|
||||||
migrations.DeleteModel(
|
migrations.DeleteModel(
|
||||||
name='Loan',
|
name="Loan",
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
from django.db import models
|
|
||||||
from autoslug import AutoSlugField
|
from autoslug import AutoSlugField
|
||||||
|
from django.db import models
|
||||||
from django.utils.timezone import now
|
from django.utils.timezone import now
|
||||||
|
|
||||||
|
|
||||||
class AbstractLoan(models.Model):
|
class AbstractLoan(models.Model):
|
||||||
lent_object = None # Fill this with a foreign key in subclasses
|
lent_object = None # Fill this with a foreign key in subclasses
|
||||||
slug = AutoSlugField(unique=True, populate_from="lent_object")
|
slug = AutoSlugField(unique=True, populate_from="lent_object")
|
||||||
borrow_date = models.DateTimeField(
|
borrow_date = models.DateTimeField(auto_now_add=True, verbose_name="Date d’emprunt")
|
||||||
auto_now_add=True, verbose_name="Date d’emprunt")
|
|
||||||
return_date = models.DateTimeField(null=True, verbose_name="Date de retour")
|
return_date = models.DateTimeField(null=True, verbose_name="Date de retour")
|
||||||
mail = models.EmailField()
|
mail = models.EmailField()
|
||||||
|
|
||||||
|
@ -28,7 +28,7 @@ class AbstractLoan(models.Model):
|
||||||
@classmethod
|
@classmethod
|
||||||
def ongoing_loans(cls, obj=None):
|
def ongoing_loans(cls, obj=None):
|
||||||
ongoing = cls.objects.filter(return_date=None)
|
ongoing = cls.objects.filter(return_date=None)
|
||||||
if obj != None:
|
if obj is not None:
|
||||||
return ongoing.filter(lent_object=obj)
|
return ongoing.filter(lent_object=obj)
|
||||||
else:
|
else:
|
||||||
return ongoing
|
return ongoing
|
||||||
|
|
|
@ -1,3 +1 @@
|
||||||
from django.test import TestCase
|
|
||||||
|
|
||||||
# Create your tests here.
|
# Create your tests here.
|
||||||
|
|
|
@ -1,9 +1,8 @@
|
||||||
from django.views.generic import DetailView, FormView, RedirectView
|
|
||||||
from django.views.generic.detail import SingleObjectMixin
|
|
||||||
from django.contrib import messages
|
from django.contrib import messages
|
||||||
from django.shortcuts import redirect
|
from django.shortcuts import redirect
|
||||||
from inventory.models import Game
|
from django.views.generic import DetailView, FormView, RedirectView
|
||||||
from .models import AbstractLoan
|
from django.views.generic.detail import SingleObjectMixin
|
||||||
|
|
||||||
from .forms import BorrowForm
|
from .forms import BorrowForm
|
||||||
|
|
||||||
|
|
||||||
|
@ -18,8 +17,9 @@ class ReturnView(SingleObjectMixin, RedirectView):
|
||||||
def get_redirect_url(self, *args, **kwargs):
|
def get_redirect_url(self, *args, **kwargs):
|
||||||
loan = self.get_object()
|
loan = self.get_object()
|
||||||
loan.return_object()
|
loan.return_object()
|
||||||
kwargs[self.redirect_slug_field] = getattr(loan.lent_object,
|
kwargs[self.redirect_slug_field] = getattr(
|
||||||
loan.lent_object_slug_field)
|
loan.lent_object, loan.lent_object_slug_field
|
||||||
|
)
|
||||||
messages.success(self.request, "Rendu effectué.")
|
messages.success(self.request, "Rendu effectué.")
|
||||||
if "next" in self.request.GET:
|
if "next" in self.request.GET:
|
||||||
return self.request.GET["next"]
|
return self.request.GET["next"]
|
||||||
|
@ -52,8 +52,10 @@ class BorrowView(SingleObjectMixin, FormView):
|
||||||
loan.save()
|
loan.save()
|
||||||
self.request.session["loan_mail"] = loan.mail
|
self.request.session["loan_mail"] = loan.mail
|
||||||
messages.success(self.request, "Votre emprunt est enregistré.")
|
messages.success(self.request, "Votre emprunt est enregistré.")
|
||||||
return redirect(self.success_pattern_name,
|
return redirect(
|
||||||
getattr(obj, loan.lent_object_slug_field))
|
self.success_pattern_name, getattr(obj, loan.lent_object_slug_field)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class DetailLoanView(DetailView):
|
class DetailLoanView(DetailView):
|
||||||
# Inherited classes should contain:
|
# Inherited classes should contain:
|
||||||
|
@ -69,4 +71,3 @@ class DetailLoanView(DetailView):
|
||||||
if is_borrowed:
|
if is_borrowed:
|
||||||
context["loan"] = loans.get()
|
context["loan"] = loans.get()
|
||||||
return context
|
return context
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,7 @@ import sys
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'gestiojeux.settings')
|
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "gestiojeux.settings")
|
||||||
try:
|
try:
|
||||||
from django.core.management import execute_from_command_line
|
from django.core.management import execute_from_command_line
|
||||||
except ImportError as exc:
|
except ImportError as exc:
|
||||||
|
@ -17,5 +17,5 @@ def main():
|
||||||
execute_from_command_line(sys.argv)
|
execute_from_command_line(sys.argv)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == "__main__":
|
||||||
main()
|
main()
|
||||||
|
|
|
@ -1,20 +1,34 @@
|
||||||
# Generated by npins. Do not modify; will be overwritten regularly
|
# Generated by npins. Do not modify; will be overwritten regularly
|
||||||
let
|
let
|
||||||
data = builtins.fromJSON (builtins.readFile ./sources.json);
|
data = builtins.fromJSON (builtins.readFile ./sources.json);
|
||||||
version = data.version;
|
inherit (data) version;
|
||||||
|
|
||||||
mkSource = spec:
|
mkSource =
|
||||||
assert spec ? type; let
|
spec:
|
||||||
|
assert spec ? type;
|
||||||
|
let
|
||||||
path =
|
path =
|
||||||
if spec.type == "Git" then mkGitSource spec
|
if spec.type == "Git" then
|
||||||
else if spec.type == "GitRelease" then mkGitSource spec
|
mkGitSource spec
|
||||||
else if spec.type == "PyPi" then mkPyPiSource spec
|
else if spec.type == "GitRelease" then
|
||||||
else if spec.type == "Channel" then mkChannelSource spec
|
mkGitSource spec
|
||||||
else builtins.throw "Unknown source type ${spec.type}";
|
else if spec.type == "PyPi" then
|
||||||
|
mkPyPiSource spec
|
||||||
|
else if spec.type == "Channel" then
|
||||||
|
mkChannelSource spec
|
||||||
|
else
|
||||||
|
builtins.throw "Unknown source type ${spec.type}";
|
||||||
in
|
in
|
||||||
spec // { outPath = path; };
|
spec // { outPath = path; };
|
||||||
|
|
||||||
mkGitSource = { repository, revision, url ? null, hash, ... }:
|
mkGitSource =
|
||||||
|
{
|
||||||
|
repository,
|
||||||
|
revision,
|
||||||
|
url ? null,
|
||||||
|
hash,
|
||||||
|
...
|
||||||
|
}:
|
||||||
assert repository ? type;
|
assert repository ? type;
|
||||||
# At the moment, either it is a plain git repository (which has an url), or it is a GitHub/GitLab repository
|
# At the moment, either it is a plain git repository (which has an url), or it is a GitHub/GitLab repository
|
||||||
# In the latter case, there we will always be an url to the tarball
|
# In the latter case, there we will always be an url to the tarball
|
||||||
|
@ -23,19 +37,23 @@ let
|
||||||
inherit url;
|
inherit url;
|
||||||
sha256 = hash; # FIXME: check nix version & use SRI hashes
|
sha256 = hash; # FIXME: check nix version & use SRI hashes
|
||||||
})
|
})
|
||||||
else assert repository.type == "Git"; builtins.fetchGit {
|
else
|
||||||
url = repository.url;
|
assert repository.type == "Git";
|
||||||
|
builtins.fetchGit {
|
||||||
|
inherit (repository) url;
|
||||||
rev = revision;
|
rev = revision;
|
||||||
# hash = hash;
|
# hash = hash;
|
||||||
};
|
};
|
||||||
|
|
||||||
mkPyPiSource = { url, hash, ... }:
|
mkPyPiSource =
|
||||||
|
{ url, hash, ... }:
|
||||||
builtins.fetchurl {
|
builtins.fetchurl {
|
||||||
inherit url;
|
inherit url;
|
||||||
sha256 = hash;
|
sha256 = hash;
|
||||||
};
|
};
|
||||||
|
|
||||||
mkChannelSource = { url, hash, ... }:
|
mkChannelSource =
|
||||||
|
{ url, hash, ... }:
|
||||||
builtins.fetchTarball {
|
builtins.fetchTarball {
|
||||||
inherit url;
|
inherit url;
|
||||||
sha256 = hash;
|
sha256 = hash;
|
||||||
|
|
|
@ -1,5 +1,17 @@
|
||||||
{
|
{
|
||||||
"pins": {
|
"pins": {
|
||||||
|
"git-hooks": {
|
||||||
|
"type": "Git",
|
||||||
|
"repository": {
|
||||||
|
"type": "GitHub",
|
||||||
|
"owner": "cachix",
|
||||||
|
"repo": "git-hooks.nix"
|
||||||
|
},
|
||||||
|
"branch": "master",
|
||||||
|
"revision": "0ff4381bbb8f7a52ca4a851660fc7a437a4c6e07",
|
||||||
|
"url": "https://github.com/cachix/git-hooks.nix/archive/0ff4381bbb8f7a52ca4a851660fc7a437a4c6e07.tar.gz",
|
||||||
|
"hash": "0bmgc731c5rvky6qxc4f6gvgyiic8dna5dv3j19kya86idf7wn0p"
|
||||||
|
},
|
||||||
"nix-pkgs": {
|
"nix-pkgs": {
|
||||||
"type": "Git",
|
"type": "Git",
|
||||||
"repository": {
|
"repository": {
|
||||||
|
|
2
pyproject.toml
Normal file
2
pyproject.toml
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
[tool.isort]
|
||||||
|
profile = "black"
|
|
@ -1,7 +1,9 @@
|
||||||
from django.contrib import admin
|
from django.contrib import admin
|
||||||
from .models import Suggestion, SuggestionComment
|
|
||||||
from comments.admin import CommentAdmin
|
from comments.admin import CommentAdmin
|
||||||
|
|
||||||
|
from .models import Suggestion, SuggestionComment
|
||||||
|
|
||||||
|
|
||||||
class SuggestionAdmin(admin.ModelAdmin):
|
class SuggestionAdmin(admin.ModelAdmin):
|
||||||
exclude = ("upvoting_users",)
|
exclude = ("upvoting_users",)
|
||||||
|
|
|
@ -2,4 +2,4 @@ from django.apps import AppConfig
|
||||||
|
|
||||||
|
|
||||||
class SuggestionsConfig(AppConfig):
|
class SuggestionsConfig(AppConfig):
|
||||||
name = 'suggestions'
|
name = "suggestions"
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
from django import forms
|
from django import forms
|
||||||
|
|
||||||
from .models import Suggestion
|
from .models import Suggestion
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,10 +1,11 @@
|
||||||
# Generated by Django 3.1.2 on 2020-12-29 23:43
|
# Generated by Django 3.1.2 on 2020-12-29 23:43
|
||||||
|
|
||||||
import autoslug.fields
|
import autoslug.fields
|
||||||
from django.conf import settings
|
|
||||||
import django.core.validators
|
import django.core.validators
|
||||||
from django.db import migrations, models
|
|
||||||
import django.db.models.deletion
|
import django.db.models.deletion
|
||||||
|
from django.conf import settings
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
import website.validators
|
import website.validators
|
||||||
|
|
||||||
|
|
||||||
|
@ -13,52 +14,189 @@ class Migration(migrations.Migration):
|
||||||
initial = True
|
initial = True
|
||||||
|
|
||||||
dependencies = [
|
dependencies = [
|
||||||
('inventory', '0001_initial'),
|
("inventory", "0001_initial"),
|
||||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||||
]
|
]
|
||||||
|
|
||||||
operations = [
|
operations = [
|
||||||
migrations.CreateModel(
|
migrations.CreateModel(
|
||||||
name='Suggestion',
|
name="Suggestion",
|
||||||
fields=[
|
fields=[
|
||||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
(
|
||||||
('title', models.CharField(max_length=256, unique=True, verbose_name='titre du jeu')),
|
"id",
|
||||||
('slug', autoslug.fields.AutoSlugField(editable=False, populate_from='title', unique=True)),
|
models.AutoField(
|
||||||
('price', models.DecimalField(decimal_places=2, max_digits=6, validators=[django.core.validators.MinValueValidator(0)], verbose_name='prix en euros')),
|
auto_created=True,
|
||||||
('buy_link', models.URLField(verbose_name="lien vers un site d'achat")),
|
primary_key=True,
|
||||||
('nb_player_min', models.PositiveSmallIntegerField(verbose_name='nombre de joueur·se·s minimum')),
|
serialize=False,
|
||||||
('nb_player_max', models.PositiveSmallIntegerField(verbose_name='nombre de joueur·se·s maximum')),
|
verbose_name="ID",
|
||||||
('player_range_precisions', models.CharField(blank=True, help_text='Pour indiquer une éventuelle contrainte (ex. parité) ou information sur le nombre de joueur·se·s', max_length=256, verbose_name='précisions sur le nombre de joueur·se·s')),
|
),
|
||||||
('duration', models.CharField(max_length=256, verbose_name='durée de partie')),
|
),
|
||||||
('game_designer', models.CharField(blank=True, max_length=256, verbose_name='game designer')),
|
(
|
||||||
('illustrator', models.CharField(blank=True, max_length=256, verbose_name='illustrateur·trice')),
|
"title",
|
||||||
('editor', models.CharField(blank=True, max_length=256, verbose_name='éditeur')),
|
models.CharField(
|
||||||
('description', models.TextField(blank=True, help_text="Peut correspondre à celle de l'éditeur et ne doit pas contenir d'avis personnel", verbose_name='description')),
|
max_length=256, unique=True, verbose_name="titre du jeu"
|
||||||
('image', models.ImageField(blank=True, help_text='Image du jeu de moins de 512 Kio à téléverser (par exemple une photo de sa boite)', upload_to='suggestion_img/', validators=[website.validators.MaxFileSizeValidator(512)], verbose_name='image')),
|
),
|
||||||
('category', models.ForeignKey(blank=True, help_text='Idée de catégorie dans laquelle ranger ce jeu', null=True, on_delete=django.db.models.deletion.SET_NULL, to='inventory.category', verbose_name='catégorie')),
|
),
|
||||||
('tags', models.ManyToManyField(blank=True, help_text="Vous pouvez en sélectionner plusieurs ou aucun (sur ordinateur Ctrl+Clic change l'état de selection d'un tag)", to='inventory.Tag', verbose_name='tags qui correspondent à ce jeu')),
|
(
|
||||||
('upvoting_users', models.ManyToManyField(blank=True, related_name='upvoted_suggestions', to=settings.AUTH_USER_MODEL, verbose_name='personnes intéressées')),
|
"slug",
|
||||||
|
autoslug.fields.AutoSlugField(
|
||||||
|
editable=False, populate_from="title", unique=True
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"price",
|
||||||
|
models.DecimalField(
|
||||||
|
decimal_places=2,
|
||||||
|
max_digits=6,
|
||||||
|
validators=[django.core.validators.MinValueValidator(0)],
|
||||||
|
verbose_name="prix en euros",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
("buy_link", models.URLField(verbose_name="lien vers un site d'achat")),
|
||||||
|
(
|
||||||
|
"nb_player_min",
|
||||||
|
models.PositiveSmallIntegerField(
|
||||||
|
verbose_name="nombre de joueur·se·s minimum"
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"nb_player_max",
|
||||||
|
models.PositiveSmallIntegerField(
|
||||||
|
verbose_name="nombre de joueur·se·s maximum"
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"player_range_precisions",
|
||||||
|
models.CharField(
|
||||||
|
blank=True,
|
||||||
|
help_text="Pour indiquer une éventuelle contrainte (ex. parité) ou information sur le nombre de joueur·se·s",
|
||||||
|
max_length=256,
|
||||||
|
verbose_name="précisions sur le nombre de joueur·se·s",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"duration",
|
||||||
|
models.CharField(max_length=256, verbose_name="durée de partie"),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"game_designer",
|
||||||
|
models.CharField(
|
||||||
|
blank=True, max_length=256, verbose_name="game designer"
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"illustrator",
|
||||||
|
models.CharField(
|
||||||
|
blank=True, max_length=256, verbose_name="illustrateur·trice"
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"editor",
|
||||||
|
models.CharField(
|
||||||
|
blank=True, max_length=256, verbose_name="éditeur"
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"description",
|
||||||
|
models.TextField(
|
||||||
|
blank=True,
|
||||||
|
help_text="Peut correspondre à celle de l'éditeur et ne doit pas contenir d'avis personnel",
|
||||||
|
verbose_name="description",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"image",
|
||||||
|
models.ImageField(
|
||||||
|
blank=True,
|
||||||
|
help_text="Image du jeu de moins de 512 Kio à téléverser (par exemple une photo de sa boite)",
|
||||||
|
upload_to="suggestion_img/",
|
||||||
|
validators=[website.validators.MaxFileSizeValidator(512)],
|
||||||
|
verbose_name="image",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"category",
|
||||||
|
models.ForeignKey(
|
||||||
|
blank=True,
|
||||||
|
help_text="Idée de catégorie dans laquelle ranger ce jeu",
|
||||||
|
null=True,
|
||||||
|
on_delete=django.db.models.deletion.SET_NULL,
|
||||||
|
to="inventory.category",
|
||||||
|
verbose_name="catégorie",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"tags",
|
||||||
|
models.ManyToManyField(
|
||||||
|
blank=True,
|
||||||
|
help_text="Vous pouvez en sélectionner plusieurs ou aucun (sur ordinateur Ctrl+Clic change l'état de selection d'un tag)",
|
||||||
|
to="inventory.Tag",
|
||||||
|
verbose_name="tags qui correspondent à ce jeu",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"upvoting_users",
|
||||||
|
models.ManyToManyField(
|
||||||
|
blank=True,
|
||||||
|
related_name="upvoted_suggestions",
|
||||||
|
to=settings.AUTH_USER_MODEL,
|
||||||
|
verbose_name="personnes intéressées",
|
||||||
|
),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
options={
|
options={
|
||||||
'verbose_name': 'suggestion de jeu',
|
"verbose_name": "suggestion de jeu",
|
||||||
'verbose_name_plural': 'suggestions de jeux',
|
"verbose_name_plural": "suggestions de jeux",
|
||||||
'ordering': ['title'],
|
"ordering": ["title"],
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
migrations.CreateModel(
|
migrations.CreateModel(
|
||||||
name='SuggestionComment',
|
name="SuggestionComment",
|
||||||
fields=[
|
fields=[
|
||||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
(
|
||||||
('text', models.TextField(verbose_name='texte')),
|
"id",
|
||||||
('created_on', models.DateTimeField(auto_now_add=True, verbose_name='date de publication')),
|
models.AutoField(
|
||||||
('modified_on', models.DateTimeField(auto_now=True, verbose_name='date de modification')),
|
auto_created=True,
|
||||||
('author', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='auteur·ice')),
|
primary_key=True,
|
||||||
('commented_object', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='comments', to='suggestions.suggestion', verbose_name='suggestion')),
|
serialize=False,
|
||||||
|
verbose_name="ID",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
("text", models.TextField(verbose_name="texte")),
|
||||||
|
(
|
||||||
|
"created_on",
|
||||||
|
models.DateTimeField(
|
||||||
|
auto_now_add=True, verbose_name="date de publication"
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"modified_on",
|
||||||
|
models.DateTimeField(
|
||||||
|
auto_now=True, verbose_name="date de modification"
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"author",
|
||||||
|
models.ForeignKey(
|
||||||
|
on_delete=django.db.models.deletion.CASCADE,
|
||||||
|
to=settings.AUTH_USER_MODEL,
|
||||||
|
verbose_name="auteur·ice",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"commented_object",
|
||||||
|
models.ForeignKey(
|
||||||
|
on_delete=django.db.models.deletion.CASCADE,
|
||||||
|
related_name="comments",
|
||||||
|
to="suggestions.suggestion",
|
||||||
|
verbose_name="suggestion",
|
||||||
|
),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
options={
|
options={
|
||||||
'verbose_name': 'commentaire sur une suggestion',
|
"verbose_name": "commentaire sur une suggestion",
|
||||||
'verbose_name_plural': 'commentaires sur des suggestions',
|
"verbose_name_plural": "commentaires sur des suggestions",
|
||||||
'ordering': ['created_on'],
|
"ordering": ["created_on"],
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
|
|
|
@ -6,29 +6,43 @@ from django.db import migrations, models
|
||||||
class Migration(migrations.Migration):
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
dependencies = [
|
dependencies = [
|
||||||
('suggestions', '0001_initial'),
|
("suggestions", "0001_initial"),
|
||||||
]
|
]
|
||||||
|
|
||||||
operations = [
|
operations = [
|
||||||
migrations.RemoveField(
|
migrations.RemoveField(
|
||||||
model_name='suggestion',
|
model_name="suggestion",
|
||||||
name='duration',
|
name="duration",
|
||||||
),
|
),
|
||||||
migrations.AddField(
|
migrations.AddField(
|
||||||
model_name='suggestion',
|
model_name="suggestion",
|
||||||
name='duration_max',
|
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'),
|
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,
|
preserve_default=False,
|
||||||
),
|
),
|
||||||
migrations.AddField(
|
migrations.AddField(
|
||||||
model_name='suggestion',
|
model_name="suggestion",
|
||||||
name='duration_min',
|
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'),
|
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,
|
preserve_default=False,
|
||||||
),
|
),
|
||||||
migrations.AddField(
|
migrations.AddField(
|
||||||
model_name='suggestion',
|
model_name="suggestion",
|
||||||
name='duration_precisions',
|
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'),
|
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",
|
||||||
|
),
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
|
|
|
@ -1,12 +1,13 @@
|
||||||
|
from autoslug import AutoSlugField
|
||||||
|
from django.contrib.auth.models import User
|
||||||
|
from django.core.exceptions import ValidationError
|
||||||
|
from django.core.validators import MinValueValidator
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
from django.core.validators import MinValueValidator
|
|
||||||
from django.core.exceptions import ValidationError
|
|
||||||
from autoslug import AutoSlugField
|
|
||||||
from website.validators import MaxFileSizeValidator
|
|
||||||
from django.contrib.auth.models import User
|
|
||||||
from inventory.models import Category, Tag
|
|
||||||
from comments.models import AbstractComment
|
from comments.models import AbstractComment
|
||||||
|
from inventory.models import Category, Tag
|
||||||
|
from website.validators import MaxFileSizeValidator
|
||||||
|
|
||||||
|
|
||||||
class Suggestion(models.Model):
|
class Suggestion(models.Model):
|
||||||
|
|
|
@ -1,3 +1 @@
|
||||||
from django.test import TestCase
|
|
||||||
|
|
||||||
# Create your tests here.
|
# Create your tests here.
|
||||||
|
|
|
@ -1,12 +1,13 @@
|
||||||
from django.urls import path
|
from django.urls import path
|
||||||
|
|
||||||
from .views import (
|
from .views import (
|
||||||
SuggestionListView,
|
AddSuggestionCommentView,
|
||||||
AddSuggestionView,
|
AddSuggestionView,
|
||||||
|
DownvoteSuggestionView,
|
||||||
|
ModifySuggestionCommentView,
|
||||||
|
SuggestionListView,
|
||||||
SuggestionView,
|
SuggestionView,
|
||||||
UpvoteSuggestionView,
|
UpvoteSuggestionView,
|
||||||
DownvoteSuggestionView,
|
|
||||||
AddSuggestionCommentView,
|
|
||||||
ModifySuggestionCommentView,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
app_name = "suggestions"
|
app_name = "suggestions"
|
||||||
|
|
|
@ -1,12 +1,14 @@
|
||||||
from django.views.generic import ListView, DetailView, FormView, RedirectView
|
|
||||||
from django.views.generic.detail import SingleObjectMixin
|
|
||||||
from django.contrib.auth.mixins import LoginRequiredMixin
|
|
||||||
from django.contrib import messages
|
from django.contrib import messages
|
||||||
from django.shortcuts import redirect
|
from django.contrib.auth.mixins import LoginRequiredMixin
|
||||||
from django.db.models import Count
|
from django.db.models import Count
|
||||||
|
from django.shortcuts import redirect
|
||||||
|
from django.views.generic import DetailView, FormView, ListView, RedirectView
|
||||||
|
from django.views.generic.detail import SingleObjectMixin
|
||||||
|
|
||||||
from comments.views import AddCommentView, ModifyCommentView
|
from comments.views import AddCommentView, ModifyCommentView
|
||||||
from .models import Suggestion, SuggestionComment
|
|
||||||
from .forms import AddSuggestionForm
|
from .forms import AddSuggestionForm
|
||||||
|
from .models import Suggestion, SuggestionComment
|
||||||
|
|
||||||
|
|
||||||
class SuggestionListView(ListView):
|
class SuggestionListView(ListView):
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
from django.contrib import admin
|
from django.contrib import admin
|
||||||
from .models import MarkdownPage
|
|
||||||
from markdownx.admin import MarkdownxModelAdmin
|
from markdownx.admin import MarkdownxModelAdmin
|
||||||
|
|
||||||
|
from .models import MarkdownPage
|
||||||
|
|
||||||
admin.site.register(MarkdownPage, MarkdownxModelAdmin)
|
admin.site.register(MarkdownPage, MarkdownxModelAdmin)
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import markdown
|
|
||||||
import re
|
import re
|
||||||
|
|
||||||
|
import markdown
|
||||||
|
|
||||||
|
|
||||||
class NbspPreprocessor(markdown.preprocessors.Preprocessor):
|
class NbspPreprocessor(markdown.preprocessors.Preprocessor):
|
||||||
"""Replace regular spaces with non-breaking spaces within a text around relevant
|
"""Replace regular spaces with non-breaking spaces within a text around relevant
|
||||||
|
|
|
@ -1,28 +1,43 @@
|
||||||
# Generated by Django 3.1.2 on 2020-12-27 11:34
|
# Generated by Django 3.1.2 on 2020-12-27 11:34
|
||||||
|
|
||||||
from django.db import migrations, models
|
|
||||||
import markdownx.models
|
import markdownx.models
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
initial = True
|
initial = True
|
||||||
|
|
||||||
dependencies = [
|
dependencies = []
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
operations = [
|
||||||
migrations.CreateModel(
|
migrations.CreateModel(
|
||||||
name='MarkdownPage',
|
name="MarkdownPage",
|
||||||
fields=[
|
fields=[
|
||||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
(
|
||||||
('slug', models.SlugField(blank=True, help_text="Identifiant de la page qui se voit dans l'URL. Ne doit pas collisionner avec une page existante. Laisser vide pour la page d'accueil, requis sinon.", unique=True, verbose_name='Adresse de la page')),
|
"id",
|
||||||
('content', markdownx.models.MarkdownxField(verbose_name='Contenu')),
|
models.AutoField(
|
||||||
|
auto_created=True,
|
||||||
|
primary_key=True,
|
||||||
|
serialize=False,
|
||||||
|
verbose_name="ID",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"slug",
|
||||||
|
models.SlugField(
|
||||||
|
blank=True,
|
||||||
|
help_text="Identifiant de la page qui se voit dans l'URL. Ne doit pas collisionner avec une page existante. Laisser vide pour la page d'accueil, requis sinon.",
|
||||||
|
unique=True,
|
||||||
|
verbose_name="Adresse de la page",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
("content", markdownx.models.MarkdownxField(verbose_name="Contenu")),
|
||||||
],
|
],
|
||||||
options={
|
options={
|
||||||
'verbose_name': 'page Markdown',
|
"verbose_name": "page Markdown",
|
||||||
'verbose_name_plural': 'pages Markdown',
|
"verbose_name_plural": "pages Markdown",
|
||||||
'ordering': ['slug'],
|
"ordering": ["slug"],
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
|
|
|
@ -1,3 +1 @@
|
||||||
from django.test import TestCase
|
|
||||||
|
|
||||||
# Create your tests here.
|
# Create your tests here.
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
from django.urls import path
|
from django.urls import path
|
||||||
|
|
||||||
from .views import MarkdownPageView
|
from .views import MarkdownPageView
|
||||||
|
|
||||||
app_name = "website"
|
app_name = "website"
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
from django.views.generic import DetailView
|
|
||||||
from django.utils.safestring import mark_safe
|
from django.utils.safestring import mark_safe
|
||||||
|
from django.views.generic import DetailView
|
||||||
from markdownx.utils import markdownify
|
from markdownx.utils import markdownify
|
||||||
|
|
||||||
from .models import MarkdownPage
|
from .models import MarkdownPage
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue