Skip to content

Commit

Permalink
ci: merge main to release (#7973)
Browse files Browse the repository at this point in the history
  • Loading branch information
rjsparks authored Sep 24, 2024
2 parents 1bbe186 + 87d2a36 commit cdc1467
Show file tree
Hide file tree
Showing 51 changed files with 907 additions and 11,243 deletions.
25 changes: 16 additions & 9 deletions client/App.vue
Original file line number Diff line number Diff line change
Expand Up @@ -30,17 +30,24 @@ const appContainer = ref(null)
// Set user theme
// --------------------------------------------------------------------
const desiredTheme = window.localStorage?.getItem('theme')
if (desiredTheme === 'dark') {
siteStore.theme = 'dark'
} else if (desiredTheme === 'light') {
siteStore.theme = 'light'
} else if (window.matchMedia("(prefers-color-scheme: dark)").matches) {
siteStore.theme = 'dark'
} else {
siteStore.theme = 'light'
function updateTheme() {
const desiredTheme = window.localStorage?.getItem('theme')
if (desiredTheme === 'dark') {
siteStore.theme = 'dark'
} else if (desiredTheme === 'light') {
siteStore.theme = 'light'
} else if (window.matchMedia("(prefers-color-scheme: dark)").matches) {
siteStore.theme = 'dark'
} else {
siteStore.theme = 'light'
}
}
updateTheme()
// this change event fires for either light or dark changes
window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', updateTheme)
// --------------------------------------------------------------------
// Handle browser resize
// --------------------------------------------------------------------
Expand Down
4 changes: 2 additions & 2 deletions dev/tests/settings_local.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@
}

IDSUBMIT_IDNITS_BINARY = "/usr/local/bin/idnits"
IDSUBMIT_REPOSITORY_PATH = "test/id/"
IDSUBMIT_STAGING_PATH = "test/staging/"
IDSUBMIT_REPOSITORY_PATH = "/assets/ietfdata/doc/draft/repository"
IDSUBMIT_STAGING_PATH = "/assets/www6s/staging/"

AGENDA_PATH = '/assets/www6s/proceedings/'
MEETINGHOST_LOGO_PATH = AGENDA_PATH
Expand Down
4 changes: 2 additions & 2 deletions docker/configs/settings_local.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
from ietf.settings_postgresqldb import DATABASES # pyflakes:ignore

IDSUBMIT_IDNITS_BINARY = "/usr/local/bin/idnits"
IDSUBMIT_STAGING_PATH = "test/staging/"
IDSUBMIT_STAGING_PATH = "/assets/www6s/staging/"

AGENDA_PATH = '/assets/www6s/proceedings/'
MEETINGHOST_LOGO_PATH = AGENDA_PATH
Expand Down Expand Up @@ -53,7 +53,7 @@
FTP_DIR = '/assets/ftp'

NOMCOM_PUBLIC_KEYS_DIR = 'data/nomcom_keys/public_keys/'
SLIDE_STAGING_PATH = 'test/staging/'
SLIDE_STAGING_PATH = '/assets/www6s/staging/'

DE_GFM_BINARY = '/usr/local/bin/de-gfm'

Expand Down
8 changes: 1 addition & 7 deletions docker/scripts/app-create-dirs.sh
Original file line number Diff line number Diff line change
@@ -1,13 +1,6 @@
#!/bin/bash

for sub in \
test/id \
test/staging \
test/archive \
test/rfc \
test/media \
test/wiki/ietf \
data/nomcom_keys/public_keys \
/assets/archive/id \
/assets/collection \
/assets/collection/draft-archive \
Expand All @@ -27,6 +20,7 @@ for sub in \
/assets/ietfdata/derived \
/assets/ietfdata/derived/bibxml \
/assets/ietfdata/derived/bibxml/bibxml-ids \
/assets/ietfdata/doc/draft/repository \
/assets/www6s \
/assets/www6s/staging \
/assets/www6s/wg-descriptions \
Expand Down
5 changes: 4 additions & 1 deletion ietf/community/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -104,12 +104,15 @@ def notify_events(sender, instance, **kwargs):
if not isinstance(instance, DocEvent):
return

if not kwargs.get("created", False):
return # only notify on creation

if instance.doc.type_id != 'draft':
return

if getattr(instance, "skip_community_list_notification", False):
return

# kludge alert: queuing a celery task in response to a signal can cause unexpected attempts to
# start a Celery task during tests. To prevent this, don't queue a celery task if we're running
# tests.
Expand Down
27 changes: 17 additions & 10 deletions ietf/community/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
import ietf.community.views
from ietf.group.models import Group
from ietf.group.utils import setup_default_community_list_for_group
from ietf.doc.factories import DocumentFactory
from ietf.doc.models import State
from ietf.doc.utils import add_state_change_event
from ietf.person.models import Person, Email, Alias
Expand Down Expand Up @@ -439,39 +440,45 @@ def test_notification_signal_receiver(self, mock_notify_task):
This implicitly tests that notify_events is hooked up to the post_save signal.
"""
# Arbitrary model that's not a DocEvent
p = PersonFactory()
person = PersonFactory()
mock_notify_task.reset_mock() # clear any calls that resulted from the factories
# be careful overriding SERVER_MODE - we do it here because the method
# under test does not make this call when in "test" mode
with override_settings(SERVER_MODE="not-test"):
p.save()
person.save()
self.assertFalse(mock_notify_task.delay.called)

d = DocEventFactory()
mock_notify_task.reset_mock() # clear any calls that resulted from the factories
# build a DocEvent that is not yet persisted
doc = DocumentFactory()
d = DocEventFactory.build(by=person, doc=doc)
# mock_notify_task.reset_mock() # clear any calls that resulted from the factories
# be careful overriding SERVER_MODE - we do it here because the method
# under test does not make this call when in "test" mode
with override_settings(SERVER_MODE="not-test"):
d.save()
self.assertEqual(mock_notify_task.delay.call_count, 1)
self.assertEqual(mock_notify_task.delay.call_count, 1, "notify_task should be run on creation of DocEvent")
self.assertEqual(mock_notify_task.delay.call_args, mock.call(event_id = d.pk))

mock_notify_task.reset_mock()
with override_settings(SERVER_MODE="not-test"):
d.save()
self.assertFalse(mock_notify_task.delay.called, "notify_task should not be run save of on existing DocEvent")

mock_notify_task.reset_mock()
d = DocEventFactory.build(by=person, doc=doc)
d.skip_community_list_notification = True
# be careful overriding SERVER_MODE - we do it here because the method
# under test does not make this call when in "test" mode
with override_settings(SERVER_MODE="not-test"):
d.save()
self.assertFalse(mock_notify_task.delay.called)
self.assertFalse(mock_notify_task.delay.called, "notify_task should not run when skip_community_list_notification is set")

del(d.skip_community_list_notification)
d.doc.type_id="rfc" # not "draft"
d.doc.save()
d = DocEventFactory.build(by=person, doc=DocumentFactory(type_id="rfc"))
# be careful overriding SERVER_MODE - we do it here because the method
# under test does not make this call when in "test" mode
with override_settings(SERVER_MODE="not-test"):
d.save()
self.assertFalse(mock_notify_task.delay.called)
self.assertFalse(mock_notify_task.delay.called, "notify_task should not run on a document with type 'rfc'")

@mock.patch("ietf.utils.mail.send_mail_text")
def test_notify_event_to_subscribers(self, mock_send_mail_text):
Expand Down
4 changes: 2 additions & 2 deletions ietf/doc/expire.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,8 @@ def expirable_drafts(queryset=None):

# Populate this first time through (but after django has been set up)
if nonexpirable_states is None:
# all IESG states except I-D Exists, AD Watching, and Dead block expiry
nonexpirable_states = list(State.objects.filter(used=True, type="draft-iesg").exclude(slug__in=("idexists","watching", "dead")))
# all IESG states except I-D Exists and Dead block expiry
nonexpirable_states = list(State.objects.filter(used=True, type="draft-iesg").exclude(slug__in=("idexists", "dead")))
# sent to RFC Editor and RFC Published block expiry (the latter
# shouldn't be possible for an active draft, though)
nonexpirable_states += list(State.objects.filter(used=True, type__in=("draft-stream-iab", "draft-stream-irtf", "draft-stream-ise"), slug__in=("rfc-edit", "pub")))
Expand Down
121 changes: 121 additions & 0 deletions ietf/doc/migrations/0024_remove_ad_is_watching_states.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
# Copyright The IETF Trust 2024, All Rights Reserved

from django.db import migrations


def get_helper(DocHistory, RelatedDocument, RelatedDocHistory, DocumentAuthor, DocHistoryAuthor):
"""Dependency injection wrapper"""

def save_document_in_history(doc):
"""Save a snapshot of document and related objects in the database.
Local copy of ietf.doc.utils.save_document_in_history() to avoid depending on the
code base in a migration.
"""

def get_model_fields_as_dict(obj):
return dict((field.name, getattr(obj, field.name))
for field in obj._meta.fields
if field is not obj._meta.pk)

# copy fields
fields = get_model_fields_as_dict(doc)
fields["doc"] = doc
fields["name"] = doc.name

dochist = DocHistory(**fields)
dochist.save()

# copy many to many
for field in doc._meta.many_to_many:
if field.remote_field.through and field.remote_field.through._meta.auto_created:
hist_field = getattr(dochist, field.name)
hist_field.clear()
hist_field.set(getattr(doc, field.name).all())

# copy remaining tricky many to many
def transfer_fields(obj, HistModel):
mfields = get_model_fields_as_dict(item)
# map doc -> dochist
for k, v in mfields.items():
if v == doc:
mfields[k] = dochist
HistModel.objects.create(**mfields)

for item in RelatedDocument.objects.filter(source=doc):
transfer_fields(item, RelatedDocHistory)

for item in DocumentAuthor.objects.filter(document=doc):
transfer_fields(item, DocHistoryAuthor)

return dochist

return save_document_in_history


def forward(apps, schema_editor):
"""Mark watching draft-iesg state unused after removing it from Documents"""
StateDocEvent = apps.get_model("doc", "StateDocEvent")
Document = apps.get_model("doc", "Document")
State = apps.get_model("doc", "State")
StateType = apps.get_model("doc", "StateType")
Person = apps.get_model("person", "Person")

save_document_in_history = get_helper(
DocHistory=apps.get_model("doc", "DocHistory"),
RelatedDocument=apps.get_model("doc", "RelatedDocument"),
RelatedDocHistory=apps.get_model("doc", "RelatedDocHistory"),
DocumentAuthor=apps.get_model("doc", "DocumentAuthor"),
DocHistoryAuthor=apps.get_model("doc", "DocHistoryAuthor"),
)

draft_iesg_state_type = StateType.objects.get(slug="draft-iesg")
idexists_state = State.objects.get(type=draft_iesg_state_type, slug="idexists")
watching_state = State.objects.get(type=draft_iesg_state_type, slug="watching")
system_person = Person.objects.get(name="(System)")

# Remove state from documents that currently have it
for doc in Document.objects.filter(states=watching_state):
assert doc.type_id == "draft"
doc.states.remove(watching_state)
doc.states.add(idexists_state)
e = StateDocEvent.objects.create(
type="changed_state",
by=system_person,
doc=doc,
rev=doc.rev,
desc=f"{draft_iesg_state_type.label} changed to <b>{idexists_state.name}</b> from {watching_state.name}",
state_type=draft_iesg_state_type,
state=idexists_state,
)
doc.time = e.time
doc.save()
save_document_in_history(doc)
assert not Document.objects.filter(states=watching_state).exists()

# Mark state as unused
watching_state.used = False
watching_state.save()


def reverse(apps, schema_editor):
"""Mark watching draft-iesg state as used
Does not try to re-apply the state to Documents modified by the forward migration. This
could be done in theory, but would either require dangerous history rewriting or add a
lot of history junk.
"""
State = apps.get_model("doc", "State")
StateType = apps.get_model("doc", "StateType")
State.objects.filter(
type=StateType.objects.get(slug="draft-iesg"), slug="watching"
).update(used=True)


class Migration(migrations.Migration):

dependencies = [
("doc", "0023_bofreqspamstate"),
]

operations = [migrations.RunPython(forward, reverse)]
2 changes: 1 addition & 1 deletion ietf/doc/templatetags/ballot_icon.py
Original file line number Diff line number Diff line change
Expand Up @@ -184,7 +184,7 @@ def state_age_colored(doc):
if not iesg_state:
return ""

if iesg_state in ["dead", "watching", "pub", "idexists"]:
if iesg_state in ["dead", "pub", "idexists"]:
return ""
try:
state_datetime = (
Expand Down
23 changes: 22 additions & 1 deletion ietf/doc/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@
from ietf.meeting.factories import ( MeetingFactory, SessionFactory, SessionPresentationFactory,
ProceedingsMaterialFactory )

from ietf.name.models import SessionStatusName, BallotPositionName, DocTypeName
from ietf.name.models import SessionStatusName, BallotPositionName, DocTypeName, RoleName
from ietf.person.models import Person
from ietf.person.factories import PersonFactory, EmailFactory
from ietf.utils.mail import outbox, empty_outbox
Expand Down Expand Up @@ -1450,6 +1450,14 @@ def test_document_draft_action_holders_buttons(self, mock_method):
"""Buttons for action holders should be shown when AD or secretary"""
draft = WgDraftFactory()
draft.action_holders.set([PersonFactory()])
other_group = GroupFactory(type_id=draft.group.type_id)

# create a test RoleName and put it in the docman_roles for the document group
RoleName.objects.create(slug="wrangler", name="Wrangler", used=True)
draft.group.features.docman_roles.append("wrangler")
draft.group.features.save()
wrangler = RoleFactory(group=draft.group, name_id="wrangler").person
wrangler_of_other_group = RoleFactory(group=other_group, name_id="wrangler").person

url = urlreverse('ietf.doc.views_doc.document_main', kwargs=dict(name=draft.name))
edit_ah_url = urlreverse('ietf.doc.views_doc.edit_action_holders', kwargs=dict(name=draft.name))
Expand Down Expand Up @@ -1482,6 +1490,8 @@ def _run_test(username=None, expect_buttons=False):

_run_test(None, False)
_run_test('plain', False)
_run_test(wrangler_of_other_group.user.username, False)
_run_test(wrangler.user.username, True)
_run_test('ad', True)
_run_test('secretary', True)

Expand Down Expand Up @@ -1683,6 +1693,17 @@ def test_document_material(self):

r = self.client.get(urlreverse("ietf.doc.views_doc.document_main", kwargs=dict(name=doc.name)))
self.assertEqual(r.status_code, 200)
self.assertNotContains(r, "The session for this document was cancelled.")

SchedulingEvent.objects.create(
session=session,
status_id='canceled',
by = Person.objects.get(user__username="marschairman"),
)

r = self.client.get(urlreverse("ietf.doc.views_doc.document_main", kwargs=dict(name=doc.name)))
self.assertEqual(r.status_code, 200)
self.assertContains(r, "The session for this document was cancelled.")

def test_document_ballot(self):
doc = IndividualDraftFactory()
Expand Down
2 changes: 1 addition & 1 deletion ietf/doc/tests_ballot.py
Original file line number Diff line number Diff line change
Expand Up @@ -559,7 +559,7 @@ def test_issue_ballot_warn_if_early(self):
q = PyQuery(r.content)
self.assertFalse(q('[class=text-danger]:contains("not completed IETF Last Call")'))

for state_slug in ["lc", "watching", "ad-eval"]:
for state_slug in ["lc", "ad-eval"]:
draft.set_state(State.objects.get(type="draft-iesg",slug=state_slug))
r = self.client.get(url)
self.assertEqual(r.status_code, 200)
Expand Down
Loading

0 comments on commit cdc1467

Please sign in to comment.