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

This commit is contained in:
Michael Hall 2018-05-20 12:35:52 -04:00
parent c3a1eefc8d
commit c4408d9a16
10 changed files with 290 additions and 7 deletions

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -0,0 +1,116 @@
{% extends "get_together/base.html" %}
{% load static markup tz %}
{% block add_to_title %} | {{event.name}}{% endblock %}
{% block styles %}
<link href="{% static 'css/bootstrap-album.css' %}" rel="stylesheet"/>
<style>
.card {
height: 300px !important;
width: 250px !important;
vertical-align: middle;
}
.card-banner {
height: 200px !important;
width: 250px !important;
}
.card-banner .card-img-top {
max-height: 200px !important;
max-width: 248px !important;
}
</style>
{% endblock %}
{% block content %}
<div class="fluid-container">
<div class="row">
<div class="col-sm-9">
<h2>Sponsors for <a href="{{event.get_absolute_url}}">{{ event.name }}</a>
</h2>
<hr/>
{% comment %}
<p>
<b>Lookup sponsors: </b><input type="text" id="id_search" name="search">
<hr/>
</p>
{% endcomment %}
<div class="container">
<div class="row">
{% for sponsor in sponsors %}
<div class="col-md-4">
<div class="card mb-1 box-shadow">
<div class="card-banner align-items-center">
<a class="card-link" href="{{sponsor.web_url}}">
<img class="card-img-top" src="{{sponsor.logo.url}}">
</a>
</div>
<div class="card-body">
<div class="card-text">
<strong>{{sponsor.name}}</strong><br/>
<div class="btn-group">
{% if sponsor in event.sponsors.all %}
<a id="sponsor-button-{{sponsor.id}}" class="btn btn-danger" href="javascript:toggle_sponsor({{sponsor.id}});">Remove</a></span>
{% else %}
<a id="sponsor-button-{{sponsor.id}}" class="btn btn-success" href="javascript:toggle_sponsor({{sponsor.id}});">Add</a></span>
{% endif %}
</div>
</div>
</div>
</div>
</div>
{% endfor %}
</div>
</div>
<hr/>
</div>
<div class="col-md-3">
<div class="container">
<div class="row">
<div class="col"><h4>New Sponsor</h4><hr/></div>
</div>
<div class="row">
<div class="col">
<form enctype="multipart/form-data" action="{% url 'manage-event-sponsors' event.id %}" method="POST">
{% csrf_token %}
{{sponsor_form.as_p}}
<button class="btn btn-primary" type="submit">Add Sponsor</button>
</form>
</div>
</div>
</div>
</div>
</div>
</div>
{% endblock %}
{% block javascript %}
<script type="text/javascript">
function toggle_sponsor(sponsor_id) {
var btn = $("#sponsor-button-"+sponsor_id)
var value = ''
if (btn[0].innerText == 'Add') {
value = 'add'
} else if (btn[0].innerText == 'Remove') {
value = 'remove'
}
$.getJSON("{% url 'sponsor-event' event.id %}?sponsor="+sponsor_id+"&action="+value, function(data, status) {
console.log(data)
if (data.status == "OK") {
if (data.action == "Added") {
btn[0].innerText = "Remove"
btn.removeClass('btn-success').addClass('btn-danger')
} else if (data.action == "Removed") {
btn[0].innerText = "Add"
btn.removeClass('btn-danger').addClass('btn-success')
}
}
});
}
</script>
{% endblock %}

View file

@ -135,6 +135,7 @@
</button>
<div class="dropdown-menu" aria-labelledby="editMenuButton">
<a href="{% url 'edit-event' event.id %}" class="dropdown-item">Event Details</a>
<a href="{% url 'manage-event-sponsors' event.id %}" class="dropdown-item">Manage Sponsors</a>
<a href="{% url 'schedule-event-talks' event.id %}" class="dropdown-item">Manage Talks</a>
<a href="{% url 'manage-attendees' event.id %}" class="dropdown-item">Manage Attendees</a>
</div>
@ -244,6 +245,19 @@
<div class="col-md-3">
<div class="container">
{% if sponsor_count > 0 %}
<div class="row">
<div class="col"><h4>Sponsors</h4><hr/></div>
</div>
{% for sponsor in sponsor_list %}
<div class="row mb-3">
<div class="col">
<a href="{{sponsor.web_url}}" target="_blank"><img src="{{sponsor.logo.url}}" alt="{{sponsor.name}} Logo" title="{{sponsor.name}}"></a>
</div>
</div>
{% endfor %}
<div class="row mb-3"></div>
{% endif %}
<div class="row">
<div class="col"><h4>Attendees ({{attendee_count}})</h4><hr/></div>
</div>

View file

@ -81,6 +81,8 @@ urlpatterns = [
path('events/<int:event_id>/+attend/', views.attend_event, name='attend-event'),
path('events/<int:event_id>/+attended/', views.attended_event, name='attended-event'),
path('events/<int:event_id>/+attendees/', views.manage_attendees, name='manage-attendees'),
path('events/<int:event_id>/+sponsors/', views.manage_event_sponsors, name='manage-event-sponsors'),
path('events/<int:event_id>/+sponsor/', views.sponsor_event, name='sponsor-event'),
path('events/<int:event_id>/+invite/', views.invite_attendees, name='invite-attendees'),
path('events/<int:event_id>/+delete/', views.delete_event, name='delete-event'),
path('events/<int:event_id>/+add_place/', views.add_place_to_event, name='add-place'),

View file

@ -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 <a href="/about/premium">Premium</a> 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)