add shortjobs and resource sums to project and user lists

This commit is contained in:
Christoph Kluge
2026-02-04 12:57:48 +01:00
parent 5d7dd62b72
commit a7a95bb866
2 changed files with 86 additions and 7 deletions

View File

@@ -466,7 +466,7 @@ func (r *JobRepository) JobCountGrouped(
// AddJobCountGrouped augments existing statistics with additional job counts by category. // AddJobCountGrouped augments existing statistics with additional job counts by category.
// //
// This method enriches JobsStatistics returned by JobsStatsGrouped or JobCountGrouped // This method enriches JobsStatistics returned by JobsStatsGrouped or JobCountGrouped
// with counts of running or short-running jobs, matched by group ID. // with counts of running or short-running (based on ShortRunningJobsDuration) jobs, matched by group ID.
// //
// Parameters: // Parameters:
// - ctx: Context for security checks // - ctx: Context for security checks

View File

@@ -7,7 +7,7 @@
--> -->
<script> <script>
import { onMount } from "svelte"; import { getContext, onMount } from "svelte";
import { import {
Row, Row,
Col, Col,
@@ -18,6 +18,7 @@
Spinner, Spinner,
InputGroup, InputGroup,
Input, Input,
Tooltip
} from "@sveltestrap/sveltestrap"; } from "@sveltestrap/sveltestrap";
import { import {
queryStore, queryStore,
@@ -29,6 +30,9 @@
scramble, scramble,
scrambleNames, scrambleNames,
} from "./generic/utils.js"; } from "./generic/utils.js";
import {
formatDurationTime
} from "./generic/units.js";
import Filters from "./generic/Filters.svelte"; import Filters from "./generic/Filters.svelte";
/* Svelte 5 Props */ /* Svelte 5 Props */
@@ -40,6 +44,7 @@
/* Const Init */ /* Const Init */
const {} = init(); const {} = init();
const client = getContextClient(); const client = getContextClient();
const shortDuration = getContext("cc-config").jobList_hideShortRunningJobs; // Always configured
/* State Init*/ /* State Init*/
let filterComponent = $state(); // see why here: https://stackoverflow.com/questions/58287729/how-can-i-export-a-function-from-a-svelte-component-that-changes-a-value-in-the let filterComponent = $state(); // see why here: https://stackoverflow.com/questions/58287729/how-can-i-export-a-function-from-a-svelte-component-that-changes-a-value-in-the
@@ -48,21 +53,36 @@
let sorting = $state({ field: "totalJobs", direction: "desc" }); let sorting = $state({ field: "totalJobs", direction: "desc" });
/* Derived Vars */ /* Derived Vars */
const fetchRunning = $derived(jobFilters.some(jf => jf?.state?.length == 1 && jf?.state?.includes("running")));
const numCols = $derived.by(() => {
let colbase = 6
if (fetchRunning) {
colbase += 2
}
return colbase
})
let stats = $derived( let stats = $derived(
queryStore({ queryStore({
client: client, client: client,
query: gql` query: gql`
query($jobFilters: [JobFilter!]!) { query($jobFilters: [JobFilter!]!, $fetchRunning: Boolean!) {
rows: jobsStatistics(filter: $jobFilters, groupBy: ${type}) { rows: jobsStatistics(filter: $jobFilters, groupBy: ${type}) {
id id
name name
totalJobs totalJobs
shortJobs
totalCores @include(if: $fetchRunning)
totalAccs @include(if: $fetchRunning)
totalWalltime totalWalltime
totalCoreHours totalCoreHours
totalAccHours totalAccHours
} }
}`, }`,
variables: { jobFilters }, variables: {
jobFilters,
fetchRunning
},
}) })
); );
@@ -184,6 +204,60 @@
{/if} {/if}
</Button> </Button>
</th> </th>
<th scope="col">
<span class="mr-1">
Short Jobs
<Icon id="shortjobs-info" style="cursor:help;" size="sm" name="info-circle"/>
</span>
<Tooltip target={`shortjobs-info`} placement="top">
Job duration less than {formatDurationTime(shortDuration)}
</Tooltip>
&#8239; <!-- Narrow Non-Breaking Space -->
<Button
color={sorting.field == "shortJobs" ? "primary" : "light"}
size="sm"
onclick={() => changeSorting("shortJobs")}
>
{#if sorting?.field == "shortJobs"}
<!-- Note on Icon-Name: Arrow-indicator always down, only numeric-indicator switches -->
<Icon name={`sort-numeric-${sorting?.direction == 'desc' ? 'down-alt' : 'down'}`} />
{:else}
<Icon name="three-dots-vertical" />
{/if}
</Button>
</th>
{#if fetchRunning}
<th scope="col">
Total Cores
<Button
color={sorting.field == "totalCores" ? "primary" : "light"}
size="sm"
onclick={() => changeSorting("totalCores")}
>
{#if sorting?.field == "totalJCores"}
<!-- Note on Icon-Name: Arrow-indicator always down, only numeric-indicator switches -->
<Icon name={`sort-numeric-${sorting?.direction == 'desc' ? 'down-alt' : 'down'}`} />
{:else}
<Icon name="three-dots-vertical" />
{/if}
</Button>
</th>
<th scope="col">
Total Accelerators
<Button
color={sorting.field == "totalAccs" ? "primary" : "light"}
size="sm"
onclick={() => changeSorting("totalAccs")}
>
{#if sorting?.field == "totalAccs"}
<!-- Note on Icon-Name: Arrow-indicator always down, only numeric-indicator switches -->
<Icon name={`sort-numeric-${sorting?.direction == 'desc' ? 'down-alt' : 'down'}`} />
{:else}
<Icon name="three-dots-vertical" />
{/if}
</Button>
</th>
{/if}
<th scope="col"> <th scope="col">
Total Walltime Total Walltime
<Button <Button
@@ -231,11 +305,11 @@
<tbody> <tbody>
{#if $stats.fetching} {#if $stats.fetching}
<tr> <tr>
<td colspan="4" style="text-align: center;"><Spinner secondary /></td> <td colspan={numCols} style="text-align: center;"><Spinner secondary /></td>
</tr> </tr>
{:else if $stats.error} {:else if $stats.error}
<tr> <tr>
<td colspan="4" <td colspan={numCols}
><Card body color="danger" class="mb-3">{$stats.error.message}</Card ><Card body color="danger" class="mb-3">{$stats.error.message}</Card
></td ></td
> >
@@ -266,13 +340,18 @@
> >
{/if} {/if}
<td>{row.totalJobs}</td> <td>{row.totalJobs}</td>
<td>{row.shortJobs}</td>
{#if fetchRunning}
<td>{row.totalCores}</td>
<td>{row.totalAccs}</td>
{/if}
<td>{row.totalWalltime}</td> <td>{row.totalWalltime}</td>
<td>{row.totalCoreHours}</td> <td>{row.totalCoreHours}</td>
<td>{row.totalAccHours}</td> <td>{row.totalAccHours}</td>
</tr> </tr>
{:else} {:else}
<tr> <tr>
<td colspan="4"><i>No {type.toLowerCase()}s/jobs found</i></td> <td colspan={numCols}><i>No {type.toLowerCase()}s/jobs found</i></td>
</tr> </tr>
{/each} {/each}
{/if} {/if}