From c4408d9a169828b97a35a24ca1b7d303699fb779 Mon Sep 17 00:00:00 2001 From: Michael Hall Date: Sun, 20 May 2018 12:35:52 -0400 Subject: [PATCH] Add Sponsor information for events. Save an event's sponsor to it's team so it can be selected for a future event. Fixes #70 --- events/admin.py | 9 +- events/forms.py | 7 +- events/migrations/0031_add_sponsors.py | 34 +++++ events/models/events.py | 2 + events/models/profiles.py | 29 ++++- events/views.py | 17 ++- .../events/manage_event_sponsors.html | 116 ++++++++++++++++++ .../get_together/events/show_event.html | 14 +++ get_together/urls.py | 2 + get_together/views/events.py | 67 +++++++++- 10 files changed, 290 insertions(+), 7 deletions(-) create mode 100644 events/migrations/0031_add_sponsors.py create mode 100644 get_together/templates/get_together/events/manage_event_sponsors.html diff --git a/events/admin.py b/events/admin.py index 0cbe79e..fd770e0 100644 --- a/events/admin.py +++ b/events/admin.py @@ -10,6 +10,7 @@ from .models.profiles import ( Member, Category, Topic, + Sponsor, ) from .models.search import Searchable from .models.events import ( @@ -54,8 +55,12 @@ class OrgAdmin(admin.ModelAdmin): list_display = ('name', 'site') admin.site.register(Organization, OrgAdmin) +class SponsorAdmin(admin.ModelAdmin): + list_display = ('name', 'web_url') +admin.site.register(Sponsor, SponsorAdmin) + class TeamAdmin(admin.ModelAdmin): - raw_id_fields = ('country', 'spr', 'city', 'owner_profile', 'admin_profiles', 'contact_profiles') + raw_id_fields = ('country', 'spr', 'city', 'owner_profile', 'admin_profiles', 'contact_profiles', 'sponsors') list_display = ('__str__', 'active', 'member_count', 'event_count', 'owner_profile', 'created_date', 'is_premium', 'premium_expires') list_filter = ('active', 'is_premium', 'organization', ('country',admin.RelatedOnlyFieldListFilter)) ordering = ('-created_date',) @@ -78,7 +83,7 @@ class PlaceAdmin(admin.ModelAdmin): admin.site.register(Place, PlaceAdmin) class EventAdmin(admin.ModelAdmin): - raw_id_fields = ('place', 'created_by') + raw_id_fields = ('place', 'created_by', 'sponsors') list_display = ('__str__', 'attendee_count', 'start_time', 'created_by', 'created_time') ordering = ('-start_time',) def attendee_count(self, event): diff --git a/events/forms.py b/events/forms.py index 9fb7def..caba46e 100644 --- a/events/forms.py +++ b/events/forms.py @@ -7,7 +7,7 @@ from django.utils import timezone from django.contrib.auth.models import User from .models.locale import Country, SPR, City -from .models.profiles import Team, UserProfile +from .models.profiles import Team, UserProfile, Sponsor from .models.events import ( Event, EventComment, @@ -291,6 +291,11 @@ class EventCommentForm(forms.ModelForm): model = EventComment fields = ['body'] +class SponsorForm(forms.ModelForm): + class Meta: + model = Sponsor + fields = ['name', 'web_url', 'logo'] + class NewPlaceForm(forms.ModelForm): class Meta: model = Place diff --git a/events/migrations/0031_add_sponsors.py b/events/migrations/0031_add_sponsors.py new file mode 100644 index 0000000..77aa341 --- /dev/null +++ b/events/migrations/0031_add_sponsors.py @@ -0,0 +1,34 @@ +# Generated by Django 2.0 on 2018-05-19 02:10 + +from django.db import migrations, models +import imagekit.models.fields + + +class Migration(migrations.Migration): + + dependencies = [ + ('events', '0030_add_attendee_actual_status'), + ] + + operations = [ + migrations.CreateModel( + name='Sponsor', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(max_length=256, verbose_name='Sponsor Name')), + ('description', models.TextField(blank=True, null=True)), + ('web_url', models.URLField(blank=True, null=True, verbose_name='Website')), + ('logo', imagekit.models.fields.ProcessedImageField(blank=True, help_text='Will be scaled and cropped to max 250x200 px.', upload_to='sponsors', verbose_name='Sponsor Logo')), + ], + ), + migrations.AddField( + model_name='event', + name='sponsors', + field=models.ManyToManyField(related_name='events', to='events.Sponsor'), + ), + migrations.AddField( + model_name='team', + name='sponsors', + field=models.ManyToManyField(related_name='teams', to='events.Sponsor'), + ), + ] diff --git a/events/models/events.py b/events/models/events.py index 3f2ff13..ae2a231 100644 --- a/events/models/events.py +++ b/events/models/events.py @@ -77,6 +77,8 @@ class Event(models.Model): attendees = models.ManyToManyField(UserProfile, through='Attendee', related_name="attending", blank=True) + sponsors = models.ManyToManyField('Sponsor', related_name='events') + @property def is_over(self): return self.end_time <= timezone.now() diff --git a/events/models/profiles.py b/events/models/profiles.py index 7751fbf..9cdefdc 100644 --- a/events/models/profiles.py +++ b/events/models/profiles.py @@ -6,7 +6,9 @@ from django.utils import timezone from django.conf import settings from imagekit.models import ProcessedImageField -from imagekit.processors import ResizeToFill +from imagekit.processors import ResizeToFill, ResizeToFit + +from rest_framework import serializers from .locale import * from .. import location @@ -195,6 +197,29 @@ class Organization(models.Model): def __str__(self): return u'%s' % (self.name) +class Sponsor(models.Model): + name = models.CharField(_("Sponsor Name"), max_length=256, null=False, blank=False) + description = models.TextField(blank=True, null=True) + web_url = models.URLField(_("Website"), null=True, blank=True) + logo = ProcessedImageField(verbose_name=_("Logo"), help_text=_("Will be scaled and cropped to max 250x200 px."), + upload_to='sponsors', + processors=[ResizeToFit(250, 200)], + format='PNG', + blank=True) + def __str__(self): + return self.name + +class SponsorSerializer(serializers.ModelSerializer): + display = serializers.CharField(source='__str__', read_only=True) + class Meta: + model = Sponsor + fields = ( + 'id', + 'name', + 'logo', + 'web_url', + ) + class Team(models.Model): name = models.CharField(_("Team Name"), max_length=256, null=False, blank=False) organization = models.ForeignKey(Organization, related_name='teams', null=True, blank=True, on_delete=models.CASCADE) @@ -224,6 +249,8 @@ class Team(models.Model): category = models.ForeignKey('Category', on_delete=models.SET_NULL, blank=False, null=True) topics = models.ManyToManyField('Topic', blank=True) + sponsors = models.ManyToManyField('Sponsor', related_name='teams') + is_premium = models.BooleanField(default=settings.EVENTS_TEAMS_DEFAULT_PREMIUM) premium_by = models.ForeignKey(UserProfile, related_name='premium_teams', null=True, on_delete=models.SET_NULL) premium_started = models.DateTimeField(blank=True, null=True) diff --git a/events/views.py b/events/views.py index 4e0995a..b3e2cee 100644 --- a/events/views.py +++ b/events/views.py @@ -9,7 +9,7 @@ from rest_framework.response import Response from .models.search import Searchable, SearchableSerializer from .models.events import Event, EventComment, Place, PlaceSerializer, Attendee from .models.locale import Country ,CountrySerializer, SPR, SPRSerializer, City, CitySerializer -from .models.profiles import Team, UserProfile, Member +from .models.profiles import Team, UserProfile, Member, Sponsor, SponsorSerializer from .forms import EventCommentForm import simplejson @@ -78,7 +78,7 @@ def city_list(request, *args, **kwargs): @api_view(['GET']) def find_city(request): cities = City.objects.all() - if "city" in request.GET: + if "name" in request.GET: cities = cities.filter(name=request.GET.get("city")) if "spr" in request.GET: cities = cities.filter(spr__name=request.GET.get("spr")) @@ -91,6 +91,19 @@ def find_city(request): except: return Response({}) + +@api_view(['GET']) +def sponsor_list(request): + if "q" in request.GET: + match = request.GET.get("q", "") + sponsors = Sponsor.objects.filter(name__icontains=match) + else: + sponsors = Sponsor.objects.all() + + serializer = SponsorSerializer(sponsors[:50], 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.')) diff --git a/get_together/templates/get_together/events/manage_event_sponsors.html b/get_together/templates/get_together/events/manage_event_sponsors.html new file mode 100644 index 0000000..105f7be --- /dev/null +++ b/get_together/templates/get_together/events/manage_event_sponsors.html @@ -0,0 +1,116 @@ +{% extends "get_together/base.html" %} +{% load static markup tz %} + +{% block add_to_title %} | {{event.name}}{% endblock %} + +{% block styles %} + + +{% endblock %} + +{% block content %} +
+
+
+

Sponsors for {{ event.name }} +

+
+ {% comment %} +

+ Lookup sponsors: +


+

+ {% endcomment %} +
+
+ {% for sponsor in sponsors %} +
+
+
+ + + +
+
+
+ {{sponsor.name}}
+
+ {% if sponsor in event.sponsors.all %} + + {% else %} + + {% endif %} +
+
+
+
+
+ {% endfor %} +
+
+
+ +
+
+
+
+

New Sponsor


+
+
+
+
+ {% csrf_token %} + {{sponsor_form.as_p}} + +
+
+
+
+
+ +
+
+{% endblock %} + +{% block javascript %} + +{% endblock %} \ No newline at end of file diff --git a/get_together/templates/get_together/events/show_event.html b/get_together/templates/get_together/events/show_event.html index 62c7e1e..8353f01 100644 --- a/get_together/templates/get_together/events/show_event.html +++ b/get_together/templates/get_together/events/show_event.html @@ -135,6 +135,7 @@ @@ -244,6 +245,19 @@
+ {% if sponsor_count > 0 %} +
+

Sponsors


+
+ {% for sponsor in sponsor_list %} +
+
+ {{sponsor.name}} Logo +
+
+ {% endfor %} +
+ {% endif %}

Attendees ({{attendee_count}})


diff --git a/get_together/urls.py b/get_together/urls.py index 1ddb6a3..009950a 100644 --- a/get_together/urls.py +++ b/get_together/urls.py @@ -81,6 +81,8 @@ urlpatterns = [ path('events//+attend/', views.attend_event, name='attend-event'), path('events//+attended/', views.attended_event, name='attended-event'), path('events//+attendees/', views.manage_attendees, name='manage-attendees'), + path('events//+sponsors/', views.manage_event_sponsors, name='manage-event-sponsors'), + path('events//+sponsor/', views.sponsor_event, name='sponsor-event'), path('events//+invite/', views.invite_attendees, name='invite-attendees'), path('events//+delete/', views.delete_event, name='delete-event'), path('events//+add_place/', views.add_place_to_event, name='add-place'), diff --git a/get_together/views/events.py b/get_together/views/events.py index f912a1b..35be0dc 100644 --- a/get_together/views/events.py +++ b/get_together/views/events.py @@ -1,4 +1,5 @@ from django.utils.translation import ugettext_lazy as _ +from django.utils.safestring import mark_safe from django.contrib import messages from django.contrib.auth.models import User @@ -6,6 +7,7 @@ from django.contrib.auth.decorators import login_required from django.shortcuts import render, redirect, reverse, get_object_or_404 from django.contrib.sites.models import Site from django.utils import timezone +from django.utils.datastructures import OrderedSet from django.core.mail import send_mail from django.template.loader import get_template, render_to_string from django.conf import settings @@ -23,7 +25,7 @@ from events.models.events import ( delete_event_searchable, ) from events.models.speakers import Speaker, Talk, SpeakerRequest, Presentation -from events.models.profiles import Team, Organization, UserProfile, Member +from events.models.profiles import Team, Organization, UserProfile, Member, Sponsor from events.forms import ( TeamEventForm, NewTeamEventForm, @@ -37,6 +39,7 @@ from events.forms import ( EventInviteEmailForm, EventInviteMemberForm, EventContactForm, + SponsorForm, ) from events import location @@ -73,6 +76,8 @@ def show_event(request, event_id, event_slug): 'team': event.team, 'event': event, 'comment_form': comment_form, + 'sponsor_count': event.sponsors.count(), + 'sponsor_list': event.sponsors.all(), 'is_attending': request.user.profile in event.attendees.all(), 'attendee_list': Attendee.objects.filter(event=event).order_by('-status'), 'attendee_count': Attendee.objects.filter(event=event, status=Attendee.YES).count(), @@ -152,6 +157,66 @@ def create_event(request, team_id): return redirect('home') +@login_required +def manage_event_sponsors(request, event_id): + event = get_object_or_404(Event, id=event_id) + if not request.user.profile.can_edit_event(event): + messages.add_message(request, messages.WARNING, message=_('You can not manage this event\'s sponsorss.')) + return redirect(event.get_absolute_url()) + if not event.team.is_premium: + messages.add_message(request, messages.ERROR, message=mark_safe(_('Upgrade this team to a Premium account to use this feature.'))) + return redirect(event.get_absolute_url()) + + team_sponsors = list(event.team.sponsors.all()) + events_sponsors = list(Sponsor.objects.filter(events__team=event.team)) + + if request.method == 'POST': + sponsor_form = SponsorForm(request.POST, request.FILES) + if sponsor_form.is_valid(): + new_sponsor = sponsor_form.save() + event.sponsors.add(new_sponsor) + event.team.sponsors.add(new_sponsor) + messages.add_message(request, messages.SUCCESS, message=_('Your sponsor has been added to this event.')) + return redirect('manage-event-sponsors', event.id) + + else: + sponsor_form = SponsorForm() + context = { + 'event': event, + 'sponsors': OrderedSet(events_sponsors + team_sponsors), + 'sponsor_form': sponsor_form, + 'can_edit_event': request.user.profile.can_edit_event(event), + } + return render(request, 'get_together/events/manage_event_sponsors.html', context) + + +@login_required +def sponsor_event(request, event_id): + event = get_object_or_404(Event, id=event_id) + sponsor = get_object_or_404(Sponsor, id=request.GET.get('sponsor', None)) + if request.user.is_anonymous: + return JsonResponse({'status': 'ERROR', 'message': _("You must be logged in manage event sponsors.")}) + + if not request.user.profile.can_edit_event(event): + return JsonResponse({'status': 'ERROR', 'message': _("You can not manage this event's sponsors.")}) + + action = 'none' + if request.GET.get('action', None) == 'add': + if sponsor in event.sponsors.all(): + return JsonResponse({'status': 'ERROR', 'message': _("Already sponsoring this event.")}) + + event.sponsors.add(sponsor) + action = 'Added' + if request.GET.get('action', None) == 'remove': + if sponsor not in event.sponsors.all(): + return JsonResponse({'status': 'ERROR', 'message': _("Not sponsoring this event.")}) + + event.sponsors.remove(sponsor) + action = 'Removed' + + return JsonResponse({'status': 'OK', 'sponsor_id': sponsor.id, 'action': action}) + + @login_required def manage_attendees(request, event_id): event = get_object_or_404(Event, id=event_id)