mirror of
				https://github.com/ClusterCockpit/cc-backend
				synced 2025-11-04 09:35:07 +01:00 
			
		
		
		
	feat: persist analysis and status pie selections
This commit is contained in:
		@@ -26,21 +26,25 @@ var Keys schema.ProgramConfig = schema.ProgramConfig{
 | 
				
			|||||||
	StopJobsExceedingWalltime: 0,
 | 
						StopJobsExceedingWalltime: 0,
 | 
				
			||||||
	ShortRunningJobsDuration:  5 * 60,
 | 
						ShortRunningJobsDuration:  5 * 60,
 | 
				
			||||||
	UiDefaults: map[string]interface{}{
 | 
						UiDefaults: map[string]interface{}{
 | 
				
			||||||
		"analysis_view_histogramMetrics":     []string{"flops_any", "mem_bw", "mem_used"},
 | 
							"analysis_view_histogramMetrics":         []string{"flops_any", "mem_bw", "mem_used"},
 | 
				
			||||||
		"analysis_view_scatterPlotMetrics":   [][]string{{"flops_any", "mem_bw"}, {"flops_any", "cpu_load"}, {"cpu_load", "mem_bw"}},
 | 
							"analysis_view_scatterPlotMetrics":       [][]string{{"flops_any", "mem_bw"}, {"flops_any", "cpu_load"}, {"cpu_load", "mem_bw"}},
 | 
				
			||||||
		"job_view_nodestats_selectedMetrics": []string{"flops_any", "mem_bw", "mem_used"},
 | 
							"job_view_nodestats_selectedMetrics":     []string{"flops_any", "mem_bw", "mem_used"},
 | 
				
			||||||
		"job_view_polarPlotMetrics":          []string{"flops_any", "mem_bw", "mem_used"},
 | 
							"job_view_polarPlotMetrics":              []string{"flops_any", "mem_bw", "mem_used"},
 | 
				
			||||||
		"job_view_selectedMetrics":           []string{"flops_any", "mem_bw", "mem_used"},
 | 
							"job_view_selectedMetrics":               []string{"flops_any", "mem_bw", "mem_used"},
 | 
				
			||||||
		"plot_general_colorBackground":       true,
 | 
							"plot_general_colorBackground":           true,
 | 
				
			||||||
		"plot_general_colorscheme":           []string{"#00bfff", "#0000ff", "#ff00ff", "#ff0000", "#ff8000", "#ffff00", "#80ff00"},
 | 
							"plot_general_colorscheme":               []string{"#00bfff", "#0000ff", "#ff00ff", "#ff0000", "#ff8000", "#ffff00", "#80ff00"},
 | 
				
			||||||
		"plot_general_lineWidth":             3,
 | 
							"plot_general_lineWidth":                 3,
 | 
				
			||||||
		"plot_list_jobsPerPage":              50,
 | 
							"plot_list_jobsPerPage":                  50,
 | 
				
			||||||
		"plot_list_selectedMetrics":          []string{"cpu_load", "mem_used", "flops_any", "mem_bw"},
 | 
							"plot_list_selectedMetrics":              []string{"cpu_load", "mem_used", "flops_any", "mem_bw"},
 | 
				
			||||||
		"plot_view_plotsPerRow":              3,
 | 
							"plot_view_plotsPerRow":                  3,
 | 
				
			||||||
		"plot_view_showPolarplot":            true,
 | 
							"plot_view_showPolarplot":                true,
 | 
				
			||||||
		"plot_view_showRoofline":             true,
 | 
							"plot_view_showRoofline":                 true,
 | 
				
			||||||
		"plot_view_showStatTable":            true,
 | 
							"plot_view_showStatTable":                true,
 | 
				
			||||||
		"system_view_selectedMetric":         "cpu_load",
 | 
							"system_view_selectedMetric":             "cpu_load",
 | 
				
			||||||
 | 
							"analysis_view_selectedTopEntity":        "user",
 | 
				
			||||||
 | 
							"analysis_view_selectedTopCategory":      "totalWalltime",
 | 
				
			||||||
 | 
							"status_view_selectedTopUserCategory":    "totalJobs",
 | 
				
			||||||
 | 
							"status_view_selectedTopProjectCategory": "totalJobs",
 | 
				
			||||||
	},
 | 
						},
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,7 +1,7 @@
 | 
				
			|||||||
<script>
 | 
					<script>
 | 
				
			||||||
    import { init, convert2uplot } from './utils.js'
 | 
					    import { init, convert2uplot } from './utils.js'
 | 
				
			||||||
    import { getContext, onMount } from 'svelte'
 | 
					    import { getContext, onMount } from 'svelte'
 | 
				
			||||||
    import { queryStore, gql, getContextClient  } from '@urql/svelte'
 | 
					    import { queryStore, gql, getContextClient, mutationStore } from '@urql/svelte'
 | 
				
			||||||
    import { Row, Col, Spinner, Card, Table, Icon } from 'sveltestrap'
 | 
					    import { Row, Col, Spinner, Card, Table, Icon } from 'sveltestrap'
 | 
				
			||||||
    import Filters from './filters/Filters.svelte'
 | 
					    import Filters from './filters/Filters.svelte'
 | 
				
			||||||
    import PlotSelection from './PlotSelection.svelte'
 | 
					    import PlotSelection from './PlotSelection.svelte'
 | 
				
			||||||
@@ -49,11 +49,12 @@
 | 
				
			|||||||
        {key: 'totalAccHours',  label: 'Accelerator Hours'}
 | 
					        {key: 'totalAccHours',  label: 'Accelerator Hours'}
 | 
				
			||||||
    ]
 | 
					    ]
 | 
				
			||||||
    const groupOptions = [
 | 
					    const groupOptions = [
 | 
				
			||||||
        {key: 'User',  label: 'User Name'},
 | 
					        {key: 'user',  label: 'User Name'},
 | 
				
			||||||
        {key: 'Project', label: 'Project ID'}
 | 
					        {key: 'project', label: 'Project ID'}
 | 
				
			||||||
    ]
 | 
					    ]
 | 
				
			||||||
    let sortSelection = sortOptions[0] // Default: Walltime
 | 
					
 | 
				
			||||||
    let groupSelection = groupOptions[0] // Default: Users
 | 
					    let sortSelection = sortOptions.find((option) => option.key == ccconfig[`analysis_view_selectedTopCategory:${filterPresets.cluster}`]) || sortOptions.find((option) => option.key == ccconfig.analysis_view_selectedTopCategory)
 | 
				
			||||||
 | 
					    let groupSelection = groupOptions.find((option) => option.key == ccconfig[`analysis_view_selectedTopEntity:${filterPresets.cluster}`]) || groupOptions.find((option) => option.key == ccconfig.analysis_view_selectedTopEntity)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    getContext('on-init')(({ data }) => {
 | 
					    getContext('on-init')(({ data }) => {
 | 
				
			||||||
        if (data != null) {
 | 
					        if (data != null) {
 | 
				
			||||||
@@ -126,6 +127,53 @@
 | 
				
			|||||||
        variables: { jobFilters, rows: 50, cols: 50, minX: 0.01, minY: 1., maxX: 1000., maxY }
 | 
					        variables: { jobFilters, rows: 50, cols: 50, minX: 0.01, minY: 1., maxX: 1000., maxY }
 | 
				
			||||||
    })
 | 
					    })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const updateConfigurationMutation = ({ name, value }) => {
 | 
				
			||||||
 | 
					        return mutationStore({
 | 
				
			||||||
 | 
					            client: client,
 | 
				
			||||||
 | 
					            query: gql`
 | 
				
			||||||
 | 
					                mutation ($name: String!, $value: String!) {
 | 
				
			||||||
 | 
					                    updateConfiguration(name: $name, value: $value)
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            `,
 | 
				
			||||||
 | 
					            variables: { name, value }
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    function updateEntityConfiguration(select) {
 | 
				
			||||||
 | 
					        if (ccconfig[`analysis_view_selectedTopEntity:${filterPresets.cluster}`] != select) {
 | 
				
			||||||
 | 
					            updateConfigurationMutation({ name: `analysis_view_selectedTopEntity:${filterPresets.cluster}`, value: JSON.stringify(select) })
 | 
				
			||||||
 | 
					            .subscribe(res => {
 | 
				
			||||||
 | 
					                if (res.fetching === false && !res.error) {
 | 
				
			||||||
 | 
					                    // console.log(`analysis_view_selectedTopEntity:${filterPresets.cluster}` + ' -> Updated!')
 | 
				
			||||||
 | 
					                } else if (res.fetching === false && res.error) {
 | 
				
			||||||
 | 
					                    throw res.error
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            })
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            // console.log('No Mutation Required: Entity')
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    function updateCategoryConfiguration(select) {
 | 
				
			||||||
 | 
					        if (ccconfig[`analysis_view_selectedTopCategory:${filterPresets.cluster}`] != select) {
 | 
				
			||||||
 | 
					            updateConfigurationMutation({ name: `analysis_view_selectedTopCategory:${filterPresets.cluster}`, value: JSON.stringify(select) })
 | 
				
			||||||
 | 
					            .subscribe(res => {
 | 
				
			||||||
 | 
					                if (res.fetching === false && !res.error) {
 | 
				
			||||||
 | 
					                    // console.log(`analysis_view_selectedTopCategory:${filterPresets.cluster}` + ' -> Updated!')
 | 
				
			||||||
 | 
					                } else if (res.fetching === false && res.error) {
 | 
				
			||||||
 | 
					                    throw res.error
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            })
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            // console.log('No Mutation Required: Category')
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    $: updateEntityConfiguration(groupSelection.key)
 | 
				
			||||||
 | 
					    $: updateCategoryConfiguration(sortSelection.key)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    onMount(() => filterComponent.update())
 | 
					    onMount(() => filterComponent.update())
 | 
				
			||||||
</script>
 | 
					</script>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -200,7 +248,7 @@
 | 
				
			|||||||
                <select class="p-0" bind:value={groupSelection}>
 | 
					                <select class="p-0" bind:value={groupSelection}>
 | 
				
			||||||
                    {#each groupOptions as option}
 | 
					                    {#each groupOptions as option}
 | 
				
			||||||
                        <option value={option}>
 | 
					                        <option value={option}>
 | 
				
			||||||
                            {option.key}s
 | 
					                            {option.key.charAt(0).toUpperCase() + option.key.slice(1)}s
 | 
				
			||||||
                        </option>
 | 
					                        </option>
 | 
				
			||||||
                    {/each}
 | 
					                    {/each}
 | 
				
			||||||
                </select>
 | 
					                </select>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,4 +1,5 @@
 | 
				
			|||||||
<script>
 | 
					<script>
 | 
				
			||||||
 | 
					    import { getContext } from 'svelte'
 | 
				
			||||||
    import Refresher from './joblist/Refresher.svelte'
 | 
					    import Refresher from './joblist/Refresher.svelte'
 | 
				
			||||||
    import Roofline, { transformPerNodeData } from './plots/Roofline.svelte'
 | 
					    import Roofline, { transformPerNodeData } from './plots/Roofline.svelte'
 | 
				
			||||||
    import Pie, { colors } from './plots/Pie.svelte'
 | 
					    import Pie, { colors } from './plots/Pie.svelte'
 | 
				
			||||||
@@ -6,9 +7,10 @@
 | 
				
			|||||||
    import { Row, Col, Spinner, Card, CardHeader, CardTitle, CardBody, Table, Progress, Icon } from 'sveltestrap'
 | 
					    import { Row, Col, Spinner, Card, CardHeader, CardTitle, CardBody, Table, Progress, Icon } from 'sveltestrap'
 | 
				
			||||||
    import { init, convert2uplot } from './utils.js'
 | 
					    import { init, convert2uplot } from './utils.js'
 | 
				
			||||||
    import { scaleNumbers } from './units.js'
 | 
					    import { scaleNumbers } from './units.js'
 | 
				
			||||||
    import { queryStore, gql, getContextClient  } from '@urql/svelte'
 | 
					    import { queryStore, gql, getContextClient, mutationStore } from '@urql/svelte'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const { query: initq } = init()
 | 
					    const { query: initq } = init()
 | 
				
			||||||
 | 
					    const ccconfig = getContext("cc-config")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    export let cluster
 | 
					    export let cluster
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -20,8 +22,9 @@
 | 
				
			|||||||
        {key: 'totalCores', label: 'Cores'},
 | 
					        {key: 'totalCores', label: 'Cores'},
 | 
				
			||||||
        {key: 'totalAccs',  label: 'Accelerators'},
 | 
					        {key: 'totalAccs',  label: 'Accelerators'},
 | 
				
			||||||
    ]
 | 
					    ]
 | 
				
			||||||
    let topProjectSelection = topOptions[0] // Default: Jobs
 | 
					
 | 
				
			||||||
    let topUserSelection    = topOptions[0] // Default: Jobs
 | 
					    let topProjectSelection = topOptions.find((option) => option.key == ccconfig[`status_view_selectedTopProjectCategory:${cluster}`]) || topOptions.find((option) => option.key == ccconfig.status_view_selectedTopProjectCategory)
 | 
				
			||||||
 | 
					    let topUserSelection    = topOptions.find((option) => option.key == ccconfig[`status_view_selectedTopUserCategory:${cluster}`])    || topOptions.find((option) => option.key == ccconfig.status_view_selectedTopUserCategory)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const client = getContextClient();
 | 
					    const client = getContextClient();
 | 
				
			||||||
    $: mainQuery = queryStore({
 | 
					    $: mainQuery = queryStore({
 | 
				
			||||||
@@ -107,6 +110,51 @@
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const updateConfigurationMutation = ({ name, value }) => {
 | 
				
			||||||
 | 
					        return mutationStore({
 | 
				
			||||||
 | 
					            client: client,
 | 
				
			||||||
 | 
					            query: gql`
 | 
				
			||||||
 | 
					                mutation ($name: String!, $value: String!) {
 | 
				
			||||||
 | 
					                    updateConfiguration(name: $name, value: $value)
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            `,
 | 
				
			||||||
 | 
					            variables: { name, value }
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    function updateTopUserConfiguration(select) {
 | 
				
			||||||
 | 
					        if (ccconfig[`status_view_selectedTopUserCategory:${cluster}`] != select) {
 | 
				
			||||||
 | 
					            updateConfigurationMutation({ name: `status_view_selectedTopUserCategory:${cluster}`, value: JSON.stringify(select) })
 | 
				
			||||||
 | 
					            .subscribe(res => {
 | 
				
			||||||
 | 
					                if (res.fetching === false && !res.error) {
 | 
				
			||||||
 | 
					                    // console.log(`status_view_selectedTopUserCategory:${cluster}` + ' -> Updated!')
 | 
				
			||||||
 | 
					                } else if (res.fetching === false && res.error) {
 | 
				
			||||||
 | 
					                    throw res.error
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            })
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            // console.log('No Mutation Required: Top User')
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    function updateTopProjectConfiguration(select) {
 | 
				
			||||||
 | 
					        if (ccconfig[`status_view_selectedTopProjectCategory:${cluster}`] != select) {
 | 
				
			||||||
 | 
					            updateConfigurationMutation({ name: `status_view_selectedTopProjectCategory:${cluster}`, value: JSON.stringify(select) })
 | 
				
			||||||
 | 
					            .subscribe(res => {
 | 
				
			||||||
 | 
					                if (res.fetching === false && !res.error) {
 | 
				
			||||||
 | 
					                    // console.log(`status_view_selectedTopProjectCategory:${cluster}` + ' -> Updated!')
 | 
				
			||||||
 | 
					                } else if (res.fetching === false && res.error) {
 | 
				
			||||||
 | 
					                    throw res.error
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            })
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            // console.log('No Mutation Required: Top Project')
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    $: updateTopUserConfiguration(topUserSelection.key)
 | 
				
			||||||
 | 
					    $: updateTopProjectConfiguration(topProjectSelection.key)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
</script>
 | 
					</script>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<!-- Loading indicator & Refresh -->
 | 
					<!-- Loading indicator & Refresh -->
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user