示例#1
0
def withings_get_data(healthdevice):
    height = None
    weight = None
    daily_exercise = None

    creds = WithingsCredentials(
        access_token=healthdevice.access_token,
        access_token_secret=healthdevice.meta['access_token_secret'],
        consumer_key=WITHINGS_SETTINGS['CONSUMER_KEY'],
        consumer_secret=WITHINGS_SETTINGS['CONSUMER_SECRET'],
        user_id=healthdevice.meta['user_id'])

    client = WithingsApi(creds)
    try:
        measures = client.get_measures(limit=1,
                                       meastype='1')  # measure type: weight
        weight = measures[0].weight
    except:
        pass

    try:
        measures = client.get_measures(limit=1,
                                       meastype='4')  # mesuare type: height
        height = measures[0].height * 100
    except:
        pass

    try:
        workouts_params = {
            'userid': healthdevice.meta['user_id'],
            'startdateymd':
            (date.today() - timedelta(days=PERIOD)).isoformat(),
            'enddateymd': date.today().isoformat()
        }
        workouts = client.request('v2/measure',
                                  'getworkouts',
                                  params=workouts_params)
        if 'series' in workouts:
            total_seconds = reduce(
                lambda acc, item: acc + withings_get_item_duration(item),
                workouts['series'], 0)
            daily_exercise = round(total_seconds / (PERIOD * 60), 0)
    except:
        pass

    if height is None and weight is None and daily_exercise is None:
        return None
    else:
        return {
            'height': height,
            'weight': weight,
            'daily_exercise': daily_exercise
        }
def retrieve_and_save_withings_measurements(withings_userid, device_id,
                                            start_ts, end_ts,
                                            measurement_type_id):
    logger.debug(
        "[medical-compliance] Query Withings API to retrieve measurement: %s" %
        (locals()))

    ## retrieve the DeviceUsage instance associated with the Withings `withings_userid` and `device_id`
    device_usage_data = store_utils.get_device_usage(
        store_utils.STORE_ENDPOINT_URI,
        device=device_id,
        access_info={"withings_userid": str(withings_userid)})

    if not device_usage_data:
        logger.error(
            "[medical-compliance] Cannot find any user - device combination with access config for "
            "withings_userid : %s and device_id : %s" %
            (str(withings_userid), str(device_id)))
        return

    logger.debug(
        "[medical-compliance] DeviceUsage data for Withings weight measurement: %s"
        % str(device_usage_data))

    ## get access_info, user and device information from device_usage object
    access_info = device_usage_data['access_info']
    cami_user_id = device_usage_data['user_id']

    credentials = WithingsCredentials(
        access_token=access_info['withings_oauth_token'],
        access_token_secret=access_info['withings_oauth_token_secret'],
        consumer_key=access_info['withings_consumer_key'],
        consumer_secret=access_info['withings_consumer_secret'],
        user_id=int(access_info['withings_userid']))

    client = WithingsApi(credentials)
    req_params = {
        'startdate': start_ts,
        'enddate': end_ts,
        'meastype': int(measurement_type_id)
    }
    response = client.request('measure', 'getmeas', req_params)

    logger.debug(
        "[medical-compliance] Got the following Withings response for user_id %s and req params %s: %s"
        % (withings_userid, req_params, response))

    measures = WithingsMeasures(response)
    timezoneStr = response['timezone']
    measurement_type = WithingsMeasurement.get_measure_type_by_id(
        int(measurement_type_id))

    for m in measures:
        ## Store WithingsMeasurement for error inspection
        meas = WithingsMeasurement(withings_user_id=int(withings_userid),
                                   type=measurement_type_id,
                                   retrieval_type=m.attrib,
                                   measurement_unit=WithingsMeasurement.
                                   MEASUREMENT_SI_UNIT[measurement_type],
                                   timestamp=m.data['date'],
                                   timezone=timezoneStr,
                                   value=m.__getattribute__(measurement_type))

        logger.debug(
            "[medical-compliance] Saving Withings measurement in cami DB: %s" %
            (meas))
        meas.save()

        logger.debug(
            "[medical-compliance] Sending Withings weight measurement for processing: %s"
            % (meas))
        process_weight_measurement.delay(
            cami_user_id,  # end user id
            device_id,  # device id
            "weight",  # measurement_type
            meas.measurement_unit,
            meas.timestamp,
            meas.timezone,
            meas.value)
class TestWithingsApi(unittest.TestCase):
    def setUp(self):
        self.mock_api = True
        if self.mock_api:
            self.creds = WithingsCredentials()
        else:
            config = ConfigParser.ConfigParser()
            config.read('withings.conf')
            self.creds = WithingsCredentials(
                consumer_key=config.get('withings', 'consumer_key'),
                consumer_secret=config.get('withings', 'consumer_secret'),
                access_token=config.get('withings', 'access_token'),
                access_token_secret=config.get('withings',
                                               'access_token_secret'),
                user_id=config.get('withings', 'user_id'))
        self.api = WithingsApi(self.creds)

    def test_attributes(self):
        """ Make sure the WithingsApi objects have the right attributes """
        assert hasattr(WithingsApi, 'URL')
        creds = WithingsCredentials(user_id='FAKEID')
        api = WithingsApi(creds)
        assert hasattr(api, 'credentials')
        assert hasattr(api, 'oauth')
        assert hasattr(api, 'client')

    def test_attribute_defaults(self):
        """
        Make sure WithingsApi object attributes have the correct defaults
        """
        self.assertEqual(WithingsApi.URL, 'http://wbsapi.withings.net')
        creds = WithingsCredentials(user_id='FAKEID')
        api = WithingsApi(creds)
        self.assertEqual(api.credentials, creds)
        self.assertEqual(api.client.auth, api.oauth)
        self.assertEqual(api.client.params, {'userid': creds.user_id})

    def test_request(self):
        """
        Make sure the request method builds the proper URI and returns the
        request body as a python dict.
        """
        self.mock_request({})
        resp = self.api.request('fake_service', 'fake_action')
        Session.request.assert_called_once_with(
            'GET', 'http://wbsapi.withings.net/fake_service',
            params={'action': 'fake_action'})
        self.assertEqual(resp, {})

    def test_request_params(self):
        """
        Check that the request method passes along extra params and works
        with different HTTP methods
        """
        self.mock_request({})
        resp = self.api.request('user', 'getbyuserid', params={'p2': 'p2'},
                                method='POST')
        Session.request.assert_called_once_with(
            'POST', 'http://wbsapi.withings.net/user',
            params={'p2': 'p2', 'action': 'getbyuserid'})
        self.assertEqual(resp, {})

    def test_request_error(self):
        """ Check that requests raises an exception when there is an error """
        self.mock_request('', status=1)
        self.assertRaises(Exception, self.api.request, ('user', 'getbyuserid'))

    def test_get_user(self):
        """ Check that the get_user method fetches the right URL """
        self.mock_request({
            'users': [
                {'id': 1111111, 'birthdate': 364305600, 'lastname': 'Baggins',
                 'ispublic': 255, 'firstname': 'Frodo', 'fatmethod': 131,
                 'gender': 0, 'shortname': 'FRO'}
            ]
        })
        resp = self.api.get_user()
        Session.request.assert_called_once_with(
            'GET', 'http://wbsapi.withings.net/user',
            params={'action': 'getbyuserid'})
        self.assertEqual(type(resp), dict)
        assert 'users' in resp
        self.assertEqual(type(resp['users']), list)
        self.assertEqual(len(resp['users']), 1)
        self.assertEqual(resp['users'][0]['firstname'], 'Frodo')
        self.assertEqual(resp['users'][0]['lastname'], 'Baggins')

    def test_get_sleep(self):
        """
        Check that get_sleep fetches the appropriate URL, the response looks
        correct, and the return value is a WithingsSleep object with the
        correct attributes
        """
        body = {
            "series": [{
                "startdate": 1387235398,
                "state": 0,
                "enddate": 1387235758
            }, {
                "startdate": 1387243618,
                "state": 1,
                "enddate": 1387244518
            }],
            "model": 16
        }
        self.mock_request(body)
        resp = self.api.get_sleep()
        Session.request.assert_called_once_with(
            'GET', 'http://wbsapi.withings.net/v2/sleep',
            params={'action': 'get'})
        self.assertEqual(type(resp), WithingsSleep)
        self.assertEqual(resp.model, body['model'])
        self.assertEqual(type(resp.series), list)
        self.assertEqual(len(resp.series), 2)
        self.assertEqual(type(resp.series[0]), WithingsSleepSeries)
        self.assertEqual(time.mktime(resp.series[0].startdate.timetuple()),
                         body['series'][0]['startdate'])
        self.assertEqual(time.mktime(resp.series[0].enddate.timetuple()),
                         body['series'][0]['enddate'])
        self.assertEqual(resp.series[1].state, 1)


    def test_get_activities(self):
        """
        Check that get_activities fetches the appropriate URL, the response
        looks correct, and the return value is a list of WithingsActivity
        objects
        """
        body = {
           "date": "2013-04-10",
           "steps": 6523,
           "distance": 4600,
           "calories": 408.52,
           "elevation": 18.2,
           "soft": 5880,
           "moderate": 1080,
           "intense": 540,
           "timezone": "Europe/Berlin"
        }
        self.mock_request(body)
        resp = self.api.get_activities()
        Session.request.assert_called_once_with(
            'GET', 'http://wbsapi.withings.net/v2/measure',
            params={'action': 'getactivity'})
        self.assertEqual(type(resp), list)
        self.assertEqual(len(resp), 1)
        self.assertEqual(type(resp[0]), WithingsActivity)
        # No need to assert all attributes, that happens elsewhere
        self.assertEqual(resp[0].data, body)

        # Test multiple activities
        new_body = {
            'activities': [
                body, {
                   "date": "2013-04-11",
                   "steps": 223,
                   "distance": 400,
                   "calories": 108.52,
                   "elevation": 1.2,
                   "soft": 160,
                   "moderate": 42,
                   "intense": 21,
                   "timezone": "Europe/Berlin"
                }
            ]
        }
        self.mock_request(new_body)
        resp = self.api.get_activities()
        Session.request.assert_called_once_with(
            'GET', 'http://wbsapi.withings.net/v2/measure',
            params={'action': 'getactivity'})
        self.assertEqual(type(resp), list)
        self.assertEqual(len(resp), 2)
        self.assertEqual(type(resp[0]), WithingsActivity)
        self.assertEqual(type(resp[1]), WithingsActivity)
        self.assertEqual(resp[0].data, new_body['activities'][0])
        self.assertEqual(resp[1].data, new_body['activities'][1])

    def test_get_measures(self):
        """
        Check that get_measures fetches the appriate URL, the response looks
        correct, and the return value is a WithingsMeasures object
        """
        body = {
            'updatetime': 1409596058,
            'measuregrps': [
                {'attrib': 2, 'measures': [
                    {'unit': -1, 'type': 1, 'value': 860}
                ], 'date': 1409361740, 'category': 1, 'grpid': 111111111},
                {'attrib': 2, 'measures': [
                    {'unit': -2, 'type': 4, 'value': 185}
                ], 'date': 1409361740, 'category': 1, 'grpid': 111111112}
            ]
        }
        self.mock_request(body)
        resp = self.api.get_measures()
        Session.request.assert_called_once_with(
            'GET', 'http://wbsapi.withings.net/measure',
            params={'action': 'getmeas'})
        self.assertEqual(type(resp), WithingsMeasures)
        self.assertEqual(len(resp), 2)
        self.assertEqual(type(resp[0]), WithingsMeasureGroup)
        self.assertEqual(resp[0].weight, 86.0)
        self.assertEqual(resp[1].height, 1.85)

        # Test limit=1
        body['measuregrps'].pop()
        self.mock_request(body)
        resp = self.api.get_measures(limit=1)
        Session.request.assert_called_once_with(
            'GET', 'http://wbsapi.withings.net/measure',
            params={'action': 'getmeas', 'limit': 1})
        self.assertEqual(len(resp), 1)
        self.assertEqual(resp[0].weight, 86.0)

    def test_subscribe(self):
        """
        Check that subscribe fetches the right URL and returns the expected
        results
        """
        self.mock_request(None)
        resp = self.api.subscribe('http://www.example.com/', 'fake_comment')
        Session.request.assert_called_once_with(
            'GET', 'http://wbsapi.withings.net/notify',
            params={'action': 'subscribe', 'appli': 1,
                    'comment': 'fake_comment',
                    'callbackurl': 'http://www.example.com/'})
        self.assertEqual(resp, None)

    def test_unsubscribe(self):
        """
        Check that unsubscribe fetches the right URL and returns the expected
        results
        """
        self.mock_request(None)
        resp = self.api.unsubscribe('http://www.example.com/')
        Session.request.assert_called_once_with(
            'GET', 'http://wbsapi.withings.net/notify',
            params={'action': 'revoke', 'appli': 1,
                    'callbackurl': 'http://www.example.com/'})
        self.assertEqual(resp, None)


    def test_is_subscribed(self):
        """
        Check that is_subscribed fetches the right URL and returns the
        expected results
        """
        url = 'http://wbsapi.withings.net/notify'
        params = {
            'callbackurl': 'http://www.example.com/',
            'action': 'get',
            'appli': 1
        }
        self.mock_request({'expires': 2147483647, 'comment': 'fake_comment'})
        resp = self.api.is_subscribed('http://www.example.com/')
        Session.request.assert_called_once_with('GET', url, params=params)
        self.assertEquals(resp, True)

        # Not subscribed
        self.mock_request(None, status=343)
        resp = self.api.is_subscribed('http://www.example.com/')
        Session.request.assert_called_once_with('GET', url, params=params)
        self.assertEquals(resp, False)

    def test_list_subscriptions(self):
        """
        Check that list_subscriptions fetches the right URL and returns the
        expected results
        """
        self.mock_request({'profiles': [
            {'comment': 'fake_comment', 'expires': 2147483647}
        ]})
        resp = self.api.list_subscriptions()
        Session.request.assert_called_once_with(
            'GET', 'http://wbsapi.withings.net/notify',
            params={'action': 'list', 'appli': 1})
        self.assertEqual(type(resp), list)
        self.assertEqual(len(resp), 1)
        self.assertEqual(resp[0]['comment'], 'fake_comment')
        self.assertEqual(resp[0]['expires'], 2147483647)

        # No subscriptions
        self.mock_request({'profiles': []})
        resp = self.api.list_subscriptions()
        Session.request.assert_called_once_with(
            'GET', 'http://wbsapi.withings.net/notify',
            params={'action': 'list', 'appli': 1})
        self.assertEqual(type(resp), list)
        self.assertEqual(len(resp), 0)

    def mock_request(self, body, status=0):
        if self.mock_api:
            json_content = {'status': status}
            if body != None:
                json_content['body'] = body
            response = MagicMock()
            response.content = json.dumps(json_content).encode('utf8')
            Session.request = MagicMock(return_value=response)
def retrieve_and_save_withings_measurements(withings_userid, device_id, start_ts, end_ts, measurement_type_id):
    logger.debug("[medical-compliance] Query Withings API to retrieve measurement: %s" % (locals()))

    ## retrieve the DeviceUsage instance associated with the Withings `withings_userid` and `device_id`
    device_usage_data = store_utils.get_device_usage(store_utils.STORE_ENDPOINT_URI, device = device_id,
                                                     access_info = {"withings_userid" : str(withings_userid)})



    if not device_usage_data:
        logger.error("[medical-compliance] Cannot find any user - device combination with access config for "
                     "withings_userid : %s and device_id : %s" % (str(withings_userid), str(device_id)))
        return

    logger.debug("[medical-compliance] DeviceUsage data for Withings weight measurement: %s" % str(device_usage_data))

    ## get access_info, user and device information from device_usage object
    access_info = device_usage_data['access_info']
    cami_user_id = device_usage_data['user_id']

    credentials = WithingsCredentials(access_token=access_info['withings_oauth_token'],
                                      access_token_secret=access_info['withings_oauth_token_secret'],
                                      consumer_key=access_info['withings_consumer_key'],
                                      consumer_secret=access_info['withings_consumer_secret'],
                                      user_id=int(access_info['withings_userid']))

    client = WithingsApi(credentials)
    req_params = {
        'startdate': start_ts,
        'enddate': end_ts,
        'meastype': int(measurement_type_id)
    }
    response = client.request('measure', 'getmeas', req_params)

    logger.debug(
        "[medical-compliance] Got the following Withings response for user_id %s and req params %s: %s" %
        (withings_userid, req_params, response)
    )

    measures = WithingsMeasures(response)
    timezoneStr = response['timezone']
    measurement_type = WithingsMeasurement.get_measure_type_by_id(int(measurement_type_id))

    for m in measures:
        ## Store WithingsMeasurement for error inspection
        meas = WithingsMeasurement(
            withings_user_id = int(withings_userid),
            type=measurement_type_id,
            retrieval_type=m.attrib,
            measurement_unit=WithingsMeasurement.MEASUREMENT_SI_UNIT[measurement_type],
            timestamp=m.data['date'],
            timezone=timezoneStr,
            value=m.__getattribute__(measurement_type))

        logger.debug("[medical-compliance] Saving Withings measurement in cami DB: %s" % (meas))
        meas.save()

        logger.debug("[medical-compliance] Sending Withings weight measurement for processing: %s" % (meas))
        process_weight_measurement.delay(cami_user_id,              # end user id
                                         device_id,                 # device id
                                         "weight",                  # measurement_type
                                         meas.measurement_unit,
                                         meas.timestamp,
                                         meas.timezone,
                                         meas.value)
class TestWithingsApi(unittest.TestCase):
    def setUp(self):
        self.mock_api = True
        if self.mock_api:
            self.creds = WithingsCredentials()
        else:
            config = ConfigParser.ConfigParser()
            config.read('withings.conf')
            self.creds = WithingsCredentials(
                consumer_key=config.get('withings', 'consumer_key'),
                consumer_secret=config.get('withings', 'consumer_secret'),
                access_token=config.get('withings', 'access_token'),
                access_token_secret=config.get('withings',
                                               'access_token_secret'),
                user_id=config.get('withings', 'user_id'))
        self.api = WithingsApi(self.creds)

    def test_attributes(self):
        """ Make sure the WithingsApi objects have the right attributes """
        assert hasattr(WithingsApi, 'URL')
        creds = WithingsCredentials(user_id='FAKEID')
        api = WithingsApi(creds)
        assert hasattr(api, 'credentials')
        assert hasattr(api, 'oauth')
        assert hasattr(api, 'client')

    def test_attribute_defaults(self):
        """
        Make sure WithingsApi object attributes have the correct defaults
        """
        self.assertEqual(WithingsApi.URL, 'http://wbsapi.withings.net')
        creds = WithingsCredentials(user_id='FAKEID')
        api = WithingsApi(creds)
        self.assertEqual(api.credentials, creds)
        self.assertEqual(api.client.auth, api.oauth)
        self.assertEqual(api.client.params, {'userid': creds.user_id})

    def test_request(self):
        """
        Make sure the request method builds the proper URI and returns the
        request body as a python dict.
        """
        self.mock_request({})
        resp = self.api.request('fake_service', 'fake_action')
        Session.request.assert_called_once_with(
            'GET', 'http://wbsapi.withings.net/fake_service',
            params={'action': 'fake_action'})
        self.assertEqual(resp, {})

    def test_request_params(self):
        """
        Check that the request method passes along extra params and works
        with different HTTP methods
        """
        self.mock_request({})
        resp = self.api.request('user', 'getbyuserid', params={'p2': 'p2'},
                                method='POST')
        Session.request.assert_called_once_with(
            'POST', 'http://wbsapi.withings.net/user',
            params={'p2': 'p2', 'action': 'getbyuserid'})
        self.assertEqual(resp, {})

    def test_request_error(self):
        """ Check that requests raises an exception when there is an error """
        self.mock_request('', status=1)
        self.assertRaises(Exception, self.api.request, ('user', 'getbyuserid'))

    def test_get_user(self):
        """ Check that the get_user method fetches the right URL """
        self.mock_request({
            'users': [
                {'id': 1111111, 'birthdate': 364305600, 'lastname': 'Baggins',
                 'ispublic': 255, 'firstname': 'Frodo', 'fatmethod': 131,
                 'gender': 0, 'shortname': 'FRO'}
            ]
        })
        resp = self.api.get_user()
        Session.request.assert_called_once_with(
            'GET', 'http://wbsapi.withings.net/user',
            params={'action': 'getbyuserid'})
        self.assertEqual(type(resp), dict)
        assert 'users' in resp
        self.assertEqual(type(resp['users']), list)
        self.assertEqual(len(resp['users']), 1)
        self.assertEqual(resp['users'][0]['firstname'], 'Frodo')
        self.assertEqual(resp['users'][0]['lastname'], 'Baggins')

    def test_get_sleep(self):
        """
        Check that get_sleep fetches the appropriate URL, the response looks
        correct, and the return value is a WithingsSleep object with the
        correct attributes
        """
        body = {
            "series": [{
                "startdate": 1387235398,
                "state": 0,
                "enddate": 1387235758
            }, {
                "startdate": 1387243618,
                "state": 1,
                "enddate": 1387244518
            }],
            "model": 16
        }
        self.mock_request(body)
        resp = self.api.get_sleep()
        Session.request.assert_called_once_with(
            'GET', 'http://wbsapi.withings.net/v2/sleep',
            params={'action': 'get'})
        self.assertEqual(type(resp), WithingsSleep)
        self.assertEqual(resp.model, body['model'])
        self.assertEqual(type(resp.series), list)
        self.assertEqual(len(resp.series), 2)
        self.assertEqual(type(resp.series[0]), WithingsSleepSeries)
        self.assertEqual(resp.series[0].startdate.timestamp,
                         body['series'][0]['startdate'])
        self.assertEqual(resp.series[0].enddate.timestamp,
                         body['series'][0]['enddate'])
        self.assertEqual(resp.series[1].state, 1)


    def test_get_activities(self):
        """
        Check that get_activities fetches the appropriate URL, the response
        looks correct, and the return value is a list of WithingsActivity
        objects
        """
        body = {
           "date": "2013-04-10",
           "steps": 6523,
           "distance": 4600,
           "calories": 408.52,
           "elevation": 18.2,
           "soft": 5880,
           "moderate": 1080,
           "intense": 540,
           "timezone": "Europe/Berlin"
        }
        self.mock_request(body)
        resp = self.api.get_activities()
        Session.request.assert_called_once_with(
            'GET', 'http://wbsapi.withings.net/v2/measure',
            params={'action': 'getactivity'})
        self.assertEqual(type(resp), list)
        self.assertEqual(len(resp), 1)
        self.assertEqual(type(resp[0]), WithingsActivity)
        # No need to assert all attributes, that happens elsewhere
        self.assertEqual(resp[0].data, body)

        # Test multiple activities
        new_body = {
            'activities': [
                body, {
                   "date": "2013-04-11",
                   "steps": 223,
                   "distance": 400,
                   "calories": 108.52,
                   "elevation": 1.2,
                   "soft": 160,
                   "moderate": 42,
                   "intense": 21,
                   "timezone": "Europe/Berlin"
                }
            ]
        }
        self.mock_request(new_body)
        resp = self.api.get_activities()
        Session.request.assert_called_once_with(
            'GET', 'http://wbsapi.withings.net/v2/measure',
            params={'action': 'getactivity'})
        self.assertEqual(type(resp), list)
        self.assertEqual(len(resp), 2)
        self.assertEqual(type(resp[0]), WithingsActivity)
        self.assertEqual(type(resp[1]), WithingsActivity)
        self.assertEqual(resp[0].data, new_body['activities'][0])
        self.assertEqual(resp[1].data, new_body['activities'][1])

    def test_get_measures(self):
        """
        Check that get_measures fetches the appriate URL, the response looks
        correct, and the return value is a WithingsMeasures object
        """
        body = {
            'updatetime': 1409596058,
            'measuregrps': [
                {'attrib': 2, 'measures': [
                    {'unit': -1, 'type': 1, 'value': 860}
                ], 'date': 1409361740, 'category': 1, 'grpid': 111111111},
                {'attrib': 2, 'measures': [
                    {'unit': -2, 'type': 4, 'value': 185}
                ], 'date': 1409361740, 'category': 1, 'grpid': 111111112}
            ]
        }
        self.mock_request(body)
        resp = self.api.get_measures()
        Session.request.assert_called_once_with(
            'GET', 'http://wbsapi.withings.net/measure',
            params={'action': 'getmeas'})
        self.assertEqual(type(resp), WithingsMeasures)
        self.assertEqual(len(resp), 2)
        self.assertEqual(type(resp[0]), WithingsMeasureGroup)
        self.assertEqual(resp[0].weight, 86.0)
        self.assertEqual(resp[1].height, 1.85)

        # Test limit=1
        body['measuregrps'].pop()
        self.mock_request(body)
        resp = self.api.get_measures(limit=1)
        Session.request.assert_called_once_with(
            'GET', 'http://wbsapi.withings.net/measure',
            params={'action': 'getmeas', 'limit': 1})
        self.assertEqual(len(resp), 1)
        self.assertEqual(resp[0].weight, 86.0)

    def test_subscribe(self):
        """
        Check that subscribe fetches the right URL and returns the expected
        results
        """
        # Unspecified appli
        self.mock_request(None)
        resp = self.api.subscribe('http://www.example.com/', 'fake_comment')
        Session.request.assert_called_once_with(
            'GET', 'http://wbsapi.withings.net/notify',
            params={'action': 'subscribe', 'comment': 'fake_comment',
                    'callbackurl': 'http://www.example.com/'})
        self.assertEqual(resp, None)

        # appli=1
        self.mock_request(None)
        resp = self.api.subscribe('http://www.example.com/', 'fake_comment',
                                  appli=1)
        Session.request.assert_called_once_with(
            'GET', 'http://wbsapi.withings.net/notify',
            params={'action': 'subscribe', 'appli': 1,
                    'comment': 'fake_comment',
                    'callbackurl': 'http://www.example.com/'})
        self.assertEqual(resp, None)

    def test_unsubscribe(self):
        """
        Check that unsubscribe fetches the right URL and returns the expected
        results
        """
        # Unspecified appli
        self.mock_request(None)
        resp = self.api.unsubscribe('http://www.example.com/')
        Session.request.assert_called_once_with(
            'GET', 'http://wbsapi.withings.net/notify',
            params={'action': 'revoke',
                    'callbackurl': 'http://www.example.com/'})
        self.assertEqual(resp, None)

        # appli=1
        self.mock_request(None)
        resp = self.api.unsubscribe('http://www.example.com/', appli=1)
        Session.request.assert_called_once_with(
            'GET', 'http://wbsapi.withings.net/notify',
            params={'action': 'revoke', 'appli': 1,
                    'callbackurl': 'http://www.example.com/'})
        self.assertEqual(resp, None)


    def test_is_subscribed(self):
        """
        Check that is_subscribed fetches the right URL and returns the
        expected results
        """
        url = 'http://wbsapi.withings.net/notify'
        params = {
            'callbackurl': 'http://www.example.com/',
            'action': 'get',
            'appli': 1
        }
        self.mock_request({'expires': 2147483647, 'comment': 'fake_comment'})
        resp = self.api.is_subscribed('http://www.example.com/')
        Session.request.assert_called_once_with('GET', url, params=params)
        self.assertEquals(resp, True)

        # Not subscribed
        self.mock_request(None, status=343)
        resp = self.api.is_subscribed('http://www.example.com/')
        Session.request.assert_called_once_with('GET', url, params=params)
        self.assertEquals(resp, False)

    def test_list_subscriptions(self):
        """
        Check that list_subscriptions fetches the right URL and returns the
        expected results
        """
        self.mock_request({'profiles': [
            {'comment': 'fake_comment', 'expires': 2147483647}
        ]})
        resp = self.api.list_subscriptions()
        Session.request.assert_called_once_with(
            'GET', 'http://wbsapi.withings.net/notify',
            params={'action': 'list', 'appli': 1})
        self.assertEqual(type(resp), list)
        self.assertEqual(len(resp), 1)
        self.assertEqual(resp[0]['comment'], 'fake_comment')
        self.assertEqual(resp[0]['expires'], 2147483647)

        # No subscriptions
        self.mock_request({'profiles': []})
        resp = self.api.list_subscriptions()
        Session.request.assert_called_once_with(
            'GET', 'http://wbsapi.withings.net/notify',
            params={'action': 'list', 'appli': 1})
        self.assertEqual(type(resp), list)
        self.assertEqual(len(resp), 0)

    def mock_request(self, body, status=0):
        if self.mock_api:
            json_content = {'status': status}
            if body != None:
                json_content['body'] = body
            response = MagicMock()
            response.content = json.dumps(json_content).encode('utf8')
            Session.request = MagicMock(return_value=response)