Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add endpoint to check if user can submit job #21

Merged
merged 1 commit into from
Oct 2, 2024
Merged
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
7 changes: 7 additions & 0 deletions backend/src/predicTCR_server/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
send_password_reset_email,
request_job,
process_result,
get_user_if_allowed_to_submit,
)


Expand Down Expand Up @@ -207,6 +208,12 @@ def result():
logger.info(f"Returning file {requested_file}")
return flask.send_file(requested_file, as_attachment=True)

@app.route("/api/user_submit_message", methods=["GET"])
@jwt_required()
def user_submit_message():
user, message = get_user_if_allowed_to_submit(current_user.email)
return jsonify(message=message)

@app.route("/api/sample", methods=["POST"])
@jwt_required()
def add_sample():
Expand Down
39 changes: 25 additions & 14 deletions backend/src/predicTCR_server/model.py
Original file line number Diff line number Diff line change
Expand Up @@ -362,32 +362,43 @@ def reset_user_password(token: str, email: str, new_password: str) -> tuple[str,
return "Password changed", 200


def add_new_sample(
email: str,
name: str,
tumor_type: str,
source: str,
h5_file: FileStorage,
csv_file: FileStorage,
) -> tuple[Sample | None, str]:
def get_user_if_allowed_to_submit(email: str) -> tuple[User | None, str]:
logger.info(f"Checking if {email} can submit a job")
user = db.session.execute(
db.select(User).filter(User.email == email)
).scalar_one_or_none()
if user is None:
return None, f"Unknown email address {email}"
return None, f"Unknown email address {email}."
if user.quota <= 0:
return None, "You have reached your sample submission quota."
mins_since_last_submission = (
timestamp_now() - user.last_submission_timestamp
) // 60
logger.debug(
f"{mins_since_last_submission}mins since last submission at {user.last_submission_timestamp}"
)
wait_time_mins = predicTCR_submission_interval_minutes - mins_since_last_submission
logger.debug(f"Submission interval: {predicTCR_submission_interval_minutes}mins")
wait_time_mins = user.submission_interval_minutes - mins_since_last_submission
logger.debug(f"Submission interval: {user.submission_interval_minutes}mins")
logger.debug(f" -> wait time: {wait_time_mins}min")
if wait_time_mins > 0:
return None, f"Your next submission is available in {wait_time_mins} minutes"
if user.quota <= 0:
return None, "You have reached your submission quota"
return (
None,
f"Your next sample submission is available in {wait_time_mins} minute{'s' if wait_time_mins > 1 else ''}.",
)
return user, ""


def add_new_sample(
email: str,
name: str,
tumor_type: str,
source: str,
h5_file: FileStorage,
csv_file: FileStorage,
) -> tuple[Sample | None, str]:
user, msg = get_user_if_allowed_to_submit(email)
if user is None:
return None, msg
user.last_submission_timestamp = timestamp_now()
user.quota -= 1
new_sample = Sample(
Expand Down
18 changes: 15 additions & 3 deletions frontend/src/components/AccountComponent.vue
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ function do_change_password() {
<p>You are currently logged in as {{ current_email }}</p>
<p>
<fwb-button
class="my-2"
@click="
userStore.user = null;
userStore.token = '';
Expand All @@ -74,8 +75,8 @@ function do_change_password() {
placeholder="current password"
maxlength="256"
autocomplete="current-password"
class="mb-2"
/>
<label for="account_passwd_new">New Password:</label>
<fwb-input
v-model="new_password"
label="New password"
Expand All @@ -85,8 +86,13 @@ function do_change_password() {
placeholder="new password"
:title="new_password_message"
maxlength="256"
class="mb-2"
/>
<fwb-alert type="danger" v-if="new_password_message.length > 0">
<fwb-alert
type="danger"
v-if="new_password_message.length > 0"
class="mb-2"
>
{{ new_password_message }}
</fwb-alert>
<fwb-input
Expand All @@ -98,13 +104,19 @@ function do_change_password() {
placeholder="new password"
:title="new_password2_message"
maxlength="256"
class="mb-2"
/>
<fwb-alert type="danger" v-if="new_password2_message.length > 0">
<fwb-alert
type="danger"
v-if="new_password2_message.length > 0"
class="mb-2"
>
{{ new_password2_message }}
</fwb-alert>
<fwb-button
@click="do_change_password"
:title="new_password_message + ' ' + new_password2_message"
class="mb-2"
:disabled="
current_password.length === 0 ||
new_password.length === 0 ||
Expand Down
191 changes: 115 additions & 76 deletions frontend/src/views/SamplesView.vue
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<script setup lang="ts">
import { ref } from "vue";
import { ref, onUnmounted } from "vue";
import SamplesTable from "@/components/SamplesTable.vue";
import { apiClient, logout } from "@/utils/api-client";
import type { Sample } from "@/utils/types";
Expand All @@ -15,7 +15,6 @@
FwbTimelinePoint,
FwbTimelineTitle,
FwbAlert,
FwbModal,
} from "flowbite-vue";

const tumor_types = [
Expand Down Expand Up @@ -99,18 +98,49 @@

const samples = ref([] as Sample[]);

apiClient
.get("samples")
.then((response) => {
samples.value = response.data;
console.log(samples.value);
})
.catch((error) => {
if (error.response.status > 400) {
logout();
}
console.log(error);
});
function update_samples() {
apiClient
.get("samples")
.then((response) => {
samples.value = response.data;
console.log(samples.value);
})
.catch((error) => {
if (error.response.status > 400) {
logout();
}
console.log(error);
});
}

update_samples();

const submit_message = ref("");

let update_submit_message_timer = setInterval(() => {
update_submit_message();
}, 30000);

onUnmounted(() => {
clearInterval(update_submit_message_timer);
});

function update_submit_message() {
console.log("update_submit_message");
apiClient
.get("user_submit_message")
.then((response) => {
submit_message.value = response.data.message;
})
.catch((error) => {
if (error.response.status > 400) {
logout();
}
console.log(error);
});
}

update_submit_message();

function add_sample() {
let formData = new FormData();
Expand All @@ -125,8 +155,9 @@
"Content-Type": "multipart/form-data",
},
})
.then((response) => {

Check warning on line 158 in frontend/src/views/SamplesView.vue

View workflow job for this annotation

GitHub Actions / Frontend :: node 22

'response' is defined but never used
samples.value.push(response.data.sample);
update_samples();
update_submit_message();
new_sample_error_message.value = "";
})
.catch((error) => {
Expand All @@ -153,65 +184,70 @@
<fwb-timeline-content>
<fwb-timeline-title>Submit a sample</fwb-timeline-title>
<fwb-timeline-body>
<div class="flex flex-col mt-2">
<fwb-input
v-model="sample_name"
required
label="Sample name"
id="sample_name"
placeholder="pXYZ_ABC_c1"
maxlength="128"
class="mb-2"
/>
<fwb-select
v-model="tumor_type"
:options="tumor_types"
id="tumor_type"
label="Tumor type"
class="mb-2"
/>
<fwb-select
v-model="source"
:options="sources"
id="source"
label="Source"
class="mb-2"
/>
<fwb-file-input
type="file"
id="input_h5_file"
label="H5 input file"
name="h5 file"
@change="on_h5_file_changed($event)"
:key="h5_file_input_key"
accept=".h5,.he5,.hdf5"
title="Select the h5 file to upload"
class="mb-2"
/>
<fwb-file-input
type="file"
id="input_csv_file"
label="CSV input file"
name="csv file"
@change="on_csv_file_changed($event)"
:key="csv_file_input_key"
accept=".csv,.txt"
title="Select the csv file to upload"
class="mb-2"
/>
<fwb-button
@click="add_sample"
:disabled="
selected_h5_file === null ||
selected_csv_file === null ||
sample_name.length === 0
"
>Submit
</fwb-button>
<fwb-alert type="danger" v-if="new_sample_error_message">
{{ new_sample_error_message }}
</fwb-alert>
</div>
<template v-if="submit_message.length > 0">
{{ submit_message }}
</template>
<template v-else>
<div class="flex flex-col mt-2">
<fwb-input
v-model="sample_name"
required
label="Sample name"
id="sample_name"
placeholder="pXYZ_ABC_c1"
maxlength="128"
class="mb-2"
/>
<fwb-select
v-model="tumor_type"
:options="tumor_types"
id="tumor_type"
label="Tumor type"
class="mb-2"
/>
<fwb-select
v-model="source"
:options="sources"
id="source"
label="Source"
class="mb-2"
/>
<fwb-file-input
type="file"
id="input_h5_file"
label="H5 input file"
name="h5 file"
@change="on_h5_file_changed($event)"
:key="h5_file_input_key"
accept=".h5,.he5,.hdf5"
title="Select the h5 file to upload"
class="mb-2"
/>
<fwb-file-input
type="file"
id="input_csv_file"
label="CSV input file"
name="csv file"
@change="on_csv_file_changed($event)"
:key="csv_file_input_key"
accept=".csv,.txt"
title="Select the csv file to upload"
class="mb-2"
/>
<fwb-button
@click="add_sample"
:disabled="
selected_h5_file === null ||
selected_csv_file === null ||
sample_name.length === 0
"
>Submit
</fwb-button>
<fwb-alert type="danger" v-if="new_sample_error_message">
{{ new_sample_error_message }}
</fwb-alert>
</div>
</template>
</fwb-timeline-body>
</fwb-timeline-content>
</fwb-timeline-item>
Expand All @@ -223,8 +259,11 @@
<fwb-timeline-title>My samples</fwb-timeline-title>
<fwb-timeline-body>
<template v-if="samples.length > 0">
<p>Your samples:</p>
<SamplesTable :samples="samples" :admin="false"></SamplesTable>
<SamplesTable
:samples="samples"
:admin="false"
class="mt-2"
></SamplesTable>
</template>
<template v-else>
<p>You don't yet have any samples.</p>
Expand Down
Loading