Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,12 @@ RUN pip3 install -r /tmp/requirements.txt

COPY patches/01_add_api_urls.patch /opt/venv/lib/python3.12/site-packages/
COPY patches/03_add_active_needs_renewal_status.patch /opt/venv/lib/python3.12/site-packages/
COPY patches/04_add_usage_chart.patch /opt/venv/lib/python3.12/site-packages/

RUN cd /opt/venv/lib/python3.12/site-packages && \
patch -p1 < 01_add_api_urls.patch && \
patch -p1 < 03_add_active_needs_renewal_status.patch
patch -p1 < 03_add_active_needs_renewal_status.patch && \
patch -p1 < 04_add_usage_chart.patch

# Final Image
FROM python:3.12-slim-bullseye
Expand All @@ -45,6 +47,11 @@ COPY src/bin/run_coldfront.sh /opt/venv/bin
# Update NERC's email templates
COPY src/email/ /opt/venv/lib/python3.12/site-packages/coldfront/templates/email/

# Add NERC usage chart static files
COPY src/static/chartjs/ /opt/venv/lib/python3.12/site-packages/coldfront/static/chartjs/
COPY src/static/css/ /opt/venv/lib/python3.12/site-packages/coldfront/static/css/
COPY src/static/js/ /opt/venv/lib/python3.12/site-packages/coldfront/static/js/

ENV PATH="/opt/venv/bin:$PATH"
ENV DJANGO_SETTINGS_MODULE="local_settings"

Expand Down
104 changes: 104 additions & 0 deletions docs/chartjs-integration.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
# Chart.js Integration in ColdFront-NERC

This document explains how Chart.js was integrated into the ColdFront-NERC project for usage visualization.

## Overview

Chart.js is added as a NERC-specific dependency to visualize allocation usage statistics on the allocation detail page.

## Files Modified

### 1. Static Files
- **Location**: `src/static/chartjs/chart.min.js`
- **Purpose**: Contains the Chart.js library (v4.4.1)

### 2. Template Override
- **Location**: `src/templates/allocation/allocation_detail.html`
- **Changes**:
- Added usage statistics card with canvas element (line ~175)
- Imported Chart.js script (line ~440)
- Added chart initialization code (line ~492)

### 3. Dockerfile
- **Changes**: Added line to copy NERC templates directory
```dockerfile
COPY src/templates/ /opt/venv/lib/python3.12/site-packages/coldfront/templates/
COPY src/static/ /opt/venv/lib/python3.12/site-packages/coldfront/static/
```

### 4. Local Settings
- **Location**: `src/local_settings.py`
- **Changes**: Added NERC static files directory to Django's search path
```python
STATICFILES_DIRS.insert(0, os.path.join(os.path.dirname(__file__), 'static'))
```

## How It Works

1. **Build Time**: During Docker build, the template and static files are copied into the container
2. **Deployment**: Kubernetes `collectstatic` command gathers all static files
3. **Runtime**: When users view an allocation detail page, they see a usage chart

## Example Chart

The current implementation shows a sample line chart with:
- Monthly CPU hours usage
- 6 months of data
- Responsive design

## Customizing the Chart

To use real data instead of sample data, modify the chart initialization in `allocation_detail.html`:

```javascript
new Chart(ctx, {
type: 'line',
data: {
labels: {{ month_labels|safe }}, // Pass from Django view
datasets: [{
label: 'CPU Hours Used',
data: {{ usage_data|safe }}, // Pass from Django view
borderColor: 'rgb(75, 192, 192)',
backgroundColor: 'rgba(75, 192, 192, 0.2)',
}]
},
options: {
// ... chart options
}
});
```

## Testing

### Local Development
1. Build the Docker image:
```bash
docker build -t coldfront-nerc:test .
```

2. Run the container:
```bash
docker run -p 8080:8080 coldfront-nerc:test
```

3. Navigate to any allocation detail page to see the chart

### Kubernetes Deployment
The chart will automatically be included when deploying to staging/production as part of the normal deployment process.

## Chart Types

Chart.js supports multiple chart types. Change the `type` parameter to use different visualizations:
- `line` - Line chart (current)
- `bar` - Bar chart
- `pie` - Pie chart
- `doughnut` - Doughnut chart
- `radar` - Radar chart
- `polarArea` - Polar area chart

## Additional Resources

- [Chart.js Documentation](https://www.chartjs.org/docs/latest/)
- [Chart.js Examples](https://www.chartjs.org/docs/latest/samples/information.html)


24 changes: 24 additions & 0 deletions issues/02_data_integration.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
---
title: "Chart Refactor: Data Integration"
labels: ["data", "backend-integration", "charting"]
assignees: []
---

## Description
Implement robust data handling for the chart, including interpolation of missing values and proper dataset mapping.

## Tasks
- [ ] Standardize the data structure passed from the backend to the frontend.
- [ ] Implement `interpolateMissingData` logic in `chart_tooltip.js` or `allocation_detail.js` to handle `null` values.
- [ ] Ensure datasets correctly map to:
- CPU (Teal)
- GPU H100 (Orange)
- GPU V100 (Purple)
- [ ] Add logic to flag "missing" data points so they can be styled differently (e.g., grayscale).
- [ ] Pass backend data (e.g., attribute lengths, notes lengths) via `data-` attributes instead of Django template tags in JS.

## Acceptance Criteria
- [ ] Missing data points are mathematically interpolated.
- [ ] Data sets utilize the correct color schemes.
- [ ] The chart handles completely empty or partial datasets gracefully.

23 changes: 23 additions & 0 deletions issues/03_responsiveness_and_interactivity.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
---
title: "Chart Refactor: Responsiveness & Interactivity"
labels: ["ux", "responsiveness", "charting"]
assignees: []
---

## Description
Fine-tune the user experience, ensuring the chart behaves correctly on resize and provides useful interactive feedback.

## Tasks
- [ ] Fix the "shrinking chart" bug where the canvas doesn't expand after shrinking.
- [ ] Verify `responsive: true` configuration in Chart.js.
- [ ] Configure x-axis ticks to avoid overcrowding (e.g., `maxTicksLimit` or custom callback to show every 3rd day).
- [ ] Implement hover effects:
- Gray out points that are interpolated.
- Show custom tooltips on hover.
- [ ] Test behavior on mobile vs. desktop viewports.

## Acceptance Criteria
- [ ] Chart resizes dynamically (grows and shrinks) with the window.
- [ ] Axis labels remain readable on smaller screens.
- [ ] Interacting with the chart (hover/click) is smooth and lag-free.

26 changes: 26 additions & 0 deletions issues/04_tooltip_logic.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
---
title: "Chart Refactor: Tooltip & Error Handling"
labels: ["ux", "enhancement", "charting"]
assignees: []
---

## Description
Implement advanced custom tooltips to display detailed information and warnings about data quality.

## Tasks
- [ ] Move tooltip logic to `src/static/js/chart_tooltip.js`.
- [ ] Implement `externalTooltipHandler` to render custom HTML tooltips.
- [ ] Standardize missing data checks:
- Use `isCurrentPointMissing` for row-level styling.
- Use `doesTooltipHaveMissingData` for the footer warning.
- [ ] formatting for the error message:
```javascript
errorMessage.innerHTML = 'Billing script was unavailable for this date.<br>Values shown are interpolated.';
```
- [ ] Ensure tooltips are positioned correctly relative to the cursor and chart boundaries.

## Acceptance Criteria
- [ ] Tooltips show exact values for valid data and "Data unavailable" for missing data.
- [ ] A warning message appears at the bottom of the tooltip if any visible dataset is missing data for that timestamp.
- [ ] Tooltips support multi-line error messages using `innerHTML`.

49 changes: 49 additions & 0 deletions patches/04_add_usage_chart.patch
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
diff --git a/coldfront/core/allocation/templates/allocation/allocation_detail.html b/coldfront/core/allocation/templates/allocation/allocation_detail.html
--- a/coldfront/core/allocation/templates/allocation/allocation_detail.html
+++ b/coldfront/core/allocation/templates/allocation/allocation_detail.html
@@ -173,6 +173,24 @@
</div>
</div>

+<!-- NERC Usage Chart -->
+<div class="card mb-3">
+ <div class="card-header">
+ <h3 class="d-inline"><i class="fas fa-chart-bar" aria-hidden="true"></i> SU Costs</h3>
+ </div>
+ <div class="card-body">
+ <!-- Chart info header section -->
+ <div class="d-flex justify-content-between align-items-start mb-3 chart-info-header">
+ <div>
+ <h5 class="mb-1 font-weight-bold chart-title" id="chart-title">SU Cost - Cumulative</h5>
+ <p class="mb-0 text-muted small" id="chart-subtitle">Showing the cumulative cost for the last month</p>
+ </div>
+ </div>
+ <div id="chart-view" style="position: relative; height: 500px; width: 100%;">
+ <canvas id="allocationUsageChart"></canvas>
+ </div>
+ </div>
+</div>
+
{% if eulas %}
<div class="card mb-3">
<div class="card-header">
@@ -426,7 +444,6 @@

{{ guage_data|json_script:"guage-data" }}

-
<script>
$(document).ready(function () {
var guage_data = JSON.parse(document.getElementById('guage-data').textContent);
@@ -477,4 +494,10 @@
}
})
</script>
+
+<!-- Load Chart.js for NERC usage visualization -->
+<script src="{% static 'chartjs/chart.min.js' %}"></script>
+<script src="{% static 'js/chart_utils.js' %}"></script>
+<script src="{% static 'js/chart_config.js' %}"></script>
+<script src="{% static 'js/allocation_detail.js' %}"></script>
{% endblock %}

14 changes: 14 additions & 0 deletions src/static/chartjs/chart.min.js

Large diffs are not rendered by default.

137 changes: 137 additions & 0 deletions src/static/css/chart_variables.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
/**
* Chart Variables - Central CSS custom properties
* All chart-related colors, spacing, and typography are defined here
*/

:root {
/* ========================================
COLOR PALETTE
======================================== */

/* Primary UI Colors */
--chart-bg-primary: #ffffff;
--chart-bg-secondary: #f0f0f0;
--chart-bg-hover: #e0e0e0;
--chart-bg-active: #ffffff;

/* Text Colors */
--chart-text-primary: #333333;
--chart-text-secondary: #666666;
--chart-text-muted: #888888;
--chart-text-light: #999999;

/* Dataset Colors - Default palette for chart series */
--chart-color-cpu: rgb(0, 192, 232);
--chart-color-cpu-bg: rgba(0, 192, 232, 0.1);

--chart-color-gpu-h100: rgb(117, 213, 92);
--chart-color-gpu-h100-bg: rgba(117, 213, 92, 0.1);

--chart-color-gpu-v100: rgb(255, 146, 138);
--chart-color-gpu-v100-bg: rgba(255, 146, 138, 0.1);

/* Status/Feedback Colors */
--chart-color-error: #d32f2f;
--chart-color-error-light: #ffebee;
--chart-color-success: #539642;
--chart-color-success-hover: #2a5c1d;

/* Missing Data Indicator */
--chart-color-missing-segment: rgba(70, 70, 70, 0.7);
--chart-color-missing-gradient: rgb(160, 160, 160);

/* ========================================
SHADOWS
======================================== */
--chart-shadow-sm: 0 1px 3px rgba(0, 0, 0, 0.1);
--chart-shadow-md: 0 2px 6px 3px rgba(0, 0, 0, 0.05);

/* ========================================
SPACING
======================================== */
--chart-spacing-xs: 2px;
--chart-spacing-sm: 4px;
--chart-spacing-md: 8px;
--chart-spacing-lg: 14px;
--chart-spacing-xl: 20px;

/* ========================================
BORDER RADIUS
======================================== */
--chart-radius-sm: 4px;
--chart-radius-md: 6px;
--chart-radius-lg: 8px;

/* ========================================
TYPOGRAPHY
======================================== */
--chart-font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;

/* Font sizes - Desktop */
--chart-font-size-xs: 10px;
--chart-font-size-sm: 11px;
--chart-font-size-md: 12px;
--chart-font-size-lg: 13px;

/* Font weights */
--chart-font-weight-normal: 400;
--chart-font-weight-semibold: 600;
--chart-font-weight-bold: 700;

/* ========================================
TRANSITIONS
======================================== */
--chart-transition-fast: 0.1s ease;
--chart-transition-normal: 0.2s ease;

/* ========================================
Z-INDEX SCALE
======================================== */
--chart-z-tooltip: 100;

/* ========================================
CHART-SPECIFIC
======================================== */

/* Gradient fill opacity */
--chart-fill-opacity-start: 0.13;
--chart-fill-opacity-missing: 0.08;

/* Chart dimensions */
--chart-height-desktop: 600px;
--chart-height-tablet: 550px;
--chart-height-mobile: 400px;
--chart-height-mobile-sm: 420px;
}

/* ========================================
RESPONSIVE VARIABLE OVERRIDES
======================================== */

@media (max-width: 768px) {
:root {
--chart-font-size-xs: 9px;
--chart-font-size-sm: 10px;
--chart-font-size-md: 11px;
--chart-font-size-lg: 12px;
}
}

@media (max-width: 576px) {
:root {
--chart-font-size-xs: 8px;
--chart-font-size-sm: 9px;
--chart-font-size-md: 10px;
--chart-font-size-lg: 11px;
}
}

@media (max-width: 400px) {
:root {
--chart-font-size-xs: 7px;
--chart-font-size-sm: 8px;
--chart-font-size-md: 9px;
--chart-font-size-lg: 10px;
}
}

Loading
Loading