Skip to content

Commit d868371

Browse files
committed
Merged in ^/branch/iola/event-saving-refactor-r10291 (via ^/personal/henrik/6.30.1-eventsave), which refactors document saving to always use doc.save_with_history(events), and requires accompanying events. This branch also provides refactoring of recurring regexes in url patterns into a dictionary. As part of the merge, also refactored new code which didn't use the save_with_history() method.
- Legacy-Id: 11841
2 parents 3e3255d + 4b0a936 commit d868371

82 files changed

Lines changed: 1683 additions & 1238 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

bin/test-crawl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
#!/usr/bin/env python
22

3-
import os, sys, re, datetime, argparse, traceback, tempfile, json, subprocess
3+
import os, sys, re, datetime, argparse, traceback, json, subprocess
44
import html5lib
55
import random
66

ietf/bin/rfc-editor-index-updates

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
#!/usr/bin/env python
22

3-
import os, sys, re, json, datetime
3+
import os, sys, datetime
44
import syslog
55
import traceback
66

@@ -33,22 +33,28 @@ if options.skip_date:
3333
skip_date = datetime.datetime.strptime(options.skip_date, "%Y-%m-%d").date()
3434

3535
from ietf.utils.pipe import pipe
36-
from ietf.sync.rfceditor import *
3736
from ietf.doc.utils import rebuild_reference_relations
37+
import ietf.sync.rfceditor
3838

3939
syslog.syslog("Updating document metadata from RFC index from %s" % settings.RFC_EDITOR_QUEUE_URL)
4040

41-
response = fetch_index_xml(settings.RFC_EDITOR_INDEX_URL)
42-
data = parse_index(response)
41+
response = ietf.sync.rfceditor.fetch_index_xml(settings.RFC_EDITOR_INDEX_URL)
42+
data = ietf.sync.rfceditor.parse_index(response)
4343

44-
if len(data) < MIN_INDEX_RESULTS:
44+
if len(data) < ietf.sync.rfceditor.MIN_INDEX_RESULTS:
4545
syslog.syslog("Not enough results, only %s" % len(data))
4646
sys.exit(1)
4747

48-
changed, new_rfcs = update_docs_from_rfc_index(data, skip_older_than_date=skip_date)
48+
new_rfcs = []
49+
for changes, doc, rfc_published in ietf.sync.rfceditor.update_docs_from_rfc_index(data, skip_older_than_date=skip_date):
50+
if rfc_published:
51+
new_rfcs.append(doc)
4952

50-
for c in changed:
51-
syslog.syslog(c)
53+
for c in changes:
54+
syslog.syslog("%s: %s" % (doc.name, c))
55+
print "%s: %s" % (doc.name, c)
56+
57+
sys.exit(0)
5258

5359
# This can be called while processing a notifying POST from the RFC Editor
5460
# Spawn a child to sync the rfcs and calculate new reference relationships

ietf/doc/expire.py

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
from pathlib import Path
77

88
from ietf.utils.mail import send_mail
9-
from ietf.doc.models import Document, DocEvent, State, save_document_in_history, IESG_SUBSTATE_TAGS
9+
from ietf.doc.models import Document, DocEvent, State, IESG_SUBSTATE_TAGS
1010
from ietf.person.models import Person
1111
from ietf.meeting.models import Meeting
1212
from ietf.doc.utils import add_state_change_event
@@ -129,8 +129,9 @@ def expire_draft(doc):
129129

130130
system = Person.objects.get(name="(System)")
131131

132+
events = []
133+
132134
# change the state
133-
save_document_in_history(doc)
134135
if doc.latest_event(type='started_iesg_process'):
135136
new_state = State.objects.get(used=True, type="draft-iesg", slug="dead")
136137
prev_state = doc.get_state(new_state.type_id)
@@ -139,15 +140,13 @@ def expire_draft(doc):
139140
doc.set_state(new_state)
140141
doc.tags.remove(*prev_tags)
141142
e = add_state_change_event(doc, system, prev_state, new_state, prev_tags=prev_tags, new_tags=[])
143+
if e:
144+
events.append(e)
142145

143-
e = DocEvent(doc=doc, by=system)
144-
e.type = "expired_document"
145-
e.desc = "Document has expired"
146-
e.save()
146+
events.append(DocEvent.objects.create(doc=doc, by=system, type="expired_document", desc="Document has expired"))
147147

148148
doc.set_state(State.objects.get(used=True, type="draft", slug="expired"))
149-
doc.time = datetime.datetime.now()
150-
doc.save()
149+
doc.save_with_history(events)
151150

152151
def clean_up_draft_files():
153152
"""Move unidentified and old files out of the Internet Draft directory."""

ietf/doc/factories.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,15 @@ def states(self, create, extracted, **kwargs):
4242
for (state_type_id,state_slug) in extracted:
4343
self.set_state(State.objects.get(type_id=state_type_id,slug=state_slug))
4444

45+
@classmethod
46+
def _after_postgeneration(cls, obj, create, results=None):
47+
"""Save again the instance if creating and at least one hook ran."""
48+
if create and results:
49+
# Some post-generation hooks ran, and may have modified us.
50+
obj._has_an_event_so_saving_is_allowed = True
51+
obj.save()
52+
53+
4554
class DocAliasFactory(factory.DjangoModelFactory):
4655
class Meta:
4756
model = DocAlias

ietf/doc/lastcall.py

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
from django.db.models import Q
66

77
from ietf.doc.models import Document, State, DocEvent, LastCallDocEvent, WriteupDocEvent
8-
from ietf.doc.models import save_document_in_history
98
from ietf.doc.models import IESG_SUBSTATE_TAGS
109
from ietf.person.models import Person
1110
from ietf.doc.utils import add_state_change_event
@@ -14,12 +13,15 @@
1413

1514
def request_last_call(request, doc):
1615
if not doc.latest_event(type="changed_ballot_writeup_text"):
17-
generate_ballot_writeup(request, doc)
16+
e = generate_ballot_writeup(request, doc)
17+
e.save()
1818
if not doc.latest_event(type="changed_ballot_approval_text"):
19-
generate_approval_mail(request, doc)
19+
e = generate_approval_mail(request, doc)
20+
e.save()
2021
if not doc.latest_event(type="changed_last_call_text"):
21-
generate_last_call_announcement(request, doc)
22-
22+
e = generate_last_call_announcement(request, doc)
23+
e.save()
24+
2325
send_last_call_request(request, doc)
2426

2527
e = DocEvent()
@@ -50,8 +52,6 @@ def expire_last_call(doc):
5052
else:
5153
raise ValueError("Unexpected document type to expire_last_call(): %s" % doc.type)
5254

53-
save_document_in_history(doc)
54-
5555
prev_state = doc.get_state(new_state.type_id)
5656
doc.set_state(new_state)
5757

@@ -60,8 +60,7 @@ def expire_last_call(doc):
6060

6161
system = Person.objects.get(name="(System)")
6262
e = add_state_change_event(doc, system, prev_state, new_state, prev_tags=prev_tags, new_tags=[])
63-
64-
doc.time = (e and e.time) or datetime.datetime.now()
65-
doc.save()
63+
if e:
64+
doc.save_with_history([e])
6665

6766
email_last_call_expired(doc)

ietf/doc/mails.py

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,21 @@ def email_iesg_processing_document(request, doc, changes):
8989
def html_to_text(html):
9090
return strip_tags(html.replace("&lt;", "<").replace("&gt;", ">").replace("&amp;", "&").replace("<br>", "\n"))
9191

92+
def email_update_telechat(request, doc, text):
93+
(to, cc) = gather_address_lists('doc_telechat_details_changed',doc=doc)
94+
95+
if not to:
96+
return
97+
98+
text = strip_tags(text)
99+
send_mail(request, to, None,
100+
"Telechat update notice: %s" % doc.file_tag(),
101+
"doc/mail/update_telechat.txt",
102+
dict(text=text,
103+
url=settings.IDTRACKER_BASE_URL + doc.get_absolute_url()),
104+
cc=cc)
105+
106+
92107
def generate_ballot_writeup(request, doc):
93108
e = doc.latest_event(type="iana_review")
94109
iana = e.desc if e else ""
@@ -99,8 +114,8 @@ def generate_ballot_writeup(request, doc):
99114
e.doc = doc
100115
e.desc = u"Ballot writeup was generated"
101116
e.text = unicode(render_to_string("doc/mail/ballot_writeup.txt", {'iana': iana}))
102-
e.save()
103-
117+
118+
# caller is responsible for saving, if necessary
104119
return e
105120

106121
def generate_ballot_rfceditornote(request, doc):
@@ -154,8 +169,8 @@ def generate_last_call_announcement(request, doc):
154169
e.doc = doc
155170
e.desc = u"Last call announcement was generated"
156171
e.text = unicode(mail)
157-
e.save()
158172

173+
# caller is responsible for saving, if necessary
159174
return e
160175

161176

@@ -173,8 +188,8 @@ def generate_approval_mail(request, doc):
173188
e.doc = doc
174189
e.desc = u"Ballot approval text was generated"
175190
e.text = unicode(mail)
176-
e.save()
177191

192+
# caller is responsible for saving, if necessary
178193
return e
179194

180195
def generate_approval_mail_approved(request, doc):
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
# -*- coding: utf-8 -*-
2+
from __future__ import unicode_literals
3+
4+
from django.db import migrations
5+
6+
def fix_buggy_author_foreignkey(apps, schema_editor):
7+
DocumentAuthor = apps.get_model("doc", "DocumentAuthor")
8+
# apparently, we have a buggy key in the DB, fix it
9+
DocumentAuthor.objects.filter(author="[<Email: d3e3e3@gmail.com>]").update(author="d3e3e3@gmail.com")
10+
11+
def save_all_documents_in_history(apps, schema_editor):
12+
State = apps.get_model("doc", "State")
13+
Document = apps.get_model("doc", "Document")
14+
DocHistory = apps.get_model("doc", "DocHistory")
15+
RelatedDocument = apps.get_model("doc", "RelatedDocument")
16+
RelatedDocHistory = apps.get_model("doc", "RelatedDocHistory")
17+
DocumentAuthor = apps.get_model("doc", "DocumentAuthor")
18+
DocHistoryAuthor = apps.get_model("doc", "DocHistoryAuthor")
19+
20+
def canonical_name(self):
21+
name = self.name
22+
state = State.objects.filter(document=self, type_id=self.type_id).first()
23+
if self.type_id == "draft" and state.slug == "rfc":
24+
a = self.docalias_set.filter(name__startswith="rfc")
25+
if a:
26+
name = a[0].name
27+
elif self.type_id == "charter":
28+
return charter_name_for_group(self.chartered_group)
29+
return name
30+
31+
def charter_name_for_group(group):
32+
if group.type_id == "rg":
33+
top_org = "irtf"
34+
else:
35+
top_org = "ietf"
36+
37+
return "charter-%s-%s" % (top_org, group.acronym)
38+
39+
def save_document_in_history(doc):
40+
"""Save a snapshot of document and related objects in the database."""
41+
def get_model_fields_as_dict(obj):
42+
return dict((field.name, getattr(obj, field.name))
43+
for field in obj._meta.fields
44+
if field is not obj._meta.pk)
45+
46+
# copy fields
47+
fields = get_model_fields_as_dict(doc)
48+
fields["doc"] = doc
49+
fields["name"] = canonical_name(doc)
50+
51+
dochist = DocHistory(**fields)
52+
dochist.save()
53+
54+
# copy many to many
55+
for field in doc._meta.many_to_many:
56+
if field.rel.through and field.rel.through._meta.auto_created:
57+
setattr(dochist, field.name, getattr(doc, field.name).all())
58+
59+
# copy remaining tricky many to many
60+
def transfer_fields(obj, HistModel):
61+
mfields = get_model_fields_as_dict(item)
62+
# map doc -> dochist
63+
for k, v in mfields.iteritems():
64+
if v == doc:
65+
mfields[k] = dochist
66+
HistModel.objects.create(**mfields)
67+
68+
for item in RelatedDocument.objects.filter(source=doc):
69+
transfer_fields(item, RelatedDocHistory)
70+
71+
for item in DocumentAuthor.objects.filter(document=doc):
72+
transfer_fields(item, DocHistoryAuthor)
73+
74+
return dochist
75+
76+
from django.conf import settings
77+
settings.DEBUG = False # prevent out-of-memory problems
78+
79+
for d in Document.objects.iterator():
80+
save_document_in_history(d)
81+
82+
class Migration(migrations.Migration):
83+
84+
dependencies = [
85+
('doc', '0010_auto_20150930_0251'),
86+
('group', '0007_auto_20150930_0758'),
87+
]
88+
89+
operations = [
90+
migrations.RunPython(fix_buggy_author_foreignkey),
91+
migrations.RunPython(save_all_documents_in_history)
92+
]

0 commit comments

Comments
 (0)