Skip to content
Open
Show file tree
Hide file tree
Changes from 36 commits
Commits
Show all changes
43 commits
Select commit Hold shift + click to select a range
1ea23a0
add sac field to request form
celestemartinez Feb 27, 2026
954b1df
parametrize committee name in description of invited and declined group
celestemartinez Mar 2, 2026
62a8420
add ICLR test with templates
celestemartinez Mar 2, 2026
dc310a6
fix tests
celestemartinez Mar 3, 2026
13c55aa
Merge branch 'master' into feat/new-ui-sac
celestemartinez Mar 3, 2026
308bff0
fix test
celestemartinez Mar 4, 2026
4873be3
Merge branch 'master' into feat/new-ui-sac
celestemartinez Mar 4, 2026
0fdfc1b
Merge branch 'master' into feat/new-ui-sac
celestemartinez Mar 11, 2026
dfd61b9
Merge branch 'master' into feat/new-ui-sac
celestemartinez Apr 3, 2026
23ae97c
add senior area chairs name to request forms
celestemartinez Apr 7, 2026
1bbfac5
Merge branch 'master' into feat/new-ui-sac
celestemartinez Apr 7, 2026
aa66077
create expertise selection invitations for all roles
celestemartinez Apr 7, 2026
e968917
test recruitment, post submissions and release to everyone after abst…
celestemartinez Apr 7, 2026
b573be0
fix full submission license edition and test author editing submissio…
celestemartinez Apr 7, 2026
3640b6e
enable bidding stage for SACs
celestemartinez Apr 8, 2026
f10870a
create all invitations for sac-ac matching
celestemartinez Apr 9, 2026
766c10c
Merge branch 'master' into feat/new-ui-sac
celestemartinez Apr 9, 2026
1d9e05b
test assignments deployment
celestemartinez Apr 9, 2026
91442fa
add senior area chairs name to request form
celestemartinez Apr 9, 2026
1caca8c
test review and comment stages
celestemartinez Apr 10, 2026
684bb1e
test rebuttal and metareview; propagate changes to metareview to meta…
celestemartinez Apr 10, 2026
5fc0cc7
Merge branch 'master' into feat/new-ui-sac
celestemartinez Apr 10, 2026
e3e35e0
test metareview release, decision and decision release
celestemartinez Apr 10, 2026
51147c1
fix test
celestemartinez Apr 13, 2026
98b53b3
Apply suggestions from code review
celestemartinez Apr 14, 2026
09ca75d
fix indentation
celestemartinez Apr 14, 2026
fe06ff6
Merge branch 'master' into feat/new-ui-sac
celestemartinez Apr 14, 2026
9e07025
Merge branch 'master' into feat/new-ui-sac
celestemartinez Apr 15, 2026
5933f9f
allow PCs to edit metareview sae revision
celestemartinez Apr 15, 2026
30f6e7b
add reminder to recompute conflicts if SAC-AC assignemnts have not be…
celestemartinez Apr 16, 2026
73e514c
Merge branch 'master' into feat/new-ui-sac
celestemartinez Apr 16, 2026
355500d
Merge branch 'master' into feat/new-ui-sac
celestemartinez Apr 16, 2026
6862608
Merge branch 'master' into feat/new-ui-sac
celestemartinez Apr 17, 2026
5765cb1
add senior acs name
celestemartinez Apr 17, 2026
8fb799d
change sac field name in request form
celestemartinez Apr 22, 2026
25ff338
Merge branch 'master' into feat/new-ui-sac
melisabok Apr 22, 2026
b90d0e1
use senior_area_chairs_names
celestemartinez Apr 23, 2026
b7bb45f
Merge branch 'master' into feat/new-ui-sac
celestemartinez Apr 23, 2026
1f98993
Merge branch 'master' into feat/new-ui-sac
celestemartinez Apr 24, 2026
8a70c54
check if venue has SACs
celestemartinez Apr 24, 2026
286498b
fix test
celestemartinez Apr 24, 2026
3522f32
Merge branch 'master' into feat/new-ui-sac
melisabok Apr 24, 2026
3eea1d4
add checking senior area chairs names in preprocess
celestemartinez Apr 24, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 10 additions & 2 deletions openreview/venue/invitation.py
Original file line number Diff line number Diff line change
Expand Up @@ -1211,7 +1211,7 @@ def set_meta_review_invitation(self):

if self.venue.is_template_related_workflow():
edit_invitations_builder = openreview.workflows.EditInvitationsBuilder(self.client, self.venue_id)
content = {
metareview_content = {
'recommendation_field_name': {
'value': {
'param': {
Expand All @@ -1224,7 +1224,7 @@ def set_meta_review_invitation(self):
}
edit_invitations_builder.set_edit_content_invitation(
meta_review_invitation_id,
content,
metareview_content,
process_file='../workflows/workflow_process/edit_recommendation_field_name_process.py',
preprocess_file='../workflows/workflow_process/edit_recommendation_field_name_pre_process.py'
)
Expand Down Expand Up @@ -1313,8 +1313,16 @@ def set_meta_review_invitation(self):
if meta_review_stage.source_submissions_query:
invitation.content['source']['value']['content'] = meta_review_stage.source_submissions_query

if self.venue.is_template_related_workflow():
invitation.description = f'Set the date/time when the meta review {sac_acronym} revision period is open to senior area chairs, when meta reviews revisions are due, and when the meta review revision form is no longer available to senior area chairs.'

self.save_invitation(invitation, replacement=False)

if self.venue.is_template_related_workflow():
edit_invitations_builder = openreview.workflows.EditInvitationsBuilder(self.client, self.venue_id)
edit_invitations_builder.set_edit_dates_invitation(meta_review_sac_edit_invitation_id)
edit_invitations_builder.set_edit_content_invitation(meta_review_sac_edit_invitation_id)

return invitation

def set_recruitment_invitation(self, committee_name, options):
Expand Down
213 changes: 117 additions & 96 deletions openreview/venue/matching.py
Original file line number Diff line number Diff line change
Expand Up @@ -1465,6 +1465,20 @@ def setup_matching_invitations(self):
paper_number = '${{2/head}/number}'

readers = [venue_id]
edge_readers = readers + ['${2/tail}']
edge_nonreaders = [venue.get_authors_id(number=paper_number)]
edge_head = {
'param': {
'type': 'note',
'withInvitation': venue.get_submission_id()
}
}
description = f'<span>This step runs automatically at its "activation date", and creates "edges" between the {venue.get_committee_name(self.match_group.id, pretty=True)} group and article submissions that represent expertise. Configure which expertise model will compute affinity scores. (We find that the model "specter2+scincl" has the best performance; refer to our <a href=https://github.com/openreview/openreview-expertise>expertise repository</a> for more information on the models.)</span>'
content = {
'committee_name': {
'value': self.match_group_name
}
}

if self.is_reviewer:
if venue.use_senior_area_chairs:
Expand All @@ -1476,24 +1490,34 @@ def setup_matching_invitations(self):
if venue.use_senior_area_chairs:
readers.append(venue.get_senior_area_chairs_id(number=paper_number))

if self.is_senior_area_chair:
edge_readers = [venue_id, '${2/tail}', '${2/head}']
edge_nonreaders = []
edge_head = {
'param': {
'type': 'profile',
'inGroup': self.alternate_matching_group
}
}
description = f'<span>This step runs automatically at its "activation date", and creates "edges" between the {venue.get_committee_name(self.match_group.id, pretty=True)} group and the {venue.get_committee_name(self.alternate_matching_group, pretty=True)} group that represent expertise. Configure which expertise model will compute affinity scores. (We find that the model "specter2+scincl" has the best performance; refer to our <a href=https://github.com/openreview/openreview-expertise>expertise repository</a> for more information on the models.)</span>'
content['alternate_committee_id'] = {
'value': self.alternate_matching_group
}

invitation = Invitation(
id = score_invitation_id,
invitees = [f'{venue_id}/Automated_Administrator'],
readers = readers,
writers = [venue_id],
signatures = [venue_id],
responseArchiveDate = venue.get_edges_archive_date(),
description = f'<span>This step runs automatically at its "activation date", and creates "edges" between the {venue.get_committee_name(self.match_group.id, pretty=True)} group and article submissions that represent expertise. Configure which expertise model will compute affinity scores. (We find that the model "specter2+scincl" has the best performance; refer to our <a href=https://github.com/openreview/openreview-expertise>expertise repository</a> for more information on the models.)</span>',
description = description,
cdate = tools.datetime_millis(venue.submission_stage.due_date) + (60*60*1000*24*3),
date_processes = [{
'dates': ["#{4/cdate}", venue.invitation_builder.update_date_string],
'script': venue.invitation_builder.get_process_content('../workflows/process/compute_affinity_scores_process.py')
}],
content = {
'committee_name': {
'value': self.match_group_name
}
},
content = content,
edge = {
'id': {
'param': {
Expand All @@ -1515,8 +1539,8 @@ def setup_matching_invitations(self):
'deletable': True
}
},
'readers': readers + ['${2/tail}'],
'nonreaders': [venue.get_authors_id(number=paper_number)],
'readers': edge_readers,
'nonreaders': edge_nonreaders,
'writers': [venue_id],
'signatures': {
'param': {
Expand All @@ -1527,12 +1551,7 @@ def setup_matching_invitations(self):
'default': [venue.get_program_chairs_id()]
}
},
'head': {
'param': {
'type': 'note',
'withInvitation': venue.get_submission_id()
}
},
'head': edge_head,
'tail': {
'param': {
'type': 'profile',
Expand Down Expand Up @@ -1562,93 +1581,95 @@ def setup_matching_invitations(self):
edit_invitations_builder.set_edit_affinities_settings_invitation(score_invitation_id)
edit_invitations_builder.set_edit_dates_one_level_invitation(score_invitation_id)

conflict_invitation_id = venue.get_conflict_score_id(self.match_group.id)
committee_role = venue.get_standard_committee_role(committee_id=self.match_group.id)
if not self.is_senior_area_chair:

invitation = Invitation(
id = conflict_invitation_id,
invitees = [f'{venue_id}/Automated_Administrator'],
readers = readers,
writers = [venue_id],
signatures = [venue_id],
responseArchiveDate = venue.get_edges_archive_date(),
description = f'This step runs automatically at its "activation date", and creates "edges" between the {venue.get_committee_name(self.match_group.id, pretty=True)} group and article submissions to represent identified conflicts of interest. Configure the conflict of interest policy to be applied and specify the number of years of data to be retrieved from the OpenReview profile for conflict detection.',
cdate = tools.datetime_millis(venue.submission_stage.due_date) + (60*60*1000*24*3),
date_processes = [{
'dates': ["#{4/cdate}", "#{4/mdate} + " + str(5000)],
'script': venue.invitation_builder.get_process_content('../workflows/process/compute_conflicts_process.py')
}],
content = {
'committee_name': {
'value': self.match_group_name
},
'committee_role': {
'value': committee_role
}
},
edge = {
'id': {
'param': {
'withInvitation': conflict_invitation_id,
'optional': True
}
},
'ddate': {
'param': {
'range': [ 0, 9999999999999 ],
'optional': True,
'deletable': True
}
},
'cdate': {
'param': {
'range': [ 0, 9999999999999 ],
'optional': True,
'deletable': True
}
},
'readers': readers + ['${2/tail}'],
'writers': [venue_id],
'signatures': {
'param': {
'items': [
{ 'value': venue_id, 'optional': True },
{ 'value': venue.get_program_chairs_id(), 'optional': True }
],
'default': [venue.get_program_chairs_id()]
}
},
'head': {
'param': {
'type': 'note',
'withInvitation': venue.get_submission_id()
conflict_invitation_id = venue.get_conflict_score_id(self.match_group.id)
committee_role = venue.get_standard_committee_role(committee_id=self.match_group.id)

invitation = Invitation(
id = conflict_invitation_id,
invitees = [f'{venue_id}/Automated_Administrator'],
readers = readers,
writers = [venue_id],
signatures = [venue_id],
responseArchiveDate = venue.get_edges_archive_date(),
description = f'This step runs automatically at its "activation date", and creates "edges" between the {venue.get_committee_name(self.match_group.id, pretty=True)} group and article submissions to represent identified conflicts of interest. Configure the conflict of interest policy to be applied and specify the number of years of data to be retrieved from the OpenReview profile for conflict detection.',
cdate = tools.datetime_millis(venue.submission_stage.due_date) + (60*60*1000*24*3),
date_processes = [{
'dates': ["#{4/cdate}", "#{4/mdate} + " + str(5000)],
'script': venue.invitation_builder.get_process_content('../workflows/process/compute_conflicts_process.py')
}],
content = {
'committee_name': {
'value': self.match_group_name
},
'committee_role': {
'value': committee_role
}
},
'tail': {
'param': {
'type': 'profile',
'options': {
'group': self.match_group.id
edge = {
'id': {
'param': {
'withInvitation': conflict_invitation_id,
'optional': True
}
},
'ddate': {
'param': {
'range': [ 0, 9999999999999 ],
'optional': True,
'deletable': True
}
},
'cdate': {
'param': {
'range': [ 0, 9999999999999 ],
'optional': True,
'deletable': True
}
},
'readers': readers + ['${2/tail}'],
'writers': [venue_id],
'signatures': {
'param': {
'items': [
{ 'value': venue_id, 'optional': True },
{ 'value': venue.get_program_chairs_id(), 'optional': True }
],
'default': [venue.get_program_chairs_id()]
}
},
'head': {
'param': {
'type': 'note',
'withInvitation': venue.get_submission_id()
}
},
'tail': {
'param': {
'type': 'profile',
'options': {
'group': self.match_group.id
}
}
},
'weight': {
'param': {
'minimum': -1
}
},
'label': {
'param': {
'regex': '.*',
'optional': True,
'deletable': True
}
}
},
'weight': {
'param': {
'minimum': -1
}
},
'label': {
'param': {
'regex': '.*',
'optional': True,
'deletable': True
}
}
}
)
)

invitation = self.venue.invitation_builder.save_invitation(invitation, replacement=True)
invitation = self.venue.invitation_builder.save_invitation(invitation, replacement=True)

edit_invitations_builder = openreview.workflows.EditInvitationsBuilder(self.client, venue_id)
edit_invitations_builder.set_edit_conflict_settings_invitation(conflict_invitation_id)
edit_invitations_builder.set_edit_dates_one_level_invitation(conflict_invitation_id)
edit_invitations_builder = openreview.workflows.EditInvitationsBuilder(self.client, venue_id)
edit_invitations_builder.set_edit_conflict_settings_invitation(conflict_invitation_id)
edit_invitations_builder.set_edit_dates_one_level_invitation(conflict_invitation_id)
39 changes: 34 additions & 5 deletions openreview/venue/venue.py
Original file line number Diff line number Diff line change
Expand Up @@ -144,8 +144,8 @@ def set_main_settings(self, request_note):
self.area_chair_roles = request_note.content.get('area_chair_roles', [self.area_chairs_name])
preferred_email_groups.append(self.get_area_chairs_id())

if 'senior_area_chairs_name' in request_note.content: ## change this once we add support for SACs
self.senior_area_chairs_name = request_note.content['senior_area_chairs_name']['value']
if request_note.content.get('senior_area_chairs_support',{}).get('value'):
self.senior_area_chairs_name = request_note.content['senior_area_chair_role_name']['value']
self.use_senior_area_chairs = True
self.senior_area_chair_roles = request_note.content.get('senior_area_chair_roles', [self.senior_area_chairs_name])
preferred_email_groups.append(self.get_senior_area_chairs_id())
Expand Down Expand Up @@ -380,6 +380,11 @@ def get_ethics_reviewers_name(self, pretty=True):
def anon_ethics_reviewers_name(self, pretty=True):
return self.get_anon_committee_name(self.ethics_reviewers_name)

def get_senior_area_chairs_name(self, pretty=True):
if pretty:
return self.get_committee_name(self.senior_area_chairs_name, pretty)
return self.senior_area_chairs_name

def get_area_chairs_name(self, pretty=True):
if pretty:
return self.get_committee_name(self.area_chairs_name, pretty)
Expand Down Expand Up @@ -1236,8 +1241,24 @@ def set_assignment_invitations(self, submission_deadline):
"""
invitation_prefix = self.support_user.replace('Support', 'Template')

if self.use_senior_area_chairs:
self.invitation_builder.set_assignment_invitation(committee_id=self.get_senior_area_chairs_id(), cdate=submission_deadline + (60*60*1000*24*7*2))

self.client.post_invitation_edit(
invitations=f'{invitation_prefix}/-/Reviewer_Assignment_Deployment',
signatures=[invitation_prefix],
content={
'venue_id': { 'value': self.venue_id },
'name': { 'value': f'{self.senior_area_chairs_name}_Assignment_Deployment' },
'activation_date': { 'value': submission_deadline + (60*60*1000*24*7*2.1) },
'committee_name': { 'value': self.senior_area_chairs_name },
'committee_pretty_name': { 'value': self.get_senior_area_chairs_name(pretty=True) }
},
await_process=True
)

if self.use_area_chairs:
self.invitation_builder.set_assignment_invitation(committee_id=self.get_area_chairs_id(), cdate=submission_deadline + (60*60*1000*24*7*2))
self.invitation_builder.set_assignment_invitation(committee_id=self.get_area_chairs_id(), cdate=submission_deadline + (60*60*1000*24*7*2.1))

self.client.post_invitation_edit(
invitations=f'{invitation_prefix}/-/Reviewer_Assignment_Deployment',
Expand Down Expand Up @@ -1268,11 +1289,15 @@ def set_assignment_invitations(self, submission_deadline):

def setup_matching_invitations(self):
"""Create matching configuration invitations for reviewers and area chairs (if enabled).

Sets up the matching invitations (affinity scores, conflicts, custom
max papers, etc.) without computing scores. Use
:meth:`setup_committee_matching` to also compute scores and conflicts.
"""

if self.use_senior_area_chairs:
venue_matching = matching.Matching(self, self.client.get_group(self.get_senior_area_chairs_id()), self.get_area_chairs_id())
venue_matching.setup_matching_invitations()

if self.use_area_chairs:
venue_matching = matching.Matching(self, self.client.get_group(self.get_area_chairs_id()))
venue_matching.setup_matching_invitations()
Expand All @@ -1282,10 +1307,14 @@ def setup_matching_invitations(self):

def setup_all_committees_matching(self):
"""Run full matching setup (invitations, affinity scores, conflicts) for all committees.

Sets up matching for area chairs (if enabled) and reviewers, including
computing affinity scores and conflicts with default settings.
"""

if self.use_senior_area_chairs:
venue_matching = matching.Matching(self, self.client.get_group(self.get_senior_area_chairs_id()), self.get_area_chairs_id())
venue_matching.setup()

if self.use_area_chairs:
venue_matching = matching.Matching(self, self.client.get_group(self.get_area_chairs_id()))
venue_matching.setup()
Expand Down
Loading