Extensive updates of idtracker.

ballot sets are now (hopefully) treated properly, except for the
row coloring.  They're also included in the "view_id" view.

Search is re-done, it turns out that using draft__<anything> means
that RFCs won't be matched, so build lists of possibly-matching
RFCs and I-Ds and pass them through.  This applies to filename,
group and rfc number.
 - Legacy-Id: 190
This commit is contained in:
Bill Fenner 2007-05-31 19:24:26 +00:00
parent 0f64cde6c6
commit b5ce6e375c
8 changed files with 160 additions and 133 deletions

View file

@ -156,10 +156,7 @@ class InternetDraft(models.Model):
def idstate(self):
idinternal = self.idinternal
if idinternal:
if idinternal.cur_sub_state:
return "%s :: %s" % ( idinternal.cur_state, idinternal.cur_sub_state )
else:
return idinternal.cur_state
return idinternal.docstate()
else:
return "I-D Exists"
def revision_display(self):
@ -200,13 +197,7 @@ class PersonOrOrgInfo(models.Model):
super(PersonOrOrgInfo, self).save()
def __str__(self):
if self.first_name == '' and self.last_name == '':
try:
postal = self.postaladdress_set.get(address_priority=1)
except PostalAddress.DoesNotExist:
return "PersonOrOrgInfo with no name, no postal address!"
except AssertionError:
return "PersonOrOrgInfo with multiple priority-1 addresses!"
return "%s" % ( postal.affiliated_company or postal.department or "???" )
return self.affiliation()
return "%s %s" % ( self.first_name or "<nofirst>", self.last_name or "<nolast>")
def email(self, priority=1, type='INET'):
name = str(self)
@ -216,13 +207,13 @@ class PersonOrOrgInfo(models.Model):
email = ''
return (name, email)
# Added by Sunny Lee to display person's affiliation - 5/26/2007
def affiliation(self, priority=1, type='INET'):
def affiliation(self, priority=1):
try:
postal = self.postaladdress_set.get(address_priority=1)
postal = self.postaladdress_set.get(address_priority=priority)
except PostalAddress.DoesNotExist:
return "PersonOrOrgInfo with no name, no postal address!"
return "PersonOrOrgInfo with no postal address!"
except AssertionError:
return "PersonOrOrgInfo with multiple priority-1 addresses!"
return "PersonOrOrgInfo with multiple priority-%d addresses!" % priority
return "%s" % ( postal.affiliated_company or postal.department or "???" )
class Meta:
db_table = 'person_or_org_info'
@ -331,7 +322,11 @@ class Rfc(models.Model):
self.rfc_name_key = self.title.upper()
super(Rfc, self).save()
def displayname(self):
return "rfc%d.txt" % ( self.rfc_number )
return "%s.txt" % ( self.filename() )
def filename(self):
return "rfc%d" % ( self.rfc_number )
def revision(self):
return "RFC"
def doclink(self):
return "http://www.ietf.org/rfc/%s" % ( self.displayname() )
class Meta:
@ -405,6 +400,10 @@ class IDInternal(models.Model):
field is defined as a FK to InternetDrafts. One side effect
of this is that select_related() will only work with
rfc_flag=0.
When searching where matches may be either I-Ds or RFCs,
you cannot use draft__ as that will cause an INNER JOIN
which will limit the responses to I-Ds.
"""
draft = models.ForeignKey(InternetDraft, primary_key=True, unique=True, db_column='id_document_tag')
rfc_flag = models.IntegerField(null=True)
@ -457,8 +456,15 @@ class IDInternal(models.Model):
return self.documentcomment_set.all().filter(rfc_flag=self.rfc_flag).order_by('-comment_date','-comment_time')
def ballot_set(self):
return IDInternal.objects.filter(ballot=self.ballot_id)
def ballot_primary(self):
return IDInternal.objects.filter(ballot=self.ballot_id,primary_flag=1)
def ballot_others(self):
return IDInternal.objects.filter(models.Q(primary_flag=0)|models.Q(primary_flag__isnull=True), ballot=self.ballot_id)
def docstate(self):
if self.cur_sub_state_id > 0:
return "%s :: %s" % ( self.cur_state, self.cur_sub_state )
else:
return self.cur_state
class Meta:
db_table = 'id_internal'
verbose_name = 'IDTracker Draft'

View file

@ -29,14 +29,14 @@ urlpatterns += patterns('',
urlpatterns += patterns('django.views.generic.list_detail',
(r'^rfc(?P<object_id>\d+)/$', 'object_detail', rfc_dict),
(r'^(?P<object_id>\d+)/$', 'object_detail', id_dict),
(r'^(?P<slug>[^/]+)/$', 'object_detail', dict(id_dict, slug_field='draft__filename')),
(r'^comment/(?P<object_id>\d+)/$', 'object_detail', comment_dict),
(r'^ballot/(?P<object_id>\d+)/$', 'object_detail', ballot_dict),
)
urlpatterns += patterns('',
(r'^(?P<slug>[^/]+)/$', views.view_id, dict(id_dict, slug_field='draft__filename')),
(r'^comment/(?P<object_id>\d+)/$', views.view_comment, comment_dict),
(r'^ballot/(?P<object_id>\d+)/$', views.view_ballot, ballot_dict),
(r'^(?P<slug>[^/]+)/comment/(?P<object_id>\d+)/$', views.comment, comment_dict),
(r'^states/(?P<state>\d+)/$', views.state_desc),
(r'^states/substate/(?P<state>\d+)/$', views.state_desc, { 'is_substate': 1 }),
(r'^(?P<id>\d+)/edit/$', views.edit_idinternal),
#(r'^(?P<id>\d+)/edit/$', views.edit_idinternal),
(r'^$', views.search),
)

View file

@ -5,7 +5,7 @@ from django.template import RequestContext, Context, loader
from django.shortcuts import get_object_or_404, render_to_response
from django.db.models import Q
from django.views.generic.list_detail import object_detail, object_list
from ietf.idtracker.models import InternetDraft, IDInternal, IDState, IDSubState
from ietf.idtracker.models import InternetDraft, IDInternal, IDState, IDSubState, Rfc
from ietf.idtracker.forms import EmailFeedback
from ietf.utils.mail import send_mail_text
@ -22,54 +22,56 @@ def myfields(f):
return f.formfield()
def search(request):
# todo: check if these field names work for backwards
# compatability
InternetDraftForm = forms.models.form_for_model(InternetDraft, formfield_callback=myfields)
idform = InternetDraftForm(request.POST)
idform = InternetDraftForm(request.REQUEST)
InternalForm = forms.models.form_for_model(IDInternal, formfield_callback=myfields)
form = InternalForm(request.POST)
t = loader.get_template('idtracker/idtracker_search.html')
form = InternalForm(request.REQUEST)
# if there's a post, do the search and supply results to the template
if request.method == 'POST':
qdict = { 'filename': 'draft__filename__contains',
searching = False
# filename, rfc_number, group searches are seperate because
# they can't be represented as simple searches in the data model.
qdict = {
'job_owner': 'job_owner',
'group': 'draft__group__acronym',
'cur_state': 'cur_state',
'cur_sub_state': 'cur_sub_state',
'rfc_number': 'draft__rfc_number',
'area_acronym': 'area_acronym',
'note': 'note__contains',
'note': 'note__icontains',
}
q_objs = [Q(**{qdict[k]: request.POST[k]})
for k in qdict.keys()
if request.POST[k] != '']
matches = IDInternal.objects.all().filter(*q_objs)
# matches = IDInternal.objects.all()
# if request.POST['filename']:
# matches = matches.filter(draft__filename__contains=request.POST["filename"])
# if request.POST['job_owner']:
# matches = matches.filter(job_owner=request.POST['job_owner'])
# if request.POST['group']:
# matches = matches.filter(draft__group__acronym=request.POST['group_acronym'])
# if request.POST['cur_state']:
# matches = matches.filter(cur_state=request.POST['cur_state'])
# if request.POST['cur_sub_state']:
# matches = matches.filter(cur_sub_state=request.POST['cur_sub_state'])
# if request.POST['rfc_number']:
# matches = matches.filter(draft__rfc_number=request.POST['rfc_number'])
# if request.POST['area_acronym']:
# matches = matches.filter(area_acronym=request.POST['area_acronym'])
# if request.POST['note']:
# matches = matches.filter(note__contains=request.POST['note'])
q_objs = []
for k in qdict.keys() + ['group', 'rfc_number', 'filename']:
if request.REQUEST.has_key(k):
searching = True
if request.REQUEST[k] != '' and qdict.has_key(k):
q_objs.append(Q(**{qdict[k]: request.REQUEST[k]}))
if searching:
group = request.REQUEST.get('group', '')
if group != '':
rfclist = [rfc.rfc_number for rfc in Rfc.objects.all().filter(group_acronym=group)]
draftlist = [draft.id_document_tag for draft in InternetDraft.objects.all().filter(group__acronym=group)]
q_objs.append(Q(draft__in=draftlist)&Q(rfc_flag=0)|Q(draft__in=rfclist)&Q(rfc_flag=1))
rfc_number = request.REQUEST.get('rfc_number', '')
if rfc_number != '':
draftlist = [draft.id_document_tag for draft in InternetDraft.objects.all().filter(rfc_number=rfc_number)]
q_objs.append(Q(draft__in=draftlist)&Q(rfc_flag=0)|Q(draft=rfc_number)&Q(rfc_flag=1))
filename = request.REQUEST.get('filename', '')
if filename != '':
draftlist = [draft.id_document_tag for draft in InternetDraft.objects.all().filter(filename__icontains=filename)]
q_objs.append(Q(draft__in=draftlist,rfc_flag=0))
matches = IDInternal.objects.all().filter(*q_objs).filter(primary_flag=1)
matches = matches.order_by('cur_state', 'cur_sub_state_id')
else:
matches = None
c = RequestContext(request, {
return render_to_response('idtracker/idtracker_search.html', {
'form': form,
'idform': idform,
'matches': matches,
})
return HttpResponse(t.render(c))
'searching': searching,
}, context_instance=RequestContext(request))
# proof of concept, orphaned for now
def edit_idinternal(request, id=None):
#draft = InternetDraft.objects.get(pk=id)
draft = get_object_or_404(InternetDraft.objects, pk=id)
@ -89,14 +91,11 @@ def edit_idinternal(request, id=None):
else:
form = None
t = loader.get_template('idtracker/idtracker_edit.html')
c = RequestContext(request, {
return render_to_response('idtracker/idtracker_edit.html', {
'form': form,
'idform': idform,
'draft': draft,
})
return HttpResponse(t.render(c))
}, context_instance=RequestContext(request))
def state_desc(request, state, is_substate=0):
if int(state) == 100:
@ -142,3 +141,15 @@ def status(request):
def last_call(request):
queryset = IDInternal.objects.filter(primary_flag=1).filter(cur_state__state__in=('In Last Call', 'Waiting for Writeup', 'Waiting for AD Go-Ahead')).order_by('cur_state', 'status_date', 'ballot_id')
return object_list(request, template_name="idtracker/status_of_items.html", queryset=queryset, extra_context={'title': 'Documents in Last Call'})
# Wrappers around object_detail to give permalink a handle.
# The named-URLs feature in django 0.97 will eliminate the
# need for these.
def view_id(*args, **kwargs):
return object_detail(*args, **kwargs)
def view_comment(*args, **kwargs):
return object_detail(*args, **kwargs)
def view_ballot(*args, **kwargs):
return object_detail(*args, **kwargs)

View file

@ -22,6 +22,19 @@
{% block idcontent %}
{% endblock %}
<hr/>
<a href="http://www.djangoproject.com/"><img src="http://media.djangoproject.com/img/badges/djangomade124x25.gif" border="0" alt="Made with Django." title="Made with Django." /></a>
<HR>
<p>
Did you find a bug? <a href="{% url ietf.idtracker.views.send_email %}?cat=bugs" >Let us know</a>.
<p>
<a href="{% url ietf.idtracker.views.send_email %}?cat=discuss" >Any question or suggestion</a>?
<p>
<i>This page produced by the <A HREF="mailto:iesg-secretary@ietf.org">IETF Secretariat</a>
for the <A HREF="mailto:iesg@ietf.org">IESG</A></i>
<p>
{% include "debug.html" %}
</body>

View file

@ -12,8 +12,7 @@
<div class="largefont">
Detail Info
</div>
{% if object.ballot_set|length_is:"1" %}
{% else %}
{% if object.ballot_others %}
<div align="right">
<a href="#action">Action List</a>
</div>
@ -55,13 +54,16 @@
</td>
<td>
{# what's the "if" here #}
<div class="largefont3">
<a href="../ballot/{{ object.ballot_id }}">IESG evaluation record</a>
{% if object.ballot.ballot_issued %}
<a href="{% url ietf.idtracker.views.view_ballot object.ballot_id %}">IESG evaluation record</a>
[<a href="/idtracker/evaluation_process/">What
they mean</a>]
[<a href="/idtracker/ballot_key/">How they are
recorded</a>]
{% else %}
No IESG evaluation record
{% endif %}
</div>
</td>
</tr>
@ -187,7 +189,7 @@
<table cellpadding="1" cellspacing="1" border="0">
<tr>
<td>
<form action="/idtracker/" method="GET">
<form action="{% url ietf.idtracker.views.search %}" method="GET">
<input type="submit" value=
"Main Menu">
</form>
@ -201,17 +203,19 @@
</tr>
</table>
{% if object.ballot_set|length_is:"1" %}
{% else %}
{% if object.ballot_others %}
<a name="action"></a>
<table border="1" bgcolor="black">
<tr><td><font color="white"><h3>Actions</h3></font>
{# this "regroup" is to get the data structure into the shape
# that search_result_table wants - it doesn't do anything real. #}
{% regroup object.ballot_primary by docstate as grouped %}
{% include "idtracker/search_result_table.html" %}
</td>
</tr>
</table>
{% endif %}
<h3>Comment Log</h3>
<table cellpadding="1" cellspacing="1" border="0">
@ -235,18 +239,22 @@
<b>*{{ comment.get_ballot_display }}*</b>]</font>
{% endif %} {{ comment.comment_text|format_textarea|truncatewords_html:"25" }}</td>
<td>
<!-- this form element technically belongs inside the <td>
but that actually changes the visible spacing in most
browsers, so we let layout concerns make us write
invalid HTML. -->
<form action="comment/{{ comment.id }}" method="GET">
<td>
<input type="submit" value="View Detail">
</form>
</td>
</form>
</tr>
{% endfor %}
</table>
<br>
<form action="/idtracker/" method="GET">
<form action="{% url ietf.idtracker.views.search %}" method="GET">
<input type="submit" value="Main Menu">
<input type="button" name="back_button" value="BACK"
onclick="history.go(-1);return true">

View file

@ -3,10 +3,10 @@
{% block title %}-- Search{% endblock %}
{% block idcontent %}
<h1 style="text-align: center; color: red;">IESG Data Tracker</h1>
<form method="post">
<center>
<table cellpadding="1" cellspacing="0" border="0">
<tr bgcolor="silver">
<th colspan="2">I-D - Search Criteria</th>
@ -37,69 +37,30 @@ state</b>: {{ form.cur_sub_state }}</label> </td>
{{ form.area_acronym }}</td>
</tr>
<tr>
<td align="right"><label for="id_note"><b>Note:</b></label> </td>
<td>{{ form.note }}</td>
</tr>
<TR BGCOLOR="silver">
<TD ALIGN="CENTER" colspan="2"><INPUT TYPE="submit" VALUE="SEARCH" name="search_button">
<input type="button" value="Clear Fields" onClick="clear_fields();">
</TD>
<tr>
<td colspan="2"><input type="submit"> </td>
</tr>
</TR>
</table>
</form>
<HR>
Document States: <a href="https://datatracker.ietf.org/state_diagram.gif">State Diagram</a> and
<a href="https://datatracker.ietf.org/public/states_table.cgi">State Explanations</a>
</center>
<hr>
{% if matches %}
{% regroup matches by draft.idstate as grouped %}
{% for group in grouped %}
<h2>{{ group.grouper }}</h2>
<table bgcolor="#DFDFDF" cellspacing="0" cellpadding="0" border="0" width="800">
<tr bgcolor="#A3A3A3"><th>&nbsp;</th><th width="250">Name (Intended Status)</th><th>Ver</th><th>Responsible AD</th><th>Status Date</th><th>Modified</th></tr>
{% for match in group.list %}
<tr bgcolor="#{% cycle F8D6F8,E2AFE2 %}">
<td><a href="{{ match.draft.id_document_tag }}/edit/">[VIEW]</a></td>
<td><a href="http://www.ietf.org/internet-drafts/{{match.draft.filename}}-{{match.draft.revision}}.txt">{{ match.draft.filename }}</a> ({{ match.draft.intended_status }})</td>
<td>{{ match.draft.revision }}</td>
<td>{{ match.job_owner }}</td>
<td>{{ match.status_date }}</td>
<td>{{ match.event_date }}</td>
</tr>
{% endfor %}
</table>
{% endfor %}
<b>Search Result</b><br>
{% regroup matches by docstate as grouped %}
{% include "idtracker/search_result_table.html" %}
{% else %}
<p>No matches to your query.</p>
{% endif %}
{% if debug %}
<div id="debug">
<h2>Queries</h2>
<p>
{{ sql_queries|length }} Queries
{% ifnotequal sql_queries|length 0 %}
(<span style="cursor: pointer;" onclick="document.getElementById('debugQueryTable').style.display='';">Show</span>)
{% endifnotequal %}
</p>
<table id="debugQueryTable" style="display: none;">
<col width="1"></col>
<col></col>
<col width="1"></col>
<thead>
<tr>
<th scope="col">#</th>
<th scope="col">SQL</th>
<th scope="col">Time</th>
</tr>
</thead>
<tbody>
{% for query in sql_queries %}<tr class="{% cycle odd,even %}">
<td>{{ forloop.counter }}</td>
<td>{{ query.sql|escape }}</td>
<td>{{ query.time }}</td>
</tr>{% endfor %}
</tbody>
</table>
</div>
{% if searching %}
<p>No matches to your query.</p>
{% endif %}
{% endif %}
{% endblock %}

View file

@ -0,0 +1,13 @@
<tr bgcolor="#{% if match.primary_flag %}{% cycle F8D6F8,E2AFE2 as ballotcolor %}{% else %}{{ ballotcolor }}{% endif %}">
<!-- this form element technically belongs inside the <td>
but that actually changes the visible spacing in most
browsers, so we let layout concerns make us write
invalid HTML. -->
<form method="GET" action="{% url ietf.idtracker.views.view_id match.document.filename %}"><td><input type="submit" value="DETAIL"></td></form>
{# todo: conditionalize doclink properly #}
<td>{% if match.primary_flag %}<li>{% else %}<dd>{% endif %}<a href="{{ match.document.doclink }}">{{ match.document.filename }}</a> ({{ match.document.intended_status }})</td>
<td>{{ match.document.revision }}</td>
<td>{{ match.job_owner }}</td>
<td>{% firstof match.status_date "" %}</td>
<td>{{ match.event_date }}</td>
</tr>

View file

@ -0,0 +1,15 @@
{% for group in grouped %}
<h2>{{ group.grouper }}</h2>
<table bgcolor="#DFDFDF" cellspacing="0" cellpadding="0" border="0" width="800">
<tr bgcolor="#A3A3A3"><th>&nbsp;</th><th width="250">Name (Intended Status)</th><th>Ver</th><th>Responsible AD</th><th>Status Date</th><th>Modified</th></tr>
{% for match in group.list %}
{% include "idtracker/search_result_row.html" %}
{% if match.primary_flag %}
{% for match in match.ballot_others %}
{% include "idtracker/search_result_row.html" %}
{% endfor %}
{% endif %}
{% endfor %}
</table>
{% endfor %}