Skip to content

Commit 89ac692

Browse files
committed
Added in the community app, which wasn't included properly in the previous commit.
- Legacy-Id: 4519
1 parent b2573a4 commit 89ac692

23 files changed

Lines changed: 2800 additions & 7 deletions

ietf/community/__init__.py

Whitespace-only changes.

ietf/community/constants.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
SIGNIFICANT_STATES = [
2+
'Adopted by a WG',
3+
'In WG Last Call',
4+
'WG Consensus: Waiting for Write-Up',
5+
'Parked WG Document',
6+
'Dead WG Document',
7+
'Active IAB Document',
8+
'Community Review',
9+
'Sent to the RFC Editor',
10+
'Active RG Document',
11+
'In RG Last Call',
12+
'Awaiting IRSG Reviews',
13+
'In IESG Review',
14+
'Document on Hold Based On IESG Request',
15+
'Submission Received',
16+
'In ISE Review',
17+
'In IESG Review',
18+
'RFC Published',
19+
'Dead',
20+
'IESG Evaluation',
21+
'Publication Requested',
22+
'In Last Call',
23+
]

ietf/community/display.py

Lines changed: 173 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,173 @@
1+
import datetime
2+
3+
from django.db.models import Q
4+
from ietf.ietfworkflows.utils import get_state_for_draft
5+
6+
from redesign.doc.models import DocAlias, DocEvent
7+
8+
9+
class DisplayField(object):
10+
11+
codename = ''
12+
description = ''
13+
14+
def get_value(self, document, raw=False):
15+
return None
16+
17+
18+
class FilenameField(DisplayField):
19+
codename = 'filename'
20+
description = 'I-D filename'
21+
22+
def get_value(self, document, raw=False):
23+
if not raw:
24+
return '<a href="%s">%s</a>' % (document.get_absolute_url(), document.canonical_name())
25+
else:
26+
return document.canonical_name()
27+
28+
29+
class TitleField(DisplayField):
30+
codename = 'title'
31+
description = 'I-D title'
32+
33+
def get_value(self, document, raw=False):
34+
return document.title
35+
36+
37+
class DateField(DisplayField):
38+
codename = 'date'
39+
description = 'Date of current I-D'
40+
41+
def get_value(self, document, raw=False):
42+
date = document.latest_event(type='new_revision')
43+
if date:
44+
return date.time.strftime('%Y-%m-%d')
45+
return document.time.strftime('%Y-%m-%d')
46+
47+
48+
class StatusField(DisplayField):
49+
codename = 'status'
50+
description = 'Status in the IETF process'
51+
52+
def get_value(self, document, raw=False):
53+
for i in ('draft', 'draft-stream-ietf', 'draft-stream-irtf', 'draft-stream-ise', 'draft-stream-iab', 'draft'):
54+
state = document.get_state(i)
55+
if state:
56+
return state
57+
return ''
58+
59+
60+
class WGField(DisplayField):
61+
codename = 'wg_rg'
62+
description = 'Associated WG or RG'
63+
64+
def get_value(self, document, raw=False):
65+
return document.group or ''
66+
67+
68+
class ADField(DisplayField):
69+
codename = 'ad'
70+
description = 'Associated AD, if any'
71+
72+
def get_value(self, document, raw=False):
73+
return document.ad or ''
74+
75+
76+
class OneDayField(DisplayField):
77+
codename = '1_day'
78+
description = 'Changed within the last 1 day'
79+
80+
def get_value(self, document, raw=False):
81+
now = datetime.datetime.now()
82+
last = now - datetime.timedelta(days=1)
83+
if document.docevent_set.filter(time__gte=last):
84+
return raw and 'YES' or '&#10004;'
85+
return ''
86+
87+
88+
class TwoDaysField(DisplayField):
89+
codename = '2_days'
90+
description = 'Changed within the last 2 days'
91+
92+
def get_value(self, document, raw=False):
93+
now = datetime.datetime.now()
94+
last = now - datetime.timedelta(days=2)
95+
if document.docevent_set.filter(time__gte=last):
96+
return raw and 'YES' or '&#10004;'
97+
return ''
98+
99+
100+
class SevenDaysField(DisplayField):
101+
codename = '7_days'
102+
description = 'Changed within the last 7 days'
103+
104+
def get_value(self, document, raw=False):
105+
now = datetime.datetime.now()
106+
last = now - datetime.timedelta(days=7)
107+
if document.docevent_set.filter(time__gte=last):
108+
return raw and 'YES' or '&#10004;'
109+
return ''
110+
111+
112+
TYPES_OF_DISPLAY_FIELDS = [(i.codename, i.description) for i in DisplayField.__subclasses__()]
113+
114+
115+
class SortMethod(object):
116+
codename = ''
117+
description = ''
118+
119+
def get_sort_field(self):
120+
return 'pk'
121+
122+
123+
class FilenameSort(SortMethod):
124+
codename = 'by_filename'
125+
description = 'Alphabetical by I-D filename and RFC number'
126+
127+
def get_sort_field(self):
128+
return 'name'
129+
130+
def get_full_rfc_sort(self, documents):
131+
return [i.document for i in DocAlias.objects.filter(document__in=documents, name__startswith='rfc').order_by('name')]
132+
133+
134+
class TitleSort(SortMethod):
135+
codename = 'by_title'
136+
description = 'Alphabetical by document title'
137+
138+
def get_sort_field(self):
139+
return 'title'
140+
141+
142+
class WGSort(SortMethod):
143+
codename = 'by_wg'
144+
description = 'Alphabetical by associated WG'
145+
146+
def get_sort_field(self):
147+
return 'group__name'
148+
149+
150+
class PublicationSort(SortMethod):
151+
codename = 'date_publication'
152+
description = 'Date of publication of current version of the document'
153+
154+
def get_sort_field(self):
155+
return '-documentchangedates__new_version_date'
156+
157+
class ChangeSort(SortMethod):
158+
codename = 'recent_change'
159+
description = 'Date of most recent change of status of any type'
160+
161+
def get_sort_field(self):
162+
return '-documentchangedates__normal_change_date'
163+
164+
165+
class SignificantSort(SortMethod):
166+
codename = 'recent_significant'
167+
description = 'Date of most recent significant change of status'
168+
169+
def get_sort_field(self):
170+
return '-documentchangedates__significant_change_date'
171+
172+
173+
TYPES_OF_SORT = [(i.codename, i.description) for i in SortMethod.__subclasses__()]

ietf/community/forms.py

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
import hashlib
2+
import datetime
3+
4+
from django import forms
5+
from django.conf import settings
6+
from django.contrib.sites.models import Site
7+
8+
from ietf.utils.mail import send_mail
9+
from ietf.community.models import Rule, DisplayConfiguration, RuleManager
10+
from ietf.community.display import DisplayField
11+
12+
13+
class RuleForm(forms.ModelForm):
14+
15+
class Meta:
16+
model = Rule
17+
fields = ('rule_type', 'value')
18+
19+
def __init__(self, *args, **kwargs):
20+
self.clist = kwargs.pop('clist', None)
21+
super(RuleForm, self).__init__(*args, **kwargs)
22+
23+
def save(self):
24+
self.instance.community_list = self.clist
25+
super(RuleForm, self).save()
26+
27+
def get_all_options(self):
28+
result = []
29+
for i in RuleManager.__subclasses__():
30+
options = i(None).options()
31+
if options:
32+
result.append({'type': i.codename,
33+
'options': options})
34+
return result
35+
36+
37+
class DisplayForm(forms.ModelForm):
38+
39+
class Meta:
40+
model = DisplayConfiguration
41+
fields = ('sort_method', )
42+
43+
def save(self):
44+
data = self.data
45+
fields = []
46+
for i in DisplayField.__subclasses__():
47+
if data.get(i.codename, None):
48+
fields.append(i.codename)
49+
self.instance.display_fields = ','.join(fields)
50+
super(DisplayForm, self).save()
51+
52+
53+
class SubscribeForm(forms.Form):
54+
55+
email = forms.EmailField("Your email")
56+
57+
def __init__(self, *args, **kwargs):
58+
self.clist = kwargs.pop('clist')
59+
self.significant = kwargs.pop('significant')
60+
super(SubscribeForm, self).__init__(*args, **kwargs)
61+
62+
def save(self, *args, **kwargs):
63+
self.send_email()
64+
return True
65+
66+
def send_email(self):
67+
domain = Site.objects.get_current().domain
68+
today = datetime.date.today().strftime('%Y%m%d')
69+
subject = 'Confirm list subscription: %s' % self.clist
70+
from_email = settings.DEFAULT_FROM_EMAIL
71+
to_email = self.cleaned_data['email']
72+
auth = hashlib.md5('%s%s%s%s%s' % (settings.SECRET_KEY, today, to_email, 'subscribe', self.significant)).hexdigest()
73+
context = {
74+
'domain': domain,
75+
'clist': self.clist,
76+
'today': today,
77+
'auth': auth,
78+
'to_email': to_email,
79+
'significant': self.significant,
80+
}
81+
send_mail(None, to_email, from_email, subject, 'community/public/subscribe_email.txt', context)
82+
83+
84+
class UnSubscribeForm(SubscribeForm):
85+
86+
def send_email(self):
87+
domain = Site.objects.get_current().domain
88+
today = datetime.date.today().strftime('%Y%m%d')
89+
subject = 'Confirm list subscription cancelation: %s' % self.clist
90+
from_email = settings.DEFAULT_FROM_EMAIL
91+
to_email = self.cleaned_data['email']
92+
auth = hashlib.md5('%s%s%s%s%s' % (settings.SECRET_KEY, today, to_email, 'unsubscribe', self.significant)).hexdigest()
93+
context = {
94+
'domain': domain,
95+
'clist': self.clist,
96+
'today': today,
97+
'auth': auth,
98+
'to_email': to_email,
99+
'significant': self.significant,
100+
}
101+
send_mail(None, to_email, from_email, subject, 'community/public/unsubscribe_email.txt', context)

ietf/community/management/__init__.py

Whitespace-only changes.

ietf/community/management/commands/__init__.py

Whitespace-only changes.
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
import sys
2+
import datetime
3+
4+
from django.core.management.base import BaseCommand
5+
6+
from ietf.community.models import Rule, CommunityList
7+
8+
9+
class Command(BaseCommand):
10+
help = (u"Update drafts in community lists by reviewing their rules")
11+
12+
13+
def handle(self, *args, **options):
14+
now = datetime.datetime.now()
15+
16+
rules = Rule.objects.filter(last_updated__lt=now - datetime.timedelta(hours=12))
17+
count = rules.count()
18+
index = 1
19+
for rule in rules:
20+
sys.stdout.write('Updating rule [%s/%s]\r' % (index, count))
21+
sys.stdout.flush()
22+
rule.save()
23+
index += 1
24+
if index > 1:
25+
print
26+
cls = CommunityList.objects.filter(cached__isnull=False)
27+
count = cls.count()
28+
index = 1
29+
for cl in cls:
30+
sys.stdout.write('Clearing community list cache [%s/%s]\r' % (index, count))
31+
sys.stdout.flush()
32+
cl.cached = None
33+
cl.save()
34+
index += 1
35+
if index > 1:
36+
print
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
import sys
2+
3+
from django.core.management.base import BaseCommand
4+
from django.db.models import Q
5+
6+
from ietf.community.constants import SIGNIFICANT_STATES
7+
from ietf.community.models import DocumentChangeDates
8+
from redesign.doc.models import Document
9+
10+
11+
class Command(BaseCommand):
12+
help = (u"Update drafts in community lists by reviewing their rules")
13+
14+
def handle(self, *args, **options):
15+
documents = Document.objects.filter(Q(type__name='Draft') | Q(states__name='rfc')).distinct()
16+
index = 1
17+
total = documents.count()
18+
19+
for doc in documents:
20+
(changes, created) = DocumentChangeDates.objects.get_or_create(document=doc)
21+
new_version = doc.latest_event(type='new_revision')
22+
normal_change = doc.latest_event()
23+
significant_change = None
24+
for event in doc.docevent_set.filter(type='changed_document'):
25+
for state in SIGNIFICANT_STATES:
26+
if ('<b>%s</b>' % state) in event.desc:
27+
significant_change = event
28+
break
29+
30+
changes.new_version_date = new_version and new_version.time.date()
31+
changes.normal_change_date = normal_change and normal_change.time.date()
32+
changes.significant_change_date = significant_change and significant_change.time.date()
33+
34+
changes.save()
35+
36+
sys.stdout.write('Document %s/%s\r' % (index, total))
37+
sys.stdout.flush()
38+
index += 1
39+
print

0 commit comments

Comments
 (0)