mirror of
				https://gitlab.crans.org/bde/nk20
				synced 2025-11-04 01:12:08 +01:00 
			
		
		
		
	Merge branch 'fix-what-i-broke' into 'master'
Corrections Closes #36 See merge request bde/nk20!56
This commit is contained in:
		
							
								
								
									
										13
									
								
								.env_example
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								.env_example
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,13 @@
 | 
				
			|||||||
 | 
					DJANGO_APP_STAGE="dev"
 | 
				
			||||||
 | 
					# Only used in dev mode, change to "postgresql" if you want to use PostgreSQL in dev
 | 
				
			||||||
 | 
					DJANGO_DEV_STORE_METHOD="sqllite"
 | 
				
			||||||
 | 
					DJANGO_DB_HOST="localhost"
 | 
				
			||||||
 | 
					DJANGO_DB_NAME="note_db"
 | 
				
			||||||
 | 
					DJANGO_DB_USER="note"
 | 
				
			||||||
 | 
					DJANGO_DB_PASSWORD="CHANGE_ME"
 | 
				
			||||||
 | 
					DJANGO_DB_PORT=""
 | 
				
			||||||
 | 
					DJANGO_SECRET_KEY="CHANGE_ME"
 | 
				
			||||||
 | 
					DJANGO_SETTINGS_MODULE="note_kfet.settings"
 | 
				
			||||||
 | 
					DOMAIN="localhost"
 | 
				
			||||||
 | 
					CONTACT_EMAIL="tresorerie.bde@localhost"
 | 
				
			||||||
 | 
					NOTE_URL="localhost"
 | 
				
			||||||
@@ -9,13 +9,13 @@ RUN apt update && \
 | 
				
			|||||||
    apt install -y gettext nginx uwsgi uwsgi-plugin-python3 && \
 | 
					    apt install -y gettext nginx uwsgi uwsgi-plugin-python3 && \
 | 
				
			||||||
    rm -rf /var/lib/apt/lists/*
 | 
					    rm -rf /var/lib/apt/lists/*
 | 
				
			||||||
 | 
					
 | 
				
			||||||
COPY requirements.txt /code/
 | 
					COPY . /code/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Comment what is not needed
 | 
				
			||||||
RUN pip install -r requirements/base.txt
 | 
					RUN pip install -r requirements/base.txt
 | 
				
			||||||
RUN pip install -r requirements/api.txt
 | 
					RUN pip install -r requirements/api.txt
 | 
				
			||||||
RUN pip install -r requirements/cas.txt
 | 
					RUN pip install -r requirements/cas.txt
 | 
				
			||||||
RUN pip install -r requirements/production.txt
 | 
					RUN pip install -r requirements/production.txt
 | 
				
			||||||
 | 
					
 | 
				
			||||||
COPY . /code/
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
ENTRYPOINT ["/code/entrypoint.sh"]
 | 
					ENTRYPOINT ["/code/entrypoint.sh"]
 | 
				
			||||||
EXPOSE 8000
 | 
					EXPOSE 8000
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										57
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										57
									
								
								README.md
									
									
									
									
									
								
							@@ -40,14 +40,13 @@ On supposera pour la suite que vous utiliser debian/ubuntu sur un serveur tout n
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        $ cp nginx_note.conf_example nginx_note.conf
 | 
					        $ cp nginx_note.conf_example nginx_note.conf
 | 
				
			||||||
 | 
					
 | 
				
			||||||
***Modifier le fichier pour être en accord avec le reste de votre config***
 | 
					    ***Modifier le fichier pour être en accord avec le reste de votre config***
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    On utilise uwsgi et Nginx pour gérer le coté serveur :
 | 
					    On utilise uwsgi et Nginx pour gérer le coté serveur :
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        $ sudo ln -sf /var/www/note_kfet/nginx_note.conf /etc/nginx/sites-enabled/
 | 
					       $ sudo ln -sf /var/www/note_kfet/nginx_note.conf /etc/nginx/sites-enabled/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   Si l'on a un emperor (plusieurs instance uwsgi):
 | 
				
			||||||
    Si l'on a un emperor (plusieurs instance uwsgi):
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        $ sudo ln -sf /var/www/note_kfet/uwsgi_note.ini /etc/uwsgi/sites/
 | 
					        $ sudo ln -sf /var/www/note_kfet/uwsgi_note.ini /etc/uwsgi/sites/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -85,7 +84,7 @@ On supposera pour la suite que vous utiliser debian/ubuntu sur un serveur tout n
 | 
				
			|||||||
        postgres=# CREATE DATABASE note_db OWNER note;
 | 
					        postgres=# CREATE DATABASE note_db OWNER note;
 | 
				
			||||||
        CREATE DATABASE
 | 
					        CREATE DATABASE
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    Si tout va bien:
 | 
					    Si tout va bien :
 | 
				
			||||||
        
 | 
					        
 | 
				
			||||||
        postgres=#\list
 | 
					        postgres=#\list
 | 
				
			||||||
        List of databases
 | 
					        List of databases
 | 
				
			||||||
@@ -96,22 +95,29 @@ On supposera pour la suite que vous utiliser debian/ubuntu sur un serveur tout n
 | 
				
			|||||||
         template0 | postgres | UTF8     | fr_FR.UTF-8 | fr_FR.UTF-8 | =c/postgres+postgres=CTc/postgres
 | 
					         template0 | postgres | UTF8     | fr_FR.UTF-8 | fr_FR.UTF-8 | =c/postgres+postgres=CTc/postgres
 | 
				
			||||||
         template1 | postgres | UTF8     | fr_FR.UTF-8 | fr_FR.UTF-8 | =c/postgres  +postgres=CTc/postgres
 | 
					         template1 | postgres | UTF8     | fr_FR.UTF-8 | fr_FR.UTF-8 | =c/postgres  +postgres=CTc/postgres
 | 
				
			||||||
        (4 rows)
 | 
					        (4 rows)
 | 
				
			||||||
 | 
					 | 
				
			||||||
    Dans un fichier `.env` à la racine du projet on renseigne des secrets:
 | 
					 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
        DJANGO_APP_STAGE='prod'
 | 
					 | 
				
			||||||
        DJANGO_DB_PASSWORD='le_mot_de_passe_de_la_bdd'
 | 
					 | 
				
			||||||
        DJANGO_SECRET_KEY='une_secret_key_longue_et_compliquee'
 | 
					 | 
				
			||||||
        ALLOWED_HOSTS='le_ndd_de_votre_instance'
 | 
					 | 
				
			||||||
    
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
6. Variable d'environnement et Migrations
 | 
					6. Variable d'environnement et Migrations
 | 
				
			||||||
        
 | 
					        
 | 
				
			||||||
 | 
					    On copie le fichier `.env_example` vers le fichier `.env` à la racine du projet 
 | 
				
			||||||
 | 
					    et on renseigne des secrets et des paramètres :
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					        DJANGO_APP_STAGE="dev"
 | 
				
			||||||
 | 
					        DJANGO_DEV_STORE_METHOD="sqllite"
 | 
				
			||||||
 | 
					        DJANGO_DB_HOST="localhost"
 | 
				
			||||||
 | 
					        DJANGO_DB_NAME="note_db"
 | 
				
			||||||
 | 
					        DJANGO_DB_USER="note"
 | 
				
			||||||
 | 
					        DJANGO_DB_PASSWORD="CHANGE_ME"
 | 
				
			||||||
 | 
					        DJANGO_DB_PORT=""
 | 
				
			||||||
 | 
					        DJANGO_SECRET_KEY="CHANGE_ME"
 | 
				
			||||||
 | 
					        DJANGO_SETTINGS_MODULE="note_kfet.settings"
 | 
				
			||||||
 | 
					        DOMAIN="localhost"
 | 
				
			||||||
 | 
					        CONTACT_EMAIL="tresorerie.bde@localhost"
 | 
				
			||||||
 | 
					        NOTE_URL="localhost"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Ensuite on (re)bascule dans l'environement virtuel et on lance les migrations
 | 
					    Ensuite on (re)bascule dans l'environement virtuel et on lance les migrations
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        $ source /env/bin/activate
 | 
					        $ source /env/bin/activate
 | 
				
			||||||
        (env)$ ./manage.py check # pas de bétise qui traine
 | 
					        (env)$ ./manage.py check # pas de bêtise qui traine
 | 
				
			||||||
        (env)$ ./manage.py makemigrations
 | 
					        (env)$ ./manage.py makemigrations
 | 
				
			||||||
        (env)$ ./manage.py migrate
 | 
					        (env)$ ./manage.py migrate
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -126,17 +132,21 @@ Il est possible de travailler sur une instance Docker.
 | 
				
			|||||||
    
 | 
					    
 | 
				
			||||||
        $ git clone git@gitlab.crans.org:bde/nk20.git
 | 
					        $ git clone git@gitlab.crans.org:bde/nk20.git
 | 
				
			||||||
 | 
					
 | 
				
			||||||
2. Dans le fichier `docker_compose.yml`, qu'on suppose déjà configuré,
 | 
					2. Copiez le fichier `.env_example` à la racine du projet vers le fichier `.env`,
 | 
				
			||||||
 | 
					et  mettez à jour vos variables d'environnement
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					3. Dans le fichier `docker_compose.yml`, qu'on suppose déjà configuré,
 | 
				
			||||||
   ajouter les lignes suivantes, en les adaptant à la configuration voulue :
 | 
					   ajouter les lignes suivantes, en les adaptant à la configuration voulue :
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        nk20:
 | 
					        nk20:
 | 
				
			||||||
          build: /chemin/vers/nk20
 | 
					          build: /chemin/vers/nk20
 | 
				
			||||||
          volumes:
 | 
					          volumes:
 | 
				
			||||||
            - /chemin/vers/nk20:/code/
 | 
					            - /chemin/vers/nk20:/code/
 | 
				
			||||||
 | 
					          env_file: /chemin/vers/nk20/.env
 | 
				
			||||||
          restart: always
 | 
					          restart: always
 | 
				
			||||||
          labels:
 | 
					          labels:
 | 
				
			||||||
            - traefik.domain=ndd.exemple.com
 | 
					            - traefik.domain=ndd.example.com
 | 
				
			||||||
            - traefik.frontend.rule=Host:ndd.exemple.com
 | 
					            - traefik.frontend.rule=Host:ndd.example.com
 | 
				
			||||||
            - traefik.port=8000
 | 
					            - traefik.port=8000
 | 
				
			||||||
 | 
					
 | 
				
			||||||
3. Enjoy :
 | 
					3. Enjoy :
 | 
				
			||||||
@@ -159,17 +169,20 @@ un serveur de développement par exemple sur son ordinateur.
 | 
				
			|||||||
        $ source venv/bin/activate
 | 
					        $ source venv/bin/activate
 | 
				
			||||||
        (env)$ pip install -r requirements.txt
 | 
					        (env)$ pip install -r requirements.txt
 | 
				
			||||||
 | 
					
 | 
				
			||||||
3. Migrations et chargement des données initiales :
 | 
					3. Copier le fichier `.env_example` vers `.env` à la racine du projet et mettre à jour
 | 
				
			||||||
 | 
					ce qu'il faut
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					4. Migrations et chargement des données initiales :
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        (env)$ ./manage.py makemigrations
 | 
					        (env)$ ./manage.py makemigrations
 | 
				
			||||||
        (env)$ ./manage.py migrate
 | 
					        (env)$ ./manage.py migrate
 | 
				
			||||||
        (env)$ ./manage.py loaddata initial
 | 
					        (env)$ ./manage.py loaddata initial
 | 
				
			||||||
 | 
					
 | 
				
			||||||
4. Créer un super-utilisateur :
 | 
					5. Créer un super-utilisateur :
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        (env)$ ./manage.py createsuperuser
 | 
					        (env)$ ./manage.py createsuperuser
 | 
				
			||||||
 | 
					
 | 
				
			||||||
5. Enjoy :
 | 
					6. Enjoy :
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        (env)$ ./manage.py runserver 0.0.0.0:8000
 | 
					        (env)$ ./manage.py runserver 0.0.0.0:8000
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -184,4 +197,4 @@ Il est disponible [ici](https://wiki.crans.org/NoteKfet/NoteKfet2018/CdC).
 | 
				
			|||||||
## Documentation
 | 
					## Documentation
 | 
				
			||||||
 | 
					
 | 
				
			||||||
La documentation est générée par django et son module admindocs.
 | 
					La documentation est générée par django et son module admindocs.
 | 
				
			||||||
**Commenter votre code !**
 | 
					**Commentez votre code !**
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,7 +1,8 @@
 | 
				
			|||||||
# Copyright (C) 2018-2020 by BDE ENS Paris-Saclay
 | 
					# Copyright (C) 2018-2020 by BDE ENS Paris-Saclay
 | 
				
			||||||
# SPDX-License-Identifier: GPL-3.0-or-later
 | 
					# SPDX-License-Identifier: GPL-3.0-or-later
 | 
				
			||||||
 | 
					from django_filters.rest_framework import DjangoFilterBackend
 | 
				
			||||||
from rest_framework import viewsets
 | 
					from rest_framework import viewsets
 | 
				
			||||||
 | 
					from rest_framework.filters import SearchFilter
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from .serializers import ActivityTypeSerializer, ActivitySerializer, GuestSerializer
 | 
					from .serializers import ActivityTypeSerializer, ActivitySerializer, GuestSerializer
 | 
				
			||||||
from ..models import ActivityType, Activity, Guest
 | 
					from ..models import ActivityType, Activity, Guest
 | 
				
			||||||
@@ -15,6 +16,8 @@ class ActivityTypeViewSet(viewsets.ModelViewSet):
 | 
				
			|||||||
    """
 | 
					    """
 | 
				
			||||||
    queryset = ActivityType.objects.all()
 | 
					    queryset = ActivityType.objects.all()
 | 
				
			||||||
    serializer_class = ActivityTypeSerializer
 | 
					    serializer_class = ActivityTypeSerializer
 | 
				
			||||||
 | 
					    filter_backends = [DjangoFilterBackend]
 | 
				
			||||||
 | 
					    filterset_fields = ['name', 'can_invite', ]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class ActivityViewSet(viewsets.ModelViewSet):
 | 
					class ActivityViewSet(viewsets.ModelViewSet):
 | 
				
			||||||
@@ -25,6 +28,8 @@ class ActivityViewSet(viewsets.ModelViewSet):
 | 
				
			|||||||
    """
 | 
					    """
 | 
				
			||||||
    queryset = Activity.objects.all()
 | 
					    queryset = Activity.objects.all()
 | 
				
			||||||
    serializer_class = ActivitySerializer
 | 
					    serializer_class = ActivitySerializer
 | 
				
			||||||
 | 
					    filter_backends = [DjangoFilterBackend]
 | 
				
			||||||
 | 
					    filterset_fields = ['name', 'description', 'activity_type', ]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class GuestViewSet(viewsets.ModelViewSet):
 | 
					class GuestViewSet(viewsets.ModelViewSet):
 | 
				
			||||||
@@ -35,3 +40,5 @@ class GuestViewSet(viewsets.ModelViewSet):
 | 
				
			|||||||
    """
 | 
					    """
 | 
				
			||||||
    queryset = Guest.objects.all()
 | 
					    queryset = Guest.objects.all()
 | 
				
			||||||
    serializer_class = GuestSerializer
 | 
					    serializer_class = GuestSerializer
 | 
				
			||||||
 | 
					    filter_backends = [SearchFilter]
 | 
				
			||||||
 | 
					    search_fields = ['$name', ]
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -3,10 +3,14 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
from django.conf.urls import url, include
 | 
					from django.conf.urls import url, include
 | 
				
			||||||
from django.contrib.auth.models import User
 | 
					from django.contrib.auth.models import User
 | 
				
			||||||
 | 
					from django.contrib.contenttypes.models import ContentType
 | 
				
			||||||
 | 
					from django_filters.rest_framework import DjangoFilterBackend
 | 
				
			||||||
from rest_framework import routers, serializers, viewsets
 | 
					from rest_framework import routers, serializers, viewsets
 | 
				
			||||||
 | 
					from rest_framework.filters import SearchFilter
 | 
				
			||||||
from activity.api.urls import register_activity_urls
 | 
					from activity.api.urls import register_activity_urls
 | 
				
			||||||
from member.api.urls import register_members_urls
 | 
					from member.api.urls import register_members_urls
 | 
				
			||||||
from note.api.urls import register_note_urls
 | 
					from note.api.urls import register_note_urls
 | 
				
			||||||
 | 
					from logs.api.urls import register_logs_urls
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class UserSerializer(serializers.ModelSerializer):
 | 
					class UserSerializer(serializers.ModelSerializer):
 | 
				
			||||||
@@ -24,6 +28,17 @@ class UserSerializer(serializers.ModelSerializer):
 | 
				
			|||||||
        )
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class ContentTypeSerializer(serializers.ModelSerializer):
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					    REST API Serializer for Users.
 | 
				
			||||||
 | 
					    The djangorestframework plugin will analyse the model `User` and parse all fields in the API.
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    class Meta:
 | 
				
			||||||
 | 
					        model = ContentType
 | 
				
			||||||
 | 
					        fields = '__all__'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class UserViewSet(viewsets.ModelViewSet):
 | 
					class UserViewSet(viewsets.ModelViewSet):
 | 
				
			||||||
    """
 | 
					    """
 | 
				
			||||||
    REST API View set.
 | 
					    REST API View set.
 | 
				
			||||||
@@ -32,15 +47,30 @@ class UserViewSet(viewsets.ModelViewSet):
 | 
				
			|||||||
    """
 | 
					    """
 | 
				
			||||||
    queryset = User.objects.all()
 | 
					    queryset = User.objects.all()
 | 
				
			||||||
    serializer_class = UserSerializer
 | 
					    serializer_class = UserSerializer
 | 
				
			||||||
 | 
					    filter_backends = [DjangoFilterBackend, SearchFilter]
 | 
				
			||||||
 | 
					    filterset_fields = ['id', 'username', 'first_name', 'last_name', 'email', 'is_superuser', 'is_staff', 'is_active', ]
 | 
				
			||||||
 | 
					    search_fields = ['$username', '$first_name', '$last_name', ]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class ContentTypeViewSet(viewsets.ReadOnlyModelViewSet):
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					    REST API View set.
 | 
				
			||||||
 | 
					    The djangorestframework plugin will get all `User` objects, serialize it to JSON with the given serializer,
 | 
				
			||||||
 | 
					    then render it on /api/users/
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					    queryset = ContentType.objects.all()
 | 
				
			||||||
 | 
					    serializer_class = ContentTypeSerializer
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# Routers provide an easy way of automatically determining the URL conf.
 | 
					# Routers provide an easy way of automatically determining the URL conf.
 | 
				
			||||||
# Register each app API router and user viewset
 | 
					# Register each app API router and user viewset
 | 
				
			||||||
router = routers.DefaultRouter()
 | 
					router = routers.DefaultRouter()
 | 
				
			||||||
 | 
					router.register('models', ContentTypeViewSet)
 | 
				
			||||||
router.register('user', UserViewSet)
 | 
					router.register('user', UserViewSet)
 | 
				
			||||||
register_members_urls(router, 'members')
 | 
					register_members_urls(router, 'members')
 | 
				
			||||||
register_activity_urls(router, 'activity')
 | 
					register_activity_urls(router, 'activity')
 | 
				
			||||||
register_note_urls(router, 'note')
 | 
					register_note_urls(router, 'note')
 | 
				
			||||||
 | 
					register_logs_urls(router, 'logs')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
app_name = 'api'
 | 
					app_name = 'api'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										0
									
								
								apps/logs/api/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								apps/logs/api/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										19
									
								
								apps/logs/api/serializers.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								apps/logs/api/serializers.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,19 @@
 | 
				
			|||||||
 | 
					# Copyright (C) 2018-2020 by BDE ENS Paris-Saclay
 | 
				
			||||||
 | 
					# SPDX-License-Identifier: GPL-3.0-or-later
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from rest_framework import serializers
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from ..models import Changelog
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class ChangelogSerializer(serializers.ModelSerializer):
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					    REST API Serializer for Changelog types.
 | 
				
			||||||
 | 
					    The djangorestframework plugin will analyse the model `Changelog` and parse all fields in the API.
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    class Meta:
 | 
				
			||||||
 | 
					        model = Changelog
 | 
				
			||||||
 | 
					        fields = '__all__'
 | 
				
			||||||
 | 
					        # noinspection PyProtectedMember
 | 
				
			||||||
 | 
					        read_only_fields = [f.name for f in model._meta.get_fields()]  # Changelogs are read-only protected
 | 
				
			||||||
							
								
								
									
										11
									
								
								apps/logs/api/urls.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								apps/logs/api/urls.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,11 @@
 | 
				
			|||||||
 | 
					# Copyright (C) 2018-2020 by BDE ENS Paris-Saclay
 | 
				
			||||||
 | 
					# SPDX-License-Identifier: GPL-3.0-or-later
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from .views import ChangelogViewSet
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def register_logs_urls(router, path):
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					    Configure router for Activity REST API.
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					    router.register(path, ChangelogViewSet)
 | 
				
			||||||
							
								
								
									
										23
									
								
								apps/logs/api/views.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								apps/logs/api/views.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,23 @@
 | 
				
			|||||||
 | 
					# Copyright (C) 2018-2020 by BDE ENS Paris-Saclay
 | 
				
			||||||
 | 
					# SPDX-License-Identifier: GPL-3.0-or-later
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from django_filters.rest_framework import DjangoFilterBackend
 | 
				
			||||||
 | 
					from rest_framework import viewsets
 | 
				
			||||||
 | 
					from rest_framework.filters import OrderingFilter
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from .serializers import ChangelogSerializer
 | 
				
			||||||
 | 
					from ..models import Changelog
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class ChangelogViewSet(viewsets.ReadOnlyModelViewSet):
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					    REST API View set.
 | 
				
			||||||
 | 
					    The djangorestframework plugin will get all `Changelog` objects, serialize it to JSON with the given serializer,
 | 
				
			||||||
 | 
					    then render it on /api/logs/
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					    queryset = Changelog.objects.all()
 | 
				
			||||||
 | 
					    serializer_class = ChangelogSerializer
 | 
				
			||||||
 | 
					    filter_backends = [DjangoFilterBackend, OrderingFilter]
 | 
				
			||||||
 | 
					    filterset_fields = ['model', 'action', "instance_pk", 'user', 'ip', ]
 | 
				
			||||||
 | 
					    ordering_fields = ['timestamp', ]
 | 
				
			||||||
 | 
					    ordering = ['-timestamp', ]
 | 
				
			||||||
@@ -2,6 +2,7 @@
 | 
				
			|||||||
# SPDX-License-Identifier: GPL-3.0-or-later
 | 
					# SPDX-License-Identifier: GPL-3.0-or-later
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from django.apps import AppConfig
 | 
					from django.apps import AppConfig
 | 
				
			||||||
 | 
					from django.db.models.signals import pre_save, post_save, post_delete
 | 
				
			||||||
from django.utils.translation import gettext_lazy as _
 | 
					from django.utils.translation import gettext_lazy as _
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -11,4 +12,7 @@ class LogsConfig(AppConfig):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    def ready(self):
 | 
					    def ready(self):
 | 
				
			||||||
        # noinspection PyUnresolvedReferences
 | 
					        # noinspection PyUnresolvedReferences
 | 
				
			||||||
        import logs.signals
 | 
					        from . import signals
 | 
				
			||||||
 | 
					        pre_save.connect(signals.pre_save_object)
 | 
				
			||||||
 | 
					        post_save.connect(signals.save_object)
 | 
				
			||||||
 | 
					        post_delete.connect(signals.delete_object)
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										55
									
								
								apps/logs/middlewares.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										55
									
								
								apps/logs/middlewares.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,55 @@
 | 
				
			|||||||
 | 
					# Copyright (C) 2018-2020 by BDE ENS Paris-Saclay
 | 
				
			||||||
 | 
					# SPDX-License-Identifier: GPL-3.0-or-later
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from django.conf import settings
 | 
				
			||||||
 | 
					from django.contrib.auth.models import AnonymousUser
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from threading import local
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					USER_ATTR_NAME = getattr(settings, 'LOCAL_USER_ATTR_NAME', '_current_user')
 | 
				
			||||||
 | 
					IP_ATTR_NAME = getattr(settings, 'LOCAL_IP_ATTR_NAME', '_current_ip')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					_thread_locals = local()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def _set_current_user_and_ip(user=None, ip=None):
 | 
				
			||||||
 | 
					    setattr(_thread_locals, USER_ATTR_NAME, user)
 | 
				
			||||||
 | 
					    setattr(_thread_locals, IP_ATTR_NAME, ip)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def get_current_user():
 | 
				
			||||||
 | 
					    return getattr(_thread_locals, USER_ATTR_NAME, None)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def get_current_ip():
 | 
				
			||||||
 | 
					    return getattr(_thread_locals, IP_ATTR_NAME, None)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def get_current_authenticated_user():
 | 
				
			||||||
 | 
					    current_user = get_current_user()
 | 
				
			||||||
 | 
					    if isinstance(current_user, AnonymousUser):
 | 
				
			||||||
 | 
					        return None
 | 
				
			||||||
 | 
					    return current_user
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class LogsMiddleware(object):
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					    This middleware get the current user with his or her IP address on each request.
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __init__(self, get_response):
 | 
				
			||||||
 | 
					        self.get_response = get_response
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __call__(self, request):
 | 
				
			||||||
 | 
					        user = request.user
 | 
				
			||||||
 | 
					        if 'HTTP_X_FORWARDED_FOR' in request.META:
 | 
				
			||||||
 | 
					            ip = request.META.get('HTTP_X_FORWARDED_FOR')
 | 
				
			||||||
 | 
					        else:
 | 
				
			||||||
 | 
					            ip = request.META.get('REMOTE_ADDR')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        _set_current_user_and_ip(user, ip)
 | 
				
			||||||
 | 
					        response = self.get_response(request)
 | 
				
			||||||
 | 
					        _set_current_user_and_ip(None, None)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return response
 | 
				
			||||||
@@ -56,6 +56,12 @@ class Changelog(models.Model):
 | 
				
			|||||||
        max_length=16,
 | 
					        max_length=16,
 | 
				
			||||||
        null=False,
 | 
					        null=False,
 | 
				
			||||||
        blank=False,
 | 
					        blank=False,
 | 
				
			||||||
 | 
					        choices=[
 | 
				
			||||||
 | 
					            ('create', _('create')),
 | 
				
			||||||
 | 
					            ('edit', _('edit')),
 | 
				
			||||||
 | 
					            ('delete', _('delete')),
 | 
				
			||||||
 | 
					        ],
 | 
				
			||||||
 | 
					        default='edit',
 | 
				
			||||||
        verbose_name=_('action'),
 | 
					        verbose_name=_('action'),
 | 
				
			||||||
    )
 | 
					    )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,67 +1,40 @@
 | 
				
			|||||||
# Copyright (C) 2018-2020 by BDE ENS Paris-Saclay
 | 
					# Copyright (C) 2018-2020 by BDE ENS Paris-Saclay
 | 
				
			||||||
# SPDX-License-Identifier: GPL-3.0-or-later
 | 
					# SPDX-License-Identifier: GPL-3.0-or-later
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import inspect
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
from django.contrib.contenttypes.models import ContentType
 | 
					from django.contrib.contenttypes.models import ContentType
 | 
				
			||||||
from django.core import serializers
 | 
					from rest_framework.renderers import JSONRenderer
 | 
				
			||||||
from django.db.models.signals import pre_save, post_save, post_delete
 | 
					from rest_framework.serializers import ModelSerializer
 | 
				
			||||||
from django.dispatch import receiver
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import getpass
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from note.models import NoteUser, Alias
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from .middlewares import get_current_authenticated_user, get_current_ip
 | 
				
			||||||
from .models import Changelog
 | 
					from .models import Changelog
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def get_request_in_signal(sender):
 | 
					# Ces modèles ne nécessitent pas de logs
 | 
				
			||||||
    req = None
 | 
					 | 
				
			||||||
    for entry in reversed(inspect.stack()):
 | 
					 | 
				
			||||||
        try:
 | 
					 | 
				
			||||||
            req = entry[0].f_locals['request']
 | 
					 | 
				
			||||||
            # Check if there is a user
 | 
					 | 
				
			||||||
            # noinspection PyStatementEffect
 | 
					 | 
				
			||||||
            req.user
 | 
					 | 
				
			||||||
            break
 | 
					 | 
				
			||||||
        except:
 | 
					 | 
				
			||||||
            pass
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    if not req:
 | 
					 | 
				
			||||||
        print("WARNING: Attempt to save " + str(sender) + " with no user")
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    return req
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
def get_user_and_ip(sender):
 | 
					 | 
				
			||||||
    req = get_request_in_signal(sender)
 | 
					 | 
				
			||||||
    try:
 | 
					 | 
				
			||||||
        user = req.user
 | 
					 | 
				
			||||||
        if 'HTTP_X_FORWARDED_FOR' in req.META:
 | 
					 | 
				
			||||||
            ip = req.META.get('HTTP_X_FORWARDED_FOR')
 | 
					 | 
				
			||||||
        else:
 | 
					 | 
				
			||||||
            ip = req.META.get('REMOTE_ADDR')
 | 
					 | 
				
			||||||
    except:
 | 
					 | 
				
			||||||
        user = None
 | 
					 | 
				
			||||||
        ip = None
 | 
					 | 
				
			||||||
    return user, ip
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
EXCLUDED = [
 | 
					EXCLUDED = [
 | 
				
			||||||
    'admin.logentry',
 | 
					    'admin.logentry',
 | 
				
			||||||
    'authtoken.token',
 | 
					    'authtoken.token',
 | 
				
			||||||
 | 
					    'cas_server.proxygrantingticket',
 | 
				
			||||||
 | 
					    'cas_server.proxyticket',
 | 
				
			||||||
 | 
					    'cas_server.serviceticket',
 | 
				
			||||||
    'cas_server.user',
 | 
					    'cas_server.user',
 | 
				
			||||||
    'cas_server.userattributes',
 | 
					    'cas_server.userattributes',
 | 
				
			||||||
    'contenttypes.contenttype',
 | 
					    'contenttypes.contenttype',
 | 
				
			||||||
    'logs.changelog',
 | 
					    'logs.changelog',  # Never remove this line
 | 
				
			||||||
    'migrations.migration',
 | 
					    'migrations.migration',
 | 
				
			||||||
    'note.noteuser',
 | 
					    'note.note'  # We only store the subclasses
 | 
				
			||||||
    'note.noteclub',
 | 
					    'note.transaction',
 | 
				
			||||||
    'note.notespecial',
 | 
					 | 
				
			||||||
    'sessions.session',
 | 
					    'sessions.session',
 | 
				
			||||||
    'reversion.revision',
 | 
					 | 
				
			||||||
    'reversion.version',
 | 
					 | 
				
			||||||
]
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@receiver(pre_save)
 | 
					 | 
				
			||||||
def pre_save_object(sender, instance, **kwargs):
 | 
					def pre_save_object(sender, instance, **kwargs):
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					    Before a model get saved, we get the previous instance that is currently in the database
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
    qs = sender.objects.filter(pk=instance.pk).all()
 | 
					    qs = sender.objects.filter(pk=instance.pk).all()
 | 
				
			||||||
    if qs.exists():
 | 
					    if qs.exists():
 | 
				
			||||||
        instance._previous = qs.get()
 | 
					        instance._previous = qs.get()
 | 
				
			||||||
@@ -69,30 +42,51 @@ def pre_save_object(sender, instance, **kwargs):
 | 
				
			|||||||
        instance._previous = None
 | 
					        instance._previous = None
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@receiver(post_save)
 | 
					 | 
				
			||||||
def save_object(sender, instance, **kwargs):
 | 
					def save_object(sender, instance, **kwargs):
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					    Each time a model is saved, an entry in the table `Changelog` is added in the database
 | 
				
			||||||
 | 
					    in order to store each modification made
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
    # noinspection PyProtectedMember
 | 
					    # noinspection PyProtectedMember
 | 
				
			||||||
    if instance._meta.label_lower in EXCLUDED:
 | 
					    if instance._meta.label_lower in EXCLUDED:
 | 
				
			||||||
        return
 | 
					        return
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # noinspection PyProtectedMember
 | 
				
			||||||
    previous = instance._previous
 | 
					    previous = instance._previous
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    user, ip = get_user_and_ip(sender)
 | 
					    # Si un utilisateur est connecté, on récupère l'utilisateur courant ainsi que son adresse IP
 | 
				
			||||||
 | 
					    user, ip = get_current_authenticated_user(), get_current_ip()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    from django.contrib.auth.models import AnonymousUser
 | 
					    if user is None:
 | 
				
			||||||
    if isinstance(user, AnonymousUser):
 | 
					        # Si la modification n'a pas été faite via le client Web, on suppose que c'est du à `manage.py`
 | 
				
			||||||
        user = None
 | 
					        # On récupère alors l'utilisateur·trice connecté·e à la VM, et on récupère la note associée
 | 
				
			||||||
 | 
					        # IMPORTANT : l'utilisateur dans la VM doit être un des alias note du respo info
 | 
				
			||||||
 | 
					        ip = "127.0.0.1"
 | 
				
			||||||
 | 
					        username = Alias.normalize(getpass.getuser())
 | 
				
			||||||
 | 
					        note = NoteUser.objects.filter(alias__normalized_name=username)
 | 
				
			||||||
 | 
					        # if not note.exists():
 | 
				
			||||||
 | 
					        #     print("WARNING: A model attempted to be saved in the DB, but the actor is unknown: " + username)
 | 
				
			||||||
 | 
					        # else:
 | 
				
			||||||
 | 
					        if note.exists():
 | 
				
			||||||
 | 
					            user = note.get().user
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # noinspection PyProtectedMember
 | 
				
			||||||
    if user is not None and instance._meta.label_lower == "auth.user" and previous:
 | 
					    if user is not None and instance._meta.label_lower == "auth.user" and previous:
 | 
				
			||||||
        # Don't save last login modifications
 | 
					        # On n'enregistre pas les connexions
 | 
				
			||||||
        if instance.last_login != previous.last_login:
 | 
					        if instance.last_login != previous.last_login:
 | 
				
			||||||
            return
 | 
					            return
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    previous_json = serializers.serialize('json', [previous, ])[1:-1] if previous else None
 | 
					    # On crée notre propre sérialiseur JSON pour pouvoir sauvegarder les modèles
 | 
				
			||||||
    instance_json = serializers.serialize('json', [instance, ])[1:-1]
 | 
					    class CustomSerializer(ModelSerializer):
 | 
				
			||||||
 | 
					        class Meta:
 | 
				
			||||||
 | 
					            model = instance.__class__
 | 
				
			||||||
 | 
					            fields = '__all__'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    previous_json = JSONRenderer().render(CustomSerializer(previous).data).decode("UTF-8") if previous else None
 | 
				
			||||||
 | 
					    instance_json = JSONRenderer().render(CustomSerializer(instance).data).decode("UTF-8")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if previous_json == instance_json:
 | 
					    if previous_json == instance_json:
 | 
				
			||||||
        # No modification
 | 
					        # Pas de log s'il n'y a pas de modification
 | 
				
			||||||
        return
 | 
					        return
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    Changelog.objects.create(user=user,
 | 
					    Changelog.objects.create(user=user,
 | 
				
			||||||
@@ -105,15 +99,38 @@ def save_object(sender, instance, **kwargs):
 | 
				
			|||||||
                             ).save()
 | 
					                             ).save()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@receiver(post_delete)
 | 
					 | 
				
			||||||
def delete_object(sender, instance, **kwargs):
 | 
					def delete_object(sender, instance, **kwargs):
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					    Each time a model is deleted, an entry in the table `Changelog` is added in the database
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
    # noinspection PyProtectedMember
 | 
					    # noinspection PyProtectedMember
 | 
				
			||||||
    if instance._meta.label_lower in EXCLUDED:
 | 
					    if instance._meta.label_lower in EXCLUDED:
 | 
				
			||||||
        return
 | 
					        return
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    user, ip = get_user_and_ip(sender)
 | 
					    # Si un utilisateur est connecté, on récupère l'utilisateur courant ainsi que son adresse IP
 | 
				
			||||||
 | 
					    user, ip = get_current_authenticated_user(), get_current_ip()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if user is None:
 | 
				
			||||||
 | 
					        # Si la modification n'a pas été faite via le client Web, on suppose que c'est du à `manage.py`
 | 
				
			||||||
 | 
					        # On récupère alors l'utilisateur·trice connecté·e à la VM, et on récupère la note associée
 | 
				
			||||||
 | 
					        # IMPORTANT : l'utilisateur dans la VM doit être un des alias note du respo info
 | 
				
			||||||
 | 
					        ip = "127.0.0.1"
 | 
				
			||||||
 | 
					        username = Alias.normalize(getpass.getuser())
 | 
				
			||||||
 | 
					        note = NoteUser.objects.filter(alias__normalized_name=username)
 | 
				
			||||||
 | 
					        # if not note.exists():
 | 
				
			||||||
 | 
					        #     print("WARNING: A model attempted to be saved in the DB, but the actor is unknown: " + username)
 | 
				
			||||||
 | 
					        # else:
 | 
				
			||||||
 | 
					        if note.exists():
 | 
				
			||||||
 | 
					            user = note.get().user
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # On crée notre propre sérialiseur JSON pour pouvoir sauvegarder les modèles
 | 
				
			||||||
 | 
					    class CustomSerializer(ModelSerializer):
 | 
				
			||||||
 | 
					        class Meta:
 | 
				
			||||||
 | 
					            model = instance.__class__
 | 
				
			||||||
 | 
					            fields = '__all__'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    instance_json = JSONRenderer().render(CustomSerializer(instance).data).decode("UTF-8")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    instance_json = serializers.serialize('json', [instance, ])[1:-1]
 | 
					 | 
				
			||||||
    Changelog.objects.create(user=user,
 | 
					    Changelog.objects.create(user=user,
 | 
				
			||||||
                             ip=ip,
 | 
					                             ip=ip,
 | 
				
			||||||
                             model=ContentType.objects.get_for_model(instance),
 | 
					                             model=ContentType.objects.get_for_model(instance),
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,8 +0,0 @@
 | 
				
			|||||||
# Copyright (C) 2018-2020 by BDE ENS Paris-Saclay
 | 
					 | 
				
			||||||
# SPDX-License-Identifier: GPL-3.0-or-later
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
app_name = 'logs'
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# TODO User interface
 | 
					 | 
				
			||||||
urlpatterns = [
 | 
					 | 
				
			||||||
]
 | 
					 | 
				
			||||||
@@ -2,6 +2,7 @@
 | 
				
			|||||||
# SPDX-License-Identifier: GPL-3.0-or-later
 | 
					# SPDX-License-Identifier: GPL-3.0-or-later
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from rest_framework import viewsets
 | 
					from rest_framework import viewsets
 | 
				
			||||||
 | 
					from rest_framework.filters import SearchFilter
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from .serializers import ProfileSerializer, ClubSerializer, RoleSerializer, MembershipSerializer
 | 
					from .serializers import ProfileSerializer, ClubSerializer, RoleSerializer, MembershipSerializer
 | 
				
			||||||
from ..models import Profile, Club, Role, Membership
 | 
					from ..models import Profile, Club, Role, Membership
 | 
				
			||||||
@@ -25,6 +26,8 @@ class ClubViewSet(viewsets.ModelViewSet):
 | 
				
			|||||||
    """
 | 
					    """
 | 
				
			||||||
    queryset = Club.objects.all()
 | 
					    queryset = Club.objects.all()
 | 
				
			||||||
    serializer_class = ClubSerializer
 | 
					    serializer_class = ClubSerializer
 | 
				
			||||||
 | 
					    filter_backends = [SearchFilter]
 | 
				
			||||||
 | 
					    search_fields = ['$name', ]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class RoleViewSet(viewsets.ModelViewSet):
 | 
					class RoleViewSet(viewsets.ModelViewSet):
 | 
				
			||||||
@@ -35,6 +38,8 @@ class RoleViewSet(viewsets.ModelViewSet):
 | 
				
			|||||||
    """
 | 
					    """
 | 
				
			||||||
    queryset = Role.objects.all()
 | 
					    queryset = Role.objects.all()
 | 
				
			||||||
    serializer_class = RoleSerializer
 | 
					    serializer_class = RoleSerializer
 | 
				
			||||||
 | 
					    filter_backends = [SearchFilter]
 | 
				
			||||||
 | 
					    search_fields = ['$name', ]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class MembershipViewSet(viewsets.ModelViewSet):
 | 
					class MembershipViewSet(viewsets.ModelViewSet):
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -46,7 +46,7 @@ class Profile(models.Model):
 | 
				
			|||||||
    class Meta:
 | 
					    class Meta:
 | 
				
			||||||
        verbose_name = _('user profile')
 | 
					        verbose_name = _('user profile')
 | 
				
			||||||
        verbose_name_plural = _('user profile')
 | 
					        verbose_name_plural = _('user profile')
 | 
				
			||||||
        indexes = [ models.Index(fields=['user']) ]
 | 
					        indexes = [models.Index(fields=['user'])]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def get_absolute_url(self):
 | 
					    def get_absolute_url(self):
 | 
				
			||||||
        return reverse('user_detail', args=(self.pk,))
 | 
					        return reverse('user_detail', args=(self.pk,))
 | 
				
			||||||
@@ -153,7 +153,7 @@ class Membership(models.Model):
 | 
				
			|||||||
    class Meta:
 | 
					    class Meta:
 | 
				
			||||||
        verbose_name = _('membership')
 | 
					        verbose_name = _('membership')
 | 
				
			||||||
        verbose_name_plural = _('memberships')
 | 
					        verbose_name_plural = _('memberships')
 | 
				
			||||||
        indexes = [ models.Index(fields=['user']) ]
 | 
					        indexes = [models.Index(fields=['user'])]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# @receiver(post_save, sender=settings.AUTH_USER_MODEL)
 | 
					# @receiver(post_save, sender=settings.AUTH_USER_MODEL)
 | 
				
			||||||
# def save_user_profile(instance, created, **_kwargs):
 | 
					# def save_user_profile(instance, created, **_kwargs):
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -300,7 +300,7 @@ class UserAutocomplete(autocomplete.Select2QuerySetView):
 | 
				
			|||||||
        qs = User.objects.all()
 | 
					        qs = User.objects.all()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if self.q:
 | 
					        if self.q:
 | 
				
			||||||
            qs = qs.filter(username__regex=self.q)
 | 
					            qs = qs.filter(username__regex="^" + self.q)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        return qs
 | 
					        return qs
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -5,7 +5,8 @@ from rest_framework import serializers
 | 
				
			|||||||
from rest_polymorphic.serializers import PolymorphicSerializer
 | 
					from rest_polymorphic.serializers import PolymorphicSerializer
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from ..models.notes import Note, NoteClub, NoteSpecial, NoteUser, Alias
 | 
					from ..models.notes import Note, NoteClub, NoteSpecial, NoteUser, Alias
 | 
				
			||||||
from ..models.transactions import TransactionTemplate, Transaction, MembershipTransaction
 | 
					from ..models.transactions import TransactionTemplate, Transaction, MembershipTransaction, TemplateCategory, \
 | 
				
			||||||
 | 
					    TemplateTransaction
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class NoteSerializer(serializers.ModelSerializer):
 | 
					class NoteSerializer(serializers.ModelSerializer):
 | 
				
			||||||
@@ -78,6 +79,17 @@ class NotePolymorphicSerializer(PolymorphicSerializer):
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class TemplateCategorySerializer(serializers.ModelSerializer):
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					    REST API Serializer for Transaction templates.
 | 
				
			||||||
 | 
					    The djangorestframework plugin will analyse the model `TemplateCategory` and parse all fields in the API.
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    class Meta:
 | 
				
			||||||
 | 
					        model = TemplateCategory
 | 
				
			||||||
 | 
					        fields = '__all__'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class TransactionTemplateSerializer(serializers.ModelSerializer):
 | 
					class TransactionTemplateSerializer(serializers.ModelSerializer):
 | 
				
			||||||
    """
 | 
					    """
 | 
				
			||||||
    REST API Serializer for Transaction templates.
 | 
					    REST API Serializer for Transaction templates.
 | 
				
			||||||
@@ -100,6 +112,17 @@ class TransactionSerializer(serializers.ModelSerializer):
 | 
				
			|||||||
        fields = '__all__'
 | 
					        fields = '__all__'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class TemplateTransactionSerializer(serializers.ModelSerializer):
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					    REST API Serializer for Transactions.
 | 
				
			||||||
 | 
					    The djangorestframework plugin will analyse the model `TemplateTransaction` and parse all fields in the API.
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    class Meta:
 | 
				
			||||||
 | 
					        model = TemplateTransaction
 | 
				
			||||||
 | 
					        fields = '__all__'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class MembershipTransactionSerializer(serializers.ModelSerializer):
 | 
					class MembershipTransactionSerializer(serializers.ModelSerializer):
 | 
				
			||||||
    """
 | 
					    """
 | 
				
			||||||
    REST API Serializer for Membership transactions.
 | 
					    REST API Serializer for Membership transactions.
 | 
				
			||||||
@@ -109,3 +132,11 @@ class MembershipTransactionSerializer(serializers.ModelSerializer):
 | 
				
			|||||||
    class Meta:
 | 
					    class Meta:
 | 
				
			||||||
        model = MembershipTransaction
 | 
					        model = MembershipTransaction
 | 
				
			||||||
        fields = '__all__'
 | 
					        fields = '__all__'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class TransactionPolymorphicSerializer(PolymorphicSerializer):
 | 
				
			||||||
 | 
					    model_serializer_mapping = {
 | 
				
			||||||
 | 
					        Transaction: TransactionSerializer,
 | 
				
			||||||
 | 
					        TemplateTransaction: TemplateTransactionSerializer,
 | 
				
			||||||
 | 
					        MembershipTransaction: MembershipTransactionSerializer,
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -2,7 +2,7 @@
 | 
				
			|||||||
# SPDX-License-Identifier: GPL-3.0-or-later
 | 
					# SPDX-License-Identifier: GPL-3.0-or-later
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from .views import NotePolymorphicViewSet, AliasViewSet, \
 | 
					from .views import NotePolymorphicViewSet, AliasViewSet, \
 | 
				
			||||||
    TransactionViewSet, TransactionTemplateViewSet, MembershipTransactionViewSet
 | 
					    TemplateCategoryViewSet, TransactionViewSet, TransactionTemplateViewSet
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def register_note_urls(router, path):
 | 
					def register_note_urls(router, path):
 | 
				
			||||||
@@ -12,6 +12,6 @@ def register_note_urls(router, path):
 | 
				
			|||||||
    router.register(path + '/note', NotePolymorphicViewSet)
 | 
					    router.register(path + '/note', NotePolymorphicViewSet)
 | 
				
			||||||
    router.register(path + '/alias', AliasViewSet)
 | 
					    router.register(path + '/alias', AliasViewSet)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    router.register(path + '/transaction/category', TemplateCategoryViewSet)
 | 
				
			||||||
    router.register(path + '/transaction/transaction', TransactionViewSet)
 | 
					    router.register(path + '/transaction/transaction', TransactionViewSet)
 | 
				
			||||||
    router.register(path + '/transaction/template', TransactionTemplateViewSet)
 | 
					    router.register(path + '/transaction/template', TransactionTemplateViewSet)
 | 
				
			||||||
    router.register(path + '/transaction/membership', MembershipTransactionViewSet)
 | 
					 | 
				
			||||||
 
 | 
				
			|||||||
@@ -2,13 +2,15 @@
 | 
				
			|||||||
# SPDX-License-Identifier: GPL-3.0-or-later
 | 
					# SPDX-License-Identifier: GPL-3.0-or-later
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from django.db.models import Q
 | 
					from django.db.models import Q
 | 
				
			||||||
 | 
					from django_filters.rest_framework import DjangoFilterBackend
 | 
				
			||||||
from rest_framework import viewsets
 | 
					from rest_framework import viewsets
 | 
				
			||||||
 | 
					from rest_framework.filters import SearchFilter
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from .serializers import NoteSerializer, NotePolymorphicSerializer, NoteClubSerializer, NoteSpecialSerializer, \
 | 
					from .serializers import NoteSerializer, NotePolymorphicSerializer, NoteClubSerializer, NoteSpecialSerializer, \
 | 
				
			||||||
    NoteUserSerializer, AliasSerializer, \
 | 
					    NoteUserSerializer, AliasSerializer, \
 | 
				
			||||||
    TransactionTemplateSerializer, TransactionSerializer, MembershipTransactionSerializer
 | 
					    TemplateCategorySerializer, TransactionTemplateSerializer, TransactionPolymorphicSerializer
 | 
				
			||||||
from ..models.notes import Note, NoteClub, NoteSpecial, NoteUser, Alias
 | 
					from ..models.notes import Note, NoteClub, NoteSpecial, NoteUser, Alias
 | 
				
			||||||
from ..models.transactions import TransactionTemplate, Transaction, MembershipTransaction
 | 
					from ..models.transactions import TransactionTemplate, Transaction, TemplateCategory
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class NoteViewSet(viewsets.ModelViewSet):
 | 
					class NoteViewSet(viewsets.ModelViewSet):
 | 
				
			||||||
@@ -69,8 +71,8 @@ class NotePolymorphicViewSet(viewsets.ModelViewSet):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        alias = self.request.query_params.get("alias", ".*")
 | 
					        alias = self.request.query_params.get("alias", ".*")
 | 
				
			||||||
        queryset = queryset.filter(
 | 
					        queryset = queryset.filter(
 | 
				
			||||||
            Q(alias__name__regex=alias)
 | 
					            Q(alias__name__regex="^" + alias)
 | 
				
			||||||
            | Q(alias__normalized_name__regex=alias.lower()))
 | 
					            | Q(alias__normalized_name__regex="^" + alias.lower()))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        note_type = self.request.query_params.get("type", None)
 | 
					        note_type = self.request.query_params.get("type", None)
 | 
				
			||||||
        if note_type:
 | 
					        if note_type:
 | 
				
			||||||
@@ -107,7 +109,7 @@ class AliasViewSet(viewsets.ModelViewSet):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        alias = self.request.query_params.get("alias", ".*")
 | 
					        alias = self.request.query_params.get("alias", ".*")
 | 
				
			||||||
        queryset = queryset.filter(
 | 
					        queryset = queryset.filter(
 | 
				
			||||||
            Q(name__regex=alias) | Q(normalized_name__regex=alias.lower()))
 | 
					            Q(name__regex="^" + alias) | Q(normalized_name__regex="^" + alias.lower()))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        note_id = self.request.query_params.get("note", None)
 | 
					        note_id = self.request.query_params.get("note", None)
 | 
				
			||||||
        if note_id:
 | 
					        if note_id:
 | 
				
			||||||
@@ -131,6 +133,18 @@ class AliasViewSet(viewsets.ModelViewSet):
 | 
				
			|||||||
        return queryset
 | 
					        return queryset
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class TemplateCategoryViewSet(viewsets.ModelViewSet):
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					    REST API View set.
 | 
				
			||||||
 | 
					    The djangorestframework plugin will get all `TemplateCategory` objects, serialize it to JSON with the given serializer,
 | 
				
			||||||
 | 
					    then render it on /api/note/transaction/category/
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					    queryset = TemplateCategory.objects.all()
 | 
				
			||||||
 | 
					    serializer_class = TemplateCategorySerializer
 | 
				
			||||||
 | 
					    filter_backends = [SearchFilter]
 | 
				
			||||||
 | 
					    search_fields = ['$name', ]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class TransactionTemplateViewSet(viewsets.ModelViewSet):
 | 
					class TransactionTemplateViewSet(viewsets.ModelViewSet):
 | 
				
			||||||
    """
 | 
					    """
 | 
				
			||||||
    REST API View set.
 | 
					    REST API View set.
 | 
				
			||||||
@@ -139,6 +153,8 @@ class TransactionTemplateViewSet(viewsets.ModelViewSet):
 | 
				
			|||||||
    """
 | 
					    """
 | 
				
			||||||
    queryset = TransactionTemplate.objects.all()
 | 
					    queryset = TransactionTemplate.objects.all()
 | 
				
			||||||
    serializer_class = TransactionTemplateSerializer
 | 
					    serializer_class = TransactionTemplateSerializer
 | 
				
			||||||
 | 
					    filter_backends = [DjangoFilterBackend]
 | 
				
			||||||
 | 
					    filterset_fields = ['name', 'amount', 'display', 'category', ]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class TransactionViewSet(viewsets.ModelViewSet):
 | 
					class TransactionViewSet(viewsets.ModelViewSet):
 | 
				
			||||||
@@ -148,14 +164,6 @@ class TransactionViewSet(viewsets.ModelViewSet):
 | 
				
			|||||||
    then render it on /api/note/transaction/transaction/
 | 
					    then render it on /api/note/transaction/transaction/
 | 
				
			||||||
    """
 | 
					    """
 | 
				
			||||||
    queryset = Transaction.objects.all()
 | 
					    queryset = Transaction.objects.all()
 | 
				
			||||||
    serializer_class = TransactionSerializer
 | 
					    serializer_class = TransactionPolymorphicSerializer
 | 
				
			||||||
 | 
					    filter_backends = [SearchFilter]
 | 
				
			||||||
 | 
					    search_fields = ['$reason', ]
 | 
				
			||||||
class MembershipTransactionViewSet(viewsets.ModelViewSet):
 | 
					 | 
				
			||||||
    """
 | 
					 | 
				
			||||||
    REST API View set.
 | 
					 | 
				
			||||||
    The djangorestframework plugin will get all `MembershipTransaction` objects, serialize it to JSON with the given serializer,
 | 
					 | 
				
			||||||
    then render it on /api/note/transaction/membership/
 | 
					 | 
				
			||||||
    """
 | 
					 | 
				
			||||||
    queryset = MembershipTransaction.objects.all()
 | 
					 | 
				
			||||||
    serializer_class = MembershipTransactionSerializer
 | 
					 | 
				
			||||||
 
 | 
				
			|||||||
@@ -3,7 +3,7 @@
 | 
				
			|||||||
        "model": "note.note",
 | 
					        "model": "note.note",
 | 
				
			||||||
        "pk": 1,
 | 
					        "pk": 1,
 | 
				
			||||||
        "fields": {
 | 
					        "fields": {
 | 
				
			||||||
            "polymorphic_ctype": 39,
 | 
					            "polymorphic_ctype": 40,
 | 
				
			||||||
            "balance": 0,
 | 
					            "balance": 0,
 | 
				
			||||||
            "is_active": true,
 | 
					            "is_active": true,
 | 
				
			||||||
            "display_image": "",
 | 
					            "display_image": "",
 | 
				
			||||||
@@ -14,7 +14,7 @@
 | 
				
			|||||||
        "model": "note.note",
 | 
					        "model": "note.note",
 | 
				
			||||||
        "pk": 2,
 | 
					        "pk": 2,
 | 
				
			||||||
        "fields": {
 | 
					        "fields": {
 | 
				
			||||||
            "polymorphic_ctype": 39,
 | 
					            "polymorphic_ctype": 40,
 | 
				
			||||||
            "balance": 0,
 | 
					            "balance": 0,
 | 
				
			||||||
            "is_active": true,
 | 
					            "is_active": true,
 | 
				
			||||||
            "display_image": "",
 | 
					            "display_image": "",
 | 
				
			||||||
@@ -25,7 +25,7 @@
 | 
				
			|||||||
        "model": "note.note",
 | 
					        "model": "note.note",
 | 
				
			||||||
        "pk": 3,
 | 
					        "pk": 3,
 | 
				
			||||||
        "fields": {
 | 
					        "fields": {
 | 
				
			||||||
            "polymorphic_ctype": 39,
 | 
					            "polymorphic_ctype": 40,
 | 
				
			||||||
            "balance": 0,
 | 
					            "balance": 0,
 | 
				
			||||||
            "is_active": true,
 | 
					            "is_active": true,
 | 
				
			||||||
            "display_image": "",
 | 
					            "display_image": "",
 | 
				
			||||||
@@ -36,7 +36,7 @@
 | 
				
			|||||||
        "model": "note.note",
 | 
					        "model": "note.note",
 | 
				
			||||||
        "pk": 4,
 | 
					        "pk": 4,
 | 
				
			||||||
        "fields": {
 | 
					        "fields": {
 | 
				
			||||||
            "polymorphic_ctype": 39,
 | 
					            "polymorphic_ctype": 40,
 | 
				
			||||||
            "balance": 0,
 | 
					            "balance": 0,
 | 
				
			||||||
            "is_active": true,
 | 
					            "is_active": true,
 | 
				
			||||||
            "display_image": "",
 | 
					            "display_image": "",
 | 
				
			||||||
@@ -47,7 +47,7 @@
 | 
				
			|||||||
        "model": "note.note",
 | 
					        "model": "note.note",
 | 
				
			||||||
        "pk": 5,
 | 
					        "pk": 5,
 | 
				
			||||||
        "fields": {
 | 
					        "fields": {
 | 
				
			||||||
            "polymorphic_ctype": 38,
 | 
					            "polymorphic_ctype": 39,
 | 
				
			||||||
            "balance": 0,
 | 
					            "balance": 0,
 | 
				
			||||||
            "is_active": true,
 | 
					            "is_active": true,
 | 
				
			||||||
            "display_image": "",
 | 
					            "display_image": "",
 | 
				
			||||||
@@ -58,7 +58,7 @@
 | 
				
			|||||||
        "model": "note.note",
 | 
					        "model": "note.note",
 | 
				
			||||||
        "pk": 6,
 | 
					        "pk": 6,
 | 
				
			||||||
        "fields": {
 | 
					        "fields": {
 | 
				
			||||||
            "polymorphic_ctype": 38,
 | 
					            "polymorphic_ctype": 39,
 | 
				
			||||||
            "balance": 0,
 | 
					            "balance": 0,
 | 
				
			||||||
            "is_active": true,
 | 
					            "is_active": true,
 | 
				
			||||||
            "display_image": "",
 | 
					            "display_image": "",
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -235,7 +235,7 @@ class Alias(models.Model):
 | 
				
			|||||||
        try:
 | 
					        try:
 | 
				
			||||||
            sim_alias = Alias.objects.get(normalized_name=normalized_name)
 | 
					            sim_alias = Alias.objects.get(normalized_name=normalized_name)
 | 
				
			||||||
            if self != sim_alias:
 | 
					            if self != sim_alias:
 | 
				
			||||||
                raise ValidationError(_('An alias with a similar name already exists: {} '.format(sim_alias)),
 | 
					                raise ValidationError(_('An alias with a similar name already exists: {} ').format(sim_alias),
 | 
				
			||||||
                                      code="same_alias"
 | 
					                                      code="same_alias"
 | 
				
			||||||
                                      )
 | 
					                                      )
 | 
				
			||||||
        except Alias.DoesNotExist:
 | 
					        except Alias.DoesNotExist:
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										14
									
								
								apps/note/templatetags/getenv.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								apps/note/templatetags/getenv.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,14 @@
 | 
				
			|||||||
 | 
					# Copyright (C) 2018-2020 by BDE ENS Paris-Saclay
 | 
				
			||||||
 | 
					# SPDX-License-Identifier: GPL-3.0-or-later
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from django import template
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import os
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def getenv(value):
 | 
				
			||||||
 | 
					    return os.getenv(value)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					register = template.Library()
 | 
				
			||||||
 | 
					register.filter('getenv', getenv)
 | 
				
			||||||
@@ -69,7 +69,7 @@ class NoteAutocomplete(autocomplete.Select2QuerySetView):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        # self.q est le paramètre de la recherche
 | 
					        # self.q est le paramètre de la recherche
 | 
				
			||||||
        if self.q:
 | 
					        if self.q:
 | 
				
			||||||
            qs = qs.filter(Q(name__regex=self.q) | Q(normalized_name__regex=Alias.normalize(self.q))) \
 | 
					            qs = qs.filter(Q(name__regex="^" + self.q) | Q(normalized_name__regex="^" + Alias.normalize(self.q))) \
 | 
				
			||||||
                .order_by('normalized_name').distinct()
 | 
					                .order_by('normalized_name').distinct()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        # Filtrage par type de note (user, club, special)
 | 
					        # Filtrage par type de note (user, club, special)
 | 
				
			||||||
@@ -141,7 +141,7 @@ class ConsoView(LoginRequiredMixin, SingleTableView):
 | 
				
			|||||||
        context = super().get_context_data(**kwargs)
 | 
					        context = super().get_context_data(**kwargs)
 | 
				
			||||||
        context['transaction_templates'] = TransactionTemplate.objects.filter(display=True) \
 | 
					        context['transaction_templates'] = TransactionTemplate.objects.filter(display=True) \
 | 
				
			||||||
            .order_by('category')
 | 
					            .order_by('category')
 | 
				
			||||||
        context['title'] = _("Consommations")
 | 
					        context['title'] = _("Consumptions")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        # select2 compatibility
 | 
					        # select2 compatibility
 | 
				
			||||||
        context['no_cache'] = True
 | 
					        context['no_cache'] = True
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -2,12 +2,17 @@
 | 
				
			|||||||
# Copyright (C) 2018-2020 by BDE ENS Paris-Saclay
 | 
					# Copyright (C) 2018-2020 by BDE ENS Paris-Saclay
 | 
				
			||||||
# SPDX-License-Identifier: GPL-3.0-or-later
 | 
					# SPDX-License-Identifier: GPL-3.0-or-later
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					if [ -z ${NOTE_URL+x} ]; then
 | 
				
			||||||
 | 
					  echo "Warning: your env files are not configurated."
 | 
				
			||||||
 | 
					else
 | 
				
			||||||
 | 
					  sed -i -e "s/example.com/$DOMAIN/g" /code/apps/member/fixtures/initial.json
 | 
				
			||||||
 | 
					  sed -i -e "s/localhost/$NOTE_URL/g" /code/note_kfet/fixtures/initial.json
 | 
				
			||||||
 | 
					  sed -i -e "s/\.\*/https?:\/\/$NOTE_URL\/.*/g" /code/note_kfet/fixtures/cas.json
 | 
				
			||||||
 | 
					  sed -i -e "s/REPLACEME/La Note Kfet \\\\ud83c\\\\udf7b/g" /code/note_kfet/fixtures/cas.json
 | 
				
			||||||
 | 
					fi
 | 
				
			||||||
 | 
					
 | 
				
			||||||
python manage.py compilemessages
 | 
					python manage.py compilemessages
 | 
				
			||||||
python manage.py makemigrations
 | 
					python manage.py makemigrations
 | 
				
			||||||
 | 
					 | 
				
			||||||
# Wait for database
 | 
					 | 
				
			||||||
sleep 5
 | 
					 | 
				
			||||||
python manage.py migrate
 | 
					python manage.py migrate
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# TODO: use uwsgi in production
 | 
					 | 
				
			||||||
python manage.py runserver 0.0.0.0:8000
 | 
					python manage.py runserver 0.0.0.0:8000
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -8,7 +8,7 @@ msgid ""
 | 
				
			|||||||
msgstr ""
 | 
					msgstr ""
 | 
				
			||||||
"Project-Id-Version: PACKAGE VERSION\n"
 | 
					"Project-Id-Version: PACKAGE VERSION\n"
 | 
				
			||||||
"Report-Msgid-Bugs-To: \n"
 | 
					"Report-Msgid-Bugs-To: \n"
 | 
				
			||||||
"POT-Creation-Date: 2020-03-07 18:01+0100\n"
 | 
					"POT-Creation-Date: 2020-03-11 11:44+0100\n"
 | 
				
			||||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
 | 
					"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
 | 
				
			||||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
 | 
					"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
 | 
				
			||||||
"Language-Team: LANGUAGE <LL@li.org>\n"
 | 
					"Language-Team: LANGUAGE <LL@li.org>\n"
 | 
				
			||||||
@@ -24,7 +24,7 @@ msgstr ""
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
#: apps/activity/models.py:19 apps/activity/models.py:44
 | 
					#: apps/activity/models.py:19 apps/activity/models.py:44
 | 
				
			||||||
#: apps/member/models.py:60 apps/member/models.py:111
 | 
					#: apps/member/models.py:60 apps/member/models.py:111
 | 
				
			||||||
#: apps/note/models/notes.py:187 apps/note/models/transactions.py:24
 | 
					#: apps/note/models/notes.py:188 apps/note/models/transactions.py:24
 | 
				
			||||||
#: apps/note/models/transactions.py:44 templates/member/profile_detail.html:15
 | 
					#: apps/note/models/transactions.py:44 templates/member/profile_detail.html:15
 | 
				
			||||||
msgid "name"
 | 
					msgid "name"
 | 
				
			||||||
msgstr ""
 | 
					msgstr ""
 | 
				
			||||||
@@ -49,7 +49,7 @@ msgstr ""
 | 
				
			|||||||
msgid "description"
 | 
					msgid "description"
 | 
				
			||||||
msgstr ""
 | 
					msgstr ""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#: apps/activity/models.py:54 apps/note/models/notes.py:163
 | 
					#: apps/activity/models.py:54 apps/note/models/notes.py:164
 | 
				
			||||||
#: apps/note/models/transactions.py:62
 | 
					#: apps/note/models/transactions.py:62
 | 
				
			||||||
msgid "type"
 | 
					msgid "type"
 | 
				
			||||||
msgstr ""
 | 
					msgstr ""
 | 
				
			||||||
@@ -90,7 +90,7 @@ msgstr ""
 | 
				
			|||||||
msgid "Logs"
 | 
					msgid "Logs"
 | 
				
			||||||
msgstr ""
 | 
					msgstr ""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#: apps/logs/models.py:21 apps/note/models/notes.py:116
 | 
					#: apps/logs/models.py:21 apps/note/models/notes.py:117
 | 
				
			||||||
msgid "user"
 | 
					msgid "user"
 | 
				
			||||||
msgstr ""
 | 
					msgstr ""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -114,15 +114,27 @@ msgstr ""
 | 
				
			|||||||
msgid "new data"
 | 
					msgid "new data"
 | 
				
			||||||
msgstr ""
 | 
					msgstr ""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#: apps/logs/models.py:59
 | 
					#: apps/logs/models.py:60
 | 
				
			||||||
 | 
					msgid "create"
 | 
				
			||||||
 | 
					msgstr ""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#: apps/logs/models.py:61
 | 
				
			||||||
 | 
					msgid "edit"
 | 
				
			||||||
 | 
					msgstr ""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#: apps/logs/models.py:62
 | 
				
			||||||
 | 
					msgid "delete"
 | 
				
			||||||
 | 
					msgstr ""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#: apps/logs/models.py:65
 | 
				
			||||||
msgid "action"
 | 
					msgid "action"
 | 
				
			||||||
msgstr ""
 | 
					msgstr ""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#: apps/logs/models.py:67
 | 
					#: apps/logs/models.py:73
 | 
				
			||||||
msgid "timestamp"
 | 
					msgid "timestamp"
 | 
				
			||||||
msgstr ""
 | 
					msgstr ""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#: apps/logs/models.py:71
 | 
					#: apps/logs/models.py:77
 | 
				
			||||||
msgid "Logs cannot be destroyed."
 | 
					msgid "Logs cannot be destroyed."
 | 
				
			||||||
msgstr ""
 | 
					msgstr ""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -188,7 +200,7 @@ msgid ""
 | 
				
			|||||||
"members can renew their membership."
 | 
					"members can renew their membership."
 | 
				
			||||||
msgstr ""
 | 
					msgstr ""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#: apps/member/models.py:93 apps/note/models/notes.py:138
 | 
					#: apps/member/models.py:93 apps/note/models/notes.py:139
 | 
				
			||||||
msgid "club"
 | 
					msgid "club"
 | 
				
			||||||
msgstr ""
 | 
					msgstr ""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -237,7 +249,7 @@ msgstr ""
 | 
				
			|||||||
msgid "Account #%(id)s: %(username)s"
 | 
					msgid "Account #%(id)s: %(username)s"
 | 
				
			||||||
msgstr ""
 | 
					msgstr ""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#: apps/member/views.py:200
 | 
					#: apps/member/views.py:202
 | 
				
			||||||
msgid "Alias successfully deleted"
 | 
					msgid "Alias successfully deleted"
 | 
				
			||||||
msgstr ""
 | 
					msgstr ""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -250,127 +262,127 @@ msgstr ""
 | 
				
			|||||||
msgid "destination"
 | 
					msgid "destination"
 | 
				
			||||||
msgstr ""
 | 
					msgstr ""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#: apps/note/apps.py:14 apps/note/models/notes.py:57
 | 
					#: apps/note/apps.py:14 apps/note/models/notes.py:58
 | 
				
			||||||
msgid "note"
 | 
					msgid "note"
 | 
				
			||||||
msgstr ""
 | 
					msgstr ""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#: apps/note/forms.py:26
 | 
					#: apps/note/forms.py:20
 | 
				
			||||||
msgid "New Alias"
 | 
					msgid "New Alias"
 | 
				
			||||||
msgstr ""
 | 
					msgstr ""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#: apps/note/forms.py:31
 | 
					#: apps/note/forms.py:25
 | 
				
			||||||
msgid "select an image"
 | 
					msgid "select an image"
 | 
				
			||||||
msgstr ""
 | 
					msgstr ""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#: apps/note/forms.py:32
 | 
					#: apps/note/forms.py:26
 | 
				
			||||||
msgid "Maximal size: 2MB"
 | 
					msgid "Maximal size: 2MB"
 | 
				
			||||||
msgstr ""
 | 
					msgstr ""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#: apps/note/forms.py:77
 | 
					#: apps/note/forms.py:70
 | 
				
			||||||
msgid "Source and destination must be different."
 | 
					msgid "Source and destination must be different."
 | 
				
			||||||
msgstr ""
 | 
					msgstr ""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#: apps/note/models/notes.py:26
 | 
					#: apps/note/models/notes.py:27
 | 
				
			||||||
msgid "account balance"
 | 
					msgid "account balance"
 | 
				
			||||||
msgstr ""
 | 
					msgstr ""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#: apps/note/models/notes.py:27
 | 
					#: apps/note/models/notes.py:28
 | 
				
			||||||
msgid "in centimes, money credited for this instance"
 | 
					msgid "in centimes, money credited for this instance"
 | 
				
			||||||
msgstr ""
 | 
					msgstr ""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#: apps/note/models/notes.py:31
 | 
					#: apps/note/models/notes.py:32
 | 
				
			||||||
msgid "last negative date"
 | 
					msgid "last negative date"
 | 
				
			||||||
msgstr ""
 | 
					msgstr ""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#: apps/note/models/notes.py:32
 | 
					#: apps/note/models/notes.py:33
 | 
				
			||||||
msgid "last time the balance was negative"
 | 
					msgid "last time the balance was negative"
 | 
				
			||||||
msgstr ""
 | 
					msgstr ""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#: apps/note/models/notes.py:37
 | 
					#: apps/note/models/notes.py:38
 | 
				
			||||||
msgid "active"
 | 
					msgid "active"
 | 
				
			||||||
msgstr ""
 | 
					msgstr ""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#: apps/note/models/notes.py:40
 | 
					#: apps/note/models/notes.py:41
 | 
				
			||||||
msgid ""
 | 
					msgid ""
 | 
				
			||||||
"Designates whether this note should be treated as active. Unselect this "
 | 
					"Designates whether this note should be treated as active. Unselect this "
 | 
				
			||||||
"instead of deleting notes."
 | 
					"instead of deleting notes."
 | 
				
			||||||
msgstr ""
 | 
					msgstr ""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#: apps/note/models/notes.py:44
 | 
					#: apps/note/models/notes.py:45
 | 
				
			||||||
msgid "display image"
 | 
					msgid "display image"
 | 
				
			||||||
msgstr ""
 | 
					msgstr ""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#: apps/note/models/notes.py:52 apps/note/models/transactions.py:102
 | 
					#: apps/note/models/notes.py:53 apps/note/models/transactions.py:102
 | 
				
			||||||
msgid "created at"
 | 
					msgid "created at"
 | 
				
			||||||
msgstr ""
 | 
					msgstr ""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#: apps/note/models/notes.py:58
 | 
					#: apps/note/models/notes.py:59
 | 
				
			||||||
msgid "notes"
 | 
					msgid "notes"
 | 
				
			||||||
msgstr ""
 | 
					msgstr ""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#: apps/note/models/notes.py:66
 | 
					#: apps/note/models/notes.py:67
 | 
				
			||||||
msgid "Note"
 | 
					msgid "Note"
 | 
				
			||||||
msgstr ""
 | 
					msgstr ""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#: apps/note/models/notes.py:76 apps/note/models/notes.py:100
 | 
					#: apps/note/models/notes.py:77 apps/note/models/notes.py:101
 | 
				
			||||||
msgid "This alias is already taken."
 | 
					msgid "This alias is already taken."
 | 
				
			||||||
msgstr ""
 | 
					msgstr ""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#: apps/note/models/notes.py:120
 | 
					#: apps/note/models/notes.py:121
 | 
				
			||||||
msgid "one's note"
 | 
					msgid "one's note"
 | 
				
			||||||
msgstr ""
 | 
					msgstr ""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#: apps/note/models/notes.py:121
 | 
					#: apps/note/models/notes.py:122
 | 
				
			||||||
msgid "users note"
 | 
					msgid "users note"
 | 
				
			||||||
msgstr ""
 | 
					msgstr ""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#: apps/note/models/notes.py:127
 | 
					#: apps/note/models/notes.py:128
 | 
				
			||||||
#, python-format
 | 
					#, python-format
 | 
				
			||||||
msgid "%(user)s's note"
 | 
					msgid "%(user)s's note"
 | 
				
			||||||
msgstr ""
 | 
					msgstr ""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#: apps/note/models/notes.py:142
 | 
					#: apps/note/models/notes.py:143
 | 
				
			||||||
msgid "club note"
 | 
					msgid "club note"
 | 
				
			||||||
msgstr ""
 | 
					msgstr ""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#: apps/note/models/notes.py:143
 | 
					#: apps/note/models/notes.py:144
 | 
				
			||||||
msgid "clubs notes"
 | 
					msgid "clubs notes"
 | 
				
			||||||
msgstr ""
 | 
					msgstr ""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#: apps/note/models/notes.py:149
 | 
					#: apps/note/models/notes.py:150
 | 
				
			||||||
#, python-format
 | 
					#, python-format
 | 
				
			||||||
msgid "Note of %(club)s club"
 | 
					msgid "Note of %(club)s club"
 | 
				
			||||||
msgstr ""
 | 
					msgstr ""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#: apps/note/models/notes.py:169
 | 
					#: apps/note/models/notes.py:170
 | 
				
			||||||
msgid "special note"
 | 
					msgid "special note"
 | 
				
			||||||
msgstr ""
 | 
					msgstr ""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#: apps/note/models/notes.py:170
 | 
					#: apps/note/models/notes.py:171
 | 
				
			||||||
msgid "special notes"
 | 
					msgid "special notes"
 | 
				
			||||||
msgstr ""
 | 
					msgstr ""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#: apps/note/models/notes.py:193
 | 
					#: apps/note/models/notes.py:194
 | 
				
			||||||
msgid "Invalid alias"
 | 
					msgid "Invalid alias"
 | 
				
			||||||
msgstr ""
 | 
					msgstr ""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#: apps/note/models/notes.py:209
 | 
					#: apps/note/models/notes.py:210
 | 
				
			||||||
msgid "alias"
 | 
					msgid "alias"
 | 
				
			||||||
msgstr ""
 | 
					msgstr ""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#: apps/note/models/notes.py:210 templates/member/profile_detail.html:37
 | 
					#: apps/note/models/notes.py:211 templates/member/profile_detail.html:37
 | 
				
			||||||
msgid "aliases"
 | 
					msgid "aliases"
 | 
				
			||||||
msgstr ""
 | 
					msgstr ""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#: apps/note/models/notes.py:228
 | 
					#: apps/note/models/notes.py:229
 | 
				
			||||||
msgid "Alias is too long."
 | 
					msgid "Alias is too long."
 | 
				
			||||||
msgstr ""
 | 
					msgstr ""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#: apps/note/models/notes.py:233
 | 
					#: apps/note/models/notes.py:234
 | 
				
			||||||
msgid "An alias with a similar name already exists: {} "
 | 
					msgid "An alias with a similar name already exists: {} "
 | 
				
			||||||
msgstr ""
 | 
					msgstr ""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#: apps/note/models/notes.py:242
 | 
					#: apps/note/models/notes.py:243
 | 
				
			||||||
msgid "You can't delete your main alias."
 | 
					msgid "You can't delete your main alias."
 | 
				
			||||||
msgstr ""
 | 
					msgstr ""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -422,11 +434,11 @@ msgstr ""
 | 
				
			|||||||
msgid "transactions"
 | 
					msgid "transactions"
 | 
				
			||||||
msgstr ""
 | 
					msgstr ""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#: apps/note/models/transactions.py:184
 | 
					#: apps/note/models/transactions.py:185
 | 
				
			||||||
msgid "membership transaction"
 | 
					msgid "membership transaction"
 | 
				
			||||||
msgstr ""
 | 
					msgstr ""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#: apps/note/models/transactions.py:185
 | 
					#: apps/note/models/transactions.py:186
 | 
				
			||||||
msgid "membership transactions"
 | 
					msgid "membership transactions"
 | 
				
			||||||
msgstr ""
 | 
					msgstr ""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -434,34 +446,54 @@ msgstr ""
 | 
				
			|||||||
msgid "Transfer money from your account to one or others"
 | 
					msgid "Transfer money from your account to one or others"
 | 
				
			||||||
msgstr ""
 | 
					msgstr ""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#: apps/note/views.py:138
 | 
					#: apps/note/views.py:139
 | 
				
			||||||
msgid "Consommations"
 | 
					msgid "Consommations"
 | 
				
			||||||
msgstr ""
 | 
					msgstr ""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#: note_kfet/settings/base.py:162
 | 
					#: note_kfet/settings/__init__.py:63
 | 
				
			||||||
msgid "German"
 | 
					 | 
				
			||||||
msgstr ""
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#: note_kfet/settings/base.py:163
 | 
					 | 
				
			||||||
msgid "English"
 | 
					 | 
				
			||||||
msgstr ""
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#: note_kfet/settings/base.py:164
 | 
					 | 
				
			||||||
msgid "French"
 | 
					 | 
				
			||||||
msgstr ""
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#: note_kfet/settings/base.py:215
 | 
					 | 
				
			||||||
msgid ""
 | 
					msgid ""
 | 
				
			||||||
"The Central Authentication Service grants you access to most of our websites "
 | 
					"The Central Authentication Service grants you access to most of our websites "
 | 
				
			||||||
"by authenticating only once, so you don't need to type your credentials "
 | 
					"by authenticating only once, so you don't need to type your credentials "
 | 
				
			||||||
"again unless your session expires or you logout."
 | 
					"again unless your session expires or you logout."
 | 
				
			||||||
msgstr ""
 | 
					msgstr ""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#: note_kfet/settings/base.py:156
 | 
				
			||||||
 | 
					msgid "German"
 | 
				
			||||||
 | 
					msgstr ""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#: note_kfet/settings/base.py:157
 | 
				
			||||||
 | 
					msgid "English"
 | 
				
			||||||
 | 
					msgstr ""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#: note_kfet/settings/base.py:158
 | 
				
			||||||
 | 
					msgid "French"
 | 
				
			||||||
 | 
					msgstr ""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#: templates/base.html:13
 | 
					#: templates/base.html:13
 | 
				
			||||||
msgid "The ENS Paris-Saclay BDE note."
 | 
					msgid "The ENS Paris-Saclay BDE note."
 | 
				
			||||||
msgstr ""
 | 
					msgstr ""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#: templates/cas_server/base.html:7 templates/cas_server/base.html:26
 | 
					#: templates/base.html:70
 | 
				
			||||||
 | 
					msgid "Consumptions"
 | 
				
			||||||
 | 
					msgstr ""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#: templates/base.html:73
 | 
				
			||||||
 | 
					msgid "Clubs"
 | 
				
			||||||
 | 
					msgstr ""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#: templates/base.html:76
 | 
				
			||||||
 | 
					msgid "Activities"
 | 
				
			||||||
 | 
					msgstr ""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#: templates/base.html:79
 | 
				
			||||||
 | 
					msgid "Button"
 | 
				
			||||||
 | 
					msgstr ""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#: templates/base.html:82 templates/note/transaction_form.html:35
 | 
				
			||||||
 | 
					msgid "Transfer"
 | 
				
			||||||
 | 
					msgstr ""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#: templates/cas_server/base.html:7
 | 
				
			||||||
msgid "Central Authentication Service"
 | 
					msgid "Central Authentication Service"
 | 
				
			||||||
msgstr ""
 | 
					msgstr ""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -511,6 +543,15 @@ msgstr ""
 | 
				
			|||||||
msgid "Connect to the service"
 | 
					msgid "Connect to the service"
 | 
				
			||||||
msgstr ""
 | 
					msgstr ""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#: templates/django_filters/rest_framework/crispy_form.html:4
 | 
				
			||||||
 | 
					#: templates/django_filters/rest_framework/form.html:2
 | 
				
			||||||
 | 
					msgid "Field filters"
 | 
				
			||||||
 | 
					msgstr ""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#: templates/django_filters/rest_framework/form.html:5
 | 
				
			||||||
 | 
					msgid "Submit"
 | 
				
			||||||
 | 
					msgstr ""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#: templates/member/club_detail.html:10
 | 
					#: templates/member/club_detail.html:10
 | 
				
			||||||
msgid "Membership starts on"
 | 
					msgid "Membership starts on"
 | 
				
			||||||
msgstr ""
 | 
					msgstr ""
 | 
				
			||||||
@@ -583,10 +624,6 @@ msgstr ""
 | 
				
			|||||||
msgid "Sign Up"
 | 
					msgid "Sign Up"
 | 
				
			||||||
msgstr ""
 | 
					msgstr ""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#: templates/note/transaction_form.html:35
 | 
					 | 
				
			||||||
msgid "Transfer"
 | 
					 | 
				
			||||||
msgstr ""
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#: templates/registration/logged_out.html:8
 | 
					#: templates/registration/logged_out.html:8
 | 
				
			||||||
msgid "Thanks for spending some quality time with the Web site today."
 | 
					msgid "Thanks for spending some quality time with the Web site today."
 | 
				
			||||||
msgstr ""
 | 
					msgstr ""
 | 
				
			||||||
@@ -596,7 +633,7 @@ msgid "Log in again"
 | 
				
			|||||||
msgstr ""
 | 
					msgstr ""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#: templates/registration/login.html:7 templates/registration/login.html:8
 | 
					#: templates/registration/login.html:7 templates/registration/login.html:8
 | 
				
			||||||
#: templates/registration/login.html:22
 | 
					#: templates/registration/login.html:26
 | 
				
			||||||
#: templates/registration/password_reset_complete.html:10
 | 
					#: templates/registration/password_reset_complete.html:10
 | 
				
			||||||
msgid "Log in"
 | 
					msgid "Log in"
 | 
				
			||||||
msgstr ""
 | 
					msgstr ""
 | 
				
			||||||
@@ -608,7 +645,7 @@ msgid ""
 | 
				
			|||||||
"page. Would you like to login to a different account?"
 | 
					"page. Would you like to login to a different account?"
 | 
				
			||||||
msgstr ""
 | 
					msgstr ""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#: templates/registration/login.html:23
 | 
					#: templates/registration/login.html:27
 | 
				
			||||||
msgid "Forgotten your password or username?"
 | 
					msgid "Forgotten your password or username?"
 | 
				
			||||||
msgstr ""
 | 
					msgstr ""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -3,7 +3,7 @@ msgid ""
 | 
				
			|||||||
msgstr ""
 | 
					msgstr ""
 | 
				
			||||||
"Project-Id-Version: PACKAGE VERSION\n"
 | 
					"Project-Id-Version: PACKAGE VERSION\n"
 | 
				
			||||||
"Report-Msgid-Bugs-To: \n"
 | 
					"Report-Msgid-Bugs-To: \n"
 | 
				
			||||||
"POT-Creation-Date: 2020-03-07 18:01+0100\n"
 | 
					"POT-Creation-Date: 2020-03-11 11:44+0100\n"
 | 
				
			||||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
 | 
					"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
 | 
				
			||||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
 | 
					"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
 | 
				
			||||||
"Language-Team: LANGUAGE <LL@li.org>\n"
 | 
					"Language-Team: LANGUAGE <LL@li.org>\n"
 | 
				
			||||||
@@ -19,7 +19,7 @@ msgstr "activité"
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
#: apps/activity/models.py:19 apps/activity/models.py:44
 | 
					#: apps/activity/models.py:19 apps/activity/models.py:44
 | 
				
			||||||
#: apps/member/models.py:60 apps/member/models.py:111
 | 
					#: apps/member/models.py:60 apps/member/models.py:111
 | 
				
			||||||
#: apps/note/models/notes.py:187 apps/note/models/transactions.py:24
 | 
					#: apps/note/models/notes.py:188 apps/note/models/transactions.py:24
 | 
				
			||||||
#: apps/note/models/transactions.py:44 templates/member/profile_detail.html:15
 | 
					#: apps/note/models/transactions.py:44 templates/member/profile_detail.html:15
 | 
				
			||||||
msgid "name"
 | 
					msgid "name"
 | 
				
			||||||
msgstr "nom"
 | 
					msgstr "nom"
 | 
				
			||||||
@@ -44,7 +44,7 @@ msgstr "types d'activité"
 | 
				
			|||||||
msgid "description"
 | 
					msgid "description"
 | 
				
			||||||
msgstr "description"
 | 
					msgstr "description"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#: apps/activity/models.py:54 apps/note/models/notes.py:163
 | 
					#: apps/activity/models.py:54 apps/note/models/notes.py:164
 | 
				
			||||||
#: apps/note/models/transactions.py:62
 | 
					#: apps/note/models/transactions.py:62
 | 
				
			||||||
msgid "type"
 | 
					msgid "type"
 | 
				
			||||||
msgstr "type"
 | 
					msgstr "type"
 | 
				
			||||||
@@ -85,15 +85,13 @@ msgstr ""
 | 
				
			|||||||
msgid "Logs"
 | 
					msgid "Logs"
 | 
				
			||||||
msgstr ""
 | 
					msgstr ""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#: apps/logs/models.py:21 apps/note/models/notes.py:116
 | 
					#: apps/logs/models.py:21 apps/note/models/notes.py:117
 | 
				
			||||||
msgid "user"
 | 
					msgid "user"
 | 
				
			||||||
msgstr "utilisateur"
 | 
					msgstr "utilisateur"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#: apps/logs/models.py:27
 | 
					#: apps/logs/models.py:27
 | 
				
			||||||
#, fuzzy
 | 
					 | 
				
			||||||
#| msgid "address"
 | 
					 | 
				
			||||||
msgid "IP Address"
 | 
					msgid "IP Address"
 | 
				
			||||||
msgstr "adresse"
 | 
					msgstr "Adresse IP"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#: apps/logs/models.py:35
 | 
					#: apps/logs/models.py:35
 | 
				
			||||||
msgid "model"
 | 
					msgid "model"
 | 
				
			||||||
@@ -108,22 +106,30 @@ msgid "previous data"
 | 
				
			|||||||
msgstr "Données précédentes"
 | 
					msgstr "Données précédentes"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#: apps/logs/models.py:52
 | 
					#: apps/logs/models.py:52
 | 
				
			||||||
#, fuzzy
 | 
					 | 
				
			||||||
#| msgid "end date"
 | 
					 | 
				
			||||||
msgid "new data"
 | 
					msgid "new data"
 | 
				
			||||||
msgstr "Nouvelles données"
 | 
					msgstr "Nouvelles données"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#: apps/logs/models.py:59
 | 
					#: apps/logs/models.py:60
 | 
				
			||||||
#, fuzzy
 | 
					msgid "create"
 | 
				
			||||||
#| msgid "section"
 | 
					msgstr "Créer"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#: apps/logs/models.py:61
 | 
				
			||||||
 | 
					msgid "edit"
 | 
				
			||||||
 | 
					msgstr "Modifier"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#: apps/logs/models.py:62
 | 
				
			||||||
 | 
					msgid "delete"
 | 
				
			||||||
 | 
					msgstr "Supprimer"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#: apps/logs/models.py:65
 | 
				
			||||||
msgid "action"
 | 
					msgid "action"
 | 
				
			||||||
msgstr "Action"
 | 
					msgstr "Action"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#: apps/logs/models.py:67
 | 
					#: apps/logs/models.py:73
 | 
				
			||||||
msgid "timestamp"
 | 
					msgid "timestamp"
 | 
				
			||||||
msgstr "Date"
 | 
					msgstr "Date"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#: apps/logs/models.py:71
 | 
					#: apps/logs/models.py:77
 | 
				
			||||||
msgid "Logs cannot be destroyed."
 | 
					msgid "Logs cannot be destroyed."
 | 
				
			||||||
msgstr "Les logs ne peuvent pas être détruits."
 | 
					msgstr "Les logs ne peuvent pas être détruits."
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -193,10 +199,16 @@ msgstr ""
 | 
				
			|||||||
"Combien de temps l'adhésion peut durer après le 1er Janvier de l'année "
 | 
					"Combien de temps l'adhésion peut durer après le 1er Janvier de l'année "
 | 
				
			||||||
"suivante avant que les adhérents peuvent renouveler leur adhésion."
 | 
					"suivante avant que les adhérents peuvent renouveler leur adhésion."
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#: apps/member/models.py:93 apps/note/models/notes.py:138
 | 
					#: apps/member/models.py:93 apps/note/models/notes.py:139
 | 
				
			||||||
msgid "club"
 | 
					msgid "club"
 | 
				
			||||||
msgstr "club"
 | 
					msgstr "club"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					msgid "New club"
 | 
				
			||||||
 | 
					msgstr "Nouveau club"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					msgid "Clubs list"
 | 
				
			||||||
 | 
					msgstr "Liste des clubs"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#: apps/member/models.py:94
 | 
					#: apps/member/models.py:94
 | 
				
			||||||
msgid "clubs"
 | 
					msgid "clubs"
 | 
				
			||||||
msgstr "clubs"
 | 
					msgstr "clubs"
 | 
				
			||||||
@@ -242,9 +254,9 @@ msgstr "Un alias avec un nom similaire existe déjà."
 | 
				
			|||||||
msgid "Account #%(id)s: %(username)s"
 | 
					msgid "Account #%(id)s: %(username)s"
 | 
				
			||||||
msgstr "Compte n°%(id)s : %(username)s"
 | 
					msgstr "Compte n°%(id)s : %(username)s"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#: apps/member/views.py:200
 | 
					#: apps/member/views.py:202
 | 
				
			||||||
msgid "Alias successfully deleted"
 | 
					msgid "Alias successfully deleted"
 | 
				
			||||||
msgstr ""
 | 
					msgstr "L'alias a bien été supprimé"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#: apps/note/admin.py:120 apps/note/models/transactions.py:93
 | 
					#: apps/note/admin.py:120 apps/note/models/transactions.py:93
 | 
				
			||||||
msgid "source"
 | 
					msgid "source"
 | 
				
			||||||
@@ -255,132 +267,128 @@ msgstr "source"
 | 
				
			|||||||
msgid "destination"
 | 
					msgid "destination"
 | 
				
			||||||
msgstr "destination"
 | 
					msgstr "destination"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#: apps/note/apps.py:14 apps/note/models/notes.py:57
 | 
					#: apps/note/apps.py:14 apps/note/models/notes.py:58
 | 
				
			||||||
msgid "note"
 | 
					msgid "note"
 | 
				
			||||||
msgstr "note"
 | 
					msgstr "note"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#: apps/note/forms.py:26
 | 
					#: apps/note/forms.py:20
 | 
				
			||||||
msgid "New Alias"
 | 
					msgid "New Alias"
 | 
				
			||||||
msgstr ""
 | 
					msgstr "Nouvel alias"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#: apps/note/forms.py:31
 | 
					#: apps/note/forms.py:25
 | 
				
			||||||
#, fuzzy
 | 
					 | 
				
			||||||
#| msgid "display image"
 | 
					 | 
				
			||||||
msgid "select an image"
 | 
					msgid "select an image"
 | 
				
			||||||
msgstr "image affichée"
 | 
					msgstr "Choisissez une image"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#: apps/note/forms.py:32
 | 
					#: apps/note/forms.py:26
 | 
				
			||||||
msgid "Maximal size: 2MB"
 | 
					msgid "Maximal size: 2MB"
 | 
				
			||||||
msgstr ""
 | 
					msgstr "Taille maximale : 2 Mo"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#: apps/note/forms.py:77
 | 
					#: apps/note/forms.py:70
 | 
				
			||||||
msgid "Source and destination must be different."
 | 
					msgid "Source and destination must be different."
 | 
				
			||||||
msgstr "La source et la destination doivent être différentes."
 | 
					msgstr "La source et la destination doivent être différentes."
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#: apps/note/models/notes.py:26
 | 
					#: apps/note/models/notes.py:27
 | 
				
			||||||
msgid "account balance"
 | 
					msgid "account balance"
 | 
				
			||||||
msgstr "solde du compte"
 | 
					msgstr "solde du compte"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#: apps/note/models/notes.py:27
 | 
					#: apps/note/models/notes.py:28
 | 
				
			||||||
msgid "in centimes, money credited for this instance"
 | 
					msgid "in centimes, money credited for this instance"
 | 
				
			||||||
msgstr "en centimes, argent crédité pour cette instance"
 | 
					msgstr "en centimes, argent crédité pour cette instance"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#: apps/note/models/notes.py:31
 | 
					#: apps/note/models/notes.py:32
 | 
				
			||||||
msgid "last negative date"
 | 
					msgid "last negative date"
 | 
				
			||||||
msgstr "dernier date de négatif"
 | 
					msgstr "dernier date de négatif"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#: apps/note/models/notes.py:32
 | 
					#: apps/note/models/notes.py:33
 | 
				
			||||||
msgid "last time the balance was negative"
 | 
					msgid "last time the balance was negative"
 | 
				
			||||||
msgstr "dernier instant où la note était en négatif"
 | 
					msgstr "dernier instant où la note était en négatif"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#: apps/note/models/notes.py:37
 | 
					#: apps/note/models/notes.py:38
 | 
				
			||||||
msgid "active"
 | 
					msgid "active"
 | 
				
			||||||
msgstr "actif"
 | 
					msgstr "actif"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#: apps/note/models/notes.py:40
 | 
					#: apps/note/models/notes.py:41
 | 
				
			||||||
msgid ""
 | 
					msgid ""
 | 
				
			||||||
"Designates whether this note should be treated as active. Unselect this "
 | 
					"Designates whether this note should be treated as active. Unselect this "
 | 
				
			||||||
"instead of deleting notes."
 | 
					"instead of deleting notes."
 | 
				
			||||||
msgstr ""
 | 
					msgstr ""
 | 
				
			||||||
"Indique si la note est active. Désactiver cela plutôt que supprimer la note."
 | 
					"Indique si la note est active. Désactiver cela plutôt que supprimer la note."
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#: apps/note/models/notes.py:44
 | 
					#: apps/note/models/notes.py:45
 | 
				
			||||||
msgid "display image"
 | 
					msgid "display image"
 | 
				
			||||||
msgstr "image affichée"
 | 
					msgstr "image affichée"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#: apps/note/models/notes.py:52 apps/note/models/transactions.py:102
 | 
					#: apps/note/models/notes.py:53 apps/note/models/transactions.py:102
 | 
				
			||||||
msgid "created at"
 | 
					msgid "created at"
 | 
				
			||||||
msgstr "créée le"
 | 
					msgstr "créée le"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#: apps/note/models/notes.py:58
 | 
					#: apps/note/models/notes.py:59
 | 
				
			||||||
msgid "notes"
 | 
					msgid "notes"
 | 
				
			||||||
msgstr "notes"
 | 
					msgstr "notes"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#: apps/note/models/notes.py:66
 | 
					#: apps/note/models/notes.py:67
 | 
				
			||||||
msgid "Note"
 | 
					msgid "Note"
 | 
				
			||||||
msgstr "Note"
 | 
					msgstr "Note"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#: apps/note/models/notes.py:76 apps/note/models/notes.py:100
 | 
					#: apps/note/models/notes.py:77 apps/note/models/notes.py:101
 | 
				
			||||||
msgid "This alias is already taken."
 | 
					msgid "This alias is already taken."
 | 
				
			||||||
msgstr "Cet alias est déjà pris."
 | 
					msgstr "Cet alias est déjà pris."
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#: apps/note/models/notes.py:120
 | 
					#: apps/note/models/notes.py:121
 | 
				
			||||||
msgid "one's note"
 | 
					msgid "one's note"
 | 
				
			||||||
msgstr "note d'un utilisateur"
 | 
					msgstr "note d'un utilisateur"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#: apps/note/models/notes.py:121
 | 
					#: apps/note/models/notes.py:122
 | 
				
			||||||
msgid "users note"
 | 
					msgid "users note"
 | 
				
			||||||
msgstr "notes des utilisateurs"
 | 
					msgstr "notes des utilisateurs"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#: apps/note/models/notes.py:127
 | 
					#: apps/note/models/notes.py:128
 | 
				
			||||||
#, python-format
 | 
					#, python-format
 | 
				
			||||||
msgid "%(user)s's note"
 | 
					msgid "%(user)s's note"
 | 
				
			||||||
msgstr "Note de %(user)s"
 | 
					msgstr "Note de %(user)s"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#: apps/note/models/notes.py:142
 | 
					#: apps/note/models/notes.py:143
 | 
				
			||||||
msgid "club note"
 | 
					msgid "club note"
 | 
				
			||||||
msgstr "note d'un club"
 | 
					msgstr "note d'un club"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#: apps/note/models/notes.py:143
 | 
					#: apps/note/models/notes.py:144
 | 
				
			||||||
msgid "clubs notes"
 | 
					msgid "clubs notes"
 | 
				
			||||||
msgstr "notes des clubs"
 | 
					msgstr "notes des clubs"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#: apps/note/models/notes.py:149
 | 
					#: apps/note/models/notes.py:150
 | 
				
			||||||
#, python-format
 | 
					#, python-format
 | 
				
			||||||
msgid "Note of %(club)s club"
 | 
					msgid "Note of %(club)s club"
 | 
				
			||||||
msgstr "Note du club %(club)s"
 | 
					msgstr "Note du club %(club)s"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#: apps/note/models/notes.py:169
 | 
					#: apps/note/models/notes.py:170
 | 
				
			||||||
msgid "special note"
 | 
					msgid "special note"
 | 
				
			||||||
msgstr "note spéciale"
 | 
					msgstr "note spéciale"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#: apps/note/models/notes.py:170
 | 
					#: apps/note/models/notes.py:171
 | 
				
			||||||
msgid "special notes"
 | 
					msgid "special notes"
 | 
				
			||||||
msgstr "notes spéciales"
 | 
					msgstr "notes spéciales"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#: apps/note/models/notes.py:193
 | 
					#: apps/note/models/notes.py:194
 | 
				
			||||||
msgid "Invalid alias"
 | 
					msgid "Invalid alias"
 | 
				
			||||||
msgstr "Alias invalide"
 | 
					msgstr "Alias invalide"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#: apps/note/models/notes.py:209
 | 
					#: apps/note/models/notes.py:210
 | 
				
			||||||
msgid "alias"
 | 
					msgid "alias"
 | 
				
			||||||
msgstr "alias"
 | 
					msgstr "alias"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#: apps/note/models/notes.py:210 templates/member/profile_detail.html:37
 | 
					#: apps/note/models/notes.py:211 templates/member/profile_detail.html:37
 | 
				
			||||||
msgid "aliases"
 | 
					msgid "aliases"
 | 
				
			||||||
msgstr "alias"
 | 
					msgstr "alias"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#: apps/note/models/notes.py:228
 | 
					#: apps/note/models/notes.py:229
 | 
				
			||||||
msgid "Alias is too long."
 | 
					msgid "Alias is too long."
 | 
				
			||||||
msgstr "L'alias est trop long."
 | 
					msgstr "L'alias est trop long."
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#: apps/note/models/notes.py:233
 | 
					#: apps/note/models/notes.py:234
 | 
				
			||||||
#, fuzzy
 | 
					 | 
				
			||||||
#| msgid "An alias with a similar name already exists:"
 | 
					 | 
				
			||||||
msgid "An alias with a similar name already exists: {} "
 | 
					msgid "An alias with a similar name already exists: {} "
 | 
				
			||||||
msgstr "Un alias avec un nom similaire existe déjà."
 | 
					msgstr "Un alias avec un nom similaire existe déjà : {}"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#: apps/note/models/notes.py:242
 | 
					#: apps/note/models/notes.py:243
 | 
				
			||||||
msgid "You can't delete your main alias."
 | 
					msgid "You can't delete your main alias."
 | 
				
			||||||
msgstr "Vous ne pouvez pas supprimer votre alias principal."
 | 
					msgstr "Vous ne pouvez pas supprimer votre alias principal."
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -393,7 +401,6 @@ msgid "transaction categories"
 | 
				
			|||||||
msgstr "catégories de transaction"
 | 
					msgstr "catégories de transaction"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#: apps/note/models/transactions.py:47
 | 
					#: apps/note/models/transactions.py:47
 | 
				
			||||||
#, fuzzy
 | 
					 | 
				
			||||||
msgid "A template with this name already exist"
 | 
					msgid "A template with this name already exist"
 | 
				
			||||||
msgstr "Un modèle de transaction avec un nom similaire existe déjà."
 | 
					msgstr "Un modèle de transaction avec un nom similaire existe déjà."
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -433,11 +440,11 @@ msgstr "transaction"
 | 
				
			|||||||
msgid "transactions"
 | 
					msgid "transactions"
 | 
				
			||||||
msgstr "transactions"
 | 
					msgstr "transactions"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#: apps/note/models/transactions.py:184
 | 
					#: apps/note/models/transactions.py:185
 | 
				
			||||||
msgid "membership transaction"
 | 
					msgid "membership transaction"
 | 
				
			||||||
msgstr "transaction d'adhésion"
 | 
					msgstr "transaction d'adhésion"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#: apps/note/models/transactions.py:185
 | 
					#: apps/note/models/transactions.py:186
 | 
				
			||||||
msgid "membership transactions"
 | 
					msgid "membership transactions"
 | 
				
			||||||
msgstr "transactions d'adhésion"
 | 
					msgstr "transactions d'adhésion"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -445,34 +452,53 @@ msgstr "transactions d'adhésion"
 | 
				
			|||||||
msgid "Transfer money from your account to one or others"
 | 
					msgid "Transfer money from your account to one or others"
 | 
				
			||||||
msgstr "Transfert d'argent de ton compte vers un ou plusieurs autres"
 | 
					msgstr "Transfert d'argent de ton compte vers un ou plusieurs autres"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#: apps/note/views.py:138
 | 
					#: note_kfet/settings/__init__.py:63
 | 
				
			||||||
msgid "Consommations"
 | 
					 | 
				
			||||||
msgstr "transactions"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#: note_kfet/settings/base.py:162
 | 
					 | 
				
			||||||
msgid "German"
 | 
					 | 
				
			||||||
msgstr ""
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#: note_kfet/settings/base.py:163
 | 
					 | 
				
			||||||
msgid "English"
 | 
					 | 
				
			||||||
msgstr ""
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#: note_kfet/settings/base.py:164
 | 
					 | 
				
			||||||
msgid "French"
 | 
					 | 
				
			||||||
msgstr ""
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#: note_kfet/settings/base.py:215
 | 
					 | 
				
			||||||
msgid ""
 | 
					msgid ""
 | 
				
			||||||
"The Central Authentication Service grants you access to most of our websites "
 | 
					"The Central Authentication Service grants you access to most of our websites "
 | 
				
			||||||
"by authenticating only once, so you don't need to type your credentials "
 | 
					"by authenticating only once, so you don't need to type your credentials "
 | 
				
			||||||
"again unless your session expires or you logout."
 | 
					"again unless your session expires or you logout."
 | 
				
			||||||
msgstr ""
 | 
					msgstr ""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#: note_kfet/settings/base.py:156
 | 
				
			||||||
 | 
					msgid "German"
 | 
				
			||||||
 | 
					msgstr ""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#: note_kfet/settings/base.py:157
 | 
				
			||||||
 | 
					msgid "English"
 | 
				
			||||||
 | 
					msgstr ""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#: note_kfet/settings/base.py:158
 | 
				
			||||||
 | 
					msgid "French"
 | 
				
			||||||
 | 
					msgstr ""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#: templates/base.html:13
 | 
					#: templates/base.html:13
 | 
				
			||||||
msgid "The ENS Paris-Saclay BDE note."
 | 
					msgid "The ENS Paris-Saclay BDE note."
 | 
				
			||||||
msgstr "La note du BDE de l'ENS Paris-Saclay."
 | 
					msgstr "La note du BDE de l'ENS Paris-Saclay."
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#: templates/cas_server/base.html:7 templates/cas_server/base.html:26
 | 
					#: templates/base.html:70
 | 
				
			||||||
 | 
					msgid "Consumptions"
 | 
				
			||||||
 | 
					msgstr "Consommations"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#: templates/base.html:73
 | 
				
			||||||
 | 
					msgid "Clubs"
 | 
				
			||||||
 | 
					msgstr "Clubs"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#: templates/base.html:76
 | 
				
			||||||
 | 
					msgid "Activities"
 | 
				
			||||||
 | 
					msgstr "Activités"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#: templates/base.html:79
 | 
				
			||||||
 | 
					msgid "Buttons"
 | 
				
			||||||
 | 
					msgstr "Boutons"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					msgid "Buttons list"
 | 
				
			||||||
 | 
					msgstr "Liste des boutons"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#: templates/base.html:82 templates/note/transaction_form.html:35
 | 
				
			||||||
 | 
					msgid "Transfer"
 | 
				
			||||||
 | 
					msgstr "Virement"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#: templates/cas_server/base.html:7
 | 
				
			||||||
msgid "Central Authentication Service"
 | 
					msgid "Central Authentication Service"
 | 
				
			||||||
msgstr ""
 | 
					msgstr ""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -510,11 +536,11 @@ msgstr ""
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
#: templates/cas_server/login.html:11
 | 
					#: templates/cas_server/login.html:11
 | 
				
			||||||
msgid ""
 | 
					msgid ""
 | 
				
			||||||
"If you don't have any Note Kfet account, please follow <a href='/accounts"
 | 
					"If you don't have any Note Kfet account, please follow <a href='/accounts/"
 | 
				
			||||||
"/signup'>this link to sign up</a>."
 | 
					"signup'>this link to sign up</a>."
 | 
				
			||||||
msgstr ""
 | 
					msgstr ""
 | 
				
			||||||
"Si vous n'avez pas de compte Note Kfet, veuillez suivre <a href='/accounts"
 | 
					"Si vous n'avez pas de compte Note Kfet, veuillez suivre <a href='/accounts/"
 | 
				
			||||||
"/signup'>ce lien pour vous inscrire</a>."
 | 
					"signup'>ce lien pour vous inscrire</a>."
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#: templates/cas_server/login.html:17
 | 
					#: templates/cas_server/login.html:17
 | 
				
			||||||
msgid "Login"
 | 
					msgid "Login"
 | 
				
			||||||
@@ -524,6 +550,15 @@ msgstr ""
 | 
				
			|||||||
msgid "Connect to the service"
 | 
					msgid "Connect to the service"
 | 
				
			||||||
msgstr ""
 | 
					msgstr ""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#: templates/django_filters/rest_framework/crispy_form.html:4
 | 
				
			||||||
 | 
					#: templates/django_filters/rest_framework/form.html:2
 | 
				
			||||||
 | 
					msgid "Field filters"
 | 
				
			||||||
 | 
					msgstr ""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#: templates/django_filters/rest_framework/form.html:5
 | 
				
			||||||
 | 
					msgid "Submit"
 | 
				
			||||||
 | 
					msgstr ""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#: templates/member/club_detail.html:10
 | 
					#: templates/member/club_detail.html:10
 | 
				
			||||||
msgid "Membership starts on"
 | 
					msgid "Membership starts on"
 | 
				
			||||||
msgstr "L'adhésion commence le"
 | 
					msgstr "L'adhésion commence le"
 | 
				
			||||||
@@ -557,10 +592,8 @@ msgid "Regenerate token"
 | 
				
			|||||||
msgstr "Regénérer le jeton"
 | 
					msgstr "Regénérer le jeton"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#: templates/member/profile_alias.html:10
 | 
					#: templates/member/profile_alias.html:10
 | 
				
			||||||
#, fuzzy
 | 
					 | 
				
			||||||
#| msgid "alias"
 | 
					 | 
				
			||||||
msgid "Add alias"
 | 
					msgid "Add alias"
 | 
				
			||||||
msgstr "alias"
 | 
					msgstr "Ajouter un alias"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#: templates/member/profile_detail.html:15
 | 
					#: templates/member/profile_detail.html:15
 | 
				
			||||||
msgid "first name"
 | 
					msgid "first name"
 | 
				
			||||||
@@ -583,10 +616,8 @@ msgid "Manage auth token"
 | 
				
			|||||||
msgstr "Gérer les jetons d'authentification"
 | 
					msgstr "Gérer les jetons d'authentification"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#: templates/member/profile_detail.html:49
 | 
					#: templates/member/profile_detail.html:49
 | 
				
			||||||
#, fuzzy
 | 
					 | 
				
			||||||
#| msgid "Update Profile"
 | 
					 | 
				
			||||||
msgid "View Profile"
 | 
					msgid "View Profile"
 | 
				
			||||||
msgstr "Modifier le profil"
 | 
					msgstr "Voir le profil"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#: templates/member/profile_detail.html:62
 | 
					#: templates/member/profile_detail.html:62
 | 
				
			||||||
msgid "View my memberships"
 | 
					msgid "View my memberships"
 | 
				
			||||||
@@ -596,13 +627,10 @@ msgstr "Voir mes adhésions"
 | 
				
			|||||||
msgid "Save Changes"
 | 
					msgid "Save Changes"
 | 
				
			||||||
msgstr "Sauvegarder les changements"
 | 
					msgstr "Sauvegarder les changements"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#: templates/member/signup.html:8
 | 
				
			||||||
#: templates/member/signup.html:14
 | 
					#: templates/member/signup.html:14
 | 
				
			||||||
msgid "Sign Up"
 | 
					msgid "Sign up"
 | 
				
			||||||
msgstr ""
 | 
					msgstr "Inscription"
 | 
				
			||||||
 | 
					 | 
				
			||||||
#: templates/note/transaction_form.html:35
 | 
					 | 
				
			||||||
msgid "Transfer"
 | 
					 | 
				
			||||||
msgstr "Virement"
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
#: templates/registration/logged_out.html:8
 | 
					#: templates/registration/logged_out.html:8
 | 
				
			||||||
msgid "Thanks for spending some quality time with the Web site today."
 | 
					msgid "Thanks for spending some quality time with the Web site today."
 | 
				
			||||||
@@ -613,7 +641,7 @@ msgid "Log in again"
 | 
				
			|||||||
msgstr ""
 | 
					msgstr ""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#: templates/registration/login.html:7 templates/registration/login.html:8
 | 
					#: templates/registration/login.html:7 templates/registration/login.html:8
 | 
				
			||||||
#: templates/registration/login.html:22
 | 
					#: templates/registration/login.html:26
 | 
				
			||||||
#: templates/registration/password_reset_complete.html:10
 | 
					#: templates/registration/password_reset_complete.html:10
 | 
				
			||||||
msgid "Log in"
 | 
					msgid "Log in"
 | 
				
			||||||
msgstr ""
 | 
					msgstr ""
 | 
				
			||||||
@@ -625,7 +653,7 @@ msgid ""
 | 
				
			|||||||
"page. Would you like to login to a different account?"
 | 
					"page. Would you like to login to a different account?"
 | 
				
			||||||
msgstr ""
 | 
					msgstr ""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#: templates/registration/login.html:23
 | 
					#: templates/registration/login.html:27
 | 
				
			||||||
msgid "Forgotten your password or username?"
 | 
					msgid "Forgotten your password or username?"
 | 
				
			||||||
msgstr ""
 | 
					msgstr ""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,4 +1,9 @@
 | 
				
			|||||||
 | 
					# Copyright (C) 2018-2020 by BDE ENS Paris-Saclay
 | 
				
			||||||
 | 
					# SPDX-License-Identifier: GPL-3.0-or-later
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from django.utils.translation import gettext_lazy as _
 | 
				
			||||||
import re
 | 
					import re
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from .base import *
 | 
					from .base import *
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -30,10 +35,6 @@ read_env()
 | 
				
			|||||||
app_stage = os.environ.get('DJANGO_APP_STAGE', 'dev')
 | 
					app_stage = os.environ.get('DJANGO_APP_STAGE', 'dev')
 | 
				
			||||||
if app_stage == 'prod':
 | 
					if app_stage == 'prod':
 | 
				
			||||||
    from .production import *
 | 
					    from .production import *
 | 
				
			||||||
 | 
					 | 
				
			||||||
    DATABASES["default"]["PASSWORD"] = os.environ.get('DJANGO_DB_PASSWORD', 'CHANGE_ME_IN_ENV_SETTINGS')
 | 
					 | 
				
			||||||
    SECRET_KEY = os.environ.get('DJANGO_SECRET_KEY', 'CHANGE_ME_IN_ENV_SETTINGS')
 | 
					 | 
				
			||||||
    ALLOWED_HOSTS = [os.environ.get('ALLOWED_HOSTS', 'localhost')]
 | 
					 | 
				
			||||||
else:
 | 
					else:
 | 
				
			||||||
    from .development import *
 | 
					    from .development import *
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -46,12 +47,14 @@ except ImportError:
 | 
				
			|||||||
if "cas" in INSTALLED_APPS:
 | 
					if "cas" in INSTALLED_APPS:
 | 
				
			||||||
    MIDDLEWARE += ['cas.middleware.CASMiddleware']
 | 
					    MIDDLEWARE += ['cas.middleware.CASMiddleware']
 | 
				
			||||||
    # CAS Settings
 | 
					    # CAS Settings
 | 
				
			||||||
 | 
					    CAS_SERVER_URL = "https://" + os.getenv("NOTE_URL", "note.example.com") + "/cas/"
 | 
				
			||||||
    CAS_AUTO_CREATE_USER = False
 | 
					    CAS_AUTO_CREATE_USER = False
 | 
				
			||||||
    CAS_LOGO_URL = "/static/img/Saperlistpopette.png"
 | 
					    CAS_LOGO_URL = "/static/img/Saperlistpopette.png"
 | 
				
			||||||
    CAS_FAVICON_URL = "/static/favicon/favicon-32x32.png"
 | 
					    CAS_FAVICON_URL = "/static/favicon/favicon-32x32.png"
 | 
				
			||||||
    CAS_SHOW_SERVICE_MESSAGES = True
 | 
					    CAS_SHOW_SERVICE_MESSAGES = True
 | 
				
			||||||
    CAS_SHOW_POWERED = False
 | 
					    CAS_SHOW_POWERED = False
 | 
				
			||||||
    CAS_REDIRECT_TO_LOGIN_AFTER_LOGOUT = False
 | 
					    CAS_REDIRECT_TO_LOGIN_AFTER_LOGOUT = False
 | 
				
			||||||
 | 
					    CAS_PROVIDE_URL_TO_LOGOUT = True
 | 
				
			||||||
    CAS_INFO_MESSAGES = {
 | 
					    CAS_INFO_MESSAGES = {
 | 
				
			||||||
        "cas_explained": {
 | 
					        "cas_explained": {
 | 
				
			||||||
            "message": _(
 | 
					            "message": _(
 | 
				
			||||||
@@ -68,7 +71,11 @@ if "cas" in INSTALLED_APPS:
 | 
				
			|||||||
        'cas_explained',
 | 
					        'cas_explained',
 | 
				
			||||||
    ]
 | 
					    ]
 | 
				
			||||||
    AUTHENTICATION_BACKENDS += ('cas.backends.CASBackend',)
 | 
					    AUTHENTICATION_BACKENDS += ('cas.backends.CASBackend',)
 | 
				
			||||||
    
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					if "logs" in INSTALLED_APPS:
 | 
				
			||||||
 | 
					    MIDDLEWARE += ('logs.middlewares.LogsMiddleware',)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
if "debug_toolbar" in INSTALLED_APPS:
 | 
					if "debug_toolbar" in INSTALLED_APPS:
 | 
				
			||||||
    MIDDLEWARE.insert(1,"debug_toolbar.middleware.DebugToolbarMiddleware")
 | 
					    MIDDLEWARE.insert(1, "debug_toolbar.middleware.DebugToolbarMiddleware")
 | 
				
			||||||
    INTERNAL_IPS = [ '127.0.0.1']
 | 
					    INTERNAL_IPS = ['127.0.0.1']
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -39,6 +39,8 @@ INSTALLED_APPS = [
 | 
				
			|||||||
    'polymorphic',
 | 
					    'polymorphic',
 | 
				
			||||||
    'crispy_forms',
 | 
					    'crispy_forms',
 | 
				
			||||||
    'django_tables2',
 | 
					    'django_tables2',
 | 
				
			||||||
 | 
					    'cas_server',
 | 
				
			||||||
 | 
					    'cas',
 | 
				
			||||||
    # Django contrib
 | 
					    # Django contrib
 | 
				
			||||||
    'django.contrib.admin',
 | 
					    'django.contrib.admin',
 | 
				
			||||||
    'django.contrib.admindocs',
 | 
					    'django.contrib.admindocs',
 | 
				
			||||||
@@ -135,11 +137,14 @@ REST_FRAMEWORK = {
 | 
				
			|||||||
    # or allow read-only access for unauthenticated users.
 | 
					    # or allow read-only access for unauthenticated users.
 | 
				
			||||||
    'DEFAULT_PERMISSION_CLASSES': [
 | 
					    'DEFAULT_PERMISSION_CLASSES': [
 | 
				
			||||||
        # TODO Maybe replace it with our custom permissions system
 | 
					        # TODO Maybe replace it with our custom permissions system
 | 
				
			||||||
        'rest_framework.permissions.DjangoModelPermissionsOrAnonReadOnly'
 | 
					        'rest_framework.permissions.DjangoModelPermissions',
 | 
				
			||||||
    ],
 | 
					    ],
 | 
				
			||||||
    'DEFAULT_AUTHENTICATION_CLASSES': [
 | 
					    'DEFAULT_AUTHENTICATION_CLASSES': [
 | 
				
			||||||
 | 
					        'rest_framework.authentication.SessionAuthentication',
 | 
				
			||||||
        'rest_framework.authentication.TokenAuthentication',
 | 
					        'rest_framework.authentication.TokenAuthentication',
 | 
				
			||||||
    ]
 | 
					    ],
 | 
				
			||||||
 | 
					    'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination',
 | 
				
			||||||
 | 
					    'PAGE_SIZE': 20,
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# Internationalization
 | 
					# Internationalization
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -17,12 +17,24 @@ import os
 | 
				
			|||||||
# https://docs.djangoproject.com/en/2.2/ref/settings/#databases
 | 
					# https://docs.djangoproject.com/en/2.2/ref/settings/#databases
 | 
				
			||||||
from . import *
 | 
					from . import *
 | 
				
			||||||
 | 
					
 | 
				
			||||||
DATABASES = {
 | 
					if os.getenv("DJANGO_DEV_STORE_METHOD", "sqllite") == "postgresql":
 | 
				
			||||||
    'default': {
 | 
					    DATABASES = {
 | 
				
			||||||
        'ENGINE': 'django.db.backends.sqlite3',
 | 
					        'default': {
 | 
				
			||||||
        'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
 | 
					            'ENGINE': 'django.db.backends.postgresql_psycopg2',
 | 
				
			||||||
 | 
					            'NAME': os.environ.get('DJANGO_DB_NAME', 'note_db'),
 | 
				
			||||||
 | 
					            'USER': os.environ.get('DJANGO_DB_USER', 'note'),
 | 
				
			||||||
 | 
					            'PASSWORD': os.environ.get('DJANGO_DB_PASSWORD', 'CHANGE_ME_IN_ENV_SETTINGS'),
 | 
				
			||||||
 | 
					            'HOST': os.environ.get('DJANGO_DB_HOST', 'localhost'),
 | 
				
			||||||
 | 
					            'PORT': os.environ.get('DJANGO_DB_PORT', ''),  # Use default port
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					else:
 | 
				
			||||||
 | 
					    DATABASES = {
 | 
				
			||||||
 | 
					        'default': {
 | 
				
			||||||
 | 
					            'ENGINE': 'django.db.backends.sqlite3',
 | 
				
			||||||
 | 
					            'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
# Break it, fix it!
 | 
					# Break it, fix it!
 | 
				
			||||||
DEBUG = True
 | 
					DEBUG = True
 | 
				
			||||||
@@ -39,7 +51,7 @@ EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'
 | 
				
			|||||||
# EMAIL_HOST_USER = 'change_me'
 | 
					# EMAIL_HOST_USER = 'change_me'
 | 
				
			||||||
# EMAIL_HOST_PASSWORD = 'change_me'
 | 
					# EMAIL_HOST_PASSWORD = 'change_me'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
SERVER_EMAIL = 'no-reply@example.org'
 | 
					SERVER_EMAIL = 'no-reply@' + os.getenv("DOMAIN", "example.com")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# Security settings
 | 
					# Security settings
 | 
				
			||||||
SECURE_CONTENT_TYPE_NOSNIFF = False
 | 
					SECURE_CONTENT_TYPE_NOSNIFF = False
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,6 +1,8 @@
 | 
				
			|||||||
# Copyright (C) 2018-2020 by BDE ENS Paris-Saclay
 | 
					# Copyright (C) 2018-2020 by BDE ENS Paris-Saclay
 | 
				
			||||||
# SPDX-License-Identifier: GPL-3.0-or-later
 | 
					# SPDX-License-Identifier: GPL-3.0-or-later
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import os
 | 
				
			||||||
 | 
					
 | 
				
			||||||
########################
 | 
					########################
 | 
				
			||||||
# Production  Settings #
 | 
					# Production  Settings #
 | 
				
			||||||
########################
 | 
					########################
 | 
				
			||||||
@@ -14,11 +16,11 @@
 | 
				
			|||||||
DATABASES = {
 | 
					DATABASES = {
 | 
				
			||||||
    'default': {
 | 
					    'default': {
 | 
				
			||||||
        'ENGINE': 'django.db.backends.postgresql_psycopg2',
 | 
					        'ENGINE': 'django.db.backends.postgresql_psycopg2',
 | 
				
			||||||
        'NAME': 'note_db',
 | 
					        'NAME': os.environ.get('DJANGO_DB_NAME', 'note_db'),
 | 
				
			||||||
        'USER': 'note',
 | 
					        'USER': os.environ.get('DJANGO_DB_USER', 'note'),
 | 
				
			||||||
        'PASSWORD': 'update_in_env_variable',
 | 
					        'PASSWORD': os.environ.get('DJANGO_DB_PASSWORD', 'CHANGE_ME_IN_ENV_SETTINGS'),
 | 
				
			||||||
        'HOST': '127.0.0.1',
 | 
					        'HOST': os.environ.get('DJANGO_DB_HOST', 'localhost'),
 | 
				
			||||||
        'PORT': '',
 | 
					        'PORT': os.environ.get('DJANGO_DB_PORT', ''),  # Use default port
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -26,7 +28,9 @@ DATABASES = {
 | 
				
			|||||||
DEBUG = True
 | 
					DEBUG = True
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# Mandatory !
 | 
					# Mandatory !
 | 
				
			||||||
ALLOWED_HOSTS = []
 | 
					ALLOWED_HOSTS = [os.environ.get('NOTE_URL', 'localhost')]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					SECRET_KEY = os.environ.get('DJANGO_SECRET_KEY', 'CHANGE_ME_IN_ENV_SETTINGS')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# Emails
 | 
					# Emails
 | 
				
			||||||
EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'
 | 
					EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'
 | 
				
			||||||
@@ -37,7 +41,7 @@ EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'
 | 
				
			|||||||
# EMAIL_HOST_USER = 'change_me'
 | 
					# EMAIL_HOST_USER = 'change_me'
 | 
				
			||||||
# EMAIL_HOST_PASSWORD = 'change_me'
 | 
					# EMAIL_HOST_PASSWORD = 'change_me'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
SERVER_EMAIL = 'no-reply@example.org'
 | 
					SERVER_EMAIL = 'no-reply@' + os.getenv("DOMAIN", "example.com")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# Security settings
 | 
					# Security settings
 | 
				
			||||||
SECURE_CONTENT_TYPE_NOSNIFF = False
 | 
					SECURE_CONTENT_TYPE_NOSNIFF = False
 | 
				
			||||||
@@ -49,4 +53,4 @@ X_FRAME_OPTIONS = 'DENY'
 | 
				
			|||||||
SESSION_COOKIE_AGE = 60 * 60 * 3
 | 
					SESSION_COOKIE_AGE = 60 * 60 * 3
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# CAS Client settings
 | 
					# CAS Client settings
 | 
				
			||||||
CAS_SERVER_URL = "https://note.crans.org/cas/"
 | 
					CAS_SERVER_URL = "https://" + os.getenv("NOTE_URL", "note.example.com") + "/cas/"
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -20,8 +20,7 @@ urlpatterns = [
 | 
				
			|||||||
    path('accounts/', include('django.contrib.auth.urls')),
 | 
					    path('accounts/', include('django.contrib.auth.urls')),
 | 
				
			||||||
    path('admin/doc/', include('django.contrib.admindocs.urls')),
 | 
					    path('admin/doc/', include('django.contrib.admindocs.urls')),
 | 
				
			||||||
    path('admin/', admin.site.urls),
 | 
					    path('admin/', admin.site.urls),
 | 
				
			||||||
    path('logs/', include('logs.urls')),
 | 
					    path('api/', include('api.urls')),
 | 
				
			||||||
    path('api/', include('api.urls')),  
 | 
					 | 
				
			||||||
]
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
 | 
					urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
 | 
				
			||||||
@@ -37,8 +36,8 @@ if "cas" in settings.INSTALLED_APPS:
 | 
				
			|||||||
    from cas import views as cas_views
 | 
					    from cas import views as cas_views
 | 
				
			||||||
    urlpatterns += [
 | 
					    urlpatterns += [
 | 
				
			||||||
        # Include CAS Client routers
 | 
					        # Include CAS Client routers
 | 
				
			||||||
        path('accounts/login/', cas_views.login, name='login'),
 | 
					        path('accounts/login/cas/', cas_views.login, name='cas_login'),
 | 
				
			||||||
        path('accounts/logout/', cas_views.logout, name='logout'),
 | 
					        path('accounts/logout/cas/', cas_views.logout, name='cas_logout'),
 | 
				
			||||||
       
 | 
					       
 | 
				
			||||||
    ]
 | 
					    ]
 | 
				
			||||||
if "debug_toolbar" in settings.INSTALLED_APPS:
 | 
					if "debug_toolbar" in settings.INSTALLED_APPS:
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,4 +1,4 @@
 | 
				
			|||||||
{% load static i18n pretty_money static %}
 | 
					{% load static i18n pretty_money static getenv %}
 | 
				
			||||||
{% comment %}
 | 
					{% comment %}
 | 
				
			||||||
SPDX-License-Identifier: GPL-3.0-or-later
 | 
					SPDX-License-Identifier: GPL-3.0-or-later
 | 
				
			||||||
{% endcomment %}
 | 
					{% endcomment %}
 | 
				
			||||||
@@ -75,6 +75,9 @@ SPDX-License-Identifier: GPL-3.0-or-later
 | 
				
			|||||||
                <li class="nav-item active">
 | 
					                <li class="nav-item active">
 | 
				
			||||||
                    <a class="nav-link" href="#"><i class="fa fa-calendar"></i> {% trans 'Activities' %}</a>
 | 
					                    <a class="nav-link" href="#"><i class="fa fa-calendar"></i> {% trans 'Activities' %}</a>
 | 
				
			||||||
                </li>
 | 
					                </li>
 | 
				
			||||||
 | 
					                <li class="nav-item active">
 | 
				
			||||||
 | 
					                    <a class="nav-link" href="{% url 'note:template_list' %}"><i class="fa fa-coffee"></i> {% trans 'Buttons' %}</a>
 | 
				
			||||||
 | 
					                </li>
 | 
				
			||||||
                <li class="nav-item active">
 | 
					                <li class="nav-item active">
 | 
				
			||||||
                    <a class="nav-link" href="{% url 'note:transfer' %}"><i class="fa fa-exchange"></i>{% trans 'Transfer' %} </a>
 | 
					                    <a class="nav-link" href="{% url 'note:transfer' %}"><i class="fa fa-exchange"></i>{% trans 'Transfer' %} </a>
 | 
				
			||||||
                </li>
 | 
					                </li>
 | 
				
			||||||
@@ -125,7 +128,7 @@ SPDX-License-Identifier: GPL-3.0-or-later
 | 
				
			|||||||
                      class="form-inline">
 | 
					                      class="form-inline">
 | 
				
			||||||
                    <span class="text-muted mr-1">
 | 
					                    <span class="text-muted mr-1">
 | 
				
			||||||
                        NoteKfet2020 —
 | 
					                        NoteKfet2020 —
 | 
				
			||||||
                        <a href="mailto:tresorie.bde@lists.crans.org"
 | 
					                        <a href="mailto:{{ "CONTACT_EMAIL" | getenv }}"
 | 
				
			||||||
                           class="text-muted">Nous contacter</a> —
 | 
					                           class="text-muted">Nous contacter</a> —
 | 
				
			||||||
                    </span>
 | 
					                    </span>
 | 
				
			||||||
                    {% csrf_token %}
 | 
					                    {% csrf_token %}
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										5
									
								
								templates/django_filters/rest_framework/crispy_form.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								templates/django_filters/rest_framework/crispy_form.html
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,5 @@
 | 
				
			|||||||
 | 
					{% load crispy_forms_tags %}
 | 
				
			||||||
 | 
					{% load i18n %}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<h2>{% trans "Field filters" %}</h2>
 | 
				
			||||||
 | 
					{% crispy filter.form %}
 | 
				
			||||||
							
								
								
									
										6
									
								
								templates/django_filters/rest_framework/form.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								templates/django_filters/rest_framework/form.html
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,6 @@
 | 
				
			|||||||
 | 
					{% load i18n %}
 | 
				
			||||||
 | 
					<h2>{% trans "Field filters" %}</h2>
 | 
				
			||||||
 | 
					<form class="form" action="" method="get">
 | 
				
			||||||
 | 
					    {{ filter.form.as_p }}
 | 
				
			||||||
 | 
					    <button type="submit" class="btn btn-primary">{% trans "Submit" %}</button>
 | 
				
			||||||
 | 
					</form>
 | 
				
			||||||
							
								
								
									
										1
									
								
								templates/django_filters/widgets/multiwidget.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								templates/django_filters/widgets/multiwidget.html
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1 @@
 | 
				
			|||||||
 | 
					{% for widget in widget.subwidgets %}{% include widget.template_name %}{% if forloop.first %}-{% endif %}{% endfor %}
 | 
				
			||||||
@@ -1,11 +1,12 @@
 | 
				
			|||||||
{% extends "base.html" %}
 | 
					{% extends "base.html" %}
 | 
				
			||||||
{% load static %}
 | 
					{% load static %}
 | 
				
			||||||
 | 
					{% load i18n %}
 | 
				
			||||||
{% load crispy_forms_tags %}
 | 
					{% load crispy_forms_tags %}
 | 
				
			||||||
{% block content %}
 | 
					{% block content %}
 | 
				
			||||||
<p><a class="btn btn-default" href="{% url 'note:template_list' %}">Template Listing</a></p>
 | 
					<p><a class="btn btn-default" href="{% url 'note:template_list' %}">{% trans "Clubs list" %}</a></p>
 | 
				
			||||||
<form method="post">
 | 
					<form method="post">
 | 
				
			||||||
{% csrf_token %}
 | 
					{% csrf_token %}
 | 
				
			||||||
{{form|crispy}}
 | 
					{{form|crispy}}
 | 
				
			||||||
<button class="btn btn-primary" type="submit">Submit</button>
 | 
					<button class="btn btn-primary" type="submit">{% trans "Submit" %}</button>
 | 
				
			||||||
</form>
 | 
					</form>
 | 
				
			||||||
{% endblock %}
 | 
					{% endblock %}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,10 +1,11 @@
 | 
				
			|||||||
{% extends "base.html" %}
 | 
					{% extends "base.html" %}
 | 
				
			||||||
{% load render_table from django_tables2 %}
 | 
					{% load render_table from django_tables2 %}
 | 
				
			||||||
 | 
					{% load i18n %}
 | 
				
			||||||
{% block content %}
 | 
					{% block content %}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
{% render_table  table %}
 | 
					{% render_table  table %}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<a class="btn btn-primary" href="{% url 'member:club_create' %}">New Club</a>
 | 
					<a class="btn btn-primary" href="{% url 'member:club_create' %}">{% trans "New club" %}</a>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
{% endblock %}
 | 
					{% endblock %}
 | 
				
			||||||
{% block extrajavascript %}
 | 
					{% block extrajavascript %}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -2,16 +2,16 @@
 | 
				
			|||||||
{% extends 'base.html' %}
 | 
					{% extends 'base.html' %}
 | 
				
			||||||
{% load crispy_forms_tags %}
 | 
					{% load crispy_forms_tags %}
 | 
				
			||||||
{% load i18n %}
 | 
					{% load i18n %}
 | 
				
			||||||
{% block title %}Sign Up{% endblock %}
 | 
					{% block title %}{% trans "Sign up" %}{% endblock %}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
{% block content %}
 | 
					{% block content %}
 | 
				
			||||||
  <h2>Sign up</h2>
 | 
					  <h2>{% trans "Sign up" %}</h2>
 | 
				
			||||||
  <form method="post">
 | 
					  <form method="post">
 | 
				
			||||||
      {% csrf_token %}
 | 
					      {% csrf_token %}
 | 
				
			||||||
      {{ form|crispy }}
 | 
					      {{ form|crispy }}
 | 
				
			||||||
      {{ profile_form|crispy }}
 | 
					      {{ profile_form|crispy }}
 | 
				
			||||||
      <button class="btn btn-success" type="submit">
 | 
					      <button class="btn btn-success" type="submit">
 | 
				
			||||||
          {% trans "Sign Up" %}
 | 
					          {% trans "Sign up" %}
 | 
				
			||||||
      </button>
 | 
					      </button>
 | 
				
			||||||
  </form>
 | 
					  </form>
 | 
				
			||||||
{% endblock %}
 | 
					{% endblock %}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,8 +1,9 @@
 | 
				
			|||||||
{% extends "base.html" %}
 | 
					{% extends "base.html" %}
 | 
				
			||||||
{% load static %}
 | 
					{% load static %}
 | 
				
			||||||
 | 
					{% load i18n %}
 | 
				
			||||||
{% load crispy_forms_tags %}
 | 
					{% load crispy_forms_tags %}
 | 
				
			||||||
{% block content %}
 | 
					{% block content %}
 | 
				
			||||||
<p><a class="btn btn-default" href="{% url 'note:template_list' %}">Template Listing</a></p>
 | 
					<p><a class="btn btn-default" href="{% url 'note:template_list' %}">{% trans "Buttons list" %}</a></p>
 | 
				
			||||||
<form method="post">
 | 
					<form method="post">
 | 
				
			||||||
{% csrf_token %}
 | 
					{% csrf_token %}
 | 
				
			||||||
{{form|crispy}}
 | 
					{{form|crispy}}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -17,6 +17,10 @@ SPDX-License-Identifier: GPL-2.0-or-later
 | 
				
			|||||||
        </p>
 | 
					        </p>
 | 
				
			||||||
    {% endif %}
 | 
					    {% endif %}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    <div class="alert alert-info">
 | 
				
			||||||
 | 
					        Vous pouvez aussi vous connecter via l'authentification centralisée <a href="{% url 'cas_login' %}">en suivant ce lien.</a>
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    <form action="{{ app_path }}" method="post" id="login-form">{% csrf_token %}
 | 
					    <form action="{{ app_path }}" method="post" id="login-form">{% csrf_token %}
 | 
				
			||||||
        {{ form | crispy }}
 | 
					        {{ form | crispy }}
 | 
				
			||||||
        <input type="submit" value="{% trans 'Log in' %}" class="btn btn-primary">
 | 
					        <input type="submit" value="{% trans 'Log in' %}" class="btn btn-primary">
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										2
									
								
								tox.ini
									
									
									
									
									
								
							
							
						
						
									
										2
									
								
								tox.ini
									
									
									
									
									
								
							@@ -32,7 +32,7 @@ deps =
 | 
				
			|||||||
    pep8-naming
 | 
					    pep8-naming
 | 
				
			||||||
    pyflakes
 | 
					    pyflakes
 | 
				
			||||||
commands =
 | 
					commands =
 | 
				
			||||||
    flake8 apps/activity apps/api apps/member apps/note
 | 
					    flake8 apps/activity apps/api apps/logs apps/member apps/note
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[flake8]
 | 
					[flake8]
 | 
				
			||||||
# Ignore too many errors, should be reduced in the future
 | 
					# Ignore too many errors, should be reduced in the future
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user