Skip to content

Commit 7340ba7

Browse files
Merge pull request #395 from AutomatingSciencePipeline/173-cancelling-an-experiment-in-progress
Add cancel feature
2 parents a4d28d0 + 74d4767 commit 7340ba7

File tree

4 files changed

+71
-17
lines changed

4 files changed

+71
-17
lines changed

apps/backend/app.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,15 @@ def spawn_job(experiment_data):
6666
job = create_job_object(experiment_data)
6767
create_job(BATCH_API, job)
6868

69+
@flaskApp.post("/cancelExperiment")
70+
def cancel_experiment():
71+
"""The query to cancel an experiment"""
72+
data = request.get_json()
73+
job_name = data['jobName']
74+
# kill the job
75+
BATCH_API.delete_namespaced_job(job_name, "default", propagation_policy="Background")
76+
return Response(status=200)
77+
6978
@flaskApp.post("/uploadResults")
7079
def upload_results():
7180
json = request.get_json()

apps/backend/spawn_runner.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,8 @@
1212
def create_job_object(experiment_data):
1313
"""Function that creates the job object for the runner"""
1414
# Configure Pod template container
15-
job_name = "runner-" + str(time.time())
15+
job_name = "runner-" + experiment_data['experiment']['id']
16+
1617
job_command = ["python3", "runner.py", json.dumps(experiment_data)]
1718

1819
runner_body = get_yaml_file_body(RUNNER_PATH)

apps/frontend/app/components/flows/ViewExperiment/ExperimentListing.tsx

Lines changed: 29 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { useEffect, useState } from 'react';
44
import { ExperimentData } from '../../../../lib/db_types';
55
import { MdEdit } from 'react-icons/md';
66
import Chart from './Chart';
7-
import { addShareLink, unfollowExperiment, updateExperimentNameById } from '../../../../lib/mongodb_funcs';
7+
import { addShareLink, unfollowExperiment, updateExperimentNameById, cancelExperimentById } from '../../../../lib/mongodb_funcs';
88
import toast from 'react-hot-toast';
99
import { useSession } from 'next-auth/react';
1010

@@ -248,13 +248,28 @@ export const ExperimentListing = ({ projectData: projectData, onCopyExperiment,
248248
</button> */}
249249
{
250250
project.creator == session?.user?.id! ?
251-
<button type="button"
252-
className='inline-flex items-center justify-center px-4 py-2 border border-transparent shadow-sm text-sm font-medium rounded-md text-white bg-blue-600 hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 xl:w-full'
253-
onClick={() => {
254-
openDeleteModal();
255-
}}>
256-
Delete Experiment
257-
</button> :
251+
(
252+
project.finished ?
253+
<button type="button"
254+
className='inline-flex items-center justify-center px-4 py-2 border border-transparent shadow-sm text-sm font-medium rounded-md text-white bg-blue-600 hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 xl:w-full'
255+
onClick={() => {
256+
openDeleteModal();
257+
}}>
258+
Delete Experiment
259+
</button>
260+
:
261+
<button type="button"
262+
className='inline-flex items-center justify-center px-4 py-2 border border-transparent shadow-sm text-sm font-medium rounded-md text-white bg-blue-600 hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 xl:w-full'
263+
onClick={() => {
264+
toast.promise(cancelExperimentById(project.expId), {
265+
success: 'Cancelled experiment', error: 'Failed to cancel experiment', loading: 'Cancelling experiment...'
266+
});
267+
}}
268+
>
269+
Cancel Experiment
270+
</button>
271+
)
272+
:
258273
<button type="button"
259274
className='inline-flex items-center justify-center px-4 py-2 border border-transparent shadow-sm text-sm font-medium rounded-md text-white bg-blue-600 hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 xl:w-full'
260275
onClick={() => {
@@ -267,12 +282,12 @@ export const ExperimentListing = ({ projectData: projectData, onCopyExperiment,
267282
</button>
268283
}
269284
{project.finished ?
270-
<button type="button"
271-
className='inline-flex items-center justify-center px-4 py-2 border border-transparent shadow-sm text-sm font-medium rounded-md text-white bg-blue-600 hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 xl:w-full'
272-
onClick={openGraphModal}
273-
>
274-
See Graph
275-
</button> : null
285+
<button type="button"
286+
className='inline-flex items-center justify-center px-4 py-2 border border-transparent shadow-sm text-sm font-medium rounded-md text-white bg-blue-600 hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 xl:w-full'
287+
onClick={openGraphModal}
288+
>
289+
See Graph
290+
</button> : null
276291
}
277292
{
278293
project.creator == session?.user?.id! ?

apps/frontend/lib/mongodb_funcs.ts

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,35 @@ export async function deleteDocumentById(expId: string) {
6767
return Promise.resolve();
6868
}
6969

70+
export async function cancelExperimentById(expId: string) {
71+
'use server';
72+
//Call the backend at the endpoint to cancel the experiment
73+
const BACKEND_PORT = process.env.BACKEND_PORT || '5050';
74+
const url = `http://glados-service-backend:${BACKEND_PORT}/cancelExperiment`;
75+
//Just post a json object with the experiment id
76+
const backendResponse = await fetch(url, {
77+
method: 'POST',
78+
headers: new Headers({
79+
'Content-Type': 'application/json',
80+
}),
81+
body: JSON.stringify({ jobName: 'runner-' + expId }),
82+
});
83+
84+
//Mark the experiment as completed
85+
const client = await clientPromise;
86+
const collection = client.db(DB_NAME).collection(COLLECTION_EXPERIMENTS);
87+
await collection
88+
.updateOne({ '_id': new ObjectId(expId) }, { $set: { 'finished': true } });
89+
90+
//If the backend returns a 200 status code, the experiment was successfully cancelled
91+
if (backendResponse.status === 200) {
92+
return Promise.resolve(true);
93+
} else {
94+
return Promise.resolve(false);
95+
}
96+
97+
}
98+
7099
export async function updateExperimentNameById(expId: string, newExpName: string) {
71100
'use server';
72101
const client = await clientPromise;
@@ -124,7 +153,7 @@ export async function getRecentFiles(userId: string) {
124153
return serializedFiles;
125154
}
126155

127-
export async function copyFile(fileID: string, userId: string){
156+
export async function copyFile(fileID: string, userId: string) {
128157
'use server';
129158
const client = await clientPromise;
130159
const db = client.db(DB_NAME);
@@ -252,4 +281,4 @@ export async function unfollowExperiment(expId: string, userId: string) {
252281
await collection.updateOne({ '_id': new ObjectId(expId) }, { $pull: { 'sharedUsers': userId as any } });
253282

254283
return Promise.resolve();
255-
}
284+
}

0 commit comments

Comments
 (0)