Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 34 additions & 0 deletions epolyscat_django_app/migrations/0023_auto_20260617_1802.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# Generated by Django 3.2.18 on 2026-06-17 18:02

from django.db import migrations, models
import django.db.models.deletion


class Migration(migrations.Migration):

dependencies = [
('epolyscat_django_app', '0022_auto_20251203_2224'),
]

operations = [
migrations.AlterField(
model_name='run',
name='experiment',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='runs', to='epolyscat_django_app.experiment'),
),
migrations.AlterField(
model_name='run',
name='filepath',
field=models.CharField(blank=True, default='', max_length=255),
),
migrations.AlterField(
model_name='run',
name='number',
field=models.CharField(blank=True, default='', max_length=64),
),
migrations.AlterField(
model_name='run',
name='root',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='runs', to='epolyscat_django_app.runsroot'),
),
]
12 changes: 6 additions & 6 deletions epolyscat_django_app/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -326,7 +326,7 @@ class RemoteExecution(models.Model):
updated = models.DateTimeField(auto_now=True)
airavata_experiment_status = models.CharField(
max_length=255,
default=ExperimentState._VALUES_TO_NAMES[ExperimentState.CREATED],
default=ExperimentState(ExperimentState.CREATED).name,
)
resource_name = models.CharField(max_length=255, blank=True, default="")
job_id = models.CharField(max_length=255, null=True)
Expand All @@ -343,23 +343,23 @@ def get_job_id(self, request):

def get_airavata_experiment_status(self, request):
terminal_states = self.get_airavata_experiment_terminal_states()
old_state = ExperimentState._NAMES_TO_VALUES[self.airavata_experiment_status]
old_state = ExperimentState[self.airavata_experiment_status].value
if old_state in terminal_states:
return self.airavata_experiment_status
else:
logger.debug(f"getExperimentStatus({self.airavata_experiment_id})")
current_status = request.airavata_client.getExperimentStatus(
request.authz_token, self.airavata_experiment_id
)
self.airavata_experiment_status = ExperimentState._VALUES_TO_NAMES[
self.airavata_experiment_status = ExperimentState(
current_status.state
]
).name
self.save()
return self.airavata_experiment_status

def is_airavata_experiment_finished(self, request):
status = self.get_airavata_experiment_status(request)
state = ExperimentState._NAMES_TO_VALUES[status]
state = ExperimentState[status].value
return state in self.get_airavata_experiment_terminal_states()

def get_application_specific_status(self, request) -> Union[str, None]:
Expand Down Expand Up @@ -441,7 +441,7 @@ def cancel(self, request):

def is_cancelable(self, request) -> bool:
status = self.get_airavata_experiment_status(request)
state = ExperimentState._NAMES_TO_VALUES[status]
state = ExperimentState[status].value
return state in self.get_airavata_experiment_cancelable_states()


Expand Down
4 changes: 2 additions & 2 deletions epolyscat_django_app/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ class RunSerializer(serializers.ModelSerializer):
job_status = serializers.SerializerMethodField()
is_tutorial = serializers.SerializerMethodField()
job_id = serializers.SerializerMethodField()
#resource = serializers.SerializerMethodField()
resource = serializers.SerializerMethodField()
#resource_short = serializers.SerializerMethodField()
#executions = serializers.SlugRelatedField(
# slug_field="airavata_experiment_id", read_only=True, many=True
Expand All @@ -82,7 +82,7 @@ class Meta:
"created", "updated", "deleted", 'is_email_notification_on',
"group_resource_profile_id", "compute_resource_id",
"queue_name", "core_count", "node_count", "walltime_limit", "total_physical_memory",
"inputs", "executions", "status", "job_status", "is_tutorial", "job_id",
"inputs", "executions", "status", "job_status", "is_tutorial", "job_id", "resource",
#"directedit", "inpc_download_url", "cancelable","can_resubmit", "input_table", "root",
#"number", "root", "experiment", "resource", #"resource_short", "job_id",
)
Expand Down
107 changes: 77 additions & 30 deletions epolyscat_django_app/src/components/Pages/Runs.vue
Original file line number Diff line number Diff line change
Expand Up @@ -65,8 +65,8 @@
</template>
</b-breadcrumb>
</dev -->
<div class="w-100 d-flex">
<div style="flex: 1;" class="overflow-auto">
<div class="w-100 d-flex flex-column" style="width: 93%;">
<div style="flex: 1;" class="overflow-auto" v-if="!!experiment || !!view">
<div class="d-inline mr-2">
<div class="overflow-auto" style="font-weight: 400; font-size: 19px;">
<template v-if="!!experiment">{{ experiment.name }}</template>
Expand All @@ -87,14 +87,21 @@
</div>
</div>

<LoadingOverlay :name="loadingOverlayName" class="d-flex w-100" style="height: calc(100% - 145px)">
<b-input-group class="filter-input my-3">
<b-form-input v-model="filterText" type="search" placeholder="Filter runs"/>
<b-input-group-append is-text>
<b-icon icon="search"/>
</b-input-group-append>
</b-input-group>

<LoadingOverlay :name="loadingOverlayName" class="d-flex w-100" style="flex-grow: 1; min-height: 300px;">
<ListView
:items="runs" :columns="[['name', 'Name'], ['status', 'Status'], ['updated', 'Last Modified'], ['actions', 'Actions']]"
:items="runs" :columns="[['name', 'Run Name'], ['status', 'Status'], ['resource', 'Resource'], ['actions', 'Actions']]"
:canSelectMultiple="true" @updateSelected="updateSelected" identifier="id"
:sorters="[
(run1, run2) => run1.name.localeCompare(run2.name),
(run1, run2) => sortStatus(run1.displayStatus, run2.displayStatus),
(run1, run2) => (new Date(run2.updated)).getTime() - (new Date(run1.updated)).getTime(),
(run1, run2) => (run1.resource || '').localeCompare(run2.resource || ''),
]"
>
<template v-slot:name="{ item }">
Expand All @@ -107,31 +114,45 @@
<template v-slot:status="{ item }">
<Badge :status="item.displayStatus"/>
</template>
<template v-slot:updated="{ item }">
{{ (new Date(item.updated)).toLocaleString() }}
<template v-slot:resource="{ item }">
{{ item.resource || "—" }}
</template>
<template v-slot:actions="{ item }">
<b-button
v-if="!isTutorials && view != null" variant="link" size="sm" @click="removeFromView([item])"
v-b-tooltip.hover title="Remove from view"
><b-icon icon="x" /></b-button>
<b-button
variant="link" size="sm" @click="cloneRun(item)"
v-b-tooltip.hover title="Clone"
><b-icon icon="clipboard" /></b-button>
<b-button
v-if="!isTutorials && item.displayStatus.toUpperCase() == 'UNSUBMITTED'" variant="link" size="sm"
@click="submitRun(item)" v-b-tooltip.hover title="Submit"
><b-icon icon="arrow-bar-up"/></b-button>
<b-button
v-else-if="!isTutorials" :disabled="['COMPLETED', 'FAILED'].indexOf(item.status.toUpperCase()) == -1" variant="link"
size="sm" @click="resubmitRun(item)"
v-b-tooltip.hover title="Resubmit"
><b-icon icon="arrow90deg-right"/></b-button>
<b-button v-if="!isTutorials"
variant="link" size="sm" @click="deleteRuns([item])"
v-b-tooltip.hover title="Delete"
><b-icon icon="trash" /></b-button>
<!-- All Runs view: clone + delete only, matching the design -->
<template v-if="view == null && !isTutorials">
<b-button
variant="link" size="sm" class="action-icon" @click="cloneRun(item)"
v-b-tooltip.hover title="Clone"
><b-icon icon="files" /></b-button>
<b-button
variant="link" size="sm" class="action-icon" @click="deleteRuns([item])"
v-b-tooltip.hover title="Delete"
><b-icon icon="trash-fill" /></b-button>
</template>
<!-- View / tutorial contexts: full action set -->
<template v-else>
<b-button
v-if="view != null" variant="link" size="sm" @click="removeFromView([item])"
v-b-tooltip.hover title="Remove from view"
><b-icon icon="x" /></b-button>
<b-button
variant="link" size="sm" @click="cloneRun(item)"
v-b-tooltip.hover title="Clone"
><b-icon icon="files" /></b-button>
<b-button
v-if="!isTutorials && item.displayStatus.toUpperCase() == 'UNSUBMITTED'" variant="link" size="sm"
@click="submitRun(item)" v-b-tooltip.hover title="Submit"
><b-icon icon="arrow-bar-up"/></b-button>
<b-button
v-else-if="!isTutorials" :disabled="['COMPLETED', 'FAILED'].indexOf(item.status.toUpperCase()) == -1" variant="link"
size="sm" @click="resubmitRun(item)"
v-b-tooltip.hover title="Resubmit"
><b-icon icon="arrow90deg-right"/></b-button>
<b-button v-if="!isTutorials"
variant="link" size="sm" @click="deleteRuns([item])"
v-b-tooltip.hover title="Delete"
><b-icon icon="trash-fill" /></b-button>
</template>
</template>
</ListView>
</LoadingOverlay>
Expand Down Expand Up @@ -396,6 +417,9 @@ export default {
*/
},
methods: {
updateSelected(selectedRuns) {
this.selected = selectedRuns;
},
runLink({runId}) {
let _link = "/runs/";
if (runId) {
Expand Down Expand Up @@ -650,8 +674,7 @@ export default {
}
},
mounted() {
if (this.viewId != null)
this.refreshData(true);
this.refreshData(true);

this.refreshDataInterval = setInterval(() => {
this.refreshData(false);
Expand All @@ -671,6 +694,30 @@ export default {
width: 93%;
}

/* "Filter runs" search box */
.filter-input >>> .form-control {
border-right: 0;
height: 44px;
font-size: 16px;
}

.filter-input >>> .input-group-text {
background: #fff;
border-left: 0;
color: #6c757d;
}

/* Clone / delete icons in the Actions column */
.action-icon {
color: #1b3a5c;
font-size: 1.1rem;
padding: 0.1rem 0.5rem;
}

.action-icon:hover {
color: #102537;
}

.button_row > * {
margin: 0 10px;

Expand Down
8 changes: 4 additions & 4 deletions epolyscat_django_app/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -1065,7 +1065,7 @@ def user_run_file_exists(request, run, filename):
experiment_model = None
for execution in run.executions.order_by("-created"):
status_name = execution.get_airavata_experiment_status(request)
experiment_state = ExperimentState._NAMES_TO_VALUES[status_name]
experiment_state = ExperimentState[status_name].value
if experiment_state == ExperimentState.COMPLETED:
logger.debug(f"getExperiment({execution.airavata_experiment_id})")
experiment_model = request.airavata_client.getExperiment(
Expand Down Expand Up @@ -1130,7 +1130,7 @@ def get_run_output_data_product_uri(request, run: models.Run, data_type: str):
# Find the output by data type
output = None
for output in experiment_model.experimentOutputs:
output_type_name = DataType._VALUES_TO_NAMES[output.type]
output_type_name = DataType(output.type).name
if output_type_name == data_type:
output = output
break
Expand Down Expand Up @@ -1840,7 +1840,7 @@ def user_run_file_exists(request, run, filename):
experiment_model = None
for execution in run.executions.order_by("-created"):
status_name = execution.get_airavata_experiment_status(request)
experiment_state = ExperimentState._NAMES_TO_VALUES[status_name]
experiment_state = ExperimentState[status_name].value
print(f"DEBUG: Execution {execution.airavata_experiment_id} status: {status_name}")
if experiment_state == ExperimentState.COMPLETED:
logger.debug(f"getExperiment({execution.airavata_experiment_id})")
Expand Down Expand Up @@ -1931,7 +1931,7 @@ def get_run_output_data_product_uri(request, run: models.Run, data_type: str):
# Find the output by data type
output = None
for output in experiment_model.experimentOutputs:
output_type_name = DataType._VALUES_TO_NAMES[output.type]
output_type_name = DataType(output.type).name
if output_type_name == data_type:
output = output
break
Expand Down