Commit 2c53fb54 authored by Thom Wiggers's avatar Thom Wiggers 📐
Browse files

Merge branch 'feature/export-active-members' into 'master'

Added extra committee membership filters and an export action

Closes #605

See merge request !760
parents 126f7201 729a4aa8
import csv
import datetime
from django import forms
from django.contrib import admin, messages
from django.contrib.auth.models import Permission
from django.forms import BaseInlineFormSet, ModelForm
from django.http import HttpResponse
from django.utils import timezone
from django.utils.translation import ugettext_lazy as _
from activemembers import models
from activemembers.forms import CommitteeMembershipForm
from django.utils.translation import ugettext_lazy as _
from utils.translation import TranslatedModelAdmin
from . import models
from utils.snippets import datetime_to_lectureyear
class CommitteeMembershipInlineFormSet(BaseInlineFormSet):
class CommitteeMembershipInlineFormSet(forms.BaseInlineFormSet):
"""
Solely here for performance reasons.
......@@ -32,7 +38,7 @@ class CommitteeMembershipInline(admin.StackedInline):
autocomplete_fields = ('member',)
class CommitteeForm(ModelForm):
class CommitteeForm(forms.ModelForm):
"""
Solely here for performance reasons.
......@@ -84,15 +90,59 @@ class BoardAdmin(TranslatedModelAdmin):
'contact_mailinglist', 'contact_email', 'since', 'until',)
class BoardFilter(admin.SimpleListFilter):
title = _('board memberships')
parameter_name = 'board'
def lookups(self, request, model_admin):
return [
('only', _('Only board memberships')),
('none', _('No board memberships')),
]
def queryset(self, request, queryset):
if self.value() == 'only':
return queryset.filter(committee__board__is_board=True)
elif self.value() == 'none':
return queryset.exclude(committee__board__is_board=True)
return queryset
class LectureYearFilter(admin.SimpleListFilter):
title = _('lecture year')
parameter_name = 'lecture_year'
def lookups(self, request, model_admin):
current_year = datetime_to_lectureyear(timezone.now())
first_year = datetime_to_lectureyear(
models.CommitteeMembership.objects.earliest('since').since
)
return [(year, '{}-{}'.format(year, year+1))
for year in range(first_year, current_year+1)]
def queryset(self, request, queryset):
if not self.value():
return queryset
year = int(self.value())
first_of_september = datetime.date(year=year, month=9, day=1)
return queryset.exclude(until__lt=first_of_september)
@admin.register(models.CommitteeMembership)
class CommitteeMembershipAdmin(TranslatedModelAdmin):
form = CommitteeMembershipForm
list_display = ('member', 'committee', 'since', 'until', 'chair', 'role')
list_filter = ('committee',)
list_filter = ('committee', BoardFilter, LectureYearFilter)
list_select_related = ('member', 'committee',)
search_fields = ('member__first_name', 'member__last_name',
'member__email')
actions = ('export',)
def changelist_view(self, request, extra_context=None):
self.message_user(request, _('Do not edit existing memberships if the '
'chair of a committee has changed, add a '
......@@ -100,6 +150,38 @@ class CommitteeMembershipAdmin(TranslatedModelAdmin):
messages.WARNING)
return super().changelist_view(request, extra_context)
def export(self, request, queryset):
response = HttpResponse(content_type='text/csv')
response['Content-Disposition'] = ('attachment;'
'filename='
'"committee_memberships.csv"')
writer = csv.writer(response)
writer.writerow([
_('First name'),
_('Last name'),
_('Email'),
_('Committee'),
_('Committee member since'),
_('Committee member until'),
_('Chair of the committee'),
_('Role'),
])
for membership in queryset:
writer.writerow([
membership.member.first_name,
membership.member.last_name,
membership.member.email,
membership.committee,
membership.since,
membership.until,
membership.chair,
membership.role,
])
return response
export.short_description = _('Export selected memberships')
@admin.register(models.Mentorship)
class MentorshipAdmin(admin.ModelAdmin):
......
......@@ -7,8 +7,8 @@ msgid ""
msgstr ""
"Project-Id-Version: \n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2018-02-12 14:06+0100\n"
"PO-Revision-Date: 2018-02-12 13:41+0100\n"
"POT-Creation-Date: 2018-03-14 21:37+0100\n"
"PO-Revision-Date: 2018-03-14 21:38+0100\n"
"Last-Translator: Sébastiaan Versteeg <se_bastiaan@outlook.com>\n"
"Language-Team: \n"
"Language: nl\n"
......@@ -16,9 +16,25 @@ msgstr ""
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
"X-Generator: Poedit 2.0.6\n"
"X-Generator: Poedit 2.0.4\n"
#: admin.py:97
#: admin.py:95
msgid "board memberships"
msgstr "Bestuurslidmaatschappen"
#: admin.py:100
msgid "Only board memberships"
msgstr "Alleen bestuurslidmaatschappen"
#: admin.py:101
msgid "No board memberships"
msgstr "Geen bestuurslidmaatschappen"
#: admin.py:114
msgid "lecture year"
msgstr "collegejaar"
#: admin.py:147
msgid ""
"Do not edit existing memberships if the chair of a committee has changed, "
"add a new committeemembership instead."
......@@ -26,6 +42,42 @@ msgstr ""
"Pas geen bestaande lidmaatschappen aan als de voorzitter van een commissie "
"is veranderd. Voeg in de plaats daarvan een nieuw commissielidmaatschap toe."
#: admin.py:160
msgid "First name"
msgstr "Voornaam"
#: admin.py:161
msgid "Last name"
msgstr "Achernaam"
#: admin.py:162
msgid "Email"
msgstr "Email"
#: admin.py:163 models.py:222
msgid "Committee"
msgstr "Commissie"
#: admin.py:164 models.py:226
msgid "Committee member since"
msgstr "Commissielid sinds"
#: admin.py:165 models.py:232
msgid "Committee member until"
msgstr "Commissielid tot"
#: admin.py:166 models.py:240
msgid "Chair of the committee"
msgstr "Voorzitter van de commissie"
#: admin.py:167
msgid "Role"
msgstr "Rol"
#: admin.py:183
msgid "Export selected memberships"
msgstr "Exporteer de geselecteerde lidmaatschappen"
#: apps.py:7
msgid "Active members"
msgstr "Actieve leden"
......@@ -94,32 +146,16 @@ msgstr "Er bestaat al een bestuur voor die jaren"
msgid "Member"
msgstr "Lid"
#: models.py:222
msgid "Committee"
msgstr "Commissie"
#: models.py:226
msgid "Committee member since"
msgstr "Commissielid sinds"
#: models.py:227
msgid "The date this member joined the committee in this role"
msgstr "De datum waarop deze persoon lid werd deze commissie in deze rol"
#: models.py:232
msgid "Committee member until"
msgstr "Commissielid tot"
#: models.py:233
msgid "A member of this committee until this time (can't be in the future)."
msgstr ""
"De datum waarop deze persoon de commissie verliet (kan niet in de toekomst "
"liggen)"
#: models.py:240
msgid "Chair of the committee"
msgstr "Voorzitter van de commissie"
#: models.py:241
msgid "There can only be one chair at a time!"
msgstr "Er kan maar één voorzitter tegelijkertijd zijn!"
......
......@@ -191,5 +191,5 @@ class RegistrationAdmin(DoNextModelAdmin):
kwargs['queryset'] = models.Event.objects.filter(
pk=int(request.GET['event_pk']))
elif db_field.name == 'member':
kwargs['queryset'] = Member.active_members.all()
kwargs['queryset'] = Member.current_members.all()
return super().formfield_for_foreignkey(db_field, request, **kwargs)
......@@ -48,7 +48,7 @@ def get_automatic_lists():
['commissievoorzitters', 'committeechairs'], '[THALIA] [CHAIRS]',
committee_chairs, moderated=False)
lists += _create_automatic_list(
['optin'], '[THALIA] [OPTIN]', Member.active_members.filter(
['optin'], '[THALIA] [OPTIN]', Member.current_members.filter(
profile__receive_optin=True),
multilingual=True)
......
......@@ -31,7 +31,7 @@ class MemberViewset(viewsets.ReadOnlyModelViewSet):
def get_queryset(self):
if self.action == 'list':
return Member.active_members.get_queryset()
return Member.current_members.get_queryset()
return Member.objects.all()
def _get_birthdays(self, member, start, end):
......@@ -68,7 +68,7 @@ class MemberViewset(viewsets.ReadOnlyModelViewSet):
detail='start or end query parameters invalid') from e
queryset = (
Member.active_members
Member.current_members
.with_birthdays_in_range(start, end)
.filter(profile__show_birthday=True)
)
......
......@@ -11,7 +11,7 @@ from thaliawebsite.settings import settings
def send_membership_announcement(dry_run=False):
members = (Member.active_members
members = (Member.current_members
.filter(membership__until__isnull=True)
.distinct())
......@@ -44,7 +44,7 @@ def send_membership_announcement(dry_run=False):
def send_information_request(dry_run=False):
members = Member.active_members.all()
members = Member.current_members.all()
with mail.get_connection() as connection:
for member in members:
......@@ -77,7 +77,7 @@ def send_information_request(dry_run=False):
def send_expiration_announcement(dry_run=False):
expiry_date = datetime.now() + timedelta(days=31)
members = (Member.active_members
members = (Member.current_members
.filter(membership__until__lte=expiry_date)
.distinct())
......
......@@ -16,7 +16,7 @@ from django.utils.translation import ugettext_lazy as _
from localflavor.generic.countries.sepa import IBAN_SEPA_COUNTRIES
from localflavor.generic.models import IBANField
from activemembers.models import Committee
from activemembers.models import Committee, CommitteeMembership
class MemberManager(UserManager):
......@@ -29,6 +29,19 @@ class MemberManager(UserManager):
class ActiveMemberManager(MemberManager):
"""Get all active members"""
def get_queryset(self):
active_memberships = (CommitteeMembership
.active_memberships
.exclude(committee__board__is_board=True))
return (super().get_queryset()
.filter(committeemembership__in=active_memberships)
.distinct())
class CurrentMemberManager(MemberManager):
"""Get all members with an active membership"""
def get_queryset(self):
return (super().get_queryset()
.exclude(membership=None)
......@@ -64,6 +77,7 @@ class Member(User):
ordering = ('first_name', 'last_name')
objects = MemberManager()
current_members = CurrentMemberManager()
active_members = ActiveMemberManager()
def __str__(self):
......
......@@ -16,7 +16,7 @@ class MemberBirthdayTest(TestCase):
def _get_members(self, start, end):
start_date = self._make_date(start)
end_date = self._make_date(end)
return Member.active_members.with_birthdays_in_range(
return Member.current_members.with_birthdays_in_range(
start_date, end_date
)
......
......@@ -182,7 +182,7 @@ def iban_export(request):
header_fields = ['name', 'username', 'iban']
rows = []
members = models.Member.active_members.filter(
members = models.Member.current_members.filter(
profile__direct_debit_authorized=True)
for member in members:
......@@ -210,7 +210,7 @@ def statistics(request):
member_types = ("member", "supporter", "honorary")
# The numbers
total = models.Member.active_members.count()
total = models.Member.current_members.count()
context = {
"total_members": total,
......
......@@ -69,7 +69,7 @@ def admin_send(request, pk):
translation.activate(language[0])
recipients = [member.email for member in
Member.active_members.all().filter(
Member.current_members.all().filter(
profile__receive_newsletter=True,
profile__language=language[0])
if member.email]
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment