Massively upgrade symposion

* Remove markitup (to be replaced with Ace editor)
* Use DUA decorators
* Removed custom signup bits
* Upgraded dependencies
* Added migrations
* Namespaced template locations
* Removed html5parser/sanitizer (for now) - parsing functionality
should be moved out entirely to a hooks
* Replaced ProposalScoreExpression object with a  function that returns
F() expressions
This commit is contained in:
Patrick Altman 2015-10-16 12:36:58 -05:00
parent 8b4282a48e
commit 11f697d137
36 changed files with 821 additions and 222 deletions

View file

@ -1,13 +1,12 @@
Django>=1.7.1
django-appconf==0.6
django-markitup==2.2.2
django-model-utils==2.2
django-reversion==1.8.5
django-sitetree==1.2.1
django-taggit==0.12.2
django-timezone-field==1.2
django-user-accounts==1.0
Django==1.8.5
django-appconf==1.0.1
django-model-utils==2.3.1
django-reversion==1.9.3
django-sitetree==1.4.0
django-taggit==0.17.1
django-timezone-field==1.3
django-user-accounts==1.2.0
easy-thumbnails==2.2
html5lib==0.999
markdown==2.5.2
pytz==2014.10
html5lib==0.9999999
markdown==2.6.2
pytz==2015.6

View file

@ -0,0 +1,44 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import models, migrations
import timezone_field.fields
class Migration(migrations.Migration):
dependencies = [
]
operations = [
migrations.CreateModel(
name='Conference',
fields=[
('id', models.AutoField(primary_key=True, auto_created=True, serialize=False, verbose_name='ID')),
('title', models.CharField(max_length=100, verbose_name='Title')),
('start_date', models.DateField(null=True, blank=True, verbose_name='Start date')),
('end_date', models.DateField(null=True, blank=True, verbose_name='End date')),
('timezone', timezone_field.fields.TimeZoneField(blank=True, verbose_name='timezone')),
],
options={
'verbose_name_plural': 'conferences',
'verbose_name': 'conference',
},
),
migrations.CreateModel(
name='Section',
fields=[
('id', models.AutoField(primary_key=True, auto_created=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=100, verbose_name='Name')),
('slug', models.SlugField(verbose_name='Slug')),
('start_date', models.DateField(null=True, blank=True, verbose_name='Start date')),
('end_date', models.DateField(null=True, blank=True, verbose_name='End date')),
('conference', models.ForeignKey(to='symposion_conference.Conference', verbose_name='Conference')),
],
options={
'ordering': ['start_date'],
'verbose_name_plural': 'sections',
'verbose_name': 'section',
},
),
]

View file

@ -1,9 +1,10 @@
from django.http import Http404
from django.shortcuts import render
from django.contrib.auth.decorators import login_required
from django.contrib.auth.models import User
from account.decorators import login_required
@login_required
def user_list(request):
@ -11,6 +12,6 @@ def user_list(request):
if not request.user.is_staff:
raise Http404()
return render(request, "conference/user_list.html", {
return render(request, "symposion/conference/user_list.html", {
"users": User.objects.all(),
})

View file

@ -1,58 +0,0 @@
try:
from collections import OrderedDict
except ImportError:
OrderedDict = None
from django import forms
import account.forms
from django.utils.translation import ugettext_lazy as _
class SignupForm(account.forms.SignupForm):
first_name = forms.CharField(label=_("First name"))
last_name = forms.CharField(label=_("Last name"))
email_confirm = forms.EmailField(label=_("Confirm Email"))
def __init__(self, *args, **kwargs):
super(SignupForm, self).__init__(*args, **kwargs)
key_order = [
"email",
"email_confirm",
"first_name",
"last_name",
"password",
"password_confirm"
]
self.fields = reorder_fields(self.fields, key_order)
def clean_email_confirm(self):
email = self.cleaned_data.get("email")
email_confirm = self.cleaned_data["email_confirm"]
if email:
if email != email_confirm:
raise forms.ValidationError(
_("Email address must match previously typed email address"))
return email_confirm
def reorder_fields(fields, order):
"""Reorder form fields by order, removing items not in order.
>>> reorder_fields(
... OrderedDict([('a', 1), ('b', 2), ('c', 3)]),
... ['b', 'c', 'a'])
OrderedDict([('b', 2), ('c', 3), ('a', 1)])
"""
for key, v in fields.items():
if key not in order:
del fields[key]
if not OrderedDict or hasattr(fields, "keyOrder"):
# fields is SortedDict
fields.keyOrder.sort(key=lambda k: order.index(k[0]))
return fields
else:
# fields is OrderedDict
return OrderedDict(sorted(fields.items(), key=lambda k: order.index(k[0])))

View file

@ -10,8 +10,9 @@ def parse(text):
text = markdown.markdown(text, extensions=["extra"], safe_mode=False)
# Sanitize using html5lib
bits = []
parser = html5parser.HTMLParser(tokenizer=sanitizer.HTMLSanitizer)
for token in parser.parseFragment(text).childNodes:
bits.append(token.toxml())
return "".join(bits)
# bits = []
# parser = html5parser.HTMLParser(tokenizer=sanitizer.HTMLSanitizer)
# for token in parser.parseFragment(text).childNodes:
# bits.append(token.toxml())
# return "".join(bits)
return text

View file

@ -4,7 +4,6 @@ from django.db.models import Q
from django.utils.translation import ugettext_lazy as _
from symposion.proposals.models import SupportingDocument
# from markitup.widgets import MarkItUpWidget
# @@@ generic proposal form

View file

@ -0,0 +1,100 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import models, migrations
from django.conf import settings
import django.utils.timezone
import symposion.proposals.models
class Migration(migrations.Migration):
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('symposion_speakers', '__first__'),
('symposion_conference', '0001_initial'),
]
operations = [
migrations.CreateModel(
name='AdditionalSpeaker',
fields=[
('id', models.AutoField(verbose_name='ID', primary_key=True, serialize=False, auto_created=True)),
('status', models.IntegerField(verbose_name='Status', default=1, choices=[(1, 'Pending'), (2, 'Accepted'), (3, 'Declined')])),
],
options={
'verbose_name': 'Addtional speaker',
'verbose_name_plural': 'Additional speakers',
},
),
migrations.CreateModel(
name='ProposalBase',
fields=[
('id', models.AutoField(verbose_name='ID', primary_key=True, serialize=False, auto_created=True)),
('title', models.CharField(verbose_name='Title', max_length=100)),
('description', models.TextField(verbose_name='Brief Description', max_length=400, 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', models.TextField(verbose_name='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>.")),
('abstract_html', models.TextField(blank=True)),
('additional_notes', models.TextField(blank=True, verbose_name='Addtional Notes', 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>.")),
('additional_notes_html', models.TextField(blank=True)),
('submitted', models.DateTimeField(editable=False, default=django.utils.timezone.now, verbose_name='Submitted')),
('cancelled', models.BooleanField(verbose_name='Cancelled', default=False)),
('additional_speakers', models.ManyToManyField(blank=True, verbose_name='Addtional speakers', through='symposion_proposals.AdditionalSpeaker', to='symposion_speakers.Speaker')),
],
),
migrations.CreateModel(
name='ProposalKind',
fields=[
('id', models.AutoField(verbose_name='ID', primary_key=True, serialize=False, auto_created=True)),
('name', models.CharField(verbose_name='Name', max_length=100)),
('slug', models.SlugField(verbose_name='Slug')),
('section', models.ForeignKey(to='symposion_conference.Section', verbose_name='Section', related_name='proposal_kinds')),
],
),
migrations.CreateModel(
name='ProposalSection',
fields=[
('id', models.AutoField(verbose_name='ID', primary_key=True, serialize=False, auto_created=True)),
('start', models.DateTimeField(blank=True, verbose_name='Start', null=True)),
('end', models.DateTimeField(blank=True, verbose_name='End', null=True)),
('closed', models.NullBooleanField(verbose_name='Closed')),
('published', models.NullBooleanField(verbose_name='Published')),
('section', models.OneToOneField(to='symposion_conference.Section', verbose_name='Section')),
],
),
migrations.CreateModel(
name='SupportingDocument',
fields=[
('id', models.AutoField(verbose_name='ID', primary_key=True, serialize=False, auto_created=True)),
('created_at', models.DateTimeField(verbose_name='Created at', default=django.utils.timezone.now)),
('file', models.FileField(verbose_name='File', upload_to=symposion.proposals.models.uuid_filename)),
('description', models.CharField(verbose_name='Description', max_length=140)),
('proposal', models.ForeignKey(to='symposion_proposals.ProposalBase', verbose_name='Proposal', related_name='supporting_documents')),
('uploaded_by', models.ForeignKey(to=settings.AUTH_USER_MODEL, verbose_name='Uploaded by')),
],
),
migrations.AddField(
model_name='proposalbase',
name='kind',
field=models.ForeignKey(to='symposion_proposals.ProposalKind', verbose_name='Kind'),
),
migrations.AddField(
model_name='proposalbase',
name='speaker',
field=models.ForeignKey(to='symposion_speakers.Speaker', verbose_name='Speaker', related_name='proposals'),
),
migrations.AddField(
model_name='additionalspeaker',
name='proposalbase',
field=models.ForeignKey(to='symposion_proposals.ProposalBase', verbose_name='Proposalbase'),
),
migrations.AddField(
model_name='additionalspeaker',
name='speaker',
field=models.ForeignKey(to='symposion_speakers.Speaker', verbose_name='Speaker'),
),
migrations.AlterUniqueTogether(
name='additionalspeaker',
unique_together=set([('speaker', 'proposalbase')]),
),
]

View file

@ -15,10 +15,9 @@ from django.core.exceptions import ValidationError
import reversion
from markitup.fields import MarkupField
from model_utils.managers import InheritanceManager
from symposion.markdown_parser import parse
from symposion.conference.models import Section
from symposion.speakers.models import Speaker
@ -94,13 +93,14 @@ class ProposalBase(models.Model):
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 = models.TextField(
_("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>.")
)
additional_notes = MarkupField(
abstract_html = models.TextField(blank=True)
additional_notes = models.TextField(
_("Addtional Notes"),
blank=True,
help_text=_("Anything else you'd like the program committee to know when making their "
@ -108,6 +108,7 @@ class ProposalBase(models.Model):
"<a href='http://daringfireball.net/projects/markdown/basics' "
"target='_blank'>Markdown</a>.")
)
additional_notes_html = models.TextField(blank=True)
submitted = models.DateTimeField(
default=now,
editable=False,
@ -115,6 +116,9 @@ class ProposalBase(models.Model):
)
speaker = models.ForeignKey(Speaker, related_name="proposals", verbose_name=_("Speaker"))
# @@@ this validation used to exist as a validators keyword on additional_speakers
# M2M field but that is no longer supported by Django. Should be moved to
# the form level
def additional_speaker_validator(self, a_speaker):
if a_speaker.speaker.email == self.speaker.email:
raise ValidationError(_("%s is same as primary speaker.") % a_speaker.speaker.email)
@ -122,10 +126,14 @@ class ProposalBase(models.Model):
raise ValidationError(_("%s has already been in speakers.") % a_speaker.speaker.email)
additional_speakers = models.ManyToManyField(Speaker, through="AdditionalSpeaker",
blank=True, verbose_name=_("Addtional speakers"),
validators=[additional_speaker_validator])
blank=True, verbose_name=_("Addtional speakers"))
cancelled = models.BooleanField(default=False, verbose_name=_("Cancelled"))
def save(self, *args, **kwargs):
self.abstract_html = parse(self.abstract)
self.additional_notes_html = parse(self.additional_notes)
return super(ProposalBase, self).save(*args, **kwargs)
def can_edit(self):
return True

View file

@ -13,11 +13,12 @@ from django.views import static
from django.contrib import messages
from django.contrib.auth.models import User
from django.contrib.auth.decorators import login_required
from django.utils.translation import ugettext_lazy as _
from account.decorators import login_required
from account.models import EmailAddress
from symposion.proposals.models import (
ProposalBase, ProposalSection, ProposalKind
)
@ -58,7 +59,7 @@ def proposal_submit(request):
for kind in proposal_section.section.proposal_kinds.all():
kinds.append(kind)
return render(request, "proposals/proposal_submit.html", {
return render(request, "symposion/proposals/proposal_submit.html", {
"kinds": kinds,
})
@ -95,9 +96,9 @@ def proposal_submit_kind(request, kind_slug):
else:
form = form_class()
return render(request, "proposals/proposal_submit_kind.html", {
return render(request, "symposion/proposals/proposal_submit_kind.html", {
"kind": kind,
"form": form,
"proposal_form": form,
})
@ -178,7 +179,7 @@ def proposal_speaker_manage(request, pk):
"speakers": proposal.speakers(),
"add_speaker_form": add_speaker_form,
}
return render(request, "proposals/proposal_speaker_manage.html", ctx)
return render(request, "symposion/proposals/proposal_speaker_manage.html", ctx)
@login_required
@ -195,7 +196,7 @@ def proposal_edit(request, pk):
"title": "Proposal editing closed",
"body": "Proposal editing is closed for this session type."
}
return render(request, "proposals/proposal_error.html", ctx)
return render(request, "symposion/proposals/proposal_error.html", ctx)
form_class = get_form(settings.PROPOSAL_FORMS[proposal.kind.slug])
@ -223,7 +224,7 @@ def proposal_edit(request, pk):
else:
form = form_class(instance=proposal)
return render(request, "proposals/proposal_edit.html", {
return render(request, "symposion/proposals/proposal_edit.html", {
"proposal": proposal,
"form": form,
})
@ -276,7 +277,7 @@ def proposal_detail(request, pk):
else:
message_form = None
return render(request, "proposals/proposal_detail.html", {
return render(request, "symposion/proposals/proposal_detail.html", {
"proposal": proposal,
"message_form": message_form
})
@ -298,7 +299,7 @@ def proposal_cancel(request, pk):
messages.success(request, "%s has been cancelled" % proposal.title)
return redirect("dashboard")
return render(request, "proposals/proposal_cancel.html", {
return render(request, "symposion/proposals/proposal_cancel.html", {
"proposal": proposal,
})
@ -321,7 +322,7 @@ def proposal_leave(request, pk):
ctx = {
"proposal": proposal,
}
return render(request, "proposals/proposal_leave.html", ctx)
return render(request, "symposion/proposals/proposal_leave.html", ctx)
@login_required
@ -372,7 +373,7 @@ def document_create(request, proposal_pk):
else:
form = SupportingDocumentCreateForm()
return render(request, "proposals/document_create.html", {
return render(request, "symposion/proposals/document_create.html", {
"proposal": proposal,
"form": form,
})

View file

@ -2,8 +2,6 @@ from __future__ import unicode_literals
from django import forms
from django.utils.translation import ugettext_lazy as _
from markitup.widgets import MarkItUpWidget
from symposion.reviews.models import Review, Comment, ProposalMessage, VOTES
@ -11,7 +9,6 @@ class ReviewForm(forms.ModelForm):
class Meta:
model = Review
fields = ["vote", "comment"]
widgets = {"comment": MarkItUpWidget()}
def __init__(self, *args, **kwargs):
super(ReviewForm, self).__init__(*args, **kwargs)
@ -25,14 +22,12 @@ class ReviewCommentForm(forms.ModelForm):
class Meta:
model = Comment
fields = ["text"]
widgets = {"text": MarkItUpWidget()}
class SpeakerCommentForm(forms.ModelForm):
class Meta:
model = ProposalMessage
fields = ["message"]
widgets = {"message": MarkItUpWidget()}
class BulkPresentationForm(forms.Form):

View file

@ -0,0 +1,143 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import models, migrations
import django.db.models.deletion
from django.conf import settings
from decimal import Decimal
import datetime
class Migration(migrations.Migration):
dependencies = [
('symposion_proposals', '0001_initial'),
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]
operations = [
migrations.CreateModel(
name='Comment',
fields=[
('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True, serialize=False)),
('text', models.TextField(verbose_name='Text')),
('text_html', models.TextField(blank=True)),
('public', models.BooleanField(verbose_name='Public', default=False, choices=[(True, 'public'), (False, 'private')])),
('commented_at', models.DateTimeField(verbose_name='Commented at', default=datetime.datetime.now)),
('commenter', models.ForeignKey(verbose_name='Commenter', to=settings.AUTH_USER_MODEL)),
('proposal', models.ForeignKey(to='symposion_proposals.ProposalBase', verbose_name='Proposal', related_name='comments')),
],
options={
'verbose_name_plural': 'comments',
'verbose_name': 'comment',
},
),
migrations.CreateModel(
name='LatestVote',
fields=[
('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True, serialize=False)),
('vote', models.CharField(choices=[('+1', '+1 \u2014 Good proposal and I will argue for it to be accepted.'), ('+0', '+0 \u2014 OK proposal, but I will not argue for it to be accepted.'), ('\u22120', '\u22120 \u2014 Weak proposal, but I will not argue strongly against acceptance.'), ('\u22121', '\u22121 \u2014 Serious issues and I will argue to reject this proposal.')], verbose_name='Vote', max_length=2)),
('submitted_at', models.DateTimeField(editable=False, verbose_name='Submitted at', default=datetime.datetime.now)),
('proposal', models.ForeignKey(to='symposion_proposals.ProposalBase', verbose_name='Proposal', related_name='votes')),
('user', models.ForeignKey(verbose_name='User', to=settings.AUTH_USER_MODEL)),
],
options={
'verbose_name_plural': 'latest votes',
'verbose_name': 'latest vote',
},
),
migrations.CreateModel(
name='NotificationTemplate',
fields=[
('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True, serialize=False)),
('label', models.CharField(verbose_name='Label', max_length=100)),
('from_address', models.EmailField(verbose_name='From address', max_length=254)),
('subject', models.CharField(verbose_name='Subject', max_length=100)),
('body', models.TextField(verbose_name='Body')),
],
options={
'verbose_name_plural': 'notification templates',
'verbose_name': 'notification template',
},
),
migrations.CreateModel(
name='ProposalMessage',
fields=[
('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True, serialize=False)),
('message', models.TextField(verbose_name='Message')),
('message_html', models.TextField(blank=True)),
('submitted_at', models.DateTimeField(editable=False, verbose_name='Submitted at', default=datetime.datetime.now)),
('proposal', models.ForeignKey(to='symposion_proposals.ProposalBase', verbose_name='Proposal', related_name='messages')),
('user', models.ForeignKey(verbose_name='User', to=settings.AUTH_USER_MODEL)),
],
options={
'verbose_name_plural': 'proposal messages',
'verbose_name': 'proposal message',
'ordering': ['submitted_at'],
},
),
migrations.CreateModel(
name='ProposalResult',
fields=[
('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True, serialize=False)),
('score', models.DecimalField(decimal_places=2, verbose_name='Score', max_digits=5, default=Decimal('0.00'))),
('comment_count', models.PositiveIntegerField(verbose_name='Comment count', default=0)),
('vote_count', models.PositiveIntegerField(verbose_name='Vote count', default=0)),
('plus_one', models.PositiveIntegerField(verbose_name='Plus one', default=0)),
('plus_zero', models.PositiveIntegerField(verbose_name='Plus zero', default=0)),
('minus_zero', models.PositiveIntegerField(verbose_name='Minus zero', default=0)),
('minus_one', models.PositiveIntegerField(verbose_name='Minus one', default=0)),
('accepted', models.NullBooleanField(verbose_name='Accepted', default=None, choices=[(True, 'accepted'), (False, 'rejected'), (None, 'undecided')])),
('status', models.CharField(choices=[('accepted', 'accepted'), ('rejected', 'rejected'), ('undecided', 'undecided'), ('standby', 'standby')], verbose_name='Status', max_length=20, default='undecided')),
('proposal', models.OneToOneField(to='symposion_proposals.ProposalBase', verbose_name='Proposal', related_name='result')),
],
options={
'verbose_name_plural': 'proposal_results',
'verbose_name': 'proposal_result',
},
),
migrations.CreateModel(
name='ResultNotification',
fields=[
('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True, serialize=False)),
('timestamp', models.DateTimeField(verbose_name='Timestamp', default=datetime.datetime.now)),
('to_address', models.EmailField(verbose_name='To address', max_length=254)),
('from_address', models.EmailField(verbose_name='From address', max_length=254)),
('subject', models.CharField(verbose_name='Subject', max_length=100)),
('body', models.TextField(verbose_name='Body')),
('proposal', models.ForeignKey(to='symposion_proposals.ProposalBase', verbose_name='Proposal', related_name='notifications')),
('template', models.ForeignKey(to='symposion_reviews.NotificationTemplate', blank=True, verbose_name='Template', null=True, on_delete=django.db.models.deletion.SET_NULL)),
],
),
migrations.CreateModel(
name='Review',
fields=[
('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True, serialize=False)),
('vote', models.CharField(blank=True, verbose_name='Vote', max_length=2, choices=[('+1', '+1 \u2014 Good proposal and I will argue for it to be accepted.'), ('+0', '+0 \u2014 OK proposal, but I will not argue for it to be accepted.'), ('\u22120', '\u22120 \u2014 Weak proposal, but I will not argue strongly against acceptance.'), ('\u22121', '\u22121 \u2014 Serious issues and I will argue to reject this proposal.')])),
('comment', models.TextField(verbose_name='Comment')),
('comment_html', models.TextField(blank=True)),
('submitted_at', models.DateTimeField(editable=False, verbose_name='Submitted at', default=datetime.datetime.now)),
('proposal', models.ForeignKey(to='symposion_proposals.ProposalBase', verbose_name='Proposal', related_name='reviews')),
('user', models.ForeignKey(verbose_name='User', to=settings.AUTH_USER_MODEL)),
],
options={
'verbose_name_plural': 'reviews',
'verbose_name': 'review',
},
),
migrations.CreateModel(
name='ReviewAssignment',
fields=[
('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True, serialize=False)),
('origin', models.IntegerField(choices=[(0, 'auto-assigned, initial'), (1, 'opted-in'), (2, 'auto-assigned, later')], verbose_name='Origin')),
('assigned_at', models.DateTimeField(verbose_name='Assigned at', default=datetime.datetime.now)),
('opted_out', models.BooleanField(verbose_name='Opted out', default=False)),
('proposal', models.ForeignKey(verbose_name='Proposal', to='symposion_proposals.ProposalBase')),
('user', models.ForeignKey(verbose_name='User', to=settings.AUTH_USER_MODEL)),
],
),
migrations.AlterUniqueTogether(
name='latestvote',
unique_together=set([('proposal', 'user')]),
),
]

View file

View file

@ -4,26 +4,22 @@ from datetime import datetime
from decimal import Decimal
from django.db import models
from django.db.models import Q
from django.db.models import Q, F
from django.db.models.signals import post_save
from django.contrib.auth.models import User
from django.utils.translation import ugettext_lazy as _
from markitup.fields import MarkupField
from symposion.markdown_parser import parse
from symposion.proposals.models import ProposalBase
from symposion.schedule.models import Presentation
class ProposalScoreExpression(object):
def as_sql(self, qn, connection=None):
sql = "((3 * plus_one + plus_zero) - (minus_zero + 3 * minus_one))"
return sql, []
def prepare_database_save(self, unused):
return self
def score_expression():
return (
(3 * F("plus_one") + F("plus_zero")) -
(F("minus_zero") + 3 * F("minus_one"))
)
class Votes(object):
@ -97,9 +93,14 @@ class ProposalMessage(models.Model):
proposal = models.ForeignKey(ProposalBase, related_name="messages", verbose_name=_("Proposal"))
user = models.ForeignKey(User, verbose_name=_("User"))
message = MarkupField(verbose_name=_("Message"))
message = models.TextField(verbose_name=_("Message"))
message_html = models.TextField(blank=True)
submitted_at = models.DateTimeField(default=datetime.now, editable=False, verbose_name=_("Submitted at"))
def save(self, *args, **kwargs):
self.message_html = parse(self.message)
return super(ProposalMessage, self).save(*args, **kwargs)
class Meta:
ordering = ["submitted_at"]
verbose_name = _("proposal message")
@ -115,10 +116,12 @@ class Review(models.Model):
# No way to encode "-0" vs. "+0" into an IntegerField, and I don't feel
# like some complicated encoding system.
vote = models.CharField(max_length=2, blank=True, choices=VOTES.CHOICES, verbose_name=_("Vote"))
comment = MarkupField(verbose_name=_("Comment"))
comment = models.TextField(verbose_name=_("Comment"))
comment_html = models.TextField(blank=True)
submitted_at = models.DateTimeField(default=datetime.now, editable=False, verbose_name=_("Submitted at"))
def save(self, **kwargs):
self.comment_html = parse(self.comment)
if self.vote:
vote, created = LatestVote.objects.get_or_create(
proposal=self.proposal,
@ -258,7 +261,7 @@ class ProposalResult(models.Model):
vote=VOTES.MINUS_ONE
).count()
result.save()
cls._default_manager.filter(pk=result.pk).update(score=ProposalScoreExpression())
cls._default_manager.filter(pk=result.pk).update(score=score_expression())
def update_vote(self, vote, previous=None, removal=False):
mapping = {
@ -287,7 +290,7 @@ class ProposalResult(models.Model):
self.comment_count = models.F("comment_count") + 1
self.save()
model = self.__class__
model._default_manager.filter(pk=self.pk).update(score=ProposalScoreExpression())
model._default_manager.filter(pk=self.pk).update(score=score_expression())
class Meta:
verbose_name = _("proposal_result")
@ -297,7 +300,8 @@ class ProposalResult(models.Model):
class Comment(models.Model):
proposal = models.ForeignKey(ProposalBase, related_name="comments", verbose_name=_("Proposal"))
commenter = models.ForeignKey(User, verbose_name=_("Commenter"))
text = MarkupField(verbose_name=_("Text"))
text = models.TextField(verbose_name=_("Text"))
text_html = models.TextField(blank=True)
# Or perhaps more accurately, can the user see this comment.
public = models.BooleanField(choices=[(True, _("public")), (False, _("private"))], default=False, verbose_name=_("Public"))
@ -307,6 +311,10 @@ class Comment(models.Model):
verbose_name = _("comment")
verbose_name_plural = _("comments")
def save(self, *args, **kwargs):
self.comment_html = parse(self.comment)
return super(Comment, self).save(*args, **kwargs)
class NotificationTemplate(models.Model):

View file

@ -5,11 +5,13 @@ from django.shortcuts import render, redirect, get_object_or_404
from django.template import Context, Template
from django.views.decorators.http import require_POST
from django.contrib.auth.decorators import login_required
from account.decorators import login_required
# @@@ switch to pinax-teams
from symposion.teams.models import Team
from symposion.conf import settings
from symposion.proposals.models import ProposalBase, ProposalSection
from symposion.teams.models import Team
from symposion.utils.mail import send_email
from symposion.reviews.forms import ReviewForm, SpeakerCommentForm
@ -21,7 +23,7 @@ from symposion.reviews.models import (
def access_not_permitted(request):
return render(request, "reviews/access_not_permitted.html")
return render(request, "symposion/reviews/access_not_permitted.html")
def proposals_generator(request, queryset, user_pk=None, check_speaker=True):
@ -96,7 +98,7 @@ def review_section(request, section_slug, assigned=False, reviewed="all"):
"reviewed": reviewed,
}
return render(request, "reviews/review_list.html", ctx)
return render(request, "symposion/reviews/review_list.html", ctx)
@login_required
@ -120,7 +122,7 @@ def review_list(request, section_slug, user_pk):
ctx = {
"proposals": proposals,
}
return render(request, "reviews/review_list.html", ctx)
return render(request, "symposion/reviews/review_list.html", ctx)
@login_required
@ -164,7 +166,7 @@ def review_admin(request, section_slug):
"section_slug": section_slug,
"reviewers": reviewers(),
}
return render(request, "reviews/review_admin.html", ctx)
return render(request, "symposion/reviews/review_admin.html", ctx)
# FIXME: This view is too complex according to flake8
@ -273,7 +275,7 @@ def review_detail(request, pk):
reviews = Review.objects.filter(proposal=proposal).order_by("-submitted_at")
messages = proposal.messages.order_by("submitted_at")
return render(request, "reviews/review_detail.html", {
return render(request, "symposion/reviews/review_detail.html", {
"proposal": proposal,
"latest_vote": latest_vote,
"reviews": reviews,
@ -353,7 +355,7 @@ def review_status(request, section_slug=None, key=None):
else:
ctx["proposals"] = proposals
return render(request, "reviews/review_stats.html", ctx)
return render(request, "symposion/reviews/review_stats.html", ctx)
@login_required
@ -364,7 +366,7 @@ def review_assignments(request):
user=request.user,
opted_out=False
)
return render(request, "reviews/review_assignment.html", {
return render(request, "symposion/reviews/review_assignment.html", {
"assignments": assignments,
})
@ -398,7 +400,7 @@ def review_bulk_accept(request, section_slug):
else:
form = BulkPresentationForm()
return render(request, "reviews/review_bulk_accept.html", {
return render(request, "symposion/reviews/review_bulk_accept.html", {
"form": form,
})
@ -417,7 +419,7 @@ def result_notification(request, section_slug, status):
"proposals": proposals,
"notification_templates": notification_templates,
}
return render(request, "reviews/result_notification.html", ctx)
return render(request, "symposion/reviews/result_notification.html", ctx)
@login_required
@ -455,7 +457,7 @@ def result_notification_prepare(request, section_slug, status):
"proposals": proposals,
"proposal_pks": ",".join([str(pk) for pk in proposal_pks]),
}
return render(request, "reviews/result_notification_prepare.html", ctx)
return render(request, "symposion/reviews/result_notification_prepare.html", ctx)
@login_required

View file

@ -9,8 +9,6 @@ from django.contrib import messages
from django.db import IntegrityError, transaction
from django.db.models import Q
from markitup.widgets import MarkItUpWidget
from symposion.schedule.models import (Day, Presentation, Room, SlotKind, Slot,
SlotRoom)
@ -44,7 +42,6 @@ class SlotEditForm(forms.Form):
def build_content_override_field(self):
kwargs = {
"label": "Content",
"widget": MarkItUpWidget(),
"required": False,
"initial": self.slot.content_override,
}

View file

@ -0,0 +1,186 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import models, migrations
import datetime
from django.conf import settings
class Migration(migrations.Migration):
dependencies = [
('symposion_speakers', '__first__'),
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('symposion_conference', '0001_initial'),
('symposion_proposals', '0001_initial'),
]
operations = [
migrations.CreateModel(
name='Day',
fields=[
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
('date', models.DateField(verbose_name='Date')),
],
options={
'ordering': ['date'],
'verbose_name': 'date',
'verbose_name_plural': 'dates',
},
),
migrations.CreateModel(
name='Presentation',
fields=[
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
('title', models.CharField(max_length=100, verbose_name='Title')),
('description', models.TextField(verbose_name='Description')),
('description_html', models.TextField(blank=True)),
('abstract', models.TextField(verbose_name='Abstract')),
('abstract_html', models.TextField(blank=True)),
('cancelled', models.BooleanField(default=False, verbose_name='Cancelled')),
('additional_speakers', models.ManyToManyField(related_name='copresentations', to='symposion_speakers.Speaker', verbose_name='Additional speakers', blank=True)),
('proposal_base', models.OneToOneField(to='symposion_proposals.ProposalBase', related_name='presentation', verbose_name='Proposal base')),
('section', models.ForeignKey(to='symposion_conference.Section', related_name='presentations', verbose_name='Section')),
],
options={
'ordering': ['slot'],
'verbose_name': 'presentation',
'verbose_name_plural': 'presentations',
},
),
migrations.CreateModel(
name='Room',
fields=[
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
('name', models.CharField(max_length=65, verbose_name='Name')),
('order', models.PositiveIntegerField(verbose_name='Order')),
],
options={
'verbose_name': 'Room',
'verbose_name_plural': 'Rooms',
},
),
migrations.CreateModel(
name='Schedule',
fields=[
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
('published', models.BooleanField(default=True, verbose_name='Published')),
('hidden', models.BooleanField(default=False, verbose_name='Hide schedule from overall conference view')),
('section', models.OneToOneField(to='symposion_conference.Section', verbose_name='Section')),
],
options={
'ordering': ['section'],
'verbose_name': 'Schedule',
'verbose_name_plural': 'Schedules',
},
),
migrations.CreateModel(
name='Session',
fields=[
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
('day', models.ForeignKey(to='symposion_schedule.Day', related_name='sessions', verbose_name='Day')),
],
options={
'verbose_name': 'Session',
'verbose_name_plural': 'Sessions',
},
),
migrations.CreateModel(
name='SessionRole',
fields=[
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
('role', models.IntegerField(verbose_name='Role', choices=[(1, 'Session Chair'), (2, 'Session Runner')])),
('status', models.NullBooleanField(verbose_name='Status')),
('submitted', models.DateTimeField(default=datetime.datetime.now)),
('session', models.ForeignKey(to='symposion_schedule.Session', verbose_name='Session')),
('user', models.ForeignKey(to=settings.AUTH_USER_MODEL, verbose_name='User')),
],
options={
'verbose_name': 'Session role',
'verbose_name_plural': 'Session roles',
},
),
migrations.CreateModel(
name='Slot',
fields=[
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
('start', models.TimeField(verbose_name='Start')),
('end', models.TimeField(verbose_name='End')),
('content_override', models.TextField(verbose_name='Content override', blank=True)),
('content_override_html', models.TextField(blank=True)),
('day', models.ForeignKey(to='symposion_schedule.Day', verbose_name='Day')),
],
options={
'ordering': ['day', 'start', 'end'],
'verbose_name': 'slot',
'verbose_name_plural': 'slots',
},
),
migrations.CreateModel(
name='SlotKind',
fields=[
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
('label', models.CharField(max_length=50, verbose_name='Label')),
('schedule', models.ForeignKey(to='symposion_schedule.Schedule', verbose_name='schedule')),
],
options={
'verbose_name': 'Slot kind',
'verbose_name_plural': 'Slot kinds',
},
),
migrations.CreateModel(
name='SlotRoom',
fields=[
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
('room', models.ForeignKey(to='symposion_schedule.Room', verbose_name='Room')),
('slot', models.ForeignKey(to='symposion_schedule.Slot', verbose_name='Slot')),
],
options={
'ordering': ['slot', 'room__order'],
'verbose_name': 'Slot room',
'verbose_name_plural': 'Slot rooms',
},
),
migrations.AddField(
model_name='slot',
name='kind',
field=models.ForeignKey(to='symposion_schedule.SlotKind', verbose_name='Kind'),
),
migrations.AddField(
model_name='session',
name='slots',
field=models.ManyToManyField(related_name='sessions', verbose_name='Slots', to='symposion_schedule.Slot'),
),
migrations.AddField(
model_name='room',
name='schedule',
field=models.ForeignKey(to='symposion_schedule.Schedule', verbose_name='Schedule'),
),
migrations.AddField(
model_name='presentation',
name='slot',
field=models.OneToOneField(to='symposion_schedule.Slot', related_name='content_ptr', blank=True, null=True, verbose_name='Slot'),
),
migrations.AddField(
model_name='presentation',
name='speaker',
field=models.ForeignKey(to='symposion_speakers.Speaker', related_name='presentations', verbose_name='Speaker'),
),
migrations.AddField(
model_name='day',
name='schedule',
field=models.ForeignKey(to='symposion_schedule.Schedule', verbose_name='Schedule'),
),
migrations.AlterUniqueTogether(
name='slotroom',
unique_together=set([('slot', 'room')]),
),
migrations.AlterUniqueTogether(
name='sessionrole',
unique_together=set([('session', 'user', 'role')]),
),
migrations.AlterUniqueTogether(
name='day',
unique_together=set([('schedule', 'date')]),
),
]

View file

@ -8,8 +8,7 @@ from django.db import models
from django.utils.encoding import python_2_unicode_compatible
from django.utils.translation import ugettext_lazy as _
from markitup.fields import MarkupField
from symposion.markdown_parser import parse
from symposion.proposals.models import ProposalBase
from symposion.conference.models import Section
from symposion.speakers.models import Speaker
@ -87,7 +86,12 @@ class Slot(models.Model):
kind = models.ForeignKey(SlotKind, verbose_name=_("Kind"))
start = models.TimeField(verbose_name=_("Start"))
end = models.TimeField(verbose_name=_("End"))
content_override = MarkupField(blank=True, verbose_name=_("Content override"))
content_override = models.TextField(blank=True, verbose_name=_("Content override"))
content_override_html = models.TextField(blank=True)
def save(self, *args, **kwargs):
self.content_override_html = parse(self.content_override)
return super(Slot, self).save(*args, **kwargs)
def assign(self, content):
"""
@ -179,8 +183,10 @@ class Presentation(models.Model):
slot = models.OneToOneField(Slot, null=True, blank=True, related_name="content_ptr", verbose_name=_("Slot"))
title = models.CharField(max_length=100, verbose_name=_("Title"))
description = MarkupField(verbose_name=_("Description"))
abstract = MarkupField(verbose_name=_("Abstract"))
description = models.TextField(verbose_name=_("Description"))
description_html = models.TextField(blank=True)
abstract = models.TextField(verbose_name=_("Abstract"))
abstract_html = models.TextField(blank=True)
speaker = models.ForeignKey(Speaker, related_name="presentations", verbose_name=_("Speaker"))
additional_speakers = models.ManyToManyField(Speaker, related_name="copresentations",
blank=True, verbose_name=_("Additional speakers"))
@ -188,6 +194,11 @@ class Presentation(models.Model):
proposal_base = models.OneToOneField(ProposalBase, related_name="presentation", verbose_name=_("Proposal base"))
section = models.ForeignKey(Section, related_name="presentations", verbose_name=_("Section"))
def save(self, *args, **kwargs):
self.description_html = parse(self.description)
self.abstract_html = parse(self.abstract)
return super(Presentation, self).save(*args, **kwargs)
@property
def number(self):
return self.proposal.number

View file

@ -22,7 +22,6 @@ try:
"django.contrib.contenttypes",
"django.contrib.sites",
"markitup",
"reversion",
"symposion",
@ -34,8 +33,6 @@ try:
],
SITE_ID=1,
NOSE_ARGS=['-s'],
MARKITUP_FILTER=('django.contrib.markup.templatetags.markup.textile', {}),
)
try:

View file

@ -6,11 +6,12 @@ from django.http import Http404, HttpResponse
from django.shortcuts import render, get_object_or_404, redirect
from django.template import loader, Context
from django.contrib.auth.decorators import login_required
from django.contrib.auth.models import User
from django.contrib import messages
from django.contrib.sites.models import Site
from account.decorators import login_required
from symposion.schedule.forms import SlotEditForm, ScheduleSectionForm
from symposion.schedule.models import Schedule, Day, Slot, Presentation, Session, SessionRole
from symposion.schedule.timetable import TimeTable
@ -47,7 +48,7 @@ def schedule_conference(request):
ctx = {
"sections": sections,
}
return render(request, "schedule/schedule_conference.html", ctx)
return render(request, "symposion/schedule/schedule_conference.html", ctx)
def schedule_detail(request, slug=None):
@ -63,7 +64,7 @@ def schedule_detail(request, slug=None):
"schedule": schedule,
"days": days,
}
return render(request, "schedule/schedule_detail.html", ctx)
return render(request, "symposion/schedule/schedule_detail.html", ctx)
def schedule_list(request, slug=None):
@ -76,7 +77,7 @@ def schedule_list(request, slug=None):
"schedule": schedule,
"presentations": presentations,
}
return render(request, "schedule/schedule_list.html", ctx)
return render(request, "symposion/schedule/schedule_list.html", ctx)
def schedule_list_csv(request, slug=None):
@ -92,7 +93,7 @@ def schedule_list_csv(request, slug=None):
file_slug = "presentations"
response["Content-Disposition"] = 'attachment; filename="%s.csv"' % file_slug
response.write(loader.get_template("schedule/schedule_list.csv").render(Context({
response.write(loader.get_template("symposion/schedule/schedule_list.csv").render(Context({
"presentations": presentations,
})))
@ -126,7 +127,7 @@ def schedule_edit(request, slug=None):
"days": days,
"form": form
}
return render(request, "schedule/schedule_edit.html", ctx)
return render(request, "symposion/schedule/schedule_edit.html", ctx)
@login_required
@ -160,7 +161,7 @@ def schedule_slot_edit(request, slug, slot_pk):
"form": form,
"slot": slot,
}
return render(request, "schedule/_slot_edit.html", ctx)
return render(request, "symposion/schedule/_slot_edit.html", ctx)
def schedule_presentation_detail(request, pk):
@ -175,7 +176,7 @@ def schedule_presentation_detail(request, pk):
"presentation": presentation,
"schedule": schedule,
}
return render(request, "schedule/presentation_detail.html", ctx)
return render(request, "symposion/schedule/presentation_detail.html", ctx)
def schedule_json(request):
@ -230,7 +231,7 @@ def schedule_json(request):
data.append(slot_data)
return HttpResponse(
json.dumps({'schedule': data}),
json.dumps({"schedule": data}),
content_type="application/json"
)
@ -238,7 +239,7 @@ def schedule_json(request):
def session_list(request):
sessions = Session.objects.all().order_by('pk')
return render(request, "schedule/session_list.html", {
return render(request, "symposion/schedule/session_list.html", {
"sessions": sessions,
})
@ -306,7 +307,7 @@ def session_detail(request, session_id):
return redirect("schedule_session_detail", session_id)
return render(request, "schedule/session_detail.html", {
return render(request, "symposion/schedule/session_detail.html", {
"session": session,
"chair": chair,
"chair_denied": chair_denied,

View file

@ -1,8 +1,6 @@
from __future__ import unicode_literals
from django import forms
from markitup.widgets import MarkItUpWidget
from symposion.speakers.models import Speaker
@ -15,6 +13,3 @@ class SpeakerForm(forms.ModelForm):
"biography",
"photo",
]
widgets = {
"biography": MarkItUpWidget(),
}

View file

@ -0,0 +1,36 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import models, migrations
import datetime
from django.conf import settings
class Migration(migrations.Migration):
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]
operations = [
migrations.CreateModel(
name='Speaker',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, verbose_name='ID', serialize=False)),
('name', models.CharField(verbose_name='Name', help_text='As you would like it to appear in the conference program.', max_length=100)),
('biography', models.TextField(verbose_name='Biography', blank=True, help_text="A little bit about you. Edit using <a href='http://warpedvisions.org/projects/markdown-cheat-sheet/target='_blank'>Markdown</a>.")),
('biography_html', models.TextField(blank=True)),
('photo', models.ImageField(verbose_name='Photo', upload_to='speaker_photos', blank=True)),
('annotation', models.TextField(verbose_name='Annotation')),
('invite_email', models.CharField(verbose_name='Invite_email', unique=True, db_index=True, max_length=200, null=True)),
('invite_token', models.CharField(verbose_name='Invite token', db_index=True, max_length=40)),
('created', models.DateTimeField(editable=False, verbose_name='Created', default=datetime.datetime.now)),
('user', models.OneToOneField(null=True, related_name='speaker_profile', verbose_name='User', to=settings.AUTH_USER_MODEL)),
],
options={
'verbose_name': 'Speaker',
'verbose_name_plural': 'Speakers',
'ordering': ['name'],
},
),
]

View file

@ -9,7 +9,7 @@ from django.utils.translation import ugettext_lazy as _
from django.contrib.auth.models import User
from markitup.fields import MarkupField
from symposion.markdown_parser import parse
@python_2_unicode_compatible
@ -24,10 +24,11 @@ class Speaker(models.Model):
name = models.CharField(verbose_name=_("Name"), max_length=100,
help_text=_("As you would like it to appear in the"
" conference program."))
biography = MarkupField(blank=True, help_text=_("A little bit about you. Edit using "
biography = models.TextField(blank=True, help_text=_("A little bit about you. Edit using "
"<a href='http://warpedvisions.org/projects/"
"markdown-cheat-sheet/target='_blank'>"
"Markdown</a>."), verbose_name=_("Biography"))
biography_html = models.TextField(blank=True)
photo = models.ImageField(upload_to="speaker_photos", blank=True, verbose_name=_("Photo"))
annotation = models.TextField(verbose_name=_("Annotation")) # staff only
invite_email = models.CharField(max_length=200, unique=True, null=True, db_index=True, verbose_name=_("Invite_email"))
@ -43,6 +44,10 @@ class Speaker(models.Model):
verbose_name = _("Speaker")
verbose_name_plural = _("Speakers")
def save(self, *args, **kwargs):
self.biography_html = parse(self.biography)
return super(Speaker, self).save(*args, **kwargs)
def __str__(self):
if self.user:
return self.name

View file

@ -4,10 +4,11 @@ from django.http import Http404
from django.shortcuts import render, redirect, get_object_or_404
from django.contrib import messages
from django.contrib.auth.decorators import login_required
from django.contrib.auth.models import User
from django.utils.translation import ugettext_lazy as _
from account.decorators import login_required
from symposion.proposals.models import ProposalBase
from symposion.speakers.forms import SpeakerForm
from symposion.speakers.models import Speaker
@ -39,9 +40,8 @@ def speaker_create(request):
return redirect("dashboard")
else:
form = SpeakerForm(initial={"name": request.user.get_full_name()})
return render(request, "speakers/speaker_create.html", {
"form": form,
return render(request, "symposion/speakers/speaker_create.html", {
"speaker_form": form,
})
@ -68,8 +68,8 @@ def speaker_create_staff(request, pk):
else:
form = SpeakerForm(initial={"name": user.get_full_name()})
return render(request, "speakers/speaker_create.html", {
"form": form,
return render(request, "symposion/speakers/speaker_create.html", {
"speaker_form": form,
})
@ -121,8 +121,8 @@ def speaker_edit(request, pk=None):
else:
form = SpeakerForm(instance=speaker)
return render(request, "speakers/speaker_edit.html", {
"form": form,
return render(request, "symposion/speakers/speaker_edit.html", {
"speaker_form": form,
})
@ -132,7 +132,7 @@ def speaker_profile(request, pk):
if not presentations and not request.user.is_staff:
raise Http404()
return render(request, "speakers/speaker_profile.html", {
return render(request, "symposion/speakers/speaker_profile.html", {
"speaker": speaker,
"presentations": presentations,
})

View file

@ -0,0 +1,115 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import models, migrations
from django.conf import settings
import datetime
class Migration(migrations.Migration):
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('symposion_conference', '0001_initial'),
]
operations = [
migrations.CreateModel(
name='Benefit',
fields=[
('id', models.AutoField(serialize=False, auto_created=True, primary_key=True, verbose_name='ID')),
('name', models.CharField(verbose_name='Name', max_length=100)),
('description', models.TextField(blank=True, verbose_name='Description')),
('type', models.CharField(default='simple', choices=[('text', 'Text'), ('file', 'File'), ('richtext', 'Rich Text'), ('weblogo', 'Web Logo'), ('simple', 'Simple'), ('option', 'Option')], verbose_name='Type', max_length=10)),
('content_type', models.CharField(default='simple', choices=[('simple', 'Simple'), ('listing_text_af', 'Listing Text (Afrikaans)'), ('listing_text_ar', 'Listing Text (Arabic)'), ('listing_text_ast', 'Listing Text (Asturian)'), ('listing_text_az', 'Listing Text (Azerbaijani)'), ('listing_text_bg', 'Listing Text (Bulgarian)'), ('listing_text_be', 'Listing Text (Belarusian)'), ('listing_text_bn', 'Listing Text (Bengali)'), ('listing_text_br', 'Listing Text (Breton)'), ('listing_text_bs', 'Listing Text (Bosnian)'), ('listing_text_ca', 'Listing Text (Catalan)'), ('listing_text_cs', 'Listing Text (Czech)'), ('listing_text_cy', 'Listing Text (Welsh)'), ('listing_text_da', 'Listing Text (Danish)'), ('listing_text_de', 'Listing Text (German)'), ('listing_text_el', 'Listing Text (Greek)'), ('listing_text_en', 'Listing Text (English)'), ('listing_text_en-au', 'Listing Text (Australian English)'), ('listing_text_en-gb', 'Listing Text (British English)'), ('listing_text_eo', 'Listing Text (Esperanto)'), ('listing_text_es', 'Listing Text (Spanish)'), ('listing_text_es-ar', 'Listing Text (Argentinian Spanish)'), ('listing_text_es-mx', 'Listing Text (Mexican Spanish)'), ('listing_text_es-ni', 'Listing Text (Nicaraguan Spanish)'), ('listing_text_es-ve', 'Listing Text (Venezuelan Spanish)'), ('listing_text_et', 'Listing Text (Estonian)'), ('listing_text_eu', 'Listing Text (Basque)'), ('listing_text_fa', 'Listing Text (Persian)'), ('listing_text_fi', 'Listing Text (Finnish)'), ('listing_text_fr', 'Listing Text (French)'), ('listing_text_fy', 'Listing Text (Frisian)'), ('listing_text_ga', 'Listing Text (Irish)'), ('listing_text_gl', 'Listing Text (Galician)'), ('listing_text_he', 'Listing Text (Hebrew)'), ('listing_text_hi', 'Listing Text (Hindi)'), ('listing_text_hr', 'Listing Text (Croatian)'), ('listing_text_hu', 'Listing Text (Hungarian)'), ('listing_text_ia', 'Listing Text (Interlingua)'), ('listing_text_id', 'Listing Text (Indonesian)'), ('listing_text_io', 'Listing Text (Ido)'), ('listing_text_is', 'Listing Text (Icelandic)'), ('listing_text_it', 'Listing Text (Italian)'), ('listing_text_ja', 'Listing Text (Japanese)'), ('listing_text_ka', 'Listing Text (Georgian)'), ('listing_text_kk', 'Listing Text (Kazakh)'), ('listing_text_km', 'Listing Text (Khmer)'), ('listing_text_kn', 'Listing Text (Kannada)'), ('listing_text_ko', 'Listing Text (Korean)'), ('listing_text_lb', 'Listing Text (Luxembourgish)'), ('listing_text_lt', 'Listing Text (Lithuanian)'), ('listing_text_lv', 'Listing Text (Latvian)'), ('listing_text_mk', 'Listing Text (Macedonian)'), ('listing_text_ml', 'Listing Text (Malayalam)'), ('listing_text_mn', 'Listing Text (Mongolian)'), ('listing_text_mr', 'Listing Text (Marathi)'), ('listing_text_my', 'Listing Text (Burmese)'), ('listing_text_nb', 'Listing Text (Norwegian Bokmal)'), ('listing_text_ne', 'Listing Text (Nepali)'), ('listing_text_nl', 'Listing Text (Dutch)'), ('listing_text_nn', 'Listing Text (Norwegian Nynorsk)'), ('listing_text_os', 'Listing Text (Ossetic)'), ('listing_text_pa', 'Listing Text (Punjabi)'), ('listing_text_pl', 'Listing Text (Polish)'), ('listing_text_pt', 'Listing Text (Portuguese)'), ('listing_text_pt-br', 'Listing Text (Brazilian Portuguese)'), ('listing_text_ro', 'Listing Text (Romanian)'), ('listing_text_ru', 'Listing Text (Russian)'), ('listing_text_sk', 'Listing Text (Slovak)'), ('listing_text_sl', 'Listing Text (Slovenian)'), ('listing_text_sq', 'Listing Text (Albanian)'), ('listing_text_sr', 'Listing Text (Serbian)'), ('listing_text_sr-latn', 'Listing Text (Serbian Latin)'), ('listing_text_sv', 'Listing Text (Swedish)'), ('listing_text_sw', 'Listing Text (Swahili)'), ('listing_text_ta', 'Listing Text (Tamil)'), ('listing_text_te', 'Listing Text (Telugu)'), ('listing_text_th', 'Listing Text (Thai)'), ('listing_text_tr', 'Listing Text (Turkish)'), ('listing_text_tt', 'Listing Text (Tatar)'), ('listing_text_udm', 'Listing Text (Udmurt)'), ('listing_text_uk', 'Listing Text (Ukrainian)'), ('listing_text_ur', 'Listing Text (Urdu)'), ('listing_text_vi', 'Listing Text (Vietnamese)'), ('listing_text_zh-cn', 'Listing Text (Simplified Chinese)'), ('listing_text_zh-hans', 'Listing Text (Simplified Chinese)'), ('listing_text_zh-hant', 'Listing Text (Traditional Chinese)'), ('listing_text_zh-tw', 'Listing Text (Traditional Chinese)')], verbose_name='content type', max_length=20)),
],
),
migrations.CreateModel(
name='BenefitLevel',
fields=[
('id', models.AutoField(serialize=False, auto_created=True, primary_key=True, verbose_name='ID')),
('max_words', models.PositiveIntegerField(blank=True, verbose_name='Max words', null=True)),
('other_limits', models.CharField(blank=True, verbose_name='Other limits', max_length=200)),
('benefit', models.ForeignKey(to='symposion_sponsorship.Benefit', related_name='benefit_levels', verbose_name='Benefit')),
],
options={
'verbose_name_plural': 'Benefit levels',
'ordering': ['level'],
'verbose_name': 'Benefit level',
},
),
migrations.CreateModel(
name='Sponsor',
fields=[
('id', models.AutoField(serialize=False, auto_created=True, primary_key=True, verbose_name='ID')),
('name', models.CharField(verbose_name='Sponsor Name', max_length=100)),
('display_url', models.URLField(blank=True, verbose_name='display URL')),
('external_url', models.URLField(verbose_name='External URL')),
('annotation', models.TextField(blank=True, verbose_name='Annotation')),
('contact_name', models.CharField(verbose_name='Contact Name', max_length=100)),
('contact_email', models.EmailField(verbose_name='Contact Email', max_length=254)),
('added', models.DateTimeField(default=datetime.datetime.now, verbose_name='added')),
('active', models.BooleanField(default=False, verbose_name='active')),
('web_logo_benefit', models.NullBooleanField(verbose_name='Web logo benefit', help_text='Web logo benefit is complete')),
('print_logo_benefit', models.NullBooleanField(verbose_name='Print logo benefit', help_text='Print logo benefit is complete')),
('print_description_benefit', models.NullBooleanField(verbose_name='Print description benefit', help_text='Print description benefit is complete')),
('company_description_benefit', models.NullBooleanField(verbose_name='Company description benefit', help_text='Company description benefit is complete')),
('applicant', models.ForeignKey(to=settings.AUTH_USER_MODEL, null=True, related_name='sponsorships', verbose_name='Applicant')),
],
options={
'verbose_name_plural': 'Sponsors',
'ordering': ['name'],
'verbose_name': 'Sponsor',
},
),
migrations.CreateModel(
name='SponsorBenefit',
fields=[
('id', models.AutoField(serialize=False, auto_created=True, primary_key=True, verbose_name='ID')),
('active', models.BooleanField(default=True, verbose_name='Active')),
('max_words', models.PositiveIntegerField(blank=True, verbose_name='Max words', null=True)),
('other_limits', models.CharField(blank=True, verbose_name='Other limits', max_length=200)),
('text', models.TextField(blank=True, verbose_name='Text')),
('upload', models.FileField(blank=True, verbose_name='File', upload_to='sponsor_files')),
('is_complete', models.NullBooleanField(verbose_name='Complete?', help_text='True - benefit complete; False - benefit incomplete; Null - n/a')),
('benefit', models.ForeignKey(to='symposion_sponsorship.Benefit', related_name='sponsor_benefits', verbose_name='Benefit')),
('sponsor', models.ForeignKey(to='symposion_sponsorship.Sponsor', related_name='sponsor_benefits', verbose_name='Sponsor')),
],
options={
'verbose_name_plural': 'Sponsor benefits',
'ordering': ['-active'],
'verbose_name': 'Sponsor benefit',
},
),
migrations.CreateModel(
name='SponsorLevel',
fields=[
('id', models.AutoField(serialize=False, auto_created=True, primary_key=True, verbose_name='ID')),
('name', models.CharField(verbose_name='Name', max_length=100)),
('order', models.IntegerField(default=0, verbose_name='Order')),
('cost', models.PositiveIntegerField(verbose_name='Cost')),
('description', models.TextField(blank=True, verbose_name='Description', help_text='This is private.')),
('conference', models.ForeignKey(to='symposion_conference.Conference', verbose_name='Conference')),
],
options={
'verbose_name_plural': 'Sponsor levels',
'ordering': ['conference', 'order'],
'verbose_name': 'Sponsor level',
},
),
migrations.AddField(
model_name='sponsor',
name='level',
field=models.ForeignKey(to='symposion_sponsorship.SponsorLevel', verbose_name='level'),
),
migrations.AddField(
model_name='sponsor',
name='sponsor_logo',
field=models.ForeignKey(blank=True, to='symposion_sponsorship.SponsorBenefit', null=True, related_name='+', verbose_name='Sponsor logo', editable=False),
),
migrations.AddField(
model_name='benefitlevel',
name='level',
field=models.ForeignKey(to='symposion_sponsorship.SponsorLevel', related_name='benefit_levels', verbose_name='Level'),
),
]

View file

@ -4,7 +4,7 @@ from django.views.generic import TemplateView
urlpatterns = patterns(
"symposion.sponsorship.views",
url(r"^$", TemplateView.as_view(template_name="sponsorship/list.html"), name="sponsor_list"),
url(r"^$", TemplateView.as_view(template_name="symposion/sponsorship/list.html"), name="sponsor_list"),
url(r"^apply/$", "sponsor_apply", name="sponsor_apply"),
url(r"^add/$", "sponsor_add", name="sponsor_add"),
url(r"^ziplogos/$", "sponsor_zip_logo_files", name="sponsor_zip_logos"),

View file

@ -9,12 +9,13 @@ from zipfile import ZipFile, ZipInfo
from django.conf import settings
from django.contrib import messages
from django.contrib.admin.views.decorators import staff_member_required
from django.contrib.auth.decorators import login_required
from django.http import Http404, HttpResponse
from django.shortcuts import render_to_response, redirect, get_object_or_404
from django.template import RequestContext
from django.utils.translation import ugettext_lazy as _
from account.decorators import login_required
from symposion.sponsorship.forms import SponsorApplicationForm, \
SponsorDetailsForm, SponsorBenefitsFormSet
from symposion.sponsorship.models import Benefit, Sponsor, SponsorBenefit, \
@ -43,7 +44,7 @@ def sponsor_apply(request):
else:
form = SponsorApplicationForm(user=request.user)
return render_to_response("sponsorship/apply.html", {
return render_to_response("symposion/sponsorship/apply.html", {
"form": form,
}, context_instance=RequestContext(request))
@ -63,7 +64,7 @@ def sponsor_add(request):
else:
form = SponsorApplicationForm(user=request.user)
return render_to_response("sponsorship/add.html", {
return render_to_response("symposion/sponsorship/add.html", {
"form": form,
}, context_instance=RequestContext(request))
@ -96,7 +97,7 @@ def sponsor_detail(request, pk):
form = SponsorDetailsForm(instance=sponsor)
formset = SponsorBenefitsFormSet(**formset_kwargs)
return render_to_response("sponsorship/detail.html", {
return render_to_response("symposion/sponsorship/detail.html", {
"sponsor": sponsor,
"form": form,
"formset": formset,

View file

@ -0,0 +1,56 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import models, migrations
import datetime
from django.conf import settings
class Migration(migrations.Migration):
dependencies = [
('auth', '0006_require_contenttypes_0002'),
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]
operations = [
migrations.CreateModel(
name='Membership',
fields=[
('id', models.AutoField(serialize=False, auto_created=True, primary_key=True, verbose_name='ID')),
('state', models.CharField(max_length=20, choices=[('applied', 'applied'), ('invited', 'invited'), ('declined', 'declined'), ('rejected', 'rejected'), ('member', 'member'), ('manager', 'manager')], verbose_name='State')),
('message', models.TextField(blank=True, verbose_name='Message')),
],
options={
'verbose_name_plural': 'Memberships',
'verbose_name': 'Membership',
},
),
migrations.CreateModel(
name='Team',
fields=[
('id', models.AutoField(serialize=False, auto_created=True, primary_key=True, verbose_name='ID')),
('slug', models.SlugField(unique=True, verbose_name='Slug')),
('name', models.CharField(max_length=100, verbose_name='Name')),
('description', models.TextField(blank=True, verbose_name='Description')),
('access', models.CharField(max_length=20, choices=[('open', 'open'), ('application', 'by application'), ('invitation', 'by invitation')], verbose_name='Access')),
('created', models.DateTimeField(editable=False, default=datetime.datetime.now, verbose_name='Created')),
('manager_permissions', models.ManyToManyField(related_name='manager_teams', blank=True, to='auth.Permission', verbose_name='Manager permissions')),
('permissions', models.ManyToManyField(related_name='member_teams', blank=True, to='auth.Permission', verbose_name='Permissions')),
],
options={
'verbose_name_plural': 'Teams',
'verbose_name': 'Team',
},
),
migrations.AddField(
model_name='membership',
name='team',
field=models.ForeignKey(verbose_name='Team', to='teams.Team', related_name='memberships'),
),
migrations.AddField(
model_name='membership',
name='user',
field=models.ForeignKey(verbose_name='User', to=settings.AUTH_USER_MODEL, related_name='memberships'),
),
]

View file

View file

@ -2,12 +2,12 @@ from __future__ import unicode_literals
from django.http import Http404, HttpResponseNotAllowed
from django.shortcuts import render, redirect, get_object_or_404
from django.contrib.auth.decorators import login_required
from django.contrib import messages
from symposion.utils.mail import send_email
from django.utils.translation import ugettext_lazy as _
from account.decorators import login_required
from symposion.utils.mail import send_email
from symposion.teams.forms import TeamInvitationForm
from symposion.teams.models import Team, Membership
@ -75,7 +75,7 @@ def team_detail(request, slug):
else:
form = None
return render(request, "teams/team_detail.html", {
return render(request, "symposion/teams/team_detail.html", {
"team": team,
"state": state,
"invite_form": form,

View file

@ -17,10 +17,10 @@ def send_email(to, kind, **kwargs):
ctx.update(kwargs.get("context", {}))
subject = "[%s] %s" % (
current_site.name,
render_to_string("emails/%s/subject.txt" % kind, ctx).strip()
render_to_string("symposion/emails/%s/subject.txt" % kind, ctx).strip()
)
message_html = render_to_string("emails/%s/message.html" % kind, ctx)
message_html = render_to_string("symposion/emails/%s/message.html" % kind, ctx)
message_plaintext = strip_tags(message_html)
from_email = settings.DEFAULT_FROM_EMAIL

View file

@ -1,53 +1,9 @@
from __future__ import unicode_literals
import hashlib
import random
from django.shortcuts import render, redirect
from django.contrib.auth.models import User
from django.contrib.auth.decorators import login_required
import account.views
import symposion.forms
class SignupView(account.views.SignupView):
form_class = symposion.forms.SignupForm
form_kwargs = {
"prefix": "signup",
}
def create_user(self, form, commit=True):
user_kwargs = {
"first_name": form.cleaned_data["first_name"],
"last_name": form.cleaned_data["last_name"]
}
return super(SignupView, self).create_user(form, commit=commit,
**user_kwargs)
def generate_username(self, form):
def random_username():
h = hashlib.sha1(form.cleaned_data["email"]).hexdigest()[:25]
# don't ask
n = random.randint(1, (10 ** (5 - 1)) - 1)
return "%s%d" % (h, n)
while True:
try:
username = random_username()
User.objects.get(username=username)
except User.DoesNotExist:
break
return username
class LoginView(account.views.LoginView):
form_class = account.forms.LoginEmailForm
form_kwargs = {
"prefix": "login",
}
from account.decorators import login_required
@login_required