From c3d740ade00f54a299231456ee07269b98b12f8f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20P=C3=A9pin?= Date: Sat, 1 Apr 2017 21:45:05 +0100 Subject: [PATCH 01/16] Handle incomplete values from the LDAP Sometime `uid` is not set in the objects fetched from the LDAP. This case has to be handled. Also, the `.uid` and `.cn` attributes of these objects in the python abstractions have a `.value` method which we should use. --- gestioncof/autocomplete.py | 32 ++++++++++++++++++++------------ kfet/autocomplete.py | 32 ++++++++++++++++++++------------ 2 files changed, 40 insertions(+), 24 deletions(-) diff --git a/gestioncof/autocomplete.py b/gestioncof/autocomplete.py index 1eae6920..98363377 100644 --- a/gestioncof/autocomplete.py +++ b/gestioncof/autocomplete.py @@ -14,6 +14,10 @@ from gestioncof.decorators import buro_required class Clipper(object): def __init__(self, clipper, fullname): + if fullname is None: + fullname = "" + assert isinstance(clipper, str) + assert isinstance(fullname, str) self.clipper = clipper self.fullname = fullname @@ -60,18 +64,22 @@ def autocomplete(request): ['(cn=*{bit:s}*)(uid=*{bit:s}*)'.format(**{"bit": bit}) for bit in bits] )) - with Connection(settings.LDAP_SERVER_URL) as conn: - conn.search( - 'dc=spi,dc=ens,dc=fr', ldap_query, - attributes=['uid', 'cn'] - ) - queries['clippers'] = conn.entries - # Clearing redundancies - queries['clippers'] = [ - Clipper(clipper.uid, clipper.cn) - for clipper in queries['clippers'] - if str(clipper.uid) not in usernames - ] + if ldap_query != "(&)": + # If none of the bits were legal, we do not perform the query + entries = None + with Connection(settings.LDAP_SERVER_URL) as conn: + conn.search( + 'dc=spi,dc=ens,dc=fr', ldap_query, + attributes=['uid', 'cn'] + ) + entries = conn.entries + # Clearing redundancies + queries['clippers'] = [ + Clipper(entry.uid.value, entry.cn.value) + for entry in entries + if entry.uid.value is not None + and entry.uid.value not in usernames + ] # Resulting data data.update(queries) diff --git a/kfet/autocomplete.py b/kfet/autocomplete.py index 64fa52cf..acc6ebd8 100644 --- a/kfet/autocomplete.py +++ b/kfet/autocomplete.py @@ -14,6 +14,10 @@ from kfet.models import Account class Clipper(object): def __init__(self, clipper, fullname): + if fullname is None: + fullname = "" + assert isinstance(clipper, str) + assert isinstance(fullname, str) self.clipper = clipper self.fullname = fullname @@ -79,18 +83,22 @@ def account_create(request): ['(cn=*{bit:s}*)(uid=*{bit:s}*)'.format(bit=word) for word in search_words] )) - with Connection(settings.LDAP_SERVER_URL) as conn: - conn.search( - 'dc=spi,dc=ens,dc=fr', ldap_query, - attributes=['uid', 'cn'] - ) - queries['clippers'] = conn.entries - # Clearing redundancies - queries['clippers'] = [ - Clipper(clipper.uid, clipper.cn) - for clipper in queries['clippers'] - if str(clipper.uid) not in usernames - ] + if ldap_query != "(&)": + # If none of the bits were legal, we do not perform the query + entries = None + with Connection(settings.LDAP_SERVER_URL) as conn: + conn.search( + 'dc=spi,dc=ens,dc=fr', ldap_query, + attributes=['uid', 'cn'] + ) + entries = conn.entries + # Clearing redundancies + queries['clippers'] = [ + Clipper(entry.uid.value, entry.cn.value) + for entry in entries + if entry.uid.value is not None + and entry.uid.value not in usernames + ] # Resulting data data.update(queries) From f6d43dffa1e0d45d978dfef9a412cc48933504d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20P=C3=A9pin?= Date: Sat, 1 Apr 2017 22:07:32 +0100 Subject: [PATCH 02/16] exclude empty strings from ldap results The uid attribute in a LDAP's entry cannot be an empty string. We need to get an actual identifier. --- gestioncof/autocomplete.py | 2 +- kfet/autocomplete.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/gestioncof/autocomplete.py b/gestioncof/autocomplete.py index 98363377..ccf8804e 100644 --- a/gestioncof/autocomplete.py +++ b/gestioncof/autocomplete.py @@ -77,7 +77,7 @@ def autocomplete(request): queries['clippers'] = [ Clipper(entry.uid.value, entry.cn.value) for entry in entries - if entry.uid.value is not None + if entry.uid.value and entry.uid.value not in usernames ] diff --git a/kfet/autocomplete.py b/kfet/autocomplete.py index acc6ebd8..c97779c1 100644 --- a/kfet/autocomplete.py +++ b/kfet/autocomplete.py @@ -96,7 +96,7 @@ def account_create(request): queries['clippers'] = [ Clipper(entry.uid.value, entry.cn.value) for entry in entries - if entry.uid.value is not None + if entry.uid.value and entry.uid.value not in usernames ] From fb5ba5fb1b6bd08825a5f248e7ddc2cf19aba3be Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aur=C3=A9lien=20Delobelle?= Date: Sun, 3 Sep 2017 14:42:38 +0200 Subject: [PATCH 03/16] Fix kfet navbar on small devices --- kfet/open/static/kfetopen/kfet-open.css | 25 ++++++++++++++++++++++--- kfet/open/static/kfetopen/kfet-open.js | 4 ++-- 2 files changed, 24 insertions(+), 5 deletions(-) diff --git a/kfet/open/static/kfetopen/kfet-open.css b/kfet/open/static/kfetopen/kfet-open.css index d44318cd..a7068626 100644 --- a/kfet/open/static/kfetopen/kfet-open.css +++ b/kfet/open/static/kfetopen/kfet-open.css @@ -14,10 +14,16 @@ .kfetopen .base { height: 50px; - padding: 15px; + max-width: 16px; - display: inline-flex; + margin-left: 5px; + margin-right: 5px; + + display: flex; + flex-wrap: wrap; + align-content: center; align-items: center; + justify-content: center; } .kfetopen .details { @@ -34,10 +40,23 @@ height: 10px; border-radius: 50%; transition: background 0.15s; + margin: 3px; } .kfetopen .warning { - margin-left: 15px; + display: none; +} + +@media (min-width: 576px) { + .kfetopen .base { + max-width: none; + margin-left: 15px; + margin-right: 15px; + } + + .kfetopen .warning { + margin-left: 15px; + } } .kfetopen .status-text { diff --git a/kfet/open/static/kfetopen/kfet-open.js b/kfet/open/static/kfetopen/kfet-open.js index b86cc5bc..74f18d8a 100644 --- a/kfet/open/static/kfetopen/kfet-open.js +++ b/kfet/open/static/kfetopen/kfet-open.js @@ -74,10 +74,10 @@ OpenKfet.prototype = { if (this.admin) { this.add_class(this.admin_status); if (this.force_close) { - this.dom.warning.addClass('in'); + this.dom.warning.show().addClass('in'); this.dom.force_close_btn.html(this.force_text['deactivate']); } else { - this.dom.warning.removeClass('in'); + this.dom.warning.removeClass('in').hide(); this.dom.force_close_btn.html(this.force_text['activate']); } } From 51f4bf3fb5e3709f77df4de9beb951361ed6ec5d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20P=C3=A9pin?= Date: Mon, 4 Sep 2017 13:25:09 +0200 Subject: [PATCH 04/16] Clipper logins may be > 8 characters --- gestioncof/forms.py | 4 ---- gestioncof/migrations/0011_longer_clippers.py | 19 +++++++++++++++++++ gestioncof/models.py | 4 +++- gestioncof/views.py | 7 ++----- kfet/forms.py | 12 +++--------- 5 files changed, 27 insertions(+), 19 deletions(-) create mode 100644 gestioncof/migrations/0011_longer_clippers.py diff --git a/gestioncof/forms.py b/gestioncof/forms.py index 3a519a39..7e36fb92 100644 --- a/gestioncof/forms.py +++ b/gestioncof/forms.py @@ -10,7 +10,6 @@ from django.contrib.auth.models import User from django.forms.widgets import RadioSelect, CheckboxSelectMultiple from django.forms.formsets import BaseFormSet, formset_factory from django.db.models import Max -from django.core.validators import MinLengthValidator from gestioncof.models import CofProfile, EventCommentValue, \ CalendarSubscription, Club @@ -203,9 +202,6 @@ class RegistrationUserForm(forms.ModelForm): super(RegistrationUserForm, self).__init__(*args, **kw) self.fields['username'].help_text = "" - def force_long_username(self): - self.fields['username'].validators = [MinLengthValidator(9)] - class Meta: model = User fields = ("username", "first_name", "last_name", "email") diff --git a/gestioncof/migrations/0011_longer_clippers.py b/gestioncof/migrations/0011_longer_clippers.py new file mode 100644 index 00000000..631d0ea8 --- /dev/null +++ b/gestioncof/migrations/0011_longer_clippers.py @@ -0,0 +1,19 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('gestioncof', '0010_delete_custommail'), + ] + + operations = [ + migrations.AlterField( + model_name='cofprofile', + name='login_clipper', + field=models.CharField(verbose_name='Login clipper', blank=True, max_length=32), + ), + ] diff --git a/gestioncof/models.py b/gestioncof/models.py index 2215b296..7a8a2577 100644 --- a/gestioncof/models.py +++ b/gestioncof/models.py @@ -38,7 +38,9 @@ TYPE_COMMENT_FIELD = ( @python_2_unicode_compatible class CofProfile(models.Model): user = models.OneToOneField(User, related_name="profile") - login_clipper = models.CharField("Login clipper", max_length=8, blank=True) + login_clipper = models.CharField( + "Login clipper", max_length=32, blank=True + ) is_cof = models.BooleanField("Membre du COF", default=False) num = models.IntegerField("Numéro d'adhérent", blank=True, default=0) phone = models.CharField("Téléphone", max_length=20, blank=True) diff --git a/gestioncof/views.py b/gestioncof/views.py index 944d9dc2..b53a9e08 100644 --- a/gestioncof/views.py +++ b/gestioncof/views.py @@ -384,7 +384,6 @@ def registration_form2(request, login_clipper=None, username=None, elif not login_clipper: # new user user_form = RegistrationPassUserForm() - user_form.force_long_username() profile_form = RegistrationProfileForm() event_formset = EventFormset(events=events, prefix='events') clubs_form = ClubsForm() @@ -427,12 +426,10 @@ def registration(request): user_form = RegistrationUserForm(request_dict, instance=member) if member.profile.login_clipper: login_clipper = member.profile.login_clipper - else: - user_form.force_long_username() except User.DoesNotExist: - user_form.force_long_username() + pass else: - user_form.force_long_username() + pass # ----- # Validation des formulaires diff --git a/kfet/forms.py b/kfet/forms.py index 7acd0880..e4afaa09 100644 --- a/kfet/forms.py +++ b/kfet/forms.py @@ -3,7 +3,6 @@ from decimal import Decimal from django import forms from django.core.exceptions import ValidationError -from django.core.validators import MinLengthValidator from django.contrib.auth.models import User, Group, Permission from django.contrib.contenttypes.models import ContentType from django.forms import modelformset_factory @@ -105,21 +104,16 @@ class CofRestrictForm(CofForm): class Meta(CofForm.Meta): fields = ['departement'] -class UserForm(forms.ModelForm): - def __init__(self, *args, **kwargs): - from_clipper = kwargs.pop('from_clipper', False) - new_user = kwargs.get('instance') is None and not from_clipper - super(UserForm, self).__init__(*args, **kwargs) - if new_user: - self.fields['username'].validators = [MinLengthValidator(9)] +class UserForm(forms.ModelForm): class Meta: - model = User + model = User fields = ['username', 'first_name', 'last_name', 'email'] help_texts = { 'username': '' } + class UserRestrictForm(UserForm): class Meta(UserForm.Meta): fields = ['first_name', 'last_name'] From 50432d969f6bd73df4d594061e019821b463ab30 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20P=C3=A9pin?= Date: Mon, 4 Sep 2017 13:25:45 +0200 Subject: [PATCH 05/16] Update available promos for account creation --- kfet/migrations/0054_update_promos.py | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 kfet/migrations/0054_update_promos.py diff --git a/kfet/migrations/0054_update_promos.py b/kfet/migrations/0054_update_promos.py new file mode 100644 index 00000000..2691e903 --- /dev/null +++ b/kfet/migrations/0054_update_promos.py @@ -0,0 +1,19 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('kfet', '0053_created_at'), + ] + + operations = [ + migrations.AlterField( + model_name='account', + name='promo', + field=models.IntegerField(blank=True, choices=[(1980, 1980), (1981, 1981), (1982, 1982), (1983, 1983), (1984, 1984), (1985, 1985), (1986, 1986), (1987, 1987), (1988, 1988), (1989, 1989), (1990, 1990), (1991, 1991), (1992, 1992), (1993, 1993), (1994, 1994), (1995, 1995), (1996, 1996), (1997, 1997), (1998, 1998), (1999, 1999), (2000, 2000), (2001, 2001), (2002, 2002), (2003, 2003), (2004, 2004), (2005, 2005), (2006, 2006), (2007, 2007), (2008, 2008), (2009, 2009), (2010, 2010), (2011, 2011), (2012, 2012), (2013, 2013), (2014, 2014), (2015, 2015), (2016, 2016), (2017, 2017)], default=2017, null=True), + ), + ] From 35b352ac1d83113ad5e06131b906279e26529eac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20P=C3=A9pin?= Date: Mon, 4 Sep 2017 14:50:12 +0200 Subject: [PATCH 06/16] Fix mistake introduced in 51f4bf3fb5e370 --- kfet/views.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/kfet/views.py b/kfet/views.py index de6c906e..1ae31283 100644 --- a/kfet/views.py +++ b/kfet/views.py @@ -282,10 +282,10 @@ def get_account_create_forms(request=None, username=None, login_clipper=None, # Form créations if request: - user_form = UserForm(request.POST, initial=user_initial, from_clipper=True) + user_form = UserForm(request.POST, initial=user_initial) cof_form = CofForm(request.POST, initial=cof_initial) else: - user_form = UserForm(initial=user_initial, from_clipper=True) + user_form = UserForm(initial=user_initial) cof_form = CofForm(initial=cof_initial) # Protection (read-only) des champs username et login_clipper From 439f49c3ba1959f590b334536fe04faaac077c69 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20P=C3=A9pin?= Date: Tue, 5 Sep 2017 15:21:19 +0200 Subject: [PATCH 07/16] =?UTF-8?q?We=20=E2=99=A5=20hardcoding=20stuff?= =?UTF-8?q?=E2=80=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- gestioncof/urls.py | 2 +- gestioncof/views.py | 64 +++++++++++++++++++++++---------------------- 2 files changed, 34 insertions(+), 32 deletions(-) diff --git a/gestioncof/urls.py b/gestioncof/urls.py index 9a562e7e..57c2e8f2 100644 --- a/gestioncof/urls.py +++ b/gestioncof/urls.py @@ -10,7 +10,7 @@ export_patterns = [ url(r'^mega/avecremarques$', views.export_mega_remarksonly), url(r'^mega/participants$', views.export_mega_participants), url(r'^mega/orgas$', views.export_mega_orgas), - url(r'^mega/(?P.+)$', views.export_mega_bytype), + # url(r'^mega/(?P.+)$', views.export_mega_bytype), url(r'^mega$', views.export_mega), ] diff --git a/gestioncof/views.py b/gestioncof/views.py index b53a9e08..a29ffb88 100644 --- a/gestioncof/views.py +++ b/gestioncof/views.py @@ -595,13 +595,13 @@ def csv_export_mega(filename, qs): @buro_required def export_mega_remarksonly(request): - filename = 'remarques_mega_2016.csv' + filename = 'remarques_mega_2017.csv' response = HttpResponse(content_type='text/csv') response['Content-Disposition'] = 'attachment; filename=' + filename writer = unicodecsv.writer(response) - event = Event.objects.get(title="Mega 2016") - commentfield = event.commentfields.get(name="Commentaires") + event = Event.objects.get(title="MEGA 2017") + commentfield = event.commentfields.get(name="Commentaire") for val in commentfield.values.all(): reg = val.registration user = reg.user @@ -613,50 +613,52 @@ def export_mega_remarksonly(request): return response -@buro_required -def export_mega_bytype(request, type): - types = {"orga-actif": "Orga élève", - "orga-branleur": "Orga étudiant", - "conscrit-eleve": "Conscrit élève", - "conscrit-etudiant": "Conscrit étudiant"} - - if type not in types: - raise Http404 - - event = Event.objects.get(title="Mega 2016") - type_option = event.options.get(name="Type") - participant_type = type_option.choices.get(value=types[type]).id - qs = EventRegistration.objects.filter(event=event).filter( - options__id__exact=participant_type) - return csv_export_mega(type + '_mega_2016.csv', qs) +# @buro_required +# def export_mega_bytype(request, type): +# types = {"orga-actif": "Orga élève", +# "orga-branleur": "Orga étudiant", +# "conscrit-eleve": "Conscrit élève", +# "conscrit-etudiant": "Conscrit étudiant"} +# +# if type not in types: +# raise Http404 +# +# event = Event.objects.get(title="MEGA 2017") +# type_option = event.options.get(name="Type") +# participant_type = type_option.choices.get(value=types[type]).id +# qs = EventRegistration.objects.filter(event=event).filter( +# options__id__exact=participant_type) +# return csv_export_mega(type + '_mega_2017.csv', qs) @buro_required def export_mega_orgas(request): - event = Event.objects.get(title="Mega 2016") - type_option = event.options.get(name="Conscrit ou orga ?") - participant_type = type_option.choices.get(value="Vieux").id - qs = EventRegistration.objects.filter(event=event).exclude( - options__id=participant_type) - return csv_export_mega('orgas_mega_2016.csv', qs) + event = Event.objects.get(title="MEGA 2017") + type_option = event.options.get(name="Conscrit/Orga ?") + participant_type = type_option.choices.get(value="Orga").id + qs = EventRegistration.objects.filter(event=event).filter( + options__id=participant_type + ) + return csv_export_mega('orgas_mega_2017.csv', qs) @buro_required def export_mega_participants(request): - event = Event.objects.get(title="Mega 2016") - type_option = event.options.get(name="Conscrit ou orga ?") + event = Event.objects.get(title="MEGA 2017") + type_option = event.options.get(name="Conscrit/Orga ?") participant_type = type_option.choices.get(value="Conscrit").id qs = EventRegistration.objects.filter(event=event).filter( - options__id=participant_type) - return csv_export_mega('participants_mega_2016.csv', qs) + options__id=participant_type + ) + return csv_export_mega('participants_mega_2017.csv', qs) @buro_required def export_mega(request): - event = Event.objects.filter(title="Mega 2016") + event = Event.objects.filter(title="MEGA 2017") qs = EventRegistration.objects.filter(event=event) \ .order_by("user__username") - return csv_export_mega('all_mega_2016.csv', qs) + return csv_export_mega('all_mega_2017.csv', qs) @buro_required From faed7bff73081519ed5781710827f82a9c6346bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20P=C3=A9pin?= Date: Sun, 10 Sep 2017 02:32:21 +0200 Subject: [PATCH 08/16] =?UTF-8?q?fix=20=3Fnext=3D=E2=80=A6=20on=20K-F?= =?UTF-8?q?=C3=AAt=20logout?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- kfet/templates/kfet/base_nav.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/kfet/templates/kfet/base_nav.html b/kfet/templates/kfet/base_nav.html index ba582bcf..abcb8e18 100644 --- a/kfet/templates/kfet/base_nav.html +++ b/kfet/templates/kfet/base_nav.html @@ -94,7 +94,7 @@ {% endif %}
  • - + Déconnexion
  • @@ -103,7 +103,7 @@ {% endif %} {% if user.is_authenticated and not perms.kfet.is_team %}
  • - +
  • From 9e6188786871db4cb8ab42bebe5a7a7a886e3bc7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aur=C3=A9lien=20Delobelle?= Date: Mon, 11 Sep 2017 16:42:14 +0200 Subject: [PATCH 09/16] =?UTF-8?q?K-F=C3=AAt'=20groups=20edits=20don't=20re?= =?UTF-8?q?move=20non-kfet=20app=20permissions.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes #168. --- kfet/forms.py | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/kfet/forms.py b/kfet/forms.py index 2d07581d..6ef3aefb 100644 --- a/kfet/forms.py +++ b/kfet/forms.py @@ -167,10 +167,22 @@ class GroupForm(forms.ModelForm): name = self.cleaned_data['name'] return 'K-Fêt %s' % name + def clean_permissions(self): + kfet_perms = self.cleaned_data['permissions'] + # TODO: With Django >=1.11, the QuerySet method 'difference' can be used. + # other_groups = self.instance.permissions.difference( + # self.fields['permissions'].queryset + # ) + other_perms = self.instance.permissions.exclude( + pk__in=[p.pk for p in self.fields['permissions'].queryset], + ) + return list(kfet_perms) + list(other_perms) + class Meta: - model = Group + model = Group fields = ['name', 'permissions'] + class AccountNegativeForm(forms.ModelForm): class Meta: model = AccountNegative From 368ee3190fac23f8dfaa1c66625e428e5c232d77 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20P=C3=A9pin?= Date: Tue, 12 Sep 2017 00:14:52 +0200 Subject: [PATCH 10/16] Update CI: use postgres --- .gitlab-ci.yml | 27 +++++++++++---------------- 1 file changed, 11 insertions(+), 16 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index f2635b7b..5080ef32 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,24 +1,20 @@ services: - - mysql:latest + - postgres:latest - redis:latest variables: # GestioCOF settings - DJANGO_SETTINGS_MODULE: "cof.settings_dev" - DBNAME: "cof_gestion" - DBUSER: "cof_gestion" - DBPASSWD: "cof_password" - DBHOST: "mysql" + DJANGO_SETTINGS_MODULE: "cof.settings.prod" + DBHOST: "postgres" REDIS_HOST: "redis" # Cached packages PYTHONPATH: "$CI_PROJECT_DIR/vendor/python" - # mysql service configuration - MYSQL_DATABASE: "$DBNAME" - MYSQL_USER: "$DBUSER" - MYSQL_PASSWORD: "$DBPASSWD" - MYSQL_ROOT_PASSWORD: "root_password" + # postgres service configuration + POSTGRES_PASSWORD: "4KZt3nGPLVeWSvtBZPSM3fSzXpzEU4" + POSTGRES_USER: "cof_gestion" + POSTGRES_DB: "cof_gestion" cache: @@ -29,13 +25,12 @@ cache: before_script: - mkdir -p vendor/{python,pip,apt} - - apt-get update -q && apt-get -o dir::cache::archives="vendor/apt" install -yqq mysql-client - - mysql --user=root --password="$MYSQL_ROOT_PASSWORD" --host="$DBHOST" - -e "GRANT ALL ON test_$DBNAME.* TO '$DBUSER'@'%'" + - apt-get update -q && apt-get -o dir::cache::archives="vendor/apt" install -yqq postgresql-client + - sed -E 's/^REDIS_HOST.*/REDIS_HOST = "redis"/' cof/settings/secret_example.py > cof/settings/secret.py # Remove the old test database if it has not been done yet - - mysql --user=root --password="$MYSQL_ROOT_PASSWORD" --host="$DBHOST" + - psql --username=cof_gestion --password="4KZt3nGPLVeWSvtBZPSM3fSzXpzEU4" --host="$DBHOST" -e "DROP DATABASE test_$DBNAME" || true - - pip install --cache-dir vendor/pip -t vendor/python -r requirements-devel.txt + - pip install --cache-dir vendor/pip -t vendor/python -r requirements.txt test: stage: test From 1921f05eba5a5a05ff08c7ee75189d4cb6a556af Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20P=C3=A9pin?= Date: Tue, 12 Sep 2017 09:22:41 +0200 Subject: [PATCH 11/16] Move STATIC_ROOT in production --- cof/settings/prod.py | 1 + 1 file changed, 1 insertion(+) diff --git a/cof/settings/prod.py b/cof/settings/prod.py index 286b5547..2ffdf02f 100644 --- a/cof/settings/prod.py +++ b/cof/settings/prod.py @@ -21,6 +21,7 @@ ALLOWED_HOSTS = [ STATIC_ROOT = os.path.join( os.path.dirname(os.path.dirname(BASE_DIR)), "public", + "gestion", "static", ) From db512a97f6a8086c7d25d45e0d5fbca8eebe1d62 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20P=C3=A9pin?= Date: Mon, 25 Sep 2017 14:22:46 +0200 Subject: [PATCH 12/16] In /admin: displays "given" when it's relevant --- bda/admin.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bda/admin.py b/bda/admin.py index 0cc66d43..83c89ea5 100644 --- a/bda/admin.py +++ b/bda/admin.py @@ -61,12 +61,12 @@ class AttributionInline(admin.TabularInline): class WithListingAttributionInline(AttributionInline): + exclude = ('given', ) form = WithListingAttributionTabularAdminForm listing = True class WithoutListingAttributionInline(AttributionInline): - exclude = ('given', ) form = WithoutListingAttributionTabularAdminForm listing = False From d18fb86a98b5044a169c154961da10b6001e1507 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aur=C3=A9lien=20Delobelle?= Date: Mon, 25 Sep 2017 18:26:54 +0200 Subject: [PATCH 13/16] Fix attribution inlines of participant in admin --- bda/admin.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bda/admin.py b/bda/admin.py index 83c89ea5..60d3c1ba 100644 --- a/bda/admin.py +++ b/bda/admin.py @@ -56,7 +56,7 @@ class AttributionInline(admin.TabularInline): def get_queryset(self, request): qs = super().get_queryset(request) if self.listing is not None: - qs.filter(spectacle__listing=self.listing) + qs = qs.filter(spectacle__listing=self.listing) return qs From 596868f5b6762629ac4d6e4f0347aa7b9b77d785 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aur=C3=A9lien=20Delobelle?= Date: Sat, 30 Sep 2017 02:39:45 +0200 Subject: [PATCH 14/16] plop --- .gitlab-ci.yml | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 5080ef32..e2c36d8d 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -16,6 +16,9 @@ variables: POSTGRES_USER: "cof_gestion" POSTGRES_DB: "cof_gestion" + # psql password authentication + PGPASSWORD: $POSTGRES_PASSWORD + cache: paths: @@ -28,11 +31,10 @@ before_script: - apt-get update -q && apt-get -o dir::cache::archives="vendor/apt" install -yqq postgresql-client - sed -E 's/^REDIS_HOST.*/REDIS_HOST = "redis"/' cof/settings/secret_example.py > cof/settings/secret.py # Remove the old test database if it has not been done yet - - psql --username=cof_gestion --password="4KZt3nGPLVeWSvtBZPSM3fSzXpzEU4" --host="$DBHOST" - -e "DROP DATABASE test_$DBNAME" || true + - psql --username=$POSTGRES_USER --host=$DBHOST -c "DROP DATABASE IF EXISTS test_$POSTGRES_DB" - pip install --cache-dir vendor/pip -t vendor/python -r requirements.txt test: stage: test script: - - python manage.py test + - python manage.py test -v3 From 435e211b3d064204eb7d77a624cdbb1f00a687a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20P=C3=A9pin?= Date: Mon, 2 Oct 2017 13:58:52 +0200 Subject: [PATCH 15/16] Add a "PEI" status + "Gratis" subscription fees --- gestioncof/migrations/0013_pei.py | 47 ++++++++++++++++++++++++++++ gestioncof/models.py | 51 ++++++++++++++++++++----------- 2 files changed, 81 insertions(+), 17 deletions(-) create mode 100644 gestioncof/migrations/0013_pei.py diff --git a/gestioncof/migrations/0013_pei.py b/gestioncof/migrations/0013_pei.py new file mode 100644 index 00000000..2fbddf1f --- /dev/null +++ b/gestioncof/migrations/0013_pei.py @@ -0,0 +1,47 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('gestioncof', '0012_merge'), + ] + + operations = [ + migrations.AlterField( + model_name='cofprofile', + name='occupation', + field=models.CharField( + verbose_name='Occupation', + max_length=9, + default='1A', + choices=[ + ('exterieur', 'Extérieur'), + ('1A', '1A'), + ('2A', '2A'), + ('3A', '3A'), + ('4A', '4A'), + ('archicube', 'Archicube'), + ('doctorant', 'Doctorant'), + ('CST', 'CST'), + ('PEI', 'PEI') + ]), + ), + migrations.AlterField( + model_name='cofprofile', + name='type_cotiz', + field=models.CharField( + verbose_name='Type de cotisation', + max_length=9, + default='normalien', + choices=[ + ('etudiant', 'Normalien étudiant'), + ('normalien', 'Normalien élève'), + ('exterieur', 'Extérieur'), + ('gratis', 'Gratuit') + ]), + ), + ] diff --git a/gestioncof/models.py b/gestioncof/models.py index 6aa6f9cd..ea2cacc4 100644 --- a/gestioncof/models.py +++ b/gestioncof/models.py @@ -8,23 +8,6 @@ from gestioncof.petits_cours_models import choices_length from bda.models import Spectacle -OCCUPATION_CHOICES = ( - ('exterieur', _("Extérieur")), - ('1A', _("1A")), - ('2A', _("2A")), - ('3A', _("3A")), - ('4A', _("4A")), - ('archicube', _("Archicube")), - ('doctorant', _("Doctorant")), - ('CST', _("CST")), -) - -TYPE_COTIZ_CHOICES = ( - ('etudiant', _("Normalien étudiant")), - ('normalien', _("Normalien élève")), - ('exterieur', _("Extérieur")), -) - TYPE_COMMENT_FIELD = ( ('text', _("Texte long")), ('char', _("Texte court")), @@ -32,6 +15,40 @@ TYPE_COMMENT_FIELD = ( class CofProfile(models.Model): + STATUS_EXTE = "exterieur" + STATUS_1A = "1A" + STATUS_2A = "2A" + STATUS_3A = "3A" + STATUS_4A = "4A" + STATUS_ARCHI = "archicube" + STATUS_DOCTORANT = "doctorant" + STATUS_CST = "CST" + STATUS_PEI = "PEI" + + OCCUPATION_CHOICES = ( + (STATUS_EXTE, _("Extérieur")), + (STATUS_1A, _("1A")), + (STATUS_2A, _("2A")), + (STATUS_3A, _("3A")), + (STATUS_4A, _("4A")), + (STATUS_ARCHI, _("Archicube")), + (STATUS_DOCTORANT, _("Doctorant")), + (STATUS_CST, _("CST")), + (STATUS_PEI, _("PEI")), + ) + + COTIZ_ETUDIANT = "etudiant" + COTIZ_NORMALIEN = "normalien" + COTIZ_EXTE = "exterieur" + COTIZ_GRATIS = "gratis" + + TYPE_COTIZ_CHOICES = ( + (COTIZ_ETUDIANT, _("Normalien étudiant")), + (COTIZ_NORMALIEN, _("Normalien élève")), + (COTIZ_EXTE, _("Extérieur")), + (COTIZ_GRATIS, _("Gratuit")), + ) + user = models.OneToOneField(User, related_name="profile") login_clipper = models.CharField( "Login clipper", max_length=32, blank=True From 4d1cb3c2d7032fe2a481610bf5e68393ac44365e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20P=C3=A9pin?= Date: Tue, 10 Oct 2017 15:26:14 +0200 Subject: [PATCH 16/16] Set password for redis in CI --- .gitlab-ci.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index e2c36d8d..85be668b 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -7,6 +7,7 @@ variables: DJANGO_SETTINGS_MODULE: "cof.settings.prod" DBHOST: "postgres" REDIS_HOST: "redis" + REDIS_PASSWD: "dummy" # Cached packages PYTHONPATH: "$CI_PROJECT_DIR/vendor/python" @@ -33,6 +34,7 @@ before_script: # Remove the old test database if it has not been done yet - psql --username=$POSTGRES_USER --host=$DBHOST -c "DROP DATABASE IF EXISTS test_$POSTGRES_DB" - pip install --cache-dir vendor/pip -t vendor/python -r requirements.txt + - redis-cli config set requirepass $REDIS_PASSWD || true test: stage: test