FEATURE: Add export poll button (#8370)

This PR aims to make poll results easily exportable to staff in a CSV format, so they can be analyzed in external software.

It also makes the export data easily customizable by allowing users to leverage any data explorer query to generate the report. By default, we use a query that ships with data explorer, but user can change the ID in settings or use 0 to disable this feature.

One potential upgrade is using the recent work that allows arbitrary group to run data explorer and allow all the groups with access to the configured query to also export polls, but that can be added later.

Co-Authored-By: Joffrey JAFFEUX <j.jaffeux@gmail.com>
This commit is contained in:
Rafael dos Santos Silva 2019-11-22 16:06:39 -03:00 committed by GitHub
parent c498780479
commit fd0025a735
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 70 additions and 0 deletions

View File

@ -425,7 +425,9 @@ createWidget("discourse-poll-buttons", {
const closed = attrs.isClosed;
const staffOnly = poll.results === "staff_only";
const isStaff = this.currentUser && this.currentUser.staff;
const dataExplorerEnabled = this.siteSettings.data_explorer_enabled;
const hideResultsDisabled = !staffOnly && (closed || topicArchived);
const exportQueryID = this.siteSettings.poll_export_data_explorer_query_id;
if (attrs.isMultiple && !hideResultsDisabled) {
const castVotesDisabled = !attrs.canCastVotes;
@ -475,6 +477,19 @@ createWidget("discourse-poll-buttons", {
}
}
if (isStaff && dataExplorerEnabled && poll.voters > 0 && exportQueryID) {
contents.push(
this.attach("button", {
className: "btn btn-default export-results",
label: "poll.export-results.label",
title: "poll.export-results.title",
icon: "download",
disabled: poll.voters === 0,
action: "exportResults"
})
);
}
if (poll.get("close")) {
const closeDate = moment.utc(poll.get("close"));
if (closeDate.isValid()) {
@ -685,6 +700,47 @@ export default createWidget("discourse-poll", {
this.state.showResults = !this.state.showResults;
},
exportResults() {
const { attrs } = this;
const queryID = this.siteSettings.poll_export_data_explorer_query_id;
// This uses the Data Explorer plugin export as CSV route
// There is detection to check if the plugin is enabled before showing the button
ajax(`/admin/plugins/explorer/queries/${queryID}/run.csv`, {
type: "POST",
data: {
// needed for data-explorer route compatibility
params: JSON.stringify({
poll_name: attrs.poll.name,
post_id: attrs.post.id.toString() // needed for data-explorer route compatibility
}),
explain: false,
limit: 1000000,
download: 1
}
})
.then(csvContent => {
const downloadLink = document.createElement("a");
const blob = new Blob([csvContent], {
type: "text/csv;charset=utf-8;"
});
downloadLink.href = URL.createObjectURL(blob);
downloadLink.setAttribute(
"download",
`poll-export-${attrs.poll.name}-${attrs.post.id}.csv`
);
downloadLink.click();
downloadLink.remove();
})
.catch(error => {
if (error) {
popupAjaxError(error);
} else {
bootbox.alert(I18n.t("poll.error_while_exporting_results"));
}
});
},
showLogin() {
this.register.lookup("route:application").send("showLogin");
},

View File

@ -36,6 +36,10 @@ div.poll {
line-height: 2em;
}
:not(:first-child):not(:last-child) {
margin-left: 1em;
}
.toggle-status {
float: right;
}

View File

@ -63,6 +63,10 @@ en:
title: "Back to your votes"
label: "Hide results"
export-results:
title: "Export the poll results"
label: "Export"
open:
title: "Open the poll"
label: "Open"
@ -80,6 +84,7 @@ en:
error_while_toggling_status: "Sorry, there was an error toggling the status of this poll."
error_while_casting_votes: "Sorry, there was an error casting your votes."
error_while_fetching_voters: "Sorry, there was an error displaying the voters."
error_while_exporting_results: "Sorry, there was an error exporting poll results."
ui_builder:
title: Build Poll

View File

@ -20,6 +20,7 @@ en:
poll_maximum_options: "Maximum number of options allowed in a poll."
poll_edit_window_mins: "Number of minutes after post creation during which polls can be edited."
poll_minimum_trust_level_to_create: "Define the minimum trust level needed to create polls."
poll_export_data_explorer_query_id: "ID of the Data Explorer Query to use for exporting poll results (0 to disable)."
poll:
poll: "poll"

View File

@ -14,3 +14,7 @@ plugins:
default: 1
client: true
enum: 'TrustLevelSetting'
poll_export_data_explorer_query_id:
default: -16
min: -9999
client: true