Run pre-commit

This commit is contained in:
Gabriel Augendre 2021-07-10 12:11:58 +02:00
parent 64c58260d9
commit 18325aa59e
59 changed files with 1227 additions and 747 deletions

View File

@ -19,4 +19,3 @@ git push heroku master
You may need to upgrade Python since Heroku tends to deprecate old patch versions rather quickly.
In this case, edit `runtime.txt`.

View File

@ -5,11 +5,11 @@ import requests
def main():
port = os.getenv('PORT', 8000)
res = requests.get(f'http://127.0.0.1:{port}/')
port = os.getenv("PORT", 8000)
res = requests.get(f"http://127.0.0.1:{port}/")
if res.status_code >= 400:
sys.exit(1)
if __name__ == '__main__':
if __name__ == "__main__":
main()

View File

@ -1,57 +1,78 @@
from django.contrib import admin, messages
from django.db.models import Prefetch
from import_export import resources, fields
from import_export import fields, resources
from import_export.admin import ExportMixin
from import_export.widgets import IntegerWidget, DecimalWidget
from import_export.widgets import DecimalWidget, IntegerWidget
from manuels.models import Teacher, Book, Level, Editor, SuppliesRequirement, CommonSupply, ISBNError
from manuels.models import (
Book,
CommonSupply,
Editor,
ISBNError,
Level,
SuppliesRequirement,
Teacher,
)
class TeacherResource(resources.ModelResource):
class Meta:
model = Teacher
fields = ('first_name', 'last_name', 'email', 'phone_number')
fields = ("first_name", "last_name", "email", "phone_number")
@admin.register(Teacher)
class TeacherAdmin(ExportMixin, admin.ModelAdmin):
resource_class = TeacherResource
list_display = ['full_name', 'email', 'phone_number', 'has_confirmed_list']
list_display = ["full_name", "email", "phone_number", "has_confirmed_list"]
def send_link(self, request, queryset):
for teacher in queryset:
teacher.send_link(request)
messages.success(request, f'Le lien a bien été envoyé aux {queryset.count()} coordonateur(s) sélectionné(s).')
messages.success(
request,
f"Le lien a bien été envoyé aux {queryset.count()} coordonateur(s) sélectionné(s).",
)
send_link.short_description = 'Envoyer le lien'
send_link.short_description = "Envoyer le lien"
actions = [send_link]
class LevelResource(resources.ModelResource):
non_acquired_book_count = fields.Field(attribute='non_acquired_book_count', widget=IntegerWidget())
non_acquired_book_price = fields.Field(attribute='non_acquired_book_price', widget=DecimalWidget())
non_acquired_consumable_count = fields.Field(attribute='non_acquired_consumable_count', widget=IntegerWidget())
non_acquired_consumable_price = fields.Field(attribute='non_acquired_consumable_price', widget=DecimalWidget())
non_acquired_total_price = fields.Field(attribute='non_acquired_total_price', widget=DecimalWidget())
non_acquired_book_count = fields.Field(
attribute="non_acquired_book_count", widget=IntegerWidget()
)
non_acquired_book_price = fields.Field(
attribute="non_acquired_book_price", widget=DecimalWidget()
)
non_acquired_consumable_count = fields.Field(
attribute="non_acquired_consumable_count", widget=IntegerWidget()
)
non_acquired_consumable_price = fields.Field(
attribute="non_acquired_consumable_price", widget=DecimalWidget()
)
non_acquired_total_price = fields.Field(
attribute="non_acquired_total_price", widget=DecimalWidget()
)
class Meta:
model = Level
fields = (
'name',
'non_acquired_book_count',
'non_acquired_book_price',
'non_acquired_consumable_count',
'non_acquired_consumable_price',
'non_acquired_total_price',
"name",
"non_acquired_book_count",
"non_acquired_book_price",
"non_acquired_consumable_count",
"non_acquired_consumable_price",
"non_acquired_total_price",
)
export_order = (
'name',
'non_acquired_book_count',
'non_acquired_book_price',
'non_acquired_consumable_count',
'non_acquired_consumable_price',
'non_acquired_total_price',
"name",
"non_acquired_book_count",
"non_acquired_book_price",
"non_acquired_consumable_count",
"non_acquired_consumable_price",
"non_acquired_total_price",
)
@ -59,87 +80,160 @@ class LevelResource(resources.ModelResource):
class LevelAdmin(ExportMixin, admin.ModelAdmin):
resource_class = LevelResource
list_display = [
'name',
'order',
'non_acquired_book_count',
'non_acquired_book_price',
'non_acquired_consumable_count',
'non_acquired_consumable_price',
'non_acquired_total_price',
"name",
"order",
"non_acquired_book_count",
"non_acquired_book_price",
"non_acquired_consumable_count",
"non_acquired_consumable_price",
"non_acquired_total_price",
]
list_editable = ['order']
list_display_links = ['name']
list_editable = ["order"]
list_display_links = ["name"]
def get_queryset(self, request):
return super(LevelAdmin, self).get_queryset(request).prefetch_related(
Prefetch("book_set", to_attr="prefetched_books"))
return (
super(LevelAdmin, self)
.get_queryset(request)
.prefetch_related(Prefetch("book_set", to_attr="prefetched_books"))
)
def non_acquired_book_count(self, obj: Level):
return obj.non_acquired_book_count
non_acquired_book_count.short_description = 'Nombre de livres à acheter (hors consommables)'
non_acquired_book_count.short_description = (
"Nombre de livres à acheter (hors consommables)"
)
def non_acquired_book_price(self, obj: Level):
return f'{obj.non_acquired_book_price:.2f}'
return f"{obj.non_acquired_book_price:.2f}"
non_acquired_book_price.short_description = 'Coût des livres à acheter (hors consommables)'
non_acquired_book_price.short_description = (
"Coût des livres à acheter (hors consommables)"
)
def non_acquired_consumable_count(self, obj: Level):
return obj.non_acquired_consumable_count
non_acquired_consumable_count.short_description = 'Nombre de consommables à acheter'
non_acquired_consumable_count.short_description = "Nombre de consommables à acheter"
def non_acquired_consumable_price(self, obj: Level):
return f'{obj.non_acquired_consumable_price:.2f}'
return f"{obj.non_acquired_consumable_price:.2f}"
non_acquired_consumable_price.short_description = 'Coût des consommables à acheter'
non_acquired_consumable_price.short_description = "Coût des consommables à acheter"
def non_acquired_total_price(self, obj: Level):
return f'{obj.non_acquired_total_price:.2f}'
return f"{obj.non_acquired_total_price:.2f}"
non_acquired_total_price.short_description = 'Coût total à acheter'
non_acquired_total_price.short_description = "Coût total à acheter"
class BookResource(resources.ModelResource):
decitre_url = fields.Field(attribute='decitre_url')
decitre_url = fields.Field(attribute="decitre_url")
class Meta:
model = Book
fields = ('title', 'authors', 'editor__name', 'publication_year', 'isbn', 'comments', 'other_editor',
'price', 'previously_acquired', 'teacher__first_name', 'teacher__last_name', 'level__name', 'field',
'consumable', "decitre_url")
export_order = ('level__name', 'field', 'title', 'authors', 'editor__name', 'publication_year', 'isbn', 'price',
'other_editor', 'previously_acquired', 'teacher__first_name', 'teacher__last_name', 'comments',
'consumable', "decitre_url")
fields = (
"title",
"authors",
"editor__name",
"publication_year",
"isbn",
"comments",
"other_editor",
"price",
"previously_acquired",
"teacher__first_name",
"teacher__last_name",
"level__name",
"field",
"consumable",
"decitre_url",
)
export_order = (
"level__name",
"field",
"title",
"authors",
"editor__name",
"publication_year",
"isbn",
"price",
"other_editor",
"previously_acquired",
"teacher__first_name",
"teacher__last_name",
"comments",
"consumable",
"decitre_url",
)
@admin.register(Book)
class BookAdmin(ExportMixin, admin.ModelAdmin):
resource_class = BookResource
list_display = ['level', 'field', 'title', 'authors', 'editor', 'other_editor', 'publication_year', 'isbn',
'price', 'previously_acquired', 'teacher', 'done', 'consumable']
list_editable = ['done']
list_filter = ['done', 'previously_acquired', 'consumable', 'level', 'editor', 'teacher']
list_display_links = ['title']
fieldsets = [
('Infos livre', {
'fields': ('title', 'consumable', 'authors', ('editor', 'other_editor'), 'publication_year',
'isbn', 'created_at', 'updated_at', 'comments')
}),
('Élève', {
'fields': ('price', 'previously_acquired',)
}),
('Coordonnateur', {
'fields': ('teacher', 'level', 'field')
}),
('Gestion', {
'fields': ('done',)
}),
list_display = [
"level",
"field",
"title",
"authors",
"editor",
"other_editor",
"publication_year",
"isbn",
"price",
"previously_acquired",
"teacher",
"done",
"consumable",
]
readonly_fields = ['created_at', 'updated_at']
list_editable = ["done"]
list_filter = [
"done",
"previously_acquired",
"consumable",
"level",
"editor",
"teacher",
]
list_display_links = ["title"]
fieldsets = [
(
"Infos livre",
{
"fields": (
"title",
"consumable",
"authors",
("editor", "other_editor"),
"publication_year",
"isbn",
"created_at",
"updated_at",
"comments",
)
},
),
(
"Élève",
{
"fields": (
"price",
"previously_acquired",
)
},
),
("Coordonnateur", {"fields": ("teacher", "level", "field")}),
("Gestion", {"fields": ("done",)}),
]
readonly_fields = ["created_at", "updated_at"]
def get_queryset(self, request):
return super(BookAdmin, self).get_queryset(request).select_related("editor", "level", "teacher")
return (
super(BookAdmin, self)
.get_queryset(request)
.select_related("editor", "level", "teacher")
)
def update_with_decitre(self, request, queryset):
for book in queryset:
@ -147,15 +241,15 @@ class BookAdmin(ExportMixin, admin.ModelAdmin):
book.update_from_decitre()
messages.success(
request,
f'Mise à jour réussie du livre "{book.title}" ({book.level.name} - {book.field})'
f'Mise à jour réussie du livre "{book.title}" ({book.level.name} - {book.field})',
)
except ISBNError as e:
messages.warning(
request,
f'Erreur lors de la mise à jour du livre "{book.title}" ({book.level.name} - {book.field}) : {e.data.get("error")}'
f'Erreur lors de la mise à jour du livre "{book.title}" ({book.level.name} - {book.field}) : {e.data.get("error")}',
)
update_with_decitre.short_description = 'Mettre à jour avec Decitre'
update_with_decitre.short_description = "Mettre à jour avec Decitre"
def mark_as_done(self, request, queryset):
queryset.update(done=True)
@ -171,25 +265,41 @@ class EditorAdmin(admin.ModelAdmin):
@admin.register(CommonSupply)
class CommonSupplyAdmin(admin.ModelAdmin):
list_display = ['name', 'order']
list_display_links = ['name']
list_editable = ['order']
list_display = ["name", "order"]
list_display_links = ["name"]
list_editable = ["order"]
class SuppliesResource(resources.ModelResource):
class Meta:
model = SuppliesRequirement
fields = ('supplies', 'field', 'level__name', 'teacher__first_name', 'teacher__last_name')
export_order = ('level__name', 'field', 'supplies', 'teacher__first_name', 'teacher__last_name')
fields = (
"supplies",
"field",
"level__name",
"teacher__first_name",
"teacher__last_name",
)
export_order = (
"level__name",
"field",
"supplies",
"teacher__first_name",
"teacher__last_name",
)
@admin.register(SuppliesRequirement)
class SuppliesRequirementAdmin(ExportMixin, admin.ModelAdmin):
resource_class = SuppliesResource
list_display = ['id', 'teacher', 'level', 'field', 'supplies', 'done']
list_editable = ['done']
readonly_fields = ['created_at', 'updated_at']
list_filter = ['done', 'teacher', 'level']
list_display = ["id", "teacher", "level", "field", "supplies", "done"]
list_editable = ["done"]
readonly_fields = ["created_at", "updated_at"]
list_filter = ["done", "teacher", "level"]
def get_queryset(self, request):
return super(SuppliesRequirementAdmin, self).get_queryset(request).select_related("level", "teacher")
return (
super(SuppliesRequirementAdmin, self)
.get_queryset(request)
.select_related("level", "teacher")
)

View File

@ -2,4 +2,4 @@ from django.apps import AppConfig
class ManuelsConfig(AppConfig):
name = 'manuels'
name = "manuels"

View File

@ -2,4 +2,4 @@ from django.conf import settings
def authorized_mails(request):
return {'authorized_mails': settings.AUTHORIZED_EMAILS}
return {"authorized_mails": settings.AUTHORIZED_EMAILS}

View File

@ -1,93 +1,136 @@
from django import forms
from django.core.exceptions import ValidationError
from manuels.models import Book, SuppliesRequirement, Level
from manuels.models import Book, Level, SuppliesRequirement
class EditBookForm(forms.ModelForm):
class Meta:
model = Book
fields = ['teacher', 'level', 'field', 'no_book', 'see_later', 'title', 'authors', 'editor', 'other_editor',
'publication_year', 'isbn', 'price', 'previously_acquired', 'comments', 'consumable']
fields = [
"teacher",
"level",
"field",
"no_book",
"see_later",
"title",
"authors",
"editor",
"other_editor",
"publication_year",
"isbn",
"price",
"previously_acquired",
"comments",
"consumable",
]
no_book = forms.BooleanField(label='Pas de livre pour cette classe/matière', required=False, initial=False)
no_book = forms.BooleanField(
label="Pas de livre pour cette classe/matière", required=False, initial=False
)
see_later = forms.BooleanField(
label='Voir à la rentrée', help_text="Notamment en cas de désaccord sur l'adoption ou non d'un manuel",
required=False, initial=False
label="Voir à la rentrée",
help_text="Notamment en cas de désaccord sur l'adoption ou non d'un manuel",
required=False,
initial=False,
)
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields['title'].widget = forms.TextInput()
self.fields['authors'].widget = forms.TextInput()
self.fields['comments'].widget.attrs.update(rows=3)
self.fields['teacher'].widget.attrs.update({'class': 'custom-select'})
self.fields['editor'].widget.attrs.update({'class': 'custom-select'})
self.fields['previously_acquired'].widget.attrs.update({'class': 'custom-select'})
self.fields['consumable'].widget.attrs.update({'class': 'custom-select'})
if 'level' in self.fields:
self.fields['level'].widget.attrs.update({'class': 'custom-select'})
self.fields["title"].widget = forms.TextInput()
self.fields["authors"].widget = forms.TextInput()
self.fields["comments"].widget.attrs.update(rows=3)
self.fields["teacher"].widget.attrs.update({"class": "custom-select"})
self.fields["editor"].widget.attrs.update({"class": "custom-select"})
self.fields["previously_acquired"].widget.attrs.update(
{"class": "custom-select"}
)
self.fields["consumable"].widget.attrs.update({"class": "custom-select"})
if "level" in self.fields:
self.fields["level"].widget.attrs.update({"class": "custom-select"})
def clean(self):
editor = self.cleaned_data['editor']
other_editor = self.cleaned_data['other_editor']
title = self.cleaned_data['title']
editor = self.cleaned_data["editor"]
other_editor = self.cleaned_data["other_editor"]
title = self.cleaned_data["title"]
if (editor
and 'autre' in editor.name.lower()
if (
editor
and "autre" in editor.name.lower()
and not other_editor
and title not in ['PAS DE LIVRE POUR CETTE CLASSE', 'VOIR À LA RENTRÉE']):
and title not in ["PAS DE LIVRE POUR CETTE CLASSE", "VOIR À LA RENTRÉE"]
):
self.add_error(
'other_editor',
"other_editor",
ValidationError(
"Vous devez préciser l'éditeur si vous n'en choisissez pas un parmi la liste.",
code='missing'
)
code="missing",
),
)
def clean_previously_acquired(self):
data = self.cleaned_data['previously_acquired']
if data is None or data == '':
raise ValidationError('Vous devez choisir une valeur')
data = self.cleaned_data["previously_acquired"]
if data is None or data == "":
raise ValidationError("Vous devez choisir une valeur")
return data
class AddBookForm(EditBookForm):
class Meta(EditBookForm.Meta):
fields = ['teacher', 'levels', 'field', 'no_book', 'see_later', 'title', 'authors', 'editor', 'other_editor',
'publication_year', 'isbn', 'price', 'previously_acquired', 'comments', 'add_another', 'consumable']
fields = [
"teacher",
"levels",
"field",
"no_book",
"see_later",
"title",
"authors",
"editor",
"other_editor",
"publication_year",
"isbn",
"price",
"previously_acquired",
"comments",
"add_another",
"consumable",
]
add_another = forms.BooleanField(label='Ajouter un autre livre', required=False, initial=True)
add_another = forms.BooleanField(
label="Ajouter un autre livre", required=False, initial=True
)
levels = forms.ModelMultipleChoiceField(
queryset=Level.objects.all(),
label='Classes',
label="Classes",
required=True,
help_text='Maintenez la touche Ctrl (ou Cmd) enfoncée pour en sélectionner plusieurs.'
help_text="Maintenez la touche Ctrl (ou Cmd) enfoncée pour en sélectionner plusieurs.",
)
class EditSuppliesForm(forms.ModelForm):
class Meta:
model = SuppliesRequirement
fields = ['teacher', 'level', 'field', 'supplies']
fields = ["teacher", "level", "field", "supplies"]
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields['supplies'].widget.attrs.update(rows=3)
self.fields['teacher'].widget.attrs.update({'class': 'custom-select'})
if 'level' in self.fields:
self.fields['level'].widget.attrs.update({'class': 'custom-select'})
self.fields["supplies"].widget.attrs.update(rows=3)
self.fields["teacher"].widget.attrs.update({"class": "custom-select"})
if "level" in self.fields:
self.fields["level"].widget.attrs.update({"class": "custom-select"})
class AddSuppliesForm(EditSuppliesForm):
class Meta(EditSuppliesForm.Meta):
fields = ['teacher', 'levels', 'field', 'supplies']
fields = ["teacher", "levels", "field", "supplies"]
add_another = forms.BooleanField(label="Ajouter d'autres fournitures", required=False, initial=True)
add_another = forms.BooleanField(
label="Ajouter d'autres fournitures", required=False, initial=True
)
levels = forms.ModelMultipleChoiceField(
queryset=Level.objects.all(),
label='Classes',
label="Classes",
required=True,
help_text='Maintenez la touche Ctrl (ou Cmd) enfoncée pour en sélectionner plusieurs.'
help_text="Maintenez la touche Ctrl (ou Cmd) enfoncée pour en sélectionner plusieurs.",
)

View File

@ -3,7 +3,7 @@ from django.core.management import BaseCommand
class Command(BaseCommand):
help = 'Clears django cache'
help = "Clears django cache"
def handle(self, *args, **options):
cache.clear()

View File

@ -1,71 +1,142 @@
# Generated by Django 2.0.5 on 2018-05-21 22:47
from django.db import migrations, models
import django.db.models.deletion
import manuels.models
import uuid
import django.db.models.deletion
from django.db import migrations, models
import manuels.models
class Migration(migrations.Migration):
initial = True
dependencies = [
]
dependencies = []
operations = [
migrations.CreateModel(
name='Book',
name="Book",
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('field', models.CharField(max_length=100, verbose_name='matière')),
('title', models.TextField(verbose_name='titre')),
('authors', models.TextField(verbose_name='auteurs')),
('editor', models.CharField(max_length=200, verbose_name='éditeur')),
('collection', models.CharField(blank=True, max_length=200, verbose_name='collection')),
('publication_year', models.PositiveIntegerField(verbose_name='année de publication')),
('isbn', models.CharField(max_length=20, validators=[manuels.models.isbn_validator], verbose_name='ISBN/EAN')),
('price', models.PositiveIntegerField(verbose_name='prix')),
('previously_acquired', models.BooleanField(choices=[(True, 'Oui'), (False, 'Non')], verbose_name="manuel acquis précédemment par l'élève")),
(
"id",
models.AutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
("field", models.CharField(max_length=100, verbose_name="matière")),
("title", models.TextField(verbose_name="titre")),
("authors", models.TextField(verbose_name="auteurs")),
("editor", models.CharField(max_length=200, verbose_name="éditeur")),
(
"collection",
models.CharField(
blank=True, max_length=200, verbose_name="collection"
),
),
(
"publication_year",
models.PositiveIntegerField(verbose_name="année de publication"),
),
(
"isbn",
models.CharField(
max_length=20,
validators=[manuels.models.isbn_validator],
verbose_name="ISBN/EAN",
),
),
("price", models.PositiveIntegerField(verbose_name="prix")),
(
"previously_acquired",
models.BooleanField(
choices=[(True, "Oui"), (False, "Non")],
verbose_name="manuel acquis précédemment par l'élève",
),
),
],
options={
'verbose_name': 'livre',
'verbose_name_plural': 'livres',
"verbose_name": "livre",
"verbose_name_plural": "livres",
},
),
migrations.CreateModel(
name='Level',
name="Level",
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=10, verbose_name='nom')),
(
"id",
models.AutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
("name", models.CharField(max_length=10, verbose_name="nom")),
],
options={
'verbose_name': 'classe',
'verbose_name_plural': 'classes',
"verbose_name": "classe",
"verbose_name_plural": "classes",
},
),
migrations.CreateModel(
name='Teacher',
name="Teacher",
fields=[
('uuid', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)),
('first_name', models.CharField(max_length=100, verbose_name='prénom')),
('last_name', models.CharField(max_length=100, verbose_name='nom')),
('phone_number', models.CharField(help_text="En cas d'urgence", max_length=10, verbose_name='numéro de téléphone')),
('email', models.EmailField(help_text='Utilisée pour vous transmettre votre lien personnel', max_length=254, unique=True, verbose_name='adresse email')),
(
"uuid",
models.UUIDField(
default=uuid.uuid4,
editable=False,
primary_key=True,
serialize=False,
),
),
("first_name", models.CharField(max_length=100, verbose_name="prénom")),
("last_name", models.CharField(max_length=100, verbose_name="nom")),
(
"phone_number",
models.CharField(
help_text="En cas d'urgence",
max_length=10,
verbose_name="numéro de téléphone",
),
),
(
"email",
models.EmailField(
help_text="Utilisée pour vous transmettre votre lien personnel",
max_length=254,
unique=True,
verbose_name="adresse email",
),
),
],
options={
'verbose_name': 'enseignant',
'verbose_name_plural': 'enseignants',
"verbose_name": "enseignant",
"verbose_name_plural": "enseignants",
},
),
migrations.AddField(
model_name='book',
name='level',
field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to='manuels.Level', verbose_name='classe'),
model_name="book",
name="level",
field=models.ForeignKey(
null=True,
on_delete=django.db.models.deletion.SET_NULL,
to="manuels.Level",
verbose_name="classe",
),
),
migrations.AddField(
model_name='book',
name='teacher',
field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to='manuels.Teacher', verbose_name='enseignant'),
model_name="book",
name="teacher",
field=models.ForeignKey(
null=True,
on_delete=django.db.models.deletion.SET_NULL,
to="manuels.Teacher",
verbose_name="enseignant",
),
),
]

View File

@ -1,30 +1,43 @@
# Generated by Django 2.0.5 on 2018-05-21 23:32
from django.db import migrations, models
import django.db.models.deletion
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('manuels', '0001_initial'),
("manuels", "0001_initial"),
]
operations = [
migrations.CreateModel(
name='Editor',
name="Editor",
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=50, verbose_name='nom')),
(
"id",
models.AutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
("name", models.CharField(max_length=50, verbose_name="nom")),
],
options={
'verbose_name': 'éditeur',
'verbose_name_plural': 'éditeurs',
"verbose_name": "éditeur",
"verbose_name_plural": "éditeurs",
},
),
migrations.AlterField(
model_name='book',
name='editor',
field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to='manuels.Editor', verbose_name='éditeur'),
model_name="book",
name="editor",
field=models.ForeignKey(
null=True,
on_delete=django.db.models.deletion.SET_NULL,
to="manuels.Editor",
verbose_name="éditeur",
),
),
]

View File

@ -1,58 +1,66 @@
# Generated by Django 2.0.5 on 2018-05-21 23:45
from django.db import migrations, models
import django.utils.timezone
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('manuels', '0002_auto_20180522_0132'),
("manuels", "0002_auto_20180522_0132"),
]
operations = [
migrations.AddField(
model_name='book',
name='created_at',
field=models.DateTimeField(auto_now_add=True, default=django.utils.timezone.now),
model_name="book",
name="created_at",
field=models.DateTimeField(
auto_now_add=True, default=django.utils.timezone.now
),
preserve_default=False,
),
migrations.AddField(
model_name='book',
name='updated_at',
model_name="book",
name="updated_at",
field=models.DateTimeField(auto_now=True),
),
migrations.AddField(
model_name='editor',
name='created_at',
field=models.DateTimeField(auto_now_add=True, default=django.utils.timezone.now),
model_name="editor",
name="created_at",
field=models.DateTimeField(
auto_now_add=True, default=django.utils.timezone.now
),
preserve_default=False,
),
migrations.AddField(
model_name='editor',
name='updated_at',
model_name="editor",
name="updated_at",
field=models.DateTimeField(auto_now=True),
),
migrations.AddField(
model_name='level',
name='created_at',
field=models.DateTimeField(auto_now_add=True, default=django.utils.timezone.now),
model_name="level",
name="created_at",
field=models.DateTimeField(
auto_now_add=True, default=django.utils.timezone.now
),
preserve_default=False,
),
migrations.AddField(
model_name='level',
name='updated_at',
model_name="level",
name="updated_at",
field=models.DateTimeField(auto_now=True),
),
migrations.AddField(
model_name='teacher',
name='created_at',
field=models.DateTimeField(auto_now_add=True, default=django.utils.timezone.now),
model_name="teacher",
name="created_at",
field=models.DateTimeField(
auto_now_add=True, default=django.utils.timezone.now
),
preserve_default=False,
),
migrations.AddField(
model_name='teacher',
name='updated_at',
model_name="teacher",
name="updated_at",
field=models.DateTimeField(auto_now=True),
),
]

View File

@ -6,48 +6,48 @@ from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('manuels', '0003_auto_20180522_0145'),
("manuels", "0003_auto_20180522_0145"),
]
operations = [
migrations.AlterField(
model_name='book',
name='created_at',
field=models.DateTimeField(auto_now_add=True, verbose_name='créé le'),
model_name="book",
name="created_at",
field=models.DateTimeField(auto_now_add=True, verbose_name="créé le"),
),
migrations.AlterField(
model_name='book',
name='updated_at',
field=models.DateTimeField(auto_now=True, verbose_name='mis à jour le'),
model_name="book",
name="updated_at",
field=models.DateTimeField(auto_now=True, verbose_name="mis à jour le"),
),
migrations.AlterField(
model_name='editor',
name='created_at',
field=models.DateTimeField(auto_now_add=True, verbose_name='créé le'),
model_name="editor",
name="created_at",
field=models.DateTimeField(auto_now_add=True, verbose_name="créé le"),
),
migrations.AlterField(
model_name='editor',
name='updated_at',
field=models.DateTimeField(auto_now=True, verbose_name='mis à jour le'),
model_name="editor",
name="updated_at",
field=models.DateTimeField(auto_now=True, verbose_name="mis à jour le"),
),
migrations.AlterField(
model_name='level',
name='created_at',
field=models.DateTimeField(auto_now_add=True, verbose_name='créé le'),
model_name="level",
name="created_at",
field=models.DateTimeField(auto_now_add=True, verbose_name="créé le"),
),
migrations.AlterField(
model_name='level',
name='updated_at',
field=models.DateTimeField(auto_now=True, verbose_name='mis à jour le'),
model_name="level",
name="updated_at",
field=models.DateTimeField(auto_now=True, verbose_name="mis à jour le"),
),
migrations.AlterField(
model_name='teacher',
name='created_at',
field=models.DateTimeField(auto_now_add=True, verbose_name='créé le'),
model_name="teacher",
name="created_at",
field=models.DateTimeField(auto_now_add=True, verbose_name="créé le"),
),
migrations.AlterField(
model_name='teacher',
name='updated_at',
field=models.DateTimeField(auto_now=True, verbose_name='mis à jour le'),
model_name="teacher",
name="updated_at",
field=models.DateTimeField(auto_now=True, verbose_name="mis à jour le"),
),
]

View File

@ -1,29 +1,59 @@
# Generated by Django 2.0.5 on 2018-05-22 07:34
from django.db import migrations, models
import django.db.models.deletion
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('manuels', '0004_auto_20180522_0148'),
("manuels", "0004_auto_20180522_0148"),
]
operations = [
migrations.CreateModel(
name='SuppliesRequirement',
name="SuppliesRequirement",
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('created_at', models.DateTimeField(auto_now_add=True, verbose_name='créé le')),
('updated_at', models.DateTimeField(auto_now=True, verbose_name='mis à jour le')),
('supplies', models.TextField(verbose_name='fournitures')),
('level', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to='manuels.Level', verbose_name='classe')),
('teacher', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to='manuels.Teacher', verbose_name='enseignant')),
(
"id",
models.AutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
(
"created_at",
models.DateTimeField(auto_now_add=True, verbose_name="créé le"),
),
(
"updated_at",
models.DateTimeField(auto_now=True, verbose_name="mis à jour le"),
),
("supplies", models.TextField(verbose_name="fournitures")),
(
"level",
models.ForeignKey(
null=True,
on_delete=django.db.models.deletion.SET_NULL,
to="manuels.Level",
verbose_name="classe",
),
),
(
"teacher",
models.ForeignKey(
null=True,
on_delete=django.db.models.deletion.SET_NULL,
to="manuels.Teacher",
verbose_name="enseignant",
),
),
],
options={
'verbose_name': 'demande de fournitures',
'verbose_name_plural': 'demandes de fournitures',
"verbose_name": "demande de fournitures",
"verbose_name_plural": "demandes de fournitures",
},
),
]

View File

@ -6,13 +6,17 @@ from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('manuels', '0005_suppliesrequirement'),
("manuels", "0005_suppliesrequirement"),
]
operations = [
migrations.AlterField(
model_name='book',
name='previously_acquired',
field=models.BooleanField(choices=[(True, 'Oui'), (False, 'Non')], default=False, verbose_name="manuel acquis précédemment par l'élève"),
model_name="book",
name="previously_acquired",
field=models.BooleanField(
choices=[(True, "Oui"), (False, "Non")],
default=False,
verbose_name="manuel acquis précédemment par l'élève",
),
),
]

View File

@ -6,13 +6,17 @@ from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('manuels', '0006_auto_20180522_1009'),
("manuels", "0006_auto_20180522_1009"),
]
operations = [
migrations.AlterField(
model_name='book',
name='previously_acquired',
field=models.BooleanField(choices=[(False, 'Non'), (True, 'Oui')], default=False, verbose_name="manuel acquis précédemment par l'élève"),
model_name="book",
name="previously_acquired",
field=models.BooleanField(
choices=[(False, "Non"), (True, "Oui")],
default=False,
verbose_name="manuel acquis précédemment par l'élève",
),
),
]

View File

@ -1,39 +1,64 @@
# Generated by Django 2.0.5 on 2018-05-22 08:51
from django.db import migrations, models
import django.db.models.deletion
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('manuels', '0007_auto_20180522_1026'),
("manuels", "0007_auto_20180522_1026"),
]
operations = [
migrations.AlterField(
model_name='book',
name='editor',
field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.PROTECT, to='manuels.Editor', verbose_name='éditeur'),
model_name="book",
name="editor",
field=models.ForeignKey(
null=True,
on_delete=django.db.models.deletion.PROTECT,
to="manuels.Editor",
verbose_name="éditeur",
),
),
migrations.AlterField(
model_name='book',
name='level',
field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.PROTECT, to='manuels.Level', verbose_name='classe'),
model_name="book",
name="level",
field=models.ForeignKey(
null=True,
on_delete=django.db.models.deletion.PROTECT,
to="manuels.Level",
verbose_name="classe",
),
),
migrations.AlterField(
model_name='book',
name='teacher',
field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.PROTECT, to='manuels.Teacher', verbose_name='enseignant'),
model_name="book",
name="teacher",
field=models.ForeignKey(
null=True,
on_delete=django.db.models.deletion.PROTECT,
to="manuels.Teacher",
verbose_name="enseignant",
),
),
migrations.AlterField(
model_name='suppliesrequirement',
name='level',
field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.PROTECT, to='manuels.Level', verbose_name='classe'),
model_name="suppliesrequirement",
name="level",
field=models.ForeignKey(
null=True,
on_delete=django.db.models.deletion.PROTECT,
to="manuels.Level",
verbose_name="classe",
),
),
migrations.AlterField(
model_name='suppliesrequirement',
name='teacher',
field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.PROTECT, to='manuels.Teacher', verbose_name='enseignant'),
model_name="suppliesrequirement",
name="teacher",
field=models.ForeignKey(
null=True,
on_delete=django.db.models.deletion.PROTECT,
to="manuels.Teacher",
verbose_name="enseignant",
),
),
]

View File

@ -6,14 +6,14 @@ from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('manuels', '0008_auto_20180522_1051'),
("manuels", "0008_auto_20180522_1051"),
]
operations = [
migrations.AddField(
model_name='suppliesrequirement',
name='field',
field=models.CharField(default='', max_length=100, verbose_name='matière'),
model_name="suppliesrequirement",
name="field",
field=models.CharField(default="", max_length=100, verbose_name="matière"),
preserve_default=False,
),
]

View File

@ -6,12 +6,12 @@ from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('manuels', '0009_suppliesrequirement_field'),
("manuels", "0009_suppliesrequirement_field"),
]
operations = [
migrations.RemoveField(
model_name='book',
name='collection',
model_name="book",
name="collection",
),
]

View File

@ -1,19 +1,25 @@
# Generated by Django 2.0.5 on 2018-05-23 22:13
from django.db import migrations, models
import manuels.models
class Migration(migrations.Migration):
dependencies = [
('manuels', '0010_remove_book_collection'),
("manuels", "0010_remove_book_collection"),
]
operations = [
migrations.AlterField(
model_name='teacher',
name='phone_number',
field=models.CharField(help_text="En cas d'urgence, 10 chiffres.", max_length=10, validators=[manuels.models.phone_validator], verbose_name='numéro de téléphone'),
model_name="teacher",
name="phone_number",
field=models.CharField(
help_text="En cas d'urgence, 10 chiffres.",
max_length=10,
validators=[manuels.models.phone_validator],
verbose_name="numéro de téléphone",
),
),
]

View File

@ -1,19 +1,25 @@
# Generated by Django 2.0.5 on 2018-05-23 22:14
from django.db import migrations, models
import manuels.models
class Migration(migrations.Migration):
dependencies = [
('manuels', '0011_auto_20180524_0013'),
("manuels", "0011_auto_20180524_0013"),
]
operations = [
migrations.AlterField(
model_name='book',
name='isbn',
field=models.CharField(help_text='Format attendu : 10 ou 13 chiffres, éventuellement séparés par des tirets et éventuellement suivis de la lettre <code>X</code>', max_length=20, validators=[manuels.models.isbn_validator], verbose_name='ISBN/EAN'),
model_name="book",
name="isbn",
field=models.CharField(
help_text="Format attendu : 10 ou 13 chiffres, éventuellement séparés par des tirets et éventuellement suivis de la lettre <code>X</code>",
max_length=20,
validators=[manuels.models.isbn_validator],
verbose_name="ISBN/EAN",
),
),
]

View File

@ -6,28 +6,28 @@ from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('manuels', '0012_auto_20180524_0014'),
("manuels", "0012_auto_20180524_0014"),
]
operations = [
migrations.AlterField(
model_name='book',
name='field',
field=models.CharField(max_length=200, verbose_name='matière'),
model_name="book",
name="field",
field=models.CharField(max_length=200, verbose_name="matière"),
),
migrations.AlterField(
model_name='editor',
name='name',
field=models.CharField(max_length=100, verbose_name='nom'),
model_name="editor",
name="name",
field=models.CharField(max_length=100, verbose_name="nom"),
),
migrations.AlterField(
model_name='level',
name='name',
field=models.CharField(max_length=50, verbose_name='nom'),
model_name="level",
name="name",
field=models.CharField(max_length=50, verbose_name="nom"),
),
migrations.AlterField(
model_name='suppliesrequirement',
name='field',
field=models.CharField(max_length=200, verbose_name='matière'),
model_name="suppliesrequirement",
name="field",
field=models.CharField(max_length=200, verbose_name="matière"),
),
]

View File

@ -6,13 +6,13 @@ from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('manuels', '0013_auto_20180524_0016'),
("manuels", "0013_auto_20180524_0016"),
]
operations = [
migrations.RenameField(
model_name='suppliesrequirement',
old_name='field',
new_name='fields',
model_name="suppliesrequirement",
old_name="field",
new_name="fields",
),
]

View File

@ -6,13 +6,13 @@ from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('manuels', '0014_auto_20180524_0034'),
("manuels", "0014_auto_20180524_0034"),
]
operations = [
migrations.AlterField(
model_name='suppliesrequirement',
name='fields',
field=models.TextField(verbose_name='matières'),
model_name="suppliesrequirement",
name="fields",
field=models.TextField(verbose_name="matières"),
),
]

View File

@ -6,18 +6,18 @@ from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('manuels', '0015_auto_20180524_0035'),
("manuels", "0015_auto_20180524_0035"),
]
operations = [
migrations.AlterField(
model_name='book',
name='field',
field=models.CharField(max_length=200, verbose_name='discipline'),
model_name="book",
name="field",
field=models.CharField(max_length=200, verbose_name="discipline"),
),
migrations.AlterField(
model_name='suppliesrequirement',
name='fields',
field=models.TextField(verbose_name='disciplines'),
model_name="suppliesrequirement",
name="fields",
field=models.TextField(verbose_name="disciplines"),
),
]

View File

@ -6,18 +6,18 @@ from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('manuels', '0016_auto_20180530_1801'),
("manuels", "0016_auto_20180530_1801"),
]
operations = [
migrations.AddField(
model_name='book',
name='done',
field=models.BooleanField(default=False, verbose_name='Traité'),
model_name="book",
name="done",
field=models.BooleanField(default=False, verbose_name="Traité"),
),
migrations.AddField(
model_name='suppliesrequirement',
name='done',
field=models.BooleanField(default=False, verbose_name='Traité'),
model_name="suppliesrequirement",
name="done",
field=models.BooleanField(default=False, verbose_name="Traité"),
),
]

View File

@ -6,14 +6,18 @@ from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('manuels', '0017_auto_20180530_1804'),
("manuels", "0017_auto_20180530_1804"),
]
operations = [
migrations.AddField(
model_name='book',
name='comments',
field=models.TextField(default='', help_text="Ce message sera visible par la documentaliste. Vous pouvez l'utiliser par exemple si vous souhaitez saisir un éditeur qui n'est pas proposé.", verbose_name='commentaires'),
model_name="book",
name="comments",
field=models.TextField(
default="",
help_text="Ce message sera visible par la documentaliste. Vous pouvez l'utiliser par exemple si vous souhaitez saisir un éditeur qui n'est pas proposé.",
verbose_name="commentaires",
),
preserve_default=False,
),
]

View File

@ -6,13 +6,17 @@ from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('manuels', '0018_book_comments'),
("manuels", "0018_book_comments"),
]
operations = [
migrations.AlterField(
model_name='book',
name='comments',
field=models.TextField(blank=True, help_text="Ce message sera visible par la documentaliste. Vous pouvez l'utiliser par exemple si vous souhaitez saisir un éditeur qui n'est pas proposé.", verbose_name='commentaires'),
model_name="book",
name="comments",
field=models.TextField(
blank=True,
help_text="Ce message sera visible par la documentaliste. Vous pouvez l'utiliser par exemple si vous souhaitez saisir un éditeur qui n'est pas proposé.",
verbose_name="commentaires",
),
),
]

View File

@ -1,28 +1,41 @@
# Generated by Django 2.0.5 on 2018-06-02 14:30
from django.db import migrations, models
import django.db.models.deletion
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('manuels', '0019_auto_20180531_0815'),
("manuels", "0019_auto_20180531_0815"),
]
operations = [
migrations.AlterModelOptions(
name='teacher',
options={'verbose_name': 'coordonnateur', 'verbose_name_plural': 'coordonnateurs'},
name="teacher",
options={
"verbose_name": "coordonnateur",
"verbose_name_plural": "coordonnateurs",
},
),
migrations.AlterField(
model_name='book',
name='teacher',
field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.PROTECT, to='manuels.Teacher', verbose_name='coordonnateur'),
model_name="book",
name="teacher",
field=models.ForeignKey(
null=True,
on_delete=django.db.models.deletion.PROTECT,
to="manuels.Teacher",
verbose_name="coordonnateur",
),
),
migrations.AlterField(
model_name='suppliesrequirement',
name='teacher',
field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.PROTECT, to='manuels.Teacher', verbose_name='coordonnateur'),
model_name="suppliesrequirement",
name="teacher",
field=models.ForeignKey(
null=True,
on_delete=django.db.models.deletion.PROTECT,
to="manuels.Teacher",
verbose_name="coordonnateur",
),
),
]

View File

@ -6,16 +6,24 @@ from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('manuels', '0020_auto_20180602_1630'),
("manuels", "0020_auto_20180602_1630"),
]
operations = [
migrations.AlterModelOptions(
name='editor',
options={'ordering': ['name'], 'verbose_name': 'éditeur', 'verbose_name_plural': 'éditeurs'},
name="editor",
options={
"ordering": ["name"],
"verbose_name": "éditeur",
"verbose_name_plural": "éditeurs",
},
),
migrations.AlterModelOptions(
name='teacher',
options={'ordering': ['first_name'], 'verbose_name': 'coordonnateur', 'verbose_name_plural': 'coordonnateurs'},
name="teacher",
options={
"ordering": ["first_name"],
"verbose_name": "coordonnateur",
"verbose_name_plural": "coordonnateurs",
},
),
]

View File

@ -6,13 +6,15 @@ from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('manuels', '0021_auto_20180602_1638'),
("manuels", "0021_auto_20180602_1638"),
]
operations = [
migrations.AddField(
model_name='teacher',
name='has_confirmed_list',
field=models.BooleanField(default=False, verbose_name='a confirmé les listes'),
model_name="teacher",
name="has_confirmed_list",
field=models.BooleanField(
default=False, verbose_name="a confirmé les listes"
),
),
]

View File

@ -1,19 +1,23 @@
# Generated by Django 2.0.5 on 2018-06-04 16:27
from django.db import migrations, models
import manuels.models
class Migration(migrations.Migration):
dependencies = [
('manuels', '0022_teacher_has_confirmed_list'),
("manuels", "0022_teacher_has_confirmed_list"),
]
operations = [
migrations.AlterField(
model_name='book',
name='price',
field=models.FloatField(validators=[manuels.models.positive_float_validator], verbose_name='prix'),
model_name="book",
name="price",
field=models.FloatField(
validators=[manuels.models.positive_float_validator],
verbose_name="prix",
),
),
]

View File

@ -6,13 +6,13 @@ from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('manuels', '0023_auto_20180604_1827'),
("manuels", "0023_auto_20180604_1827"),
]
operations = [
migrations.AddField(
model_name='book',
name='other_editor',
field=models.CharField(blank=True, max_length=100, verbose_name='préciser'),
model_name="book",
name="other_editor",
field=models.CharField(blank=True, max_length=100, verbose_name="préciser"),
),
]

View File

@ -6,13 +6,17 @@ from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('manuels', '0024_book_other_editor'),
("manuels", "0024_book_other_editor"),
]
operations = [
migrations.AlterField(
model_name='book',
name='comments',
field=models.TextField(blank=True, help_text='Ce message sera visible par la documentaliste.', verbose_name='commentaires'),
model_name="book",
name="comments",
field=models.TextField(
blank=True,
help_text="Ce message sera visible par la documentaliste.",
verbose_name="commentaires",
),
),
]

View File

@ -1,19 +1,25 @@
# Generated by Django 2.0.6 on 2018-06-16 07:16
from django.db import migrations, models
import manuels.models
class Migration(migrations.Migration):
dependencies = [
('manuels', '0025_auto_20180607_0746'),
("manuels", "0025_auto_20180607_0746"),
]
operations = [
migrations.AlterField(
model_name='book',
name='isbn',
field=models.CharField(help_text="Format attendu : 10 ou 13 chiffres, éventuellement séparés par des tirets et éventuellement suivis de la lettre <code>X</code>. La recherche sur Decitre ne fonctionnera qu'avec un code ISBN à13 chiffres (ou EAN)", max_length=20, validators=[manuels.models.isbn_validator], verbose_name='ISBN/EAN'),
model_name="book",
name="isbn",
field=models.CharField(
help_text="Format attendu : 10 ou 13 chiffres, éventuellement séparés par des tirets et éventuellement suivis de la lettre <code>X</code>. La recherche sur Decitre ne fonctionnera qu'avec un code ISBN à13 chiffres (ou EAN)",
max_length=20,
validators=[manuels.models.isbn_validator],
verbose_name="ISBN/EAN",
),
),
]

View File

@ -6,28 +6,34 @@ from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('manuels', '0026_auto_20180616_0916'),
("manuels", "0026_auto_20180616_0916"),
]
operations = [
migrations.AlterField(
model_name='book',
name='done',
field=models.BooleanField(blank=True, default=False, verbose_name='Traité'),
model_name="book",
name="done",
field=models.BooleanField(blank=True, default=False, verbose_name="Traité"),
),
migrations.AlterField(
model_name='book',
name='previously_acquired',
field=models.BooleanField(choices=[(None, '------------'), (False, 'Non'), (True, 'Oui')], default=None, verbose_name="manuel acquis précédemment par l'élève"),
model_name="book",
name="previously_acquired",
field=models.BooleanField(
choices=[(None, "------------"), (False, "Non"), (True, "Oui")],
default=None,
verbose_name="manuel acquis précédemment par l'élève",
),
),
migrations.AlterField(
model_name='suppliesrequirement',
name='done',
field=models.BooleanField(blank=True, default=False, verbose_name='Traité'),
model_name="suppliesrequirement",
name="done",
field=models.BooleanField(blank=True, default=False, verbose_name="Traité"),
),
migrations.AlterField(
model_name='teacher',
name='has_confirmed_list',
field=models.BooleanField(blank=True, default=False, verbose_name='a confirmé les listes'),
model_name="teacher",
name="has_confirmed_list",
field=models.BooleanField(
blank=True, default=False, verbose_name="a confirmé les listes"
),
),
]

View File

@ -6,23 +6,31 @@ from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('manuels', '0027_auto_20190401_2036'),
("manuels", "0027_auto_20190401_2036"),
]
operations = [
migrations.AlterModelOptions(
name='level',
options={'ordering': ['order', 'name'], 'verbose_name': 'classe', 'verbose_name_plural': 'classes'},
name="level",
options={
"ordering": ["order", "name"],
"verbose_name": "classe",
"verbose_name_plural": "classes",
},
),
migrations.AddField(
model_name='book',
name='consumable',
field=models.BooleanField(default=False, help_text="Exemple : un cahier d'exercices est un consommable", verbose_name='consommable'),
model_name="book",
name="consumable",
field=models.BooleanField(
default=False,
help_text="Exemple : un cahier d'exercices est un consommable",
verbose_name="consommable",
),
preserve_default=False,
),
migrations.AddField(
model_name='level',
name='order',
field=models.IntegerField(default=0, verbose_name='ordre'),
model_name="level",
name="order",
field=models.IntegerField(default=0, verbose_name="ordre"),
),
]

View File

@ -6,13 +6,18 @@ from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('manuels', '0028_auto_20190406_1901'),
("manuels", "0028_auto_20190406_1901"),
]
operations = [
migrations.AlterField(
model_name='book',
name='consumable',
field=models.BooleanField(choices=[(None, '------------'), (False, 'Non'), (True, 'Oui')], default=None, help_text="Exemple : un cahier d'exercices est un consommable", verbose_name='consommable'),
model_name="book",
name="consumable",
field=models.BooleanField(
choices=[(None, "------------"), (False, "Non"), (True, "Oui")],
default=None,
help_text="Exemple : un cahier d'exercices est un consommable",
verbose_name="consommable",
),
),
]

View File

@ -6,13 +6,13 @@ from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('manuels', '0029_auto_20190406_1904'),
("manuels", "0029_auto_20190406_1904"),
]
operations = [
migrations.RenameField(
model_name='suppliesrequirement',
old_name='fields',
new_name='field',
model_name="suppliesrequirement",
old_name="fields",
new_name="field",
),
]

View File

@ -6,13 +6,13 @@ from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('manuels', '0030_auto_20190406_1912'),
("manuels", "0030_auto_20190406_1912"),
]
operations = [
migrations.AlterField(
model_name='suppliesrequirement',
name='field',
field=models.CharField(max_length=50, verbose_name='disciplines'),
model_name="suppliesrequirement",
name="field",
field=models.CharField(max_length=50, verbose_name="disciplines"),
),
]

View File

@ -6,13 +6,13 @@ from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('manuels', '0031_auto_20190406_1912'),
("manuels", "0031_auto_20190406_1912"),
]
operations = [
migrations.AlterField(
model_name='suppliesrequirement',
name='field',
field=models.CharField(max_length=50, verbose_name='discipline'),
model_name="suppliesrequirement",
name="field",
field=models.CharField(max_length=50, verbose_name="discipline"),
),
]

View File

@ -1,19 +1,25 @@
# Generated by Django 2.2 on 2019-04-06 17:19
from django.db import migrations, models
import manuels.models
class Migration(migrations.Migration):
dependencies = [
('manuels', '0032_auto_20190406_1913'),
("manuels", "0032_auto_20190406_1913"),
]
operations = [
migrations.AlterField(
model_name='book',
name='isbn',
field=models.CharField(help_text="Format attendu : 10 ou 13 chiffres, éventuellement séparés par des tirets et éventuellement suivis de la lettre <code>X</code>. La recherche sur Decitre ne fonctionnera qu'avec un code ISBN à 13 chiffres (ou EAN)", max_length=20, validators=[manuels.models.isbn_validator], verbose_name='ISBN/EAN'),
model_name="book",
name="isbn",
field=models.CharField(
help_text="Format attendu : 10 ou 13 chiffres, éventuellement séparés par des tirets et éventuellement suivis de la lettre <code>X</code>. La recherche sur Decitre ne fonctionnera qu'avec un code ISBN à 13 chiffres (ou EAN)",
max_length=20,
validators=[manuels.models.isbn_validator],
verbose_name="ISBN/EAN",
),
),
]

View File

@ -6,21 +6,35 @@ from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('manuels', '0033_auto_20190406_1919'),
("manuels", "0033_auto_20190406_1919"),
]
operations = [
migrations.CreateModel(
name='CommonSupply',
name="CommonSupply",
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('created_at', models.DateTimeField(auto_now_add=True, verbose_name='créé le')),
('updated_at', models.DateTimeField(auto_now=True, verbose_name='mis à jour le')),
('name', models.CharField(max_length=200, verbose_name='nom')),
(
"id",
models.AutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
(
"created_at",
models.DateTimeField(auto_now_add=True, verbose_name="créé le"),
),
(
"updated_at",
models.DateTimeField(auto_now=True, verbose_name="mis à jour le"),
),
("name", models.CharField(max_length=200, verbose_name="nom")),
],
options={
'verbose_name': 'fourniture commune',
'verbose_name_plural': 'fournitures communes',
"verbose_name": "fourniture commune",
"verbose_name_plural": "fournitures communes",
},
),
]

View File

@ -6,14 +6,14 @@ from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('manuels', '0034_commonsupply'),
("manuels", "0034_commonsupply"),
]
operations = [
migrations.AddField(
model_name='commonsupply',
name='order',
field=models.IntegerField(default=0, verbose_name='ordre'),
model_name="commonsupply",
name="order",
field=models.IntegerField(default=0, verbose_name="ordre"),
preserve_default=False,
),
]

View File

@ -6,12 +6,16 @@ from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('manuels', '0035_commonsupply_order'),
("manuels", "0035_commonsupply_order"),
]
operations = [
migrations.AlterModelOptions(
name='commonsupply',
options={'ordering': ('order',), 'verbose_name': 'fourniture commune', 'verbose_name_plural': 'fournitures communes'},
name="commonsupply",
options={
"ordering": ("order",),
"verbose_name": "fourniture commune",
"verbose_name_plural": "fournitures communes",
},
),
]

View File

@ -8,18 +8,27 @@ from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('manuels', '0036_auto_20190615_1114'),
("manuels", "0036_auto_20190615_1114"),
]
operations = [
migrations.AlterModelOptions(
name='commonsupply',
options={'ordering': ('order', 'name'), 'verbose_name': 'fourniture commune', 'verbose_name_plural': 'fournitures communes'},
name="commonsupply",
options={
"ordering": ("order", "name"),
"verbose_name": "fourniture commune",
"verbose_name_plural": "fournitures communes",
},
),
CITextExtension(),
migrations.AlterField(
model_name='teacher',
name='email',
field=django.contrib.postgres.fields.citext.CIEmailField(help_text='Utilisée pour vous transmettre votre lien personnel', max_length=254, unique=True, verbose_name='adresse email'),
model_name="teacher",
name="email",
field=django.contrib.postgres.fields.citext.CIEmailField(
help_text="Utilisée pour vous transmettre votre lien personnel",
max_length=254,
unique=True,
verbose_name="adresse email",
),
),
]

View File

@ -1,19 +1,25 @@
# Generated by Django 3.0.7 on 2021-05-10 06:54
from django.db import migrations, models
import manuels.models
class Migration(migrations.Migration):
dependencies = [
('manuels', '0037_auto_20190627_1905'),
("manuels", "0037_auto_20190627_1905"),
]
operations = [
migrations.AlterField(
model_name='book',
name='isbn',
field=models.CharField(help_text="Format attendu : 10 ou 13 chiffres, éventuellement séparés par des tirets et éventuellement suivis de la lettre <code>X</code>. La recherche sur Decitre ne fonctionnera qu'avec un code ISBN à 13 chiffres (ou EAN)", max_length=20, validators=[manuels.models.isbn_validator], verbose_name='ISBN/EAN du manuel élève (hors specimen)'),
model_name="book",
name="isbn",
field=models.CharField(
help_text="Format attendu : 10 ou 13 chiffres, éventuellement séparés par des tirets et éventuellement suivis de la lettre <code>X</code>. La recherche sur Decitre ne fonctionnera qu'avec un code ISBN à 13 chiffres (ou EAN)",
max_length=20,
validators=[manuels.models.isbn_validator],
verbose_name="ISBN/EAN du manuel élève (hors specimen)",
),
),
]

View File

@ -28,101 +28,105 @@ class BaseModel(models.Model):
class Meta:
abstract = True
created_at = models.DateTimeField('créé le', auto_now_add=True)
updated_at = models.DateTimeField('mis à jour le', auto_now=True)
created_at = models.DateTimeField("créé le", auto_now_add=True)
updated_at = models.DateTimeField("mis à jour le", auto_now=True)
def phone_validator(value):
regex = re.compile(r'^\d{10}$')
regex = re.compile(r"^\d{10}$")
if not regex.match(value):
raise ValidationError(
"%(value)s n'est pas un numéro de téléphone valide. Format attendu : 10 chiffres.",
params={'value': value}
params={"value": value},
)
class Teacher(BaseModel):
class Meta:
verbose_name = 'coordonnateur'
verbose_name_plural = 'coordonnateurs'
ordering = ['first_name']
verbose_name = "coordonnateur"
verbose_name_plural = "coordonnateurs"
ordering = ["first_name"]
uuid = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
first_name = models.CharField('prénom', max_length=100)
last_name = models.CharField('nom', max_length=100)
first_name = models.CharField("prénom", max_length=100)
last_name = models.CharField("nom", max_length=100)
phone_number = models.CharField(
'numéro de téléphone',
"numéro de téléphone",
help_text="En cas d'urgence, 10 chiffres.",
max_length=10,
validators=[phone_validator]
validators=[phone_validator],
)
email = CIEmailField(
'adresse email',
help_text='Utilisée pour vous transmettre votre lien personnel',
unique=True
"adresse email",
help_text="Utilisée pour vous transmettre votre lien personnel",
unique=True,
)
has_confirmed_list = models.BooleanField(
'a confirmé les listes',
default=False,
blank=True
"a confirmé les listes", default=False, blank=True
)
def get_absolute_url(self):
from django.urls import reverse
return reverse('list_books', kwargs={'pk': str(self.pk)})
return reverse("list_books", kwargs={"pk": str(self.pk)})
@property
def full_name(self):
return f'{self.first_name} {self.last_name}'
return f"{self.first_name} {self.last_name}"
def __str__(self):
return self.full_name
def send_link(self, request):
dest = self.email
link = request.build_absolute_uri(reverse('list_books', args=[str(self.pk)]))
link = request.build_absolute_uri(reverse("list_books", args=[str(self.pk)]))
msg = EmailMultiAlternatives(
subject='Gestion des manuels scolaires',
body=f'Bonjour {self.first_name},\n'
f'Voici votre lien pour la gestion des manuels scolaires : {link}',
subject="Gestion des manuels scolaires",
body=f"Bonjour {self.first_name},\n"
f"Voici votre lien pour la gestion des manuels scolaires : {link}",
from_email=settings.SERVER_EMAIL,
to=[dest],
)
reply_to = [os.getenv('REPLY_TO')]
reply_to = [os.getenv("REPLY_TO")]
if reply_to:
msg.reply_to = reply_to
msg.attach_alternative(
render_to_string('manuels/emails_link.html', {'link': link, 'teacher': self}), "text/html"
render_to_string(
"manuels/emails_link.html", {"link": link, "teacher": self}
),
"text/html",
)
msg.send()
def send_confirmation(self, request):
dest = settings.LIBRARIAN_EMAILS
link = request.build_absolute_uri(reverse('home_page'))
link = request.build_absolute_uri(reverse("home_page"))
msg = EmailMultiAlternatives(
subject="Gestion des manuels scolaires - Confirmation d'un coordonnateur",
body=f'Bonjour,\n'
f'{self.first_name} a confirmé ses listes sur {link}',
body=f"Bonjour,\n" f"{self.first_name} a confirmé ses listes sur {link}",
from_email=settings.SERVER_EMAIL,
to=dest,
)
reply_to = [os.getenv('REPLY_TO')]
reply_to = [os.getenv("REPLY_TO")]
if reply_to:
msg.reply_to = reply_to
msg.attach_alternative(
render_to_string('manuels/emails_confirmation.html', {'link': link, 'teacher': self}), "text/html"
render_to_string(
"manuels/emails_confirmation.html", {"link": link, "teacher": self}
),
"text/html",
)
msg.send()
class Level(BaseModel):
class Meta:
verbose_name = 'classe'
verbose_name_plural = 'classes'
ordering = ['order', 'name']
verbose_name = "classe"
verbose_name_plural = "classes"
ordering = ["order", "name"]
name = models.CharField('nom', max_length=50)
order = models.IntegerField('ordre', default=0)
name = models.CharField("nom", max_length=50)
order = models.IntegerField("ordre", default=0)
def __str__(self):
return self.name
@ -130,7 +134,10 @@ class Level(BaseModel):
@property
def non_acquired_consumables(self):
if hasattr(self, "prefetched_books"):
return filter(lambda book: book.consumable and not book.previously_acquired, self.prefetched_books)
return filter(
lambda book: book.consumable and not book.previously_acquired,
self.prefetched_books,
)
return self.book_set.filter(consumable=True, previously_acquired=False)
@property
@ -143,13 +150,17 @@ class Level(BaseModel):
def non_acquired_consumable_price(self):
if hasattr(self, "prefetched_books"):
return sum(map(lambda book: book.price, self.non_acquired_consumables))
return self.non_acquired_consumables.aggregate(Sum('price')).get(
'price__sum', 0)
return self.non_acquired_consumables.aggregate(Sum("price")).get(
"price__sum", 0
)
@property
def non_acquired_books(self):
if hasattr(self, "prefetched_books"):
return filter(lambda book: not book.consumable and not book.previously_acquired, self.prefetched_books)
return filter(
lambda book: not book.consumable and not book.previously_acquired,
self.prefetched_books,
)
return self.book_set.filter(consumable=False, previously_acquired=False)
@property
@ -162,39 +173,41 @@ class Level(BaseModel):
def non_acquired_book_price(self):
if hasattr(self, "prefetched_books"):
return sum(map(lambda book: book.price, self.non_acquired_books))
return self.non_acquired_books.aggregate(Sum('price')).get(
'price__sum', 0)
return self.non_acquired_books.aggregate(Sum("price")).get("price__sum", 0)
@property
def non_acquired_items(self):
if hasattr(self, "prefetched_books"):
return filter(lambda book: not book.previously_acquired, self.prefetched_books)
return filter(
lambda book: not book.previously_acquired, self.prefetched_books
)
return self.book_set.filter(previously_acquired=False)
@property
def non_acquired_total_price(self):
if hasattr(self, "prefetched_books"):
return sum(map(lambda book: book.price, self.non_acquired_items))
return self.non_acquired_items.aggregate(Sum('price')).get(
'price__sum', 0)
return self.non_acquired_items.aggregate(Sum("price")).get("price__sum", 0)
class Editor(BaseModel):
class Meta:
verbose_name = 'éditeur'
verbose_name_plural = 'éditeurs'
ordering = ['name']
verbose_name = "éditeur"
verbose_name_plural = "éditeurs"
ordering = ["name"]
name = models.CharField('nom', max_length=100)
name = models.CharField("nom", max_length=100)
def __str__(self):
return self.name
def isbn_validator(value):
regex = re.compile(r'(\d-?){10,13}X?')
regex = re.compile(r"(\d-?){10,13}X?")
if not regex.match(value):
raise ValidationError("%(value)s n'est pas un ISBN valide.", params={'value': value})
raise ValidationError(
"%(value)s n'est pas un ISBN valide.", params={"value": value}
)
def positive_float_validator(value):
@ -209,30 +222,36 @@ def positive_float_validator(value):
class Book(BaseModel):
class Meta:
verbose_name = 'livre'
verbose_name_plural = 'livres'
verbose_name = "livre"
verbose_name_plural = "livres"
teacher = models.ForeignKey(verbose_name='coordonnateur', to=Teacher, on_delete=models.PROTECT, null=True)
level = models.ForeignKey(verbose_name='classe', to=Level, on_delete=models.PROTECT, null=True)
field = models.CharField('discipline', max_length=200)
title = models.TextField('titre')
authors = models.TextField('auteurs')
editor = models.ForeignKey(verbose_name='éditeur', to=Editor, on_delete=models.PROTECT, null=True)
other_editor = models.CharField(verbose_name='préciser', max_length=100, blank=True)
publication_year = models.PositiveIntegerField('année de publication')
teacher = models.ForeignKey(
verbose_name="coordonnateur", to=Teacher, on_delete=models.PROTECT, null=True
)
level = models.ForeignKey(
verbose_name="classe", to=Level, on_delete=models.PROTECT, null=True
)
field = models.CharField("discipline", max_length=200)
title = models.TextField("titre")
authors = models.TextField("auteurs")
editor = models.ForeignKey(
verbose_name="éditeur", to=Editor, on_delete=models.PROTECT, null=True
)
other_editor = models.CharField(verbose_name="préciser", max_length=100, blank=True)
publication_year = models.PositiveIntegerField("année de publication")
isbn = models.CharField(
'ISBN/EAN du manuel élève (hors specimen)',
"ISBN/EAN du manuel élève (hors specimen)",
max_length=20,
help_text="Format attendu : 10 ou 13 chiffres, éventuellement séparés par des tirets et éventuellement "
"suivis de la lettre <code>X</code>. La recherche sur Decitre ne fonctionnera qu'avec un code ISBN à "
"13 chiffres (ou EAN)",
validators=[isbn_validator]
validators=[isbn_validator],
)
price = models.FloatField('prix', validators=[positive_float_validator])
price = models.FloatField("prix", validators=[positive_float_validator])
YES_NO_CHOICE = (
(None, '------------'),
(False, 'Non'),
(True, 'Oui'),
(None, "------------"),
(False, "Non"),
(True, "Oui"),
)
previously_acquired = models.BooleanField(
"manuel acquis précédemment par l'élève",
@ -240,18 +259,14 @@ class Book(BaseModel):
blank=False,
default=None,
)
done = models.BooleanField(
'Traité',
blank=True,
default=False
)
done = models.BooleanField("Traité", blank=True, default=False)
comments = models.TextField(
'commentaires',
"commentaires",
blank=True,
help_text="Ce message sera visible par la documentaliste."
help_text="Ce message sera visible par la documentaliste.",
)
consumable = models.BooleanField(
'consommable',
"consommable",
help_text="Exemple : un cahier d'exercices est un consommable",
choices=YES_NO_CHOICE,
blank=False,
@ -261,32 +276,35 @@ class Book(BaseModel):
@property
def previously_acquired_text(self):
if self.previously_acquired:
return 'Oui'
return "Oui"
else:
return 'Non'
return "Non"
@property
def consumable_text(self):
if self.consumable:
return 'Oui'
return "Oui"
else:
return 'Non'
return "Non"
def __str__(self):
return f'{self.title} ({self.authors}) - {self.isbn}'
return f"{self.title} ({self.authors}) - {self.isbn}"
def get_absolute_url(self):
from django.urls import reverse
return reverse('edit_book', kwargs={'teacher_pk': str(self.teacher.pk), 'pk': str(self.pk)})
return reverse(
"edit_book", kwargs={"teacher_pk": str(self.teacher.pk), "pk": str(self.pk)}
)
def update_from_decitre(self):
decitre_data = self.fetch_from_decitre(self.isbn)
self.title = decitre_data.get('title')
self.authors = decitre_data.get('authors')
self.price = decitre_data.get('price')
self.publication_year = decitre_data.get('year')
self.title = decitre_data.get("title")
self.authors = decitre_data.get("authors")
self.price = decitre_data.get("price")
self.publication_year = decitre_data.get("year")
editor = decitre_data.get('editor')
editor = decitre_data.get("editor")
potential_editor = (
Editor.objects.filter(name__iexact=editor).first()
or Editor.objects.filter(name__icontains=editor).first()
@ -302,31 +320,35 @@ class Book(BaseModel):
@property
def decitre_url(self):
isbn = self.isbn.strip().replace('-', '')
isbn = self.isbn.strip().replace("-", "")
if not validate_isbn(isbn) or len(isbn) == 10:
return ""
return f'https://www.decitre.fr/livres/{isbn}.html'
return f"https://www.decitre.fr/livres/{isbn}.html"
@staticmethod
def fetch_from_decitre(isbn: str):
isbn = isbn.strip().replace('-', '')
isbn = isbn.strip().replace("-", "")
if not validate_isbn(isbn):
raise ISBNError({
'error': "L'ISBN saisi n'est pas valide."
})
raise ISBNError({"error": "L'ISBN saisi n'est pas valide."})
if len(isbn) == 10:
raise ISBNError({
'error': "La recherche sur Decitre ne fonctionne qu'avec un ISBN 13 (ou EAN)."
})
raise ISBNError(
{
"error": "La recherche sur Decitre ne fonctionne qu'avec un ISBN 13 (ou EAN)."
}
)
try:
res = requests.get(f'https://www.decitre.fr/livres/{isbn}.html', timeout=10)
res = requests.get(f"https://www.decitre.fr/livres/{isbn}.html", timeout=10)
except requests.exceptions.Timeout as exc:
raise ISBNError({
'error': "Decitre n'a pas répondu dans les temps. Message : {}".format(str(exc))
})
raise ISBNError(
{
"error": "Decitre n'a pas répondu dans les temps. Message : {}".format(
str(exc)
)
}
)
try:
res.raise_for_status()
@ -334,89 +356,92 @@ class Book(BaseModel):
message = (
"Erreur lors de la recherche. Il se peut que le livre n'existe pas dans la base de connaissances "
"de Decitre ou que vous ayez mal saisi l'ISBN. Vous pouvez toujours saisir "
"les informations du livre à la main. Message : {}").format(str(exc))
raise ISBNError({
'error': message
})
"les informations du livre à la main. Message : {}"
).format(str(exc))
raise ISBNError({"error": message})
decitre_soup = bs4.BeautifulSoup(res.text, "html.parser")
title = decitre_soup.select('h1.product-title')
title = decitre_soup.select("h1.product-title")
if title:
title = title[0]
if title.span:
title.span.extract()
title = title.get_text(strip=True)
authors = decitre_soup.select('.authors')
authors = decitre_soup.select(".authors")
if authors:
authors = authors[0]
authors = authors.get_text(strip=True)
price = decitre_soup.select('.fp-top--add-to-cart div.price span.final-price')
price = decitre_soup.select(".fp-top--add-to-cart div.price span.final-price")
if price:
price = price[0]
price = price.get_text().replace('', '').replace(',', '.').strip()
price = price.get_text().replace("", "").replace(",", ".").strip()
year = None
editor = None
extra_info = decitre_soup.select('.informations-container')
logger.debug('raw extra_info')
extra_info = decitre_soup.select(".informations-container")
logger.debug("raw extra_info")
logger.debug(extra_info)
if not extra_info:
logger.debug('#fiche-technique')
extra_info = decitre_soup.select('#fiche-technique')
logger.debug('raw extra_info fiche technique')
logger.debug("#fiche-technique")
extra_info = decitre_soup.select("#fiche-technique")
logger.debug("raw extra_info fiche technique")
logger.debug(extra_info)
if extra_info:
extra_info = extra_info[0].get_text(strip=True)
logger.debug('extra_info')
logger.debug("extra_info")
logger.debug(extra_info)
matches = re.search(
r'Date de parution(?: :)?\d{2}/\d{2}/(?P<year>\d{4})Editeur(?: :)?(?P<editor>.+?)(?:ISBN|Collection)',
extra_info)
r"Date de parution(?: :)?\d{2}/\d{2}/(?P<year>\d{4})Editeur(?: :)?(?P<editor>.+?)(?:ISBN|Collection)",
extra_info,
)
if matches:
groups = matches.groupdict()
year = groups.get('year')
editor = groups.get('editor').strip()
year = groups.get("year")
editor = groups.get("editor").strip()
return {
'title': title,
'authors': authors,
'isbn': isbn,
'price': float(price) if price else None,
'year': year,
'editor': editor,
"title": title,
"authors": authors,
"isbn": isbn,
"price": float(price) if price else None,
"year": year,
"editor": editor,
}
class SuppliesRequirement(BaseModel):
class Meta:
verbose_name = 'demande de fournitures'
verbose_name_plural = 'demandes de fournitures'
verbose_name = "demande de fournitures"
verbose_name_plural = "demandes de fournitures"
teacher = models.ForeignKey(verbose_name='coordonnateur', to=Teacher, on_delete=models.PROTECT, null=True)
level = models.ForeignKey(verbose_name='classe', to=Level, on_delete=models.PROTECT, null=True)
field = models.CharField('discipline', max_length=50)
supplies = models.TextField('fournitures')
done = models.BooleanField(
'Traité',
blank=True,
default=False
teacher = models.ForeignKey(
verbose_name="coordonnateur", to=Teacher, on_delete=models.PROTECT, null=True
)
level = models.ForeignKey(
verbose_name="classe", to=Level, on_delete=models.PROTECT, null=True
)
field = models.CharField("discipline", max_length=50)
supplies = models.TextField("fournitures")
done = models.BooleanField("Traité", blank=True, default=False)
def __str__(self):
return f'{self.supplies} pour {self.level} ({self.teacher})'
return f"{self.supplies} pour {self.level} ({self.teacher})"
class CommonSupply(BaseModel):
class Meta:
verbose_name = 'fourniture commune'
verbose_name_plural = 'fournitures communes'
ordering = ('order', 'name',)
verbose_name = "fourniture commune"
verbose_name_plural = "fournitures communes"
ordering = (
"order",
"name",
)
name = models.CharField('nom', max_length=200)
order = models.IntegerField('ordre')
name = models.CharField("nom", max_length=200)
order = models.IntegerField("ordre")
def __str__(self):
return self.name

View File

@ -1 +0,0 @@

View File

@ -4,4 +4,3 @@ from django.test import TestCase
class MyTestCase(TestCase):
def test_something(self):
self.assertEqual(True, False)

View File

@ -1,24 +1,57 @@
from django.conf import settings
from django.urls import path, include
from django.urls import include, path
from manuels.views import AddBookView, ListBooksView, clear_teacher_view, AddSuppliesView, EditBookView, \
EditSuppliesView, DeleteBookView, DeleteSuppliesView, ConfirmTeacherView, isbn_api
from manuels.views import (
AddBookView,
AddSuppliesView,
ConfirmTeacherView,
DeleteBookView,
DeleteSuppliesView,
EditBookView,
EditSuppliesView,
ListBooksView,
clear_teacher_view,
isbn_api,
)
urlpatterns = [
path('teacher/<uuid:pk>/add_book', AddBookView.as_view(), name='add_book'),
path('teacher/<uuid:pk>/add_supplies', AddSuppliesView.as_view(), name='add_supplies'),
path('teacher/<uuid:pk>', ListBooksView.as_view(), name='list_books'),
path('teacher/<uuid:teacher_pk>/book/<int:pk>', EditBookView.as_view(), name='edit_book'),
path('teacher/<uuid:teacher_pk>/book/<int:pk>/delete', DeleteBookView.as_view(), name='delete_book'),
path('teacher/<uuid:teacher_pk>/supplies/<int:pk>', EditSuppliesView.as_view(), name='edit_supplies'),
path('teacher/<uuid:teacher_pk>/supplies/<int:pk>/delete', DeleteSuppliesView.as_view(), name='delete_supplies'),
path('teacher/<uuid:pk>/confirm', ConfirmTeacherView.as_view(), name='confirm_teacher'),
path('clear', clear_teacher_view, name='clear_teacher'),
path('isbn_api/<str:isbn>', isbn_api, name='isbn_api'),
path("teacher/<uuid:pk>/add_book", AddBookView.as_view(), name="add_book"),
path(
"teacher/<uuid:pk>/add_supplies", AddSuppliesView.as_view(), name="add_supplies"
),
path("teacher/<uuid:pk>", ListBooksView.as_view(), name="list_books"),
path(
"teacher/<uuid:teacher_pk>/book/<int:pk>",
EditBookView.as_view(),
name="edit_book",
),
path(
"teacher/<uuid:teacher_pk>/book/<int:pk>/delete",
DeleteBookView.as_view(),
name="delete_book",
),
path(
"teacher/<uuid:teacher_pk>/supplies/<int:pk>",
EditSuppliesView.as_view(),
name="edit_supplies",
),
path(
"teacher/<uuid:teacher_pk>/supplies/<int:pk>/delete",
DeleteSuppliesView.as_view(),
name="delete_supplies",
),
path(
"teacher/<uuid:pk>/confirm",
ConfirmTeacherView.as_view(),
name="confirm_teacher",
),
path("clear", clear_teacher_view, name="clear_teacher"),
path("isbn_api/<str:isbn>", isbn_api, name="isbn_api"),
]
if settings.DEBUG:
import debug_toolbar
urlpatterns = [
path('__debug__/', include(debug_toolbar.urls)),
path("__debug__/", include(debug_toolbar.urls)),
] + urlpatterns

View File

@ -2,7 +2,7 @@ def validate_isbn(isbn):
_sum = 0
if len(isbn) == 10:
for i, digit in enumerate(isbn):
if digit == 'X':
if digit == "X":
digit = 10
else:
digit = int(digit)

View File

@ -8,10 +8,10 @@ from django.http import HttpResponseRedirect, JsonResponse
from django.shortcuts import redirect
from django.urls import reverse
from django.views.decorators.cache import cache_page
from django.views.generic import CreateView, UpdateView, DeleteView, TemplateView
from django.views.generic import CreateView, DeleteView, TemplateView, UpdateView
from manuels.forms import AddBookForm, AddSuppliesForm, EditBookForm, EditSuppliesForm
from manuels.models import Teacher, Book, SuppliesRequirement, CommonSupply, ISBNError
from manuels.models import Book, CommonSupply, ISBNError, SuppliesRequirement, Teacher
from manuels.utils import validate_isbn
logger = logging.getLogger(__name__)
@ -19,13 +19,13 @@ logger = logging.getLogger(__name__)
class HomePageView(CreateView):
model = Teacher
fields = ['first_name', 'last_name', 'phone_number', 'email']
template_name = 'manuels/home_page.html'
fields = ["first_name", "last_name", "phone_number", "email"]
template_name = "manuels/home_page.html"
def get(self, request, *args, **kwargs):
teacher_pk = request.session.get('teacher_pk')
teacher_pk = request.session.get("teacher_pk")
if teacher_pk:
return redirect('list_books', pk=teacher_pk)
return redirect("list_books", pk=teacher_pk)
return super().get(request, *args, **kwargs)
@ -37,26 +37,31 @@ class HomePageView(CreateView):
class BaseTeacherView:
teacher = None
teacher_field = 'pk'
teacher_field = "pk"
def dispatch(self, request, *args, **kwargs):
self.teacher = Teacher.objects.filter(pk=self.kwargs[self.teacher_field]).first()
self.teacher = Teacher.objects.filter(
pk=self.kwargs[self.teacher_field]
).first()
if not self.teacher:
messages.warning(request, "Impossible de trouver le coordonnateur demandé. Si vous pensez que ceci est "
"une erreur, merci de vous adresser à votre documentaliste.")
return redirect('clear_teacher')
request.session['teacher_pk'] = str(self.teacher.pk)
messages.warning(
request,
"Impossible de trouver le coordonnateur demandé. Si vous pensez que ceci est "
"une erreur, merci de vous adresser à votre documentaliste.",
)
return redirect("clear_teacher")
request.session["teacher_pk"] = str(self.teacher.pk)
return super().dispatch(request, *args, **kwargs)
def get_context_data(self, **kwargs):
context = super().get_context_data()
context['teacher'] = self.teacher
context["teacher"] = self.teacher
return context
class ListBooksView(BaseTeacherView, TemplateView):
template_name = 'manuels/list_books_supplies.html'
template_name = "manuels/list_books_supplies.html"
class ItemView(BaseTeacherView):
@ -66,79 +71,80 @@ class ItemView(BaseTeacherView):
success_target = None
message_template = None
verb = None
button_class = 'primary'
button_icon = 'fas fa-check-circle'
button_class = "primary"
button_icon = "fas fa-check-circle"
def dispatch(self, request, *args, **kwargs):
response = super().dispatch(request, *args, **kwargs)
if self.teacher and self.teacher.has_confirmed_list:
messages.error(request, "Vous avez déjà confirmé vos listes. Il n'est plus possible de les modifier.")
return redirect('list_books', pk=self.teacher.pk)
messages.error(
request,
"Vous avez déjà confirmé vos listes. Il n'est plus possible de les modifier.",
)
return redirect("list_books", pk=self.teacher.pk)
return response
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['item'] = self.item_text
context['item_plural'] = self.item_text_plural
context['message_template'] = self.message_template
context['verb'] = self.verb
context['button_class'] = self.button_class
context['button_icon'] = self.button_icon
context["item"] = self.item_text
context["item_plural"] = self.item_text_plural
context["message_template"] = self.message_template
context["verb"] = self.verb
context["button_class"] = self.button_class
context["button_icon"] = self.button_icon
return context
def get_initial(self):
return {
'teacher': self.teacher
}
return {"teacher": self.teacher}
def get_form(self, form_class=None):
form = super().get_form(form_class)
form.fields['teacher'].queryset = Teacher.objects.filter(pk=self.teacher.pk)
form.fields["teacher"].queryset = Teacher.objects.filter(pk=self.teacher.pk)
return form
class AddItemView(ItemView, CreateView):
verb = 'Ajouter'
button_icon = 'fas fa-plus-circle'
verb = "Ajouter"
button_icon = "fas fa-plus-circle"
def get_success_url(self):
if self.add_another:
return reverse(self.success_target, args=[str(self.teacher.pk)])
else:
return reverse('list_books', args=[str(self.teacher.pk)])
return reverse("list_books", args=[str(self.teacher.pk)])
def form_valid(self, form):
self.add_another = form.cleaned_data['add_another']
self.add_another = form.cleaned_data["add_another"]
return HttpResponseRedirect(self.get_success_url())
class BookView:
model = Book
success_target = 'add_book'
item_text = 'un livre'
item_text_plural = 'livres'
success_target = "add_book"
item_text = "un livre"
item_text_plural = "livres"
class AddBookView(BookView, AddItemView):
form_class = AddBookForm
template_name = 'manuels/add_book.html'
template_name = "manuels/add_book.html"
def form_valid(self, form: AddBookForm):
for level in form.cleaned_data['levels']:
for level in form.cleaned_data["levels"]:
book = Book.objects.create(
teacher=form.cleaned_data['teacher'],
teacher=form.cleaned_data["teacher"],
level=level,
field=form.cleaned_data['field'],
title=form.cleaned_data['title'],
authors=form.cleaned_data['authors'],
editor=form.cleaned_data['editor'],
other_editor=form.cleaned_data['other_editor'],
publication_year=form.cleaned_data['publication_year'],
isbn=form.cleaned_data['isbn'],
price=form.cleaned_data['price'],
previously_acquired=form.cleaned_data['previously_acquired'],
comments=form.cleaned_data['comments'],
consumable=form.cleaned_data['consumable'],
field=form.cleaned_data["field"],
title=form.cleaned_data["title"],
authors=form.cleaned_data["authors"],
editor=form.cleaned_data["editor"],
other_editor=form.cleaned_data["other_editor"],
publication_year=form.cleaned_data["publication_year"],
isbn=form.cleaned_data["isbn"],
price=form.cleaned_data["price"],
previously_acquired=form.cleaned_data["previously_acquired"],
comments=form.cleaned_data["comments"],
consumable=form.cleaned_data["consumable"],
)
messages.success(self.request, f'"{book}" a été ajouté.')
@ -147,28 +153,28 @@ class AddBookView(BookView, AddItemView):
class SuppliesView:
model = SuppliesRequirement
success_target = 'add_supplies'
item_text = 'des fournitures'
item_text_plural = 'fournitures'
success_target = "add_supplies"
item_text = "des fournitures"
item_text_plural = "fournitures"
class AddSuppliesView(SuppliesView, AddItemView):
form_class = AddSuppliesForm
message_template = 'manuels/supplies_message.html'
template_name = 'manuels/add_supplies.html'
message_template = "manuels/supplies_message.html"
template_name = "manuels/add_supplies.html"
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['common_supplies'] = CommonSupply.objects.all()
context["common_supplies"] = CommonSupply.objects.all()
return context
def form_valid(self, form: AddBookForm):
for level in form.cleaned_data['levels']:
for level in form.cleaned_data["levels"]:
supplies = SuppliesRequirement.objects.create(
teacher=form.cleaned_data['teacher'],
teacher=form.cleaned_data["teacher"],
level=level,
field=form.cleaned_data['field'],
supplies=form.cleaned_data['supplies'],
field=form.cleaned_data["field"],
supplies=form.cleaned_data["supplies"],
)
messages.success(self.request, f'"{supplies}" a été ajouté.')
@ -176,46 +182,46 @@ class AddSuppliesView(SuppliesView, AddItemView):
class EditItemView(ItemView, UpdateView):
teacher_field = 'teacher_pk'
teacher_field = "teacher_pk"
item_text = None
item_text_plural = None
message_template = None
verb = 'Modifier'
verb = "Modifier"
def get_queryset(self):
return self.model.objects.filter(teacher=self.teacher)
def get_success_url(self):
messages.success(self.request, f'"{self.object}" a été modifié.')
return reverse('list_books', args=[str(self.teacher.pk)])
return reverse("list_books", args=[str(self.teacher.pk)])
class EditBookView(BookView, EditItemView):
form_class = EditBookForm
template_name = 'manuels/add_book.html'
template_name = "manuels/add_book.html"
class EditSuppliesView(SuppliesView, EditItemView):
form_class = EditSuppliesForm
template_name = 'manuels/add_supplies.html'
template_name = "manuels/add_supplies.html"
class DeleteItemView(ItemView, DeleteView):
teacher_field = 'teacher_pk'
teacher_field = "teacher_pk"
item_text = None
item_text_plural = None
message_template = 'manuels/confirm_delete.html'
verb = 'Supprimer'
button_class = 'danger'
button_icon = 'fas fa-trash'
template_name = 'manuels/add_supplies.html'
message_template = "manuels/confirm_delete.html"
verb = "Supprimer"
button_class = "danger"
button_icon = "fas fa-trash"
template_name = "manuels/add_supplies.html"
def get_queryset(self):
return self.model.objects.filter(teacher=self.teacher)
def get_success_url(self):
messages.success(self.request, f'"{self.object}" a été supprimé.')
return reverse('list_books', args=[str(self.teacher.pk)])
return reverse("list_books", args=[str(self.teacher.pk)])
class DeleteBookView(BookView, DeleteItemView):
@ -227,22 +233,25 @@ class DeleteSuppliesView(SuppliesView, DeleteItemView):
def clear_teacher_view(request):
if 'teacher_pk' in request.session:
del request.session['teacher_pk']
return redirect('home_page')
if "teacher_pk" in request.session:
del request.session["teacher_pk"]
return redirect("home_page")
class ConfirmTeacherView(BaseTeacherView, UpdateView):
model = Teacher
fields = []
template_name = 'manuels/confirm_teacher.html'
template_name = "manuels/confirm_teacher.html"
def form_valid(self, form):
response = super().form_valid(form)
self.object.has_confirmed_list = True
self.object.save()
self.object.send_confirmation(request=self.request)
messages.success(self.request, "Vos listes ont été validées. Votre documentaliste a été notifiée par email.")
messages.success(
self.request,
"Vos listes ont été validées. Votre documentaliste a été notifiée par email.",
)
return response

View File

@ -13,9 +13,9 @@ https://docs.djangoproject.com/en/2.0/ref/settings/
import os
from pathlib import Path
import environ
from django.contrib.messages import constants as messages
import environ
BASE_DIR = Path(__file__).resolve(strict=True).parent.parent
env = environ.Env(
@ -27,8 +27,8 @@ env = environ.Env(
SERVER_EMAIL=str,
AUTHORIZED_EMAILS=(list, []),
LIBRARIAN_EMAILS=(list, []),
MAILGUN_ACCESS_KEY=(str, ''),
MAILGUN_SERVER_NAME=(str, ''),
MAILGUN_ACCESS_KEY=(str, ""),
MAILGUN_SERVER_NAME=(str, ""),
)
env_file = os.getenv("ENV_FILE", None)
if env_file:
@ -39,36 +39,35 @@ if env_file:
SECRET_KEY = env("SECRET_KEY")
# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = env('DJANGO_ENV') == 'dev'
DEBUG = env("DJANGO_ENV") == "dev"
ALLOWED_HOSTS = ['web', '127.0.0.1']
ALLOWED_HOSTS = ["web", "127.0.0.1"]
if DEBUG:
ALLOWED_HOSTS.extend([
'localhost',
env('CURRENT_IP')
])
ALLOWED_HOSTS.extend(["localhost", env("CURRENT_IP")])
ALLOWED_HOSTS.extend(env("HOST"))
ADMINS = [('Gabriel', env('ADMIN_EMAIL')), ]
SERVER_EMAIL = env('SERVER_EMAIL')
EMAIL_SUBJECT_PREFIX = '[Manuels] '
ADMINS = [
("Gabriel", env("ADMIN_EMAIL")),
]
SERVER_EMAIL = env("SERVER_EMAIL")
EMAIL_SUBJECT_PREFIX = "[Manuels] "
AUTHORIZED_EMAILS = env('AUTHORIZED_EMAILS')
LIBRARIAN_EMAILS = env('LIBRARIAN_EMAILS')
AUTHORIZED_EMAILS = env("AUTHORIZED_EMAILS")
LIBRARIAN_EMAILS = env("LIBRARIAN_EMAILS")
# Application definition
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'anymail',
'bootstrap4',
'manuels',
'import_export',
"django.contrib.admin",
"django.contrib.auth",
"django.contrib.contenttypes",
"django.contrib.sessions",
"django.contrib.messages",
"django.contrib.staticfiles",
"anymail",
"bootstrap4",
"manuels",
"import_export",
]
if DEBUG:
@ -77,47 +76,47 @@ if DEBUG:
]
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'whitenoise.middleware.WhiteNoiseMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
"django.middleware.security.SecurityMiddleware",
"whitenoise.middleware.WhiteNoiseMiddleware",
"django.contrib.sessions.middleware.SessionMiddleware",
"django.middleware.common.CommonMiddleware",
"django.middleware.csrf.CsrfViewMiddleware",
"django.contrib.auth.middleware.AuthenticationMiddleware",
"django.contrib.messages.middleware.MessageMiddleware",
"django.middleware.clickjacking.XFrameOptionsMiddleware",
]
if DEBUG:
MIDDLEWARE.insert(0, "debug_toolbar.middleware.DebugToolbarMiddleware")
STATICFILES_STORAGE = 'whitenoise.storage.CompressedManifestStaticFilesStorage'
STATICFILES_STORAGE = "whitenoise.storage.CompressedManifestStaticFilesStorage"
ROOT_URLCONF = 'manuels_collection.urls'
ROOT_URLCONF = "manuels_collection.urls"
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [],
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
'django.template.context_processors.debug',
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
'manuels.context_processors.authorized_mails'
"BACKEND": "django.template.backends.django.DjangoTemplates",
"DIRS": [],
"APP_DIRS": True,
"OPTIONS": {
"context_processors": [
"django.template.context_processors.debug",
"django.template.context_processors.request",
"django.contrib.auth.context_processors.auth",
"django.contrib.messages.context_processors.messages",
"manuels.context_processors.authorized_mails",
],
},
},
]
WSGI_APPLICATION = 'manuels_collection.wsgi.application'
WSGI_APPLICATION = "manuels_collection.wsgi.application"
# Database
# https://docs.djangoproject.com/en/2.0/ref/settings/#databases
default_db_path = BASE_DIR / 'db.sqlite3'
default_db_path = BASE_DIR / "db.sqlite3"
DATABASES = {
'default': env.db(default=f'sqlite:///{default_db_path}'),
"default": env.db(default=f"sqlite:///{default_db_path}"),
}
# Password validation
@ -125,16 +124,16 @@ DATABASES = {
AUTH_PASSWORD_VALIDATORS = [
{
'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
"NAME": "django.contrib.auth.password_validation.UserAttributeSimilarityValidator",
},
{
'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
"NAME": "django.contrib.auth.password_validation.MinimumLengthValidator",
},
{
'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
"NAME": "django.contrib.auth.password_validation.CommonPasswordValidator",
},
{
'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
"NAME": "django.contrib.auth.password_validation.NumericPasswordValidator",
},
]
@ -145,9 +144,9 @@ INTERNAL_IPS = [
# Internationalization
# https://docs.djangoproject.com/en/2.0/topics/i18n/
LANGUAGE_CODE = 'fr-fr'
LANGUAGE_CODE = "fr-fr"
TIME_ZONE = 'Europe/Paris'
TIME_ZONE = "Europe/Paris"
USE_I18N = True
@ -156,53 +155,47 @@ USE_L10N = True
USE_TZ = True
# Logging
LOG_LEVEL = 'DEBUG' if DEBUG else 'INFO'
LOG_LEVEL = "DEBUG" if DEBUG else "INFO"
LOGGING = {
'version': 1,
'disable_existing_loggers': False,
'formatters': {
'verbose': {
'format': '[%(asctime)s] [%(process)d] [%(levelname)s] %(module)s - %(message)s'
"version": 1,
"disable_existing_loggers": False,
"formatters": {
"verbose": {
"format": "[%(asctime)s] [%(process)d] [%(levelname)s] %(module)s - %(message)s"
},
},
'handlers': {
'console': {
'class': 'logging.StreamHandler',
'formatter': 'verbose'
},
},
'loggers': {
'manuels': {
'handlers': ['console'],
'level': LOG_LEVEL
"handlers": {
"console": {"class": "logging.StreamHandler", "formatter": "verbose"},
},
"loggers": {
"manuels": {"handlers": ["console"], "level": LOG_LEVEL},
},
}
# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/2.0/howto/static-files/
STATIC_URL = '/static/'
STATIC_ROOT = BASE_DIR / 'staticfiles'
STATIC_URL = "/static/"
STATIC_ROOT = BASE_DIR / "staticfiles"
LOGIN_REDIRECT_URL = 'rooms-list'
LOGIN_REDIRECT_URL = "rooms-list"
ANYMAIL = {
"MAILGUN_API_KEY": env('MAILGUN_ACCESS_KEY'),
"MAILGUN_SENDER_DOMAIN": env('MAILGUN_SERVER_NAME'),
"MAILGUN_API_KEY": env("MAILGUN_ACCESS_KEY"),
"MAILGUN_SENDER_DOMAIN": env("MAILGUN_SERVER_NAME"),
}
EMAIL_BACKEND = 'anymail.backends.mailgun.EmailBackend'
EMAIL_BACKEND = "anymail.backends.mailgun.EmailBackend"
MESSAGE_TAGS = {
messages.ERROR: 'danger',
messages.ERROR: "danger",
}
CACHES = {
'default': {
'BACKEND': 'django.core.cache.backends.db.DatabaseCache',
'LOCATION': 'manuels_cache',
"default": {
"BACKEND": "django.core.cache.backends.db.DatabaseCache",
"LOCATION": "manuels_cache",
}
}
DEFAULT_AUTO_FIELD = 'django.db.models.AutoField'
DEFAULT_AUTO_FIELD = "django.db.models.AutoField"

View File

@ -14,14 +14,14 @@ Including another URLconf
2. Add a URL to urlpatterns: path('blog/', include('blog.urls'))
"""
from django.contrib import admin
from django.urls import path, include
from django.urls import include, path
from manuels.views import HomePageView
admin.site.site_header = 'Manuels scolaires - Administration'
admin.site.site_header = "Manuels scolaires - Administration"
urlpatterns = [
path('admin/', admin.site.urls),
path('', HomePageView.as_view(), name='home_page'),
path('', include('manuels.urls')),
path("admin/", admin.site.urls),
path("", HomePageView.as_view(), name="home_page"),
path("", include("manuels.urls")),
]