Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RaRBJFZKSTA7naHGuQHfnQ
419 lines
14 KiB
TypeScript
419 lines
14 KiB
TypeScript
// The Lucia project.
|
|
// Copyright 2023-2026 DSP, inc. All rights reserved.
|
|
// Authors:
|
|
// chiayin.kuo@dsp.im (chiayin), 2023/1/31
|
|
// imacat.yang@dsp.im (imacat), 2023/9/23
|
|
// cindy.chang@dsp.im (Cindy Chang), 2024/5/30
|
|
/**
|
|
* @module stores/conformance Conformance checking store for managing
|
|
* rule definitions, conformance check results, and report data.
|
|
*/
|
|
|
|
import { defineStore } from "pinia";
|
|
import moment from "moment";
|
|
import { Decimal } from "decimal.js";
|
|
import abbreviateNumber from "@/module/abbreviateNumber.js";
|
|
import apiClient from "@/api/client.js";
|
|
import apiError from "@/module/apiError.js";
|
|
|
|
/**
|
|
* Returns the API base path for the current conformance check,
|
|
* prioritizing temp IDs over created IDs, and filter over log.
|
|
* @param {object} state - The store state.
|
|
* @returns {string} The API base path.
|
|
*/
|
|
function getCheckApiBase(state) {
|
|
const {
|
|
conformanceFilterTempCheckId,
|
|
conformanceLogTempCheckId,
|
|
conformanceFilterCreateCheckId,
|
|
conformanceLogCreateCheckId,
|
|
} = state;
|
|
if (conformanceFilterTempCheckId !== null)
|
|
return `/api/temp-filter-checks/${conformanceFilterTempCheckId}`;
|
|
if (conformanceLogTempCheckId !== null)
|
|
return `/api/temp-log-checks/${conformanceLogTempCheckId}`;
|
|
if (conformanceFilterCreateCheckId !== null)
|
|
return `/api/filter-checks/${conformanceFilterCreateCheckId}`;
|
|
if (conformanceLogCreateCheckId !== null)
|
|
return `/api/log-checks/${conformanceLogCreateCheckId}`;
|
|
return "";
|
|
}
|
|
|
|
/**
|
|
* Returns the API path for conformance params/temp-checks,
|
|
* prioritizing filter over log.
|
|
* @param {object} state - The store state.
|
|
* @returns {{prefix: string, idParam: string}} The API prefix and ID query param.
|
|
*/
|
|
function getFileTypeApi(state) {
|
|
const { conformanceFilterId, conformanceLogId } = state;
|
|
if (conformanceFilterId !== null) {
|
|
return { prefix: "filter", idParam: `filter_id=${conformanceFilterId}` };
|
|
}
|
|
return { prefix: "log", idParam: `log_id=${conformanceLogId}` };
|
|
}
|
|
|
|
/** Pinia store for conformance checking and rule management. */
|
|
export const useConformanceStore = defineStore("conformanceStore", {
|
|
state: () => ({
|
|
conformanceLogId: null, // Log file
|
|
conformanceFilterId: null, // Filter file
|
|
conformanceLogTempCheckId: null, // Temporary check ID for log (before saving)
|
|
conformanceFilterTempCheckId: null, // Temporary check ID for filter (before saving)
|
|
conformanceLogCreateCheckId: null, // Created check ID for log (rule, after saving)
|
|
conformanceFilterCreateCheckId: null, // Created check ID for filter (rule, after saving)
|
|
allConformanceTask: [],
|
|
allCfmSeqStart: [],
|
|
allCfmSeqEnd: [],
|
|
allProcessingTime: {},
|
|
allWaitingTime: {},
|
|
allCycleTime: {},
|
|
allConformanceTempReportData: null,
|
|
allRouteFile: null,
|
|
allIssueTraces: null,
|
|
allTaskSeq: null,
|
|
allCases: null,
|
|
allLoopTraces: null,
|
|
allLoopTaskSeq: null,
|
|
allLoopCases: null,
|
|
selectedRuleType: "Have activity", // radio
|
|
selectedActivitySequence: "Start & End", // radio
|
|
selectedMode: "Directly follows", // radio
|
|
selectedProcessScope: "End to end", // radio
|
|
selectedActSeqMore: "All", // radio
|
|
selectedActSeqFromTo: "From", // radio
|
|
infinite404: null,
|
|
isStartSelected: null, // Whether start is selected in linked start & end selection
|
|
isEndSelected: null, // Whether end is selected in linked start & end selection
|
|
conformanceRuleData: null, // create checkId's data to save
|
|
isUpdateConformance: false, // Show modal after successful save
|
|
conformanceFileName: null, // File name displayed in the save success modal
|
|
}),
|
|
getters: {
|
|
conformanceTask: (state) => {
|
|
return state.allConformanceTask.map((i) => i.label);
|
|
},
|
|
cfmPtEteWhole: (state) => {
|
|
return state.allProcessingTime.end_to_end.whole;
|
|
},
|
|
cfmPtEteStart: (state) => {
|
|
return state.allProcessingTime.end_to_end.starts_with;
|
|
},
|
|
cfmPtEteEnd: (state) => {
|
|
return state.allProcessingTime.end_to_end.ends_with;
|
|
},
|
|
cfmPtEteSE: (state) => {
|
|
return state.allProcessingTime.end_to_end.start_end;
|
|
},
|
|
cfmPtPStart: (state) => {
|
|
return state.allProcessingTime.partial.starts_with;
|
|
},
|
|
cfmPtPEnd: (state) => {
|
|
return state.allProcessingTime.partial.ends_with;
|
|
},
|
|
cfmPtPSE: (state) => {
|
|
return state.allProcessingTime.partial.start_end;
|
|
},
|
|
cfmWtEteWhole: (state) => {
|
|
return state.allWaitingTime.end_to_end.whole;
|
|
},
|
|
cfmWtEteStart: (state) => {
|
|
return state.allWaitingTime.end_to_end.starts_with;
|
|
},
|
|
cfmWtEteEnd: (state) => {
|
|
return state.allWaitingTime.end_to_end.ends_with;
|
|
},
|
|
cfmWtEteSE: (state) => {
|
|
return state.allWaitingTime.end_to_end.start_end;
|
|
},
|
|
cfmWtPStart: (state) => {
|
|
return state.allWaitingTime.partial.starts_with;
|
|
},
|
|
cfmWtPEnd: (state) => {
|
|
return state.allWaitingTime.partial.ends_with;
|
|
},
|
|
cfmWtPSE: (state) => {
|
|
return state.allWaitingTime.partial.start_end;
|
|
},
|
|
cfmCtEteWhole: (state) => {
|
|
return state.allCycleTime.end_to_end.whole;
|
|
},
|
|
cfmCtEteStart: (state) => {
|
|
return state.allCycleTime.end_to_end.starts_with;
|
|
},
|
|
cfmCtEteEnd: (state) => {
|
|
return state.allCycleTime.end_to_end.ends_with;
|
|
},
|
|
cfmCtEteSE: (state) => {
|
|
return state.allCycleTime.end_to_end.start_end;
|
|
},
|
|
cases: (state) => {
|
|
if (state.allCases !== null) {
|
|
return state.allCases.map((c) => {
|
|
const facets = c.facets.map((fac) => {
|
|
const copy = { ...fac };
|
|
switch (copy.type) {
|
|
case "dummy": //sonar-qube
|
|
case "duration-list":
|
|
copy.value = copy.value.map((v) =>
|
|
v === null
|
|
? null
|
|
: abbreviateNumber(new Decimal(v.toFixed(2))),
|
|
);
|
|
copy.value = copy.value.map((v) => (v ?? "").trim()).join(", ");
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
return copy;
|
|
});
|
|
const attributes = c.attributes.map((att) => {
|
|
const copy = { ...att };
|
|
switch (copy.type) {
|
|
case "date":
|
|
copy.value =
|
|
copy.value === null
|
|
? null
|
|
: moment(copy.value).format("YYYY/MM/DD HH:mm:ss");
|
|
break;
|
|
case "float":
|
|
copy.value =
|
|
copy.value === null
|
|
? null
|
|
: new Decimal(copy.value).toFixed(2);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
return copy;
|
|
});
|
|
return {
|
|
...c,
|
|
started_at: moment(c.started_at).format("YYYY/MM/DD HH:mm"),
|
|
completed_at: moment(c.completed_at).format("YYYY/MM/DD HH:mm"),
|
|
facets,
|
|
attributes,
|
|
};
|
|
});
|
|
}
|
|
return [];
|
|
},
|
|
loopCases: (state) => {
|
|
if (state.allLoopCases !== null) {
|
|
return state.allLoopCases.map((c) => {
|
|
const attributes = c.attributes.map((att) => {
|
|
const copy = { ...att };
|
|
switch (copy.type) {
|
|
case "date":
|
|
copy.value =
|
|
copy.value === null
|
|
? null
|
|
: moment(copy.value).format("YYYY/MM/DD HH:mm:ss");
|
|
break;
|
|
case "float":
|
|
copy.value =
|
|
copy.value === null
|
|
? null
|
|
: new Decimal(copy.value).toFixed(2);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
return copy;
|
|
});
|
|
return {
|
|
...c,
|
|
started_at: moment(c.started_at).format("YYYY/MM/DD HH:mm"),
|
|
completed_at: moment(c.completed_at).format("YYYY/MM/DD HH:mm"),
|
|
attributes,
|
|
};
|
|
});
|
|
}
|
|
return [];
|
|
},
|
|
},
|
|
actions: {
|
|
/**
|
|
* fetch Log Conformance Parameters api for Rule Settings.
|
|
*/
|
|
async getConformanceParams() {
|
|
const { prefix, idParam } = getFileTypeApi(this);
|
|
const api = `/api/${prefix}-checks/params?${idParam}`;
|
|
try {
|
|
const response = await apiClient.get(api);
|
|
this.allConformanceTask = response.data.tasks;
|
|
this.allCfmSeqStart = response.data.sources;
|
|
this.allCfmSeqEnd = response.data.sinks;
|
|
this.allProcessingTime = response.data.processing_time;
|
|
this.allWaitingTime = response.data.waiting_time;
|
|
this.allCycleTime = response.data.cycle_time;
|
|
} catch (error) {
|
|
apiError("Failed to load the Conformance Parameters.");
|
|
}
|
|
},
|
|
/**
|
|
* Creates a new temporary check for a log.
|
|
* @param {object} data - The request payload for the backend.
|
|
*/
|
|
async addConformanceCheckId(data) {
|
|
const { prefix, idParam } = getFileTypeApi(this);
|
|
const api = `/api/temp-${prefix}-checks?${idParam}`;
|
|
|
|
try {
|
|
const response = await apiClient.post(api, data);
|
|
if (prefix === "filter") {
|
|
this.conformanceFilterTempCheckId = response.data.id;
|
|
} else {
|
|
this.conformanceLogTempCheckId = response.data.id;
|
|
}
|
|
} catch (error) {
|
|
apiError("Failed to add the Temporary Check for a file.");
|
|
}
|
|
},
|
|
/**
|
|
* Get the Temporary Log Conformance Report
|
|
* @param {boolean} getRouteFile - Whether called to get the route file (used outside the Conformance page).
|
|
*/
|
|
async getConformanceReport(getRouteFile = false) {
|
|
const api = getCheckApiBase(this);
|
|
try {
|
|
const response = await apiClient.get(api);
|
|
if (getRouteFile) {
|
|
this.allRouteFile = response.data.file;
|
|
} else {
|
|
this.allConformanceTempReportData = response.data;
|
|
}
|
|
} catch (error) {
|
|
apiError("Failed to Get the Temporary Log Conformance Report.");
|
|
}
|
|
},
|
|
/**
|
|
* Get the detail of a temporary log conformance issue.
|
|
* @param {number} issueNo - The issue number.
|
|
*/
|
|
async getConformanceIssue(issueNo) {
|
|
const api = `${getCheckApiBase(this)}/issues/${issueNo}`;
|
|
try {
|
|
const response = await apiClient.get(api);
|
|
this.allIssueTraces = response.data.traces;
|
|
} catch (error) {
|
|
apiError(
|
|
"Failed to Get the detail of a temporary log conformance issue.",
|
|
);
|
|
}
|
|
},
|
|
/**
|
|
* Get the Trace Details of a Temporary Log Conformance lssue.
|
|
* @param {number} issueNo - The issue number.
|
|
* @param {number} traceId - The trace ID.
|
|
* @param {number} start - The starting index for loading traces.
|
|
*/
|
|
async getConformanceTraceDetail(issueNo, traceId, start) {
|
|
const api = `${getCheckApiBase(this)}/issues/${issueNo}/traces/${traceId}?start=${start}&page_size=20`;
|
|
try {
|
|
const response = await apiClient.get(api);
|
|
this.allTaskSeq = response.data.task_seq;
|
|
this.allCases = response.data.cases;
|
|
return response.data.cases;
|
|
} catch (error) {
|
|
if (error.response?.status === 404) {
|
|
this.infinite404 = 404;
|
|
return null;
|
|
}
|
|
apiError(
|
|
"Failed to Get the detail of a temporary log conformance issue.",
|
|
);
|
|
return null;
|
|
}
|
|
},
|
|
/**
|
|
* Get the Details of a Temporary Log Conformance Loop.
|
|
* @param {number} loopNo - The loop number.
|
|
*/
|
|
async getConformanceLoop(loopNo) {
|
|
const api = `${getCheckApiBase(this)}/loops/${loopNo}`;
|
|
try {
|
|
const response = await apiClient.get(api);
|
|
this.allLoopTraces = response.data.traces;
|
|
} catch (error) {
|
|
apiError(
|
|
"Failed to Get the detail of a temporary log conformance loop.",
|
|
);
|
|
}
|
|
},
|
|
/**
|
|
* Get the Trace Details of a Temporary Log Conformance Loops.
|
|
* @param {number} loopNo - The loop number.
|
|
* @param {number} traceId - The trace ID.
|
|
* @param {number} start - The starting index for loading traces.
|
|
*/
|
|
async getConformanceLoopsTraceDetail(loopNo, traceId, start) {
|
|
const api = `${getCheckApiBase(this)}/loops/${loopNo}/traces/${traceId}?start=${start}&page_size=20`;
|
|
try {
|
|
const response = await apiClient.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;
|
|
return null;
|
|
}
|
|
apiError(
|
|
"Failed to Get the detail of a temporary log conformance loop.",
|
|
);
|
|
return null;
|
|
}
|
|
},
|
|
/**
|
|
* Add a New Log Conformance Check, Save the log file.
|
|
* @param {string} value file's name
|
|
*/
|
|
async addConformanceCreateCheckId(value) {
|
|
const { prefix, idParam } = getFileTypeApi(this);
|
|
const api = `/api/${prefix}-checks?${idParam}`;
|
|
const data = {
|
|
name: value,
|
|
rule: this.conformanceRuleData,
|
|
};
|
|
|
|
try {
|
|
const response = await apiClient.post(api, data);
|
|
if (prefix === "filter") {
|
|
this.conformanceFilterCreateCheckId = response.data.id;
|
|
this.conformanceFilterTempCheckId = null;
|
|
} else {
|
|
this.conformanceLogCreateCheckId = response.data.id;
|
|
this.conformanceLogTempCheckId = null;
|
|
}
|
|
} catch (error) {
|
|
apiError("Failed to add the Conformance Check for a file.");
|
|
}
|
|
},
|
|
/**
|
|
* Update an Existing Conformance Check, log and filter
|
|
*/
|
|
async updateConformance() {
|
|
const api = getCheckApiBase(this);
|
|
const data = this.conformanceRuleData;
|
|
|
|
try {
|
|
const response = await apiClient.put(api, data);
|
|
this.isUpdateConformance = response.status === 200;
|
|
this.conformanceLogTempCheckId = null;
|
|
this.conformanceFilterTempCheckId = null;
|
|
} catch (error) {
|
|
apiError("Failed to update an Existing Conformance.");
|
|
}
|
|
},
|
|
/**
|
|
* Set the state value of conformance log creation check ID
|
|
* @param {string} createCheckID
|
|
*/
|
|
setConformanceLogCreateCheckId(createCheckID) {
|
|
this.conformanceLogCreateCheckId = createCheckID;
|
|
},
|
|
},
|
|
});
|