def process_email(self, message):
        """
        Review the metadata and calendar attachment (if present) of an email
        message to see if it fits the our criteria of a valid Data Hub meeting
        request.  If it does, create a draft Interaction for it.

        :param message: mailparser.MailParser object - the message to process
        """
        # Parse the email for interaction data
        email_parser = CalendarInteractionEmailParser(message)
        try:
            interaction_data = email_parser.extract_interaction_data_from_email(
            )
        except InvalidInviteError as exc:
            self._handle_invalid_invite(exc, message)
            return (False, repr(exc))

        # Make the same-company check easy to remove later if we allow Interactions
        # to have contacts from more than one company
        sanitised_contacts = _filter_contacts_to_single_company(
            interaction_data['contacts'],
            interaction_data['top_company'],
        )
        interaction_data['contacts'] = sanitised_contacts

        # Replace the meeting invite subject with one which details the people attending
        interaction_data['subject'] = _get_meeting_subject(
            interaction_data['sender'],
            interaction_data['contacts'],
            interaction_data['secondary_advisers'],
        )

        # Get a serializer for the interaction data
        try:
            serializer = self.validate_with_serializer(interaction_data)
        except serializers.ValidationError as exc:
            errors = _flatten_serializer_errors_to_list(exc.detail)
            self._notify_meeting_ingest_failure(message, errors)
            return (False, ', '.join(errors))

        # For our initial iteration of this feature, we are ignoring meeting updates
        matching_interactions = Interaction.objects.filter(source__contains={
            'meeting': {
                'id': interaction_data['meeting_details']['uid']
            }
        }, )
        if matching_interactions.exists():
            return (False, 'Meeting already exists as an interaction')

        interaction = self.save_serializer_as_interaction(
            serializer, interaction_data)
        notify_meeting_ingest_success(
            interaction_data['sender'],
            interaction,
            get_all_recipients(message),
        )
        return (True, f'Successfully created interaction #{interaction.id}')
 def _notify_meeting_ingest_failure(self, message, errors):
     try:
         sender_email = message.from_[0][1]
     except IndexError:
         logger.info(
             'Cannot extract email of the sender from message. '
             'Failure notification will not be sent.', )
         return
     sender_adviser = get_best_match_adviser_by_email(sender_email)
     if not sender_adviser:
         logger.info(
             'Cannot find adviser matching the sender email in message. '
             'Failure notification will not be sent.', )
         return
     recipient_emails = get_all_recipients(message)
     notify_meeting_ingest_failure(sender_adviser, errors, recipient_emails)
    def extract_interaction_data_from_email(self):
        """
        Extract interaction data from the email message as a dictionary.

        This raises an InvalidInviteError if the interaction data could not be fully extracted
        or is invalid according to business logic.
        """
        calendar_event = self._extract_and_validate_calendar_event_metadata()
        all_recipients = get_all_recipients(self.message)
        sender = self._extract_and_validate_sender_adviser()
        secondary_advisers = self._extract_secondary_advisers(
            all_recipients, sender)
        contacts = self._extract_and_validate_contacts(all_recipients)
        top_company = _get_top_company_from_contacts(contacts)

        return {
            'sender': sender,
            'contacts': contacts,
            'secondary_advisers': secondary_advisers,
            'top_company': top_company,
            'date': calendar_event['start'],
            'meeting_details': calendar_event,
            'subject': calendar_event['subject'],
        }