Add ability to join a team as a member, migrate team owners to admin members, add Attendee objects
This commit is contained in:
parent
386d757fe9
commit
b29eaf5457
12 changed files with 162 additions and 13 deletions
|
@ -2,9 +2,9 @@ from django.contrib import admin
|
||||||
|
|
||||||
# Register your models here.
|
# Register your models here.
|
||||||
from .models.locale import Language, Continent, Country, SPR, City
|
from .models.locale import Language, Continent, Country, SPR, City
|
||||||
from .models.profiles import UserProfile, Organization, Team
|
from .models.profiles import UserProfile, Organization, Team, Member
|
||||||
from .models.search import Searchable
|
from .models.search import Searchable
|
||||||
from .models.events import Place, Event
|
from .models.events import Place, Event, Attendee
|
||||||
|
|
||||||
admin.site.register(Language)
|
admin.site.register(Language)
|
||||||
admin.site.register(Continent)
|
admin.site.register(Continent)
|
||||||
|
@ -42,4 +42,6 @@ class EventAdmin(admin.ModelAdmin):
|
||||||
raw_id_fields = ('place', 'created_by')
|
raw_id_fields = ('place', 'created_by')
|
||||||
admin.site.register(Event, EventAdmin)
|
admin.site.register(Event, EventAdmin)
|
||||||
|
|
||||||
|
admin.site.register(Member)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -4,6 +4,13 @@ import datetime
|
||||||
from django.db import migrations, models
|
from django.db import migrations, models
|
||||||
import django.db.models.deletion
|
import django.db.models.deletion
|
||||||
|
|
||||||
|
ADMIN=2
|
||||||
|
|
||||||
|
def make_owner_admin(apps, schema_editor):
|
||||||
|
Team = apps.get_model('events', 'Team')
|
||||||
|
Member = apps.get_model('events', 'Member')
|
||||||
|
for team in Team.objects.all():
|
||||||
|
Member.objects.get_or_create(team=team, user=team.owner_profile, role=ADMIN, joined_date=team.created_date or datetime.datetime.now())
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
@ -27,4 +34,5 @@ class Migration(migrations.Migration):
|
||||||
name='members',
|
name='members',
|
||||||
field=models.ManyToManyField(blank=True, related_name='memberships', through='events.Member', to='events.UserProfile'),
|
field=models.ManyToManyField(blank=True, related_name='memberships', through='events.Member', to='events.UserProfile'),
|
||||||
),
|
),
|
||||||
|
migrations.RunPython(make_owner_admin),
|
||||||
]
|
]
|
||||||
|
|
36
events/migrations/0006_auto_20180124_0303.py
Normal file
36
events/migrations/0006_auto_20180124_0303.py
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
# Generated by Django 2.0 on 2018-01-24 03:03
|
||||||
|
|
||||||
|
import datetime
|
||||||
|
from django.db import migrations, models
|
||||||
|
import django.db.models.deletion
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('events', '0005_auto_20180108_2221'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='Attendee',
|
||||||
|
fields=[
|
||||||
|
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('role', models.SmallIntegerField(choices=[(0, 'Normal'), (1, 'Crew'), (2, 'Host')], db_index=True, default=0, verbose_name='Role')),
|
||||||
|
('status', models.SmallIntegerField(choices=[(-1, 'No'), (0, 'Maybe'), (1, 'Yes')], db_index=True, verbose_name='Attending?')),
|
||||||
|
('joined_date', models.DateTimeField(default=datetime.datetime.now)),
|
||||||
|
('event', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='events.Event')),
|
||||||
|
('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='events.UserProfile')),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='team',
|
||||||
|
name='name',
|
||||||
|
field=models.CharField(max_length=256, verbose_name='Team Name'),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='event',
|
||||||
|
name='attendees',
|
||||||
|
field=models.ManyToManyField(blank=True, related_name='attending', through='events.Attendee', to='events.UserProfile'),
|
||||||
|
),
|
||||||
|
]
|
|
@ -68,6 +68,8 @@ class Event(models.Model):
|
||||||
#image
|
#image
|
||||||
#replies
|
#replies
|
||||||
|
|
||||||
|
attendees = models.ManyToManyField(UserProfile, through='Attendee', related_name="attending", blank=True)
|
||||||
|
|
||||||
def get_absolute_url(self):
|
def get_absolute_url(self):
|
||||||
return reverse('show-event', kwargs={'event_id': self.id, 'event_slug': self.slug})
|
return reverse('show-event', kwargs={'event_id': self.id, 'event_slug': self.slug})
|
||||||
|
|
||||||
|
@ -105,7 +107,7 @@ def update_event_searchable(event):
|
||||||
searchable.longitude = event.place.longitude or None
|
searchable.longitude = event.place.longitude or None
|
||||||
searchable.latitude = event.place.latitude
|
searchable.latitude = event.place.latitude
|
||||||
else:
|
else:
|
||||||
searchable.location_name = ""
|
searchable.location_name = team.location_name
|
||||||
searchable.longitude = None
|
searchable.longitude = None
|
||||||
searchable.latitude = None
|
searchable.latitude = None
|
||||||
searchable.save()
|
searchable.save()
|
||||||
|
@ -124,3 +126,26 @@ def slugify(s, ok=SLUG_OK, lower=True, spaces=False):
|
||||||
if not spaces:
|
if not spaces:
|
||||||
new = re.sub('[-\s]+', '-', new)
|
new = re.sub('[-\s]+', '-', new)
|
||||||
return new.lower() if lower else new
|
return new.lower() if lower else new
|
||||||
|
|
||||||
|
class Attendee(models.Model):
|
||||||
|
NORMAL=0
|
||||||
|
CREW=1
|
||||||
|
HOST=2
|
||||||
|
ROLES = [
|
||||||
|
(NORMAL, _("Normal")),
|
||||||
|
(CREW, _("Crew")),
|
||||||
|
(HOST, _("Host"))
|
||||||
|
]
|
||||||
|
NO=-1
|
||||||
|
MAYBE=0
|
||||||
|
YES=1
|
||||||
|
STATUSES = [
|
||||||
|
(NO, _("No")),
|
||||||
|
(MAYBE, _("Maybe")),
|
||||||
|
(YES, _("Yes")),
|
||||||
|
]
|
||||||
|
event = models.ForeignKey(Event, on_delete=models.CASCADE)
|
||||||
|
user = models.ForeignKey(UserProfile, on_delete=models.CASCADE)
|
||||||
|
role = models.SmallIntegerField(_("Role"), choices=ROLES, default=NORMAL, db_index=True)
|
||||||
|
status = models.SmallIntegerField(_("Attending?"), choices=STATUSES, db_index=True)
|
||||||
|
joined_date = models.DateTimeField(default=datetime.datetime.now)
|
||||||
|
|
|
@ -175,3 +175,11 @@ class Member(models.Model):
|
||||||
user = models.ForeignKey(UserProfile, on_delete=models.CASCADE)
|
user = models.ForeignKey(UserProfile, on_delete=models.CASCADE)
|
||||||
role = models.SmallIntegerField(_("Member Role"), choices=ROLES, default=NORMAL, db_index=True)
|
role = models.SmallIntegerField(_("Member Role"), choices=ROLES, default=NORMAL, db_index=True)
|
||||||
joined_date = models.DateTimeField(default=datetime.datetime.now)
|
joined_date = models.DateTimeField(default=datetime.datetime.now)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def role_name(self):
|
||||||
|
print("Role name: %s=%s" % (self.role, Member.ROLES[self.role]))
|
||||||
|
return Member.ROLES[self.role][1]
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return '%s in %s' % (self.user, self.team)
|
||||||
|
|
15
events/templates/events/member_list.html
Normal file
15
events/templates/events/member_list.html
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
|
||||||
|
{% if member_list %}
|
||||||
|
<table border="0" width="960px">
|
||||||
|
{% for member in member_list %}
|
||||||
|
<tr>
|
||||||
|
<td>{{ member.user }}</td>
|
||||||
|
<td>{{ member.joined_date }}</td>
|
||||||
|
<td>{% if member.role > 0 %}{{ member.role_name }}{% endif %}</td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</table>
|
||||||
|
{% else %}
|
||||||
|
<p>No members yet.</p>
|
||||||
|
{% endif %}
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
from django.shortcuts import render
|
from django.shortcuts import render, redirect
|
||||||
from django.http import HttpResponse, JsonResponse
|
from django.http import HttpResponse, JsonResponse
|
||||||
|
from django.contrib import messages
|
||||||
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
from rest_framework.decorators import api_view, throttle_classes
|
from rest_framework.decorators import api_view, throttle_classes
|
||||||
from rest_framework.response import Response
|
from rest_framework.response import Response
|
||||||
|
@ -7,6 +9,7 @@ from rest_framework.response import Response
|
||||||
from .models.search import Searchable, SearchableSerializer
|
from .models.search import Searchable, SearchableSerializer
|
||||||
from .models.events import Event, Place, PlaceSerializer
|
from .models.events import Event, Place, PlaceSerializer
|
||||||
from .models.locale import Country ,CountrySerializer, SPR, SPRSerializer, City, CitySerializer
|
from .models.locale import Country ,CountrySerializer, SPR, SPRSerializer, City, CitySerializer
|
||||||
|
from .models.profiles import Team, UserProfile, Member
|
||||||
|
|
||||||
import simplejson
|
import simplejson
|
||||||
|
|
||||||
|
@ -71,3 +74,27 @@ def city_list(request, *args, **kwargs):
|
||||||
serializer = CitySerializer(cities, many=True)
|
serializer = CitySerializer(cities, many=True)
|
||||||
return Response(serializer.data)
|
return Response(serializer.data)
|
||||||
|
|
||||||
|
def join_team(request, team_id):
|
||||||
|
if request.user.is_anonymous:
|
||||||
|
messages.add_message(request, messages.WARNING, message=_('You must be logged in to join a team.'))
|
||||||
|
return redirect('show-team', team_id=team_id)
|
||||||
|
team = Team.objects.get(id=team_id)
|
||||||
|
if request.user.profile in team.members.all():
|
||||||
|
messages.add_message(request, messages.INFO, message=_('You are already a member of this team.'))
|
||||||
|
return redirect('show-team', team_id=team_id)
|
||||||
|
new_member = Member.objects.create(team=team, user=request.user.profile, role=Member.NORMAL)
|
||||||
|
messages.add_message(request, messages.SUCCESS, message=_('Welcome to the team!'))
|
||||||
|
return redirect('show-team', team_id=team_id)
|
||||||
|
|
||||||
|
def leave_team(request, team_id):
|
||||||
|
if request.user.is_anonymous:
|
||||||
|
messages.add_message(request, messages.WARNING, message=_('You must be logged in to leave a team.'))
|
||||||
|
return redirect('show-team', team_id=team_id)
|
||||||
|
team = Team.objects.get(id=team_id)
|
||||||
|
if request.user.profile not in team.members.all():
|
||||||
|
messages.add_message(request, messages.INFO, message=_('You are not a member of this team.'))
|
||||||
|
return redirect('show-team', team_id=team_id)
|
||||||
|
Member.objects.filter(team=team, user=request.user.profile).delete()
|
||||||
|
messages.add_message(request, messages.SUCCESS, message=_('You are no longer on this team.'))
|
||||||
|
return redirect('show-team', team_id=team_id)
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<h2>Update {{team.name}}</h2>
|
<h2>Update {{team.name}}</h2>
|
||||||
<form action="{% url "create-team" %}" method="post">
|
<form action="{% url "edit-team" team.id %}" method="post">
|
||||||
{% csrf_token %}
|
{% csrf_token %}
|
||||||
{% include "events/team_form.html" %}
|
{% include "events/team_form.html" %}
|
||||||
<br />
|
<br />
|
||||||
|
|
|
@ -3,6 +3,10 @@
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<h2>Welcome to {{ team.name }}</h2>
|
<h2>Welcome to {{ team.name }}</h2>
|
||||||
|
|
||||||
|
<h4>Members</h4>
|
||||||
|
{% include "events/member_list.html" %}
|
||||||
|
<br/>
|
||||||
|
|
||||||
<h4>Upcoming Events</h4>
|
<h4>Upcoming Events</h4>
|
||||||
{% include "events/event_list.html" %}
|
{% include "events/event_list.html" %}
|
||||||
{% if can_create_event %}
|
{% if can_create_event %}
|
||||||
|
@ -17,5 +21,15 @@
|
||||||
<button type="submit" class="btn btn-secondary">Edit Team</button>
|
<button type="submit" class="btn btn-secondary">Edit Team</button>
|
||||||
</form>
|
</form>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
|
{% if is_member %}
|
||||||
|
<form action="{% url 'leave-team' team.id %}" method="get">
|
||||||
|
<button type="submit" class="btn btn-danger">Leave Team</button>
|
||||||
|
</form>
|
||||||
|
{% else %}
|
||||||
|
<form action="{% url 'join-team' team.id %}" method="get">
|
||||||
|
<button type="submit" class="btn btn-success">Join Team</button>
|
||||||
|
</form>
|
||||||
|
{% endif %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,14 @@
|
||||||
{% extends "get_together/base.html" %}
|
{% extends "get_together/base.html" %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
{% include "events/team_list.html" %}
|
{% if my_teams %}
|
||||||
|
<h4>My Teams</h4>
|
||||||
|
{% include "events/team_list.html" with teams_list=my_teams %}
|
||||||
|
<br/>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
<h4>All Teams</h4>
|
||||||
|
{% include "events/team_list.html" with teams_list=all_teams%}
|
||||||
|
|
||||||
{% if request.user.is_authenticated %}
|
{% if request.user.is_authenticated %}
|
||||||
<br/>
|
<br/>
|
||||||
|
|
|
@ -32,16 +32,18 @@ urlpatterns = [
|
||||||
path('api/cities/', event_views.city_list),
|
path('api/cities/', event_views.city_list),
|
||||||
|
|
||||||
path('events/', views.events_list, name='events'),
|
path('events/', views.events_list, name='events'),
|
||||||
path('create-team/', views.create_team, name='create-team'),
|
path('+create-team/', views.create_team, name='create-team'),
|
||||||
path('teams/', views.teams_list, name='teams'),
|
path('teams/', views.teams_list, name='teams'),
|
||||||
path('team/<int:team_id>/', views.show_team, name='show-team'),
|
path('team/<int:team_id>/', views.show_team, name='show-team'),
|
||||||
path('team/<int:team_id>/edit/', views.edit_team, name='edit-team'),
|
path('team/<int:team_id>/+edit/', views.edit_team, name='edit-team'),
|
||||||
path('team/<int:team_id>/create-event/', views.create_event, name='create-event'),
|
path('team/<int:team_id>/+join/', event_views.join_team, name='join-team'),
|
||||||
path('events/<int:event_id>/edit/', views.edit_event, name='edit-event'),
|
path('team/<int:team_id>/+leave/', event_views.leave_team, name='leave-team'),
|
||||||
|
path('team/<int:team_id>/+create-event/', views.create_event, name='create-event'),
|
||||||
|
path('events/<int:event_id>/+edit/', views.edit_event, name='edit-event'),
|
||||||
path('events/<int:event_id>/<str:event_slug>/', views.show_event, name='show-event'),
|
path('events/<int:event_id>/<str:event_slug>/', views.show_event, name='show-event'),
|
||||||
|
|
||||||
path('places/', views.places_list, name='places'),
|
path('places/', views.places_list, name='places'),
|
||||||
path('create-place/', views.create_place, name='create-place'),
|
path('+create-place/', views.create_place, name='create-place'),
|
||||||
|
|
||||||
path('oauth/', include('social_django.urls', namespace='social')),
|
path('oauth/', include('social_django.urls', namespace='social')),
|
||||||
]
|
]
|
||||||
|
|
|
@ -4,7 +4,7 @@ from django.contrib import messages
|
||||||
from django.shortcuts import render, redirect
|
from django.shortcuts import render, redirect
|
||||||
from django.http import HttpResponse, JsonResponse
|
from django.http import HttpResponse, JsonResponse
|
||||||
|
|
||||||
from events.models.profiles import Team
|
from events.models.profiles import Team, UserProfile, Member
|
||||||
from events.forms import TeamForm, NewTeamForm, TeamEventForm, NewTeamEventForm, NewPlaceForm
|
from events.forms import TeamForm, NewTeamForm, TeamEventForm, NewTeamEventForm, NewPlaceForm
|
||||||
|
|
||||||
from events.models.events import Event, Place
|
from events.models.events import Event, Place
|
||||||
|
@ -45,6 +45,7 @@ def create_team(request, *args, **kwargs):
|
||||||
new_team = form.save()
|
new_team = form.save()
|
||||||
new_team.owner_profile = request.user.profile
|
new_team.owner_profile = request.user.profile
|
||||||
new_team.save()
|
new_team.save()
|
||||||
|
Member.objects.create(team=new_team, user=request.user.profile, role=Member.ADMIN)
|
||||||
return redirect('show-team', team_id=new_team.pk)
|
return redirect('show-team', team_id=new_team.pk)
|
||||||
else:
|
else:
|
||||||
context = {
|
context = {
|
||||||
|
@ -87,8 +88,10 @@ def edit_team(request, team_id):
|
||||||
def teams_list(request, *args, **kwargs):
|
def teams_list(request, *args, **kwargs):
|
||||||
teams = Team.objects.all()
|
teams = Team.objects.all()
|
||||||
context = {
|
context = {
|
||||||
'teams_list': teams,
|
'all_teams': teams,
|
||||||
}
|
}
|
||||||
|
if request.user.is_authenticated:
|
||||||
|
context['my_teams'] = request.user.profile.memberships.all()
|
||||||
return render(request, 'get_together/teams.html', context)
|
return render(request, 'get_together/teams.html', context)
|
||||||
|
|
||||||
|
|
||||||
|
@ -98,6 +101,8 @@ def show_team(request, team_id, *args, **kwargs):
|
||||||
context = {
|
context = {
|
||||||
'team': team,
|
'team': team,
|
||||||
'events_list': team_events,
|
'events_list': team_events,
|
||||||
|
'is_member': request.user.profile in team.members.all(),
|
||||||
|
'member_list': Member.objects.filter(team=team),
|
||||||
'can_create_event': request.user.profile.can_create_event(team),
|
'can_create_event': request.user.profile.can_create_event(team),
|
||||||
'can_edit_team': request.user.profile.can_edit_team(team),
|
'can_edit_team': request.user.profile.can_edit_team(team),
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue