33
44import datetime , json
55
6- from django .http import HttpResponseForbidden , HttpResponseRedirect , Http404
7- from django .shortcuts import render , get_object_or_404 , redirect
8- from django .urls import reverse as urlreverse
9- from django .template .loader import render_to_string
106from django import forms
117from django .conf import settings
8+ from django .http import HttpResponse , HttpResponseForbidden , HttpResponseRedirect , Http404
9+ from django .shortcuts import render , get_object_or_404 , redirect
10+ from django .template .defaultfilters import striptags
11+ from django .template .loader import render_to_string
12+ from django .urls import reverse as urlreverse
13+ from django .views .decorators .csrf import csrf_exempt
14+
1215
1316import debug # pyflakes:ignore
1417
3033from ietf .person .models import Person
3134from ietf .utils import log
3235from ietf .utils .mail import send_mail_text , send_mail_preformatted
36+ from ietf .utils .decorators import require_api_key
3337
3438BALLOT_CHOICES = (("yes" , "Yes" ),
3539 ("noobj" , "No Objection" ),
@@ -106,6 +110,74 @@ def clean_discuss(self):
106110 raise forms .ValidationError ("You must enter a non-empty discuss" )
107111 return entered_discuss
108112
113+ def save_position (form , doc , ballot , ad , login = None ):
114+ # save the vote
115+ if login is None :
116+ login = ad
117+ clean = form .cleaned_data
118+
119+ old_pos = doc .latest_event (BallotPositionDocEvent , type = "changed_ballot_position" , ad = ad , ballot = ballot )
120+ pos = BallotPositionDocEvent (doc = doc , rev = doc .rev , by = login )
121+ pos .type = "changed_ballot_position"
122+ pos .ballot = ballot
123+ pos .ad = ad
124+ pos .pos = clean ["position" ]
125+ pos .comment = clean ["comment" ].rstrip ()
126+ pos .comment_time = old_pos .comment_time if old_pos else None
127+ pos .discuss = clean ["discuss" ].rstrip ()
128+ if not pos .pos .blocking :
129+ pos .discuss = ""
130+ pos .discuss_time = old_pos .discuss_time if old_pos else None
131+
132+ changes = []
133+ added_events = []
134+ # possibly add discuss/comment comments to history trail
135+ # so it's easy to see what's happened
136+ old_comment = old_pos .comment if old_pos else ""
137+ if pos .comment != old_comment :
138+ pos .comment_time = pos .time
139+ changes .append ("comment" )
140+
141+ if pos .comment :
142+ e = DocEvent (doc = doc , rev = doc .rev )
143+ e .by = ad # otherwise we can't see who's saying it
144+ e .type = "added_comment"
145+ e .desc = "[Ballot comment]\n " + pos .comment
146+ added_events .append (e )
147+
148+ old_discuss = old_pos .discuss if old_pos else ""
149+ if pos .discuss != old_discuss :
150+ pos .discuss_time = pos .time
151+ changes .append ("discuss" )
152+
153+ if pos .pos .blocking :
154+ e = DocEvent (doc = doc , rev = doc .rev , by = login )
155+ e .by = ad # otherwise we can't see who's saying it
156+ e .type = "added_comment"
157+ e .desc = "[Ballot %s]\n " % pos .pos .name .lower ()
158+ e .desc += pos .discuss
159+ added_events .append (e )
160+
161+ # figure out a description
162+ if not old_pos and pos .pos .slug != "norecord" :
163+ pos .desc = u"[Ballot Position Update] New position, %s, has been recorded for %s" % (pos .pos .name , pos .ad .plain_name ())
164+ elif old_pos and pos .pos != old_pos .pos :
165+ pos .desc = "[Ballot Position Update] Position for %s has been changed to %s from %s" % (pos .ad .plain_name (), pos .pos .name , old_pos .pos .name )
166+
167+ if not pos .desc and changes :
168+ pos .desc = u"Ballot %s text updated for %s" % (u" and " .join (changes ), ad .plain_name ())
169+
170+ # only add new event if we actually got a change
171+ if pos .desc :
172+ if login != ad :
173+ pos .desc += u" by %s" % login .plain_name ()
174+
175+ pos .save ()
176+
177+ for e in added_events :
178+ e .save () # save them after the position is saved to get later id for sorting order
179+
180+
109181@role_required ('Area Director' ,'Secretariat' )
110182def edit_position (request , name , ballot_id ):
111183 """Vote and edit discuss and comment on document as Area Director."""
@@ -126,77 +198,14 @@ def edit_position(request, name, ballot_id):
126198 raise Http404
127199 ad = get_object_or_404 (Person , pk = ad_id )
128200
129- old_pos = doc .latest_event (BallotPositionDocEvent , type = "changed_ballot_position" , ad = ad , ballot = ballot )
130-
131201 if request .method == 'POST' :
132202 if not has_role (request .user , "Secretariat" ) and not ad .role_set .filter (name = "ad" , group__type = "area" , group__state = "active" ):
133203 # prevent pre-ADs from voting
134204 return HttpResponseForbidden ("Must be a proper Area Director in an active area to cast ballot" )
135205
136206 form = EditPositionForm (request .POST , ballot_type = ballot .ballot_type )
137207 if form .is_valid ():
138- # save the vote
139- clean = form .cleaned_data
140-
141- pos = BallotPositionDocEvent (doc = doc , rev = doc .rev , by = login )
142- pos .type = "changed_ballot_position"
143- pos .ballot = ballot
144- pos .ad = ad
145- pos .pos = clean ["position" ]
146- pos .comment = clean ["comment" ].rstrip ()
147- pos .comment_time = old_pos .comment_time if old_pos else None
148- pos .discuss = clean ["discuss" ].rstrip ()
149- if not pos .pos .blocking :
150- pos .discuss = ""
151- pos .discuss_time = old_pos .discuss_time if old_pos else None
152-
153- changes = []
154- added_events = []
155- # possibly add discuss/comment comments to history trail
156- # so it's easy to see what's happened
157- old_comment = old_pos .comment if old_pos else ""
158- if pos .comment != old_comment :
159- pos .comment_time = pos .time
160- changes .append ("comment" )
161-
162- if pos .comment :
163- e = DocEvent (doc = doc , rev = doc .rev )
164- e .by = ad # otherwise we can't see who's saying it
165- e .type = "added_comment"
166- e .desc = "[Ballot comment]\n " + pos .comment
167- added_events .append (e )
168-
169- old_discuss = old_pos .discuss if old_pos else ""
170- if pos .discuss != old_discuss :
171- pos .discuss_time = pos .time
172- changes .append ("discuss" )
173-
174- if pos .pos .blocking :
175- e = DocEvent (doc = doc , rev = doc .rev , by = login )
176- e .by = ad # otherwise we can't see who's saying it
177- e .type = "added_comment"
178- e .desc = "[Ballot %s]\n " % pos .pos .name .lower ()
179- e .desc += pos .discuss
180- added_events .append (e )
181-
182- # figure out a description
183- if not old_pos and pos .pos .slug != "norecord" :
184- pos .desc = u"[Ballot Position Update] New position, %s, has been recorded for %s" % (pos .pos .name , pos .ad .plain_name ())
185- elif old_pos and pos .pos != old_pos .pos :
186- pos .desc = "[Ballot Position Update] Position for %s has been changed to %s from %s" % (pos .ad .plain_name (), pos .pos .name , old_pos .pos .name )
187-
188- if not pos .desc and changes :
189- pos .desc = u"Ballot %s text updated for %s" % (u" and " .join (changes ), ad .plain_name ())
190-
191- # only add new event if we actually got a change
192- if pos .desc :
193- if login != ad :
194- pos .desc += u" by %s" % login .plain_name ()
195-
196- pos .save ()
197-
198- for e in added_events :
199- e .save () # save them after the position is saved to get later id for sorting order
208+ save_position (form , doc , ballot , ad , login )
200209
201210 if request .POST .get ("send_mail" ):
202211 qstr = ""
@@ -211,6 +220,7 @@ def edit_position(request, name, ballot_id):
211220 return HttpResponseRedirect (return_to_url )
212221 else :
213222 initial = {}
223+ old_pos = doc .latest_event (BallotPositionDocEvent , type = "changed_ballot_position" , ad = ad , ballot = ballot )
214224 if old_pos :
215225 initial ['position' ] = old_pos .pos .slug
216226 initial ['discuss' ] = old_pos .discuss
@@ -234,6 +244,45 @@ def edit_position(request, name, ballot_id):
234244 blocking_positions = json .dumps (blocking_positions ),
235245 ))
236246
247+ @require_api_key
248+ @role_required ('Area Director' , 'Secretariat' )
249+ @csrf_exempt
250+ def api_set_position (request ):
251+ def err (code , text ):
252+ return HttpResponse (text , status = code , content_type = 'text/plain' )
253+ if request .method == 'POST' :
254+ ad = request .user .person
255+ name = request .POST .get ('doc' )
256+ if not name :
257+ return err (400 , "Missing document name" )
258+ try :
259+ doc = Document .objects .get (docalias__name = name )
260+ except Document .DoesNotExist :
261+ return err (404 , "Document not found" )
262+ position_names = BallotPositionName .objects .values_list ('slug' , flat = True )
263+ position = request .POST .get ('position' )
264+ if not position :
265+ return err (400 , "Missing parameter: position, one of: %s " % ',' .join (position_names ))
266+ if not position in position_names :
267+ return err (400 , "Bad position name, must be one of: %s " % ',' .join (position_names ))
268+ ballot = doc .active_ballot ()
269+ if not ballot :
270+ return err (404 , "No open ballot found" )
271+ form = EditPositionForm (request .POST , ballot_type = ballot .ballot_type )
272+ if form .is_valid ():
273+ save_position (form , doc , ballot , ad )
274+ else :
275+ debug .type ('form.errors' )
276+ debug .show ('form.errors' )
277+ errors = form .errors
278+ summary = ',' .join ([ "%s: %s" % (f , striptags (errors [f ])) for f in errors ])
279+ return err (400 , "Form not valid: %s" % summary )
280+ else :
281+ return err (405 , "Method not allowed" )
282+
283+ return HttpResponse ("Done" , status = 200 , content_type = 'text/plain' )
284+
285+
237286@role_required ('Area Director' ,'Secretariat' )
238287def send_ballot_comment (request , name , ballot_id ):
239288 """Email document ballot position discuss/comment for Area Director."""
0 commit comments