From f9a732d37bbccefb4406dead0704d5b2c8f5e4c0 Mon Sep 17 00:00:00 2001 From: Melisa Bok Date: Thu, 27 Jun 2024 11:54:53 -0400 Subject: [PATCH] ARR: fix several issues (#2212) * enable SAC tab with paper status * customize assignment urls * update description * use above instead of below * enable reviewer assignment in the paper status tab * fix test * support multiple research areas * check ARR instances * check if ddate is instance of int and not delete: true * use arr hide fields in the submission stage * reduce count --- openreview/arr/arr.py | 3 +- .../arr/process/emergency_load_preprocess.py | 2 +- .../arr/process/emergency_load_process.py | 263 ++++++++---------- .../arr/webfield/programChairsWebfield.js | 2 + .../arr/webfield/seniorAreaChairsWebfield.js | 70 ++--- openreview/stages/arr_content.py | 16 +- openreview/tools.py | 24 ++ .../venue/webfield/programChairsWebfield.js | 2 + .../webfield/seniorAreaChairsWebfield.js | 12 + .../venue_request/process/deployProcess.py | 2 +- .../process/postSubmissionProcess.py | 2 +- .../process/recruitmentProcess.py | 2 +- tests/test_arr_venue_v2.py | 79 +++--- 13 files changed, 240 insertions(+), 239 deletions(-) diff --git a/openreview/arr/arr.py b/openreview/arr/arr.py index 86a7e81a9..78651cc22 100644 --- a/openreview/arr/arr.py +++ b/openreview/arr/arr.py @@ -19,6 +19,7 @@ from openreview.arr.helpers import ( setup_arr_invitations ) +from openreview.stages.arr_content import hide_fields SHORT_BUFFER_MIN = 30 LONG_BUFFER_DAYS = 10 @@ -133,6 +134,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.submission_stage.hide_fields = self.submission_stage.hide_fields + hide_fields self.venue.submission_stage = self.submission_stage self.venue.review_stage = self.review_stage self.venue.bid_stages = self.bid_stages @@ -423,7 +425,6 @@ def recruit_reviewers(self, # For stage invitations, pass value to inner venue objects def create_submission_stage(self): - self.venue.submission_stage = self.submission_stage stage_value = self.venue.create_submission_stage() invitation = self.client.get_invitation(self.get_submission_id()) invitation.preprocess = self.invitation_builder.get_process_content('process/submission_preprocess.py') diff --git a/openreview/arr/process/emergency_load_preprocess.py b/openreview/arr/process/emergency_load_preprocess.py index 614219f61..81c3d0950 100644 --- a/openreview/arr/process/emergency_load_preprocess.py +++ b/openreview/arr/process/emergency_load_preprocess.py @@ -7,4 +7,4 @@ def process(client, edit, invitation): if not emergency_load: raise openreview.OpenReviewException('You have agreed to emergency reviewing, please enter the additional load that you want to be assigned.') if not research_area: - raise openreview.OpenReviewException('You have agreed to emergency reviewing, please enter your closest relevant research area.') \ No newline at end of file + raise openreview.OpenReviewException('You have agreed to emergency reviewing, please enter your closest relevant research areas.') \ No newline at end of file diff --git a/openreview/arr/process/emergency_load_process.py b/openreview/arr/process/emergency_load_process.py index b00819edd..805c2a5c0 100644 --- a/openreview/arr/process/emergency_load_process.py +++ b/openreview/arr/process/emergency_load_process.py @@ -1,48 +1,51 @@ def process(client, edit, invitation): - import time - - if "No" in edit.note.content['emergency_reviewing_agreement']: - return domain = client.get_group(invitation.domain) venue_id = domain.id - submission_venue_id = domain.content['submission_venue_id']['value'] - meta_invitation_id = domain.content['meta_invitation_id']['value'] - venue_name = domain.content['title']['value'] - - CONFERENCE_ID = domain.id - SAC_ID = domain.content['senior_area_chairs_id']['value'] - AC_ID = domain.content['area_chairs_id']['value'] - REV_ID = domain.content['reviewers_id']['value'] user = client.get_profile(edit.signatures[0]).id + role = invitation.id.split('/-/')[0] + + print('Clear existing emergency edges...') + + client.delete_edges( + invitation=f"{role}/-/Emergency_Load", + head=role, + tail=user, + wait_to_finish=True, + soft_delete=True + ) + client.delete_edges( + invitation=f"{role}/-/Emergency_Area", + head=role, + tail=user, + wait_to_finish=True, + soft_delete=True + ) + + registered_loads = client.get_edges(invitation=f"{role}/-/Registered_Load", tail=user) + + client.delete_edges( + invitation=f"{role}/-/Registered_Load", + head=role, + tail=user, + wait_to_finish=True, + soft_delete=True + ) + client.delete_edges( + invitation=f"{role}/-/Emergency_Score", + tail=user, + wait_to_finish=True, + soft_delete=True + ) + + if isinstance(edit.note.ddate, int) or "No" in edit.note.content['emergency_reviewing_agreement']: + + if not registered_loads: + print('No registered load, nothing to do') + return + + print('Emergency agreement was deleted, restore original load') - edge_readers = [CONFERENCE_ID] - inv_role = invitation.id.split('/')[-3] - role = None - for venue_role in [SAC_ID, AC_ID, REV_ID]: - if f"/{inv_role}" in venue_role: - role = venue_role - if venue_role == AC_ID: - edge_readers += [SAC_ID] - elif venue_role == REV_ID: - edge_readers += [SAC_ID, AC_ID] - - if not role: - raise openreview.OpenReviewException('Invalid role for emergency edges') - edge_readers += [user] - - deployed_label = [ - note for note in client.get_all_notes(invitation=f"{role}/-/Assignment_Configuration") if 'Deploy' in note.content['status']['value'] - ][0].content['title']['value'] - aggregate_score_edges = client.get_all_edges(invitation=f"{role}/-/Aggregate_Score", label=deployed_label, tail=user) - emergency_score_edges = client.get_all_edges(invitation=f"{role}/-/Emergency_Score", label=deployed_label, tail=user) - - if edit.note.ddate: - reg_edges = client.get_all_edges(invitation=f"{role}/-/Registered_Load", tail=user) - if len(reg_edges) <= 0: - repost_load = 0 - else: - repost_load = reg_edges[0].weight client.delete_edges( invitation=f"{role}/-/Custom_Max_Papers", head=role, @@ -50,130 +53,100 @@ def process(client, edit, invitation): wait_to_finish=True, soft_delete=True ) - client.delete_edges( - invitation=f"{role}/-/Emergency_Load", - head=role, - tail=user, - wait_to_finish=True, - soft_delete=True - ) - client.delete_edges( - invitation=f"{role}/-/Emergency_Area", - head=role, - tail=user, - wait_to_finish=True, - soft_delete=True - ) - client.delete_edges( - invitation=f"{role}/-/Registered_Load", - head=role, - tail=user, - wait_to_finish=True, - soft_delete=True - ) - client.delete_edges( - invitation=f"{role}/-/Emergency_Score", - tail=user, - wait_to_finish=True, - soft_delete=True - ) client.post_edge( openreview.api.Edge( invitation=f"{role}/-/Custom_Max_Papers", - readers=edge_readers, - writers=[CONFERENCE_ID], - signatures=[CONFERENCE_ID], + signatures=[venue_id], head=role, tail=user, - weight=repost_load + weight=registered_loads[0].weight if registered_loads else 0 ) ) return - cmp_edges = client.get_all_edges(invitation=f"{role}/-/Custom_Max_Papers", tail=user) - if len(cmp_edges) <= 0: - original_load = 0 - else: - original_load = cmp_edges[0].weight - emergency_load = edit.note.content.get('emergency_load', {}).get('value', 0) + print('Emergency agreement was accepted, update emergency loads') - edge_invitation_ids = [ - f"{role}/-/Custom_Max_Papers", - f"{role}/-/Registered_Load", - f"{role}/-/Emergency_Load", - f"{role}/-/Emergency_Area", - ] - - edge_values = [ - { - 'head': role, - 'tail': user, - 'weight': emergency_load + original_load - }, - { - 'head': role, - 'tail': user, - 'weight': original_load - }, - { - 'head': role, - 'tail': user, - 'weight': emergency_load - } - ] - area_value = None - if 'research_area' in edit.note.content: - area_value = { - 'head': role, - 'tail': user, - 'label': edit.note.content.get('research_area', {}).get('value', 'N/A') - } - edge_values.append(area_value) + original_loads = registered_loads if registered_loads else client.get_edges(invitation=f"{role}/-/Custom_Max_Papers", tail=user) + original_load = original_loads[0].weight if original_loads else 0 + emergency_load = edit.note.content.get('emergency_load', {}).get('value', 0) - for inv_id, edge_val in zip(edge_invitation_ids, edge_values): - if edge_val is None: - continue + client.delete_edges( + invitation=f"{role}/-/Custom_Max_Papers", + head=role, + tail=user, + wait_to_finish=True, + soft_delete=True + ) - client.delete_edges( - invitation=inv_id, - head=edge_val['head'], - tail=edge_val['tail'], - wait_to_finish=True, - soft_delete=True + client.post_edge( + openreview.api.Edge( + invitation=f"{role}/-/Custom_Max_Papers", + signatures=[venue_id], + head=role, + tail=user, + weight=emergency_load + original_load ) + ) - client.post_edge( - openreview.api.Edge( - invitation=inv_id, - readers=edge_readers, - writers=[CONFERENCE_ID], - signatures=[CONFERENCE_ID], - **edge_val - ) + client.post_edge( + openreview.api.Edge( + invitation=f"{role}/-/Registered_Load", + signatures=[venue_id], + head=role, + tail=user, + weight=original_load ) + ) - edges_to_post = [] - for edge in aggregate_score_edges: - edges_to_post.append(openreview.api.Edge( - invitation=f"{role}/-/Emergency_Score", - readers=edge.readers, - writers=edge.writers, - nonreaders=edge.nonreaders, - signatures=edge.signatures, - head=edge.head, - tail=edge.tail, - weight=edge.weight - )) + client.post_edge( + openreview.api.Edge( + invitation=f"{role}/-/Emergency_Load", + signatures=[venue_id], + head=role, + tail=user, + weight=emergency_load + ) + ) - client.delete_edges( - invitation=f"{role}/-/Emergency_Score", - label=deployed_label, - tail=user, - wait_to_finish=True, - soft_delete=True - ) - openreview.tools.post_bulk_edges(client, edges_to_post) + if 'research_area' in edit.note.content: + areas = edit.note.content['research_area']['value'] + print('Update emergency areas', areas) + for area in areas: + client.post_edge( + openreview.api.Edge( + invitation=f"{role}/-/Emergency_Area", + signatures=[venue_id], + head=role, + tail=user, + label=area + ) + ) + + emergency_score_edges = client.get_all_edges(invitation=f"{role}/-/Emergency_Score", tail=user) + + ## Post emergency score edges if they don't exist + if len(emergency_score_edges) == 0: + print('Create emergency score edges...') + deployed_label = [ + note for note in client.get_all_notes(invitation=f"{role}/-/Assignment_Configuration") if 'Deployed' in note.content['status']['value'] + ][0].content['title']['value'] + aggregate_score_edges = client.get_all_edges(invitation=f"{role}/-/Aggregate_Score", label=deployed_label, tail=user) + + edges_to_post = [] + for edge in aggregate_score_edges: + edges_to_post.append(openreview.api.Edge( + invitation=f"{role}/-/Emergency_Score", + readers=edge.readers, + writers=edge.writers, + nonreaders=edge.nonreaders, + signatures=edge.signatures, + head=edge.head, + tail=edge.tail, + weight=edge.weight + )) + + openreview.tools.post_bulk_edges(client, edges_to_post) ## Delete availability, assume they can review more than resubmissions client.delete_edges( diff --git a/openreview/arr/webfield/programChairsWebfield.js b/openreview/arr/webfield/programChairsWebfield.js index ac47bfe8e..c31a4939a 100644 --- a/openreview/arr/webfield/programChairsWebfield.js +++ b/openreview/arr/webfield/programChairsWebfield.js @@ -87,6 +87,8 @@ return { messageSubmissionReviewersInvitationId: domain.content.reviewers_message_submission_id?.value, messageAreaChairsInvitationId: domain.content.area_chairs_message_id?.value, messageReviewersInvitationId: domain.content.reviewers_message_id?.value, + messageSeniorAreaChairsInvitationId: domain.content.meta_invitation_id?.value, + sacDirectPaperAssignment: domain.content.sac_paper_assignments?.value, submissionVenueId: domain.content.submission_venue_id?.value, withdrawnVenueId: domain.content.withdrawn_venue_id?.value, deskRejectedVenueId: domain.content.desk_rejected_venue_id?.value, diff --git a/openreview/arr/webfield/seniorAreaChairsWebfield.js b/openreview/arr/webfield/seniorAreaChairsWebfield.js index eec19e00f..2b5c56bf8 100644 --- a/openreview/arr/webfield/seniorAreaChairsWebfield.js +++ b/openreview/arr/webfield/seniorAreaChairsWebfield.js @@ -1,51 +1,38 @@ // Webfield component const committee_name = entity.id.split('/').slice(-1)[0] -const committee_ac_name = committee_name.replace(domain.content.senior_area_chairs_name?.value, domain.content.area_chairs_name?.value) -const committee_reviewer_name = committee_ac_name.replace(domain.content.area_chairs_name?.value, domain.content.reviewers_name?.value) -const replaceAreaChairName = (invitationId) => invitationId.replace(domain.content.area_chairs_name?.value, committee_ac_name) -const replaceReviewerName = (invitationId) => invitationId.replace(domain.content.reviewers_name?.value, committee_reviewer_name) -const startParam = `${replaceAreaChairName(domain.content.area_chairs_assignment_id?.value)},tail:{ac.profile.id}` -const traverseParam = `${replaceReviewerName(domain.content.reviewers_assignment_id?.value)}` -const editParam = `${replaceReviewerName(domain.content.reviewers_invite_assignment_id?.value)}` -const browseInvitations = [] const assignmentInvitation = domain.content.sac_paper_assignments?.value ? null : domain.content.senior_area_chairs_assignment_id?.value -if (domain.content.reviewers_affinity_score_id?.value) { - browseInvitations.push(replaceReviewerName(domain.content.reviewers_affinity_score_id?.value)) -} -if (domain.content.bid_name?.value) { - browseInvitations.push(replaceReviewerName(`${domain.content.reviewers_id?.value}/-/${domain.content.bid_name?.value}`)) -} -if (domain.content.reviewers_custom_max_papers_id?.value) { - browseInvitations.push(replaceReviewerName(`${domain.content.reviewers_custom_max_papers_id?.value},head:ignore`)) -} - -const otherParams = `&hide=${replaceReviewerName(domain.content.reviewers_conflict_id?.value)}&maxColumns=2&version=2&referrer=[Senior Area Chair Console](/group?id=${entity.id})` - -const start = `${domain.content.senior_area_chairs_assignment_id?.value},tail:${user.profile.id}` -const traverse = `${domain.content.area_chairs_assignment_id?.value}` -const edit = `${domain.content.area_chairs_assignment_id?.value};${domain.content.area_chairs_assignment_id?.value.replace('Assignment', 'Invite_Assignment')}` -const browse = [ - `${domain.content.area_chairs_id?.value}/-/Agreggate_Score`, - domain.content.area_chairs_affinity_score_id?.value, - `${domain.content.area_chairs_id?.value}/-/Emergency_Score`, - `${domain.content.area_chairs_id?.value}/-/Research_Area`, - `${domain.content.area_chairs_custom_max_papers_id?.value},head:ignore`, - `${domain.content.area_chairs_id?.value}/-/Registered_Load,head:ignore`, - `${domain.content.area_chairs_id?.value}/-/Emergency_Load,head:ignore`, - `${domain.content.area_chairs_id?.value}/-/Emergency,head:ignore`, - `${domain.content.area_chairs_id?.value}/-/Emergency_Area,head:ignore`, - -] -const other = `hide=${domain.content.area_chairs_conflict_id?.value}&maxColumns=2&version=2&referrer=[Senior Area Chair Console](/group?id=${entity.id})` - -const edgeBrowserLink = `/edges/browse?start=${start}&traverse=${traverse}&edit=${edit}&browse=${browse.join(';')}&${other}` - const assignmentUrls = {} const reviewersId = domain.content.reviewers_id?.value const areaChairsId = domain.content.area_chairs_id?.value const automaticAssignment = domain.content.automatic_reviewer_assignment?.value +const browseAreaChairInvitations = [ + `${areaChairsId}/-/Agreggate_Score`, + domain.content.area_chairs_affinity_score_id?.value, + `${areaChairsId}/-/Emergency_Score`, + `${areaChairsId}/-/Research_Area`, +].join(';') + +const headBrowseAreaChairInvitations = [ + domain.content.area_chairs_custom_max_papers_id?.value, + `${areaChairsId}/-/Registered_Load`, + `${areaChairsId}/-/Emergency_Load`, + `${areaChairsId}/-/Emergency`, + `${areaChairsId}/-/Emergency_Area`, +].map(invitationId => `${invitationId},head:ignore`).join(';') + +const allBrowseAreaChairInvitations = [ + browseAreaChairInvitations, + headBrowseAreaChairInvitations, +].join(';') + +const manualAreaChairAssignmentUrl = `/edges/browse?traverse=${domain.content.area_chairs_assignment_id?.value}&edit=${domain.content.area_chairs_assignment_id?.value};${domain.content.area_chairs_assignment_id?.value.replace('Assignment', 'Invite_Assignment')}&browse=${allBrowseAreaChairInvitations}&version=2` +assignmentUrls[domain.content.area_chairs_name?.value] = { + manualAssignmentUrl: manualAreaChairAssignmentUrl, + automaticAssignment: automaticAssignment +} + const browseReviewerInvitations = [ domain.content.reviewers_affinity_score_id?.value, domain.content.reviewers_conflict_id?.value, @@ -80,7 +67,7 @@ return { properties: { header: { title: `${committee_name.replaceAll('_', ' ')} Console`, - instructions: `

This page provides information and status updates for the ${domain.content.subtitle?.value}. It will be regularly updated as the conference progresses, so please check back frequently.


AE Assignment Browser: Modify AE Assignments` + instructions: `

This page provides information and status updates for the ${domain.content.subtitle?.value}. It will be regularly updated as the conference progresses, so please check back frequently.


` }, venueId: domain.id, assignmentInvitation: assignmentInvitation, @@ -92,11 +79,13 @@ return { deskRejectedVenueId: domain.content.desk_rejected_venue_id?.value, submissionName: domain.content.submission_name?.value, reviewerName: domain.content.reviewers_name?.value, + reviewerAssignmentId: domain.content.reviewers_assignment_id?.value, assignmentUrls: assignmentUrls, reviewersId: reviewersId, anonReviewerName: domain.content.reviewers_anon_name?.value, areaChairName: domain.content.area_chairs_name?.value, areaChairsId: areaChairsId, + areaChairAssignmentId: domain.content.area_chairs_assignment_id?.value, anonAreaChairName: domain.content.area_chairs_anon_name?.value, secondaryAreaChairName: domain.content.secondary_area_chairs_name?.value, secondaryAnonAreaChairName: domain.content.secondary_area_chairs_anon_name?.value, @@ -110,7 +99,6 @@ return { shortPhrase: domain.content.subtitle?.value, enableQuerySearch: true, emailReplyTo: domain.content.contact?.value, - edgeBrowserDeployedUrl: `/edges/browse?start=${startParam}&traverse=${traverseParam}&edit=${editParam}&browse=${browseInvitations.join(';')}${otherParams}`, filterFunction: entity.content?.track?.value && `return note.content?.track?.value==="${entity.content?.track?.value}"`, propertiesAllowed: { reviewerChecklistCount: ` diff --git a/openreview/stages/arr_content.py b/openreview/stages/arr_content.py index 473b63963..994e66f9b 100644 --- a/openreview/stages/arr_content.py +++ b/openreview/stages/arr_content.py @@ -1296,7 +1296,7 @@ } }, "order": 12, - "description": "Should this paper be sent for an in-depth ethics review? We have a small ethics committee that can specially review very challenging papers when it comes to ethical issues. If this seems to be such a paper, then please explain why here, and we will try to ensure that it receives a separate review." + "description": "Should this paper be sent for an in-depth ethics review? Before you answer this question, please refer to https://aclrollingreview.org/ethics-flagging-guidelines/ for guidelines on what papers should and shouldn't be flagged. If your answer is yes, then ensure you have explained why in the question above, and we will try to ensure that it receives a separate ethics review." }, "reproducibility": { "value": { @@ -1650,7 +1650,7 @@ } }, "order": 9, - "description": "Should this paper be sent for an in-depth ethics review? We have a small ethics committee that can specially review very challenging papers when it comes to ethical issues. If this seems to be such a paper, then please explain why here, and we will try to ensure that it receives a separate review." + "description": "Should this paper be sent for an in-depth ethics review? Before you answer this question, please refer to https://aclrollingreview.org/ethics-flagging-guidelines/ for guidelines on what papers should and shouldn't be flagged. If your answer is yes, then ensure you have explained why in the question above, and we will try to ensure that it receives a separate ethics review." }, "author_identity_guess": { "value": { @@ -1941,13 +1941,13 @@ "research_area": { "value": { "param": { - "input": "radio", + "input": "checkbox", "enum": arr_tracks, "optional": True, - "type": "string" + "type": "string[]" } }, - "description": "Research Areas / Tracks. Select the most relevant research area / track for your expertise", + "description": "Research Areas / Tracks. Select the most relevant research areas / tracks for your expertise", "order": 3 } } @@ -1986,13 +1986,13 @@ "research_area": { "value": { "param": { - "input": "radio", + "input": "checkbox", "enum": arr_tracks, "optional": True, - "type": "string" + "type": "string[]" } }, - "description": "Research Areas / Tracks. Select the most relevant research area / track for your expertise", + "description": "Research Areas / Tracks. Select the most relevant research areas / tracks for your expertise", "order": 3 } } diff --git a/openreview/tools.py b/openreview/tools.py index 19a114ae3..c9d91bf03 100644 --- a/openreview/tools.py +++ b/openreview/tools.py @@ -287,6 +287,30 @@ def get_group(client, id): raise e return group +def get_note(client, id): + """ + Get a single Note by id if available + + :param client: User that will retrieve the note + :type client: Client + :param id: id of the note + :type id: str + + :return: Note that matches the passed id + :rtype: Note + """ + note = None + try: + note = client.get_note(id = id) + except openreview.OpenReviewException as e: + # throw an error if it is something other than "not found" + error = e.args[0] + if error.get('name') == 'NotFoundError' or error.get('message').startswith('Note Not Found'): + return None + else: + raise e + return note + def get_invitation(client, id): """ Get a single Invitation by id if available diff --git a/openreview/venue/webfield/programChairsWebfield.js b/openreview/venue/webfield/programChairsWebfield.js index 69a6a3ef4..1b92d1c7f 100644 --- a/openreview/venue/webfield/programChairsWebfield.js +++ b/openreview/venue/webfield/programChairsWebfield.js @@ -39,6 +39,8 @@ return { messageSubmissionReviewersInvitationId: domain.content.reviewers_message_submission_id?.value, messageAreaChairsInvitationId: domain.content.area_chairs_message_id?.value, messageReviewersInvitationId: domain.content.reviewers_message_id?.value, + messageSeniorAreaChairsInvitationId: domain.content.meta_invitation_id?.value, + sacDirectPaperAssignment: domain.content.sac_paper_assignments?.value, submissionVenueId: domain.content.submission_venue_id?.value, withdrawnVenueId: domain.content.withdrawn_venue_id?.value, deskRejectedVenueId: domain.content.desk_rejected_venue_id?.value, diff --git a/openreview/venue/webfield/seniorAreaChairsWebfield.js b/openreview/venue/webfield/seniorAreaChairsWebfield.js index ee1b135e6..8e0ea0168 100644 --- a/openreview/venue/webfield/seniorAreaChairsWebfield.js +++ b/openreview/venue/webfield/seniorAreaChairsWebfield.js @@ -9,6 +9,8 @@ const traverseParam = `${replaceReviewerName(domain.content.reviewers_assignment const editParam = `${replaceReviewerName(domain.content.reviewers_invite_assignment_id?.value)}` const browseInvitations = [] const assignmentInvitation = domain.content.sac_paper_assignments?.value ? null : domain.content.senior_area_chairs_assignment_id?.value +const automaticAssignment = domain.content.automatic_reviewer_assignment?.value +const assignmentUrls = {} if (domain.content.reviewers_affinity_score_id?.value) { browseInvitations.push(replaceReviewerName(domain.content.reviewers_affinity_score_id?.value)) @@ -22,6 +24,12 @@ if (domain.content.reviewers_custom_max_papers_id?.value) { const otherParams = `&hide=${replaceReviewerName(domain.content.reviewers_conflict_id?.value)}&maxColumns=2&version=2&referrer=[Senior Area Chair Console](/group?id=${entity.id})` +const manualReviewerAssignmentUrl = `/edges/browse?traverse=${traverseParam}&edit=${editParam}&browse=${browseInvitations.join(';')}${otherParams}` +assignmentUrls[domain.content.reviewers_name?.value] = { + manualAssignmentUrl: manualReviewerAssignmentUrl, + automaticAssignment: automaticAssignment +} + return { component: 'SeniorAreaChairConsole', version: 1, @@ -40,8 +48,12 @@ return { deskRejectedVenueId: domain.content.desk_rejected_venue_id?.value, submissionName: domain.content.submission_name?.value, reviewerName: domain.content.reviewers_name?.value, + reviewersId: domain.content.reviewers_id?.value, + reviewerAssignmentId: domain.content.reviewers_assignment_id?.value, + assignmentUrls: assignmentUrls, anonReviewerName: domain.content.reviewers_anon_name?.value, areaChairName: domain.content.area_chairs_name?.value, + areaChairsId: domain.content.area_chairs_id?.value, anonAreaChairName: domain.content.area_chairs_anon_name?.value, secondaryAreaChairName: domain.content.secondary_area_chairs_name?.value, secondaryAnonAreaChairName: domain.content.secondary_area_chairs_anon_name?.value, diff --git a/openreview/venue_request/process/deployProcess.py b/openreview/venue_request/process/deployProcess.py index 0dd47479e..983915d7c 100644 --- a/openreview/venue_request/process/deployProcess.py +++ b/openreview/venue_request/process/deployProcess.py @@ -302,7 +302,7 @@ def process(client, note, invitation): signatures = ['~Super_User1'] ##Temporarily use the super user, until we can get a way to send email to invitees ) - if isinstance(conference, openreview.venue.Venue): + if isinstance(conference, openreview.venue.Venue) or isinstance(conference, openreview.arr.ARR): recruitment_invitation.preprocess = recruitment_pre_preprocess remind_recruitment_invitation.preprocess = recruitment_pre_preprocess diff --git a/openreview/venue_request/process/postSubmissionProcess.py b/openreview/venue_request/process/postSubmissionProcess.py index 4151c7e4e..11b2ca333 100644 --- a/openreview/venue_request/process/postSubmissionProcess.py +++ b/openreview/venue_request/process/postSubmissionProcess.py @@ -9,7 +9,7 @@ def process(client, note, invitation): try: conference.setup_post_submission_stage(force=note.content['force'] == 'Yes', hide_fields=note.content.get('hide_fields', [])) - if isinstance(conference, openreview.venue.Venue): + if isinstance(conference, openreview.venue.Venue) or isinstance(conference, openreview.arr.ARR): conference.create_post_submission_stage() print('Conference: ', conference.get_id()) diff --git a/openreview/venue_request/process/recruitmentProcess.py b/openreview/venue_request/process/recruitmentProcess.py index a8648b059..396d19b19 100644 --- a/openreview/venue_request/process/recruitmentProcess.py +++ b/openreview/venue_request/process/recruitmentProcess.py @@ -53,7 +53,7 @@ def process(client, note, invitation): raise openreview.OpenReviewException(f'Unable to retrieve field contact_email from the request form') # Set allow_accept_with_reduced_load for api2 venues only - if isinstance(conference, openreview.venue.Venue): + if isinstance(conference, openreview.venue.Venue) or isinstance(conference, openreview.arr.ARR): recruitment_status=conference.recruit_reviewers( invitees = invitee_emails, invitee_names = invitee_names, diff --git a/tests/test_arr_venue_v2.py b/tests/test_arr_venue_v2.py index edc848445..180ab670d 100644 --- a/tests/test_arr_venue_v2.py +++ b/tests/test_arr_venue_v2.py @@ -155,6 +155,18 @@ def test_august_cycle(self, client, openreview_client, helpers, test_client, pro assert openreview_client.get_invitation('aclweb.org/ACL/ARR/2023/August/Reviewers/-/Expertise_Selection') + post_submission_invitation = openreview_client.get_invitation('aclweb.org/ACL/ARR/2023/August/-/Post_Submission') + assert 'TLDR' in post_submission_invitation.edit['note']['content'] + assert 'preprint' in post_submission_invitation.edit['note']['content'] + assert 'existing_preprints' in post_submission_invitation.edit['note']['content'] + assert 'consent_to_share_data' in post_submission_invitation.edit['note']['content'] + assert 'consent_to_share_submission_details' in post_submission_invitation.edit['note']['content'] + assert 'Association_for_Computational_Linguistics_-_Blind_Submission_License_Agreement' in post_submission_invitation.edit['note']['content'] + assert 'section_2_permission_to_publish_peer_reviewers_content_agreement' in post_submission_invitation.edit['note']['content'] + assert 'reviewing_volunteers' in post_submission_invitation.edit['note']['content'] + assert 'reviewing_no_volunteers_reason' in post_submission_invitation.edit['note']['content'] + assert 'preprint_status' in post_submission_invitation.edit['note']['content'] + request_page(selenium, 'http://localhost:3030/group?id=aclweb.org/ACL/ARR/2023/August', pc_client.token, wait_for_element='header') header_div = selenium.find_element(By.ID, 'header') assert header_div @@ -227,23 +239,17 @@ def test_august_cycle(self, client, openreview_client, helpers, test_client, pro )) helpers.await_queue_edit(client, invitation=f'openreview.net/Support/-/Request{request_form_note.number}/Revision') - # hide pdf - post_submission_note=pc_client.post_note(openreview.Note( - content= { - 'force': 'Yes', - 'hide_fields': hide_fields, - 'submission_readers': 'Assigned program committee (assigned reviewers, assigned area chairs, assigned senior area chairs if applicable)' - }, - forum=request_form_note.id, - invitation=f'openreview.net/Support/-/Request{request_form_note.number}/Post_Submission', - readers= ['aclweb.org/ACL/ARR/2023/August/Program_Chairs', 'openreview.net/Support'], - referent=request_form_note.id, - replyto=request_form_note.id, - signatures= ['~Program_ARRChair1'], - writers= [], - )) - - helpers.await_queue() + post_submission_invitation = openreview_client.get_invitation('aclweb.org/ACL/ARR/2023/August/-/Post_Submission') + assert 'TLDR' in post_submission_invitation.edit['note']['content'] + assert 'preprint' in post_submission_invitation.edit['note']['content'] + assert 'existing_preprints' in post_submission_invitation.edit['note']['content'] + assert 'consent_to_share_data' in post_submission_invitation.edit['note']['content'] + assert 'consent_to_share_submission_details' in post_submission_invitation.edit['note']['content'] + assert 'Association_for_Computational_Linguistics_-_Blind_Submission_License_Agreement' in post_submission_invitation.edit['note']['content'] + assert 'section_2_permission_to_publish_peer_reviewers_content_agreement' in post_submission_invitation.edit['note']['content'] + assert 'reviewing_volunteers' in post_submission_invitation.edit['note']['content'] + assert 'reviewing_no_volunteers_reason' in post_submission_invitation.edit['note']['content'] + assert 'preprint_status' in post_submission_invitation.edit['note']['content'] request_page(selenium, 'http://localhost:3030/group?id=aclweb.org/ACL/ARR/2023/August', pc_client.token, wait_for_element='header') header_div = selenium.find_element(By.ID, 'header') @@ -2043,7 +2049,7 @@ def test_post_submission(self, client, openreview_client, helpers, test_client, )) helpers.await_queue() - helpers.await_queue_edit(openreview_client, 'aclweb.org/ACL/ARR/2023/August/-/Post_Submission-0-1', count=3) + helpers.await_queue_edit(openreview_client, 'aclweb.org/ACL/ARR/2023/August/-/Post_Submission-0-1', count=2) pc_client_v2=openreview.api.OpenReviewClient(username='pc@aclrollingreview.org', password=helpers.strong_password) assert len(pc_client_v2.get_all_invitations(invitation='aclweb.org/ACL/ARR/2023/August/-/Withdrawal')) == 101 @@ -2953,18 +2959,6 @@ def test_sae_ae_assignments(self, client, openreview_client, helpers, test_clien sac3_group = openreview_client.get_group('aclweb.org/ACL/ARR/2023/August/Submission3/Senior_Area_Chairs') assert sac3_group.members == ['~SAC_ARRTwo1'] - sac_client = openreview.api.OpenReviewClient(username='sac2@aclrollingreview.com', password=helpers.strong_password) - request_page(selenium, "http://localhost:3030/group?id=aclweb.org/ACL/ARR/2023/August/Senior_Area_Chairs", sac_client.token, wait_for_element='header') - header_div = selenium.find_element(By.ID, 'header') - assert header_div - description = header_div.find_element(By.CLASS_NAME, 'description') - assert 'AE Assignment Browser:' in description.text - url = header_div.find_element(By.ID, 'edge_browser_url') - assert url - - edge_browser_url = 'http://localhost:3030/edges/browse?start=aclweb.org/ACL/ARR/2023/August/Senior_Area_Chairs/-/Assignment,tail:~SAC_ARRTwo1&traverse=aclweb.org/ACL/ARR/2023/August/Area_Chairs/-/Assignment&edit=aclweb.org/ACL/ARR/2023/August/Area_Chairs/-/Assignment;aclweb.org/ACL/ARR/2023/August/Area_Chairs/-/Invite_Assignment&browse=aclweb.org/ACL/ARR/2023/August/Area_Chairs/-/Agreggate_Score;aclweb.org/ACL/ARR/2023/August/Area_Chairs/-/Affinity_Score;aclweb.org/ACL/ARR/2023/August/Area_Chairs/-/Emergency_Score;aclweb.org/ACL/ARR/2023/August/Area_Chairs/-/Research_Area;aclweb.org/ACL/ARR/2023/August/Area_Chairs/-/Custom_Max_Papers,head:ignore;aclweb.org/ACL/ARR/2023/August/Area_Chairs/-/Registered_Load,head:ignore;aclweb.org/ACL/ARR/2023/August/Area_Chairs/-/Emergency_Load,head:ignore;aclweb.org/ACL/ARR/2023/August/Area_Chairs/-/Emergency,head:ignore;aclweb.org/ACL/ARR/2023/August/Area_Chairs/-/Emergency_Area,head:ignore&hide=aclweb.org/ACL/ARR/2023/August/Area_Chairs/-/Conflict&maxColumns=2&version=2&referrer=[Senior%20Area%20Chair%20Console](/group?id=aclweb.org/ACL/ARR/2023/August/Senior_Area_Chairs)' - assert url.get_attribute('href') == edge_browser_url - openreview_client.post_edge(openreview.api.Edge( invitation = 'aclweb.org/ACL/ARR/2023/August/Area_Chairs/-/Proposed_Assignment', head = submissions[1].id, @@ -4063,11 +4057,11 @@ def test_emergency_reviewing_forms(self, client, openreview_client, helpers): note=openreview.api.Note( content = { 'emergency_reviewing_agreement': { 'value': 'Yes' }, - 'research_area': { 'value': 'Generation' } + 'research_area': { 'value': ['Generation'] } } ) ) - with pytest.raises(openreview.OpenReviewException, match=r'You have agreed to emergency reviewing, please enter your closest relevant research area.'): + with pytest.raises(openreview.OpenReviewException, match=r'You have agreed to emergency reviewing, please enter your closest relevant research areas.'): user_note_edit = user_client.post_note_edit( invitation=f'{role}/-/{inv_name}', signatures=[signature], @@ -4087,7 +4081,7 @@ def test_emergency_reviewing_forms(self, client, openreview_client, helpers): content = { 'emergency_reviewing_agreement': { 'value': 'Yes' }, 'emergency_load': { 'value': 2 }, - 'research_area': { 'value': 'Generation' } + 'research_area': { 'value': ['Generation'] } } ) ) @@ -4106,12 +4100,14 @@ def test_emergency_reviewing_forms(self, client, openreview_client, helpers): if 'Reviewer' in user: assert cmp_edges[user][0] == 6 assert cmp_original == reg_original + emg_original + assert len(area_edges[user]) == 1 assert area_edges[user][0] == 'Generation' aggregate_score_edges = {o['id']['tail']: [j['weight'] for j in o['values']] for o in pc_client_v2.get_grouped_edges(invitation=f"{role}/-/Aggregate_Score", groupby='tail', select='weight')} score_edges = {o['id']['tail']: [j['weight'] for j in o['values']] for o in pc_client_v2.get_grouped_edges(invitation=f"{role}/-/Emergency_Score", groupby='tail', select='weight')} assert all(weight < 10 for weight in score_edges[user]) assert all(weight < 10 for weight in aggregate_score_edges[user]) + assert len(score_edges[user]) == 101 # Test editing note user_note_edit = user_client.post_note_edit( @@ -4121,8 +4117,8 @@ def test_emergency_reviewing_forms(self, client, openreview_client, helpers): id=user_note_edit['note']['id'], content = { 'emergency_reviewing_agreement': { 'value': 'Yes' }, - 'emergency_load': { 'value': 4 }, - 'research_area': { 'value': 'Machine Translation' } + 'emergency_load': { 'value': 6 }, + 'research_area': { 'value': ['Generation', 'Machine Translation'] } } ) ) @@ -4135,19 +4131,22 @@ def test_emergency_reviewing_forms(self, client, openreview_client, helpers): area_edges = {o['id']['tail']: [j['label'] for j in o['values']] for o in pc_client_v2.get_grouped_edges(invitation=f"{role}/-/Emergency_Area", groupby='tail', select='label')} assert all(user in edges for edges in [cmp_edges, reg_edges, emg_edges, area_edges]) - assert all(len(edges[user]) == 1 for edges in [cmp_edges, reg_edges, emg_edges, area_edges]) + assert all(len(edges[user]) == 1 for edges in [cmp_edges, reg_edges, emg_edges]) if 'Reviewer' in user: assert cmp_edges[user][0] == 10 assert cmp_edges[user][0] != cmp_original - assert reg_edges[user][0] != reg_original + assert reg_edges[user][0] == reg_original assert emg_edges[user][0] != emg_original assert cmp_edges[user][0] == reg_edges[user][0] + emg_edges[user][0] - assert area_edges[user][0] == 'Machine Translation' + assert len(area_edges[user]) == 2 + assert area_edges[user][0] == 'Generation' + assert area_edges[user][1] == 'Machine Translation' aggregate_score_edges = {o['id']['tail']: [j['weight'] for j in o['values']] for o in pc_client_v2.get_grouped_edges(invitation=f"{role}/-/Aggregate_Score", groupby='tail', select='weight')} score_edges = {o['id']['tail']: [j['weight'] for j in o['values']] for o in pc_client_v2.get_grouped_edges(invitation=f"{role}/-/Emergency_Score", groupby='tail', select='weight')} assert all(weight < 10 for weight in score_edges[user]) assert all(weight < 10 for weight in aggregate_score_edges[user]) + assert len(score_edges[user]) == 101 # Test deleting note user_note_edit = user_client.post_note_edit( @@ -4158,8 +4157,8 @@ def test_emergency_reviewing_forms(self, client, openreview_client, helpers): ddate=openreview.tools.datetime_millis(now), content = { 'emergency_reviewing_agreement': { 'value': 'Yes' }, - 'emergency_load': { 'value': 4 }, - 'research_area': { 'value': 'Machine Translation' } + 'emergency_load': { 'value': 6 }, + 'research_area': { 'value': ['Generation', 'Machine Translation'] } } ) )