Commit Graph

303 Commits

Author SHA1 Message Date
stellarshenson
d3913b4d69 fix: admin volume button only for stopped servers, positioned last
- Volume management button now only appears for users with stopped servers
- Button dynamically removed when server starts running
- Button positioned as last element in action cell (after Edit User)
- MutationObserver handles React re-renders
2026-01-26 23:50:36 +01:00
stellarshenson
ea814b70f8 docs: update journal with README documentation changes 2026-01-26 13:38:08 +01:00
stellarshenson
920c8b82d5 docs: add volume sizes and resource tracking to README
- Add volume sizes to Activity Monitor feature description
- Add Volume sizes feature with per-volume breakdown tooltip
- Add JUPYTERHUB_ACTIVITYMON_VOLUMES_UPDATE_INTERVAL config option
2026-01-26 13:37:32 +01:00
stellarshenson
0905d5bd07 docs: add admin volume management to README
- Add feature bullet for admin volume management button
- Add note about admin capability in User Self-Service Workflow section
- Admins can manage any user's volumes via database icon in admin panel
2026-01-26 13:37:01 +01:00
stellarshenson
2a533f9140 docs: update journal with admin volume button feature
- Document final implementation using tr.user-row selector
- Username extracted from span[data-testid="user-name-div-{username}"]
- Button matches Edit User button styling (btn btn-light btn-xs)
- Positioned before Edit User button in actions cell
2026-01-26 13:35:26 +01:00
stellarshenson
32d645182d fix: admin volume button now appears for all users
- Find user rows via tr.user-row selector
- Get username from span[data-testid="user-name-div-{username}"]
- Find Edit User button by text content (no title attribute)
- Insert volume button before Edit User button
- Copies exact classes from Edit User button (btn btn-light btn-xs)
2026-01-26 13:32:16 +01:00
stellarshenson
a67af7a090 fix: admin volume button for all users
- Find Edit buttons directly instead of looking for td.actions
- Inject button before each Edit User button found
- Works with JupyterHub React admin panel structure
- Walks up DOM to find user container and extract username
2026-01-26 13:31:30 +01:00
stellarshenson
a7ec100878 fix: admin volume button positioning and styling
- Find username via multiple methods (admin link, server link, text)
- Copy exact class from Edit button for consistent styling
- Insert button before Edit button (between server actions and edit)
- Works for all users, not just those with running servers
2026-01-26 13:28:54 +01:00
stellarshenson
f63de6454c feat: add admin volume management button
- Add Manage Volumes modal to admin panel with volume checkboxes
- MutationObserver injects database icon button into each user row
- Button uses btn-outline-secondary matching other admin buttons
- Calls existing DELETE /api/users/{username}/manage-volumes endpoint
- Admins can reset any user's volumes directly from admin panel
2026-01-26 13:25:39 +01:00
stellarshenson
9162de622f fix: volume sizes logging and tooltip cleanup
- Add _get_logger() helper for JupyterHub application logger integration
- Module-level logger doesn't inherit JupyterHub handlers, causing
  invisible logs for VolumeSizeRefresher and Volume Sizes operations
- Update all volume-related functions to use _get_logger()
- Add cache validation to prevent empty results overwriting valid cache
- Remove "(>8h/day)" suffix from activity tooltip when score > 100%
2026-01-26 12:57:32 +01:00
stellarshenson
4d1f4d1407 upraded latest version 2026-01-25 23:23:38 +01:00
stellarshenson
6ced633450 feat: add deletion spinner and initialize activity for new users
- Admin panel shows spinner with "Deleting user {username}..." during deletion
- Refactored loading modal to accept dynamic message text
- New users get initial activity sample recorded automatically
- New users show 0% activity bar instead of '--' in Activity Monitor
2026-01-25 23:18:30 +01:00
stellarshenson
5678af11f8 fix: add volume refresher tick logging for diagnostics
- Added tick completion log showing user count and total MB after each refresh
- Helps diagnose if VolumeSizeRefresher is running or died silently
FIX_VOLUME_REFRESHER_3.7.16
2026-01-25 21:50:25 +01:00
stellarshenson
d2a93a08f2 fix: Auth column width and Volumes tooltip
- Increased .col-auth width from 4em to 5em to prevent truncation
- Changed Volumes tooltip to generic "hover for breakdown" since
  volume names are autodiscovered
2026-01-25 12:50:06 +01:00
stellarshenson
845d458d75 feat: add column tooltips and authorization status column
- Added explanatory tooltips to all 9 column headers in Activity Monitor
- Added sortable Auth column showing NativeAuthenticator authorization status
- Green checkmark for authorized users, red X for not authorized
- Backend queries users_info table with graceful fallback
- Updated documentation with new column and API schema
3.7.16_cuda-13.0.2_jh-5.4.2
2026-01-25 12:44:11 +01:00
stellarshenson
ad1d37b16f feat: add 24h minimum data requirement for activity score
- Activity tooltip shows "Not enough data (Nh of 24h collected)" until
  sufficient samples collected (144 samples at 10-min intervals)
- Progress bar still renders to show emerging trend, tooltip clarifies
  percentage not yet reliable
- Added activitymon_sample_interval to template_vars for frontend access
- Rewrote docs/activity-tracking-methodology.md as comprehensive
  implementation specification covering data collection, scoring formulas,
  UI components, API endpoints, and design rationale
2026-01-25 12:25:38 +01:00
stellarshenson
05c99624d9 feat: add activity score normalization with TARGET_HOURS
Added JUPYTERHUB_ACTIVITYMON_TARGET_HOURS env var (default 8) to
normalize activity scores based on expected daily work hours.

- Raw score (% of sampled time active) normalized to target
- 8h/day worker with 33% raw score -> 100% normalized
- Progress bar capped at 5 segments (100%)
- Tooltip shows real % with "(>8h/day)" indicator if over 100%

Files: Dockerfile, settings_dictionary.yml, jupyterhub_config.py,
activity.html
2026-01-25 12:17:26 +01:00
stellarshenson
3343c568a7 docs: clarify configured vs effective half-life
Rewrote simulation section explaining why effective half-life differs
from configured half-life:

1. Decay is CONTINUOUS (24/7 in calendar time)
2. Work is SPARSE (only during work hours)
3. Decay during BREAKS (overnight with no new work)

Added single table showing effective half-life for work patterns
(12h, 10h, 8h, 6h, 4h, 2h) vs configured half-lives (24h, 48h, 72h).

Key insight: 72h configured = 18h effective for 8h/day worker.
2026-01-25 12:09:12 +01:00
stellarshenson
b7b3f0e87c docs: add half-life simulation tables for different work patterns
Added detailed simulation results showing how calendar half-life
translates to effective working-time decay:

- 10h/day (intensive): 72h -> 28.5 work hours at 50%
- 8h/day (typical): 72h -> 22.8 work hours at 50%
- 4h/day (part-time): 72h -> 11.5 work hours at 50%

Key finding: 72h calendar half-life consistently yields ~2.9 work
days at the 50% point, regardless of daily work hours. Activity
scores correctly reflect work fraction (8h/24h = 33.3%).
2026-01-25 11:54:54 +01:00
stellarshenson
cdf1e5eaa4 docs: add rationale for 72-hour half-life choice
Explains why 72h calendar half-life was chosen:
- Decay applies to wall-clock time, not working time
- Users work ~8h/day (1/3 of 24h period)
- 72h calendar = ~24h of working time = one full workday
- Prevents overnight breaks from penalizing scores

This calibrates decay to actual engagement patterns rather
than raw calendar time.
2026-01-25 11:51:20 +01:00
stellarshenson
a76c99d6ab 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
2026-01-25 11:50:19 +01:00
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