Переглянути джерело

remove overengineering, added hard_delete for submission

Toan Quach 1 рік тому
батько
коміт
e3f4eb2716

+ 0 - 25
taipy/core/common/_check_entity_and_get_manager.py

@@ -1,25 +0,0 @@
-# Copyright 2021-2024 Avaiga Private Limited
-#
-# Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
-# the License. You may obtain a copy of the License at
-#
-#        http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
-# an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
-# specific language governing permissions and limitations under the License.
-
-from typing import List, Optional, Union
-
-from .._entity._entity import _Entity
-from .._entity._reload import _get_manager
-from .._manager._manager import _Manager
-
-
-def _check_entity_and_get_manager(entity: Union[_Entity, str], possible_classes: List) -> Optional[_Manager]:
-    for possible_class in possible_classes:
-        if isinstance(entity, possible_class) or (
-            isinstance(entity, str) and entity.startswith(possible_class._ID_PREFIX)  # type: ignore
-        ):
-            return _get_manager(possible_class._MANAGER_NAME)  # type: ignore
-    return None

+ 49 - 0
taipy/core/common/_check_instance.py

@@ -0,0 +1,49 @@
+# Copyright 2021-2024 Avaiga Private Limited
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+#
+#        http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
+# an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations under the License.
+
+from typing import Union
+
+from .._entity._entity import _Entity
+from ..cycle.cycle import Cycle
+from ..data.data_node import DataNode
+from ..job.job import Job
+from ..scenario.scenario import Scenario
+from ..sequence.sequence import Sequence
+from ..submission.submission import Submission
+from ..task.task import Task
+
+
+def _is_cycle(entity: Union[_Entity, str]) -> bool:
+    return isinstance(entity, Cycle) or (isinstance(entity, str) and entity.startswith(Cycle._ID_PREFIX))
+
+
+def _is_scenario(entity: Union[_Entity, str]) -> bool:
+    return isinstance(entity, Scenario) or (isinstance(entity, str) and entity.startswith(Scenario._ID_PREFIX))
+
+
+def _is_sequence(entity: Union[_Entity, str]) -> bool:
+    return isinstance(entity, Sequence) or (isinstance(entity, str) and entity.startswith(Sequence._ID_PREFIX))
+
+
+def _is_task(entity: Union[_Entity, str]) -> bool:
+    return isinstance(entity, Task) or (isinstance(entity, str) and entity.startswith(Task._ID_PREFIX))
+
+
+def _is_job(entity: Union[_Entity, str]) -> bool:
+    return isinstance(entity, Job) or (isinstance(entity, str) and entity.startswith(Job._ID_PREFIX))
+
+
+def _is_data_node(entity: Union[_Entity, str]) -> bool:
+    return isinstance(entity, DataNode) or (isinstance(entity, str) and entity.startswith(DataNode._ID_PREFIX))
+
+
+def _is_submission(entity: Union[_Entity, str]) -> bool:
+    return isinstance(entity, Submission) or (isinstance(entity, str) and entity.startswith(Submission._ID_PREFIX))

+ 17 - 0
taipy/core/submission/_submission_manager.py

@@ -11,6 +11,7 @@
 
 from typing import List, Optional, Union
 
+from .._entity._entity_ids import _EntityIds
 from .._manager._manager import _Manager
 from .._repository._abstract_repository import _AbstractRepository
 from .._version._version_mixin import _VersionMixin
@@ -70,6 +71,22 @@ class _SubmissionManager(_Manager[Submission], _VersionMixin):
             cls._logger.error(err)
             raise err
 
+    @classmethod
+    def _hard_delete(cls, submission_id: SubmissionId):
+        submission = cls._get(submission_id)
+        entity_ids_to_delete = cls._get_children_entity_ids(submission)
+        entity_ids_to_delete.submission_ids.add(submission.id)
+        cls._delete_entities_of_multiple_types(entity_ids_to_delete)
+
+    @classmethod
+    def _get_children_entity_ids(cls, submission: Submission):
+        entity_ids = _EntityIds()
+
+        for job in submission.jobs:
+            entity_ids.job_ids.add(job.id)
+
+        return entity_ids
+
     @classmethod
     def _is_deletable(cls, submission: Union[Submission, SubmissionId]) -> bool:
         if isinstance(submission, str):

+ 117 - 31
taipy/core/taipy.py

@@ -20,7 +20,15 @@ from taipy.logger._taipy_logger import _TaipyLogger
 from ._entity._entity import _Entity
 from ._entity._reload import _get_manager
 from ._version._version_manager_factory import _VersionManagerFactory
-from .common._check_entity_and_get_manager import _check_entity_and_get_manager
+from .common._check_instance import (
+    _is_cycle,
+    _is_data_node,
+    _is_job,
+    _is_scenario,
+    _is_sequence,
+    _is_submission,
+    _is_task,
+)
 from .common._warnings import _warn_no_core_service
 from .config.data_node_config import DataNodeConfig
 from .config.scenario_config import ScenarioConfig
@@ -62,8 +70,16 @@ def set(entity: Union[DataNode, Task, Sequence, Scenario, Cycle]):
         entity (Union[DataNode^, Task^, Sequence^, Scenario^, Cycle^]): The
             entity to save or update.
     """
-    if manager := _check_entity_and_get_manager(entity, [Cycle, Scenario, Sequence, Task, DataNode]):
-        manager._set(entity)
+    if _is_cycle(entity):
+        return _get_manager(Cycle._MANAGER_NAME)._set(entity)
+    if _is_scenario(entity):
+        return _get_manager(Scenario._MANAGER_NAME)._set(entity)
+    if _is_sequence(entity):
+        return _get_manager(Sequence._MANAGER_NAME)._set(entity)
+    if _is_task(entity):
+        return _get_manager(Task._MANAGER_NAME)._set(entity)
+    if _is_data_node(entity):
+        return _get_manager(DataNode._MANAGER_NAME)._set(entity)
 
 
 def is_submittable(entity: Union[Scenario, ScenarioId, Sequence, SequenceId, Task, TaskId]) -> bool:
@@ -74,8 +90,12 @@ def is_submittable(entity: Union[Scenario, ScenarioId, Sequence, SequenceId, Tas
     Returns:
         True if the given entity can be submitted. False otherwise.
     """
-    if manager := _check_entity_and_get_manager(entity, [Scenario, Sequence, Task]):
-        return manager._is_submittable(entity)  # type: ignore
+    if _is_scenario(entity):
+        return _get_manager(Scenario._MANAGER_NAME)._is_submittable(entity)  # type: ignore
+    if _is_sequence(entity):
+        return _get_manager(Sequence._MANAGER_NAME)._is_submittable(entity)  # type: ignore
+    if _is_task(entity):
+        return _get_manager(Task._MANAGER_NAME)._is_submittable(entity)  # type: ignore
     return False
 
 
@@ -104,8 +124,20 @@ def is_editable(
     Returns:
         True if the given entity can be edited. False otherwise.
     """
-    if manager := _check_entity_and_get_manager(entity, [Cycle, Scenario, Sequence, Task, Job, DataNode, Submission]):
-        return manager._is_editable(entity)
+    if _is_cycle(entity):
+        return _get_manager(Cycle._MANAGER_NAME)._is_editable(entity)
+    if _is_scenario(entity):
+        return _get_manager(Scenario._MANAGER_NAME)._is_editable(entity)
+    if _is_sequence(entity):
+        return _get_manager(Sequence._MANAGER_NAME)._is_editable(entity)
+    if _is_task(entity):
+        return _get_manager(Task._MANAGER_NAME)._is_editable(entity)
+    if _is_job(entity):
+        return _get_manager(Job._MANAGER_NAME)._is_editable(entity)
+    if _is_data_node(entity):
+        return _get_manager(DataNode._MANAGER_NAME)._is_editable(entity)
+    if _is_submission(entity):
+        return _get_manager(Submission._MANAGER_NAME)._is_editable(entity)
     return False
 
 
@@ -134,8 +166,20 @@ def is_readable(
     Returns:
         True if the given entity can be read. False otherwise.
     """
-    if manager := _check_entity_and_get_manager(entity, [Cycle, Scenario, Sequence, Task, Job, DataNode, Submission]):
-        return manager._is_readable(entity)
+    if _is_cycle(entity):
+        return _get_manager(Cycle._MANAGER_NAME)._is_readable(entity)
+    if _is_scenario(entity):
+        return _get_manager(Scenario._MANAGER_NAME)._is_readable(entity)
+    if _is_sequence(entity):
+        return _get_manager(Sequence._MANAGER_NAME)._is_readable(entity)
+    if _is_task(entity):
+        return _get_manager(Task._MANAGER_NAME)._is_readable(entity)
+    if _is_job(entity):
+        return _get_manager(Job._MANAGER_NAME)._is_readable(entity)
+    if _is_data_node(entity):
+        return _get_manager(DataNode._MANAGER_NAME)._is_readable(entity)
+    if _is_submission(entity):
+        return _get_manager(Submission._MANAGER_NAME)._is_readable(entity)
     return False
 
 
@@ -167,8 +211,16 @@ def submit(
             - If a `Scenario^` or a `Sequence^` is provided, it will return a list of `Job^`.
             - If a `Task^` is provided, it will return the created `Job^`.
     """
-    if manager := _check_entity_and_get_manager(entity, [Scenario, Sequence, Task]):
-        return manager._submit(entity, force=force, wait=wait, timeout=timeout)  # type: ignore
+    if _is_scenario(entity):
+        return _get_manager(Scenario._MANAGER_NAME)._submit(  # type: ignore
+            entity, force=force, wait=wait, timeout=timeout
+        )
+    if _is_sequence(entity):
+        return _get_manager(Sequence._MANAGER_NAME)._submit(  # type: ignore
+            entity, force=force, wait=wait, timeout=timeout
+        )
+    if _is_task(entity):
+        return _get_manager(Task._MANAGER_NAME)._submit(entity, force=force, wait=wait, timeout=timeout)  # type: ignore
     return None
 
 
@@ -235,10 +287,20 @@ def exists(entity_id: Union[TaskId, DataNodeId, SequenceId, ScenarioId, JobId, C
         (`Job^`, `Cycle^`, `Scenario^`, `Sequence^`, `Task^`, `DataNode^`)
         based on their respective identifier prefixes.
     """
-    if manager := _check_entity_and_get_manager(
-        entity_id, [Cycle, Scenario, Sequence, Task, Job, DataNode, Submission]
-    ):
-        return manager._exists(entity_id)
+    if _is_job(entity_id):
+        return _get_manager(Job._MANAGER_NAME)._exists(JobId(entity_id))
+    if _is_cycle(entity_id):
+        return _get_manager(Cycle._MANAGER_NAME)._exists(CycleId(entity_id))
+    if _is_scenario(entity_id):
+        return _get_manager(Scenario._MANAGER_NAME)._exists(ScenarioId(entity_id))
+    if _is_sequence(entity_id):
+        return _get_manager(Sequence._MANAGER_NAME)._exists(SequenceId(entity_id))
+    if _is_task(entity_id):
+        return _get_manager(Task._MANAGER_NAME)._exists(TaskId(entity_id))
+    if _is_data_node(entity_id):
+        return _get_manager(DataNode._MANAGER_NAME)._exists(DataNodeId(entity_id))
+    if _is_submission(entity_id):
+        return _get_manager(Submission._MANAGER_NAME)._exists(SubmissionId(entity_id))
 
     raise ModelNotFound("NOT_DETERMINED", entity_id)
 
@@ -305,10 +367,20 @@ def get(
     Raises:
         ModelNotFound^: If the provided *entity_id* does not match any known entity pattern.
     """
-    if manager := _check_entity_and_get_manager(
-        entity_id, [Cycle, Scenario, Sequence, Task, Job, DataNode, Submission]
-    ):
-        return manager._get(entity_id)
+    if _is_job(entity_id):
+        return _get_manager(Job._MANAGER_NAME)._get(JobId(entity_id))
+    if _is_cycle(entity_id):
+        return _get_manager(Cycle._MANAGER_NAME)._get(CycleId(entity_id))
+    if _is_scenario(entity_id):
+        return _get_manager(Scenario._MANAGER_NAME)._get(ScenarioId(entity_id))
+    if _is_sequence(entity_id):
+        return _get_manager(Sequence._MANAGER_NAME)._get(SequenceId(entity_id))
+    if _is_task(entity_id):
+        return _get_manager(Task._MANAGER_NAME)._get(TaskId(entity_id))
+    if _is_data_node(entity_id):
+        return _get_manager(DataNode._MANAGER_NAME)._get(DataNodeId(entity_id))
+    if _is_submission(entity_id):
+        return _get_manager(Submission._MANAGER_NAME)._get(SubmissionId(entity_id))
 
     raise ModelNotFound("NOT_DETERMINED", entity_id)
 
@@ -337,8 +409,12 @@ def is_deletable(entity: Union[Scenario, Job, Submission, ScenarioId, JobId, Sub
     Returns:
         True if the given scenario, job or submission can be deleted. False otherwise.
     """
-    if manager := _check_entity_and_get_manager(entity, [Scenario, Job, Submission]):
-        return manager._is_deletable(entity)  # type: ignore
+    if _is_job(entity):
+        return _get_manager(Job._MANAGER_NAME)._is_deletable(entity)  # type: ignore
+    if _is_scenario(entity):
+        return _get_manager(Scenario._MANAGER_NAME)._is_deletable(entity)  # type: ignore
+    if _is_submission(entity):
+        return _get_manager(Submission._MANAGER_NAME)._is_deletable(entity)  # type: ignore
     return True
 
 
@@ -349,29 +425,39 @@ def delete(entity_id: Union[TaskId, DataNodeId, SequenceId, ScenarioId, JobId, C
     The behavior varies depending on the type of entity provided:
 
     - If a `CycleId` is provided, the nested scenarios, tasks, data nodes, and jobs are deleted.
-    - If a `ScenarioId` is provided, the nested tasks, data nodes, and jobs are deleted.
+    - If a `ScenarioId` is provided, the nested sequences, tasks, data nodes, submissions and jobs are deleted.
       If the scenario is primary, it can only be deleted if it is the only scenario in the cycle.
       In that case, its cycle is also deleted. Use the `is_deletable()^` function to check if
       the scenario can be deleted.
     - If a `SequenceId` is provided, the related jobs are deleted.
     - If a `TaskId` is provided, the related data nodes, and jobs are deleted.
     - If a `DataNodeId` is provided, the data node is deleted.
-    - If a `SubmissionId^` or a `JobId^` is provided, the submission or job entity can only be deleted if
-      the execution has been finished.
+    - If a `SubmissionId^` is provided, the related jobs are deleted.
+      The submission can only be deleted if the execution has been finished.
+    - If a `JobId^` is provided, the job entity can only be deleted if the execution has been finished.
 
     Parameters:
-        entity_id (Union[TaskId, DataNodeId, SequenceId, ScenarioId, JobId, CycleId]):
+        entity_id (Union[TaskId, DataNodeId, SequenceId, ScenarioId, SubmissionId, JobId, CycleId]):
             The identifier of the entity to delete.
 
     Raises:
         ModelNotFound: No entity corresponds to the specified *entity_id*.
     """
-    if manager := _check_entity_and_get_manager(entity_id, [Cycle, Scenario, Sequence, Task]):
-        return manager._hard_delete(entity_id)  # type: ignore
-
-    if manager := _check_entity_and_get_manager(entity_id, [Job, DataNode, Submission]):
-        return manager._delete(entity_id)  # type: ignore
-
+    if _is_job(entity_id):
+        job_manager = _get_manager(Job._MANAGER_NAME)
+        return job_manager._delete(job_manager._get(JobId(entity_id)))  # type: ignore
+    if _is_cycle(entity_id):
+        return _get_manager(Cycle._MANAGER_NAME)._hard_delete(CycleId(entity_id))  # type: ignore
+    if _is_scenario(entity_id):
+        return _get_manager(Scenario._MANAGER_NAME)._hard_delete(ScenarioId(entity_id))  # type: ignore
+    if _is_sequence(entity_id):
+        return _get_manager(Sequence._MANAGER_NAME)._hard_delete(SequenceId(entity_id))  # type: ignore
+    if _is_task(entity_id):
+        return _get_manager(Task._MANAGER_NAME)._hard_delete(TaskId(entity_id))  # type: ignore
+    if _is_data_node(entity_id):
+        return _get_manager(DataNode._MANAGER_NAME)._delete(DataNodeId(entity_id))  # type: ignore
+    if _is_submission(entity_id):
+        return _get_manager(Submission._MANAGER_NAME)._hard_delete(SubmissionId(entity_id))  # type: ignore
     raise ModelNotFound("NOT_DETERMINED", entity_id)
 
 

+ 44 - 5
tests/core/submission/test_submission_manager.py

@@ -16,9 +16,12 @@ import pytest
 
 from taipy.core._version._version_manager_factory import _VersionManagerFactory
 from taipy.core.exceptions.exceptions import SubmissionNotDeletedException
+from taipy.core.job._job_manager_factory import _JobManagerFactory
+from taipy.core.job.job import Job
 from taipy.core.submission._submission_manager_factory import _SubmissionManagerFactory
 from taipy.core.submission.submission import Submission
 from taipy.core.submission.submission_status import SubmissionStatus
+from taipy.core.task._task_manager_factory import _TaskManagerFactory
 from taipy.core.task.task import Task
 
 
@@ -54,12 +57,24 @@ def test_get_all_submission():
     version_manager = _VersionManagerFactory._build_manager()
 
     submission_manager._set(
-        Submission("entity_id", "submission_id", "entity_config_id", version=version_manager._get_latest_version())
+        Submission(
+            "entity_id",
+            "entity_type",
+            "entity_config_id",
+            "submission_id",
+            version=version_manager._get_latest_version(),
+        )
     )
     for version_name in ["abc", "xyz"]:
         for i in range(10):
             submission_manager._set(
-                Submission("entity_id", f"submission_{version_name}_{i}", "entity_config_id", version=f"{version_name}")
+                Submission(
+                    "entity_id",
+                    "entity_type",
+                    "entity_config_id",
+                    f"submission_{version_name}_{i}",
+                    version=f"{version_name}",
+                )
             )
 
     assert len(submission_manager._get_all()) == 1
@@ -99,7 +114,8 @@ def test_get_latest_submission():
 def test_delete_submission():
     submission_manager = _SubmissionManagerFactory._build_manager()
 
-    submission = Submission("entity_id", "submission_id", "entity_config_id")
+    submission = Submission("entity_id", "entity_type", "entity_config_id", "submission_id")
+
     submission_manager._set(submission)
 
     with pytest.raises(SubmissionNotDeletedException):
@@ -108,7 +124,7 @@ def test_delete_submission():
     submission.submission_status = SubmissionStatus.COMPLETED
 
     for i in range(10):
-        submission_manager._set(Submission("entity_id", f"submission_{i}", "entity_config_id"))
+        submission_manager._set(Submission("entity_id", "entity_type", "entity_config_id", f"submission_{i}"))
 
     assert len(submission_manager._get_all()) == 11
     assert isinstance(submission_manager._get(submission.id), Submission)
@@ -124,7 +140,7 @@ def test_delete_submission():
 def test_is_deletable():
     submission_manager = _SubmissionManagerFactory._build_manager()
 
-    submission = Submission("entity_id", "submission_id", "entity_config_id")
+    submission = Submission("entity_id", "entity_type", "entity_config_id", "submission_id")
     submission_manager._set(submission)
 
     assert len(submission_manager._get_all()) == 1
@@ -175,3 +191,26 @@ def test_is_deletable():
     assert submission.is_deletable()
     assert submission_manager._is_deletable(submission)
     assert submission_manager._is_deletable(submission.id)
+
+
+def test_hard_delete():
+    submission_manager = _SubmissionManagerFactory._build_manager()
+    job_manager = _JobManagerFactory._build_manager()
+    task_manager = _TaskManagerFactory._build_manager()
+
+    task = Task("task_config_id", {}, print)
+    submission = Submission(task.id, task._ID_PREFIX, task.config_id, "SUBMISSION_submission_id")
+    job_1 = Job("JOB_job_id_1", task, submission.id, submission.entity_id)  # will be deleted with submission
+    job_2 = Job("JOB_job_id_2", task, "SUBMISSION_submission_id_2", submission.entity_id)  # will not be deleted
+    submission.jobs = [job_1]
+
+    task_manager._set(task)
+    submission_manager._set(submission)
+    job_manager._set(job_1)
+    job_manager._set(job_2)
+
+    assert len(job_manager._get_all()) == 2
+    assert len(submission_manager._get_all()) == 1
+    submission_manager._hard_delete(submission.id)
+    assert len(job_manager._get_all()) == 1
+    assert len(submission_manager._get_all()) == 0