Skip to content
Snippets Groups Projects
Commit efe7b67d authored by Jonathan Weth's avatar Jonathan Weth :keyboard:
Browse files

Merge branch 'group-stats' into 'master'

Add statistics about group to group view

See merge request AlekSIS!229
parents f90161bc 35de8737
No related branches found
No related tags found
1 merge request!229Add statistics about group to group view
Pipeline #4221 passed
# Generated by Django 3.0.7 on 2020-06-28 14:02
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('core', '0002_school_term'),
]
operations = [
migrations.AlterModelOptions(
name='group',
options={'ordering': ['short_name', 'name'], 'permissions': (('assign_child_groups_to_groups', 'Can assign child groups to groups'), ('view_group_stats', 'Can view statistics about group.')), 'verbose_name': 'Group', 'verbose_name_plural': 'Groups'},
),
migrations.AlterField(
model_name='group',
name='additional_fields',
field=models.ManyToManyField(blank=True, to='core.AdditionalField', verbose_name='Additional fields'),
),
]
...@@ -224,13 +224,13 @@ class Person(ExtensibleModel): ...@@ -224,13 +224,13 @@ class Person(ExtensibleModel):
return self.age_at(timezone.now().date()) return self.age_at(timezone.now().date())
def age_at(self, today): def age_at(self, today):
"""Age of the person at a given date and time.""" if self.date_of_birth:
years = today.year - self.date_of_birth.year years = today.year - self.date_of_birth.year
if self.date_of_birth.month > today.month or ( if (self.date_of_birth.month > today.month
self.date_of_birth.month == today.month and self.date_of_birth.day > today.day or (self.date_of_birth.month == today.month
): and self.date_of_birth.day > today.day)):
years -= 1 years -= 1
return years return years
def save(self, *args, **kwargs): def save(self, *args, **kwargs):
super().save(*args, **kwargs) super().save(*args, **kwargs)
...@@ -326,7 +326,10 @@ class Group(SchoolTermRelatedExtensibleModel): ...@@ -326,7 +326,10 @@ class Group(SchoolTermRelatedExtensibleModel):
ordering = ["short_name", "name"] ordering = ["short_name", "name"]
verbose_name = _("Group") verbose_name = _("Group")
verbose_name_plural = _("Groups") verbose_name_plural = _("Groups")
permissions = (("assign_child_groups_to_groups", _("Can assign child groups to groups")),) permissions = (
("assign_child_groups_to_groups", _("Can assign child groups to groups")),
("view_group_stats", _("Can view statistics about group.")),
)
constraints = [ constraints = [
models.UniqueConstraint(fields=["school_term", "name"], name="unique_school_term_name"), models.UniqueConstraint(fields=["school_term", "name"], name="unique_school_term_name"),
models.UniqueConstraint( models.UniqueConstraint(
...@@ -380,6 +383,22 @@ class Group(SchoolTermRelatedExtensibleModel): ...@@ -380,6 +383,22 @@ class Group(SchoolTermRelatedExtensibleModel):
"""Flat list of all members and owners to fulfill announcement API contract.""" """Flat list of all members and owners to fulfill announcement API contract."""
return list(self.members.all()) + list(self.owners.all()) return list(self.members.all()) + list(self.owners.all())
@property
def get_group_stats(self) -> dict:
""" Get stats about a given group """
stats = {}
stats['members'] = len(self.members.all())
ages = [person.age for person in self.members.filter(date_of_birth__isnull=False)]
if ages:
stats['age_avg'] = sum(ages) / len(ages)
stats['age_range_min'] = min(ages)
stats['age_range_max'] = max(ages)
return stats
def __str__(self) -> str: def __str__(self) -> str:
if self.school_term: if self.school_term:
return f"{self.name} ({self.short_name}) ({self.school_term})" return f"{self.name} ({self.short_name}) ({self.school_term})"
......
...@@ -272,3 +272,9 @@ view_admin_menu_predicate = has_person & ( ...@@ -272,3 +272,9 @@ view_admin_menu_predicate = has_person & (
| view_announcements_predicate | view_announcements_predicate
) )
rules.add_perm("core.view_admin_menu", view_admin_menu_predicate) rules.add_perm("core.view_admin_menu", view_admin_menu_predicate)
# View group stats
view_group_stats_predicate = has_person & (
has_global_perm("core.view_group_stats") | has_object_perm("core.view_group_stats")
)
rules.add_perm("core.view_group_stats", view_group_stats_predicate)
...@@ -14,6 +14,7 @@ ...@@ -14,6 +14,7 @@
{% has_perm 'core.edit_group' user group as can_change_group %} {% has_perm 'core.edit_group' user group as can_change_group %}
{% has_perm 'core.change_group_preferences' user group as can_change_group_preferences %} {% has_perm 'core.change_group_preferences' user group as can_change_group_preferences %}
{% has_perm 'core.delete_group' user group as can_delete_group %} {% has_perm 'core.delete_group' user group as can_delete_group %}
{% has_perm 'core.view_group_stats' user group as can_view_group_stats %}
{% if can_change_group or can_change_group_preferences or can_delete_group %} {% if can_change_group or can_change_group_preferences or can_delete_group %}
<p> <p>
...@@ -59,6 +60,25 @@ ...@@ -59,6 +60,25 @@
</tr> </tr>
</table> </table>
{% if can_view_group_stats %}
<h5>{% blocktrans %}Statistics{% endblocktrans %}</h5>
<ul>
<li>
{% trans "Count of members" %}: {{ stats.members }}
</li>
{% if stats.age_avg %}
<li>
{% trans "Average age" %}: {{ stats.age_avg|floatformat }}
</li>
{% endif %}
{% if stats.age_range_min %}
<li>
{% trans "Age range" %}: {{ stats.age_range_min }} {% trans "years to" %} {{ stats.age_range_max }} {% trans "years "%}
</li>
{% endif %}
</ul>
{% endif %}
<h5>{% blocktrans %}Owners{% endblocktrans %}</h5> <h5>{% blocktrans %}Owners{% endblocktrans %}</h5>
{% render_table owners_table %} {% render_table owners_table %}
......
...@@ -203,6 +203,9 @@ def group(request: HttpRequest, id_: int) -> HttpResponse: ...@@ -203,6 +203,9 @@ def group(request: HttpRequest, id_: int) -> HttpResponse:
RequestConfig(request).configure(owners_table) RequestConfig(request).configure(owners_table)
context["owners_table"] = owners_table context["owners_table"] = owners_table
# Get statistics
context["stats"] = group.get_group_stats
return render(request, "core/group/full.html", context) return render(request, "core/group/full.html", context)
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment