mirror of
https://gitlab.com/wgp/dougal/software.git
synced 2025-12-06 12:57:08 +00:00
893 lines
30 KiB
Vue
893 lines
30 KiB
Vue
<template>
|
||
<v-container fluid>
|
||
|
||
<v-card>
|
||
<v-card-title>
|
||
<v-toolbar flat>
|
||
<v-toolbar-title>Sequences</v-toolbar-title>
|
||
|
||
<v-spacer></v-spacer>
|
||
<v-text-field
|
||
v-model="filter"
|
||
append-icon="mdi-magnify"
|
||
label="Filter"
|
||
single-line
|
||
clearable
|
||
hint="Filter by sequence, line, date or remarks"
|
||
></v-text-field>
|
||
</v-toolbar>
|
||
</v-card-title>
|
||
<v-card-text>
|
||
|
||
<v-menu
|
||
v-model="contextMenuShow"
|
||
:close-on-content-click="false"
|
||
:position-x="contextMenuX"
|
||
:position-y="contextMenuY"
|
||
absolute
|
||
offset-y
|
||
>
|
||
<v-list dense v-if="contextMenuItem">
|
||
<v-list-item @click="addToPlan(false); contextMenuShow=false" v-if="writeaccess">
|
||
<v-list-item-title>Reshoot</v-list-item-title>
|
||
</v-list-item>
|
||
<v-list-item @click="addToPlan(true); contextMenuShow=false" v-if="writeaccess">
|
||
<v-list-item-title>Reshoot with overlap</v-list-item-title>
|
||
</v-list-item>
|
||
<v-list-item
|
||
:href="`/projects/${$route.params.project}/graphs/sequence/${contextMenuItem.sequence}`"
|
||
@click="contextMenuShow=false"
|
||
>
|
||
<v-list-item-title>View graphics</v-list-item-title>
|
||
</v-list-item>
|
||
<v-list-group>
|
||
<template v-slot:activator>
|
||
<v-list-item-title>Download report</v-list-item-title>
|
||
</template>
|
||
<v-list-item
|
||
:href="`/api/project/${$route.params.project}/event/-/${contextMenuItem.sequence}?mime=application%2Fvnd.seis%2Bjson&download`"
|
||
title="Download as a Multiseis-compatible Seis+JSON file."
|
||
@click="contextMenuShow=false"
|
||
>
|
||
<v-list-item-title>Seis+JSON</v-list-item-title>
|
||
</v-list-item>
|
||
<v-list-item
|
||
:href="`/api/project/${$route.params.project}/event/-/${contextMenuItem.sequence}?mime=application%2Fgeo%2Bjson&download`"
|
||
title="Download as a QGIS-compatible GeoJSON file"
|
||
@click="contextMenuShow=false"
|
||
>
|
||
<v-list-item-title>GeoJSON</v-list-item-title>
|
||
</v-list-item>
|
||
<v-list-item
|
||
:href="`/api/project/${$route.params.project}/event/-/${contextMenuItem.sequence}?mime=application%2Fjson&download`"
|
||
title="Download as a generic JSON file"
|
||
@click="contextMenuShow=false"
|
||
>
|
||
<v-list-item-title>JSON</v-list-item-title>
|
||
</v-list-item>
|
||
<v-list-item
|
||
:href="`/api/project/${$route.params.project}/event/-/${contextMenuItem.sequence}?mime=text%2Fhtml&download`"
|
||
title="Download as an HTML formatted file"
|
||
@click="contextMenuShow=false"
|
||
>
|
||
<v-list-item-title>HTML</v-list-item-title>
|
||
</v-list-item>
|
||
<v-list-item
|
||
:href="`/api/project/${$route.params.project}/event/-/${contextMenuItem.sequence}?mime=application%2Fpdf&download`"
|
||
title="Download as a Portable Document File"
|
||
@click="contextMenuShow=false"
|
||
>
|
||
<v-list-item-title>PDF</v-list-item-title>
|
||
</v-list-item>
|
||
</v-list-group>
|
||
|
||
<!-- ASAQC transfer queue actions -->
|
||
|
||
<!-- Item is not in queue -->
|
||
<v-list-item
|
||
v-if="writeaccess && !contextMenuItemInTransferQueue"
|
||
@click="addToTransferQueue(); contextMenuShow=false"
|
||
>
|
||
<v-list-item-content>
|
||
<v-list-item-title>Send to ASAQC</v-list-item-title>
|
||
</v-list-item-content>
|
||
<v-list-item-icon>
|
||
<v-icon small>mdi-tray-plus</v-icon>
|
||
</v-list-item-icon>
|
||
</v-list-item>
|
||
<!-- Item queued, not yet sent -->
|
||
<v-list-item two-line
|
||
v-else-if="writeaccess && contextMenuItemInTransferQueue.status == 'queued'"
|
||
@click="removeFromTransferQueue(); contextMenuShow=false"
|
||
>
|
||
<v-list-item-content>
|
||
<v-list-item-title class="red--text">Cancel sending to ASAQC</v-list-item-title>
|
||
<v-list-item-subtitle class="info--text">
|
||
Queued since: {{contextMenuItemInTransferQueue.created_on}}
|
||
</v-list-item-subtitle>
|
||
</v-list-item-content>
|
||
<v-list-item-icon>
|
||
<v-icon small color="red">mdi-tray-remove</v-icon>
|
||
</v-list-item-icon>
|
||
</v-list-item>
|
||
<!-- Item already sent -->
|
||
<v-list-item two-line
|
||
v-else-if="writeaccess && contextMenuItemInTransferQueue.status == 'sent'"
|
||
@click="addToTransferQueue(); contextMenuShow=false"
|
||
>
|
||
<v-list-item-content>
|
||
<v-list-item-title>Resend to ASAQC</v-list-item-title>
|
||
<v-list-item-subtitle class="success--text">
|
||
Last sent on: {{ contextMenuItemInTransferQueue.created_on }}
|
||
</v-list-item-subtitle>
|
||
</v-list-item-content>
|
||
<v-list-item-icon>
|
||
<v-icon small>mdi-tray-plus</v-icon>
|
||
</v-list-item-icon>
|
||
</v-list-item>
|
||
<!-- Item sending was cancelled -->
|
||
<v-list-item two-line
|
||
v-else-if="writeaccess && contextMenuItemInTransferQueue.status == 'cancelled'"
|
||
@click="addToTransferQueue(); contextMenuShow=false"
|
||
>
|
||
<v-list-item-content>
|
||
<v-list-item-title>Send to ASAQC</v-list-item-title>
|
||
<v-list-item-subtitle class="info--text">
|
||
Last send cancelled on: {{contextMenuItemInTransferQueue.updated_on}}
|
||
</v-list-item-subtitle>
|
||
</v-list-item-content>
|
||
<v-list-item-icon>
|
||
<v-icon small>mdi-tray-plus</v-icon>
|
||
</v-list-item-icon>
|
||
</v-list-item>
|
||
|
||
</v-list>
|
||
</v-menu>
|
||
|
||
<v-data-table
|
||
:headers="headers"
|
||
:items="items"
|
||
:items-per-page.sync="itemsPerPage"
|
||
item-key="sequence"
|
||
:server-items-length="num_rows"
|
||
:search="filter"
|
||
:custom-filter="customFilter"
|
||
:loading="loading"
|
||
:fixed-header="true"
|
||
:footer-props='{itemsPerPageOptions: [ 10, 25, 50, 100, 500, -1 ]}'
|
||
show-expand
|
||
:item-class="(item) => activeItem == item ? 'blue accent-1 elevation-3' : ''"
|
||
@click:row="setActiveItem"
|
||
@contextmenu:row="contextMenu"
|
||
>
|
||
|
||
<template v-slot:expanded-item="{ headers, item }">
|
||
<td :colspan="headers.length" class="pa-0">
|
||
<v-container fluid class="pa-0">
|
||
<v-row no-gutters class="d-flex flex-column flex-sm-row">
|
||
<v-col cols="6" class="d-flex flex-column">
|
||
<v-card outlined class="flex-grow-1">
|
||
<v-card-title>
|
||
Acquisition remarks
|
||
<template v-if="writeaccess">
|
||
<template v-if="edit && edit.sequence == item.sequence && edit.key == 'remarks'">
|
||
<v-btn
|
||
class="ml-3"
|
||
icon
|
||
small
|
||
title="Cancel edit"
|
||
:disabled="loading"
|
||
@click="edit.value = item.remarks; edit = null"
|
||
>
|
||
<v-icon small>mdi-close</v-icon>
|
||
</v-btn>
|
||
<v-btn v-if="edit.value != item.remarks"
|
||
icon
|
||
small
|
||
title="Save edits"
|
||
:disabled="loading"
|
||
@click="edit = null"
|
||
>
|
||
<v-icon small>mdi-content-save-edit-outline</v-icon>
|
||
</v-btn>
|
||
</template>
|
||
<v-btn v-else-if="edit === null"
|
||
class="ml-3"
|
||
icon
|
||
small
|
||
title="Edit"
|
||
:disabled="loading"
|
||
@click="editItem(item, 'remarks')"
|
||
>
|
||
<v-icon small>mdi-square-edit-outline</v-icon>
|
||
</v-btn>
|
||
</template>
|
||
</v-card-title>
|
||
<v-card-subtitle>
|
||
</v-card-subtitle>
|
||
<v-card-text v-if="edit && edit.sequence == item.sequence && edit.key == 'remarks'">
|
||
<v-textarea
|
||
class="markdown"
|
||
autofocus
|
||
placeholder="Enter your text here"
|
||
:disabled="loading"
|
||
v-model="edit.value"
|
||
>
|
||
</v-textarea>
|
||
</v-card-text>
|
||
<v-card-text v-else v-html="$options.filters.markdown(item.remarks)">
|
||
</v-card-text>
|
||
</v-card>
|
||
<v-card outlined class="flex-grow-1" v-if="item.remarks_final !== null">
|
||
<v-card-title>
|
||
Processing remarks
|
||
<template v-if="writeaccess">
|
||
<template v-if="edit && edit.sequence == item.sequence && edit.key == 'remarks_final'">
|
||
<v-btn
|
||
class="ml-3"
|
||
icon
|
||
small
|
||
title="Cancel edit"
|
||
:disabled="loading"
|
||
@click="edit.value = item.remarks_final; edit = null"
|
||
>
|
||
<v-icon small>mdi-close</v-icon>
|
||
</v-btn>
|
||
<v-btn v-if="edit.value != item.remarks_final"
|
||
icon
|
||
small
|
||
title="Save edits"
|
||
:disabled="loading"
|
||
@click="edit = null"
|
||
>
|
||
<v-icon small>mdi-content-save-edit-outline</v-icon>
|
||
</v-btn>
|
||
</template>
|
||
<v-btn v-else-if="edit === null"
|
||
class="ml-3"
|
||
icon
|
||
small
|
||
title="Edit"
|
||
:disabled="loading"
|
||
@click="editItem(item, 'remarks_final')"
|
||
>
|
||
<v-icon small>mdi-square-edit-outline</v-icon>
|
||
</v-btn>
|
||
</template>
|
||
</v-card-title>
|
||
<v-card-subtitle>
|
||
</v-card-subtitle>
|
||
<v-card-text v-if="edit && edit.sequence == item.sequence && edit.key == 'remarks_final'">
|
||
<v-textarea
|
||
class="markdown"
|
||
autofocus
|
||
placeholder="Enter your text here"
|
||
:disabled="loading"
|
||
v-model="edit.value"
|
||
>
|
||
</v-textarea>
|
||
</v-card-text>
|
||
<v-card-text v-html="$options.filters.markdown(item.remarks_final)">
|
||
</v-card-text>
|
||
</v-card>
|
||
</v-col>
|
||
<v-col cols="6" class="d-flex">
|
||
<v-card outlined class="flex-grow-1">
|
||
<v-card-title>
|
||
Source files
|
||
</v-card-title>
|
||
<v-card-subtitle>
|
||
</v-card-subtitle>
|
||
<v-card-text>
|
||
<v-list>
|
||
<v-list-group value="true" v-if="item.raw_files">
|
||
<template v-slot:activator>
|
||
<v-list-item-title>
|
||
Raw files
|
||
<span class="grey--text text--lighten-1">
|
||
{{item.raw_files.length}}
|
||
</span>
|
||
</v-list-item-title>
|
||
</template>
|
||
<v-list-item v-for="(path, index) in item.raw_files"
|
||
key="index"
|
||
link
|
||
title="Download file"
|
||
:href="`/api/files${path}`"
|
||
>
|
||
{{ basename(path) }}
|
||
<v-list-item-action>
|
||
<v-icon right small>mdi-cloud-download</v-icon>
|
||
</v-list-item-action>
|
||
</v-list-item>
|
||
</v-list-group>
|
||
<v-list-group value="true" v-if="item.final_files">
|
||
<template v-slot:activator>
|
||
<v-list-item-title>
|
||
Final files
|
||
<span class="grey--text text--lighten-1">
|
||
{{item.final_files.length}}
|
||
</span>
|
||
</v-list-item-title>
|
||
</template>
|
||
<v-list-item v-for="(path, index) in item.final_files"
|
||
key="index"
|
||
title="Download file"
|
||
:href="`/api/files${path}`"
|
||
>
|
||
{{ basename(path) }}
|
||
<v-list-item-action>
|
||
<v-icon right small>mdi-cloud-download</v-icon>
|
||
</v-list-item-action>
|
||
</v-list-item>
|
||
</v-list-group>
|
||
</v-list>
|
||
</v-card-text>
|
||
</v-card>
|
||
</v-col>
|
||
</v-row>
|
||
</v-container>
|
||
</td>
|
||
</template>
|
||
|
||
<template v-slot:item.sequence="{value}">
|
||
<a
|
||
:href="`/projects/${$route.params.project}/log/sequence/${value}`"
|
||
title="View the event log for this sequence">{{value}}</a>
|
||
</template>
|
||
|
||
<template v-slot:item.line="{value}">
|
||
<b>{{value}}</b>
|
||
</template>
|
||
|
||
<template v-slot:item.fsp_final="{value}">
|
||
<b v-if="value">{{value}}</b>
|
||
</template>
|
||
|
||
<template v-slot:item.lsp_final="{value}">
|
||
<b v-if="value">{{value}}</b>
|
||
</template>
|
||
|
||
<template v-slot:item.status="{value, item}">
|
||
<span :class="{'success--text': value=='final', 'warning--text': value=='raw', 'error--text': value=='ntbp'}">
|
||
{{ value == "final" ? "Processed" : value == "raw" ? item.raw_files ? "Acquired" : "In acquisition" : value == "ntbp" ? "NTBP" : `Unknown (${status})` }}
|
||
<v-icon small :title="`Sent to ASAQC on ${queuedItem(item.sequence).updated_on}`"
|
||
color="success"
|
||
v-if="queuedItem(item.sequence).status == 'sent'"
|
||
>mdi-upload</v-icon>
|
||
<v-icon small
|
||
title="Queued for sending to ASAQC"
|
||
v-else-if="queuedItem(item.sequence).status == 'queued'"
|
||
>mdi-upload-outline</v-icon>
|
||
<v-icon small
|
||
:title="`ASAQC transfer cancelled at ${queuedItem(item.sequence).updated_on}`"
|
||
v-else-if="queuedItem(item.sequence).status == 'cancelled'"
|
||
>mdi-upload-off-outline</v-icon>
|
||
<v-icon small
|
||
color="warning"
|
||
:title="`ASAQC transfer failed at ${queuedItem(item.sequence).updated_on}`"
|
||
v-else-if="queuedItem(item.sequence).status == 'failed'"
|
||
>mdi-upload-off</v-icon>
|
||
</span>
|
||
</template>
|
||
|
||
<template v-slot:item.duration="{item: {duration: value}}">
|
||
{{
|
||
value
|
||
?
|
||
"" +
|
||
(value.days
|
||
? value.days + "d "
|
||
: "") +
|
||
String(value.hours || 0).padStart(2, "0") +
|
||
":" + String(value.minutes || 0).padStart(2, "0") +
|
||
":" + String(value.seconds || 0).padStart(2, "0")
|
||
: "N/A"
|
||
}}
|
||
</template>
|
||
|
||
<template v-slot:item.duration_final="{item: {duration_final: value}}">
|
||
<b>{{
|
||
value
|
||
?
|
||
"" +
|
||
(value.days
|
||
? value.days + "d "
|
||
: "") +
|
||
String(value.hours || 0).padStart(2, "0") +
|
||
":" + String(value.minutes || 0).padStart(2, "0") +
|
||
":" + String(value.seconds || 0).padStart(2, "0")
|
||
: "N/A"
|
||
}}</b>
|
||
</template>
|
||
|
||
<template v-slot:item.ts0="{value}">
|
||
<span v-if="value">
|
||
{{ value.replace(/(.{10})T(.{8}).{4}Z$/, "$1 $2") }}
|
||
</span>
|
||
</template>
|
||
|
||
<template v-slot:item.ts1="{value}">
|
||
<span v-if="value">
|
||
{{ value.replace(/(.{10})T(.{8}).{4}Z$/, "$1 $2") }}
|
||
</span>
|
||
</template>
|
||
|
||
<template v-slot:item.ts0_final="{value}">
|
||
<b v-if="value">
|
||
{{ value.replace(/(.{10})T(.{8}).{4}Z$/, "$1 $2") }}
|
||
</b>
|
||
</template>
|
||
|
||
<template v-slot:item.ts1_final="{value}">
|
||
<b v-if="value">
|
||
{{ value.replace(/(.{10})T(.{8}).{4}Z$/, "$1 $2") }}
|
||
</b>
|
||
</template>
|
||
|
||
<template v-slot:item.missing_shots="{value}">
|
||
<span :class="value && 'warning--text'">{{ value }}</span>
|
||
</template>
|
||
|
||
<template v-slot:item.length="props">
|
||
<span>{{ Math.round(props.value) }} m</span>
|
||
</template>
|
||
|
||
<template v-slot:item.azimuth="{value}">
|
||
<span>{{ value.toFixed? value.toFixed(2) : value }} °</span>
|
||
</template>
|
||
|
||
</v-data-table>
|
||
</v-card-text>
|
||
</v-card>
|
||
</v-container>
|
||
</template>
|
||
|
||
<style scoped>
|
||
td span {
|
||
white-space: nowrap;
|
||
}
|
||
|
||
.status-raw {
|
||
color: orange;
|
||
}
|
||
|
||
.status-final {
|
||
color: green;
|
||
}
|
||
|
||
.status-ntbp {
|
||
color: red;
|
||
}
|
||
|
||
tr :nth-child(5), tr :nth-child(8), tr :nth-child(11), tr :nth-child(14) {
|
||
opacity: 0.7;
|
||
}
|
||
</style>
|
||
|
||
<script>
|
||
import { mapActions, mapGetters } from 'vuex';
|
||
import { basename } from 'path';
|
||
import throttle from '@/lib/throttle';
|
||
|
||
export default {
|
||
name: "SequenceList",
|
||
|
||
data () {
|
||
return {
|
||
headers: [
|
||
{
|
||
value: 'data-table-expand'
|
||
},
|
||
{
|
||
value: "sequence",
|
||
text: "Sequence"
|
||
},
|
||
{
|
||
value: "status",
|
||
text: "Status"
|
||
},
|
||
{
|
||
value: "line",
|
||
text: "Line"
|
||
},
|
||
{
|
||
value: "fsp",
|
||
text: "FSP",
|
||
align: "end"
|
||
},
|
||
{
|
||
value: "fsp_final",
|
||
text: "FPSP",
|
||
align: "end"
|
||
},
|
||
{
|
||
value: "lsp_final",
|
||
text: "LPSP",
|
||
align: "end"
|
||
},
|
||
{
|
||
value: "lsp",
|
||
text: "LSP",
|
||
align: "end"
|
||
},
|
||
{
|
||
value: "duration_final",
|
||
text: "Prime duration",
|
||
align: "end"
|
||
},
|
||
{
|
||
value: "duration",
|
||
text: "Total duration",
|
||
align: "end"
|
||
},
|
||
{
|
||
value: "ts0",
|
||
text: "Start time",
|
||
align: "end"
|
||
},
|
||
{
|
||
value: "ts0_final",
|
||
text: "FPSP time",
|
||
align: "end"
|
||
},
|
||
{
|
||
value: "ts1_final",
|
||
text: "LPSP time",
|
||
align: "end"
|
||
},
|
||
{
|
||
value: "ts1",
|
||
text: "End time",
|
||
align: "end"
|
||
},
|
||
{
|
||
value: "num_points",
|
||
text: "Shots acquired",
|
||
align: "end"
|
||
},
|
||
{
|
||
value: "missing_shots",
|
||
text: "Shots missed",
|
||
align: "end"
|
||
},
|
||
{
|
||
value: "length",
|
||
text: "Length",
|
||
align: "end"
|
||
},
|
||
{
|
||
value: "azimuth",
|
||
text: "Azimuth",
|
||
align: "end"
|
||
}
|
||
],
|
||
expanded: [],
|
||
items: [],
|
||
filter: "",
|
||
options: {},
|
||
num_rows: null,
|
||
activeItem: null,
|
||
edit: null, // {sequence, key, value}
|
||
queuedReload: false,
|
||
itemsPerPage: 25,
|
||
|
||
// Planner related stuff
|
||
preplots: null,
|
||
plannerConfig: null,
|
||
|
||
// Context menu stuff
|
||
contextMenuShow: false,
|
||
contextMenuX: 0,
|
||
contextMenuY: 0,
|
||
contextMenuItem: null,
|
||
|
||
// ASAQC transfer queue
|
||
queuedItems: []
|
||
}
|
||
},
|
||
|
||
computed: {
|
||
|
||
contextMenuItemInTransferQueue () {
|
||
return this.queuedItems.find(i => i.payload.sequence == this.contextMenuItem.sequence);
|
||
},
|
||
|
||
...mapGetters(['user', 'writeaccess', 'loading', 'serverEvent'])
|
||
},
|
||
|
||
watch: {
|
||
options: {
|
||
handler () {
|
||
this.getSequences();
|
||
},
|
||
deep: true
|
||
},
|
||
|
||
async edit (newVal, oldVal) {
|
||
if (newVal === null && oldVal !== null) {
|
||
const item = this.items.find(i => i.sequence == oldVal.sequence);
|
||
if (item && item[oldVal.key] != oldVal.value) {
|
||
if (await this.saveItem(oldVal)) {
|
||
item[oldVal.key] = oldVal.value;
|
||
} else {
|
||
this.edit = oldVal;
|
||
}
|
||
}
|
||
}
|
||
},
|
||
|
||
async serverEvent (event) {
|
||
const subscriptions = ["raw_lines", "final_lines", "final_shots"];
|
||
if (subscriptions.includes(event.channel) && event.payload.pid == this.$route.params.project) {
|
||
if (!this.loading && !this.queuedReload) {
|
||
// Do not force a non-cached response if refreshing as a result
|
||
// of an event notification. We will assume that the server has
|
||
// already had time to update the cache by the time our request
|
||
// gets back to it.
|
||
this.getSequences();
|
||
} else {
|
||
this.queuedReload = true;
|
||
}
|
||
} else if (event.channel == "queue_items") {
|
||
const project =
|
||
event.payload?.project ??
|
||
event.payload?.new?.payload?.project ??
|
||
event.payload?.old?.payload?.project;
|
||
|
||
if (project == this.$route.params.project) {
|
||
this.getQueuedItems();
|
||
}
|
||
}
|
||
},
|
||
|
||
queuedReload (newVal, oldVal) {
|
||
if (newVal && !oldVal && !this.loading) {
|
||
this.getSequences();
|
||
}
|
||
},
|
||
|
||
loading (newVal, oldVal) {
|
||
if (!newVal && oldVal && this.queuedReload) {
|
||
this.getSequences();
|
||
}
|
||
},
|
||
|
||
itemsPerPage (newVal, oldVal) {
|
||
localStorage.setItem(`dougal/prefs/${this.user?.name}/${this.$route.params.project}/${this.$options.name}/items-per-page`, newVal);
|
||
},
|
||
|
||
user (newVal, oldVal) {
|
||
this.itemsPerPage = Number(localStorage.getItem(`dougal/prefs/${this.user?.name}/${this.$route.params.project}/${this.$options.name}/items-per-page`)) || 25;
|
||
}
|
||
|
||
},
|
||
|
||
methods: {
|
||
|
||
contextMenu (e, {item}) {
|
||
e.preventDefault();
|
||
this.contextMenuShow = false;
|
||
this.contextMenuX = e.clientX;
|
||
this.contextMenuY = e.clientY;
|
||
this.contextMenuItem = item;
|
||
this.$nextTick( () => this.contextMenuShow = true );
|
||
},
|
||
|
||
async getReshootEndpoints (item, overlap) {
|
||
const urlPreplot = `/project/${this.$route.params.project}/line`;
|
||
const urlPlannerConfig = `/project/${this.$route.params.project}/configuration/planner`;
|
||
if (!this.preplots) {
|
||
this.preplots = await this.api([urlPreplot]);
|
||
}
|
||
if (!this.plannerConfig) {
|
||
this.plannerConfig = await this.api([urlPlannerConfig]) || {
|
||
overlapBefore: 0,
|
||
overlapAfter: 0
|
||
};
|
||
}
|
||
|
||
const preplot = this.preplots.find(l => l.line == item.line);
|
||
const incr = item.fsp <= item.lsp;
|
||
const lim0 = incr
|
||
? Math.max : Math.min;
|
||
const lim1 = incr
|
||
? Math.min : Math.max;
|
||
const dir = incr ? 1 : -1;
|
||
|
||
const sp0 = overlap
|
||
? lim0((item.fsp_final || item.fsp) - this.plannerConfig.overlapBefore * dir, preplot.fsp)
|
||
: lim0(item.fsp_final || item.fsp, preplot.fsp);
|
||
|
||
const sp1 = overlap
|
||
? lim1((item.lsp_final || item.lsp) + this.plannerConfig.overlapAfter * dir, preplot.lsp)
|
||
: lim1(item.lsp_final || item.lsp, preplot.lsp);
|
||
|
||
return {sp0, sp1}
|
||
},
|
||
|
||
async addToPlan (overlap=false) {
|
||
|
||
const { sp0, sp1 } = await this.getReshootEndpoints(this.contextMenuItem, overlap);
|
||
|
||
const payload = {
|
||
line: this.contextMenuItem.line,
|
||
fsp: sp0,
|
||
lsp: sp1,
|
||
remarks: `Reshoot of sequence ${this.contextMenuItem.sequence}.`
|
||
}
|
||
console.log("Plan", payload);
|
||
const url = `/project/${this.$route.params.project}/plan`;
|
||
const init = {
|
||
method: "POST",
|
||
headers: { "Content-Type": "application/json" },
|
||
body: payload
|
||
}
|
||
await this.api([url, init]);
|
||
},
|
||
|
||
async addToTransferQueue () {
|
||
const payload = [
|
||
{
|
||
project: this.$route.params.project,
|
||
sequence: this.contextMenuItem.sequence
|
||
}
|
||
];
|
||
|
||
const url = `/queue/outgoing/asaqc`;
|
||
const init = {
|
||
method: "POST",
|
||
headers: { "Content-Type": "application/json" },
|
||
body: payload
|
||
}
|
||
|
||
const callback = (err, res) => {
|
||
if (res && res.ok) {
|
||
const text = `Sequence ${this.contextMenuItem.sequence} queued for sending to ASAQC`;
|
||
this.showSnack([text, "info"]);
|
||
}
|
||
};
|
||
|
||
await this.api([url, init, callback]);
|
||
},
|
||
|
||
async removeFromTransferQueue () {
|
||
const item_id = this.contextMenuItemInTransferQueue.item_id;
|
||
|
||
if (item_id) {
|
||
|
||
const url = `/queue/outgoing/asaqc/${item_id}`;
|
||
const init = {
|
||
method: "DELETE",
|
||
headers: { "Content-Type": "application/json" }
|
||
}
|
||
|
||
const callback = (err, res) => {
|
||
if (res && res.ok) {
|
||
const text = `Cancelled sending of sequence ${this.contextMenuItem.sequence} to ASAQC`;
|
||
this.showSnack([text, "primary"]);
|
||
}
|
||
};
|
||
|
||
this.api([url, init, callback]);
|
||
} else {
|
||
this.showSnack(["No item ID in transfer queue", "error"]);
|
||
}
|
||
},
|
||
|
||
queuedItem (sequence) {
|
||
return this.queuedItems.find(i => i.payload.sequence == sequence) || {};
|
||
},
|
||
|
||
editItem (item, key) {
|
||
this.edit = {
|
||
sequence: item.sequence,
|
||
key,
|
||
value: item[key]
|
||
}
|
||
},
|
||
|
||
async saveItem (edit) {
|
||
if (!edit) return;
|
||
|
||
try {
|
||
const url = `/project/${this.$route.params.project}/sequence/${edit.sequence}`;
|
||
const init = {
|
||
method: "PATCH",
|
||
body: {
|
||
[edit.key]: edit.value
|
||
}
|
||
};
|
||
|
||
let res;
|
||
await this.api([url, init, (e, r) => res = r]);
|
||
return res && res.ok;
|
||
} catch (err) {
|
||
return false;
|
||
}
|
||
},
|
||
|
||
setActiveItem (item) {
|
||
this.activeItem = this.activeItem == item
|
||
? null
|
||
: item;
|
||
},
|
||
|
||
async getNumLines () {
|
||
const projectInfo = await this.api([`/project/${this.$route.params.project}`]);
|
||
this.num_rows = projectInfo.sequences;
|
||
},
|
||
|
||
async getSequences () {
|
||
|
||
const query = new URLSearchParams(this.options);
|
||
query.set("filter", this.filter);
|
||
query.set("files", true);
|
||
if (this.options.itemsPerPage < 0) {
|
||
query.delete("itemsPerPage");
|
||
}
|
||
const url = `/project/${this.$route.params.project}/sequence?${query.toString()}`;
|
||
|
||
this.queuedReload = false;
|
||
this.items = await this.api([url]) || [];
|
||
|
||
},
|
||
|
||
async getQueuedItems () {
|
||
const callback = async () => {
|
||
const url = `/queue/outgoing/asaqc/project/${this.$route.params.project}`;
|
||
this.queuedItems = Object.freeze(await this.api([url]) || []);
|
||
}
|
||
throttle(callback, this.getQueuedItems, 100, 500);
|
||
},
|
||
|
||
basename (path, ext) {
|
||
return basename(path, ext);
|
||
},
|
||
|
||
customFilter (value, search, item) {
|
||
if (!search) return true;
|
||
|
||
const number = Number(search);
|
||
|
||
if (!isNaN(number)) {
|
||
if (item.sequence == number) return true;
|
||
|
||
if (search.length > 3) {
|
||
const searchShots = [ "line", "fsp", "lsp", "fsp_final", "lsp_final" ].some( k =>
|
||
item[k] == number
|
||
);
|
||
if (searchShots) return true;
|
||
}
|
||
}
|
||
|
||
if (search.length > 2) {
|
||
const searchDates = [ "ts0", "ts1" ].some(k => {
|
||
const i = item[k].indexOf(search);
|
||
return i >= 0 && i < 10;
|
||
});
|
||
if (searchDates) return true;
|
||
}
|
||
|
||
if ((item.remarks||"").indexOf(search) != -1) return true;
|
||
|
||
if ((item.remarks_final||"").indexOf(search) != -1) return true;
|
||
|
||
if (item.status.indexOf(search.toLowerCase()) == 0) return true;
|
||
|
||
return false;
|
||
},
|
||
|
||
...mapActions(["api", "showSnack"])
|
||
},
|
||
|
||
mounted () {
|
||
this.getSequences();
|
||
this.getNumLines();
|
||
this.getQueuedItems();
|
||
}
|
||
|
||
}
|
||
|
||
</script>
|