From 533c1b3efafeaed7ca9f22a5417086c8564f54d0 Mon Sep 17 00:00:00 2001 From: Thomas Steen Rasmussen Date: Tue, 11 Jul 2017 22:50:31 +0200 Subject: [PATCH] add initial people stuff --- src/bornhack/settings.py | 1 + src/bornhack/urls.py | 6 +++ .../migrations/0021_auto_20170711_2247.py | 19 +++++++ src/camps/models.py | 1 + src/people/__init__.py | 0 src/people/admin.py | 3 ++ src/people/apps.py | 5 ++ src/people/migrations/__init__.py | 0 src/people/models.py | 3 ++ src/people/templates/people.html | 54 +++++++++++++++++++ src/people/tests.py | 3 ++ src/people/views.py | 9 ++++ src/profiles/admin.py | 18 ++++++- .../migrations/0006_auto_20170711_1757.py | 34 ++++++++++++ .../migrations/0007_auto_20170711_2025.py | 34 ++++++++++++ src/profiles/models.py | 25 ++++++--- src/profiles/templates/profile_detail.html | 25 +++++---- src/profiles/views.py | 10 +++- ...lic_credit_names_for_team_responsibles.py} | 0 .../migrations/0008_auto_20160808_1747.py | 2 +- src/program/signal_handlers.py | 22 +++++--- .../migrations/0016_auto_20170711_2247.py | 21 ++++++++ src/teams/models.py | 10 +++- src/teams/templates/team_detail.html | 39 +++++++++++--- src/teams/templates/team_list.html | 2 +- src/teams/templates/team_manage.html | 6 +++ src/teams/views.py | 1 + 27 files changed, 316 insertions(+), 37 deletions(-) create mode 100644 src/camps/migrations/0021_auto_20170711_2247.py create mode 100644 src/people/__init__.py create mode 100644 src/people/admin.py create mode 100644 src/people/apps.py create mode 100644 src/people/migrations/__init__.py create mode 100644 src/people/models.py create mode 100644 src/people/templates/people.html create mode 100644 src/people/tests.py create mode 100644 src/people/views.py create mode 100644 src/profiles/migrations/0006_auto_20170711_1757.py create mode 100644 src/profiles/migrations/0007_auto_20170711_2025.py rename src/program/migrations/{0007_auto_20160807_1333.py => 0007_populate_public_credit_names_for_team_responsibles.py} (100%) create mode 100644 src/teams/migrations/0016_auto_20170711_2247.py diff --git a/src/bornhack/settings.py b/src/bornhack/settings.py index 3d397aec..ab46c8eb 100644 --- a/src/bornhack/settings.py +++ b/src/bornhack/settings.py @@ -41,6 +41,7 @@ INSTALLED_APPS = [ 'sponsors', 'ircbot', 'teams', + 'people', 'allauth', 'allauth.account', diff --git a/src/bornhack/urls.py b/src/bornhack/urls.py index 7a681f75..6220debd 100644 --- a/src/bornhack/urls.py +++ b/src/bornhack/urls.py @@ -11,6 +11,7 @@ from villages.views import * from program.views import * from sponsors.views import * from teams.views import * +from people.views import * urlpatterns = [ url( @@ -101,6 +102,11 @@ urlpatterns = [ name='village_list_redirect', ), + url( + r'^people/$', + PeopleView.as_view(), + ), + # camp specific urls below here url( diff --git a/src/camps/migrations/0021_auto_20170711_2247.py b/src/camps/migrations/0021_auto_20170711_2247.py new file mode 100644 index 00000000..8b97dfd5 --- /dev/null +++ b/src/camps/migrations/0021_auto_20170711_2247.py @@ -0,0 +1,19 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.10.5 on 2017-07-11 20:47 +from __future__ import unicode_literals + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('camps', '0020_camp_read_only'), + ] + + operations = [ + migrations.AlterModelOptions( + name='camp', + options={'ordering': ['-title'], 'verbose_name': 'Camp', 'verbose_name_plural': 'Camps'}, + ), + ] diff --git a/src/camps/models.py b/src/camps/models.py index 40d52b58..62b034d9 100644 --- a/src/camps/models.py +++ b/src/camps/models.py @@ -15,6 +15,7 @@ class Camp(CreatedUpdatedModel, UUIDModel): class Meta: verbose_name = 'Camp' verbose_name_plural = 'Camps' + ordering = ['-title'] title = models.CharField( verbose_name='Title', diff --git a/src/people/__init__.py b/src/people/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/src/people/admin.py b/src/people/admin.py new file mode 100644 index 00000000..8c38f3f3 --- /dev/null +++ b/src/people/admin.py @@ -0,0 +1,3 @@ +from django.contrib import admin + +# Register your models here. diff --git a/src/people/apps.py b/src/people/apps.py new file mode 100644 index 00000000..3eae75ac --- /dev/null +++ b/src/people/apps.py @@ -0,0 +1,5 @@ +from django.apps import AppConfig + + +class PeopleConfig(AppConfig): + name = 'people' diff --git a/src/people/migrations/__init__.py b/src/people/migrations/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/src/people/models.py b/src/people/models.py new file mode 100644 index 00000000..71a83623 --- /dev/null +++ b/src/people/models.py @@ -0,0 +1,3 @@ +from django.db import models + +# Create your models here. diff --git a/src/people/templates/people.html b/src/people/templates/people.html new file mode 100644 index 00000000..39f26174 --- /dev/null +++ b/src/people/templates/people.html @@ -0,0 +1,54 @@ +{% extends 'base.html' %} +{% load commonmark %} +{% load teams_tags %} + +{% block title %} +People | {{ block.super }} +{% endblock %} + +{% block content %} + +

The People of BornHack

+

The following is a list of the volunteers that have helped make BornHack happen. It goes without saying that an event like BornHack would not be possible to pull off without our volunteers. We are forever grateful, and we leave your names here as a recognition of the work you put in. Thank you! +{% for camp in camp_list %} + {% if camp.teams.exists %} +

{{ camp.title }} Teams

+ + + + + + + + + + {% for team in camp.teams.all %} + + + + + + + {% endfor %} + +
NameDescriptionMembers
+ {{ team.name }} Team + + {{ team.description|unsafecommonmark }} + + {% if team.approvedmembers.count == team.anoncount %} + {{ team.anoncount }} anonymous member(s) + {% endif %} + + {% for member in team.approvedmembers.all %} + {% if member.user.profile.approved_public_credit_name %} + {{ member.user.profile.approved_public_credit_name }}{% if member.responsible %} (responsible){% endif %}
+ {% endif %} + {% endfor %} + {% if team.anoncount and team.anoncount != team.approvedmembers.count %} + plus {{ team.anoncount }} anonymous member(s). + {% endif %} +
+ {% endif %} +{% endfor %} +{% endblock %} diff --git a/src/people/tests.py b/src/people/tests.py new file mode 100644 index 00000000..7ce503c2 --- /dev/null +++ b/src/people/tests.py @@ -0,0 +1,3 @@ +from django.test import TestCase + +# Create your tests here. diff --git a/src/people/views.py b/src/people/views.py new file mode 100644 index 00000000..e9ec181f --- /dev/null +++ b/src/people/views.py @@ -0,0 +1,9 @@ +from django.shortcuts import render +from django.views.generic import ListView +from camps.models import Camp + + +class PeopleView(ListView): + template_name = 'people.html' + model = Camp + diff --git a/src/profiles/admin.py b/src/profiles/admin.py index 5e7bf791..a2c46950 100644 --- a/src/profiles/admin.py +++ b/src/profiles/admin.py @@ -1,5 +1,21 @@ from django.contrib import admin from .models import Profile +@admin.register(Profile) +class OrderAdmin(admin.ModelAdmin): + actions = ['approve_public_credit_names'] + + list_display = [ + 'user', + 'name', + 'description', + 'public_credit_name', + 'public_credit_name_approved', + ] + + def approve_public_credit_names(self, request, queryset): + for profile in queryset.filter(public_credit_name_approved=False): + profile.approve_public_credit_name() + approve_public_credit_names.description = 'Approve Public Credit Name(s)' + -admin.site.register(Profile) diff --git a/src/profiles/migrations/0006_auto_20170711_1757.py b/src/profiles/migrations/0006_auto_20170711_1757.py new file mode 100644 index 00000000..967d3740 --- /dev/null +++ b/src/profiles/migrations/0006_auto_20170711_1757.py @@ -0,0 +1,34 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.10.5 on 2017-07-11 15:57 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('profiles', '0005_auto_20170711_1721'), + ] + + operations = [ + migrations.RemoveField( + model_name='profile', + name='public_credits', + ), + migrations.AddField( + model_name='profile', + name='public_credit_name_approved', + field=models.BooleanField(default=False, help_text='Check this box to approve this users public_credit_name. This will be unchecked automatically when the user edits public_credit_name'), + ), + migrations.AlterField( + model_name='profile', + name='name', + field=models.CharField(blank=True, default='', help_text='Your name or handle (only visible to team responsible and organisers)', max_length=200), + ), + migrations.AlterField( + model_name='profile', + name='public_credit_name', + field=models.CharField(blank=True, help_text='The name you want to appear on in the credits section of the public website (the People pages). Leave empty if you want no public credit.', max_length=100), + ), + ] diff --git a/src/profiles/migrations/0007_auto_20170711_2025.py b/src/profiles/migrations/0007_auto_20170711_2025.py new file mode 100644 index 00000000..e1dea74c --- /dev/null +++ b/src/profiles/migrations/0007_auto_20170711_2025.py @@ -0,0 +1,34 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.10.5 on 2017-07-11 18:25 +from __future__ import unicode_literals + +from django.db import migrations + +def populate_team_responsible_public_credit_names(apps, schema_editor): + Profile = apps.get_model("profiles", "Profile") + TeamMember = apps.get_model("teams", "TeamMember") + Team = apps.get_model("teams", "TeamMember") + + for team in Team.objects.all(): + if TeamMember.objects.filter(team=team, responsible=True).exists(): + responsibles = User.objects.filter(teammember__team=team, teammember__responsible=True) + else: + responsibles = team.area.responsible.all() + for resp in responsibles: + if not resp.profile.public_credit_name: + profile = resp.profile + profile.public_credit_name = resp.profile.name + profile.save() + + +class Migration(migrations.Migration): + + dependencies = [ + ('profiles', '0006_auto_20170711_1757'), + ('teams', '0014_remove_teammember_deleted'), + ] + + operations = [ + migrations.RunPython(populate_team_responsible_public_credit_names), + ] + diff --git a/src/profiles/models.py b/src/profiles/models.py index 49325c36..a9ec072f 100644 --- a/src/profiles/models.py +++ b/src/profiles/models.py @@ -22,7 +22,7 @@ class Profile(CreatedUpdatedModel, UUIDModel): max_length=200, default='', blank=True, - help_text='Your name or handle' + help_text='Your name or handle (only visible to team responsible and organisers)' ) description = models.TextField( @@ -31,15 +31,15 @@ class Profile(CreatedUpdatedModel, UUIDModel): help_text='Please include any info you think could be relevant, like drivers license, first aid certificates, crafts, skills and previous experience. Please also include availability if you are not there for the full week.', ) - public_credits = models.BooleanField( - default=False, - help_text='Check this box if you want your name to appear in the list of volunteers for this event. Please inform your team responsible what you would like to be credited as.' - ) - public_credit_name = models.CharField( blank=True, max_length=100, - help_text='The name used on the public list of volunteers for this event. Only used if public_credits is True. Not editable by users (to avoid getting junk on the website).' + help_text='The name you want to appear on in the credits section of the public website (the People pages). Leave empty if you want no public credit.' + ) + + public_credit_name_approved = models.BooleanField( + default=False, + help_text='Check this box to approve this users public_credit_name. This will be unchecked automatically when the user edits public_credit_name' ) @property @@ -49,6 +49,17 @@ class Profile(CreatedUpdatedModel, UUIDModel): def __str__(self): return self.user.username + def approve_public_credit_name(self): + self.public_credit_name_approved = True + self.save() + + @property + def approved_public_credit_name(self): + if self.public_credit_name_approved: + return self.public_credit_name + else: + return False + @receiver(post_save, sender=User) def create_profile(sender, created, instance, **kwargs): diff --git a/src/profiles/templates/profile_detail.html b/src/profiles/templates/profile_detail.html index f6e9d83a..3f8a6d34 100644 --- a/src/profiles/templates/profile_detail.html +++ b/src/profiles/templates/profile_detail.html @@ -1,15 +1,22 @@ {% extends 'profile_base.html' %} {% block profile_content %} -

The information in your profile is only visible to team responsible and organisers.

-

If you intend to join one or more teams you can use the profile to help the team responsible understand who you are and what you have to offer.

-
+

Apart from Public Credit Name the information in your profile is only visible to team responsible and organisers.

+

If you intend to join one or more teams you can use the profile to help the team responsible understand who you are and what you have to offer. Please also include availability if you are not joining us for the entire week.

Your Profile

-
-
Name
-
{{ profile.name|default:"N/A" }}
-
Description
-
{{ profile.description|default:"N/A" }}
-
+ + + + + + + + + + + + + +
Name (not visible to the public){{ profile.name|default:"N/A" }}
Description (not visible to the public){{ profile.description|default:"N/A" }}
Public Credit Name (visible to the public, leave empty if you want no credits){{ profile.public_credit_name|default:"N/A" }} {% if profile.public_credit_name %}({% if profile.public_credit_name_approved %}approved{% else %}pending approval{% endif %}){% endif %}
Edit Profile {% endblock profile_content %} diff --git a/src/profiles/views.py b/src/profiles/views.py index f8f6f3f1..b35cd3fe 100644 --- a/src/profiles/views.py +++ b/src/profiles/views.py @@ -16,7 +16,7 @@ class ProfileDetail(LoginRequiredMixin, DetailView): class ProfileUpdate(LoginRequiredMixin, UpdateView): model = models.Profile - fields = ['name', 'description'] + fields = ['name', 'description', 'public_credit_name'] success_url = reverse_lazy('profiles:detail') template_name = 'profile_form.html' @@ -24,6 +24,12 @@ class ProfileUpdate(LoginRequiredMixin, UpdateView): return models.Profile.objects.get(user=self.request.user) def form_valid(self, form, **kwargs): + print(form.cleaned_data['public_credit_name']) + print(form.instance.public_credit_name) + if 'public_credit_name' in form.changed_data and form.cleaned_data['public_credit_name']: + # user changed the name (to something non blank) + form.instance.public_credit_name_approved = False + form.instance.save() messages.info(self.request, 'Your profile has been updated.') - return super(ProfileUpdate, self).form_valid(form, **kwargs) + return super().form_valid(form, **kwargs) diff --git a/src/program/migrations/0007_auto_20160807_1333.py b/src/program/migrations/0007_populate_public_credit_names_for_team_responsibles.py similarity index 100% rename from src/program/migrations/0007_auto_20160807_1333.py rename to src/program/migrations/0007_populate_public_credit_names_for_team_responsibles.py diff --git a/src/program/migrations/0008_auto_20160808_1747.py b/src/program/migrations/0008_auto_20160808_1747.py index f6c8b506..e0658c38 100644 --- a/src/program/migrations/0008_auto_20160808_1747.py +++ b/src/program/migrations/0008_auto_20160808_1747.py @@ -8,7 +8,7 @@ from django.db import migrations, models class Migration(migrations.Migration): dependencies = [ - ('program', '0007_auto_20160807_1333'), + ('program', '0007_populate_public_credit_names_for_team_responsibles'), ] operations = [ diff --git a/src/program/signal_handlers.py b/src/program/signal_handlers.py index 98685b46..4f374b9d 100644 --- a/src/program/signal_handlers.py +++ b/src/program/signal_handlers.py @@ -3,13 +3,21 @@ from django.core.exceptions import ValidationError def check_speaker_event_camp_consistency(sender, instance, **kwargs): if kwargs['action'] == 'pre_add': - # loop over speakers being added to this event - for pk in kwargs['pk_set']: - # check if this speaker belongs to a different event than the event does - from program.models import Speaker - speaker = Speaker.objects.get(id=pk) - if speaker.camp != instance.camp: - raise ValidationError({'speakers': 'The speaker (%s) belongs to a different camp (%s) than the event does (%s)' % (speaker, speaker.camp, instance.camp)}) + from program.models import Speaker, Event + if isinstance(instance, Event): + # loop over speakers being added to this event + for pk in kwargs['pk_set']: + # check if this speaker belongs to a different Camp than the event does + speaker = Speaker.objects.get(id=pk) + if speaker.camp != instance.camp: + raise ValidationError({'speakers': 'The speaker (%s) belongs to a different camp (%s) than the event does (%s)' % (speaker, speaker.camp, instance.camp)}) + elif isinstance(instance, Speaker): + # loop over events being added to this speaker + for pk in kwargs['pk_set']: + # check if this event belongs to a different Camp than the speaker does + event = Event.objects.get(id=pk) + if event.camp != instance.camp: + raise ValidationError({'events': 'The event (%s) belongs to a different camp (%s) than the event does (%s)' % (event, event.camp, instance.camp)}) def check_speaker_camp_change(sender, instance, **kwargs): diff --git a/src/teams/migrations/0016_auto_20170711_2247.py b/src/teams/migrations/0016_auto_20170711_2247.py new file mode 100644 index 00000000..6683edf3 --- /dev/null +++ b/src/teams/migrations/0016_auto_20170711_2247.py @@ -0,0 +1,21 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.10.5 on 2017-07-11 20:47 +from __future__ import unicode_literals + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('teams', '0015_team_mailing_list'), + ] + + operations = [ + migrations.AlterField( + model_name='team', + name='camp', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='teams', to='camps.Camp'), + ), + ] diff --git a/src/teams/models.py b/src/teams/models.py index 2f360db4..21016777 100644 --- a/src/teams/models.py +++ b/src/teams/models.py @@ -34,7 +34,7 @@ class Team(CampRelatedModel): name = models.CharField(max_length=255) slug = models.SlugField(max_length=255, blank=True) - camp = models.ForeignKey('camps.Camp') + camp = models.ForeignKey('camps.Camp', related_name="teams") area = models.ForeignKey( 'teams.TeamArea', related_name='teams', @@ -85,6 +85,14 @@ class Team(CampRelatedModel): else: return self.area.responsible.all() + @property + def anoncount(self): + return self.approvedmembers.filter(user__profile__public_credit_name_approved=False).count() + + @property + def approvedmembers(self): + return TeamMember.objects.filter(team=self, approved=True) + class TeamMember(CampRelatedModel): user = models.ForeignKey('auth.User') diff --git a/src/teams/templates/team_detail.html b/src/teams/templates/team_detail.html index 66025522..6fe34559 100644 --- a/src/teams/templates/team_detail.html +++ b/src/teams/templates/team_detail.html @@ -12,7 +12,7 @@ Team: {{ team.name }} | {{ block.super }}

{{ team.name }} Team ({{ team.area.name }} area)

{{ team.description|unsafecommonmark }} -

Currently {{ team.members.count }} people are members of this team{% if request.user in team.members.all %} (including you){% endif %}.

+

Currently {{ team.approvedmembers.count }} people are members of this team{% if request.user in team.members.all %} (including you){% endif %}.

{% if request.user in team.members.all %}

Your team status: {% membershipstatus request.user team %}

@@ -30,12 +30,35 @@ Team: {{ team.name }} | {{ block.super }} Manage Team {% endif %}
-

Team Responsible

-

The following people are responsible for the {{ team.name }} team:

-