From b29eaf5457b5ddc9ff541827e36af19bd44ef220 Mon Sep 17 00:00:00 2001 From: Michael Hall Date: Tue, 23 Jan 2018 23:15:14 -0500 Subject: [PATCH] Add ability to join a team as a member, migrate team owners to admin members, add Attendee objects --- events/admin.py | 6 ++-- events/migrations/0005_auto_20180108_2221.py | 8 +++++ events/migrations/0006_auto_20180124_0303.py | 36 +++++++++++++++++++ events/models/events.py | 27 +++++++++++++- events/models/profiles.py | 8 +++++ events/templates/events/member_list.html | 15 ++++++++ events/views.py | 29 ++++++++++++++- .../templates/get_together/edit_team.html | 2 +- .../templates/get_together/show_team.html | 14 ++++++++ .../templates/get_together/teams.html | 9 ++++- get_together/urls.py | 12 ++++--- get_together/views.py | 9 +++-- 12 files changed, 162 insertions(+), 13 deletions(-) create mode 100644 events/migrations/0006_auto_20180124_0303.py create mode 100644 events/templates/events/member_list.html diff --git a/events/admin.py b/events/admin.py index 2b345a0..4aa87f2 100644 --- a/events/admin.py +++ b/events/admin.py @@ -2,9 +2,9 @@ from django.contrib import admin # Register your models here. 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.events import Place, Event +from .models.events import Place, Event, Attendee admin.site.register(Language) admin.site.register(Continent) @@ -42,4 +42,6 @@ class EventAdmin(admin.ModelAdmin): raw_id_fields = ('place', 'created_by') admin.site.register(Event, EventAdmin) +admin.site.register(Member) + diff --git a/events/migrations/0005_auto_20180108_2221.py b/events/migrations/0005_auto_20180108_2221.py index 1043ec9..edec913 100644 --- a/events/migrations/0005_auto_20180108_2221.py +++ b/events/migrations/0005_auto_20180108_2221.py @@ -4,6 +4,13 @@ import datetime from django.db import migrations, models 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): @@ -27,4 +34,5 @@ class Migration(migrations.Migration): name='members', field=models.ManyToManyField(blank=True, related_name='memberships', through='events.Member', to='events.UserProfile'), ), + migrations.RunPython(make_owner_admin), ] diff --git a/events/migrations/0006_auto_20180124_0303.py b/events/migrations/0006_auto_20180124_0303.py new file mode 100644 index 0000000..b21e1f7 --- /dev/null +++ b/events/migrations/0006_auto_20180124_0303.py @@ -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'), + ), + ] diff --git a/events/models/events.py b/events/models/events.py index d10ae73..cc33d23 100644 --- a/events/models/events.py +++ b/events/models/events.py @@ -68,6 +68,8 @@ class Event(models.Model): #image #replies + attendees = models.ManyToManyField(UserProfile, through='Attendee', related_name="attending", blank=True) + def get_absolute_url(self): 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.latitude = event.place.latitude else: - searchable.location_name = "" + searchable.location_name = team.location_name searchable.longitude = None searchable.latitude = None searchable.save() @@ -124,3 +126,26 @@ def slugify(s, ok=SLUG_OK, lower=True, spaces=False): if not spaces: new = re.sub('[-\s]+', '-', 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) diff --git a/events/models/profiles.py b/events/models/profiles.py index 80a8dd9..362085d 100644 --- a/events/models/profiles.py +++ b/events/models/profiles.py @@ -175,3 +175,11 @@ class Member(models.Model): user = models.ForeignKey(UserProfile, on_delete=models.CASCADE) role = models.SmallIntegerField(_("Member Role"), choices=ROLES, default=NORMAL, db_index=True) 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) diff --git a/events/templates/events/member_list.html b/events/templates/events/member_list.html new file mode 100644 index 0000000..d7a3ae3 --- /dev/null +++ b/events/templates/events/member_list.html @@ -0,0 +1,15 @@ + +{% if member_list %} + + {% for member in member_list %} + + + + + + {% endfor %} +
{{ member.user }}{{ member.joined_date }}{% if member.role > 0 %}{{ member.role_name }}{% endif %}
+{% else %} +

No members yet.

+{% endif %} + diff --git a/events/views.py b/events/views.py index fc5dc00..1eb83a0 100644 --- a/events/views.py +++ b/events/views.py @@ -1,5 +1,7 @@ -from django.shortcuts import render +from django.shortcuts import render, redirect 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.response import Response @@ -7,6 +9,7 @@ from rest_framework.response import Response from .models.search import Searchable, SearchableSerializer from .models.events import Event, Place, PlaceSerializer from .models.locale import Country ,CountrySerializer, SPR, SPRSerializer, City, CitySerializer +from .models.profiles import Team, UserProfile, Member import simplejson @@ -71,3 +74,27 @@ def city_list(request, *args, **kwargs): serializer = CitySerializer(cities, many=True) 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) + diff --git a/get_together/templates/get_together/edit_team.html b/get_together/templates/get_together/edit_team.html index 1fd907f..9ef9c65 100644 --- a/get_together/templates/get_together/edit_team.html +++ b/get_together/templates/get_together/edit_team.html @@ -2,7 +2,7 @@ {% block content %}

Update {{team.name}}

-
+ {% csrf_token %} {% include "events/team_form.html" %}
diff --git a/get_together/templates/get_together/show_team.html b/get_together/templates/get_together/show_team.html index 35a8261..502357f 100644 --- a/get_together/templates/get_together/show_team.html +++ b/get_together/templates/get_together/show_team.html @@ -3,6 +3,10 @@ {% block content %}

Welcome to {{ team.name }}

+

Members

+{% include "events/member_list.html" %} +
+

Upcoming Events

{% include "events/event_list.html" %} {% if can_create_event %} @@ -17,5 +21,15 @@
{% endif %} + +{% if is_member %} +
+ +
+{% else %} +
+ +
+{% endif %} {% endblock %} diff --git a/get_together/templates/get_together/teams.html b/get_together/templates/get_together/teams.html index fbc8da1..6cc77d5 100644 --- a/get_together/templates/get_together/teams.html +++ b/get_together/templates/get_together/teams.html @@ -1,7 +1,14 @@ {% extends "get_together/base.html" %} {% block content %} -{% include "events/team_list.html" %} +{% if my_teams %} +

My Teams

+{% include "events/team_list.html" with teams_list=my_teams %} +
+{% endif %} + +

All Teams

+{% include "events/team_list.html" with teams_list=all_teams%} {% if request.user.is_authenticated %}
diff --git a/get_together/urls.py b/get_together/urls.py index 25dceca..71a5444 100644 --- a/get_together/urls.py +++ b/get_together/urls.py @@ -32,16 +32,18 @@ urlpatterns = [ path('api/cities/', event_views.city_list), 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('team//', views.show_team, name='show-team'), - path('team//edit/', views.edit_team, name='edit-team'), - path('team//create-event/', views.create_event, name='create-event'), - path('events//edit/', views.edit_event, name='edit-event'), + path('team//+edit/', views.edit_team, name='edit-team'), + path('team//+join/', event_views.join_team, name='join-team'), + path('team//+leave/', event_views.leave_team, name='leave-team'), + path('team//+create-event/', views.create_event, name='create-event'), + path('events//+edit/', views.edit_event, name='edit-event'), path('events///', views.show_event, name='show-event'), 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')), ] diff --git a/get_together/views.py b/get_together/views.py index 8d88e1e..d91a421 100644 --- a/get_together/views.py +++ b/get_together/views.py @@ -4,7 +4,7 @@ from django.contrib import messages from django.shortcuts import render, redirect 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.models.events import Event, Place @@ -45,6 +45,7 @@ def create_team(request, *args, **kwargs): new_team = form.save() new_team.owner_profile = request.user.profile 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) else: context = { @@ -87,8 +88,10 @@ def edit_team(request, team_id): def teams_list(request, *args, **kwargs): teams = Team.objects.all() 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) @@ -98,6 +101,8 @@ def show_team(request, team_id, *args, **kwargs): context = { 'team': team, '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_edit_team': request.user.profile.can_edit_team(team), }