This commit is contained in:
Élie Bouttier 2017-05-29 22:48:49 +02:00
parent b6d26f675d
commit fcbff0e9dd
12 changed files with 581 additions and 166 deletions

View File

@ -9,15 +9,13 @@ from django.utils.translation import ugettext
from ponyconf.utils import PonyConfModel, enum_to_choices
from .utils import generate_user_uid
#from .utils import generate_user_uid
class Profile(PonyConfModel):
user = models.OneToOneField(User)
phone_number = models.CharField(max_length=16, blank=True, default='', verbose_name=_('Phone number'))
biography = models.TextField(blank=True, verbose_name=_('Biography'))
email_token = models.CharField(max_length=12, default=generate_user_uid, unique=True)
twitter = models.CharField(max_length=100, blank=True, default='', verbose_name=_('Twitter'))
linkedin = models.CharField(max_length=100, blank=True, default='', verbose_name=_('LinkedIn'))
@ -29,125 +27,5 @@ class Profile(PonyConfModel):
def __str__(self):
return self.user.get_full_name() or self.user.username
def get_absolute_url(self):
return reverse('profile')
class Option(models.Model):
name = models.CharField(max_length=64, unique=True)
class Meta:
abstract = True
def __str__(self):
return ugettext(self.name)
class Transport(Option):
pass
class Connector(Option):
pass
class Participation(PonyConfModel):
LICENCES = IntEnum('Video licence', 'CC-Zero CC-BY CC-BY-SA CC-BY-ND CC-BY-NC CC-BY-NC-SA CC-BY-NC-ND')
ACCOMMODATION_NO = 0
ACCOMMODATION_HOTEL = 1
ACCOMMODATION_HOMESTAY = 2
ACCOMMODATION_CHOICES = (
(ACCOMMODATION_NO, _('No')),
(ACCOMMODATION_HOTEL, _('Hotel')),
(ACCOMMODATION_HOMESTAY, _('Homestay')),
)
site = models.ForeignKey(Site, on_delete=models.CASCADE)
user = models.ForeignKey(User)
need_transport = models.NullBooleanField(verbose_name=_('Defray transportation?'), default=None)
arrival = models.DateTimeField(blank=True, null=True)
departure = models.DateTimeField(blank=True, null=True)
transport = models.ManyToManyField(Transport, verbose_name=_("I want to travel by"), blank=True)
transport_city_outward = models.CharField(blank=True, default='', max_length=256, verbose_name=_("Departure city"))
transport_city_return = models.CharField(blank=True, default='', max_length=256, verbose_name=_("Return city"), help_text=_("If different from departure city"))
transport_booked = models.BooleanField(default=False)
accommodation = models.IntegerField(choices=ACCOMMODATION_CHOICES, verbose_name=_('Need accommodation?'), null=True, blank=True)
accommodation_booked = models.BooleanField(default=False)
constraints = models.TextField(blank=True, verbose_name=_("Constraints"))
connector = models.ManyToManyField(Connector, verbose_name=_("I can output"), blank=True)
sound = models.BooleanField(_("I need sound"), default=False)
videotaped = models.BooleanField(_("I'm ok to be recorded on video"), default=True)
video_licence = models.IntegerField(choices=enum_to_choices(LICENCES), default=2, verbose_name=_("Video licence"))
notes = models.TextField(default='', blank=True, verbose_name=_("Notes"), help_text=_('This field is only visible by organizers.'))
orga = models.BooleanField(default=False)
class Meta:
# A User can participe only once to a Conference (= Site)
unique_together = ('site', 'user')
def __str__(self):
return str(self.user.profile)
def get_absolute_url(self):
return reverse('show-participant', kwargs={'username': self.user.username})
def is_orga(self):
return self.orga
def is_staff(self):
return self.is_orga() or self.topic_set.exists() or self.track_set.exists()
@property
def topic_set(self):
return self.user.topic_set.filter(site=self.site)
@property
def track_set(self):
return self.user.track_set.filter(site=self.site)
@property
def talk_set(self):
return self.user.talk_set.filter(site=self.site)
@property
def accepted_talk_set(self):
return self.talk_set.filter(accepted=True)
@property
def pending_talk_set(self):
return self.talk_set.filter(accepted=None)
@property
def refused_talk_set(self):
return self.talk_set.filter(accepted=False)
@property
def not_refused_talk_set(self): # accepted + pending
return self.talk_set.exclude(accepted=False)
# return True, False or None if availabilities have not been filled
def is_available(self, start, end=None):
if not self.availabilities.exists():
return None
for timeslot in self.availabilities.all():
if start < timeslot.start:
continue
if start > timeslot.end:
continue
if end:
assert(start < end)
if end > timeslot.end:
continue
return True
return False
class AvailabilityTimeslot(models.Model):
participation = models.ForeignKey(Participation, related_name='availabilities')
start = models.DateTimeField(blank=True)
end = models.DateTimeField(blank=True)
#def get_absolute_url(self):
# return reverse('profile')

View File

@ -2,16 +2,17 @@ from django.conf import settings
from django.conf.urls import include, url
from django.contrib.auth import views as auth_views
from . import views
#from . import views
urlpatterns = [
url(r'^profile/$', views.profile, name='profile'),
url(r'^login/$', auth_views.login, {'extra_context': {'buttons': [views.RESET_PASSWORD_BUTTON]}}, name='login'),
#url(r'^profile/$', views.profile, name='profile'),
#url(r'^login/$', auth_views.login, {'extra_context': {'buttons': [views.RESET_PASSWORD_BUTTON]}}, name='login'),
url(r'^login/$', auth_views.login, name='login'),
url(r'^logout/$', auth_views.logout, {'next_page': settings.LOGOUT_REDIRECT_URL}, name='logout'),
url(r'^participant/$', views.participation_list, name='list-participants'),
url(r'^participant/(?P<username>[\w.@+-]+)$', views.participant_details, name='show-participant'),
url(r'^participant/(?P<username>[\w.@+-]+)/edit/$', views.participant_edit, name='edit-participant'),
url(r'^avatar/', include('avatar.urls')),
#url(r'^participant/$', views.participation_list, name='list-participants'),
#url(r'^participant/(?P<username>[\w.@+-]+)$', views.participant_details, name='show-participant'),
#url(r'^participant/(?P<username>[\w.@+-]+)/edit/$', views.participant_edit, name='edit-participant'),
#url(r'^avatar/', include('avatar.urls')),
url(r'', include('django.contrib.auth.urls')),
url(r'', include('registration.backends.default.urls')),
#url(r'', include('registration.backends.default.urls')),
]

View File

@ -1,19 +0,0 @@
from django.contrib.sites.shortcuts import get_current_site
from django.utils.crypto import get_random_string
def generate_user_uid():
return get_random_string(length=12, allowed_chars='abcdefghijklmnopqrstuvwxyz0123456789')
def is_orga(request, user):
return user.is_authenticated and user.participation_set.get(site=get_current_site(request)).is_orga()
def is_staff(request, user):
return user.is_authenticated and user.participation_set.get(site=get_current_site(request)).is_staff()
def can_edit_profile(request, profile):
editor = request.user.participation_set.get(site=get_current_site(request))
return editor.is_orga() or editor.topic_set.filter(talk__speakers=profile.user).exists()

0
cfp/__init__.py Normal file
View File

11
cfp/apps.py Normal file
View File

@ -0,0 +1,11 @@
from django.apps import AppConfig
from django.db.models.signals import post_migrate
class CFPConfig(AppConfig):
name = 'cfp'
def ready(self):
pass
#import cfp.signals # noqa
#post_migrate.connect(proposals.signals.call_first_site_post_save, sender=self)

View File

@ -0,0 +1,144 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.10.5 on 2017-05-29 20:47
from __future__ import unicode_literals
import autoslug.fields
import colorful.fields
from django.conf import settings
import django.core.validators
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
initial = True
dependencies = [
('sites', '0002_alter_domain_unique'),
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]
operations = [
migrations.CreateModel(
name='Conference',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('home', models.TextField(blank=True, default='')),
('venue', models.TextField(blank=True, default='')),
('city', models.CharField(blank=True, default='', max_length=64)),
('site', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to='sites.Site')),
],
),
migrations.CreateModel(
name='Participant',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('created', models.DateTimeField(auto_now_add=True)),
('updated', models.DateTimeField(auto_now=True)),
('name', models.CharField(max_length=128)),
('email', models.EmailField(max_length=254)),
('phone_number', models.CharField(blank=True, default='', max_length=16, verbose_name='Phone number')),
('biography', models.TextField(blank=True, verbose_name='Biography')),
('notes', models.TextField(blank=True, default='', help_text='This field is only visible by organizers.', verbose_name='Notes')),
('vip', models.BooleanField(default=False)),
('site', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='sites.Site')),
],
),
migrations.CreateModel(
name='Talk',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('created', models.DateTimeField(auto_now_add=True)),
('updated', models.DateTimeField(auto_now=True)),
('title', models.CharField(help_text='After submission, title can only be changed by the staff.', max_length=128, verbose_name='Title')),
('slug', autoslug.fields.AutoSlugField(editable=False, populate_from='title', unique=True)),
('description', models.TextField(blank=True, verbose_name='Description')),
('notes', models.TextField(blank=True, verbose_name='Message to organizers')),
('videotaped', models.BooleanField(default=True, verbose_name="I'm ok to be recorded on video")),
('video_licence', models.IntegerField(choices=[(1, 'CC-Zero'), (2, 'CC-BY'), (3, 'CC-BY-SA'), (4, 'CC-BY-ND'), (5, 'CC-BY-NC'), (6, 'CC-BY-NC-SA'), (7, 'CC-BY-NC-ND')], default=2, verbose_name='Video licence')),
('sound', models.BooleanField(default=False, verbose_name='I need sound')),
('accepted', models.NullBooleanField(default=None)),
('duration', models.PositiveIntegerField(default=0, verbose_name='Duration (min)')),
('plenary', models.BooleanField(default=False)),
],
options={
'ordering': ('category__id', 'title'),
},
),
migrations.CreateModel(
name='TalkCategory',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=64)),
('duration', models.PositiveIntegerField(default=0, verbose_name='Default duration (min)')),
('color', colorful.fields.RGBColorField(default='#ffffff', verbose_name='Color on program')),
('label', models.CharField(blank=True, default='', max_length=64, verbose_name='Label on program')),
('opening_date', models.DateTimeField(blank=True, default=None, null=True)),
('closing_date', models.DateTimeField(blank=True, default=None, null=True)),
('site', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='sites.Site')),
],
options={
'ordering': ('pk',),
},
),
migrations.CreateModel(
name='Track',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('created', models.DateTimeField(auto_now_add=True)),
('updated', models.DateTimeField(auto_now=True)),
('name', models.CharField(max_length=128, verbose_name='Name')),
('slug', autoslug.fields.AutoSlugField(editable=False, populate_from='name')),
('description', models.TextField(blank=True, verbose_name='Description')),
('site', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='sites.Site')),
],
),
migrations.CreateModel(
name='Vote',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('created', models.DateTimeField(auto_now_add=True)),
('updated', models.DateTimeField(auto_now=True)),
('vote', models.IntegerField(default=0, validators=[django.core.validators.MinValueValidator(-2), django.core.validators.MaxValueValidator(2)])),
('talk', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='cfp.Talk')),
('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
],
),
migrations.AddField(
model_name='talk',
name='category',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='cfp.TalkCategory', verbose_name='Intervention kind'),
),
migrations.AddField(
model_name='talk',
name='site',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='sites.Site'),
),
migrations.AddField(
model_name='talk',
name='speakers',
field=models.ManyToManyField(to='cfp.Participant', verbose_name='Speakers'),
),
migrations.AddField(
model_name='talk',
name='track',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='cfp.Track', verbose_name='Track'),
),
migrations.AlterUniqueTogether(
name='vote',
unique_together=set([('talk', 'user')]),
),
migrations.AlterUniqueTogether(
name='track',
unique_together=set([('site', 'name')]),
),
migrations.AlterUniqueTogether(
name='talkcategory',
unique_together=set([('site', 'name')]),
),
migrations.AlterUniqueTogether(
name='participant',
unique_together=set([('site', 'email')]),
),
]

View File

339
cfp/models.py Normal file
View File

@ -0,0 +1,339 @@
from enum import IntEnum
from datetime import timedelta
from os.path import join, basename
from django.contrib.auth.models import User
from django.contrib.sites.models import Site
from django.core.urlresolvers import reverse
from django.core.validators import MaxValueValidator, MinValueValidator
from django.db import models
from django.db.models import Q
from django.utils.translation import ugettext_lazy as _
from django.utils.translation import ugettext
from django.utils import timezone
#from autoslug import AutoSlugField
#from colorful.fields import RGBColorField
#from accounts.models import Participation
from ponyconf.utils import PonyConfModel, enum_to_choices
#from .utils import generate_user_uid
from enum import IntEnum
from datetime import timedelta
from os.path import join, basename
from django.contrib.auth.models import User
from django.contrib.sites.models import Site
from django.core.urlresolvers import reverse
from django.core.validators import MaxValueValidator, MinValueValidator
from django.db import models
from django.db.models import Q
from django.utils.translation import ugettext_lazy as _
from django.utils.translation import ugettext
from django.utils import timezone
from autoslug import AutoSlugField
from colorful.fields import RGBColorField
#from accounts.models import Participation
#from ponyconf.utils import PonyConfModel, enum_to_choices
#from planning.models import Room
from .utils import query_sum
from .utils import generate_user_uid
from enum import IntEnum
from django.contrib.auth.models import User
from django.contrib.sites.models import Site
from django.core.urlresolvers import reverse
from django.db import models
from django.utils.translation import ugettext_lazy as _
from django.utils.translation import ugettext
#from ponyconf.utils import PonyConfModel, enum_to_choices
class Participant(PonyConfModel):
#LICENCES = IntEnum('Video licence', 'CC-Zero CC-BY CC-BY-SA CC-BY-ND CC-BY-NC CC-BY-NC-SA CC-BY-NC-ND')
site = models.ForeignKey(Site, on_delete=models.CASCADE)
name = models.CharField(max_length=128)
email = models.EmailField()
phone_number = models.CharField(max_length=16, blank=True, default='', verbose_name=_('Phone number'))
biography = models.TextField(blank=True, verbose_name=_('Biography'))
#email_token = models.CharField(max_length=12, default=generate_user_uid, unique=True)
# TALK
#videotaped = models.BooleanField(_("I'm ok to be recorded on video"), default=True)
#video_licence = models.IntegerField(choices=enum_to_choices(LICENCES), default=2, verbose_name=_("Video licence"))
#sound = models.BooleanField(_("I need sound"), default=False)
notes = models.TextField(default='', blank=True, verbose_name=_("Notes"), help_text=_('This field is only visible by organizers.'))
vip = models.BooleanField(default=False)
class Meta:
# A User can participe only once to a Conference (= Site)
unique_together = ('site', 'email')
def __str__(self):
return str(self.name)
#def get_absolute_url(self):
# return reverse('show-participant', kwargs={'username': self.user.username})
#def is_orga(self):
# return self.orga
#def is_staff(self):
# return self.is_orga() or self.topic_set.exists() or self.track_set.exists()
#@property
#def topic_set(self):
# return self.user.topic_set.filter(site=self.site)
#@property
#def track_set(self):
# return self.user.track_set.filter(site=self.site)
#@property
#def talk_set(self):
# return self.user.talk_set.filter(site=self.site)
#@property
#def accepted_talk_set(self):
# return self.talk_set.filter(accepted=True)
#@property
#def pending_talk_set(self):
# return self.talk_set.filter(accepted=None)
#@property
#def refused_talk_set(self):
# return self.talk_set.filter(accepted=False)
#@property
#def not_refused_talk_set(self): # accepted + pending
# return self.talk_set.exclude(accepted=False)
class Conference(models.Model):
site = models.OneToOneField(Site, on_delete=models.CASCADE)
home = models.TextField(blank=True, default="")
venue = models.TextField(blank=True, default="")
city = models.CharField(max_length=64, blank=True, default="")
#subscriptions_open = models.BooleanField(default=False) # workshop subscription
#def cfp_is_open(self):
# events = Event.objects.filter(site=self.site)
# return any(map(lambda x: x.is_open(), events))
#@property
#def opened_events(self):
# now = timezone.now()
# return Event.objects.filter(site=self.site)\
# .filter(Q(opening_date__isnull=True) | Q(opening_date__lte=now))\
# .filter(Q(closing_date__isnull=True) | Q(closing_date__gte=now))
def __str__(self):
return str(self.site)
class Track(PonyConfModel):
site = models.ForeignKey(Site, on_delete=models.CASCADE)
name = models.CharField(max_length=128, verbose_name=_('Name'))
slug = AutoSlugField(populate_from='name')
description = models.TextField(blank=True, verbose_name=_('Description'))
#managers = models.ManyToManyField(User, blank=True, verbose_name=_('Managers'))
class Meta:
unique_together = ('site', 'name')
#def estimated_duration(self):
# return sum([talk.estimated_duration for talk in self.talk_set.all()])
def __str__(self):
return self.name
#def get_absolute_url(self):
# return reverse('list-talks') + '?track=%s' % self.slug
#class Topic(PonyConfModel):
#
# site = models.ForeignKey(Site, on_delete=models.CASCADE)
#
# name = models.CharField(max_length=128, verbose_name=_('Name'))
# slug = AutoSlugField(populate_from='name', unique=True)
# description = models.TextField(blank=True, verbose_name=_('Description'))
# track = models.ForeignKey(Track, blank=True, null=True, verbose_name=_('Destination track'))
#
# reviewers = models.ManyToManyField(User, blank=True, verbose_name=_('Reviewers'))
#
# class Meta:
# unique_together = ('site', 'name')
#
# def __str__(self):
# return self.name
#
# def get_absolute_url(self):
# return reverse('list-talks') + '?topic=%s' % self.slug
class TalkCategory(models.Model): # type of talk (conf 30min, 1h, stand, …)
site = models.ForeignKey(Site, on_delete=models.CASCADE)
name = models.CharField(max_length=64)
duration = models.PositiveIntegerField(default=0, verbose_name=_('Default duration (min)'))
color = RGBColorField(default='#ffffff', verbose_name=_("Color on program"))
label = models.CharField(max_length=64, verbose_name=_("Label on program"), blank=True, default="")
opening_date = models.DateTimeField(null=True, blank=True, default=None)
closing_date = models.DateTimeField(null=True, blank=True, default=None)
def is_open(self):
now = timezone.now()
if self.opening_date and now < self.opening_date:
return False
if self.closing_date and now > self.closing_date:
return False
return True
class Meta:
unique_together = ('site', 'name')
ordering = ('pk',)
def __str__(self):
return ugettext(self.name)
#def get_absolute_url(self):
# return reverse('list-talks') + '?kind=%d' % self.pk
#class Attendee(PonyConfModel):
#
# user = models.ForeignKey(User, null=True)
# name = models.CharField(max_length=64, blank=True, default="")
# email = models.EmailField(blank=True, default="")
#
# def get_name(self):
# if self.user:
# return str(self.user.profile)
# else:
# return self.name
# get_name.short_description = _('Name')
#
# def get_email(self):
# if self.user:
# return self.user.email
# else:
# return self.email
# get_email.short_description = _('Email')
#
# def __str__(self):
# return self.get_name()
#def talk_materials_destination(talk, filename):
# return join(talk.site.name, talk.slug, filename)
class Talk(PonyConfModel):
LICENCES = IntEnum('Video licence', 'CC-Zero CC-BY CC-BY-SA CC-BY-ND CC-BY-NC CC-BY-NC-SA CC-BY-NC-ND')
site = models.ForeignKey(Site, on_delete=models.CASCADE)
#proposer = models.ForeignKey(User, related_name='+')
speakers = models.ManyToManyField(Participant, verbose_name=_('Speakers'))
title = models.CharField(max_length=128, verbose_name=_('Title'), help_text=_('After submission, title can only be changed by the staff.'))
slug = AutoSlugField(populate_from='title', unique=True)
#abstract = models.CharField(max_length=255, blank=True, verbose_name=_('Abstract'))
description = models.TextField(blank=True, verbose_name=_('Description'))
track = models.ForeignKey(Track, blank=True, null=True, verbose_name=_('Track'))
notes = models.TextField(blank=True, verbose_name=_('Message to organizers'))
category = models.ForeignKey(TalkCategory, verbose_name=_('Intervention kind'))
videotaped = models.BooleanField(_("I'm ok to be recorded on video"), default=True)
video_licence = models.IntegerField(choices=enum_to_choices(LICENCES), default=2, verbose_name=_("Video licence"))
sound = models.BooleanField(_("I need sound"), default=False)
accepted = models.NullBooleanField(default=None)
#start_date = models.DateTimeField(null=True, blank=True, default=None)
duration = models.PositiveIntegerField(default=0, verbose_name=_('Duration (min)'))
#room = models.ForeignKey(Room, blank=True, null=True, default=None)
plenary = models.BooleanField(default=False)
#materials = models.FileField(null=True, upload_to=talk_materials_destination, verbose_name=_('Materials'),
# help_text=_('You can use this field to share some materials related to your intervention.'))
class Meta:
ordering = ('title',)
def __str__(self):
return self.title
def get_speakers_str(self):
speakers = [str(Participant.objects.get(site=self.site, user=speaker)) for speaker in self.speakers.all()]
if len(speakers) == 0:
return 'superman'
elif len(speakers) == 1:
return speakers[0]
else:
return ', '.join(speakers[:-1]) + ' & ' + str(speakers[-1])
@property
def estimated_duration(self):
return self.duration or self.category.duration
#def get_absolute_url(self):
# return reverse('show-talk', kwargs={'slug': self.slug})
def score(self):
if self.vote_set.exists():
return query_sum(self.vote_set, 'vote') / len(self.vote_set.all())
else:
return 0
@property
def end_date(self):
if self.estimated_duration:
return self.start_date + timedelta(minutes=self.estimated_duration)
else:
return None
#@property
#def dtstart(self):
# return self.start_date.strftime('%Y%m%dT%H%M%SZ')
#@property
#def dtend(self):
# return self.end_date.strftime('%Y%m%dT%H%M%SZ')
#@property
#def materials_name(self):
# return basename(self.materials.name)
class Meta:
ordering = ('category__id', 'title',)
class Vote(PonyConfModel):
talk = models.ForeignKey(Talk)
user = models.ForeignKey(User)
vote = models.IntegerField(validators=[MinValueValidator(-2), MaxValueValidator(2)], default=0)
class Meta:
unique_together = ('talk', 'user')
def __str__(self):
return "%+i by %s for %s" % (self.vote, self.user, self.talk)
#def get_absolute_url(self):
# return self.talk.get_absolute_url()

28
cfp/urls.py Normal file
View File

@ -0,0 +1,28 @@
from django.conf.urls import url
#from proposals import views
urlpatterns = [
#url(r'^markdown/$', views.markdown_preview, name='markdown'),
#url(r'^$', views.home, name='home'),
#url(r'^staff/$', views.staff, name='staff'),
#url(r'^conference/$', views.conference, name='edit-conference'),
#url(r'^talk/propose/$', views.participate, name='participate-as-speaker'),
#url(r'^talk/$', views.talk_list, name='list-talks'),
#url(r'^talk/add/$', views.talk_edit, name='add-talk'),
#url(r'^talk/edit/(?P<talk>[-\w]+)$', views.talk_edit, name='edit-talk'),
#url(r'^talk/vote/(?P<talk>[-\w]+)/(?P<score>[-0-2]+)$', views.vote, name='vote'),
#url(r'^talk/details/(?P<slug>[-\w]+)$', views.TalkDetail.as_view(), name='show-talk'),
#url(r'^talk/accept/(?P<talk>[-\w]+)/$', views.talk_decide, {'accepted': True}, name='accept-talk'),
#url(r'^talk/decline/(?P<talk>[-\w]+)/$', views.talk_decide, {'accepted': False}, name='decline-talk'),
#url(r'^talk/assign-to-track/(?P<talk>[-\w]+)/(?P<track>[-\w]+)/$', views.talk_assign_to_track, name='assign-talk-to-track'),
#url(r'^topic/$', views.TopicList.as_view(), name='list-topics'),
#url(r'^topic/add/$', views.TopicCreate.as_view(), name='add-topic'),
#url(r'^topic/(?P<slug>[-\w]+)/edit/$', views.TopicUpdate.as_view(), name='edit-topic'),
#url(r'^track/$', views.TrackList.as_view(), name='list-tracks'),
#url(r'^track/add/$', views.TrackCreate.as_view(), name='add-track'),
#url(r'^track/(?P<slug>[-\w]+)/edit/$', views.TrackUpdate.as_view(), name='edit-track'),
#url(r'^speakers/$', views.speaker_list, name='list-speakers'),
#url(r'^register/$', views.talk_registrable_list, name='list-registrable-talks'),
#url(r'^register/(?P<talk>[-\w]+)$', views.talk_register, name='register-for-a-talk'),
]

31
cfp/utils.py Normal file
View File

@ -0,0 +1,31 @@
from django.utils.crypto import get_random_string
def generate_user_uid():
return get_random_string(length=12, allowed_chars='abcdefghijklmnopqrstuvwxyz0123456789')
from django.contrib.sites.shortcuts import get_current_site
from django.db.models import Q, Sum
from django.db.models.functions import Coalesce
from django.utils.safestring import mark_safe
#from accounts.models import Participation
from markdown import markdown
import bleach
def query_sum(queryset, field):
return queryset.aggregate(s=Coalesce(Sum(field), 0))['s']
def allowed_talks(talks, request):
if not Participation.objects.get(site=get_current_site(request), user=request.user).is_orga():
talks = talks.filter(Q(topics__reviewers=request.user) | Q(speakers=request.user) | Q(proposer=request.user))
return talks.distinct()
def markdown_to_html(md):
html = markdown(md)
allowed_tags = bleach.ALLOWED_TAGS + ['p', 'pre', 'span' ] + ['h%d' % i for i in range(1, 7) ]
html = bleach.clean(html, tags=allowed_tags)
return mark_safe(html)

View File

@ -37,19 +37,20 @@ INSTALLED_APPS = [
'django.contrib.sites',
# our apps
'accounts',
#'accounts',
'ponyconf',
'proposals',
'conversations',
'planning',
'volunteers',
'cfp',
#'proposals',
#'conversations',
#'planning',
#'volunteers',
# external apps
'djangobower',
#'djangobower',
'bootstrap3',
'registration',
'django_select2',
'avatar',
#'registration',
#'django_select2',
#'avatar',
# build-in apps
'django.contrib.admin',

View File

@ -21,9 +21,10 @@ from django.conf import settings
urlpatterns = [
url(r'^admin/', admin.site.urls),
url(r'^accounts/', include('accounts.urls')),
url(r'', include('proposals.urls')),
url(r'', include('planning.urls')),
url(r'^volunteers/', include('volunteers.urls')),
url(r'^conversations/', include('conversations.urls')),
url(r'^select2/', include('django_select2.urls')),
url(r'^', include('cfp.urls')),
#url(r'', include('proposals.urls')),
#url(r'', include('planning.urls')),
#url(r'^volunteers/', include('volunteers.urls')),
#url(r'^conversations/', include('conversations.urls')),
#url(r'^select2/', include('django_select2.urls')),
] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)