Skip to content

Commit ce26f60

Browse files
committed
Fix #114 - when downloading, return empty csv file (with headers) when no results
1 parent a291508 commit ce26f60

File tree

4 files changed

+53
-3
lines changed

4 files changed

+53
-3
lines changed

lib.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ function report_customsql_pluginfile($course, $cm, $context, $filearea, $args, $
8989
if ($report->runable !== 'manual') {
9090
$runtime = $report->lastrun;
9191
}
92-
$csvtimestamp = report_customsql_generate_csv($report, $runtime);
92+
$csvtimestamp = report_customsql_generate_csv($report, $runtime, true);
9393
}
9494
list($csvfilename) = report_customsql_csv_filename($report, $csvtimestamp);
9595

locallib.php

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -96,14 +96,29 @@ function report_customsql_get_element_type($name) {
9696
return 'text';
9797
}
9898

99-
function report_customsql_generate_csv($report, $timenow) {
99+
/**
100+
* Generate customsql csv file.
101+
*
102+
* @param stdclass $report report record from customsql table.
103+
* @param int $timetimenow unix timestamp - usually "now()"
104+
* @param bool $returnheaderwhenempty if true, a CSV file with headers will always be generated, even if there are no results.
105+
*/
106+
function report_customsql_generate_csv($report, $timenow, $returnheaderwhenempty = false) {
100107
global $DB;
101108
$starttime = microtime(true);
102109

103110
$sql = report_customsql_prepare_sql($report, $timenow);
104111

105112
$queryparams = !empty($report->queryparams) ? unserialize($report->queryparams) : [];
106113
$querylimit = $report->querylimit ?? get_config('report_customsql', 'querylimitdefault');
114+
if ($returnheaderwhenempty) {
115+
// We want the export to always generate a CSV file so we modify the query slightly
116+
// to generate an extra "null" values row, so we can get the column names,
117+
// then we ignore rows that contain null records in every row when generating the csv.
118+
$sql = "SELECT subq.*
119+
FROM (SELECT 1) as ignoreme
120+
LEFT JOIN ($sql) as subq on true";
121+
}
107122
// Query one extra row, so we can tell if we hit the limit.
108123
$rs = report_customsql_execute_query($sql, $queryparams, $querylimit + 1);
109124

@@ -124,6 +139,11 @@ function report_customsql_generate_csv($report, $timenow) {
124139
}
125140

126141
$data = get_object_vars($row);
142+
143+
if ($returnheaderwhenempty && array_unique(array_values($data)) === [null]) {
144+
// This is a row with all null values - ignore it.
145+
continue;
146+
}
127147
foreach ($data as $name => $value) {
128148
if (report_customsql_get_element_type($name) == 'date_time_selector' &&
129149
report_customsql_is_integer($value) && $value > 0) {

tests/behat/behat_report_customsql.php

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@
3030

3131
use Behat\Gherkin\Node\PyStringNode as PyStringNode;
3232
use Behat\Gherkin\Node\TableNode;
33-
33+
use Behat\Mink\Exception\ExpectationException;
3434

3535
/**
3636
* Behat steps for the the custom SQL report.
@@ -248,6 +248,29 @@ public function adhoc_database_queries_thinks_the_time_is($time) {
248248
set_config('behat_fixed_time', $value, 'report_customsql');
249249
}
250250

251+
/**
252+
* Simulates downloading an empty report to ensure it shows table headers.
253+
*
254+
* For example:
255+
* When downloading the empty custom sql report "Frog" it contains the headers "frogname,freddy"
256+
*
257+
* @Then /^downloading custom sql report "(?P<REPORT_NAME>[^"]*)" returns a file with headers "([^"]*)"$/
258+
* @param string $reportname the name of the report to go to.
259+
* @param string $headers the headers that shuold be returned.
260+
*/
261+
public function downloading_custom_sql_report_x_returns_a_file_with_headers(string $reportname, string $headers) {
262+
$report = $this->get_report_by_name($reportname);
263+
$url = new \moodle_url('/pluginfile.php/1/report_customsql/download/' . $report->id, ['dataformat' => 'csv']);
264+
265+
$session = $this->getSession()->getCookie('MoodleSession');
266+
$filecontent = trim(download_file_content($url, ['Cookie' => 'MoodleSession=' . $session]));
267+
$filecontent = core_text::trim_utf8_bom($filecontent);
268+
if ($filecontent != $headers) {
269+
throw new ExpectationException(
270+
"File headers: $filecontent did not match expected: $headers", $this->getSession());
271+
}
272+
}
273+
251274
/**
252275
* Find a report by name and get all the details.
253276
*

tests/behat/report_customsql.feature

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,13 @@ Feature: Ad-hoc database queries report
7676
And I view the "Test query" custom sql report
7777
Then I should see "This query did not return any data."
7878

79+
Scenario: Download an Ad-hoc database query that returns no data but includes headers
80+
Given the following custom sql report exists:
81+
| name | Test query |
82+
| querysql | SELECT * FROM {config} WHERE name = '-1' |
83+
When I log in as "admin"
84+
Then downloading custom sql report "Test query" returns a file with headers "id,name,value"
85+
7986
Scenario: Create an Ad-hoc database queries category
8087
When I log in as "admin"
8188
And I navigate to "Reports > Ad-hoc database queries" in site administration

0 commit comments

Comments
 (0)