from __future__ import absolute_import
from queue import Empty
import csv
import json
import time
import datetime
import re
import traceback
from concurrent.futures import ThreadPoolExecutor
from enum import Enum
from io import StringIO
from multiprocessing import cpu_count
from tqdm import tqdm
import os
import concurrent.futures
import openreview
#from .. import openreview, OpenReviewException
from .. import tools
from . import webfield
from . import invitation
from . import matching
from openreview.stages import *
from deprecated.sphinx import deprecated
[docs]
class Conference(object):
def __init__(self, client):
self.client = client
self.client_v2 = openreview.Client(baseurl=openreview.tools.get_base_urls(client)[1], token=client.token)
self.request_form_id = None
self.support_user = 'OpenReview.net/Support'
self.venue_revision_name = 'Venue_Revision'
self.new = False
self.use_area_chairs = False
self.use_senior_area_chairs = False
self.use_secondary_area_chairs = False
self.use_ethics_chairs = False
self.use_ethics_reviewers = False
self.use_recruitment_template = False
self.groups = []
self.name = ''
self.short_name = ''
self.year = datetime.datetime.now().year
self.homepage_header = {}
self.authorpage_header = {}
self.reviewerpage_header = {}
self.areachairpage_header = {}
self.expertise_selection_page_header = {}
self.invitation_builder = invitation.InvitationBuilder(client)
self.webfield_builder = webfield.WebfieldBuilder(client)
self.authors_name = 'Authors'
self.reviewers_name = 'Reviewers'
self.reviewer_roles = ['Reviewers']
self.area_chair_roles = ['Area_Chairs']
self.senior_area_chair_roles = ['Senior_Area_Chairs']
self.area_chairs_name = 'Area_Chairs'
self.senior_area_chairs_name = 'Senior_Area_Chairs'
self.secondary_area_chairs_name = 'Secondary_Area_Chairs'
self.ethics_chairs_name = 'Ethics_Chairs'
self.ethics_reviewers_name = 'Ethics_Reviewers'
self.program_chairs_name = 'Program_Chairs'
self.recommendation_name = 'Recommendation'
self.submission_stage = SubmissionStage()
self.bid_stages = {}
self.expertise_selection_stage = ExpertiseSelectionStage()
self.review_stage = ReviewStage()
self.ethics_review_stage = None
self.review_rebuttal_stage = None
self.review_revision_stage = None
self.review_rating_stage = None
self.submission_revision_stage = None
self.comment_stage = CommentStage()
self.meta_review_stage = MetaReviewStage()
self.meta_review_revision_stage = None
self.decision_stage = DecisionStage()
self.layout = 'tabs'
self.venue_heading_map = {}
self.enable_reviewer_reassignment = False
self.default_reviewer_load = {}
self.reviewer_identity_readers = []
self.area_chair_identity_readers = []
self.senior_area_chair_identity_readers = []
self.use_publication_chairs = False # compatibility with venue class
def __create_group(self, group_id, group_owner_id, members = [], is_signatory = True, additional_readers = [], exclude_self_reader=False):
group = tools.get_group(self.client, id = group_id)
if group is None:
readers = [self.id, group_owner_id] if exclude_self_reader else [self.id, group_owner_id, group_id]
return self.client.post_group(openreview.Group(
id = group_id,
readers = ['everyone'] if 'everyone' in additional_readers else readers + additional_readers,
writers = [self.id, group_owner_id],
signatures = [self.id],
signatories = [self.id, group_id] if is_signatory else [self.id, group_owner_id],
members = members))
else:
return self.client.add_members_to_group(group, members)
def __set_author_page(self, override=False):
authors_group = tools.get_group(self.client, self.get_authors_id())
if authors_group:
return self.webfield_builder.set_author_page(self, authors_group, override)
def __set_reviewer_page(self):
reviewers_group = tools.get_group(self.client, self.get_reviewers_id())
if reviewers_group:
return self.webfield_builder.set_reviewer_page(self, reviewers_group)
def __set_area_chair_page(self):
area_chairs_group = tools.get_group(self.client, self.get_area_chairs_id())
if area_chairs_group:
return self.webfield_builder.set_area_chair_page(self, area_chairs_group)
def __set_senior_area_chair_page(self):
senior_area_chairs_group = tools.get_group(self.client, self.get_senior_area_chairs_id())
if senior_area_chairs_group:
return self.webfield_builder.set_senior_area_chair_page(self, senior_area_chairs_group)
def __set_expertise_selection_page(self):
expertise_selection_invitation = tools.get_invitation(self.client, self.get_expertise_selection_id())
if expertise_selection_invitation:
return self.webfield_builder.set_expertise_selection_page(self, expertise_selection_invitation)
def __set_bid_page(self, stage):
"""
Set a webfield to each available bid invitation
"""
bid_invitation = tools.get_invitation(self.client, self.get_bid_id(group_id=stage.committee_id))
if bid_invitation:
self.webfield_builder.set_bid_page(self, bid_invitation, stage)
def __set_recommendation_page(self, assignment_title, score_ids, conflict_id, total_recommendations):
recommendation_invitation = tools.get_invitation(self.client, self.get_recommendation_id())
if recommendation_invitation:
return self.webfield_builder.set_recommendation_page(self, recommendation_invitation, assignment_title, score_ids, conflict_id, total_recommendations)
def expire_invitation(self, invitation_id):
# Get invitation
invitation = tools.get_invitation(self.client, id = invitation_id)
if invitation:
# Force the expdate
now = round(time.time() * 1000)
if not invitation.expdate or invitation.expdate > now:
invitation.expdate = now
invitation.duedate = now
invitation = self.client.post_invitation(invitation)
return invitation
def __create_submission_stage(self):
under_submission = self.submission_stage.is_under_submission()
return self.invitation_builder.set_submission_invitation(self, under_submission=under_submission)
def __create_expertise_selection_stage(self):
self.invitation_builder.set_expertise_selection_invitation(self)
return self.__set_expertise_selection_page()
def __create_registration_stage(self, stage):
return self.invitation_builder.set_registration_invitation(self, stage)
def __create_bid_stage(self, stage):
self.invitation_builder.set_bid_invitation(self, stage)
return self.__set_bid_page(stage)
def __create_review_stage(self):
notes = list(self.get_submissions())
invitations = self.invitation_builder.set_review_invitation(self, notes)
if self.review_stage.rating_field_name:
self.webfield_builder.edit_web_string_value(self.client.get_group(self.get_program_chairs_id()), 'REVIEW_RATING_NAME', self.review_stage.rating_field_name)
if self.use_area_chairs:
self.webfield_builder.edit_web_string_value(self.client.get_group(self.get_area_chairs_id()), 'REVIEW_RATING_NAME', self.review_stage.rating_field_name)
self.webfield_builder.edit_web_string_value(self.client.get_group(self.get_reviewers_id()), 'REVIEW_RATING_NAME', self.review_stage.rating_field_name)
self.webfield_builder.edit_web_string_value(self.client.get_group(self.get_authors_id()), 'REVIEW_RATING_NAME', self.review_stage.rating_field_name)
if self.review_stage.confidence_field_name:
self.webfield_builder.edit_web_string_value(self.client.get_group(self.get_program_chairs_id()), 'REVIEW_CONFIDENCE_NAME', self.review_stage.confidence_field_name)
if self.use_area_chairs:
self.webfield_builder.edit_web_string_value(self.client.get_group(self.get_area_chairs_id()), 'REVIEW_CONFIDENCE_NAME', self.review_stage.confidence_field_name)
self.webfield_builder.edit_web_string_value(self.client.get_group(self.get_authors_id()), 'REVIEW_CONFIDENCE_NAME', self.review_stage.confidence_field_name)
return invitations
def __create_ethics_review_stage(self):
numbers = ','.join(map(str, self.ethics_review_stage.submission_numbers))
print('flagged submissions', numbers)
notes = list(self.get_submissions(number=numbers))
## Unflag existing papers with no assigned reviewers
groups = self.client.get_groups(regex=self.id + '/Paper.*')
for group in groups:
print('process group', group.id)
if group.id.endswith(self.ethics_reviewers_name) and len(group.members) == 0:
number = self.get_number_from_committee(group.id)
if number and number not in self.ethics_review_stage.submission_numbers:
## Delete group
self.client.delete_group(group.id)
## Expire the invitation
invitation = tools.get_invitation(self.client, self.get_invitation_id(self.ethics_review_stage.name, number))
if invitation:
invitation.expdate = openreview.tools.datetime_millis(datetime.datetime.utcnow())
self.client.post_invitation(invitation)
## Create ethics paper groups
for note in tqdm(notes):
ethics_reviewers_id=self.get_ethics_reviewers_id(number=note.number)
group = tools.get_group(self.client, id = ethics_reviewers_id)
if not group:
self.client.post_group(openreview.Group(id=ethics_reviewers_id,
readers=[self.id, self.get_ethics_chairs_id(), ethics_reviewers_id],
nonreaders=[self.get_authors_id(note.number)],
deanonymizers=[self.id, self.get_ethics_chairs_id()],
writers=[self.id],
signatures=[self.id],
signatories=[self.id],
anonids=True,
members=group.members if group else [])
)
## Make submissions visible to the ethics committee
if self.submission_stage.double_blind:
self.create_blind_submissions(number=numbers)
else:
self.invitation_builder.set_submission_invitation(conference=self)
submissions = self.get_submissions()
for s in submissions:
final_readers = self.submission_stage.get_readers(conference=self, number=s.number)
if s.readers != final_readers:
s.readers = final_readers
self.client.post_note(s)
## Setup paper matching
group = tools.get_group(self.client, id=self.get_ethics_reviewers_id())
if group and len(group.members) > 0:
self.setup_committee_matching(self.get_ethics_reviewers_id(), compute_affinity_scores=False, compute_conflicts=True)
self.invitation_builder.set_assignment_invitation(self, self.get_ethics_reviewers_id())
## Make reviews visible to the ethics committee
self.invitation_builder.set_review_invitation(self, notes)
invitations = self.invitation_builder.set_ethics_review_invitation(self, notes)
return invitations
def __create_review_rebuttal_stage(self):
return self.invitation_builder.set_review_rebuttal_invitation(self)
def __create_review_revision_stage(self):
submissions = self.get_submissions(details='directReplies')
reviews = []
for submission in submissions:
for reply in submission.details['directReplies']:
if reply['invitation'] == self.get_invitation_id(self.review_stage.name, submission.number):
reviews.append(openreview.Note.from_json(reply))
return self.invitation_builder.set_review_revision_invitation(self, reviews)
def __create_review_rating_stage(self):
submissions = self.get_submissions(details='directReplies')
reviews = []
for submission in submissions:
for reply in submission.details['directReplies']:
if reply['invitation'] == self.get_invitation_id(self.review_stage.name, submission.number):
reviews.append(openreview.Note.from_json(reply))
return self.invitation_builder.set_review_rating_invitation(self, reviews)
def __create_comment_stage(self):
## Create comment invitations per paper
notes = list(self.get_submissions())
return self.invitation_builder.set_comment_invitation(self, notes)
def __create_meta_review_stage(self):
notes = list(self.get_submissions())
return self.invitation_builder.set_meta_review_invitation(self, notes)
def __create_meta_review_revision_stage(self):
submissions = self.get_submissions(details='directReplies')
metareviews = []
for submission in submissions:
for reply in submission.details['directReplies']:
if reply['invitation'] == self.get_invitation_id(self.meta_review_stage.name, submission.number):
metareviews.append(openreview.Note.from_json(reply))
return self.invitation_builder.set_meta_review_revision_invitation(self, metareviews)
def __create_decision_stage(self):
notes = list(self.get_submissions())
return self.invitation_builder.set_decision_invitation(self, notes)
def __create_submission_revision_stage(self):
invitation = tools.get_invitation(self.client, self.get_submission_id())
if invitation:
notes = self.get_submissions(accepted=self.submission_revision_stage.only_accepted, details='original')
if self.submission_revision_stage.only_accepted:
all_notes = self.get_submissions(details='original')
accepted_note_ids = [note.id for note in notes]
non_accepted_notes = [note for note in all_notes if note.id not in accepted_note_ids]
expire_invitation_ids = [self.get_invitation_id(self.submission_revision_stage.name, note.number) for note in non_accepted_notes]
tools.concurrent_requests(self.expire_invitation, expire_invitation_ids)
return self.invitation_builder.set_revise_submission_invitation(self, notes, invitation.reply['content'])
## Deprecated, use this only for manual assignments
def set_reviewer_reassignment(self, enabled = True):
self.enable_reviewer_reassignment = enabled
# Update PC & AC homepages
pc_group = self.client.get_group(self.get_program_chairs_id())
self.webfield_builder.edit_web_value(pc_group, 'ENABLE_REVIEWER_REASSIGNMENT', str(enabled).lower())
if self.use_area_chairs:
ac_group = self.client.get_group(self.get_area_chairs_id())
self.webfield_builder.edit_web_value(ac_group, 'ENABLE_REVIEWER_REASSIGNMENT', str(enabled).lower())
## Use for proposed/deployed assignments
def set_reviewer_edit_assignments(self, assignment_title=None):
print('set_reviewer_edit_assignments', assignment_title)
if self.use_area_chairs:
ac_group = self.client.get_group(self.get_area_chairs_id())
ac_group=self.webfield_builder.edit_web_value(ac_group, 'ENABLE_EDIT_REVIEWER_ASSIGNMENTS', 'true')
if assignment_title:
self.webfield_builder.edit_web_string_value(ac_group, 'REVIEWER_ASSIGNMENT_TITLE', assignment_title)
else:
self.webfield_builder.edit_web_string_value(ac_group, 'REVIEWER_ASSIGNMENT_TITLE', '')
def set_id(self, id):
self.id = id
def get_id(self):
return self.id
def is_new(self):
return self.new
def set_name(self, name):
self.name = name
def get_name(self):
return self.name
def set_short_name(self, name):
self.short_name = name
def get_short_name(self):
return self.short_name
def set_year(self, year):
self.year = year
def get_year(self):
return self.year
def set_reviewers_name(self, name):
self.reviewers_name = name
def set_submission_stage(self, stage):
self.submission_stage = stage
return self.__create_submission_stage()
## API2 only
def create_submission_stage(self):
return
def set_expertise_selection_stage(self, stage):
self.expertise_selection_stage = stage
return self.__create_expertise_selection_stage()
def set_registration_stage(self, stage):
return self.__create_registration_stage(stage)
def create_bid_stages(self):
if self.bid_stages:
for stage in self.bid_stages.values():
self.__create_bid_stage(stage)
def create_review_stage(self):
if self.review_stage:
return self.__create_review_stage()
def create_ethics_review_stage(self):
if self.ethics_review_stage:
return self.__create_ethics_review_stage()
def create_meta_review_stage(self):
if self.meta_review_stage:
return self.__create_meta_review_stage()
def create_meta_review_revision_stage(self):
if self.meta_review_revision_stage:
return self.__create_meta_review_revision_stage()
def create_comment_stage(self):
if self.comment_stage:
return self.__create_comment_stage()
def create_review_rebuttal_stage(self):
if self.review_rebuttal_stage:
return self.__create_review_rebuttal_stage()
def create_review_revision_stage(self):
if self.review_revision_stage:
return self.__create_review_revision_stage()
def set_review_rating_stage(self, stage):
self.review_rating_stage = stage
return self.__create_review_rating_stage()
def create_submission_revision_stage(self):
if self.submission_revision_stage:
self.__create_submission_revision_stage()
def create_decision_stage(self):
if self.decision_stage:
self.__create_decision_stage()
if self.decision_stage.decisions_file:
decisions = self.client.get_attachment(id=self.request_form_id, field_name='decisions_file')
self.post_decisions(decisions)
def set_area_chairs_name(self, name):
if self.use_area_chairs:
self.area_chairs_name = name
else:
raise openreview.OpenReviewException('Venue\'s "has_area_chairs" setting is disabled')
def set_secondary_area_chairs_name(self, name):
if self.use_secondary_area_chairs:
self.secondary_area_chairs_name = name
else:
raise openreview.OpenReviewException('Venue\'s "has_secondary_area_chairs" setting is disabled')
def set_program_chairs_name(self, name):
self.program_chairs_name = name
def get_program_chairs_id(self):
return self.id + '/' + self.program_chairs_name
def get_reviewers_id(self, number = None):
return self.get_committee_id(self.reviewers_name, number)
def get_anon_reviewer_id(self, number=None, anon_id=None, name=None):
reviewers_name = name if name else self.reviewers_name
single_reviewer_name=reviewers_name[:-1] if reviewers_name.endswith('s') else reviewers_name
return f'{self.id}/Paper{number}/{single_reviewer_name}_{anon_id}'
def get_anon_area_chair_id(self, number=None, anon_id=None):
single_area_chair_name=self.area_chairs_name[:-1] if self.area_chairs_name.endswith('s') else self.area_chairs_name
return f'{self.id}/Paper{number}/{single_area_chair_name}_{anon_id}'
def get_anon_secondary_area_chair_id(self, number=None, anon_id=None):
single_area_chair_name=self.secondary_area_chairs_name[:-1] if self.secondary_area_chairs_name.endswith('s') else self.secondary_area_chairs_name
return f'{self.id}/Paper{number}/{single_area_chair_name}_{anon_id}'
def get_reviewers_name(self, pretty=True):
if pretty:
name=self.reviewers_name.replace('_', ' ')
return name[:-1] if name.endswith('s') else name
return self.reviewers_name
def get_authors_name(self, pretty=True):
if pretty:
name=self.authors_name.replace('_', ' ')
return name[:-1] if name.endswith('s') else name
return self.authors_name
def get_ethics_reviewers_name(self, pretty=True):
if pretty:
name=self.ethics_reviewers_name.replace('_', ' ')
return name[:-1] if name.endswith('s') else name
return self.ethics_reviewers_name
def get_area_chairs_name(self, pretty=True):
if pretty:
name=self.area_chairs_name.replace('_', ' ')
return name[:-1] if name.endswith('s') else name
return self.area_chairs_name
def get_senior_area_chairs_name(self, pretty=True):
if pretty:
name=self.senior_area_chairs_name.replace('_', ' ')
return name[:-1] if name.endswith('s') else name
return self.senior_area_chairs_name
def get_secondary_area_chairs_name(self, pretty=True):
if pretty:
return self.use_secondary_area_chairs.replace('_', ' ')
return self.use_secondary_area_chairs
def get_authors_id(self, number = None):
return self.get_committee_id(self.authors_name, number)
def get_accepted_authors_id(self):
return self.id + '/' + self.authors_name + '/Accepted'
def get_area_chairs_id(self, number = None):
return self.get_committee_id(self.area_chairs_name, number)
def get_senior_area_chairs_id(self, number = None):
return self.get_committee_id(self.senior_area_chairs_name, number)
def get_ethics_chairs_id(self, number = None):
return self.get_committee_id(self.ethics_chairs_name, number)
def get_ethics_reviewers_id(self, number = None):
return self.get_committee_id(self.ethics_reviewers_name, number)
def get_secondary_area_chairs_id(self, number=None):
return self.get_committee_id(self.secondary_area_chairs_name, number)
def get_committee(self, number = None, submitted_reviewers = False, with_authors = False):
committee = [self.get_id()]
if with_authors:
committee.append(self.get_authors_id(number))
if submitted_reviewers:
committee.append(self.get_reviewers_id(number) + '/Submitted')
else:
committee.append(self.get_reviewers_id(number))
if self.use_area_chairs:
committee.append(self.get_area_chairs_id(number))
if self.use_senior_area_chairs:
committee.append(self.get_senior_area_chairs_id(number))
committee.append(self.get_program_chairs_id())
return committee
def get_committee_names(self):
committee=[self.reviewers_name]
if self.use_area_chairs:
committee.append(self.area_chairs_name)
if self.use_senior_area_chairs:
committee.append(self.senior_area_chairs_name)
return committee
def get_committee_id(self, name, number=None):
committee_id = self.id + '/'
if number:
committee_id = f'{committee_id}Paper{number}/{name}'
else:
committee_id = committee_id + name
return committee_id
def get_number_from_committee(self, committee_id):
tokens = committee_id.split('/')
for token in tokens:
if token.startswith('Paper'):
token = token.replace('Paper', '')
return int(token)
return None
def get_committee_name(self, committee_id, pretty=False):
name = committee_id.split('/')[-1]
if pretty:
name = name.replace('_', ' ')
if name.endswith('s'):
return name[:-1]
return name
def get_publication_chairs_id(self):
return self.id + '/Publication_Chairs'
def get_roles(self):
roles = self.reviewer_roles
if self.use_area_chairs:
roles = self.reviewer_roles + self.area_chair_roles
if self.use_senior_area_chairs:
roles = roles + self.senior_area_chair_roles
if self.use_ethics_chairs:
roles = roles + [self.ethics_chairs_name]
if self.use_ethics_reviewers:
roles = roles + [self.ethics_reviewers_name]
return roles
def submission_tracks(self):
return []
def get_submission_id(self):
return self.submission_stage.get_submission_id(self)
def get_blind_submission_id(self):
return self.submission_stage.get_blind_submission_id(self)
def get_expertise_selection_id(self):
return self.get_invitation_id(self.expertise_selection_stage.name)
def get_bid_id(self, group_id):
return self.get_invitation_id('Bid', prefix=group_id)
def get_recommendation_id(self, group_id=None):
if not group_id:
group_id = self.get_reviewers_id()
return self.get_invitation_id(self.recommendation_name, prefix=group_id)
def get_registration_id(self, committee_id):
return self.get_invitation_id(name = 'Registration', prefix = committee_id)
def get_recruitment_id(self, committee_id):
if self.use_recruitment_template:
return self.get_invitation_id('Recruitment', prefix=committee_id)
return self.get_invitation_id('Recruit_' + self.get_committee_name(committee_id))
def get_invitation_id(self, name, number = None, prefix = None):
invitation_id = self.id
if prefix:
invitation_id = prefix
if number:
invitation_id = invitation_id + '/Paper' + str(number) + '/-/'
else:
invitation_id = invitation_id + '/-/'
invitation_id = invitation_id + name
return invitation_id
def set_conference_groups(self, groups):
self.groups = groups
def get_conference_groups(self):
return self.groups
def get_paper_assignment_id(self, group_id, deployed=False, invite=False):
if deployed:
return self.get_invitation_id('Assignment', prefix=group_id)
if invite:
return self.get_invitation_id('Invite_Assignment', prefix=group_id)
return self.get_invitation_id('Proposed_Assignment', prefix=group_id)
def get_affinity_score_id(self, group_id):
return self.get_invitation_id('Affinity_Score', prefix=group_id)
def get_elmo_score_id(self, group_id):
return self.get_invitation_id('ELMo_Score', prefix=group_id)
def get_conflict_score_id(self, group_id):
return self.get_invitation_id('Conflict', prefix=group_id)
def get_custom_max_papers_id(self, group_id):
return self.get_invitation_id('Custom_Max_Papers', prefix=group_id)
def set_homepage_header(self, header):
self.homepage_header = header
def set_authorpage_header(self, header):
self.authorpage_header = header
return self.__set_author_page(override=True)
def get_authorpage_header(self):
return self.authorpage_header
def set_reviewerpage_header(self, header):
self.reviewerpage_header = header
return self.__set_reviewer_page()
def get_reviewerpage_header(self):
return self.reviewerpage_header
def set_areachairpage_header(self, header):
if self.use_area_chairs:
self.areachairpage_header = header
return self.__set_area_chair_page()
else:
raise openreview.OpenReviewException('Conference "has_area_chairs" setting is disabled')
def get_areachairpage_header(self):
return self.areachairpage_header
def set_expertise_selection_page_header(self, header):
self.expertise_selection_page_header = header
return self.__set_expertise_selection_page
def get_expertise_selection_page_header(self):
return self.expertise_selection_page_header
def set_homepage_layout(self, layout):
self.layout = layout
def set_venue_heading_map(self, decision_heading_map):
venue_heading_map = {}
for decision, tab_name in decision_heading_map.items():
venue_heading_map[tools.decision_to_venue(self.short_name, decision)] = tab_name
self.venue_heading_map = venue_heading_map
def has_area_chairs(self, has_area_chairs):
self.use_area_chairs = has_area_chairs
pc_group = tools.get_group(self.client, self.get_program_chairs_id())
if pc_group and pc_group.web:
# update PC console
if self.use_area_chairs:
self.webfield_builder.edit_web_string_value(pc_group, 'AREA_CHAIRS_ID', self.get_area_chairs_id())
else:
self.webfield_builder.edit_web_string_value(pc_group, 'AREA_CHAIRS_ID', '')
def has_senior_area_chairs(self, has_senior_area_chairs):
self.use_senior_area_chairs = has_senior_area_chairs
def has_secondary_area_chairs(self, has_secondary_area_chairs):
self.use_secondary_area_chairs = has_secondary_area_chairs
def get_homepage_options(self):
options = {}
if self.name:
options['subtitle'] = self.name
if self.homepage_header:
options['title'] = self.homepage_header.get('title')
options['subtitle'] = self.homepage_header.get('subtitle')
options['location'] = self.homepage_header.get('location')
options['date'] = self.homepage_header.get('date')
options['website'] = self.homepage_header.get('website')
options['instructions'] = self.homepage_header.get('instructions')
options['deadline'] = self.homepage_header.get('deadline')
options['contact'] = self.homepage_header.get('contact')
return options
def get_submissions(self, accepted = False, number=None, details = None, sort = 'tmdate'):
invitation = self.get_blind_submission_id()
if accepted and (not details or 'directReplies' not in details):
details = (details + ',') if details else ''
details = details + 'directReplies'
notes = self.client.get_all_notes(invitation = invitation, number=number, details = details, sort = sort)
if accepted:
accepted_notes = []
for note in notes:
decisions = [reply for reply in note.details['directReplies'] if self.get_invitation_id(self.decision_stage.name, note.number) == reply['invitation']]
if decisions and 'Accept' in decisions[0]['content']['decision']:
accepted_notes.append(note)
return accepted_notes
return notes
def get_withdrawn_submissions(self, details=None):
invitation = self.submission_stage.get_withdrawn_submission_id(self)
return self.client.get_all_notes(invitation=invitation, details=details)
def get_desk_rejected_submissions(self, details=None):
invitation = self.submission_stage.get_desk_rejected_submission_id(self)
return self.client.get_all_notes(invitation=invitation, details=details)
def get_reviewer_identity_readers(self, number):
## default value
if not self.reviewer_identity_readers:
identity_readers=[self.id]
if self.use_senior_area_chairs:
identity_readers.append(self.get_senior_area_chairs_id(number))
if self.use_area_chairs:
identity_readers.append(self.get_area_chairs_id(number))
return identity_readers
return openreview.stages.IdentityReaders.get_readers(self, number, self.reviewer_identity_readers)
def get_area_chair_identity_readers(self, number):
## default value
if not self.area_chair_identity_readers:
identity_readers=[self.id]
if self.use_senior_area_chairs:
identity_readers.append(self.get_senior_area_chairs_id(number))
identity_readers.append(self.get_area_chairs_id(number))
return identity_readers
return openreview.stages.IdentityReaders.get_readers(self, number, self.area_chair_identity_readers)
def get_senior_area_chair_identity_readers(self, number):
## default value
if not self.senior_area_chair_identity_readers:
return [self.id, self.get_senior_area_chairs_id(number)]
return openreview.stages.IdentityReaders.get_readers(self, number, self.senior_area_chair_identity_readers)
def get_reviewer_paper_group_readers(self, number):
readers=[self.id]
if self.use_senior_area_chairs:
readers.append(self.get_senior_area_chairs_id(number))
if self.use_area_chairs:
readers.append(self.get_area_chairs_id(number))
readers.append(self.get_reviewers_id(number))
return readers
def get_reviewer_paper_group_writers(self, number):
readers=[self.id]
if self.use_senior_area_chairs:
readers.append(self.get_senior_area_chairs_id(number))
if self.use_area_chairs:
readers.append(self.get_area_chairs_id(number))
return readers
def get_area_chair_paper_group_readers(self, number):
readers=[self.id, self.get_program_chairs_id()]
if self.use_senior_area_chairs:
readers.append(self.get_senior_area_chairs_id(number))
readers.append(self.get_area_chairs_id(number))
if openreview.stages.IdentityReaders.REVIEWERS_ASSIGNED in self.area_chair_identity_readers:
readers.append(self.get_reviewers_id(number))
return readers
def create_withdraw_invitations(self, reveal_authors=False, reveal_submission=False, email_pcs=False,
hide_fields=[], force=False):
if not force and reveal_submission and not self.submission_stage.public:
raise openreview.OpenReviewException('Can not reveal withdrawn submissions that are not originally public')
if not force and not reveal_authors and not self.submission_stage.double_blind:
raise openreview.OpenReviewException('Can not hide authors of submissions in single blind or open venue')
return self.invitation_builder.set_withdraw_invitation(self, reveal_authors, reveal_submission, email_pcs, hide_fields=hide_fields)
def create_desk_reject_invitations(self, reveal_authors=False, reveal_submission=False, email_pcs=False,
hide_fields=None, force=False):
if not force and reveal_submission and not self.submission_stage.public:
raise openreview.OpenReviewException('Can not reveal desk-rejected submissions that are not originally public')
if not force and not reveal_authors and not self.submission_stage.double_blind:
raise openreview.OpenReviewException('Can not hide authors of submissions in single blind or open venue')
return self.invitation_builder.set_desk_reject_invitation(self, reveal_authors, reveal_submission, email_pcs, hide_fields=hide_fields)
def create_paper_groups(self, authors=False, reviewers=False, area_chairs=False, senior_area_chairs=False, overwrite=False):
notes_iterator = self.get_submissions(sort='number:asc', details='original')
author_group_ids = []
paper_reviewer_group_invitation=self.invitation_builder.set_paper_group_invitation(self, self.get_reviewers_id())
if self.use_area_chairs:
paper_area_chair_group_invitation=self.invitation_builder.set_paper_group_invitation(self, self.get_area_chairs_id())
group_by_id = { g.id: g for g in self.client.get_all_groups(regex=f'{self.id}/Paper.*') }
for n in tqdm(list(notes_iterator), desc='create_paper_groups'):
# Paper group
group_id = '{conference_id}/Paper{number}'.format(conference_id=self.id, number=n.number)
group = group_by_id.get(group_id)
if not group or overwrite:
self.client.post_group(openreview.Group(id=group_id,
readers=[self.id],
writers=[self.id],
signatures=[self.id],
signatories=[self.id],
members=group.members if group else []
))
# Author Paper group
if authors:
authorids = n.content.get('authorids')
if n.details and n.details.get('original'):
authorids = n.details['original']['content']['authorids']
author_paper_group = self.__create_group(self.get_authors_id(n.number), self.id, authorids)
author_group_ids.append(author_paper_group.id)
# Reviewers Paper group
if reviewers:
reviewers_id=self.get_reviewers_id(number=n.number)
group = group_by_id.get(reviewers_id)
if not group or overwrite:
self.client.post_group(openreview.Group(id=reviewers_id,
invitation=paper_reviewer_group_invitation.id,
readers=self.get_reviewer_paper_group_readers(n.number),
nonreaders=[self.get_authors_id(n.number)],
deanonymizers=self.get_reviewer_identity_readers(n.number),
writers=self.get_reviewer_paper_group_writers(n.number),
signatures=[self.id],
signatories=[self.id],
anonids=True,
members=group.members if group else []
))
# Reviewers Submitted Paper group
reviewers_submitted_id = self.get_reviewers_id(number=n.number) + '/Submitted'
group = group_by_id.get(reviewers_submitted_id)
if not group or overwrite:
readers=[self.id]
if self.use_senior_area_chairs:
readers.append(self.get_senior_area_chairs_id(n.number))
if self.use_area_chairs:
readers.append(self.get_area_chairs_id(n.number))
readers.append(reviewers_submitted_id)
self.client.post_group(openreview.Group(id=reviewers_submitted_id,
readers=readers,
writers=[self.id],
signatures=[self.id],
signatories=[self.id],
members=group.members if group else []
))
# Area Chairs Paper group
if self.use_area_chairs and area_chairs:
area_chairs_id=self.get_area_chairs_id(number=n.number)
group = group_by_id.get(area_chairs_id)
if not group or overwrite:
self.client.post_group(openreview.Group(id=area_chairs_id,
invitation=paper_area_chair_group_invitation.id,
readers=self.get_area_chair_paper_group_readers(n.number),
nonreaders=[self.get_authors_id(n.number)],
deanonymizers=self.get_area_chair_identity_readers(n.number),
writers=[self.id],
signatures=[self.id],
signatories=[self.id],
anonids=True,
members=group.members if group else []
))
# Senior Area Chairs Paper group
if self.use_senior_area_chairs and senior_area_chairs:
senior_area_chairs_id=self.get_senior_area_chairs_id(number=n.number)
group = group_by_id.get(senior_area_chairs_id)
if not group or overwrite:
self.client.post_group(openreview.Group(id=senior_area_chairs_id,
readers=self.get_senior_area_chair_identity_readers(n.number),
nonreaders=[self.get_authors_id(n.number)],
writers=[self.id],
signatures=[self.id],
signatories=[self.id, senior_area_chairs_id],
members=group.members if group else []
))
if author_group_ids:
self.__create_group(self.get_authors_id(), self.id, author_group_ids, additional_readers=['everyone'])
# Add this group to active_venues
active_venues = self.client_v2.get_group('active_venues')
self.client_v2.add_members_to_group(active_venues, self.id)
def create_blind_submissions(self, hide_fields=[], number=None):
if not self.submission_stage.double_blind:
raise openreview.OpenReviewException('Conference is not double blind')
submissions_by_original = { note.original: note for note in self.get_submissions() }
withdrawn_submissions_by_original = {note.original: note for note in self.get_withdrawn_submissions()}
desk_rejected_submissions_by_original = {note.original: note for note in self.get_desk_rejected_submissions()}
self.invitation_builder.set_blind_submission_invitation(self, hide_fields)
online_date = openreview.tools.datetime_millis(datetime.datetime.utcnow())
blinded_notes = []
for note in tqdm(self.client.get_all_notes(invitation=self.get_submission_id(), sort='number:asc', number=number), desc='create_blind_submissions'):
# If the note was either withdrawn or desk-rejected already, we should not create another blind copy
if withdrawn_submissions_by_original.get(note.id) or desk_rejected_submissions_by_original.get(note.id):
continue
existing_blind_note = submissions_by_original.get(note.id)
blind_content = {
'authors': ['Anonymous'],
'authorids': [self.get_authors_id(number=note.number)],
}
for field in hide_fields:
blind_content[field] = ''
blind_readers = self.submission_stage.get_readers(self, note.number)
blind_note = openreview.Note(
id = existing_blind_note.id if existing_blind_note else None,
original= note.id,
invitation= self.get_blind_submission_id(),
forum=None,
signatures= [self.id],
writers= [self.id],
readers= blind_readers,
content= blind_content)
blind_note = self.client.post_note(blind_note)
generate_bibtex = not existing_blind_note or existing_blind_note and 'venue' not in existing_blind_note.content
if self.submission_stage.public and generate_bibtex:
bibtex = tools.generate_bibtex(
note=note,
venue_fullname=self.name,
url_forum=blind_note.id,
year=str(self.get_year()))
revision_note = self.client.post_note(openreview.Note(
invitation = f'{self.support_user}/-/{self.venue_revision_name}',
odate = online_date if note.odate is None else note.odate,
forum = note.id,
referent = note.id,
readers = ['everyone'],
writers = [self.id],
signatures = [self.id],
content = {
'_bibtex': bibtex
}
))
blinded_notes.append(blind_note)
# Update PC console with double blind submissions
pc_group = self.client.get_group(self.get_program_chairs_id())
self.webfield_builder.edit_web_string_value(pc_group, 'BLIND_SUBMISSION_ID', self.get_blind_submission_id())
return blinded_notes
def setup_first_deadline_stage(self, force=False, hide_fields=[], allow_author_reorder=False):
if self.submission_stage.double_blind:
self.create_blind_submissions(hide_fields=hide_fields)
else:
self.invitation_builder.set_submission_invitation(conference=self)
submissions = self.get_submissions()
online_date = openreview.tools.datetime_millis(datetime.datetime.utcnow())
for s in submissions:
final_readers = self.submission_stage.get_readers(conference=self, number=s.number)
if s.readers != final_readers:
s.readers = final_readers
s.odate = online_date if ('everyone' in s.readers and s.odate is None) else s.odate
self.client.post_note(s)
self.create_paper_groups(authors=True, reviewers=True, area_chairs=True, senior_area_chairs=True)
self.submission_revision_stage = SubmissionRevisionStage(name='Revision',
start_date=None if force else self.submission_stage.due_date,
due_date=self.submission_stage.second_due_date,
additional_fields=self.submission_stage.additional_fields,
remove_fields=self.submission_stage.remove_fields,
only_accepted=False,
multiReply=False if self.submission_stage.double_blind else True,
allow_author_reorder=allow_author_reorder
)
self.__create_submission_revision_stage()
self.create_withdraw_invitations(
reveal_authors=not self.submission_stage.double_blind,
reveal_submission=False,
email_pcs=False,
hide_fields=hide_fields,
force=True
)
self.create_desk_reject_invitations(
reveal_authors=not self.submission_stage.double_blind,
reveal_submission=False,
hide_fields=hide_fields,
email_pcs=False,
force=True
)
def setup_final_deadline_stage(self, force=False, hide_fields=[]):
if self.submission_stage.double_blind and not (self.submission_stage.author_names_revealed or self.submission_stage.papers_released):
self.create_blind_submissions(hide_fields)
if not self.submission_stage.double_blind and not self.submission_stage.papers_released and not self.submission_stage.create_groups:
self.invitation_builder.set_submission_invitation(self)
online_date = openreview.tools.datetime_millis(datetime.datetime.utcnow())
for note in tqdm(self.client.get_all_notes(invitation=self.get_submission_id(), sort='number:asc'), desc='set_final_readers'):
final_readers = self.submission_stage.get_readers(conference=self, number=note.number)
if note.readers != final_readers:
note.readers = final_readers
note.odate = online_date if ('everyone' in note.readers and note.odate is None) else note.odate
self.client.post_note(note)
self.create_paper_groups(authors=True, reviewers=True, area_chairs=True, senior_area_chairs=True)
self.create_withdraw_invitations(
reveal_authors=self.submission_stage.withdrawn_submission_reveal_authors,
reveal_submission=self.submission_stage.withdrawn_submission_public,
email_pcs=self.submission_stage.email_pcs_on_withdraw,
hide_fields=hide_fields
)
self.create_desk_reject_invitations(
reveal_authors=self.submission_stage.desk_rejected_submission_reveal_authors,
reveal_submission=self.submission_stage.desk_rejected_submission_public,
email_pcs=self.submission_stage.email_pcs_on_desk_reject,
hide_fields=hide_fields
)
self.set_authors()
self.set_reviewers()
if self.use_area_chairs:
self.set_area_chairs()
def setup_post_submission_stage(self, force=False, hide_fields=[]):
now = datetime.datetime.utcnow()
if self.submission_stage.second_due_date:
if self.submission_stage.due_date < now and now < self.submission_stage.second_due_date:
self.setup_first_deadline_stage(force, hide_fields, self.submission_stage.author_reorder_after_first_deadline)
elif self.submission_stage.second_due_date < now:
self.setup_final_deadline_stage(force, hide_fields)
elif force:
## For testing purposes
self.setup_final_deadline_stage(force, hide_fields)
else:
if force or (self.submission_stage.due_date and self.submission_stage.due_date < datetime.datetime.now()):
self.setup_final_deadline_stage(force, hide_fields)
def open_recommendations(self, assignment_title = None, start_date = None, due_date = None, total_recommendations = 7):
score_ids = []
invitation_ids = [
self.get_invitation_id('TPMS_Score', prefix=self.get_reviewers_id()),
self.get_invitation_id('Affinity_Score', prefix=self.get_reviewers_id()),
self.get_bid_id(self.get_reviewers_id())
]
for invitation_id in invitation_ids:
if tools.get_invitation(self.client, invitation_id):
score_ids.append(invitation_id)
self.invitation_builder.set_recommendation_invitation(self, start_date, due_date, total_recommendations)
return self.__set_recommendation_page(assignment_title, score_ids, self.get_conflict_score_id(self.get_reviewers_id()), total_recommendations)
def open_paper_ranking(self, committee_id, start_date=None, due_date=None):
return self.invitation_builder.set_paper_ranking_invitation(self, committee_id, start_date, due_date)
def set_program_chairs(self, emails = []):
pcs = self.__create_group(self.get_program_chairs_id(), self.id, emails)
self.webfield_builder.set_program_chair_page(self, pcs)
## Give program chairs admin permissions
self.__create_group(self.id, '~Super_User1', [self.get_program_chairs_id()])
return pcs
def set_senior_area_chairs(self, emails = []):
if self.use_senior_area_chairs:
self.__create_group(group_id=self.get_senior_area_chairs_id(), group_owner_id=self.id, members=emails)
return self.__set_senior_area_chair_page()
else:
raise openreview.OpenReviewException('Conference "has_senior_area_chairs" setting is disabled')
def set_area_chairs(self, emails = []):
if self.use_area_chairs:
readers=[self.get_senior_area_chairs_id()] if self.use_senior_area_chairs else []
self.__create_group(group_id=self.get_area_chairs_id(), group_owner_id=self.id, members=emails, additional_readers=readers)
return self.__set_area_chair_page()
else:
raise openreview.OpenReviewException('Conference "has_area_chairs" setting is disabled')
def set_secondary_area_chairs(self):
if self.use_secondary_area_chairs:
self.__create_group(self.get_secondary_area_chairs_id(), self.id)
else:
raise openreview.OpenReviewException('Conference "has_secondary_area_chairs" setting is disabled')
def set_senior_area_chair_recruitment_groups(self):
if self.use_senior_area_chairs:
parent_group_id = self.get_senior_area_chairs_id()
parent_group_declined_id = parent_group_id + '/Declined'
parent_group_invited_id = parent_group_id + '/Invited'
parent_group_accepted_id = parent_group_id
pcs_id = self.get_program_chairs_id()
# parent_group_accepted_group
self.__create_group(parent_group_accepted_id, pcs_id)
# parent_group_declined_group
self.__create_group(parent_group_declined_id, pcs_id, exclude_self_reader=True)
# parent_group_invited_group
self.__create_group(parent_group_invited_id, pcs_id, exclude_self_reader=True)
else:
raise openreview.OpenReviewException('Conference "has_senior_area_chairs" setting is disabled')
def set_area_chair_recruitment_groups(self):
if self.use_area_chairs:
parent_group_id = self.get_area_chairs_id()
parent_group_declined_id = parent_group_id + '/Declined'
parent_group_invited_id = parent_group_id + '/Invited'
parent_group_accepted_id = parent_group_id
pcs_id = self.get_program_chairs_id()
# parent_group_accepted_group
self.__create_group(parent_group_accepted_id, pcs_id)
# parent_group_declined_group
self.__create_group(parent_group_declined_id, pcs_id, exclude_self_reader=True)
# parent_group_invited_group
self.__create_group(parent_group_invited_id, pcs_id, exclude_self_reader=True)
else:
raise openreview.OpenReviewException('Conference "has_area_chairs" setting is disabled')
def set_ethics_reviewer_recruitment_groups(self):
parent_group_id = self.get_ethics_reviewers_id()
parent_group_declined_id = parent_group_id + '/Declined'
parent_group_invited_id = parent_group_id + '/Invited'
pcs_id = self.get_ethics_chairs_id()
self.set_ethics_reviewers()
self.__create_group(parent_group_declined_id, pcs_id, exclude_self_reader=True)
self.__create_group(parent_group_invited_id, pcs_id, exclude_self_reader=True)
def set_ethics_chair_recruitment_groups(self):
parent_group_id = self.get_ethics_chairs_id()
parent_group_declined_id = parent_group_id + '/Declined'
parent_group_invited_id = parent_group_id + '/Invited'
pcs_id = self.get_program_chairs_id()
self.set_ethics_chairs()
self.__create_group(parent_group_declined_id, pcs_id, exclude_self_reader=True)
self.__create_group(parent_group_invited_id, pcs_id, exclude_self_reader=True)
def set_reviewer_recruitment_groups(self):
parent_group_id = self.get_reviewers_id()
parent_group_declined_id = parent_group_id + '/Declined'
parent_group_invited_id = parent_group_id + '/Invited'
pcs_id = self.get_program_chairs_id()
self.__create_group(parent_group_id, self.get_area_chairs_id() if self.use_area_chairs else self.id)
self.__create_group(parent_group_declined_id, pcs_id, exclude_self_reader=True)
self.__create_group(parent_group_invited_id, pcs_id, exclude_self_reader=True)
def set_external_reviewer_recruitment_groups(self, name='External_Reviewers', create_paper_groups=False):
if name == self.reviewers_name:
raise openreview.OpenReviewException(f'Can not use {name} as external reviewer name')
parent_group_id = self.get_committee_id(name=name)
parent_group_invited_id = parent_group_id + '/Invited'
self.__create_group(parent_group_id, self.id)
self.__create_group(parent_group_invited_id, self.id, exclude_self_reader=True)
## create groups per submissions
def create_paper_group(submission):
paper_group_id = self.get_committee_id(name=name, number=submission.number)
self.client.post_group(openreview.Group(
id=paper_group_id,
readers=[self.id, paper_group_id],
writers=[self.id],
signatures=[self.id],
signatories=[self.id],
members=[]
))
paper_invited_group_id = self.get_committee_id(name=name + '/Invited', number=submission.number)
return self.client.post_group(openreview.Group(
id=paper_invited_group_id,
readers=[self.id],
writers=[self.id],
signatures=[self.id],
signatories=[self.id],
members=[]
))
if create_paper_groups:
tools.concurrent_requests(create_paper_group, self.get_submissions(), desc='Creating paper groups')
def set_reviewers(self, emails = []):
readers = []
if self.use_senior_area_chairs:
readers.append(self.get_senior_area_chairs_id())
if self.use_area_chairs:
readers.append(self.get_area_chairs_id())
self.__create_group(
group_id = self.get_reviewers_id(),
group_owner_id = self.get_area_chairs_id() if self.use_area_chairs else self.id,
members = emails,
additional_readers = readers)
return self.__set_reviewer_page()
def set_ethics_reviewers(self, emails = []):
readers = [self.id, self.get_ethics_chairs_id()]
ethics_reviewer_group = self.__create_group(
group_id = self.get_ethics_reviewers_id(),
group_owner_id = self.get_ethics_chairs_id(),
members = emails,
additional_readers = readers)
return self.webfield_builder.set_ethics_reviewer_page(self, ethics_reviewer_group)
def set_ethics_chairs(self, emails = []):
readers = [self.id, self.get_ethics_chairs_id()]
ethics_reviewer_group = self.__create_group(
group_id = self.get_ethics_chairs_id(),
group_owner_id = self.id,
members = emails,
additional_readers = readers)
return self.webfield_builder.set_ethics_chairs_page(self, ethics_reviewer_group)
def set_authors(self):
# Creating venue level authors group
authors_group = self.__create_group(self.get_authors_id(), self.id, additional_readers=['everyone'])
# Creating venue level accepted authors group
self.__create_group(self.get_accepted_authors_id(), self.id)
return self.webfield_builder.set_author_page(self, authors_group)
def set_impersonators(self, group_ids = []):
# Only super user can call this
conference_group = tools.get_group(self.client, self.id)
conference_group.impersonators = group_ids
self.client.post_group(conference_group)
[docs]
@deprecated(version='1.0.24', reason="Use setup_committeee_matching() instead")
def setup_matching(self, committee_id=None, affinity_score_file=None, tpms_score_file=None, elmo_score_file=None, build_conflicts=None, alternate_matching_group=None):
if committee_id is None:
committee_id=self.get_reviewers_id()
if self.use_senior_area_chairs and committee_id == self.get_senior_area_chairs_id() and not alternate_matching_group:
alternate_matching_group = self.get_area_chairs_id()
conference_matching = matching.Matching(self, self.client.get_group(committee_id), alternate_matching_group)
return conference_matching.setup(affinity_score_file, tpms_score_file, elmo_score_file, build_conflicts)
def setup_committee_matching(self, committee_id=None, compute_affinity_scores=False, compute_conflicts=False, compute_conflicts_n_years=None, alternate_matching_group=None, submission_track=None): ## only APi 2 venue can select the submission track
if committee_id is None:
committee_id=self.get_reviewers_id()
if self.use_senior_area_chairs and committee_id == self.get_senior_area_chairs_id() and not alternate_matching_group:
alternate_matching_group = self.get_area_chairs_id()
conference_matching = matching.Matching(self, self.client.get_group(committee_id), alternate_matching_group)
return conference_matching.setup(compute_affinity_scores=compute_affinity_scores, build_conflicts=compute_conflicts, compute_conflicts_n_years=compute_conflicts_n_years)
def set_matching_conflicts(self, profile_id, build_conflicts=True):
# Re-generates conflicts for a single reviewer
committee_id=self.get_reviewers_id()
conference_matching = matching.Matching(self, self.client.get_group(committee_id))
return conference_matching.append_note_conflicts(profile_id, build_conflicts)
def set_matching_alternate_conflicts(self, committee_id, source_committee_id, source_assignment_title, conflict_label):
conference_matching = matching.Matching(self, self.client.get_group(source_committee_id), committee_id)
conference_matching.compute_alternate_conflicts(source_assignment_title, conflict_label)
def setup_assignment_recruitment(self, committee_id, hash_seed, due_date, assignment_title=None, invitation_labels={}, email_template=None):
conference_matching = matching.Matching(self, self.client.get_group(committee_id))
return conference_matching.setup_invite_assignment(hash_seed, assignment_title, due_date, invitation_labels=invitation_labels, email_template=email_template)
def set_assignment(self, user, number, is_area_chair = False):
if is_area_chair:
self.client.add_members_to_group(self.get_area_chairs_id(number=number), user)
else:
self.client.add_members_to_group(self.get_reviewers_id(number=number), user)
def set_assignments(self, assignment_title, committee_id, enable_reviewer_reassignment=False, overwrite=False):
match_group = self.client.get_group(committee_id)
conference_matching = matching.Matching(self, match_group)
return conference_matching.deploy(assignment_title, overwrite, enable_reviewer_reassignment)
def set_invite_assignments(self, assignment_title, committee_id, enable_reviewer_reassignment=False, email_template=None):
match_group = self.client.get_group(committee_id)
conference_matching = matching.Matching(self, match_group)
return conference_matching.deploy_invite(assignment_title, enable_reviewer_reassignment, email_template)
def set_default_load(self, default_load, reviewers_name = 'Reviewers'):
self.default_reviewer_load[reviewers_name] = default_load
def recruit_reviewers(self,
invitees = [],
title = None,
message = None,
reviewers_name = 'Reviewers',
remind = False,
invitee_names = [],
retry_declined=False,
contact_info = 'info@openreview.net',
reduced_load_on_decline=None,
default_load=0,
allow_overlap_official_committee=False,
accept_recruitment_template=None):
pcs_id = self.get_program_chairs_id()
reviewers_id = self.id + '/' + reviewers_name
reviewers_declined_id = reviewers_id + '/Declined'
reviewers_invited_id = reviewers_id + '/Invited'
reviewers_accepted_id = reviewers_id
hash_seed = '1234'
invitees = [e.lower() if '@' in e else e for e in invitees if len(e) > 0]
self.set_default_load(default_load, reviewers_name)
reviewers_accepted_group = self.__create_group(reviewers_accepted_id, pcs_id)
reviewers_declined_group = self.__create_group(reviewers_declined_id, pcs_id)
reviewers_invited_group = self.__create_group(reviewers_invited_id, pcs_id)
official_committee_roles=self.get_committee_names()
committee_roles = official_committee_roles if (reviewers_name in official_committee_roles and not allow_overlap_official_committee) else [reviewers_name]
recruitment_status = {
'invited': [],
'reminded': [],
'already_invited': {},
'already_member': {},
'errors': {}
}
options = {
'reviewers_name': reviewers_name,
'reviewers_accepted_id': reviewers_accepted_id,
'reviewers_invited_id': reviewers_invited_id,
'reviewers_declined_id': reviewers_declined_id,
'hash_seed': hash_seed,
'reduced_load_id': None,
'allow_overlap_official_committee': allow_overlap_official_committee,
'reduced_load_on_decline': reduced_load_on_decline,
'accept_recruitment_template': accept_recruitment_template
}
if reduced_load_on_decline and not self.use_recruitment_template:
options['reduced_load_id'] = self.get_invitation_id('Reduced_Load', prefix = reviewers_id)
invitation = self.invitation_builder.set_reviewer_reduced_load_invitation(self, options)
invitation = self.webfield_builder.set_reduced_load_page(self.id, invitation, self.get_homepage_options())
invitation = self.invitation_builder.set_reviewer_recruiter_invitation(self, options)
invitation = self.webfield_builder.set_recruit_page(self, invitation, options['reduced_load_id'])
role = reviewers_name.replace('_', ' ')
role = role[:-1] if role.endswith('s') else role
invitation_link = '''To response the invitation, please click on the following link:
{{invitation_url}}
''' if self.use_recruitment_template else '''To ACCEPT the invitation, please click on the following link:
{{accept_url}}
To DECLINE the invitation, please click on the following link:
{{decline_url}}
'''
recruit_message = f'''Dear {{{{fullname}}}},
You have been nominated by the program chair committee of {self.get_short_name()} to serve as {role}. As a respected researcher in the area, we hope you will accept and help us make {self.get_short_name()} a success.
You are also welcome to submit papers, so please also consider submitting to {self.get_short_name()}.
We will be using OpenReview.net with the intention of have an engaging reviewing process inclusive of the whole community.
{invitation_link}
Please answer within 10 days.
If you accept, please make sure that your OpenReview account is updated and lists all the emails you are using. Visit http://openreview.net/profile after logging in.
If you have any questions, please contact {{{{contact_info}}}}.
Cheers!
Program Chairs
'''
recruit_message_subj = f'[{self.get_short_name()}]: Invitation to serve as {role.title()}'
if title:
recruit_message_subj = title
if message:
recruit_message = message
if remind:
invited_reviewers = reviewers_invited_group.members
print('Sending reminders for recruitment invitations')
for reviewer_id in tqdm(invited_reviewers, desc='remind_reviewers'):
memberships = [g.id for g in self.client.get_groups(member=reviewer_id, regex=reviewers_id)] if tools.get_group(self.client, reviewer_id) else []
if reviewers_id not in memberships and reviewers_declined_id not in memberships:
reviewer_name = 'invitee'
if reviewer_id.startswith('~') :
reviewer_name = None
elif (reviewer_id in invitees) and invitee_names:
reviewer_name = invitee_names[invitees.index(reviewer_id)]
try:
tools.recruit_reviewer(self.client, reviewer_id, reviewer_name,
hash_seed,
invitation.id,
recruit_message,
'Reminder: ' + recruit_message_subj,
reviewers_invited_id,
contact_info = contact_info,
verbose = False)
recruitment_status['reminded'].append(reviewer_id)
except Exception as e:
self.client.remove_members_from_group(reviewers_invited_group, reviewer_id)
if repr(e) not in recruitment_status['errors']:
recruitment_status['errors'][repr(e)] = []
recruitment_status['errors'][repr(e)].append(reviewer_id)
if retry_declined:
declined_reviewers = reviewers_declined_group.members
print('Sending retry to declined reviewers')
for reviewer_id in tqdm(declined_reviewers, desc='retry_declined'):
memberships = [g.id for g in self.client.get_groups(member=reviewer_id, regex=reviewers_id)] if tools.get_group(self.client, reviewer_id) else []
if reviewers_id not in memberships:
reviewer_name = 'invitee'
if reviewer_id.startswith('~'):
reviewer_name = None
elif (reviewer_id in invitees) and invitee_names:
reviewer_name = invitee_names[invitees.index(reviewer_id)]
try:
tools.recruit_reviewer(self.client, reviewer_id, reviewer_name,
hash_seed,
invitation.id,
recruit_message,
recruit_message_subj,
reviewers_invited_id,
contact_info = contact_info,
verbose = False)
except Exception as e:
self.client.remove_members_from_group(reviewers_invited_group, reviewer_id)
if repr(e) not in recruitment_status['errors']:
recruitment_status['errors'][repr(e)] = []
recruitment_status['errors'][repr(e)].append(reviewer_id)
print('Sending recruitment invitations')
for index, email in enumerate(tqdm(invitees, desc='send_invitations')):
memberships = [g.id for g in self.client.get_groups(member=email, regex=self.id)] if tools.get_group(self.client, email) else []
invited_roles = [f'{self.id}/{role}/Invited' for role in committee_roles]
member_roles = [f'{self.id}/{role}' for role in committee_roles]
invited_group_ids=list(set(invited_roles) & set(memberships))
member_group_ids=list(set(member_roles) & set(memberships))
if invited_group_ids:
invited_group_id=invited_group_ids[0]
if invited_group_id not in recruitment_status['already_invited']:
recruitment_status['already_invited'][invited_group_id] = []
recruitment_status['already_invited'][invited_group_id].append(email)
elif member_group_ids:
member_group_id = member_group_ids[0]
if member_group_id not in recruitment_status['already_member']:
recruitment_status['already_member'][member_group_id] = []
recruitment_status['already_member'][member_group_id].append(email)
else:
name = invitee_names[index] if (invitee_names and index < len(invitee_names)) else None
if not name and not email.startswith('~'):
name = 'invitee'
try:
tools.recruit_reviewer(self.client, email, name,
hash_seed,
invitation.id,
recruit_message,
recruit_message_subj,
reviewers_invited_id,
contact_info = contact_info,
verbose = False)
recruitment_status['invited'].append(email)
except Exception as e:
self.client.remove_members_from_group(reviewers_invited_group, email)
if repr(e) not in recruitment_status['errors']:
recruitment_status['errors'][repr(e)] = []
recruitment_status['errors'][repr(e)].append(email)
return recruitment_status
## temporary function, move to somewhere else
def remind_registration_stage(self, subject, message, committee_id, invitation_id):
reviewers = self.client.get_group(committee_id).members
profiles_by_email = self.client.search_profiles(confirmedEmails=[m for m in reviewers if '@' in m])
confirmations = {c.tauthor: c for c in self.client.get_all_notes(invitation=invitation_id)}
print('reviewers:', len(reviewers))
print('profiles:', len(profiles_by_email))
print('confirmations', len(confirmations))
reminders=[]
confirmed=[]
for reviewer in reviewers:
if reviewer in profiles_by_email:
emails = profiles_by_email[reviewer].content['emails']
found = False
for email in emails:
if email in confirmations:
found = True
if not found:
reminders.append(reviewer)
else:
confirmed.append(reviewer)
else:
reminders.append(reviewer)
self.client.post_message(subject, reminders, message)
return reminders
def set_homepage_decisions(self, invitation_name = 'Decision', decision_heading_map = None):
home_group = self.client.get_group(self.id)
options = {}
options['decision_heading_map'] = decision_heading_map
self.webfield_builder.set_home_page(conference = self, group = home_group, layout = 'decisions', options = options)
def get_submissions_attachments(self, field_name='pdf', field_type='pdf', folder_path='./pdfs', accepted=False):
print('Loading submissions...')
submissions = list(self.get_submissions(accepted))
pbar = tqdm(total=len(submissions), desc='Downloading files...')
if not os.path.exists(folder_path):
os.makedirs(folder_path)
def get_attachment_file(submission):
pbar.update(1)
if field_name in submission.content:
paper_number = submission.number
try:
with open('{folder_path}/Paper{number}.{field_type}'.format(folder_path=folder_path, number=paper_number, field_type=field_type), 'wb') as f:
f.write(self.client.get_attachment(submission.id, field_name))
except Exception as e:
print('Error during attachment download for paper number {}, error: {}'.format(submission.number, e))
return True
return None
futures = []
with concurrent.futures.ThreadPoolExecutor() as executor:
# Start the load operations and mark each future with its URL
for submission in submissions:
futures.append(executor.submit(get_attachment_file, submission))
pbar.close()
for future in futures:
result = future.result()
def post_decision_stage(self, reveal_all_authors=False, reveal_authors_accepted=False, decision_heading_map=None, submission_readers=None, hide_fields=[]):
publication_date = openreview.tools.datetime_millis(datetime.datetime.utcnow())
submissions = self.get_submissions(details='original,directReplies')
def is_release_authors(is_note_accepted):
return reveal_all_authors or (reveal_authors_accepted and is_note_accepted)
if submission_readers:
self.submission_stage.readers = submission_readers
def update_note(submission):
decisions = [reply for reply in submission.details['directReplies'] if self.get_invitation_id(self.decision_stage.name, submission.number) == reply['invitation']]
decision_note = openreview.Note.from_json(decisions[0]) if decisions else None
note_accepted = decision_note and 'Accept' in decision_note.content['decision']
submission.readers = self.submission_stage.get_readers(self, submission.number, decision_note.content['decision'] if decision_note else None)
#double-blind
if self.submission_stage.double_blind:
release_authors = is_release_authors(note_accepted)
submission.content = {}
if not release_authors:
submission.content['authors'] = ['Anonymous']
submission.content['authorids'] = [self.get_authors_id(number=submission.number)]
for field in hide_fields:
submission.content[field] = ''
bibtex = tools.generate_bibtex(
openreview.Note.from_json(submission.details['original']),
venue_fullname=self.name,
year=str(self.year),
url_forum=submission.forum,
paper_status = 'accepted' if note_accepted else 'rejected',
anonymous=(not release_authors)
)
#single-blind
else:
bibtex = tools.generate_bibtex(
submission,
venue_fullname=self.name,
year=str(self.year),
url_forum=submission.forum,
paper_status = 'accepted' if note_accepted else 'rejected',
anonymous=False
)
self.client.post_note(submission)
#add venue_id, venue and bibtex revision to all notes
venue = self.short_name
decision_option = decision_note.content['decision'] if decision_note else ''
venue = tools.decision_to_venue(venue, decision_option)
original_id = submission.id if not self.submission_stage.double_blind else submission.details['original']['id']
revision_note = self.client.post_note(openreview.Note(
invitation = f'{self.support_user}/-/{self.venue_revision_name}',
forum = original_id,
referent = original_id,
readers = ['everyone'],
writers = [self.id],
signatures = [self.id],
content = {
'venue': venue,
'venueid': self.id,
'_bibtex': bibtex
},
pdate = publication_date if (note_accepted and submission.pdate is None) else submission.pdate,
odate = publication_date if ('everyone' in submission.readers and submission.odate is None) else submission.odate
))
tools.concurrent_requests(update_note, submissions)
venue_heading_map = {}
if decision_heading_map:
for decision, tab_name in decision_heading_map.items():
venue_heading_map[tools.decision_to_venue(self.short_name, decision)] = tab_name
if venue_heading_map:
self.set_homepage_decisions(decision_heading_map=venue_heading_map)
self.client_v2.remove_members_from_group('active_venues', self.id)
# expire recruitment invitations
self.expire_recruitment_invitations()
def send_decision_notifications(self, decision_options, messages):
paper_notes = self.get_submissions(details='directReplies')
def send_notification(note):
decision_note = None
for reply in note.details['directReplies']:
if reply['invitation'].endswith('/-/' + self.decision_stage.name):
decision_note = reply
break
subject = "[{SHORT_NAME}] Decision notification for your submission {submission_number}: {submission_title}".format(
SHORT_NAME=self.get_short_name(),
submission_number=note.number,
submission_title=note.content['title']
)
if decision_note and not self.client.get_messages(subject=subject):
message = messages[decision_note['content']['decision']]
final_message = message.replace("{{submission_title}}", note.content['title'])
final_message = final_message.replace("{{forum_url}}", f'https://openreview.net/forum?id={note.id}')
self.client.post_message(subject, recipients=note.content['authorids'], message=final_message)
tools.concurrent_requests(send_notification, paper_notes)
def post_decisions(self, decisions_file):
decisions_data = list(csv.reader(StringIO(decisions_file.decode()), delimiter=","))
paper_notes = {n.number: n for n in self.get_submissions(details='directReplies')}
forum_note = self.client.get_note(self.request_form_id)
def post_decision(paper_decision):
if len(paper_decision) < 2:
raise openreview.OpenReviewException(
"Not enough values provided in the decision file. Expected values are: paper_number, decision, comment")
if len(paper_decision) > 3:
raise openreview.OpenReviewException(
"Too many values provided in the decision file. Expected values are: paper_number, decision, comment"
)
if len(paper_decision) == 3:
paper_number, decision, comment = paper_decision
else:
paper_number, decision = paper_decision
comment = ''
paper_number = int(paper_number)
print(f"Posting Decision {decision} for Paper {paper_number}")
paper_note = paper_notes.get(paper_number, None)
if not paper_note:
raise openreview.OpenReviewException(
f"Paper {paper_number} not found. Please check the submitted paper numbers."
)
decisions = [reply for reply in paper_note.details['directReplies'] if self.get_invitation_id(self.decision_stage.name, paper_note.number) == reply['invitation']]
paper_decision_note = openreview.Note.from_json(decisions[0]) if decisions else None
if paper_decision_note:
paper_decision_note.readers = self.decision_stage.get_readers(conference=self, number=paper_note.number)
paper_decision_note.nonreaders = self.decision_stage.get_nonreaders(conference=self, number=paper_note.number)
paper_decision_note.content = {
'title': 'Paper Decision',
'decision': decision.strip(),
'comment': comment,
}
else:
paper_decision_note = openreview.Note(
invitation=self.get_invitation_id(name=self.decision_stage.name, number=paper_note.number),
writers=[self.get_program_chairs_id()],
readers=self.decision_stage.get_readers(conference=self, number=paper_note.number),
nonreaders=self.decision_stage.get_nonreaders(conference=self, number=paper_note.number),
signatures=[self.get_program_chairs_id()],
content={
'title': 'Paper Decision',
'decision': decision.strip(),
'comment': comment,
},
forum=paper_note.forum,
replyto=paper_note.forum
)
self.client.post_note(paper_decision_note)
print(f"Decision posted for Paper {paper_number}")
futures = []
futures_param_mapping = {}
gathering_responses = tqdm(total=len(decisions_data), desc='Gathering Responses')
results = []
errors = {}
with ThreadPoolExecutor(max_workers=min(6, cpu_count() - 1)) as executor:
for _decision in decisions_data:
_future = executor.submit(post_decision, _decision)
futures.append(_future)
futures_param_mapping[_future] = str(_decision)
for future in futures:
gathering_responses.update(1)
try:
results.append(future.result())
except Exception as e:
errors[futures_param_mapping[future]] = e.args[0] if isinstance(e, openreview.OpenReviewException) else repr(e)
gathering_responses.close()
error_status = ''
if errors:
error_status = f'''
Total Errors: {len(errors)}
```python
{json.dumps({key: errors[key] for key in list(errors.keys())[:10]}, indent=2)}
```
'''
status_note = openreview.Note(
invitation=self.support_user + '/-/Request' + str(forum_note.number) + '/Decision_Upload_Status',
forum=self.request_form_id,
replyto=self.request_form_id,
readers=[self.get_program_chairs_id(), self.support_user],
writers=[],
signatures=[self.support_user],
content={
'title': 'Decision Upload Status',
'decision_posted': f'''{len(results)} Papers''',
'error': error_status
}
)
self.client.post_note(status_note)
def expire_recruitment_invitations(self):
recruitment_invitation = openreview.tools.get_invitation(self.client, self.get_invitation_id(f'Recruit_{self.reviewers_name}'))
if recruitment_invitation:
self.expire_invitation(recruitment_invitation.id)
recruitment_invitation = openreview.tools.get_invitation(self.client, self.get_invitation_id(f'Recruit_{self.area_chairs_name}'))
if recruitment_invitation:
self.expire_invitation(recruitment_invitation.id)
recruitment_invitation = openreview.tools.get_invitation(self.client, self.get_invitation_id(f'Recruit_{self.senior_area_chairs_name}'))
if recruitment_invitation:
self.expire_invitation(recruitment_invitation.id)
recruitment_invitation = openreview.tools.get_invitation(self.client, self.get_invitation_id(f'Recruit_{self.ethics_reviewers_name}'))
if recruitment_invitation:
self.expire_invitation(recruitment_invitation.id)
[docs]
class ConferenceBuilder(object):
def __init__(self, client, support_user=None):
self.client = client
self.client_v2 = openreview.Client(baseurl=openreview.tools.get_base_urls(client)[1], token=client.token)
self.conference = Conference(client)
self.webfield_builder = webfield.WebfieldBuilder(client)
self.submission_stage = None
self.expertise_selection_stage = None
self.registration_stages = []
self.bid_stages = []
self.review_stage = None
self.ethics_review_stage = None
self.review_rebuttal_stage = None
self.comment_stage = None
self.meta_review_stage = None
self.decision_stage = None
self.program_chairs_ids = []
self.set_conference_support_user(support_user)
def __build_groups(self, conference_id):
path_components = conference_id.split('/')
paths = ['/'.join(path_components[0:index+1]) for index, path in enumerate(path_components)]
groups = []
for p in paths:
group = tools.get_group(self.client, id = p)
if group is None:
group = self.client.post_group(openreview.Group(
id = p,
readers = ['everyone'],
nonreaders = [],
writers = [p],
signatories = [p],
signatures = ['~Super_User1'],
members = [],
details = { 'writable': True })
)
self.conference.new = True
groups.append(group)
return groups
def set_conference_id(self, id):
self.conference.set_id(id)
def set_conference_support_user(self, user):
if user:
self.conference.support_user = user
def set_conference_name(self, name):
self.conference.set_name(name)
def set_conference_short_name(self, name):
self.conference.set_short_name(name)
def set_conference_year(self, year):
self.conference.set_year(year)
def set_conference_reviewers_name(self, name):
self.conference.set_reviewers_name(name)
def set_reviewer_roles(self, roles):
self.conference.reviewer_roles = roles
def set_conference_area_chairs_name(self, name):
self.conference.has_area_chairs(True)
self.conference.set_area_chairs_name(name)
def set_area_chair_roles(self, roles):
self.conference.area_chair_roles = roles
def set_senior_area_chair_roles(self, roles):
self.conference.senior_area_chair_roles = roles
def set_conference_program_chairs_name(self, name):
self.conference.set_program_chairs_name(name)
def set_conference_program_chairs_ids(self, ids):
self.program_chairs_ids = ids
def set_homepage_header(self, header):
self.conference.set_homepage_header(header)
def set_authorpage_header(self, header):
self.conference.set_authorpage_header(header)
def set_reviewerpage_header(self, header):
self.conference.set_reviewerpage_header(header)
def set_areachairpage_header(self, header):
self.conference.has_area_chairs(True)
self.conference.set_areachairpage_header(header)
def set_homepage_layout(self, layout):
self.conference.set_homepage_layout(layout)
def set_venue_heading_map(self, decision_heading_map):
self.conference.set_venue_heading_map(decision_heading_map)
def has_area_chairs(self, has_area_chairs):
self.conference.has_area_chairs(has_area_chairs)
def has_senior_area_chairs(self, has_senior_area_chairs):
self.conference.has_senior_area_chairs(has_senior_area_chairs)
def has_ethics_chairs(self, has_ethics_chairs):
self.conference.use_ethics_chairs = has_ethics_chairs
def has_ethics_reviewers(self, has_ethics_reviewers):
self.conference.use_ethics_reviewers = has_ethics_reviewers
def has_secondary_area_chairs(self, has_secondary_area_chairs):
self.conference.has_secondary_area_chairs(has_secondary_area_chairs)
def enable_reviewer_reassignment(self, enable):
self.conference.enable_reviewer_reassignment = enable
def set_submission_stage(
self,
name='Submission',
start_date=None,
due_date=None,
second_due_date=None,
public=None, ## deprecated, please use readers parameter to specify the readers of the submissions
double_blind=False,
additional_fields={},
remove_fields=[],
hide_fields=[],
subject_areas=[],
email_pcs=False,
create_groups=False,
create_review_invitation=False,
withdraw_submission_exp_date=None,
withdrawn_submission_public=False,
withdrawn_submission_reveal_authors=False,
email_pcs_on_withdraw=False,
desk_rejected_submission_public=False,
desk_rejected_submission_reveal_authors=False,
email_pcs_on_desk_reject=False,
author_names_revealed=False,
papers_released=False,
readers=None,
author_reorder_after_first_deadline=False,
submission_email=None,
force_profiles=False
):
submissions_readers=[SubmissionStage.Readers.SENIOR_AREA_CHAIRS_ASSIGNED, SubmissionStage.Readers.AREA_CHAIRS_ASSIGNED, SubmissionStage.Readers.REVIEWERS_ASSIGNED]
if readers is not None:
submissions_readers=readers
if public:
submissions_readers=[SubmissionStage.Readers.EVERYONE]
self.submission_stage = SubmissionStage(
name,
start_date,
due_date,
second_due_date,
submissions_readers,
double_blind,
additional_fields,
remove_fields,
hide_fields,
subject_areas,
email_pcs,
create_groups,
create_review_invitation,
withdraw_submission_exp_date,
withdrawn_submission_public,
withdrawn_submission_reveal_authors,
email_pcs_on_withdraw,
desk_rejected_submission_public,
desk_rejected_submission_reveal_authors,
email_pcs_on_desk_reject,
author_names_revealed,
papers_released,
author_reorder_after_first_deadline,
submission_email,
force_profiles,
{},
[]
)
def set_expertise_selection_stage(self, start_date = None, due_date = None, include_option=False):
self.expertise_selection_stage = ExpertiseSelectionStage(start_date, due_date, include_option)
def set_registration_stage(self, committee_id, name = 'Registration', start_date = None, due_date = None, expdate = None, additional_fields = {}, instructions = None):
default_instructions = 'Help us get to know our committee better and the ways to make the reviewing process smoother by answering these questions. If you don\'t see the form below, click on the blue "Registration" button.\n\nLink to Profile: https://openreview.net/profile/edit \nLink to Expertise Selection interface: https://openreview.net/invitation?id={conference_id}/-/Expertise_Selection'.format(conference_id = self.conference.get_id())
reviewer_instructions = instructions if instructions else default_instructions
self.registration_stages.append(RegistrationStage(committee_id, name, start_date, due_date, expdate, additional_fields, reviewer_instructions))
def set_bid_stages(self, stages):
for stage in stages:
self.conference.bid_stages[stage.committee_id] = stage
def set_review_stage(self, stage):
self.conference.review_stage = stage
def set_review_rebuttal_stage(self, stage):
self.conference.review_rebuttal_stage = stage
def set_review_rating_stage(self, start_date = None, due_date = None, name = None, additional_fields = {}, remove_fields = [], public = False, release_to_reviewers=ReviewRatingStage.Readers.NO_REVIEWERS):
self.review_rating_stage = ReviewRatingStage(start_date, due_date, name, additional_fields, remove_fields, public, release_to_reviewers)
def set_comment_stage(self, stage):
self.conference.comment_stage = stage
def set_meta_review_stage(self, stage):
self.conference.meta_review_stage = stage
def set_decision_stage(self, stage):
self.conference.decision_stage = stage
def set_submission_revision_stage(self, stage):
self.conference.submission_revision_stage = stage
def set_ethics_review_stage(self, stage):
self.conference.ethics_review_stage = stage
def set_request_form_id(self, id):
self.conference.request_form_id = id
def set_support_user(self, support_user):
self.conference.support_user = support_user
def set_default_reviewers_load(self, default_load):
# Required to render a default load in the WebField template
self.conference.set_default_load(default_load, self.conference.reviewers_name)
def set_reviewer_identity_readers(self, readers):
self.conference.reviewer_identity_readers = readers
def set_area_chair_identity_readers(self, readers):
self.conference.area_chair_identity_readers = readers
def set_senior_area_chair_identity_readers(self, readers):
self.conference.senior_area_chair_identity_readers = readers
def use_recruitment_template(self, use_template):
self.conference.use_recruitment_template = use_template
def get_result(self):
if self.conference.reviewer_identity_readers:
if self.conference.use_area_chairs and openreview.stages.IdentityReaders.AREA_CHAIRS_ASSIGNED not in self.conference.reviewer_identity_readers and openreview.stages.IdentityReaders.AREA_CHAIRS not in self.conference.reviewer_identity_readers:
raise openreview.OpenReviewException('Assigned area chairs must see the reviewer identity')
if self.conference.use_senior_area_chairs and openreview.stages.IdentityReaders.SENIOR_AREA_CHAIRS_ASSIGNED not in self.conference.reviewer_identity_readers and openreview.stages.IdentityReaders.SENIOR_AREA_CHAIRS not in self.conference.reviewer_identity_readers:
raise openreview.OpenReviewException('Assigned senior area chairs must see the reviewer identity')
id = self.conference.get_id()
groups = self.__build_groups(id)
for i, g in enumerate(groups[:-1]):
self.webfield_builder.set_landing_page(g, groups[i-1] if i > 0 else None)
host = self.client.get_group(id = 'host', details='writable')
root_id = groups[0].id
home_group = groups[-1]
if root_id == root_id.lower():
root_id = groups[1].id
if host.details.get('writable'):
self.client_v2.add_members_to_group(host, root_id)
home_group.host = root_id
self.client.post_group(home_group)
venues = self.client.get_group(id = 'venues', details='writable')
if venues.details.get('writable'):
self.client_v2.add_members_to_group('venues', home_group.id)
if self.submission_stage:
self.conference.set_submission_stage(self.submission_stage)
## Create committee groups before any other stage that requires them to create groups and/or invitations
self.conference.set_program_chairs(emails=self.program_chairs_ids)
self.conference.set_authors()
self.conference.set_reviewers()
if self.conference.use_senior_area_chairs:
self.conference.set_senior_area_chairs()
if self.conference.use_area_chairs:
self.conference.set_area_chairs()
parent_group_id = groups[-2].id if len(groups) > 1 else ''
venue_heading_map = self.conference.venue_heading_map
groups[-1] = self.webfield_builder.set_home_page(conference = self.conference, group = home_group, layout = self.conference.layout, options = { 'parent_group_id': parent_group_id, 'decision_heading_map': venue_heading_map })
self.conference.set_conference_groups(groups)
if self.conference.use_senior_area_chairs:
self.conference.set_senior_area_chair_recruitment_groups()
if self.conference.use_area_chairs:
self.conference.set_area_chair_recruitment_groups()
if self.conference.use_ethics_chairs:
self.conference.set_ethics_chair_recruitment_groups()
if self.conference.use_ethics_reviewers:
self.conference.set_ethics_reviewer_recruitment_groups()
self.conference.set_reviewer_recruitment_groups()
if self.expertise_selection_stage:
self.conference.set_expertise_selection_stage(self.expertise_selection_stage)
for s in self.registration_stages:
self.conference.set_registration_stage(s)
return self.conference