communications app
This commit is contained in:
parent
d30ce1e3a5
commit
ff9577b1f4
|
@ -1,5 +1,5 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
# Generated by Django 1.9.7 on 2016-06-12 15:10
|
# Generated by Django 1.9.7 on 2016-06-12 21:41
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
|
@ -14,8 +14,8 @@ class Migration(migrations.Migration):
|
||||||
initial = True
|
initial = True
|
||||||
|
|
||||||
dependencies = [
|
dependencies = [
|
||||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
|
||||||
('sites', '0002_alter_domain_unique'),
|
('sites', '0002_alter_domain_unique'),
|
||||||
|
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||||
]
|
]
|
||||||
|
|
||||||
operations = [
|
operations = [
|
||||||
|
@ -35,7 +35,7 @@ class Migration(migrations.Migration):
|
||||||
('departure', models.DateTimeField(blank=True, null=True)),
|
('departure', models.DateTimeField(blank=True, null=True)),
|
||||||
('transport', models.IntegerField(blank=True, choices=[(1, 'train'), (2, 'plane')], null=True)),
|
('transport', models.IntegerField(blank=True, choices=[(1, 'train'), (2, 'plane')], null=True)),
|
||||||
('connector', models.IntegerField(blank=True, choices=[(1, 'VGA'), (2, 'HDMI'), (3, 'miniDP')], null=True)),
|
('connector', models.IntegerField(blank=True, choices=[(1, 'VGA'), (2, 'HDMI'), (3, 'miniDP')], null=True)),
|
||||||
('constraints', models.TextField()),
|
('constraints', models.TextField(blank=True)),
|
||||||
('site', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='sites.Site')),
|
('site', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='sites.Site')),
|
||||||
('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
|
('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
|
||||||
],
|
],
|
||||||
|
|
|
@ -37,7 +37,7 @@ class Speaker(models.Model):
|
||||||
departure = models.DateTimeField(blank=True, null=True)
|
departure = models.DateTimeField(blank=True, null=True)
|
||||||
transport = models.IntegerField(choices=enum_to_choices(TRANSPORTS), blank=True, null=True)
|
transport = models.IntegerField(choices=enum_to_choices(TRANSPORTS), blank=True, null=True)
|
||||||
connector = models.IntegerField(choices=enum_to_choices(CONNECTORS), blank=True, null=True)
|
connector = models.IntegerField(choices=enum_to_choices(CONNECTORS), blank=True, null=True)
|
||||||
constraints = models.TextField()
|
constraints = models.TextField(blank=True)
|
||||||
|
|
||||||
objects = models.Manager()
|
objects = models.Manager()
|
||||||
on_site = CurrentSiteManager()
|
on_site = CurrentSiteManager()
|
||||||
|
|
|
@ -28,7 +28,7 @@
|
||||||
|
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-md-offset-4 col-md-4">
|
<div class="col-md-offset-4 col-md-4">
|
||||||
You do not have an account? Please <a href="{% url 'registration_register' %}">register</a>.
|
You do not have an account yet? Please <a href="{% url 'registration_register' %}">register</a>.
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
default_app_config = 'conversations.apps.ConversationsConfig'
|
|
@ -0,0 +1,7 @@
|
||||||
|
from django.contrib import admin
|
||||||
|
|
||||||
|
from .models import Conversation, Message
|
||||||
|
|
||||||
|
|
||||||
|
admin.site.register(Conversation)
|
||||||
|
admin.site.register(Message)
|
|
@ -0,0 +1,8 @@
|
||||||
|
from django.apps import AppConfig
|
||||||
|
|
||||||
|
|
||||||
|
class ConversationsConfig(AppConfig):
|
||||||
|
name = 'conversations'
|
||||||
|
|
||||||
|
def ready(self):
|
||||||
|
import conversations.signals
|
|
@ -0,0 +1,42 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Generated by Django 1.9.7 on 2016-06-12 21:41
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
from django.conf import settings
|
||||||
|
from django.db import migrations, models
|
||||||
|
import django.db.models.deletion
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
initial = True
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||||
|
('accounts', '0001_initial'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='Conversation',
|
||||||
|
fields=[
|
||||||
|
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('speaker', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='conversation', to='accounts.Speaker')),
|
||||||
|
('subscribers', models.ManyToManyField(related_name='_conversation_subscribers_+', to=settings.AUTH_USER_MODEL)),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='Message',
|
||||||
|
fields=[
|
||||||
|
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('token', models.CharField(max_length=64)),
|
||||||
|
('date', models.DateTimeField(auto_now_add=True)),
|
||||||
|
('content', models.TextField()),
|
||||||
|
('author', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
|
||||||
|
('conversation', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='messages', to='conversations.Conversation')),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'ordering': ['date'],
|
||||||
|
},
|
||||||
|
),
|
||||||
|
]
|
|
@ -0,0 +1,29 @@
|
||||||
|
from django.db import models
|
||||||
|
from django.contrib.auth.models import User
|
||||||
|
|
||||||
|
from accounts.models import Speaker
|
||||||
|
|
||||||
|
|
||||||
|
class Conversation(models.Model):
|
||||||
|
|
||||||
|
speaker = models.ForeignKey(Speaker, related_name='conversation')
|
||||||
|
subscribers = models.ManyToManyField(User, related_name='+')
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return "Conversation with %s" % self.speaker
|
||||||
|
|
||||||
|
|
||||||
|
class Message(models.Model):
|
||||||
|
|
||||||
|
token = models.CharField(max_length=64)
|
||||||
|
conversation = models.ForeignKey(Conversation, related_name='messages')
|
||||||
|
|
||||||
|
author = models.ForeignKey(User)
|
||||||
|
date = models.DateTimeField(auto_now_add=True)
|
||||||
|
content = models.TextField()
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
ordering = ['date']
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return "Message from %s" % self.author
|
|
@ -0,0 +1,77 @@
|
||||||
|
from django.db.models.signals import post_save
|
||||||
|
from django.dispatch import receiver
|
||||||
|
from django.conf import settings
|
||||||
|
from django.core.urlresolvers import reverse
|
||||||
|
from django.template.loader import render_to_string
|
||||||
|
from django.core import mail
|
||||||
|
from django.core.mail import EmailMultiAlternatives
|
||||||
|
|
||||||
|
|
||||||
|
from .models import Message
|
||||||
|
from .utils import get_reply_addr
|
||||||
|
from proposals.models import Talk, Topic
|
||||||
|
|
||||||
|
|
||||||
|
@receiver(post_save, sender=Message, dispatch_uid="Notify new message")
|
||||||
|
def notify_new_message(sender, instance, created, **kwargs):
|
||||||
|
if not created:
|
||||||
|
# We could send a modification notification
|
||||||
|
return
|
||||||
|
message = instance
|
||||||
|
conversation = message.conversation
|
||||||
|
site = conversation.speaker.site
|
||||||
|
subject = site.name
|
||||||
|
sender = instance.author
|
||||||
|
dests = list(conversation.subscribers.all())
|
||||||
|
if conversation.speaker.user not in dests:
|
||||||
|
dests += [conversation.speaker.user]
|
||||||
|
data = {
|
||||||
|
'content': message.content,
|
||||||
|
'uri': site.domain + reverse('show-conversation', args=[conversation.id]),
|
||||||
|
}
|
||||||
|
message_id = message.token
|
||||||
|
ref = None
|
||||||
|
if conversation.messages.first().id != message.id:
|
||||||
|
ref = conversation.messages.first().token
|
||||||
|
notify_by_email(data, 'new_message', subject, sender, dests, message_id, ref)
|
||||||
|
|
||||||
|
|
||||||
|
def notify_by_email(data, template, subject, sender, dests, message_id, ref=None):
|
||||||
|
|
||||||
|
if hasattr(settings, 'REPLY_EMAIL'):
|
||||||
|
data.update({'answering': True})
|
||||||
|
|
||||||
|
text_message = render_to_string('conversations/%s.txt' % template, data)
|
||||||
|
html_message = render_to_string('conversations/%s.html' % template, data)
|
||||||
|
|
||||||
|
from_email = '{name} <{email}>'.format(
|
||||||
|
name=sender.get_full_name() or sender.username,
|
||||||
|
email=settings.DEFAULT_FROM_EMAIL)
|
||||||
|
|
||||||
|
# Generating headers
|
||||||
|
headers = {
|
||||||
|
'Message-ID': "<%s.%s>" % (message_id, settings.DEFAULT_FROM_EMAIL),
|
||||||
|
}
|
||||||
|
if ref:
|
||||||
|
# This email reference a previous one
|
||||||
|
headers.update({
|
||||||
|
'References': '<%s.%s>' % (ref, settings.DEFAULT_FROM_EMAIL),
|
||||||
|
})
|
||||||
|
|
||||||
|
mails = []
|
||||||
|
for dest in dests:
|
||||||
|
if not dest.email:
|
||||||
|
continue
|
||||||
|
|
||||||
|
reply_to = get_reply_addr(message_id, dest)
|
||||||
|
|
||||||
|
mails += [(subject, (text_message, html_message), from_email, [dest.email], reply_to, headers)]
|
||||||
|
|
||||||
|
messages = []
|
||||||
|
for subject, message, from_email, dests, reply_to, headers in mails:
|
||||||
|
text_message, html_message = message
|
||||||
|
msg = EmailMultiAlternatives(subject, text_message, from_email, dests, reply_to=reply_to, headers=headers)
|
||||||
|
msg.attach_alternative(html_message, 'text/html')
|
||||||
|
messages += [msg]
|
||||||
|
with mail.get_connection() as connection:
|
||||||
|
connection.send_messages(messages)
|
|
@ -0,0 +1,8 @@
|
||||||
|
{{ content|safe }}
|
||||||
|
|
||||||
|
<hr>
|
||||||
|
{% if answering %}
|
||||||
|
Reply to this email directly or <a href="{{ uri }}">view it online</a>.
|
||||||
|
{% else %}
|
||||||
|
<a href="{{ uri }}">Reply online</a>.
|
||||||
|
{% endif %}
|
|
@ -0,0 +1,4 @@
|
||||||
|
{{ content|safe }}
|
||||||
|
|
||||||
|
--
|
||||||
|
Reply {% if answering %}to this email directly or view it {% endif %}online: {{ uri }}
|
|
@ -0,0 +1,3 @@
|
||||||
|
from django.test import TestCase
|
||||||
|
|
||||||
|
# Create your tests here.
|
|
@ -0,0 +1,8 @@
|
||||||
|
from django.conf.urls import url
|
||||||
|
|
||||||
|
from conversations import views
|
||||||
|
|
||||||
|
|
||||||
|
urlpatterns = [
|
||||||
|
url(r'^(?P<conversation>[0-9]+)$', views.conversation_details, name='show-conversation'),
|
||||||
|
]
|
|
@ -0,0 +1,26 @@
|
||||||
|
from django.conf import settings
|
||||||
|
|
||||||
|
import hashlib
|
||||||
|
|
||||||
|
|
||||||
|
def hexdigest_sha256(*args):
|
||||||
|
|
||||||
|
r = hashlib.sha256()
|
||||||
|
for arg in args:
|
||||||
|
r.update(str(arg).encode('utf-8'))
|
||||||
|
|
||||||
|
return r.hexdigest()
|
||||||
|
|
||||||
|
|
||||||
|
def get_reply_addr(message_id, dest):
|
||||||
|
|
||||||
|
if not hasattr(settings, 'REPLY_EMAIL'):
|
||||||
|
return []
|
||||||
|
|
||||||
|
addr = settings.REPLY_EMAIL
|
||||||
|
pos = addr.find('@')
|
||||||
|
name = addr[:pos]
|
||||||
|
domain = addr[pos:]
|
||||||
|
key = hexdigest_sha256(settings.SECRET_KEY, message_id, dest.pk)
|
||||||
|
|
||||||
|
return ['%s+%10s%s%10s%s' % (name, dest.pk, message_id, key, domain)]
|
|
@ -0,0 +1,13 @@
|
||||||
|
from django.shortcuts import render
|
||||||
|
from django.shortcuts import get_object_or_404, redirect, render
|
||||||
|
from django.contrib.sites.shortcuts import get_current_site
|
||||||
|
|
||||||
|
from .models import Message
|
||||||
|
|
||||||
|
|
||||||
|
def conversation_details(request, conversation):
|
||||||
|
conversation = get_object_or_404(Conversation,
|
||||||
|
id=conversation, speaker__site=get_current_site(request))
|
||||||
|
return render(request, 'conversations/message.html', {
|
||||||
|
'messages': conversation.messages,
|
||||||
|
})
|
|
@ -31,8 +31,7 @@ ALLOWED_HOSTS = []
|
||||||
# Application definition
|
# Application definition
|
||||||
|
|
||||||
INSTALLED_APPS = [
|
INSTALLED_APPS = [
|
||||||
'accounts',
|
# build-in apps
|
||||||
'registration',
|
|
||||||
'django.contrib.admin',
|
'django.contrib.admin',
|
||||||
'django.contrib.auth',
|
'django.contrib.auth',
|
||||||
'django.contrib.contenttypes',
|
'django.contrib.contenttypes',
|
||||||
|
@ -41,11 +40,16 @@ INSTALLED_APPS = [
|
||||||
'django.contrib.staticfiles',
|
'django.contrib.staticfiles',
|
||||||
'django.contrib.sites',
|
'django.contrib.sites',
|
||||||
|
|
||||||
|
# external apps
|
||||||
'djangobower',
|
'djangobower',
|
||||||
'bootstrap3',
|
'bootstrap3',
|
||||||
|
'registration',
|
||||||
|
|
||||||
|
# our apps
|
||||||
|
'accounts',
|
||||||
'ponyconf',
|
'ponyconf',
|
||||||
'proposals',
|
'proposals',
|
||||||
|
'conversations',
|
||||||
]
|
]
|
||||||
|
|
||||||
MIDDLEWARE_CLASSES = [
|
MIDDLEWARE_CLASSES = [
|
||||||
|
|
|
@ -21,4 +21,5 @@ urlpatterns = [
|
||||||
url(r'^accounts/', include('accounts.urls')),
|
url(r'^accounts/', include('accounts.urls')),
|
||||||
url(r'^registration/', include('registration.backends.default.urls')),
|
url(r'^registration/', include('registration.backends.default.urls')),
|
||||||
url(r'^', include('proposals.urls')),
|
url(r'^', include('proposals.urls')),
|
||||||
|
url(r'^conversations/', include('conversations.urls')),
|
||||||
]
|
]
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
from django.contrib import admin
|
from django.contrib import admin
|
||||||
|
|
||||||
from proposals.models import Speach, Talk, Topic
|
from proposals.models import Speech, Talk, Topic
|
||||||
|
|
||||||
admin.site.register(Topic)
|
admin.site.register(Topic)
|
||||||
admin.site.register(Talk)
|
admin.site.register(Talk)
|
||||||
admin.site.register(Speach)
|
admin.site.register(Speech)
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
# Generated by Django 1.9.7 on 2016-06-12 16:42
|
# Generated by Django 1.9.7 on 2016-06-12 21:41
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
import autoslug.fields
|
import autoslug.fields
|
||||||
|
@ -21,7 +21,7 @@ class Migration(migrations.Migration):
|
||||||
|
|
||||||
operations = [
|
operations = [
|
||||||
migrations.CreateModel(
|
migrations.CreateModel(
|
||||||
name='Speach',
|
name='Speech',
|
||||||
fields=[
|
fields=[
|
||||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
('order', models.IntegerField(choices=[(1, '1'), (2, '2'), (3, '3'), (4, '4'), (5, '5'), (6, '6'), (7, '7')], default=1)),
|
('order', models.IntegerField(choices=[(1, '1'), (2, '2'), (3, '3'), (4, '4'), (5, '5'), (6, '6'), (7, '7')], default=1)),
|
||||||
|
@ -38,8 +38,9 @@ class Migration(migrations.Migration):
|
||||||
('title', models.CharField(max_length=128, verbose_name='Title')),
|
('title', models.CharField(max_length=128, verbose_name='Title')),
|
||||||
('slug', autoslug.fields.AutoSlugField(editable=False, populate_from='title', unique=True)),
|
('slug', autoslug.fields.AutoSlugField(editable=False, populate_from='title', unique=True)),
|
||||||
('description', models.TextField(blank=True, verbose_name='Description')),
|
('description', models.TextField(blank=True, verbose_name='Description')),
|
||||||
|
('event', models.IntegerField(choices=[(1, 'conference'), (2, 'workshop'), (3, 'stand'), (4, 'other')], default=1)),
|
||||||
('site', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='sites.Site')),
|
('site', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='sites.Site')),
|
||||||
('speakers', models.ManyToManyField(through='proposals.Speach', to=settings.AUTH_USER_MODEL)),
|
('speakers', models.ManyToManyField(through='proposals.Speech', to=settings.AUTH_USER_MODEL)),
|
||||||
],
|
],
|
||||||
managers=[
|
managers=[
|
||||||
('objects', django.db.models.manager.Manager()),
|
('objects', django.db.models.manager.Manager()),
|
||||||
|
@ -60,12 +61,12 @@ class Migration(migrations.Migration):
|
||||||
field=models.ManyToManyField(blank=True, to='proposals.Topic'),
|
field=models.ManyToManyField(blank=True, to='proposals.Topic'),
|
||||||
),
|
),
|
||||||
migrations.AddField(
|
migrations.AddField(
|
||||||
model_name='speach',
|
model_name='speech',
|
||||||
name='talk',
|
name='talk',
|
||||||
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='proposals.Talk'),
|
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='proposals.Talk'),
|
||||||
),
|
),
|
||||||
migrations.AlterUniqueTogether(
|
migrations.AlterUniqueTogether(
|
||||||
name='speach',
|
name='speech',
|
||||||
unique_together=set([('order', 'talk'), ('speaker', 'talk')]),
|
unique_together=set([('order', 'talk'), ('speaker', 'talk')]),
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
|
|
|
@ -1,20 +0,0 @@
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
# Generated by Django 1.9.7 on 2016-06-12 20:26
|
|
||||||
from __future__ import unicode_literals
|
|
||||||
|
|
||||||
from django.db import migrations, models
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
|
||||||
|
|
||||||
dependencies = [
|
|
||||||
('proposals', '0001_initial'),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
|
||||||
migrations.AddField(
|
|
||||||
model_name='talk',
|
|
||||||
name='event',
|
|
||||||
field=models.IntegerField(choices=[(1, 'conference'), (2, 'workshop'), (3, 'stand'), (4, 'other')], default=1),
|
|
||||||
),
|
|
||||||
]
|
|
|
@ -10,7 +10,7 @@ from autoslug import AutoSlugField
|
||||||
|
|
||||||
from accounts.models import enum_to_choices
|
from accounts.models import enum_to_choices
|
||||||
|
|
||||||
__all__ = ['Topic', 'Talk', 'Speach']
|
__all__ = ['Topic', 'Talk', 'Speech']
|
||||||
|
|
||||||
|
|
||||||
class Topic(models.Model):
|
class Topic(models.Model):
|
||||||
|
@ -31,7 +31,7 @@ class Talk(models.Model):
|
||||||
|
|
||||||
site = models.ForeignKey(Site, on_delete=models.CASCADE)
|
site = models.ForeignKey(Site, on_delete=models.CASCADE)
|
||||||
|
|
||||||
speakers = models.ManyToManyField(User, through='Speach')
|
speakers = models.ManyToManyField(User, through='Speech')
|
||||||
title = models.CharField(max_length=128, verbose_name='Title')
|
title = models.CharField(max_length=128, verbose_name='Title')
|
||||||
slug = AutoSlugField(populate_from='title', unique=True)
|
slug = AutoSlugField(populate_from='title', unique=True)
|
||||||
description = models.TextField(blank=True, verbose_name='Description')
|
description = models.TextField(blank=True, verbose_name='Description')
|
||||||
|
@ -48,7 +48,7 @@ class Talk(models.Model):
|
||||||
return reverse('show-talk', kwargs={'slug': self.slug})
|
return reverse('show-talk', kwargs={'slug': self.slug})
|
||||||
|
|
||||||
|
|
||||||
class Speach(models.Model):
|
class Speech(models.Model):
|
||||||
|
|
||||||
SPEAKER_NO = tuple((i, str(i)) for i in range(1, 8))
|
SPEAKER_NO = tuple((i, str(i)) for i in range(1, 8))
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,7 @@ from django.contrib.auth.models import User
|
||||||
from django.core.urlresolvers import reverse
|
from django.core.urlresolvers import reverse
|
||||||
from django.test import TestCase
|
from django.test import TestCase
|
||||||
|
|
||||||
from .models import Talk, Topic, Speach
|
from .models import Talk, Topic, Speech
|
||||||
|
|
||||||
|
|
||||||
class ProposalsTests(TestCase):
|
class ProposalsTests(TestCase):
|
||||||
|
@ -36,7 +36,7 @@ class ProposalsTests(TestCase):
|
||||||
self.assertEqual(self.client.get(reverse('list-talks')).status_code, 200)
|
self.assertEqual(self.client.get(reverse('list-talks')).status_code, 200)
|
||||||
|
|
||||||
# Models str & get_asbolute_url
|
# Models str & get_asbolute_url
|
||||||
for model in [Talk, Topic, Speach]:
|
for model in [Talk, Topic, Speech]:
|
||||||
item = model.objects.first()
|
item = model.objects.first()
|
||||||
self.assertEqual(self.client.get(item.get_absolute_url()).status_code, 200)
|
self.assertEqual(self.client.get(item.get_absolute_url()).status_code, 200)
|
||||||
self.assertTrue(str(item))
|
self.assertTrue(str(item))
|
||||||
|
|
|
@ -9,7 +9,7 @@ from django.views.generic import DetailView, ListView
|
||||||
|
|
||||||
from accounts.models import Speaker
|
from accounts.models import Speaker
|
||||||
from proposals.forms import TalkForm
|
from proposals.forms import TalkForm
|
||||||
from proposals.models import Speach, Talk, Topic
|
from proposals.models import Speech, Talk, Topic
|
||||||
|
|
||||||
|
|
||||||
def home(request):
|
def home(request):
|
||||||
|
@ -63,7 +63,7 @@ def talk_edit(request, talk=None):
|
||||||
talk.save()
|
talk.save()
|
||||||
form.save_m2m()
|
form.save_m2m()
|
||||||
Speaker.on_site.get_or_create(user=request.user, site=site)
|
Speaker.on_site.get_or_create(user=request.user, site=site)
|
||||||
Speach.objects.create(speaker=request.user, talk=talk)
|
Speech.objects.create(speaker=request.user, talk=talk)
|
||||||
messages.success(request, 'Talk proposed successfully!')
|
messages.success(request, 'Talk proposed successfully!')
|
||||||
return redirect(talk.get_absolute_url())
|
return redirect(talk.get_absolute_url())
|
||||||
return render(request, 'proposals/talk_edit.html', {
|
return render(request, 'proposals/talk_edit.html', {
|
||||||
|
@ -80,7 +80,7 @@ class TopicList(LoginRequiredMixin, ListView):
|
||||||
|
|
||||||
|
|
||||||
class SpeakerList(LoginRequiredMixin, ListView):
|
class SpeakerList(LoginRequiredMixin, ListView):
|
||||||
queryset = User.objects.filter(speach__talk=Talk.on_site.all())
|
queryset = User.objects.filter(speech__talk=Talk.on_site.all())
|
||||||
template_name = 'proposals/speaker_list.html'
|
template_name = 'proposals/speaker_list.html'
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue