add initial people stuff

This commit is contained in:
Thomas Steen Rasmussen 2017-07-11 22:50:31 +02:00
parent f4a2f6cea6
commit 533c1b3efa
27 changed files with 316 additions and 37 deletions

View file

@ -41,6 +41,7 @@ INSTALLED_APPS = [
'sponsors',
'ircbot',
'teams',
'people',
'allauth',
'allauth.account',

View file

@ -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(

View file

@ -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'},
),
]

View file

@ -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',

0
src/people/__init__.py Normal file
View file

3
src/people/admin.py Normal file
View file

@ -0,0 +1,3 @@
from django.contrib import admin
# Register your models here.

5
src/people/apps.py Normal file
View file

@ -0,0 +1,5 @@
from django.apps import AppConfig
class PeopleConfig(AppConfig):
name = 'people'

View file

3
src/people/models.py Normal file
View file

@ -0,0 +1,3 @@
from django.db import models
# Create your models here.

View file

@ -0,0 +1,54 @@
{% extends 'base.html' %}
{% load commonmark %}
{% load teams_tags %}
{% block title %}
People | {{ block.super }}
{% endblock %}
{% block content %}
<h3>The People of BornHack</h3>
<p class="lead">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 %}
<h3>{{ camp.title }} Teams</h3>
<table class="table table-hover">
<thead>
<tr>
<th>Name</th>
<th>Description</th>
<th>Members</th>
</tr>
</thead>
<tbody>
{% for team in camp.teams.all %}
<tr>
<td>
{{ team.name }} Team
</td>
<td>
{{ team.description|unsafecommonmark }}
</td>
<td>
{% 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 %}<br>
{% endif %}
{% endfor %}
{% if team.anoncount and team.anoncount != team.approvedmembers.count %}
plus {{ team.anoncount }} anonymous member(s).
{% endif %}
</td>
</tr>
{% endfor %}
</tbody>
</table>
{% endif %}
{% endfor %}
{% endblock %}

3
src/people/tests.py Normal file
View file

@ -0,0 +1,3 @@
from django.test import TestCase
# Create your tests here.

9
src/people/views.py Normal file
View file

@ -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

View file

@ -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)

View file

@ -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),
),
]

View file

@ -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),
]

View file

@ -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):

View file

@ -1,15 +1,22 @@
{% extends 'profile_base.html' %}
{% block profile_content %}
<p class="lead">The information in your profile is only visible to team responsible and organisers.</p>
<p class="lead">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.</p>
<hr>
<p class="lead">Apart from Public Credit Name the information in your profile is only visible to team responsible and organisers.</p>
<p class="lead">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.</p>
<h3>Your Profile</h3>
<dl>
<dt>Name</dt>
<dd>{{ profile.name|default:"N/A" }}</dd>
<dt>Description</dt>
<dd>{{ profile.description|default:"N/A" }}</dd>
</dl>
<table class="table table-horisontal">
<tr>
<td><b>Name (not visible to the public)</b></td>
<td>{{ profile.name|default:"N/A" }}</td>
</tr>
<tr>
<td><b>Description (not visible to the public)</b></td>
<td>{{ profile.description|default:"N/A" }}</td>
</tr>
<tr>
<td><b>Public Credit Name (visible to the public, leave empty if you want no credits)</b></td>
<td>{{ profile.public_credit_name|default:"N/A" }} {% if profile.public_credit_name %}({% if profile.public_credit_name_approved %}<span class="text-success">approved</span>{% else %}<span class="text-danger">pending approval</span>{% endif %}){% endif %}</td>
</tr>
</table>
<a href="{% url 'profiles:update' %}" class="btn btn-black"><i class="fa fa-edit"></i> Edit Profile</a>
{% endblock profile_content %}

View file

@ -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)

View file

@ -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 = [

View file

@ -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):

View file

@ -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'),
),
]

View file

@ -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')

View file

@ -12,7 +12,7 @@ Team: {{ team.name }} | {{ block.super }}
<h3>{{ team.name }} Team ({{ team.area.name }} area)</h3>
{{ team.description|unsafecommonmark }}
<p>Currently {{ team.members.count }} people are members of this team{% if request.user in team.members.all %} (including you){% endif %}.</p>
<p>Currently {{ team.approvedmembers.count }} people are members of this team{% if request.user in team.members.all %} (including you){% endif %}.</p>
{% if request.user in team.members.all %}
<p>Your team status: {% membershipstatus request.user team %}</p>
@ -30,12 +30,35 @@ Team: {{ team.name }} | {{ block.super }}
<a href="{% url 'team_manage' camp_slug=camp.slug slug=team.slug %}" class="btn btn-success">Manage Team</a>
{% endif %}
<hr />
<h3>Team Responsible</h3>
<p>The following people are responsible for the <b>{{ team.name }} team</b>:</p>
<ul>
{% for resp in team.responsible.all %}
<li><b>{{ resp.profile.name }}</b></li>
<h3>Team Members</h3>
<p>The following people are members of the <b>{{ team.name }} team</b>:</p>
<table class="table">
<thead>
<tr>
<th>
Name
</th>
<th>
Status
</th>
</tr>
</thead>
<tbody>
{% for teammember in team.approvedmembers.all %}
{% if teammember.user.profile.public_credit_name and teammember.approved or teammember.responsible %}
<tr>
<td>
{{ teammember.user.profile.public_credit_name }}
</td>
<td>
{% if teammember.responsible %}Team Responsible{% else %}Team Member{% endif %}
</td>
</tr>
{% endif %}
{% endfor %}
</ul>
</tbody>
</table>
{% if team.anoncount %}
<p>Plus <b>{{ team.anoncount }}</b> member(s) who prefer to remain anonymous.</p>
{% endif %}
{% endblock %}

View file

@ -47,7 +47,7 @@ Teams | {{ block.super }}
</td>
<td class="text-center">
<span class="badge">{{ team.members.count }}</span><br>
<span class="badge">{{ team.approvedmembers.count }}</span><br>
{% if team.needs_members %}(more needed){% endif %}
</td>

View file

@ -37,6 +37,9 @@ Manage Team: {{ team.name }} | {{ block.super }}
<th>
Description
</th>
<th>
Public Credit Name
</th>
<th>
Membership
</th>
@ -60,6 +63,9 @@ Manage Team: {{ team.name }} | {{ block.super }}
<td>
{{ membership.user.profile.description }}
</td>
<td>
{{ membership.user.profile.public_credit_name|default:"N/A" }}
</td>
<td>
{% if membership.approved %}member{% else %}pending{% endif %}
</td>

View file

@ -27,6 +27,7 @@ class EnsureTeamResponsibleMixin(SingleObjectMixin):
)
class TeamListView(CampViewMixin, ListView):
template_name = "team_list.html"
model = Team