Skip to content
Snippets Groups Projects
Commit c5cce4de authored by Dylan Sechet's avatar Dylan Sechet
Browse files

Merge branch 'master' into dev

parents b6ac58c4 f0d02577
Branches
No related tags found
14 merge requests!58Hamza,!52Hamza,!54Add field "en attentes" in participations options,!51Hamza,!48Hamza,!45Hamza,!40Solve public permission files on AWS,!36Notifications for Sec-Gen,!34Dev-> Master (ordering students by updated_date, school filter, export_csv for visits),!31Sorties,!24Amélioration de l'interface admin,!28Updated student admin filters,!27Better filtering for registration,!26merge dev into master
Showing
with 426 additions and 17 deletions
...@@ -50,13 +50,17 @@ Le site utilise une base de données SQL. Plusieurs technologies existent mais o ...@@ -50,13 +50,17 @@ Le site utilise une base de données SQL. Plusieurs technologies existent mais o
Après avoir installé PostgreSQL, démarrez le serveur en ouvrant pgAdmin, l'interface graphique qui sera installée en même temps que Postgres. Après avoir installé PostgreSQL, démarrez le serveur en ouvrant pgAdmin, l'interface graphique qui sera installée en même temps que Postgres.
- Créez une nouvelle base de données :
Dans le menu de gauche, dépliez `Servers (1)`, puis votre serveur (nommé `PostgreSQL XX` par défaut), puis faites un clic droit sur l'onglet `Databases` et créez une base de données nommée `oser_backend_db`.
### Installation du projet ### Installation du projet
- (Recommandé) Créez un environnement virtuel (ici appelé `env`) puis activez-le : - (Recommandé) Créez un environnement virtuel (appelé ici oser-back) à l'aide de conda et activez-le :
```bash ```bash
$ python -m venv env $ conda create -n oser-back
$ source env/bin/activate $ conda activate oser-back
``` ```
- Installez les dépendances : - Installez les dépendances :
...@@ -70,6 +74,7 @@ $ pip install -r requirements.txt ...@@ -70,6 +74,7 @@ $ pip install -r requirements.txt
```bash ```bash
$ python manage.py migrate $ python manage.py migrate
``` ```
(En cas d'erreur, les logs du serveur PostgreSQL sont disponibles dans : %PROGRAMFILES%\PostgreSQL\POSTGRESQL_VERSION_NUM\data\log)
Il ne vous reste plus qu'à lancer le serveur de développement : Il ne vous reste plus qu'à lancer le serveur de développement :
......
...@@ -26,7 +26,7 @@ router.register('users', users_views.UserViewSet) ...@@ -26,7 +26,7 @@ router.register('users', users_views.UserViewSet)
# Profiles views # Profiles views
router.register('tutors', profiles_views.TutorViewSet) router.register('tutors', profiles_views.TutorViewSet)
router.register('students', profiles_views.StudentViewSet) router.register('students', profiles_views.StudentViewSet, base_name='student')
# Register views # Register views
router.register('registrations', register_views.RegistrationViewSet) router.register('registrations', register_views.RegistrationViewSet)
......
...@@ -5,7 +5,7 @@ import factory.django ...@@ -5,7 +5,7 @@ import factory.django
from . import models from . import models
class DocumentFactory(factory.DjangoModelFactory): class DocumentFactory(factory.django.DjangoModelFactory):
"""Document object factory.""" """Document object factory."""
class Meta: # noqa class Meta: # noqa
...@@ -15,7 +15,7 @@ class DocumentFactory(factory.DjangoModelFactory): ...@@ -15,7 +15,7 @@ class DocumentFactory(factory.DjangoModelFactory):
content = factory.Faker('text', max_nb_chars=1000, locale='fr') content = factory.Faker('text', max_nb_chars=1000, locale='fr')
class AddressFactory(factory.DjangoModelFactory): class AddressFactory(factory.django.DjangoModelFactory):
"""Address object factory.""" """Address object factory."""
class Meta: # noqa class Meta: # noqa
......
...@@ -28,7 +28,7 @@ class PasswordResetSerializer(serializers.Serializer): ...@@ -28,7 +28,7 @@ class PasswordResetSerializer(serializers.Serializer):
###### USE YOUR TEXT FILE ###### ###### USE YOUR TEXT FILE ######
'email_template_name': 'email-reset-template.txt', 'email_template_name': 'email-reset-template.txt',
'subject_template_name': 'subject-reset-template.txt',
'request': request, 'request': request,
} }
self.reset_form.save(**opts) self.reset_form.save(**opts)
\ No newline at end of file
...@@ -5,10 +5,13 @@ Common settings suitable for all environmebts. ...@@ -5,10 +5,13 @@ Common settings suitable for all environmebts.
""" """
import os import os
from dotenv import load_dotenv
import dj_database_url import dj_database_url
import pymdownx.emoji import pymdownx.emoji
load_dotenv()
# Build paths inside the project like this: os.path.join(BASE_DIR, ...) # Build paths inside the project like this: os.path.join(BASE_DIR, ...)
dn = os.path.dirname dn = os.path.dirname
BASE_DIR = dn(dn(dn(os.path.abspath(__file__)))) BASE_DIR = dn(dn(dn(os.path.abspath(__file__))))
...@@ -35,7 +38,7 @@ DJANGO_APPS = [ ...@@ -35,7 +38,7 @@ DJANGO_APPS = [
'whitenoise.runserver_nostatic', 'whitenoise.runserver_nostatic',
'django.contrib.staticfiles', 'django.contrib.staticfiles',
'django.forms', 'django.forms',
'django.contrib.sites', 'django.contrib.sites'
] ]
THIRD_PARTY_APPS = [ THIRD_PARTY_APPS = [
......
...@@ -5,7 +5,7 @@ from .common import * ...@@ -5,7 +5,7 @@ from .common import *
from .common import BASE_DIR from .common import BASE_DIR
DEBUG = True DEBUG = True
ALLOWED_HOSTS = ['localhost'] ALLOWED_HOSTS = ['localhost','127.0.0.1']
# Static files (CSS, JavaScript, Images) and media files (user-uploaded) # Static files (CSS, JavaScript, Images) and media files (user-uploaded)
...@@ -25,4 +25,4 @@ DEFAULT_FILE_STORAGE = 'django.core.files.storage.FileSystemStorage' ...@@ -25,4 +25,4 @@ DEFAULT_FILE_STORAGE = 'django.core.files.storage.FileSystemStorage'
# Mails config # Mails config
# However emails won't be delivered by SendGrid (use dev_sendgrid settings) # However emails won't be delivered by SendGrid (use dev_sendgrid settings)
MAILS_ENABLED = True MAILS_ENABLED = False
...@@ -5,3 +5,4 @@ from .dev import * ...@@ -5,3 +5,4 @@ from .dev import *
# Allow to send emails with SendGrid while in DEBUG mode. # Allow to send emails with SendGrid while in DEBUG mode.
# See: https://github.com/sklarsa/django-sendgrid-v5#other-settings # See: https://github.com/sklarsa/django-sendgrid-v5#other-settings
SENDGRID_SANDBOX_MODE_IN_DEBUG = False SENDGRID_SANDBOX_MODE_IN_DEBUG = False
from django.contrib import admin
from django.contrib.admin.utils import reverse_field_path
from django.utils.translation import gettext_lazy as _
class MultiSelectFieldListFilter(admin.FieldListFilter):
def __init__(self, field, request, params, model, model_admin, field_path):
self.lookup_kwarg = field_path + '__in'
self.lookup_kwarg_isnull = field_path + '__isnull'
super().__init__(field, request, params, model, model_admin, field_path)
self.lookup_val = self.used_parameters.get(self.lookup_kwarg, [])
if len(self.lookup_val) == 1 and self.lookup_val[0] == '':
self.lookup_val = []
self.lookup_val_isnull = self.used_parameters.get(self.lookup_kwarg_isnull)
self.empty_value_display = model_admin.get_empty_value_display()
parent_model, reverse_path = reverse_field_path(model, field_path)
# Obey parent ModelAdmin queryset when deciding which options to show
if model == parent_model:
queryset = model_admin.get_queryset(request)
else:
queryset = parent_model._default_manager.all()
self.lookup_choices = queryset.distinct().order_by(field.name).values_list(field.name, flat=True)
def expected_parameters(self):
return [self.lookup_kwarg, self.lookup_kwarg_isnull]
def choices(self, changelist):
yield {
'selected': not self.lookup_val and self.lookup_val_isnull is None,
'query_string': changelist.get_query_string(remove=[self.lookup_kwarg, self.lookup_kwarg_isnull]),
'display': _('All'),
}
include_none = False
for val in self.lookup_choices:
if val is None:
include_none = True
continue
val = str(val)
if val in self.lookup_val:
values = [v for v in self.lookup_val if v != val]
else:
values = self.lookup_val + [ val ]
if values:
yield {
'selected': val in self.lookup_val,
'query_string': changelist.get_query_string({self.lookup_kwarg: ','.join(values)}, [self.lookup_kwarg_isnull]),
'display': val,
}
else:
yield {
'selected': val in self.lookup_val,
'query_string': changelist.get_query_string(remove=[self.lookup_kwarg]),
'display': val,
}
if include_none:
yield {
'selected': bool(self.lookup_val_isnull),
'query_string': changelist.get_query_string({self.lookup_kwarg_isnull: 'True'}, [self.lookup_kwarg]),
'display': self.empty_value_display,
}
\ No newline at end of file
...@@ -2,6 +2,8 @@ ...@@ -2,6 +2,8 @@
from django.contrib import admin from django.contrib import admin
from .models import Student, Tutor from .models import Student, Tutor
from .MultiSelectFieldListFilter import MultiSelectFieldListFilter
import codecs
import csv import csv
from django.http import HttpResponse from django.http import HttpResponse
...@@ -13,7 +15,8 @@ class ExportCsvMixin: ...@@ -13,7 +15,8 @@ class ExportCsvMixin:
response = HttpResponse(content_type='text/csv') response = HttpResponse(content_type='text/csv')
response['Content-Disposition'] = 'attachment; filename={}.csv'.format(meta) response['Content-Disposition'] = 'attachment; filename={}.csv'.format(meta)
writer = csv.writer(response) response.write(codecs.BOM_UTF8) #force response to be UTF-8
writer = csv.writer(response, delimiter=';')
writer.writerow(field_names) writer.writerow(field_names)
for obj in queryset: for obj in queryset:
...@@ -21,7 +24,7 @@ class ExportCsvMixin: ...@@ -21,7 +24,7 @@ class ExportCsvMixin:
return response return response
export_as_csv.short_description = "Export Selected" export_as_csv.short_description = "Exporter sélection (en .csv)"
class ProfileAdminMixin: class ProfileAdminMixin:
...@@ -40,10 +43,11 @@ class TutorAdmin(ProfileAdminMixin, admin.ModelAdmin,ExportCsvMixin): ...@@ -40,10 +43,11 @@ class TutorAdmin(ProfileAdminMixin, admin.ModelAdmin,ExportCsvMixin):
model = Tutor model = Tutor
actions = ["export_as_csv"] actions = ["export_as_csv"]
@admin.register(Student) @admin.register(Student)
class StudentAdmin(ProfileAdminMixin, admin.ModelAdmin,ExportCsvMixin): class StudentAdmin(ProfileAdminMixin, admin.ModelAdmin,ExportCsvMixin):
"""Student admin panel.""" """Student admin panel."""
list_filter = (('school',MultiSelectFieldListFilter), 'year')
class Meta: # noqa class Meta: # noqa
model = Student model = Student
actions = ["export_as_csv"] actions = ["export_as_csv"]
\ No newline at end of file
...@@ -12,7 +12,7 @@ from users.factory import UserFactory ...@@ -12,7 +12,7 @@ from users.factory import UserFactory
from . import models from . import models
class StudentFactory(factory.DjangoModelFactory): class StudentFactory(factory.django.DjangoModelFactory):
"""Student object factory.""" """Student object factory."""
class Meta: # noqa class Meta: # noqa
...@@ -24,7 +24,7 @@ class StudentFactory(factory.DjangoModelFactory): ...@@ -24,7 +24,7 @@ class StudentFactory(factory.DjangoModelFactory):
_this_year = datetime.today().year _this_year = datetime.today().year
class TutorFactory(factory.DjangoModelFactory): class TutorFactory(factory.django.DjangoModelFactory):
"""Tutor object factory.""" """Tutor object factory."""
class Meta: # noqa class Meta: # noqa
......
# Generated by Django 2.2 on 2020-09-13 22:57
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('profiles', '0002_auto_20180911_1938'),
]
operations = [
migrations.AddField(
model_name='student',
name='addressNumber',
field=models.IntegerField(blank=True, null=True, verbose_name='numéro de rue'),
),
migrations.AddField(
model_name='student',
name='city',
field=models.CharField(blank=True, max_length=50, null=True, verbose_name='nom de ville'),
),
migrations.AddField(
model_name='student',
name='dependantsNumber',
field=models.CharField(blank=True, max_length=12, null=True, verbose_name="numero d'urgence"),
),
migrations.AddField(
model_name='student',
name='fatherActivity',
field=models.CharField(blank=True, max_length=70, null=True, verbose_name='metier du pere'),
),
migrations.AddField(
model_name='student',
name='gender',
field=models.CharField(blank=True, max_length=20, null=True, verbose_name='genre'),
),
migrations.AddField(
model_name='student',
name='grade',
field=models.CharField(blank=True, max_length=20, null=True, verbose_name='nom du niveau de la classe'),
),
migrations.AddField(
model_name='student',
name='motherActivity',
field=models.CharField(blank=True, max_length=70, null=True, verbose_name='metier de la mere'),
),
migrations.AddField(
model_name='student',
name='parentsEmail',
field=models.EmailField(blank=True, max_length=20, null=True, verbose_name='adresse mail parentale'),
),
migrations.AddField(
model_name='student',
name='parentsPhone',
field=models.CharField(blank=True, max_length=12, null=True, verbose_name='numéro de téléphone parental'),
),
migrations.AddField(
model_name='student',
name='parentsStatus',
field=models.CharField(blank=True, max_length=30, null=True, verbose_name='statut des parents'),
),
migrations.AddField(
model_name='student',
name='personnalPhone',
field=models.CharField(blank=True, max_length=12, null=True, verbose_name='numéro de téléphone personnel'),
),
migrations.AddField(
model_name='student',
name='scholarship',
field=models.BooleanField(blank=True, null=True, verbose_name='boursier'),
),
migrations.AddField(
model_name='student',
name='school',
field=models.CharField(blank=True, max_length=70, null=True, verbose_name="nom de l'ecole"),
),
migrations.AddField(
model_name='student',
name='street',
field=models.CharField(blank=True, max_length=70, null=True, verbose_name='nom de rue'),
),
migrations.AlterField(
model_name='tutor',
name='promotion',
field=models.IntegerField(choices=[(2023, '2023'), (2022, '2022'), (2021, '2021'), (2020, '2020'), (2019, '2019')], default=2023),
),
]
# Generated by Django 2.2 on 2020-09-15 16:27
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('profiles', '0003_auto_20200914_0057'),
]
operations = [
migrations.AlterField(
model_name='student',
name='dependantsNumber',
field=models.CharField(blank=True, max_length=12, null=True, verbose_name="numéro d'urgence"),
),
migrations.AlterField(
model_name='student',
name='fatherActivity',
field=models.CharField(blank=True, max_length=70, null=True, verbose_name='métier du père'),
),
migrations.AlterField(
model_name='student',
name='grade',
field=models.CharField(blank=True, max_length=20, null=True, verbose_name='niveau de la classe'),
),
migrations.AlterField(
model_name='student',
name='motherActivity',
field=models.CharField(blank=True, max_length=70, null=True, verbose_name='métier de la mère'),
),
migrations.AlterField(
model_name='student',
name='scholarship',
field=models.NullBooleanField(verbose_name='boursier'),
),
migrations.AlterField(
model_name='student',
name='school',
field=models.CharField(blank=True, max_length=70, null=True, verbose_name="nom de l'école"),
),
]
# Generated by Django 2.2 on 2020-09-18 11:20
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('profiles', '0003_auto_20200914_0057'),
]
operations = [
migrations.AlterField(
model_name='student',
name='dependantsNumber',
field=models.CharField(blank=True, max_length=12, null=True, verbose_name="numéro d'urgence"),
),
migrations.AlterField(
model_name='student',
name='fatherActivity',
field=models.CharField(blank=True, max_length=70, null=True, verbose_name='métier du père'),
),
migrations.AlterField(
model_name='student',
name='grade',
field=models.CharField(blank=True, max_length=20, null=True, verbose_name='niveau de la classe'),
),
migrations.AlterField(
model_name='student',
name='motherActivity',
field=models.CharField(blank=True, max_length=70, null=True, verbose_name='métier de la mère'),
),
migrations.AlterField(
model_name='student',
name='scholarship',
field=models.NullBooleanField(verbose_name='boursier'),
),
migrations.AlterField(
model_name='student',
name='school',
field=models.CharField(blank=True, max_length=70, null=True, verbose_name="nom de l'école"),
),
]
# Generated by Django 2.2 on 2020-09-18 16:05
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('profiles', '0004_auto_20200918_1320'),
]
operations = [
migrations.AlterField(
model_name='student',
name='scholarship',
field=models.CharField(blank=True, max_length=10, null=True, verbose_name='boursier'),
),
]
# Generated by Django 2.2 on 2020-09-18 16:18
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('profiles', '0005_auto_20200918_1805'),
]
operations = [
migrations.AddField(
model_name='student',
name='nationality',
field=models.CharField(blank=True, max_length=30, null=True, verbose_name='nationalité'),
),
migrations.AddField(
model_name='student',
name='specialTeaching',
field=models.CharField(blank=True, max_length=30, null=True, verbose_name='enseignement de spécialité'),
),
migrations.AddField(
model_name='student',
name='zipCode',
field=models.CharField(blank=True, max_length=10, null=True, verbose_name='code postal'),
),
]
# Generated by Django 2.2 on 2020-09-18 16:24
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('profiles', '0006_auto_20200918_1818'),
('profiles', '0004_auto_20200915_1827'),
]
operations = [
]
# Generated by Django 2.2 on 2020-09-18 17:13
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('profiles', '0007_merge_20200918_1824'),
]
operations = [
migrations.AlterField(
model_name='student',
name='scholarship',
field=models.CharField(blank=True, max_length=10, null=True, verbose_name='boursier'),
),
]
# Generated by Django 2.2 on 2020-09-23 11:29
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('profiles', '0008_auto_20200918_1913'),
]
operations = [
migrations.AlterField(
model_name='student',
name='dependantsNumber',
field=models.IntegerField(blank=True, null=True, verbose_name='nombre de personnes à charge'),
),
migrations.AlterField(
model_name='student',
name='nationality',
field=models.CharField(blank=True, max_length=50, null=True, verbose_name='nationalité'),
),
migrations.AlterField(
model_name='student',
name='parentsEmail',
field=models.EmailField(blank=True, max_length=70, null=True, verbose_name='adresse mail parentale'),
),
migrations.AlterField(
model_name='student',
name='parentsStatus',
field=models.CharField(blank=True, max_length=70, null=True, verbose_name='statut des parents'),
),
migrations.AlterField(
model_name='student',
name='scholarship',
field=models.CharField(blank=True, max_length=50, null=True, verbose_name='boursier'),
),
migrations.AlterField(
model_name='student',
name='specialTeaching',
field=models.CharField(blank=True, max_length=50, null=True, verbose_name='enseignement de spécialité'),
),
]
# Generated by Django 2.2 on 2020-09-23 12:44
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('profiles', '0009_auto_20200923_1329'),
]
operations = [
migrations.RenameField(
model_name='student',
old_name='personnalPhone',
new_name='personalPhone',
),
]
# Generated by Django 2.2 on 2020-09-24 16:56
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('profiles', '0010_auto_20200923_1444'),
]
operations = [
migrations.AddField(
model_name='student',
name='classType',
field=models.CharField(blank=True, max_length=50, null=True, verbose_name='général/techno'),
),
]
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment