commit
6d5c24e635
66 changed files with 611 additions and 604 deletions
|
@ -1,5 +1,3 @@
|
||||||
import datetime
|
|
||||||
|
|
||||||
from django.db import models
|
from django.db import models
|
||||||
|
|
||||||
from django.contrib.auth.models import User
|
from django.contrib.auth.models import User
|
||||||
|
|
|
@ -1,10 +1,5 @@
|
||||||
from django import template
|
from django import template
|
||||||
from django.core.exceptions import ImproperlyConfigured
|
|
||||||
from django.core.urlresolvers import reverse
|
from django.core.urlresolvers import reverse
|
||||||
from django.utils.safestring import mark_safe
|
|
||||||
from django.utils.encoding import smart_str
|
|
||||||
from django.utils.translation import ugettext_lazy as _
|
|
||||||
from django.template.defaulttags import kwarg_re
|
|
||||||
|
|
||||||
from symposion.boxes.models import Box
|
from symposion.boxes.models import Box
|
||||||
from symposion.boxes.forms import BoxForm
|
from symposion.boxes.forms import BoxForm
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
# flake8: noqa
|
||||||
from django.conf.urls.defaults import url, patterns
|
from django.conf.urls.defaults import url, patterns
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -7,7 +7,8 @@ from symposion.boxes.forms import BoxForm
|
||||||
from symposion.boxes.models import Box
|
from symposion.boxes.models import Box
|
||||||
|
|
||||||
|
|
||||||
# @@@ problem with this is that the box_edit.html and box_create.html won't have domain objects in context
|
# @@@ problem with this is that the box_edit.html and box_create.html won't have domain objects in
|
||||||
|
# context
|
||||||
def get_auth_vars(request):
|
def get_auth_vars(request):
|
||||||
auth_vars = {}
|
auth_vars = {}
|
||||||
if request.method == "POST":
|
if request.method == "POST":
|
||||||
|
|
|
@ -4,6 +4,7 @@ import reversion
|
||||||
|
|
||||||
from .models import Page
|
from .models import Page
|
||||||
|
|
||||||
|
|
||||||
class PageAdmin(reversion.VersionAdmin):
|
class PageAdmin(reversion.VersionAdmin):
|
||||||
|
|
||||||
pass
|
pass
|
||||||
|
|
|
@ -2,6 +2,7 @@ from datetime import datetime
|
||||||
|
|
||||||
from django.db import models
|
from django.db import models
|
||||||
|
|
||||||
|
|
||||||
class PublishedPageManager(models.Manager):
|
class PublishedPageManager(models.Manager):
|
||||||
|
|
||||||
def get_query_set(self):
|
def get_query_set(self):
|
||||||
|
|
|
@ -53,7 +53,8 @@ class Page(models.Model):
|
||||||
def clean_fields(self, exclude=None):
|
def clean_fields(self, exclude=None):
|
||||||
super(Page, self).clean_fields(exclude)
|
super(Page, self).clean_fields(exclude)
|
||||||
if not re.match(settings.SYMPOSION_PAGE_REGEX, self.path):
|
if not re.match(settings.SYMPOSION_PAGE_REGEX, self.path):
|
||||||
raise ValidationError({"path": [_("Path can only contain letters, numbers and hyphens and end with /")]})
|
raise ValidationError(
|
||||||
|
{"path": [_("Path can only contain letters, numbers and hyphens and end with /")]})
|
||||||
|
|
||||||
|
|
||||||
reversion.register(Page)
|
reversion.register(Page)
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
# flake8: noqa
|
||||||
from django.conf.urls.defaults import url, patterns
|
from django.conf.urls.defaults import url, patterns
|
||||||
|
|
||||||
PAGE_RE = r"(([\w-]{1,})(/[\w-]{1,})*)/"
|
PAGE_RE = r"(([\w-]{1,})(/[\w-]{1,})*)/"
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
from django.conf import settings
|
|
||||||
|
|
||||||
from appconf import AppConf
|
from appconf import AppConf
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -6,6 +6,6 @@ from symposion.conference.models import Conference, Section
|
||||||
admin.site.register(Conference, list_display=("title", "start_date", "end_date"))
|
admin.site.register(Conference, list_display=("title", "start_date", "end_date"))
|
||||||
admin.site.register(
|
admin.site.register(
|
||||||
Section,
|
Section,
|
||||||
prepopulated_fields = {"slug": ("name",)},
|
prepopulated_fields={"slug": ("name",)},
|
||||||
list_display = ("name", "conference", "start_date", "end_date")
|
list_display=("name", "conference", "start_date", "end_date")
|
||||||
)
|
)
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
from django.conf.urls.defaults import *
|
# flake8: noqa
|
||||||
|
from django.conf.urls.defaults import patterns, url
|
||||||
|
|
||||||
|
|
||||||
urlpatterns = patterns("symposion.conference.views",
|
urlpatterns = patterns("symposion.conference.views",
|
||||||
|
|
|
@ -26,5 +26,6 @@ class SignupForm(account.forms.SignupForm):
|
||||||
email_confirm = self.cleaned_data["email_confirm"]
|
email_confirm = self.cleaned_data["email_confirm"]
|
||||||
if email:
|
if email:
|
||||||
if email != email_confirm:
|
if email != email_confirm:
|
||||||
raise forms.ValidationError("Email address must match previously typed email address")
|
raise forms.ValidationError(
|
||||||
|
"Email address must match previously typed email address")
|
||||||
return email_confirm
|
return email_confirm
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
import html5lib
|
|
||||||
from html5lib import html5parser, sanitizer
|
from html5lib import html5parser, sanitizer
|
||||||
|
|
||||||
import markdown
|
import markdown
|
||||||
|
|
|
@ -3,9 +3,8 @@ import csv
|
||||||
from django.http import HttpResponse
|
from django.http import HttpResponse
|
||||||
|
|
||||||
|
|
||||||
def export_as_csv_action(
|
def export_as_csv_action(description="Export selected objects as CSV file",
|
||||||
description="Export selected objects as CSV file",
|
fields=None, exclude=None, header=True):
|
||||||
fields=None, exclude=None, header=True):
|
|
||||||
"""
|
"""
|
||||||
This function returns an export csv action
|
This function returns an export csv action
|
||||||
'fields' and 'exclude' work like in Django ModelForm
|
'fields' and 'exclude' work like in Django ModelForm
|
||||||
|
@ -24,12 +23,14 @@ def export_as_csv_action(
|
||||||
excludeset = set(exclude)
|
excludeset = set(exclude)
|
||||||
field_names = field_names - excludeset
|
field_names = field_names - excludeset
|
||||||
response = HttpResponse(mimetype="text/csv")
|
response = HttpResponse(mimetype="text/csv")
|
||||||
response["Content-Disposition"] = "attachment; filename=%s.csv" % unicode(opts).replace(".", "_")
|
response["Content-Disposition"] = \
|
||||||
|
"attachment; filename=%s.csv" % unicode(opts).replace(".", "_")
|
||||||
writer = csv.writer(response)
|
writer = csv.writer(response)
|
||||||
if header:
|
if header:
|
||||||
writer.writerow(list(field_names))
|
writer.writerow(list(field_names))
|
||||||
for obj in queryset:
|
for obj in queryset:
|
||||||
writer.writerow([unicode(getattr(obj, field)).encode("utf-8", "replace") for field in field_names])
|
writer.writerow(
|
||||||
|
[unicode(getattr(obj, field)).encode("utf-8", "replace") for field in field_names])
|
||||||
return response
|
return response
|
||||||
export_as_csv.short_description = description
|
export_as_csv.short_description = description
|
||||||
return export_as_csv
|
return export_as_csv
|
||||||
|
|
|
@ -1,27 +0,0 @@
|
||||||
from django.db import models
|
|
||||||
from django.db.models.query import QuerySet
|
|
||||||
|
|
||||||
|
|
||||||
class CachingM2MQuerySet(QuerySet):
|
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
|
||||||
super(CachingM2MQuerySet, self).__init__(*args, **kwargs)
|
|
||||||
self.cached_m2m_field = kwargs["m2m_field"]
|
|
||||||
|
|
||||||
def iterator(self):
|
|
||||||
parent_iter = super(CachingM2MQuerySet, self).iterator()
|
|
||||||
m2m_model = getattr(self.model, self.cached_m2m_field).through
|
|
||||||
|
|
||||||
for obj in parent_iter:
|
|
||||||
if obj.id in cached_objects:
|
|
||||||
setattr(obj, "_cached_m2m_%s" % self.cached_m2m_field)
|
|
||||||
yield obj
|
|
||||||
|
|
||||||
|
|
||||||
class ProposalManager(models.Manager):
|
|
||||||
def cache_m2m(self, m2m_field):
|
|
||||||
return CachingM2MQuerySet(self.model, using=self._db, m2m_field=m2m_field)
|
|
||||||
AdditionalSpeaker = queryset.model.additional_speakers.through
|
|
||||||
additional_speakers = collections.defaultdict(set)
|
|
||||||
for additional_speaker in AdditionalSpeaker._default_manager.filter(proposal__in=queryset).select_related("speaker__user"):
|
|
||||||
additional_speakers[additional_speaker.proposal_id].add(additional_speaker.speaker)
|
|
|
@ -85,22 +85,29 @@ class ProposalBase(models.Model):
|
||||||
description = models.TextField(
|
description = models.TextField(
|
||||||
_("Brief Description"),
|
_("Brief Description"),
|
||||||
max_length=400, # @@@ need to enforce 400 in UI
|
max_length=400, # @@@ need to enforce 400 in UI
|
||||||
help_text="If your proposal is accepted this will be made public and printed in the program. Should be one paragraph, maximum 400 characters."
|
help_text=_("If your proposal is accepted this will be made public and printed in the "
|
||||||
|
"program. Should be one paragraph, maximum 400 characters.")
|
||||||
)
|
)
|
||||||
abstract = MarkupField(
|
abstract = MarkupField(
|
||||||
_("Detailed Abstract"),
|
_("Detailed Abstract"),
|
||||||
help_text=_("Detailed outline. Will be made public if your proposal is accepted. Edit using <a href='http://daringfireball.net/projects/markdown/basics' target='_blank'>Markdown</a>.")
|
help_text=_("Detailed outline. Will be made public if your proposal is accepted. Edit "
|
||||||
|
"using <a href='http://daringfireball.net/projects/markdown/basics' "
|
||||||
|
"target='_blank'>Markdown</a>.")
|
||||||
)
|
)
|
||||||
additional_notes = MarkupField(
|
additional_notes = MarkupField(
|
||||||
blank=True,
|
blank=True,
|
||||||
help_text=_("Anything else you'd like the program committee to know when making their selection: your past experience, etc. This is not made public. Edit using <a href='http://daringfireball.net/projects/markdown/basics' target='_blank'>Markdown</a>.")
|
help_text=_("Anything else you'd like the program committee to know when making their "
|
||||||
|
"selection: your past experience, etc. This is not made public. Edit using "
|
||||||
|
"<a href='http://daringfireball.net/projects/markdown/basics' "
|
||||||
|
"target='_blank'>Markdown</a>.")
|
||||||
)
|
)
|
||||||
submitted = models.DateTimeField(
|
submitted = models.DateTimeField(
|
||||||
default=datetime.datetime.now,
|
default=datetime.datetime.now,
|
||||||
editable=False,
|
editable=False,
|
||||||
)
|
)
|
||||||
speaker = models.ForeignKey("speakers.Speaker", related_name="proposals")
|
speaker = models.ForeignKey("speakers.Speaker", related_name="proposals")
|
||||||
additional_speakers = models.ManyToManyField("speakers.Speaker", through="AdditionalSpeaker", blank=True)
|
additional_speakers = models.ManyToManyField("speakers.Speaker", through="AdditionalSpeaker",
|
||||||
|
blank=True)
|
||||||
cancelled = models.BooleanField(default=False)
|
cancelled = models.BooleanField(default=False)
|
||||||
|
|
||||||
def can_edit(self):
|
def can_edit(self):
|
||||||
|
@ -120,7 +127,9 @@ class ProposalBase(models.Model):
|
||||||
|
|
||||||
def speakers(self):
|
def speakers(self):
|
||||||
yield self.speaker
|
yield self.speaker
|
||||||
for speaker in self.additional_speakers.exclude(additionalspeaker__status=AdditionalSpeaker.SPEAKING_STATUS_DECLINED):
|
speakers = self.additional_speakers.exclude(
|
||||||
|
additionalspeaker__status=AdditionalSpeaker.SPEAKING_STATUS_DECLINED)
|
||||||
|
for speaker in speakers:
|
||||||
yield speaker
|
yield speaker
|
||||||
|
|
||||||
def notification_email_context(self):
|
def notification_email_context(self):
|
||||||
|
@ -172,4 +181,5 @@ class SupportingDocument(models.Model):
|
||||||
description = models.CharField(max_length=140)
|
description = models.CharField(max_length=140)
|
||||||
|
|
||||||
def download_url(self):
|
def download_url(self):
|
||||||
return reverse("proposal_document_download", args=[self.pk, os.path.basename(self.file.name).lower()])
|
return reverse("proposal_document_download",
|
||||||
|
args=[self.pk, os.path.basename(self.file.name).lower()])
|
||||||
|
|
|
@ -70,4 +70,3 @@ def associated_proposals(parser, token):
|
||||||
{% associated_proposals as associated_proposals %}
|
{% associated_proposals as associated_proposals %}
|
||||||
"""
|
"""
|
||||||
return AssociatedProposalsNode.handle_token(parser, token)
|
return AssociatedProposalsNode.handle_token(parser, token)
|
||||||
|
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
# flake8: noqa
|
||||||
from django.conf.urls.defaults import *
|
from django.conf.urls.defaults import *
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -135,13 +135,13 @@ def proposal_speaker_manage(request, pk):
|
||||||
# fire off email to user to create profile
|
# fire off email to user to create profile
|
||||||
send_email(
|
send_email(
|
||||||
[email_address], "speaker_no_profile",
|
[email_address], "speaker_no_profile",
|
||||||
context = message_ctx
|
context=message_ctx
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
# fire off email to user letting them they are loved.
|
# fire off email to user letting them they are loved.
|
||||||
send_email(
|
send_email(
|
||||||
[email_address], "speaker_addition",
|
[email_address], "speaker_addition",
|
||||||
context = message_ctx
|
context=message_ctx
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
speaker, token = create_speaker_token(email_address)
|
speaker, token = create_speaker_token(email_address)
|
||||||
|
@ -150,9 +150,10 @@ def proposal_speaker_manage(request, pk):
|
||||||
# account and speaker profile
|
# account and speaker profile
|
||||||
send_email(
|
send_email(
|
||||||
[email_address], "speaker_invite",
|
[email_address], "speaker_invite",
|
||||||
context = message_ctx
|
context=message_ctx
|
||||||
)
|
)
|
||||||
invitation, created = AdditionalSpeaker.objects.get_or_create(proposalbase=proposal.proposalbase_ptr, speaker=speaker)
|
invitation, created = AdditionalSpeaker.objects.get_or_create(
|
||||||
|
proposalbase=proposal.proposalbase_ptr, speaker=speaker)
|
||||||
messages.success(request, "Speaker invited to proposal.")
|
messages.success(request, "Speaker invited to proposal.")
|
||||||
return redirect("proposal_speaker_manage", proposal.pk)
|
return redirect("proposal_speaker_manage", proposal.pk)
|
||||||
else:
|
else:
|
||||||
|
@ -311,7 +312,8 @@ def proposal_leave(request, pk):
|
||||||
@login_required
|
@login_required
|
||||||
def proposal_pending_join(request, pk):
|
def proposal_pending_join(request, pk):
|
||||||
proposal = get_object_or_404(ProposalBase, pk=pk)
|
proposal = get_object_or_404(ProposalBase, pk=pk)
|
||||||
speaking = get_object_or_404(AdditionalSpeaker, speaker=request.user.speaker_profile, proposalbase=proposal)
|
speaking = get_object_or_404(AdditionalSpeaker, speaker=request.user.speaker_profile,
|
||||||
|
proposalbase=proposal)
|
||||||
if speaking.status == AdditionalSpeaker.SPEAKING_STATUS_PENDING:
|
if speaking.status == AdditionalSpeaker.SPEAKING_STATUS_PENDING:
|
||||||
speaking.status = AdditionalSpeaker.SPEAKING_STATUS_ACCEPTED
|
speaking.status = AdditionalSpeaker.SPEAKING_STATUS_ACCEPTED
|
||||||
speaking.save()
|
speaking.save()
|
||||||
|
@ -324,7 +326,8 @@ def proposal_pending_join(request, pk):
|
||||||
@login_required
|
@login_required
|
||||||
def proposal_pending_decline(request, pk):
|
def proposal_pending_decline(request, pk):
|
||||||
proposal = get_object_or_404(ProposalBase, pk=pk)
|
proposal = get_object_or_404(ProposalBase, pk=pk)
|
||||||
speaking = get_object_or_404(AdditionalSpeaker, speaker=request.user.speaker_profile, proposalbase=proposal)
|
speaking = get_object_or_404(AdditionalSpeaker, speaker=request.user.speaker_profile,
|
||||||
|
proposalbase=proposal)
|
||||||
if speaking.status == AdditionalSpeaker.SPEAKING_STATUS_PENDING:
|
if speaking.status == AdditionalSpeaker.SPEAKING_STATUS_PENDING:
|
||||||
speaking.status = AdditionalSpeaker.SPEAKING_STATUS_DECLINED
|
speaking.status = AdditionalSpeaker.SPEAKING_STATUS_DECLINED
|
||||||
speaking.save()
|
speaking.save()
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
from django.contrib.contenttypes.models import ContentType
|
|
||||||
|
|
||||||
from symposion.proposals.models import ProposalSection
|
from symposion.proposals.models import ProposalSection
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -9,13 +9,13 @@ class ReviewForm(forms.ModelForm):
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Review
|
model = Review
|
||||||
fields = ["vote", "comment"]
|
fields = ["vote", "comment"]
|
||||||
widgets = { "comment": MarkItUpWidget() }
|
widgets = {"comment": MarkItUpWidget()}
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
super(ReviewForm, self).__init__(*args, **kwargs)
|
super(ReviewForm, self).__init__(*args, **kwargs)
|
||||||
self.fields["vote"] = forms.ChoiceField(
|
self.fields["vote"] = forms.ChoiceField(
|
||||||
widget = forms.RadioSelect(),
|
widget=forms.RadioSelect(),
|
||||||
choices = VOTES.CHOICES
|
choices=VOTES.CHOICES
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@ -23,14 +23,14 @@ class ReviewCommentForm(forms.ModelForm):
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Comment
|
model = Comment
|
||||||
fields = ["text"]
|
fields = ["text"]
|
||||||
widgets = { "text": MarkItUpWidget() }
|
widgets = {"text": MarkItUpWidget()}
|
||||||
|
|
||||||
|
|
||||||
class SpeakerCommentForm(forms.ModelForm):
|
class SpeakerCommentForm(forms.ModelForm):
|
||||||
class Meta:
|
class Meta:
|
||||||
model = ProposalMessage
|
model = ProposalMessage
|
||||||
fields = ["message"]
|
fields = ["message"]
|
||||||
widgets = { "message": MarkItUpWidget() }
|
widgets = {"message": MarkItUpWidget()}
|
||||||
|
|
||||||
|
|
||||||
class BulkPresentationForm(forms.Form):
|
class BulkPresentationForm(forms.Form):
|
||||||
|
|
|
@ -1,8 +1,3 @@
|
||||||
import csv
|
|
||||||
import os
|
|
||||||
import random
|
|
||||||
|
|
||||||
from django.contrib.auth import models
|
|
||||||
from django.core.management.base import BaseCommand
|
from django.core.management.base import BaseCommand
|
||||||
|
|
||||||
from symposion.reviews.models import ReviewAssignment
|
from symposion.reviews.models import ReviewAssignment
|
||||||
|
|
|
@ -1,7 +1,5 @@
|
||||||
from django.core.management.base import BaseCommand
|
from django.core.management.base import BaseCommand
|
||||||
|
|
||||||
from django.contrib.auth.models import Group
|
|
||||||
|
|
||||||
from symposion.reviews.models import ProposalResult
|
from symposion.reviews.models import ProposalResult
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -12,4 +12,5 @@ class Command(BaseCommand):
|
||||||
|
|
||||||
for result in accepted_proposals:
|
for result in accepted_proposals:
|
||||||
promote_proposal(result.proposal)
|
promote_proposal(result.proposal)
|
||||||
connections["default"].cursor().execute("SELECT setval('schedule_session_id_seq', (SELECT max(id) FROM schedule_session))")
|
connections["default"].cursor().execute(
|
||||||
|
"SELECT setval('schedule_session_id_seq', (SELECT max(id) FROM schedule_session))")
|
||||||
|
|
|
@ -117,11 +117,11 @@ class Review(models.Model):
|
||||||
def save(self, **kwargs):
|
def save(self, **kwargs):
|
||||||
if self.vote:
|
if self.vote:
|
||||||
vote, created = LatestVote.objects.get_or_create(
|
vote, created = LatestVote.objects.get_or_create(
|
||||||
proposal = self.proposal,
|
proposal=self.proposal,
|
||||||
user = self.user,
|
user=self.user,
|
||||||
defaults = dict(
|
defaults=dict(
|
||||||
vote = self.vote,
|
vote=self.vote,
|
||||||
submitted_at = self.submitted_at,
|
submitted_at=self.submitted_at,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
if not created:
|
if not created:
|
||||||
|
@ -152,7 +152,8 @@ class Review(models.Model):
|
||||||
if self == latest:
|
if self == latest:
|
||||||
# self is the latest review; revert the latest vote to the
|
# self is the latest review; revert the latest vote to the
|
||||||
# previous vote
|
# previous vote
|
||||||
previous = user_reviews.filter(submitted_at__lt=self.submitted_at).order_by("-submitted_at")[0]
|
previous = user_reviews.filter(submitted_at__lt=self.submitted_at)\
|
||||||
|
.order_by("-submitted_at")[0]
|
||||||
self.proposal.result.update_vote(self.vote, previous=previous.vote, removal=True)
|
self.proposal.result.update_vote(self.vote, previous=previous.vote, removal=True)
|
||||||
lv = LatestVote.objects.filter(proposal=self.proposal, user=self.user)
|
lv = LatestVote.objects.filter(proposal=self.proposal, user=self.user)
|
||||||
lv.update(
|
lv.update(
|
||||||
|
@ -231,20 +232,20 @@ class ProposalResult(models.Model):
|
||||||
result.comment_count = Review.objects.filter(proposal=proposal).count()
|
result.comment_count = Review.objects.filter(proposal=proposal).count()
|
||||||
result.vote_count = LatestVote.objects.filter(proposal=proposal).count()
|
result.vote_count = LatestVote.objects.filter(proposal=proposal).count()
|
||||||
result.plus_one = LatestVote.objects.filter(
|
result.plus_one = LatestVote.objects.filter(
|
||||||
proposal = proposal,
|
proposal=proposal,
|
||||||
vote = VOTES.PLUS_ONE
|
vote=VOTES.PLUS_ONE
|
||||||
).count()
|
).count()
|
||||||
result.plus_zero = LatestVote.objects.filter(
|
result.plus_zero = LatestVote.objects.filter(
|
||||||
proposal = proposal,
|
proposal=proposal,
|
||||||
vote = VOTES.PLUS_ZERO
|
vote=VOTES.PLUS_ZERO
|
||||||
).count()
|
).count()
|
||||||
result.minus_zero = LatestVote.objects.filter(
|
result.minus_zero = LatestVote.objects.filter(
|
||||||
proposal = proposal,
|
proposal=proposal,
|
||||||
vote = VOTES.MINUS_ZERO
|
vote=VOTES.MINUS_ZERO
|
||||||
).count()
|
).count()
|
||||||
result.minus_one = LatestVote.objects.filter(
|
result.minus_one = LatestVote.objects.filter(
|
||||||
proposal = proposal,
|
proposal=proposal,
|
||||||
vote = VOTES.MINUS_ONE
|
vote=VOTES.MINUS_ONE
|
||||||
).count()
|
).count()
|
||||||
result.save()
|
result.save()
|
||||||
cls._default_manager.filter(pk=result.pk).update(score=ProposalScoreExpression())
|
cls._default_manager.filter(pk=result.pk).update(score=ProposalScoreExpression())
|
||||||
|
@ -303,7 +304,8 @@ class NotificationTemplate(models.Model):
|
||||||
class ResultNotification(models.Model):
|
class ResultNotification(models.Model):
|
||||||
|
|
||||||
proposal = models.ForeignKey("proposals.ProposalBase", related_name="notifications")
|
proposal = models.ForeignKey("proposals.ProposalBase", related_name="notifications")
|
||||||
template = models.ForeignKey(NotificationTemplate, null=True, blank=True, on_delete=models.SET_NULL)
|
template = models.ForeignKey(NotificationTemplate, null=True, blank=True,
|
||||||
|
on_delete=models.SET_NULL)
|
||||||
timestamp = models.DateTimeField(default=datetime.now)
|
timestamp = models.DateTimeField(default=datetime.now)
|
||||||
to_address = models.EmailField()
|
to_address = models.EmailField()
|
||||||
from_address = models.EmailField()
|
from_address = models.EmailField()
|
||||||
|
@ -321,12 +323,12 @@ def promote_proposal(proposal):
|
||||||
presentation = proposal.presentation
|
presentation = proposal.presentation
|
||||||
else:
|
else:
|
||||||
presentation = Presentation(
|
presentation = Presentation(
|
||||||
title = proposal.title,
|
title=proposal.title,
|
||||||
description = proposal.description,
|
description=proposal.description,
|
||||||
abstract = proposal.abstract,
|
abstract=proposal.abstract,
|
||||||
speaker = proposal.speaker,
|
speaker=proposal.speaker,
|
||||||
section = proposal.section,
|
section=proposal.section,
|
||||||
proposal_base = proposal,
|
proposal_base=proposal,
|
||||||
)
|
)
|
||||||
presentation.save()
|
presentation.save()
|
||||||
for speaker in proposal.additional_speakers.all():
|
for speaker in proposal.additional_speakers.all():
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
from django import template
|
from django import template
|
||||||
|
|
||||||
from symposion.reviews.models import Review, ReviewAssignment
|
from symposion.reviews.models import ReviewAssignment
|
||||||
|
|
||||||
|
|
||||||
register = template.Library()
|
register = template.Library()
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
# flake8: noqa
|
||||||
from django.conf.urls.defaults import patterns, url
|
from django.conf.urls.defaults import patterns, url
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -10,8 +10,8 @@ def has_permission(user, proposal, speaker=False, reviewer=False):
|
||||||
if user.is_superuser:
|
if user.is_superuser:
|
||||||
return True
|
return True
|
||||||
if speaker:
|
if speaker:
|
||||||
if (user == proposal.speaker.user or
|
if user == proposal.speaker.user or \
|
||||||
proposal.additional_speakers.filter(user=user).exists()):
|
proposal.additional_speakers.filter(user=user).exists():
|
||||||
return True
|
return True
|
||||||
if reviewer:
|
if reviewer:
|
||||||
if user.groups.filter(name="reviewers").exists():
|
if user.groups.filter(name="reviewers").exists():
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
import re
|
|
||||||
|
|
||||||
from django.core.mail import send_mass_mail
|
from django.core.mail import send_mass_mail
|
||||||
from django.db.models import Q
|
from django.db.models import Q
|
||||||
from django.http import HttpResponseBadRequest, HttpResponseNotAllowed
|
from django.http import HttpResponseBadRequest, HttpResponseNotAllowed
|
||||||
|
@ -62,8 +60,8 @@ def proposals_generator(request, queryset, user_pk=None, check_speaker=True):
|
||||||
yield obj
|
yield obj
|
||||||
|
|
||||||
|
|
||||||
# Returns a list of all proposals, proposals reviewed by the user, or the proposals the user has yet to review
|
# Returns a list of all proposals, proposals reviewed by the user, or the proposals the user has
|
||||||
# depending on the link user clicks in dashboard
|
# yet to review depending on the link user clicks in dashboard
|
||||||
@login_required
|
@login_required
|
||||||
def review_section(request, section_slug, assigned=False, reviewed="all"):
|
def review_section(request, section_slug, assigned=False, reviewed="all"):
|
||||||
|
|
||||||
|
@ -74,7 +72,8 @@ def review_section(request, section_slug, assigned=False, reviewed="all"):
|
||||||
queryset = ProposalBase.objects.filter(kind__section=section)
|
queryset = ProposalBase.objects.filter(kind__section=section)
|
||||||
|
|
||||||
if assigned:
|
if assigned:
|
||||||
assignments = ReviewAssignment.objects.filter(user=request.user).values_list("proposal__id")
|
assignments = ReviewAssignment.objects.filter(user=request.user)\
|
||||||
|
.values_list("proposal__id")
|
||||||
queryset = queryset.filter(id__in=assignments)
|
queryset = queryset.filter(id__in=assignments)
|
||||||
|
|
||||||
# passing reviewed in from reviews.urls and out to review_list for
|
# passing reviewed in from reviews.urls and out to review_list for
|
||||||
|
@ -99,6 +98,7 @@ def review_section(request, section_slug, assigned=False, reviewed="all"):
|
||||||
|
|
||||||
return render(request, "reviews/review_list.html", ctx)
|
return render(request, "reviews/review_list.html", ctx)
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
def review_list(request, section_slug, user_pk):
|
def review_list(request, section_slug, user_pk):
|
||||||
|
|
||||||
|
@ -142,20 +142,20 @@ def review_admin(request, section_slug):
|
||||||
user.comment_count = Review.objects.filter(user=user).count()
|
user.comment_count = Review.objects.filter(user=user).count()
|
||||||
user.total_votes = LatestVote.objects.filter(user=user).count()
|
user.total_votes = LatestVote.objects.filter(user=user).count()
|
||||||
user.plus_one = LatestVote.objects.filter(
|
user.plus_one = LatestVote.objects.filter(
|
||||||
user = user,
|
user=user,
|
||||||
vote = LatestVote.VOTES.PLUS_ONE
|
vote=LatestVote.VOTES.PLUS_ONE
|
||||||
).count()
|
).count()
|
||||||
user.plus_zero = LatestVote.objects.filter(
|
user.plus_zero = LatestVote.objects.filter(
|
||||||
user = user,
|
user=user,
|
||||||
vote = LatestVote.VOTES.PLUS_ZERO
|
vote=LatestVote.VOTES.PLUS_ZERO
|
||||||
).count()
|
).count()
|
||||||
user.minus_zero = LatestVote.objects.filter(
|
user.minus_zero = LatestVote.objects.filter(
|
||||||
user = user,
|
user=user,
|
||||||
vote = LatestVote.VOTES.MINUS_ZERO
|
vote=LatestVote.VOTES.MINUS_ZERO
|
||||||
).count()
|
).count()
|
||||||
user.minus_one = LatestVote.objects.filter(
|
user.minus_one = LatestVote.objects.filter(
|
||||||
user = user,
|
user=user,
|
||||||
vote = LatestVote.VOTES.MINUS_ONE
|
vote=LatestVote.VOTES.MINUS_ONE
|
||||||
).count()
|
).count()
|
||||||
|
|
||||||
yield user
|
yield user
|
||||||
|
@ -222,7 +222,7 @@ def review_detail(request, pk):
|
||||||
}
|
}
|
||||||
send_email(
|
send_email(
|
||||||
[speaker.email], "proposal_new_message",
|
[speaker.email], "proposal_new_message",
|
||||||
context = ctx
|
context=ctx
|
||||||
)
|
)
|
||||||
|
|
||||||
return redirect(request.path)
|
return redirect(request.path)
|
||||||
|
@ -315,16 +315,26 @@ def review_status(request, section_slug=None, key=None):
|
||||||
queryset = queryset.filter(kind__section__slug=section_slug)
|
queryset = queryset.filter(kind__section__slug=section_slug)
|
||||||
|
|
||||||
proposals = {
|
proposals = {
|
||||||
# proposals with at least VOTE_THRESHOLD reviews and at least one +1 and no -1s, sorted by the 'score'
|
# proposals with at least VOTE_THRESHOLD reviews and at least one +1 and no -1s, sorted by
|
||||||
"positive": queryset.filter(result__vote_count__gte=VOTE_THRESHOLD, result__plus_one__gt=0, result__minus_one=0).order_by("-result__score"),
|
# the 'score'
|
||||||
# proposals with at least VOTE_THRESHOLD reviews and at least one -1 and no +1s, reverse sorted by the 'score'
|
"positive": queryset.filter(result__vote_count__gte=VOTE_THRESHOLD, result__plus_one__gt=0,
|
||||||
"negative": queryset.filter(result__vote_count__gte=VOTE_THRESHOLD, result__minus_one__gt=0, result__plus_one=0).order_by("result__score"),
|
result__minus_one=0).order_by("-result__score"),
|
||||||
# proposals with at least VOTE_THRESHOLD reviews and neither a +1 or a -1, sorted by total votes (lowest first)
|
# proposals with at least VOTE_THRESHOLD reviews and at least one -1 and no +1s, reverse
|
||||||
"indifferent": queryset.filter(result__vote_count__gte=VOTE_THRESHOLD, result__minus_one=0, result__plus_one=0).order_by("result__vote_count"),
|
# sorted by the 'score'
|
||||||
# proposals with at least VOTE_THRESHOLD reviews and both a +1 and -1, sorted by total votes (highest first)
|
"negative": queryset.filter(result__vote_count__gte=VOTE_THRESHOLD, result__minus_one__gt=0,
|
||||||
"controversial": queryset.filter(result__vote_count__gte=VOTE_THRESHOLD, result__plus_one__gt=0, result__minus_one__gt=0).order_by("-result__vote_count"),
|
result__plus_one=0).order_by("result__score"),
|
||||||
|
# proposals with at least VOTE_THRESHOLD reviews and neither a +1 or a -1, sorted by total
|
||||||
|
# votes (lowest first)
|
||||||
|
"indifferent": queryset.filter(result__vote_count__gte=VOTE_THRESHOLD, result__minus_one=0,
|
||||||
|
result__plus_one=0).order_by("result__vote_count"),
|
||||||
|
# proposals with at least VOTE_THRESHOLD reviews and both a +1 and -1, sorted by total
|
||||||
|
# votes (highest first)
|
||||||
|
"controversial": queryset.filter(result__vote_count__gte=VOTE_THRESHOLD,
|
||||||
|
result__plus_one__gt=0, result__minus_one__gt=0)
|
||||||
|
.order_by("-result__vote_count"),
|
||||||
# proposals with fewer than VOTE_THRESHOLD reviews
|
# proposals with fewer than VOTE_THRESHOLD reviews
|
||||||
"too_few": queryset.filter(result__vote_count__lt=VOTE_THRESHOLD).order_by("result__vote_count"),
|
"too_few": queryset.filter(result__vote_count__lt=VOTE_THRESHOLD)
|
||||||
|
.order_by("result__vote_count"),
|
||||||
}
|
}
|
||||||
|
|
||||||
admin = request.user.has_perm("reviews.can_manage_%s" % section_slug)
|
admin = request.user.has_perm("reviews.can_manage_%s" % section_slug)
|
||||||
|
@ -332,7 +342,8 @@ def review_status(request, section_slug=None, key=None):
|
||||||
for status in proposals:
|
for status in proposals:
|
||||||
if key and key != status:
|
if key and key != status:
|
||||||
continue
|
continue
|
||||||
proposals[status] = list(proposals_generator(request, proposals[status], check_speaker=not admin))
|
proposals[status] = list(proposals_generator(request, proposals[status],
|
||||||
|
check_speaker=not admin))
|
||||||
|
|
||||||
if key:
|
if key:
|
||||||
ctx.update({
|
ctx.update({
|
||||||
|
@ -361,14 +372,13 @@ def review_assignments(request):
|
||||||
@login_required
|
@login_required
|
||||||
@require_POST
|
@require_POST
|
||||||
def review_assignment_opt_out(request, pk):
|
def review_assignment_opt_out(request, pk):
|
||||||
review_assignment = get_object_or_404(ReviewAssignment,
|
review_assignment = get_object_or_404(
|
||||||
pk=pk,
|
ReviewAssignment, pk=pk, user=request.user)
|
||||||
user=request.user
|
|
||||||
)
|
|
||||||
if not review_assignment.opted_out:
|
if not review_assignment.opted_out:
|
||||||
review_assignment.opted_out = True
|
review_assignment.opted_out = True
|
||||||
review_assignment.save()
|
review_assignment.save()
|
||||||
ReviewAssignment.create_assignments(review_assignment.proposal, origin=ReviewAssignment.AUTO_ASSIGNED_LATER)
|
ReviewAssignment.create_assignments(
|
||||||
|
review_assignment.proposal, origin=ReviewAssignment.AUTO_ASSIGNED_LATER)
|
||||||
return redirect("review_assignments")
|
return redirect("review_assignments")
|
||||||
|
|
||||||
|
|
||||||
|
@ -398,7 +408,9 @@ def result_notification(request, section_slug, status):
|
||||||
if not request.user.has_perm("reviews.can_manage_%s" % section_slug):
|
if not request.user.has_perm("reviews.can_manage_%s" % section_slug):
|
||||||
return access_not_permitted(request)
|
return access_not_permitted(request)
|
||||||
|
|
||||||
proposals = ProposalBase.objects.filter(kind__section__slug=section_slug, result__status=status).select_related("speaker__user", "result").select_subclasses()
|
proposals = ProposalBase.objects.filter(kind__section__slug=section_slug,
|
||||||
|
result__status=status)\
|
||||||
|
.select_related("speaker__user", "result").select_subclasses()
|
||||||
notification_templates = NotificationTemplate.objects.all()
|
notification_templates = NotificationTemplate.objects.all()
|
||||||
|
|
||||||
ctx = {
|
ctx = {
|
||||||
|
|
|
@ -2,7 +2,6 @@ from django.core.exceptions import ObjectDoesNotExist
|
||||||
from django.db import models
|
from django.db import models
|
||||||
|
|
||||||
from markitup.fields import MarkupField
|
from markitup.fields import MarkupField
|
||||||
from model_utils.managers import InheritanceManager
|
|
||||||
|
|
||||||
from symposion.proposals.models import ProposalBase
|
from symposion.proposals.models import ProposalBase
|
||||||
from symposion.conference.models import Section
|
from symposion.conference.models import Section
|
||||||
|
@ -127,7 +126,8 @@ class Presentation(models.Model):
|
||||||
description = MarkupField()
|
description = MarkupField()
|
||||||
abstract = MarkupField()
|
abstract = MarkupField()
|
||||||
speaker = models.ForeignKey("speakers.Speaker", related_name="presentations")
|
speaker = models.ForeignKey("speakers.Speaker", related_name="presentations")
|
||||||
additional_speakers = models.ManyToManyField("speakers.Speaker", related_name="copresentations", blank=True)
|
additional_speakers = models.ManyToManyField("speakers.Speaker", related_name="copresentations",
|
||||||
|
blank=True)
|
||||||
cancelled = models.BooleanField(default=False)
|
cancelled = models.BooleanField(default=False)
|
||||||
proposal_base = models.OneToOneField(ProposalBase, related_name="presentation")
|
proposal_base = models.OneToOneField(ProposalBase, related_name="presentation")
|
||||||
section = models.ForeignKey(Section, related_name="presentations")
|
section = models.ForeignKey(Section, related_name="presentations")
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
import itertools
|
import itertools
|
||||||
import operator
|
|
||||||
|
|
||||||
from django.db.models import Count, Min
|
from django.db.models import Count, Min
|
||||||
|
|
||||||
|
@ -19,7 +18,8 @@ class TimeTable(object):
|
||||||
def rooms(self):
|
def rooms(self):
|
||||||
qs = Room.objects.all()
|
qs = Room.objects.all()
|
||||||
qs = qs.filter(schedule=self.day.schedule)
|
qs = qs.filter(schedule=self.day.schedule)
|
||||||
qs = qs.filter(pk__in=SlotRoom.objects.filter(slot__in=self.slots_qs().values("pk")).values("room"))
|
qs = qs.filter(
|
||||||
|
pk__in=SlotRoom.objects.filter(slot__in=self.slots_qs().values("pk")).values("room"))
|
||||||
qs = qs.order_by("order")
|
qs = qs.order_by("order")
|
||||||
return qs
|
return qs
|
||||||
|
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
# flake8: noqa
|
||||||
from django.conf.urls.defaults import url, patterns
|
from django.conf.urls.defaults import url, patterns
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
from django.core.exceptions import ObjectDoesNotExist
|
|
||||||
from django.http import Http404, HttpResponse
|
from django.http import Http404, HttpResponse
|
||||||
from django.shortcuts import render, get_object_or_404, redirect
|
from django.shortcuts import render, get_object_or_404, redirect
|
||||||
from django.template import loader, Context
|
from django.template import loader, Context
|
||||||
|
|
|
@ -4,6 +4,5 @@ from symposion.speakers.models import Speaker
|
||||||
|
|
||||||
|
|
||||||
admin.site.register(Speaker,
|
admin.site.register(Speaker,
|
||||||
list_display = ["name", "email", "created"],
|
list_display=["name", "email", "created"],
|
||||||
search_fields = ["name"],
|
search_fields=["name"])
|
||||||
)
|
|
||||||
|
|
|
@ -19,8 +19,8 @@ def speakers():
|
||||||
Speaker.objects.create(
|
Speaker.objects.create(
|
||||||
user=matz,
|
user=matz,
|
||||||
name="Yukihiro Matsumoto",
|
name="Yukihiro Matsumoto",
|
||||||
biography="I wrote Ruby, and named it after the rare gem Ruby, a pun "
|
biography=("I wrote Ruby, and named it after the rare gem Ruby, a pun "
|
||||||
"on Perl/pearl.",
|
"on Perl/pearl."),
|
||||||
)
|
)
|
||||||
Speaker.objects.create(
|
Speaker.objects.create(
|
||||||
user=larry,
|
user=larry,
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import csv
|
import csv
|
||||||
import os
|
import os
|
||||||
|
|
||||||
from django.core.management.base import BaseCommand, CommandError
|
from django.core.management.base import BaseCommand
|
||||||
|
|
||||||
from symposion.speakers.models import Speaker
|
from symposion.speakers.models import Speaker
|
||||||
|
|
||||||
|
|
|
@ -16,15 +16,19 @@ class Speaker(models.Model):
|
||||||
]
|
]
|
||||||
|
|
||||||
user = models.OneToOneField(User, null=True, related_name="speaker_profile")
|
user = models.OneToOneField(User, null=True, related_name="speaker_profile")
|
||||||
name = models.CharField(max_length=100, help_text="As you would like it to appear in the conference program.")
|
name = models.CharField(max_length=100, help_text=("As you would like it to appear in the "
|
||||||
biography = MarkupField(blank=True, help_text="A little bit about you. Edit using <a href='http://warpedvisions.org/projects/markdown-cheat-sheet/' target='_blank'>Markdown</a>.")
|
"conference program."))
|
||||||
|
biography = MarkupField(blank=True, help_text=("A little bit about you. Edit using "
|
||||||
|
"<a href='http://warpedvisions.org/projects/"
|
||||||
|
"markdown-cheat-sheet/target='_blank'>"
|
||||||
|
"Markdown</a>."))
|
||||||
photo = models.ImageField(upload_to="speaker_photos", blank=True)
|
photo = models.ImageField(upload_to="speaker_photos", blank=True)
|
||||||
annotation = models.TextField() # staff only
|
annotation = models.TextField() # staff only
|
||||||
invite_email = models.CharField(max_length=200, unique=True, null=True, db_index=True)
|
invite_email = models.CharField(max_length=200, unique=True, null=True, db_index=True)
|
||||||
invite_token = models.CharField(max_length=40, db_index=True)
|
invite_token = models.CharField(max_length=40, db_index=True)
|
||||||
created = models.DateTimeField(
|
created = models.DateTimeField(
|
||||||
default = datetime.datetime.now,
|
default=datetime.datetime.now,
|
||||||
editable = False
|
editable=False
|
||||||
)
|
)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
|
@ -36,7 +40,6 @@ class Speaker(models.Model):
|
||||||
else:
|
else:
|
||||||
return "?"
|
return "?"
|
||||||
|
|
||||||
|
|
||||||
def get_absolute_url(self):
|
def get_absolute_url(self):
|
||||||
return reverse("speaker_edit")
|
return reverse("speaker_edit")
|
||||||
|
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
# flake8: noqa
|
||||||
from django.conf.urls.defaults import *
|
from django.conf.urls.defaults import *
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -88,8 +88,8 @@ def speaker_create_token(request, token):
|
||||||
).update(
|
).update(
|
||||||
speaker=existing_speaker
|
speaker=existing_speaker
|
||||||
)
|
)
|
||||||
messages.info(request, "You have been associated with all pending "
|
messages.info(request, ("You have been associated with all pending "
|
||||||
"talk proposals")
|
"talk proposals"))
|
||||||
return redirect("dashboard")
|
return redirect("dashboard")
|
||||||
else:
|
else:
|
||||||
if not request.user.is_authenticated():
|
if not request.user.is_authenticated():
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
from django.contrib import admin
|
from django.contrib import admin
|
||||||
|
|
||||||
from symposion.sponsorship.models import SponsorLevel, Sponsor, Benefit, BenefitLevel, SponsorBenefit
|
from symposion.sponsorship.models import SponsorLevel, Sponsor, Benefit, BenefitLevel, \
|
||||||
|
SponsorBenefit
|
||||||
|
|
||||||
|
|
||||||
class BenefitLevelInline(admin.TabularInline):
|
class BenefitLevelInline(admin.TabularInline):
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
from django.core.management.base import BaseCommand
|
from django.core.management.base import BaseCommand
|
||||||
|
|
||||||
from django.contrib.auth.models import Group
|
from symposion.sponsorship.models import Sponsor, SponsorBenefit, SponsorLevel
|
||||||
|
|
||||||
from symposion.sponsorship.models import Sponsor, SponsorBenefit
|
|
||||||
|
|
||||||
|
|
||||||
class Command(BaseCommand):
|
class Command(BaseCommand):
|
||||||
|
|
|
@ -35,7 +35,8 @@ class SponsorLevel(models.Model):
|
||||||
|
|
||||||
class Sponsor(models.Model):
|
class Sponsor(models.Model):
|
||||||
|
|
||||||
applicant = models.ForeignKey(User, related_name="sponsorships", verbose_name=_("applicant"), null=True)
|
applicant = models.ForeignKey(User, related_name="sponsorships", verbose_name=_("applicant"),
|
||||||
|
null=True)
|
||||||
|
|
||||||
name = models.CharField(_("Sponsor Name"), max_length=100)
|
name = models.CharField(_("Sponsor Name"), max_length=100)
|
||||||
external_url = models.URLField(_("external URL"))
|
external_url = models.URLField(_("external URL"))
|
||||||
|
@ -47,7 +48,8 @@ class Sponsor(models.Model):
|
||||||
active = models.BooleanField(_("active"), default=False)
|
active = models.BooleanField(_("active"), default=False)
|
||||||
|
|
||||||
# Denormalization (this assumes only one logo)
|
# Denormalization (this assumes only one logo)
|
||||||
sponsor_logo = models.ForeignKey("SponsorBenefit", related_name="+", null=True, blank=True, editable=False)
|
sponsor_logo = models.ForeignKey("SponsorBenefit", related_name="+", null=True, blank=True,
|
||||||
|
editable=False)
|
||||||
|
|
||||||
objects = SponsorManager()
|
objects = SponsorManager()
|
||||||
|
|
||||||
|
@ -66,7 +68,8 @@ class Sponsor(models.Model):
|
||||||
@property
|
@property
|
||||||
def website_logo(self):
|
def website_logo(self):
|
||||||
if self.sponsor_logo is None:
|
if self.sponsor_logo is None:
|
||||||
benefits = self.sponsor_benefits.filter(benefit__type="weblogo", upload__isnull=False)[:1]
|
benefits = self.sponsor_benefits.filter(
|
||||||
|
benefit__type="weblogo", upload__isnull=False)[:1]
|
||||||
if benefits.count():
|
if benefits.count():
|
||||||
if benefits[0].upload:
|
if benefits[0].upload:
|
||||||
self.sponsor_logo = benefits[0]
|
self.sponsor_logo = benefits[0]
|
||||||
|
@ -120,7 +123,8 @@ class Sponsor(models.Model):
|
||||||
|
|
||||||
# Any remaining sponsor benefits that don't normally belong to
|
# Any remaining sponsor benefits that don't normally belong to
|
||||||
# this level are set to inactive
|
# this level are set to inactive
|
||||||
self.sponsor_benefits.exclude(pk__in=allowed_benefits).update(active=False, max_words=None, other_limits="")
|
self.sponsor_benefits.exclude(pk__in=allowed_benefits)\
|
||||||
|
.update(active=False, max_words=None, other_limits="")
|
||||||
|
|
||||||
def send_coordinator_emails(self):
|
def send_coordinator_emails(self):
|
||||||
pass # @@@ should this just be done centrally?
|
pass # @@@ should this just be done centrally?
|
||||||
|
@ -150,7 +154,8 @@ class Benefit(models.Model):
|
||||||
|
|
||||||
name = models.CharField(_("name"), max_length=100)
|
name = models.CharField(_("name"), max_length=100)
|
||||||
description = models.TextField(_("description"), blank=True)
|
description = models.TextField(_("description"), blank=True)
|
||||||
type = models.CharField(_("type"), choices=BENEFIT_TYPE_CHOICES, max_length=10, default="simple")
|
type = models.CharField(_("type"), choices=BENEFIT_TYPE_CHOICES, max_length=10,
|
||||||
|
default="simple")
|
||||||
|
|
||||||
def __unicode__(self):
|
def __unicode__(self):
|
||||||
return self.name
|
return self.name
|
||||||
|
|
|
@ -30,9 +30,12 @@ class SponsorsNode(template.Node):
|
||||||
conference = current_conference()
|
conference = current_conference()
|
||||||
if self.level:
|
if self.level:
|
||||||
level = self.level.resolve(context)
|
level = self.level.resolve(context)
|
||||||
queryset = Sponsor.objects.filter(level__conference = conference, level__name__iexact = level, active = True).order_by("added")
|
queryset = Sponsor.objects.filter(
|
||||||
|
level__conference=conference, level__name__iexact=level, active=True)\
|
||||||
|
.order_by("added")
|
||||||
else:
|
else:
|
||||||
queryset = Sponsor.objects.filter(level__conference = conference, active = True).order_by("level__order", "added")
|
queryset = Sponsor.objects.filter(level__conference=conference, active=True)\
|
||||||
|
.order_by("level__order", "added")
|
||||||
context[self.context_var] = queryset
|
context[self.context_var] = queryset
|
||||||
return u""
|
return u""
|
||||||
|
|
||||||
|
@ -72,4 +75,3 @@ def sponsor_levels(parser, token):
|
||||||
{% sponsor_levels as levels %}
|
{% sponsor_levels as levels %}
|
||||||
"""
|
"""
|
||||||
return SponsorLevelNode.handle_token(parser, token)
|
return SponsorLevelNode.handle_token(parser, token)
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
# flake8: noqa
|
||||||
from django.conf.urls.defaults import patterns, url
|
from django.conf.urls.defaults import patterns, url
|
||||||
from django.views.generic.simple import direct_to_template
|
from django.views.generic.simple import direct_to_template
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,8 @@ from django.template import RequestContext
|
||||||
from django.contrib import messages
|
from django.contrib import messages
|
||||||
from django.contrib.auth.decorators import login_required
|
from django.contrib.auth.decorators import login_required
|
||||||
|
|
||||||
from symposion.sponsorship.forms import SponsorApplicationForm, SponsorDetailsForm, SponsorBenefitsFormSet
|
from symposion.sponsorship.forms import SponsorApplicationForm, SponsorDetailsForm, \
|
||||||
|
SponsorBenefitsFormSet
|
||||||
from symposion.sponsorship.models import Sponsor, SponsorBenefit
|
from symposion.sponsorship.models import Sponsor, SponsorBenefit
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -5,8 +5,7 @@ import reversion
|
||||||
from symposion.teams.models import Team, Membership
|
from symposion.teams.models import Team, Membership
|
||||||
|
|
||||||
admin.site.register(Team,
|
admin.site.register(Team,
|
||||||
prepopulated_fields={"slug": ("name",)},
|
prepopulated_fields={"slug": ("name",)})
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class MembershipAdmin(reversion.VersionAdmin):
|
class MembershipAdmin(reversion.VersionAdmin):
|
||||||
|
|
|
@ -10,7 +10,8 @@ from symposion.teams.models import Membership
|
||||||
|
|
||||||
class TeamInvitationForm(forms.Form):
|
class TeamInvitationForm(forms.Form):
|
||||||
|
|
||||||
email = forms.EmailField(help_text="email address must be that of an account on this conference site")
|
email = forms.EmailField(help_text=("email address must be that of an account on this "
|
||||||
|
"conference site"))
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
self.team = kwargs.pop("team")
|
self.team = kwargs.pop("team")
|
||||||
|
@ -28,7 +29,9 @@ class TeamInvitationForm(forms.Form):
|
||||||
except User.DoesNotExist:
|
except User.DoesNotExist:
|
||||||
# eventually we can invite them but for now assume they are
|
# eventually we can invite them but for now assume they are
|
||||||
# already on the site
|
# already on the site
|
||||||
raise forms.ValidationError(mark_safe("no account with email address <b>%s</b> found on this conference site" % escape(email)))
|
raise forms.ValidationError(
|
||||||
|
mark_safe("no account with email address <b>%s</b> found on this conference "
|
||||||
|
"site" % escape(email)))
|
||||||
|
|
||||||
state = self.team.get_state_for_user(user)
|
state = self.team.get_state_for_user(user)
|
||||||
|
|
||||||
|
|
|
@ -25,7 +25,8 @@ class Team(models.Model):
|
||||||
permissions = models.ManyToManyField(Permission, blank=True, related_name="member_teams")
|
permissions = models.ManyToManyField(Permission, blank=True, related_name="member_teams")
|
||||||
|
|
||||||
# manager permissions
|
# manager permissions
|
||||||
manager_permissions = models.ManyToManyField(Permission, blank=True, related_name="manager_teams")
|
manager_permissions = models.ManyToManyField(Permission, blank=True,
|
||||||
|
related_name="manager_teams")
|
||||||
|
|
||||||
created = models.DateTimeField(default=datetime.datetime.now, editable=False)
|
created = models.DateTimeField(default=datetime.datetime.now, editable=False)
|
||||||
|
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
# flake8: noqa
|
||||||
from django.conf.urls.defaults import *
|
from django.conf.urls.defaults import *
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -10,7 +10,7 @@ from symposion.teams.forms import TeamInvitationForm
|
||||||
from symposion.teams.models import Team, Membership
|
from symposion.teams.models import Team, Membership
|
||||||
|
|
||||||
|
|
||||||
## perm checks
|
# perm checks
|
||||||
#
|
#
|
||||||
# @@@ these can be moved
|
# @@@ these can be moved
|
||||||
|
|
||||||
|
@ -50,7 +50,7 @@ def can_invite(team, user):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
## views
|
# views
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
|
|
Loading…
Reference in a new issue