Commit Graph

282 Commits

Author SHA1 Message Date
stellarshenson
8980e552cf refactor: simplify Activity column header
Removed "(7 days)" suffix from Activity column header in activity.html.
The retention period is a configuration detail, not needed in the UI.
2026-01-25 11:48:49 +01:00
stellarshenson
ea5e8b730a feat: add volume size tooltip with per-volume breakdown
Activity Monitor now shows hover tooltip on volume sizes displaying
individual volume sizes (home, workspace, cache) instead of just total.

Backend changes:
- _fetch_volume_sizes() returns {total, volumes: {suffix: size}}
- ActivityDataHandler passes volume_breakdown to frontend

Frontend changes:
- formatVolumeSize() displays tooltip with sorted volume list
- Dotted underline indicates tooltip availability

Also: removed clean dependency from build targets in Makefile
2026-01-22 22:39:54 +01:00
stellarshenson
4d9a97eb82 feat: increase activity monitor half-life to 48 hours
Changed JUPYTERHUB_ACTIVITYMON_HALF_LIFE default from 24h to 48h
for smoother decay in activity scoring. A sample from 48 hours ago
now has 50% weight (was 50% at 24h).
3.7.15_cuda-13.0.2_jh-5.4.2
2026-01-22 01:52:01 +01:00
stellarshenson
e70d4f7236 docs: migrated documentation folder 2026-01-22 01:46:37 +01:00
stellarshenson
92a7e8fa96 docs: add activity tracking methodology research
Research document covering industry approaches for activity tracking:
- Exponential Moving Average (EMA) with half-life decay
- Hubstaff's hour-based approach
- Daily target method (8h=100%)
- GitHub contribution graph methodology

Validates current EMA implementation aligns with industry standards.
2026-01-22 01:45:18 +01:00
stellarshenson
92149de4a4 docs: update journal with activity sampler service changes 2026-01-22 01:30:17 +01:00
stellarshenson
a737058903 feat: activity sampler as independent JupyterHub service
Activity tracking now runs as a JupyterHub managed service that starts
on boot, independent of page views. Uses REST API to fetch user data.

Changes:
- Add activity_sampler.py standalone service script
- Configure as JupyterHub service with roles/scopes in config
- Add aiohttp dependency for async HTTP requests
- Remove lazy-start from ActivityDataHandler (now independent)
- Rename env var to JUPYTERHUB_ACTIVITYMON_SAMPLE_INTERVAL
- Update settings_dictionary.yml

Volume size refresher still lazy-starts on Activity page view.
2026-01-22 01:28:31 +01:00
stellarshenson
aaca516bae feat: add VolumeSizeRefresher for independent background refresh
- VolumeSizeRefresher class using Tornado PeriodicCallback
- Refreshes volume sizes every hour (JUPYTERHUB_ACTIVITYMON_VOLUMES_UPDATE_INTERVAL)
- Lazy start on first Activity page access (like ActivitySampler)
- Runs first refresh immediately, then at configured interval
2026-01-22 01:23:09 +01:00
stellarshenson
ae387e7734 refactor: use logging module instead of print statements 2026-01-22 01:17:04 +01:00
stellarshenson
30a1665e23 fix: add flush=True to volume sizes logging 2026-01-22 01:12:55 +01:00
stellarshenson
4125b838a9 docs: add BUILD_OPTS to make help output 2026-01-22 01:04:08 +01:00
stellarshenson
017c42ac68 feat: BUILD_OPTS for skipping version increment in Makefile
- Add BUILD_OPTS variable with --no-version-increment support
- Add maybe_increment_version conditional target
- Filter out custom opts before passing to docker
- Usage: make build BUILD_OPTS='--no-version-increment'
- Update journal with activity sampler fixes and color changes
2026-01-22 01:03:46 +01:00
stellarshenson
a2b7a0249a fix: activity bar colors 1=red, 2-3=yellow, 4-5=green 2026-01-21 16:50:36 +01:00
stellarshenson
e94680178c updated version 3.7.14_cuda-13.0.2_jh-5.4.2 2026-01-21 15:53:20 +01:00
stellarshenson
887a3569fb fix: pass db and find_user to activity sampler instead of app
The sampler was expecting an app object with db and users attributes,
but handler settings don't provide that. Changed to pass handler's
self.db and self.find_user directly to the sampler.

Also added flush=True to print statements for immediate log output.
2026-01-21 15:01:13 +01:00
stellarshenson
b8ab1f6614 fix: lazy start activity sampler on first Activity page access
post_init_hook is not a valid JupyterHub config option - sampler never
started. Changed to lazy initialization: ActivityDataHandler starts
the sampler when admin first visits the Activity page.
2026-01-21 14:56:41 +01:00
stellarshenson
24c51a959c feat: background activity sampler with Tornado PeriodicCallback
Add ActivitySampler singleton class that automatically samples activity
for ALL users (active, idle, offline) at configurable interval using
Tornado's PeriodicCallback for non-blocking execution.

- Uses JUPYTERHUB_ACTIVITYMON_ACTIVITY_UPDATE_INTERVAL (default 600s)
- Starts via post_init_hook after JupyterHub initialization
- Runs first sample immediately on startup
- Logs tick statistics after each sample cycle
- Fixes: missing parenthesis and stray code from previous edit
2026-01-21 14:38:17 +01:00
stellarshenson
7f9fb3f871 feat: activity sampling for all users including offline
- Added record_samples_for_all_users() to sample ALL users
- Offline users marked inactive (activity bars decay over time)
- New API: POST /hub/api/activity/sample (admin, for cron jobs)
- Returns counts: total, active, inactive, offline
2026-01-21 13:40:23 +01:00
stellarshenson
2c561490b3 perf: background caching for volume sizes
- Volume sizes now cached and refreshed hourly in background
- Activity page loads instantly (no longer waits for docker df)
- Added JUPYTERHUB_ACTIVITYMON_VOLUMES_UPDATE_INTERVAL (default 3600s)
- First load shows empty volumes until background refresh completes
2026-01-21 08:56:36 +01:00
stellarshenson
48da028968 feat: add Volumes column to activity monitor
- Backend: get_all_user_volumes_sizes() fetches Docker volume sizes
- Frontend: sortable Volumes column with human-friendly display (GB/MB)
- Badge now shows "N users (x active, y idle, z offline)"
- CSS: .col-volumes class (8em width)
2026-01-21 08:45:27 +01:00
stellarshenson
a118bb6d7f fix: remove CPU/Memory color coding in activity monitor
Threshold-based coloring (green/yellow/red) doesn't work for
multi-core systems where CPU usage can exceed 100% (e.g., 6400%
on 64-core machines). Now displays plain text values.
2026-01-21 08:37:29 +01:00
stellarshenson
b80f10a36c updated with new version 2026-01-20 21:41:30 +01:00
stellarshenson
3ab2106c37 style: adjust activity table column widths for better proportions
- CPU: 7.5em, Memory: 9em, Time Left: 12em
- Last Active: 13.5em, Activity: 20em
- User column takes remaining space
- table-layout: fixed with 100% width
3.7.10_cuda-13.0.2_jh-5.4.2
2026-01-20 21:35:24 +01:00
stellarshenson
d691390549 feat: show all users in activity monitor and refactor table styles
- Activity Monitor now shows all users with historical activity, not just active servers
- Badge changed from "X active servers" to "X users (Y active)" format
- Status column now sortable (green > amber > red priority)
- Default sort changed to status descending with secondary sort by username
- Moved inline table styles to CSS classes using em units
- Added .activity-table, .settings-table, .notifications-table classes
- Simplified Activity page subtitle
2026-01-20 21:26:24 +01:00
stellarshenson
21501e0a2d feat: add JUPYTERHUB_ACTIVITYMON_SAMPLING_INTERVAL env var
- Add sampling interval config (default 600s / 10min)
- Separate from RESOURCES_UPDATE_INTERVAL (10s for UI refresh)
- Sampling interval controls how often activity samples are recorded
- Added to Dockerfile, settings_dictionary.yml, and ActivityMonitor class
2026-01-20 20:40:33 +01:00
stellarshenson
9262e30fe8 refactor: separate resource refresh from activity sampling
- Remove "Measured X ago" timer display (adds no value)
- Change auto-refresh interval from 30s to 10s for real-time monitoring
- Add JUPYTERHUB_ACTIVITYMON_RESOURCES_UPDATE_INTERVAL env var (default 10s)
- Resource refresh updates status, CPU, memory, timers only
- Activity sampling is separate (controlled by background process)
- Remove formatTimeAgo function and related timestamp tracking
2026-01-20 20:39:10 +01:00
stellarshenson
094be29d6d feat: add column sorting and improved last active display
- Add clickable column sorting for User, CPU, Memory, Time Left, Last Active
- Sorting cycles: descending -> ascending -> none (default)
- Sort icons show current direction
- Last Active now shows full words ("8 minutes ago", "4 months ago")
- Proper singular/plural handling for time units
2026-01-20 20:33:23 +01:00
stellarshenson
221ab54162 docs: update journal with activity monitor improvements 3.7.9_cuda-13.0.2_jh-5.4.2 2026-01-20 19:35:05 +01:00
stellarshenson
c1cabde01c fix: hide 'Measured' text when time empty, simplify settings text
- Only show "Measured X ago" when timeAgo has value
- Remove compose.yml/compose_override.yml mention from Settings page
2026-01-20 19:34:08 +01:00
stellarshenson
04ef42ce41 refactor: remove sample interval, simplify activity sampling
- Remove JUPYTERHUB_ACTIVITYMON_SAMPLE_INTERVAL env var entirely
- Remove on-demand sampling from activity page view
- Simplify record_sample() to always insert (caller controls frequency)
- Fix formatTimeAgo to show nothing when time ≤0s
- Update Dockerfile, settings_dictionary.yml, custom_handlers.py
2026-01-20 19:29:26 +01:00
stellarshenson
ff32729bd6 docs: update journal with activity monitor improvements 2026-01-20 19:06:38 +01:00
stellarshenson
064c553808 feat: dynamic refresh timer and simplified last active display
- "Measured X ago" now updates every second (shows 5s ago, 3min ago, etc.)
- Last Active column left-aligned and shows rounded times (5min, 2h, 3d)
- Removed redundant minutes/hours from time display
2026-01-20 19:06:17 +01:00
stellarshenson
4b2fc084bf fix: use separate SQLite database for activity monitor
- ActivityMonitor now uses /data/activity_samples.sqlite instead of
  JupyterHub's main database to avoid SQLite locking conflicts
- Fixes "database is locked" errors that prevented login when both
  JupyterHub and ActivityMonitor wrote simultaneously
- Added "Last Active" column to activity table showing relative time
2026-01-20 19:04:39 +01:00
stellarshenson
66de0e7c5c fix: activity bar UI redesign and sample cleanup enforcement
- Redesign activity bar as thin continuous bar (80x8px) with subtle dividers
- Fix inconsistent timestamp text ("Last updated" vs "Measured")
- Fix sample cleanup to always run on record_sample(), not just on inserts
- Ensures samples older than RETENTION_DAYS are properly pruned
2026-01-20 18:53:03 +01:00
stellarshenson
d793f5a68d updated 3.7.3_cuda-13.0.2_jh-5.4.2 2026-01-20 17:49:45 +01:00
stellarshenson
60eb59a854 docs: clarify sample update vs insert behavior in docstring 2026-01-20 17:21:31 +01:00
stellarshenson
aecd6821c7 fix: update last sample on refresh instead of inserting new one 2026-01-20 17:21:15 +01:00
stellarshenson
5f0aa860be docs: update journal with refresh spinner and relative time 2026-01-20 17:16:27 +01:00
stellarshenson
a0dee35bd6 fix: add spinner to refresh button and show relative time 2026-01-20 17:16:00 +01:00
stellarshenson
5e563af39b feat: add Activity Monitor admin page with 3-state status and reset functionality
- Add ActivitySample SQLAlchemy model for database persistence
- Add ActivityMonitor singleton with scoring, reset, lifecycle methods
- Add JUPYTERHUB_ACTIVITYMON_INACTIVE_AFTER env var (default 60 min)
- Update defaults: SAMPLE_INTERVAL=600s, RETENTION_DAYS=7
- Fix score calculation to use measured samples only (not theoretical max)
- Add 3-state status: green (active), yellow (inactive), red (offline)
- Add recently_active field in API response
- Add Reset button with confirmation dialog
- Fix green color (explicit #28a745 instead of text-success)
- Add ThreadPoolExecutor for non-blocking Docker stats
- Remove old background sampler code (on-demand sampling now)
- Bump version to 3.7.0
2026-01-20 17:08:23 +01:00
stellarshenson
8a4be041ac docs: update journal with spawner.orm_spawner.last_activity fix CHECKPOINT_BEFORE_ACTIVITY_TAB_3.6.24 FIX_SPAWNER_ACTIVITY_TRACKING_3.6.24 3.6.28_cuda-13.0.2_jh-5.4.2 3.6.24_cuda-13.0.2_jh-5.4.2 2026-01-20 10:08:35 +01:00
stellarshenson
30f3000ab7 chore: bump version to 3.6.24 2026-01-20 10:08:03 +01:00
stellarshenson
d2ff63b2e1 fix: use spawner.orm_spawner.last_activity (Server object has no last_activity) 2026-01-20 10:05:53 +01:00
stellarshenson
44f8ce0318 docs: update journal with server.last_activity fix and checkpoint command 2026-01-20 10:02:35 +01:00
stellarshenson
5d24f17900 fix: use server.last_activity instead of user.last_activity for idle tracking
- user.last_activity updates on Hub page access (causes timer reset on refresh)
- server.last_activity only updates on actual JupyterLab activity
- Matches what jupyterhub-idle-culler uses for culling decisions
2026-01-20 10:01:52 +01:00
stellarshenson
4b4f254dfb docs: clarify checkpoint tag format - shortened version as suffix 2026-01-20 09:47:42 +01:00
stellarshenson
f0f1ddc0ee docs: rename short_version to version_suffix in checkpoint command 2026-01-20 09:47:16 +01:00
stellarshenson
8fa6fa0dd9 feat: add /checkpoint command for creating milestone tags 2026-01-20 09:46:54 +01:00
stellarshenson
a5fd82b3ea updated version CHECKPOINT_BEFORE_ACTIVITY_TRACKER_3.6.22 2026-01-19 08:06:52 +01:00
stellarshenson
b440a7fe07 fix: show max extension error as disappearing warning alert, remove duplicate static text 3.6.22_cuda-13.0.2_jh-5.4.2 2026-01-18 21:58:25 +01:00