Commit 8d015ca5 authored by Sébastiaan Versteeg's avatar Sébastiaan Versteeg

Change graph types on statistics page

parent 851af8bb
from django.db.models import Count, Q
from django.utils import timezone
from activemembers.models import Committee
def generate_statistics():
"""
Generate statistics about number of members in each committee
:return: Dict with key, value being resp. name, member count of committees.
"""
committees = Committee.active_objects.annotate(member_count=(
Count('members', filter=(
Q(membergroupmembership__until=None) |
Q(membergroupmembership__until__gte=timezone.now())))))
data = {}
for committee in committees:
data.update({
committee.name: committee.member_count
})
return data
from collections import OrderedDict
from django.utils import timezone
from django.utils.datetime_safe import date
from django.utils.translation import ugettext_lazy as _, get_language
from events import emails
from events.exceptions import RegistrationError
from events.models import Registration, RegistrationInformationField
from events.models import Registration, RegistrationInformationField, Event
from payments.models import Payment
from utils.snippets import datetime_to_lectureyear
def is_user_registered(member, event):
......@@ -286,3 +288,24 @@ def update_registration_by_organiser(registration, member, data):
registration.present = data['present']
registration.save()
def generate_category_statistics():
"""
Generate statistics about events, number of events per category
:return: Dict with key, value resp. being category, event count.
"""
year = datetime_to_lectureyear(timezone.now())
data = {}
for i in range(5):
year_start = date(year=year - i, month=9, day=1)
year_end = date(year=year - i + 1, month=9, day=1)
data[str(year - i)] = {
str(display): Event.objects.filter(category=key,
start__gte=year_start,
end__lte=year_end).count()
for key, display in Event.EVENT_CATEGORIES
}
return data
......@@ -6,13 +6,15 @@ from django.db.models import Q, Count
from django.utils import timezone
from django.utils.translation import gettext
from activemembers.models import Committee
from events.models import Event
from members import emails
from members.models import Membership, Member
from utils.snippets import datetime_to_lectureyear
def _member_group_memberships(
member: Member, condition: Callable[[Membership], bool]
member: Member, condition: Callable[[Membership], bool]
) -> Dict[str, Any]:
"""
Determines the group membership of a user based on a condition
......@@ -34,7 +36,7 @@ def _member_group_memberships(
period['role'] = membership.role
if (membership.until is None and
hasattr(membership.group, 'board')):
hasattr(membership.group, 'board')):
period['until'] = membership.group.board.until
name = membership.group.name
......@@ -85,60 +87,58 @@ def member_societies(member) -> List:
return sorted(societies.values(), key=lambda x: x['earliest'])
def gen_stats_member_type(member_types) -> Dict[str, int]:
def gen_stats_member_type() -> Dict[str, int]:
"""
Generate a dictionary where every key is a member type with
the value being the number of current members of that type
"""
total = dict()
for member_type in member_types:
total[member_type] = (Membership
data = {}
for key, display in Membership.MEMBERSHIP_TYPES:
data[str(display)] = (Membership
.objects
.filter(since__lte=date.today())
.filter(Q(until__isnull=True) |
Q(until__gt=date.today()))
.filter(type=member_type)
.filter(type=key)
.count())
return total
return data
def gen_stats_year(
member_types) -> List[Dict[Union[str, Any], Union[int, Any]]]:
def gen_stats_year() -> Dict[str, Dict[str, int]]:
"""
Generate list with 6 entries, where each entry represents the total amount
of Thalia members in a year. The sixth element contains all the multi-year
students.
"""
stats_year = []
stats_year = {}
current_year = datetime_to_lectureyear(date.today())
for i in range(5):
new = dict()
new['cohort'] = current_year - i
for member_type in member_types:
new[member_type] = (
new = {}
for key, _ in Membership.MEMBERSHIP_TYPES:
new[key] = (
Membership.objects
.filter(user__profile__starting_year=current_year - i)
.filter(since__lte=date.today())
.filter(Q(until__isnull=True) |
Q(until__gt=date.today()))
.filter(type=member_type)
.filter(type=key)
.count())
stats_year.append(new)
stats_year[str(current_year - i)] = new
# Add multi year members
new = dict()
new['cohort'] = gettext('Older')
for member_type in member_types:
new[member_type] = (
new = {}
for key, _ in Membership.MEMBERSHIP_TYPES:
new[key] = (
Membership.objects
.filter(user__profile__starting_year__lt=current_year - 4)
.filter(since__lte=date.today())
.filter(Q(until__isnull=True) |
Q(until__gt=date.today()))
.filter(type=member_type)
.filter(type=key)
.count())
stats_year.append(new)
stats_year[str(gettext('Older'))] = new
return stats_year
......@@ -205,7 +205,7 @@ def execute_data_minimisation(dry_run=False, members=None) -> List[Member]:
processed_members = []
for member in members:
if (member.latest_membership is None or
member.latest_membership.until <= deletion_period):
member.latest_membership.until <= deletion_period):
processed_members.append(member)
profile = member.profile
profile.student_number = None
......
......@@ -12,18 +12,25 @@
<h2 class="text-center mb-4">{% trans "Total amount of Thalia members" %}: {{ total_members }}</h2>
<div class="row">
<div class="col-sm-6 col-lg-4">
<div class="col-lg-6">
<canvas id="members-type-chart"></canvas>
</div>
<div class="col-sm-6 col-lg-4">
<div class="col-lg-6">
<canvas id="total-year-chart"></canvas>
</div>
<div class="col-sm-6 col-lg-4">
<canvas id="members-year-chart"></canvas>
<div class="col-12 mt-2 mb-2">
<canvas id="committees-members-chart"></canvas>
</div>
<div class="col-12 mb-2">
<canvas id="event-category-chart"></canvas>
</div>
<div class="col-sm-6 col-lg-4">
<canvas id="benefactors-year-chart"></canvas>
<canvas id="pizza-total-type-chart"></canvas>
</div>
<div class="col-sm-6 col-lg-4">
<canvas id="pizza-total-type-chart"></canvas>
</div>
......
......@@ -35,26 +35,24 @@ class StatisticsTest(TestCase):
return sum(members.values())
def test_gen_stats_year_no_members(self):
member_types = [t[0] for t in Membership.MEMBERSHIP_TYPES]
result = gen_stats_year(member_types)
result = gen_stats_year()
self.assertEqual(0, self.sum_members(result))
def test_gen_stats_active(self):
"""
Testing if active and non-active objects are counted correctly
"""
member_types = [t[0] for t in Membership.MEMBERSHIP_TYPES]
current_year = datetime_to_lectureyear(date.today())
# Set start date to current year - 1:
for m in Member.objects.all():
m.profile.starting_year = current_year - 1
m.profile.save()
result = gen_stats_year(member_types)
result = gen_stats_year()
self.assertEqual(10, self.sum_members(result))
self.assertEqual(10, self.sum_members(result, Membership.MEMBER))
result = gen_stats_member_type(member_types)
result = gen_stats_member_type()
self.assertEqual(10, self.sum_member_types(result))
# Change one membership to benefactor should decrease amount of members
......@@ -62,12 +60,12 @@ class StatisticsTest(TestCase):
m.type = Membership.BENEFACTOR
m.save()
result = gen_stats_year(member_types)
result = gen_stats_year()
self.assertEqual(10, self.sum_members(result))
self.assertEqual(9, self.sum_members(result, Membership.MEMBER))
self.assertEqual(1, self.sum_members(result, Membership.BENEFACTOR))
result = gen_stats_member_type(member_types)
result = gen_stats_member_type()
self.assertEqual(10, self.sum_member_types(result))
self.assertEqual(9, result[Membership.MEMBER])
self.assertEqual(1, result[Membership.BENEFACTOR])
......@@ -77,13 +75,13 @@ class StatisticsTest(TestCase):
m.type = Membership.HONORARY
m.save()
result = gen_stats_year(member_types)
result = gen_stats_year()
self.assertEqual(10, self.sum_members(result))
self.assertEqual(8, self.sum_members(result, Membership.MEMBER))
self.assertEqual(1, self.sum_members(result, Membership.BENEFACTOR))
self.assertEqual(1, self.sum_members(result, Membership.HONORARY))
result = gen_stats_member_type(member_types)
result = gen_stats_member_type()
self.assertEqual(10, self.sum_member_types(result))
self.assertEqual(8, result[Membership.MEMBER])
self.assertEqual(1, result[Membership.BENEFACTOR])
......@@ -94,13 +92,13 @@ class StatisticsTest(TestCase):
m = Membership.objects.all()[2]
m.until = timezone.now() - timedelta(days=365)
m.save()
result = gen_stats_year(member_types)
result = gen_stats_year()
self.assertEqual(9, self.sum_members(result))
self.assertEqual(7, self.sum_members(result, Membership.MEMBER))
self.assertEqual(1, self.sum_members(result, Membership.BENEFACTOR))
self.assertEqual(1, self.sum_members(result, Membership.HONORARY))
result = gen_stats_member_type(member_types)
result = gen_stats_member_type()
self.assertEqual(9, self.sum_member_types(result))
self.assertEqual(7, result[Membership.MEMBER])
self.assertEqual(1, result[Membership.BENEFACTOR])
......@@ -138,7 +136,7 @@ class StatisticsTest(TestCase):
m.profile.save()
# 4 active members
result = gen_stats_year(member_types)
result = gen_stats_year()
self.assertEqual(4, self.sum_members(result))
self.assertEqual(4, self.sum_members(result, Membership.MEMBER))
......
......@@ -27,6 +27,8 @@ from . import models
from .forms import ProfileForm
from .services import member_achievements
from .services import member_societies
import events.services as event_services
import activemembers.services as activemembers_services
class ObtainThaliaAuthToken(ObtainAuthToken):
......@@ -219,18 +221,23 @@ class StatisticsView(TemplateView):
def get_context_data(self, **kwargs) -> dict:
context = super().get_context_data(**kwargs)
member_types = [t[0] for t in Membership.MEMBERSHIP_TYPES]
total = models.Member.current_members.count()
context.update({
"total_members": total,
"statistics": json.dumps({
"cohort_sizes": services.gen_stats_year(member_types),
"cohort_sizes":
services.gen_stats_year(),
"member_type_distribution":
services.gen_stats_member_type(member_types),
"total_pizza_orders": pizzas.services.gen_stats_pizza_orders(),
services.gen_stats_member_type(),
"total_pizza_orders":
pizzas.services.gen_stats_pizza_orders(),
"current_pizza_orders":
pizzas.services.gen_stats_current_pizza_orders(),
"committee_sizes":
activemembers_services.generate_statistics(),
"event_categories":
event_services.generate_category_statistics(),
})
})
......
import operator
from events.services import is_organiser
from . models import Product, Order, PizzaEvent
def gen_stats_pizza_orders():
total = []
total = {}
for product in Product.objects.all():
total.append({
'name': product.name,
'total': Order.objects.filter(product=product).count(),
total.update({
product.name: Order.objects.filter(product=product).count(),
})
total.sort(key=lambda prod: prod['total'], reverse=True)
return total
return {
i[0]: i[1]
for i in sorted(total.items(), key=lambda x: x[1], reverse=True)[:5]
if i[1] > 0
}
def gen_stats_current_pizza_orders():
total = []
total = {}
current_pizza_event = PizzaEvent.current()
if not current_pizza_event:
return None
for product in Product.objects.filter():
total.append({
'name': product.name,
'total': Order.objects.filter(
total.update({
product.name: Order.objects.filter(
product=product,
pizza_event=current_pizza_event,
).count(),
})
total.sort(key=lambda prod: prod['total'], reverse=True)
return total
return {
i[0]: i[1]
for i in sorted(total.items(), key=lambda x: x[1], reverse=True)[:5]
if i[1] > 0
}
def can_change_order(member, pizza_event):
......
Markdown is supported
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