Skip to content

Commit 648c1cf

Browse files
Merge pull request #95 from NessieCanCode/ensure-export-button-auto-populates-data
Populate filters from database data
2 parents 251efc1 + 665e040 commit 648c1cf

File tree

2 files changed

+45
-14
lines changed

2 files changed

+45
-14
lines changed

src/slurmcostmanager.js

Lines changed: 34 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -591,23 +591,34 @@ function UserDetails({ users }) {
591591
);
592592
}
593593

594-
function Details({ details, daily }) {
594+
function Details({ details, daily, partitions = [], accounts = [], users = [] }) {
595595
const [expanded, setExpanded] = useState(null);
596596
const [dateRange, setDateRange] = useState('30');
597597
const [filters, setFilters] = useState({
598598
partition: '',
599599
account: '',
600-
department: '',
601-
pi: ''
600+
user: ''
602601
});
603602

604603
function toggle(account) {
605604
setExpanded(prev => (prev === account ? null : account));
606605
}
607606

607+
const filteredDetails = details
608+
.map(d => {
609+
if (filters.account && d.account !== filters.account) return null;
610+
let userList = d.users || [];
611+
if (filters.user) {
612+
userList = userList.filter(u => u.user === filters.user);
613+
if (!userList.length) return null;
614+
}
615+
return { ...d, users: userList };
616+
})
617+
.filter(Boolean);
618+
608619
function exportCSV() {
609620
const rows = [['Account', 'Core Hours', 'Cost']];
610-
details.forEach(d => {
621+
filteredDetails.forEach(d => {
611622
rows.push([d.account, d.core_hours, d.cost]);
612623
(d.users || []).forEach(u => {
613624
rows.push([` ${u.user}`, u.core_hours, u.cost]);
@@ -648,17 +659,21 @@ function Details({ details, daily }) {
648659
React.createElement('option', { value: 'q' }, 'Q-to-date'),
649660
React.createElement('option', { value: 'y' }, 'Year')
650661
),
651-
['Partition', 'Account', 'Department', 'PI'].map(name =>
652-
React.createElement(
662+
['Partition', 'Account', 'User'].map(name => {
663+
const opts =
664+
name === 'Partition' ? partitions : name === 'Account' ? accounts : users;
665+
const key = name.toLowerCase();
666+
return React.createElement(
653667
'select',
654668
{
655669
key: name,
656-
onChange: e =>
657-
setFilters({ ...filters, [name.toLowerCase()]: e.target.value })
670+
value: filters[key],
671+
onChange: e => setFilters({ ...filters, [key]: e.target.value })
658672
},
659-
React.createElement('option', { value: '' }, name)
660-
)
661-
),
673+
React.createElement('option', { value: '' }, name),
674+
opts.map(o => React.createElement('option', { key: o, value: o }, o))
675+
);
676+
}),
662677
React.createElement('button', { onClick: exportCSV }, 'Export')
663678
),
664679
React.createElement(
@@ -681,7 +696,7 @@ function Details({ details, daily }) {
681696
React.createElement(
682697
'tbody',
683698
null,
684-
details.reduce((acc, d) => {
699+
filteredDetails.reduce((acc, d) => {
685700
acc.push(
686701
React.createElement(
687702
'tr',
@@ -980,7 +995,13 @@ function App() {
980995
}),
981996
data &&
982997
view === 'details' &&
983-
React.createElement(Details, { details: data.details, daily: data.daily }),
998+
React.createElement(Details, {
999+
details: data.details,
1000+
daily: data.daily,
1001+
partitions: data.partitions,
1002+
accounts: data.accounts,
1003+
users: data.users
1004+
}),
9841005
view === 'settings' && React.createElement(Rates, { onRatesUpdated: reload })
9851006
);
9861007
}

src/slurmdb.py

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -242,7 +242,7 @@ def fetch_usage_records(self, start_time, end_time):
242242
cpu_col = "cpus_req"
243243

244244
query = (
245-
f"SELECT j.id_job AS jobid, j.account, a.user AS user_name, j.time_start, j.time_end, "
245+
f"SELECT j.id_job AS jobid, j.account, j.partition, a.user AS user_name, j.time_start, j.time_end, "
246246
f"j.tres_alloc, j.{cpu_col} AS cpus_alloc FROM {job_table} AS j "
247247
f"LEFT JOIN {assoc_table} AS a ON j.id_assoc = a.id_assoc "
248248
f"WHERE j.time_start >= %s AND j.time_end <= %s"
@@ -261,6 +261,9 @@ def aggregate_usage(self, start_time, end_time):
261261
'daily_gpu': {},
262262
'monthly_gpu': {},
263263
'yearly_gpu': {},
264+
'partitions': set(),
265+
'accounts': set(),
266+
'users': set(),
264267
}
265268
for row in rows:
266269
start = self._to_datetime(row['time_start'])
@@ -271,6 +274,7 @@ def aggregate_usage(self, start_time, end_time):
271274
year = start.strftime('%Y')
272275
account = row.get('account') or 'unknown'
273276
user = row.get('user_name') or 'unknown'
277+
partition = row.get('partition') or 'unknown'
274278
job = str(row.get('jobid') or 'unknown')
275279
cpus = self._parse_tres(row.get('tres_alloc'), 'cpu')
276280
if not cpus:
@@ -288,6 +292,9 @@ def aggregate_usage(self, start_time, end_time):
288292
totals['daily_gpu'][day] = totals['daily_gpu'].get(day, 0.0) + gpus * dur_hours
289293
totals['monthly_gpu'][month] = totals['monthly_gpu'].get(month, 0.0) + gpus * dur_hours
290294
totals['yearly_gpu'][year] = totals['yearly_gpu'].get(year, 0.0) + gpus * dur_hours
295+
totals['partitions'].add(partition)
296+
totals['accounts'].add(account)
297+
totals['users'].add(user)
291298

292299
month_entry = agg.setdefault(month, {})
293300
acct_entry = month_entry.setdefault(
@@ -472,6 +479,9 @@ def export_summary(self, start_time, end_time):
472479
for y in sorted(set(totals['yearly']) | set(totals.get('yearly_gpu', {})))
473480
]
474481
summary['invoices'] = self.fetch_invoices(start_time, end_time)
482+
summary['partitions'] = sorted(totals.get('partitions', []))
483+
summary['accounts'] = sorted(totals.get('accounts', []))
484+
summary['users'] = sorted(totals.get('users', []))
475485
return summary
476486

477487

0 commit comments

Comments
 (0)