Better app and model names. Some model hygiene. Added admin.

- Legacy-Id: 9993
This commit is contained in:
Robert Sparks 2015-08-09 20:11:26 +00:00
parent c0acadf222
commit 7649397f5b
21 changed files with 171 additions and 135 deletions

View file

@ -14,7 +14,7 @@ from ietf.doc.utils import needed_ballot_positions
from ietf.person.models import Person
from ietf.group.models import Group, Role
from ietf.doc.models import Document
from ietf.eventmail.utils import gather_addresses
from ietf.mailtoken.utils import gather_addresses
def email_state_changed(request, doc, text):
to = [x.strip() for x in doc.notify.replace(';', ',').split(',')]

View file

@ -15,7 +15,7 @@ from ietf.utils.test_utils import TestCase
from ietf.utils.mail import outbox
from ietf.utils.test_data import make_test_data
from ietf.utils.test_utils import login_testing_unauthorized
from ietf.eventmail.utils import gather_addresses
from ietf.mailtoken.utils import gather_addresses
class EditPositionTests(TestCase):

View file

@ -27,7 +27,7 @@ from ietf.message.utils import infer_message
from ietf.name.models import BallotPositionName
from ietf.person.models import Person
from ietf.utils.mail import send_mail_text, send_mail_preformatted
from ietf.eventmail.utils import gather_addresses
from ietf.mailtoken.utils import gather_addresses
BALLOT_CHOICES = (("yes", "Yes"),
("noobj", "No Objection"),

View file

@ -37,7 +37,7 @@ from ietf.person.models import Person, Email
from ietf.secr.lib.template import jsonapi
from ietf.utils.mail import send_mail, send_mail_message
from ietf.utils.textupload import get_cleaned_text_file_content
from ietf.eventmail.utils import gather_addresses
from ietf.mailtoken.utils import gather_addresses
class ChangeStateForm(forms.Form):
state = forms.ModelChoiceField(State.objects.filter(used=True, type="draft-iesg"), empty_label=None, required=True)

View file

@ -1,11 +0,0 @@
from django.conf.urls import patterns, url
from django.views.generic import RedirectView
from django.core.urlresolvers import reverse_lazy
urlpatterns = patterns('ietf.eventmail.views',
url(r'^$', RedirectView.as_view(url=reverse_lazy('eventmail_show_patterns'), permanent=True)),
url(r'^event/$', 'show_patterns', name='eventmail_show_patterns' ),
url(r'^event/(?P<eventmail_slug>[-\w]+)/$', 'show_patterns' ),
url(r'^recipient/$', 'show_ingredients' ),
url(r'^recipient/(?P<ingredient_slug>[-\w]+)/$', 'show_ingredients' ),
)

View file

@ -1,24 +0,0 @@
# Copyright The IETF Trust 2015, All Rights Reserved
from inspect import getsourcelines
from django.shortcuts import render
from ietf.eventmail.models import Recipe, Ingredient
def show_patterns(request, eventmail_slug=None):
recipes = Recipe.objects.all()
if eventmail_slug:
recipes = recipes.filter(slug=eventmail_slug) # TODO better 404 behavior here and below
return render(request,'eventmail/show_patterns.html',{'eventmail_slug':eventmail_slug,
'recipes':recipes})
def show_ingredients(request, ingredient_slug=None):
ingredients = Ingredient.objects.all()
if ingredient_slug:
ingredients = ingredients.filter(slug=ingredient_slug)
for ingredient in ingredients:
fname = 'gather_%s'%ingredient.slug
if hasattr(ingredient,fname):
ingredient.code = ''.join(getsourcelines(getattr(ingredient,fname))[0])
return render(request,'eventmail/ingredient.html',{'ingredient_slug':ingredient_slug,
'ingredients':ingredients})

17
ietf/mailtoken/admin.py Normal file
View file

@ -0,0 +1,17 @@
from django.contrib import admin
from ietf.mailtoken.models import MailToken, Recipient
class RecipientAdmin(admin.ModelAdmin):
list_display = [ 'slug', 'desc', 'template', 'has_code', ]
def has_code(self, obj):
return hasattr(obj,'gather_%s'%obj.slug)
has_code.boolean = True
admin.site.register(Recipient, RecipientAdmin)
class MailTokenAdmin(admin.ModelAdmin):
list_display = [ 'slug', 'desc', ]
filter_horizontal = [ 'recipients' ]
admin.site.register(MailToken, MailTokenAdmin)

View file

@ -11,25 +11,32 @@ class Migration(migrations.Migration):
operations = [
migrations.CreateModel(
name='Ingredient',
name='MailToken',
fields=[
('slug', models.CharField(max_length=32, serialize=False, primary_key=True)),
('desc', models.TextField(blank=True)),
],
options={
'ordering': ['slug'],
},
bases=(models.Model,),
),
migrations.CreateModel(
name='Recipient',
fields=[
('slug', models.CharField(max_length=32, serialize=False, primary_key=True)),
('desc', models.TextField(blank=True)),
('template', models.CharField(max_length=512, null=True, blank=True)),
],
options={
'ordering': ['slug'],
},
bases=(models.Model,),
),
migrations.CreateModel(
name='Recipe',
fields=[
('slug', models.CharField(max_length=32, serialize=False, primary_key=True)),
('desc', models.TextField(blank=True)),
('ingredients', models.ManyToManyField(to='eventmail.Ingredient', null=True, blank=True)),
],
options={
},
bases=(models.Model,),
migrations.AddField(
model_name='mailtoken',
name='recipients',
field=models.ManyToManyField(to='mailtoken.Recipient', null=True, blank=True),
preserve_default=True,
),
]

View file

@ -3,16 +3,28 @@
from django.db import models
from django.template import Template, Context
class Recipe(models.Model):
class MailToken(models.Model):
slug = models.CharField(max_length=32, primary_key=True)
desc = models.TextField(blank=True)
ingredients = models.ManyToManyField('Ingredient', null=True, blank=True)
recipients = models.ManyToManyField('Recipient', null=True, blank=True)
class Ingredient(models.Model):
class Meta:
ordering = ["slug"]
def __unicode__(self):
return self.slug
class Recipient(models.Model):
slug = models.CharField(max_length=32, primary_key=True)
desc = models.TextField(blank=True)
template = models.CharField(max_length=512, null=True, blank=True)
class Meta:
ordering = ["slug"]
def __unicode__(self):
return self.slug
def gather(self, **kwargs):
retval = []
if hasattr(self,'gather_%s'%self.slug):
@ -45,73 +57,73 @@ class Ingredient(models.Model):
addrs = []
if 'doc' in kwargs:
for reldoc in kwargs['doc'].related_that_doc(['conflrev','tohist','tois','tops']):
addrs.extend(Ingredient.objects.get(slug='doc_authors').gather(**{'doc':reldoc.document}))
addrs.extend(Recipient.objects.get(slug='doc_authors').gather(**{'doc':reldoc.document}))
return addrs
def gather_doc_affecteddoc_group_chairs(self, **kwargs):
addrs = []
if 'doc' in kwargs:
for reldoc in kwargs['doc'].related_that_doc(['conflrev','tohist','tois','tops']):
addrs.extend(Ingredient.objects.get(slug='doc_group_chairs').gather(**{'doc':reldoc.document}))
addrs.extend(Recipient.objects.get(slug='doc_group_chairs').gather(**{'doc':reldoc.document}))
return addrs
def make_ingredients():
def make_recipients():
Ingredient.objects.all().delete()
Ingredient.objects.create(slug='iesg',
Recipient.objects.all().delete()
Recipient.objects.create(slug='iesg',
desc='The IESG',
template='The IESG <iesg@ietf.org>')
Ingredient.objects.create(slug='ietf_announce',
Recipient.objects.create(slug='ietf_announce',
desc='The IETF Announce list',
template='IETF-Announce <ietf-announce@ietf.org>')
Ingredient.objects.create(slug='rfc_editor',
Recipient.objects.create(slug='rfc_editor',
desc='The RFC Editor',
template='<rfc-editor@rfc-editor.org>')
Ingredient.objects.create(slug='iesg_secretary',
Recipient.objects.create(slug='iesg_secretary',
desc='The Secretariat',
template='<iesg-secretary@ietf.org>')
Ingredient.objects.create(slug='doc_authors',
Recipient.objects.create(slug='doc_authors',
desc="The document's authors",
template='{{doc.name}}@ietf.org')
Ingredient.objects.create(slug='doc_notify',
Recipient.objects.create(slug='doc_notify',
desc="The addresses in the document's notify field",
template='{{doc.notify}}')
Ingredient.objects.create(slug='doc_group_chairs',
Recipient.objects.create(slug='doc_group_chairs',
desc="The document's group chairs (if the document is assigned to a working or research group)",
template=None)
Ingredient.objects.create(slug='doc_affecteddoc_authors',
Recipient.objects.create(slug='doc_affecteddoc_authors',
desc="The authors of the subject documents of a conflict-review or status-change",
template=None)
Ingredient.objects.create(slug='doc_affecteddoc_group_chairs',
Recipient.objects.create(slug='doc_affecteddoc_group_chairs',
desc="The chairs of groups of the subject documents of a conflict-review or status-change",
template=None)
Ingredient.objects.create(slug='doc_shepherd',
Recipient.objects.create(slug='doc_shepherd',
desc="The document's shepherd",
template='{% if doc.shepherd %}{{doc.shepherd.address}}{% endif %}' )
Ingredient.objects.create(slug='doc_ad',
Recipient.objects.create(slug='doc_ad',
desc="The document's responsible Area Director",
template='{% if doc.ad %}{{doc.ad.email_address}}{% endif %}' )
Ingredient.objects.create(slug='doc_group_mail_list',
Recipient.objects.create(slug='doc_group_mail_list',
desc="The list address of the document's group",
template=None )
Ingredient.objects.create(slug='conflict_review_stream_owner',
Recipient.objects.create(slug='conflict_review_stream_owner',
desc="The stream owner of a document being reviewed for IETF stream conflicts",
template='{% ifequal doc.type_id "conflrev" %}{% ifequal doc.stream_id "ise" %}<rfc-ise@rfc-editor.org>{% endifequal %}{% ifequal doc.stream_id "irtf" %}<irtf-chair@irtf.org>{% endifequal %}{% endifequal %}')
Ingredient.objects.create(slug='iana_approve',
Recipient.objects.create(slug='iana_approve',
desc="IANA's draft approval address",
template='IANA <drafts-approval@icann.org>')
def make_recipes():
def make_mailtokens():
Recipe.objects.all().delete()
MailToken.objects.all().delete()
r = Recipe.objects.create(slug='ballot_saved',
desc='Recipients when a new ballot position (with discusses, other blocking positions, or comments) is saved')
r.ingredients = Ingredient.objects.filter(slug__in=['iesg'])
m = MailToken.objects.create(slug='ballot_saved',
desc='Recipients when a new ballot position (with discusses, other blocking positions, or comments) is saved')
m.recipients = Recipient.objects.filter(slug__in=['iesg'])
r = Recipe.objects.create(slug='ballot_saved_cc',
desc='Copied when a new ballot position (with discusses, other blocking positions, or comments) is saved')
r.ingredients = Ingredient.objects.filter(slug__in=['doc_authors',
m = MailToken.objects.create(slug='ballot_saved_cc',
desc='Copied when a new ballot position (with discusses, other blocking positions, or comments) is saved')
m.recipients = Recipient.objects.filter(slug__in=['doc_authors',
'doc_group_chairs',
'doc_shepherd',
'doc_affecteddoc_authors',
@ -119,9 +131,9 @@ def make_recipes():
'conflict_review_stream_owner',
])
r = Recipe.objects.create(slug='ballot_deferred',
desc='Recipients when a ballot is deferred to or undeferred from a future telechat')
r.ingredients = Ingredient.objects.filter(slug__in=['iesg',
m = MailToken.objects.create(slug='ballot_deferred',
desc='Recipients when a ballot is deferred to or undeferred from a future telechat')
m.recipients = Recipient.objects.filter(slug__in=['iesg',
'iesg_secretary',
'doc_group_chairs',
'doc_notify',
@ -132,13 +144,13 @@ def make_recipes():
'conflict_review_stream_owner',
])
r = Recipe.objects.create(slug='ballot_approved_ietf_stream',
desc='Recipients when an IETF stream document ballot is approved')
r.ingredients = Ingredient.objects.filter(slug__in=['ietf_announce'])
m = MailToken.objects.create(slug='ballot_approved_ietf_stream',
desc='Recipients when an IETF stream document ballot is approved')
m.recipients = Recipient.objects.filter(slug__in=['ietf_announce'])
r = Recipe.objects.create(slug='ballot_approved_ietf_stream_cc',
desc='Copied when an IETF stream document ballot is approved')
r.ingredients = Ingredient.objects.filter(slug__in=['iesg',
m = MailToken.objects.create(slug='ballot_approved_ietf_stream_cc',
desc='Copied when an IETF stream document ballot is approved')
m.recipients = Recipient.objects.filter(slug__in=['iesg',
'doc_notify',
'doc_ad',
'doc_authors',
@ -148,8 +160,8 @@ def make_recipes():
'rfc_editor',
])
r = Recipe.objects.create(slug='ballot_approved_ietf_stream_iana',
desc='Recipients for IANA message when an IETF stream document ballot is approved')
r.ingredients = Ingredient.objects.filter(slug__in=['iana_approve'])
m = MailToken.objects.create(slug='ballot_approved_ietf_stream_iana',
desc='Recipients for IANA message when an IETF stream document ballot is approved')
m.recipients = Recipient.objects.filter(slug__in=['iana_approve'])

View file

@ -5,29 +5,29 @@ from tastypie.constants import ALL, ALL_WITH_RELATIONS # pyflakes:ignore
from ietf import api
from ietf.eventmail.models import * # pyflakes:ignore
from ietf.mailtoken.models import * # pyflakes:ignore
class IngredientResource(ModelResource):
class RecipientResource(ModelResource):
class Meta:
queryset = Ingredient.objects.all()
#resource_name = 'ingredient'
queryset = Recipient.objects.all()
#resource_name = 'recipient'
filtering = {
"slug": ALL,
"desc": ALL,
"template": ALL,
}
api.eventmail.register(IngredientResource())
api.mailtoken.register(RecipientResource())
class RecipeResource(ModelResource):
ingredients = ToManyField(IngredientResource, 'ingredients', null=True)
class MailTokenResource(ModelResource):
recipients = ToManyField(RecipientResource, 'recipients', null=True)
class Meta:
queryset = Recipe.objects.all()
#resource_name = 'recipe'
queryset = MailToken.objects.all()
#resource_name = 'mailtoken'
filtering = {
"slug": ALL,
"desc": ALL,
"ingredients": ALL_WITH_RELATIONS,
"recipients": ALL_WITH_RELATIONS,
}
api.eventmail.register(RecipeResource())
api.mailtoken.register(MailTokenResource())

View file

@ -2,42 +2,42 @@ from django.core.urlresolvers import reverse as urlreverse
from ietf.utils.test_utils import TestCase
from ietf.utils.test_data import make_test_data
from ietf.eventmail.models import Ingredient
from ietf.mailtoken.models import Recipient
class EventMailTests(TestCase):
def setUp(self):
make_test_data()
def test_show_patterns(self):
def test_show_tokens(self):
url = urlreverse('ietf.eventmail.views.show_patterns')
url = urlreverse('ietf.mailtoken.views.show_tokens')
r = self.client.get(url)
self.assertEqual(r.status_code, 200)
self.assertTrue('ballot_saved_cc' in r.content)
url = urlreverse('ietf.eventmail.views.show_patterns',kwargs=dict(eventmail_slug='ballot_saved_cc'))
url = urlreverse('ietf.mailtoken.views.show_tokens',kwargs=dict(mailtoken_slug='ballot_saved_cc'))
r = self.client.get(url)
self.assertEqual(r.status_code, 200)
self.assertTrue('ballot_saved_cc' in r.content)
def test_show_recipients(self):
url = urlreverse('ietf.eventmail.views.show_ingredients')
url = urlreverse('ietf.mailtoken.views.show_recipients')
r = self.client.get(url)
self.assertEqual(r.status_code, 200)
self.assertTrue('bogus' in r.content)
url = urlreverse('ietf.eventmail.views.show_ingredients',kwargs=dict(ingredient_slug='bogus'))
url = urlreverse('ietf.mailtoken.views.show_recipients',kwargs=dict(recipient_slug='bogus'))
r = self.client.get(url)
self.assertEqual(r.status_code, 200)
self.assertTrue('bogus' in r.content)
class IngredientTests(TestCase):
class RecipientTests(TestCase):
def test_ingredient_functions(self):
def test_recipient_functions(self):
draft = make_test_data()
ingredient = Ingredient.objects.first()
for funcname in [name for name in dir(ingredient) if name.startswith('gather_')]:
func=getattr(ingredient,funcname)
recipient = Recipient.objects.first()
for funcname in [name for name in dir(recipient) if name.startswith('gather_')]:
func=getattr(recipient,funcname)
func(**{'doc':draft,'group':draft.group})

11
ietf/mailtoken/urls.py Normal file
View file

@ -0,0 +1,11 @@
from django.conf.urls import patterns, url
from django.views.generic import RedirectView
from django.core.urlresolvers import reverse_lazy
urlpatterns = patterns('ietf.mailtoken.views',
url(r'^$', RedirectView.as_view(url=reverse_lazy('mailtoken_show_tokens'), permanent=True)),
url(r'^token/$', 'show_tokens', name='mailtoken_show_tokens' ),
url(r'^token/(?P<mailtoken_slug>[-\w]+)/$', 'show_tokens' ),
url(r'^recipient/$', 'show_recipients' ),
url(r'^recipient/(?P<recipient_slug>[-\w]+)/$', 'show_recipients' ),
)

View file

@ -1,20 +1,20 @@
from django.core.exceptions import ObjectDoesNotExist
from ietf.eventmail.models import Recipe
from ietf.mailtoken.models import MailToken
def gather_addresses(slug,**kwargs):
addrs = []
try:
recipe = Recipe.objects.get(slug=slug)
mailtoken = MailToken.objects.get(slug=slug)
except ObjectDoesNotExist:
# TODO remove the raise here, or find a better way to detect runtime misconfiguration
raise
return addrs
for ingredient in recipe.ingredients.all():
addrs.extend(ingredient.gather(**kwargs))
for recipient in mailtoken.recipients.all():
addrs.extend(recipient.gather(**kwargs))
return addrs

24
ietf/mailtoken/views.py Normal file
View file

@ -0,0 +1,24 @@
# Copyright The IETF Trust 2015, All Rights Reserved
from inspect import getsourcelines
from django.shortcuts import render
from ietf.mailtoken.models import MailToken, Recipient
def show_tokens(request, mailtoken_slug=None):
mailtokens = MailToken.objects.all()
if mailtoken_slug:
mailtokens = mailtokens.filter(slug=mailtoken_slug) # TODO better 404 behavior here and below
return render(request,'mailtoken/token.html',{'mailtoken_slug':mailtoken_slug,
'mailtokens':mailtokens})
def show_recipients(request, recipient_slug=None):
recipients = Recipient.objects.all()
if recipient_slug:
recipients = recipients.filter(slug=recipient_slug)
for recipient in recipients:
fname = 'gather_%s'%recipient.slug
if hasattr(recipient,fname):
recipient.code = ''.join(getsourcelines(getattr(recipient,fname))[0])
return render(request,'mailtoken/recipient.html',{'recipient_slug':recipient_slug,
'recipients':recipients})

View file

@ -212,7 +212,6 @@ INSTALLED_APPS = (
'ietf.community',
'ietf.dbtemplate',
'ietf.doc',
'ietf.eventmail',
'ietf.group',
'ietf.idindex',
'ietf.iesg',
@ -220,6 +219,7 @@ INSTALLED_APPS = (
'ietf.ipr',
'ietf.liaisons',
'ietf.mailinglists',
'ietf.mailtoken',
'ietf.meeting',
'ietf.message',
'ietf.name',

View file

@ -13,22 +13,22 @@
<thead>
<tr>
<th>Recipient</th>
<th>Event</th>
<th>Events</th>
<th>Template</th>
<th>Code</th>
</tr>
</thead>
<tbody>
{% for ingredient in ingredients %}
{% for recipient in recipients %}
<tr>
<td><span title="{{ingredient.desc}}">{{ingredient.slug}}</span></td>
<td><span title="{{recipient.desc}}">{{recipient.slug}}</span></td>
<td>
{% for recipe in ingredient.recipe_set.all %}
<a href="{% url 'ietf.eventmail.views.show_patterns' recipe.slug %}" title="{{recipe.desc}}">{{recipe.slug}}{% if not forloop.last %}, {%endif%}
{% for mailtoken in recipient.mailtoken_set.all %}
<a href="{% url 'ietf.mailtoken.views.show_tokens' mailtoken.slug %}" title="{{mailtoken.desc}}">{{mailtoken.slug}}{% if not forloop.last %}, {%endif%}
{% endfor %}
</td>
<td>{{ingredient.template}}</td>
<td>{% if ingredient.code %}<pre>{{ingredient.code}}</pre>{% endif %}</td>
<td>{{recipient.template}}</td>
<td>{% if recipient.code %}<pre>{{recipient.code}}</pre>{% endif %}</td>
</tr>
{% endfor %}
</tbody>

View file

@ -17,13 +17,13 @@
</tr>
</thead>
<tbody>
{% for recipe in recipes %}
{% for mailtoken in mailtokens %}
<tr>
<td><span title="{{recipe.desc}}">{{recipe.slug}}</span></td>
<td><span title="{{mailtoken.desc}}">{{mailtoken.slug}}</span></td>
<td>
{% for ingredient in recipe.ingredients.all %}
{% comment %}<span title="{{ingredient.desc}}">{{ingredient.slug}}</span>{% endcomment %}
<a href="{% url 'ietf.eventmail.views.show_ingredients' ingredient.slug %}" title="{{ingredient.desc}}">{{ingredient.slug}}</a>{% if not forloop.last %}, {% endif %}
{% for recipient in mailtoken.recipients.all %}
{% comment %}<span title="{{recipient.desc}}">{{recipient.slug}}</span>{% endcomment %}
<a href="{% url 'ietf.mailtoken.views.show_recipients' recipient.slug %}" title="{{recipient.desc}}">{{recipient.slug}}</a>{% if not forloop.last %}, {% endif %}
{% endfor %}
</td>
</tr>

View file

@ -35,7 +35,7 @@ urlpatterns = patterns('',
(r'^accounts/settings/', include('ietf.cookies.urls')),
(r'^doc/', include('ietf.doc.urls')),
(r'^drafts/', include('ietf.doc.redirect_drafts_urls')),
(r'^eventmail/',include('ietf.eventmail.urls')),
(r'^mailtoken/',include('ietf.mailtoken.urls')),
(r'^feed/', include('ietf.feed_urls')),
(r'^group/', include('ietf.group.urls')),
(r'^help/', include('ietf.help.urls')),

View file

@ -12,7 +12,7 @@ from ietf.ipr.models import HolderIprDisclosure, IprDocRel, IprDisclosureStateNa
from ietf.meeting.models import Meeting
from ietf.name.models import StreamName
from ietf.person.models import Person, Email
from ietf.eventmail.models import Recipe, Ingredient
from ietf.mailtoken.models import MailToken, Recipient
def create_person(group, role_name, name=None, username=None, email_address=None, password=None):
"""Add person/user/email and role."""
@ -334,9 +334,9 @@ def make_test_data():
# EventMail tokens used by the views
# This won't allow testing the results of the production configuration - if we want to do that, we'll need to
# extract the production data either directly, or as a fixture
ingredient = Ingredient.objects.create(slug='bogus_ingredient',desc='Bogus Ingredient',template='bogus@example.com')
recipient = Recipient.objects.create(slug='bogus_recipient',desc='Bogus Recipient',template='bogus@example.com')
for slug in [u'ballot_approved_ietf_stream', u'ballot_approved_ietf_stream_cc', u'ballot_approved_ietf_stream_iana', u'ballot_deferred', u'ballot_saved', u'ballot_saved_cc']:
r=Recipe.objects.create(slug=slug,desc=slug)
r.ingredients=[ingredient]
m = MailToken.objects.create(slug=slug,desc=slug)
m.recipients=[recipient]
return draft