1
0
mirror of https://github.com/quay/quay.git synced 2026-01-27 18:42:52 +03:00
Files
quay/util/saas/useranalytics.py
2020-02-05 19:55:07 -08:00

179 lines
5.8 KiB
Python

import logging
from hashlib import sha1
from concurrent.futures import ThreadPoolExecutor
from marketorestpython.client import MarketoClient
from util.asyncwrapper import AsyncExecutorWrapper, NullExecutor, NullExecutorCancelled
logger = logging.getLogger(__name__)
class LeadNotFoundException(Exception):
pass
def build_error_callback(message_when_exception):
def maybe_log_error(response_future):
try:
response_future.result()
except NullExecutorCancelled:
pass
except Exception:
logger.exception("User analytics: %s", message_when_exception)
return maybe_log_error
class _MarketoAnalyticsClient(object):
"""
User analytics implementation which will report user changes to the Marketo API.
"""
def __init__(self, marketo_client, munchkin_private_key, lead_source):
"""
Instantiate with the given marketorestpython.client, the Marketo Munchkin Private Key, and
the Lead Source that we want to set when we create new lead records in Marketo.
"""
self._marketo = marketo_client
self._munchkin_private_key = munchkin_private_key
self._lead_source = lead_source
def _get_lead_metadata(self, given_name, family_name, company, location):
metadata = {}
if given_name:
metadata["firstName"] = given_name
if family_name:
metadata["lastName"] = family_name
if company:
metadata["company"] = company
if location:
metadata["location"] = location
return metadata
def create_lead(self, email, username, given_name, family_name, company, location):
lead_data = dict(
email=email,
Quay_Username__c=username,
leadSource="Web - Product Trial",
Lead_Source_Detail__c=self._lead_source,
)
lead_data.update(self._get_lead_metadata(given_name, family_name, company, location))
self._marketo.create_update_leads(
action="createOrUpdate", leads=[lead_data], asyncProcessing=True, lookupField="email",
)
def _find_leads_by_email(self, email):
# Fetch the existing user from the database by email
found = self._marketo.get_multiple_leads_by_filter_type(
filterType="email", filterValues=[email],
)
if not found:
raise LeadNotFoundException("No lead found with email: {}".format(email))
return found
def change_email(self, old_email, new_email):
found = self._find_leads_by_email(old_email)
# Update using their user id.
updated = [dict(id=lead["id"], email=new_email) for lead in found]
self._marketo.create_update_leads(
action="updateOnly", leads=updated, asyncProcessing=True, lookupField="id",
)
def change_metadata(
self, email, given_name=None, family_name=None, company=None, location=None
):
lead_data = self._get_lead_metadata(given_name, family_name, company, location)
if not lead_data:
return
# Update using their email address.
lead_data["email"] = email
self._marketo.create_update_leads(
action="updateOnly", leads=[lead_data], asyncProcessing=True, lookupField="email",
)
def change_username(self, email, new_username):
# Update using their email.
self._marketo.create_update_leads(
action="updateOnly",
leads=[{"email": email, "Quay_Username__c": new_username,}],
asyncProcessing=True,
lookupField="email",
)
@AsyncExecutorWrapper.sync
def get_user_analytics_metadata(self, user_obj):
"""
Return a list of properties that should be added to the user object to allow analytics
associations.
"""
if not self._munchkin_private_key:
return dict()
marketo_user_hash = sha1(self._munchkin_private_key)
marketo_user_hash.update(user_obj.email)
return dict(marketo_user_hash=marketo_user_hash.hexdigest(),)
class UserAnalytics(object):
def __init__(self, app=None):
self.app = app
if app is not None:
self.state = self.init_app(app)
else:
self.state = None
def init_app(self, app):
analytics_type = app.config.get("USER_ANALYTICS_TYPE", "FakeAnalytics")
marketo_munchkin_id = ""
marketo_munchkin_private_key = ""
marketo_client_id = ""
marketo_client_secret = ""
marketo_lead_source = ""
executor = NullExecutor()
if analytics_type == "Marketo":
marketo_munchkin_id = app.config["MARKETO_MUNCHKIN_ID"]
marketo_munchkin_private_key = app.config["MARKETO_MUNCHKIN_PRIVATE_KEY"]
marketo_client_id = app.config["MARKETO_CLIENT_ID"]
marketo_client_secret = app.config["MARKETO_CLIENT_SECRET"]
marketo_lead_source = app.config["MARKETO_LEAD_SOURCE"]
logger.debug(
"Initializing marketo with keys: %s %s %s",
marketo_munchkin_id,
marketo_client_id,
marketo_client_secret,
)
executor = ThreadPoolExecutor(max_workers=1)
marketo_client = MarketoClient(
marketo_munchkin_id, marketo_client_id, marketo_client_secret
)
client_wrapper = _MarketoAnalyticsClient(
marketo_client, marketo_munchkin_private_key, marketo_lead_source
)
user_analytics = AsyncExecutorWrapper(client_wrapper, executor)
# register extension with app
app.extensions = getattr(app, "extensions", {})
app.extensions["user_analytics"] = user_analytics
return user_analytics
def __getattr__(self, name):
return getattr(self.state, name, None)