SciPost Code Repository

Skip to content
Snippets Groups Projects
Code owners
Assign users and groups as approvers for specific file changes. Learn more.
utils.py 78.88 KiB
import datetime

from django.core.mail import EmailMessage, EmailMultiAlternatives
from django.template import Context, Template
from django.utils import timezone

from .constants import NO_REQUIRED_ACTION_STATUSES,\
                       STATUS_REVISION_REQUESTED, STATUS_EIC_ASSIGNED,\
                       STATUS_RESUBMISSION_INCOMING, STATUS_AWAITING_ED_REC
from .exceptions import CycleUpdateDeadlineError

from scipost.utils import EMAIL_FOOTER
from common.utils import BaseMailUtil


class BaseSubmissionCycle:
    """
    The submission cycle may take different approaches. All steps within a specific
    cycle are handles by the class related to the specific cycle chosen. This class
    is meant as an abstract blueprint for the overall submission cycle and its needed
    actions.
    """
    default_days = 28
    may_add_referees = True
    may_reinvite_referees = True
    minimum_referees = 3
    name = None
    required_actions = []
    submission = None
    updated_action = False

    def __init__(self, submission):
        self.submission = submission

    def __str__(self):
        return self.submission.get_refereeing_cycle_display()

    def _update_actions(self):
        """
        Create the list of required_actions for the current submission to be used on the
        editorial page.
        """
        self.required_actions = []
        if self.submission.status in NO_REQUIRED_ACTION_STATUSES:
            '''Submission does not appear in the pool, no action required.'''
            return False

        if self.submission.status == STATUS_REVISION_REQUESTED:
            ''''Editor-in-charge has requested revision'''
            return False

        if self.submission.eicrecommendations.exists():
            '''A Editorial Recommendation has already been submitted. Cycle done.'''
            return False

        if self.submission.status == STATUS_RESUBMISSION_INCOMING:
            """
            Submission is a resubmission and the EIC still has to determine which
            cycle to proceed with.
            """
            self.required_actions.append(('choose_cycle',
                                          'Choose the submission cycle to proceed with.',))
            return False

        comments_to_vet = self.submission.comments.awaiting_vetting().count()
        if comments_to_vet > 0:
            '''There are comments on the submission awaiting vetting.'''
            if comments_to_vet > 1:
                text = '%i Comment\'s have' % comments_to_vet
            else:
                text = 'One Comment has'
            text += ' been delivered but is not yet vetted. Please vet it.'
            self.required_actions.append(('vet_comments', text,))

        nr_ref_inv = self.submission.referee_invitations.count()
        if nr_ref_inv < self.minimum_referees:
            """
            The submission cycle does not meet the criteria of a minimum of
            `self.minimum_referees` referees yet.
            """
            text = 'No' if nr_ref_inv == 0 else 'Only %i' % nr_ref_inv
            text += ' Referees have yet been invited.'
            text += ' At least %i should be.' % self.minimum_referees
            self.required_actions.append(('invite_referees', text,))

        reports_awaiting_vetting = self.submission.reports.awaiting_vetting().count()
        if reports_awaiting_vetting > 0:
            '''There are reports on the submission awaiting vetting.'''
            if reports_awaiting_vetting > 1:
                text = '%i Reports have' % reports_awaiting_vetting
            else:
                text = 'One Report has'
            text += ' been delivered but is not yet vetted. Please vet it.'
            self.required_actions.append(('vet_reports', text,))

        return True

    def reinvite_referees(self, referees, request=None):
        """
        Reinvite referees if allowed. This method does not check if it really is
        a reinvitation or just a new invitation.
        """
        if self.may_reinvite_referees:
            SubmissionUtils.load({'submission': self.submission})
            for referee in referees:
                invitation = referee
                invitation.pk = None  # Duplicate, do not remove the old invitation
                invitation.submission = self.submission
                invitation.reset_content()
                invitation.date_invited = timezone.now()
                invitation.save()
                SubmissionUtils.load({'invitation': invitation}, request)
                SubmissionUtils.reinvite_referees_email()

    def update_deadline(self, period=None):
        """
        Reset the reporting deadline according to current datetime and default cycle length.
        New reporting deadline may be explicitly given as datetime instance.
        """
        if self.submission.status == STATUS_RESUBMISSION_INCOMING:
            raise CycleUpdateDeadlineError('Submission has invalid status: %s'
                                           % self.submission.status)
        delta_d = period or self.default_days
        deadline = timezone.now() + datetime.timedelta(days=delta_d)
        self.submission.reporting_deadline = deadline
        self.submission.save()


    def get_required_actions(self):
        '''Return list of the submission its required actions'''
        if not self.updated_action:
            self._update_actions()
            self.updated_action = True
        return self.required_actions

    def has_required_actions(self):
        """
        Certain submission statuses will not show the required actions block.
        The decision to show this block is taken by this method.
        """
        return self.submission.status not in NO_REQUIRED_ACTION_STATUSES

    def update_status(self):
        """
        Implement:
        Let the submission status be centrally handled by this method. This makes sure
        the status cycle is clear and makes sure the cycle isn't broken due to unclear coding
        elsewhere. The next status to go to should ideally be determined on all the
        available in the submission with only few exceptions to explicilty force a new status code.
        """
        raise NotImplementedError


class BaseRefereeSubmissionCycle(BaseSubmissionCycle):
    """
    This *abstract* submission cycle adds the specific actions needed for submission cycles
    that require referees to be invited.
    """
    def update_status(self):
        if self.submission.status == STATUS_RESUBMISSION_INCOMING:
            self.submission.status = STATUS_EIC_ASSIGNED
            self.submission.save()

    def _update_actions(self):
        continue_update = super()._update_actions()
        if not continue_update:
            return False

        for ref_inv in self.submission.referee_invitations.all():
            if not ref_inv.cancelled:
                if ref_inv.accepted is None:
                    '''An invited referee may have not responsed yet.'''
                    timelapse = timezone.now() - ref_inv.date_invited
                    if timelapse > datetime.timedelta(days=3):
                        text = ('Referee %s has not responded for %i days. '
                                'Consider sending a reminder or cancelling the invitation.'
                                % (ref_inv.referee_str, timelapse.days))
                        self.required_actions.append(('referee_no_response', text,))
                elif ref_inv.accepted and not ref_inv.fulfilled:
                    '''A referee has not fulfilled its duty and the deadline is closing in.'''
                    timeleft = self.submission.reporting_deadline - timezone.now()
                    if timeleft < datetime.timedelta(days=7):
                        text = ('Referee %s has accepted to send a Report, '
                                'but not yet delivered it ' % ref_inv.referee_str)
                        if timeleft.days < 0:
                            text += '(%i days overdue). ' % (- timeleft.days)
                        elif timeleft.days == 1:
                            text += '(with 1 day left). '
                        else:
                            text += '(with %i days left). ' % timeleft.days
                        text += 'Consider sending a reminder or cancelling the invitation.'
                        self.required_actions.append(('referee_no_delivery', text,))

        if self.submission.reporting_deadline < timezone.now():
            text = ('The refereeing deadline has passed. Please either extend it, '
                    'or formulate your Editorial Recommendation if at least '
                    'one Report has been received.')
            self.required_actions.append(('deadline_passed', text,))

        return True


class GeneralSubmissionCycle(BaseRefereeSubmissionCycle):
    """
    The default submission cycle assigned to all 'regular' submissions and resubmissions
    which are explicitly assigned to go trough the default cycle by the EIC.
    It's a four week cycle with full capabilities i.e. invite referees, vet reports, etc. etc.
    """
    pass


class ShortSubmissionCycle(BaseRefereeSubmissionCycle):
    """
    This cycle is used if the EIC has explicitly chosen to do a short version of the general
    submission cycle. The deadline is within two weeks instead of the default four weeks.

    This cycle is only available for resubmitted submissions!
    """
    default_days = 14
    may_add_referees = False
    minimum_referees = 1
    pass


class DirectRecommendationSubmissionCycle(BaseSubmissionCycle):
    """
    This cycle is used if the EIC has explicitly chosen to immediately write an
    editorial recommendation.

    This cycle is only available for resubmitted submissions!
    """
    may_add_referees = False
    may_reinvite_referees = False
    minimum_referees = 0

    def update_status(self):
        if self.submission.status == STATUS_RESUBMISSION_INCOMING:
            self.submission.status = STATUS_AWAITING_ED_REC
            self.submission.save()

    def _update_actions(self):
        continue_update = super()._update_actions()
        if not continue_update:
            return False

        # No EIC Recommendation has been formulated yet
        text = 'Formulate an Editorial Recommendation.'
        self.required_actions.append(('need_eic_rec', text,))

        return True


class SubmissionUtils(BaseMailUtil):
    mail_sender = 'submissions@scipost.org'
    mail_sender_title = 'SciPost Editorial Admin'

    @classmethod
    def deprecate_other_assignments(cls):
        """
        Called when a Fellow has accepted or volunteered to become EIC.
        """
        # Import here due to circular import error
        from .models import EditorialAssignment

        assignments_to_deprecate = (EditorialAssignment.objects
                                    .filter(submission=cls.assignment.submission, accepted=None)
                                    .exclude(to=cls.assignment.to))
        for atd in assignments_to_deprecate:
            atd.deprecated = True
            atd.save()

    @classmethod
    def deprecate_all_assignments(cls):
        """
        Called when the pre-screening has failed.
        Requires loading 'submission' attribute.
        """
        # Import here due to circular import error
        from .models import EditorialAssignment

        assignments_to_deprecate = (EditorialAssignment.objects
                                    .filter(submission=cls.submission, accepted=None))
        for atd in assignments_to_deprecate:
            atd.deprecated = True
            atd.save()

    @classmethod
    def reinvite_referees_email(cls):
        """
        Email to be sent to referees when they are being reinvited by the EIC.

        Requires context to contain:
        - `invitation`
        """
        extra_bcc_list = [cls._context['invitation'].submission.editor_in_charge.user.email]
        cls._send_mail(cls, 'submission_cycle_reinvite_referee',
                       [cls._context['invitation'].email_address],
                       'Invitation on resubmission',
                       extra_bcc=extra_bcc_list)


    @classmethod
    def send_authors_submission_ack_email(cls):
        """ Requires loading 'submission' attribute. """
        email_text = ('Dear ' + cls.submission.submitted_by.get_title_display() + ' ' +
                      cls.submission.submitted_by.user.last_name +
                      ', \n\nWe have received your Submission to SciPost,\n\n' +
                      cls.submission.title + ' by ' + cls.submission.author_list + '.' +
                      '\n\nWe will update you on the results of the pre-screening process '
                      'within the next 5 working days.'
                      '\n\nYou can track your Submission at any time '
                      'from your personal page https://scipost.org/personal_page.' +
                      '\n\nWith many thanks,' +
                      '\n\nThe SciPost Team.')
        email_text_html = (
            '<p>Dear {{ title }} {{ last_name }},</p>'
            '<p>We have received your Submission to SciPost,</p>'
            '<p>{{ sub_title }}</p>'
            '\n<p>by {{ author_list }}.</p>'
            '\n<p>We will update you on the results of the pre-screening process '
            'within the next 5 working days.</p>'
            '\n<p>You can track your Submission at any time '
            'from your <a href="https://scipost.org/personal_page">personal page</a>.</p>'
            '<p>With many thanks,</p>'
            '<p>The SciPost Team.</p>')
        email_context = Context({
            'title': cls.submission.submitted_by.get_title_display(),
            'last_name': cls.submission.submitted_by.user.last_name,
            'sub_title': cls.submission.title,
            'author_list': cls.submission.author_list,
        })
        email_text_html += '<br/>' + EMAIL_FOOTER
        html_template = Template(email_text_html)
        html_version = html_template.render(email_context)
        emailmessage = EmailMultiAlternatives(
            'SciPost: Submission received', email_text,
            'SciPost Editorial Admin <submissions@scipost.org>',
            [cls.submission.submitted_by.user.email],
            bcc=['submissions@scipost.org'],
            reply_to=['submissions@scipost.org'])
        emailmessage.attach_alternative(html_version, 'text/html')
        emailmessage.send(fail_silently=False)

    @classmethod
    def send_authors_resubmission_ack_email(cls):
        """ Requires loading 'submission' attribute. """
        email_text = ('Dear ' + cls.submission.submitted_by.get_title_display() + ' ' +
                      cls.submission.submitted_by.user.last_name +
                      ', \n\nWe have received your Resubmission to SciPost,\n\n' +
                      cls.submission.title + ' by ' + cls.submission.author_list + '.' +
                      '\n\nYou can track your Submission at any time '
                      'from your personal page https://scipost.org/personal_page.' +
                      '\n\nWith many thanks,' +
                      '\n\nThe SciPost Team.')
        email_text_html = (
            '<p>Dear {{ title }} {{ last_name}},</p>'
            '<p>We have received your Resubmission to SciPost,</p>'
            '<p>{{ sub_title }}</p>'
            '\n<p>by {{ author_list }}.</p>'
            '\n<p>Your manuscript will soon be handled by the Editor-in-charge.</p>'
            '\n<p>You can track your Submission at any time '
            'from your <a href="https://scipost.org/personal_page">personal page</a>.</p>'
            '<p>With many thanks,</p>'
            '<p>The SciPost Team</p>')
        email_context = Context({
            'title': cls.submission.submitted_by.get_title_display(),
            'last_name': cls.submission.submitted_by.user.last_name,
            'sub_title': cls.submission.title,
            'author_list': cls.submission.author_list,
        })
        email_text_html += '<br/>' + EMAIL_FOOTER
        html_template = Template(email_text_html)
        html_version = html_template.render(email_context)
        emailmessage = EmailMultiAlternatives(
            'SciPost: Resubmission received', email_text,
            'SciPost Editorial Admin <submissions@scipost.org>',
            [cls.submission.submitted_by.user.email],
            bcc=['submissions@scipost.org'],
            reply_to=['submissions@scipost.org'])
        emailmessage.attach_alternative(html_version, 'text/html')
        emailmessage.send(fail_silently=False)

    @classmethod
    def send_assignment_request_email(cls):
        """ Requires loading 'assignment' attribute. """
        email_text = ('Dear ' + cls.assignment.to.get_title_display() + ' ' +
                      cls.assignment.to.user.last_name +
                      ', \n\nWe have received a Submission to SciPost ' +
                      'for which we would like you to consider becoming Editor-in-charge:\n\n' +
                      cls.assignment.submission.title + ' by '
                      + cls.assignment.submission.author_list + '.' +
                      '\n\nPlease visit https://scipost.org/submissions/pool ' +
                      'in order to accept or decline (it is important for you to inform us '
                      'even if you decline, since this affects the result '
                      'of the pre-screening process). '
                      'Note that this assignment request is automatically '
                      'deprecated if another Fellow '
                      'takes charge of this Submission or if pre-screening '
                      'fails in the meantime.'
                      '\n\nMany thanks in advance for your collaboration,' +
                      '\n\nThe SciPost Team.')
        email_text_html = (
            '<p>Dear {{ title }} {{ last_name }},</p>'
            '<p>We have received a Submission to SciPost ' +
            'for which we would like you to consider becoming Editor-in-charge:</p>'
            '<p>{{ sub_title }}</p>\n<p>by {{ author_list }}.</p>'
            '\n<p>Please visit the '
            '<a href="https://scipost.org/submissions/pool">Submissions Pool</a> '
            'in order to accept or decline (it is important for you to inform us '
            'even if you decline, since this affects the result '
            'of the pre-screening process).</p>'
            '<p>Note that this assignment request is automatically '
            'deprecated if another Fellow '
            'takes charge of this Submission or if pre-screening '
            'fails in the meantime.</p>'
            '\n<p>Many thanks in advance for your collaboration,</p>'
            '<p>The SciPost Team.</p>')
        email_context = Context({
            'title': cls.assignment.to.get_title_display(),
            'last_name': cls.assignment.to.user.last_name,
            'sub_title': cls.assignment.submission.title,
            'author_list': cls.assignment.submission.author_list,
        })
        email_text_html += '<br/>' + EMAIL_FOOTER
        html_template = Template(email_text_html)
        html_version = html_template.render(email_context)
        emailmessage = EmailMultiAlternatives(
            'SciPost: potential Submission assignment', email_text,
            'SciPost Editorial Admin <submissions@scipost.org>',
            [cls.assignment.to.user.email],
            bcc=['submissions@scipost.org'],
            reply_to=['submissions@scipost.org'])
        emailmessage.attach_alternative(html_version, 'text/html')
        emailmessage.send(fail_silently=False)

    @classmethod
    def send_EIC_appointment_email(cls):
        """ Requires loading 'assignment' attribute. """
        email_text = ('Dear ' + cls.assignment.to.get_title_display() + ' '
                      + cls.assignment.to.user.last_name
                      + ', \n\nThank you for accepting to become Editor-in-charge '
                      'of the SciPost Submission\n\n'
                      + cls.assignment.submission.title + ' by '
                      + cls.assignment.submission.author_list + '.'
                      '\n\nYou can take your editorial actions from the editorial page '
                      'https://scipost.org/submission/editorial_page/'
                      + cls.assignment.submission.arxiv_identifier_w_vn_nr
                      + ' (also accessible from your personal page '
                      'https://scipost.org/personal_page under the Editorial Actions tab). '
                      'In particular, you should now invite at least 3 referees; you might want to'
                      ' make sure you are aware of the '
                      'detailed procedure described in the Editorial College by-laws at '
                      'https://scipost.org/EdCol_by-laws.'
                      '\n\nMany thanks in advance for your collaboration,'
                      '\n\nThe SciPost Team.')
        email_text_html = (
            '<p>Dear {{ title }} {{ last_name }},</p>'
            '<p>Thank you for accepting to become Editor-in-charge '
            'of the SciPost Submission</p>'
            '<p>{{ sub_title }}</p>'
            '\n<p>by {{ author_list }}.</p>'
            '\n<p>You can take your editorial actions from the '
            '<a href="https://scipost.org/submission/editorial_page/'
            '{{ arxiv_identifier_w_vn_nr }}">editorial page</a> '
            '(also accessible from your '
            '<a href="https://scipost.org/personal_page">personal page</a> '
            'under the Editorial Actions tab).</p>'
            '\n<p>In particular, you should now invite at least 3 referees; you might want to '
            'make sure you are aware of the '
            'detailed procedure described in the '
            '<a href="https://scipost.org/EdCol_by-laws">Editorial College by-laws</a>.</p>'
            '<p>Many thanks in advance for your collaboration,</p>'
            '<p>The SciPost Team.</p>')
        email_context = Context({
            'title': cls.assignment.to.get_title_display(),
            'last_name': cls.assignment.to.user.last_name,
            'sub_title': cls.assignment.submission.title,
            'author_list': cls.assignment.submission.author_list,
            'arxiv_identifier_w_vn_nr': cls.assignment.submission.arxiv_identifier_w_vn_nr,
        })
        email_text_html += '<br/>' + EMAIL_FOOTER
        html_template = Template(email_text_html)
        html_version = html_template.render(email_context)
        emailmessage = EmailMultiAlternatives(
            'SciPost: assignment as EIC', email_text,
            'SciPost Editorial Admin <submissions@scipost.org>',
            [cls.assignment.to.user.email],
            bcc=['submissions@scipost.org'],
            reply_to=['submissions@scipost.org'])
        emailmessage.attach_alternative(html_version, 'text/html')
        emailmessage.send(fail_silently=False)

    @classmethod
    def send_EIC_reappointment_email(cls):
        """ Requires loading 'submission' attribute. """
        cls._send_mail(cls, 'submission_eic_reappointment',
                       [cls._context['submission'].editor_in_charge.user.email],
                       'resubmission received')

    @classmethod
    def send_author_prescreening_passed_email(cls):
        """ Requires loading 'assignment' attribute. """
        email_text = ('Dear ' + cls.assignment.submission.submitted_by.get_title_display() + ' '
                      + cls.assignment.submission.submitted_by.user.last_name
                      + ', \n\nWe are pleased to inform you that your recent Submission to SciPost,\n\n'
                      + cls.assignment.submission.title + ' by ' + cls.assignment.submission.author_list
                      + '\n\nhas successfully passed the pre-screening stage. '
                      '\n\nA Submission Page has been activated at '
                      'https://scipost.org/submission/'
                      + cls.assignment.submission.arxiv_identifier_w_vn_nr
                      + ' and a refereeing round has been started, with deadline '
                      'currently set at '
                      + datetime.datetime.strftime(cls.assignment.submission.reporting_deadline, "%Y-%m-%d")
                      + '.\n\n'
                      'During the refereeing round, you are welcome to provide replies to any '
                      'Report or Comment posted on your Submission (you can do so from the '
                      'Submission Page; you will be informed by email of any such Report or '
                      'Comment being delivered). In order to facilitate the work of the '
                      'Editorial College, we recommend limiting these replies to short '
                      'to-the-point clarifications of any issue raised on your manuscript.\n\n'
                      'Please wait for the Editor-in-charge\'s Editorial Recommendation '
                      'before any resubmission of your manuscript.'
                      '\n\nTo facilitate metadata handling, we recommend that all authors '
                      'have an ORCID id (easily obtained from https://orcid.org), '
                      'and be registered as SciPost Contributors. Could we please ask you '
                      '(and your eventual coathors) to ensure that this is the case?'
                      '\n\nWe thank you very much for your contribution.'
                      '\n\nSincerely,' +
                      '\n\nThe SciPost Team.')
        email_text_html = (
            '<p>Dear {{ title }} {{ last_name }},</p>'
            '<p>We are pleased to inform you that your recent Submission to SciPost,</p>'
            '<p>{{ sub_title }}</p>'
            '\n<p>by {{ author_list }}</p>'
            '\n<p>has successfully passed the pre-screening stage.</p>'
            '\n<p>A <a href="https://scipost.org/submission/{{ arxiv_identifier_w_vn_nr }}">'
            'Submission Page</a> has been activated '
            'and a refereeing round has been started, with deadline '
            'currently set at {{ deadline }}.</p>'
            '<h3>Further procedure</h3>'
            '<p>During the refereeing round, you are welcome to provide replies to any '
            'Report or Comment posted on your Submission (you can do so from the '
            'Submission Page; you will be informed by email of any such Report or '
            'Comment being delivered). In order to facilitate the work of the '
            'Editorial College, we recommend limiting these replies to short '
            'to-the-point clarifications of any issue raised on your manuscript.</p>'
            '<p>Please wait for the Editor-in-charge\'s Editorial Recommendation '
            'before any resubmission of your manuscript.</p>'
            '<h4>Author information</h4>'
            '<p>To facilitate metadata handling, we recommend that all authors '
            'have an ORCID id (easily obtained from <a href="https://orcid.org">orcid.org</a>), '
            'and be registered as SciPost Contributors. Could we please ask you '
            '(and your eventual coathors) to ensure that this is the case?</p>'
            '<p>We thank you very much for your contribution.</p>'
            '<p>Sincerely,</p>'
            '<p>The SciPost Team.</p>')
        email_context = Context({
            'title': cls.assignment.submission.submitted_by.get_title_display(),
            'last_name': cls.assignment.submission.submitted_by.user.last_name,
            'sub_title': cls.assignment.submission.title,
            'author_list': cls.assignment.submission.author_list,
            'arxiv_identifier_w_vn_nr': cls.assignment.submission.arxiv_identifier_w_vn_nr,
            'deadline': datetime.datetime.strftime(cls.assignment.submission.reporting_deadline,
                                                   "%Y-%m-%d"),
        })
        email_text_html += '<br/>' + EMAIL_FOOTER
        html_template = Template(email_text_html)
        html_version = html_template.render(email_context)
        emailmessage = EmailMultiAlternatives(
            'SciPost: pre-screening passed', email_text,
            'SciPost Editorial Admin <submissions@scipost.org>',
            [cls.assignment.submission.submitted_by.user.email],
            bcc=['submissions@scipost.org'],
            reply_to=['submissions@scipost.org'])
        emailmessage.attach_alternative(html_version, 'text/html')
        emailmessage.send(fail_silently=False)

    @classmethod
    def assignment_failed_email_authors(cls):
        """ Requires loading 'submission' attribute. """
        email_text = ('Dear ' + cls.submission.submitted_by.get_title_display() + ' '
                      + cls.submission.submitted_by.user.last_name
                      + ', \n\nYou recent Submission to SciPost,\n\n'
                      + cls.submission.title + ' by ' + cls.submission.author_list
                      + '\n\nhas unfortunately not passed the pre-screening stage. '
                      'We therefore regret to inform you that we will not '
                      'process your paper further towards publication, and that you '
                      'are now free to send your manuscript to an alternative journal.')
        if len(cls.personal_message) > 3:
            email_text += '\n\n' + cls.personal_message
        email_text += ('\n\nWe nonetheless thank you very much for your contribution.'
                       '\n\nSincerely,' +
                       '\n\nThe SciPost Team.')
        email_text_html = (
            '<p>Dear {{ title }} {{ last_name }},</p>'
            '<p>Your recent Submission to SciPost,</p>'
            '<p>{{ sub_title }}</p>'
            '\n<p>by {{ author_list }}</p>'
            '\n<p>has unfortunately not passed the pre-screening stage. '
            'We therefore regret to inform you that we will not '
            'process your paper further towards publication, and that you '
            'are now free to send your manuscript to an alternative journal.</p>')
        if len(cls.personal_message) > 3:
            email_text_html += '{{ personal_message|linebreaks }}'
        email_text_html += (
            '<p>We nonetheless thank you very much for your contribution.</p>'
            '<p>Sincerely,</p>'
            '<p>The SciPost Team.</p>')
        email_context = Context({
            'title': cls.submission.submitted_by.get_title_display(),
            'last_name': cls.submission.submitted_by.user.last_name,
            'sub_title': cls.submission.title,
            'author_list': cls.submission.author_list,
            'personal_message': cls.personal_message,
        })
        email_text_html += '<br/>' + EMAIL_FOOTER
        html_template = Template(email_text_html)
        html_version = html_template.render(email_context)
        emailmessage = EmailMultiAlternatives(
            'SciPost: pre-screening not passed', email_text,
            'SciPost Editorial Admin <submissions@scipost.org>',
            [cls.submission.submitted_by.user.email],
            bcc=['submissions@scipost.org'],
            reply_to=['submissions@scipost.org'])
        emailmessage.attach_alternative(html_version, 'text/html')
        emailmessage.send(fail_silently=False)


    @classmethod
    def send_refereeing_invitation_email(cls):
        """
        This method is called by send_refereeing_invitation in submissions/views.
        It is used when the referee is already a registered contributor.
        If a referee is not yet registered, the method recruit_referee is used
        instead, which calls the send_registration_email method in scipost/utils.
        Requires loading 'invitation' attribute.
        """
        email_text = ('Dear ' + cls.invitation.referee.get_title_display() + ' ' +
                      cls.invitation.referee.user.last_name +
                      ', \n\nWe have received a Submission to SciPost '
                      'which, in view of your expertise and on behalf of the Editor-in-charge '
                      + cls.invitation.submission.editor_in_charge.get_title_display() + ' '
                      + cls.invitation.submission.editor_in_charge.user.last_name
                      + ', we would like to invite you to referee:\n\n'
                      + cls.invitation.submission.title + ' by ' + cls.invitation.submission.author_list
                      + ' (see https://scipost.org/submission/'
                      + cls.invitation.submission.arxiv_identifier_w_vn_nr + ').'
                      '\n\nPlease visit https://scipost.org/submissions/accept_or_decline_ref_invitations '
                      '(login required) as soon as possible (ideally within the next 2 days) '
                      'in order to accept or decline this invitation.'
                      '\n\nIf you accept, your report can be submitted by simply '
                      'clicking on the "Contribute a Report" link at '
                      'https://scipost.org/submission/'
                      + cls.invitation.submission.arxiv_identifier_w_vn_nr
                      + ' before the reporting deadline (currently set at '
                      + datetime.datetime.strftime(cls.invitation.submission.reporting_deadline, "%Y-%m-%d")
                      + '; your report will be automatically recognized as an invited report). '
                      'You might want to make sure you are familiar with our refereeing code of conduct '
                      'https://scipost.org/journals/journals_terms_and_conditions and with the '
                      'refereeing procedure https://scipost.org/submissions/sub_and_ref_procedure.'
                      '\n\nWe would be extremely grateful for your contribution, '
                      'and thank you in advance for your consideration.'
                      '\n\nThe SciPost Team.')
        email_text_html = (
            '<p>Dear {{ title }} {{ last_name }},</p>'
            '<p>We have received a Submission to SciPost '
            'which, in view of your expertise and on behalf of the Editor-in-charge '
            '{{ EIC_title }} {{ EIC_last_name }}, '
            'we would like to invite you to referee:</p>'
            '<p>{{ sub_title }}</p>'
            '\n<p>by {{ author_list }}</p>'
            '\n<p>(see <a href="https://scipost.org/submission/'
            '{{ arxiv_identifier_w_vn_nr }}">this link</a>).</p>'
            '\n<p>Please <a href="https://scipost.org/submissions/accept_or_decline_ref_invitations">'
            'accept or decline the invitation</a> '
            '(login required) as soon as possible (ideally within the next 2 days).</p>'
            '\n<p>If you accept, your report can be submitted by simply '
            'clicking on the "Contribute a Report" link on the '
            '<a href="https://scipost.org/submission/{{ arxiv_identifier_w_vn_nr }}">'
            'Submission\'s Page</a> '
            'before the reporting deadline (currently set at {{ deadline }}'
            '; your report will be automatically recognized as an invited report).</p>'
            '<p>You might want to make sure you are familiar with our '
            '<a href="https://scipost.org/journals/journals_terms_and_conditions">'
            'refereeing code of conduct</a> and with the '
            '<a href="https://scipost.org/submissions/sub_and_ref_procedure">refereeing procedure</a>.</p>'
            '\n<p>We would be extremely grateful for your contribution, '
            'and thank you in advance for your consideration.</p>'
            '<p>The SciPost Team.</p>')
        email_context = Context({
            'title': cls.invitation.referee.get_title_display(),
            'last_name': cls.invitation.referee.user.last_name,
            'EIC_title': cls.invitation.submission.editor_in_charge.get_title_display(),
            'EIC_last_name': cls.invitation.submission.editor_in_charge.user.last_name,
            'sub_title': cls.invitation.submission.title,
            'author_list': cls.invitation.submission.author_list,
            'arxiv_identifier_w_vn_nr': cls.invitation.submission.arxiv_identifier_w_vn_nr,
            'deadline': datetime.datetime.strftime(cls.invitation.submission.reporting_deadline,
                                                   "%Y-%m-%d"),
        })
        email_text_html += '<br/>' + EMAIL_FOOTER
        html_template = Template(email_text_html)
        html_version = html_template.render(email_context)
        emailmessage = EmailMultiAlternatives(
            'SciPost: refereeing request', email_text,
            'SciPost Refereeing <refereeing@scipost.org>',
            [cls.invitation.referee.user.email],
            bcc=[cls.invitation.submission.editor_in_charge.user.email,
                 'refereeing@scipost.org'],
            reply_to=['refereeing@scipost.org'])
        emailmessage.attach_alternative(html_version, 'text/html')
        emailmessage.send(fail_silently=False)

    @classmethod
    def send_unreg_ref_reminder_email(cls):
        """
        This method is used to remind a referee who has not yet responded.
        It is used for unregistered referees only.
        It is called from the ref_invitation_reminder method in submissions/views.py.
        """
        email_text = (
            'Dear ' + cls.invitation.get_title_display() + ' '
            + cls.invitation.last_name + ',\n\n'
            'On behalf of the Editor-in-charge '
            + cls.invitation.submission.editor_in_charge.get_title_display() + ' '
            + cls.invitation.submission.editor_in_charge.user.last_name
            + ', we would like to cordially remind you of our recent request to referee\n\n'
            + cls.invitation.submission.title + ' by '
            + cls.invitation.submission.author_list + '.')
        email_text_html = (
            '<p>Dear {{ title }} {{ last_name }},</p>'
            '<p>On behalf of the Editor-in-charge {{ EIC_title }} {{ EIC_last_name }}, '
            'we would like to cordially remind you of our recent request to referee</p>'
            '<p>{{ sub_title }}</p>'
            '\n<p>by {{ author_list }}.</p>')
        email_text += (
            '\n\nWe would also like to renew '
            'our invitation to become a Contributor on SciPost '
            '(our records show that you are not yet registered); '
            'your partially pre-filled registration form is still available at\n\n'
            'https://scipost.org/invitation/' + cls.invitation.invitation_key + '\n\n'
            'after which your registration will be activated, giving you full access to '
            'the portal\'s facilities (in particular allowing you to '
            'provide referee reports).\n\n'
            'To ensure timely processing of the submission (out of respect for the authors), '
            'we would appreciate a quick accept/decline response from you, '
            'ideally within the next 2 days.\n\n'
            'If you are not able to provide a Report, you can quickly let us know by simply '
            'navigating to \n\nhttps://scipost.org/submissions/decline_ref_invitation/'
            + cls.invitation.invitation_key + '\n\n'
            'If you are able to provide a Report, you can confirm this after registering '
            'and logging in (you will automatically be prompted for a confirmation). '
            'Your report can thereafter be submitted by simply clicking on '
            'the "Contribute a Report" link at '
            'https://scipost.org/submission/'
            + cls.invitation.submission.arxiv_identifier_w_vn_nr
            + ' before the reporting deadline (currently set at '
            + datetime.datetime.strftime(cls.invitation.submission.reporting_deadline, "%Y-%m-%d")
            + '; your report will be automatically recognized as an invited report). '
            'You might want to make sure you are familiar with our refereeing code of conduct '
            'https://scipost.org/journals/journals_terms_and_conditions and with the '
            'refereeing procedure https://scipost.org/submissions/sub_and_ref_procedure.'
            '\n\nWe very much hope we can count on your expertise,'
            '\n\nMany thanks in advance,\n\nThe SciPost Team'
        )
        email_text_html += (
            '\n<p>We would also like to renew '
            'our invitation to become a Contributor on SciPost '
            '(our records show that you are not yet registered); '
            'your partially pre-filled '
            '<a href="https://scipost.org/invitation/{{ invitation_key }}">'
            'registration form</a> is still available, '
            'after which your registration will be activated, giving you full access to '
            'the portal\'s facilities (in particular allowing you to provide referee reports).</p>'
            '<p>To ensure timely processing of the submission (out of respect for the authors), '
            'we would appreciate a quick accept/decline response from you, '
            'ideally within the next 2 days.</p>'
            '<p>If you are <strong>not</strong> able to provide a Report, '
            'you can quickly let us know by simply '
            '<a href="https://scipost.org/submissions/decline_ref_invitation/{{ invitation_key }}">'
            'clicking here</a>.</p>'
            '<p>If you are able to provide a Report, you can confirm this after registering '
            'and logging in (you will automatically be prompted for a confirmation). '
            'Your report can thereafter be submitted by simply clicking on '
            'the "Contribute a Report" link at '
            'the <a href="https://scipost.org/submission/{{ arxiv_identifier_w_vn_nr }}">'
            'Submission\'s page</a> before the reporting deadline (currently set at '
            '{{ deadline }}; your report will be automatically recognized as an invited report).</p>'
            '\n<p>You might want to make sure you are familiar with our '
            '<a href="https://scipost.org/journals/journals_terms_and_conditions">'
            'refereeing code of conduct</a> and with the '
            '<a href="https://scipost.org/submissions/sub_and_ref_procedure">'
            'refereeing procedure</a>.</p>'
            '<p>We very much hope we can count on your expertise,</p>'
            '<p>Many thanks in advance,</p>'
            '<p>The SciPost Team</p>')
        email_context = Context({
            'title': cls.invitation.get_title_display(),
            'last_name': cls.invitation.last_name,
            'EIC_title': cls.invitation.submission.editor_in_charge.get_title_display(),
            'EIC_last_name': cls.invitation.submission.editor_in_charge.user.last_name,
            'sub_title': cls.invitation.submission.title,
            'author_list': cls.invitation.submission.author_list,
            'arxiv_identifier_w_vn_nr': cls.invitation.submission.arxiv_identifier_w_vn_nr,
            'deadline': datetime.datetime.strftime(cls.invitation.submission.reporting_deadline,
                                                   "%Y-%m-%d"),
            'invitation_key': cls.invitation.invitation_key,
        })
        email_text_html += '<br/>' + EMAIL_FOOTER
        html_template = Template(email_text_html)
        html_version = html_template.render(email_context)
        emailmessage = EmailMultiAlternatives(
            'SciPost: reminder (refereeing request and registration invitation)', email_text,
            'SciPost Refereeing <refereeing@scipost.org>',
            [cls.invitation.email_address],
            bcc=[cls.invitation.submission.editor_in_charge.user.email,
                 'refereeing@scipost.org'],
            reply_to=['refereeing@scipost.org'])
        emailmessage.attach_alternative(html_version, 'text/html')
        emailmessage.send(fail_silently=False)


    @classmethod
    def send_ref_reminder_email(cls):
        """
        This method is used to remind a referee who has not yet responded.
        It is used for registered Contributors only.
        It is called from the ref_invitation_reminder method in submissions/views.py.
        """
        email_text = (
            'Dear ' + cls.invitation.get_title_display() + ' '
            + cls.invitation.last_name + ',\n\n'
            'On behalf of the Editor-in-charge '
            + cls.invitation.submission.editor_in_charge.get_title_display() + ' '
            + cls.invitation.submission.editor_in_charge.user.last_name
            + ', we would like to cordially remind you of our recent request to referee\n\n'
            + cls.invitation.submission.title + ' by '
            + cls.invitation.submission.author_list + '.')
        email_text_html = (
            '<p>Dear {{ title }} {{ last_name }},</p>'
            '<p>On behalf of the Editor-in-charge {{ EIC_title }} {{ EIC_last_name }}, '
            'we would like to cordially remind you of our recent request to referee</p>'
            '<p>{{ sub_title }}</p>'
            '\n<p>by {{ author_list }}.</p>')
        if cls.invitation.accepted is None:
            email_text += (
                '\n\nPlease visit '
                'https://scipost.org/submissions/accept_or_decline_ref_invitations '
                '(login required) as soon as possible (ideally within the next 2 days) '
                'in order to accept or decline this invitation.')
            email_text_html += (
                '\n<p>Please '
                '<a href="https://scipost.org/submissions/accept_or_decline_ref_invitations">'
                'accept or decline the invitation</a> '
                '(login required) as soon as possible (ideally within the next 2 days) '
                'in order to ensure rapid processing of the submission.')
        email_text += (
            '\n\nYour report can be submitted by simply clicking on '
            'the "Contribute a Report" link at '
            'https://scipost.org/submission/'
            + cls.invitation.submission.arxiv_identifier_w_vn_nr
            + ' before the reporting deadline (currently set at '
            + datetime.datetime.strftime(cls.invitation.submission.reporting_deadline, "%Y-%m-%d")
            + '; your report will be automatically recognized as an invited report). '
            'You might want to make sure you are familiar with our refereeing code of conduct '
            'https://scipost.org/journals/journals_terms_and_conditions and with the '
            'refereeing procedure https://scipost.org/submissions/sub_and_ref_procedure.'
            '\n\nWe very much hope we can count on your expertise,'
            '\n\nMany thanks in advance,\n\nThe SciPost Team')
        email_text_html += (
            '\n<p>Your report can be submitted by simply clicking on '
            'the "Contribute a Report" link at '
            'the <a href="https://scipost.org/submission/{{ arxiv_identifier_w_vn_nr }}">'
            'Submission\'s page</a> before the reporting deadline (currently set at '
            '{{ deadline }}; your report will be automatically recognized as an invited report).</p>'
            '\n<p>You might want to make sure you are familiar with our '
            '<a href="https://scipost.org/journals/journals_terms_and_conditions">'
            'refereeing code of conduct</a> and with the '
            '<a href="https://scipost.org/submissions/sub_and_ref_procedure">'
            'refereeing procedure</a>.</p>'
            '<p>We very much hope we can count on your expertise,</p>'
            '<p>Many thanks in advance,</p>'
            '<p>The SciPost Team</p>')
        email_context = Context({
            'title': cls.invitation.get_title_display(),
            'last_name': cls.invitation.last_name,
            'EIC_title': cls.invitation.submission.editor_in_charge.get_title_display(),
            'EIC_last_name': cls.invitation.submission.editor_in_charge.user.last_name,
            'sub_title': cls.invitation.submission.title,
            'author_list': cls.invitation.submission.author_list,
            'arxiv_identifier_w_vn_nr': cls.invitation.submission.arxiv_identifier_w_vn_nr,
            'deadline': datetime.datetime.strftime(cls.invitation.submission.reporting_deadline,
                                                   "%Y-%m-%d"),
            'invitation_key': cls.invitation.invitation_key,
        })
        email_text_html += '<br/>' + EMAIL_FOOTER
        html_template = Template(email_text_html)
        html_version = html_template.render(email_context)
        emailmessage = EmailMultiAlternatives(
            'SciPost: reminder (refereeing request and registration invitation)', email_text,
            'SciPost Refereeing <refereeing@scipost.org>',
            [cls.invitation.email_address],
            bcc=[cls.invitation.submission.editor_in_charge.user.email,
                 'refereeing@scipost.org'],
            reply_to=['refereeing@scipost.org'])
        emailmessage.attach_alternative(html_version, 'text/html')
        emailmessage.send(fail_silently=False)


    @classmethod
    def send_ref_cancellation_email(cls):
        """
        This method is used to inform a referee that his/her services are no longer required.
        It is called from the cancel_ref_invitation method in submissions/views.py.
        """
        email_text = ('Dear ' + cls.invitation.get_title_display() + ' '
                      + cls.invitation.last_name + ',\n\n'
                      'On behalf of the Editor-in-charge '
                      + cls.invitation.submission.editor_in_charge.get_title_display() + ' '
                      + cls.invitation.submission.editor_in_charge.user.last_name
                      + ', we would like to inform you that your report on\n\n'
                      + cls.invitation.submission.title + ' by '
                      + cls.invitation.submission.author_list
                      + '\n\nis no longer required.'
                      '\n\nWe very much hope we can count on your expertise '
                      'at some other point in the future,'
                      '\n\nMany thanks for your time,\n\nThe SciPost Team')
        email_text_html = (
            '<p>Dear {{ title }} {{ last_name }},</p>'
            '<p>On behalf of the Editor-in-charge {{ EIC_title }} {{ EIC_last_name }}, '
            'we would like to inform you that your report on</p>'
            '<p>{{ sub_title }}</p>'
            '\n<p>by {{ author_list }}</p>'
            '\n<p>is no longer required.</p>'
            '<p>We very much hope we can count on your expertise '
            'at some other point in the future,</p>'
            '<p>Many thanks for your time,</p>'
            '<p>The SciPost Team</p>')
        if cls.invitation.referee is None:
            email_text += ('\n\nP.S.: We would also like to renew '
                           'our invitation to become a Contributor on SciPost '
                           '(our records show that you are not yet registered); '
                           'your partially pre-filled registration form is still available at\n\n'
                           'https://scipost.org/invitation/' + cls.invitation.invitation_key + '\n\n'
                           'after which your registration will be activated, giving you full access to '
                           'the portal\'s facilities (in particular allowing you to provide referee reports).')
            email_text_html += (
                '\n<br/><p>P.S.: We would also like to renew '
                'our invitation to become a Contributor on SciPost '
                '(our records show that you are not yet registered); '
                'your partially pre-filled '
                '<a href="https://scipost.org/invitation/{{ invitation_key }}">'
                'registration form</a> is still available '
                'after which your registration will be activated, giving you full access to '
                'the portal\'s facilities (in particular allowing you to provide referee reports).</p>')
        email_context = Context({
            'title': cls.invitation.get_title_display(),
            'last_name': cls.invitation.last_name,
            'EIC_title': cls.invitation.submission.editor_in_charge.get_title_display(),
            'EIC_last_name': cls.invitation.submission.editor_in_charge.user.last_name,
            'sub_title': cls.invitation.submission.title,
            'author_list': cls.invitation.submission.author_list,
            'invitation_key': cls.invitation.invitation_key,
        })
        email_text_html += '<br/>' + EMAIL_FOOTER
        html_template = Template(email_text_html)
        html_version = html_template.render(email_context)
        emailmessage = EmailMultiAlternatives(
            'SciPost: report no longer needed', email_text,
            'SciPost Refereeing <refereeing@scipost.org>',
            [cls.invitation.email_address],
            bcc=[cls.invitation.submission.editor_in_charge.user.email,
                 'refereeing@scipost.org'],
            reply_to=['refereeing@scipost.org'])
        emailmessage.attach_alternative(html_version, 'text/html')
        emailmessage.send(fail_silently=False)

    @classmethod
    def email_referee_response_to_EIC(cls):
        '''Requires loading `invitation` attribute.'''
        if cls._context['invitation'].accepted:
            email_subject = 'referee accepts to review'
        else:
            email_subject = 'referee declines to review'
        cls._send_mail(cls, 'referee_response_to_EIC',
                       [cls._context['invitation'].submission.editor_in_charge.user.email],
                       email_subject)

    @classmethod
    def email_referee_in_response_to_decision(cls):
        '''Requires loading `invitation` attribute.'''
        if cls._context['invitation'].accepted:
            email_subject = 'confirmation accepted invitation'
        else:
            email_subject = 'confirmation declined invitation'
        cls._send_mail(cls, 'referee_in_response_to_decision',
                       [cls._context['invitation'].referee.user.email],
                       email_subject)

    @classmethod
    def email_EIC_report_delivered(cls):
        """ Requires loading 'report' attribute. """
        cls._send_mail(cls, 'report_delivered_eic',
                       [cls._context['report'].submission.editor_in_charge.user.email],
                       'Report delivered')

    @classmethod
    def email_referee_report_delivered(cls):
        """ Requires loading 'report' attribute. """
        cls._send_mail(cls, 'report_delivered_referee',
                       [cls._context['report'].author.user.email],
                       'Report delivered')

    @classmethod
    def acknowledge_report_email(cls):
        """ Requires loading 'report' attribute. """
        email_text = ('Dear ' + cls.report.author.get_title_display() + ' ' +
                      cls.report.author.user.last_name + ','
                      '\n\nMany thanks for your Report on Submission\n\n' +
                      cls.report.submission.title + ' by '
                      + cls.report.submission.author_list + '.')
        email_text_html = (
            '<p>Dear {{ ref_title }} {{ ref_last_name }},</p>'
            '<p>Many thanks for your Report on Submission</p>'
            '<p>{{ sub_title }}</p>\n<p>by {{ author_list }}.</p>')
        if cls.report.status == 1:
            email_text += ('\n\nYour Report has been vetted through and is viewable at '
                           'https://scipost.org/submissions/'
                           + cls.report.submission.arxiv_identifier_w_vn_nr + '.')
            email_text_html += (
                '\n<p>Your Report has been vetted through and is viewable at '
                'the <a href="https://scipost.org/submissions/'
                '{{ arxiv_identifier_w_vn_nr }}">Submission\'s page</a>.</p>')
        else:
            email_text += ('\n\nYour Report has been reviewed by the Editor-in-charge of the Submission, '
                           'who decided not to admit it for online posting, citing the reason: '
                           + cls.report.get_status_display() + '.'
                           ' We copy the text entries of your report below for your convenience, '
                           'if ever you wish to reformulate it and resubmit it.')
            email_text_html += (
                '\n<p>Your Report has been reviewed by the Editor-in-charge of the Submission, '
                'who decided not to admit it for online posting, citing the reason: '
                '{{ refusal_reason }}.</p>'
                '\n<p>We copy the text entries of your report below for your convenience, '
                'if ever you wish to reformulate it and resubmit it.</p>')
        email_text += ('\n\nMany thanks for your collaboration,'
                       '\n\nThe SciPost Team.')
        email_text_html += ('<p>Many thanks for your collaboration,</p>'
                            '<p>The SciPost Team.</p>')
        if cls.report.status != 1:
            if cls.email_response is not None:
                email_text += '\n\nAdditional info from the Editor-in-charge: \n'
                email_text += cls.email_response
                email_text_html += '\n<p>Additional info from the Editor-in-charge: </p><br/>'
                email_text_html += cls.email_response
            email_text += ('\n\nThe text entries of your Report: ' +
                           '\n\nStrengths: \n' + cls.report.strengths +
                           '\n\nWeaknesses: \n' + cls.report.weaknesses +
                           '\n\nReport: \n' + cls.report.report +
                           '\n\nRequested changes: \n' + cls.report.requested_changes +
                           '\n\nRemarks for Editors: \n' + cls.report.remarks_for_editors)
            email_text_html += (
                '\n<p>The text entries of your Report: </p>'
                '\n<strong>Strengths</strong>: <br/><p>{{ strengths|linebreaks }}</p>'
                '\n<strong>Weaknesses</strong>: <br/><p>{{ weaknesses|linebreaks }}</p>'
                '\n<strong>Report</strong>: <br/><p>{{ report|linebreaks }}</p>'
                '\n<strong>Requested changes</strong>: <br/><p>{{ requested_changes|linebreaks }}</p>'
                '\n<strong>Remarks for Editors</strong>: <br/><p>{{ remarks_for_editors|linebreaks }}</p>')
        email_context = Context({
            'ref_title': cls.report.author.get_title_display(),
            'ref_last_name': cls.report.author.user.last_name,
            'sub_title': cls.report.submission.title,
            'author_list': cls.report.submission.author_list,
            'arxiv_identifier_w_vn_nr': cls.report.submission.arxiv_identifier_w_vn_nr,
            'strengths': cls.report.strengths,
            'weaknesses': cls.report.weaknesses,
            'report': cls.report.report,
            'requested_changes': cls.report.requested_changes,
            'remarks_for_editors': cls.report.remarks_for_editors,
        })
        if cls.report.status < 0:
            email_context['refusal_reason'] = cls.report.get_status_display()
        email_text_html += '<br/>' + EMAIL_FOOTER
        html_template = Template(email_text_html)
        html_version = html_template.render(email_context)
        emailmessage = EmailMultiAlternatives(
            'SciPost: Report acknowledgement', email_text,
            'SciPost Refereeing <refereeing@scipost.org>',
            [cls.report.author.user.email],
            bcc=[cls.report.submission.editor_in_charge.user.email,
                 'refereeing@scipost.org'],
            reply_to=['refereeing@scipost.org'])
        emailmessage.attach_alternative(html_version, 'text/html')
        emailmessage.send(fail_silently=False)

    @classmethod
    def send_author_report_received_email(cls):
        """ Requires loading 'report' attribute. """
        email_text = ('Dear ' + cls.report.submission.submitted_by.get_title_display() + ' ' +
                      cls.report.submission.submitted_by.user.last_name +
                      ', \n\nA Report has been posted on your recent Submission to SciPost,\n\n' +
                      cls.report.submission.title + ' by ' + cls.report.submission.author_list + '.'
                      '\n\nYou can view it at the Submission Page '
                      'https://scipost.org/submission/'
                      + cls.report.submission.arxiv_identifier_w_vn_nr + '.'
                      '\n\nWe remind you that you can provide an author reply '
                      '(only if you wish, to clarify points eventually raised '
                      'by the report) directly from this Submission Page. '
                      'Any eventual modification to your manuscript '
                      'should await the Recommendation from the Editor-in-charge.'
                      '\n\nWe thank you very much for your contribution.'
                      '\n\nSincerely,' +
                      '\n\nThe SciPost Team.')
        email_text_html = (
            '<p>Dear {{ auth_title }} {{ auth_last_name }},</p>'
            '<p>A Report has been posted on your recent Submission to SciPost,</p>'
            '<p>{{ sub_title }}</p>\n<p>by {{ author_list }}.</p>'
            '\n<p>You can view it at the '
            '<a href="https://scipost.org/submission/{{ arxiv_identifier_w_vn_nr }}">'
            'Submission\'s page</a>.</p>'
            '<p>We remind you that you can provide an author reply '
            '(only if you wish, to clarify points eventually raised '
            'by the report) directly from this Submission Page. '
            'Any eventual modification to your manuscript '
            'should await the Recommendation from the Editor-in-charge.</p>'
            '\n<p>We thank you very much for your contribution.</p>'
            '<p>Sincerely,</p>'
            '<p>The SciPost Team.</p>')
        email_context = Context({
            'auth_title': cls.report.submission.submitted_by.get_title_display(),
            'auth_last_name': cls.report.submission.submitted_by.user.last_name,
            'sub_title': cls.report.submission.title,
            'author_list': cls.report.submission.author_list,
            'arxiv_identifier_w_vn_nr': cls.report.submission.arxiv_identifier_w_vn_nr,
        })
        email_text_html += '<br/>' + EMAIL_FOOTER
        html_template = Template(email_text_html)
        html_version = html_template.render(email_context)
        emailmessage = EmailMultiAlternatives(
            'SciPost: Report received on your Submission', email_text,
            'SciPost Editorial Admin <submissions@scipost.org>',
            [cls.report.submission.submitted_by.user.email],
            bcc=['submissions@scipost.org'],
            reply_to=['submissions@scipost.org'])
        emailmessage.attach_alternative(html_version, 'text/html')
        emailmessage.send(fail_silently=False)

    @classmethod
    def send_author_comment_received_email(cls):
        """ Requires loading 'submission' attribute. """
        email_text = ('Dear ' + cls.submission.submitted_by.get_title_display() + ' ' +
                      cls.submission.submitted_by.user.last_name +
                      ', \n\nA Comment has been posted on your recent Submission to SciPost,\n\n' +
                      cls.submission.title + ' by ' + cls.submission.author_list + '.'
                      '\n\nYou can view it at the Submission Page '
                      'https://scipost.org/submission/'
                      + cls.submission.arxiv_identifier_w_vn_nr + '.'
                      '\n\nWe thank you very much for your contribution.'
                      '\n\nSincerely,' +
                      '\n\nThe SciPost Team.')
        email_text_html = (
            '<p>Dear {{ auth_title }} {{ auth_last_name }},</p>'
            '<p>A Comment has been posted on your recent Submission to SciPost,</p>'
            '<p>{{ sub_title }}</p>\n<p>by {{ author_list }}.</p>'
            '\n<p>You can view it at the '
            '<a href="https://scipost.org/submission/{{ arxiv_identifier_w_vn_nr }}">'
            'Submission\'s Page</a>.</p>'
            '\n<p>We thank you very much for your contribution.</p>'
            '<p>Sincerely,</p>'
            '<p>The SciPost Team.</p>')
        email_context = Context({
            'auth_title': cls.submission.submitted_by.get_title_display(),
            'auth_last_name': cls.submission.submitted_by.user.last_name,
            'sub_title': cls.submission.title,
            'author_list': cls.submission.author_list,
            'arxiv_identifier_w_vn_nr': cls.submission.arxiv_identifier_w_vn_nr,
        })
        email_text_html += '<br/>' + EMAIL_FOOTER
        html_template = Template(email_text_html)
        html_version = html_template.render(email_context)
        emailmessage = EmailMultiAlternatives(
            'SciPost: Comment received on your Submission', email_text,
            'SciPost Editorial Admin <submissions@scipost.org>',
            [cls.submission.submitted_by.user.email],
            bcc=[cls.submission.editor_in_charge.user.email,
                 'submissions@scipost.org'],
            reply_to=['submissions@scipost.org'])
        emailmessage.attach_alternative(html_version, 'text/html')
        emailmessage.send(fail_silently=False)

    @classmethod
    def send_communication_email(cls):
        """
        After an EditorialCommunication has been created and saved,
        this method sends emails to the relevant people.
        Requires loading 'communication' attribute.
        """
        recipient_email = []
        bcc_emails = []
        further_action_page = None
        if cls.communication.comtype in ['AtoE', 'RtoE', 'StoE']:
            recipient_email.append(cls.communication.submission.editor_in_charge.user.email)
            recipient_greeting = ('Dear ' +
                                  cls.communication.submission.editor_in_charge.get_title_display() + ' ' +
                                  cls.communication.submission.editor_in_charge.user.last_name)
            further_action_page = ('https://scipost.org/submission/editorial_page/'
                                   + cls.communication.submission.arxiv_identifier_w_vn_nr)
            if cls.communication.comtype == 'RtoE':
                bcc_emails.append(cls.communication.referee.user.email)
            bcc_emails.append('submissions@scipost.org')
        elif cls.communication.comtype in ['EtoA']:
            recipient_email.append(cls.communication.submission.submitted_by.user.email)
            recipient_greeting = ('Dear ' +
                                  cls.communication.submission.submitted_by.get_title_display() + ' ' +
                                  cls.communication.submission.submitted_by.user.last_name)
            bcc_emails.append(cls.communication.submission.editor_in_charge)
            bcc_emails.append('submissions@scipost.org')
        elif cls.communication.comtype in ['EtoR']:
            recipient_email.append(cls.communication.referee.user.email)
            recipient_greeting = ('Dear ' +
                                  cls.communication.referee.get_title_display() + ' ' +
                                  cls.communication.referee.user.last_name)
            bcc_emails.append(cls.communication.submission.editor_in_charge)
            bcc_emails.append('submissions@scipost.org')
        elif cls.communication.comtype in ['EtoS']:
            recipient_email.append('submissions@scipost.org')
            recipient_greeting = 'Dear Editorial Administrators'
            bcc_emails.append(cls.communication.submission.editor_in_charge)
            further_action_page = 'https://scipost.org/submissions/pool'

        email_text = (recipient_greeting +
                      ', \n\nPlease find here a communication (' +
                      cls.communication.get_comtype_display() + ') '
                      'concerning Submission\n\n' +
                      cls.communication.submission.title + ' by '
                      + cls.communication.submission.author_list + '.'
                      '\n\nText of the communication:\n------------------------------------------\n' +
                      cls.communication.text + '\n------------------------------------------')
        if further_action_page:
            email_text += '\n\nYou can take follow-up actions from ' + further_action_page + '.'
        email_text += ('\n\nWe thank you very much for your contribution.'
                       '\n\nSincerely,' +
                       '\n\nThe SciPost Team.')
        emailmessage = EmailMessage(
            'SciPost: communication (' + cls.communication.get_comtype_display() + ')',
            email_text,
            'SciPost Editorial Admin <submissions@scipost.org>',
            recipient_email,
            bcc_emails,
            reply_to=['submissions@scipost.org'])
        emailmessage.send(fail_silently=False)

    @classmethod
    def send_author_revision_requested_email(cls):
        """ Requires loading 'submission' and 'recommendation' attributes. """
        email_text = ('Dear ' + cls.submission.submitted_by.get_title_display() + ' ' +
                      cls.submission.submitted_by.user.last_name +
                      ', \n\nThe Editor-in-charge of your recent Submission to SciPost,\n\n' +
                      cls.submission.title + ' by ' + cls.submission.author_list + ','
                      '\n\nhas formulated an Editorial Recommendation, asking for a ')
        email_text_html = (
            '<p>Dear {{ auth_title }} {{ auth_last_name }},</p>'
            '<p>The Editor-in-charge of your recent Submission to SciPost,</p>'
            '<p>{{ sub_title }}</p>\n<p>by {{ author_list }},</p>'
            '\n<p>has formulated an Editorial Recommendation, asking for a ')
        if cls.recommendation.recommendation == -1:
            email_text += 'minor'
            email_text_html += 'minor'
        elif cls.recommendation.recommendation == -2:
            email_text += 'major'
            email_text_html += 'major'
        email_text += (' revision.'
                       '\n\nYou can view it at the Submission Page '
                       'https://scipost.org/submission/'
                       + cls.submission.arxiv_identifier_w_vn_nr + '. '
                       'Note that the recommendation is viewable only by '
                       'the registered authors of the submission.'
                       'To resubmit your paper, please first update the version '
                       'on the arXiv; after appearance, go to the submission page '
                       'https://scipost.org/submissions/submit_manuscript and fill '
                       'in the forms. Your submission will be automatically recognized '
                       'as a resubmission.'
                       '\n\nWe thank you very much for your contribution.'
                       '\n\nSincerely,' +
                       '\n\nThe SciPost Team.')
        email_text_html += (
            ' revision.</p>'
            '\n<p>You can view it at the '
            '<a href="https://scipost.org/submission/'
            '{{ arxiv_identifier_w_vn_nr }}">Submission\'s Page</a>.</p>'
            '<p>Note that the recommendation is viewable only by '
            'the registered authors of the submission.</p>'
            '<p>To resubmit your paper, please first update the version '
            'on the arXiv; after appearance, go to the '
            '<a href="https://scipost.org/submissions/submit_manuscript">'
            'submission page</a> and fill '
            'the forms in. Your submission will be automatically recognized '
            'as a resubmission.</p>'
            '\n<p>We thank you very much for your contribution.</p>'
            '<p>Sincerely,</p>'
            '<p>The SciPost Team.</p>')
        email_context = Context({
            'auth_title': cls.submission.submitted_by.get_title_display(),
            'auth_last_name': cls.submission.submitted_by.user.last_name,
            'sub_title': cls.submission.title,
            'author_list': cls.submission.author_list,
            'arxiv_identifier_w_vn_nr': cls.submission.arxiv_identifier_w_vn_nr,
        })
        email_text_html += '<br/>' + EMAIL_FOOTER
        html_template = Template(email_text_html)
        html_version = html_template.render(email_context)
        emailmessage = EmailMultiAlternatives(
            'SciPost: revision requested', email_text,
            'SciPost Editorial Admin <submissions@scipost.org>',
            [cls.submission.submitted_by.user.email],
            bcc=[cls.submission.editor_in_charge.user.email,
                 'submissions@scipost.org'],
            reply_to=['submissions@scipost.org'])
        emailmessage.attach_alternative(html_version, 'text/html')
        emailmessage.send(fail_silently=False)

    @classmethod
    def send_author_College_decision_email(cls):
        """ Requires loading 'submission' and 'recommendation' attributes. """
        email_text = ('Dear ' + cls.submission.submitted_by.get_title_display() + ' ' +
                      cls.submission.submitted_by.user.last_name +
                      ', \n\nThe Editorial College of SciPost has taken a decision '
                      'regarding your recent Submission,\n\n' +
                      cls.submission.title + ' by ' + cls.submission.author_list + '.\n\n')
        email_text_html = (
            '<p>Dear {{ auth_title }} {{ auth_last_name }},</p>'
            '<p>The Editorial College of SciPost has taken a decision '
            'regarding your recent Submission,</p>'
            '<p>{{ sub_title }}</p>\n<p>by {{ author_list }}.</p>')
        if (cls.recommendation.recommendation == 1
            or cls.recommendation.recommendation == 2
            or cls.recommendation.recommendation == 3):
            email_text += ('We are pleased to inform you that your Submission '
                           'has been accepted for publication in '
                           + cls.submission.get_submitted_to_journal_display())
            email_text_html += (
                '<p>We are pleased to inform you that your Submission '
                'has been accepted for publication in <strong>{{ journal }}</strong>')
            if cls.recommendation.recommendation == 1 and False:  # Temporary deactivation of Select
                email_text += (', with a promotion to Select. We warmly congratulate you '
                               'on this achievement, which is reserved to papers deemed in '
                               'the top ten percent of papers we publish.')
                email_text_html += (
                    ', with a promotion to <strong>Select</strong>. We warmly congratulate you '
                    'on this achievement, which is reserved to papers deemed in '
                    'the top ten percent of papers we publish.</p>')
            else:
                email_text += '.'
                email_text_html += '.'
            email_text += ('\n\nYour manuscript will now be taken charge of by our '
                           'production team, who will soon send you proofs '
                           'to check before final publication.')
            email_text_html += ('\n<p>Your manuscript will now be taken charge of by our '
                                'production team, who will soon send you proofs '
                                'to check before final publication.</p>')

        elif cls.recommendation.recommendation == -3:
            email_text += ('We are sorry to inform you that your Submission '
                           'has not been accepted for publication. '
                           '\n\nYou can view more details at the Submission Page '
                           'https://scipost.org/submission/'
                           + cls.submission.arxiv_identifier_w_vn_nr + '. '
                           'Note that these details are viewable only by '
                           'the registered authors of the submission.'
                           '\n\nThis Submission Page has now been removed '
                           'from general public view; if you wish, you can email us and '
                           'request to make it publicly visible again.')
            email_text_html += (
                '<p>We are sorry to inform you that your Submission '
                'has not been accepted for publication.</p>'
                '\n<p>You can view more details at the '
                '<a href="https://scipost.org/submission/'
                '{{ arxiv_identifier_w_vn_nr }}">Submission\'s Page</a>. '
                'Note that these details are viewable only by '
                'the registered authors of the submission.</p>'
                '<p>This Submission Page has now been removed '
                'from general public view; if you wish, you can email us and '
                'request to make it publicly visible again.</p>'
            )
        email_text += ('\n\nWe thank you very much for your contribution.'
                       '\n\nSincerely,'
                       '\n\nThe SciPost Team.')
        email_text_html += ('\n<p>We thank you very much for your contribution.</p>'
                            '<p>Sincerely,</p>'
                            '<p>The SciPost Team.</p>')
        email_context = Context({
            'auth_title': cls.submission.submitted_by.get_title_display(),
            'auth_last_name': cls.submission.submitted_by.user.last_name,
            'sub_title': cls.submission.title,
            'author_list': cls.submission.author_list,
            'arxiv_identifier_w_vn_nr': cls.submission.arxiv_identifier_w_vn_nr,
            'journal': cls.submission.get_submitted_to_journal_display(),
        })
        email_text_html += '<br/>' + EMAIL_FOOTER
        html_template = Template(email_text_html)
        html_version = html_template.render(email_context)
        emailmessage = EmailMultiAlternatives(
            'SciPost: College decision', email_text,
            'SciPost Editorial Admin <submissions@scipost.org>',
            [cls.submission.submitted_by.user.email],
            bcc=[cls.submission.editor_in_charge.user.email,
                 'submissions@scipost.org'],
            reply_to=['submissions@scipost.org'])
        emailmessage.attach_alternative(html_version, 'text/html')
        emailmessage.send(fail_silently=False)

    @classmethod
    def send_Fellows_voting_reminder_email(cls):
        """
        Requires loading 'Fellow_emails' attribute, which is a list of email addresses.
        """
        email_text = ('Dear Fellow,'
                      '\n\nYou have pending voting duties in the SciPost '
                      'submissions pool at https://scipost.org/submissions/pool'
                      ' (also accessible from your personal page '
                      'https://scipost.org/personal_page under the Editorial Actions tab). '
                      'Could you please have a quick look within the next couple of days, '
                      'so we can finish processing these submissions?'
                      '\n\nMany thanks in advance,'
                      '\n\nThe SciPost Team.')
        email_text_html = (
            '<p>Dear Fellow,</p>'
            '<p>You have pending voting duties in the SciPost '
            'submissions pool https://scipost.org/submissions/pool'
            ' (also accessible from your personal page '
            'https://scipost.org/personal_page under the Editorial Actions tab).</p>'
            '<p>Could you please have a quick look within the next couple of days, '
            'so we can finish processing these submissions?</p>'
            '<p>Many thanks in advance,</p>'
            '<p>The SciPost Team.</p><br/>' + EMAIL_FOOTER)
        email_context = Context({})
        html_template = Template(email_text_html)
        html_version = html_template.render(email_context)
        emailmessage = EmailMultiAlternatives(
            'SciPost: voting duties', email_text,
            'SciPost Editorial Admin <admin@scipost.org>',
            to=['admin@scipost.org'],
            bcc=cls.Fellow_emails,
            reply_to=['admin@scipost.org'])
        emailmessage.attach_alternative(html_version, 'text/html')
        emailmessage.send(fail_silently=False)