Example #1
0
 def weight(client: FitbitApi) -> t.Optional[dict]:
     data = client.get_bodyweight(period="7d")
     if len(data["weight"]) == 0:
         log.info("No weight data")
         return None
     entries = data["weight"]
     for entry in entries:
         entry["datetime"] = pendulum.parse(f"{entry['date']}T{entry['time']}")
     entries.sort(key=itemgetter("datetime"), reverse=True)
     most_recent = entries[0]
     log.info(f"weight data: {most_recent}")
     return most_recent
Example #2
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,
        }