コード例 #1
0
 def test_post_timeout_single(self):
     keen.project_id = "5004ded1163d66114f000000"
     api_key = "2e79c6ec1d0145be8891bf668599c79a"
     keen.write_key = scoped_keys.encrypt(api_key, {"allowed_operations": ["write"]})
     client = KeenClient(keen.project_id, write_key=keen.write_key, read_key=None,
                         post_timeout=0.0001)
     client.add_event("python_test", {"hello": "goodbye"})
コード例 #2
0
ファイル: daily_load.py プロジェクト: tonyin/spire
def main():
    print "[INFO] Getting raw spire data..."
    date = (datetime.today() - timedelta(days=DAYS_AGO)).strftime("%Y-%m-%d")

    # Breath
    breath_client = KeenClient(
        project_id=keen["breath"]["project_id"],
        read_key=keen["breath"]["read_key"],
        write_key=keen["breath"]["write_key"],
    )

    if len(breath_client.extraction("sessions", timeframe="this_" + str(DAYS_AGO) + "_days")) > 0:
        print "[INFO] Already uploaded breath data for " + date + "."
    else:
        breaths = get_spire(spire["breath"], date)

        if len(breaths["data"]) > 0:
            print "[INFO] Uploading breath data..."

            keen_breaths = {}
            keen_breaths["breaths"] = [{"timestamp": b["timestamp"], "value": b["value"]} for b in breaths["data"]]
            keen_breaths["metadata"] = breaths["metadata"]

            keen_breaths["metadata"]["min"] = min([b["value"] for b in breaths["data"]])
            keen_breaths["metadata"]["max"] = max([b["value"] for b in breaths["data"]])
            keen_breaths["id"] = USER + date
            breath_client.add_event("sessions", keen_breaths)
            print "[INFO] Uploaded breath data for " + date + "."
        else:
            print "[INFO] No breath data found for " + date + "."

    # Steps
    steps_client = KeenClient(
        project_id=keen["steps"]["project_id"], read_key=keen["steps"]["read_key"], write_key=keen["steps"]["write_key"]
    )

    if len(steps_client.extraction("sessions", timeframe="this_" + str(DAYS_AGO) + "_days")) > 0:
        print "[INFO] Already uploaded steps data for " + date + "."
    else:
        steps = get_spire(spire["step"], date)

        if len(steps["data"]) > 0:
            print "[INFO] Uploading steps data..."

            keen_steps = {}
            keen_steps["steps"] = [{"timestamp": s["timestamp"], "value": s["value"]} for s in steps["data"]]
            keen_steps["metadata"] = steps["metadata"]
            keen_steps["id"] = USER + date
            steps_client.add_event("sessions", keen_steps)
            print "[INFO] Uploaded steps data for " + date + "."
        else:
            print "[INFO] No steps data found for " + date + "."
コード例 #3
0
class KeenEventService:
    def __init__(self):
        self.client = KeenClient(
            project_id=ENV["keen"]["project_id"],
            write_key=ENV["keen"]["write_key"],
            read_key=ENV["keen"]["read_key"],
            master_key=ENV["keen"]["master_key"],
        )

    #
    # insert an event log
    #
    def insert_event(self, event_collection, event_log):
        try:
            self.client.add_event(event_collection, event_log)
        except Exception, e:
            log.exception(e)
コード例 #4
0
class CustomJSONEncoderTests(BaseTestCase):

    def setUp(self):
        super(CustomJSONEncoderTests, self).setUp()
        api_key = "2e79c6ec1d0145be8891bf668599c79a"
        self.client = KeenClient(project_id='5004ded1163d66114f000000',
                                 write_key=scoped_keys.encrypt(api_key, {"allowed_operations": ["write"]}),
                                 read_key=scoped_keys.encrypt(api_key, {"allowed_operations": ["read"]}),
                                 json_encoder=CustomEncoder)

    def tearDown(self):
        self.client = None
        super(CustomJSONEncoderTests, self).tearDown()

    def test_custom_encoder_with_datetime_type(self):
        self.client.add_event("sign_ups", {
            "username": "******",
            "referred_by": "harry",
            "confirmed_at": datetime.datetime.utcnow()
        })
コード例 #5
0
ファイル: keen.py プロジェクト: marcindziedzic/intuition
class AsyncKeenClient(object):
    def __init__(self):
        self._client = KeenClient(
            project_id=os.environ['KEEN_PROJECT_ID'],
            write_key=os.environ['KEEN_WRITE_KEY'],
            post_timeout=100
        )

    def add_event(self, collection, body):
        push = lambda: self._client.add_event(collection, body)
        IOLoop.current().spawn_callback(push)
コード例 #6
0
 def test_direct_persistence_strategy(self):
     project_id = "5004ded1163d66114f000000"
     api_key = "2e79c6ec1d0145be8891bf668599c79a"
     write_key = scoped_keys.encrypt(api_key, {"allowed_operations": ["write"]})
     read_key = scoped_keys.encrypt(api_key, {"allowed_operations": ["read"]})
     client = KeenClient(project_id, write_key=write_key, read_key=read_key)
     client.add_event("python_test", {"hello": "goodbye"})
     client.add_event("python_test", {"hello": "goodbye"})
     client.add_events(
         {
             "sign_ups": [{
                 "username": "******",
                 "referred_by": "steve",
                 "son_of": "my_mom"
             }],
             "purchases": [
                 {"price": 5},
                 {"price": 6},
                 {"price": 7}
             ]}
     )
コード例 #7
0
 def test_direct_persistence_strategy(self):
     project_id = "5004ded1163d66114f000000"
     api_key = "2e79c6ec1d0145be8891bf668599c79a"
     write_key = scoped_keys.encrypt(api_key,
                                     {"allowed_operations": ["write"]})
     read_key = scoped_keys.encrypt(api_key,
                                    {"allowed_operations": ["read"]})
     client = KeenClient(project_id, write_key=write_key, read_key=read_key)
     client.add_event("python_test", {"hello": "goodbye"})
     client.add_event("python_test", {"hello": "goodbye"})
     client.add_events({
         "sign_ups": [{
             "username": "******",
             "referred_by": "steve",
             "son_of": "my_mom"
         }],
         "purchases": [{
             "price": 5
         }, {
             "price": 6
         }, {
             "price": 7
         }]
     })
コード例 #8
0
ファイル: app.py プロジェクト: bearxiong99/flowbright
from keen.client import KeenClient
import json

client = KeenClient(
    project_id="50ba759d897a2c45c2000000",
    write_key=
    "72a1ad85456fd2361cfe8b65d240eab16b083caa944c11d9507761e536d34822eca4cefad12e482373ee41278953f98c6ff724b33b56fd5971e8d34cdd284cb6c3f202d9f13329e39c01eb9a35d1124cae81d9a4505992fce03be4030e32b19c72df266c55a18937f001e7158f6eac44",
    read_key=
    "f7a829ecc9e237949b0b97245c828661ff0c9f46eaec35a7dc4cfd3b4ef42804f5fc1859ecad05bd7d72b6c3a2e4047ae39cba86b5d51c770be26b0a406cbb44650d20fd05fb7896293f015cec3af3ca2df8a507927bf4cfed9275eb89e8dee855cf45db75b221074bda2f737d2810f2"
)

headers = {"Accept": "text/event-stream"}

messages = SSEClient('https://flickering-fire-322.firebaseio.com/.json',
                     headers=headers)

first = True

for msg in messages:
    print msg.event
    print msg.data
    if not first and 'put' in msg.event:
        data = json.loads(msg.data)
        path = data['path'].split("/")
        device = path[1]
        timestamp = path[2]
        data = int(data['data'])
        event = {"device": device, "volume": data, "timestamp": timestamp}
        client.add_event("flow", event)
    first = False
コード例 #9
0
ファイル: app.py プロジェクト: Devarajr/flowbright
import json

client = KeenClient(
    project_id="50ba759d897a2c45c2000000",
    write_key="72a1ad85456fd2361cfe8b65d240eab16b083caa944c11d9507761e536d34822eca4cefad12e482373ee41278953f98c6ff724b33b56fd5971e8d34cdd284cb6c3f202d9f13329e39c01eb9a35d1124cae81d9a4505992fce03be4030e32b19c72df266c55a18937f001e7158f6eac44",
    read_key="f7a829ecc9e237949b0b97245c828661ff0c9f46eaec35a7dc4cfd3b4ef42804f5fc1859ecad05bd7d72b6c3a2e4047ae39cba86b5d51c770be26b0a406cbb44650d20fd05fb7896293f015cec3af3ca2df8a507927bf4cfed9275eb89e8dee855cf45db75b221074bda2f737d2810f2"
)

headers = {"Accept": "text/event-stream"}

messages = SSEClient('https://flickering-fire-322.firebaseio.com/.json', headers=headers)

first = True

for msg in messages:
    print msg.event
    print msg.data
    if not first and 'put' in msg.event:
        data = json.loads(msg.data)
        path = data['path'].split("/")
        device = path[1]
        timestamp = path[2]
        data = int(data['data'])
        event = {
            "device": device,
            "volume": data,
            "timestamp": timestamp
        }
        client.add_event("flow", event)
    first = False
コード例 #10
0
 def test_direct_persistence_strategy(self):
     project_id = "5004ded1163d66114f000000"
     auth_token = "2e79c6ec1d0145be8891bf668599c79a"
     client = KeenClient(project_id, auth_token)
     client.add_event("python_test", {"hello": "goodbye"})
コード例 #11
0
# Clean up the bloomsky data
bloomskyData[0]['Data'].pop('ImageURL', 0)
bloomskyData[0]['Data'].pop('TS', 0)
bloomskyData[0]['Data'].pop('ImageTS', 0)

# Convert to Celsius and millibar
# Also below I'm doing bunch of field name normalizations to conform
# my naming standards with other IoTs that I have at home
bloomskyData[0]['Data']['temperature'] = toCelsius(
    bloomskyData[0]['Data']['Temperature'])
bloomskyData[0]['Data'].pop('Temperature')
bloomskyData[0]['Data']['pressure'] = toMilliBar(
    bloomskyData[0]['Data']['Pressure'])
bloomskyData[0]['Data'].pop('Pressure')
bloomskyData[0]['Data']['humidity'] = bloomskyData[0]['Data'].pop('Humidity')
bloomskyData[0]['Data']['agentName'] = agent_name
bloomskyData[0]['Data']['timestamp'] = datetime.datetime.utcnow().strftime(
    "%FT%H:%M:%S.%fZ")
bloomskyData[0]['Data']['day'] = not bloomskyData[0]['Data'].pop('Night')
bloomskyData[0]['Data']['rain'] = bloomskyData[0]['Data'].pop('Rain')
# Hoping that Electric Imp lux and BloomSky Luminance are similar :)
bloomskyData[0]['Data']['lux'] = bloomskyData[0]['Data'].pop('Luminance')

# Create the Keen.io client
client = KeenClient(project_id=project_id,
                    write_key=write_key,
                    get_timeout=100)
#print bloomskyData[0]['Data']
client.add_event(event_collector, bloomskyData[0]['Data'])
コード例 #12
0
ファイル: gen.py プロジェクト: Devarajr/flowbright
                     },
         "weekend" : {
                          "night": 8,
                          "work": 10,
                          "home": 10                    
                      }
}

def random_use(d):
    a = model[day(d)][hour(d)]
    b = random.randrange(int(a - 0.2 * a), int(a + 0.2 * a)) + random.random()
    return b - (0.1 * b)
    

# <codecell>

cur  = start
while cur < stop:
    use = random_use(cur)
    data = {"keen": {"timestamp": cur.isoformat()}, "device": "230c51eb6e4936ee", "volume": use}
    client.add_event("flow", data)
    cur = cur + one_hour

# <codecell>

stop.isoformat()

# <codecell>


コード例 #13
0
ファイル: alerter.py プロジェクト: hanman18/chargers2
class Alerter:
   def __init__(self):
      logging.basicConfig()
      self.logger = logging.getLogger()
      self.logger.setLevel(logging.INFO)
      self.garage_data = anyconfig.load("garage_data.json")['garage_data']
      self.sg = sendgrid.SendGridClient(SENDGRID_USERNAME, SENDGRID_PASSWORD)
      self.keen_client = None

   def __keen_client(self):
      if KEEN_CLIENT_ENABLED and self.keen_client == None:
         self.keen_client = KeenClient(
            project_id=KEEN_PROJECT_ID,
            write_key=KEEN_WRITE_KEY,
            read_key=KEEN_READ_KEY,
            base_url=KEEN_API_URL
         )
      return self.keen_client

   def is_email(self, address):
      assert isinstance(address, str)
      return "@" in address


   def is_phone(self, address):
      assert isinstance(address, str)
      address = address.replace("-", "")
      pattern = re.compile("^[\dA-Z]{3}[\dA-Z]{3}[\dA-Z]{4}$", re.IGNORECASE)
      return pattern.match(address) is not None


   @newrelic.agent.background_task()
   def send_alert(self, address, garage):
      if self.is_email(address):
         self.send_email(address, garage)
      elif self.is_phone(address):
         self.send_txt(address, garage)
      self.clear_subs_for_user(address)


   @newrelic.agent.background_task()
   def send_email(self, address, garage):
      self.logger.info("send_email(): Sending email about {} to {}".format(garage, address))
      message = sendgrid.Mail()
      message.add_to(address)
      message_text = "{} has a new spot open".format(garage)
      message.set_subject(message_text)
      message.set_text(message_text) #todo add how many spots are there
      message.set_from(SENDGRID_EMAIL_FROM)
      self.sg.send(message)


   @newrelic.agent.background_task()
   def send_txt(self, address, garage):
      self.logger.info("send_txt(): Sending txt about {} to {}".format(garage, address))
      body = "{} has a new spot open".format(garage),
      EasySms.send_sms(to=address, body=body)


   @newrelic.agent.background_task()
   def find_changes(self):
      current = json.loads(self.redis_connection.hget("current", "data"))
      previous = json.loads(self.redis_connection.hget("previous", "data"))

      cur_stations = {}
      prev_stations = {}

      for garage in current.itervalues():
         cur_stations.update(garage['stations'])

      for garage in previous.itervalues():
         prev_stations.update(garage['stations'])

      stations_with_new_spots = []
      for station_name in cur_stations.keys():
         if cur_stations[station_name] > prev_stations[station_name]:
            stations_with_new_spots.append(station_name)

      all_changes = {}
      for station_name in cur_stations.keys():
         if cur_stations[station_name] != prev_stations[station_name]:
            all_changes[station_name] = cur_stations[station_name]

      self.logger.info("find_changes(): all chargers changes: {}".format(all_changes))

      if KEEN_CLIENT_ENABLED and all_changes > 0:
         self.logger.info("find_changes(): sending keen events: {}".format(all_changes))
         self.keen_client.add_event("chargers", all_changes)

      self.logger.info("find_changes(): stations with new spots {}".format(stations_with_new_spots))
      return stations_with_new_spots


   @newrelic.agent.background_task()
   def clear_subs_for_user(self, target):
      self.logger.info("clear_subs_for_user(): Clearing sub for user {}".format(target))
      for key in self.redis_connection.keys("SUB-*"):
         if self.redis_connection.type(key) == "set":
            self.redis_connection.srem(key, target)
            self.logger.info("clear_subs_for_user(): Removing user {} from subscription to {}".format(target, key))

   def find_garage(self, station):
      # station = PG3-STATION 26 for example
      # PG1 is Hilltop, PG2 is Central, PG3 is Creekside
      garages = ['Hilltop', 'Central', 'Creekside']
      garage_array_index = int(station[2]) - 1
      return garages[garage_array_index]


   @newrelic.agent.background_task()
   def main_loop(self):
      self.redis_connection = redis.StrictRedis.from_url(REDIS_URL)
      pubsub = self.redis_connection.pubsub()
      pubsub.subscribe(REDIS_CHANNEL)

      # todo add tests
      for item in pubsub.listen():
         self.logger.info("alerter.main_loop(): Got a new alert. Processing ...")

         if item['type'] == "message":
            self.logger.info("alerter.main_loop(): received alert of new info with timstamp {}".format(item['data']))

            for station in self.find_changes():
               self.logger.info("alerter.main_loop(): Found new station {}...checking for subscriptions".format(station))

               garage_name = self.find_garage(station)
               queue_name = "SUB-{}".format(garage_name)

               self.logger.info("alerter.main_loop(): Checking queue {} for members".format(queue_name))
               queue_members = self.redis_connection.smembers(queue_name)

               if len(queue_members) == 0:
                  self.logger.info("alerter.main_loop(): No notifications to send for garage {}".format(garage_name))
               for target in queue_members:
                  self.logger.info("alerter.main_loop(): Notifying {} for {} changes.".format(target, garage_name))
                  self.send_alert(target, garage_name)

         self.logger.info("alerter.main_loop(): done processing alert.")
コード例 #14
0
ファイル: hire_keen.py プロジェクト: sreenathp20/analytics
class HireKeen:

    def __init__(self):
        self.keenWriter = KeenClient(
            project_id=config.KEEN_PROJECTID,
            write_key=config.KEEN_WRITEKEY,
            read_key=config.KEEN_READKEY
        )        

    def record_keen(self, index, event):
        self.keenWriter.add_event(index, event)

    def extract_keen(self, index, timeframe, filters):
        return self.keenWriter.extraction(index, timeframe, filters=filters)

    def pred_identify(self, user_id):
        self.identified = True
        self.predclient.identify(user_id)

    def pred_ranked(self, engine, data):
        return self.predclient.get_itemrank_ranked(engine, data)

    def pred_top_rec(self, engine, number):
        return self.predclient.get_itemrec_topn(engine, number)

    def pred_create(self, user, userType):
        self.predclient.create_user(user, {'userType': userType})

    def event_record_register(self, loggedin_id):
        user_details = self.db_user.find_one({'linkedin_id': loggedin_id})
        if user_details:
            event_data = {
                "action": "registration",
                "userId": user_details['linkedin_id'],
                "userCompanyId": user_details['linkedin_company_id']
            }
            recordEvent.delay('siteActions', event_data)

    def event_record_activate(self, loggedin_id):
        user_details = self.db_user.find_one({'linkedin_id': loggedin_id})
        if user_details:
            event_data = {
                "action": "activation",
                "userId": user_details['linkedin_id'],
                "userCompanyId": user_details['linkedin_company_id']
            }
            recordEvent.delay('siteActions', event_data)

    def event_record_create_project(self, project_slug, user_id):
        project_details = self.db_project.find_one({'slug': project_slug, 'loggedin_id': user_id})
        if project_details:
            try:
                user_details = self.db_user.find_one({'linkedin_id': project_details['loggedin_id']})
                company_details = self.db_linkedincompany.find_one({'id': user_details['linkedin_company_id']})
                event_data = {
                    "action": "project creation",
                    "userId": project_details['loggedin_id'],
                    "userCompanyId": user_details['linkedin_company_id'],
                    "slug": project_slug,
                    "skills": [],
                    "addlSkills": [],
                    "userCompany": company_details['name'],
                    "forCompanyId": project_details['companyId'],
                    "forCompanyName": project_details['company'],
                    "name": project_details['name']
                }
                for skill in project_details['skills']:
                    if skill['skill'] not in event_data['skills']:
                        event_data['skills'].append(skill['skill'])
                for addlskill in project_details['addlSkills']:
                    if addlskill not in event_data['addlSkills']:
                        event_data['addlSkills'].append(addlskill)
                recordEvent.delay('siteActions', event_data)
            except:
                rollbar.report_exc_info()

    def event_record_create_position(self, project_slug, position_slug, user_id):
        position_details = self.db_position.find_one({'project': project_slug, 'slug': position_slug, 'loggedin_id': user_id})
        if position_details:
            try:
                user_details = self.db_user.find_one({'linkedin_id': position_details['loggedin_id']})
                company_details = self.db_linkedincompany.find_one({'id': user_details['linkedin_company_id']})
                project_details = self.db_project.find_one({'slug': project_slug})
                event_data = {
                    "action": "position creation",
                    "userId": user_details['linkedin_id'],
                    "companyId": user_details['linkedin_company_id'],
                    "slug": position_details['slug'],
                    "projectslug": position_details['project'],
                    "skills": [],
                    "addlSkills": [],
                    "userCompany": company_details['name'],
                    "forCompanyId": project_details['companyId'],
                    "forCompanyName": project_details['company'],
                    "title": position_details['title'],
                    "projectName": project_details['name']
                }
                for skill in position_details['skills']:
                    if skill['skill'] not in event_data['skills']:
                        event_data['skills'].append(skill['skill'])
                for addlskill in position_details['addlSkills']:
                    if addlskill not in event_data['addlSkills']:
                        event_data['addlSkills'].append(addlskill)
                recordEvent.delay('siteActions', event_data)
            except:
                rollbar.report_exc_info()

    def event_record_resumesearch(self, position_slug, loggedin_id):
        resumesearch_details = self.db_searchlog.find_one({'slug': position_slug, 'loggedin_id': loggedin_id})
        if resumesearch_details:
            try:
                user_details = self.db_user.find_one({'linkedin_id': loggedin_id})
                company_details = self.db_linkedincompany.find_one({'id': user_details['linkedin_company_id']})
                position_details = self.db_position.find_one({'loggedin_id': loggedin_id, 'slug': position_slug})
                project_details = self.db_project.find_one({'slug': position_details['project']})
                event_data = {
                    "action": "resume search",
                    "userId": loggedin_id,
                    "companyId": user_details['linkedin_company_id'],
                    "positionslug": position_slug,
                    "projectslug": position_details['project'],
                    "userCompany": company_details['name'],
                    "forCompanyId": project_details['companyId'],
                    "forCompanyName": project_details['company'],
                    "title": position_details['title'],
                    "projectName": project_details['name'],
                    "results": resumesearch_details['results'],
                    "site": resumesearch_details['site']
                }
                recordEvent.delay('siteActions', event_data)
            except:
                rollbar.report_exc_info()

    def event_record_resumedownload(self, position_slug, loggedin_id, doc_id):
        download_details = self.db_positionexternalrel.find_one({'position_slug': position_slug, 'loggedin_id': loggedin_id, 'doc_id': doc_id})
        if download_details:
            try:
                user_details = self.db_user.find_one({'linkedin_id': loggedin_id})
                company_details = self.db_linkedincompany.find_one({'id': user_details['linkedin_company_id']})
                position_details = self.db_position.find_one({'loggedin_id': loggedin_id, 'slug': position_slug})
                project_details = self.db_project.find_one({'slug': position_details['project']})
                event_data = {
                    "action": "resume download",
                    "userId": loggedin_id,
                    "companyId": user_details['linkedin_company_id'],
                    "positionslug": position_slug,
                    "projectslug": position_details['project'],
                    "userCompany": company_details['name'],
                    "forCompanyId": project_details['companyId'],
                    "forCompanyName": project_details['company'],
                    "title": position_details['title'],
                    "projectName": project_details['name'],
                    "site": download_details['source'],
                    "resumeid": doc_id
                }
                recordEvent.delay('siteActions', event_data)
            except:
                rollbar.report_exc_info()
コード例 #15
0
ファイル: gen.py プロジェクト: bearxiong99/flowbright
    }
}


def random_use(d):
    a = model[day(d)][hour(d)]
    b = random.randrange(int(a - 0.2 * a), int(a + 0.2 * a)) + random.random()
    return b - (0.1 * b)


# <codecell>

cur = start
while cur < stop:
    use = random_use(cur)
    data = {
        "keen": {
            "timestamp": cur.isoformat()
        },
        "device": "230c51eb6e4936ee",
        "volume": use
    }
    client.add_event("flow", data)
    cur = cur + one_hour

# <codecell>

stop.isoformat()

# <codecell>
コード例 #16
0
 def test_direct_persistence_strategy(self):
     project_id = "5004ded1163d66114f000000"
     api_key = "2e79c6ec1d0145be8891bf668599c79a"
     write_key = scoped_keys.encrypt(api_key, {"allowed_operations": ["write"]})
     client = KeenClient(project_id, write_key=write_key)
     client.add_event("python_test", {"hello": "goodbye"})
コード例 #17
0
ファイル: alerter.py プロジェクト: ndd314/chargers2
class Alerter:
    def __init__(self):
        logging.basicConfig()
        self.logger = logging.getLogger()
        self.logger.setLevel(logging.INFO)
        self.garage_data = anyconfig.load("garage_data.json")['garage_data']
        self.sg = sendgrid.SendGridClient(SENDGRID_USERNAME, SENDGRID_PASSWORD)
        self.keen_client = None

    def __keen_client(self):
        if KEEN_CLIENT_ENABLED and self.keen_client == None:
            self.keen_client = KeenClient(project_id=KEEN_PROJECT_ID,
                                          write_key=KEEN_WRITE_KEY,
                                          read_key=KEEN_READ_KEY,
                                          base_url=KEEN_API_URL)
        return self.keen_client

    def is_email(self, address):
        assert isinstance(address, str)
        return "@" in address

    def is_phone(self, address):
        assert isinstance(address, str)
        address = address.replace("-", "")
        pattern = re.compile("^[\dA-Z]{3}[\dA-Z]{3}[\dA-Z]{4}$", re.IGNORECASE)
        return pattern.match(address) is not None

    @newrelic.agent.background_task()
    def send_alert(self, address, garage):
        if self.is_email(address):
            self.send_email(address, garage)
        elif self.is_phone(address):
            self.send_txt(address, garage)
        self.clear_subs_for_user(address)

    @newrelic.agent.background_task()
    def send_email(self, address, garage):
        self.logger.info("send_email(): Sending email about {} to {}".format(
            garage, address))
        message = sendgrid.Mail()
        message.add_to(address)
        message_text = "{} has a new spot open".format(garage)
        message.set_subject(message_text)
        message.set_text(message_text)  #todo add how many spots are there
        message.set_from(SENDGRID_EMAIL_FROM)
        self.sg.send(message)

    @newrelic.agent.background_task()
    def send_txt(self, address, garage):
        self.logger.info("send_txt(): Sending txt about {} to {}".format(
            garage, address))
        body = "{} has a new spot open".format(garage),
        EasySms.send_sms(to=address, body=body)

    @newrelic.agent.background_task()
    def find_changes(self):
        current = json.loads(self.redis_connection.hget("current", "data"))
        previous = json.loads(self.redis_connection.hget("previous", "data"))

        cur_stations = {}
        prev_stations = {}

        for garage in current.itervalues():
            cur_stations.update(garage['stations'])

        for garage in previous.itervalues():
            prev_stations.update(garage['stations'])

        stations_with_new_spots = []
        for station_name in cur_stations.keys():
            if cur_stations[station_name] > prev_stations[station_name]:
                stations_with_new_spots.append(station_name)

        all_changes = {}
        for station_name in cur_stations.keys():
            if cur_stations[station_name] != prev_stations[station_name]:
                all_changes[station_name] = cur_stations[station_name]

        self.logger.info(
            "find_changes(): all chargers changes: {}".format(all_changes))

        if KEEN_CLIENT_ENABLED and all_changes > 0:
            self.logger.info(
                "find_changes(): sending keen events: {}".format(all_changes))
            self.keen_client.add_event("chargers", all_changes)

        self.logger.info("find_changes(): stations with new spots {}".format(
            stations_with_new_spots))
        return stations_with_new_spots

    @newrelic.agent.background_task()
    def clear_subs_for_user(self, target):
        self.logger.info(
            "clear_subs_for_user(): Clearing sub for user {}".format(target))
        for key in self.redis_connection.keys("SUB-*"):
            if self.redis_connection.type(key) == "set":
                self.redis_connection.srem(key, target)
                self.logger.info(
                    "clear_subs_for_user(): Removing user {} from subscription to {}"
                    .format(target, key))

    def find_garage(self, station):
        # station = PG3-STATION 26 for example
        # PG1 is Hilltop, PG2 is Central, PG3 is Creekside
        garages = ['Hilltop', 'Central', 'Creekside']
        garage_array_index = int(station[2]) - 1
        return garages[garage_array_index]

    @newrelic.agent.background_task()
    def main_loop(self):
        self.redis_connection = redis.StrictRedis.from_url(REDIS_URL)
        pubsub = self.redis_connection.pubsub()
        pubsub.subscribe(REDIS_CHANNEL)

        # todo add tests
        for item in pubsub.listen():
            self.logger.info(
                "alerter.main_loop(): Got a new alert. Processing ...")

            if item['type'] == "message":
                self.logger.info(
                    "alerter.main_loop(): received alert of new info with timstamp {}"
                    .format(item['data']))

                for station in self.find_changes():
                    self.logger.info(
                        "alerter.main_loop(): Found new station {}...checking for subscriptions"
                        .format(station))

                    garage_name = self.find_garage(station)
                    queue_name = "SUB-{}".format(garage_name)

                    self.logger.info(
                        "alerter.main_loop(): Checking queue {} for members".
                        format(queue_name))
                    queue_members = self.redis_connection.smembers(queue_name)

                    if len(queue_members) == 0:
                        self.logger.info(
                            "alerter.main_loop(): No notifications to send for garage {}"
                            .format(garage_name))
                    for target in queue_members:
                        self.logger.info(
                            "alerter.main_loop(): Notifying {} for {} changes."
                            .format(target, garage_name))
                        self.send_alert(target, garage_name)

            self.logger.info("alerter.main_loop(): done processing alert.")