file_export.ts 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170
  1. import { isBoolean, isNumber, sortedUniq, sortedIndexOf, unescape as htmlUnescaped } from 'lodash';
  2. import { saveAs } from 'file-saver';
  3. import { isNullOrUndefined } from 'util';
  4. import { dateTime } from '@grafana/data';
  5. const DEFAULT_DATETIME_FORMAT = 'YYYY-MM-DDTHH:mm:ssZ';
  6. const POINT_TIME_INDEX = 1;
  7. const POINT_VALUE_INDEX = 0;
  8. const END_COLUMN = ';';
  9. const END_ROW = '\r\n';
  10. const QUOTE = '"';
  11. const EXPORT_FILENAME = 'grafana_data_export.csv';
  12. function csvEscaped(text) {
  13. if (!text) {
  14. return text;
  15. }
  16. return text
  17. .split(QUOTE)
  18. .join(QUOTE + QUOTE)
  19. .replace(/^([-+=@])/, "'$1")
  20. .replace(/\s+$/, '');
  21. }
  22. const domParser = new DOMParser();
  23. function htmlDecoded(text) {
  24. if (!text) {
  25. return text;
  26. }
  27. const regexp = /&[^;]+;/g;
  28. function htmlDecoded(value) {
  29. const parsedDom = domParser.parseFromString(value, 'text/html');
  30. return parsedDom.body.textContent;
  31. }
  32. return text.replace(regexp, htmlDecoded).replace(regexp, htmlDecoded);
  33. }
  34. function formatSpecialHeader(useExcelHeader) {
  35. return useExcelHeader ? `sep=${END_COLUMN}${END_ROW}` : '';
  36. }
  37. function formatRow(row, addEndRowDelimiter = true) {
  38. let text = '';
  39. for (let i = 0; i < row.length; i += 1) {
  40. if (isBoolean(row[i]) || isNumber(row[i]) || isNullOrUndefined(row[i])) {
  41. text += row[i];
  42. } else {
  43. text += `${QUOTE}${csvEscaped(htmlUnescaped(htmlDecoded(row[i])))}${QUOTE}`;
  44. }
  45. if (i < row.length - 1) {
  46. text += END_COLUMN;
  47. }
  48. }
  49. return addEndRowDelimiter ? text + END_ROW : text;
  50. }
  51. export function convertSeriesListToCsv(seriesList, dateTimeFormat = DEFAULT_DATETIME_FORMAT, excel = false) {
  52. let text = formatSpecialHeader(excel) + formatRow(['Series', 'Time', 'Value']);
  53. for (let seriesIndex = 0; seriesIndex < seriesList.length; seriesIndex += 1) {
  54. for (let i = 0; i < seriesList[seriesIndex].datapoints.length; i += 1) {
  55. text += formatRow(
  56. [
  57. seriesList[seriesIndex].alias,
  58. dateTime(seriesList[seriesIndex].datapoints[i][POINT_TIME_INDEX]).format(dateTimeFormat),
  59. seriesList[seriesIndex].datapoints[i][POINT_VALUE_INDEX],
  60. ],
  61. i < seriesList[seriesIndex].datapoints.length - 1 || seriesIndex < seriesList.length - 1
  62. );
  63. }
  64. }
  65. return text;
  66. }
  67. export function exportSeriesListToCsv(seriesList, dateTimeFormat = DEFAULT_DATETIME_FORMAT, excel = false) {
  68. const text = convertSeriesListToCsv(seriesList, dateTimeFormat, excel);
  69. saveSaveBlob(text, EXPORT_FILENAME);
  70. }
  71. export function convertSeriesListToCsvColumns(seriesList, dateTimeFormat = DEFAULT_DATETIME_FORMAT, excel = false) {
  72. // add header
  73. let text =
  74. formatSpecialHeader(excel) +
  75. formatRow(
  76. ['Time'].concat(
  77. seriesList.map(val => {
  78. return val.alias;
  79. })
  80. )
  81. );
  82. // process data
  83. const extendedDatapointsList = mergeSeriesByTime(seriesList);
  84. // make text
  85. for (let i = 0; i < extendedDatapointsList[0].length; i += 1) {
  86. const timestamp = dateTime(extendedDatapointsList[0][i][POINT_TIME_INDEX]).format(dateTimeFormat);
  87. text += formatRow(
  88. [timestamp].concat(
  89. extendedDatapointsList.map(datapoints => {
  90. return datapoints[i][POINT_VALUE_INDEX];
  91. })
  92. ),
  93. i < extendedDatapointsList[0].length - 1
  94. );
  95. }
  96. return text;
  97. }
  98. /**
  99. * Collect all unique timestamps from series list and use it to fill
  100. * missing points by null.
  101. */
  102. function mergeSeriesByTime(seriesList) {
  103. let timestamps = [];
  104. for (let i = 0; i < seriesList.length; i++) {
  105. const seriesPoints = seriesList[i].datapoints;
  106. for (let j = 0; j < seriesPoints.length; j++) {
  107. timestamps.push(seriesPoints[j][POINT_TIME_INDEX]);
  108. }
  109. }
  110. timestamps = sortedUniq(timestamps.sort());
  111. const result = [];
  112. for (let i = 0; i < seriesList.length; i++) {
  113. const seriesPoints = seriesList[i].datapoints;
  114. const seriesTimestamps = seriesPoints.map(p => p[POINT_TIME_INDEX]);
  115. const extendedDatapoints = [];
  116. for (let j = 0; j < timestamps.length; j++) {
  117. const timestamp = timestamps[j];
  118. const pointIndex = sortedIndexOf(seriesTimestamps, timestamp);
  119. if (pointIndex !== -1) {
  120. extendedDatapoints.push(seriesPoints[pointIndex]);
  121. } else {
  122. extendedDatapoints.push([null, timestamp]);
  123. }
  124. }
  125. result.push(extendedDatapoints);
  126. }
  127. return result;
  128. }
  129. export function exportSeriesListToCsvColumns(seriesList, dateTimeFormat = DEFAULT_DATETIME_FORMAT, excel = false) {
  130. const text = convertSeriesListToCsvColumns(seriesList, dateTimeFormat, excel);
  131. saveSaveBlob(text, EXPORT_FILENAME);
  132. }
  133. export function convertTableDataToCsv(table, excel = false) {
  134. let text = formatSpecialHeader(excel);
  135. // add headline
  136. text += formatRow(table.columns.map(val => val.title || val.text));
  137. // process data
  138. for (let i = 0; i < table.rows.length; i += 1) {
  139. text += formatRow(table.rows[i], i < table.rows.length - 1);
  140. }
  141. return text;
  142. }
  143. export function exportTableDataToCsv(table, excel = false) {
  144. const text = convertTableDataToCsv(table, excel);
  145. saveSaveBlob(text, EXPORT_FILENAME);
  146. }
  147. export function saveSaveBlob(payload, fname) {
  148. const blob = new Blob([payload], { type: 'text/csv;charset=utf-8;header=present;' });
  149. saveAs(blob, fname);
  150. }