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:
parent
c3a1eefc8d
commit
c4408d9a16
10 changed files with 290 additions and 7 deletions
|
@ -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):
|
||||
|
|
|
@ -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
|
||||
|
|
34
events/migrations/0031_add_sponsors.py
Normal file
34
events/migrations/0031_add_sponsors.py
Normal 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'),
|
||||
),
|
||||
]
|
|
@ -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()
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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.'))
|
||||
|
|
|
@ -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 %}
|
|
@ -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>
|
||||
|
|
|
@ -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'),
|
||||
|
|
|
@ -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)
|
||||
|
|
Loading…
Reference in a new issue