コード例 #1
0
    def patch(self, campaign_id):
        """
        This endpoint updates an existing campaign
        :param int|long campaign_id: Id of campaign
        """
        raise_if_dict_values_are_not_int_or_long(dict(campaign_id=campaign_id))
        # Get and validate request data
        data = request.get_json(silent=True)
        if not data:
            raise InvalidUsage("Received empty request body",
                               error_code=INVALID_REQUEST_BODY[1])
        is_hidden = data.get('is_hidden', False)
        if is_hidden not in (True, False, 1, 0):
            raise InvalidUsage(
                "is_hidden field should be a boolean, given: %s" % is_hidden,
                error_code=INVALID_INPUT[1])
        email_campaign = EmailCampaignBase.get_campaign_if_domain_is_valid(
            campaign_id, request.user)
        # Unschedule task from scheduler_service
        if email_campaign.scheduler_task_id:
            headers = {'Authorization': request.oauth_token}
            # campaign was scheduled, remove task from scheduler_service
            if CampaignUtils.delete_scheduled_task(
                    email_campaign.scheduler_task_id, headers):
                email_campaign.update(
                    scheduler_task_id='')  # Delete scheduler task id

        email_campaign.update(is_hidden=is_hidden)
        logger.info("Email campaign(id:%s) has been archived successfully" %
                    campaign_id)
        return dict(message="Email campaign (id: %s) updated successfully" %
                    campaign_id), codes.OK
コード例 #2
0
    def get(self):
        """
        GET /v1/email-campaigns         Fetches all EmailCampaign objects from auth user's domain
        """
        user = request.user
        page, per_page = get_pagination_params(
            request, error_code=INVALID_VALUE_OF_PAGINATION_PARAM[1])
        sort_type = request.args.get('sort_type', 'DESC')
        search_keyword = request.args.get('search', '')
        sort_by = request.args.get('sort_by', 'added_datetime')
        is_hidden = request.args.get('is_hidden', 0)
        user_id = request.args.get('user_id')

        # Validation of query parameters
        if isinstance(user_id, basestring):
            if not user_id.strip().isdigit() or int(user_id) <= 0:
                raise InvalidUsage(NOT_NON_ZERO_NUMBER[0].format('`user_id`'),
                                   error_code=NOT_NON_ZERO_NUMBER[1])
            if request.user.role.name != Role.TALENT_ADMIN \
                    and User.get_domain_id(user_id) != request.user.domain_id:
                raise ForbiddenError(
                    "Logged-in user and requested user_id are of different domains",
                    EMAIL_CAMPAIGN_FORBIDDEN[1])
            user_id = int(user_id)

        if not is_number(is_hidden) or int(is_hidden) not in (0, 1):
            raise InvalidUsage('`is_hidden` can be either 0 or 1',
                               error_code=INVALID_VALUE_OF_QUERY_PARAM[1])

        if sort_by not in ('added_datetime', 'name'):
            raise InvalidUsage('Value of sort_by parameter is not valid',
                               error_code=INVALID_VALUE_OF_QUERY_PARAM[1])

        if sort_type not in SORT_TYPES:
            raise InvalidUsage(
                'Value of sort_type parameter is not valid. Valid values are %s'
                % list(SORT_TYPES),
                error_code=INVALID_VALUE_OF_QUERY_PARAM[1])

        # Get all email campaigns from logged in user's domain
        query = EmailCampaign.get_by_domain_id_and_filter_by_name(
            user.domain_id,
            search_keyword,
            sort_by,
            sort_type,
            int(is_hidden),
            user_id=user_id)

        return get_paginated_response('email_campaigns',
                                      query,
                                      page,
                                      per_page,
                                      parser=EmailCampaign.to_dict)
コード例 #3
0
def validate_and_format_data_for_email_template_creation(data, domain_id):
    """
    Validates the data for creation of email-template-folder
    :param dict data: Data received from UI
    :param int|long domain_id: Id of domain of logged-in user
    :return: Dictionary of formatted data
    :rtype: dict
    """
    # Validation of required fields
    validate_required_fields(data, ('name', 'body_html'), error_code=MISSING_FIELD[1])
    template_name = data['name']
    body_html = data['body_html']
    template_folder_id = None
    # Raise errors if invalid input of string inputs
    if [item for item in (template_name, body_html) if not (isinstance(item, basestring) and str(item).strip())]:
        raise InvalidUsage("Expecting `name` and `body_html` as non-empty string", error_code=INVALID_INPUT[1])

    # Check if the name is already exists in the domain
    existing_template = UserEmailTemplate.get_by_name_and_domain_id(template_name, domain_id)
    if existing_template:
        raise InvalidUsage('Email template with name=%s already exists in the domain.' % template_name,
                           error_code=DUPLICATE_TEMPLATE_NAME[1])
    if 'template_folder_id' in data:
        template_folder_id = data['template_folder_id']
        if template_folder_id is None or not isinstance(template_folder_id, (int, long)) \
                or (isinstance(template_folder_id, (int, long)) and template_folder_id <= 0):
            raise InvalidUsage('Expecting template_folder_id to be positive integer', INVALID_INPUT[1])
        # Validate parent_id is valid
        EmailTemplateFolder.get_valid_template_folder(template_folder_id, domain_id)

    # If is_immutable value is not passed, make it as 0
    is_immutable = data.get('is_immutable', 0)

    if is_immutable is None or is_immutable not in (0, 1):
        raise InvalidUsage(error_message='Invalid input: is_immutable should be integer with value 0 or 1',
                           error_code=INVALID_INPUT[1])

    # strip whitespaces and return data
    return {
        'name': template_name.strip(),
        'body_html': body_html.strip(),
        'body_text': data.get('body_text'),
        'template_folder_id': template_folder_id,
        'is_immutable': is_immutable
    }
コード例 #4
0
def validate_and_format_data_for_template_folder_creation(data, domain_id):
    """
    Validates the data for creation of email-template-folder
    :param dict data: Data received from UI
    :param int|long domain_id: Id of domain of logged-in user
    :return: Dictionary of formatted data
    :rtype: dict
    """
    parent_id = None
    folder_name = data.get('name')
    # Validation of required fields
    validate_required_fields(data, ('name',), error_code=MISSING_FIELD[1])

    # Validation of folder name
    if not isinstance(folder_name, basestring) or not str(folder_name).strip():
        raise InvalidUsage('Invalid input: Folder name must be a valid string.', error_code=INVALID_INPUT[1])

    # Check if the name already exists under same domain
    duplicate = EmailTemplateFolder.get_by_name_and_domain_id(folder_name, domain_id)
    if duplicate:
        raise InvalidUsage(DUPLICATE_TEMPLATE_FOLDER_NAME[0], error_code=DUPLICATE_TEMPLATE_FOLDER_NAME[1])
    if 'parent_id' in data:
        parent_id = data['parent_id']
        # Validate parent_id is valid
        if parent_id is None or not isinstance(parent_id, (int, long)) \
                or (isinstance(parent_id, (int, long)) and parent_id <= 0):
            raise InvalidUsage('Expecting parent_id to be positive integer', INVALID_INPUT[1])

        EmailTemplateFolder.get_valid_template_folder(parent_id, request.user.domain_id)
    # If is_immutable value is not passed, make it as 0
    is_immutable = data.get('is_immutable', 0)

    if is_immutable is None or is_immutable not in (0, 1):
        raise InvalidUsage(error_message='Invalid input: is_immutable should be integer with value 0 or 1',
                           error_code=INVALID_INPUT[1])

    # strip whitespaces and return data
    return {
        'name': folder_name.strip(),
        'parent_id': parent_id,
        'is_immutable': is_immutable
    }
コード例 #5
0
    def post(self):
        """
        This will save an entry in database table email_client_credentials.

        .. Request Body::
                        {
                            "host": "Host Name",
                            "port": 123,
                            "name": "Server Name",
                            "email": "*****@*****.**",
                            "password": "******",
                        }

        .. Response::
                       {
                          "id": 347
                       }

        .. Status:: 201 (Resource created)
                    400 (Bad request)
                    401 (Unauthorized to access getTalent)
                    500 (Internal server error)
        """
        data = get_json_data_if_validated(request, EMAIL_CLIENTS_SCHEMA)
        data = format_email_client_data(data)
        data['user_id'] = request.user.id
        client_in_db = EmailClientCredentials.get_by_user_id_host_and_email(
            data['user_id'], data['host'], data['email'])
        if client_in_db:
            raise InvalidUsage(
                'Email client with given data already present in database')

        client = EmailClientBase.get_client(data['host'])
        client = client(data['host'], data['port'], data['email'],
                        data['password'])
        logger.info('Connecting with given email-client')
        client.connect()
        client.authenticate()
        logger.info(
            'Successfully connected and authenticated with given email-client')
        # Encrypt password
        ciphered_password = encrypt(
            app.config[TalentConfigKeys.ENCRYPTION_KEY], data['password'])
        b64_password = b64encode(ciphered_password)
        data['password'] = b64_password
        email_client = EmailClientCredentials(**data)
        EmailClientCredentials.save(email_client)
        headers = {
            'Location':
            EmailCampaignApiUrl.EMAIL_CLIENT_WITH_ID % email_client.id
        }
        return ApiResponse(dict(id=email_client.id),
                           status=requests.codes.CREATED,
                           headers=headers)
コード例 #6
0
 def authenticate(self, connection_quit=True):
     """
     This first connects with POP server. It then tries to login to server.
     """
     try:
         self.connection.user(self.email)
         self.connection.pass_(self.password)
     except poplib.error_proto as error:
         logger.exception(error.message)
         raise InvalidUsage(
             'Invalid credentials provided. Could not authenticate with POP server',
             additional_error_info=dict(pop_error=error.message))
コード例 #7
0
 def authenticate(self, connection_quit=True):
     """
     This first connects with IMAP server. It then tries to login to server.
     :type connection_quit: bool
     """
     try:
         self.connection.login(self.email, self.password)
     except imaplib.IMAP4_SSL.error as error:
         logger.exception(error.message)
         raise InvalidUsage(
             'Could not authenticate with IMAP server.',
             additional_error_info=dict(imap_error=error.message))
コード例 #8
0
    def post(self):
        """
        This creates a base campaign with following payload

                {
                    "name": "Jobs at getTalent
                    "Description": "We are looking for Python developers
                }

        """
        user = request.user
        data = request.get_json(silent=True)
        if not data:
            raise InvalidUsage("Received empty request body")
        name = data.get('name', '')
        description = data.get('description', '')
        if not name or not description:
            raise InvalidUsage('Name and description are required fields')
        base_campaign = BaseCampaign(user_id=user.id, name=name, description=description)
        base_campaign.save()
        return {'id': base_campaign.id}, codes.CREATED
コード例 #9
0
def do_prefs_url_replacement(text, candidate, candidate_address):
    """
    Here we do the replacement of merge tag "*|PREFERENCES_URL|*". After replacement this will become the
    URL for the candidate to unsubscribe the email-campaign.
    :param string text: This maybe subject, body_html or body_text of email-campaign
    :param Candidate candidate: Object of candidate to which email-campaign is supposed to be sent
    :param basestring candidate_address: Address of Candidate to which email campaign is being sent
    :rtype: string
    """
    candidate_id = candidate.id

    if not (isinstance(text, basestring) and text):
        raise InvalidUsage('Text should be non-empty string')
    if not (isinstance(candidate_id, (int, long)) and candidate_id):
        raise InvalidUsage('candidate_id should be positive int"long')
    host_name = get_web_app_url()
    secret_key_id = jwt_security_key()
    secret_key = redis_store.get(secret_key_id)
    s = Serializer(secret_key, expires_in=SIX_MONTHS_EXPIRATION_TIME)

    payload = {"candidate_id": candidate_id}

    unsubscribe_url = host_name + (
        '/candidates/%s/preferences?%s' %
        (str(candidate_id),
         urllib.urlencode({
             'token': '%s.%s' % (s.dumps(payload), secret_key_id),
             'email': candidate_address or ''
         })))

    # In case the user accidentally wrote http://*|PREFERENCES_URL|* or https://*|PREFERENCES_URL|*
    text = text.replace("http://" + DEFAULT_PREFERENCES_URL_MERGETAG,
                        unsubscribe_url)
    text = text.replace("https://" + DEFAULT_PREFERENCES_URL_MERGETAG,
                        unsubscribe_url)

    # The normal case
    text = text.replace(DEFAULT_PREFERENCES_URL_MERGETAG, unsubscribe_url)
    return text
コード例 #10
0
 def get_client(host):
     """
     This gets the required client for given host.
     :param string host: Hostname e.g. smtp.gmail.com
     """
     host = host.strip().lower()
     if 'smtp' in host:
         client = SMTP
     elif 'imap' in host:
         client = IMAP
     elif 'pop' in host:
         client = POP
     else:
         raise InvalidUsage('Unknown host provided')
     return client
コード例 #11
0
 def test_create_email_campaign_with_invalid_email_client_id(
         self, access_token_first, smartlist_user1_domain1_in_db):
     """
     Here we try to create an email-campaign with invalid email-client-id. It should
     result in invalid usage error.
     """
     campaign_data = create_data_for_campaign_creation(
         smartlist_user1_domain1_in_db['id'])
     campaign_data[
         'email_client_id'] = CampaignsTestsHelpers.get_non_existing_id(
             EmailClient)
     response = create_email_campaign_via_api(access_token_first,
                                              campaign_data)
     assert response.status_code == InvalidUsage.http_status_code()
     json_response = response.json()
     assert 'email_client_id' in json_response['error']['message']
コード例 #12
0
def validate_data_to_schedule_campaign(campaign_data):
    """
    This validates the data provided to schedule a campaign.
    - Get number of seconds by validating given frequency_id
    - If end_datetime is not given and frequency is for periodic task, we raise Invalid usage.
    - Returns frequency_id, start_datetime and end_datetime
    This function is used in data_validation_for_campaign_schedule() of CampaignBase class.
    :param dict campaign_data: Campaign data
    :rtype: tuple
    """
    start_datetime_obj, end_datetime_obj = None, None
    frequency_id = campaign_data.get('frequency_id')  # required
    # Get number of seconds from frequency_id. If frequency is there then there must be a start time.
    try:
        frequency = Frequency.get_seconds_from_id(frequency_id)
    except InvalidUsage as error:
        raise InvalidUsage(error.message, error_code=INVALID_INPUT[1])
    # Get start datetime string
    start_datetime = campaign_data.get('start_datetime')
    # Get end datetime string
    end_datetime = campaign_data.get('end_datetime')

    if frequency and not start_datetime:
        raise UnprocessableEntity("Frequency requires `start_datetime`.", error_code=MISSING_FIELD[1])

    if frequency and not end_datetime:
        raise UnprocessableEntity("`end_datetime` is required to schedule a periodic task",
                                  error_code=MISSING_FIELD[1])
    # Validate format and value
    if start_datetime:
        start_datetime_obj = DatetimeUtils.get_datetime_obj_if_format_is_valid(start_datetime,
                                                                               error_code=INVALID_DATETIME_FORMAT[1])
        if not DatetimeUtils(start_datetime_obj).is_in_future():
            raise UnprocessableEntity('`start_datetime` must be in future. Given {}'.format(start_datetime),
                                      error_code=INVALID_DATETIME_VALUE[1])
    if end_datetime:
        end_datetime_obj = DatetimeUtils.get_datetime_obj_if_format_is_valid(end_datetime,
                                                                             error_code=INVALID_DATETIME_FORMAT[1])
        if not DatetimeUtils(end_datetime_obj).is_in_future():
            raise UnprocessableEntity('`end_datetime` must be in future. Given {}'.format(end_datetime),
                                      error_code=INVALID_DATETIME_VALUE[1])

    if start_datetime and end_datetime and start_datetime_obj > end_datetime_obj:
        raise UnprocessableEntity("`end_datetime` cannot be before `start_datetime`",
                                  error_code=INVALID_DATETIME_VALUE[1])
    return frequency_id, frequency, start_datetime, end_datetime
コード例 #13
0
 def authenticate(self, connection_quit=True):
     """
     This first connects with SMTP server. It then tries to login to server.
     """
     try:
         self.connection.login(self.email, self.password)
     except smtplib.SMTPAuthenticationError as error:
         logger.exception(error.smtp_error)
         raise InvalidUsage(
             'Invalid credentials provided. Could not authenticate with SMTP server',
             additional_error_info=dict(smtp_error=error.smtp_error))
     except smtplib.SMTPException as error:
         logger.exception(error.message)
         raise InternalServerError(
             'Could not authenticate with SMTP server',
             additional_error_info=dict(smtp_error=error.message))
     if connection_quit:
         self.connection.quit()
コード例 #14
0
def validate_and_format_request_data(data, current_user):
    """
    Validates the request form data and returns the formatted data with leading and trailing
    white spaces stripped.
    :param dict data: Data received from UI
    :param User current_user: Logged-in user's object
    :return: Dictionary of formatted data
    :rtype: dict
    """
    name = data.get('name')  # required
    subject = data.get('subject')  # required
    description = data.get('description', '')  # required
    _from = data.get('from')
    reply_to = data.get('reply_to')
    body_html = data.get('body_html')  # required
    body_text = data.get('body_text')
    list_ids = data.get('list_ids')  # required
    email_client_id = data.get('email_client_id')
    frequency_id = data.get('frequency_id')  # required
    email_client_credentials_id = data.get('email_client_credentials_id')
    base_campaign_id = data.get('base_campaign_id')

    # Find if any required key has no valid value
    validate_required_fields(data, EmailCampaignBase.REQUIRED_FIELDS, error_code=MISSING_FIELD[1])

    # Raise errors if invalid input of string inputs
    if [item for item in (name, subject, body_html) if not (isinstance(item, basestring) and str(item).strip())]:
        raise InvalidUsage("Expecting `name`, `subject` and `body_html` as non-empty string",
                           error_code=INVALID_INPUT[1])
    if not frequency_id:
        raise InvalidUsage(INVALID_INPUT[0].format("`frequency_id`"), error_code=INVALID_INPUT[1])

    # Validation for list ids belonging to same domain
    validate_smartlist_ids(list_ids, current_user, error_code=INVALID_INPUT[1],
                           resource_not_found_error_code=SMARTLIST_NOT_FOUND[1],
                           forbidden_error_code=SMARTLIST_FORBIDDEN[1])

    frequency_id, frequency, start_datetime, end_datetime = validate_data_to_schedule_campaign(data)

    if email_client_id:
        # Check if email_client_id is valid
        email_client = EmailClient.query.get(email_client_id)
        if not email_client:
            raise InvalidUsage("`email_client_id` is not valid id.")

    # TODO: Add custom error code in GET-2573
    # In case user wants to send email-campaign with its own account
    if email_client_credentials_id:
        email_client_credentials = EmailClientCredentials.get_by_id(email_client_credentials_id)
        if not EmailClientBase.is_outgoing(email_client_credentials.host):
            raise InvalidUsage("Selected email-client must be of type `outgoing`")

    # TODO: Add custom error code in GET-2556
    # Validation for base_campaign_id
    if base_campaign_id:
        validate_base_campaign_id(base_campaign_id, current_user.domain_id)

    # strip whitespaces and return data
    return {
        'name': name.strip(),
        'subject': subject.strip(),
        'description': description.strip(),
        'from': get_or_set_valid_value(_from, basestring, '').strip(),
        'reply_to': get_or_set_valid_value(reply_to, basestring, '').strip(),
        'body_html': body_html.strip(),
        'body_text': get_or_set_valid_value(body_text, basestring, '').strip(),
        'list_ids': list_ids,
        'email_client_id': email_client_id,
        'start_datetime': start_datetime,
        'end_datetime': end_datetime,
        'frequency_id': frequency_id,
        'email_client_credentials_id': email_client_credentials_id,
        'base_campaign_id': base_campaign_id
    }
コード例 #15
0
    def get(self, base_campaign_id):
        """
        This resource returns event and all chained campaigns with given base_campaign_id.
        ..Response::

        {
        "event": {
                "cost": 0,
                "start_datetime": "2016-08-13 16:21:42",
                "venue_id": 307,
                "user_id": 1,
                "is_deleted_from_vendor": 0,
                "description": "Test Event Description",
                "social_network_id": 13,
                "url": "",
                "title": "Eventbrite Test Event",
                "registration_instruction": "Just Come",
                "max_attendees": 10,
                "timezone": "Asia/Karachi",
                "currency": "USD",
                "venue": {
                              "city": "Lahore",
                              "user_id": 1,
                              "social_network_id": 18,
                              "country": "",
                              "longitude": 0,
                              "social_network_venue_id": "16271034",
                              "state": "Punjab",
                              "latitude": 0,
                              "zip_code": "",
                              "address_line_2": "H# 163, Block A",
                              "id": 307,
                              "address_line_1": "New Muslim Town"
                            },

                "rsvps": [
                          {
                            "social_network_rsvp_id": "6956",
                            "status": "yes",
                            "social_network_id": 13,
                            "event_id": 1,
                            "payment_status": "",
                            "datetime": "",
                            "candidate_id": 362553,
                            "id": 2
                          },
                          {
                            "social_network_rsvp_id": "2983",
                            "status": "yes",
                            "social_network_id": 13,
                            "event_id": 1,
                            "payment_status": "",
                            "datetime": "",
                            "candidate_id": 362555,
                            "id": 3
                          },
                          {
                            "social_network_rsvp_id": "5146",
                            "status": "yes",
                            "social_network_id": 13,
                            "event_id": 1,
                            "payment_status": "",
                            "datetime": "",
                            "candidate_id": 362557,
                            "id": 4
                          }
                        ],
                "tickets_id": 53364240,
                "social_network_group_id": "18837246",
                "group_url_name": "QC-Python-Learning",
                "organizer_id": 33,
                "base_campaign_id": 1,
                "id": 1,
                "social_network_event_id": "27067814562",
                "end_datetime": "2016-08-14 16:21:42"
              },
        "email_campaigns": [
                {
                      "email_client_credentials_id": null,
                      "start_datetime": null,
                      "user_id": 1,
                      "name": "Email campaign",
                      "body_text": null,
                      "description": null,
                      "list_ids": [1],
                      "body_html": null,
                      "blasts": [
                            {
                              "updated_datetime": "2016-02-10 20:35:57",
                              "sends": 0,
                              "bounces": 0,
                              "campaign_id": 11,
                              "text_clicks": 0,
                              "html_clicks": 0,
                              "complaints": 0,
                              "id": 15,
                              "opens": 0,
                              "sent_datetime": "2016-02-10 20:38:39"
                            },
                            {
                              "updated_datetime": "2016-02-10 20:35:57",
                              "sends": 0,
                              "bounces": 0,
                              "campaign_id": 11,
                              "text_clicks": 0,
                              "html_clicks": 0,
                              "complaints": 0,
                              "id": 16,
                              "opens": 0,
                              "sent_datetime": "2016-02-10 20:38:40"
                            }
                          ],
                      "added_datetime": "2016-02-10T20:35:57+00:00",
                      "frequency": null,
                      "end_datetime": null,
                      "talent_pipelines": [],
                      "reply_to": "*****@*****.**",
                      "from": "basit",
                      "is_hidden": false,
                      "base_campaign_id": 1,
                      "id": 11,
                      "subject": "email campaign sample subject"
                    },
                {
                      "email_client_credentials_id": null,
                      "start_datetime": null,
                      "user_id": 1,
                      "name": "Email campaign",
                      "body_text": null,
                      "description": null,
                      "list_ids": [1],
                      "body_html": null,
                      "blasts": [
                            {
                              "updated_datetime": "2016-02-10 20:39:54",
                              "sends": 1,
                              "bounces": 0,
                              "campaign_id": 12,
                              "text_clicks": 0,
                              "html_clicks": 0,
                              "complaints": 0,
                              "id": 18,
                              "opens": 0,
                              "sent_datetime": "2016-02-10 20:39:44"
                            }
                      ],
                      "added_datetime": "2016-02-10T20:35:57+00:00",
                      "frequency": null,
                      "end_datetime": null,
                      "talent_pipelines": [],
                      "reply_to": "*****@*****.**",
                      "from": "basit",
                      "is_hidden": false,
                      "base_campaign_id": 1,
                      "id": 12,
                      "subject": "email campaign sample subject"
                    }
              ]
        }
        """
        json_event = None
        email_campaigns_list = []
        validate_base_campaign_id(base_campaign_id, request.user.domain_id)
        base_campaign = BaseCampaign.get_by_id(base_campaign_id)
        base_campaign_event = BaseCampaignEvent.filter_by_keywords(base_campaign_id=base_campaign_id)
        if base_campaign_event:
            event = base_campaign_event[0].event  # Pick first associated event
            json_event = event.to_json()
            json_event['rsvps'] = [rsvp.to_json() for rsvp in event.rsvps.all()]
            json_event['venue'] = event.venue.to_json() if event.venue else {}
        email_campaigns = base_campaign.email_campaigns.all()
        if not json_event and not email_campaigns:
            raise InvalidUsage('Requested base campaign is orphaned')
        for email_campaign in email_campaigns:
            json_email_campaign = email_campaign.to_dict()
            json_email_campaign['blasts'] = [blast.to_json() for blast in email_campaign.blasts.all()]
            email_campaigns_list.append(json_email_campaign)
        return {'event': json_event, 'email_campaigns': email_campaigns_list}