mirror of
https://github.com/stellarshenson/stellars-jupyterhub-ds.git
synced 2026-03-10 23:20:28 +00:00
feat: increase activity monitor half-life to 72 hours (3 days)
Changed JUPYTERHUB_ACTIVITYMON_HALF_LIFE default from 48h to 72h for more stable activity scores. Activity from 3 days ago now has 50% weight, better suited for users with irregular schedules. Updated: Dockerfile, custom_handlers.py, activity_sampler.py, settings_dictionary.yml, README.md, docs/activity-tracking-methodology.md
This commit is contained in:
@@ -249,3 +249,6 @@ This journal tracks substantive work on documents, diagrams, and documentation c
|
||||
|
||||
82. **Task - Simplify Activity column header**: Removed retention period from column title<br>
|
||||
**Result**: Changed Activity column header from "Activity (7 days)" to just "Activity" in activity.html
|
||||
|
||||
83. **Task - Activity monitor half-life to 72h (3 days)**: Extended decay half-life for smoother scoring<br>
|
||||
**Result**: Updated JUPYTERHUB_ACTIVITYMON_HALF_LIFE default from 48h to 72h across Dockerfile, custom_handlers.py, activity_sampler.py, settings_dictionary.yml, README.md, and docs/activity-tracking-methodology.md. With 72h half-life, activity from 3 days ago has 50% weight, providing more stable activity scores for users with irregular schedules
|
||||
|
||||
@@ -392,7 +392,7 @@ services:
|
||||
environment:
|
||||
- JUPYTERHUB_ACTIVITYMON_SAMPLE_INTERVAL=600 # 10 minutes (default) - how often to record samples
|
||||
- JUPYTERHUB_ACTIVITYMON_RETENTION_DAYS=7 # 7 days (default) - how long to keep samples
|
||||
- JUPYTERHUB_ACTIVITYMON_HALF_LIFE=48 # 48 hours (default) - decay half-life for scoring
|
||||
- JUPYTERHUB_ACTIVITYMON_HALF_LIFE=72 # 72 hours / 3 days (default) - decay half-life for scoring
|
||||
- JUPYTERHUB_ACTIVITYMON_INACTIVE_AFTER=60 # 60 minutes (default) - threshold for inactive status
|
||||
```
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ Our current approach uses **exponential decay scoring**:
|
||||
- Each sample marked active/inactive based on `last_activity` within threshold
|
||||
- Score calculated as weighted ratio: `weighted_active / weighted_total`
|
||||
- Weight formula: `weight = exp(-λ × age_hours)` where `λ = ln(2) / half_life`
|
||||
- Default half-life: 24 hours (activity from yesterday worth 50%)
|
||||
- Default half-life: 72 hours / 3 days (activity from 3 days ago worth 50%)
|
||||
|
||||
## Industry Approaches
|
||||
|
||||
@@ -157,14 +157,13 @@ Our current implementation is actually well-designed for the use case:
|
||||
|--------|------------------------|
|
||||
| Sampling | Every 10 min (configurable) |
|
||||
| Active threshold | 60 min since last_activity |
|
||||
| Decay | 24-hour half-life |
|
||||
| Decay | 72-hour (3-day) half-life |
|
||||
| Score range | 0-100% |
|
||||
| Visualization | 5-segment bar with color coding |
|
||||
|
||||
**Suggested improvements:**
|
||||
1. Add tooltip showing actual score percentage
|
||||
2. Consider longer half-life (48-72h) for less frequent users
|
||||
3. Document what the score represents
|
||||
2. Document what the score represents
|
||||
|
||||
### Option B: Hybrid Daily + Decay
|
||||
|
||||
@@ -205,8 +204,8 @@ Weekly score = sum of daily points / 7
|
||||
- Option: Relative to user's own historical average
|
||||
|
||||
2. **How fast should old activity decay?**
|
||||
- Current: 24-hour half-life (aggressive decay)
|
||||
- Alternative: 72-hour half-life (more stable)
|
||||
- Current: 72-hour / 3-day half-life (balanced decay)
|
||||
- Alternative: 24-hour half-life (aggressive decay)
|
||||
- Alternative: 7-day half-life (weekly trend)
|
||||
|
||||
3. **Should weekends count differently?**
|
||||
|
||||
@@ -90,7 +90,7 @@ ENV JUPYTERHUB_IDLE_CULLER_MAX_EXTENSION=24
|
||||
# Activity monitor
|
||||
ENV JUPYTERHUB_ACTIVITYMON_SAMPLE_INTERVAL=600
|
||||
ENV JUPYTERHUB_ACTIVITYMON_RETENTION_DAYS=7
|
||||
ENV JUPYTERHUB_ACTIVITYMON_HALF_LIFE=48
|
||||
ENV JUPYTERHUB_ACTIVITYMON_HALF_LIFE=72
|
||||
ENV JUPYTERHUB_ACTIVITYMON_INACTIVE_AFTER=60
|
||||
ENV JUPYTERHUB_ACTIVITYMON_RESOURCES_UPDATE_INTERVAL=10
|
||||
ENV JUPYTERHUB_ACTIVITYMON_VOLUMES_UPDATE_INTERVAL=3600
|
||||
|
||||
@@ -14,7 +14,7 @@ Environment Variables:
|
||||
JUPYTERHUB_API_URL: Hub API URL (provided by JupyterHub)
|
||||
JUPYTERHUB_ACTIVITYMON_SAMPLE_INTERVAL: Sampling interval in seconds (default: 600)
|
||||
JUPYTERHUB_ACTIVITYMON_RETENTION_DAYS: Days to retain samples (default: 7)
|
||||
JUPYTERHUB_ACTIVITYMON_HALF_LIFE: Score decay half-life in hours (default: 48)
|
||||
JUPYTERHUB_ACTIVITYMON_HALF_LIFE: Score decay half-life in hours (default: 72)
|
||||
JUPYTERHUB_ACTIVITYMON_INACTIVE_AFTER: Minutes before marking inactive (default: 60)
|
||||
"""
|
||||
|
||||
@@ -71,7 +71,7 @@ class ActivitySamplerService:
|
||||
# Sampling configuration
|
||||
self.sample_interval = int(os.environ.get('JUPYTERHUB_ACTIVITYMON_SAMPLE_INTERVAL', 600))
|
||||
self.retention_days = int(os.environ.get('JUPYTERHUB_ACTIVITYMON_RETENTION_DAYS', 7))
|
||||
self.half_life_hours = int(os.environ.get('JUPYTERHUB_ACTIVITYMON_HALF_LIFE', 48))
|
||||
self.half_life_hours = int(os.environ.get('JUPYTERHUB_ACTIVITYMON_HALF_LIFE', 72))
|
||||
self.inactive_after_minutes = int(os.environ.get('JUPYTERHUB_ACTIVITYMON_INACTIVE_AFTER', 60))
|
||||
|
||||
# Database
|
||||
|
||||
@@ -94,7 +94,7 @@ class ActivityMonitor:
|
||||
|
||||
# Default configuration
|
||||
DEFAULT_RETENTION_DAYS = 7 # 7 days
|
||||
DEFAULT_HALF_LIFE = 48 # 48 hours
|
||||
DEFAULT_HALF_LIFE = 72 # 72 hours (3 days)
|
||||
DEFAULT_INACTIVE_AFTER = 60 # 60 minutes
|
||||
DEFAULT_ACTIVITY_UPDATE_INTERVAL = 600 # 10 minutes
|
||||
|
||||
|
||||
@@ -87,7 +87,7 @@ Activity Monitor:
|
||||
|
||||
- name: JUPYTERHUB_ACTIVITYMON_HALF_LIFE
|
||||
description: Decay half-life in hours (1-168)
|
||||
default: "48"
|
||||
default: "72"
|
||||
|
||||
- name: JUPYTERHUB_ACTIVITYMON_INACTIVE_AFTER
|
||||
description: Minutes until user considered inactive (1-1440)
|
||||
|
||||
Reference in New Issue
Block a user