diff --git a/openreview/arr/arr.py b/openreview/arr/arr.py index aec01a931..344a0d7ff 100644 --- a/openreview/arr/arr.py +++ b/openreview/arr/arr.py @@ -89,6 +89,7 @@ def __init__(self, client, venue_id, support_user, venue=None): self.source_submissions_query_mapping = {} self.sac_paper_assignments = False self.submission_assignment_max_reviewers = None + self.submission_assignment_max_area_chairs = 1 self.comment_notification_threshold = None def copy_to_venue(self): @@ -138,6 +139,7 @@ def copy_to_venue(self): self.venue.source_submissions_query_mapping = self.source_submissions_query_mapping self.venue.sac_paper_assignments = self.sac_paper_assignments self.venue.submission_assignment_max_reviewers = self.submission_assignment_max_reviewers + self.venue.submission_assignment_max_area_chairs = self.submission_assignment_max_area_chairs self.venue.comment_notification_threshold = self.comment_notification_threshold self.submission_stage.hide_fields = self.submission_stage.hide_fields + hide_fields diff --git a/openreview/conference/helpers.py b/openreview/conference/helpers.py index 0c7c3c691..2ad018c06 100644 --- a/openreview/conference/helpers.py +++ b/openreview/conference/helpers.py @@ -141,6 +141,7 @@ def get_conference(client, request_form_id, support_user='OpenReview.net/Support venue.source_submissions_query_mapping = note.content.get('source_submissions_query_mapping', {}) venue.sac_paper_assignments = note.content.get('senior_area_chairs_assignment', 'Area Chairs') == 'Submissions' venue.submission_assignment_max_reviewers = int(note.content.get('submission_assignment_max_reviewers')) if note.content.get('submission_assignment_max_reviewers') is not None else None + venue.submission_assignment_max_area_chairs = int(note.content.get('submission_assignment_max_area_chairs')) if note.content.get('submission_assignment_max_area_chairs') is not None else venue.submission_assignment_max_area_chairs venue.comment_notification_threshold = int(note.content.get('comment_notification_threshold')) if note.content.get('comment_notification_threshold') is not None else None venue.preferred_emails_groups = note.content.get('preferred_emails_groups') if not venue.preferred_emails_groups: diff --git a/openreview/venue/group.py b/openreview/venue/group.py index aa5058f62..9468b001a 100644 --- a/openreview/venue/group.py +++ b/openreview/venue/group.py @@ -247,6 +247,7 @@ def create_venue_group(self): content['area_chairs_conflict_id'] = { 'value': self.venue.get_conflict_score_id(self.venue.get_area_chairs_id()) } content['area_chairs_recruitment_id'] = { 'value': self.venue.get_recruitment_id(self.venue.get_area_chairs_id()) } content['area_chairs_assignment_id'] = { 'value': self.venue.get_assignment_id(self.venue.get_area_chairs_id(), deployed=True) } + content['area_chairs_invite_assignment_id'] = { 'value': self.venue.get_assignment_id(self.venue.get_area_chairs_id(), invite=True) } content['area_chairs_message_id'] = { 'value': self.venue.get_message_id(committee_id=self.venue.get_area_chairs_id()) } content['area_chairs_message_submission_id'] = { 'value': self.venue.get_message_id(committee_id=self.venue.get_area_chairs_id('{number}')) } @@ -346,6 +347,9 @@ def create_venue_group(self): if self.venue.submission_assignment_max_reviewers: content['submission_assignment_max_reviewers'] = { 'value': self.venue.submission_assignment_max_reviewers } + if self.venue.submission_assignment_max_area_chairs: + content['submission_assignment_max_area_chairs'] = { 'value': self.venue.submission_assignment_max_area_chairs } + if self.venue.comment_notification_threshold: content['comment_notification_threshold'] = { 'value': self.venue.comment_notification_threshold } diff --git a/openreview/venue/venue.py b/openreview/venue/venue.py index fde028024..e6b83b504 100644 --- a/openreview/venue/venue.py +++ b/openreview/venue/venue.py @@ -84,6 +84,7 @@ def __init__(self, client, venue_id, support_user): self.source_submissions_query_mapping = {} self.sac_paper_assignments = False self.submission_assignment_max_reviewers = None + self.submission_assignment_max_area_chairs = None self.preferred_emails_groups = [] self.iThenticate_plagiarism_check = False self.iThenticate_plagiarism_check_api_key = '' diff --git a/openreview/venue_request/venue_request.py b/openreview/venue_request/venue_request.py index 99c4e233b..edb2c379e 100644 --- a/openreview/venue_request/venue_request.py +++ b/openreview/venue_request/venue_request.py @@ -57,10 +57,16 @@ def setup_venue_revision(self): } revision_content['submission_assignment_max_reviewers'] = { 'description': 'If set, this limits the number of reviewers that can be invited and directly assigned to a submission after the assignments have been deployed. Default is no limit.', - 'value-regex': '[0-9]*', + 'value-regex': '(^$)|(^[1-9][0-9]*$)', 'order': 35, 'required': False } + revision_content['submission_assignment_max_area_chairs'] = { + 'description': 'If set, this limits the number of area chairs that can be invited and directly assigned to a submission after the assignments have been deployed. Default is no limit.', + 'value-regex': '(^$)|(^[1-9][0-9]*$)', + 'order': 36, + 'required': False + } with open(os.path.join(os.path.dirname(__file__), 'process/revision_pre_process.py')) as pre: pre_process_file_content = pre.read() @@ -1846,14 +1852,20 @@ def setup_request_form(self): 'hidden': True }, 'submission_assignment_max_reviewers': { - 'value-regex': '[0-9]*', + 'value-regex': '(^$)|(^[1-9][0-9]*$)', 'order': 67, 'required': False, 'hidden': True }, + 'submission_assignment_max_area_chairs': { + 'value-regex': '(^$)|(^[1-9][0-9]*$)', + 'order': 68, + 'required': False, + 'hidden': True + }, 'comment_notification_threshold': { 'value-regex': '.*', - 'order': 68, + 'order': 69, 'required': False, 'hidden': True } diff --git a/tests/test_arr_venue_v2.py b/tests/test_arr_venue_v2.py index 790d96d3a..7f5a81250 100644 --- a/tests/test_arr_venue_v2.py +++ b/tests/test_arr_venue_v2.py @@ -241,6 +241,7 @@ def test_august_cycle(self, client, openreview_client, helpers, test_client, req group = openreview_client.get_group('aclweb.org/ACL/ARR/2023/August') assert group assert 'submission_assignment_max_reviewers' not in group.content + assert group.content['submission_assignment_max_area_chairs']['value'] == 1 assert openreview_client.get_group('aclweb.org/ACL/ARR/2023/August/Senior_Area_Chairs') assert openreview_client.get_group('aclweb.org/ACL/ARR/2023/August/Area_Chairs') assert openreview_client.get_group('aclweb.org/ACL/ARR/2023/August/Reviewers') @@ -252,6 +253,7 @@ def test_august_cycle(self, client, openreview_client, helpers, test_client, req assert 'Emergency_Score' in openreview_client.get_group('aclweb.org/ACL/ARR/2023/August/Senior_Area_Chairs').web ac_group = openreview_client.get_group('aclweb.org/ACL/ARR/2023/August/Area_Chairs') assert 'Emergency_Score' in ac_group.web + assert group.content['area_chairs_invite_assignment_id']['value'] == 'aclweb.org/ACL/ARR/2023/August/Area_Chairs/-/Invite_Assignment' openreview_client.post_group_edit( invitation='aclweb.org/ACL/ARR/2023/August/-/Edit', @@ -393,6 +395,9 @@ def test_august_cycle(self, client, openreview_client, helpers, test_client, req assert 'paper_type' in submission_invitation.edit['note']['content'] assert 'keywords' not in submission_invitation.edit['note']['content'] + revision_invitation = client.get_invitation(f'openreview.net/Support/-/Request{request_form_note.number}/Revision') + assert 'submission_assignment_max_area_chairs' in revision_invitation.reply['content'] + domain = openreview_client.get_group('aclweb.org/ACL/ARR/2023/August') assert 'overall_assessment' == domain.content['meta_review_recommendation']['value'] @@ -407,6 +412,7 @@ def test_august_cycle(self, client, openreview_client, helpers, test_client, req assert domain.content['ethics_reviewers_id']['value'] == venue.get_ethics_reviewers_id() assert domain.content['anon_ethics_reviewer_name']['value'] == venue.anon_ethics_reviewers_name() assert domain.content['submission_assignment_max_reviewers']['value'] == 3 + assert domain.content['submission_assignment_max_area_chairs']['value'] == 1 # Verify that ethics reviewers and ethics chairs are in the preferred_emails_groups preferred_emails_groups = domain.content['preferred_emails_groups']['value'] @@ -4191,6 +4197,25 @@ def test_sae_ae_assignments(self, client, openreview_client, helpers, test_clien assignment_invitation = openreview_client.get_invitation('aclweb.org/ACL/ARR/2023/August/Area_Chairs/-/Assignment') assert 'sync_sac_id' not in assignment_invitation.content + with pytest.raises(openreview.OpenReviewException, match=r'Can not make assignment, total assignments and invitations must not exceed 1'): + openreview_client.post_edge(openreview.api.Edge( + invitation = 'aclweb.org/ACL/ARR/2023/August/Area_Chairs/-/Assignment', + head = submissions[1].id, + tail = '~AC_ARROne1', + signatures = ['aclweb.org/ACL/ARR/2023/August/Submission2/Senior_Area_Chairs'], + weight = 1 + )) + + with pytest.raises(openreview.OpenReviewException, match=r'Can not invite assignment, total assignments and invitations must not exceed 1'): + openreview_client.post_edge(openreview.api.Edge( + invitation = 'aclweb.org/ACL/ARR/2023/August/Area_Chairs/-/Invite_Assignment', + head = submissions[1].id, + tail = '~AC_ARROne1', + signatures = ['aclweb.org/ACL/ARR/2023/August/Program_Chairs'], + weight = 0, + label = 'Invitation Sent' + )) + # Remove an AC and replace sac_client = openreview.api.OpenReviewClient(username = 'sac2@aclrollingreview.com', password=helpers.strong_password) assert len(sac_client.get_edges(invitation = 'aclweb.org/ACL/ARR/2023/August/Area_Chairs/-/Assignment', head=submissions[1].id, tail='~AC_ARRTwo1')) == 1