230 lines
No EOL
6.3 KiB
Markdown
230 lines
No EOL
6.3 KiB
Markdown
# Chunk 7: Jobs Pages
|
|
|
|
**Parent:** [Build Graph Dashboard](../build-graph-dashboard.md)
|
|
**Previous:** [Chunk 6: Partition Pages](./chunk-6-partition-pages.md)
|
|
**Next:** [Chunk 8: Graph Analysis](./chunk-8-graph-analysis.md)
|
|
|
|
## Overview
|
|
|
|
Implement job listing and individual job metrics pages with performance data, success rates, and execution history.
|
|
|
|
## Scope
|
|
|
|
### In Scope
|
|
- Jobs listing page with high-level metadata
|
|
- Individual job metrics and performance pages
|
|
- Success rate tracking and trends
|
|
- Recent job runs with execution details
|
|
- Average duration and timing analysis
|
|
- Job label URL encoding for safe navigation
|
|
|
|
### Out of Scope
|
|
- Complex performance analytics
|
|
- Historical trend analysis beyond recent runs
|
|
- Job configuration editing
|
|
- Advanced filtering beyond basic search
|
|
|
|
## Technical Approach
|
|
|
|
### Data Sources
|
|
From Build Graph Service API and build event log:
|
|
- Job list from graph analysis
|
|
- Job execution history from build events
|
|
- Performance metrics aggregated from event data
|
|
- Success/failure rates from job events
|
|
|
|
### URL Encoding
|
|
```typescript
|
|
// Handle job labels in URLs (similar to partition refs)
|
|
function encodeJobLabel(label: string): string {
|
|
return btoa(label).replace(/\+/g, '-').replace(/\//g, '_').replace(/=/g, '');
|
|
}
|
|
|
|
function decodeJobLabel(encoded: string): string {
|
|
const padded = encoded.replace(/-/g, '+').replace(/_/g, '/');
|
|
return atob(padded);
|
|
}
|
|
```
|
|
|
|
### Component Structure
|
|
```typescript
|
|
const JobsList = {
|
|
oninit: () => {
|
|
this.jobs = [];
|
|
this.searchTerm = '';
|
|
this.loadJobs();
|
|
},
|
|
|
|
view: () => [
|
|
m('.jobs-header', [
|
|
m('h1', 'Jobs'),
|
|
m('input', {
|
|
placeholder: 'Search jobs...',
|
|
oninput: (e) => this.searchTerm = e.target.value,
|
|
})
|
|
]),
|
|
|
|
m('.jobs-table', [
|
|
m('table.table', [
|
|
m('thead', [
|
|
m('tr', [
|
|
m('th', 'Job Label'),
|
|
m('th', 'Success Rate'),
|
|
m('th', 'Avg Duration'),
|
|
m('th', 'Recent Runs'),
|
|
m('th', 'Last Run'),
|
|
])
|
|
]),
|
|
m('tbody', this.filteredJobs().map(job =>
|
|
m('tr', [
|
|
m('td', m('a', {
|
|
href: `/jobs/${encodeJobLabel(job.label)}`
|
|
}, job.label)),
|
|
m('td', [
|
|
m('.badge', job.success_rate >= 0.9 ? 'success' : 'warning'),
|
|
` ${Math.round(job.success_rate * 100)}%`
|
|
]),
|
|
m('td', formatDuration(job.avg_duration)),
|
|
m('td', job.recent_runs),
|
|
m('td', formatTime(job.last_run)),
|
|
])
|
|
))
|
|
])
|
|
])
|
|
]
|
|
};
|
|
|
|
const JobMetrics = {
|
|
oninit: (vnode) => {
|
|
this.jobLabel = decodeJobLabel(vnode.attrs.label);
|
|
this.metrics = null;
|
|
this.recentRuns = [];
|
|
this.loadJobMetrics();
|
|
},
|
|
|
|
view: () => [
|
|
m('.job-header', [
|
|
m('h1', this.jobLabel),
|
|
m('.job-stats', [
|
|
m('.stat', [
|
|
m('.stat-title', 'Success Rate'),
|
|
m('.stat-value', `${Math.round(this.metrics?.success_rate * 100)}%`),
|
|
]),
|
|
m('.stat', [
|
|
m('.stat-title', 'Avg Duration'),
|
|
m('.stat-value', formatDuration(this.metrics?.avg_duration)),
|
|
]),
|
|
m('.stat', [
|
|
m('.stat-title', 'Total Runs'),
|
|
m('.stat-value', this.metrics?.total_runs),
|
|
]),
|
|
])
|
|
]),
|
|
|
|
m('.job-content', [
|
|
m('.performance-chart', [
|
|
m('h2', 'Performance Trends'),
|
|
m('.chart-placeholder', 'Success rate and duration trends over time'),
|
|
// Simple trend visualization or table
|
|
m('table.table', [
|
|
m('thead', [
|
|
m('tr', [
|
|
m('th', 'Date'),
|
|
m('th', 'Success Rate'),
|
|
m('th', 'Avg Duration'),
|
|
])
|
|
]),
|
|
m('tbody', this.metrics?.daily_stats?.map(stat =>
|
|
m('tr', [
|
|
m('td', formatDate(stat.date)),
|
|
m('td', `${Math.round(stat.success_rate * 100)}%`),
|
|
m('td', formatDuration(stat.avg_duration)),
|
|
])
|
|
))
|
|
])
|
|
]),
|
|
|
|
m('.recent-runs', [
|
|
m('h2', 'Recent Runs'),
|
|
m('table.table', [
|
|
m('thead', [
|
|
m('tr', [
|
|
m('th', 'Build Request'),
|
|
m('th', 'Partitions'),
|
|
m('th', 'Status'),
|
|
m('th', 'Duration'),
|
|
m('th', 'Started'),
|
|
])
|
|
]),
|
|
m('tbody', this.recentRuns.map(run =>
|
|
m('tr', [
|
|
m('td', m('a', { href: `/builds/${run.build_id}` }, run.build_id)),
|
|
m('td', run.partitions.join(', ')),
|
|
m('td', m('.badge', run.status)),
|
|
m('td', formatDuration(run.duration)),
|
|
m('td', formatTime(run.started_at)),
|
|
])
|
|
))
|
|
])
|
|
])
|
|
])
|
|
]
|
|
};
|
|
```
|
|
|
|
### Metrics Calculation
|
|
From build event log:
|
|
- Success rate: completed jobs / total jobs
|
|
- Average duration: mean time from SCHEDULED to COMPLETED/FAILED
|
|
- Recent runs: last 50 job executions
|
|
- Daily aggregations for trend analysis
|
|
|
|
## Implementation Strategy
|
|
|
|
1. **Create Data Layer**
|
|
- API integration for job data
|
|
- Metrics calculation from build events
|
|
- Search and filtering logic
|
|
|
|
2. **Build Jobs List Page**
|
|
- Job table with search
|
|
- High-level metrics display
|
|
- Performance indicators
|
|
|
|
3. **Individual Job Pages**
|
|
- Detailed metrics display
|
|
- Performance trends
|
|
- Recent runs history
|
|
|
|
4. **Performance Visualization**
|
|
- Simple trend charts or tables
|
|
- Success rate indicators
|
|
- Duration analysis
|
|
|
|
## Deliverables
|
|
|
|
- [ ] Jobs listing page with search and metrics
|
|
- [ ] Individual job metrics pages
|
|
- [ ] Success rate tracking and display
|
|
- [ ] Performance trends visualization
|
|
- [ ] Recent job runs history
|
|
- [ ] Job label URL encoding/decoding
|
|
|
|
## Success Criteria
|
|
|
|
- Jobs list loads with accurate metrics
|
|
- Individual job pages show detailed performance data
|
|
- Success rates are calculated correctly
|
|
- Performance trends are meaningful
|
|
- Recent runs link to build details
|
|
- URL encoding handles all job label formats
|
|
|
|
## Testing
|
|
|
|
- Test jobs list search functionality
|
|
- Verify individual job pages load correctly
|
|
- Validate metrics calculations against known data
|
|
- Test performance trend accuracy
|
|
- Check recent runs links and data
|
|
- Validate URL encoding with complex job labels
|
|
- Test with jobs that have no runs yet |