Skip to content

Commit

Permalink
Added backend API endpoint when there is no Job ID (Tool Form Page): …
Browse files Browse the repository at this point in the history
…passes newly included Error-Transcript JSON and User object to the email report methods
  • Loading branch information
hujambo-dunia committed Apr 9, 2024
1 parent a8cc845 commit 53f1cf2
Show file tree
Hide file tree
Showing 8 changed files with 257 additions and 55 deletions.
28 changes: 20 additions & 8 deletions client/src/components/Common/UserReportingError.vue
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ import { useMarkdown } from "@/composables/markdown";
import { useUserStore } from "@/stores/userStore";
import { sendErrorReport } from "../DatasetInformation/services";
import { sendErrorReportTool } from "../ToolInformation/services";
export default {
components: {
Expand Down Expand Up @@ -141,14 +142,25 @@ export default {
submit(dataset, userEmailJob) {
const email = userEmailJob || this.currentUserEmail;
const message = this.message;
sendErrorReport(dataset, message, email, this.transcript).then(
(resultMessages) => {
this.resultMessages = resultMessages;
},
(errorMessage) => {
this.errorMessage = errorMessage;
}
);
if (this.transcript) {
sendErrorReportTool(dataset, message, email, this.transcript).then(
(resultMessages) => {
this.resultMessages = resultMessages;
},
(errorMessage) => {
this.errorMessage = errorMessage;
}
);
} else {
sendErrorReport(dataset, message, email, this.transcript).then(
(resultMessages) => {
this.resultMessages = resultMessages;
},
(errorMessage) => {
this.errorMessage = errorMessage;
}
);
}
},
hasDetails(outputs) {
return (
Expand Down
19 changes: 19 additions & 0 deletions client/src/components/ToolInformation/services.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import axios from "axios";
import { getAppRoot } from "onload/loadConfig";
import { rethrowSimple } from "utils/simple-error";

export async function sendErrorReportTool(dataset, message, email, transcript) {
const payload = {
dataset_id: dataset.id,
message,
email,
transcript,
};
const url = `${getAppRoot()}api/user-reporting/error`;
try {
const { data } = await axios.post(url, payload);
return data.messages;
} catch (e) {
rethrowSimple(e);
}
}
31 changes: 31 additions & 0 deletions lib/galaxy/schema/schema.py
Original file line number Diff line number Diff line change
Expand Up @@ -3657,3 +3657,34 @@ class DatasetSummary(Model):
file_size: int
total_size: int
uuid: UuidField


class UserReportingErrorPayload(Model):
dataset_id: DecodedDatabaseIdField = Field(
default=...,
title="History Dataset Association ID",
description="The History Dataset Association ID related to the error.",
)
message: Optional[str] = Field(
default=None,
title="Message",
description="The optional message sent with the error report.",
)
email: Optional[str] = Field(
default=None,
title="Email",
description="Email address for communication with the user. Only required for anonymous users.",
)
transcript: Optional[str] = Field(
default=None,
title="Transcript",
description="The optional tool transcript sent with the error report.",
)


class UserReportingErrorSummary(Model):
messages: List[List[str]] = Field(
default=...,
title="Error messages",
description="The error messages for the specified job.",
)
3 changes: 2 additions & 1 deletion lib/galaxy/tools/error_reports/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,8 @@ def _can_access_dataset(self, dataset, user):
roles = []
return self.app.security_agent.can_access_dataset(roles, dataset.dataset)

def submit_report(self, dataset, job, tool, user=None, user_submission=False, **kwargs):
def submit_report(self, dataset, job, tool, user_submission=False, **kwargs):
user = kwargs.get("user")
if user_submission:
assert self._can_access_dataset(dataset, user), Exception("You are not allowed to access this dataset.")

Expand Down
17 changes: 15 additions & 2 deletions lib/galaxy/tools/error_reports/plugins/email.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,25 @@ def __init__(self, **kwargs):

def submit_report(self, dataset, job, tool, **kwargs):
"""Send report as an email"""
data_uid = None
try:
error_reporter = EmailErrorReporter(dataset.id, self.app)
report_type = kwargs.get("report_type")
transcript = kwargs.get("transcript")
if report_type == "dataset":
data_uid = dataset.id
user = job.get_user()
elif report_type == "tool":
data_uid = dataset
user = kwargs.get("user")

error_reporter = EmailErrorReporter(data_uid, self.app, report_type)
error_reporter.send_report(
user=job.get_user(),
user=user,
email=kwargs.get("email", None),
message=kwargs.get("message", None),
report_type=report_type,
tool=tool,
transcript=transcript,
redact_user_details_in_bugreport=self.redact_user_details_in_bugreport,
)
return ("Your error report has been sent", "success")
Expand Down
161 changes: 117 additions & 44 deletions lib/galaxy/tools/errors.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,9 @@
job traceback:
${job_traceback}
-----------------------------------------------------------------------------
tool transcript:
${tool_transcript}
-----------------------------------------------------------------------------
(This is an automated message).
"""

Expand Down Expand Up @@ -125,28 +128,37 @@
${job_traceback}
</pre>
<h4>Tool Transcript</h4>
<pre style="white-space: pre-wrap;background: #eeeeee;border:1px solid black;padding:1em;">
${tool_transcript}
</pre>
This is an automated message. Do not reply to this address.
</body></html>
"""


class ErrorReporter:
def __init__(self, hda, app):
# Get the dataset
sa_session = app.model.context
if not isinstance(hda, model.HistoryDatasetAssociation):
hda_id = hda
try:
hda = sa_session.get(model.HistoryDatasetAssociation, hda_id)
assert hda is not None, ValueError("No HDA yet")
except Exception:
hda = sa_session.get(model.HistoryDatasetAssociation, app.security.decode_id(hda_id))
assert isinstance(hda, model.HistoryDatasetAssociation), ValueError(f"Bad value provided for HDA ({hda}).")
self.hda = hda
# Get the associated job
self.job = hda.creating_job
def __init__(self, hda, app, report_type="dataset"):
if report_type == "dataset":
# Get the dataset
sa_session = app.model.context
if not isinstance(hda, model.HistoryDatasetAssociation):
hda_id = hda
try:
hda = sa_session.get(model.HistoryDatasetAssociation, hda_id)
assert hda is not None, ValueError("No HDA yet")
except Exception:
hda = sa_session.get(model.HistoryDatasetAssociation, app.security.decode_id(hda_id))
assert isinstance(hda, model.HistoryDatasetAssociation), ValueError(f"Bad value provided for HDA ({hda}).")
self.hda = hda
# Get the associated job
self.job = hda.creating_job
self.tool_id = self.job.tool_id
elif report_type == "tool":
self.hda = None
self.tool_id = hda
self.app = app
self.tool_id = self.job.tool_id
self.report = None

def _can_access_dataset(self, user):
Expand All @@ -157,15 +169,65 @@ def _can_access_dataset(self, user):
return self.app.security_agent.can_access_dataset(roles, self.hda.dataset)

def create_report(self, user, email="", message="", redact_user_details_in_bugreport=False, **kwd):
hda = self.hda
job = self.job
host = self.app.url_for("/", qualified=True)
history_id_encoded = self.app.security.encode_id(hda.history_id)
history_view_link = self.app.url_for("/histories/view", id=history_id_encoded, qualified=True)
hda_id_encoded = self.app.security.encode_id(hda.id)
hda_show_params_link = self.app.url_for(
controller="dataset", action="details", dataset_id=hda_id_encoded, qualified=True
)
report_type = kwd.get("report_type")
tool = kwd.get("tool")
hda = ""
job = ""
history_id_encoded = ""
history_view_link = ""
hda_id_encoded = ""
hda_show_params_link = ""
dataset_id_encoded=""
dataset_id=""
history_id=""
hid=""
history_item_name=""
history_id_encoded = ""
history_view_link = ""
hda_id_encoded = ""
hda_show_params_link = ""
job_id_encoded = ""
job_id=""
tool_version=""
job_tool_id=""
job_tool_version=""
tool_transcript=""
job_runner_external_id=""
job_command_line=""
job_stderr=""
job_stdout=""
job_info=""
job_traceback=""

if report_type == "dataset":
hda = self.hda
job = self.job
dataset_id_encoded=self.app.security.encode_id(hda.dataset_id)
dataset_id=hda.dataset_id
history_id=hda.history_id
hid=hda.hid
history_item_name=hda.get_display_name()
history_id_encoded = self.app.security.encode_id(hda.history_id)
history_view_link = self.app.url_for("/histories/view", id=history_id_encoded, qualified=True)
hda_id_encoded = self.app.security.encode_id(hda.id)
hda_show_params_link = self.app.url_for(
controller="dataset", action="details", dataset_id=hda_id_encoded, qualified=True
)
job_id_encoded=self.app.security.encode_id(job.id)
job_id=job.id
tool_version=job.tool_version
job_tool_id=job.tool_id
job_tool_version=hda.tool_version
job_runner_external_id=job.job_runner_external_id
job_command_line=job.command_line
job_stderr=util.unicodify(job.stderr)
job_stdout=util.unicodify(job.stdout)
job_info=util.unicodify(job.info)
job_traceback=util.unicodify(job.traceback)
elif report_type == "tool":
job_tool_id=tool.id
job_tool_version=tool.version
tool_transcript = kwd.get("transcript")
# Build the email message
if redact_user_details_in_bugreport:
# This is sub-optimal but it is hard to solve fully. This affects
Expand Down Expand Up @@ -193,27 +255,28 @@ def create_report(self, user, email="", message="", redact_user_details_in_bugre
email_str = "'%s'" % (email or "anonymous")

report_variables = dict(
host=host,
dataset_id_encoded=self.app.security.encode_id(hda.dataset_id),
dataset_id=hda.dataset_id,
host=self.app.url_for("/", qualified=True),
dataset_id_encoded=dataset_id_encoded,
dataset_id=dataset_id,
history_id_encoded=history_id_encoded,
history_id=hda.history_id,
history_id=history_id,
hda_id_encoded=hda_id_encoded,
hid=hda.hid,
history_item_name=hda.get_display_name(),
hid=hid,
history_item_name=history_item_name,
history_view_link=history_view_link,
hda_show_params_link=hda_show_params_link,
job_id_encoded=self.app.security.encode_id(job.id),
job_id=job.id,
tool_version=job.tool_version,
job_tool_id=job.tool_id,
job_tool_version=hda.tool_version,
job_runner_external_id=job.job_runner_external_id,
job_command_line=job.command_line,
job_stderr=util.unicodify(job.stderr),
job_stdout=util.unicodify(job.stdout),
job_info=util.unicodify(job.info),
job_traceback=util.unicodify(job.traceback),
job_id_encoded=job_id_encoded,
job_id=job_id,
tool_version=tool_version,
job_tool_id=job_tool_id,
job_tool_version=job_tool_version,
tool_transcript=tool_transcript,
job_runner_external_id=job_runner_external_id,
job_command_line=job_command_line,
job_stderr=job_stderr,
job_stdout=job_stdout,
job_info=job_info,
job_traceback=job_traceback,
email_str=email_str,
message=util.unicodify(message),
)
Expand All @@ -235,9 +298,16 @@ def send_report(self, user, email=None, message=None, **kwd):
self.create_report(user, email=email, message=message, **kwd)
return self._send_report(user, email=email, message=message, **kwd)

def email_report(self, user, email=None, message=None, **kwd):
if self.report is None:
self.create_report(user, email=email, message=message, **kwd)
return self._send_report(user, email=email, message=message, **kwd)


class EmailErrorReporter(ErrorReporter):
def _send_report(self, user, email=None, message=None, **kwd):
report_type = kwd.get("report_type")
tool = kwd.get("tool")
smtp_server = self.app.config.smtp_server
assert smtp_server, ValueError("Mail is not configured for this Galaxy instance")
to = self.app.config.error_email_to
Expand All @@ -248,9 +318,12 @@ def _send_report(self, user, email=None, message=None, **kwd):
to += f", {email.strip()}"
subject = f"Galaxy tool error report from {email}"
try:
subject = "{} ({})".format(
subject, self.app.toolbox.get_tool(self.job.tool_id, self.job.tool_version).old_id
)
if report_type == "dataset":
subject = "{} ({})".format(
subject, self.app.toolbox.get_tool(self.job.tool_id, self.job.tool_version).old_id
)
elif report_type == "tool":
subject = "{} ({})".format(subject, tool.old_id)
except Exception:
pass

Expand Down
1 change: 1 addition & 0 deletions lib/galaxy/webapps/galaxy/api/jobs.py
Original file line number Diff line number Diff line change
Expand Up @@ -328,6 +328,7 @@ def error(
job=job,
tool=tool,
user_submission=True,
report_type="dataset",
user=trans.user,
email=email,
message=payload.message,
Expand Down
Loading

0 comments on commit 53f1cf2

Please sign in to comment.