Merge pull request #6807 from ietf-tools/main
ci: merge main to release
This commit is contained in:
commit
f3a574c8fb
|
@ -28,6 +28,8 @@ from ietf.doc.factories import IndividualDraftFactory, WgDraftFactory, WgRfcFact
|
|||
from ietf.group.factories import RoleFactory
|
||||
from ietf.meeting.factories import MeetingFactory, SessionFactory
|
||||
from ietf.meeting.models import Session
|
||||
from ietf.nomcom.models import Volunteer, NomCom
|
||||
from ietf.nomcom.factories import NomComFactory, nomcom_kwargs_for_year
|
||||
from ietf.person.factories import PersonFactory, random_faker
|
||||
from ietf.person.models import User
|
||||
from ietf.person.models import PersonalApiKey
|
||||
|
@ -630,6 +632,7 @@ class CustomApiTests(TestCase):
|
|||
'reg_type': 'hackathon',
|
||||
'ticket_type': '',
|
||||
'checkedin': 'False',
|
||||
'is_nomcom_volunteer': 'False',
|
||||
}
|
||||
url = urlreverse('ietf.api.views.api_new_meeting_registration')
|
||||
r = self.client.post(url, reg)
|
||||
|
@ -691,6 +694,50 @@ class CustomApiTests(TestCase):
|
|||
missing_fields = [f.strip() for f in fields.split(',')]
|
||||
self.assertEqual(set(missing_fields), set(drop_fields))
|
||||
|
||||
def test_api_new_meeting_registration_nomcom_volunteer(self):
|
||||
'''Test that Volunteer is created if is_nomcom_volunteer=True
|
||||
is submitted to API
|
||||
'''
|
||||
meeting = MeetingFactory(type_id='ietf')
|
||||
reg = {
|
||||
'apikey': 'invalid',
|
||||
'affiliation': "Alguma Corporação",
|
||||
'country_code': 'PT',
|
||||
'meeting': meeting.number,
|
||||
'reg_type': 'onsite',
|
||||
'ticket_type': '',
|
||||
'checkedin': 'False',
|
||||
'is_nomcom_volunteer': 'True',
|
||||
}
|
||||
person = PersonFactory()
|
||||
reg['email'] = person.email().address
|
||||
reg['first_name'] = person.first_name()
|
||||
reg['last_name'] = person.last_name()
|
||||
now = datetime.datetime.now()
|
||||
if now.month > 10:
|
||||
year = now.year + 1
|
||||
else:
|
||||
year = now.year
|
||||
# create appropriate group and nomcom objects
|
||||
nomcom = NomComFactory.create(is_accepting_volunteers=True, **nomcom_kwargs_for_year(year))
|
||||
url = urlreverse('ietf.api.views.api_new_meeting_registration')
|
||||
r = self.client.post(url, reg)
|
||||
self.assertContains(r, 'Invalid apikey', status_code=403)
|
||||
oidcp = PersonFactory(user__is_staff=True)
|
||||
# Make sure 'oidcp' has an acceptable role
|
||||
RoleFactory(name_id='robot', person=oidcp, email=oidcp.email(), group__acronym='secretariat')
|
||||
key = PersonalApiKey.objects.create(person=oidcp, endpoint=url)
|
||||
reg['apikey'] = key.hash()
|
||||
r = self.client.post(url, reg)
|
||||
nomcom = NomCom.objects.last()
|
||||
self.assertContains(r, "Accepted, New registration", status_code=202)
|
||||
# assert Volunteer exists
|
||||
self.assertEqual(Volunteer.objects.count(), 1)
|
||||
volunteer = Volunteer.objects.last()
|
||||
self.assertEqual(volunteer.person, person)
|
||||
self.assertEqual(volunteer.nomcom, nomcom)
|
||||
self.assertEqual(volunteer.origin, 'registration')
|
||||
|
||||
def test_api_version(self):
|
||||
DumpInfo.objects.create(date=timezone.datetime(2022,8,31,7,10,1,tzinfo=datetime.timezone.utc), host='testapi.example.com',tz='UTC')
|
||||
url = urlreverse('ietf.api.views.version')
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
# Copyright The IETF Trust 2017-2020, All Rights Reserved
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
|
||||
import json
|
||||
import pytz
|
||||
import re
|
||||
|
@ -38,6 +37,7 @@ from ietf.doc.utils import fuzzy_find_documents
|
|||
from ietf.ietfauth.views import send_account_creation_email
|
||||
from ietf.ietfauth.utils import role_required
|
||||
from ietf.meeting.models import Meeting
|
||||
from ietf.nomcom.models import Volunteer, NomCom
|
||||
from ietf.stats.models import MeetingRegistration
|
||||
from ietf.utils import log
|
||||
from ietf.utils.decorators import require_api_key
|
||||
|
@ -140,7 +140,7 @@ def api_new_meeting_registration(request):
|
|||
def err(code, text):
|
||||
return HttpResponse(text, status=code, content_type='text/plain')
|
||||
required_fields = [ 'meeting', 'first_name', 'last_name', 'affiliation', 'country_code',
|
||||
'email', 'reg_type', 'ticket_type', 'checkedin']
|
||||
'email', 'reg_type', 'ticket_type', 'checkedin', 'is_nomcom_volunteer']
|
||||
fields = required_fields + []
|
||||
if request.method == 'POST':
|
||||
# parameters:
|
||||
|
@ -202,6 +202,19 @@ def api_new_meeting_registration(request):
|
|||
else:
|
||||
send_account_creation_email(request, email)
|
||||
response += ", Email sent"
|
||||
|
||||
# handle nomcom volunteer
|
||||
if data['is_nomcom_volunteer'] and object.person:
|
||||
try:
|
||||
nomcom = NomCom.objects.get(is_accepting_volunteers=True)
|
||||
except (NomCom.DoesNotExist, NomCom.MultipleObjectsReturned):
|
||||
nomcom = None
|
||||
if nomcom:
|
||||
Volunteer.objects.create(
|
||||
nomcom=nomcom,
|
||||
person=object.person,
|
||||
affiliation=data['affiliation'],
|
||||
origin='registration')
|
||||
return HttpResponse(response, status=202, content_type='text/plain')
|
||||
else:
|
||||
return HttpResponse(status=405)
|
||||
|
|
|
@ -651,8 +651,8 @@ class DocumentInfo(models.Model):
|
|||
| models.Q(source__type__slug="rfc")
|
||||
)
|
||||
|
||||
|
||||
def referenced_by_rfcs(self):
|
||||
"""Get refs to this doc from RFCs"""
|
||||
return self.relations_that(("refnorm", "refinfo", "refunk", "refold")).filter(
|
||||
source__type__slug="rfc"
|
||||
)
|
||||
|
@ -675,6 +675,13 @@ class DocumentInfo(models.Model):
|
|||
def part_of(self):
|
||||
return self.related_that("contains")
|
||||
|
||||
def referenced_by_rfcs_as_rfc_or_draft(self):
|
||||
"""Get refs to this doc, or a draft/rfc it came from, from an RFC"""
|
||||
refs_to = self.referenced_by_rfcs()
|
||||
if self.type_id == "rfc" and self.came_from_draft():
|
||||
refs_to |= self.came_from_draft().referenced_by_rfcs()
|
||||
return refs_to
|
||||
|
||||
class Meta:
|
||||
abstract = True
|
||||
|
||||
|
|
|
@ -2951,4 +2951,51 @@ class DocInfoMethodsTests(TestCase):
|
|||
self.assertEqual(draft.revisions_by_dochistory(),[f"{i:02d}" for i in range(8,10)])
|
||||
self.assertEqual(draft.revisions_by_newrevisionevent(),[f"{i:02d}" for i in [*range(0,5), *range(6,10)]])
|
||||
|
||||
def test_referenced_by_rfcs(self):
|
||||
# n.b., no significance to the ref* values in this test
|
||||
referring_draft = WgDraftFactory()
|
||||
(rfc, referring_rfc) = WgRfcFactory.create_batch(2)
|
||||
rfc.targets_related.create(relationship_id="refnorm", source=referring_draft)
|
||||
rfc.targets_related.create(relationship_id="refnorm", source=referring_rfc)
|
||||
self.assertCountEqual(
|
||||
rfc.referenced_by_rfcs(),
|
||||
rfc.targets_related.filter(source=referring_rfc),
|
||||
)
|
||||
|
||||
def test_referenced_by_rfcs_as_rfc_or_draft(self):
|
||||
# n.b., no significance to the ref* values in this test
|
||||
draft = WgDraftFactory()
|
||||
rfc = WgRfcFactory()
|
||||
draft.relateddocument_set.create(relationship_id="became_rfc", target=rfc)
|
||||
|
||||
# Draft referring to the rfc and the draft - should not be reported at all
|
||||
draft_referring_to_both = WgDraftFactory()
|
||||
draft_referring_to_both.relateddocument_set.create(relationship_id="refnorm", target=draft)
|
||||
draft_referring_to_both.relateddocument_set.create(relationship_id="refnorm", target=rfc)
|
||||
|
||||
# RFC referring only to the draft - should be reported for either the draft or the rfc
|
||||
rfc_referring_to_draft = WgRfcFactory()
|
||||
rfc_referring_to_draft.relateddocument_set.create(relationship_id="refinfo", target=draft)
|
||||
|
||||
# RFC referring only to the rfc - should be reported only for the rfc
|
||||
rfc_referring_to_rfc = WgRfcFactory()
|
||||
rfc_referring_to_rfc.relateddocument_set.create(relationship_id="refinfo", target=rfc)
|
||||
|
||||
# RFC referring only to the rfc - should be reported only for the rfc
|
||||
rfc_referring_to_rfc = WgRfcFactory()
|
||||
rfc_referring_to_rfc.relateddocument_set.create(relationship_id="refinfo", target=rfc)
|
||||
|
||||
# RFC referring to the rfc and the draft - should be reported for both
|
||||
rfc_referring_to_both = WgRfcFactory()
|
||||
rfc_referring_to_both.relateddocument_set.create(relationship_id="refnorm", target=draft)
|
||||
rfc_referring_to_both.relateddocument_set.create(relationship_id="refnorm", target=rfc)
|
||||
|
||||
self.assertCountEqual(
|
||||
draft.referenced_by_rfcs_as_rfc_or_draft(),
|
||||
draft.targets_related.filter(source__type="rfc"),
|
||||
)
|
||||
|
||||
self.assertCountEqual(
|
||||
rfc.referenced_by_rfcs_as_rfc_or_draft(),
|
||||
draft.targets_related.filter(source__type="rfc") | rfc.targets_related.filter(source__type="rfc"),
|
||||
)
|
||||
|
|
|
@ -1437,8 +1437,26 @@ def document_references(request, name):
|
|||
return render(request, "doc/document_references.html",dict(doc=doc,refs=sorted(refs,key=lambda x:x.target.name),))
|
||||
|
||||
def document_referenced_by(request, name):
|
||||
"""View documents that reference the named document
|
||||
|
||||
The view lists both direct references to a the named document, plus references to
|
||||
related other documents. For a draft that became an RFC, this will include references
|
||||
to the RFC. For an RFC, this will include references to the draft it came from, if any.
|
||||
For a subseries document, this will include references to any of the RFC documents it
|
||||
contains.
|
||||
|
||||
In the rendered output, a badge is applied to indicate the name of the document the
|
||||
reference actually targeted. E.g., on the display for a draft that became RFC NNN,
|
||||
references included because they point to that RFC would be shown with a tag "As RFC NNN".
|
||||
The intention is to make the "Referenced By" page useful for finding related work while
|
||||
accurately reflecting the actual reference relationships.
|
||||
"""
|
||||
doc = get_object_or_404(Document,name=name)
|
||||
refs = doc.referenced_by()
|
||||
if doc.came_from_draft():
|
||||
refs |= doc.came_from_draft().referenced_by()
|
||||
if doc.became_rfc():
|
||||
refs |= doc.became_rfc().referenced_by()
|
||||
if doc.type_id in ["bcp","std","fyi"]:
|
||||
for rfc in doc.contains():
|
||||
refs |= rfc.referenced_by()
|
||||
|
|
|
@ -418,13 +418,19 @@ STATE_SLUGS = {
|
|||
for dt in AD_WORKLOAD
|
||||
}
|
||||
|
||||
|
||||
def state_to_doc_type(state):
|
||||
for dt in STATE_SLUGS:
|
||||
if state in STATE_SLUGS[dt]:
|
||||
return dt
|
||||
return None
|
||||
|
||||
|
||||
IESG_STATES = State.objects.filter(type="draft-iesg").values_list("name", flat=True)
|
||||
|
||||
|
||||
def date_to_bucket(date, now, num_buckets):
|
||||
return num_buckets - min(
|
||||
num_buckets, int((now.date() - date.date()).total_seconds() / 60 / 60 / 24)
|
||||
)
|
||||
return num_buckets - int((now.date() - date.date()).total_seconds() / 60 / 60 / 24)
|
||||
|
||||
|
||||
def ad_workload(request):
|
||||
|
@ -477,6 +483,7 @@ def ad_workload(request):
|
|||
to_state = state_name(dt, state, shorten=False)
|
||||
elif e.desc.endswith("has been replaced"):
|
||||
# stop tracking
|
||||
last = e.time
|
||||
break
|
||||
|
||||
if not to_state:
|
||||
|
@ -501,26 +508,30 @@ def ad_workload(request):
|
|||
elif to_state == "RFC Published":
|
||||
to_state = "RFC"
|
||||
|
||||
if dt == "rfc":
|
||||
new_dt = state_to_doc_type(to_state)
|
||||
if new_dt is not None and new_dt != dt:
|
||||
dt = new_dt
|
||||
|
||||
if to_state not in STATE_SLUGS[dt].keys() or to_state == "Replaced":
|
||||
# change into a state the AD dashboard doesn't display
|
||||
if to_state in IESG_STATES or to_state == "Replaced":
|
||||
# if it's an IESG state we don't display, we're done with this doc
|
||||
# if it's an IESG state we don't display, record it's time
|
||||
last = e.time
|
||||
break
|
||||
# if it's not an IESG state, keep going with next event
|
||||
# keep going with next event
|
||||
continue
|
||||
|
||||
sn = STATE_SLUGS[dt][to_state]
|
||||
buckets_start = date_to_bucket(e.time, now, days)
|
||||
buckets_end = date_to_bucket(last, now, days)
|
||||
|
||||
if buckets_end >= days:
|
||||
# this event is older than we record in the history
|
||||
if last == now:
|
||||
# but since we didn't record any state yet,
|
||||
# this is the state the doc was in for the
|
||||
# entire history
|
||||
for b in range(buckets_start, days):
|
||||
if dt == "charter" and to_state == "Approved" and buckets_start < 0:
|
||||
# don't count old charter approvals
|
||||
break
|
||||
|
||||
if buckets_start <= 0:
|
||||
if buckets_end >= 0:
|
||||
for b in range(0, buckets_end):
|
||||
ad.buckets[dt][sn][b].append(doc.name)
|
||||
sums[dt][sn][b].append(doc.name)
|
||||
last = e.time
|
||||
|
@ -532,15 +543,6 @@ def ad_workload(request):
|
|||
sums[dt][sn][b].append(doc.name)
|
||||
last = e.time
|
||||
|
||||
if last == now:
|
||||
s = state_name(dt, state, shorten=False)
|
||||
if s in STATE_SLUGS[dt].keys():
|
||||
# we didn't have a single event for this doc, assume
|
||||
# the current state applied throughput the history
|
||||
for b in range(days):
|
||||
ad.buckets[dt][state][b].append(doc.name)
|
||||
sums[dt][state][b].append(doc.name)
|
||||
|
||||
metadata = [
|
||||
{
|
||||
"type": (dt, doc_type_name(dt)),
|
||||
|
@ -564,8 +566,11 @@ def ad_workload(request):
|
|||
|
||||
def docs_for_ad(request, name):
|
||||
def sort_key(doc):
|
||||
key = list(AD_WORKLOAD.keys()).index(doc_type(doc))
|
||||
return key
|
||||
dt = doc_type(doc)
|
||||
dt_key = list(AD_WORKLOAD.keys()).index(dt)
|
||||
ds = doc_state(doc)
|
||||
ds_key = AD_WORKLOAD[dt].index(ds) if ds in AD_WORKLOAD[dt] else 99
|
||||
return dt_key * 100 + ds_key
|
||||
|
||||
ad = None
|
||||
responsible = Document.objects.values_list("ad", flat=True).distinct()
|
||||
|
|
|
@ -1296,7 +1296,7 @@ def stream_documents(request, acronym):
|
|||
qs = Document.objects.filter(stream=acronym).filter(
|
||||
Q(type_id="draft", states__type="draft", states__slug="active")
|
||||
| Q(type_id="rfc")
|
||||
)
|
||||
).distinct()
|
||||
docs, meta = prepare_document_table(request, qs, max_results=1000)
|
||||
return render(request, 'group/stream_documents.html', {'stream':stream, 'docs':docs, 'meta':meta, 'editable':editable } )
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
# Copyright The IETF Trust 2016-2020, All Rights Reserved
|
||||
# Copyright The IETF Trust 2016-2023, All Rights Reserved
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
|
||||
|
@ -360,7 +360,13 @@ class InterimSessionModelForm(forms.ModelForm):
|
|||
class InterimAnnounceForm(forms.ModelForm):
|
||||
class Meta:
|
||||
model = Message
|
||||
fields = ('to', 'frm', 'cc', 'bcc', 'reply_to', 'subject', 'body')
|
||||
fields = ('to', 'cc', 'frm', 'subject', 'body')
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(InterimAnnounceForm, self).__init__(*args, **kwargs)
|
||||
self.fields['frm'].label='From'
|
||||
self.fields['frm'].widget.attrs['readonly'] = True
|
||||
self.fields['to'].widget.attrs['readonly'] = True
|
||||
|
||||
def save(self, *args, **kwargs):
|
||||
user = kwargs.pop('user')
|
||||
|
|
|
@ -0,0 +1,27 @@
|
|||
# Generated by Django 4.2.7 on 2023-11-05 09:45
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
dependencies = [
|
||||
("nomcom", "0003_alter_nomination_share_nominator"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name="volunteer",
|
||||
name="origin",
|
||||
field=models.CharField(default="datatracker", max_length=32),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name="volunteer",
|
||||
name="time",
|
||||
field=models.DateTimeField(auto_now_add=True, null=True, blank=True),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name="volunteer",
|
||||
name="withdrawn",
|
||||
field=models.DateTimeField(blank=True, null=True),
|
||||
),
|
||||
]
|
|
@ -327,7 +327,10 @@ class Volunteer(models.Model):
|
|||
nomcom = ForeignKey('NomCom')
|
||||
person = ForeignKey(Person)
|
||||
affiliation = models.CharField(blank=True, max_length=255)
|
||||
|
||||
time = models.DateTimeField(auto_now_add=True, null=True, blank=True)
|
||||
origin = models.CharField(max_length=32, default='datatracker')
|
||||
withdrawn = models.DateTimeField(blank=True, null=True)
|
||||
|
||||
def __str__(self):
|
||||
return f'{self.person} for {self.nomcom}'
|
||||
|
||||
|
|
|
@ -18,9 +18,11 @@ class Command(BaseCommand):
|
|||
parser.add_argument('-n', '--dry-run', action='store_true', default=False,
|
||||
help="Don't delete events, just show what would be done")
|
||||
|
||||
|
||||
def handle(self, *args, **options):
|
||||
keep_days = options['keep_days']
|
||||
dry_run = options['dry_run']
|
||||
verbosity = options.get("verbosity", 1)
|
||||
|
||||
def _format_count(count, unit='day'):
|
||||
return '{} {}{}'.format(count, unit, ('' if count == 1 else 's'))
|
||||
|
@ -28,10 +30,11 @@ class Command(BaseCommand):
|
|||
if keep_days < 0:
|
||||
raise CommandError('Negative keep_days not allowed ({} was specified)'.format(keep_days))
|
||||
|
||||
self.stdout.write('purge_old_personal_api_key_events: Finding events older than {}\n'.format(_format_count(keep_days)))
|
||||
if dry_run:
|
||||
self.stdout.write('Dry run requested, records will not be deleted\n')
|
||||
self.stdout.flush()
|
||||
if verbosity > 1:
|
||||
self.stdout.write('purge_old_personal_api_key_events: Finding events older than {}\n'.format(_format_count(keep_days)))
|
||||
if dry_run:
|
||||
self.stdout.write('Dry run requested, records will not be deleted\n')
|
||||
self.stdout.flush()
|
||||
|
||||
now = timezone.now()
|
||||
old_events = PersonApiKeyEvent.objects.filter(
|
||||
|
@ -41,7 +44,8 @@ class Command(BaseCommand):
|
|||
stats = old_events.aggregate(Min('time'), Max('time'))
|
||||
old_count = old_events.count()
|
||||
if old_count == 0:
|
||||
self.stdout.write('No events older than {} found\n'.format(_format_count(keep_days)))
|
||||
if verbosity > 1:
|
||||
self.stdout.write('No events older than {} found\n'.format(_format_count(keep_days)))
|
||||
return
|
||||
|
||||
oldest_date = stats['time__min']
|
||||
|
@ -50,10 +54,11 @@ class Command(BaseCommand):
|
|||
newest_ago = now - newest_date
|
||||
|
||||
action_fmt = 'Would delete {}\n' if dry_run else 'Deleting {}\n'
|
||||
self.stdout.write(action_fmt.format(_format_count(old_count, 'event')))
|
||||
self.stdout.write(' Oldest at {} ({} ago)\n'.format(oldest_date, _format_count(oldest_ago.days)))
|
||||
self.stdout.write(' Most recent at {} ({} ago)\n'.format(newest_date, _format_count(newest_ago.days)))
|
||||
self.stdout.flush()
|
||||
if verbosity > 1:
|
||||
self.stdout.write(action_fmt.format(_format_count(old_count, 'event')))
|
||||
self.stdout.write(' Oldest at {} ({} ago)\n'.format(oldest_date, _format_count(oldest_ago.days)))
|
||||
self.stdout.write(' Most recent at {} ({} ago)\n'.format(newest_date, _format_count(newest_ago.days)))
|
||||
self.stdout.flush()
|
||||
|
||||
if not dry_run:
|
||||
old_events.delete()
|
||||
|
|
|
@ -76,26 +76,26 @@ class CommandTests(TestCase):
|
|||
num_recent_events = len(recent_events)
|
||||
|
||||
# call with dry run
|
||||
output = self._call_command('purge_old_personal_api_key_events', str(keep_days), '--dry-run')
|
||||
output = self._call_command('purge_old_personal_api_key_events', str(keep_days), '--dry-run', '-v2')
|
||||
self._assert_purge_dry_run_results(output, num_old_events, old_events + recent_events)
|
||||
|
||||
# call for real
|
||||
output = self._call_command('purge_old_personal_api_key_events', str(keep_days))
|
||||
output = self._call_command('purge_old_personal_api_key_events', str(keep_days), '-v2')
|
||||
self._assert_purge_results(output, num_old_events, recent_events)
|
||||
self.assertEqual(PersonEvent.objects.count(), personevents_before + num_recent_events,
|
||||
'PersonEvents were not cleaned up properly')
|
||||
|
||||
# repeat - there should be nothing left to delete
|
||||
output = self._call_command('purge_old_personal_api_key_events', '--dry-run', str(keep_days))
|
||||
output = self._call_command('purge_old_personal_api_key_events', '--dry-run', str(keep_days), '-v2')
|
||||
self._assert_purge_dry_run_results(output, 0, recent_events)
|
||||
|
||||
output = self._call_command('purge_old_personal_api_key_events', str(keep_days))
|
||||
output = self._call_command('purge_old_personal_api_key_events', str(keep_days), '-v2')
|
||||
self._assert_purge_results(output, 0, recent_events)
|
||||
self.assertEqual(PersonEvent.objects.count(), personevents_before + num_recent_events,
|
||||
'PersonEvents were not cleaned up properly')
|
||||
|
||||
# and now delete the remaining events
|
||||
output = self._call_command('purge_old_personal_api_key_events', '0')
|
||||
output = self._call_command('purge_old_personal_api_key_events', '0', '-v2')
|
||||
self._assert_purge_results(output, num_recent_events, [])
|
||||
self.assertEqual(PersonEvent.objects.count(), personevents_before,
|
||||
'PersonEvents were not cleaned up properly')
|
||||
|
|
|
@ -38,10 +38,10 @@
|
|||
</thead>
|
||||
<tbody>
|
||||
{% for ref in refs %}
|
||||
{% with ref.source.name as name %}
|
||||
{% with ref.source.name as src_name %}
|
||||
<tr>
|
||||
<td>
|
||||
<a href="{% url 'ietf.doc.views_doc.document_main' name=name %}">{{ name|prettystdname }}</a>
|
||||
<a href="{% url 'ietf.doc.views_doc.document_main' name=src_name %}">{{ src_name|prettystdname }}</a>
|
||||
{% if ref.target.name != name %}
|
||||
<br>
|
||||
<span class="badge rounded-pill text-bg-info">As {{ ref.target.name }}</span>
|
||||
|
@ -51,13 +51,13 @@
|
|||
<b>{{ ref.source.title }}</b>
|
||||
<br>
|
||||
<a class="btn btn-primary btn-sm"
|
||||
href="{% url 'ietf.doc.views_doc.document_references' name %}"
|
||||
href="{% url 'ietf.doc.views_doc.document_references' src_name %}"
|
||||
rel="nofollow">
|
||||
<i class="bi bi-arrow-left"></i>
|
||||
References
|
||||
</a>
|
||||
<a class="btn btn-primary btn-sm"
|
||||
href="{% url 'ietf.doc.views_doc.document_referenced_by' name %}"
|
||||
href="{% url 'ietf.doc.views_doc.document_referenced_by' src_name %}"
|
||||
rel="nofollow">
|
||||
<i class="bi bi-arrow-right"></i>
|
||||
Referenced by
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
{% extends "base.html" %}
|
||||
{# Copyright The IETF Trust 2015, All Rights Reserved #}
|
||||
{# Copyright The IETF Trust 2015-2023, All Rights Reserved #}
|
||||
{% load origin %}
|
||||
{% load static django_bootstrap5 widget_tweaks %}
|
||||
{% block title %}Announce Interim Meeting{% endblock %}
|
||||
|
@ -11,26 +11,7 @@
|
|||
<h1>Announce Interim Meeting</h1>
|
||||
<form method="post" class="my-3">
|
||||
{% csrf_token %}
|
||||
<div class="row mb-3">
|
||||
<label for="{{ form.to.id_for_label }}" class="col-md-2 fw-bold col-form-label">To</label>
|
||||
<div class="col-md-10">{% render_field form.to class="form-control" readonly="readonly" %}</div>
|
||||
</div>
|
||||
<div class="row mb-3">
|
||||
<label for="{{ form.cc.id_for_label }}" class="col-md-2 fw-bold col-form-label">Cc</label>
|
||||
<div class="col-md-10">{% render_field form.cc class="form-control" %}</div>
|
||||
</div>
|
||||
<div class="row mb-3">
|
||||
<label for="{{ form.frm.id_for_label }}" class="col-md-2 fw-bold col-form-label">From</label>
|
||||
<div class="col-md-10">{% render_field form.frm class="form-control" readonly="readonly" %}</div>
|
||||
</div>
|
||||
<div class="row mb-3">
|
||||
<label for="{{ form.subject.id_for_label }}" class="col-md-2 fw-bold col-form-label">Subject</label>
|
||||
<div class="col-md-10">{% render_field form.subject class="form-control" %}</div>
|
||||
</div>
|
||||
<div class="row mb-3">
|
||||
<label for="{{ form.body.id_for_label }}" class="col-md-2 fw-bold col-form-label">Body</label>
|
||||
<div class="col-md-10">{% render_field form.body class="form-control" %}</div>
|
||||
</div>
|
||||
{% bootstrap_form form layout="horizontal" %}
|
||||
<button class="btn btn-primary" type="submit" name="send">Send</button>
|
||||
<a class="btn btn-secondary float-end"
|
||||
href="{% url 'ietf.meeting.views.interim_request_details' number=meeting.number %}">
|
||||
|
|
|
@ -109,7 +109,7 @@
|
|||
<td>{{ doc.pub_date|date:"b Y"|title }}</td>
|
||||
<td>{{ doc.title|urlize_ietf_docs }}</td>
|
||||
<td class="text-end">
|
||||
{% with doc.referenced_by_rfcs.count as refbycount %}
|
||||
{% with doc.referenced_by_rfcs_as_rfc_or_draft.count as refbycount %}
|
||||
{% if refbycount %}
|
||||
<a class="text-end"
|
||||
href="{% url 'ietf.doc.views_doc.document_referenced_by' doc.name %}"
|
||||
|
|
Loading…
Reference in a new issue