浏览代码

take into account ReasonCollection (#1654)

resolves #1608

Co-authored-by: Fred Lefévère-Laoide <Fred.Lefevere-Laoide@Taipy.io>
Fred Lefévère-Laoide 9 月之前
父节点
当前提交
a52da347aa

+ 3 - 3
frontend/taipy/src/DataNodeTable.tsx

@@ -58,14 +58,14 @@ interface DataNodeTableProps {
     onLock?: string;
     onLock?: string;
     editInProgress?: boolean;
     editInProgress?: boolean;
     editLock: MutableRefObject<boolean>;
     editLock: MutableRefObject<boolean>;
-    editable: boolean;
+    notEditableReason: string;
     updateDnVars?: string;
     updateDnVars?: string;
 }
 }
 
 
 const pushRightSx = { ml: "auto" };
 const pushRightSx = { ml: "auto" };
 
 
 const DataNodeTable = (props: DataNodeTableProps) => {
 const DataNodeTable = (props: DataNodeTableProps) => {
-    const { uniqid, configId, nodeId, columns = "", onViewTypeChange, editable, updateDnVars = "" } = props;
+    const { uniqid, configId, nodeId, columns = "", onViewTypeChange, notEditableReason, updateDnVars = "" } = props;
 
 
     const dispatch = useDispatch();
     const dispatch = useDispatch();
     const module = useModule();
     const module = useModule();
@@ -202,7 +202,7 @@ const DataNodeTable = (props: DataNodeTableProps) => {
                 ) : null}
                 ) : null}
                 <Grid item sx={tableEdit ? undefined : pushRightSx}>
                 <Grid item sx={tableEdit ? undefined : pushRightSx}>
                     <FormControlLabel
                     <FormControlLabel
-                        disabled={!props.active || !editable || !!props.editInProgress}
+                        disabled={!props.active || !!notEditableReason || !!props.editInProgress}
                         control={<Switch color="primary" checked={tableEdit} onChange={toggleTableEdit} />}
                         control={<Switch color="primary" checked={tableEdit} onChange={toggleTableEdit} />}
                         label="Edit data"
                         label="Edit data"
                         labelPlacement="start"
                         labelPlacement="start"

+ 13 - 13
frontend/taipy/src/DataNodeViewer.tsx

@@ -114,8 +114,8 @@ type DataNodeFull = [
     DatanodeData, // data
     DatanodeData, // data
     boolean, // editInProgress
     boolean, // editInProgress
     string, // editorId
     string, // editorId
-    boolean, // readable
-    boolean // editable
+    string, // notReadableReason
+    string // notEditableReason
 ];
 ];
 
 
 enum DataNodeFullProps {
 enum DataNodeFullProps {
@@ -131,8 +131,8 @@ enum DataNodeFullProps {
     data,
     data,
     editInProgress,
     editInProgress,
     editorId,
     editorId,
-    readable,
-    editable,
+    notReadableReason,
+    notEditableReason,
 }
 }
 const DataNodeFullLength = Object.keys(DataNodeFullProps).length / 2;
 const DataNodeFullLength = Object.keys(DataNodeFullProps).length / 2;
 
 
@@ -206,8 +206,8 @@ const invalidDatanode: DataNodeFull = [
     [null, null, null, null],
     [null, null, null, null],
     false,
     false,
     "",
     "",
-    false,
-    false,
+    "invalid",
+    "invalid",
 ];
 ];
 
 
 enum TabValues {
 enum TabValues {
@@ -253,8 +253,8 @@ const DataNodeViewer = (props: DataNodeViewerProps) => {
         dnData,
         dnData,
         dnEditInProgress,
         dnEditInProgress,
         dnEditorId,
         dnEditorId,
-        dnReadable,
-        dnEditable,
+        dnNotReadableReason,
+        dnNotEditableReason,
     ] = datanode;
     ] = datanode;
     const dtType = dnData[DatanodeDataProps.type];
     const dtType = dnData[DatanodeDataProps.type];
     const dtValue = dnData[DatanodeDataProps.value] ?? (dtType == "float" ? null : undefined);
     const dtValue = dnData[DatanodeDataProps.value] ?? (dtType == "float" ? null : undefined);
@@ -454,7 +454,7 @@ const DataNodeViewer = (props: DataNodeViewerProps) => {
         [dnId, id, dispatch, module, props.onLock, updateDnVars]
         [dnId, id, dispatch, module, props.onLock, updateDnVars]
     );
     );
 
 
-    const active = useDynamicProperty(props.active, props.defaultActive, true) && dnReadable;
+    const active = useDynamicProperty(props.active, props.defaultActive, true) && !dnNotReadableReason;
     const className = useClassNames(props.libClassName, props.dynamicClassName, props.className);
     const className = useClassNames(props.libClassName, props.dynamicClassName, props.className);
 
 
     // history & data
     // history & data
@@ -715,7 +715,7 @@ const DataNodeViewer = (props: DataNodeViewerProps) => {
                                         onClick={onFocus}
                                         onClick={onFocus}
                                         sx={hoverSx}
                                         sx={hoverSx}
                                     >
                                     >
-                                        {active && dnEditable && focusName === "label" ? (
+                                        {active && !dnNotEditableReason && focusName === "label" ? (
                                             <TextField
                                             <TextField
                                                 label="Label"
                                                 label="Label"
                                                 variant="outlined"
                                                 variant="outlined"
@@ -859,7 +859,7 @@ const DataNodeViewer = (props: DataNodeViewerProps) => {
                                     setFocusName={setFocusName}
                                     setFocusName={setFocusName}
                                     onFocus={onFocus}
                                     onFocus={onFocus}
                                     onEdit={props.onEdit}
                                     onEdit={props.onEdit}
-                                    editable={dnEditable}
+                                    notEditableReason={dnNotEditableReason}
                                     updatePropVars={updateDnVars}
                                     updatePropVars={updateDnVars}
                                 />
                                 />
                             </Grid>
                             </Grid>
@@ -929,7 +929,7 @@ const DataNodeViewer = (props: DataNodeViewerProps) => {
                                         sx={hoverSx}
                                         sx={hoverSx}
                                     >
                                     >
                                         {active &&
                                         {active &&
-                                        dnEditable &&
+                                        !dnNotEditableReason &&
                                         dnEditInProgress &&
                                         dnEditInProgress &&
                                         dnEditorId === editorId &&
                                         dnEditorId === editorId &&
                                         focusName === dataValueFocus ? (
                                         focusName === dataValueFocus ? (
@@ -1088,7 +1088,7 @@ const DataNodeViewer = (props: DataNodeViewerProps) => {
                                                 onLock={props.onLock}
                                                 onLock={props.onLock}
                                                 editInProgress={dnEditInProgress && dnEditorId !== editorId}
                                                 editInProgress={dnEditInProgress && dnEditorId !== editorId}
                                                 editLock={editLock}
                                                 editLock={editLock}
-                                                editable={dnEditable}
+                                                notEditableReason={dnNotEditableReason}
                                                 updateDnVars={updateDnVars}
                                                 updateDnVars={updateDnVars}
                                             />
                                             />
                                         ) : (
                                         ) : (

+ 4 - 4
frontend/taipy/src/PropertiesEditor.tsx

@@ -50,7 +50,7 @@ interface PropertiesEditorProps {
     setFocusName: (name: string) => void;
     setFocusName: (name: string) => void;
     isDefined: boolean;
     isDefined: boolean;
     onEdit?: string;
     onEdit?: string;
-    editable: boolean;
+    notEditableReason: string;
     updatePropVars?: string;
     updatePropVars?: string;
 }
 }
 
 
@@ -65,7 +65,7 @@ const PropertiesEditor = (props: PropertiesEditorProps) => {
         focusName,
         focusName,
         setFocusName,
         setFocusName,
         entProperties,
         entProperties,
-        editable,
+        notEditableReason,
         updatePropVars = "",
         updatePropVars = "",
     } = props;
     } = props;
 
 
@@ -195,7 +195,7 @@ const PropertiesEditor = (props: PropertiesEditorProps) => {
                                   onClick={onFocus}
                                   onClick={onFocus}
                                   sx={hoverSx}
                                   sx={hoverSx}
                               >
                               >
-                                  {active && editable && focusName === propName ? (
+                                  {active && !notEditableReason && focusName === propName ? (
                                       <>
                                       <>
                                           <Grid item xs={4}>
                                           <Grid item xs={4}>
                                               <TextField
                                               <TextField
@@ -284,7 +284,7 @@ const PropertiesEditor = (props: PropertiesEditorProps) => {
                                           </Grid>
                                           </Grid>
                                           <Grid item xs={5}>
                                           <Grid item xs={5}>
                                               <Typography variant="subtitle2">{property.value}</Typography>
                                               <Typography variant="subtitle2">{property.value}</Typography>
-                                          </Grid>{" "}
+                                          </Grid>
                                           <Grid item xs={3} />
                                           <Grid item xs={3} />
                                       </>
                                       </>
                                   )}
                                   )}

+ 19 - 19
frontend/taipy/src/ScenarioViewer.tsx

@@ -106,7 +106,7 @@ interface SequencesRowProps {
     focusName: string;
     focusName: string;
     setFocusName: (name: string) => void;
     setFocusName: (name: string) => void;
     notSubmittableReason: string;
     notSubmittableReason: string;
-    editable: boolean;
+    notEditableReason: string;
     isValid: (sLabel: string, label: string) => boolean;
     isValid: (sLabel: string, label: string) => boolean;
 }
 }
 
 
@@ -119,12 +119,12 @@ const tagsAutocompleteSx = {
     maxWidth: "none",
     maxWidth: "none",
 };
 };
 
 
-type SequenceFull = [string, string[], string, boolean];
+type SequenceFull = [string, string[], string, string];
 // enum SeFProps {
 // enum SeFProps {
 //     label,
 //     label,
 //     tasks,
 //     tasks,
-//     submittable,
-//     editable,
+//     notSubmittableReason,
+//     notEditablereason,
 // }
 // }
 
 
 const SequenceRow = ({
 const SequenceRow = ({
@@ -141,7 +141,7 @@ const SequenceRow = ({
     focusName,
     focusName,
     setFocusName,
     setFocusName,
     notSubmittableReason,
     notSubmittableReason,
-    editable,
+    notEditableReason,
     isValid,
     isValid,
 }: SequencesRowProps) => {
 }: SequencesRowProps) => {
     const [label, setLabel] = useState("");
     const [label, setLabel] = useState("");
@@ -202,7 +202,7 @@ const SequenceRow = ({
 
 
     return (
     return (
         <Grid item xs={12} container justifyContent="space-between" data-focus={name} onClick={onFocus} sx={hoverSx}>
         <Grid item xs={12} container justifyContent="space-between" data-focus={name} onClick={onFocus} sx={hoverSx}>
-            {active && editable && focusName === name ? (
+            {active && !notEditableReason && focusName === name ? (
                 <>
                 <>
                     <Grid item xs={4}>
                     <Grid item xs={4}>
                         <TextField
                         <TextField
@@ -324,11 +324,11 @@ const invalidScenario: ScenarioFull = [
     [],
     [],
     {},
     {},
     [],
     [],
-    false,
-    false,
     "invalid",
     "invalid",
-    false,
-    false,
+    "invalid",
+    "invalid",
+    "invalid",
+    "invalid",
 ];
 ];
 
 
 const ScenarioViewer = (props: ScenarioViewerProps) => {
 const ScenarioViewer = (props: ScenarioViewerProps) => {
@@ -390,11 +390,11 @@ const ScenarioViewer = (props: ScenarioViewerProps) => {
         scDeletable,
         scDeletable,
         scPromotable,
         scPromotable,
         scNotSubmittableReason,
         scNotSubmittableReason,
-        scReadable,
-        scEditable,
+        scNotReadableReason,
+        scNotEditableReason,
     ] = scenario || invalidScenario;
     ] = scenario || invalidScenario;
 
 
-    const active = useDynamicProperty(props.active, props.defaultActive, true) && scReadable;
+    const active = useDynamicProperty(props.active, props.defaultActive, true) && !scNotReadableReason;
     const className = useClassNames(props.libClassName, props.dynamicClassName, props.className);
     const className = useClassNames(props.libClassName, props.dynamicClassName, props.className);
 
 
     const [deleteDialog, setDeleteDialogOpen] = useState(false);
     const [deleteDialog, setDeleteDialogOpen] = useState(false);
@@ -595,7 +595,7 @@ const ScenarioViewer = (props: ScenarioViewerProps) => {
         [sequences]
         [sequences]
     );
     );
 
 
-    const addSequenceHandler = useCallback(() => setSequences((seq) => [...seq, ["", [], "", true]]), []);
+    const addSequenceHandler = useCallback(() => setSequences((seq) => [...seq, ["", [], "", ""]]), []);
 
 
     // on scenario change
     // on scenario change
     useEffect(() => {
     useEffect(() => {
@@ -714,7 +714,7 @@ const ScenarioViewer = (props: ScenarioViewerProps) => {
                                     onClick={onFocus}
                                     onClick={onFocus}
                                     sx={hoverSx}
                                     sx={hoverSx}
                                 >
                                 >
-                                    {active && scEditable && focusName === "label" ? (
+                                    {active && !scNotEditableReason && focusName === "label" ? (
                                         <TextField
                                         <TextField
                                             label="Label"
                                             label="Label"
                                             variant="outlined"
                                             variant="outlined"
@@ -770,7 +770,7 @@ const ScenarioViewer = (props: ScenarioViewerProps) => {
                                         onClick={onFocus}
                                         onClick={onFocus}
                                         sx={hoverSx}
                                         sx={hoverSx}
                                     >
                                     >
-                                        {active && scEditable && focusName === "tags" ? (
+                                        {active && !scNotEditableReason && focusName === "tags" ? (
                                             <Autocomplete
                                             <Autocomplete
                                                 multiple
                                                 multiple
                                                 options={scAuthorizedTags}
                                                 options={scAuthorizedTags}
@@ -857,7 +857,7 @@ const ScenarioViewer = (props: ScenarioViewerProps) => {
                                 setFocusName={setFocusName}
                                 setFocusName={setFocusName}
                                 onFocus={onFocus}
                                 onFocus={onFocus}
                                 onEdit={props.onEdit}
                                 onEdit={props.onEdit}
-                                editable={scEditable}
+                                notEditableReason={scNotEditableReason}
                                 updatePropVars={updateScVars}
                                 updatePropVars={updateScVars}
                             />
                             />
                             {showSequences ? (
                             {showSequences ? (
@@ -874,7 +874,7 @@ const ScenarioViewer = (props: ScenarioViewerProps) => {
                                     </Grid>
                                     </Grid>
 
 
                                     {sequences.map((item, index) => {
                                     {sequences.map((item, index) => {
-                                        const [label, taskIds, notSubmittableReason, editable] = item;
+                                        const [label, taskIds, notSubmittableReason, notEditableReason] = item;
                                         return (
                                         return (
                                             <SequenceRow
                                             <SequenceRow
                                                 active={active}
                                                 active={active}
@@ -891,7 +891,7 @@ const ScenarioViewer = (props: ScenarioViewerProps) => {
                                                 focusName={focusName}
                                                 focusName={focusName}
                                                 setFocusName={setFocusName}
                                                 setFocusName={setFocusName}
                                                 notSubmittableReason={notSubmittableReason}
                                                 notSubmittableReason={notSubmittableReason}
-                                                editable={editable}
+                                                notEditableReason={notEditableReason}
                                                 isValid={isValidSequence}
                                                 isValid={isValidSequence}
                                             />
                                             />
                                         );
                                         );

+ 5 - 5
frontend/taipy/src/utils.ts

@@ -24,14 +24,14 @@ export type ScenarioFull = [
     string,     // label
     string,     // label
     string[],   // tags
     string[],   // tags
     Array<[string, string]>,    // properties
     Array<[string, string]>,    // properties
-    Array<[string, string[], string, boolean]>,   // sequences (label, task ids, notSubmittableReason, editable)
+    Array<[string, string[], string, string]>,   // sequences (label, task ids, notSubmittableReason, notEditableReason)
     Record<string, string>, // tasks (id: label)
     Record<string, string>, // tasks (id: label)
     string[],   // authorized_tags
     string[],   // authorized_tags
-    boolean,    // deletable
-    boolean,    // promotable
+    string,    // notDeletableReason
+    string,    // notPromotableReason
     string,     // notSubmittableReason
     string,     // notSubmittableReason
-    boolean,    // readable
-    boolean     // editable
+    string,     // notReadableReason
+    string      // notEditableReason
 ];
 ];
 
 
 export enum ScFProps {
 export enum ScFProps {

+ 13 - 9
taipy/gui_core/_adapters.py

@@ -37,6 +37,7 @@ from taipy.core import (
 from taipy.core import get as core_get
 from taipy.core import get as core_get
 from taipy.core.config import Config
 from taipy.core.config import Config
 from taipy.core.data._tabular_datanode_mixin import _TabularDataNodeMixin
 from taipy.core.data._tabular_datanode_mixin import _TabularDataNodeMixin
+from taipy.core.reason import ReasonCollection
 from taipy.gui._warnings import _warn
 from taipy.gui._warnings import _warn
 from taipy.gui.gui import _DoNotUpdate
 from taipy.gui.gui import _DoNotUpdate
 from taipy.gui.utils import _is_boolean, _is_true, _TaipyBase
 from taipy.gui.utils import _is_boolean, _is_true, _TaipyBase
@@ -55,6 +56,9 @@ class _EntityType(Enum):
     DATANODE = 3
     DATANODE = 3
 
 
 
 
+def _get_reason(rc: ReasonCollection, message: str):
+    return "" if rc else f"{message}: {rc.reasons}"
+
 class _GuiCoreScenarioAdapter(_TaipyBase):
 class _GuiCoreScenarioAdapter(_TaipyBase):
     __INNER_PROPS = ["name"]
     __INNER_PROPS = ["name"]
 
 
@@ -84,8 +88,8 @@ class _GuiCoreScenarioAdapter(_TaipyBase):
                             (
                             (
                                 s.get_simple_label(),
                                 s.get_simple_label(),
                                 [t.id for t in s.tasks.values()] if hasattr(s, "tasks") else [],
                                 [t.id for t in s.tasks.values()] if hasattr(s, "tasks") else [],
-                                "" if (reason := is_submittable(s)) else f"Sequence not submittable: {reason.reasons}",
-                                is_editable(s),
+                                _get_reason(is_submittable(s), "Sequence not submittable"),
+                                _get_reason(is_editable(s), "Sequence not editable"),
                             )
                             )
                             for s in scenario.sequences.values()
                             for s in scenario.sequences.values()
                         ]
                         ]
@@ -95,11 +99,11 @@ class _GuiCoreScenarioAdapter(_TaipyBase):
                         if hasattr(scenario, "tasks")
                         if hasattr(scenario, "tasks")
                         else {},
                         else {},
                         list(scenario.properties.get("authorized_tags", [])) if scenario.properties else [],
                         list(scenario.properties.get("authorized_tags", [])) if scenario.properties else [],
-                        is_deletable(scenario),
-                        is_promotable(scenario),
-                        "" if (reason := is_submittable(scenario)) else f"Scenario not submittable: {reason.reasons}",
-                        is_readable(scenario),
-                        is_editable(scenario),
+                        _get_reason(is_deletable(scenario), "Scenario not deletable"),
+                        _get_reason(is_promotable(scenario), "Scenario not promotable"),
+                        _get_reason(is_submittable(scenario), "Scenario not submittable"),
+                        _get_reason(is_readable(scenario), "Scenario not readable"),
+                        _get_reason(is_editable(scenario), "Scenario not editable"),
                     ]
                     ]
             except Exception as e:
             except Exception as e:
                 _warn(f"Access to scenario ({data.id if hasattr(data, 'id') else 'No_id'}) failed", e)
                 _warn(f"Access to scenario ({data.id if hasattr(data, 'id') else 'No_id'}) failed", e)
@@ -221,8 +225,8 @@ class _GuiCoreDatanodeAdapter(_TaipyBase):
                         self.__get_data(datanode),
                         self.__get_data(datanode),
                         datanode._edit_in_progress,
                         datanode._edit_in_progress,
                         datanode._editor_id,
                         datanode._editor_id,
-                        is_readable(datanode),
-                        is_editable(datanode),
+                        _get_reason(is_readable(datanode), "Datanode not readable"),
+                        _get_reason(is_editable(datanode), "Datanode not editable"),
                     ]
                     ]
             except Exception as e:
             except Exception as e:
                 _warn(f"Access to datanode ({data.id if hasattr(data, 'id') else 'No_id'}) failed", e)
                 _warn(f"Access to datanode ({data.id if hasattr(data, 'id') else 'No_id'}) failed", e)