Skip to content

Commit 9945650

Browse files
authored
feat(frontend): update Job dashboard and Job creation pages for swaps
- Update create job page to allow user to select swaps - Show partially rendered graph when the user selects/changes entrypoint, and as they select swaps - Show swaps on job dashboard page
1 parent 2609733 commit 9945650

12 files changed

Lines changed: 736 additions & 276 deletions

File tree

src/dioptra/restapi/v1/entrypoints/schema.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -452,10 +452,11 @@ def validate_sections(self, sections: list[str]) -> None:
452452

453453

454454
class DynamicGlobalParametersResponseSchema(Schema):
455-
globalParameters = fields.List(
456-
fields.String(),
455+
globalParameters = fields.Nested(
456+
EntrypointParameterSchema,
457457
attribute="entrypoint_params",
458458
data_key="entrypointParams",
459+
many=True,
459460
metadata={
460461
"description": (
461462
"A list of global parameters used in the entrypoint task graph."

src/dioptra/restapi/v1/entrypoints/service.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1425,7 +1425,14 @@ def get_params(
14251425
active_plugins.append(epp.plugin)
14261426

14271427
return {
1428-
"entrypoint_params": list(needed_vars),
1428+
"entrypoint_params": [
1429+
parameter
1430+
for parameter in sorted(
1431+
entry_point.parameters,
1432+
key=lambda parameter: parameter.parameter_number,
1433+
)
1434+
if parameter.name in needed_vars
1435+
],
14291436
"topological_sort": topsorted,
14301437
"active_plugins": active_plugins,
14311438
}

src/dioptra/restapi/v1/jobs/schema.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -238,6 +238,12 @@ class JobSwapSchema(Schema):
238238
metadata={"description": "Alias of the chosen task."},
239239
dump_only=True,
240240
)
241+
taskName = fields.String(
242+
attribute="task_name",
243+
data_key="taskName",
244+
metadata={"description": "Name of the chosen task."},
245+
dump_only=True,
246+
)
241247
pluginFileResourceSnapshotId = fields.Integer(
242248
attribute="plugin_file_resource_snapshot_id",
243249
data_key="pluginFileResourceSnapshotId",

src/dioptra/restapi/v1/jobs/service.py

Lines changed: 56 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -334,6 +334,9 @@ def create(
334334
job=new_job,
335335
artifacts=[],
336336
has_draft=False,
337+
swap_task_names=_build_swap_task_name_lookup(
338+
new_job, self._swaps_retrieval_service, log
339+
),
337340
)
338341

339342
def _build_job_swaps(
@@ -553,12 +556,19 @@ def get(
553556
)
554557

555558
jobs = list(db.session.scalars(jobs_stmt).all())
556-
return _build_job_dict(jobs), total_num_jobs
559+
return (
560+
_build_job_dict(jobs, self._swaps_retrieval_service, log),
561+
total_num_jobs,
562+
)
557563

558564

559565
class JobIdService(object):
560566
"""The service methods for registering and managing jobs by their unique id."""
561567

568+
@inject
569+
def __init__(self, swaps_retrieval_service: SwapsRetrievalService) -> None:
570+
self._swaps_retrieval_service = swaps_retrieval_service
571+
562572
def get(
563573
self,
564574
job_id: int,
@@ -606,6 +616,9 @@ def get(
606616
job=job,
607617
artifacts=artifacts,
608618
has_draft=False,
619+
swap_task_names=_build_swap_task_name_lookup(
620+
job, self._swaps_retrieval_service, log
621+
),
609622
)
610623

611624
def delete(self, job_id: int, **kwargs) -> dict[str, Any]:
@@ -955,7 +968,10 @@ class ExperimentJobService(object):
955968

956969
@inject
957970
def __init__(
958-
self, experiment_id_service: ExperimentIdService, job_service: JobService
971+
self,
972+
experiment_id_service: ExperimentIdService,
973+
job_service: JobService,
974+
swaps_retrieval_service: SwapsRetrievalService,
959975
) -> None:
960976
"""Initialize the ExperimentIdJob service.
961977
@@ -967,6 +983,7 @@ def __init__(
967983
"""
968984
self._experiment_id_service = experiment_id_service
969985
self._job_service = job_service
986+
self._swaps_retrieval_service = swaps_retrieval_service
970987

971988
def create(
972989
self,
@@ -1111,7 +1128,10 @@ def get(
11111128
)
11121129

11131130
jobs = list(db.session.scalars(jobs_stmt).all())
1114-
return _build_job_dict(jobs), total_num_jobs
1131+
return (
1132+
_build_job_dict(jobs, self._swaps_retrieval_service, log),
1133+
total_num_jobs,
1134+
)
11151135

11161136

11171137
class ExperimentJobIdService(object):
@@ -1672,12 +1692,44 @@ def get_logs(
16721692
return records, total_count
16731693

16741694

1675-
def _build_job_dict(jobs: list[models.Job]) -> list[utils.JobDict]:
1695+
def _build_swap_task_name_lookup(
1696+
job: models.Job,
1697+
swaps_retrieval_service: SwapsRetrievalService,
1698+
logger: BoundLogger,
1699+
) -> dict[tuple[int, str, str], str]:
1700+
if not job.job_swaps:
1701+
return {}
1702+
1703+
entrypoint = job.entry_point_job.entry_point
1704+
available_swaps = swaps_retrieval_service.get_swaps(
1705+
entrypoint_id=entrypoint.resource_id,
1706+
entrypoint_snapshot_id=entrypoint.resource_snapshot_id,
1707+
logger=logger,
1708+
)
1709+
1710+
return {
1711+
(
1712+
swap["plugin_file_resource_snapshot_id"],
1713+
swap["swap_name"],
1714+
swap["task_alias"],
1715+
): swap["task_name"]
1716+
for swap in available_swaps
1717+
}
1718+
1719+
1720+
def _build_job_dict(
1721+
jobs: list[models.Job],
1722+
swaps_retrieval_service: SwapsRetrievalService,
1723+
logger: BoundLogger,
1724+
) -> list[utils.JobDict]:
16761725
job_dicts: dict[int, utils.JobDict] = {
16771726
job.resource_id: utils.JobDict(
16781727
job=job,
16791728
artifacts=[],
16801729
has_draft=False,
1730+
swap_task_names=_build_swap_task_name_lookup(
1731+
job, swaps_retrieval_service, logger
1732+
),
16811733
)
16821734
for job in jobs
16831735
}

src/dioptra/restapi/v1/utils.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,7 @@ class JobDict(TypedDict):
148148
job: models.Job
149149
artifacts: list[models.Artifact]
150150
has_draft: bool | None
151+
swap_task_names: dict[tuple[int, str, str], str]
151152

152153

153154
class ModelWithVersionDict(TypedDict):
@@ -790,6 +791,7 @@ def build_job(job_dict: JobDict) -> dict[str, Any]:
790791
job = job_dict["job"]
791792
artifacts = job_dict.get("artifacts", None)
792793
has_draft = job_dict.get("has_draft", None)
794+
swap_task_names = job_dict.get("swap_task_names", {})
793795

794796
data = {
795797
"id": job.resource_id,
@@ -808,6 +810,13 @@ def build_job(job_dict: JobDict) -> dict[str, Any]:
808810
{
809811
"swap_name": swap.swap_name,
810812
"task_alias": swap.task_alias,
813+
"task_name": swap_task_names.get(
814+
(
815+
swap.plugin_file_resource_snapshot_id,
816+
swap.swap_name,
817+
swap.task_alias,
818+
)
819+
),
811820
"plugin_file_resource_snapshot_id": swap.plugin_file_resource_snapshot_id,
812821
}
813822
for swap in sorted(job.job_swaps, key=lambda swap: swap.swap_name)

src/frontend/src/components/CodeEditor.vue

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,12 @@ const handleReady = (payload) => {
125125
view.value = payload.view;
126126
};
127127
128+
function refreshLayout() {
129+
view.value?.requestMeasure();
130+
}
131+
132+
defineExpose({ refreshLayout });
133+
128134
function highlightPlaceholder(update) {
129135
if (!view.value || update.docChanged || props.language === "python") return;
130136
const from = view.value.state.selection.ranges[0].from;

src/frontend/src/dialogs/InfoPopupDialog.vue

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
<q-dialog
33
v-model="showDialog"
44
aria-labelledby="modalTitle"
5+
@show="emit('show')"
56
>
67
<q-card
78
style="width: 900px; max-width: 90vw; max-height: 80vh"
@@ -33,4 +34,5 @@
3334

3435
<script setup>
3536
const showDialog = defineModel();
37+
const emit = defineEmits(["show"]);
3638
</script>

src/frontend/src/services/dataApi.ts

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -327,6 +327,33 @@ export async function deleteJob(id: number, jobId: number) {
327327
return await axios.delete(`/api/experiments/${id}/jobs/${jobId}`);
328328
}
329329

330+
export async function getSwaps(entrypointId: number, snapshotId: number) {
331+
return await axios.get(`/api/entrypoints/${entrypointId}/snapshots/${snapshotId}/swaps`);
332+
}
333+
334+
export async function getGraph(entrypointId: number, snapshotId: number, swaps: Record<string, string>) {
335+
const swapsQueryParam = Object.entries(swaps)
336+
.map(([swapName, taskAlias]) => `${swapName}:${taskAlias}`)
337+
.join(",");
338+
339+
return await axios.get(`/api/entrypoints/${entrypointId}/snapshots/${snapshotId}/config`, {
340+
params: {
341+
swaps: swapsQueryParam,
342+
sections: "graph",
343+
partial: true,
344+
},
345+
});
346+
}
347+
348+
export async function getUsedParams(entrypointId: number, snapshotId: number, swaps: Record<string, string>) {
349+
const swapsQueryParam = Object.entries(swaps)
350+
.map(([swapName, taskAlias]) => `${swapName}:${taskAlias}`)
351+
.join(",");
352+
return await axios.get(`/api/entrypoints/${entrypointId}/snapshots/${snapshotId}/dynamicGlobalParameters`, {
353+
params: { swaps: swapsQueryParam },
354+
});
355+
}
356+
330357
export async function addDraft<T extends keyof CreateParams>(type: T, params: CreateParams[T], id: number) {
331358
if (id) {
332359
return await axios.post(`/api/${type}/${id}/draft`, params);

0 commit comments

Comments
 (0)