Merge branch 'master' into aureplop/fix_cache

This commit is contained in:
Aurélien Delobelle 2017-04-15 14:27:20 +02:00
commit e772d12721
13 changed files with 198 additions and 112 deletions

1
cof/settings/.gitignore vendored Normal file
View file

@ -0,0 +1 @@
secret.py

View file

@ -1,32 +1,40 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
""" """
Django settings for cof project. Django common settings for cof project.
For more information on this file, see Everything which is supposed to be identical between the production server and
https://docs.djangoproject.com/en/1.8/topics/settings/ the local development server should be here.
For the full list of settings and their values, see
https://docs.djangoproject.com/en/1.8/ref/settings/
""" """
# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
import os import os
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) # Database credentials
try:
from .secret import DBNAME, DBUSER, DBPASSWD
except ImportError:
# On the local development VM, theses credentials are in the environment
DBNAME = os.environ["DBNAME"]
DBUSER = os.environ["DBUSER"]
DBPASSWD = os.environ["DBPASSWD"]
except KeyError:
raise RuntimeError("Secrets missing")
EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend' # Other secrets
try:
from .secret import (
SECRET_KEY, RECAPTCHA_PUBLIC_KEY, RECAPTCHA_PRIVATE_KEY, ADMINS
)
except ImportError:
raise RuntimeError("Secrets missing")
# Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/1.8/howto/deployment/checklist/
# SECURITY WARNING: keep the secret key used in production secret! BASE_DIR = os.path.dirname(
SECRET_KEY = 'q()(zn4m63i%5cp4)f+ww4-28_w+ly3q9=6imw2ciu&_(5_4ah' os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
)
# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True
# Application definition # Application definition
INSTALLED_APPS = ( INSTALLED_APPS = [
'gestioncof', 'gestioncof',
'django.contrib.auth', 'django.contrib.auth',
'django.contrib.contenttypes', 'django.contrib.contenttypes',
@ -41,17 +49,15 @@ INSTALLED_APPS = (
'autocomplete_light', 'autocomplete_light',
'captcha', 'captcha',
'django_cas_ng', 'django_cas_ng',
'debug_toolbar',
'bootstrapform', 'bootstrapform',
'kfet', 'kfet',
'channels', 'channels',
'widget_tweaks', 'widget_tweaks',
'custommail', 'custommail',
'djconfig', 'djconfig',
) ]
MIDDLEWARE_CLASSES = ( MIDDLEWARE_CLASSES = [
'debug_toolbar.middleware.DebugToolbarMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware', 'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware', 'django.middleware.csrf.CsrfViewMiddleware',
@ -62,7 +68,7 @@ MIDDLEWARE_CLASSES = (
'django.middleware.clickjacking.XFrameOptionsMiddleware', 'django.middleware.clickjacking.XFrameOptionsMiddleware',
'django.middleware.security.SecurityMiddleware', 'django.middleware.security.SecurityMiddleware',
'djconfig.middleware.DjConfigMiddleware', 'djconfig.middleware.DjConfigMiddleware',
) ]
ROOT_URLCONF = 'cof.urls' ROOT_URLCONF = 'cof.urls'
@ -89,17 +95,12 @@ TEMPLATES = [
}, },
] ]
# WSGI_APPLICATION = 'cof.wsgi.application'
# Database
# https://docs.djangoproject.com/en/1.8/ref/settings/#databases
DATABASES = { DATABASES = {
'default': { 'default': {
'ENGINE': 'django.db.backends.mysql', 'ENGINE': 'django.db.backends.mysql',
'NAME': os.environ['DBNAME'], 'NAME': DBNAME,
'USER': os.environ['DBUSER'], 'USER': DBUSER,
'PASSWORD': os.environ['DBPASSWD'], 'PASSWORD': DBPASSWD,
'HOST': os.environ.get('DBHOST', 'localhost'), 'HOST': os.environ.get('DBHOST', 'localhost'),
} }
} }
@ -118,19 +119,6 @@ USE_L10N = True
USE_TZ = True USE_TZ = True
# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/1.8/howto/static-files/
STATIC_URL = '/static/'
STATIC_ROOT = '/var/www/static/'
# Media upload (through ImageField, SiteField)
# https://docs.djangoproject.com/en/1.9/ref/models/fields/
MEDIA_ROOT = os.path.join(BASE_DIR, 'media/')
MEDIA_URL = '/media/'
# Various additional settings # Various additional settings
SITE_ID = 1 SITE_ID = 1
@ -163,12 +151,6 @@ AUTHENTICATION_BACKENDS = (
'kfet.backends.GenericTeamBackend', 'kfet.backends.GenericTeamBackend',
) )
# LDAP_SERVER_URL = 'ldaps://ldap.spi.ens.fr:636'
# EMAIL_HOST="nef.ens.fr"
RECAPTCHA_PUBLIC_KEY = "DUMMY"
RECAPTCHA_PRIVATE_KEY = "DUMMY"
RECAPTCHA_USE_SSL = True RECAPTCHA_USE_SSL = True
@ -209,22 +191,4 @@ CHANNEL_LAYERS = {
} }
} }
def show_toolbar(request):
"""
On ne veut pas la vérification de INTERNAL_IPS faite par la debug-toolbar
car cela interfère avec l'utilisation de Vagrant. En effet, l'adresse de la
machine physique n'est pas forcément connue, et peut difficilement être
mise dans les INTERNAL_IPS.
"""
if not DEBUG:
return False
if request.is_ajax():
return False
return True
DEBUG_TOOLBAR_CONFIG = {
'SHOW_TOOLBAR_CALLBACK': show_toolbar,
}
FORMAT_MODULE_PATH = 'cof.locale' FORMAT_MODULE_PATH = 'cof.locale'

51
cof/settings/dev.py Normal file
View file

@ -0,0 +1,51 @@
"""
Django development settings for the cof project.
The settings that are not listed here are imported from .common
"""
import os
from .common import *
EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'
DEBUG = True
# ---
# Apache static/media config
# ---
STATIC_URL = '/static/'
STATIC_ROOT = '/var/www/static/'
MEDIA_ROOT = os.path.join(BASE_DIR, 'media/')
MEDIA_URL = '/media/'
# ---
# Debug tool bar
# ---
def show_toolbar(request):
"""
On ne veut pas la vérification de INTERNAL_IPS faite par la debug-toolbar
car cela interfère avec l'utilisation de Vagrant. En effet, l'adresse de la
machine physique n'est pas forcément connue, et peut difficilement être
mise dans les INTERNAL_IPS.
"""
if not DEBUG:
return False
if request.is_ajax():
return False
return True
INSTALLED_APPS += ["debug_toolbar"]
MIDDLEWARE_CLASSES = (
["debug_toolbar.middleware.DebugToolbarMiddleware"]
+ MIDDLEWARE_CLASSES
)
DEBUG_TOOLBAR_CONFIG = {
'SHOW_TOOLBAR_CALLBACK': show_toolbar,
}

26
cof/settings/prod.py Normal file
View file

@ -0,0 +1,26 @@
"""
Django development settings for the cof project.
The settings that are not listed here are imported from .common
"""
import os
from .common import *
DEBUG = False
ALLOWED_HOSTS = [
"cof.ens.fr",
"www.cof.ens.fr",
"dev.cof.ens.fr"
]
STATIC_ROOT = os.path.join(os.path.dirname(BASE_DIR), "static")
STATIC_URL = "/gestion/static/"
MEDIA_ROOT = os.path.join(os.path.dirname(BASE_DIR), "media")
MEDIA_URL = "/gestion/media/"
LDAP_SERVER_URL = "ldaps://ldap.spi.ens.fr:636"
EMAIL_HOST = "nef.ens.fr"

View file

@ -0,0 +1,4 @@
SECRET_KEY = 'q()(zn4m63i%5cp4)f+ww4-28_w+ly3q9=6imw2ciu&_(5_4ah'
RECAPTCHA_PUBLIC_KEY = "DUMMY"
RECAPTCHA_PRIVATE_KEY = "DUMMY"
ADMINS = None

View file

@ -1,26 +1,41 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
from __future__ import (absolute_import, division, from django.core.serializers.json import json, DjangoJSONEncoder
print_function, unicode_literals)
from builtins import *
from channels import Group
from channels.generic.websockets import JsonWebsocketConsumer from channels.generic.websockets import JsonWebsocketConsumer
class KPsul(JsonWebsocketConsumer):
# Set to True if you want them, else leave out class DjangoJsonWebsocketConsumer(JsonWebsocketConsumer):
strict_ordering = False """Custom Json Websocket Consumer.
slight_ordering = False
def connection_groups(self, **kwargs): Encode to JSON with DjangoJSONEncoder.
return ['kfet.kpsul']
"""
@classmethod
def encode_json(cls, content):
return json.dumps(content, cls=DjangoJSONEncoder)
class PermConsumerMixin(object):
"""Add support to check permissions on Consumers.
Attributes:
perms_connect (list): Required permissions to connect to this
consumer.
"""
http_user = True # Enable message.user
perms_connect = []
def connect(self, message, **kwargs): def connect(self, message, **kwargs):
pass """Check permissions on connection."""
if message.user.has_perms(self.perms_connect):
super().connect(message, **kwargs)
else:
self.close()
def receive(self, content, **kwargs):
pass
def disconnect(self, message, **kwargs): class KPsul(PermConsumerMixin, DjangoJsonWebsocketConsumer):
pass groups = ['kfet.kpsul']
perms_connect = ['kfet.is_team']

View file

@ -81,7 +81,7 @@ class Account(models.Model):
# Propriétés supplémentaires # Propriétés supplémentaires
@property @property
def real_balance(self): def real_balance(self):
if (hasattr(self, 'negative')): if hasattr(self, 'negative') and self.negative.balance_offset:
return self.balance - self.negative.balance_offset return self.balance - self.negative.balance_offset
return self.balance return self.balance
@ -210,6 +210,29 @@ class Account(models.Model):
def delete(self, *args, **kwargs): def delete(self, *args, **kwargs):
pass pass
def update_negative(self):
if self.real_balance < 0:
if hasattr(self, 'negative') and not self.negative.start:
self.negative.start = timezone.now()
self.negative.save()
elif not hasattr(self, 'negative'):
self.negative = (
AccountNegative.objects.create(
account=self, start=timezone.now(),
)
)
elif hasattr(self, 'negative'):
# self.real_balance >= 0
balance_offset = self.negative.balance_offset
if balance_offset:
(
Account.objects
.filter(pk=self.pk)
.update(balance=F('balance')-balance_offset)
)
self.refresh_from_db()
self.negative.delete()
class UserHasAccount(Exception): class UserHasAccount(Exception):
def __init__(self, trigramme): def __init__(self, trigramme):
self.trigramme = trigramme self.trigramme = trigramme

View file

@ -8,7 +8,6 @@
<script type="text/javascript" src="{% static 'kfet/js/moment.js' %}"></script> <script type="text/javascript" src="{% static 'kfet/js/moment.js' %}"></script>
<script type="text/javascript" src="{% static 'kfet/js/moment-fr.js' %}"></script> <script type="text/javascript" src="{% static 'kfet/js/moment-fr.js' %}"></script>
<script type="text/javascript" src="{% static 'kfet/js/moment-timezone-with-data-2010-2020.js' %}"></script> <script type="text/javascript" src="{% static 'kfet/js/moment-timezone-with-data-2010-2020.js' %}"></script>
<script type="text/javascript" src="{% static 'kfet/js/kfet.js' %}"></script>
<script type="text/javascript" src="{% static 'kfet/js/history.js' %}"></script> <script type="text/javascript" src="{% static 'kfet/js/history.js' %}"></script>
{% if account.user == request.user %} {% if account.user == request.user %}
<script type="text/javascript" src="{% static 'kfet/js/Chart.bundle.js' %}"></script> <script type="text/javascript" src="{% static 'kfet/js/Chart.bundle.js' %}"></script>
@ -18,11 +17,11 @@
$(document).ready(function() { $(document).ready(function() {
var stat_last = new StatsGroup( var stat_last = new StatsGroup(
"{% url 'kfet.account.stat.operation.list' trigramme=account.trigramme %}", "{% url 'kfet.account.stat.operation.list' trigramme=account.trigramme %}",
$("#stat_last"), $("#stat_last")
); );
var stat_balance = new StatsGroup( var stat_balance = new StatsGroup(
"{% url 'kfet.account.stat.balance.list' trigramme=account.trigramme %}", "{% url 'kfet.account.stat.balance.list' trigramme=account.trigramme %}",
$("#stat_balance"), $("#stat_balance")
); );
}); });
</script> </script>

View file

@ -1105,22 +1105,15 @@ def kpsul_perform_operations(request):
with transaction.atomic(): with transaction.atomic():
# If not cash account, # If not cash account,
# saving account's balance and adding to Negative if not in # saving account's balance and adding to Negative if not in
if not operationgroup.on_acc.is_cash: on_acc = operationgroup.on_acc
Account.objects.filter(pk=operationgroup.on_acc.pk).update( if not on_acc.is_cash:
balance=F('balance') + operationgroup.amount) (
operationgroup.on_acc.refresh_from_db() Account.objects
if operationgroup.on_acc.balance < 0: .filter(pk=on_acc.pk)
if hasattr(operationgroup.on_acc, 'negative'): .update(balance=F('balance') + operationgroup.amount)
if not operationgroup.on_acc.negative.start: )
operationgroup.on_acc.negative.start = timezone.now() on_acc.refresh_from_db()
operationgroup.on_acc.negative.save() on_acc.update_negative()
else:
negative = AccountNegative(
account=operationgroup.on_acc, start=timezone.now())
negative.save()
elif (hasattr(operationgroup.on_acc, 'negative') and
not operationgroup.on_acc.negative.balance_offset):
operationgroup.on_acc.negative.delete()
# Updating checkout's balance # Updating checkout's balance
if to_checkout_balance: if to_checkout_balance:
@ -1311,8 +1304,15 @@ def kpsul_cancel_operations(request):
(Operation.objects.filter(pk__in=opes) (Operation.objects.filter(pk__in=opes)
.update(canceled_by=canceled_by, canceled_at=canceled_at)) .update(canceled_by=canceled_by, canceled_at=canceled_at))
for account in to_accounts_balances: for account in to_accounts_balances:
Account.objects.filter(pk=account.pk).update( (
balance = F('balance') + to_accounts_balances[account]) Account.objects
.filter(pk=account.pk)
.update(balance=F('balance') + to_accounts_balances[account])
)
if not account.is_cash:
# Should always be true, but we want to be sure
account.refresh_from_db()
account.update_negative()
for checkout in to_checkouts_balances: for checkout in to_checkouts_balances:
Checkout.objects.filter(pk=checkout.pk).update( Checkout.objects.filter(pk=checkout.pk).update(
balance = F('balance') + to_checkouts_balances[checkout]) balance = F('balance') + to_checkouts_balances[checkout])

View file

@ -36,7 +36,7 @@ chown -R ubuntu:www-data /var/www/static
# Mise en place du .bash_profile pour tout configurer lors du `vagrant ssh` # Mise en place du .bash_profile pour tout configurer lors du `vagrant ssh`
cat >> ~ubuntu/.bashrc <<EOF cat >> ~ubuntu/.bashrc <<EOF
# On utilise la version de développement de GestioCOF # On utilise la version de développement de GestioCOF
export DJANGO_SETTINGS_MODULE='cof.settings_dev' export DJANGO_SETTINGS_MODULE='cof.settings.dev'
# Identifiants MySQL # Identifiants MySQL
export DBUSER="$DBUSER" export DBUSER="$DBUSER"
@ -61,8 +61,11 @@ sudo -H -u ubuntu python3 -m venv ~ubuntu/venv
sudo -H -u ubuntu ~ubuntu/venv/bin/pip install -U pip sudo -H -u ubuntu ~ubuntu/venv/bin/pip install -U pip
sudo -H -u ubuntu ~ubuntu/venv/bin/pip install -r requirements.txt -r requirements-devel.txt sudo -H -u ubuntu ~ubuntu/venv/bin/pip install -r requirements.txt -r requirements-devel.txt
# Installation des secrets
sudo -H -u ubuntu cp cof/settings/secret_example.py cof/settings/secret.py
# Préparation de Django # Préparation de Django
sudo -H -u ubuntu DJANGO_SETTINGS_MODULE='cof.settings_dev' DBUSER=$DBUSER DBNAME=$DBNAME DBPASSWD=$DBPASSWD bash provisioning/prepare_django.sh sudo -H -u ubuntu DJANGO_SETTINGS_MODULE='cof.settings.dev' DBUSER=$DBUSER DBNAME=$DBNAME DBPASSWD=$DBPASSWD bash provisioning/prepare_django.sh
# Installation du cron pour les mails de rappels # Installation du cron pour les mails de rappels
sudo -H -u ubuntu crontab provisioning/cron.dev sudo -H -u ubuntu crontab provisioning/cron.dev

View file

@ -1,5 +1,5 @@
# On utilise la version de développement de GestioCOF # On utilise la version de développement de GestioCOF
DJANGO_SETTINGS_MODULE='cof.settings_dev' DJANGO_SETTINGS_MODULE='cof.settings.dev'
# Identifiants MySQL # Identifiants MySQL
DBUSER="cof_gestion" DBUSER="cof_gestion"

View file

@ -2,7 +2,7 @@
command=/home/ubuntu/venv/bin/python /vagrant/manage.py runworker command=/home/ubuntu/venv/bin/python /vagrant/manage.py runworker
directory=/vagrant/ directory=/vagrant/
user=ubuntu user=ubuntu
environment=DBUSER={DBUSER},DBNAME={DBNAME},DBPASSWD={DBPASSWD},DJANGO_SETTINGS_MODULE="cof.settings_dev" environment=DBUSER={DBUSER},DBNAME={DBNAME},DBPASSWD={DBPASSWD},DJANGO_SETTINGS_MODULE="cof.settings.dev"
autostart=true autostart=true
autorestart=true autorestart=true
redirect_stderr=true redirect_stderr=true
@ -11,7 +11,7 @@ redirect_stderr=true
[program:interface] [program:interface]
command=/home/ubuntu/venv/bin/daphne -b 127.0.0.1 -p 8001 cof.asgi:channel_layer command=/home/ubuntu/venv/bin/daphne -b 127.0.0.1 -p 8001 cof.asgi:channel_layer
environment=DBUSER={DBUSER},DBNAME={DBNAME},DBPASSWD={DBPASSWD},DJANGO_SETTINGS_MODULE="cof.settings_dev" environment=DBUSER={DBUSER},DBNAME={DBNAME},DBPASSWD={DBPASSWD},DJANGO_SETTINGS_MODULE="cof.settings.dev"
directory=/vagrant/ directory=/vagrant/
redirect_stderr=true redirect_stderr=true
autostart=true autostart=true

View file

@ -13,13 +13,13 @@ six==1.10.0
unicodecsv==0.14.1 unicodecsv==0.14.1
icalendar==3.10 icalendar==3.10
django-bootstrap-form==3.2.1 django-bootstrap-form==3.2.1
asgiref==0.14.0 asgiref==1.1.1
daphne==0.14.3 daphne==1.2.0
asgi-redis==0.14.0 asgi-redis==1.3.0
statistics==1.0.3.5 statistics==1.0.3.5
future==0.15.2 future==0.15.2
django-widget-tweaks==1.4.1 django-widget-tweaks==1.4.1
git+https://git.eleves.ens.fr/cof-geek/django_custommail.git#egg=django_custommail git+https://git.eleves.ens.fr/cof-geek/django_custommail.git#egg=django_custommail
ldap3 ldap3
git+https://github.com/Aureplop/channels.git#egg=channels channels==1.1.3
python-dateutil python-dateutil