Skip to content

Commit d5c8c36

Browse files
committed
Refactored Dataset Error Page email web form
Make component reusable, changes made: * Componentized email web form into Vue.js * Decoupled the View (html email) from the Controller (API methods) * Abstracted the View elements for reuse with other objects (eg.Tools) * Utilized existing dependency (Python Jinja2) for repo consistency Reported by @hexylena in issue #17560 .
1 parent 25d7ab4 commit d5c8c36

File tree

6 files changed

+263
-190
lines changed

6 files changed

+263
-190
lines changed
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
<script setup lang="ts">
2+
import { library } from "@fortawesome/fontawesome-svg-core";
3+
import { faBug } from "@fortawesome/free-solid-svg-icons";
4+
import { FontAwesomeIcon } from "@fortawesome/vue-fontawesome";
5+
import { BAlert, BButton } from "bootstrap-vue";
6+
import { computed, ref } from "vue";
7+
8+
import { submitReport } from "@/components/Collections/common/reporting";
9+
import { useMarkdown } from "@/composables/markdown";
10+
import localize from "@/utils/localization";
11+
12+
import FormElement from "@/components/Form/FormElement.vue";
13+
14+
library.add(faBug);
15+
16+
interface Props {
17+
reportableData: object;
18+
reportingEmail: string;
19+
}
20+
21+
const props = defineProps<Props>();
22+
const { renderMarkdown } = useMarkdown({ openLinksInNewPage: true });
23+
const message = ref("");
24+
const errorMessage = ref("");
25+
const resultMessages = ref<string[][]>([]);
26+
const showForm = computed(() => {
27+
const noResult = !resultMessages.value.length;
28+
const hasError = resultMessages.value.some((msg) => msg[1] === "danger");
29+
return noResult || hasError;
30+
});
31+
const FIELD_MESSAGE = {
32+
loginRequired: localize("You must be logged in to send emails."),
33+
dataRequired: localize("You must provide a valid object to send emails."),
34+
};
35+
const fieldMessages = computed(() =>
36+
[!props.reportableData && FIELD_MESSAGE.dataRequired, !props.reportingEmail && FIELD_MESSAGE.loginRequired].filter(
37+
Boolean
38+
)
39+
);
40+
41+
async function handleSubmit(data?: any, email?: string | null) {
42+
if (!data || !email) {
43+
return;
44+
}
45+
46+
const { messages, error } = await submitReport(data, message.value, email);
47+
48+
if (error) {
49+
errorMessage.value = error;
50+
} else {
51+
resultMessages.value = messages;
52+
}
53+
}
54+
</script>
55+
56+
<template>
57+
<div>
58+
<BAlert v-for="(resultMessage, index) in resultMessages" :key="index" :variant="resultMessage[1]" show>
59+
<span v-html="renderMarkdown(resultMessage[0])" />
60+
</BAlert>
61+
62+
<div v-if="showForm" id="data-error-form">
63+
<div>
64+
<span class="mr-2 font-weight-bold">{{ localize("Your email address") }}</span>
65+
<span v-if="props.reportingEmail">{{ props.reportingEmail }}</span>
66+
<span v-else>{{ FIELD_MESSAGE.loginRequired }}</span>
67+
</div>
68+
<div>
69+
<span class="mr-2 font-weight-bold">{{
70+
localize("Please provide detailed information on the activities leading to this issue:")
71+
}}</span>
72+
<span v-if="!props.reportableData">{{ FIELD_MESSAGE.dataRequired }}</span>
73+
</div>
74+
<FormElement v-if="props.reportableData" id="object-error-message" v-model="message" :area="true" />
75+
</div>
76+
77+
<BButton
78+
id="data-error-submit"
79+
v-b-tooltip.hover
80+
:title="fieldMessages.join('\n')"
81+
variant="primary"
82+
class="mt-3"
83+
@click="handleSubmit(props.reportableData, props.reportingEmail)">
84+
<FontAwesomeIcon :icon="faBug" class="mr-1" />
85+
Report
86+
</BButton>
87+
</div>
88+
</template>
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import { GalaxyApi } from "@/api";
2+
import { type HDADetailed } from "@/api";
3+
import { errorMessageAsString } from "@/utils/simple-error";
4+
5+
export interface ReportableObject {
6+
id: string;
7+
creating_job: string;
8+
}
9+
10+
export async function submitReport(
11+
reportableData: HDADetailed,
12+
message: string,
13+
email: string
14+
): Promise<{ messages: string[][]; error?: string }> {
15+
try {
16+
const { data, error } = await GalaxyApi().POST("/api/jobs/{job_id}/error", {
17+
params: {
18+
path: { job_id: reportableData.creating_job },
19+
},
20+
body: {
21+
dataset_id: reportableData.id,
22+
message,
23+
email,
24+
},
25+
});
26+
27+
if (error) {
28+
return { messages: [], error: errorMessageAsString(error) };
29+
}
30+
return { messages: data.messages };
31+
} catch (err) {
32+
return { messages: [], error: errorMessageAsString(err) };
33+
}
34+
}

client/src/components/DatasetInformation/DatasetError.vue

Lines changed: 4 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,18 @@
11
<script setup lang="ts">
22
import { library } from "@fortawesome/fontawesome-svg-core";
33
import { faBug } from "@fortawesome/free-solid-svg-icons";
4-
import { FontAwesomeIcon } from "@fortawesome/vue-fontawesome";
5-
import { BAlert, BButton, BCard } from "bootstrap-vue";
4+
import { BAlert, BCard } from "bootstrap-vue";
65
import { storeToRefs } from "pinia";
7-
import { computed, onMounted, ref } from "vue";
6+
import { onMounted, ref } from "vue";
87
98
import { GalaxyApi, type HDADetailed } from "@/api";
109
import { fetchDatasetDetails } from "@/api/datasets";
1110
import { type JobDetails, type JobInputSummary } from "@/api/jobs";
12-
import { useMarkdown } from "@/composables/markdown";
1311
import { useUserStore } from "@/stores/userStore";
14-
import localize from "@/utils/localization";
1512
import { errorMessageAsString } from "@/utils/simple-error";
1613
14+
import UserReportingError from "../Collections/common/UserReportingError.vue";
1715
import DatasetErrorDetails from "@/components/DatasetInformation/DatasetErrorDetails.vue";
18-
import FormElement from "@/components/Form/FormElement.vue";
1916
import GalaxyWizard from "@/components/GalaxyWizard.vue";
2017
2118
library.add(faBug);
@@ -25,28 +22,15 @@ interface Props {
2522
}
2623
2724
const props = defineProps<Props>();
28-
2925
const userStore = useUserStore();
3026
const { currentUser } = storeToRefs(userStore);
31-
32-
const { renderMarkdown } = useMarkdown({ openLinksInNewPage: true });
33-
34-
const message = ref("");
3527
const jobLoading = ref(true);
3628
const errorMessage = ref("");
3729
const datasetLoading = ref(false);
3830
const jobDetails = ref<JobDetails>();
3931
const jobProblems = ref<JobInputSummary>();
40-
const resultMessages = ref<string[][]>([]);
4132
const dataset = ref<HDADetailed>();
4233
43-
const showForm = computed(() => {
44-
const noResult = !resultMessages.value.length;
45-
const hasError = resultMessages.value.some((msg) => msg[1] === "danger");
46-
47-
return noResult || hasError;
48-
});
49-
5034
async function getDatasetDetails() {
5135
datasetLoading.value = true;
5236
try {
@@ -93,31 +77,6 @@ async function getJobProblems(jobId: string) {
9377
jobProblems.value = data;
9478
}
9579
96-
async function submit(dataset?: HDADetailed, userEmailJob?: string | null) {
97-
if (!dataset) {
98-
errorMessage.value = "No dataset found.";
99-
return;
100-
}
101-
102-
const { data, error } = await GalaxyApi().POST("/api/jobs/{job_id}/error", {
103-
params: {
104-
path: { job_id: dataset.creating_job },
105-
},
106-
body: {
107-
dataset_id: dataset.id,
108-
message: message.value,
109-
email: userEmailJob,
110-
},
111-
});
112-
113-
if (error) {
114-
errorMessage.value = errorMessageAsString(error);
115-
return;
116-
}
117-
118-
resultMessages.value = data.messages;
119-
}
120-
12180
function onMissingJobId() {
12281
errorMessage.value = "No job ID found for this dataset.";
12382
}
@@ -205,30 +164,7 @@ onMounted(async () => {
205164
</p>
206165

207166
<h4 class="mb-3 h-md">Issue Report</h4>
208-
<BAlert v-for="(resultMessage, index) in resultMessages" :key="index" :variant="resultMessage[1]" show>
209-
<span v-html="renderMarkdown(resultMessage[0])" />
210-
</BAlert>
211-
212-
<div v-if="showForm" id="dataset-error-form">
213-
<span class="mr-2 font-weight-bold">{{ localize("Your email address") }}</span>
214-
<span v-if="currentUser?.email">{{ currentUser.email }}</span>
215-
<span v-else>{{ localize("You must be logged in to receive emails") }}</span>
216-
217-
<FormElement
218-
id="dataset-error-message"
219-
v-model="message"
220-
:area="true"
221-
title="Please provide detailed information on the activities leading to this issue:" />
222-
223-
<BButton
224-
id="dataset-error-submit"
225-
variant="primary"
226-
class="mt-3"
227-
@click="submit(dataset, jobDetails?.user_email)">
228-
<FontAwesomeIcon :icon="faBug" class="mr-1" />
229-
Report
230-
</BButton>
231-
</div>
167+
<UserReportingError :reportable-data="dataset" :reporting-email="currentUser?.email" />
232168
</div>
233169
</div>
234170
</template>
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
<html>
2+
<body>
3+
<h1>Galaxy Tool Error Report</h1>
4+
<span class="sub">
5+
<i>from </i>
6+
<span style="font-family: monospace;">
7+
<a href="{{ host }}">{{ host }}</a>
8+
</span>
9+
</span>
10+
<h3>Error Localization</h3>
11+
<table style="margin:1em">
12+
<tbody>
13+
<tr>
14+
<td>Dataset</td>
15+
<td>
16+
<a href="{{ hda_show_params_link }}">{{ dataset_id }} ({{ dataset_id_encoded }})</a>
17+
</td>
18+
</tr>
19+
<tr style="background-color: #f2f2f2">
20+
<td>History</td>
21+
<td>
22+
<a href="{{ history_view_link }}">{{ history_id }} ({{ history_id_encoded }})</a>
23+
</td>
24+
</tr>
25+
<tr>
26+
<td>Failed Job</td>
27+
<td>{{ hid }}: {{ history_item_name }} ({{ hda_id_encoded }})</td>
28+
</tr>
29+
</tbody>
30+
</table>
31+
<h3>User Provided Information</h3>
32+
The user
33+
<span style="font-family: monospace;">{{ email_str }}</span> provided the following information:
34+
<pre style="white-space: pre-wrap;background: #eeeeee;border:1px solid black;padding:1em;">
35+
{{ message }}
36+
</pre>
37+
<h3>Detailed Job Information</h3>
38+
Job environment and execution information is available at the job
39+
<a href="{{ hda_show_params_link }}">info page</a>.
40+
<table style="margin:1em">
41+
<tbody>
42+
<tr>
43+
<td>Job ID</td>
44+
<td>{{ job_id }} ({{ job_id_encoded }})</td>
45+
</tr>
46+
<tr style="background-color: #f2f2f2">
47+
<td>Tool ID</td>
48+
<td>{{ job_tool_id }}</td>
49+
</tr>
50+
<tr>
51+
<td>Tool Version</td>
52+
<td>{{ tool_version }}</td>
53+
</tr>
54+
<tr style="background-color: #f2f2f2">
55+
<td>Job PID or DRM id</td>
56+
<td>{{ job_runner_external_id }}</td>
57+
</tr>
58+
<tr>
59+
<td>Job Tool Version</td>
60+
<td>{{ job_tool_version }}</td>
61+
</tr>
62+
</tbody>
63+
</table>
64+
<h3>Job Execution and Failure Information</h3>
65+
<h4>Command Line</h4>
66+
<pre style="white-space: pre-wrap;background: #eeeeee;border:1px solid black;padding:1em;">
67+
{{ job_command_line }}
68+
</pre>
69+
<h4>stderr</h4>
70+
<pre style="white-space: pre-wrap;background: #eeeeee;border:1px solid black;padding:1em;">
71+
{{ job_stderr }}
72+
</pre>
73+
<h4>stdout</h4>
74+
<pre style="white-space: pre-wrap;background: #eeeeee;border:1px solid black;padding:1em;">
75+
{{ job_stdout }}
76+
</pre>
77+
<h4>Job Information</h4>
78+
<pre style="white-space: pre-wrap;background: #eeeeee;border:1px solid black;padding:1em;">
79+
{{ job_info }}
80+
</pre>
81+
<h4>Job Traceback</h4>
82+
<pre style="white-space: pre-wrap;background: #eeeeee;border:1px solid black;padding:1em;">
83+
{{ job_traceback }}
84+
</pre>
85+
This is an automated message. Do not reply to this address.
86+
</body>
87+
</html>
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
GALAXY TOOL ERROR REPORT
2+
------------------------
3+
4+
This error report was sent from the Galaxy instance hosted on the server
5+
"{{ host }}"
6+
-----------------------------------------------------------------------------
7+
This is in reference to dataset id {{ dataset_id }} ({{ dataset_id_encoded }}) from history id {{ history_id }} ({{ history_id_encoded }})
8+
-----------------------------------------------------------------------------
9+
You should be able to view the history containing the related history item ({{ hda_id_encoded }})
10+
11+
{{ hid }}: {{ history_item_name }}
12+
13+
by logging in as a Galaxy admin user to the Galaxy instance referenced above
14+
and pointing your browser to the following link.
15+
16+
{{ history_view_link }}
17+
-----------------------------------------------------------------------------
18+
The user {{ email_str }} provided the following information:
19+
20+
{{ message }}
21+
-----------------------------------------------------------------------------
22+
info url: {{ hda_show_params_link }}
23+
job id: {{ job_id }} ({{ job_id_encoded }})
24+
tool id: {{ job_tool_id }}
25+
tool version: {{ tool_version }}
26+
job pid or drm id: {{ job_runner_external_id }}
27+
job tool version: {{ job_tool_version }}
28+
-----------------------------------------------------------------------------
29+
job command line:
30+
{{ job_command_line }}
31+
-----------------------------------------------------------------------------
32+
job stderr:
33+
{{ job_stderr }}
34+
-----------------------------------------------------------------------------
35+
job stdout:
36+
{{ job_stdout }}
37+
-----------------------------------------------------------------------------
38+
job info:
39+
{{ job_info }}
40+
-----------------------------------------------------------------------------
41+
job traceback:
42+
{{ job_traceback }}
43+
-----------------------------------------------------------------------------
44+
(This is an automated message).

0 commit comments

Comments
 (0)