Compare commits

...

15 Commits

Author SHA1 Message Date
Yohann D'ANELLO 434a393f3b During the beta, don't update the wiki automatically 2020-08-06 13:09:35 +02:00
Yohann D'ANELLO cba6a35b6c Display matched alias in user table 2020-08-06 13:07:22 +02:00
Yohann D'ANELLO 0de69cbfaf 💚 Fix linters 2020-08-06 12:50:24 +02:00
Yohann D'ANELLO d9cf812074 🐛 Prevent transactions to have the same source and destination 2020-08-06 12:46:44 +02:00
Yohann D'ANELLO 252ddb832d 🐛 Invert membership date inequalities 2020-08-06 12:36:28 +02:00
Yohann D'ANELLO 6dcb82855d 🐛 Default comment is an empty string, not None 2020-08-06 12:31:57 +02:00
Yohann D'ANELLO 1247818033 🐛 lxml is required to parse html pages 2020-08-06 12:30:14 +02:00
Yohann D'ANELLO 9439b3cb2d 🐛 Add Beautifulsoup4 as a dependency 2020-08-06 12:22:19 +02:00
Yohann D'ANELLO a07b942738 Export activities in the Crans Wiki 2020-08-06 12:15:37 +02:00
Yohann D'ANELLO b7ae411f96 Export activities in the Crans Wiki 2020-08-06 12:15:23 +02:00
Yohann D'ANELLO 315af75c45 Backup database daily 2020-08-06 09:27:33 +02:00
Yohann D'ANELLO fd7e314ca3 Don't log mails in database 2020-08-06 08:53:47 +02:00
Yohann D'ANELLO 0b46140771 Cron file must be owned by root 2020-08-06 08:21:05 +02:00
Yohann D'ANELLO 547fbf564b Add cron to refresh highlighted buttons 2020-08-06 08:14:54 +02:00
Yohann D'ANELLO 2aebeb8927 respoinfo.bde => respo-info.bde 2020-08-06 08:13:44 +02:00
26 changed files with 87 additions and 43 deletions

View File

@ -16,3 +16,5 @@ EMAIL_HOST=smtp.localhost
EMAIL_PORT=465 EMAIL_PORT=465
EMAIL_USER=notekfet@localhost EMAIL_USER=notekfet@localhost
EMAIL_PASSWORD=CHANGE_ME EMAIL_PASSWORD=CHANGE_ME
WIKI_USER=NoteKfet2020
WIKI_PASSWORD=

1
.gitignore vendored
View File

@ -39,6 +39,7 @@ secrets.py
.env .env
map.json map.json
*.log *.log
backups/
media/ media/
# Virtualenv # Virtualenv

View File

@ -124,6 +124,8 @@ On supposera pour la suite que vous utilisez Debian/Ubuntu sur un serveur tout n
EMAIL_PORT=465 EMAIL_PORT=465
EMAIL_USER=notekfet@localhost EMAIL_USER=notekfet@localhost
EMAIL_PASSWORD=CHANGE_ME EMAIL_PASSWORD=CHANGE_ME
WIKI_USER=NoteKfet2020
WIKI_PASSWORD=CHANGE_ME
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

View File

@ -41,3 +41,9 @@
systemd: systemd:
name: cron name: cron
state: restarted state: restarted
- name: Update permissions for the cron file
file:
path: /var/www/note_kfet/note.cron
owner: root
group: www-data

View File

@ -2,8 +2,8 @@
# SPDX-License-Identifier: GPL-3.0-or-later # SPDX-License-Identifier: GPL-3.0-or-later
from django.contrib import admin from django.contrib import admin
from note_kfet.admin import admin_site from note_kfet.admin import admin_site
from .models import Activity, ActivityType, Guest, Entry from .models import Activity, ActivityType, Guest, Entry

View File

@ -1,7 +1,10 @@
# 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 datetime import timedelta, datetime
from datetime import timedelta, datetime
from threading import Thread
from django.conf import settings
from django.contrib.auth.models import User from django.contrib.auth.models import User
from django.db import models from django.db import models
from django.db.models import Q from django.db.models import Q
@ -54,6 +57,13 @@ class Activity(models.Model):
verbose_name=_('description'), verbose_name=_('description'),
) )
location = models.CharField(
verbose_name=_('location'),
max_length=255,
blank=True,
default="",
)
activity_type = models.ForeignKey( activity_type = models.ForeignKey(
ActivityType, ActivityType,
on_delete=models.PROTECT, on_delete=models.PROTECT,
@ -99,6 +109,19 @@ class Activity(models.Model):
verbose_name=_('open'), verbose_name=_('open'),
) )
def save(self, *args, **kwargs):
"""
Update the activity wiki page each time the activity is updated (validation, change description, ...)
"""
ret = super().save(*args, **kwargs)
if self.pk and "scripts" in settings.INSTALLED_APPS:
def refresh_activities():
from scripts.management.commands.refresh_activities import Command as RefreshActivitiesCommand
RefreshActivitiesCommand.refresh_human_readable_wiki_page("Modification de l'activité " + self.name)
RefreshActivitiesCommand.refresh_raw_wiki_page("Modification de l'activité " + self.name)
Thread(daemon=True, target=refresh_activities).start()
return ret
def __str__(self): def __str__(self):
return self.name return self.name

View File

@ -23,6 +23,9 @@ EXCLUDED = [
'cas_server.userattributes', 'cas_server.userattributes',
'contenttypes.contenttype', 'contenttypes.contenttype',
'logs.changelog', # Never remove this line 'logs.changelog', # Never remove this line
'mailer.dontsendentry',
'mailer.message',
'mailer.messagelog',
'migrations.migration', 'migrations.migration',
'note.note' # We only store the subclasses 'note.note' # We only store the subclasses
'note.transaction', 'note.transaction',

View File

@ -16,7 +16,6 @@ from django.utils.encoding import force_bytes
from django.utils.http import urlsafe_base64_encode from django.utils.http import urlsafe_base64_encode
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
from phonenumber_field.modelfields import PhoneNumberField from phonenumber_field.modelfields import PhoneNumberField
from permission.models import Role from permission.models import Role
from registration.tokens import email_validation_token from registration.tokens import email_validation_token
from note.models import MembershipTransaction from note.models import MembershipTransaction
@ -329,7 +328,6 @@ class Membership(models.Model):
club=self.club, club=self.club,
date_start=max(self.date_end + datetime.timedelta(days=1), self.club.membership_start), date_start=max(self.date_end + datetime.timedelta(days=1), self.club.membership_start),
) )
from django.forms import model_to_dict
if hasattr(self, '_force_renew_parent') and self._force_renew_parent: if hasattr(self, '_force_renew_parent') and self._force_renew_parent:
new_membership._force_renew_parent = True new_membership._force_renew_parent = True
if hasattr(self, '_soge') and self._soge: if hasattr(self, '_soge') and self._soge:
@ -445,8 +443,8 @@ class Membership(models.Model):
) )
transaction._force_save = True transaction._force_save = True
if hasattr(self, '_soge') and "treasury" in settings.INSTALLED_APPS\ if hasattr(self, '_soge') and "treasury" in settings.INSTALLED_APPS\
and (self.club.name == "BDE" or self.club.name == "Kfet" or and (self.club.name == "BDE" or self.club.name == "Kfet"
("wei" in settings.INSTALLED_APPS and hasattr(self.club, "weiclub") and self.club.weiclub)): or ("wei" in settings.INSTALLED_APPS and hasattr(self.club, "weiclub") and self.club.weiclub)):
# If the soge pays, then the transaction is unvalidated in a first time, then submitted for control # If the soge pays, then the transaction is unvalidated in a first time, then submitted for control
# to treasurers. # to treasurers.
transaction.valid = False transaction.valid = False

View File

@ -38,6 +38,8 @@ class UserTable(tables.Table):
""" """
List all users. List all users.
""" """
alias = tables.Column()
section = tables.Column(accessor='profile.section') section = tables.Column(accessor='profile.section')
balance = tables.Column(accessor='note.balance', verbose_name=_("Balance")) balance = tables.Column(accessor='note.balance', verbose_name=_("Balance"))
@ -50,7 +52,7 @@ class UserTable(tables.Table):
'class': 'table table-condensed table-striped table-hover' 'class': 'table table-condensed table-striped table-hover'
} }
template_name = 'django_tables2/bootstrap4.html' template_name = 'django_tables2/bootstrap4.html'
fields = ('last_name', 'first_name', 'username', 'email') fields = ('last_name', 'first_name', 'username', 'alias', 'email')
model = User model = User
row_attrs = { row_attrs = {
'class': 'table-row', 'class': 'table-row',

View File

@ -10,7 +10,7 @@ from django.contrib.auth import logout
from django.contrib.auth.mixins import LoginRequiredMixin from django.contrib.auth.mixins import LoginRequiredMixin
from django.contrib.auth.models import User from django.contrib.auth.models import User
from django.contrib.auth.views import LoginView from django.contrib.auth.views import LoginView
from django.db.models import Q from django.db.models import Q, F
from django.shortcuts import redirect from django.shortcuts import redirect
from django.urls import reverse_lazy from django.urls import reverse_lazy
from django.utils import timezone from django.utils import timezone
@ -172,7 +172,9 @@ class UserListView(ProtectQuerysetMixin, LoginRequiredMixin, SingleTableView):
""" """
Filter the user list with the given pattern. Filter the user list with the given pattern.
""" """
qs = super().get_queryset().distinct().filter(profile__registration_valid=True) qs = super().get_queryset().distinct("pk").annotate(alias=F("note__alias__name"))\
.annotate(normalized_alias=F("note__alias__normalized_name"))\
.filter(profile__registration_valid=True)
if "search" in self.request.GET: if "search" in self.request.GET:
pattern = self.request.GET["search"] pattern = self.request.GET["search"]
@ -184,8 +186,8 @@ class UserListView(ProtectQuerysetMixin, LoginRequiredMixin, SingleTableView):
| Q(last_name__iregex=pattern) | Q(last_name__iregex=pattern)
| Q(profile__section__iregex=pattern) | Q(profile__section__iregex=pattern)
| Q(username__iregex="^" + pattern) | Q(username__iregex="^" + pattern)
| Q(note__alias__name__iregex="^" + pattern) | Q(alias__iregex="^" + pattern)
| Q(note__alias__normalized_name__iregex=Alias.normalize("^" + pattern)) | Q(normalized_alias__iregex=Alias.normalize("^" + pattern))
) )
else: else:
qs = qs.none() qs = qs.none()
@ -455,7 +457,7 @@ class ClubAddMemberView(ProtectQuerysetMixin, LoginRequiredMixin, CreateView):
while c.parent_club is not None: while c.parent_club is not None:
c = c.parent_club c = c.parent_club
clubs_renewal.append(c) clubs_renewal.append(c)
additional_fee_renewal += c.membership_fee_paid if user.profile.paid else c.membership_fee_unpaid additional_fee_renewal += c.membership_fee_paid
context["clubs_renewal"] = clubs_renewal context["clubs_renewal"] = clubs_renewal
context["additional_fee_renewal"] = additional_fee_renewal context["additional_fee_renewal"] = additional_fee_renewal

View File

@ -1,10 +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.utils import timezone
from rest_framework import serializers
from rest_framework.serializers import ListSerializer
from rest_polymorphic.serializers import PolymorphicSerializer
from rest_framework import serializers
from rest_polymorphic.serializers import PolymorphicSerializer
from member.api.serializers import MembershipSerializer from member.api.serializers import MembershipSerializer
from member.models import Membership from member.models import Membership
from note_kfet.middlewares import get_current_authenticated_user from note_kfet.middlewares import get_current_authenticated_user

View File

@ -7,7 +7,7 @@ from django.utils import timezone
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
from polymorphic.models import PolymorphicModel from polymorphic.models import PolymorphicModel
from .notes import Note, NoteClub, NoteSpecial, NoteUser from .notes import Note, NoteClub, NoteSpecial
from ..templatetags.pretty_money import pretty_money from ..templatetags.pretty_money import pretty_money
""" """
@ -222,8 +222,7 @@ class Transaction(PolymorphicModel):
self.destination_alias = str(self.destination) self.destination_alias = str(self.destination)
if self.source.pk == self.destination.pk: if self.source.pk == self.destination.pk:
# When source == destination, no money is transferred # When source == destination, no money is transferred and no transaction is created
super().save(*args, **kwargs)
return return
self.log("Saving") self.log("Saving")

View File

@ -10,7 +10,6 @@ from django.utils.translation import gettext_lazy as _
from django.views.generic import CreateView, UpdateView, DetailView from django.views.generic import CreateView, UpdateView, DetailView
from django_tables2 import SingleTableView from django_tables2 import SingleTableView
from django.urls import reverse_lazy from django.urls import reverse_lazy
from activity.models import Entry from activity.models import Entry
from note_kfet.inputs import AmountInput from note_kfet.inputs import AmountInput
from permission.backends import PermissionBackend from permission.backends import PermissionBackend

View File

@ -5,7 +5,6 @@ import functools
import json import json
import operator import operator
from copy import copy from copy import copy
from time import sleep
from django.contrib.contenttypes.models import ContentType from django.contrib.contenttypes.models import ContentType
from django.core.exceptions import ValidationError from django.core.exceptions import ValidationError

View File

@ -4,7 +4,6 @@
import django_tables2 as tables import django_tables2 as tables
from django.urls import reverse_lazy from django.urls import reverse_lazy
from django.utils.html import format_html from django.utils.html import format_html
from member.models import Membership from member.models import Membership
from note_kfet.middlewares import get_current_authenticated_user from note_kfet.middlewares import get_current_authenticated_user
from permission.backends import PermissionBackend from permission.backends import PermissionBackend

@ -1 +1 @@
Subproject commit 47dc4dd9e6742c3fd0f0d4ea654b1e9a0a39f4f7 Subproject commit 3806feb67fcb1fe822cfdedddbbc4ca7eeef3829

View File

@ -6,7 +6,6 @@ from django.contrib.auth.models import User
from django.db.models import Q from django.db.models import Q
from django.forms import CheckboxSelectMultiple from django.forms import CheckboxSelectMultiple
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
from note.models import NoteSpecial from note.models import NoteSpecial
from note_kfet.inputs import AmountInput, DatePickerInput, Autocomplete, ColorWidget from note_kfet.inputs import AmountInput, DatePickerInput, Autocomplete, ColorWidget

View File

@ -9,7 +9,6 @@ from django.contrib.auth.models import User
from django.db import models from django.db import models
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
from phonenumber_field.modelfields import PhoneNumberField from phonenumber_field.modelfields import PhoneNumberField
from member.models import Club, Membership from member.models import Club, Membership
from note.models import MembershipTransaction from note.models import MembershipTransaction
from permission.models import Role from permission.models import Role

View File

@ -7,9 +7,9 @@ from django.utils import timezone
from django.utils.html import format_html from django.utils.html import format_html
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
from django_tables2 import A from django_tables2 import A
from note_kfet.middlewares import get_current_authenticated_user from note_kfet.middlewares import get_current_authenticated_user
from permission.backends import PermissionBackend from permission.backends import PermissionBackend
from .models import WEIClub, WEIRegistration, Bus, BusTeam, WEIMembership from .models import WEIClub, WEIRegistration, Bus, BusTeam, WEIMembership

View File

@ -28,7 +28,6 @@ from note.models import Transaction, NoteClub, Alias, SpecialTransaction, NoteSp
from note.tables import HistoryTable from note.tables import HistoryTable
from note_kfet.settings import BASE_DIR from note_kfet.settings import BASE_DIR
from permission.backends import PermissionBackend from permission.backends import PermissionBackend
from permission.models import Role
from permission.views import ProtectQuerysetMixin from permission.views import ProtectQuerysetMixin
from .forms.registration import WEIChooseBusForm from .forms.registration import WEIChooseBusForm

View File

@ -6,14 +6,17 @@
* * * * * root cd /var/www/note_kfet && env/bin/python manage.py send_mail >> /var/www/note_kfet/cron_mail.log * * * * * root cd /var/www/note_kfet && env/bin/python manage.py send_mail >> /var/www/note_kfet/cron_mail.log
* * * * * root cd /var/www/note_kfet && env/bin/python manage.py retry_deferred >> /var/www/note_kfet/cron_mail_deferred.log * * * * * root cd /var/www/note_kfet && env/bin/python manage.py retry_deferred >> /var/www/note_kfet/cron_mail_deferred.log
00 0 * * * root cd /var/www/note_kfet && env/bin/python manage.py purge_mail_log 7 >> /var/www/note_kfet/cron_mail_purge.log 00 0 * * * root cd /var/www/note_kfet && env/bin/python manage.py purge_mail_log 7 >> /var/www/note_kfet/cron_mail_purge.log
# Faire une sauvegarde de la base de données
00 2 * * * root cd /var/www/note_kfet && apps/scripts/shell/backup_db
# Vérifier la cohérence de la base et mailer en cas de problème # Vérifier la cohérence de la base et mailer en cas de problème
00 4 * * * root cd /var/www/note_kfet && env/bin/python manage.py check_consistency --sum-all --check-all --mail 00 4 * * * root cd /var/www/note_kfet && env/bin/python manage.py check_consistency --sum-all --check-all --mail
# TODO
# Mettre à jour le wiki (modification sans (dé)validation, activités passées) # Mettre à jour le wiki (modification sans (dé)validation, activités passées)
#30 5 * * * root /home/note/note-kfet-2015-serveur/serveur/Wiki.py --human --raw #30 5 * * * root cd /var/www/note_kfet && env/bin/python manage.py refresh_activities --raw --comment refresh
# Spammer les gens en négatif # Spammer les gens en négatif
00 5 * * 2 root cd /var/www/note_kfet && env/bin/python manage.py send_mail_to_negative_balances --spam 00 5 * * 2 root cd /var/www/note_kfet && env/bin/python manage.py send_mail_to_negative_balances --spam
# Envoyer le rapport mensuel aux trésoriers et respos info # Envoyer le rapport mensuel aux trésoriers et respos info
00 8 6 * * root cd /var/www/note_kfet && env/bin/python manage.py send_mail_to_negative_balances --report 00 8 6 * * root cd /var/www/note_kfet && env/bin/python manage.py send_mail_to_negative_balances --report
# Envoyer les rapports aux gens # Envoyer les rapports aux gens
55 6 * * * root cd /var/www/note_kfet && env/bin/python manage.py send_reports 55 6 * * * root cd /var/www/note_kfet && env/bin/python manage.py send_reports
# Envoyer les rapports aux gens
00 9 * * * root cd /var/www/note_kfet && env/bin/python manage.py refresh_highlighted_buttons

View File

@ -1,3 +1,4 @@
beautifulsoup4==4.9.1
certifi==2019.6.16 certifi==2019.6.16
chardet==3.0.4 chardet==3.0.4
defusedxml==0.6.0 defusedxml==0.6.0
@ -12,6 +13,7 @@ django-polymorphic==2.0.3
django-tables2==2.1.0 django-tables2==2.1.0
docutils==0.14 docutils==0.14
idna==2.8 idna==2.8
lxml==4.5.2
oauthlib==3.1.0 oauthlib==3.1.0
phonenumbers==8.12.7 phonenumbers==8.12.7
Pillow==7.1.2 Pillow==7.1.2

View File

@ -218,7 +218,7 @@ function consume(source, source_alias, dest, quantity, amount, reason, type, cat
addMsg("Attention, La transaction depuis la note " + source_alias + " a été réalisée avec " + addMsg("Attention, La transaction depuis la note " + source_alias + " a été réalisée avec " +
"succès, mais la note émettrice " + source_alias + " est en négatif.", "succès, mais la note émettrice " + source_alias + " est en négatif.",
"warning", 30000); "warning", 30000);
if (source.note.membership && source.note.membership.date_end > new Date().toISOString()) if (source.note.membership && source.note.membership.date_end < new Date().toISOString())
addMsg("Attention : la note émettrice " + source.name + " n'est plus adhérente.", addMsg("Attention : la note émettrice " + source.name + " n'est plus adhérente.",
"danger", 30000); "danger", 30000);
} }

View File

@ -246,6 +246,13 @@ $("#btn_transfer").click(function() {
// We copy the arrays to ensure that transactions are well-processed even if the form is reset // We copy the arrays to ensure that transactions are well-processed even if the form is reset
[...sources_notes_display].forEach(function (source) { [...sources_notes_display].forEach(function (source) {
[...dests_notes_display].forEach(function (dest) { [...dests_notes_display].forEach(function (dest) {
if (source.note.id === dest.note.id) {
addMsg("Attention : la transaction de " + pretty_money(amount) + " de la note " + source.name
+ " vers la note " + dest.name + " n'a pas été faite car il s'agit de la même note au départ" +
" et à l'arrivée.","warning", 10000);
return;
}
$.post("/api/note/transaction/transaction/", $.post("/api/note/transaction/transaction/",
{ {
"csrfmiddlewaretoken": CSRF_TOKEN, "csrfmiddlewaretoken": CSRF_TOKEN,
@ -260,10 +267,10 @@ $("#btn_transfer").click(function() {
"destination": dest.note.id, "destination": dest.note.id,
"destination_alias": dest.name "destination_alias": dest.name
}).done(function () { }).done(function () {
if (source.note.membership && source.note.membership.date_end > new Date().toISOString()) if (source.note.membership && source.note.membership.date_end < new Date().toISOString())
addMsg("Attention : la note émettrice " + source.name + " n'est plus adhérente.", addMsg("Attention : la note émettrice " + source.name + " n'est plus adhérente.",
"danger", 30000); "danger", 30000);
if (dest.note.membership && dest.note.membership.date_end > new Date().toISOString()) if (dest.note.membership && dest.note.membership.date_end < new Date().toISOString())
addMsg("Attention : la note destination " + dest.name + " n'est plus adhérente.", addMsg("Attention : la note destination " + dest.name + " n'est plus adhérente.",
"danger", 30000); "danger", 30000);
@ -373,7 +380,7 @@ $("#btn_transfer").click(function() {
"bank": $("#bank").val() "bank": $("#bank").val()
}).done(function () { }).done(function () {
addMsg("Le crédit/retrait a bien été effectué !", "success", 10000); addMsg("Le crédit/retrait a bien été effectué !", "success", 10000);
if (user_note.membership && user_note.membership.date_end > new Date().toISOString()) if (user_note.membership && user_note.membership.date_end < new Date().toISOString())
addMsg("Attention : la note " + alias + " n'est plus adhérente.", "danger", 10000); addMsg("Attention : la note " + alias + " n'est plus adhérente.", "danger", 10000);
reset(); reset();
}).fail(function (err) { }).fail(function (err) {

View File

@ -22,5 +22,7 @@ socket = /var/www/note_kfet/note_kfet.sock
chmod-socket = 664 chmod-socket = 664
# clear environment on exit # clear environment on exit
vacuum = true vacuum = true
#Touch reload # Touch reload
touch-reload = /var/www/note_kfet/note_kfet/settings/__init__.py touch-reload = /var/www/note_kfet/note_kfet/settings/__init__.py
# Enable threads
enable-threads = true