Skip to content

Commit 8c88117

Browse files
Venue Config Invitations (#2543)
* change subject of email sent to PCs on deployment * invitation to send email notifications to authors * add duedate to invitation set date for emailing decisions * review release inv and subinvitations * add exclusion invitations * add review release date process and add test * add expdate to review release and include Submission_Group inv * create date invitation for reviewers invited response * fix tests * remove hardcoded number --------- Co-authored-by: Melisa Bok <[email protected]>
1 parent 8952481 commit 8c88117

13 files changed

+707
-19
lines changed

openreview/api/client.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2790,6 +2790,7 @@ def __init__(self,
27902790
process = None,
27912791
preprocess = None,
27922792
date_processes = None,
2793+
post_processes = None,
27932794
duedate = None,
27942795
expdate = None,
27952796
cdate = None,
@@ -2835,6 +2836,7 @@ def __init__(self,
28352836
self.process = process
28362837
self.preprocess = preprocess
28372838
self.date_processes = date_processes
2839+
self.post_processes = post_processes
28382840
self.content = content
28392841
self.description = description
28402842
self.instructions = instructions
@@ -2940,6 +2942,8 @@ def to_json(self):
29402942
body['preprocess']=self.preprocess
29412943
if self.date_processes:
29422944
body['dateprocesses']=self.date_processes
2945+
if self.post_processes:
2946+
body['postprocesses']=self.post_processes
29432947
if self.edit is not None:
29442948
if self.type == 'Note':
29452949
body['edit']=self.edit
@@ -3001,6 +3005,8 @@ def from_json(Invitation,i):
30013005
invitation.preprocess = i['preprocess']
30023006
if 'dateprocesses' in i:
30033007
invitation.date_processes = i['dateprocesses']
3008+
if 'postprocesses' in i:
3009+
invitation.post_processes = i['postprocesses']
30043010
if 'edge' in i:
30053011
invitation.edit = i['edge']
30063012
invitation.type = 'Edge'

openreview/workflows/edit_invitations.py

Lines changed: 183 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1310,5 +1310,188 @@ def set_edit_group_deanonymizers_invitation(self, super_invitation_id):
13101310
}
13111311
)
13121312

1313+
self.save_invitation(invitation, replacement=False)
1314+
return invitation
1315+
1316+
def set_edit_email_date_invitation(self, super_invitation_id, due_date=None):
1317+
1318+
venue_id = self.venue_id
1319+
invitation_id = super_invitation_id + '/Dates'
1320+
1321+
invitation = Invitation(
1322+
id = invitation_id,
1323+
invitees = [venue_id],
1324+
signatures = [venue_id],
1325+
readers = [venue_id],
1326+
writers = [venue_id],
1327+
process = self.get_process_content('simple_dual_anonymous_workflow/process/email_decisions_dates_process.py'),
1328+
edit = {
1329+
'content': {
1330+
'activation_date': {
1331+
'value': {
1332+
'param': {
1333+
'type': 'date',
1334+
'range': [ 0, 9999999999999 ]
1335+
}
1336+
}
1337+
}
1338+
},
1339+
'signatures': [self.get_content_value('program_chairs_id', f'{venue_id}/Program_Chairs')],
1340+
'readers': [venue_id],
1341+
'writers': [venue_id],
1342+
'invitation': {
1343+
'id': super_invitation_id,
1344+
'signatures': [venue_id],
1345+
'content': {
1346+
'activation_date': {
1347+
'value': '${4/content/activation_date/value}'
1348+
}
1349+
}
1350+
}
1351+
}
1352+
)
1353+
1354+
if due_date:
1355+
invitation.duedate = due_date
1356+
1357+
self.save_invitation(invitation, replacement=True)
1358+
return invitation
1359+
1360+
def set_edit_email_template_invitation(self, super_invitation_id):
1361+
1362+
venue_id = self.venue_id
1363+
invitation_id = super_invitation_id + '/Message'
1364+
1365+
invitation = Invitation(
1366+
id = invitation_id,
1367+
invitees = [venue_id],
1368+
signatures = [venue_id],
1369+
readers = [venue_id],
1370+
writers = [venue_id],
1371+
edit = {
1372+
'content': {
1373+
'email_subject': {
1374+
'description': 'The subject of the email to be sent to authors. Make sure not to remove the parenthesized tokens.',
1375+
'value': {
1376+
'param': {
1377+
'type': 'string',
1378+
'regex': '.+',
1379+
}
1380+
}
1381+
},
1382+
'email_content': {
1383+
'description': 'The content of the email to be sent to authors. Make sure not to remove the parenthesized tokens.',
1384+
'value': {
1385+
'param': {
1386+
'type': 'string',
1387+
'maxLength': 100000,
1388+
'input': 'textarea'
1389+
}
1390+
}
1391+
}
1392+
},
1393+
'signatures': [self.get_content_value('program_chairs_id', f'{venue_id}/Program_Chairs')],
1394+
'readers': [venue_id],
1395+
'writers': [venue_id],
1396+
'invitation': {
1397+
'id': super_invitation_id,
1398+
'signatures': [venue_id],
1399+
'content': {
1400+
'subject': {
1401+
'value': '${4/content/email_subject/value}'
1402+
},
1403+
'message': {
1404+
'value': '${4/content/email_content/value}'
1405+
}
1406+
}
1407+
}
1408+
}
1409+
)
1410+
1411+
self.save_invitation(invitation, replacement=True)
1412+
return invitation
1413+
1414+
def set_review_release_reply_readers_invitation(self, super_invitation_id, include_signatures=True, due_date=None):
1415+
1416+
venue_id = self.venue_id
1417+
invitation_id = super_invitation_id + '/Readers'
1418+
submission_name = self.get_content_value('submission_name', 'Submission')
1419+
program_chairs_id = self.get_content_value('program_chairs_id', f'{venue_id}/Program_Chairs')
1420+
authors_name = self.domain_group.get_content_value('authors_name', 'Authors')
1421+
reviewers_name = self.domain_group.get_content_value('reviewers_name', 'Reviewers')
1422+
1423+
reply_readers = [
1424+
{'value': program_chairs_id, 'optional': False, 'description': 'Program Chairs'}
1425+
]
1426+
1427+
senior_area_chairs_name = self.get_content_value('senior_area_chairs_name')
1428+
if senior_area_chairs_name:
1429+
reply_readers.extend([
1430+
{'value': self.get_content_value('senior_area_chairs_id'), 'optional': True, 'description': 'All Senior Area Chairs'},
1431+
{'value': f'{venue_id}/{submission_name}' + '${5/content/noteNumber/value}' +f'/{senior_area_chairs_name}', 'optional': True, 'description': 'Assigned Senior Area Chairs'}
1432+
])
1433+
1434+
area_chairs_name = self.get_content_value('area_chairs_name')
1435+
if area_chairs_name:
1436+
reply_readers.extend([
1437+
{'value': self.get_content_value('area_chairs_id'), 'optional': True, 'description': 'All Area Chairs'},
1438+
{'value': f'{venue_id}/{submission_name}' + '${5/content/noteNumber/value}' +f'/{area_chairs_name}', 'optional': True, 'description': 'Assigned Area Chairs'}
1439+
])
1440+
1441+
reply_readers.extend([
1442+
{'value': self.get_content_value('reviewers_id'), 'optional': True, 'description': 'All Reviewers'},
1443+
{'value': f'{venue_id}/{submission_name}' + '${5/content/noteNumber/value}' +f'/{reviewers_name}', 'optional': True, 'description': 'Assigned Reviewers'},
1444+
{'value': f'{venue_id}/{submission_name}' + '${5/content/noteNumber/value}' +f'/{reviewers_name}/Submitted', 'optional': True, 'description': 'Assigned Reviewers who already submitted their review'}
1445+
])
1446+
1447+
if include_signatures:
1448+
reply_readers.append({'value': '${3/signatures}', 'optional': True, 'description': 'Reviewer who submitted the review'})
1449+
1450+
reply_readers.append({'value': f'{venue_id}/{submission_name}' + '${5/content/noteNumber/value}' +f'/{authors_name}', 'optional': True, 'description': 'Submission Authors'})
1451+
1452+
invitation = Invitation(
1453+
id = invitation_id,
1454+
invitees = [venue_id],
1455+
signatures = [venue_id],
1456+
readers = [venue_id],
1457+
writers = [venue_id],
1458+
edit = {
1459+
'signatures': [venue_id],
1460+
'readers': [venue_id],
1461+
'writers': [venue_id],
1462+
'content' :{
1463+
'readers': {
1464+
'value': {
1465+
'param': {
1466+
'type': 'string[]',
1467+
'input': 'select',
1468+
'items': reply_readers
1469+
}
1470+
}
1471+
}
1472+
},
1473+
'invitation': {
1474+
'id': super_invitation_id,
1475+
'signatures': [venue_id],
1476+
'edit': {
1477+
'invitation': {
1478+
'edit': {
1479+
'invitation': {
1480+
'edit': {
1481+
'note': {
1482+
'readers': ['${9/content/readers/value}']
1483+
}
1484+
}
1485+
}
1486+
}
1487+
}
1488+
}
1489+
}
1490+
}
1491+
)
1492+
1493+
if due_date:
1494+
invitation.duedate = due_date
1495+
13131496
self.save_invitation(invitation, replacement=False)
13141497
return invitation
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
def process(client, invitation):
2+
import csv
3+
from tqdm import tqdm
4+
import time
5+
6+
now = openreview.tools.datetime_millis(datetime.datetime.utcnow())
7+
cdate = invitation.cdate
8+
9+
if cdate > now:
10+
## invitation is in the future, do not process
11+
print('invitation is not yet active', cdate)
12+
return
13+
14+
domain = client.get_group(invitation.domain)
15+
venue_id = domain.id
16+
short_name = domain.get_content_value('subtitle')
17+
submission_venue_id = domain.get_content_value('submission_venue_id')
18+
decision_name = domain.get_content_value('decision_name')
19+
submission_name = domain.get_content_value('submission_name')
20+
authors_name = domain.get_content_value('authors_name')
21+
22+
email_subject = invitation.get_content_value('subject')
23+
email_content = invitation.get_content_value('message')
24+
25+
active_submissions = client.get_notes(content={'venueid': submission_venue_id}, details='directReplies')
26+
print('# active submissions:', len(active_submissions))
27+
28+
def send_decision_email(submission):
29+
subject = email_subject.format(
30+
short_name=short_name,
31+
submission_number=submission.number,
32+
submission_title=submission.content['title']['value']
33+
)
34+
decisions = [openreview.api.Note.from_json(reply) for reply in submission.details['directReplies'] if f'{venue_id}/{submission_name}{submission.number}/-/{decision_name}' in reply['invitations']]
35+
if decisions:
36+
decision = decisions[0]
37+
decision_comment = f'''\n\nComment: {decision.content['comment']['value']}''' if 'comment' in decision.content else ''
38+
39+
formatted_decision = f'''Decision: {decision.content['decision']['value']} {decision_comment}'''
40+
41+
message = email_content.format(
42+
submission_number=submission.number,
43+
submission_title=submission.content['title']['value'],
44+
short_name=short_name,
45+
formatted_decision=formatted_decision,
46+
submission_forum=submission.id
47+
)
48+
49+
client.post_message(
50+
subject=subject,
51+
recipients=[f'{venue_id}/{submission_name}{submission.number}/{authors_name}'],
52+
message=message,
53+
invitation=invitation.id
54+
)
55+
56+
openreview.tools.concurrent_requests(send_decision_email, active_submissions)
57+
58+
print(f'{len(active_submissions)} decision emails sent to authors')
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
def process(client, invitation):
2+
3+
now = openreview.tools.datetime_millis(datetime.datetime.utcnow())
4+
cdate = invitation.cdate
5+
6+
if cdate > now:
7+
## invitation is in the future, do not process
8+
print('invitation is not yet active', cdate)
9+
return
10+
11+
client.post_invitation_edit(
12+
invitations=invitation.id,
13+
invitation=openreview.api.Invitation()
14+
)

openreview/workflows/simple_dual_anonymous_workflow/process/deploy_process.py

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -306,6 +306,19 @@ def process(client, edit, invitation):
306306
await_process=True
307307
)
308308

309+
client.post_invitation_edit(
310+
invitations=f'{support_user}/Simple_Dual_Anonymous/Venue_Configuration_Request/-/Review_Release_Template',
311+
signatures=[support_user],
312+
content={
313+
'venue_id': { 'value': venue_id },
314+
'name': { 'value': 'Review_Release' },
315+
'activation_date': { 'value': note.content['submission_deadline']['value'] + (60*60*1000*24*7*5) },
316+
'submission_name': { 'value': 'Submission' },
317+
'review_name': { 'value': 'Review' }
318+
},
319+
await_process=True
320+
)
321+
309322
client.post_invitation_edit(
310323
invitations=f'{support_user}/Simple_Dual_Anonymous/Venue_Configuration_Request/-/Author_Rebuttal_Template',
311324
signatures=[support_user],
@@ -343,6 +356,21 @@ def process(client, edit, invitation):
343356
await_process=True
344357
)
345358

359+
from_email = note.content['abbreviated_venue_name']['value'].replace(' ', '').replace(':', '-').replace('@', '').replace('(', '').replace(')', '').replace(',', '-').lower()
360+
from_email = f'{from_email}[email protected]'
361+
client.post_invitation_edit(
362+
invitations='openreview.net/Support/Simple_Dual_Anonymous/Venue_Configuration_Request/-/Email_Decisions_to_Authors_Template',
363+
signatures=['openreview.net/Support'],
364+
content={
365+
'venue_id': { 'value': venue_id },
366+
'name': { 'value': 'Email_Decisions_to_Authors' },
367+
'activation_date': { 'value': note.content['submission_deadline']['value'] + (60*60*1000*24*7*7) },
368+
'from_name': { 'value': note.content['abbreviated_venue_name']['value'] },
369+
'from_email': { 'value': from_email },
370+
'message_reply_to': { 'value': note.content['contact_email']['value'] },
371+
}
372+
)
373+
346374
client.post_invitation_edit(
347375
invitations=f'{support_user}/Simple_Dual_Anonymous/Venue_Configuration_Request/-/Reviewer_Paper_Aggregate_Score_Template',
348376
signatures=[support_user],
@@ -429,7 +457,7 @@ def process(client, edit, invitation):
429457
'submission_deadline': { 'readers': [support_user] },
430458
'submission_license': { 'readers': [support_user] },
431459
'program_chair_console': { 'value': f'https://openreview.net/group?id={venue_id}/Program_Chairs' },
432-
'workflow_timeline': { 'value': f'https://openreview.net/group/edit?id={venue_id}' }
460+
'workflow_timeline': { 'value': f'https://openreview.net/group/info?id={venue_id}' }
433461
}
434462
)
435463
)
@@ -461,7 +489,7 @@ def process(client, edit, invitation):
461489
- This page is visible to the public. This is where authors will submit papers and reviewers will access their console.
462490
- Venue Program Chairs console: {baseurl}/group?id={venue_id}/Program_Chairs
463491
- This page is visible only to Program Chairs, and is where you can see all submissions as well as stats about your venue.
464-
- Venue Timeline: {baseurl}/group/edit?id={venue_id}
492+
- Venue Timeline: {baseurl}/group/info?id={venue_id}
465493
- This page is visible only to Program Chairs, and is where you can configure your venue, including recruiting reviewers, modifying the submission form and assigning reviewers to submissions.
466494
467495
If you need special features that are not included in your request form, you can post a comment here or contact us at [email protected] and we will assist you. We recommend reaching out to us well in advance and setting deadlines for a Monday.
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
def process(client, edit, invitation):
2+
3+
domain = client.get_group(edit.domain)
4+
venue_id = domain.id
5+
meta_invitation_id = domain.get_content_value('meta_invitation_id')
6+
7+
invitation_id = invitation.id
8+
cdate = edit.content['activation_date']['value']
9+
10+
# edit cdate when activation date is set
11+
client.post_invitation_edit(
12+
invitations=meta_invitation_id,
13+
signatures=[venue_id],
14+
invitation=openreview.api.Invitation(
15+
id=f'{venue_id}/-/Email_Decisions_to_Authors',
16+
cdate= cdate
17+
)
18+
)
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
def process(client, edit, invitation):
2+
3+
support_user = f'{invitation.domain}/Support'
4+
domain = client.get_group(edit.domain)
5+
meta_invitation_id = domain.content.get('meta_invitation_id', {}).get('value')
6+
7+
stage_name = edit.content['name']['value']
8+
9+
cdate = edit.content['activation_date']['value']-1800000 # 30 min before cdate
10+
11+
edit_invitations_builder = openreview.workflows.EditInvitationsBuilder(client, domain.id)
12+
invitation_id = f'{domain.id}/-/{stage_name}'
13+
edit_invitations_builder.set_edit_email_date_invitation(invitation_id, due_date=cdate)
14+
edit_invitations_builder.set_edit_email_template_invitation(invitation_id)

0 commit comments

Comments
 (0)