def load_new_affiliate_users(): api = Hasoffers(network_token=settings.HASOFFERS_NETWORK_TOKEN, network_id=settings.HASOFFERS_NETWORK_ID, proxies=settings.PROXIES) latest_aff_user = AffiliateUser.objects.all().order_by('-id').first() max_id = latest_aff_user.id if latest_aff_user else 0 params = dict( fields=['id', 'email', 'affiliate_id'], contain=['Affiliate'], filters={'id': { 'GREATER_THAN': max_id }}, limit=1000, sort={'id': 'asc'}, ) resp = api.AffiliateUser.findAll(**params) for ho_user in resp.extract_all(): db_user = AffiliateUser() db_user.id = ho_user.id db_user.email = ho_user.email db_user.affiliate_id = ho_user.affiliate_id db_user.account_manager_id = ho_user.Affiliate['account_manager_id'] db_user.save()
def load_new_offers(): api = Hasoffers(network_token=settings.HASOFFERS_NETWORK_TOKEN, network_id=settings.HASOFFERS_NETWORK_ID, proxies=settings.PROXIES) max_offer = Offer.objects.all().order_by('-id').first() max_offer_id = max_offer.id if max_offer else 0 params = dict( fields=['id', 'name'], contain=['OfferCategory'], filters={'id': { 'GREATER_THAN': max_offer_id }}, limit=1000, sort={'id': 'asc'}, ) resp = api.Offer.findAll(**params) for ho_offer in resp.extract_all(): categories_str = ','.join(dict(ho_offer.OfferCategory).keys()) data = { 'id': ho_offer.id, 'name': ho_offer.name, 'categories_str': categories_str, 'last_active_at': datetime.datetime.utcnow() } persist_offer.delay(data)
def update_active_offers(): api = Hasoffers(network_token=settings.HASOFFERS_NETWORK_TOKEN, network_id=settings.HASOFFERS_NETWORK_ID, proxies=settings.PROXIES) params = dict( fields=['id', 'name'], contain=['OfferCategory'], filters={'status': 'active'}, limit=10000 ) resp = api.Offer.findAll(**params) for offer in resp.extract_all(): offer_categories_ids = list(dict(offer.OfferCategory).keys()) try: db_offer = Offer.objects.get(pk=offer.id) except Offer.DoesNotExist: pass else: db_offer.name = offer.name db_offer.categories_str = ','.join(offer_categories_ids) db_offer.status = 'active' db_offer.last_active_at = datetime.datetime.utcnow() db_offer.save()
def fetch_offer(offer_id): api = Hasoffers(network_token=settings.HASOFFERS_NETWORK_TOKEN, network_id=settings.HASOFFERS_NETWORK_ID, proxies=settings.PROXIES) resp = api.Offer.findById(id=offer_id, contain=['Thumbnail']) offer = resp.extract_one() return offer
def get_stats(offer_id, goal_id, lookback): from_date = (datetime.datetime.now(pytz.timezone(settings.TIME_ZONE)) - datetime.timedelta(days=lookback)) api = Hasoffers(network_token=settings.HASOFFERS_NETWORK_TOKEN, network_id=settings.HASOFFERS_NETWORK_ID, proxies=settings.PROXIES) response = api.Report.getStats( fields=['Stat.conversions'], groups=['Stat.offer_id', 'Stat.affiliate_id', 'Stat.goal_id'], filters={ 'Stat.offer_id': { 'conditional': 'EQUAL_TO', 'values': offer_id }, 'Stat.goal_id': { 'conditional': 'EQUAL_TO', 'values': [0, goal_id] }, 'Stat.date': { 'conditional': 'GREATER_THAN_OR_EQUAL_TO', 'values': str(from_date.date()) }, 'Stat.hour': { 'conditional': 'GREATER_THAN_OR_EQUAL_TO', 'values': from_date.hour } }, limit=10000) out = (seq(response.data['data']).map(lambda row: row['Stat']).to_list()) return out
def update_active_offers(): api = Hasoffers(network_token=settings.HASOFFERS_NETWORK_TOKEN, network_id=settings.HASOFFERS_NETWORK_ID, proxies=settings.PROXIES) params = dict(fields=['id', 'name'], contain=['OfferCategory'], filters={'status': 'active'}, limit=10000) resp = api.Offer.findAll(**params) for offer in resp.extract_all(): offer_categories_id = (list( map(int, list(dict(offer.OfferCategory).keys())))) is_incent = bool( set(offer_categories_id) & set(settings.INCENT_CATEGORIES)) try: db_offer = Offer.objects.get(pk=offer.id) if db_offer.incent != is_incent: db_offer.incent = is_incent db_offer.save() except Offer.DoesNotExist: continue
def load_new_goals(): api = Hasoffers(network_token=settings.HASOFFERS_NETWORK_TOKEN, network_id=settings.HASOFFERS_NETWORK_ID, proxies=settings.PROXIES) max_goal = Goal.objects.all().order_by('-id').first() max_goal_id = max_goal.id if max_goal else 0 params = dict(fields=['id', 'name', 'offer_id', 'status'], contain=['Offer'], filters={'id': { 'GREATER_THAN': max_goal_id }}, limit=1000, sort={'id': 'asc'}) resp = api.Goal.findAll(**params) for ho_goal in resp.extract_all(): db_goal = Goal() db_goal.id = ho_goal.id db_goal.name = ho_goal.name db_goal.offer_id = ho_goal.offer_id db_goal.status = ho_goal.status db_goal.save()
def send(self, payload): print(f"EmailerService.send: received {payload} ") try: api = Hasoffers(network_token=settings.HASOFFERS_NETWORK_TOKEN, network_id=settings.HASOFFERS_NETWORK_ID, proxies=settings.PROXIES) affiliate_id = payload["affiliate_id"] offer_id = payload["offer_id"] # Check Affiliate is not in blacklist if affiliate_id in settings.UNSUBSCRIBED_AFFILIATES: print(f"Affiliate #{affiliate_id} is in blacklist, so return") return offer = get_offer(offer_id, api) tr_link = get_tracking_link(offer_id, affiliate_id, api) data = { "thumbnail": (offer.Thumbnail["thumbnail"] if offer.Thumbnail else None), "offer_id": offer.id, "offer_name": offer.name, "payout": offer.default_payout, "conversion_cap": offer.conversion_cap, "revenue_cap": offer.revenue_cap, "preview_url": offer.preview_url, "tracking_link": tr_link, "offer_description": offer.description } emails = get_affiliate_emails(affiliate_id, api) html = create_content(data) config = create_mail_config( from_email=settings.COMPANY_EMAIL, subject=f"You are approved for the offer #{offer.id}", to_emails=emails, content=html) sg = sendgrid.SendGridAPIClient(apikey=settings.SENDGRID_API_KEY) res = send(config, sg) print(f"EmailerService.send: sent email payload {payload} " f"result {res}") except Exception as e: print(f"EmailerService.send: exception {e}")
def load_offer(offer_id): api = Hasoffers(network_token=settings.HASOFFERS_NETWORK_TOKEN, network_id=settings.HASOFFERS_NETWORK_ID, proxies=settings.PROXIES) ho_offer = api.Offer.findById(id=offer_id).extract_one() data = {'id': ho_offer.id, 'name': ho_offer.name} persist_offer.delay(data)
def get_offer(offer_id: int): api = Hasoffers(network_token=settings.HASOFFERS_NETWORK_TOKEN, network_id=settings.HASOFFERS_NETWORK_ID, proxies=settings.PROXIES, retry_count=20) offer = (api.Offer.findById(id=offer_id, fields=['id'], contain=['Advertiser']).extract_one()) return offer
def offer_requires_approval(offer_id: int) -> bool: api = Hasoffers(network_token=settings.HASOFFERS_NETWORK_TOKEN, network_id=settings.HASOFFERS_NETWORK_ID, retry_count=20, proxies=settings.PROXIES) offer = (api.Offer .findById(id=offer_id, fields=['id', 'require_approval']) .extract_one()) return True if offer.require_approval == "1" else False
def get_affiliate(affiliate_id: int): api = Hasoffers(network_token=settings.HASOFFERS_NETWORK_TOKEN, network_id=settings.HASOFFERS_NETWORK_ID, proxies=settings.PROXIES, retry_count=20) affiliate = (api.Affiliate.findById(id=affiliate_id, fields=['id', 'account_manager_id' ]).extract_one()) return affiliate
def fetch_active_offers(): """ Returns: list of offer ids - [1, 2, 3] """ api = Hasoffers(network_token=settings.HASOFFERS_NETWORK_TOKEN, network_id=settings.HASOFFERS_NETWORK_ID, proxies=settings.PROXIES) params = dict(fields=['id'], filters={'status': 'active'}, limit=10000) resp = api.Offer.findAll(**params) offer_ids = [offer.id for offer in resp.extract_all()] return offer_ids
def load_all_caps(): api = Hasoffers(network_token=settings.HASOFFERS_NETWORK_TOKEN, network_id=settings.HASOFFERS_NETWORK_ID, proxies=settings.PROXIES) params = dict(fields=['id'], filters={'status': 'active'}, limit=1000) resp = api.Offer.findAll(**params) for offer in resp.extract_all(): load_offer_caps.delay(offer.id)
def get_stats(): from_date = datetime.datetime.now(pytz.timezone(settings.TIME_ZONE)) - datetime.timedelta(days=1) api = Hasoffers(network_token=settings.HASOFFERS_NETWORK_TOKEN, network_id=settings.HASOFFERS_NETWORK_ID, proxies=settings.PROXIES) response = api.Report.getStats( fields=['Stat.clicks', 'Stat.conversions'], groups=['Stat.offer_id', 'Stat.affiliate_id'], filters={'Stat.goal_id': {'conditional': 'EQUAL_TO', 'values': 0}, 'Stat.date': {'conditional': 'GREATER_THAN_OR_EQUAL_TO', 'values': str(from_date.date())}, 'Stat.hour': {'conditional': 'GREATER_THAN_OR_EQUAL_TO', 'values': from_date.hour}}, limit=10000) return response
def get_affiliate_emails(affiliate_id: int) -> list: api = Hasoffers(network_token=settings.HASOFFERS_NETWORK_TOKEN, network_id=settings.HASOFFERS_NETWORK_ID, proxies=settings.PROXIES, retry_count=20) affiliate_users = api.AffiliateUser.findAll(fields=['id', 'email'], filters={ 'affiliate_id': affiliate_id, 'status': 'active' }).extract_all() return [affiliate_user.email for affiliate_user in affiliate_users]
def load_employees(): api = Hasoffers(network_token=settings.HASOFFERS_NETWORK_TOKEN, network_id=settings.HASOFFERS_NETWORK_ID, proxies=settings.PROXIES) resp = api.Employee.findAll(fields=['id', 'email'], limit=1000) for ho_employee in resp.extract_all(): try: Employee.objects.get(pk=ho_employee.id) except Employee.DoesNotExist: db_employee = Employee() db_employee.id = ho_employee.id db_employee.email = ho_employee.email db_employee.save()
def notify_manager_approved(trigger_check, metric_log): print("notify_manager_approved: Starting...") sg = sendgrid.SendGridAPIClient(apikey=settings.SENDGRID_API_KEY) now = datetime.datetime.utcnow().strftime("%Y-%m-%d %H:%M:%S") html = f""" <p> Offer id: {metric_log.offer_id}; Affiliate id: {metric_log.affiliate_id} Key: {trigger_check.trigger.name}; Value: {metric_log.value}; </p> <br><br> <small>generated at: {now}</small> """ from_email = Email(settings.NETWORK_EMAIL) subject = (f'Affiliate #{metric_log.affiliate_id} was APPROVED for ' f'the offer #{metric_log.offer_id}') content = Content("text/html", html) for recipient in Recipient.objects.filter(active=True): to_email = Email(recipient.email) mail = Mail(from_email, subject, to_email, content) sg.client.mail.send.post(request_body=mail.get()) # SEND TO MANAGER api = Hasoffers(network_token=settings.HASOFFERS_NETWORK_TOKEN, network_id=settings.HASOFFERS_NETWORK_ID, proxies=settings.PROXIES) affiliate = (api.Affiliate.findById( id=metric_log.affiliate_id).extract_one()) employee = models.Employee.objects.get(pk=affiliate.account_manager_id) email_address = (employee.email if not employee.use_secondary else employee.secondary_email) to_email = Email(email_address) mail = Mail(from_email, subject, to_email, content) res = sg.client.mail.send.post(request_body=mail.get()) print(f'notify_manager_approved: ' f'affiliate_id={metric_log.affiliate_id} ' f'offer_id={metric_log.offer_id} ' f'trigger_check_id={trigger_check.id}') return str(res)
def notify_affiliate_approved(trigger_check, metric_log): print("notify_affiliate_approved: Starting...") api = Hasoffers(network_token=settings.HASOFFERS_NETWORK_TOKEN, network_id=settings.HASOFFERS_NETWORK_ID, proxies=settings.PROXIES, retry_count=20) affiliate_id = metric_log.affiliate_id offer_id = metric_log.offer_id offer = get_offer(offer_id, api) tr_link = get_tracking_link(offer_id, affiliate_id, api) data = { "thumbnail": (offer.Thumbnail["thumbnail"] if offer.Thumbnail else None), "offer_id": offer.id, "offer_name": offer.name, "preview_url": offer.preview_url, "tracking_link": tr_link, "offer_description": offer.description, } affiliate = get_affiliate_by_id(affiliate_id, api) employee = get_employee_by_id(affiliate.account_manager_id, api) emails = get_affiliate_emails(affiliate_id, api) emails.append(employee.email) html = create_content(data) config = create_mail_config( from_email=settings.NETWORK_EMAIL, subject=f"You are approved for the offer #{offer.id}", to_emails=emails, content=html) sg = sendgrid.SendGridAPIClient(apikey=settings.SENDGRID_API_KEY) res = send(config, sg) print(f"notify_affiliate_approved: sent email with data {data} " f"result {res}")
def get_pacc(): api = Hasoffers(network_token=settings.HASOFFERS_NETWORK_TOKEN, network_id=settings.HASOFFERS_NETWORK_ID, proxies=settings.PROXIES) from_date = datetime.datetime.now(pytz.timezone( settings.TIME_ZONE)) - datetime.timedelta(days=1) res = api.Report.getStats(fields=['Stat.gross_clicks', 'Stat.profit'], groups=['Stat.offer_id', 'Stat.affiliate_id'], filters={ 'Stat.date': { 'conditional': 'GREATER_THAN_OR_EQUAL_TO', 'values': str(from_date.date()) }, 'Stat.hour': { 'conditional': 'GREATER_THAN_OR_EQUAL_TO', 'values': from_date.hour } }, limit=10000) out = (seq( res.data['data']).map(lambda row: row['Stat']).filter(lambda row: int( row['gross_clicks']) >= get_offer_min_clicks(row['offer_id'])). filter(lambda row: offer_exists_and_monitoring_true(row['offer_id']) ).map(lambda row: update_in(row, ['profit'], float)). map(lambda row: update_in(row, ['gross_clicks'], int)).map( lambda row: assoc(row, 'value', (row['profit'] - (row[ 'gross_clicks'] * settings.CLICK_COST)))).to_list()) metric = Metric.objects.get(key='pacc') for row in out: metric_log = MetricLog() metric_log.offer_id = row['offer_id'] metric_log.affiliate_id = row['affiliate_id'] metric_log.metric = metric metric_log.value = row['value'] metric_log.save() pacc_trigger.delay(metric_log)
def get_clicks_if_zero_conv(): print("Getting Clicks stats") api = Hasoffers(network_token=settings.HASOFFERS_NETWORK_TOKEN, network_id=settings.HASOFFERS_NETWORK_ID, proxies=settings.PROXIES) from_date = (datetime.datetime.now(pytz.timezone(settings.TIME_ZONE)) - datetime.timedelta(days=1)) res = api.Report.getStats(fields=['Stat.clicks', 'Stat.conversions'], groups=['Stat.offer_id', 'Stat.affiliate_id'], filters={ 'Stat.date': { 'conditional': 'GREATER_THAN_OR_EQUAL_TO', 'values': str(from_date.date()) }, 'Stat.hour': { 'conditional': 'GREATER_THAN_OR_EQUAL_TO', 'values': from_date.hour } }, limit=10000) out = (seq( res.data['data']).map(lambda row: row['Stat']).filter(lambda row: (int( row['clicks']) >= get_offer_min_clicks(row['offer_id']))).filter( lambda row: (offer_exists_and_monitoring_true(row['offer_id'])) ).map(lambda row: update_in(row, ['conversions'], int)).map( lambda row: update_in(row, ['clicks'], int)).filter( lambda row: row['conversions'] == 0).map(lambda row: assoc( row, 'value', row['clicks'])).to_list()) metric = Metric.objects.get(key='clicks_zero_conv') for row in out: metric_log = MetricLog() metric_log.offer_id = row['offer_id'] metric_log.affiliate_id = row['affiliate_id'] metric_log.metric = metric metric_log.value = row['value'] metric_log.save() celery_pubsub.publish('metric.loaded', metric_log)
def load_all_offers(): api = Hasoffers(network_token=settings.HASOFFERS_NETWORK_TOKEN, network_id=settings.HASOFFERS_NETWORK_ID, proxies=settings.PROXIES) params = dict( fields=['id', 'name'], limit=10000, sort={'id': 'asc'} ) for response in api.Offer.findAll(**params): for ho_offer in response.extract_all(): data = { 'id': ho_offer.id, 'name': ho_offer.name } persist_offer.delay(data)
def unapprove_affiliate_offer(trigger): api = Hasoffers(network_token=settings.HASOFFERS_NETWORK_TOKEN, network_id=settings.HASOFFERS_NETWORK_ID, retry_count=20, proxies=settings.PROXIES) params = dict(id=trigger.offer_id, affiliate_id=trigger.affiliate_id, status='rejected', notes=f'reject reason: {trigger.key}') resp = api.Offer.setAffiliateApproval(**params) if resp.status == 1: ul = UnapproveLog() ul.offer_id = trigger.offer_id ul.affiliate_id = trigger.affiliate_id ul.save()
def offer_block_affiliate(trigger_check, metric_log): print("offer_block_affiliate: " f"offer_id={metric_log.offer_id}" f"affiliate_id={metric_log.affiliate_id}") api = Hasoffers(network_token=settings.HASOFFERS_NETWORK_TOKEN, network_id=settings.HASOFFERS_NETWORK_ID, retry_count=20, proxies=settings.PROXIES) params = dict(id=metric_log.offer_id, affiliate_id=metric_log.affiliate_id) resp = api.Offer.blockAffiliate(**params) if resp.status != 1: raise Error("Error during offer_block_affiliate call " f"offer_id={metric_log.offer_id} " f"affiliate_id={metric_log.affiliate_id}")
def load_offer_caps(offer_id): api = Hasoffers(network_token=settings.HASOFFERS_NETWORK_TOKEN, network_id=settings.HASOFFERS_NETWORK_ID, proxies=settings.PROXIES) resp = api.Offer.getConversionCaps(id=offer_id) if resp.data: out = (seq(resp.data.items( )).map(lambda r: r[1]).map(lambda r: r['OfferConversionCap']).map( lambda r: update_in(r, ['offer_id'], int) ).map(lambda r: update_in(r, ['affiliate_id'], int)).map( lambda r: update_in(r, ['conversion_cap'], int)).map( lambda r: dict(offer_id=r['offer_id'], affiliate_id=r['affiliate_id'], conversion_cap=r['conversion_cap'])).to_list()) for row in out: persist_affiliate_cap.delay(row)
def load_goals(): api = Hasoffers(network_token=settings.HASOFFERS_NETWORK_TOKEN, network_id=settings.HASOFFERS_NETWORK_ID, proxies=settings.PROXIES) params = dict( fields=['id', 'name', 'offer_id'], contain=['Offer'], limit=10000, ) resp = api.Goal.findAll(**params) for ho_goal in resp.extract_all(): goal = { 'id': ho_goal.id, 'name': ho_goal.name, 'offer_id': ho_goal.offer_id } persist_goal.delay(goal)
def get_stats() -> list: """ @returns Example: ``` [ { "Stat": { "conversions": "-194", "clicks": "0", "offer_id": "1650", "affiliate_id": "4358" } }, { "Stat": { "conversions": "-153", "clicks": "0", "offer_id": "2966", "affiliate_id": "7984" } } ] ``` """ from_date = (datetime.datetime.now(pytz.timezone(settings.TIME_ZONE)) - datetime.timedelta(days=1)) api = Hasoffers(network_token=settings.HASOFFERS_NETWORK_TOKEN, network_id=settings.HASOFFERS_NETWORK_ID, proxies=settings.PROXIES) response = api.Report.getStats( fields=['Stat.clicks', 'Stat.conversions'], groups=['Stat.offer_id', 'Stat.affiliate_id'], filters={'Stat.goal_id': {'conditional': 'EQUAL_TO', 'values': 0}, 'Stat.date': {'conditional': 'GREATER_THAN_OR_EQUAL_TO', 'values': str(from_date.date())}, 'Stat.hour': {'conditional': 'GREATER_THAN_OR_EQUAL_TO', 'values': from_date.hour}}, limit=10000) return response.data['data']
def approve_affiliate_offer(trigger_check, metric_log): print("approve_affiliate_offer: " f"offer_id={metric_log.offer_id}" f"affiliate_id={metric_log.affiliate_id}") api = Hasoffers(network_token=settings.HASOFFERS_NETWORK_TOKEN, network_id=settings.HASOFFERS_NETWORK_ID, retry_count=20, proxies=settings.PROXIES) params = dict( id=metric_log.offer_id, affiliate_id=metric_log.affiliate_id, status='approved' ) resp = api.Offer.setAffiliateApproval(**params) if resp.status != 1: raise Error("Error during HasOffers approving call " f"offer_id={metric_log.offer_id} " f"affiliate_id={metric_log.affiliate_id}")
def notify_manager_unapprovement(trigger): sg = sendgrid.SendGridAPIClient(apikey=settings.SENDGRID_API_KEY) html = f""" <p> Offer id: {trigger.offer_id}; Affiliate id: {trigger.affiliate_id} Key: {trigger.key}; Value: {trigger.value}; </p> """ from_email = Email(settings.NETWORK_EMAIL) subject = f'Affiliate #{trigger.affiliate_id} was unapproved from the offer #{trigger.offer_id}' content = Content("text/html", html) for recipient in Recipient.objects.filter(active=True): to_email = Email(recipient.email) mail = Mail(from_email, subject, to_email, content) sg.client.mail.send.post(request_body=mail.get()) # SEND TO MANAGER api = Hasoffers(network_token=settings.HASOFFERS_NETWORK_TOKEN, network_id=settings.HASOFFERS_NETWORK_ID, proxies=settings.PROXIES) affiliate = api.Affiliate.findById(id=trigger.affiliate_id).extract_one() employee = models.Employee.objects.get(pk=affiliate.account_manager_id) email_address = employee.email if not employee.use_secondary else employee.secondary_email to_email = Email(email_address) mail = Mail(from_email, subject, to_email, content) res = sg.client.mail.send.post(request_body=mail.get()) print( f'worker=notify_manager_unapprovement affiliate_id={trigger.affiliate_id} offer_id={trigger.offer_id} trigger_id={trigger.id}' ) return res
def send(self, payload): print(f"EmailerService.send: received {payload} ") try: api = Hasoffers(network_token=settings.HASOFFERS_NETWORK_TOKEN, network_id=settings.HASOFFERS_NETWORK_ID, proxies=settings.PROXIES) affiliate_id = payload["affiliate_id"] offer_id = payload["offer_id"] # Check Affiliate is not in blacklist if int(affiliate_id) in settings.UNSUBSCRIBED_AFFILIATES: print(f"Affiliate #{affiliate_id} is in blacklist, so return") return offer = get_offer(offer_id, api) tr_link = get_tracking_link(offer_id, affiliate_id, api) payout = find_payout(affiliate_id, offer, api) # Conversion Cap and Daily Revenue caps = get_offer_convesion_caps(affiliate_id, api) caps_for_offer = list(filter(cmp_offer_id(offer_id), caps)) try: conversion_cap = int(caps_for_offer[0]["conversion_cap"]) revenue_cap = float(caps_for_offer[0]["revenue_cap"]) except IndexError: conversion_cap = 0 revenue_cap = 0.0 payout_value = f"${payout}" if conversion_cap: cap_value = f"{conversion_cap} conversions" elif revenue_cap: cap_value = f"${revenue_cap}" else: cap_value = "Ask your account manager" data = { "thumbnail": (offer.Thumbnail["thumbnail"] if offer.Thumbnail else None), "offer_id": offer.id, "offer_name": offer.name, "payout_value": payout_value, "cap_value": cap_value, "preview_url": offer.preview_url, "tracking_link": tr_link, "offer_description": offer.description, "network_domain": settings.NETWORK_DOMAIN } affiliate = get_affiliate_by_id(affiliate_id, api) employee = get_employee_by_id(affiliate.account_manager_id, api) emails = get_affiliate_emails(affiliate_id, api) emails.append(employee.email) html = create_content(data) config = create_mail_config( from_email=settings.COMPANY_EMAIL, subject=f"You are approved for the offer #{offer.id}", to_emails=emails, content=html) sg = sendgrid.SendGridAPIClient(apikey=settings.SENDGRID_API_KEY) res = send(config, sg) print(f"EmailerService.send: sent email payload {payload} " f"result {res}") except Exception as e: print(f"EmailerService.send: exception {e}")