mirror of
https://github.com/ClusterCockpit/cc-backend
synced 2024-11-10 00:47:26 +01:00
Merge pull request #259 from ClusterCockpit/hotfix
Hotfix: Improve hasNextPage and jobName Queries
This commit is contained in:
commit
0b2f2214f9
@ -243,14 +243,21 @@ func (r *queryResolver) Jobs(ctx context.Context, filter []*model.JobFilter, pag
|
|||||||
|
|
||||||
if !config.Keys.UiDefaults["job_list_usePaging"].(bool) {
|
if !config.Keys.UiDefaults["job_list_usePaging"].(bool) {
|
||||||
hasNextPage := false
|
hasNextPage := false
|
||||||
page.Page += 1
|
// page.Page += 1 : Simple, but expensive
|
||||||
|
// Example Page 4 @ 10 IpP : Does item 41 exist?
|
||||||
|
// Minimal Page 41 @ 1 IpP : If len(result) is 1, Page 5 @ 10 IpP exists.
|
||||||
|
nextPage := &model.PageRequest{
|
||||||
|
ItemsPerPage: 1,
|
||||||
|
Page: ((page.Page * page.ItemsPerPage) + 1),
|
||||||
|
}
|
||||||
|
|
||||||
nextJobs, err := r.Repo.QueryJobs(ctx, filter, page, order)
|
nextJobs, err := r.Repo.QueryJobs(ctx, filter, nextPage, order)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Warn("Error while querying next jobs")
|
log.Warn("Error while querying next jobs")
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if len(nextJobs) > 0 {
|
|
||||||
|
if len(nextJobs) == 1 {
|
||||||
hasNextPage = true
|
hasNextPage = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -135,7 +135,7 @@ func BuildWhereClause(filter *model.JobFilter, query sq.SelectBuilder) sq.Select
|
|||||||
query = buildStringCondition("job.project", filter.Project, query)
|
query = buildStringCondition("job.project", filter.Project, query)
|
||||||
}
|
}
|
||||||
if filter.JobName != nil {
|
if filter.JobName != nil {
|
||||||
query = buildStringCondition("job.meta_data", filter.JobName, query)
|
query = buildMetaJsonCondition("jobName", filter.JobName, query)
|
||||||
}
|
}
|
||||||
if filter.Cluster != nil {
|
if filter.Cluster != nil {
|
||||||
query = buildStringCondition("job.cluster", filter.Cluster, query)
|
query = buildStringCondition("job.cluster", filter.Cluster, query)
|
||||||
@ -235,6 +235,25 @@ func buildStringCondition(field string, cond *model.StringInput, query sq.Select
|
|||||||
return query
|
return query
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func buildMetaJsonCondition(jsonField string, cond *model.StringInput, query sq.SelectBuilder) sq.SelectBuilder {
|
||||||
|
if cond.Eq != nil {
|
||||||
|
return query.Where("JSON_EXTRACT(meta_data, \"$."+jsonField+"\") = ?", *cond.Eq)
|
||||||
|
}
|
||||||
|
if cond.Neq != nil {
|
||||||
|
return query.Where("JSON_EXTRACT(meta_data, \"$."+jsonField+"\") != ?", *cond.Neq)
|
||||||
|
}
|
||||||
|
if cond.StartsWith != nil {
|
||||||
|
return query.Where("JSON_EXTRACT(meta_data, \"$."+jsonField+"\") LIKE ?", fmt.Sprint(*cond.StartsWith, "%"))
|
||||||
|
}
|
||||||
|
if cond.EndsWith != nil {
|
||||||
|
return query.Where("JSON_EXTRACT(meta_data, \"$."+jsonField+"\") LIKE ?", fmt.Sprint("%", *cond.EndsWith))
|
||||||
|
}
|
||||||
|
if cond.Contains != nil {
|
||||||
|
return query.Where("JSON_EXTRACT(meta_data, \"$."+jsonField+"\") LIKE ?", fmt.Sprint("%", *cond.Contains, "%"))
|
||||||
|
}
|
||||||
|
return query
|
||||||
|
}
|
||||||
|
|
||||||
var matchFirstCap = regexp.MustCompile("(.)([A-Z][a-z]+)")
|
var matchFirstCap = regexp.MustCompile("(.)([A-Z][a-z]+)")
|
||||||
var matchAllCap = regexp.MustCompile("([a-z0-9])([A-Z])")
|
var matchAllCap = regexp.MustCompile("([a-z0-9])([A-Z])")
|
||||||
|
|
||||||
|
@ -24,9 +24,9 @@ var (
|
|||||||
type UserCfgRepo struct {
|
type UserCfgRepo struct {
|
||||||
DB *sqlx.DB
|
DB *sqlx.DB
|
||||||
Lookup *sqlx.Stmt
|
Lookup *sqlx.Stmt
|
||||||
lock sync.RWMutex
|
|
||||||
uiDefaults map[string]interface{}
|
uiDefaults map[string]interface{}
|
||||||
cache *lrucache.Cache
|
cache *lrucache.Cache
|
||||||
|
lock sync.RWMutex
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetUserCfgRepo() *UserCfgRepo {
|
func GetUserCfgRepo() *UserCfgRepo {
|
||||||
@ -112,8 +112,8 @@ func (uCfg *UserCfgRepo) GetUIConfig(user *schema.User) (map[string]interface{},
|
|||||||
// configuration.
|
// configuration.
|
||||||
func (uCfg *UserCfgRepo) UpdateConfig(
|
func (uCfg *UserCfgRepo) UpdateConfig(
|
||||||
key, value string,
|
key, value string,
|
||||||
user *schema.User) error {
|
user *schema.User,
|
||||||
|
) error {
|
||||||
if user == nil {
|
if user == nil {
|
||||||
var val interface{}
|
var val interface{}
|
||||||
if err := json.Unmarshal([]byte(value), &val); err != nil {
|
if err := json.Unmarshal([]byte(value), &val); err != nil {
|
||||||
|
@ -341,7 +341,6 @@
|
|||||||
scopes={item.data.map((x) => x.scope)}
|
scopes={item.data.map((x) => x.scope)}
|
||||||
{width}
|
{width}
|
||||||
isShared={$initq.data.job.exclusive != 1}
|
isShared={$initq.data.job.exclusive != 1}
|
||||||
resources={$initq.data.job.resources}
|
|
||||||
/>
|
/>
|
||||||
{:else}
|
{:else}
|
||||||
<Card body color="warning"
|
<Card body color="warning"
|
||||||
@ -361,7 +360,7 @@
|
|||||||
<div style="margin: 10px;">
|
<div style="margin: 10px;">
|
||||||
<Card color="warning">
|
<Card color="warning">
|
||||||
<CardHeader>
|
<CardHeader>
|
||||||
<CardTitle>Missing Metrics/Reseources</CardTitle>
|
<CardTitle>Missing Metrics/Resources</CardTitle>
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
<CardBody>
|
<CardBody>
|
||||||
{#if missingMetrics.length > 0}
|
{#if missingMetrics.length > 0}
|
||||||
|
@ -24,7 +24,7 @@
|
|||||||
export let configName;
|
export let configName;
|
||||||
export let allMetrics = null;
|
export let allMetrics = null;
|
||||||
export let cluster = null;
|
export let cluster = null;
|
||||||
export let showFootprint;
|
export let showFootprint = false;
|
||||||
export let view = "job";
|
export let view = "job";
|
||||||
|
|
||||||
const clusters = getContext("clusters"),
|
const clusters = getContext("clusters"),
|
||||||
|
@ -275,7 +275,7 @@
|
|||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<Row cols={3} class="p-2 g-2">
|
<Row cols={4} class="p-2 g-2">
|
||||||
<!-- LINE WIDTH -->
|
<!-- LINE WIDTH -->
|
||||||
<Col
|
<Col
|
||||||
><Card class="h-100">
|
><Card class="h-100">
|
||||||
@ -422,6 +422,60 @@
|
|||||||
</form>
|
</form>
|
||||||
</Card></Col
|
</Card></Col
|
||||||
>
|
>
|
||||||
|
|
||||||
|
<!-- PAGING -->
|
||||||
|
<Col
|
||||||
|
><Card class="h-100">
|
||||||
|
<form
|
||||||
|
id="paging-form"
|
||||||
|
method="post"
|
||||||
|
action="/api/configuration/"
|
||||||
|
class="card-body"
|
||||||
|
on:submit|preventDefault={() =>
|
||||||
|
handleSettingSubmit("#paging-form", "pag")}
|
||||||
|
>
|
||||||
|
<!-- Svelte 'class' directive only on DOMs directly, normal 'class="xxx"' does not work, so style-array it is. -->
|
||||||
|
<CardTitle
|
||||||
|
style="margin-bottom: 1em; display: flex; align-items: center;"
|
||||||
|
>
|
||||||
|
<div>Paging Type</div>
|
||||||
|
{#if displayMessage && message.target == "pag"}<div
|
||||||
|
style="margin-left: auto; font-size: 0.9em;"
|
||||||
|
>
|
||||||
|
<code style="color: {message.color};" out:fade
|
||||||
|
>Update: {message.msg}</code
|
||||||
|
>
|
||||||
|
</div>{/if}
|
||||||
|
</CardTitle>
|
||||||
|
<input type="hidden" name="key" value="job_list_usePaging" />
|
||||||
|
<div class="mb-3">
|
||||||
|
<div>
|
||||||
|
{#if config.job_list_usePaging}
|
||||||
|
<input type="radio" id="true" name="value" value="true" checked />
|
||||||
|
{:else}
|
||||||
|
<input type="radio" id="true" name="value" value="true" />
|
||||||
|
{/if}
|
||||||
|
<label for="true">Paging with selectable count of jobs.</label>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
{#if config.job_list_usePaging}
|
||||||
|
<input type="radio" id="false" name="value" value="false" />
|
||||||
|
{:else}
|
||||||
|
<input
|
||||||
|
type="radio"
|
||||||
|
id="false"
|
||||||
|
name="value"
|
||||||
|
value="false"
|
||||||
|
checked
|
||||||
|
/>
|
||||||
|
{/if}
|
||||||
|
<label for="false">Continuous scroll iteratively adding 10 jobs.</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<Button color="primary" type="submit">Submit</Button>
|
||||||
|
</form>
|
||||||
|
</Card></Col
|
||||||
|
>
|
||||||
</Row>
|
</Row>
|
||||||
|
|
||||||
<Row cols={1} class="p-2 g-2">
|
<Row cols={1} class="p-2 g-2">
|
||||||
|
@ -20,7 +20,7 @@
|
|||||||
<td>{user.name}</td>
|
<td>{user.name}</td>
|
||||||
<td>{user.projects}</td>
|
<td>{user.projects}</td>
|
||||||
<td>{user.email}</td>
|
<td>{user.email}</td>
|
||||||
<td><code>{user.roles.join(", ")}</code></td>
|
<td><code>{user?.roles ? user.roles.join(", ") : "No Roles"}</code></td>
|
||||||
<td>
|
<td>
|
||||||
{#if !jwt}
|
{#if !jwt}
|
||||||
<Button color="success" on:click={getUserJwt(user.username)}
|
<Button color="success" on:click={getUserJwt(user.username)}
|
||||||
|
@ -461,7 +461,7 @@
|
|||||||
},
|
},
|
||||||
scales: {
|
scales: {
|
||||||
x: { time: false },
|
x: { time: false },
|
||||||
y: maxY ? { range: [0, maxY * 1.1] } : {},
|
y: maxY ? { min: 0, max: (maxY * 1.1) } : {auto: true}, // Add some space to upper render limit
|
||||||
},
|
},
|
||||||
legend: {
|
legend: {
|
||||||
// Display legend until max 12 Y-dataseries
|
// Display legend until max 12 Y-dataseries
|
||||||
|
@ -298,6 +298,24 @@
|
|||||||
// Reset grid lineWidth
|
// Reset grid lineWidth
|
||||||
u.ctx.lineWidth = 0.15;
|
u.ctx.lineWidth = 0.15;
|
||||||
}
|
}
|
||||||
|
if (renderTime) {
|
||||||
|
// The Color Scale For Time Information
|
||||||
|
const posX = u.valToPos(0.1, "x", true)
|
||||||
|
const posXLimit = u.valToPos(100, "x", true)
|
||||||
|
const posY = u.valToPos(15000.0, "y", true)
|
||||||
|
u.ctx.fillStyle = 'black'
|
||||||
|
u.ctx.fillText('Start', posX, posY)
|
||||||
|
const start = posX + 10
|
||||||
|
for (let x = start; x < posXLimit; x += 10) {
|
||||||
|
let c = (x - start) / (posXLimit - start)
|
||||||
|
u.ctx.fillStyle = getRGB(c)
|
||||||
|
u.ctx.beginPath()
|
||||||
|
u.ctx.arc(x, posY, 3, 0, Math.PI * 2, false)
|
||||||
|
u.ctx.fill()
|
||||||
|
}
|
||||||
|
u.ctx.fillStyle = 'black'
|
||||||
|
u.ctx.fillText('End', posXLimit + 23, posY)
|
||||||
|
}
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
Loading…
Reference in New Issue
Block a user