class PushCampaignBlasts(Resource): """ Endpoint looks like /v1/push-campaigns/:id/blasts. This class returns all the blast objects associated with given campaign. """ decorators = [require_oauth()] def get(self, campaign_id): """ This endpoint returns a list of blast objects (dict) associated with a specific push campaign. :param campaign_id: int, unique id of a push campaign :return: dict containing list of blasts :Example: >>> import requests >>> headers = {'Authorization': 'Bearer <access_token>'} >>> campaign_id = 1 >>> response = requests.get(PushCampaignApiUrl.BLASTS % campaign_id, >>> headers=headers) .. Response:: { "blasts": [ { "campaign_id": 2, "clicks": 6, "id": 1, "sends": 10, "updated_datetime": "2015-12-30 14:33:44" }, { "campaign_id": 2, "clicks": 11, "id": 2, "sends": 20, "updated_datetime": "2015-12-30 14:33:00" } ] } .. Status:: 200 (OK) 401 (Unauthorized to access getTalent) 403 (Forbidden, Not authorized to access this campaign's sends) 404 (Campaign not found) 500 (Internal Server Error) """ user = request.user page, per_page = get_pagination_params(request) # Get a campaign that was created by this user campaign = PushCampaignBase.get_campaign_if_domain_is_valid( campaign_id, user, CampaignUtils.PUSH) # query = PushCampaignBlast.query.filter_by(campaign_id=campaign.id) return get_paginated_response('blasts', campaign.blasts, page, per_page)
class PushCampaignSends(Resource): """ Endpoint looks like /v1/push-campaigns/:id/sends This resource is used to GET Campaign sends """ decorators = [require_oauth()] def get(self, campaign_id): """ Returns campaign sends for given push campaign id :param campaign_id: integer, unique id representing push campaign in getTalent database :return: list of Push campaign's sends :Example: >>> import requests >>> headers = {'Authorization': 'Bearer <access_token>'} >>> campaign_id = 1 >>> response = requests.get(PushCampaignApiUrl.SENDS % campaign_id, >>> headers=headers) .. Response:: { "campaign_sends": [ { "campaign_blast_id": 10, "candidate_id": 268, "id": 6, "sent_datetime": "2015-12-30 17:03:53" }, { "campaign_blast_id": 12, "candidate_id": 268, "id": 7, "sent_datetime": "2015-12-30 17:07:39" } ] } .. Status:: 200 (OK) 401 (Unauthorized to access getTalent) 403 (Forbidden, Not authorized to access this campaign's sends) 404 (Campaign not found) 500 (Internal Server Error) """ user = request.user page, per_page = get_pagination_params(request) # Get a campaign that was created by this user campaign = PushCampaignBase.get_campaign_if_domain_is_valid( campaign_id, user, CampaignUtils.PUSH) # Add sends for every blast to `sends` list to get all sends of a campaign. # A campaign can have multiple blasts query = PushCampaignSend.query.join(PushCampaignBlast) query = query.filter(PushCampaignBlast.campaign_id == campaign.id) return get_paginated_response('sends', query, page, per_page)
class PushCampaignBlastById(Resource): """ Resource is used to retrieve a specific campaign's blast. """ decorators = [require_oauth()] def get(self, campaign_id, blast_id): """ This endpoint returns a specific blast object (dict) associated with a specific push campaign. :param campaign_id: int, unique id of a push campaign :param blast_id: int, unique id of a blast of campaign :return: dict containing blast data :Example: >>> import requests >>> headers = {'Authorization': 'Bearer <access_token>'} >>> campaign_id = 1 >>> blast_id = 3 >>> response = requests.get(PushCampaignApiUrl.BLAST % (campaign_id, blast_id), >>> headers=headers) .. Response:: { "blast": { "campaign_id": 2, "clicks": 6, "id": 3, "sends": 10, "updated_datetime": "2015-12-30 14:33:44" } } .. Status:: 200 (OK) 401 (Unauthorized to access getTalent) 403 (Forbidden, Not authorized to access this campaign's blast) 404 (Blast not found, Campaign not found) 500 (Internal Server Error) """ user = request.user # Get a campaign that was created by this user blast = CampaignBase.get_valid_blast_obj(campaign_id, blast_id, user, CampaignUtils.PUSH) return dict(blast=blast.to_json()), codes.OK
class SendPushCampaign(Resource): """ Resource sends a specific campaign to associated candidates. """ decorators = [require_oauth()] def post(self, campaign_id): """ It sends given Campaign (from given campaign id) to the smartlist candidates associated with given campaign. :Example: >>> import requests >>> headers = {'Authorization': 'Bearer <access_token>'} >>> campaign_id = 1 >>> response = requests.post(PushCampaignApiUrl.SEND % campaign_id, >>> headers=headers) .. Response:: { "message": "Push campaign (id: 1) has been sent successfully to all candidates" } .. Status:: 200 (OK) 401 (Unauthorized to access getTalent) 404 (Campaign not found) 500 (Internal Server Error) .. Error Codes:: 5102 (NoSmartlistAssociated) :param campaign_id: integer, unique id representing campaign in GT database """ user = request.user campaign_obj = PushCampaignBase(user_id=user.id, campaign_id=campaign_id) campaign_obj.send() return dict(message='Campaign(id:%s) is being sent to candidates' % campaign_id), codes.OK
class UrlConversionResource(Resource): """ Resource is used to retrieve URL conversion object. """ decorators = [require_oauth()] def get(self, **kwargs): """ This endpoint returns a UrlConversion object given by id. To get this resource, user must be in same domain as the owner of this send. :Example: >>> import requests >>> headers = {'Authorization': 'Bearer <access_token>'} >>> _id = 10 >>> response = requests.get(PushCampaignApiUrl.URL_CONVERSION % _id, >>> headers=headers) Or you can get UrlConversion JSON object using campaign send id >>> send_id = 10 >>> response = requests.get(PushCampaignApiUrl.URL_CONVERSION_BY_SEND_ID % send_id, >>> headers=headers) .. Response:: { "url_conversion": { "id": 10, "last_hit_time": "", "hit_count": 0, "added_time": "2016-02-12 12:46:09", "source_url": "http://127.0.0.1:8013/v1/redirect/1638?valid_until= 1486903569.01&auth_user=no_user&extra=&signature=ha9B947UcLJ0 jbqqSRF4O82%2Bb5E%3D", "destination_url": "https://www.digitalocean.com/community/tutorials/ how-to-install-and-use-docker-getting-started" } } .. Status:: 200 (OK) 401 (Unauthorized to access getTalent) 403 (Can't get send url conversion with different domain) 500 (Internal Server Error) """ user = request.user _id = kwargs.get('_id') send_id = kwargs.get('send_id') if _id: url_conversion = UrlConversion.get_by_id(_id) if not url_conversion: raise ResourceNotFound('Resource not found with id: %s' % _id) url_conversion = UrlConversion.get_by_id_and_domain_id_for_push_campaign_send( _id, user.domain_id) # send_url_conversion = url_conversion.push_campaign_sends_url_conversions.first() if not url_conversion: raise ForbiddenError( "You can not get other domain's url_conversion records") return {'url_conversion': url_conversion.to_json()}, codes.OK elif send_id: url_conversion = PushCampaignBase.get_url_conversion_by_send_id( send_id, CampaignUtils.PUSH, user) return {'url_conversion': url_conversion.to_json()}, codes.OK def delete(self, **kwargs): """ This endpoint deletes a UrlConversion object given by url_conversion id or campaign send id. To delete this resource, user must be in same domain as the owner of this send. :Example: >>> import requests >>> headers = {'Authorization': 'Bearer <access_token>'} >>> _id = 10 >>> response = requests.delete(PushCampaignApiUrl.URL_CONVERSION % _id, >>> headers=headers) Or you can delete a UrlConversion rescord by campaign send id. >>> send_id = 10 >>> response = requests.delete(PushCampaignApiUrl.URL_CONVERSION_BY_SEND_ID % send_id, >>> headers=headers) .. Response:: { "message": "UrlConversion (id: %s) deleted successfully" } .. Status:: 200 (OK) 401 (Unauthorized to access getTalent) 403 (Can't get send url conversion with different domain) 404 (Resource Not found) 500 (Internal Server Error) """ user = request.user _id = kwargs.get('_id') send_id = kwargs.get('send_id') if _id: url_conversion = UrlConversion.get_by_id(_id) if not url_conversion: raise ResourceNotFound('Resource not found with id: %s' % _id) url_conversion = UrlConversion.get_by_id_and_domain_id_for_push_campaign_send( _id, user.domain_id) if not url_conversion: raise ForbiddenError( "You can not delete other domain's url_conversion records") UrlConversion.delete(url_conversion) return { 'message': "UrlConversion (id: %s) deleted successfully" % _id }, codes.OK elif send_id: url_conversion = PushCampaignBase.get_url_conversion_by_send_id( send_id, CampaignUtils.PUSH, user) _id = url_conversion.id UrlConversion.delete(url_conversion) return { 'message': "UrlConversion (id: %s) deleted successfully" % _id }, codes.OK
class PushCampaignBlastSends(Resource): """ Endpoint looks like /v1/push-campaigns/:id/blasts/:id/sends This resource is used to GET Campaign "sends" for one particular blast of a given campaign. """ decorators = [require_oauth()] def get(self, campaign_id, blast_id): """ Returns Campaign sends for given campaign_id and blast_id. We can pass query params like page number and page size like /v1/push-campaigns/:campaign_id/blasts/:id/sends?page=2&per_page=20 :param blast_id: integer, blast unique id :param campaign_id: integer, unique id representing campaign in getTalent database :return: 1- count of campaign sends and 2- Push campaign sends records as dict :Example: >>> import requests >>> headers = {'Authorization': 'Bearer <access_token>'} >>> campaign_id = 1 >>> blast_id = 1 >>> response = requests.get(PushCampaignApiUrl.BLAST_SENDS % (campaign_id, blast_id), >>> headers=headers) .. Response:: { "sends": [ { "candidate_id": 1, "id": 9, "sent_datetime": "2015-11-23 18:25:09", "blast_id": 1, "updated_datetime": "2015-11-23 18:25:08" }, { "candidate_id": 2, "id": 10, "sent_datetime": "2015-11-23 18:25:13", "blast_id": 1, "updated_datetime": "2015-11-23 18:25:13" } ] } .. Status:: 200 (OK) 400 (Bad request) 401 (Unauthorized to access getTalent) 403 (Forbidden, Not authorized to access this campaign) 404 (Campaign not found) 500 (Internal Server Error) """ user = request.user page, per_page = get_pagination_params(request) # Get a campaign that was created by this user campaign = PushCampaignBase.get_campaign_if_domain_is_valid( campaign_id, user, CampaignUtils.PUSH) blast = PushCampaignBlast.get_by_id(blast_id) if not blast: raise ResourceNotFound('Campaign Blast not found with id: %s' % blast_id) if blast.campaign_id == campaign.id: query = PushCampaignSend.query.filter_by(blast_id=blast.id) return get_paginated_response('sends', query, page, per_page) else: raise ForbiddenError( 'Campaign Blast (id: %s) is not associated with campaign (id: %s)' % (blast_id, campaign.id))
class SchedulePushCampaignResource(Resource): """ This resource is used to schedule, reschedule and unschedule a specific campaign. """ decorators = [require_oauth()] def post(self, campaign_id): """ It schedules an Push Notification campaign using given campaign_id by making HTTP request to scheduler_service. :Example: >>> import json >>> import requests >>> headers = {'Authorization': 'Bearer <access_token>', >>> 'Content-type': 'application/json'} >>> schedule_data = >>> { >>> "frequency_id": 2, >>> "start_datetime": "2015-11-26T08:00:00Z", >>> "end_datetime": "2015-11-30T08:00:00Z" >>> } >>> campaign_id = str(1) >>> schedule_data = json.dumps(schedule_data) >>> response = requests.post(PushCampaignApiUrl.SCHEDULE % campaign_id, >>> headers=headers, data=schedule_data) .. Response:: { "message": "Campaign(id:1) is has been scheduled. "task_id"; "33e32e8ac45e4e2aa710b2a04ed96371" } .. Status:: 200 (OK) 400 (Bad request) 401 (Unauthorized to access getTalent) 403 (Forbidden Error) 404 (Campaign not found) 500 (Internal Server Error) .. Error codes: 7006 (CampaignAlreadyScheduled) :param campaign_id: integer, unique id representing campaign in GT database :return: dict containing message and task_id. """ user = request.user get_json_data_if_validated(request, CAMPAIGN_SCHEDULE_SCHEMA) if not campaign_id: raise InvalidUsage('campaign_id should be a positive number') pre_processed_data = PushCampaignBase.data_validation_for_campaign_schedule( request, campaign_id, CampaignUtils.PUSH) campaign_obj = PushCampaignBase(user.id) PushCampaignBase.get_campaign_if_domain_is_valid( campaign_id, user, CampaignUtils.PUSH) campaign_obj.campaign = pre_processed_data['campaign'] task_id = campaign_obj.schedule(pre_processed_data['data_to_schedule']) message = 'Campaign(id:%s) has been scheduled.' % campaign_id return dict(message=message, task_id=task_id), codes.OK def put(self, campaign_id): """ This endpoint is to reschedule a campaign. It first deletes the old schedule of campaign from scheduler_service and then creates new task. :Example: >>> import json >>> import requests >>> headers = {'Authorization': 'Bearer <access_token>', >>> 'Content-type': 'application/json'} >>> schedule_data = >>> { >>> "frequency_id": 2, >>> "start_datetime": "2015-11-26T08:00:00Z", >>> "end_datetime": "2015-11-30T08:00:00Z" >>> } >>> campaign_id = 1 >>> data = json.dumps(schedule_data) >>> response = requests.put(PushCampaignApiUrl.CAMPAIGN % campaign_id, >>> headers=headers, data=data) .. Response:: { "message": "Campaign(id:1) is has been re-scheduled. "task_id"; "33e32e8ac45e4e2aa710b2a04ed96371" } .. Status:: 200 (OK) 400 (Bad request) 401 (Unauthorized to access getTalent) 403 (Forbidden Error) 404 (Campaign not found) 500 (Internal Server Error) :param campaign_id: integer, unique id representing campaign in GT database :return: dict containing message and task_id. """ get_json_data_if_validated(request, CAMPAIGN_SCHEDULE_SCHEMA) if not campaign_id: raise InvalidUsage('campaign_id should be a positive number') # create object of class PushCampaignBase push_camp_obj = PushCampaignBase(request.user.id) task_id = push_camp_obj.reschedule(request, campaign_id) if task_id: message = 'Campaign(id:%s) has been re-scheduled.' % campaign_id else: message = 'Campaign(id:%s) is already scheduled with given data.' % campaign_id task_id = push_camp_obj.campaign.scheduler_task_id return dict(message=message, task_id=task_id), codes.OK def delete(self, campaign_id): """ Unschedule a single campaign from scheduler_service and removes the scheduler_task_id from getTalent's database. :param campaign_id: (Integer) unique id in push_campaign table on GT database. :Example: >>> import requests >>> headers = { >>> 'Authorization': 'Bearer <access_token>', >>> } >>> campaign_id = 1 >>> response = requests.delete(PushCampaignApiUrl.CAMPAIGN % campaign_id, >>> headers=headers, >>> ) .. Response:: { 'message': 'Campaign(id:125) has been unscheduled.' } .. Status:: 200 (Resource Deleted) 403 (Forbidden: Current user cannot delete Push campaign) 404 (Campaign not found) 500 (Internal Server Error) """ if not campaign_id: raise InvalidUsage('campaign_id should be a positive number') task_unscheduled = PushCampaignBase.unschedule(campaign_id, request, CampaignUtils.PUSH) if task_unscheduled: return dict(message='Campaign(id:%s) has been unscheduled.' % campaign_id), codes.OK else: return dict(message='Campaign(id:%s) is already unscheduled.' % campaign_id), codes.OK
class CampaignByIdResource(Resource): """ Resource to update, get and delete a specific campaign. """ decorators = [require_oauth()] def get(self, campaign_id): """ This action returns a single campaign created by current user. :param campaign_id: push campaign id :type campaign_id: int | long :return campaign_data: a dictionary containing campaign JSON serializable data :rtype dict :Example: >>> import requests >>> headers = {'Authorization': 'Bearer <access_token>'} >>> campaign_id = 1 >>> response = requests.get(PushCampaignApiUrl.CAMPAIGN % campaign_id, >>> headers=headers) .. Response:: { "campaign":{ "added_datetime": "2015-11-19 18:54:04", "frequency_id": 2, "id": 1, "name": "QC Technologies", "start_datetime": "2015-11-19 18:55:08", "end_datetime": "2015-11-25 18:55:08" "body_text": "Join QC Technologies.", "url": "https://www.qc-technologies.com/careers" } } .. Status:: 200 (OK) 401 (Unauthorized to access getTalent) 403 (Forbidden, not authorized to access this campaign) 404 (ResourceNotFound) 500 (Internal Server Error) """ user = request.user campaign = PushCampaignBase.get_campaign_if_domain_is_valid( campaign_id, user, CampaignUtils.PUSH) response = dict(campaign=campaign.to_json()) return response, codes.OK def put(self, campaign_id): """ This method takes data to update a Push campaign components. :param campaign_id: unique id of push campaign :type campaign_id: int, long :return: success message :type: dict :Example: >>> import json >>> import requests >>> campaign_data = { >>> "name": "QC Technologies", >>> "body_text": "New job openings...", >>> "url": "https://www.qc-technologies.com", >>> "smartlist_ids": [1, 2, 3] >>> } >>> headers = { >>> "Authorization": "Bearer <access_token>", >>> "content-type": "application/json" >>> } >>> campaign_id = 1 >>> data = json.dumps(campaign_data) >>> response = requests.put( >>> PushCampaignApiUrl.CAMPAIGN % campaign_id , >>> data=data, >>> headers=headers, >>> ) .. Response:: { "message": "Push campaign has been updated successfully" } .. Status:: 201 (Resource Created) 401 (Unauthorized to access getTalent) 403 (Forbidden, not authorized to update this campaign) 400 (Invalid Usage) 500 (Internal Server Error) ..Error Codes:: 7003 (RequiredFieldsMissing) """ user = request.user data = get_json_data_if_validated(request, CAMPAIGN_SCHEMA) camp_obj = PushCampaignBase(user.id, campaign_id) camp_obj.update(data, campaign_id=campaign_id) response = dict(message='Push campaign was updated successfully') return response, codes.OK def delete(self, campaign_id): """ Removes a single campaign from getTalent's database. :param campaign_id: (Integer) unique id in push_campaign table on GT database. :Example: >>> import requests >>> headers = {'Authorization': 'Bearer <access_token>'} >>> campaign_id = 1 >>> response = requests.delete(PushCampaignApiUrl.CAMPAIGN % campaign_id, >>> headers=headers) .. Response:: { 'message': 'Campaign(id:125) has been deleted successfully' } .. Status:: 200 (Resource Deleted) 400 (Bad request) 403 (Forbidden: Current user cannot delete Push campaign) 404 (Campaign not found) 500 (Internal Server Error) ..Error codes:: 5015 (ERROR_DELETING_CAMPAIGN) """ campaign_obj = PushCampaignBase(request.user.id, campaign_id) campaign_obj.delete() return dict(message='Campaign(id:%s) has been deleted successfully.' % campaign_id), codes.OK
class PushCampaignsResource(Resource): """ Resource to get, create and delete campaigns """ decorators = [require_oauth()] def get(self): """ This action returns a list of push campaigns that are associated with current user's domain. It accepts `page` and `per_page` query parameters for pagination, defaault values are 1 and 10 respectively. :return campaigns_data: a dictionary containing list of campaigns and their count :rtype JSON object :Example: >>> import requests >>> headers = {'Authorization': 'Bearer <access_token>'} >>> response = requests.get(PushCampaignApiUrl.CAMPAIGNS, headers=headers) .. Response:: { "campaigns": [ { "added_datetime": "2015-11-19 18:54:04", "frequency_id": 2, "id": 3, "name": "QC Technologies", "start_datetime": "2015-11-19 18:55:08", "end_datetime": "2015-11-25 18:55:08" "body_text": "Join QC Technologies.", "url": "https://www.qc-technologies.com/careers" }, { "added_datetime": "2015-11-19 18:55:08", "frequency_id": 1, "id": 4, "name": "getTalent", "start_datetime": "2015-12-12 10:55:08", "end_datetime": "2015-12-31 18:55:08" "body_text": "Job opening at QC Technologies", "url": "https://www.qc-technologies.com/careers" } ] } .. Status:: 200 (OK) 401 (Unauthorized to access getTalent) 500 (Internal Server Error) """ page, per_page = get_pagination_params(request) query = PushCampaignBase.get_all_campaigns(request.user.domain_id) return get_paginated_response('campaigns', query, page, per_page, parser=PushCampaign.to_json) def post(self): """ This method takes data to create a Push campaign in database. This campaign is just a draft and we need to schedule or send it later. :return: id of created campaign and a success message :type: dict :Example: >>> import json >>> import requests >>> campaign_data = { >>> "name": "QC Technologies", >>> "body_text": "New job openings...", >>> "url": "https://www.qc-technologies.com", >>> "smartlist_ids": [1, 2, 3] >>> } >>> headers = { >>> "Authorization": "Bearer <access_token>", >>> "content-type": "application/json" >>> } >>> data = json.dumps(campaign_data) >>> response = requests.post( >>> PushCampaignApiUrl.CAMPAIGNS, >>> data=data, >>> headers=headers, >>> ) .. Response:: { "id": 11, "message": "Push campaign was created successfully" } .. Status:: 201 (Resource Created) 401 (Unauthorized to access getTalent) 403 (Forbidden error) 500 (Internal Server Error) ..Error Codes:: 7003 (RequiredFieldsMissing) """ user = request.user data = get_json_data_if_validated(request, CAMPAIGN_SCHEMA) campaign = PushCampaignBase(user_id=user.id) campaign_id = campaign.save(data) response = dict(id=campaign_id, message='Push campaign was created successfully') headers = dict(Location=PushCampaignApiUrl.CAMPAIGN % campaign_id) return ApiResponse(response, headers=headers, status=codes.CREATED) def delete(self): """ Deletes multiple campaigns using ids given in list in request data. :Example: >>> import json >>> import requests >>> headers = {'Authorization': 'Bearer <access_token>', >>> 'content-type': 'application/json' >>> } >>> campaign_ids = {'ids': [1, 2, 3]} >>> data = json.dumps(campaign_ids) >>> response = requests.delete(PushCampaignApiUrl.CAMPAIGNS, headers=headers, data=data) .. Response:: { 'message': '3 Campaigns have been deleted successfully' } .. Status:: 200 (Resource deleted) 400 (Bad request) 403 (Forbidden error) 404 (Resource Not Found error) 500 (Internal Server Error) """ return CampaignUtils.process_campaigns_delete(request, PushCampaignBase)