mirror of
https://gitlab.com/wgp/dougal/software.git
synced 2025-12-06 10:57:07 +00:00
Compare commits
62 Commits
a8ff7f3b52
...
673c60a359
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
673c60a359 | ||
|
|
99e425270c | ||
|
|
63633715e2 | ||
|
|
8afac5c150 | ||
|
|
11168def68 | ||
|
|
0f477b8e65 | ||
|
|
03b00a4ea7 | ||
|
|
c5faa53bee | ||
|
|
46b2512530 | ||
|
|
db4c9a0235 | ||
|
|
1a12ea13ed | ||
|
|
81717c37f1 | ||
|
|
6377e8854c | ||
|
|
d3446d03bd | ||
|
|
a52f7811f2 | ||
|
|
ef2bd4888e | ||
|
|
8801442c92 | ||
|
|
30f65dbeaa | ||
|
|
c2f53ac150 | ||
|
|
4328fc4d2a | ||
|
|
2c2eb8fceb | ||
|
|
767c2f2cb1 | ||
|
|
57a73f7d1c | ||
|
|
9f299056d8 | ||
|
|
5d3c59867c | ||
|
|
76b8355ede | ||
|
|
76b55f514d | ||
|
|
4e1d3209df | ||
|
|
f21ff7ee38 | ||
|
|
2446b42785 | ||
|
|
196e772004 | ||
|
|
674d818fee | ||
|
|
5527576679 | ||
|
|
fe7c016dea | ||
|
|
b7543aa6c4 | ||
|
|
b48a060dc0 | ||
|
|
c0f9a2de5a | ||
|
|
32a9c7a5f2 | ||
|
|
f1f74080f6 | ||
|
|
c5eb8e45f1 | ||
|
|
caab968fd6 | ||
|
|
5f28d1be7b | ||
|
|
22c9537889 | ||
|
|
e95aaa7de7 | ||
|
|
4f44f5a10c | ||
|
|
0ba467d34c | ||
|
|
2b5b302e54 | ||
|
|
28938e27a9 | ||
|
|
97f96fdc1e | ||
|
|
1e3ce35f76 | ||
|
|
619a886781 | ||
|
|
c054e63325 | ||
|
|
fd94b3b6f4 | ||
|
|
7b67b4afc9 | ||
|
|
7c52ada922 | ||
|
|
9072bbe389 | ||
|
|
6639b7110b | ||
|
|
be6652b539 | ||
|
|
bf054d3902 | ||
|
|
2734870871 | ||
|
|
52f49e6799 | ||
|
|
30150a8728 |
@@ -1,5 +1,7 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
|
|
||||||
|
# Maximum runtime in seconds before killing an overdue instance (e.g., 10 minutes)
|
||||||
|
MAX_RUNTIME_SECONDS=$((15 * 60))
|
||||||
|
|
||||||
DOUGAL_ROOT=${DOUGAL_ROOT:-$(dirname "$0")/..}
|
DOUGAL_ROOT=${DOUGAL_ROOT:-$(dirname "$0")/..}
|
||||||
|
|
||||||
@@ -80,8 +82,9 @@ function run () {
|
|||||||
# DESCRIPTION=""
|
# DESCRIPTION=""
|
||||||
SERVICE="deferred_imports"
|
SERVICE="deferred_imports"
|
||||||
|
|
||||||
$BINDIR/send_alert.py -t "$TITLE" -s "$SERVICE" -l "critical" \
|
# Disable GitLab alerts. They're just not very practical
|
||||||
-O "$(cat $STDOUTLOG)" -E "$(cat $STDERRLOG)"
|
# $BINDIR/send_alert.py -t "$TITLE" -s "$SERVICE" -l "critical" \
|
||||||
|
# -O "$(cat $STDOUTLOG)" -E "$(cat $STDERRLOG)"
|
||||||
|
|
||||||
exit 2
|
exit 2
|
||||||
}
|
}
|
||||||
@@ -97,14 +100,37 @@ function cleanup () {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if [[ -f $LOCKFILE ]]; then
|
if [[ -f $LOCKFILE ]]; then
|
||||||
PID=$(cat "$LOCKFILE")
|
PID=$(cat "$LOCKFILE")
|
||||||
if pgrep -F "$LOCKFILE"; then
|
if kill -0 "$PID" 2>/dev/null; then # Check if process is running
|
||||||
print_warning $(printf "The previous process is still running (%d)" $PID)
|
# Get elapsed time in D-HH:MM:SS format and convert to seconds
|
||||||
exit 1
|
ELAPSED_STR=$(ps -p "$PID" -o etime= | tr -d '[:space:]')
|
||||||
else
|
if [ -n "$ELAPSED_STR" ]; then
|
||||||
rm "$LOCKFILE"
|
# Convert D-HH:MM:SS to seconds
|
||||||
print_warning $(printf "Previous process (%d) not found. Must have died unexpectedly" $PID)
|
ELAPSED_SECONDS=$(echo "$ELAPSED_STR" | awk -F'[-:]' '{
|
||||||
fi
|
seconds = 0
|
||||||
|
if (NF == 4) { seconds += $1 * 86400 } # Days
|
||||||
|
if (NF >= 3) { seconds += $NF-2 * 3600 } # Hours
|
||||||
|
if (NF >= 2) { seconds += $NF-1 * 60 } # Minutes
|
||||||
|
seconds += $NF # Seconds
|
||||||
|
print seconds
|
||||||
|
}')
|
||||||
|
if [ "$ELAPSED_SECONDS" -gt "$MAX_RUNTIME_SECONDS" ]; then
|
||||||
|
# Kill the overdue process (SIGTERM; use -9 for SIGKILL if needed)
|
||||||
|
kill "$PID" 2>/dev/null
|
||||||
|
print_warning $(printf "Killed overdue process (%d) that ran for %s (%d seconds)" "$PID" "$ELAPSED_STR" "$ELAPSED_SECONDS")
|
||||||
|
rm "$LOCKFILE"
|
||||||
|
else
|
||||||
|
print_warning $(printf "Previous process is still running (%d) for %s (%d seconds)" "$PID" "$ELAPSED_STR" "$ELAPSED_SECONDS")
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
print_warning $(printf "Could not retrieve elapsed time for process (%d)" "$PID")
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
rm "$LOCKFILE"
|
||||||
|
print_warning $(printf "Previous process (%d) not found. Must have died unexpectedly" "$PID")
|
||||||
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
echo "$$" > "$LOCKFILE" || {
|
echo "$$" > "$LOCKFILE" || {
|
||||||
|
|||||||
@@ -2,8 +2,32 @@
|
|||||||
|
|
||||||
const cmp = require('../lib/www/server/lib/comparisons');
|
const cmp = require('../lib/www/server/lib/comparisons');
|
||||||
|
|
||||||
|
async function purgeComparisons () {
|
||||||
|
const groups = await cmp.groups();
|
||||||
|
const comparisons = await cmp.getGroup();
|
||||||
|
|
||||||
|
const pids = new Set(Object.values(groups).flat().map( p => p.pid ));
|
||||||
|
const comparison_pids = new Set(comparisons.map( c => [ c.baseline_pid, c.monitor_pid ] ).flat());
|
||||||
|
|
||||||
|
for (const pid of comparison_pids) {
|
||||||
|
if (!pids.has(pid)) {
|
||||||
|
console.log(`${pid} no longer par of a group. Deleting comparisons`);
|
||||||
|
|
||||||
|
staleComps = comparisons.filter( c => c.baseline_pid == pid || c.monitor_pid == pid );
|
||||||
|
for (c of staleComps) {
|
||||||
|
console.log(`Deleting comparison ${c.baseline_pid} → ${c.monitor_pid}`);
|
||||||
|
await cmp.remove(c.baseline_pid, c.monitor_pid);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
async function main () {
|
async function main () {
|
||||||
|
|
||||||
|
console.log("Looking for unreferenced comparisons to purge");
|
||||||
|
await purgeComparisons();
|
||||||
|
|
||||||
console.log("Retrieving project groups");
|
console.log("Retrieving project groups");
|
||||||
const groups = await cmp.groups();
|
const groups = await cmp.groups();
|
||||||
|
|
||||||
@@ -12,7 +36,7 @@ async function main () {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log(`Found ${groups.length} groups: ${Object.keys(groups).join(", ")}`);
|
console.log(`Found ${Object.keys(groups)?.length} groups: ${Object.keys(groups).join(", ")}`);
|
||||||
|
|
||||||
for (const groupName of Object.keys(groups)) {
|
for (const groupName of Object.keys(groups)) {
|
||||||
const projects = groups[groupName];
|
const projects = groups[groupName];
|
||||||
@@ -21,6 +45,11 @@ async function main () {
|
|||||||
|
|
||||||
const comparisons = await cmp.getGroup(groupName);
|
const comparisons = await cmp.getGroup(groupName);
|
||||||
|
|
||||||
|
if (!comparisons || !comparisons.length) {
|
||||||
|
console.log(`No comparisons found for ${groupName}`);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
// Check if there are any projects that have been modified since last comparison
|
// Check if there are any projects that have been modified since last comparison
|
||||||
// or if there are any pairs that are no longer part of the group
|
// or if there are any pairs that are no longer part of the group
|
||||||
|
|
||||||
|
|||||||
@@ -39,7 +39,8 @@ export default {
|
|||||||
default:
|
default:
|
||||||
return {
|
return {
|
||||||
editable: false,
|
editable: false,
|
||||||
displaylogo: false
|
displaylogo: false,
|
||||||
|
responsive: true
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -48,7 +49,8 @@ export default {
|
|||||||
const base = {
|
const base = {
|
||||||
font: {
|
font: {
|
||||||
color: this.$vuetify.theme.isDark ? "#fff" : undefined
|
color: this.$vuetify.theme.isDark ? "#fff" : undefined
|
||||||
}
|
},
|
||||||
|
autosize: true
|
||||||
};
|
};
|
||||||
|
|
||||||
switch (this.facet) {
|
switch (this.facet) {
|
||||||
@@ -274,18 +276,25 @@ export default {
|
|||||||
replot () {
|
replot () {
|
||||||
if (this.plotted) {
|
if (this.plotted) {
|
||||||
const ref = this.$refs.graph;
|
const ref = this.$refs.graph;
|
||||||
Plotly.relayout(ref, {
|
if (ref && ref.clientWidth > 0 && ref.clientHeight > 0) {
|
||||||
width: ref.clientWidth,
|
Plotly.relayout(ref, {
|
||||||
height: ref.clientHeight
|
width: ref.clientWidth,
|
||||||
});
|
height: ref.clientHeight
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
},
|
},
|
||||||
|
|
||||||
mounted () {
|
mounted () {
|
||||||
this.resizeObserver = new ResizeObserver(this.replot)
|
this.$nextTick( () => {
|
||||||
this.resizeObserver.observe(this.$refs.graph);
|
if (this.items?.length) {
|
||||||
|
this.plot();
|
||||||
|
}
|
||||||
|
this.resizeObserver = new ResizeObserver(this.replot)
|
||||||
|
this.resizeObserver.observe(this.$refs.graph);
|
||||||
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
beforeDestroy () {
|
beforeDestroy () {
|
||||||
|
|||||||
@@ -36,7 +36,8 @@ export default {
|
|||||||
config () {
|
config () {
|
||||||
return {
|
return {
|
||||||
editable: false,
|
editable: false,
|
||||||
displaylogo: false
|
displaylogo: false,
|
||||||
|
responsive: true
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -53,7 +54,8 @@ export default {
|
|||||||
title: "Time (s)"
|
title: "Time (s)"
|
||||||
},
|
},
|
||||||
plot_bgcolor:"rgba(0,0,0,0)",
|
plot_bgcolor:"rgba(0,0,0,0)",
|
||||||
paper_bgcolor:"rgba(0,0,0,0)"
|
paper_bgcolor:"rgba(0,0,0,0)",
|
||||||
|
autosize: true
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -154,10 +156,12 @@ export default {
|
|||||||
replot () {
|
replot () {
|
||||||
if (this.plotted) {
|
if (this.plotted) {
|
||||||
const ref = this.$refs.graph;
|
const ref = this.$refs.graph;
|
||||||
Plotly.relayout(ref, {
|
if (ref && ref.clientWidth > 0 && ref.clientHeight > 0) {
|
||||||
width: ref.clientWidth,
|
Plotly.relayout(ref, {
|
||||||
height: ref.clientHeight
|
width: ref.clientWidth,
|
||||||
});
|
height: ref.clientHeight
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -190,8 +194,13 @@ export default {
|
|||||||
},
|
},
|
||||||
|
|
||||||
mounted () {
|
mounted () {
|
||||||
this.resizeObserver = new ResizeObserver(this.replot)
|
this.$nextTick( () => {
|
||||||
this.resizeObserver.observe(this.$refs.graph);
|
if (this.items?.length) {
|
||||||
|
this.plot();
|
||||||
|
}
|
||||||
|
this.resizeObserver = new ResizeObserver(this.replot)
|
||||||
|
this.resizeObserver.observe(this.$refs.graph);
|
||||||
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
beforeDestroy () {
|
beforeDestroy () {
|
||||||
|
|||||||
@@ -158,6 +158,7 @@ Vue.use(VueRouter)
|
|||||||
component: SequenceList
|
component: SequenceList
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
name: "shotlog",
|
||||||
path: "sequences/:sequence",
|
path: "sequences/:sequence",
|
||||||
component: SequenceSummary
|
component: SequenceSummary
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ async function refreshEvents ({commit, dispatch, state, rootState}, [modifiedAft
|
|||||||
|
|
||||||
/** Return a subset of events from state.events
|
/** Return a subset of events from state.events
|
||||||
*/
|
*/
|
||||||
async function getEvents ({commit, dispatch, state}, [projectId, {sequence, date0, date1, sortBy, sortDesc, itemsPerPage, page, text, label}]) {
|
async function getEvents ({commit, dispatch, state}, [projectId, {sequence, date0, date1, sortBy, sortDesc, itemsPerPage, page, text, label, excludeLabels}]) {
|
||||||
let filteredEvents = [...state.events];
|
let filteredEvents = [...state.events];
|
||||||
|
|
||||||
if (sortBy) {
|
if (sortBy) {
|
||||||
@@ -114,6 +114,10 @@ async function getEvents ({commit, dispatch, state}, [projectId, {sequence, date
|
|||||||
filteredEvents = filteredEvents.filter( event => event.labels?.includes(label) );
|
filteredEvents = filteredEvents.filter( event => event.labels?.includes(label) );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (excludeLabels) {
|
||||||
|
filteredEvents = filteredEvents.filter( event => !excludeLabels?.some( label => event.labels?.includes(label) ) );
|
||||||
|
}
|
||||||
|
|
||||||
const count = filteredEvents.length;
|
const count = filteredEvents.length;
|
||||||
|
|
||||||
if (itemsPerPage && itemsPerPage > 0) {
|
if (itemsPerPage && itemsPerPage > 0) {
|
||||||
|
|||||||
@@ -5,6 +5,22 @@
|
|||||||
<v-card-title>
|
<v-card-title>
|
||||||
<v-toolbar flat>
|
<v-toolbar flat>
|
||||||
<v-toolbar-title>
|
<v-toolbar-title>
|
||||||
|
<template v-if="$route.params.sequence">
|
||||||
|
<v-btn icon small
|
||||||
|
:disabled="sequenceIndex >= (sequences.length - 1)"
|
||||||
|
:to="{name: 'logBySequence', params: { sequence: (sequences[sequences.length-1]||{}).sequence }}"
|
||||||
|
title="Go to the first sequence"
|
||||||
|
>
|
||||||
|
<v-icon dense>mdi-chevron-double-left</v-icon>
|
||||||
|
</v-btn>
|
||||||
|
<v-btn icon small
|
||||||
|
:disabled="sequenceIndex >= (sequences.length - 1)"
|
||||||
|
:to="{name: 'logBySequence', params: { sequence: (sequences[sequenceIndex+1]||{}).sequence }}"
|
||||||
|
title="Go to the previous sequence"
|
||||||
|
>
|
||||||
|
<v-icon dense>mdi-chevron-left</v-icon>
|
||||||
|
</v-btn>
|
||||||
|
</template>
|
||||||
<span class="d-none d-lg-inline">
|
<span class="d-none d-lg-inline">
|
||||||
{{
|
{{
|
||||||
$route.params.sequence
|
$route.params.sequence
|
||||||
@@ -31,18 +47,38 @@
|
|||||||
: ""
|
: ""
|
||||||
}}
|
}}
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
|
<template v-if="$route.params.sequence">
|
||||||
|
<v-btn icon small
|
||||||
|
:disabled="sequenceIndex==0"
|
||||||
|
:to="{name: 'logBySequence', params: { sequence: (sequences[sequenceIndex-1]||{}).sequence }}"
|
||||||
|
title="Go to the next sequence"
|
||||||
|
>
|
||||||
|
<v-icon dense>mdi-chevron-right</v-icon>
|
||||||
|
</v-btn>
|
||||||
|
<v-btn icon small class="mr-1"
|
||||||
|
:disabled="sequenceIndex==0"
|
||||||
|
:to="{name: 'logBySequence', params: { sequence: (sequences[0]||{}).sequence }}"
|
||||||
|
title="Go to the last sequence"
|
||||||
|
>
|
||||||
|
<v-icon dense>mdi-chevron-double-right</v-icon>
|
||||||
|
</v-btn>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<a v-if="$route.params.sequence"
|
||||||
|
class="mr-3"
|
||||||
|
:href="`/projects/${$route.params.project}/sequences/${$route.params.sequence}`"
|
||||||
|
title="View the shotlog for this sequence"
|
||||||
|
>
|
||||||
|
<v-icon
|
||||||
|
right
|
||||||
|
color="teal"
|
||||||
|
>mdi-format-list-numbered</v-icon>
|
||||||
|
</a>
|
||||||
|
|
||||||
</v-toolbar-title>
|
</v-toolbar-title>
|
||||||
|
|
||||||
<a v-if="$route.params.sequence"
|
|
||||||
class="mr-3"
|
|
||||||
:href="`/projects/${$route.params.project}/sequences/${$route.params.sequence}`"
|
|
||||||
title="View the shotlog for this sequence"
|
|
||||||
>
|
|
||||||
<v-icon
|
|
||||||
right
|
|
||||||
color="teal"
|
|
||||||
>mdi-format-list-numbered</v-icon>
|
|
||||||
</a>
|
|
||||||
|
|
||||||
<dougal-event-edit v-if="$parent.writeaccess()"
|
<dougal-event-edit v-if="$parent.writeaccess()"
|
||||||
v-model="eventDialog"
|
v-model="eventDialog"
|
||||||
@@ -494,17 +530,6 @@ export default {
|
|||||||
rows () {
|
rows () {
|
||||||
const rows = {};
|
const rows = {};
|
||||||
this.items
|
this.items
|
||||||
.filter(i => {
|
|
||||||
return !this.$route.params.sequence || (this.$route.params.sequence == i.sequence)
|
|
||||||
})
|
|
||||||
.filter(i => {
|
|
||||||
for (const label of this.filterableLabels) {
|
|
||||||
if (!this.shownLabels.includes(label) && i.labels.includes(label)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
})
|
|
||||||
.forEach(i => {
|
.forEach(i => {
|
||||||
const key = (i.sequence && i.point) ? (i.sequence+"@"+i.point) : i.tstamp;
|
const key = (i.sequence && i.point) ? (i.sequence+"@"+i.point) : i.tstamp;
|
||||||
if (!rows[key]) {
|
if (!rows[key]) {
|
||||||
@@ -535,6 +560,10 @@ export default {
|
|||||||
.sort( (a, b) => b[1]-a[1] );
|
.sort( (a, b) => b[1]-a[1] );
|
||||||
},
|
},
|
||||||
|
|
||||||
|
filteredLabels () {
|
||||||
|
return this.filterableLabels.filter( label => !this.shownLabels.includes(label) );
|
||||||
|
},
|
||||||
|
|
||||||
presetRemarks () {
|
presetRemarks () {
|
||||||
return this.projectConfiguration?.events?.presetRemarks ?? [];
|
return this.projectConfiguration?.events?.presetRemarks ?? [];
|
||||||
},
|
},
|
||||||
@@ -547,7 +576,17 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
...mapGetters(['user', 'eventsLoading', 'online', 'sequence', 'line', 'point', 'position', 'timestamp', 'lineName', 'events', 'labels', 'userLabels', 'projectConfiguration']),
|
sequenceIndex () {
|
||||||
|
if ("sequence" in this.$route.params) {
|
||||||
|
const index = this.sequences.findIndex( i => i.sequence == this.$route.params.sequence );
|
||||||
|
if (index != -1) {
|
||||||
|
return index;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// return undefined
|
||||||
|
},
|
||||||
|
|
||||||
|
...mapGetters(['user', 'eventsLoading', 'online', 'sequence', 'sequences', 'line', 'point', 'position', 'timestamp', 'lineName', 'events', 'labels', 'userLabels', 'projectConfiguration']),
|
||||||
...mapState({projectSchema: state => state.project.projectSchema})
|
...mapState({projectSchema: state => state.project.projectSchema})
|
||||||
|
|
||||||
},
|
},
|
||||||
@@ -555,6 +594,7 @@ export default {
|
|||||||
watch: {
|
watch: {
|
||||||
options: {
|
options: {
|
||||||
async handler () {
|
async handler () {
|
||||||
|
this.savePrefs(),
|
||||||
await this.fetchEvents();
|
await this.fetchEvents();
|
||||||
},
|
},
|
||||||
deep: true
|
deep: true
|
||||||
@@ -573,12 +613,19 @@ export default {
|
|||||||
},
|
},
|
||||||
|
|
||||||
filter (newVal, oldVal) {
|
filter (newVal, oldVal) {
|
||||||
|
this.savePrefs();
|
||||||
if (newVal?.toLowerCase() != oldVal?.toLowerCase()) {
|
if (newVal?.toLowerCase() != oldVal?.toLowerCase()) {
|
||||||
this.fetchEvents();
|
this.fetchEvents();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
labelSearch () {
|
labelSearch () {
|
||||||
|
this.savePrefs();
|
||||||
|
this.fetchEvents();
|
||||||
|
},
|
||||||
|
|
||||||
|
filteredLabels () {
|
||||||
|
this.savePrefs()
|
||||||
this.fetchEvents();
|
this.fetchEvents();
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -587,7 +634,7 @@ export default {
|
|||||||
},
|
},
|
||||||
|
|
||||||
user (newVal, oldVal) {
|
user (newVal, oldVal) {
|
||||||
this.itemsPerPage = Number(localStorage.getItem(`dougal/prefs/${this.user?.name}/${this.$route.params.project}/${this.$options.name}/items-per-page`)) || 25;
|
this.loadPrefs();
|
||||||
}
|
}
|
||||||
|
|
||||||
},
|
},
|
||||||
@@ -638,8 +685,10 @@ export default {
|
|||||||
|
|
||||||
async fetchEvents (opts = {}) {
|
async fetchEvents (opts = {}) {
|
||||||
const options = {
|
const options = {
|
||||||
|
sequence: this.$route.params.sequence,
|
||||||
text: this.filter,
|
text: this.filter,
|
||||||
label: this.labelSearch,
|
label: this.labelSearch,
|
||||||
|
excludeLabels: this.filteredLabels,
|
||||||
...this.options
|
...this.options
|
||||||
};
|
};
|
||||||
const res = await this.getEvents([this.$route.params.project, options]);
|
const res = await this.getEvents([this.$route.params.project, options]);
|
||||||
@@ -877,10 +926,36 @@ export default {
|
|||||||
*/
|
*/
|
||||||
},
|
},
|
||||||
|
|
||||||
|
getPrefsKey () {
|
||||||
|
return `dougal/prefs/${this.user?.name}/${this.$route.params.project}/Log/v1`;
|
||||||
|
},
|
||||||
|
|
||||||
|
savePrefs () {
|
||||||
|
const prefs = {
|
||||||
|
shownLabels: this.shownLabels,
|
||||||
|
labelSearch: this.labelSearch,
|
||||||
|
filter: this.filter,
|
||||||
|
options: this.options
|
||||||
|
};
|
||||||
|
localStorage.setItem(this.getPrefsKey(), JSON.stringify(prefs));
|
||||||
|
},
|
||||||
|
|
||||||
|
loadPrefs () {
|
||||||
|
const stored = localStorage.getItem(this.getPrefsKey());
|
||||||
|
if (stored) {
|
||||||
|
const prefs = JSON.parse(stored);
|
||||||
|
if (prefs.shownLabels !== undefined) this.shownLabels = prefs.shownLabels;
|
||||||
|
if (prefs.labelSearch !== undefined) this.labelSearch = prefs.labelSearch;
|
||||||
|
if (prefs.filter !== undefined) this.filter = prefs.filter;
|
||||||
|
if (prefs.options !== undefined) this.options = prefs.options;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
...mapActions(["api", "showSnack", "refreshEvents", "getEvents"])
|
...mapActions(["api", "showSnack", "refreshEvents", "getEvents"])
|
||||||
},
|
},
|
||||||
|
|
||||||
async mounted () {
|
async mounted () {
|
||||||
|
this.loadPrefs();
|
||||||
this.fetchEvents();
|
this.fetchEvents();
|
||||||
|
|
||||||
window.addEventListener('keyup', this.handleKeyboardEvent);
|
window.addEventListener('keyup', this.handleKeyboardEvent);
|
||||||
|
|||||||
@@ -6,8 +6,42 @@
|
|||||||
<v-progress-linear indeterminate v-if="loading"></v-progress-linear>
|
<v-progress-linear indeterminate v-if="loading"></v-progress-linear>
|
||||||
<v-toolbar flat>
|
<v-toolbar flat>
|
||||||
<v-toolbar-title>
|
<v-toolbar-title>
|
||||||
|
<template v-if="$route.params.sequence">
|
||||||
|
<v-btn icon small
|
||||||
|
:disabled="sequenceIndex >= (sequences.length - 1)"
|
||||||
|
:to="{name: 'shotlog', params: { sequence: (sequences[sequences.length-1]||{}).sequence }}"
|
||||||
|
title="Go to the first sequence"
|
||||||
|
>
|
||||||
|
<v-icon dense>mdi-chevron-double-left</v-icon>
|
||||||
|
</v-btn>
|
||||||
|
<v-btn icon small
|
||||||
|
:disabled="sequenceIndex >= (sequences.length - 1)"
|
||||||
|
:to="{name: 'shotlog', params: { sequence: (sequences[sequenceIndex+1]||{}).sequence }}"
|
||||||
|
title="Go to the previous sequence"
|
||||||
|
>
|
||||||
|
<v-icon dense>mdi-chevron-left</v-icon>
|
||||||
|
</v-btn>
|
||||||
|
</template>
|
||||||
|
|
||||||
Sequence {{sequenceNumber}}
|
Sequence {{sequenceNumber}}
|
||||||
<small :class="statusColour" v-if="sequence">({{sequence.status}})</small>
|
<small :class="statusColour" v-if="sequence">({{sequence.status}})</small>
|
||||||
|
|
||||||
|
<template v-if="$route.params.sequence">
|
||||||
|
<v-btn icon small
|
||||||
|
:disabled="sequenceIndex==0"
|
||||||
|
:to="{name: 'shotlog', params: { sequence: (sequences[sequenceIndex-1]||{}).sequence }}"
|
||||||
|
title="Go to the next sequence"
|
||||||
|
>
|
||||||
|
<v-icon dense>mdi-chevron-right</v-icon>
|
||||||
|
</v-btn>
|
||||||
|
<v-btn icon small class="mr-1"
|
||||||
|
:disabled="sequenceIndex==0"
|
||||||
|
:to="{name: 'shotlog', params: { sequence: (sequences[0]||{}).sequence }}"
|
||||||
|
title="Go to the last sequence"
|
||||||
|
>
|
||||||
|
<v-icon dense>mdi-chevron-double-right</v-icon>
|
||||||
|
</v-btn>
|
||||||
|
</template>
|
||||||
</v-toolbar-title>
|
</v-toolbar-title>
|
||||||
|
|
||||||
<a v-if="$route.params.sequence"
|
<a v-if="$route.params.sequence"
|
||||||
@@ -352,6 +386,16 @@ export default {
|
|||||||
return this.sequences.find(i => i.sequence == this.sequenceNumber);
|
return this.sequences.find(i => i.sequence == this.sequenceNumber);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
sequenceIndex () {
|
||||||
|
if ("sequence" in this.$route.params) {
|
||||||
|
const index = this.sequences.findIndex( i => i.sequence == this.$route.params.sequence );
|
||||||
|
if (index != -1) {
|
||||||
|
return index;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// return undefined
|
||||||
|
},
|
||||||
|
|
||||||
remarks () {
|
remarks () {
|
||||||
return this.sequence?.remarks || "Nil.";
|
return this.sequence?.remarks || "Nil.";
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -330,44 +330,80 @@ async function saveGroup (group, opts = {}) {
|
|||||||
|
|
||||||
async function getGroup (groupName, opts = {}) {
|
async function getGroup (groupName, opts = {}) {
|
||||||
|
|
||||||
const group = (await groups())?.[groupName]?.map( i => i.pid)?.sort();
|
|
||||||
|
|
||||||
if (!group?.length) return;
|
|
||||||
|
|
||||||
const client = await pool.connect();
|
const client = await pool.connect();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|
||||||
const pairs = combinations(group, 2);
|
if (groupName) {
|
||||||
const flatValues = pairs.flat();
|
|
||||||
const placeholders = [];
|
|
||||||
for (let i = 0; i < pairs.length; i++) {
|
|
||||||
placeholders.push(`($${i * 2 + 1}, $${i * 2 + 2})`);
|
|
||||||
}
|
|
||||||
const inClause = placeholders.join(',');
|
|
||||||
const selectFields = opts.returnData ? 'data, meta' : 'meta';
|
|
||||||
|
|
||||||
const text = `
|
const group = (await groups())?.[groupName]?.map( i => i.pid)?.sort();
|
||||||
SELECT baseline_pid, monitor_pid, ${selectFields}
|
|
||||||
FROM comparisons.comparisons
|
|
||||||
WHERE type = 'geometric_difference'
|
|
||||||
AND (baseline_pid, monitor_pid) IN (VALUES ${inClause})
|
|
||||||
ORDER BY baseline_pid, monitor_pid
|
|
||||||
`;
|
|
||||||
|
|
||||||
const res = await client.query(text, flatValues);
|
if (!group?.length || group?.length < 2) return;
|
||||||
if (!res.rows.length) {
|
|
||||||
console.log("Comparison not found in database");
|
|
||||||
return;
|
const pairs = combinations(group, 2);
|
||||||
}
|
const flatValues = pairs.flat();
|
||||||
|
const placeholders = [];
|
||||||
|
for (let i = 0; i < pairs.length; i++) {
|
||||||
|
placeholders.push(`($${i * 2 + 1}, $${i * 2 + 2})`);
|
||||||
|
}
|
||||||
|
const inClause = placeholders.join(',');
|
||||||
|
const selectFields = opts.returnData ? 'data, meta' : 'meta';
|
||||||
|
|
||||||
|
const text = `
|
||||||
|
SELECT baseline_pid, monitor_pid, ${selectFields}
|
||||||
|
FROM comparisons.comparisons
|
||||||
|
WHERE type = 'geometric_difference'
|
||||||
|
AND (baseline_pid, monitor_pid) IN (VALUES ${inClause})
|
||||||
|
ORDER BY baseline_pid, monitor_pid
|
||||||
|
`;
|
||||||
|
|
||||||
|
if (!placeholders) {
|
||||||
|
console.log("No pairs found in group");
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
const res = await client.query(text, flatValues);
|
||||||
|
if (!res.rows.length) {
|
||||||
|
console.log("Comparison not found in database");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (opts.returnData) {
|
||||||
|
return res.rows.map( row => ({
|
||||||
|
...row,
|
||||||
|
data: DougalBinaryBundle.clone(row.data),
|
||||||
|
}));
|
||||||
|
} else {
|
||||||
|
return res.rows;
|
||||||
|
}
|
||||||
|
|
||||||
if (opts.returnData) {
|
|
||||||
return res.rows.map( row => ({
|
|
||||||
...row,
|
|
||||||
data: DougalBinaryBundle.clone(row.data),
|
|
||||||
}));
|
|
||||||
} else {
|
} else {
|
||||||
return res.rows;
|
|
||||||
|
const selectFields = opts.returnData ? 'data, meta' : 'meta';
|
||||||
|
|
||||||
|
const text = `
|
||||||
|
SELECT baseline_pid, monitor_pid, ${selectFields}
|
||||||
|
FROM comparisons.comparisons
|
||||||
|
WHERE type = 'geometric_difference'
|
||||||
|
ORDER BY baseline_pid, monitor_pid
|
||||||
|
`;
|
||||||
|
|
||||||
|
const res = await client.query(text);
|
||||||
|
if (!res.rows.length) {
|
||||||
|
console.log("Comparison not found in database");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (opts.returnData) {
|
||||||
|
return res.rows.map( row => ({
|
||||||
|
...row,
|
||||||
|
data: DougalBinaryBundle.clone(row.data),
|
||||||
|
}));
|
||||||
|
} else {
|
||||||
|
return res.rows;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error(err);
|
console.error(err);
|
||||||
|
|||||||
Reference in New Issue
Block a user