Why Gemfury? Push, build, and install  RubyGems npm packages Python packages Maven artifacts PHP packages Go Modules Debian packages RPM packages NuGet packages

Repository URL to install this package:

Details    
healthgraph-api / healthgraph / resources.py
Size: Mime:
"""Python Client Library for Health Graph API (http://developer.runkeeper.com/healthgraph). 

The API is used for accessing RunKeeper (http://runkeeper.com) for retrieving, 
updating, deleting and uploading Fitness Activity and Health Measurements Information.

This module contains the resource definitions for retrieving, updating, deleting 
and uploading Fitness Activity and Health Measurements information.

"""

import urllib
try:
    import urlparse  # Python 2
except:
    import urllib.parse as urlparse  # Python 3
import inspect
from collections import namedtuple, MutableMapping
import healthgraph.settings
import healthgraph.content_types
import healthgraph.sessionmgr
from .parser import (parse_resource_dict, 
                     parse_bool, 
                     parse_distance, parse_distance_km, 
                     parse_date, parse_datetime, 
                     parse_date_param)


__author__ = "Ali Onur Uyar"
__copyright__ = "Copyright 2012, Ali Onur Uyar"
__credits__ = []
__license__ = "GPL"
__version__ = "0.3.0"
__email__ = "aouyar at gmail.com"
__status__ = "Development"

    
class PersonalRecordType:
    """Personal record types."""    
    
    THIS_WEEK = 'THIS_WEEK'
    THIS_MONTH = 'THIS_MONTH'
    LAST_WEEK = 'LAST_WEEK'
    LAST_MONTH = 'LAST_MONTH'
    OVERALL = 'OVERALL'
    BEST_ACTIVITY = 'BEST_ACTIVITY'
    BEST_WEEK = 'BEST_WEEK'
    BEST_MONTH = 'BEST_MONTH'


class ResourceLink(namedtuple('ResourceLink', ('clsname', 'resource'))):
    pass


class PropResourceLink(object):
    
    def __init__(self, clsname):
        self._clsname = clsname
    
    def __call__(self, resource=None):
        return ResourceLink(self._clsname, resource)
    

class ContainerMixin(MutableMapping):
    
    def __getitem__(self, k):
        return self._prop_dict[k]
    
    def __setitem__(self, k, v):
        self._prop_dict[k] = v
        
    def __delitem__(self, k):
        del self._prop_dict[k]
        
    def __len__(self):
        return len(self._prop_dict)
    
    def __iter__(self):
        return iter(self._prop_dict)


class APIobject(object):
    _prop_defs = None
    _prop_main = None
    
    def __init__(self, session=None):
        self._prop_dict = {}
        self._resource = None
        if session is not None:
            self._session = session
        else:
            self._session = healthgraph.sessionmgr.get_session()
            
    def _get_resource_data(self, resource, content_type, params=None):
        resp = self._session.get(resource, content_type, params)
        return resp.json() # TODO - Error Checking
    
    def _get_linked_resource(self, link, cls_override=None, **kwargs):
        if link is not None:
            if cls_override is None:
                cls = globals().get(link.clsname)
            else:
                cls = globals().get(cls_override)
            if inspect.isclass(cls) and issubclass(cls, BaseResource):
                return cls(link.resource, session=self._session, **kwargs)
            else:
                pass
        else:
            return None
        
    def __str__(self):
        if self._resource is not None:
            prop_strs = ["resource=%s" % self._resource,]
        else:
            prop_strs = []
        for k in self._prop_main:
            if self._prop_dict[k] is not None:
                prop_strs.append("%s=%s" % (k, self._prop_dict[k]))
        return "%s(%s)" % (self.__class__.__name__,
                           ', '.join(prop_strs))


class BaseResource(APIobject):
    
    _content_type = None
    
    def __init__(self, resource = None, session=None, params=None):
        super(BaseResource,self).__init__(session=session)
        self._resource = resource
        self.load(params)
            
    @property
    def resource(self):
        return self._resource
    
    @property
    def content_type(self):
        return self._content_type
    
    def load(self, params=None):
        if self._resource is not None:
            data = self._get_resource_data(self._resource, self._content_type, 
                                            params)
            self._prop_dict = self._parse_data(data)
            
    def _parse_data(self, data):
        return parse_resource_dict(self._prop_defs, data)


class ResourceItem(APIobject, ContainerMixin):

    def __init__(self, data=None, session=None):
        super(ResourceItem, self).__init__(session=session)
        if data is not None:
            self._prop_dict = parse_resource_dict(self._prop_defs, data)
        else:
            self._prop_dict = {}
            

class ResourceArray(list):
    
    def __init__(self, data=None):
        super(ResourceArray, self).__init__(data or [])
    
    def __str__(self):
        cnt = len(self)
        if cnt > 0:
            cont_str = 'count: %s' % cnt
        else:
            cont_str = '[]'
        return "%s(%s)" % (self.__class__.__name__,
                           cont_str)
    
    __repr__ = __str__


class ArrayDistance(ResourceArray):
    def __init__(self, data=None):
        super(ArrayDistance, self).__init__(data)


class ArrayHeartRate(ResourceArray):
    def __init__(self, data=None):
        super(ArrayHeartRate, self).__init__(data)


class ArrayCalories(ResourceArray):
    def __init__(self, data=None):
        super(ArrayCalories, self).__init__(data)


class ArrayPath(ResourceArray):
    def __init__(self, data=None):
        super(ArrayPath, self).__init__(data)


class ArrayImages(ResourceArray):
    def __init__(self, data=None):
        super(ArrayImages, self).__init__(data)


class ArrayComments(ResourceArray):
    def __init__(self, data=None):
        super(ArrayComments, self).__init__(data)

        
class Resource(BaseResource, ContainerMixin):
    
    def __init__(self, resource = None, params=None, session=None):
        super(Resource, self).__init__(resource, params=params, session=session)


class ResourceFeedIter(BaseResource):
    
    _prop_defs = {'size': None,
                  'items': None,
                  'previous': PropResourceLink('ResourceFeedIter'),
                  'next': PropResourceLink('ResourceFeedIter')}
    _item_cls = None
    _prop_main = ('size',)
    
    def __init__(self, resource, 
                 date_min=None, date_max=None, 
                 mod_date_min=None, mod_date_max=None,
                 descending=True,
                 session=None):
        func_params = locals()
        params = {'pageSize': healthgraph.settings.DEFAULT_PAGE_SIZE,}
        for func_key, api_key in (('date_min', 'noEarlierThan'),
                                  ('date_max', 'noLaterThan'),
                                  ('mod_date_min', 'modifiedNoEarlierThan'),
                                  ('mod_date_max', 'modifiedNoLater'),):
            val = parse_date_param(func_params[func_key])
            if val is not None:
                params[api_key] = val
        super(ResourceFeedIter, self).__init__(resource, params=params,
                                               session=session)
        self._descending = descending
        if descending:
            self._iter = iter(self._prop_dict['items'])
        else:
            self._last_page()
            self._iter = reversed(self._prop_dict['items'])
            
        
    def count(self):
        return self._prop_dict['size']
             
    def __iter__(self):
        if self._item_cls is not None:
            return self
        else:
            pass
    
    def next(self):
        try:
            item = self._iter.next()
        except StopIteration:
            if self._descending and self._next_page():
                self._iter = iter(self._prop_dict['items'])
                item = self._iter.next()
            elif not self._descending and self._prev_page():
                self._iter = reversed(self._prop_dict['items'])
                item = self._iter.next()
            else:
                raise StopIteration
        return self._item_cls(item, self._session)
                
    def _prev_page(self):
        link = self._prop_dict.get('previous')
        if link is not None:
            self._resource = link.resource
            self.load()
            return True
        else:
            return False
    
    def _next_page(self):
        link = self._prop_dict.get('next')
        if link is not None:
            self._resource = link.resource
            self.load()
            return True
        else:
            return False
        
    def _last_page(self):
        link = self._prop_dict.get('next')
        if link is not None:
            size = self.count()
            last_page = size / healthgraph.settings.DEFAULT_PAGE_SIZE
            if size % healthgraph.settings.DEFAULT_PAGE_SIZE == 0:
                last_page -= 1
            if last_page > 0:
                self._resource, qs = urllib.splitquery(link.resource)
                params = urlparse.parse_qs(qs)
                params['page'] = last_page
                self.load(params=params)
                return True
            else:
                return False
        else:
            return False


class FeedItem(ResourceItem):
    
    def __init__(self, data, session=None):
        super(FeedItem, self).__init__(data, session=session)


class User(Resource):
    
    _content_type = healthgraph.content_types.USER
    _prop_defs = {'userID': None,
                  'profile': PropResourceLink('Profile'),
                  'settings': PropResourceLink('Settings'),
                  'fitness_activities': PropResourceLink('FitnessActivityIter'),
                  'strength_training_activities': PropResourceLink('StrengthActivityIter'),
                  'background_activities': PropResourceLink('BackgroundActivityIter'),
                  'sleep': PropResourceLink('SleepMeasurementIter'),
                  'nutrition': PropResourceLink('NutritionMeasurementIter'),
                  'weight': PropResourceLink('WeightMeasurementIter'),
                  'general_measurements': PropResourceLink('GeneralMeasurementIter'),
                  'diabetes': PropResourceLink('DiabetesMeasurementIter'),
                  'records': PropResourceLink('PersonalRecords'),
                  'team': PropResourceLink('FriendIter'),                
                  }
    _prop_main = ('userID',)
    
    def __init__(self, session=None):
        super(User, self).__init__(
            healthgraph.settings.USER_RESOURCE, session=session)
    
    def get_profile(self):
        return self._get_linked_resource(self._prop_dict['profile'])
        
    def get_settings(self):
        return self._get_linked_resource(self._prop_dict['settings'])
    
    def get_records(self):
        return self._get_linked_resource(self._prop_dict['records'])
    
    def get_fitness_activity_iter(self, 
                                  date_min=None, date_max=None, 
                                  mod_date_min=None, mod_date_max=None,
                                  descending=True):
        return self._get_linked_resource(self._prop_dict['fitness_activities'],
                                         date_min=date_min, 
                                         date_max=date_max,
                                         mod_date_min=mod_date_min,
                                         mod_date_max=mod_date_max,
                                         descending=descending)
    
    def get_strength_activity_iter(self,
                                   date_min=None, date_max=None, 
                                   mod_date_min=None, mod_date_max=None,
                                   descending=True):
        return self._get_linked_resource(self._prop_dict['strength_training_activities'],
                                         date_min=date_min, 
                                         date_max=date_max,
                                         mod_date_min=mod_date_min,
                                         mod_date_max=mod_date_max,
                                         descending=descending)
    
    def get_weight_measurement_iter(self,
                                    date_min=None, date_max=None, 
                                    mod_date_min=None, mod_date_max=None,
                                    descending=True):
        return self._get_linked_resource(self._prop_dict['weight'],
                                         date_min=date_min, 
                                         date_max=date_max,
                                         mod_date_min=mod_date_min,
                                         mod_date_max=mod_date_max,
                                         descending=descending)
    
    def get_sleep_measurement_iter(self,
                                    date_min=None, date_max=None, 
                                    mod_date_min=None, mod_date_max=None,
                                    descending=True):
        return self._get_linked_resource(self._prop_dict['sleep'],
                                         date_min=date_min, 
                                         date_max=date_max,
                                         mod_date_min=mod_date_min,
                                         mod_date_max=mod_date_max,
                                         descending=descending)

class Profile(Resource):
    
    _content_type = healthgraph.content_types.PROFILE
    _prop_defs = {'name': None,
                  'location': None,
                  'athlete_type': None,
                  'gender': None,
                  'birthday': parse_date,
                  'elite': parse_bool,
                  'profile': None,
                  'small_picture': None,
                  'normal_picture': None,
                  'medium_picture': None,
                  'large_picture': None,
                  }
    _prop_main = ('name', 'gender', 'birthday',)
    
    def __init__(self, resource, session=None):
        super(Profile, self).__init__(resource, session=session)


class Settings(Resource):
    
    _content_type = healthgraph.content_types.SETTINGS
    _prop_defs = {'facebook_connected': parse_bool,
                  'twitter_connected': parse_bool,
                  'foursquare_connected': parse_bool,
                  'share_fitness_activities': None,
                  'share_map': None,
                  'post_fitness_activity_facebook': parse_bool,
                  'post_fitness_activity_twitter': parse_bool,
                  'post_live_fitness_activity_facebook': parse_bool,
                  'post_live_fitness_activity_twitter': parse_bool,
                  'share_background_activities': None,
                  'post_background_activity_facebook': parse_bool,
                  'post_background_activity_twitter': parse_bool,
                  'share_sleep': None,
                  'post_sleep_facebook': parse_bool,
                  'post_sleep_twitter': parse_bool,
                  'share_nutrition': None,
                  'post_nutrition_facebook': parse_bool,
                  'post_nutrition_twitter': parse_bool,
                  'share_weight': None,
                  'post_weight_facebook': parse_bool,
                  'post_weight_twitter': parse_bool,
                  'share_general_measurements': None,
                  'post_general_measurements_facebook': parse_bool,
                  'post_general_measurements_twitter': parse_bool,
                  'share_diabetes': None,
                  'post_diabetes_facebook': parse_bool,
                  'post_diabetes_twitter': parse_bool,
                  'distance_units': None,
                  'weight_units': None,
                  'first_day_of_week': None,
                  }
    
    def __init__(self, resource, session=None):
        super(Settings, self).__init__(resource, session=session)
      

class PersonalRecords(Resource):
    
    _content_type = healthgraph.content_types.PERSONAL_RECORDS

    def __init__(self, resource, session=None):
        super(PersonalRecords, self).__init__(resource, session=session)

    def _parse_data(self, data):
        prop_dict = {'totals': {}, 'bests': {},}
        for actrecs in data:
            totals = {}
            bests = {}
            overall = 0
            act_type = actrecs['activity_type']
            for stats in actrecs['stats']:
                stat_type = stats['stat_type']
                stat_date = stats.get('date')
                if stat_type == 'OVERALL':
                    stat_dist = parse_distance(stats['value'])
                    overall = stat_dist
                else:
                    stat_dist = parse_distance_km(stats['value'])
                if stat_date is None:
                    totals[stat_type] = stat_dist
                else:
                    bests[stat_type] = (stat_date, stat_dist)
            if overall > 0:
                if len(totals) > 0:
                    prop_dict['totals'][act_type] = totals
                if len(bests) > 0:
                    prop_dict['bests'][act_type] = bests
        return prop_dict
    
    def get_activity_types(self):
        return self._prop_dict.keys()
    
    def get_totals(self):
        return self._prop_dict['totals']
    
    def get_bests(self):
        return self._prop_dict['bests']
    
    def get_activity_totals(self, activity_type):
        try:
            return self._prop_dict['totals'][activity_type]
        except KeyError:
            return None
    
    def get_activity_bests(self, activity_type):
        try:
            return self._prop_dict['bests'][activity_type]
        except KeyError:
            return None


class FitnessActivity(Resource):
    
    _content_type = healthgraph.content_types.FITNESS_ACTIVITY
    _prop_defs = {'uri': PropResourceLink('FitnessActivity'),
                  'userID': None,
                  'type': None,
                  'secondary_type': None,
                  'equipment': None,
                  'start_time': parse_datetime,
                  'total_distance': parse_distance,
                  'distance': ArrayDistance,
                  'duration': None,
                  'average_heart_rate': None,
                  'heart_rate': ArrayHeartRate,
                  'total_calories': None,
                  'calories': ArrayCalories,
                  'climb': None,
                  'notes': None,
                  'is_live': parse_bool,
                  'path': ArrayPath,
                  'images': ArrayImages,
                  'source': None,
                  'activity': None,
                  'comments': PropResourceLink('CommentThread'),
                  'previous': PropResourceLink('FitnessActivity'),
                  'next': PropResourceLink('FitnessActivity'),
                  'nearest_teammate_fitness_activities': None,
                  'nearest_strength_training_activity': None,
                  'nearest_teammate_strength_training_activities': None,
                  'nearest_background_activity': None,
                  'nearest_teammate_background_activities': None,
                  'nearest_sleep': None,
                  'nearest_teammate_sleep': None,
                  'nearest_nutrition': None,
                  'nearest_teammate_nutrition': None,
                  'nearest_weight': None,
                  'nearest_teammate_weight': None,
                  'nearest_general_measurement': None,
                  'nearest_teammate_general_measurements': None,
                  'nearest_diabetes': None,
                  'nearest_teammate_diabetes': None,
                  }
    
    _prop_main = ('type', 'start_time',)
    
    def __init__(self, resource, session=None):
        super(FitnessActivity, self).__init__(resource, session=session)

    def get_comment_thread(self):
        return self._get_linked_resource(self._prop_dict['comments'])

    def get_prev_activity(self):
        return self._get_linked_resource(self._prop_dict['previous'])
    
    def get_next_activity(self):
        return self._get_linked_resource(self._prop_dict['next'])
    

class FitnessActivitySummary(Resource):
    
    _content_type = healthgraph.content_types.FITNESS_ACTIVITY_SUMMARY
    _prop_defs = {'uri': PropResourceLink('FitnessActivity'),
                  'userID': None,
                  'type': None,
                  'secondary_type': None,
                  'equipment': None,
                  'start_time': parse_datetime,
                  'total_distance': parse_distance,
                  'duration': None,
                  'average_heart_rate': None,
                  'total_calories': None,
                  'climb': None,
                  'notes': None,
                  'is_live': parse_bool,
                  'source': None,
                  'activity': None,
                  }
    _prop_main = ('type', 'start_time',)
    
    def __init__(self, resource, session=None):
        super(FitnessActivitySummary, self).__init__(resource, session=session)
        
    def get_activity_detail(self):
        return self._get_linked_resource(self._prop_dict['uri'])
    

class FitnessActivityFeedItem(FeedItem):
    
    _prop_defs = {'start_time': parse_datetime,
                  'type': None,
                  'duration': None,
                  'total_distance': parse_distance,
                  'total_calories': None,
                  'has_path': parse_bool,
                  'entry_mode': None,
                  'uri': PropResourceLink('FitnessActivity'),
                  }
    _prop_main = ('type', 'start_time',)
    
    def __init__(self, data, session=None):
        super(FitnessActivityFeedItem, self).__init__(data, session=session)
        
    def get_activity_detail(self):
        return self._get_linked_resource(self._prop_dict['uri'])
    
    def get_activity_summary(self):
        return self._get_linked_resource(self._prop_dict['uri'], 'FitnessActivitySummary')


class FitnessActivityIter(ResourceFeedIter):
    
    _content_type = healthgraph.content_types.FITNESS_ACTIVITY_FEED
    _item_cls = FitnessActivityFeedItem
    
    def __init__(self, resource, 
                 date_min=None, date_max=None, 
                 mod_date_min=None, mod_date_max=None,
                 descending=True,
                 session=None):
        super(FitnessActivityIter, self).__init__(resource,
                                                  date_min=date_min,
                                                  date_max=date_max,
                                                  mod_date_min=mod_date_min,
                                                  mod_date_max=mod_date_max,
                                                  descending=descending,
                                                  session=session)


class StrengthActivityFeedItem(FeedItem):
    
    _prop_defs = {'start_time': parse_datetime,
                  'uri': PropResourceLink('StrengthActivity')}
    _prop_main = ('start_time',)
    
    def __init__(self, data, session=None):
        super(StrengthActivityFeedItem, self).__init__(data, session=session)


class StrengthActivityIter(ResourceFeedIter):
    
    _content_type = healthgraph.content_types.STRENGTH_ACTIVITY_FEED
    _item_cls = StrengthActivityFeedItem
    
    def __init__(self, resource, 
                 date_min=None, date_max=None, 
                 mod_date_min=None, mod_date_max=None,
                 descending=True,
                 session=None):
        super(StrengthActivityIter, self).__init__(resource, 
                                                   date_min=date_min,
                                                   date_max=date_max,
                                                   mod_date_min=mod_date_min,
                                                   mod_date_max=mod_date_max,
                                                   descending=descending, 
                                                   session=session)


class WeightMeasurementFeedItem(FeedItem):

    _prop_defs = {'uri': PropResourceLink('WeightMeasurement'),
                  'timestamp': parse_datetime,
                  'weight': float,
                  'height': None,
                  'free_mass': None,
                  'fat_percent': float,
                  'mass_weight': float,
                  'bmi': float}
    _prop_main = ('timestamp',)

    def __init__(self, data, session=None):
        super(WeightMeasurementFeedItem, self).__init__(data, session=session)


class WeightMeasurementIter(ResourceFeedIter):

    _content_type = healthgraph.content_types.WEIGHT_MEASUREMENT_FEED
    _item_cls = WeightMeasurementFeedItem

    def __init__(self, resource, 
                 date_min=None, date_max=None,
                 mod_date_min=None, mod_date_max=None,
                 descending=True,
                 session=None):
        super(WeightMeasurementIter, self).__init__(resource,
                                                    date_min=date_min,
                                                    date_max=date_max,
                                                    mod_date_min=mod_date_min,
                                                    mod_date_max=mod_date_max,
                                                    descending=descending,
                                                    session=session)


class SleepMeasurementFeedItem(FeedItem):

    _prop_defs = {'uri': PropResourceLink('SleepMeasurement'),
                  'timestamp': parse_datetime,
                  'total_sleep': float,
                  'rem': float,
                  'deep': float,
                  'light': float,
                  'awake': float,
                  'times_woken': float,
                  'source': None,
                  'previous': None,
                  'next': None,
                  # nearest_* fields for other types are omitted for now
                  }
    _prop_main = ('timestamp',)

    def __init__(self, data, session=None):
        super(SleepMeasurementFeedItem, self).__init__(data, session=session)


class SleepMeasurementIter(ResourceFeedIter):
    
    _content_type = healthgraph.content_types.SLEEP_MEASUREMENT_FEED
    _item_cls = SleepMeasurementFeedItem
    
    def __init__(self, resource, 
                 date_min=None, date_max=None, 
                 mod_date_min=None, mod_date_max=None,
                 descending=True,
                 session=None):
        super(SleepMeasurementIter, self).__init__(resource, 
                                                    date_min=date_min,
                                                    date_max=date_max,
                                                    mod_date_min=mod_date_min,
                                                    mod_date_max=mod_date_max,
                                                    descending=descending, 
                                                    session=session)


class CommentThread(Resource):
    
    _content_type = healthgraph.content_types.COMMENT_THREAD
    _prop_defs = {'uri': None,
                  'userID': None,
                  'comments': ArrayComments,
                  }
    _prop_main = ('uri',)
    
    def __init__(self, resource, session=None):
        super(CommentThread, self).__init__(resource, session=session)