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:
parent
8b4282a48e
commit
11f697d137
36 changed files with 821 additions and 222 deletions
|
@ -1,13 +1,12 @@
|
||||||
Django>=1.7.1
|
Django==1.8.5
|
||||||
django-appconf==0.6
|
django-appconf==1.0.1
|
||||||
django-markitup==2.2.2
|
django-model-utils==2.3.1
|
||||||
django-model-utils==2.2
|
django-reversion==1.9.3
|
||||||
django-reversion==1.8.5
|
django-sitetree==1.4.0
|
||||||
django-sitetree==1.2.1
|
django-taggit==0.17.1
|
||||||
django-taggit==0.12.2
|
django-timezone-field==1.3
|
||||||
django-timezone-field==1.2
|
django-user-accounts==1.2.0
|
||||||
django-user-accounts==1.0
|
|
||||||
easy-thumbnails==2.2
|
easy-thumbnails==2.2
|
||||||
html5lib==0.999
|
html5lib==0.9999999
|
||||||
markdown==2.5.2
|
markdown==2.6.2
|
||||||
pytz==2014.10
|
pytz==2015.6
|
||||||
|
|
44
symposion/conference/migrations/0001_initial.py
Normal file
44
symposion/conference/migrations/0001_initial.py
Normal 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',
|
||||||
|
},
|
||||||
|
),
|
||||||
|
]
|
0
symposion/conference/migrations/__init__.py
Normal file
0
symposion/conference/migrations/__init__.py
Normal file
|
@ -1,9 +1,10 @@
|
||||||
from django.http import Http404
|
from django.http import Http404
|
||||||
from django.shortcuts import render
|
from django.shortcuts import render
|
||||||
|
|
||||||
from django.contrib.auth.decorators import login_required
|
|
||||||
from django.contrib.auth.models import User
|
from django.contrib.auth.models import User
|
||||||
|
|
||||||
|
from account.decorators import login_required
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
def user_list(request):
|
def user_list(request):
|
||||||
|
@ -11,6 +12,6 @@ def user_list(request):
|
||||||
if not request.user.is_staff:
|
if not request.user.is_staff:
|
||||||
raise Http404()
|
raise Http404()
|
||||||
|
|
||||||
return render(request, "conference/user_list.html", {
|
return render(request, "symposion/conference/user_list.html", {
|
||||||
"users": User.objects.all(),
|
"users": User.objects.all(),
|
||||||
})
|
})
|
||||||
|
|
|
@ -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])))
|
|
|
@ -10,8 +10,9 @@ def parse(text):
|
||||||
text = markdown.markdown(text, extensions=["extra"], safe_mode=False)
|
text = markdown.markdown(text, extensions=["extra"], safe_mode=False)
|
||||||
|
|
||||||
# Sanitize using html5lib
|
# Sanitize using html5lib
|
||||||
bits = []
|
# bits = []
|
||||||
parser = html5parser.HTMLParser(tokenizer=sanitizer.HTMLSanitizer)
|
# parser = html5parser.HTMLParser(tokenizer=sanitizer.HTMLSanitizer)
|
||||||
for token in parser.parseFragment(text).childNodes:
|
# for token in parser.parseFragment(text).childNodes:
|
||||||
bits.append(token.toxml())
|
# bits.append(token.toxml())
|
||||||
return "".join(bits)
|
# return "".join(bits)
|
||||||
|
return text
|
||||||
|
|
|
@ -4,7 +4,6 @@ from django.db.models import Q
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
from symposion.proposals.models import SupportingDocument
|
from symposion.proposals.models import SupportingDocument
|
||||||
# from markitup.widgets import MarkItUpWidget
|
|
||||||
|
|
||||||
|
|
||||||
# @@@ generic proposal form
|
# @@@ generic proposal form
|
||||||
|
|
100
symposion/proposals/migrations/0001_initial.py
Normal file
100
symposion/proposals/migrations/0001_initial.py
Normal 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')]),
|
||||||
|
),
|
||||||
|
]
|
0
symposion/proposals/migrations/__init__.py
Normal file
0
symposion/proposals/migrations/__init__.py
Normal file
|
@ -15,10 +15,9 @@ from django.core.exceptions import ValidationError
|
||||||
|
|
||||||
import reversion
|
import reversion
|
||||||
|
|
||||||
from markitup.fields import MarkupField
|
|
||||||
|
|
||||||
from model_utils.managers import InheritanceManager
|
from model_utils.managers import InheritanceManager
|
||||||
|
|
||||||
|
from symposion.markdown_parser import parse
|
||||||
from symposion.conference.models import Section
|
from symposion.conference.models import Section
|
||||||
from symposion.speakers.models import Speaker
|
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 "
|
help_text=_("If your proposal is accepted this will be made public and printed in the "
|
||||||
"program. Should be one paragraph, maximum 400 characters.")
|
"program. Should be one paragraph, maximum 400 characters.")
|
||||||
)
|
)
|
||||||
abstract = MarkupField(
|
abstract = models.TextField(
|
||||||
_("Detailed Abstract"),
|
_("Detailed Abstract"),
|
||||||
help_text=_("Detailed outline. Will be made public if your proposal is accepted. Edit "
|
help_text=_("Detailed outline. Will be made public if your proposal is accepted. Edit "
|
||||||
"using <a href='http://daringfireball.net/projects/markdown/basics' "
|
"using <a href='http://daringfireball.net/projects/markdown/basics' "
|
||||||
"target='_blank'>Markdown</a>.")
|
"target='_blank'>Markdown</a>.")
|
||||||
)
|
)
|
||||||
additional_notes = MarkupField(
|
abstract_html = models.TextField(blank=True)
|
||||||
|
additional_notes = models.TextField(
|
||||||
_("Addtional Notes"),
|
_("Addtional Notes"),
|
||||||
blank=True,
|
blank=True,
|
||||||
help_text=_("Anything else you'd like the program committee to know when making their "
|
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' "
|
"<a href='http://daringfireball.net/projects/markdown/basics' "
|
||||||
"target='_blank'>Markdown</a>.")
|
"target='_blank'>Markdown</a>.")
|
||||||
)
|
)
|
||||||
|
additional_notes_html = models.TextField(blank=True)
|
||||||
submitted = models.DateTimeField(
|
submitted = models.DateTimeField(
|
||||||
default=now,
|
default=now,
|
||||||
editable=False,
|
editable=False,
|
||||||
|
@ -115,6 +116,9 @@ class ProposalBase(models.Model):
|
||||||
)
|
)
|
||||||
speaker = models.ForeignKey(Speaker, related_name="proposals", verbose_name=_("Speaker"))
|
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):
|
def additional_speaker_validator(self, a_speaker):
|
||||||
if a_speaker.speaker.email == self.speaker.email:
|
if a_speaker.speaker.email == self.speaker.email:
|
||||||
raise ValidationError(_("%s is same as primary speaker.") % a_speaker.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)
|
raise ValidationError(_("%s has already been in speakers.") % a_speaker.speaker.email)
|
||||||
|
|
||||||
additional_speakers = models.ManyToManyField(Speaker, through="AdditionalSpeaker",
|
additional_speakers = models.ManyToManyField(Speaker, through="AdditionalSpeaker",
|
||||||
blank=True, verbose_name=_("Addtional speakers"),
|
blank=True, verbose_name=_("Addtional speakers"))
|
||||||
validators=[additional_speaker_validator])
|
|
||||||
cancelled = models.BooleanField(default=False, verbose_name=_("Cancelled"))
|
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):
|
def can_edit(self):
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
|
@ -13,11 +13,12 @@ from django.views import static
|
||||||
|
|
||||||
from django.contrib import messages
|
from django.contrib import messages
|
||||||
from django.contrib.auth.models import User
|
from django.contrib.auth.models import User
|
||||||
from django.contrib.auth.decorators import login_required
|
|
||||||
|
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
|
from account.decorators import login_required
|
||||||
from account.models import EmailAddress
|
from account.models import EmailAddress
|
||||||
|
|
||||||
from symposion.proposals.models import (
|
from symposion.proposals.models import (
|
||||||
ProposalBase, ProposalSection, ProposalKind
|
ProposalBase, ProposalSection, ProposalKind
|
||||||
)
|
)
|
||||||
|
@ -58,7 +59,7 @@ def proposal_submit(request):
|
||||||
for kind in proposal_section.section.proposal_kinds.all():
|
for kind in proposal_section.section.proposal_kinds.all():
|
||||||
kinds.append(kind)
|
kinds.append(kind)
|
||||||
|
|
||||||
return render(request, "proposals/proposal_submit.html", {
|
return render(request, "symposion/proposals/proposal_submit.html", {
|
||||||
"kinds": kinds,
|
"kinds": kinds,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -95,9 +96,9 @@ def proposal_submit_kind(request, kind_slug):
|
||||||
else:
|
else:
|
||||||
form = form_class()
|
form = form_class()
|
||||||
|
|
||||||
return render(request, "proposals/proposal_submit_kind.html", {
|
return render(request, "symposion/proposals/proposal_submit_kind.html", {
|
||||||
"kind": kind,
|
"kind": kind,
|
||||||
"form": form,
|
"proposal_form": form,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
|
@ -178,7 +179,7 @@ def proposal_speaker_manage(request, pk):
|
||||||
"speakers": proposal.speakers(),
|
"speakers": proposal.speakers(),
|
||||||
"add_speaker_form": add_speaker_form,
|
"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
|
@login_required
|
||||||
|
@ -195,7 +196,7 @@ def proposal_edit(request, pk):
|
||||||
"title": "Proposal editing closed",
|
"title": "Proposal editing closed",
|
||||||
"body": "Proposal editing is closed for this session type."
|
"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])
|
form_class = get_form(settings.PROPOSAL_FORMS[proposal.kind.slug])
|
||||||
|
|
||||||
|
@ -223,7 +224,7 @@ def proposal_edit(request, pk):
|
||||||
else:
|
else:
|
||||||
form = form_class(instance=proposal)
|
form = form_class(instance=proposal)
|
||||||
|
|
||||||
return render(request, "proposals/proposal_edit.html", {
|
return render(request, "symposion/proposals/proposal_edit.html", {
|
||||||
"proposal": proposal,
|
"proposal": proposal,
|
||||||
"form": form,
|
"form": form,
|
||||||
})
|
})
|
||||||
|
@ -276,7 +277,7 @@ def proposal_detail(request, pk):
|
||||||
else:
|
else:
|
||||||
message_form = None
|
message_form = None
|
||||||
|
|
||||||
return render(request, "proposals/proposal_detail.html", {
|
return render(request, "symposion/proposals/proposal_detail.html", {
|
||||||
"proposal": proposal,
|
"proposal": proposal,
|
||||||
"message_form": message_form
|
"message_form": message_form
|
||||||
})
|
})
|
||||||
|
@ -298,7 +299,7 @@ def proposal_cancel(request, pk):
|
||||||
messages.success(request, "%s has been cancelled" % proposal.title)
|
messages.success(request, "%s has been cancelled" % proposal.title)
|
||||||
return redirect("dashboard")
|
return redirect("dashboard")
|
||||||
|
|
||||||
return render(request, "proposals/proposal_cancel.html", {
|
return render(request, "symposion/proposals/proposal_cancel.html", {
|
||||||
"proposal": proposal,
|
"proposal": proposal,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -321,7 +322,7 @@ def proposal_leave(request, pk):
|
||||||
ctx = {
|
ctx = {
|
||||||
"proposal": proposal,
|
"proposal": proposal,
|
||||||
}
|
}
|
||||||
return render(request, "proposals/proposal_leave.html", ctx)
|
return render(request, "symposion/proposals/proposal_leave.html", ctx)
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
|
@ -372,7 +373,7 @@ def document_create(request, proposal_pk):
|
||||||
else:
|
else:
|
||||||
form = SupportingDocumentCreateForm()
|
form = SupportingDocumentCreateForm()
|
||||||
|
|
||||||
return render(request, "proposals/document_create.html", {
|
return render(request, "symposion/proposals/document_create.html", {
|
||||||
"proposal": proposal,
|
"proposal": proposal,
|
||||||
"form": form,
|
"form": form,
|
||||||
})
|
})
|
||||||
|
|
|
@ -2,8 +2,6 @@ from __future__ import unicode_literals
|
||||||
from django import forms
|
from django import forms
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
from markitup.widgets import MarkItUpWidget
|
|
||||||
|
|
||||||
from symposion.reviews.models import Review, Comment, ProposalMessage, VOTES
|
from symposion.reviews.models import Review, Comment, ProposalMessage, VOTES
|
||||||
|
|
||||||
|
|
||||||
|
@ -11,7 +9,6 @@ class ReviewForm(forms.ModelForm):
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Review
|
model = Review
|
||||||
fields = ["vote", "comment"]
|
fields = ["vote", "comment"]
|
||||||
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)
|
||||||
|
@ -25,14 +22,12 @@ class ReviewCommentForm(forms.ModelForm):
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Comment
|
model = Comment
|
||||||
fields = ["text"]
|
fields = ["text"]
|
||||||
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()}
|
|
||||||
|
|
||||||
|
|
||||||
class BulkPresentationForm(forms.Form):
|
class BulkPresentationForm(forms.Form):
|
||||||
|
|
143
symposion/reviews/migrations/0001_initial.py
Normal file
143
symposion/reviews/migrations/0001_initial.py
Normal 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')]),
|
||||||
|
),
|
||||||
|
]
|
0
symposion/reviews/migrations/__init__.py
Normal file
0
symposion/reviews/migrations/__init__.py
Normal file
|
@ -4,26 +4,22 @@ from datetime import datetime
|
||||||
from decimal import Decimal
|
from decimal import Decimal
|
||||||
|
|
||||||
from django.db import models
|
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.db.models.signals import post_save
|
||||||
|
|
||||||
from django.contrib.auth.models import User
|
from django.contrib.auth.models import User
|
||||||
from django.utils.translation import ugettext_lazy as _
|
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.proposals.models import ProposalBase
|
||||||
from symposion.schedule.models import Presentation
|
from symposion.schedule.models import Presentation
|
||||||
|
|
||||||
|
|
||||||
class ProposalScoreExpression(object):
|
def score_expression():
|
||||||
|
return (
|
||||||
def as_sql(self, qn, connection=None):
|
(3 * F("plus_one") + F("plus_zero")) -
|
||||||
sql = "((3 * plus_one + plus_zero) - (minus_zero + 3 * minus_one))"
|
(F("minus_zero") + 3 * F("minus_one"))
|
||||||
return sql, []
|
)
|
||||||
|
|
||||||
def prepare_database_save(self, unused):
|
|
||||||
return self
|
|
||||||
|
|
||||||
|
|
||||||
class Votes(object):
|
class Votes(object):
|
||||||
|
@ -97,9 +93,14 @@ class ProposalMessage(models.Model):
|
||||||
proposal = models.ForeignKey(ProposalBase, related_name="messages", verbose_name=_("Proposal"))
|
proposal = models.ForeignKey(ProposalBase, related_name="messages", verbose_name=_("Proposal"))
|
||||||
user = models.ForeignKey(User, verbose_name=_("User"))
|
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"))
|
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:
|
class Meta:
|
||||||
ordering = ["submitted_at"]
|
ordering = ["submitted_at"]
|
||||||
verbose_name = _("proposal message")
|
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
|
# No way to encode "-0" vs. "+0" into an IntegerField, and I don't feel
|
||||||
# like some complicated encoding system.
|
# like some complicated encoding system.
|
||||||
vote = models.CharField(max_length=2, blank=True, choices=VOTES.CHOICES, verbose_name=_("Vote"))
|
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"))
|
submitted_at = models.DateTimeField(default=datetime.now, editable=False, verbose_name=_("Submitted at"))
|
||||||
|
|
||||||
def save(self, **kwargs):
|
def save(self, **kwargs):
|
||||||
|
self.comment_html = parse(self.comment)
|
||||||
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,
|
||||||
|
@ -258,7 +261,7 @@ class ProposalResult(models.Model):
|
||||||
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=score_expression())
|
||||||
|
|
||||||
def update_vote(self, vote, previous=None, removal=False):
|
def update_vote(self, vote, previous=None, removal=False):
|
||||||
mapping = {
|
mapping = {
|
||||||
|
@ -287,7 +290,7 @@ class ProposalResult(models.Model):
|
||||||
self.comment_count = models.F("comment_count") + 1
|
self.comment_count = models.F("comment_count") + 1
|
||||||
self.save()
|
self.save()
|
||||||
model = self.__class__
|
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:
|
class Meta:
|
||||||
verbose_name = _("proposal_result")
|
verbose_name = _("proposal_result")
|
||||||
|
@ -297,7 +300,8 @@ class ProposalResult(models.Model):
|
||||||
class Comment(models.Model):
|
class Comment(models.Model):
|
||||||
proposal = models.ForeignKey(ProposalBase, related_name="comments", verbose_name=_("Proposal"))
|
proposal = models.ForeignKey(ProposalBase, related_name="comments", verbose_name=_("Proposal"))
|
||||||
commenter = models.ForeignKey(User, verbose_name=_("Commenter"))
|
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.
|
# Or perhaps more accurately, can the user see this comment.
|
||||||
public = models.BooleanField(choices=[(True, _("public")), (False, _("private"))], default=False, verbose_name=_("Public"))
|
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 = _("comment")
|
||||||
verbose_name_plural = _("comments")
|
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):
|
class NotificationTemplate(models.Model):
|
||||||
|
|
||||||
|
|
|
@ -5,11 +5,13 @@ from django.shortcuts import render, redirect, get_object_or_404
|
||||||
from django.template import Context, Template
|
from django.template import Context, Template
|
||||||
from django.views.decorators.http import require_POST
|
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.conf import settings
|
||||||
from symposion.proposals.models import ProposalBase, ProposalSection
|
from symposion.proposals.models import ProposalBase, ProposalSection
|
||||||
from symposion.teams.models import Team
|
|
||||||
from symposion.utils.mail import send_email
|
from symposion.utils.mail import send_email
|
||||||
|
|
||||||
from symposion.reviews.forms import ReviewForm, SpeakerCommentForm
|
from symposion.reviews.forms import ReviewForm, SpeakerCommentForm
|
||||||
|
@ -21,7 +23,7 @@ from symposion.reviews.models import (
|
||||||
|
|
||||||
|
|
||||||
def access_not_permitted(request):
|
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):
|
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,
|
"reviewed": reviewed,
|
||||||
}
|
}
|
||||||
|
|
||||||
return render(request, "reviews/review_list.html", ctx)
|
return render(request, "symposion/reviews/review_list.html", ctx)
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
|
@ -120,7 +122,7 @@ def review_list(request, section_slug, user_pk):
|
||||||
ctx = {
|
ctx = {
|
||||||
"proposals": proposals,
|
"proposals": proposals,
|
||||||
}
|
}
|
||||||
return render(request, "reviews/review_list.html", ctx)
|
return render(request, "symposion/reviews/review_list.html", ctx)
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
|
@ -164,7 +166,7 @@ def review_admin(request, section_slug):
|
||||||
"section_slug": section_slug,
|
"section_slug": section_slug,
|
||||||
"reviewers": reviewers(),
|
"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
|
# 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")
|
reviews = Review.objects.filter(proposal=proposal).order_by("-submitted_at")
|
||||||
messages = proposal.messages.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,
|
"proposal": proposal,
|
||||||
"latest_vote": latest_vote,
|
"latest_vote": latest_vote,
|
||||||
"reviews": reviews,
|
"reviews": reviews,
|
||||||
|
@ -353,7 +355,7 @@ def review_status(request, section_slug=None, key=None):
|
||||||
else:
|
else:
|
||||||
ctx["proposals"] = proposals
|
ctx["proposals"] = proposals
|
||||||
|
|
||||||
return render(request, "reviews/review_stats.html", ctx)
|
return render(request, "symposion/reviews/review_stats.html", ctx)
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
|
@ -364,7 +366,7 @@ def review_assignments(request):
|
||||||
user=request.user,
|
user=request.user,
|
||||||
opted_out=False
|
opted_out=False
|
||||||
)
|
)
|
||||||
return render(request, "reviews/review_assignment.html", {
|
return render(request, "symposion/reviews/review_assignment.html", {
|
||||||
"assignments": assignments,
|
"assignments": assignments,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -398,7 +400,7 @@ def review_bulk_accept(request, section_slug):
|
||||||
else:
|
else:
|
||||||
form = BulkPresentationForm()
|
form = BulkPresentationForm()
|
||||||
|
|
||||||
return render(request, "reviews/review_bulk_accept.html", {
|
return render(request, "symposion/reviews/review_bulk_accept.html", {
|
||||||
"form": form,
|
"form": form,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -417,7 +419,7 @@ def result_notification(request, section_slug, status):
|
||||||
"proposals": proposals,
|
"proposals": proposals,
|
||||||
"notification_templates": notification_templates,
|
"notification_templates": notification_templates,
|
||||||
}
|
}
|
||||||
return render(request, "reviews/result_notification.html", ctx)
|
return render(request, "symposion/reviews/result_notification.html", ctx)
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
|
@ -455,7 +457,7 @@ def result_notification_prepare(request, section_slug, status):
|
||||||
"proposals": proposals,
|
"proposals": proposals,
|
||||||
"proposal_pks": ",".join([str(pk) for pk in proposal_pks]),
|
"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
|
@login_required
|
||||||
|
|
|
@ -9,8 +9,6 @@ from django.contrib import messages
|
||||||
from django.db import IntegrityError, transaction
|
from django.db import IntegrityError, transaction
|
||||||
from django.db.models import Q
|
from django.db.models import Q
|
||||||
|
|
||||||
from markitup.widgets import MarkItUpWidget
|
|
||||||
|
|
||||||
from symposion.schedule.models import (Day, Presentation, Room, SlotKind, Slot,
|
from symposion.schedule.models import (Day, Presentation, Room, SlotKind, Slot,
|
||||||
SlotRoom)
|
SlotRoom)
|
||||||
|
|
||||||
|
@ -44,7 +42,6 @@ class SlotEditForm(forms.Form):
|
||||||
def build_content_override_field(self):
|
def build_content_override_field(self):
|
||||||
kwargs = {
|
kwargs = {
|
||||||
"label": "Content",
|
"label": "Content",
|
||||||
"widget": MarkItUpWidget(),
|
|
||||||
"required": False,
|
"required": False,
|
||||||
"initial": self.slot.content_override,
|
"initial": self.slot.content_override,
|
||||||
}
|
}
|
||||||
|
|
186
symposion/schedule/migrations/0001_initial.py
Normal file
186
symposion/schedule/migrations/0001_initial.py
Normal 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')]),
|
||||||
|
),
|
||||||
|
]
|
0
symposion/schedule/migrations/__init__.py
Normal file
0
symposion/schedule/migrations/__init__.py
Normal file
|
@ -8,8 +8,7 @@ from django.db import models
|
||||||
from django.utils.encoding import python_2_unicode_compatible
|
from django.utils.encoding import python_2_unicode_compatible
|
||||||
from django.utils.translation import ugettext_lazy as _
|
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.proposals.models import ProposalBase
|
||||||
from symposion.conference.models import Section
|
from symposion.conference.models import Section
|
||||||
from symposion.speakers.models import Speaker
|
from symposion.speakers.models import Speaker
|
||||||
|
@ -87,7 +86,12 @@ class Slot(models.Model):
|
||||||
kind = models.ForeignKey(SlotKind, verbose_name=_("Kind"))
|
kind = models.ForeignKey(SlotKind, verbose_name=_("Kind"))
|
||||||
start = models.TimeField(verbose_name=_("Start"))
|
start = models.TimeField(verbose_name=_("Start"))
|
||||||
end = models.TimeField(verbose_name=_("End"))
|
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):
|
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"))
|
slot = models.OneToOneField(Slot, null=True, blank=True, related_name="content_ptr", verbose_name=_("Slot"))
|
||||||
title = models.CharField(max_length=100, verbose_name=_("Title"))
|
title = models.CharField(max_length=100, verbose_name=_("Title"))
|
||||||
description = MarkupField(verbose_name=_("Description"))
|
description = models.TextField(verbose_name=_("Description"))
|
||||||
abstract = MarkupField(verbose_name=_("Abstract"))
|
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"))
|
speaker = models.ForeignKey(Speaker, related_name="presentations", verbose_name=_("Speaker"))
|
||||||
additional_speakers = models.ManyToManyField(Speaker, related_name="copresentations",
|
additional_speakers = models.ManyToManyField(Speaker, related_name="copresentations",
|
||||||
blank=True, verbose_name=_("Additional speakers"))
|
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"))
|
proposal_base = models.OneToOneField(ProposalBase, related_name="presentation", verbose_name=_("Proposal base"))
|
||||||
section = models.ForeignKey(Section, related_name="presentations", verbose_name=_("Section"))
|
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
|
@property
|
||||||
def number(self):
|
def number(self):
|
||||||
return self.proposal.number
|
return self.proposal.number
|
||||||
|
|
|
@ -22,7 +22,6 @@ try:
|
||||||
"django.contrib.contenttypes",
|
"django.contrib.contenttypes",
|
||||||
"django.contrib.sites",
|
"django.contrib.sites",
|
||||||
|
|
||||||
"markitup",
|
|
||||||
"reversion",
|
"reversion",
|
||||||
|
|
||||||
"symposion",
|
"symposion",
|
||||||
|
@ -34,8 +33,6 @@ try:
|
||||||
],
|
],
|
||||||
SITE_ID=1,
|
SITE_ID=1,
|
||||||
NOSE_ARGS=['-s'],
|
NOSE_ARGS=['-s'],
|
||||||
|
|
||||||
MARKITUP_FILTER=('django.contrib.markup.templatetags.markup.textile', {}),
|
|
||||||
)
|
)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
|
|
@ -6,11 +6,12 @@ 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
|
||||||
|
|
||||||
from django.contrib.auth.decorators import login_required
|
|
||||||
from django.contrib.auth.models import User
|
from django.contrib.auth.models import User
|
||||||
from django.contrib import messages
|
from django.contrib import messages
|
||||||
from django.contrib.sites.models import Site
|
from django.contrib.sites.models import Site
|
||||||
|
|
||||||
|
from account.decorators import login_required
|
||||||
|
|
||||||
from symposion.schedule.forms import SlotEditForm, ScheduleSectionForm
|
from symposion.schedule.forms import SlotEditForm, ScheduleSectionForm
|
||||||
from symposion.schedule.models import Schedule, Day, Slot, Presentation, Session, SessionRole
|
from symposion.schedule.models import Schedule, Day, Slot, Presentation, Session, SessionRole
|
||||||
from symposion.schedule.timetable import TimeTable
|
from symposion.schedule.timetable import TimeTable
|
||||||
|
@ -47,7 +48,7 @@ def schedule_conference(request):
|
||||||
ctx = {
|
ctx = {
|
||||||
"sections": sections,
|
"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):
|
def schedule_detail(request, slug=None):
|
||||||
|
@ -63,7 +64,7 @@ def schedule_detail(request, slug=None):
|
||||||
"schedule": schedule,
|
"schedule": schedule,
|
||||||
"days": days,
|
"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):
|
def schedule_list(request, slug=None):
|
||||||
|
@ -76,7 +77,7 @@ def schedule_list(request, slug=None):
|
||||||
"schedule": schedule,
|
"schedule": schedule,
|
||||||
"presentations": presentations,
|
"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):
|
def schedule_list_csv(request, slug=None):
|
||||||
|
@ -92,7 +93,7 @@ def schedule_list_csv(request, slug=None):
|
||||||
file_slug = "presentations"
|
file_slug = "presentations"
|
||||||
response["Content-Disposition"] = 'attachment; filename="%s.csv"' % file_slug
|
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,
|
"presentations": presentations,
|
||||||
|
|
||||||
})))
|
})))
|
||||||
|
@ -126,7 +127,7 @@ def schedule_edit(request, slug=None):
|
||||||
"days": days,
|
"days": days,
|
||||||
"form": form
|
"form": form
|
||||||
}
|
}
|
||||||
return render(request, "schedule/schedule_edit.html", ctx)
|
return render(request, "symposion/schedule/schedule_edit.html", ctx)
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
|
@ -160,7 +161,7 @@ def schedule_slot_edit(request, slug, slot_pk):
|
||||||
"form": form,
|
"form": form,
|
||||||
"slot": slot,
|
"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):
|
def schedule_presentation_detail(request, pk):
|
||||||
|
@ -175,7 +176,7 @@ def schedule_presentation_detail(request, pk):
|
||||||
"presentation": presentation,
|
"presentation": presentation,
|
||||||
"schedule": schedule,
|
"schedule": schedule,
|
||||||
}
|
}
|
||||||
return render(request, "schedule/presentation_detail.html", ctx)
|
return render(request, "symposion/schedule/presentation_detail.html", ctx)
|
||||||
|
|
||||||
|
|
||||||
def schedule_json(request):
|
def schedule_json(request):
|
||||||
|
@ -230,7 +231,7 @@ def schedule_json(request):
|
||||||
data.append(slot_data)
|
data.append(slot_data)
|
||||||
|
|
||||||
return HttpResponse(
|
return HttpResponse(
|
||||||
json.dumps({'schedule': data}),
|
json.dumps({"schedule": data}),
|
||||||
content_type="application/json"
|
content_type="application/json"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -238,7 +239,7 @@ def schedule_json(request):
|
||||||
def session_list(request):
|
def session_list(request):
|
||||||
sessions = Session.objects.all().order_by('pk')
|
sessions = Session.objects.all().order_by('pk')
|
||||||
|
|
||||||
return render(request, "schedule/session_list.html", {
|
return render(request, "symposion/schedule/session_list.html", {
|
||||||
"sessions": sessions,
|
"sessions": sessions,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -306,7 +307,7 @@ def session_detail(request, session_id):
|
||||||
|
|
||||||
return redirect("schedule_session_detail", 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,
|
"session": session,
|
||||||
"chair": chair,
|
"chair": chair,
|
||||||
"chair_denied": chair_denied,
|
"chair_denied": chair_denied,
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
from django import forms
|
from django import forms
|
||||||
|
|
||||||
from markitup.widgets import MarkItUpWidget
|
|
||||||
|
|
||||||
from symposion.speakers.models import Speaker
|
from symposion.speakers.models import Speaker
|
||||||
|
|
||||||
|
|
||||||
|
@ -15,6 +13,3 @@ class SpeakerForm(forms.ModelForm):
|
||||||
"biography",
|
"biography",
|
||||||
"photo",
|
"photo",
|
||||||
]
|
]
|
||||||
widgets = {
|
|
||||||
"biography": MarkItUpWidget(),
|
|
||||||
}
|
|
||||||
|
|
36
symposion/speakers/migrations/0001_initial.py
Normal file
36
symposion/speakers/migrations/0001_initial.py
Normal 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'],
|
||||||
|
},
|
||||||
|
),
|
||||||
|
]
|
0
symposion/speakers/migrations/__init__.py
Normal file
0
symposion/speakers/migrations/__init__.py
Normal file
|
@ -9,7 +9,7 @@ from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
from django.contrib.auth.models import User
|
from django.contrib.auth.models import User
|
||||||
|
|
||||||
from markitup.fields import MarkupField
|
from symposion.markdown_parser import parse
|
||||||
|
|
||||||
|
|
||||||
@python_2_unicode_compatible
|
@python_2_unicode_compatible
|
||||||
|
@ -24,10 +24,11 @@ class Speaker(models.Model):
|
||||||
name = models.CharField(verbose_name=_("Name"), max_length=100,
|
name = models.CharField(verbose_name=_("Name"), max_length=100,
|
||||||
help_text=_("As you would like it to appear in the"
|
help_text=_("As you would like it to appear in the"
|
||||||
" conference program."))
|
" 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/"
|
"<a href='http://warpedvisions.org/projects/"
|
||||||
"markdown-cheat-sheet/target='_blank'>"
|
"markdown-cheat-sheet/target='_blank'>"
|
||||||
"Markdown</a>."), verbose_name=_("Biography"))
|
"Markdown</a>."), verbose_name=_("Biography"))
|
||||||
|
biography_html = models.TextField(blank=True)
|
||||||
photo = models.ImageField(upload_to="speaker_photos", blank=True, verbose_name=_("Photo"))
|
photo = models.ImageField(upload_to="speaker_photos", blank=True, verbose_name=_("Photo"))
|
||||||
annotation = models.TextField(verbose_name=_("Annotation")) # staff only
|
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"))
|
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 = _("Speaker")
|
||||||
verbose_name_plural = _("Speakers")
|
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):
|
def __str__(self):
|
||||||
if self.user:
|
if self.user:
|
||||||
return self.name
|
return self.name
|
||||||
|
|
|
@ -4,10 +4,11 @@ from django.http import Http404
|
||||||
from django.shortcuts import render, redirect, get_object_or_404
|
from django.shortcuts import render, redirect, get_object_or_404
|
||||||
|
|
||||||
from django.contrib import messages
|
from django.contrib import messages
|
||||||
from django.contrib.auth.decorators import login_required
|
|
||||||
from django.contrib.auth.models import User
|
from django.contrib.auth.models import User
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
|
from account.decorators import login_required
|
||||||
|
|
||||||
from symposion.proposals.models import ProposalBase
|
from symposion.proposals.models import ProposalBase
|
||||||
from symposion.speakers.forms import SpeakerForm
|
from symposion.speakers.forms import SpeakerForm
|
||||||
from symposion.speakers.models import Speaker
|
from symposion.speakers.models import Speaker
|
||||||
|
@ -39,9 +40,8 @@ def speaker_create(request):
|
||||||
return redirect("dashboard")
|
return redirect("dashboard")
|
||||||
else:
|
else:
|
||||||
form = SpeakerForm(initial={"name": request.user.get_full_name()})
|
form = SpeakerForm(initial={"name": request.user.get_full_name()})
|
||||||
|
return render(request, "symposion/speakers/speaker_create.html", {
|
||||||
return render(request, "speakers/speaker_create.html", {
|
"speaker_form": form,
|
||||||
"form": form,
|
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
|
@ -68,8 +68,8 @@ def speaker_create_staff(request, pk):
|
||||||
else:
|
else:
|
||||||
form = SpeakerForm(initial={"name": user.get_full_name()})
|
form = SpeakerForm(initial={"name": user.get_full_name()})
|
||||||
|
|
||||||
return render(request, "speakers/speaker_create.html", {
|
return render(request, "symposion/speakers/speaker_create.html", {
|
||||||
"form": form,
|
"speaker_form": form,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
|
@ -121,8 +121,8 @@ def speaker_edit(request, pk=None):
|
||||||
else:
|
else:
|
||||||
form = SpeakerForm(instance=speaker)
|
form = SpeakerForm(instance=speaker)
|
||||||
|
|
||||||
return render(request, "speakers/speaker_edit.html", {
|
return render(request, "symposion/speakers/speaker_edit.html", {
|
||||||
"form": form,
|
"speaker_form": form,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
|
@ -132,7 +132,7 @@ def speaker_profile(request, pk):
|
||||||
if not presentations and not request.user.is_staff:
|
if not presentations and not request.user.is_staff:
|
||||||
raise Http404()
|
raise Http404()
|
||||||
|
|
||||||
return render(request, "speakers/speaker_profile.html", {
|
return render(request, "symposion/speakers/speaker_profile.html", {
|
||||||
"speaker": speaker,
|
"speaker": speaker,
|
||||||
"presentations": presentations,
|
"presentations": presentations,
|
||||||
})
|
})
|
||||||
|
|
115
symposion/sponsorship/migrations/0001_initial.py
Normal file
115
symposion/sponsorship/migrations/0001_initial.py
Normal 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'),
|
||||||
|
),
|
||||||
|
]
|
0
symposion/sponsorship/migrations/__init__.py
Normal file
0
symposion/sponsorship/migrations/__init__.py
Normal file
|
@ -4,7 +4,7 @@ from django.views.generic import TemplateView
|
||||||
|
|
||||||
urlpatterns = patterns(
|
urlpatterns = patterns(
|
||||||
"symposion.sponsorship.views",
|
"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"^apply/$", "sponsor_apply", name="sponsor_apply"),
|
||||||
url(r"^add/$", "sponsor_add", name="sponsor_add"),
|
url(r"^add/$", "sponsor_add", name="sponsor_add"),
|
||||||
url(r"^ziplogos/$", "sponsor_zip_logo_files", name="sponsor_zip_logos"),
|
url(r"^ziplogos/$", "sponsor_zip_logo_files", name="sponsor_zip_logos"),
|
||||||
|
|
|
@ -9,12 +9,13 @@ from zipfile import ZipFile, ZipInfo
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.contrib import messages
|
from django.contrib import messages
|
||||||
from django.contrib.admin.views.decorators import staff_member_required
|
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.http import Http404, HttpResponse
|
||||||
from django.shortcuts import render_to_response, redirect, get_object_or_404
|
from django.shortcuts import render_to_response, redirect, get_object_or_404
|
||||||
from django.template import RequestContext
|
from django.template import RequestContext
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
|
from account.decorators import login_required
|
||||||
|
|
||||||
from symposion.sponsorship.forms import SponsorApplicationForm, \
|
from symposion.sponsorship.forms import SponsorApplicationForm, \
|
||||||
SponsorDetailsForm, SponsorBenefitsFormSet
|
SponsorDetailsForm, SponsorBenefitsFormSet
|
||||||
from symposion.sponsorship.models import Benefit, Sponsor, SponsorBenefit, \
|
from symposion.sponsorship.models import Benefit, Sponsor, SponsorBenefit, \
|
||||||
|
@ -43,7 +44,7 @@ def sponsor_apply(request):
|
||||||
else:
|
else:
|
||||||
form = SponsorApplicationForm(user=request.user)
|
form = SponsorApplicationForm(user=request.user)
|
||||||
|
|
||||||
return render_to_response("sponsorship/apply.html", {
|
return render_to_response("symposion/sponsorship/apply.html", {
|
||||||
"form": form,
|
"form": form,
|
||||||
}, context_instance=RequestContext(request))
|
}, context_instance=RequestContext(request))
|
||||||
|
|
||||||
|
@ -63,7 +64,7 @@ def sponsor_add(request):
|
||||||
else:
|
else:
|
||||||
form = SponsorApplicationForm(user=request.user)
|
form = SponsorApplicationForm(user=request.user)
|
||||||
|
|
||||||
return render_to_response("sponsorship/add.html", {
|
return render_to_response("symposion/sponsorship/add.html", {
|
||||||
"form": form,
|
"form": form,
|
||||||
}, context_instance=RequestContext(request))
|
}, context_instance=RequestContext(request))
|
||||||
|
|
||||||
|
@ -96,7 +97,7 @@ def sponsor_detail(request, pk):
|
||||||
form = SponsorDetailsForm(instance=sponsor)
|
form = SponsorDetailsForm(instance=sponsor)
|
||||||
formset = SponsorBenefitsFormSet(**formset_kwargs)
|
formset = SponsorBenefitsFormSet(**formset_kwargs)
|
||||||
|
|
||||||
return render_to_response("sponsorship/detail.html", {
|
return render_to_response("symposion/sponsorship/detail.html", {
|
||||||
"sponsor": sponsor,
|
"sponsor": sponsor,
|
||||||
"form": form,
|
"form": form,
|
||||||
"formset": formset,
|
"formset": formset,
|
||||||
|
|
56
symposion/teams/migrations/0001_initial.py
Normal file
56
symposion/teams/migrations/0001_initial.py
Normal 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'),
|
||||||
|
),
|
||||||
|
]
|
0
symposion/teams/migrations/__init__.py
Normal file
0
symposion/teams/migrations/__init__.py
Normal file
|
@ -2,12 +2,12 @@ from __future__ import unicode_literals
|
||||||
from django.http import Http404, HttpResponseNotAllowed
|
from django.http import Http404, HttpResponseNotAllowed
|
||||||
from django.shortcuts import render, redirect, get_object_or_404
|
from django.shortcuts import render, redirect, get_object_or_404
|
||||||
|
|
||||||
from django.contrib.auth.decorators import login_required
|
|
||||||
from django.contrib import messages
|
from django.contrib import messages
|
||||||
|
|
||||||
from symposion.utils.mail import send_email
|
|
||||||
from django.utils.translation import ugettext_lazy as _
|
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.forms import TeamInvitationForm
|
||||||
from symposion.teams.models import Team, Membership
|
from symposion.teams.models import Team, Membership
|
||||||
|
|
||||||
|
@ -75,7 +75,7 @@ def team_detail(request, slug):
|
||||||
else:
|
else:
|
||||||
form = None
|
form = None
|
||||||
|
|
||||||
return render(request, "teams/team_detail.html", {
|
return render(request, "symposion/teams/team_detail.html", {
|
||||||
"team": team,
|
"team": team,
|
||||||
"state": state,
|
"state": state,
|
||||||
"invite_form": form,
|
"invite_form": form,
|
||||||
|
|
|
@ -17,10 +17,10 @@ def send_email(to, kind, **kwargs):
|
||||||
ctx.update(kwargs.get("context", {}))
|
ctx.update(kwargs.get("context", {}))
|
||||||
subject = "[%s] %s" % (
|
subject = "[%s] %s" % (
|
||||||
current_site.name,
|
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)
|
message_plaintext = strip_tags(message_html)
|
||||||
|
|
||||||
from_email = settings.DEFAULT_FROM_EMAIL
|
from_email = settings.DEFAULT_FROM_EMAIL
|
||||||
|
|
|
@ -1,53 +1,9 @@
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
import hashlib
|
|
||||||
import random
|
|
||||||
|
|
||||||
from django.shortcuts import render, redirect
|
from django.shortcuts import render, redirect
|
||||||
|
|
||||||
from django.contrib.auth.models import User
|
|
||||||
from django.contrib.auth.decorators import login_required
|
|
||||||
|
|
||||||
import account.views
|
from account.decorators import login_required
|
||||||
|
|
||||||
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",
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
|
|
Loading…
Reference in a new issue