2023-01-27 18:36:58 +01:00
<!--
2022-06-22 11:20:57 +02:00
@component List of users or projects
-->
< script >
2023-05-03 16:41:17 +02:00
import { onMount } from "svelte";
import { init } from "./utils.js";
2023-05-05 10:07:12 +02:00
import {
Row,
Col,
Button,
Icon,
Table,
Card,
Spinner,
InputGroup,
Input,
} from "sveltestrap";
2023-05-03 16:41:17 +02:00
import Filters from "./filters/Filters.svelte";
import { queryStore , gql , getContextClient } from "@urql/svelte";
import { scramble , scrambleNames } from "./joblist/JobInfo.svelte";
2022-06-22 11:20:57 +02:00
2023-05-03 16:41:17 +02:00
const {} = init();
2022-06-22 11:20:57 +02:00
2023-05-03 16:41:17 +02:00
export let type;
export let filterPresets;
2022-06-22 11:20:57 +02:00
2023-06-19 17:59:44 +02:00
// By default, look at the jobs of the last 30 days:
if (filterPresets?.startTime == null) {
if (filterPresets == null)
filterPresets = {}
const lastMonth = (new Date(Date.now() - (30*24*60*60*1000))).toISOString()
const now = (new Date(Date.now())).toISOString()
filterPresets.startTime = { from : lastMonth , to : now , text : 'Last 30 Days' , url : 'last30d' }
}
2023-05-03 16:41:17 +02:00
console.assert(
type == "USER" || type == "PROJECT",
"Invalid list type provided!"
);
2022-06-22 11:20:57 +02:00
2023-06-06 17:03:08 +02:00
let filterComponent; // 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 jobFilters = [];
let nameFilter = "";
let sorting = { field : "totalJobs" , direction : "down" } ;
2023-05-09 15:01:56 +02:00
const client = getContextClient();
$: stats = queryStore({
2023-05-12 11:19:37 +02:00
client: client,
query: gql`
2023-06-06 17:03:08 +02:00
query($jobFilters: [JobFilter!]!) {
rows: jobsStatistics(filter: $jobFilters, groupBy: ${ type } ) {
2023-05-12 11:19:37 +02:00
id
name
totalJobs
totalWalltime
totalCoreHours
2023-06-09 12:28:24 +02:00
totalAccHours
2023-05-12 11:19:37 +02:00
}
}`,
2023-06-06 17:03:08 +02:00
variables: { jobFilters }
2023-05-03 16:41:17 +02:00
});
2022-06-22 11:20:57 +02:00
function changeSorting(event, field) {
2023-05-03 16:41:17 +02:00
let target = event.target;
while (target.tagName != "BUTTON") target = target.parentElement;
2022-06-22 11:20:57 +02:00
2023-05-03 16:41:17 +02:00
let direction = target.children[0].className.includes("up")
? "down"
: "up";
target.children[0].className = `bi-sort-numeric-${ direction } `;
sorting = { field , direction } ;
2022-06-22 11:20:57 +02:00
}
function sort(stats, sorting, nameFilter) {
2023-05-03 16:41:17 +02:00
const cmp =
sorting.field == "id"
? sorting.direction == "up"
? (a, b) => a.id < b.id
: (a, b) => a.id > b.id
: sorting.direction == "up"
2022-06-22 11:20:57 +02:00
? (a, b) => a[sorting.field] - b[sorting.field]
2023-05-03 16:41:17 +02:00
: (a, b) => b[sorting.field] - a[sorting.field];
2023-01-27 18:36:58 +01:00
2023-05-03 16:41:17 +02:00
return stats.filter((u) => u.id.includes(nameFilter)).sort(cmp);
2022-06-22 11:20:57 +02:00
}
2023-06-06 17:03:08 +02:00
onMount(() => filterComponent.update());
2022-06-22 11:20:57 +02:00
< / script >
< Row >
< Col xs = "auto" >
< InputGroup >
< Button disabled outline >
Search { type . toLowerCase ()} s
< / Button >
2023-05-03 16:41:17 +02:00
< Input
bind:value={ nameFilter }
placeholder="Filter by {{
USER: 'username',
PROJECT: 'project',
}[type]}"
/>
2022-06-22 11:20:57 +02:00
< / InputGroup >
< / Col >
< Col xs = "auto" >
< Filters
2023-06-06 17:03:08 +02:00
bind:this={ filterComponent }
2023-05-03 16:41:17 +02:00
{ filterPresets }
2022-06-22 11:20:57 +02:00
startTimeQuickSelect={ true }
menuText="Only { type . toLowerCase ()} s with jobs that match the filters will show up"
on:update={({ detail }) => {
2023-06-06 17:03:08 +02:00
jobFilters = detail.filters;
2023-05-03 16:41:17 +02:00
}}
/>
2022-06-22 11:20:57 +02:00
< / Col >
< / Row >
< Table >
< thead >
< tr >
< th scope = "col" >
2023-07-17 15:45:40 +02:00
{({
USER: "Username",
PROJECT: "Project Name",
})[type]}
2023-05-03 16:41:17 +02:00
< Button
color={ sorting . field == "id" ? "primary" : "light" }
size="sm"
on:click={( e ) => changeSorting ( e , "id" )}
>
2022-06-22 11:20:57 +02:00
< Icon name = "sort-numeric-down" / >
< / Button >
< / th >
2023-05-03 16:41:17 +02:00
{ #if type == "USER" }
2023-02-17 10:45:27 +01:00
< th scope = "col" >
Name
2023-05-03 16:41:17 +02:00
< Button
color={ sorting . field == "name" ? "primary" : "light" }
size="sm"
on:click={( e ) => changeSorting ( e , "name" )}
>
2023-02-17 10:45:27 +01:00
< Icon name = "sort-numeric-down" / >
< / Button >
< / th >
{ /if }
2022-06-22 11:20:57 +02:00
< th scope = "col" >
Total Jobs
2023-05-03 16:41:17 +02:00
< Button
color={ sorting . field == "totalJobs" ? "primary" : "light" }
size="sm"
on:click={( e ) => changeSorting ( e , "totalJobs" )}
>
2022-06-22 11:20:57 +02:00
< Icon name = "sort-numeric-down" / >
< / Button >
< / th >
< th scope = "col" >
Total Walltime
2023-05-03 16:41:17 +02:00
< Button
color={ sorting . field == "totalWalltime"
? "primary"
: "light"}
size="sm"
on:click={( e ) => changeSorting ( e , "totalWalltime" )}
>
2022-06-22 11:20:57 +02:00
< Icon name = "sort-numeric-down" / >
< / Button >
< / th >
< th scope = "col" >
Total Core Hours
2023-05-03 16:41:17 +02:00
< Button
color={ sorting . field == "totalCoreHours"
? "primary"
: "light"}
size="sm"
on:click={( e ) => changeSorting ( e , "totalCoreHours" )}
>
2022-06-22 11:20:57 +02:00
< Icon name = "sort-numeric-down" / >
< / Button >
< / th >
2023-06-09 12:28:24 +02:00
< th scope = "col" >
Total Accelerator Hours
< Button
color={ sorting . field == "totalAccHours"
? "primary"
: "light"}
size="sm"
on:click={( e ) => changeSorting ( e , "totalAccHours" )}
>
< Icon name = "sort-numeric-down" / >
< / Button >
< / th >
2022-06-22 11:20:57 +02:00
< / tr >
< / thead >
< tbody >
{ #if $stats . fetching }
< tr >
2023-05-03 16:41:17 +02:00
< td colspan = "4" style = "text-align: center;"
>< Spinner secondary / > < /td
>
2022-06-22 11:20:57 +02:00
< / tr >
{ :else if $stats . error }
< tr >
2023-05-03 16:41:17 +02:00
< td colspan = "4"
>< Card body color = "danger" class = "mb-3"
>{ $stats . error . message } < /Card
>< /td
>
2022-06-22 11:20:57 +02:00
< / tr >
{ :else if $stats . data }
{ #each sort ( $stats . data . rows , sorting , nameFilter ) as row ( row . id )}
< tr >
< td >
2023-05-03 16:41:17 +02:00
{ #if type == "USER" }
< a href = "/monitoring/user/ { row . id } "
>{ scrambleNames ? scramble ( row . id ) : row . id } < /a
>
{ :else if type == "PROJECT" }
< a href = "/monitoring/jobs/?project= { row . id } "
2023-07-17 15:45:40 +02:00
>{ scrambleNames ? scramble ( row . id ) : row . id } < /a
2023-05-03 16:41:17 +02:00
>
2022-06-22 11:20:57 +02:00
{ : else }
{ row . id }
{ /if }
< / td >
2023-05-03 16:41:17 +02:00
{ #if type == "USER" }
2023-07-17 15:45:40 +02:00
< td > { scrambleNames ? scramble ( row ? . name ? row . name : "-" ) : row ? . name ? row . name : "-" } </ td >
2023-02-17 10:45:27 +01:00
{ /if }
2022-06-22 11:20:57 +02:00
< td > { row . totalJobs } </ td >
< td > { row . totalWalltime } </ td >
< td > { row . totalCoreHours } </ td >
2023-06-09 12:28:24 +02:00
< td > { row . totalAccHours } </ td >
2022-06-22 11:20:57 +02:00
< / tr >
{ : else }
< tr >
2023-05-03 16:41:17 +02:00
< td colspan = "4"
>< i > No { type . toLowerCase ()} s/jobs found</ i > < /td
>
2022-06-22 11:20:57 +02:00
< / tr >
{ /each }
{ /if }
< / tbody >
2023-01-27 18:36:58 +01:00
< / Table >