diff --git a/app/api/helpers/mail.py b/app/api/helpers/mail.py index cb43415219..dab4a7bec8 100644 --- a/app/api/helpers/mail.py +++ b/app/api/helpers/mail.py @@ -19,7 +19,7 @@ MONTHLY_PAYMENT_EMAIL, MONTHLY_PAYMENT_FOLLOWUP_EMAIL, NEW_SESSION, - SESSION_ACCEPT_REJECT, + SESSION_STATE_CHANGE, TEST_MAIL, TICKET_CANCELLED, TICKET_PURCHASED, @@ -32,6 +32,9 @@ from app.models.user import User from app.settings import get_settings +logger = logging.getLogger(__name__) +# pytype: disable=attribute-error + def check_smtp_config(smtp_encryption): """ @@ -168,22 +171,39 @@ def send_email_new_session(email, event_name, link): ) -def send_email_session_accept_reject(email, session, link): +def send_email_session_state_change(email, session): """email for new session""" - session_name = session.title - session_acceptance = session.state + event = session.event + + settings = get_settings() + app_name = settings['app_name'] + frontend_url = settings['frontend_url'] + session_link = "{}/events/{}/sessions/{}".format( + frontend_url, event.identifier, session.id + ) + event_link = f"{frontend_url}/e/{event.identifier}" + + context = { + 'session_name': session.title, + 'session_link': session_link, + 'session_state': session.state, + 'event_name': event.name, + 'event_link': event_link, + 'app_name': app_name, + 'frontend_link': frontend_url, + } + + try: + mail = MAILS[SESSION_STATE_CHANGE][session.state] + except KeyError: + logger.error('No mail found for session state change: ' + session.state) + return + send_email( to=email, - action=SESSION_ACCEPT_REJECT, - subject=MAILS[SESSION_ACCEPT_REJECT]['subject'].format( - session_name=session_name, acceptance=session_acceptance - ), - html=MAILS[SESSION_ACCEPT_REJECT]['message'].format( - email=email, - session_name=session_name, - acceptance=session_acceptance, - link=link, - ), + action=SESSION_STATE_CHANGE, + subject=mail['subject'].format(**context), + html=mail['message'].format(**context), ) diff --git a/app/api/helpers/notification.py b/app/api/helpers/notification.py index ee8fcdf99a..9dc5703bab 100644 --- a/app/api/helpers/notification.py +++ b/app/api/helpers/notification.py @@ -11,7 +11,7 @@ get_monthly_payment_follow_up_notification_actions, get_monthly_payment_notification_actions, get_new_session_notification_actions, - get_session_accept_reject_notification_actions, + get_session_state_change_notification_actions, get_ticket_purchased_attendee_notification_actions, get_ticket_purchased_notification_actions, get_ticket_purchased_organizer_notification_actions, @@ -27,7 +27,7 @@ MONTHLY_PAYMENT_FOLLOWUP_NOTIF, MONTHLY_PAYMENT_NOTIF, NEW_SESSION, - SESSION_ACCEPT_REJECT, + SESSION_STATE_CHANGE, TICKET_CANCELLED, TICKET_CANCELLED_ORGANIZER, TICKET_PURCHASED, @@ -73,9 +73,9 @@ def send_notif_new_session_organizer(user, event_name, link, session_id): send_notification(user, title, message, actions) -def send_notif_session_accept_reject(user, session_name, acceptance, link, session_id): +def send_notif_session_state_change(user, session_name, acceptance, link, session_id): """ - Send notification to the session creator about a session being accepted or rejected. + Send notification to the session creator about a session status being changed. :param user: :param session_name: :param acceptance: @@ -84,11 +84,11 @@ def send_notif_session_accept_reject(user, session_name, acceptance, link, sessi :return: """ message_settings = MessageSettings.query.filter_by( - action=SESSION_ACCEPT_REJECT + action=SESSION_STATE_CHANGE ).first() if not message_settings or message_settings.notification_status == 1: - actions = get_session_accept_reject_notification_actions(session_id, link) - notification = NOTIFS[SESSION_ACCEPT_REJECT] + actions = get_session_state_change_notification_actions(session_id, link) + notification = NOTIFS[SESSION_STATE_CHANGE] title = notification['title'].format( session_name=session_name, acceptance=acceptance ) @@ -156,7 +156,7 @@ def send_notif_monthly_fee_payment( :return: """ message_settings = MessageSettings.query.filter_by( - action=SESSION_ACCEPT_REJECT + action=SESSION_STATE_CHANGE ).first() if not message_settings or message_settings.notification_status == 1: actions = get_monthly_payment_notification_actions(event_id, link) @@ -184,7 +184,7 @@ def send_followup_notif_monthly_fee_payment( :return: """ message_settings = MessageSettings.query.filter_by( - action=SESSION_ACCEPT_REJECT + action=SESSION_STATE_CHANGE ).first() if not message_settings or message_settings.notification_status == 1: actions = get_monthly_payment_follow_up_notification_actions(event_id, link) diff --git a/app/api/helpers/system_mails.py b/app/api/helpers/system_mails.py index 114512248d..910f2a957d 100644 --- a/app/api/helpers/system_mails.py +++ b/app/api/helpers/system_mails.py @@ -19,8 +19,8 @@ PASSWORD_CHANGE, PASSWORD_RESET, PASSWORD_RESET_AND_VERIFY, - SESSION_ACCEPT_REJECT, SESSION_SCHEDULE, + SESSION_STATE_CHANGE, TEST_MAIL, TICKET_CANCELLED, TICKET_PURCHASED, @@ -52,14 +52,74 @@ + u"
Visit this link to fill up details: {link}" ), }, - SESSION_ACCEPT_REJECT: { + SESSION_STATE_CHANGE: { 'recipient': 'Speaker', - 'subject': u'Session {session_name} has been {acceptance}', - 'message': ( - u"Hi {email},
" - + u"The session {session_name} has been {acceptance} by the organizer. " - + u"
Visit this link to view the session: {link}" - ), + 'pending': { + 'subject': 'Your speaker submission for {event_name} titled {session_name}', + 'message': "Hello,

" + "This is an automatic message from {app_name}.

" + "We have received your submission {session_name} for {event_name}

" + "Your proposal will be reviewed by the event organizers and review team. The current status of your session is now \"Pending\".

" + "You can also check the status and details of your submission on the session page {session_link}. You need to be logged in to view it.

" + "More details about the event are on the event page at {event_link}.

" + "Thank you.
" + "{app_name}", + }, + 'accepted': { + 'subject': 'Accepted! Congratulations Your submission for {event_name} titled {session_name} has been Accepted', + 'message': "Hello,

" + "This is an automatic message from {app_name}.

" + "Your session status for the submission {session_name} for {event_name} was changed to \"Accepted\". Congratulations!

" + "Your proposal will be scheduled by the event organizers and review team. Please (re)confirm your participation with the organizers of the event, if required.

" + "You can also check the status and details of your submission on the session page {session_link}. You need to be logged in to view it.

" + "More details about the event are on the event page at {event_link}.

" + "Thank you.
" + "{app_name}", + }, + 'confirmed': { + 'subject': 'Confirmed! Congratulations Your submission for {event_name} titled {session_name} has been Confirmed', + 'message': "Hello,

" + "This is an automatic message from {app_name}.

" + "Your session status for the submission {session_name} for {event_name} was changed to \"Confirmed\". Congratulations!

" + "Your proposal will be scheduled by the event organizers and review team. Please inform the event organizers in case there are any changes to your participation.

" + "You can also check the status and details of your submission on the session page {session_link}. You need to be logged in to view it.

" + "More details about the event are on the event page at {event_link}.

" + "Thank you.
" + "{app_name}", + }, + 'rejected': { + 'subject': 'Not Accepted. Your submission for {event_name} titled {session_name} was not accepted', + 'message': "Hello,

" + "This is an automatic message from {app_name}.

" + "Unfortunately your submission {session_name} for {event_name} was not accepted. Your session status was changed to \"Rejected\".

" + "The status change was done by event organizers. If there are questions about this change please contact the organizers.

" + "You can also check the status and details of your submission on the session page {session_link}. You need to be logged in to view it.

" + "More details about the event are on the event page at {event_link}.

" + "Thank you.
" + "{app_name}", + }, + 'canceled': { + 'subject': 'Canceled! Your submission for {event_name} titled {session_name} has been Canceled', + 'message': "Hello,

" + "This is an automatic message from {app_name}.

" + "Your session status for the submission {session_name} for {event_name} was changed to \"Canceled\".

" + "The status change was done by event organizers. If there are questions about this change please contact the organizers.

" + "You can also check the status and details of your submission on the session page {session_link}. You need to be logged in to view it.

" + "More details about the event are on the event page at {event_link}.

" + "Thank you.
" + "{app_name}", + }, + 'withdrawn': { + 'subject': 'Withdrawn! Your submission for {event_name} titled {session_name} has been Withdrawn', + 'message': "Hello,

" + "This is an automatic message from {app_name}.

" + "Your session status for the submission {session_name} for {event_name} was changed to \"Withdrawn\".

" + "The status change was done by event organizers. If there are questions about this change please contact the organizers.

" + "You can also check the status and details of your submission on the session page {session_link}. You need to be logged in to view it.

" + "More details about the event are on the event page at {event_link}.

" + "Thank you.
" + "{app_name}", + }, }, SESSION_SCHEDULE: { 'recipient': 'Owner, Organizer, Speaker', diff --git a/app/api/helpers/system_notifications.py b/app/api/helpers/system_notifications.py index 7cbfac1565..e6a45fd24d 100644 --- a/app/api/helpers/system_notifications.py +++ b/app/api/helpers/system_notifications.py @@ -16,8 +16,8 @@ NEW_SESSION, NEXT_EVENT, PASSWORD_CHANGE, - SESSION_ACCEPT_REJECT, SESSION_SCHEDULE, + SESSION_STATE_CHANGE, TICKET_CANCELLED, TICKET_CANCELLED_ORGANIZER, TICKET_PURCHASED, @@ -201,9 +201,9 @@ def get_next_event_notification_actions(event_id, link): return [view_event_action] -def get_session_accept_reject_notification_actions(session_id, link): +def get_session_state_change_notification_actions(session_id, link): """ - Get the actions associated with a notification of a session getting accepted/rejected. + Get the actions associated with a notification of a session status being changed. :param session_id: id of the session. :param link: link to view the session. :return: actions @@ -363,7 +363,7 @@ def get_invite_papers_notification_actions(cfs_link, submit_link): 'message': u"Here are upcoming events: {up_coming_events}.", 'recipient': 'Owner, Organizer, Speaker', }, - SESSION_ACCEPT_REJECT: { + SESSION_STATE_CHANGE: { 'title': u'Session {session_name} has been {acceptance}', 'message': u"The session {session_name} has been" + u" {acceptance} by the Organizer.", diff --git a/app/api/helpers/tasks.py b/app/api/helpers/tasks.py index 54338b066d..3baa72e980 100644 --- a/app/api/helpers/tasks.py +++ b/app/api/helpers/tasks.py @@ -100,8 +100,8 @@ def send_email_task_sendgrid(payload, headers, smtp_config): message.add_attachment(attachment) sendgrid_client = SendGridAPIClient(get_settings()['sendgrid_key']) logging.info( - 'Sending an email regarding {} on behalf of {}'.format( - payload["subject"], payload["from"] + 'Sending an email to {} regarding "{}" on behalf of {}'.format( + payload['to'], payload["subject"], payload["from"] ) ) try: diff --git a/app/api/schema/message_settings.py b/app/api/schema/message_settings.py index 5477836378..b22e501cc4 100644 --- a/app/api/schema/message_settings.py +++ b/app/api/schema/message_settings.py @@ -14,7 +14,7 @@ PASSWORD_RESET = 'Reset Password' PASSWORD_CHANGE = 'Change Password' EVENT_ROLE = 'Event Role Invitation' -SESSION_ACCEPT_REJECT = 'Session Accept or Reject' +SESSION_STATE_CHANGE = 'Session State Change' SESSION_SCHEDULE = 'Session Schedule Change' EVENT_PUBLISH = 'Event Published' AFTER_EVENT = 'After Event' @@ -60,7 +60,7 @@ class Meta: USER_REGISTER, PASSWORD_RESET, EVENT_ROLE, - SESSION_ACCEPT_REJECT, + SESSION_STATE_CHANGE, SESSION_SCHEDULE, NEXT_EVENT, EVENT_PUBLISH, diff --git a/app/api/sessions.py b/app/api/sessions.py index eb2a97f3c8..10247bb364 100644 --- a/app/api/sessions.py +++ b/app/api/sessions.py @@ -9,10 +9,10 @@ from app.api.helpers.db import get_count, safe_query, safe_query_kwargs, save_to_db from app.api.helpers.errors import ForbiddenError from app.api.helpers.files import make_frontend_url -from app.api.helpers.mail import send_email_new_session, send_email_session_accept_reject +from app.api.helpers.mail import send_email_new_session, send_email_session_state_change from app.api.helpers.notification import ( send_notif_new_session_organizer, - send_notif_session_accept_reject, + send_notif_session_state_change, ) from app.api.helpers.permission_manager import has_access from app.api.helpers.query import event_query @@ -259,7 +259,14 @@ def before_update_object(self, session, data, view_kwargs): if new_state and new_state != session.state: # State change detected. Verify that state change is allowed - g.send_email = new_state == 'accepted' or new_state == 'rejected' + g.send_email = new_state in [ + 'accepted', + 'rejected', + 'confirmed', + 'rejected', + 'canceled', + 'withdrawn', + ] key = 'speaker' if is_organizer: key = 'organizer' @@ -294,32 +301,8 @@ def after_update_object(self, session, data, view_kwargs): """ Send email if session accepted or rejected """ if data.get('send_email', None) and g.get('send_email'): - event = session.event - # Email for speaker - speakers = session.speakers - for speaker in speakers: - frontend_url = get_settings()['frontend_url'] - link = "{}/events/{}/sessions/{}".format( - frontend_url, event.identifier, session.id - ) - if not speaker.is_email_overridden: - send_email_session_accept_reject(speaker.email, session, link) - send_notif_session_accept_reject( - speaker, session.title, session.state, link, session.id - ) + notify_for_session(session) - # Email for owner - if session.event.get_owner(): - owner = session.event.get_owner() - owner_email = owner.email - frontend_url = get_settings()['frontend_url'] - link = "{}/events/{}/sessions/{}".format( - frontend_url, event.identifier, session.id - ) - send_email_session_accept_reject(owner_email, session, link) - send_notif_session_accept_reject( - owner, session.title, session.state, link, session.id - ) if 'state' in data: entry_count = SessionsSpeakersLink.query.filter_by(session_id=session.id) if entry_count.count() == 0: @@ -355,6 +338,28 @@ def after_update_object(self, session, data, view_kwargs): } +def notify_for_session(session): + event = session.event + frontend_url = get_settings()['frontend_url'] + link = "{}/events/{}/sessions/{}".format(frontend_url, event.identifier, session.id) + # Email for speaker + speakers = session.speakers + for speaker in speakers: + if not speaker.is_email_overridden: + send_email_session_state_change(speaker.email, session) + send_notif_session_state_change( + speaker.user, session.title, session.state, link, session.id + ) + + # Email for owner + if session.event.get_owner(): + owner = session.event.get_owner() + send_email_session_state_change(owner.email, session) + send_notif_session_state_change( + owner, session.title, session.state, link, session.id + ) + + class SessionRelationshipRequired(ResourceRelationship): """ Session Relationship diff --git a/app/models/mail.py b/app/models/mail.py index d6b93c2ef5..4aeb8293fa 100644 --- a/app/models/mail.py +++ b/app/models/mail.py @@ -13,7 +13,7 @@ PASSWORD_CHANGE = 'Change Password' EVENT_ROLE = 'Event Role Invitation' USER_EVENT_ROLE = 'User Event Role Invitation' -SESSION_ACCEPT_REJECT = 'Session Accept or Reject' +SESSION_STATE_CHANGE = 'Session State Change' SESSION_SCHEDULE = 'Session Schedule Change' EVENT_PUBLISH = 'Event Published' AFTER_EVENT = 'After Event' diff --git a/app/models/message_setting.py b/app/models/message_setting.py index e5eacc8471..603616944a 100644 --- a/app/models/message_setting.py +++ b/app/models/message_setting.py @@ -14,7 +14,7 @@ PASSWORD_RESET = 'Reset Password' PASSWORD_CHANGE = 'Change Password' EVENT_ROLE = 'Event Role Invitation' -SESSION_ACCEPT_REJECT = 'Session Accept or Reject' +SESSION_STATE_CHANGE = 'Session State Change' SESSION_SCHEDULE = 'Session Schedule Change' EVENT_PUBLISH = 'Event Published' AFTER_EVENT = 'After Event' @@ -58,7 +58,7 @@ def _email_message(cls, action, attr=None): USER_REGISTER, PASSWORD_RESET, EVENT_ROLE, - SESSION_ACCEPT_REJECT, + SESSION_STATE_CHANGE, SESSION_SCHEDULE, NEXT_EVENT, EVENT_PUBLISH, @@ -107,7 +107,7 @@ def _notification_message(cls, action, attr=None): NEW_SESSION, SESSION_SCHEDULE, NEXT_EVENT, - SESSION_ACCEPT_REJECT, + SESSION_STATE_CHANGE, INVITE_PAPERS, AFTER_EVENT, EVENT_PUBLISH, diff --git a/app/models/notification.py b/app/models/notification.py index 976191f7af..2eec20afa4 100644 --- a/app/models/notification.py +++ b/app/models/notification.py @@ -13,7 +13,7 @@ EVENT_IMPORTED = 'Event Imported' SESSION_SCHEDULE = 'Session Schedule Change' NEXT_EVENT = 'Next Event' -SESSION_ACCEPT_REJECT = 'Session Accept or Reject' +SESSION_STATE_CHANGE = 'Session State Change' INVITE_PAPERS = 'Invitation For Papers' AFTER_EVENT = 'After Event' EVENT_PUBLISH = 'Event Published' diff --git a/migrations/versions/rev-2020-08-08-16:56:27-61bec04ae010_.py b/migrations/versions/rev-2020-08-08-16:56:27-61bec04ae010_.py new file mode 100644 index 0000000000..a610aa8e7b --- /dev/null +++ b/migrations/versions/rev-2020-08-08-16:56:27-61bec04ae010_.py @@ -0,0 +1,30 @@ +"""empty message + +Revision ID: 61bec04ae010 +Revises: 507022789bcb +Create Date: 2020-08-08 16:56:27.061106 + +""" + +from alembic import op +import sqlalchemy as sa +import sqlalchemy_utils + + +# revision identifiers, used by Alembic. +revision = '61bec04ae010' +down_revision = '507022789bcb' + + +def upgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.execute("UPDATE mails set action = 'Session State Change' WHERE action = 'Session Accept or Reject'") + op.execute("UPDATE message_settings set action = 'Session State Change' WHERE action = 'Session Accept or Reject'") + # ### end Alembic commands ### + + +def downgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.execute("UPDATE mails set action = 'Session Accept or Reject' WHERE action = 'Session State Change'") + op.execute("UPDATE message_settings set action = 'Session Accept or Reject' WHERE action = 'Session State Change'") + # ### end Alembic commands ### diff --git a/populate_db.py b/populate_db.py index e47dfc6270..5c979a685f 100644 --- a/populate_db.py +++ b/populate_db.py @@ -351,7 +351,7 @@ def create_admin_message_settings(): "Invitation For Papers", "After Event", "Ticket(s) Purchased", - "Session Accept or Reject", + "Session State Change", "Event Published", "Event Export Failed", "Event Exported", diff --git a/tests/all/integration/api/helpers/test_notification.py b/tests/all/integration/api/helpers/test_notification.py index e8e75569a4..208b592878 100644 --- a/tests/all/integration/api/helpers/test_notification.py +++ b/tests/all/integration/api/helpers/test_notification.py @@ -8,7 +8,7 @@ send_notif_event_role, send_notif_monthly_fee_payment, send_notif_new_session_organizer, - send_notif_session_accept_reject, + send_notif_session_state_change, send_notif_ticket_purchase_organizer, ) from app.models.notification import Notification @@ -39,9 +39,9 @@ def test_send_notif_new_session_organizer(user): ) -def test_send_notif_session_accept_reject(user): +def test_send_notif_session_state_change(user): """Method to test session accept reject notification""" - send_notif_session_accept_reject( + send_notif_session_state_change( user, 'Homeless Therapy', 'accepted', link, 1, ) notification = Notification.query.first() diff --git a/tests/all/integration/api/helpers/test_systemnotifications.py b/tests/all/integration/api/helpers/test_systemnotifications.py index 89e2306c47..204274eee1 100644 --- a/tests/all/integration/api/helpers/test_systemnotifications.py +++ b/tests/all/integration/api/helpers/test_systemnotifications.py @@ -10,8 +10,8 @@ get_monthly_payment_notification_actions, get_new_session_notification_actions, get_next_event_notification_actions, - get_session_accept_reject_notification_actions, get_session_schedule_notification_actions, + get_session_state_change_notification_actions, get_ticket_purchased_attendee_notification_actions, get_ticket_purchased_notification_actions, get_ticket_purchased_organizer_notification_actions, @@ -277,7 +277,7 @@ def test_session_accept_reject_notif(self): with self.app.test_request_context(): request_url = 'https://localhost/e/session/345525' request_session_id = 1 - response = get_session_accept_reject_notification_actions( + response = get_session_state_change_notification_actions( request_session_id, request_url ) expected_action = NotificationAction(