|
@@ -24,13 +24,26 @@ import pandas as pd
|
|
|
from dateutil import parser
|
|
|
|
|
|
from taipy.config import Config
|
|
|
-from taipy.core import Cycle, DataNode, Job, Scenario, Sequence, cancel_job, create_scenario
|
|
|
+from taipy.core import (
|
|
|
+ Cycle,
|
|
|
+ DataNode,
|
|
|
+ DataNodeId,
|
|
|
+ Job,
|
|
|
+ JobId,
|
|
|
+ Scenario,
|
|
|
+ ScenarioId,
|
|
|
+ Sequence,
|
|
|
+ SequenceId,
|
|
|
+ cancel_job,
|
|
|
+ create_scenario,
|
|
|
+)
|
|
|
from taipy.core import delete as core_delete
|
|
|
from taipy.core import delete_job
|
|
|
from taipy.core import get as core_get
|
|
|
from taipy.core import (
|
|
|
get_cycles_scenarios,
|
|
|
get_data_nodes,
|
|
|
+ get_jobs,
|
|
|
is_deletable,
|
|
|
is_editable,
|
|
|
is_promotable,
|
|
@@ -44,6 +57,7 @@ from taipy.core.notification import CoreEventConsumerBase, EventEntityType
|
|
|
from taipy.core.notification.event import Event, EventOperation
|
|
|
from taipy.core.notification.notifier import Notifier
|
|
|
from taipy.core.submission.submission import Submission
|
|
|
+from taipy.core.submission._submission_manager_factory import _SubmissionManagerFactory
|
|
|
from taipy.core.submission.submission_status import SubmissionStatus
|
|
|
from taipy.gui import Gui, State
|
|
|
from taipy.gui._warnings import _warn
|
|
@@ -111,17 +125,19 @@ class _GuiCoreContext(CoreEventConsumerBase):
|
|
|
def process_event(self, event: Event):
|
|
|
if event.entity_type == EventEntityType.SCENARIO:
|
|
|
if event.operation == EventOperation.SUBMISSION:
|
|
|
- self.scenario_status_callback(event.attribute_name, True)
|
|
|
+ self.scenario_status_callback(event.attribute_name)
|
|
|
return
|
|
|
self.scenario_refresh(
|
|
|
- event.entity_id if event.operation != EventOperation.DELETION and is_readable(event.entity_id) else None
|
|
|
+ event.entity_id
|
|
|
+ if event.operation != EventOperation.DELETION and is_readable(t.cast(ScenarioId, event.entity_id))
|
|
|
+ else None
|
|
|
)
|
|
|
elif event.entity_type == EventEntityType.SEQUENCE and event.entity_id:
|
|
|
sequence = None
|
|
|
try:
|
|
|
sequence = (
|
|
|
core_get(event.entity_id)
|
|
|
- if event.operation != EventOperation.DELETION and is_readable(event.entity_id)
|
|
|
+ if event.operation != EventOperation.DELETION and is_readable(t.cast(SequenceId, event.entity_id))
|
|
|
else None
|
|
|
)
|
|
|
if sequence and hasattr(sequence, "parent_ids") and sequence.parent_ids:
|
|
@@ -130,6 +146,9 @@ class _GuiCoreContext(CoreEventConsumerBase):
|
|
|
)
|
|
|
except Exception as e:
|
|
|
_warn(f"Access to sequence {event.entity_id} failed", e)
|
|
|
+ elif event.entity_type == EventEntityType.JOB:
|
|
|
+ with self.lock:
|
|
|
+ self.jobs_list = None
|
|
|
elif event.entity_type == EventEntityType.SUBMISSION:
|
|
|
self.scenario_status_callback(event.entity_id)
|
|
|
elif event.entity_type == EventEntityType.DATA_NODE:
|
|
@@ -149,18 +168,16 @@ class _GuiCoreContext(CoreEventConsumerBase):
|
|
|
{"scenario": scenario_id or True},
|
|
|
)
|
|
|
|
|
|
- def scenario_status_callback(self, submission_id: str, is_submission: t.Optional[bool] = False):
|
|
|
- if not submission_id or not (is_submission or is_readable(submission_id)):
|
|
|
+ def scenario_status_callback(self, submission_id: t.Optional[str]):
|
|
|
+ if not submission_id or not is_readable_submission(submission_id):
|
|
|
return
|
|
|
try:
|
|
|
- if is_submission:
|
|
|
- sub_id = submission_id
|
|
|
- submission = None
|
|
|
- else:
|
|
|
- submission: Submission = core_get(submission_id)
|
|
|
- sub_details: t.Optional[_SubmissionDetails] = self.client_submission.get(sub_id)
|
|
|
+ sub_details: t.Optional[_SubmissionDetails] = self.client_submission.get(submission_id)
|
|
|
+ if not sub_details:
|
|
|
+ return
|
|
|
|
|
|
- if not submission or not submission.entity_id or sub_details:
|
|
|
+ submission = core_get_submission(submission_id)
|
|
|
+ if not submission or not submission.entity_id:
|
|
|
return
|
|
|
|
|
|
entity = core_get(submission.entity_id)
|
|
@@ -182,12 +199,12 @@ class _GuiCoreContext(CoreEventConsumerBase):
|
|
|
SubmissionStatus.FAILED,
|
|
|
SubmissionStatus.CANCELED,
|
|
|
):
|
|
|
- self.client_submission.pop(sub_id, None)
|
|
|
+ self.client_submission.pop(submission_id, None)
|
|
|
else:
|
|
|
- self.client_submission[sub_id] = sub_details.set_submission(new_status)
|
|
|
+ self.client_submission[submission_id] = sub_details.set_submission(submission)
|
|
|
|
|
|
except Exception as e:
|
|
|
- _warn(f"Job ({submission_id}) is not available", e)
|
|
|
+ _warn(f"Submission ({submission_id}) is not available", e)
|
|
|
|
|
|
finally:
|
|
|
self.gui._broadcast(_GuiCoreContext._CORE_CHANGED_NAME, {"jobs": True})
|
|
@@ -243,10 +260,10 @@ class _GuiCoreContext(CoreEventConsumerBase):
|
|
|
state.assign(_GuiCoreContext._SCENARIO_SELECTOR_ID_VAR, args[0])
|
|
|
|
|
|
def get_scenario_by_id(self, id: str) -> t.Optional[Scenario]:
|
|
|
- if not id or not is_readable(id):
|
|
|
+ if not id or not is_readable(t.cast(ScenarioId, id)):
|
|
|
return None
|
|
|
try:
|
|
|
- return core_get(id)
|
|
|
+ return core_get(t.cast(ScenarioId, id))
|
|
|
except Exception:
|
|
|
return None
|
|
|
|
|
@@ -417,14 +434,14 @@ class _GuiCoreContext(CoreEventConsumerBase):
|
|
|
if entity:
|
|
|
try:
|
|
|
jobs = core_submit(entity)
|
|
|
+ submission_entity = core_get_submission(jobs[0].submit_id if isinstance(jobs, list) else jobs.submit_id)
|
|
|
if submission_cb := data.get("on_submission_change"):
|
|
|
submission_fn = self.gui._get_user_function(submission_cb)
|
|
|
if callable(submission_fn):
|
|
|
- submission_entity = core_get(jobs[0].submit_id if isinstance(jobs, list) else jobs.submit_id)
|
|
|
client_id = self.gui._get_client_id()
|
|
|
module_context = self.gui._get_locals_context()
|
|
|
with self.submissions_lock:
|
|
|
- self.client_submission[submission_entity.submit_entity_id] = _SubmissionDetails(
|
|
|
+ self.client_submission[submission_entity.id] = _SubmissionDetails(
|
|
|
client_id,
|
|
|
module_context,
|
|
|
submission_fn,
|
|
@@ -490,6 +507,12 @@ class _GuiCoreContext(CoreEventConsumerBase):
|
|
|
|
|
|
return None
|
|
|
|
|
|
+ def get_jobs_list(self):
|
|
|
+ with self.lock:
|
|
|
+ if self.jobs_list is None:
|
|
|
+ self.jobs_list = get_jobs()
|
|
|
+ return self.jobs_list
|
|
|
+
|
|
|
def job_adapter(self, job):
|
|
|
try:
|
|
|
if hasattr(job, "id") and is_readable(job.id) and core_get(job.id) is not None:
|
|
@@ -617,10 +640,10 @@ class _GuiCoreContext(CoreEventConsumerBase):
|
|
|
cycles_scenarios.extend(scenarios)
|
|
|
else:
|
|
|
cycles_scenarios.append(cycle)
|
|
|
- elif is_readable(owner_id):
|
|
|
+ elif is_readable(t.cast(ScenarioId, owner_id)):
|
|
|
entity = core_get(owner_id)
|
|
|
- if entity and (scenarios := self.scenario_by_cycle.get(entity)):
|
|
|
- cycles_scenarios.extend(scenarios)
|
|
|
+ if entity and (scenarios_cycle := self.scenario_by_cycle.get(t.cast(Cycle, entity))):
|
|
|
+ cycles_scenarios.extend(scenarios_cycle)
|
|
|
elif isinstance(entity, Scenario):
|
|
|
cycles_scenarios.append(entity)
|
|
|
return cycles_scenarios
|
|
@@ -636,7 +659,7 @@ class _GuiCoreContext(CoreEventConsumerBase):
|
|
|
res = []
|
|
|
for e in dn.edits:
|
|
|
job_id = e.get("job_id")
|
|
|
- job: Job = None
|
|
|
+ job: t.Optional[Job] = None
|
|
|
if job_id:
|
|
|
if not is_readable(job_id):
|
|
|
job_id += " not readable"
|
|
@@ -685,10 +708,10 @@ class _GuiCoreContext(CoreEventConsumerBase):
|
|
|
return _DoNotUpdate()
|
|
|
|
|
|
def __check_readable_editable(self, state: State, id: str, type: str, var: str):
|
|
|
- if not is_readable(id):
|
|
|
+ if not is_readable(t.cast(DataNodeId, id)):
|
|
|
state.assign(var, f"{type} {id} is not readable.")
|
|
|
return False
|
|
|
- if not is_editable(id):
|
|
|
+ if not is_editable(t.cast(DataNodeId, id)):
|
|
|
state.assign(var, f"{type} {id} is not editable.")
|
|
|
return False
|
|
|
return True
|
|
@@ -759,7 +782,7 @@ class _GuiCoreContext(CoreEventConsumerBase):
|
|
|
id
|
|
|
and isinstance(datanode, DataNode)
|
|
|
and id == datanode.id
|
|
|
- and is_readable(id)
|
|
|
+ and is_readable(t.cast(DataNodeId, id))
|
|
|
and (dn := core_get(id))
|
|
|
and isinstance(dn, DataNode)
|
|
|
and dn.is_ready_for_reading
|
|
@@ -775,7 +798,7 @@ class _GuiCoreContext(CoreEventConsumerBase):
|
|
|
id
|
|
|
and isinstance(datanode, DataNode)
|
|
|
and id == datanode.id
|
|
|
- and is_readable(id)
|
|
|
+ and is_readable(t.cast(DataNodeId, id))
|
|
|
and (dn := core_get(id))
|
|
|
and isinstance(dn, DataNode)
|
|
|
and dn.is_ready_for_reading
|
|
@@ -793,7 +816,7 @@ class _GuiCoreContext(CoreEventConsumerBase):
|
|
|
id
|
|
|
and isinstance(datanode, DataNode)
|
|
|
and id == datanode.id
|
|
|
- and is_readable(id)
|
|
|
+ and is_readable(t.cast(DataNodeId, id))
|
|
|
and (dn := core_get(id))
|
|
|
and isinstance(dn, DataNode)
|
|
|
and dn.is_ready_for_reading
|
|
@@ -819,3 +842,12 @@ class _GuiCoreContext(CoreEventConsumerBase):
|
|
|
state.assign(_GuiCoreContext._DATANODE_VIZ_DATA_ID_VAR, data_id)
|
|
|
elif chart_id := data.get("chart_id"):
|
|
|
state.assign(_GuiCoreContext._DATANODE_VIZ_DATA_CHART_ID_VAR, chart_id)
|
|
|
+
|
|
|
+
|
|
|
+# TODO remove when Submission is supported by Core API
|
|
|
+def is_readable_submission(id: str):
|
|
|
+ return _SubmissionManagerFactory._build_manager()._is_readable(t.cast(Submission, id))
|
|
|
+
|
|
|
+
|
|
|
+def core_get_submission(id: str):
|
|
|
+ return _SubmissionManagerFactory._build_manager()._get(id)
|