Conformance: Activity sequence Short loop(s) done.

This commit is contained in:
chiayin
2023-08-15 10:31:55 +08:00
parent 8f1711de99
commit 82f18ee5e4
4 changed files with 193 additions and 58 deletions

View File

@@ -1,6 +1,5 @@
<template> <template>
<section class="p-4 mr-0.5 space-y-2 h-full w-[calc(100vw_-_316px)] overflow-y-auto scrollbar float-right"> <section class="p-4 mr-0.5 space-y-2 h-full w-[calc(100vw_-_316px)] overflow-y-auto scrollbar float-right">
<!-- cover plate -->
<div v-show="isCoverPlate" class="w-[calc(100vw_-_300px)] h-screen-main fixed bottom-0 right-0 bg-gradient-to-tr from-neutral-500/50 to-neutral-900/50 z-[1]"> <div v-show="isCoverPlate" class="w-[calc(100vw_-_300px)] h-screen-main fixed bottom-0 right-0 bg-gradient-to-tr from-neutral-500/50 to-neutral-900/50 z-[1]">
</div> </div>
<!-- title --> <!-- title -->
@@ -97,9 +96,38 @@
</div> </div>
</div> </div>
</section> </section>
<!-- Loop group -->
<section>
<div v-if="data.loops == null"></div>
<div v-else>
<p class="h2 text-base">Loop List</p>
<div class="border rounded border-neutral-300 p-2 bg-neutral-10 w-full">
<p class="h2 pl-2 mb-2">Short Loop(s)</p>
<table class="text-sm min-w-full table-fixed">
<tbody>
<tr v-for="(trace, key) in data.loops" :key="key">
<td class="p-2 pl-6 truncate max-w-0 w-1/3">
<span class="material-symbols-outlined disc text-sm align-middle mr-1">fiber_manual_record</span>{{ trace.label }}
</td>
<td class="p-2 min-w-[96px] w-2/5">
<div class="h-4 w-full bg-neutral-300 rounded-sm overflow-hidden">
<div class="h-full bg-primary" :style="trace.value"></div>
</div>
</td>
<td class="p-2 text-right truncate">{{ trace.count }}</td>
<td class="p-2 text-center">{{ trace.ratio }}%</td>
<td class="p-2 text-center">
<div class="btn btn-sm btn-c-primary cursor-pointer" @click="openLoopMore(trace.no)">More</div>
</td>
</tr>
</tbody>
</table>
</div>
</div>
</section>
<!-- Issues group --> <!-- Issues group -->
<section> <section>
<div v-if="data.issues == ''"></div> <div v-if="data.issues == null"></div>
<div v-else> <div v-else>
<p class="h2 text-base">Non-conformance Issues</p> <p class="h2 text-base">Non-conformance Issues</p>
<div class="flex gap-4 w-full"> <div class="flex gap-4 w-full">
@@ -117,8 +145,8 @@
<tbody> <tbody>
<tr v-for="(trace, key) in data.issues" :key="key"> <tr v-for="(trace, key) in data.issues" :key="key">
<td class="p-2 pl-6 truncate max-w-0 w-1/3"> <td class="p-2 pl-6 truncate max-w-0 w-1/3">
<span class="material-symbols-outlined disc text-sm align-middle mr-1">fiber_manual_record</span>{{ trace.issue }} <span class="material-symbols-outlined disc text-sm align-middle mr-1">fiber_manual_record</span>{{ trace.label }}
</td> </td>
<td class="p-2 min-w-[96px] w-2/5"> <td class="p-2 min-w-[96px] w-2/5">
<div class="h-4 w-full bg-neutral-300 rounded-sm overflow-hidden"> <div class="h-4 w-full bg-neutral-300 rounded-sm overflow-hidden">
<div class="h-full bg-primary" :style="trace.value"></div> <div class="h-full bg-primary" :style="trace.value"></div>
@@ -136,7 +164,8 @@
</div> </div>
</div> </div>
</section> </section>
<MoreModal :issusModal="issusModal" @update:visible="issusModal = $event" :issueTraces="issueTraces" :issusNo="issusNo" :traceId="traceId" :firstCases="firstCases" ref="moreModal"></MoreModal> <MoreModal :listModal="issuesModal" @update:visible="issusModal = $event" :listTraces="issueTraces" :taskSeq="taskSeq" :cases="cases" :listNo="issuesNo" :traceId="traceId" :firstCases="firstCases" :category="'issue'"></MoreModal>
<MoreModal :listModal="loopModal" @update:visible="loopModal = $event" :listTraces="loopTraces" :taskSeq="loopTaskSeq" :cases="loopCases" :listNo="loopNo" :traceId="looptraceId" :firstCases="loopFirstCases" :category="'loop'"></MoreModal>
</section> </section>
</template> </template>
<script> <script>
@@ -152,9 +181,9 @@ import getMoment from 'moment';
export default { export default {
setup() { setup() {
const conformanceStore = ConformanceStore(); const conformanceStore = ConformanceStore();
const { conformanceTempReportData, issueTraces, taskSeq, } = storeToRefs(conformanceStore); const { conformanceTempReportData, issueTraces, taskSeq, cases, loopTraces, loopTaskSeq, loopCases } = storeToRefs(conformanceStore);
return { conformanceTempReportData, issueTraces, taskSeq, conformanceStore } return { conformanceTempReportData, issueTraces, taskSeq, cases, loopTraces, loopTaskSeq, loopCases, conformanceStore }
}, },
data() { data() {
return { return {
@@ -180,17 +209,22 @@ export default {
time: null, time: null,
tasks: null, tasks: null,
}, },
loops: null,
issues: 'reset' issues: 'reset'
}, },
isCoverPlate: false, isCoverPlate: false,
issusModal: false, issuesModal: false,
loopModal: false,
rateChartData: null, rateChartData: null,
rateChartOptions: null, rateChartOptions: null,
casesChartData: null, casesChartData: null,
casesChartOptions: null, casesChartOptions: null,
issusNo: null, issuesNo: null,
traceId: null, traceId: null,
firstCases: null, firstCases: null,
loopNo: null,
looptraceId: null,
loopFirstCases: null,
tooltip: { tooltip: {
results: { results: {
value: 'This page will perform a conformance check based on the filtering results of the map.', value: 'This page will perform a conformance check based on the filtering results of the map.',
@@ -208,7 +242,6 @@ export default {
watch: { watch: {
conformanceTempReportData: { conformanceTempReportData: {
handler: function(newValue) { handler: function(newValue) {
// this.data = newValue;
this.data = this.setConformanceTempReportData(newValue); this.data = this.setConformanceTempReportData(newValue);
}, },
} }
@@ -238,14 +271,29 @@ export default {
convertSecToDay(sec) { convertSecToDay(sec) {
return (sec / 86400) return (sec / 86400)
}, },
/**
* Open Issues Modal.
* @param {number} no trace no
*/
async openMore(no) { async openMore(no) {
// async await 解決非同步資料延遲傳遞導致未讀取到而出錯的問題 // async await 解決非同步資料延遲傳遞導致未讀取到而出錯的問題
this.issusNo = no; this.issuesNo = no;
await this.conformanceStore.getLogConformanceIssue(no); await this.conformanceStore.getLogConformanceIssue(no);
this.traceId = await this.issueTraces[0].id; this.traceId = await this.issueTraces[0].id;
this.firstCases = await this.conformanceStore.getLogConformanceTraceDetail(no, this.issueTraces[0].id, 0); this.firstCases = await this.conformanceStore.getLogConformanceTraceDetail(no, this.issueTraces[0].id, 0);
this.issusModal = await true; this.issuesModal = await true;
await this.$refs.moreModal.createCy() },
/**
* Open Loop Modal.
* @param {number} no trace no
*/
async openLoopMore(no) {
// async await 解決非同步資料延遲傳遞導致未讀取到而出錯的問題
this.loopNo = no;
await this.conformanceStore.getLogConformanceLoop(no);
this.looptraceId = await this.loopTraces[0].id;
this.loopFirstCases = await this.conformanceStore.getLogConformanceLoopsTraceDetail(no, this.loopTraces[0].id, 0);
this.loopModal = await true;
}, },
/** /**
* set conformance report data * set conformance report data
@@ -256,6 +304,16 @@ export default {
let rate = ((data.counts.conforming / sum) * 100).toFixed(1); let rate = ((data.counts.conforming / sum) * 100).toFixed(1);
let isNullTime = value => value === null ? null : getNumberLabel((value / 86400).toFixed(1)); let isNullTime = value => value === null ? null : getNumberLabel((value / 86400).toFixed(1));
let isNullCase = value => value === null ? null : getNumberLabel(value.toFixed(1)); let isNullCase = value => value === null ? null : getNumberLabel(value.toFixed(1));
let setListData = value => value.map(item => {
return {
no: item.no,
label: item.description,
value: `width:${this.getPercentLabel(item.count / sum)}%;`,
count: item.count,
ratio: this.getPercentLabel(item.count / sum),
}
});
let isNullLists = value => value === null ? null : setListData(value);
let result = { let result = {
counts: { counts: {
@@ -294,15 +352,8 @@ export default {
difference: isNullCase(data.effect.tasks.conforming - data.effect.tasks.not_conforming), difference: isNullCase(data.effect.tasks.conforming - data.effect.tasks.not_conforming),
}, },
}, },
issues: data.issues.map(item => { loops: isNullLists(data.loops),
return { issues: isNullLists(data.issues),
no: item.no,
issue: item.description,
value: `width:${this.getPercentLabel(item.count / sum)}%;`,
count: item.count,
ratio: this.getPercentLabel(item.count / sum),
}
})
}; };
this.setRateChartData(result.charts.rate.data); // 建立圖表 Rate Chart.js this.setRateChartData(result.charts.rate.data); // 建立圖表 Rate Chart.js
this.setCasesChartData(result.charts.cases.data.conforming, result.charts.cases.data.not_conforming, data.charts.cases.x_axis.max, data.charts.cases.x_axis.min); // 建立圖表 Cases Chart.js this.setCasesChartData(result.charts.cases.data.conforming, result.charts.cases.data.not_conforming, data.charts.cases.x_axis.max, data.charts.cases.x_axis.min); // 建立圖表 Cases Chart.js

View File

@@ -144,7 +144,7 @@ export default {
isMode = 'eventually-follows'; isMode = 'eventually-follows';
break; break;
case 'Short loop(s)': case 'Short loop(s)':
isMode = 'b'; isMode = 'short-loops';
break; break;
case 'Self loop(s)': case 'Self loop(s)':
isMode = 'c'; isMode = 'c';

View File

@@ -1,5 +1,5 @@
<template> <template>
<Dialog :visible="issusModal" @update:visible="$emit('update:isVisible', $event)" modal :style="{ width: '90vw', height: '90vh' }" :contentClass="contentClass"> <Dialog :visible="listModal" @update:visible="$emit('update:isVisible', $event)" modal :style="{ width: '90vw', height: '90vh' }" :contentClass="contentClass">
<template #header> <template #header>
<div class=" py-5"> <div class=" py-5">
<p class="text-base font-bold">Non-conformance Issue</p> <p class="text-base font-bold">Non-conformance Issue</p>
@@ -60,12 +60,12 @@ import ConformanceStore from '@/stores/conformance.js';
import cytoscapeMapTrace from '@/module/cytoscapeMapTrace.js'; import cytoscapeMapTrace from '@/module/cytoscapeMapTrace.js';
export default { export default {
props: ['issusModal', 'issusNo', 'traceId', 'firstCases'], props: ['listModal', 'listNo', 'traceId', 'firstCases', 'listTraces', 'taskSeq', 'cases', 'category'],
setup() { setup() {
const conformanceStore = ConformanceStore(); const conformanceStore = ConformanceStore();
const { issueTraces, taskSeq, cases, infinite404 } = storeToRefs(conformanceStore); const { infinite404 } = storeToRefs(conformanceStore);
return { issueTraces, taskSeq, cases, infinite404, conformanceStore } return { infinite404, conformanceStore }
}, },
data() { data() {
return { return {
@@ -85,9 +85,9 @@ export default {
return this.traceList.length; return this.traceList.length;
}, },
traceList: function() { traceList: function() {
let sum = this.issueTraces.map(trace => trace.count).reduce((acc, cur) => acc + cur, 0); let sum = this.listTraces.map(trace => trace.count).reduce((acc, cur) => acc + cur, 0);
return this.issueTraces.map(trace => { return this.listTraces.map(trace => {
return { return {
id: trace.id, id: trace.id,
value: Number((this.getPercentLabel(trace.count / sum))), value: Number((this.getPercentLabel(trace.count / sum))),
@@ -120,8 +120,11 @@ export default {
}, },
}, },
watch: { watch: {
listModal: function(newValue) { // 第一次打開 Modal 要繪圖
if(newValue) this.createCy();
},
taskSeq: function(newValue){ taskSeq: function(newValue){
if(newValue !== null) this.createCy(); if (newValue != null) this.createCy();
}, },
traceId: function(newValue) { traceId: function(newValue) {
// 當 traceId 屬性變化時更新 showTraceId // 當 traceId 屬性變化時更新 showTraceId
@@ -167,7 +170,10 @@ export default {
this.maxItems = false; this.maxItems = false;
this.showTraceId = id; this.showTraceId = id;
this.startNum = 0; this.startNum = 0;
let result = await this.conformanceStore.getLogConformanceTraceDetail(this.issusNo, id, 0);
let result;
if(this.category === 'issue') result = await this.conformanceStore.getLogConformanceTraceDetail(this.listNo, id, 0);
else if(this.category === 'loop') result = await this.conformanceStore.getLogConformanceLoopsTraceDetail(this.listNo, id, 0);
this.infiniteData = await result; this.infiniteData = await result;
}, },
/** /**
@@ -177,35 +183,39 @@ export default {
// 避免每次渲染都重複累加 // 避免每次渲染都重複累加
this.processMap.nodes = []; this.processMap.nodes = [];
// 將 api call 回來的資料帶進 node // 將 api call 回來的資料帶進 node
this.taskSeq.forEach((node, index) => { if(this.taskSeq != null) {
this.processMap.nodes.push({ this.taskSeq.forEach((node, index) => {
data: { this.processMap.nodes.push({
id: index, data: {
label: node, id: index,
backgroundColor: '#CCE5FF', label: node,
bordercolor: '#003366', backgroundColor: '#CCE5FF',
shape: 'round-rectangle', bordercolor: '#003366',
height: 80, shape: 'round-rectangle',
width: 100 height: 80,
} width: 100
}
});
}); });
}) };
}, },
/** /**
* 將 trace edge line 資料彙整 * 將 trace edge line 資料彙整
*/ */
setEdgesData(){ setEdgesData(){
this.processMap.edges = []; this.processMap.edges = [];
this.taskSeq.forEach((edge, index) => { if(this.taskSeq != null) {
this.processMap.edges.push({ this.taskSeq.forEach((edge, index) => {
data: { this.processMap.edges.push({
source: `${index}`, data: {
target: `${index + 1}`, source: `${index}`,
lineWidth: 1, target: `${index + 1}`,
style: 'solid' lineWidth: 1,
} style: 'solid'
}
});
}); });
}); };
// 關係線數量筆節點少一個 // 關係線數量筆節點少一個
this.processMap.edges.pop(); this.processMap.edges.pop();
}, },
@@ -218,7 +228,7 @@ export default {
this.setNodesData(); this.setNodesData();
this.setEdgesData(); this.setEdgesData();
if(graphId !== null) cytoscapeMapTrace(this.processMap.nodes, this.processMap.edges, graphId); if(graphId != null) cytoscapeMapTrace(this.processMap.nodes, this.processMap.edges, graphId);
}); });
}, },
/** /**
@@ -228,7 +238,7 @@ export default {
try { try {
this.loading = true; this.loading = true;
this.startNum += 20 this.startNum += 20
const result = await this.conformanceStore.getLogConformanceTraceDetail(this.issusNo, this.showTraceId, this.startNum); const result = await this.conformanceStore.getLogConformanceTraceDetail(this.listNo, this.showTraceId, this.startNum);
this.infiniteData = [...this.infiniteData, ...result]; this.infiniteData = [...this.infiniteData, ...result];
} catch(error) { } catch(error) {
// console.error('Failed to load data:', error); // console.error('Failed to load data:', error);

View File

@@ -24,6 +24,9 @@ export default defineStore('conformanceStore', {
allIssueTraces: null, allIssueTraces: null,
allTaskSeq: null, allTaskSeq: null,
allCases: null, allCases: null,
allLoopTraces: null,
allLoopTaskSeq: null,
allLoopCases: null,
selectedRuleType: 'Have activity', // radio selectedRuleType: 'Have activity', // radio
selectedActivitySequence: 'Start & End', // radio selectedActivitySequence: 'Start & End', // radio
selectedMode: 'Directly follows', // radio selectedMode: 'Directly follows', // radio
@@ -78,6 +81,35 @@ export default defineStore('conformanceStore', {
}; };
return state.allCases; return state.allCases;
}, },
loopTraces: state => {
return state.allLoopTraces;
},
loopTaskSeq: state => {
return state.allLoopTaskSeq;
},
loopCases: state => {
if(state.allLoopCases !== null){
state.allLoopCases.map(c => {
c.started_at = moment(c.started_at).format('YYYY/MM/DD HH:MM');
c.completed_at = moment(c.completed_at).format('YYYY/MM/DD HH:MM');
c.attributes.map(att => {
switch (att.type) {
case 'date':
att.value = att.value !== null ? moment(att.value).format('YYYY/MM/DD HH:MM:ss') : null;
break;
case 'float':
att.value = att.value !== null ? new Decimal(att.value).toFixed(2) : null;
break
default:
break;
}
return att;
})
return c;
});
};
return state.allLoopCases;
},
}, },
actions: { actions: {
/** /**
@@ -157,8 +189,7 @@ export default defineStore('conformanceStore', {
loading.isLoading = false; loading.isLoading = false;
await delay(500); await delay(500);
$toast.default('Failed to Get the detail of a temporary log conformance issue.',{position: 'bottom'}); $toast.default('Failed to Get the detail of a temporary log conformance issue.',{position: 'bottom'});
};
}
}, },
/** /**
* Get the Trace Details of a Temporary Log Conformance lssue. * Get the Trace Details of a Temporary Log Conformance lssue.
@@ -182,7 +213,50 @@ export default defineStore('conformanceStore', {
await delay(500); await delay(500);
$toast.default('Failed to Get the detail of a temporary log conformance issue.',{position: 'bottom'}); $toast.default('Failed to Get the detail of a temporary log conformance issue.',{position: 'bottom'});
}; };
} };
},
/**
* Get the Details of a Temporary Log Conformance Loop.
*/
async getLogConformanceLoop(loopNo) {
let checkerId = this.conformanceTempCheckerId;
let api = `/api/temp-log-checkers/${checkerId}/loops/${loopNo}`;
try {
const response = await this.$axios.get(api);
this.allLoopTraces = response.data.traces;
} catch(error) {
await delay();
loading.isLoading = true;
await delay(1000);
loading.isLoading = false;
await delay(500);
$toast.default('Failed to Get the detail of a temporary log conformance loop.',{position: 'bottom'});
};
},
/**
* Get the Trace Details of a Temporary Log Conformance Loops.
*/
async getLogConformanceLoopsTraceDetail(loopNo, traceId, start) {
let checkerId = this.conformanceTempCheckerId;
const api = `/api/temp-log-checkers/${checkerId}/loops/${loopNo}/traces/${traceId}?start=${start}&page_size=20`;
try {
const response = await this.$axios.get(api);
this.allLoopTaskSeq = response.data.task_seq;
this.allLoopCases = response.data.cases;
return response.data.cases;
} catch(error) {
if(error.response.status === 404) this.infinite404 = 404;
else {
await delay();
loading.isLoading = true;
await delay(1000);
loading.isLoading = false;
await delay(500);
$toast.default('Failed to Get the detail of a temporary log conformance loop.',{position: 'bottom'});
};
};
}, },
}, },
}) })