Esempio n. 1
0
class FitBitHelper(object):
    """ Wraps around Fitbit api and contains useful methods for interacting with FitBit data """
    def __init__(self, user_id):
        self.user_id = user_id

        # look up the user in the database
        self.user = current.db(
            current.db.auth_user.id == user_id).select().first()
        if self.user is None:
            raise ValueError(
                "User {user_id} wasn't found in the database!".format(
                    user_id=user_id))

        self.fitbit = Fitbit(current.client_id,
                             current.client_secret,
                             access_token=self.user.token['access_token'],
                             refresh_token=self.user.token['refresh_token'],
                             expires_at=self.user.token['expires_at'],
                             refresh_cb=_update_token)

    def get_sleep_history(self, period=datetime.timedelta(days=14)):
        """
        Gets a slice of sleep history ending with today

        The default slice is two weeks.
        """
        # get a two week sleep history
        today = datetime.datetime.today()
        old = today - datetime.timedelta(days=14)
        return self.fitbit.time_series("sleep",
                                       user_id=self.user.username,
                                       base_date=old,
                                       end_date=today)
Esempio n. 2
0
 def steps(client: FitbitApi) -> t.Optional[int]:
     data = client.time_series(resource="activities/steps", period="1w")
     entries = data["activities-steps"]
     yesterday = next(
         (
             entry
             for entry in entries
             if pendulum.parse(entry["dateTime"]).day == pendulum.yesterday().day
         ),
         None,
     )
     return int(yesterday["value"]) if yesterday else None
Esempio n. 3
0
def run(args):
    client_id, client_secret, auth_data = get_fitbit_credentials()
    if client_id is None:
        return 1

    fitbit_api = Fitbit(client_id=client_id,
                        client_secret=client_secret,
                        access_token=auth_data['access_token'],
                        refresh_token=auth_data['refresh_token'],
                        expires_at=auth_data['expires_at'])

    try:
        if args.start_date:
            try:
                start_date = dt.datetime.strptime(args.start_date, DATE_FORMAT)
            except ValueError:
                log.error("'{}' does not look like a date in format {}".format(
                    args.start_date, DATE_FORMAT))
                return 1
            end_date = dt.datetime.now()
            hr_data = fitbit_api.time_series('heart',
                                             base_date=start_date,
                                             end_date=end_date)
        else:
            hr_data = fitbit_api.time_series('heart', period=args.period)
    except TokenExpiredError as e:
        log.exception(e)
        log.error('You need to reauthenticate with fitbit oauth')
        return 1
    except TokenUpdated as e:
        log.exception(e)
        log.error('You need to reauthenticate with fitbit oauth')
        return 1

    print(json.dumps(hr_data, indent=4))

    return 0
Esempio n. 4
0
def maybe_sync_data_from_fitbit():

    r = db(db.fitbit_user_t.user_email == auth.user.email).select(db.fitbit_user_t.ALL)

    if len(r) == 0:
        return False
    else:
        if time.time() > r[0].expires_at:
            oauth = OAuth2Session(client_id)

            token = oauth.refresh_token(
                        refresh_token_url,
                        refresh_token=r[0].refresh_token,
                        auth=requests.auth.HTTPBasicAuth(client_id, client_secret)
            )

            r = db(db.fitbit_user_t.user_email == auth.user.email).update(access_token = token["access_token"],
                                                                    refresh_token = token["refresh_token"],
                                                                    expires_at = token["expires_at"])
            r = db(db.fitbit_user_t.user_email == auth.user.email).select(db.fitbit_user_t.ALL)

        client = Fitbit(client_id, client_secret, access_token=r[0].access_token,
                        refresh_token=r[0].refresh_token)
        user = client.user_profile_get()

        act = client.activity_stats()
        steps = client.time_series(resource="activities/steps", period="7d")
        wt_goal = client.body_weight_goal()
        day = time.gmtime().tm_wday
        day_list = [0, 1, 2, 3, 4, 5, 6]
        new_day_list = day_list[day+1:] + day_list[:day+1]
        row_dict = {}
        row_dict["user_email"] = auth.user.email
        week_val = 0
        for idx, day in enumerate(new_day_list):
            step_val = int(steps['activities-steps'][idx]['value'])
            row_dict["d" + str(day)] = step_val
            week_val = week_val + step_val
        row_dict["week_total"] = week_val
        row_dict["lifetime"] = act["lifetime"]["total"]["steps"]
        row_dict["last_updated_day"] = day
        u = db.user_t(db.user_t.user_email == auth.user.email).update(weight=user["user"]["weight"],
                                                                       weight_target=wt_goal["goal"]["weight"])
        s = db.steps_t.update_or_insert((db.steps_t.user_email == auth.user.email),
                                        **row_dict)
        return True
Esempio n. 5
0
class FitbitUser:
    service = FitbitService

    def __init__(
        self,
        gargling_id: int,
        first_name: str,
        access_token: str,
        refresh_token: str,
        expires_at: int,
        service_user_id: None,
    ):
        self.gargling_id = gargling_id
        self.first_name = first_name
        self.client = FitbitApi(
            config.fitbit_client_id,
            config.fitbit_client_secret,
            access_token=access_token,
            refresh_token=refresh_token,
            expires_at=expires_at,
            refresh_cb=self.service.persist_token,
            system=FitbitApi.METRIC,
        )

    def _steps_api_call(self, date: pendulum.Date) -> dict:  # no test coverage
        kwargs = {
            "resource": "activities/steps",
            "base_date": date,
            "period": "1d"
        }
        exc = None
        data = None
        for _ in range(10):
            try:
                data = self.client.time_series(**kwargs)
                break
            except fitbit.exceptions.HTTPServerError as e:
                log.info("Error fetching fitbit data. Retrying")
                exc = e
                continue
        if data is None:
            assert exc is not None
            raise exc
        return data

    def steps(self, date: pendulum.Date) -> t.Optional[int]:
        data = self._steps_api_call(date)
        if not data["activities-steps"]:
            return 0
        entry = data["activities-steps"][0]
        return int(entry["value"]) if entry else 0

    def _weight_api_call(self,
                         date: pendulum.Date) -> dict:  # no test coverage
        return self.client.get_bodyweight(base_date=date, period="1w")

    def _bodyfat_api_call(self,
                          date: pendulum.Date) -> dict:  # no test coverage
        return self.client.get_bodyfat(base_date=date, period="1w")

    def body(self, date: pendulum.Date) -> dict:
        def most_recent(entries: list[dict]) -> t.Optional[dict]:
            if len(entries) == 0:
                return None
            for entry in entries:
                parsed = pendulum.parse(f"{entry['date']}T{entry['time']}")
                assert isinstance(parsed, pendulum.DateTime)
                entry["datetime"] = parsed.date()
            entries.sort(key=itemgetter("datetime"), reverse=True)
            return entries[0]

        weight = None
        elapsed = None
        fat = None
        weight_data = self._weight_api_call(date)
        weight_entry = most_recent(weight_data["weight"])
        if weight_entry is not None:
            if weight_entry["datetime"] == date:
                weight = weight_entry["weight"]
            else:
                elapsed = (date - weight_entry["datetime"]).days
        fat_data = self._bodyfat_api_call(date)
        fat_entry = most_recent(fat_data["fat"])
        if fat_entry is not None and fat_entry["datetime"] == date:
            fat = fat_entry["fat"]

        return {
            "weight": weight,
            "elapsed": elapsed,
            "fat": fat,
        }