get_hourly_productivity
Analyze hourly productivity patterns to identify peak performance times and schedule focused work sessions effectively.
Instructions
Get productivity breakdown by hour.
Args: date_str: Date to query - 'today', 'yesterday', or 'YYYY-MM-DD'
Shows when during the day you were most/least productive. Useful for identifying peak productivity hours and scheduling deep work.
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| date_str | No | today |
Implementation Reference
- src/rescuetime_mcp/server.py:243-315 (handler)Implements the get_hourly_productivity tool using @mcp.tool() decorator. Resolves date, fetches hourly data via RescueTimeClient, aggregates by hour, computes weighted productivity, and formats a visual hourly breakdown with bars and peak summary.@mcp.tool() async def get_hourly_productivity(date_str: str = "today") -> str: """Get productivity breakdown by hour. Args: date_str: Date to query - 'today', 'yesterday', or 'YYYY-MM-DD' Shows when during the day you were most/least productive. Useful for identifying peak productivity hours and scheduling deep work. """ try: client = RescueTimeClient() resolved_date = resolve_date(date_str) hourly = await client.get_hourly_data( restrict_begin=resolved_date, restrict_end=resolved_date, ) if not hourly: return f"No hourly data for {resolved_date}." lines = [f"Hourly Productivity ({resolved_date}):", ""] # Group by hour and find the peak hour_data = {} for h in hourly: if h.hour not in hour_data: hour_data[h.hour] = {"seconds": 0, "productivity_sum": 0, "count": 0} hour_data[h.hour]["seconds"] += h.time_seconds hour_data[h.hour]["productivity_sum"] += h.productivity * h.time_seconds hour_data[h.hour]["count"] += 1 if not hour_data: return f"No hourly data for {resolved_date}." # Display each hour for hour in sorted(hour_data.keys()): data = hour_data[hour] mins = data["seconds"] / 60 if mins < 1: continue # Weighted average productivity avg_prod = data["productivity_sum"] / data["seconds"] if data["seconds"] > 0 else 0 # Visual representation bar_len = min(int(mins / 6), 10) # 60 mins = 10 blocks bar = "\u2588" * bar_len + "\u2591" * (10 - bar_len) # Productivity indicator prod_char = { 2: "++", 1: "+ ", 0: " ", -1: " -", -2: "--" }.get(round(avg_prod), " ") hour_str = f"{hour:02d}:00" lines.append(f"{hour_str} [{prod_char}] {bar} {mins:.0f}m") # Summary total_mins = sum(d["seconds"] for d in hour_data.values()) / 60 if hour_data: peak_hour = max(hour_data.keys(), key=lambda h: hour_data[h]["seconds"]) lines.append("") lines.append(f"Peak hour: {peak_hour:02d}:00 ({hour_data[peak_hour]['seconds']/60:.0f}m)") lines.append(f"Total: {total_mins:.0f} minutes logged") return "\n".join(lines) except RescueTimeAuthError as e: return f"Authentication error: {e}" except RescueTimeAPIError as e: return f"API error: {e}"
- src/rescuetime_mcp/models.py:79-91 (schema)Pydantic BaseModel HourlyData defines the structure for hourly productivity data used by the tool's client method.class HourlyData(BaseModel): """Hourly productivity data from interval perspective.""" hour: int # 0-23 date: str time_seconds: int productivity: int @property def time_minutes(self) -> float: """Time in minutes.""" return self.time_seconds / 60
- src/rescuetime_mcp/client.py:146-189 (helper)RescueTimeClient.get_hourly_data() method fetches raw hourly productivity data from RescueTime API using interval perspective, parses rows into HourlyData objects.async def get_hourly_data( self, restrict_begin: Optional[str] = None, restrict_end: Optional[str] = None, ) -> list[HourlyData]: """Get hourly productivity breakdown. Args: restrict_begin: Start date (YYYY-MM-DD), defaults to today restrict_end: End date (YYYY-MM-DD), defaults to today """ today = date.today().isoformat() params = { "perspective": "interval", "resolution_time": "hour", "restrict_kind": "productivity", "restrict_begin": restrict_begin or today, "restrict_end": restrict_end or today, } data = await self._request("data", params) if not data or "rows" not in data: return [] hourly = [] for row in data["rows"]: # Row format for interval: [date, time_seconds, num_people, productivity] # Date is like "2024-01-15T14:00:00" date_str = row[0] hour = int(date_str.split("T")[1].split(":")[0]) date_part = date_str.split("T")[0] hourly.append( HourlyData( hour=hour, date=date_part, time_seconds=row[1], productivity=row[3] if len(row) > 3 else 0, ) ) return hourly
- src/rescuetime_mcp/server.py:36-43 (helper)resolve_date() utility function converts 'today'/'yesterday' to ISO date strings, used in the tool.def resolve_date(date_str: str) -> str: """Resolve 'today', 'yesterday', or return as-is.""" if date_str.lower() == "today": return date.today().isoformat() elif date_str.lower() == "yesterday": return (date.today() - timedelta(days=1)).isoformat() return date_str