Skip to content

Commit 02873d2

Browse files
committed
Port send queue script and tests and import ScheduledAnnouncement + some cleanups
- Legacy-Id: 3163
1 parent 1ca085f commit 02873d2

9 files changed

Lines changed: 219 additions & 45 deletions

File tree

ietf/announcements/admin.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ class ScheduledAnnouncementAdmin(admin.ModelAdmin):
2626
if settings.USE_DB_REDESIGN_PROXY_CLASSES:
2727
class MessageAdmin(admin.ModelAdmin):
2828
list_display = ["time", "by", "subject", "groups"]
29-
search_fields = ["text"]
29+
search_fields = ["body"]
3030
raw_id_fields = ["by"]
3131

3232
def groups(self, instance):
@@ -37,7 +37,7 @@ def groups(self, instance):
3737
class SendQueueAdmin(admin.ModelAdmin):
3838
list_display = ["time", "by", "message", "send_at", "sent_at"]
3939
list_filter = ["time", "send_at", "sent_at"]
40-
search_fields = ["message__text"]
40+
search_fields = ["message__body"]
4141
raw_id_fields = ["by"]
4242

4343
admin.site.register(SendQueue, SendQueueAdmin)

ietf/announcements/models.py

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -100,11 +100,12 @@ class Message(models.Model):
100100

101101
subject = models.CharField(max_length=255)
102102
frm = models.CharField(max_length=255)
103-
to = models.CharField(max_length=255)
104-
cc = models.CharField(max_length=255, blank=True)
103+
to = models.CharField(max_length=1024)
104+
cc = models.CharField(max_length=1024, blank=True)
105105
bcc = models.CharField(max_length=255, blank=True)
106106
reply_to = models.CharField(max_length=255, blank=True)
107-
text = models.TextField()
107+
body = models.TextField()
108+
content_type = models.CharField(max_length=255, blank=True)
108109

109110
related_groups = models.ManyToManyField(Group, blank=True)
110111

@@ -118,10 +119,12 @@ class SendQueue(models.Model):
118119
time = models.DateTimeField(default=datetime.datetime.now)
119120
by = models.ForeignKey(Person)
120121

121-
comment = models.TextField()
122122
message = models.ForeignKey(Message)
123+
123124
send_at = models.DateTimeField(blank=True, null=True)
124125
sent_at = models.DateTimeField(blank=True, null=True)
125126

127+
note = models.TextField(blank=True)
128+
126129
class Meta:
127130
ordering = ['time']

ietf/announcements/send_scheduled.py

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
import re, datetime, email
22

3+
from django.conf import settings
4+
35
from ietf.utils.mail import send_mail_text, send_mail_mime
46

57
first_dot_on_line_re = re.compile(r'^\.', re.MULTILINE)
@@ -8,7 +10,6 @@ def send_scheduled_announcement(announcement):
810
# for some reason, the old Perl code base substituted away . on line starts
911
body = first_dot_on_line_re.sub("", announcement.body)
1012

11-
announcement.content_type
1213
extra = {}
1314
if announcement.replyto:
1415
extra['Reply-To'] = announcement.replyto
@@ -30,3 +31,32 @@ def send_scheduled_announcement(announcement):
3031
announcement.actual_sent_time = str(now.time())
3132
announcement.mail_sent = True
3233
announcement.save()
34+
35+
36+
def send_scheduled_announcementREDESIGN(send_queue):
37+
message = send_queue.message
38+
39+
# for some reason, the old Perl code base substituted away . on line starts
40+
body = first_dot_on_line_re.sub("", message.body)
41+
42+
extra = {}
43+
if message.reply_to:
44+
extra['Reply-To'] = message.reply_to
45+
46+
content_type = message.content_type.lower()
47+
if not content_type or 'text/plain' in content_type:
48+
send_mail_text(None, message.to, message.frm, message.subject,
49+
body, cc=message.cc, bcc=message.bcc)
50+
elif 'multipart' in content_type:
51+
# make body a real message so we can parse it
52+
body = ("MIME-Version: 1.0\r\nContent-Type: %s\r\n" % content_type) + body
53+
54+
msg = email.message_from_string(body.encode("utf-8"))
55+
send_mail_mime(None, message.to, message.frm, message.subject,
56+
msg, cc=message.cc, bcc=message.bcc)
57+
58+
send_queue.sent_at = datetime.datetime.now()
59+
send_queue.save()
60+
61+
if settings.USE_DB_REDESIGN_PROXY_CLASSES:
62+
send_scheduled_announcement = send_scheduled_announcementREDESIGN

ietf/announcements/tests.py

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,11 @@
1+
import datetime
2+
3+
from django.conf import settings
14
import django.test
25

36
from ietf.utils.test_utils import SimpleUrlTestCase, canonicalize_sitemap
47
from ietf.utils.test_runner import mail_outbox
8+
from ietf.utils.test_data import make_test_data
59

610
from ietf.announcements.models import ScheduledAnnouncement
711

@@ -57,3 +61,73 @@ def test_send_mime_announcement(self):
5761
self.assertTrue("This is a test" in mail_outbox[-1]["Subject"])
5862
self.assertTrue("--NextPart" in mail_outbox[-1].as_string())
5963
self.assertTrue(ScheduledAnnouncement.objects.get(id=a.id).mail_sent)
64+
65+
66+
class SendScheduledAnnouncementsTestCaseREDESIGN(django.test.TestCase):
67+
def test_send_plain_announcement(self):
68+
from ietf.announcements.models import Message, SendQueue
69+
from redesign.person.models import Person
70+
71+
make_test_data()
72+
73+
msg = Message.objects.create(
74+
by=Person.objects.get(name="(System)"),
75+
subject="This is a test",
76+
to="test@example.com",
77+
frm="testmonkey@example.com",
78+
cc="cc.a@example.com, cc.b@example.com",
79+
bcc="bcc@example.com",
80+
body="Hello World!",
81+
content_type="",
82+
)
83+
84+
q = SendQueue.objects.create(
85+
by=Person.objects.get(name="(System)"),
86+
message=msg,
87+
send_at=datetime.datetime.now() + datetime.timedelta(hours=12)
88+
)
89+
90+
mailbox_before = len(mail_outbox)
91+
92+
from ietf.announcements.send_scheduled import send_scheduled_announcement
93+
send_scheduled_announcement(q)
94+
95+
self.assertEquals(len(mail_outbox), mailbox_before + 1)
96+
self.assertTrue("This is a test" in mail_outbox[-1]["Subject"])
97+
self.assertTrue(SendQueue.objects.get(id=q.id).sent_at)
98+
99+
def test_send_mime_announcement(self):
100+
from ietf.announcements.models import Message, SendQueue
101+
from redesign.person.models import Person
102+
103+
make_test_data()
104+
105+
msg = Message.objects.create(
106+
by=Person.objects.get(name="(System)"),
107+
subject="This is a test",
108+
to="test@example.com",
109+
frm="testmonkey@example.com",
110+
cc="cc.a@example.com, cc.b@example.com",
111+
bcc="bcc@example.com",
112+
body='--NextPart\r\n\r\nA New Internet-Draft is available from the on-line Internet-Drafts directories.\r\n--NextPart\r\nContent-Type: Message/External-body;\r\n\tname="draft-huang-behave-bih-01.txt";\r\n\tsite="ftp.ietf.org";\r\n\taccess-type="anon-ftp";\r\n\tdirectory="internet-drafts"\r\n\r\nContent-Type: text/plain\r\nContent-ID: <2010-07-30001541.I-D@ietf.org>\r\n\r\n--NextPart--',
113+
content_type='Multipart/Mixed; Boundary="NextPart"',
114+
)
115+
116+
q = SendQueue.objects.create(
117+
by=Person.objects.get(name="(System)"),
118+
message=msg,
119+
send_at=datetime.datetime.now() + datetime.timedelta(hours=12)
120+
)
121+
122+
mailbox_before = len(mail_outbox)
123+
124+
from ietf.announcements.send_scheduled import send_scheduled_announcement
125+
send_scheduled_announcement(q)
126+
127+
self.assertEquals(len(mail_outbox), mailbox_before + 1)
128+
self.assertTrue("This is a test" in mail_outbox[-1]["Subject"])
129+
self.assertTrue("--NextPart" in mail_outbox[-1].as_string())
130+
self.assertTrue(SendQueue.objects.get(id=q.id).sent_at)
131+
132+
if settings.USE_DB_REDESIGN_PROXY_CLASSES:
133+
SendScheduledAnnouncementsTestCase = SendScheduledAnnouncementsTestCaseREDESIGN

ietf/bin/send-scheduled-mail

Lines changed: 23 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ from ietf.announcements.models import ScheduledAnnouncement
1414
from ietf.announcements.send_scheduled import *
1515
from django.db.models import Q
1616

17-
if not len(sys.argv) == 2 or sys.argv[1] not in ('all', 'rsync', 'specific'):
17+
if len(sys.argv) != 2 or sys.argv[1] not in ('all', 'rsync', 'specific'):
1818
print "USAGE: %s <all | rsync | specific>" % os.path.basename(__file__)
1919
print "'all' means all not sent"
2020
print "'rsync' means all not sent where to-be-sent-date is null"
@@ -24,19 +24,28 @@ if not len(sys.argv) == 2 or sys.argv[1] not in ('all', 'rsync', 'specific'):
2424
mode = sys.argv[1]
2525

2626
now = datetime.datetime.now()
27-
now = datetime.datetime(2010, 8, 5)
28-
29-
announcements = ScheduledAnnouncement.objects.filter(mail_sent=False)
30-
if mode == "rsync":
31-
# include bogus 0000-00-00 entries
32-
announcements = announcements.filter(Q(to_be_sent_date=None) | Q(to_be_sent_date__lte=datetime.date.min))
33-
elif mode == "specific":
34-
# exclude null/bogus entries
35-
announcements = announcements.exclude(Q(to_be_sent_date=None) | Q(to_be_sent_date__lte=datetime.date.min))
36-
announcements = announcements.filter(to_be_sent_date__lte=now.date(),
37-
to_be_sent_time__lte=now.time())
27+
28+
if settings.USE_DB_REDESIGN_PROXY_CLASSES:
29+
from ietf.announcements.models import SendQueue
30+
announcements = SendQueue.objects.filter(sent_at=None)
31+
if mode == "rsync":
32+
announcements = announcements.filter(send_at=None)
33+
elif mode == "specific":
34+
announcements = announcements.exclude(send_at=None).filter(send_at__lte=now)
35+
else:
36+
announcements = ScheduledAnnouncement.objects.filter(mail_sent=False)
37+
if mode == "rsync":
38+
# include bogus 0000-00-00 entries
39+
announcements = announcements.filter(Q(to_be_sent_date=None) | Q(to_be_sent_date__lte=datetime.date.min))
40+
elif mode == "specific":
41+
# exclude null/bogus entries
42+
announcements = announcements.exclude(Q(to_be_sent_date=None) | Q(to_be_sent_date__lte=datetime.date.min))
43+
44+
announcements = announcements.filter(to_be_sent_date__lte=now.date(),
45+
to_be_sent_time__lte=now.time())
3846

3947
for announcement in announcements:
4048
send_scheduled_announcement(announcement)
41-
42-
syslog.syslog('Sent scheduled announcement %s "%s"' % (announcement.id, announcement.subject))
49+
50+
subject = announcement.message.subject if settings.USE_DB_REDESIGN_PROXY_CLASSES else announcement.subject
51+
syslog.syslog(u'Sent scheduled announcement %s "%s"' % (announcement.id, subject))

ietf/idrfc/views_ballot.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -292,7 +292,6 @@ def edit_positionREDESIGN(request, name):
292292
doc.time = pos.time
293293
doc.save()
294294

295-
# FIXME: test
296295
if request.POST.get("send_mail"):
297296
qstr = "?return_to_url=%s" % return_to_url
298297
if request.GET.get('ad'):

ietf/templates/announcements/message_detail.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,6 @@ <h1>NomCom Message</h1>
1313
</p>
1414
<hr width="400" align="left" />
1515
<pre>
16-
{{ message.text|escape }}
16+
{{ message.body|escape }}
1717
</pre>
1818
{% endblock %}

redesign/doc/models.py

Lines changed: 0 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -185,21 +185,6 @@ class Meta:
185185
verbose_name_plural = "document aliases"
186186

187187

188-
# class Ballot(models.Model): # A collection of ballot positions
189-
# """A collection of ballot positions, and the actions taken during the
190-
# lifetime of the ballot.
191-
192-
# The actual ballot positions are found by searching Messages for
193-
# BallotPositions for this document between the dates indicated by
194-
# self.initiated.time and (self.closed.time or now)
195-
# """
196-
# initiated = models.ForeignKey(Message, related_name="initiated_ballots")
197-
# deferred = models.ForeignKey(Message, null=True, blank=True, related_name="deferred_ballots")
198-
# last_call = models.ForeignKey(Message, null=True, blank=True, related_name="lastcalled_ballots")
199-
# closed = models.ForeignKey(Message, null=True, blank=True, related_name="closed_ballots")
200-
# announced = models.ForeignKey(Message, null=True, blank=True, related_name="announced_ballots")
201-
202-
203188
EVENT_TYPES = [
204189
# core events
205190
("new_revision", "Added new revision"),

redesign/importing/import-announcements.py

Lines changed: 81 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -16,21 +16,21 @@
1616
from redesign.group.models import *
1717
from redesign.name.utils import name
1818
from redesign.importing.utils import old_person_to_person
19-
from ietf.announcements.models import Message
20-
from ietf.announcements.models import Announcement, PersonOrOrgInfo, AnnouncedTo, AnnouncedFrom
19+
from ietf.announcements.models import Message, SendQueue
20+
from ietf.announcements.models import Announcement, PersonOrOrgInfo, AnnouncedTo, AnnouncedFrom, ScheduledAnnouncement
21+
from ietf.idtracker.models import IESGLogin
2122

2223
# assumptions:
2324
# - nomcom groups have been imported
24-
# - persons have been imported
25+
# - persons have been imported (Announcement originators and IESGLogins)
2526

26-
# imports Announcements
27-
28-
# FIXME: should import ScheduledAnnouncements
27+
# imports Announcement, ScheduledAnnouncement
2928

3029
system = Person.objects.get(name="(System)")
3130

3231
# Announcement
3332
for o in Announcement.objects.all().select_related('announced_to', 'announced_from').order_by('announcement_id').iterator():
33+
print "importing Announcement", o.pk
3434
try:
3535
message = Message.objects.get(id=o.announcement_id)
3636
except Message.DoesNotExist:
@@ -74,7 +74,7 @@
7474
message.bcc = l[len("bcc:"):].strip()
7575
elif l.lower().startswith("reply-to:"):
7676
message.reply_to = l[len("reply-to:"):].strip()
77-
message.text = o.text
77+
message.body = o.text
7878
message.save()
7979

8080
message.related_groups.clear()
@@ -86,3 +86,77 @@
8686

8787
message.related_groups.add(nomcom)
8888

89+
90+
# precompute scheduled_by's to speed up the loop a bit
91+
scheduled_by_mapping = {}
92+
for by in ScheduledAnnouncement.objects.all().values_list("scheduled_by", flat=True).distinct():
93+
logins = IESGLogin.objects.filter(login_name=by)
94+
if logins:
95+
l = logins[0]
96+
person = l.person
97+
if not person:
98+
person = PersonOrOrgInfo.objects.get(first_name=l.first_name, last_name=l.last_name)
99+
found = old_person_to_person(person)
100+
else:
101+
found = system
102+
103+
print "mapping", by, "to", found
104+
scheduled_by_mapping[by] = found
105+
106+
# ScheduledAnnouncement
107+
for o in ScheduledAnnouncement.objects.all().order_by('id').iterator():
108+
print "importing ScheduledAnnouncement", o.pk
109+
try:
110+
q = SendQueue.objects.get(id=o.id)
111+
except SendQueue.DoesNotExist:
112+
q = SendQueue(id=o.id)
113+
# make sure there's no id overlap with ordinary already-imported announcements
114+
q.message = Message(id=o.id + 4000)
115+
116+
time = datetime.datetime.combine(o.scheduled_date,
117+
datetime.time(*(int(x) for x in o.scheduled_time.split(":"))))
118+
by = scheduled_by_mapping[o.scheduled_by]
119+
120+
q.message.time = time
121+
q.message.by = by
122+
123+
q.message.subject = (o.subject or "").strip()
124+
q.message.to = (o.to_val or "").strip()
125+
q.message.frm = (o.from_val or "").strip()
126+
q.message.cc = (o.cc_val or "").strip()
127+
q.message.bcc = (o.bcc_val or "").strip()
128+
q.message.reply_to = (o.replyto or "").strip()
129+
q.message.body = o.body or ""
130+
q.message.content_type = o.content_type or ""
131+
q.message.save()
132+
133+
q.time = time
134+
q.by = by
135+
136+
d = None
137+
if o.to_be_sent_date:
138+
try:
139+
t = datetime.time(*(int(x) for x in o.to_be_sent_time.split(":")))
140+
except ValueError:
141+
t = datetime.time(0, 0, 0)
142+
d = datetime.datetime.combine(o.to_be_sent_date, t)
143+
144+
q.send_at = d
145+
146+
d = None
147+
if o.actual_sent_date:
148+
try:
149+
t = datetime.time(*(int(x) for x in o.scheduled_time.split(":")))
150+
except ValueError:
151+
t = datetime.time(0, 0, 0)
152+
153+
d = datetime.datetime.combine(o.actual_sent_date, t)
154+
155+
q.sent_at = d
156+
157+
n = (o.note or "").strip()
158+
if n.startswith("<br>"):
159+
n = n[len("<br>"):]
160+
q.note = n
161+
162+
q.save()

0 commit comments

Comments
 (0)