Преглед на файлове

add submission indicator to scenario (#1393)

* add submission indicator to scenario
refresh dag without on_submission

* Fab's comment

---------

Co-authored-by: Fred Lefévère-Laoide <Fred.Lefevere-Laoide@Taipy.io>
Fred Lefévère-Laoide преди 11 месеца
родител
ревизия
046bfef965
променени са 4 файла, в които са добавени 74 реда и са изтрити 51 реда
  1. 14 43
      frontend/taipy/src/JobSelector.tsx
  2. 10 0
      frontend/taipy/src/ScenarioViewer.tsx
  3. 34 0
      frontend/taipy/src/StatusChip.tsx
  4. 16 8
      taipy/gui_core/_context.py

+ 14 - 43
frontend/taipy/src/JobSelector.tsx

@@ -16,7 +16,6 @@ import { DeleteOutline, StopCircleOutlined, Add, FilterList } from "@mui/icons-m
 import Box from "@mui/material/Box";
 import Button from "@mui/material/Button";
 import Checkbox from "@mui/material/Checkbox";
-import Chip from "@mui/material/Chip";
 import FormControl from "@mui/material/FormControl";
 import Grid from "@mui/material/Grid";
 import IconButton from "@mui/material/IconButton";
@@ -49,6 +48,7 @@ import {
 } from "taipy-gui";
 
 import { disableColor, popoverOrigin, useClassNames } from "./utils";
+import StatusChip, { Status } from "./StatusChip";
 
 interface JobSelectorProps {
     updateVarName?: string;
@@ -93,41 +93,12 @@ enum JobProps {
 }
 const JobLength = Object.keys(JobProps).length / 2;
 
-enum JobStatus {
-    SUBMITTED = 1,
-    BLOCKED = 2,
-    PENDING = 3,
-    RUNNING = 4,
-    CANCELED = 5,
-    FAILED = 6,
-    COMPLETED = 7,
-    SKIPPED = 8,
-    ABANDONED = 9,
-}
-
 const containerSx = { width: "100%", mb: 2 };
 const selectSx = { height: 50 };
 const containerPopupSx = { width: "619px" };
 const tableWidthSx = { minWidth: 750 };
 const toolbarRightSx = { mr: 6 };
 
-const ChipStatus = ({ status }: { status: number }) => {
-    const statusText = JobStatus[status];
-    let colorFill: "warning" | "default" | "success" | "error" = "warning";
-
-    if (status === JobStatus.COMPLETED || status === JobStatus.SKIPPED) {
-        colorFill = "success";
-    } else if (status === JobStatus.FAILED) {
-        colorFill = "error";
-    } else if (status === JobStatus.CANCELED || status === JobStatus.ABANDONED) {
-        colorFill = "default";
-    }
-
-    const variant = status === JobStatus.FAILED || status === JobStatus.RUNNING ? "filled" : "outlined";
-
-    return <Chip label={statusText} variant={variant} color={colorFill} />;
-};
-
 type JobSelectorColumns = {
     id: string;
     primaryLabel: string;
@@ -443,13 +414,13 @@ const JobSelectedTableRow = ({
             ) : null}
             {showDate ? <TableCell>{creationDate ? new Date(creationDate).toLocaleString() : ""}</TableCell> : null}
             <TableCell>
-                <ChipStatus status={status} />
+                <StatusChip status={status} />
             </TableCell>
             {showCancel || showDelete ? (
                 <TableCell>
-                    {status === JobStatus.RUNNING ? null : status === JobStatus.BLOCKED ||
-                      status === JobStatus.PENDING ||
-                      status === JobStatus.SUBMITTED ? (
+                    {status === Status.RUNNING ? null : status === Status.BLOCKED ||
+                      status === Status.PENDING ||
+                      status === Status.SUBMITTED ? (
                         showCancel ? (
                             <Tooltip title="Cancel Job">
                                 <IconButton data-id={id} onClick={handleCancelJobs}>
@@ -648,9 +619,9 @@ const JobSelector = (props: JobSelectorProps) => {
                 .filter((job) => checked.includes(job[JobProps.id]))
                 .every(
                     (job) =>
-                        job[JobProps.status] === JobStatus.SUBMITTED ||
-                        job[JobProps.status] === JobStatus.BLOCKED ||
-                        job[JobProps.status] === JobStatus.PENDING
+                        job[JobProps.status] === Status.SUBMITTED ||
+                        job[JobProps.status] === Status.BLOCKED ||
+                        job[JobProps.status] === Status.PENDING
                 ),
         [jobRows, checked]
     );
@@ -662,11 +633,11 @@ const JobSelector = (props: JobSelectorProps) => {
                 .filter((job) => checked.includes(job[JobProps.id]))
                 .every(
                     (job) =>
-                        job[JobProps.status] === JobStatus.CANCELED ||
-                        job[JobProps.status] === JobStatus.FAILED ||
-                        job[JobProps.status] === JobStatus.COMPLETED ||
-                        job[JobProps.status] === JobStatus.SKIPPED ||
-                        job[JobProps.status] === JobStatus.ABANDONED
+                        job[JobProps.status] === Status.CANCELED ||
+                        job[JobProps.status] === Status.FAILED ||
+                        job[JobProps.status] === Status.COMPLETED ||
+                        job[JobProps.status] === Status.SKIPPED ||
+                        job[JobProps.status] === Status.ABANDONED
                 ),
         [jobRows, checked]
     );
@@ -689,7 +660,7 @@ const JobSelector = (props: JobSelectorProps) => {
                     filteredJobRows = filteredJobRows.filter((job) => {
                         let rowColumnValue = "";
                         if (filter.data === JobProps.status) {
-                            rowColumnValue = JobStatus[job[JobProps.status]].toLowerCase();
+                            rowColumnValue = Status[job[JobProps.status]].toLowerCase();
                         } else if (filter.data === JobProps.id) {
                             rowColumnValue = `${job[JobProps.id].toLowerCase()}${job[JobProps.name].toLowerCase()}`;
                         } else if (filter.data === JobProps.submitted_id) {

+ 10 - 0
frontend/taipy/src/ScenarioViewer.tsx

@@ -60,6 +60,7 @@ import {
 } from "./utils";
 import ConfirmDialog from "./utils/ConfirmDialog";
 import PropertiesEditor from "./PropertiesEditor";
+import StatusChip, { Status } from "./StatusChip";
 
 interface ScenarioViewerProps {
     id?: string;
@@ -365,6 +366,7 @@ const ScenarioViewer = (props: ScenarioViewerProps) => {
             }
         }
         setValid(!!sc);
+        setSubmissionStatus(0);
         setScenario((oldSc) => (oldSc === sc ? oldSc : sc ? (deepEqual(oldSc, sc) ? oldSc : sc) : invalidScenario));
     }, [props.scenario, props.defaultScenario]);
 
@@ -450,6 +452,7 @@ const ScenarioViewer = (props: ScenarioViewerProps) => {
                         error_id: getUpdateVar(updateScVar, "error_id"),
                     })
                 );
+                setSubmissionStatus(Status.SUBMITTED);
             }
         },
         [valid, props.onSubmit, props.onSubmissionChange, id, scId, dispatch, module, updateScVar]
@@ -575,6 +578,9 @@ const ScenarioViewer = (props: ScenarioViewerProps) => {
 
     const addSequenceHandler = useCallback(() => setSequences((seq) => [...seq, ["", [], "", true]]), []);
 
+    // Submission status
+    const [submissionStatus, setSubmissionStatus] = useState(0);
+
     // on scenario change
     useEffect(() => {
         showTags && setTags(scTags);
@@ -589,6 +595,9 @@ const ScenarioViewer = (props: ScenarioViewerProps) => {
         const ids = props.coreChanged?.scenario;
         if (typeof ids === "string" ? ids === scId : Array.isArray(ids) ? ids.includes(scId) : ids) {
             props.updateVarName && dispatch(createRequestUpdateAction(id, module, [props.updateVarName], true));
+            if (props.coreChanged?.submission !== undefined) {
+                setSubmissionStatus(props.coreChanged?.submission as number);
+            }
         }
     }, [props.coreChanged, props.updateVarName, id, module, dispatch, scId]);
 
@@ -620,6 +629,7 @@ const ScenarioViewer = (props: ScenarioViewerProps) => {
                                         sx={ChipSx}
                                     />
                                 ) : null}
+                                {submissionStatus ? <StatusChip status={submissionStatus} sx={ChipSx} /> : null}
                             </Grid>
                             <Grid item>
                                 {showSubmit ? (

+ 34 - 0
frontend/taipy/src/StatusChip.tsx

@@ -0,0 +1,34 @@
+import React from "react";
+import { SxProps, Theme } from "@mui/material";
+import Chip from "@mui/material/Chip";
+
+export enum Status {
+    SUBMITTED = 1,
+    BLOCKED = 2,
+    PENDING = 3,
+    RUNNING = 4,
+    CANCELED = 5,
+    FAILED = 6,
+    COMPLETED = 7,
+    SKIPPED = 8,
+    ABANDONED = 9,
+}
+
+const StatusChip = ({ status, sx }: { status: number; sx?: SxProps<Theme> }) => {
+    const statusText = Status[status];
+    let colorFill: "warning" | "default" | "success" | "error" = "warning";
+
+    if (status === Status.COMPLETED || status === Status.SKIPPED) {
+        colorFill = "success";
+    } else if (status === Status.FAILED) {
+        colorFill = "error";
+    } else if (status === Status.CANCELED || status === Status.ABANDONED) {
+        colorFill = "default";
+    }
+
+    const variant = status === Status.FAILED || status === Status.RUNNING ? "filled" : "outlined";
+
+    return statusText ? <Chip label={statusText} variant={variant} color={colorFill} sx={sx} /> : null;
+};
+
+export default StatusChip;

+ 16 - 8
taipy/gui_core/_context.py

@@ -149,6 +149,8 @@ class _GuiCoreContext(CoreEventConsumerBase):
     def submission_status_callback(self, submission_id: t.Optional[str] = None, event: t.Optional[Event] = None):
         if not submission_id or not is_readable(t.cast(SubmissionId, submission_id)):
             return
+        submission = None
+        new_status = None
         try:
             last_status = self.client_submission.get(submission_id)
             if not last_status:
@@ -158,7 +160,7 @@ class _GuiCoreContext(CoreEventConsumerBase):
             if not submission or not submission.entity_id:
                 return
 
-            new_status = submission.submission_status
+            new_status = t.cast(SubmissionStatus, submission.submission_status)
 
             client_id = submission.properties.get("client_id")
             if client_id:
@@ -207,7 +209,14 @@ class _GuiCoreContext(CoreEventConsumerBase):
             _warn(f"Submission ({submission_id}) is not available", e)
 
         finally:
-            self.gui._broadcast(_GuiCoreContext._CORE_CHANGED_NAME, {"jobs": True})
+            self.gui._broadcast(
+                _GuiCoreContext._CORE_CHANGED_NAME,
+                {
+                    "jobs": True,
+                    "scenario": (submission.entity_id if submission else None) or False,
+                    "submission": new_status.value if new_status else None,
+                },
+            )
 
     def no_change_adapter(self, entity: t.List):
         return entity
@@ -575,13 +584,12 @@ class _GuiCoreContext(CoreEventConsumerBase):
                     client_id=self.gui._get_client_id(),
                     module_context=self.gui._get_locals_context(),
                 )
-                if on_submission:
+                with self.submissions_lock:
+                    self.client_submission[submission_entity.id] = submission_entity.submission_status
+                if Config.core.mode == "development":
                     with self.submissions_lock:
-                        self.client_submission[submission_entity.id] = submission_entity.submission_status
-                    if Config.core.mode == "development":
-                        with self.submissions_lock:
-                            self.client_submission[submission_entity.id] = SubmissionStatus.SUBMITTED
-                        self.submission_status_callback(submission_entity.id)
+                        self.client_submission[submission_entity.id] = SubmissionStatus.SUBMITTED
+                    self.submission_status_callback(submission_entity.id)
                 _GuiCoreContext.__assign_var(state, error_var, "")
         except Exception as e:
             _GuiCoreContext.__assign_var(state, error_var, f"Error submitting entity. {e}")