Ejemplo n.º 1
0
class CustomerIOBackend(BaseBackend):
    template_name = 'customerevents/customerio.html'

    def __init__(self, SITE_ID, API_KEY, **kwargs):
        self.site_id = SITE_ID
        self.connection = CustomerIO(SITE_ID, API_KEY)
        super(CustomerIOBackend, self).__init__(**kwargs)

    def get_context(self, identity, properties, events, **kwargs):
        if identity.startswith('session:'):
            raise NotImplementedError #anonymous sessions are not implemented
        context = {
            'identify': {
                'id': identity
            },
            'site_id': self.site_id,
            'tracks': list()
        }
        context['identify'].update(properties)
        context['identify'] = json.dumps(context['identify'])
        context.update(kwargs)
        for event_name, event_properties in events:
            context['tracks'].append((event_name, json.dumps(event_properties)))
        return context

    def send(self, identity, properties, aliases, events, request_meta):
        if identity.startswith('session:'): #ignore anonymous sessions
            return
        self.connection.identify(id=identity, **properties)
        for event_name, event_properties in events:
            ep = dict(event_properties)
            ep.update({'customer_id': identity,
                       'name': event_name})
            self.connection.track(**ep)
Ejemplo n.º 2
0
    def handle(self, *args, **options):
        cio = CustomerIO(settings.CUSTOMERIO_SITE_ID,
                         settings.CUSTOMERIO_API_KEY)

        for user in User.objects.all():
            cio.track(customer_id=user.pk, name='Sign Up')
            self.stdout.write('SIGN UP {}'.format(user.username))
Ejemplo n.º 3
0
	def post(self):
		name = self.request.get("name")
		data = json.loads(self.request.get("data"))
		
		logging.info("customer.io event %s"%(name))
		
		cio = CustomerIO(config.CIO_SITE_ID, config.CIO_API_KEY)
		cio.track(data["user"]["id"], name, **data)
Ejemplo n.º 4
0
    def setUp(self):
        self.cio = CustomerIO(site_id='siteid',
                              api_key='apikey',
                              host=self.server.server_address[0],
                              port=self.server.server_port,
                              retries=5,
                              backoff_factor=0)

        # do not verify the ssl certificate as it is self signed
        # should only be done for tests
        self.cio.http.verify = False
Ejemplo n.º 5
0
 def send_reminder_email(self):
     if self.email:
         cio = CustomerIO(settings.CUSTOMERIO_SITE_ID, settings.CUSTOMERIO_API_KEY)
         cio.track(
             customer_id=self.email,
             name='app_expiring_soon',
             app_url=self.url,
             status_url= "http://launch.appsembler.com/" + reverse('deployment_detail', kwargs={'deploy_id': self.deploy_id}),
             remaining_minutes=self.get_remaining_minutes(),
             expiration_time=timezone.localtime(self.expiration_time)
         )
Ejemplo n.º 6
0
class CustomerIOWrapper(object):
    BASE_URL = 'https://beta-api.customer.io/v1/api'

    def __init__(self, site_id, api_key):
        self.site_id = site_id
        self.api_key = api_key
        self.cio = CustomerIO(site_id, api_key)

    def fetch(self, uri, payload):
        response = requests.get(
            self.BASE_URL + uri,
            data=payload,
            auth=(self.site_id, self.api_key),
        )
        return response

    def get_messages(self, message_type=None):
        payload = {
            'type': message_type,
        }
        response = self.fetch('/messages', payload)
        data = json.loads(response.content)
        return data.get('messages')

    def remove_bulk(self, filename):
        with open(filename) as f:
            reader = csv.DictReader(f, delimiter=',')
            items = list(reader)
            item_count = len(items)

            if not items:
                print(f'Error: no rows to in the file: {filename}')
                return False

            if 'id' not in items[0]:
                print(f'Error: id column not exists in the file: {filename}')
                return False

            for idx, item in enumerate(items):
                if 'id' not in item:
                    continue

                try:
                    self.cio.delete(customer_id=item['id'])
                    item_label = item['email'] if 'email' in item else item[
                        'id']
                    item_percent = round((idx / item_count) * 100, 2)
                    print(
                        f'{item_percent}% DELETE ({idx} of {item_count}): {item_label}'
                    )
                except Exception as e:
                    print(f'Exception: {e}')

            return True
Ejemplo n.º 7
0
 def send_reminder_email(self):
     if self.email:
         cio = CustomerIO(settings.CUSTOMERIO_SITE_ID,
                          settings.CUSTOMERIO_API_KEY)
         cio.track(customer_id=self.email,
                   name='app_expiring_soon',
                   app_url=self.url,
                   status_url="http://launch.appsembler.com/" +
                   reverse('deployment_detail',
                           kwargs={'deploy_id': self.deploy_id}),
                   remaining_minutes=self.get_remaining_minutes(),
                   expiration_time=timezone.localtime(self.expiration_time))
Ejemplo n.º 8
0
    def __init__(self,
                 settings: SettingsType,
                 distinct_id=None,
                 global_event_props=None) -> None:
        """Initialize API connector."""
        self.distinct_id = distinct_id

        self.events = self._resolve_events(settings.get("mixpanel.events"))
        self.event_properties = self._resolve_event_properties(
            settings.get("mixpanel.event_properties"))
        self.profile_properties = self._resolve_profile_properties(
            settings.get("mixpanel.profile_properties"))
        self.profile_meta_properties = self._resolve_profile_meta_properties(
            settings.get("mixpanel.profile_meta_properties"))

        use_structlog = settings.get("pyramid_heroku.structlog", False) is True
        consumer = self._resolve_consumer(settings.get("mixpanel.consumer"),
                                          use_structlog)
        if settings.get("mixpanel.token"):
            self.api = Mixpanel(token=settings["mixpanel.token"],
                                consumer=consumer)
        else:
            self.api = Mixpanel(token="testing",
                                consumer=MockedConsumer())  # nosec

        if global_event_props:
            self.global_event_props = global_event_props
        else:
            self.global_event_props = {}

        if (settings.get("customerio.tracking.site_id")
                and settings.get("customerio.tracking.api_key")
                and settings.get("customerio.tracking.region")):
            # This is here because customerio support is an install extra,
            # i.e. it is optional
            from customerio import CustomerIO
            from customerio import Regions

            if settings["customerio.tracking.region"] == "eu":
                region = Regions.EU
            elif settings["customerio.tracking.region"] == "us":
                region = Regions.US
            else:
                raise ValueError("Unknown customer.io region")

            self.cio = CustomerIO(
                settings["customerio.tracking.site_id"],
                settings["customerio.tracking.api_key"],
                region=region,
            )
        else:
            self.cio = None
Ejemplo n.º 9
0
class CustomerIoIdentifyOperator(BaseOperator):
    """
    Send Track Event to Segment for a specified user_id and event

    :param csv_file: a CSV (plain or zipped) path with data to be used as event properties.
        One of them *must* be called 'userId' (templated)
    :type csv_file: str
    :param customerio_site_id: The 'site_id' property from Customer.IO account we want to update
    :type customerio_site_id: str
    :param customerio_api_key: The 'api_key' property from Customer.IO account we want to update
    :type customerio_api_key: str
    """
    template_fields = ('csv_file', 'event')
    ui_color = '#ffd700'

    @apply_defaults
    def __init__(self, csv_file, customerio_site_id, customerio_api_key, *args,
                 **kwargs):
        super().__init__(*args, **kwargs)
        self.csv_file = csv_file
        self.cio = CustomerIO(site_id=customerio_site_id,
                              api_key=customerio_api_key)

    def execute(self, context):

        if self.csv_file.endswith('.gz'):
            file_reader = gzip.open(self.csv_file, "rt", newline="")
        else:
            file_reader = open(self.csv_file, 'r')

        csv_reader = csv.DictReader(file_reader)
        for row in csv_reader:
            # converts a csv row into a map: header1 -> value, header2 -> value...
            props = dict(row)
            user_id = props.pop('userId', None)

            if user_id is None:
                self.log.info('No userId set in CSV row: %s >>> Skipping.',
                              props)
                continue

            # fixing numerics types set as strings from csv
            clean_props = dict()
            for key in props:
                clean_props[key] = retype(props.get(key))

            self.log.info('Sending identify for userId %s with properties: %s',
                          user_id, clean_props)

            self.cio.identify(id=user_id, **clean_props)
    def test_client_connection_handling(self):
        retries = 5
        cio = CustomerIO(
            site_id="siteid",
            api_key="apikey",
            host=self.server.server_address[0],
            port=self.server.server_port,
            retries=retries)

        # should not raise exception as i should be less than retries and 
        # therefore the last request should return a valid response
        for i in xrange(retries):
            cio.identify(i, fail_count=i)

        # should raise expection as we get invalid responses for all retries
        with self.assertRaises(CustomerIOException):
            cio.identify(retries, fail_count=retries)
Ejemplo n.º 11
0
    def setUp(self):
        self.cio = CustomerIO(
            site_id='siteid',
            api_key='apikey',
            host=self.server.server_address[0],
            port=self.server.server_port,
            retries=5,
            backoff_factor=0)

        # do not verify the ssl certificate as it is self signed
        # should only be done for tests
        self.cio.http.verify = False
Ejemplo n.º 12
0
    def test_base_url(self):
        test_cases = [
            # host, port, prefix, result
            (None, None, None, 'https://track.customer.io/api/v1'),
            (None, None, 'v2', 'https://track.customer.io/v2'),
            (None, None, '/v2/', 'https://track.customer.io/v2'),
            ('sub.domain.com', 1337, '/v2/', 'https://sub.domain.com:1337/v2'),
            ('/sub.domain.com/', 1337, '/v2/', 'https://sub.domain.com:1337/v2'),
            ('http://sub.domain.com/', 1337, '/v2/', 'https://sub.domain.com:1337/v2'),
        ]

        for host, port, prefix, result in test_cases:
            cio = CustomerIO(host=host, port=port, url_prefix=prefix)
            self.assertEqual(cio.base_url, result)
def lambda_handler(event, context):
    config = ConfigParser.RawConfigParser()
    config.read('configcustomer.properties')
    conn, cursor = py_db(config.get('db-config', 'HOST'),
                         config.get('db-config', 'USERNAME'),
                         config.get('db-config', 'PASSWD'),
                         config.get('db-config', 'PORT'),
                         config.get('db-config', 'DB_NAME'))

    site_id = config.get('customerio-config', 'site_id')
    api_key = config.get('customerio-config', 'api_key')

    # write DB contents into csv file and inserting into customer.io
    queryprofileStmt = queryData(conn, cursor,
                                 config.get('query-config', 'profile'))
    cio = CustomerIO(site_id, api_key)
    customeriopostfunc.customerIOProfileAPICall(queryprofileStmt, cio)
    cursor.close()
    return "Success"
Ejemplo n.º 14
0
    def test_client_connection_handling(self):
        retries = 5
        cio = CustomerIO(site_id="siteid",
                         api_key="apikey",
                         host=self.server.server_address[0],
                         port=self.server.server_port,
                         retries=retries)

        # should not raise exception as i should be less than retries and
        # therefore the last request should return a valid response
        for i in xrange(retries):
            cio.identify(i, fail_count=i)

        # should raise expection as we get invalid responses for all retries
        with self.assertRaises(CustomerIOException):
            cio.identify(retries, fail_count=retries)
Ejemplo n.º 15
0
class CIOApi(object):
    """Wrapper for CustomerIO python library."""
    def __init__(self):
        super(CIOApi, self).__init__()
        self.cio = CustomerIO(settings.CUSTOMERIO_SITE_ID, settings.CUSTOMERIO_API_KEY)

    def api_call(func):
        """Wrapper for API cals."""
        def wrapped_func(*args, **kwargs):
            args_str = u', '.join(unicode(a) for a in args[1:])
            args_str += u', '.join(unicode(v) for v in kwargs.values())
            logger.debug(u'CustomerIO api call: %s %s' % (func.__name__, args_str))
            if not settings.CUSTOMERIO_ENABLED:
                return None
            try:
                return func(*args, **kwargs)
            except Exception as e:
                logger.exception(e)
        return wrapped_func

    @api_call
    def create_or_update(self, membership):
        self.cio.identify(
            id=membership.id,
            email=membership.user.email,
            created_at=membership.user.date_joined,
            last_login=membership.last_login,
            short_name=membership.get_short_name(),
            full_name=membership.get_full_name(),
            role=membership.get_role_display(),
            account_name=membership.account.name,
            account_plan=membership.account.plan.get_name_display(),
            account_is_trial=membership.account.is_trial(),
            account_is_active=membership.account.is_active,
            account_date_cancel=membership.account.date_cancel,
            account_date_created=membership.account.date_created,
        )

    @api_call
    def track_event(self, membership, name, **kwargs):
        self.cio.track(customer_id=membership.id, name=name, **kwargs)

    @api_call
    def delete(self, membership):
        self.cio.delete(customer_id=membership.id)
Ejemplo n.º 16
0
 def __init__(self, csv_file, customerio_site_id, customerio_api_key, *args,
              **kwargs):
     super().__init__(*args, **kwargs)
     self.csv_file = csv_file
     self.cio = CustomerIO(site_id=customerio_site_id,
                           api_key=customerio_api_key)
Ejemplo n.º 17
0
    def deploy(self):
        instance = self._get_pusher_instance()
        instance[self.deploy_id].trigger('info_update', {
            'message': "Creating a new container...",
            'percent': 30
        })
        headers = {
            'content-type': 'application/json',
        }
        # run the container
        ports = self.project.ports.split(' ')
        hostnames = self.project.hostnames.split(' ')
        payload = {
            "image": self.project.image_name,
            "hosts": ["/api/v1/hosts/1/"],
            "ports": ports,
            "command": "",
            "links": "",
            "memory": "",
            "environment": self.project.env_vars,
        }
        if "edx" in self.project.name.lower():
            edx_env = []
            edx_env.append("EDX_LMS_BASE=lms-{0}.demo.appsembler.com".format(
                self.deploy_id))
            edx_env.append(
                "EDX_PREVIEW_LMS_BASE=lms-{0}.demo.appsembler.com".format(
                    self.deploy_id))
            edx_env.append("EDX_CMS_BASE=cms-{0}.demo.appsembler.com".format(
                self.deploy_id))
            env_string = " ".join(edx_env)
            env_string = " " + env_string
            payload['environment'] += env_string

        r = requests.post(
            "{0}/api/v1/containers/?username={1}&api_key={2}".format(
                settings.SHIPYARD_HOST, settings.SHIPYARD_USER,
                settings.SHIPYARD_KEY),
            data=json.dumps(payload),
            headers=headers)
        if r.status_code == 201:
            # This sleep is needed to avoid problems with the API
            time.sleep(3)
            container_uri = urlparse(r.headers['location']).path
            self.remote_container_id = container_uri.split('/')[-2]

            # create the app (for dynamic routing)
            instance[self.deploy_id].trigger('info_update', {
                'message': "Assigning an URL to the app...",
                'percent': 60
            })
            time.sleep(2)
            app_ids = []
            domains = []
            for port, hostname in zip(ports, hostnames):
                domain_name = "{0}.demo.appsembler.com".format(self.deploy_id)
                if hostname:
                    domain_name = "{0}-{1}".format(hostname, domain_name)
                domains.append(domain_name)
                payload = {
                    "name":
                    self.deploy_id,
                    "description":
                    "{0} for {1}".format(self.project.name, self.email),
                    "domain_name":
                    domain_name,
                    "backend_port":
                    port,
                    "protocol":
                    "https" if "443" in self.project.ports else "http",
                    "containers": [container_uri]
                }
                r = requests.post(
                    "{0}/api/v1/applications/?username={1}&api_key={2}".format(
                        settings.SHIPYARD_HOST, settings.SHIPYARD_USER,
                        settings.SHIPYARD_KEY),
                    data=json.dumps(payload),
                    headers=headers)
                if r.status_code == 201:
                    app_uri = urlparse(r.headers['location']).path
                    app_ids.append(app_uri.split('/')[-2])
            self.remote_app_id = " ".join(app_ids)
        status = r.status_code
        time.sleep(1)
        instance[self.deploy_id].trigger('info_update', {
            'message': "Getting information...",
            'percent': 90
        })
        time.sleep(1)
        if status == 201:
            scheme = "https" if "443" in self.project.ports else "http"
            app_urls = []
            for domain in domains:
                app_url = "{0}://{1}".format(scheme, domain)
                app_urls.append(app_url)
            self.url = " ".join(app_urls)
            self.status = 'Completed'
            self.launch_time = timezone.now()
            self.expiration_time = self.expiration_datetime()
            instance[self.deploy_id].trigger(
                'deployment_complete', {
                    'app_name': self.project.name,
                    'message': "Deployment complete!",
                    'app_url': self.url,
                    'username': self.project.default_username,
                    'password': self.project.default_password
                })
            if self.email:
                cio = CustomerIO(settings.CUSTOMERIO_SITE_ID,
                                 settings.CUSTOMERIO_API_KEY)
                cio.track(customer_id=self.email,
                          name='app_deploy_complete',
                          app_url=self.url.replace(" ", "\n"),
                          app_name=self.project.name,
                          status_url="http://launcher.appsembler.com" +
                          reverse('deployment_detail',
                                  kwargs={'deploy_id': self.deploy_id}),
                          username=self.project.default_username,
                          password=self.project.default_password)
        else:
            self.status = 'Failed'
            error_log = DeploymentErrorLog(deployment=self,
                                           http_status=status,
                                           error_log=r.text)
            error_log.save()
            instance[self.deploy_id].trigger('deployment_failed', {
                'message': "Deployment failed!",
            })
        self.save()
Ejemplo n.º 18
0
 def __init__(self, site_id, api_key):
     self.site_id = site_id
     self.api_key = api_key
     self.cio = CustomerIO(site_id, api_key)
Ejemplo n.º 19
0
 def __init__(self):
     super(CIOApi, self).__init__()
     self.cio = CustomerIO(settings.CUSTOMERIO_SITE_ID, settings.CUSTOMERIO_API_KEY)
Ejemplo n.º 20
0
 def deploy(self):
     instance = self._get_pusher_instance()
     li = self._get_openshift_instance()
     instance[self.deploy_id].trigger('info_update', {
         'message': "Creating a new app...",
         'percent': 30
     })
     message = None
     log_error = False
     try:
         status, res = li.app_create(
             app_name=self.deploy_id,
             app_type=self.project.cartridges_list(),
             init_git_url=self.project.github_url
         )
         data = res()
     except (OpenShiftException, SSLError, ValueError) as e:
         # workaround to be able to log errors when the deployment fails
         if e.__class__ == OpenShiftException:
             log_error = True
         status = 500
         message = "A critical error has occured."
         logger.error("Critical error has occured during deployment".format(self.project.name),
             exc_info=True,
             extra={
                 'user_email': self.email,
                 'project_name': self.project.name,
             }
         )
     instance[self.deploy_id].trigger('info_update', {
         'message': "Getting results...",
         'percent': 60
     })
     if status == 201:
         app_url = data['data'].get('app_url')
         self.url = app_url
         self.status = 'Completed'
         self.launch_time = timezone.now()
         self.expiration_time = self.launch_time + datetime.timedelta(minutes=60)
         instance[self.deploy_id].trigger('deployment_complete', {
             'app_name': self.project.name,
             'message': "Deployment complete!",
             'app_url': app_url,
             'username': self.project.default_username,
             'password': self.project.default_password
         })
         if self.email:
             cio = CustomerIO(settings.CUSTOMERIO_SITE_ID, settings.CUSTOMERIO_API_KEY)
             cio.track(customer_id=self.email,
                       name='app_deploy_complete',
                       app_url=app_url,
                       status_url="http://launch.appsembler.com/" + reverse('deployment_detail', kwargs={'deploy_id': self.deploy_id}),
                       username=self.project.default_username,
                       password=self.project.default_password
             )
     else:
         self.status = 'Failed'
         if log_error:
             error_log = DeploymentErrorLog(deployment=self, http_status=status, error_log=data['messages'][0]['text'])
             error_log.save()
         instance[self.deploy_id].trigger('deployment_failed', {
             'message': "Deployment failed!",
         })
     self.save()
Ejemplo n.º 21
0
 def __init__(self, SITE_ID, API_KEY, **kwargs):
     self.site_id = SITE_ID
     self.connection = CustomerIO(SITE_ID, API_KEY)
     super(CustomerIOBackend, self).__init__(**kwargs)
Ejemplo n.º 22
0
    def deploy(self):
        instance = self._get_pusher_instance()
        instance[self.deploy_id].trigger('info_update', {
            'message': "Creating a new container...",
            'percent': 30
        })
        headers = {
            'content-type': 'application/json',
        }
        # run the container
        ports = self.project.ports.split(' ')
        hostnames = self.project.hostnames.split(' ')
        payload = {
            "image": self.project.image_name,
            "hosts": ["/api/v1/hosts/1/"],
            "ports": ports,
            "command": "",
            "links": "",
            "memory": "",
            "environment": self.project.env_vars,
        }
        if "edx" in self.project.name.lower():
            edx_env = []
            edx_env.append("EDX_LMS_BASE=lms-{0}.demo.appsembler.com".format(self.deploy_id))
            edx_env.append("EDX_PREVIEW_LMS_BASE=lms-{0}.demo.appsembler.com".format(self.deploy_id))
            edx_env.append("EDX_CMS_BASE=cms-{0}.demo.appsembler.com".format(self.deploy_id))
            env_string = " ".join(edx_env)
            env_string = " " + env_string
            payload['environment'] += env_string

        r = requests.post(
            "{0}/api/v1/containers/?username={1}&api_key={2}".format(settings.SHIPYARD_HOST, settings.SHIPYARD_USER, settings.SHIPYARD_KEY),
            data=json.dumps(payload),
            headers=headers
        )
        if r.status_code == 201:
            # This sleep is needed to avoid problems with the API
            time.sleep(3)
            container_uri = urlparse(r.headers['location']).path
            self.remote_container_id = container_uri.split('/')[-2]

            # create the app (for dynamic routing)
            instance[self.deploy_id].trigger('info_update', {
                'message': "Assigning an URL to the app...",
                'percent': 60
            })
            time.sleep(2)
            app_ids = []
            domains = []
            for port, hostname in zip(ports, hostnames):
                domain_name = "{0}.demo.appsembler.com".format(self.deploy_id)
                if hostname:
                    domain_name = "{0}-{1}".format(hostname, domain_name)
                domains.append(domain_name)
                payload = {
                    "name": self.deploy_id,
                    "description": "{0} for {1}".format(self.project.name, self.email),
                    "domain_name": domain_name,
                    "backend_port": port,
                    "protocol": "https" if "443" in self.project.ports else "http",
                    "containers": [container_uri]
                }
                r = requests.post(
                    "{0}/api/v1/applications/?username={1}&api_key={2}".format(settings.SHIPYARD_HOST, settings.SHIPYARD_USER, settings.SHIPYARD_KEY),
                    data=json.dumps(payload),
                    headers=headers
                )
                if r.status_code == 201:
                    app_uri = urlparse(r.headers['location']).path
                    app_ids.append(app_uri.split('/')[-2])
            self.remote_app_id = " ".join(app_ids)
        status = r.status_code
        time.sleep(1)
        instance[self.deploy_id].trigger('info_update', {
            'message': "Getting information...",
            'percent': 90
        })
        time.sleep(1)
        if status == 201:
            scheme = "https" if "443" in self.project.ports else "http"
            app_urls = []
            for domain in domains:
                app_url = "{0}://{1}".format(scheme, domain)
                app_urls.append(app_url)
            self.url = " ".join(app_urls)
            self.status = 'Completed'
            self.launch_time = timezone.now()
            self.expiration_time = self.expiration_datetime()
            instance[self.deploy_id].trigger('deployment_complete', {
                'app_name': self.project.name,
                'message': "Deployment complete!",
                'app_url': self.url,
                'username': self.project.default_username,
                'password': self.project.default_password
            })
            if self.email:
                cio = CustomerIO(settings.CUSTOMERIO_SITE_ID, settings.CUSTOMERIO_API_KEY)
                cio.track(customer_id=self.email,
                          name='app_deploy_complete',
                          app_url=self.url.replace(" ", "\n"),
                          app_name=self.project.name,
                          status_url="http://launcher.appsembler.com" + reverse('deployment_detail', kwargs={'deploy_id': self.deploy_id}),
                          username=self.project.default_username,
                          password=self.project.default_password
                )
        else:
            self.status = 'Failed'
            error_log = DeploymentErrorLog(deployment=self, http_status=status, error_log=r.text)
            error_log.save()
            instance[self.deploy_id].trigger('deployment_failed', {
                'message': "Deployment failed!",
            })
        self.save()
Ejemplo n.º 23
0
class TestCustomerIO(HTTPSTestCase):
    '''Starts server which the client connects to in the following tests'''
    def setUp(self):
        self.cio = CustomerIO(
            site_id='siteid',
            api_key='apikey',
            host=self.server.server_address[0],
            port=self.server.server_port,
            retries=5,
            backoff_factor=0)

        # do not verify the ssl certificate as it is self signed
        # should only be done for tests
        self.cio.http.verify = False

    def _check_request(self, resp, rq, *args, **kwargs):
        request = resp.request
        body = request.body.decode('utf-8') if isinstance(request.body, bytes) else request.body
        self.assertEqual(request.method, rq['method'])
        self.assertEqual(json.loads(body), rq['body'])
        self.assertEqual(request.headers['Authorization'], rq['authorization'])
        self.assertEqual(request.headers['Content-Type'], rq['content_type'])
        self.assertEqual(int(request.headers['Content-Length']), len(json.dumps(rq['body'])))
        self.assertTrue(request.url.endswith(rq['url_suffix']),
            'url: {} expected suffix: {}'.format(request.url, rq['url_suffix']))


    def test_client_connection_handling(self):
        retries = self.cio.retries
        # should not raise exception as i should be less than retries and 
        # therefore the last request should return a valid response
        for i in range(retries):
            self.cio.identify(i, fail_count=i)

        # should raise expection as we get invalid responses for all retries
        with self.assertRaises(CustomerIOException):
            self.cio.identify(retries, fail_count=retries)


    def test_identify_call(self):
        self.cio.http.hooks=dict(response=partial(self._check_request, rq={
            'method': 'PUT',
            'authorization': _basic_auth_str('siteid', 'apikey'),
            'content_type': 'application/json',
            'url_suffix': '/customers/1',
            'body': {"name": "john", "email": "*****@*****.**"},
        }))

        self.cio.identify(id=1, name='john', email='*****@*****.**')

        with self.assertRaises(TypeError):
            self.cio.identify(random_attr="some_value")


    def test_track_call(self):
        self.cio.http.hooks=dict(response=partial(self._check_request, rq={
            'method': 'POST',
            'authorization': _basic_auth_str('siteid', 'apikey'),
            'content_type': 'application/json',
            'url_suffix': '/customers/1/events',
            'body': {"data": {"email": "*****@*****.**"}, "name": "sign_up"},
        }))

        self.cio.track(customer_id=1, name='sign_up', email='*****@*****.**')

        with self.assertRaises(TypeError):
            self.cio.track(random_attr="some_value")


    def test_pageview_call(self):
        self.cio.http.hooks=dict(response=partial(self._check_request, rq={
            'method': 'POST',
            'authorization': _basic_auth_str('siteid', 'apikey'),
            'content_type': 'application/json',
            'url_suffix': '/customers/1/events',
            'body': {"data": {"referer": "category_1"}, "type": "page", "name": "product_1"},
        }))

        self.cio.pageview(customer_id=1, page='product_1', referer='category_1')

        with self.assertRaises(TypeError):
            self.cio.pageview(random_attr="some_value")


    def test_delete_call(self):
        self.cio.http.hooks=dict(response=partial(self._check_request, rq={
            'method': 'DELETE',
            'authorization': _basic_auth_str('siteid', 'apikey'),
            'content_type': 'application/json',
            'url_suffix': '/customers/1',
            'body': {},
        }))

        self.cio.delete(customer_id=1)

        with self.assertRaises(TypeError):
            self.cio.delete(random_attr="some_value")


    def test_backfill_call(self):
        self.cio.http.hooks=dict(response=partial(self._check_request, rq={
            'method': 'POST',
            'authorization': _basic_auth_str('siteid', 'apikey'),
            'content_type': 'application/json',
            'url_suffix': '/customers/1/events',
            'body': {"timestamp": 1234567890, "data": {"email": "*****@*****.**"}, "name": "signup"},
        }))

        self.cio.backfill(customer_id=1, name='signup', timestamp=1234567890, email='*****@*****.**')

        with self.assertRaises(TypeError):
            self.cio.backfill(random_attr="some_value")

    def test_base_url(self):
        test_cases = [
            # host, port, prefix, result
            (None, None, None, 'https://track.customer.io/api/v1'),
            (None, None, 'v2', 'https://track.customer.io/v2'),
            (None, None, '/v2/', 'https://track.customer.io/v2'),
            ('sub.domain.com', 1337, '/v2/', 'https://sub.domain.com:1337/v2'),
            ('/sub.domain.com/', 1337, '/v2/', 'https://sub.domain.com:1337/v2'),
            ('http://sub.domain.com/', 1337, '/v2/', 'https://sub.domain.com:1337/v2'),
        ]

        for host, port, prefix, result in test_cases:
            cio = CustomerIO(host=host, port=port, url_prefix=prefix)
            self.assertEqual(cio.base_url, result)


    def test_device_call(self):
        self.cio.http.hooks=dict(response=partial(self._check_request, rq={
            'method': 'PUT',
            'authorization': _basic_auth_str('siteid', 'apikey'),
            'content_type': 'application/json',
            'url_suffix': '/customers/1/devices',
            'body': {"device": {"id": "device_1", "platform":"ios"}}
        }))

        self.cio.add_device(customer_id=1, device_id="device_1", platform="ios")
        with self.assertRaises(TypeError):
            self.cio.add_device(random_attr="some_value")

    def test_device_call_last_used(self):
        self.cio.http.hooks=dict(response=partial(self._check_request, rq={
            'method': 'PUT',
            'authorization': _basic_auth_str('siteid', 'apikey'),
            'content_type': 'application/json',
            'url_suffix': '/customers/1/devices',
            'body': {"device": {"id": "device_2", "platform": "android", "last_used": 1234567890}}
        }))

        self.cio.add_device(customer_id=1, device_id="device_2", platform="android", last_used=1234567890)

    def test_device_call_valid_platform(self):
        self.cio.http.hooks=dict(response=partial(self._check_request, rq={
            'method': 'PUT',
            'authorization': _basic_auth_str('siteid', 'apikey'),
            'content_type': 'application/json',
            'url_suffix': '/customers/1/devices',
            'body': {"device": {"id": "device_3", "platform": "notsupported"}}
        }))

        with self.assertRaises(CustomerIOException):
            self.cio.add_device(customer_id=1, device_id="device_3", platform=None)
   
    def test_device_call_has_customer_id(self):
        self.cio.http.hooks=dict(response=partial(self._check_request, rq={
            'method': 'PUT',
            'authorization': _basic_auth_str('siteid', 'apikey'),
            'content_type': 'application/json',
            'url_suffix': '/customers/1/devices',
            'body': {"device": {"id": "device_4", "platform": "ios"}}
        }))

        with self.assertRaises(CustomerIOException):
            self.cio.add_device(customer_id="", device_id="device_4", platform="ios")
    
    def test_device_call_has_device_id(self):
        self.cio.http.hooks=dict(response=partial(self._check_request, rq={
            'method': 'PUT',
            'authorization': _basic_auth_str('siteid', 'apikey'),
            'content_type': 'application/json',
            'url_suffix': '/customers/1/devices',
            'body': {"device": {"id": "device_5", "platform": "ios"}}
        }))

        with self.assertRaises(CustomerIOException):
            self.cio.add_device(customer_id=1, device_id="", platform="ios")

    def test_device_delete_call(self):
        self.cio.http.hooks=dict(response=partial(self._check_request, rq={
            'method': 'DELETE',
            'authorization': _basic_auth_str('siteid', 'apikey'),
            'content_type': 'application/json',
            'url_suffix': '/customers/1/devices/device_1',
            'body': {}
        }))

        self.cio.delete_device(customer_id=1, device_id="device_1")
        with self.assertRaises(TypeError):
            self.cio.delete_device(random_attr="some_value")
    
    def test_suppress_call(self):
        self.cio.http.hooks=dict(response=partial(self._check_request, rq={
            'method': 'POST',
            'authorization': _basic_auth_str('siteid', 'apikey'),
            'content_type': 'application/json',
            'url_suffix': '/customers/1/suppress',
            'body': {},
        }))

        self.cio.suppress(customer_id=1)

        with self.assertRaises(CustomerIOException):
            self.cio.suppress(None)

    def test_unsuppress_call(self):
        self.cio.http.hooks=dict(response=partial(self._check_request, rq={
            'method': 'POST',
            'authorization': _basic_auth_str('siteid', 'apikey'),
            'content_type': 'application/json',
            'url_suffix': '/customers/1/unsuppress',
            'body': {},
        }))

        self.cio.unsuppress(customer_id=1)

        with self.assertRaises(CustomerIOException):
            self.cio.unsuppress(None)

    def test_add_to_segment_call(self):
        self.cio.http.hooks=dict(response=partial(self._check_request, rq={
            'method': 'POST',
            'authorization': _basic_auth_str('siteid', 'apikey'),
            'content_type': 'application/json',
            'url_suffix': '/segments/1/add_customers',
            'body': {'ids': ['1','2','3']},
        }))

        self.cio.add_to_segment(segment_id=1, customer_ids=[1,2,3])

        with self.assertRaises(CustomerIOException):
            self.cio.add_to_segment(None, None)

        with self.assertRaises(CustomerIOException):
            self.cio.add_to_segment(segment_id=1, customer_ids=False)

        with self.assertRaises(CustomerIOException):
            self.cio.add_to_segment(segment_id=1, customer_ids=[False,True,False])

    def test_remove_from_segment_call(self):
        self.cio.http.hooks=dict(response=partial(self._check_request, rq={
            'method': 'POST',
            'authorization': _basic_auth_str('siteid', 'apikey'),
            'content_type': 'application/json',
            'url_suffix': '/segments/1/remove_customers',
            'body': {'ids': ['1','2','3']},
        }))

        self.cio.remove_from_segment(segment_id=1, customer_ids=[1,2,3])

        with self.assertRaises(CustomerIOException):
            self.cio.remove_from_segment(None, None)

        with self.assertRaises(CustomerIOException):
            self.cio.add_to_segment(segment_id=1, customer_ids=False)

        with self.assertRaises(CustomerIOException):
            self.cio.add_to_segment(segment_id=1, customer_ids=[False,True,False])


    @unittest.skipIf(sys.version_info.major > 2, "python2 specific test case")
    def test_sanitize_py2(self):
        data_in = dict(dt=datetime.fromtimestamp(1234567890))
        data_out = self.cio._sanitize(data_in)
        self.assertEqual(data_out, dict(dt=1234567890))


    @unittest.skipIf(sys.version_info.major < 3, "python3 specific test case")
    def test_sanitize_py3(self):
        from datetime import timezone
        data_in = dict(dt=datetime(2009, 2, 13, 23, 31, 30, 0, timezone.utc))
        data_out = self.cio._sanitize(data_in)
        self.assertEqual(data_out, dict(dt=1234567890))
Ejemplo n.º 24
0
class MixpanelTrack:
    """Wrapper around the official `mixpanel` server-side integration for Mixpanel.

    You can track events and/or set people profiles. Uses
    https://pypi.python.org/pypi/mixpanel under the hood.

    Prepared as `request.mixpanel` for easy handling.
    """

    events: Events
    event_properties: EventProperties
    global_event_props: PropertiesType
    profile_properties: ProfileProperties
    profile_meta_properties: ProfileMetaProperties

    @staticmethod
    def _resolve_events(dotted_name: t.Optional[object] = None) -> Events:
        """Resolve a dotted-name into an Events object."""
        if not dotted_name:
            return Events()
        if not isinstance(dotted_name, str):
            raise ValueError(
                f"dotted_name must be a string, but it is: {dotted_name.__class__.__name__}"
            )
        else:
            resolved = DottedNameResolver().resolve(dotted_name)
            if not issubclass(resolved, Events):
                raise ValueError(
                    "class in dotted_name needs to be based on pyramid_mixpanel.Events"
                )
            return resolved()

    @staticmethod
    def _resolve_event_properties(
        dotted_name: t.Optional[object] = None, ) -> EventProperties:
        """Resolve a dotted-name into an EventProperties object."""
        if not dotted_name:
            return EventProperties()
        if not isinstance(dotted_name, str):
            raise ValueError(
                f"dotted_name must be a string, but it is: {dotted_name.__class__.__name__}"
            )
        else:
            resolved = DottedNameResolver().resolve(dotted_name)
            if not issubclass(resolved, EventProperties):
                raise ValueError(
                    "class in dotted_name needs to be based on pyramid_mixpanel.EventProperties"
                )
            return resolved()

    @staticmethod
    def _resolve_profile_properties(
        dotted_name: t.Optional[object] = None, ) -> ProfileProperties:
        """Resolve a dotted-name into an ProfileProperties object."""
        if not dotted_name:
            return ProfileProperties()
        if not isinstance(dotted_name, str):
            raise ValueError(
                f"dotted_name must be a string, but it is: {dotted_name.__class__.__name__}"
            )
        else:
            resolved = DottedNameResolver().resolve(dotted_name)
            if not issubclass(resolved, ProfileProperties):
                raise ValueError(
                    "class in dotted_name needs to be based on pyramid_mixpanel.ProfileProperties"
                )
            return resolved()

    @staticmethod
    def _resolve_profile_meta_properties(
        dotted_name: t.Optional[object] = None, ) -> ProfileMetaProperties:
        """Resolve a dotted-name into an ProfileMetaProperties object."""
        if not dotted_name:
            return ProfileMetaProperties()
        if not isinstance(dotted_name, str):
            raise ValueError(
                f"dotted_name must be a string, but it is: {dotted_name.__class__.__name__}"
            )
        else:
            resolved = DottedNameResolver().resolve(dotted_name)
            if not issubclass(resolved, ProfileMetaProperties):
                raise ValueError(
                    "class in dotted_name needs to be based on pyramid_mixpanel.ProfileMetaProperties"
                )
            return resolved()

    @staticmethod
    def _resolve_consumer(dotted_name: t.Optional[object] = None,
                          use_structlog: t.Optional[bool] = False) -> Consumer:
        """Resolve a dotted-name into a Consumer object."""
        if not dotted_name:
            return PoliteBufferedConsumer(use_structlog)
        if not isinstance(dotted_name, str):
            raise ValueError(
                f"dotted_name must be a string, but it is: {dotted_name.__class__.__name__}"
            )
        else:
            resolved = DottedNameResolver().resolve(dotted_name)
            if not (issubclass(resolved, Consumer)
                    or issubclass(resolved, BufferedConsumer)):
                raise ValueError(
                    "class in dotted_name needs to be based on mixpanel.(Buffered)Consumer"
                )
            return resolved()

    def __init__(self,
                 settings: SettingsType,
                 distinct_id=None,
                 global_event_props=None) -> None:
        """Initialize API connector."""
        self.distinct_id = distinct_id

        self.events = self._resolve_events(settings.get("mixpanel.events"))
        self.event_properties = self._resolve_event_properties(
            settings.get("mixpanel.event_properties"))
        self.profile_properties = self._resolve_profile_properties(
            settings.get("mixpanel.profile_properties"))
        self.profile_meta_properties = self._resolve_profile_meta_properties(
            settings.get("mixpanel.profile_meta_properties"))

        use_structlog = settings.get("pyramid_heroku.structlog", False) is True
        consumer = self._resolve_consumer(settings.get("mixpanel.consumer"),
                                          use_structlog)
        if settings.get("mixpanel.token"):
            self.api = Mixpanel(token=settings["mixpanel.token"],
                                consumer=consumer)
        else:
            self.api = Mixpanel(token="testing",
                                consumer=MockedConsumer())  # nosec

        if global_event_props:
            self.global_event_props = global_event_props
        else:
            self.global_event_props = {}

        if (settings.get("customerio.tracking.site_id")
                and settings.get("customerio.tracking.api_key")
                and settings.get("customerio.tracking.region")):
            # This is here because customerio support is an install extra,
            # i.e. it is optional
            from customerio import CustomerIO
            from customerio import Regions

            if settings["customerio.tracking.region"] == "eu":
                region = Regions.EU
            elif settings["customerio.tracking.region"] == "us":
                region = Regions.US
            else:
                raise ValueError("Unknown customer.io region")

            self.cio = CustomerIO(
                settings["customerio.tracking.site_id"],
                settings["customerio.tracking.api_key"],
                region=region,
            )
        else:
            self.cio = None

    @distinct_id_is_required
    def track(self,
              event: Event,
              props: t.Optional[PropertiesType] = None) -> None:
        """Track a Mixpanel event."""
        if event not in self.events.__dict__.values():
            raise ValueError(f"Event '{event}' is not a member of self.events")

        if props:
            props = {**self.global_event_props, **props}
        else:
            props = self.global_event_props
        for prop in props:
            if prop not in self.event_properties.__dict__.values():
                raise ValueError(
                    f"Property '{prop}' is not a member of self.event_properties"
                )

        self.api.track(
            self.distinct_id,
            event.name,
            {prop.name: value
             for (prop, value) in props.items()},
        )
        if self.cio:
            msg = {
                "customer_id": self.distinct_id,
                "name": event.name,
                **{
                    prop.name.replace("$", ""): value
                    for (prop, value) in props.items()
                },
            }

            if self.api._consumer.__class__ == MockedConsumer:
                self.api._consumer.mocked_messages.append(
                    MockedMessage(endpoint="customer.io", msg=msg))
            else:
                self.cio.track(**msg)

    @distinct_id_is_required
    def profile_set(self,
                    props: PropertiesType,
                    meta: t.Optional[PropertiesType] = None) -> None:
        """Set properties to a Profile.

        This creates a profile if one does not yet exist.

        Use `meta` to override are Mixpanel special properties, such as $ip.
        """
        if not meta:
            meta = {}

        for prop in props:
            if prop not in self.profile_properties.__dict__.values():
                raise ValueError(
                    f"Property '{prop}' is not a member of self.profile_properties"
                )

        for prop in meta:
            if prop not in self.profile_meta_properties.__dict__.values():
                raise ValueError(
                    f"Property '{prop}' is not a member of self.profile_meta_properties"
                )

        # mixpanel and customerio expect different date formats
        # so we have to save the props here so we can format them
        # differently later on in the `if self.cio:` block
        customerio_props = deepcopy(props)

        for (prop, value) in props.items():
            if isinstance(value, datetime):
                props[prop] = value.isoformat()

        self.api.people_set(
            self.distinct_id,
            {prop.name: value
             for (prop, value) in props.items()},
            {prop.name: value
             for (prop, value) in meta.items()},
        )
        if self.cio:

            # customer.io expects dates in unix/epoch format
            for prop, value in customerio_props.items():
                if isinstance(value, datetime):
                    customerio_props[prop] = round(value.timestamp())

            # customer.io expects created timestamp as `created_at`
            if customerio_props.get(Property("$created")):
                customerio_props[Property("created_at")] = customerio_props[
                    Property("$created")]
                del customerio_props[Property("$created")]

            msg = {
                "id": self.distinct_id,
                **{
                    prop.name.replace("$", ""): value
                    for (prop, value) in customerio_props.items()
                },
                **{
                    prop.name.replace("$", ""): value
                    for (prop, value) in meta.items()
                },
            }

            if self.api._consumer.__class__ == MockedConsumer:
                self.api._consumer.mocked_messages.append(
                    MockedMessage(endpoint="customer.io", msg=msg))
            else:
                self.cio.identify(**msg)

    @distinct_id_is_required
    def people_append(self,
                      props: PropertiesType,
                      meta: t.Optional[PropertiesType] = None) -> None:
        """Wrap around api.people_append to set distinct_id."""
        if not meta:
            meta = {}

        for prop in props:
            if prop not in self.profile_properties.__dict__.values():
                raise ValueError(
                    f"Property '{prop}' is not a member of self.profile_properties"
                )

        for prop in meta:
            if prop not in self.profile_meta_properties.__dict__.values():
                raise ValueError(
                    f"Property '{prop}' is not a member of self.profile_meta_properties"
                )

        self.api.people_append(
            self.distinct_id,
            {prop.name: value
             for (prop, value) in props.items()},
            {prop.name: value
             for (prop, value) in meta.items()},
        )

    @distinct_id_is_required
    def people_union(self,
                     props: PropertiesType,
                     meta: t.Optional[PropertiesType] = None) -> None:
        """Wrap around api.people_union to set properties."""
        if not meta:
            meta = {}

        for prop in props:
            if prop not in self.profile_properties.__dict__.values():
                raise ValueError(
                    f"Property '{prop}' is not a member of self.profile_properties"
                )
            if not isinstance(props[prop], list):
                raise TypeError(f"Property '{prop}' value is not a list")

        for prop in meta:
            if prop not in self.profile_meta_properties.__dict__.values():
                raise ValueError(
                    f"Property '{prop}' is not a member of self.profile_meta_properties"
                )
            if not isinstance(meta[prop], list):
                raise TypeError(f"Property '{prop}' value is not a list")

        self.api.people_union(
            self.distinct_id,
            {prop.name: value
             for (prop, value) in props.items()},
            {prop.name: value
             for (prop, value) in meta.items()},
        )

    @distinct_id_is_required
    def profile_increment(self, props: t.Dict[Property, int]) -> None:
        """Wrap around api.people_increment to set distinct_id."""
        for prop in props:
            if prop not in self.profile_properties.__dict__.values():
                raise ValueError(
                    f"Property '{prop}' is not a member of self.profile_properties"
                )

        self.api.people_increment(
            self.distinct_id,
            {prop.name: value
             for (prop, value) in props.items()})

    @distinct_id_is_required
    def profile_track_charge(
            self,
            amount: int,
            props: t.Optional[t.Dict[Property, str]] = None) -> None:
        """Wrap around api.people_track_charge to set distinct_id."""
        if not props:
            props = {}

        for prop in props:
            if prop not in self.profile_properties.__dict__.values():
                raise ValueError(
                    f"Property '{prop}' is not a member of self.profile_properties"
                )

        self.api.people_track_charge(
            self.distinct_id,
            amount,
            {prop.name: value
             for (prop, value) in props.items()},
        )
Ejemplo n.º 25
0
from cron.models import CronLog
from django.conf import settings
import analytics
import time
import logging
from django.utils import timezone
from datetime import timedelta
import traceback
from requests.auth import HTTPBasicAuth
from django.db.models import Sum
from openpyxl import Workbook
from io import BytesIO
import base64

from customerio import CustomerIO
cio = CustomerIO(settings.CUSTOMER_SITE_ID, settings.CUSTOMER_API_KEY)



wb = Workbook(encoding='utf-8')
ws = wb.active
ws.title = "Financial Report"

analytics.identify("Admin_",{"email" : "EMAIL"})
time.sleep(1)



def get_invoice_data(dic):
    Q = Invoice.objects.all()
    for (key,value) in dic.items():
Ejemplo n.º 26
0
"""
import csv
import logging
from customerio import CustomerIO, CustomerIOException
from config import *

try:
    logging.basicConfig(
        format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
        level=logging.INFO,
        filename=LOG_FILE)
except Exception as error_desc:
    print('Can not open logfile.\r\n', error_desc)
    exit(13)

cio = CustomerIO(SITE_ID, API_KEY)
logger = logging.getLogger(__name__)


def print_progress_bar(iteration,
                       total,
                       prefix='',
                       suffix='',
                       decimals=1,
                       length=100,
                       fill='█',
                       print_end="\r"):
    """
    Call in a loop to create terminal progress bar
    @params:
        iteration   - Required  : current iteration (Int)
try:
	import sys
	sys.path.insert(0, '/Users/nick/dev/theconversation')
except:
	print "could not import -- must be running on heroku"

from customerio import CustomerIO
import settings
from lib import userdb

cio = CustomerIO(settings.get('customer_io_site_id'), settings.get('customer_io_api_key'))

users = userdb.get_all()
for user_info in users:
	if user_info and 'user' in user_info.keys() and 'email_address' in user_info.keys() and user_info['email_address'] != "":
		cio.identify(id=user_info['user']['username'], email=user_info['email_address'], name=user_info['user']['fullname'])
		print "added @" + user_info['user']['username']
Ejemplo n.º 28
0
 def deploy(self):
     instance = self._get_pusher_instance()
     li = self._get_openshift_instance()
     instance[self.deploy_id].trigger('info_update', {
         'message': "Creating a new app...",
         'percent': 30
     })
     message = None
     log_error = False
     try:
         status, res = li.app_create(
             app_name=self.deploy_id,
             app_type=self.project.cartridges_list(),
             init_git_url=self.project.github_url)
         data = res()
     except (OpenShiftException, SSLError, ValueError) as e:
         # workaround to be able to log errors when the deployment fails
         if e.__class__ == OpenShiftException:
             log_error = True
         status = 500
         message = "A critical error has occured."
         logger.error("Critical error has occured during deployment".format(
             self.project.name),
                      exc_info=True,
                      extra={
                          'user_email': self.email,
                          'project_name': self.project.name,
                      })
     instance[self.deploy_id].trigger('info_update', {
         'message': "Getting results...",
         'percent': 60
     })
     if status == 201:
         app_url = data['data'].get('app_url')
         self.url = app_url
         self.status = 'Completed'
         self.launch_time = timezone.now()
         self.expiration_time = self.launch_time + datetime.timedelta(
             minutes=60)
         instance[self.deploy_id].trigger(
             'deployment_complete', {
                 'app_name': self.project.name,
                 'message': "Deployment complete!",
                 'app_url': app_url,
                 'username': self.project.default_username,
                 'password': self.project.default_password
             })
         if self.email:
             cio = CustomerIO(settings.CUSTOMERIO_SITE_ID,
                              settings.CUSTOMERIO_API_KEY)
             cio.track(customer_id=self.email,
                       name='app_deploy_complete',
                       app_url=app_url,
                       status_url="http://launch.appsembler.com/" +
                       reverse('deployment_detail',
                               kwargs={'deploy_id': self.deploy_id}),
                       username=self.project.default_username,
                       password=self.project.default_password)
     else:
         self.status = 'Failed'
         if log_error:
             error_log = DeploymentErrorLog(
                 deployment=self,
                 http_status=status,
                 error_log=data['messages'][0]['text'])
             error_log.save()
         instance[self.deploy_id].trigger('deployment_failed', {
             'message': "Deployment failed!",
         })
     self.save()
Ejemplo n.º 29
0
class TestCustomerIO(HTTPSTestCase):
    '''Starts server which the client connects to in the following tests'''
    def setUp(self):
        self.cio = CustomerIO(
            site_id='siteid',
            api_key='apikey',
            host=self.server.server_address[0],
            port=self.server.server_port,
            retries=5,
            backoff_factor=0)

        # do not verify the ssl certificate as it is self signed
        # should only be done for tests
        self.cio.http.verify = False

    def _check_request(self, resp, rq, *args, **kwargs):
        request = resp.request
        self.assertEqual(request.method, rq['method'])
        self.assertEqual(json.loads(request.body.decode('utf-8')), rq['body'])
        self.assertEqual(request.headers['Authorization'], rq['authorization'])
        self.assertEqual(request.headers['Content-Type'], rq['content_type'])
        self.assertEqual(int(request.headers['Content-Length']), len(json.dumps(rq['body'])))
        self.assertTrue(request.url.endswith(rq['url_suffix']),
            'url: {} expected suffix: {}'.format(request.url, rq['url_suffix']))


    def test_client_connection_handling(self):
        retries = self.cio.retries
        # should not raise exception as i should be less than retries and 
        # therefore the last request should return a valid response
        for i in range(retries):
            self.cio.identify(i, fail_count=i)

        # should raise expection as we get invalid responses for all retries
        with self.assertRaises(CustomerIOException):
            self.cio.identify(retries, fail_count=retries)


    def test_identify_call(self):
        self.cio.http.hooks=dict(response=partial(self._check_request, rq={
            'method': 'PUT',
            'authorization': _basic_auth_str('siteid', 'apikey'),
            'content_type': 'application/json',
            'url_suffix': '/customers/1',
            'body': {"name": "john", "email": "*****@*****.**"},
        }))

        self.cio.identify(id=1, name='john', email='*****@*****.**')

        with self.assertRaises(TypeError):
            self.cio.identify(random_attr="some_value")


    def test_track_call(self):
        self.cio.http.hooks=dict(response=partial(self._check_request, rq={
            'method': 'POST',
            'authorization': _basic_auth_str('siteid', 'apikey'),
            'content_type': 'application/json',
            'url_suffix': '/customers/1/events',
            'body': {"data": {"email": "*****@*****.**"}, "name": "sign_up"},
        }))

        self.cio.track(customer_id=1, name='sign_up', email='*****@*****.**')

        with self.assertRaises(TypeError):
            self.cio.track(random_attr="some_value")


    def test_pageview_call(self):
        self.cio.http.hooks=dict(response=partial(self._check_request, rq={
            'method': 'POST',
            'authorization': _basic_auth_str('siteid', 'apikey'),
            'content_type': 'application/json',
            'url_suffix': '/customers/1/events',
            'body': {"data": {"referer": "category_1"}, "type": "page", "name": "product_1"},
        }))

        self.cio.pageview(customer_id=1, page='product_1', referer='category_1')

        with self.assertRaises(TypeError):
            self.cio.pageview(random_attr="some_value")


    def test_delete_call(self):
        self.cio.http.hooks=dict(response=partial(self._check_request, rq={
            'method': 'DELETE',
            'authorization': _basic_auth_str('siteid', 'apikey'),
            'content_type': 'application/json',
            'url_suffix': '/customers/1',
            'body': {},
        }))

        self.cio.delete(customer_id=1)

        with self.assertRaises(TypeError):
            self.cio.delete(random_attr="some_value")


    def test_backfill_call(self):
        self.cio.http.hooks=dict(response=partial(self._check_request, rq={
            'method': 'POST',
            'authorization': _basic_auth_str('siteid', 'apikey'),
            'content_type': 'application/json',
            'url_suffix': '/customers/1/events',
            'body': {"timestamp": 1234567890, "data": {"email": "*****@*****.**"}, "name": "signup"},
        }))

        self.cio.backfill(customer_id=1, name='signup', timestamp=1234567890, email='*****@*****.**')

        with self.assertRaises(TypeError):
            self.cio.backfill(random_attr="some_value")

    def test_base_url(self):
        test_cases = [
            # host, port, prefix, result
            (None, None, None, 'https://track.customer.io/api/v1'),
            (None, None, 'v2', 'https://track.customer.io/v2'),
            (None, None, '/v2/', 'https://track.customer.io/v2'),
            ('sub.domain.com', 1337, '/v2/', 'https://sub.domain.com:1337/v2'),
            ('/sub.domain.com/', 1337, '/v2/', 'https://sub.domain.com:1337/v2'),
            ('http://sub.domain.com/', 1337, '/v2/', 'https://sub.domain.com:1337/v2'),
        ]

        for host, port, prefix, result in test_cases:
            cio = CustomerIO(host=host, port=port, url_prefix=prefix)
            self.assertEqual(cio.base_url, result)


    def test_device_call(self):
        self.cio.http.hooks=dict(response=partial(self._check_request, rq={
            'method': 'PUT',
            'authorization': _basic_auth_str('siteid', 'apikey'),
            'content_type': 'application/json',
            'url_suffix': '/customers/1/devices',
            'body': {"device": {"id": "device_1", "platform":"ios"}}
        }))

        self.cio.add_device(customer_id=1, device_id="device_1", platform="ios")
        with self.assertRaises(TypeError):
            self.cio.add_device(random_attr="some_value")

    def test_device_call_last_used(self):
        self.cio.http.hooks=dict(response=partial(self._check_request, rq={
            'method': 'PUT',
            'authorization': _basic_auth_str('siteid', 'apikey'),
            'content_type': 'application/json',
            'url_suffix': '/customers/1/devices',
            'body': {"device": {"id": "device_2", "platform": "android", "last_used": 1234567890}}
        }))

        self.cio.add_device(customer_id=1, device_id="device_2", platform="android", last_used=1234567890)

    def test_device_call_valid_platform(self):
        self.cio.http.hooks=dict(response=partial(self._check_request, rq={
            'method': 'PUT',
            'authorization': _basic_auth_str('siteid', 'apikey'),
            'content_type': 'application/json',
            'url_suffix': '/customers/1/devices',
            'body': {"device": {"id": "device_3", "platform": "notsupported"}}
        }))

        with self.assertRaises(CustomerIOException):
            self.cio.add_device(customer_id=1, device_id="device_3", platform=None)
   
    def test_device_call_has_customer_id(self):
        self.cio.http.hooks=dict(response=partial(self._check_request, rq={
            'method': 'PUT',
            'authorization': _basic_auth_str('siteid', 'apikey'),
            'content_type': 'application/json',
            'url_suffix': '/customers/1/devices',
            'body': {"device": {"id": "device_4", "platform": "ios"}}
        }))

        with self.assertRaises(CustomerIOException):
            self.cio.add_device(customer_id="", device_id="device_4", platform="ios")
    
    def test_device_call_has_device_id(self):
        self.cio.http.hooks=dict(response=partial(self._check_request, rq={
            'method': 'PUT',
            'authorization': _basic_auth_str('siteid', 'apikey'),
            'content_type': 'application/json',
            'url_suffix': '/customers/1/devices',
            'body': {"device": {"id": "device_5", "platform": "ios"}}
        }))

        with self.assertRaises(CustomerIOException):
            self.cio.add_device(customer_id=1, device_id="", platform="ios")

    def test_device_delete_call(self):
        self.cio.http.hooks=dict(response=partial(self._check_request, rq={
            'method': 'DELETE',
            'authorization': _basic_auth_str('siteid', 'apikey'),
            'content_type': 'application/json',
            'url_suffix': '/customers/1/devices/device_1',
            'body': {}
        }))

        self.cio.delete_device(customer_id=1, device_id="device_1")
        with self.assertRaises(TypeError):
            self.cio.delete_device(random_attr="some_value")
    
    def test_suppress_call(self):
        self.cio.http.hooks=dict(response=partial(self._check_request, rq={
            'method': 'POST',
            'authorization': _basic_auth_str('siteid', 'apikey'),
            'content_type': 'application/json',
            'url_suffix': '/customers/1/suppress',
            'body': {},
        }))

        self.cio.suppress(customer_id=1)

        with self.assertRaises(CustomerIOException):
            self.cio.suppress(None)

    def test_unsuppress_call(self):
        self.cio.http.hooks=dict(response=partial(self._check_request, rq={
            'method': 'POST',
            'authorization': _basic_auth_str('siteid', 'apikey'),
            'content_type': 'application/json',
            'url_suffix': '/customers/1/unsuppress',
            'body': {},
        }))

        self.cio.unsuppress(customer_id=1)

        with self.assertRaises(CustomerIOException):
            self.cio.unsuppress(None)


    @unittest.skipIf(sys.version_info.major > 2, "python2 specific test case")
    def test_sanitize_py2(self):
        data_in = dict(dt=datetime.fromtimestamp(1234567890))
        data_out = self.cio._sanitize(data_in)
        self.assertEqual(data_out, dict(dt=1234567890))


    @unittest.skipIf(sys.version_info.major < 3, "python3 specific test case")
    def test_sanitize_py3(self):
        from datetime import timezone
        data_in = dict(dt=datetime(2009, 2, 13, 23, 31, 30, 0, timezone.utc))
        data_out = self.cio._sanitize(data_in)
        self.assertEqual(data_out, dict(dt=1234567890))
Ejemplo n.º 30
0
def track_event(user_id, event_name, **kwargs):
    cio = CustomerIO(os.environ['CUSTOMER_IO_SITE_ID'],
                     os.environ['CUSTOMER_IO_API_KEY'])
    cio.track(customer_id=user_id, name=event_name, **kwargs)