@instructure/quiz-core 18.5.1-rc.7 → 18.5.1-rc.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (17) hide show
  1. package/es/reporting/components/resources/NewQuizAndItemAnalysis/NewQuizAnalysis/presenter.js +20 -1
  2. package/es/reporting/components/resources/NewQuizAndItemAnalysis/charts/ScoreDistribution/presenter.js +16 -3
  3. package/es/reporting/components/resources/NewQuizAndItemAnalysis/errorMessages.js +3 -0
  4. package/es/reporting/components/resources/NewQuizAndItemAnalysis/exportReports.js +65 -0
  5. package/es/reporting/components/resources/NewQuizAndItemAnalysis/propTypes.js +8 -3
  6. package/es/reporting/components/resources/NewQuizAndItemAnalysis/utils.js +4 -0
  7. package/es/reporting/components/resources/NewQuizAndItemAnalysis/withAnalysisState.js +2 -0
  8. package/es/reporting/components/resources/NewQuizAndItemAnalysis/withAnalysisStatePresenter.js +41 -1
  9. package/lib/reporting/components/resources/NewQuizAndItemAnalysis/NewQuizAnalysis/presenter.js +20 -1
  10. package/lib/reporting/components/resources/NewQuizAndItemAnalysis/charts/ScoreDistribution/presenter.js +16 -3
  11. package/lib/reporting/components/resources/NewQuizAndItemAnalysis/errorMessages.js +7 -1
  12. package/lib/reporting/components/resources/NewQuizAndItemAnalysis/exportReports.js +80 -0
  13. package/lib/reporting/components/resources/NewQuizAndItemAnalysis/propTypes.js +12 -4
  14. package/lib/reporting/components/resources/NewQuizAndItemAnalysis/utils.js +10 -2
  15. package/lib/reporting/components/resources/NewQuizAndItemAnalysis/withAnalysisState.js +3 -0
  16. package/lib/reporting/components/resources/NewQuizAndItemAnalysis/withAnalysisStatePresenter.js +43 -1
  17. package/package.json +10 -9
@@ -1,3 +1,4 @@
1
+ import _toConsumableArray from "@babel/runtime/helpers/esm/toConsumableArray";
1
2
  import _classCallCheck from "@babel/runtime/helpers/esm/classCallCheck";
2
3
  import _createClass from "@babel/runtime/helpers/esm/createClass";
3
4
  import _inherits from "@babel/runtime/helpers/esm/inherits";
@@ -120,7 +121,7 @@ export var NewQuizAnalysis = (_dec = themeable(theme, styles), _dec(_class = (_t
120
121
  title: expanded ? t('collapse quiz analysis') : t('expand quiz analysis'),
121
122
  isExpanded: expanded
122
123
  }))), expanded && _this.renderExpanded(), /*#__PURE__*/React.createElement(ScoreDistribution, {
123
- decileDistribution: scores === null || scores === void 0 ? void 0 : scores.decile_distribution
124
+ decileDistribution: _this.getPreparedDecileDistribution()
124
125
  }));
125
126
  };
126
127
 
@@ -128,6 +129,24 @@ export var NewQuizAnalysis = (_dec = themeable(theme, styles), _dec(_class = (_t
128
129
  }
129
130
 
130
131
  _createClass(NewQuizAnalysis, [{
132
+ key: "getPreparedDecileDistribution",
133
+ value: function getPreparedDecileDistribution() {
134
+ var quizAnalysis = this.props.quizAnalysis;
135
+ var scores = quizAnalysis.scores,
136
+ metadata = quizAnalysis.metadata;
137
+
138
+ var decileDistribution = _toConsumableArray(scores === null || scores === void 0 ? void 0 : scores.decile_distribution);
139
+
140
+ if (metadata.decile_distribution && (metadata.decile_distribution.high_extreme_values_count > 0 || metadata.decile_distribution.low_extreme_values_count > 0)) {
141
+ var _metadata$decile_dist, _metadata$decile_dist2;
142
+
143
+ decileDistribution.unshift((_metadata$decile_dist = metadata.decile_distribution) === null || _metadata$decile_dist === void 0 ? void 0 : _metadata$decile_dist.low_extreme_values_count);
144
+ decileDistribution.push((_metadata$decile_dist2 = metadata.decile_distribution) === null || _metadata$decile_dist2 === void 0 ? void 0 : _metadata$decile_dist2.high_extreme_values_count);
145
+ }
146
+
147
+ return decileDistribution;
148
+ }
149
+ }, {
131
150
  key: "render",
132
151
  value: function render() {
133
152
  var fetchInProgress = this.props.fetchInProgress;
@@ -39,7 +39,19 @@ export var ScoreDistribution = (_dec = themeable(theme, styles), _dec(_class = (
39
39
 
40
40
  _this = _super.call.apply(_super, [this].concat(args));
41
41
 
42
- _this.calculateLabelForColumn = function (index, length) {
42
+ _this.calculateLabelForColumnsWithExtremeValues = function (index, length) {
43
+ if (index === 0) {
44
+ return t('< 0%');
45
+ }
46
+
47
+ if (index === length - 1) {
48
+ return t('> 100%');
49
+ }
50
+
51
+ return _this.calculateLabelForColumns(index - 1, length - 2);
52
+ };
53
+
54
+ _this.calculateLabelForColumns = function (index, length) {
43
55
  var label = Math.floor(index * 100 / length);
44
56
  return t('{label, number}%', {
45
57
  label: label
@@ -52,6 +64,7 @@ export var ScoreDistribution = (_dec = themeable(theme, styles), _dec(_class = (
52
64
  _createClass(ScoreDistribution, [{
53
65
  key: "render",
54
66
  value: function render() {
67
+ var decileDistribution = this.props.decileDistribution;
55
68
  return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement("div", {
56
69
  className: styles.headingContainer
57
70
  }, /*#__PURE__*/React.createElement(Heading, {
@@ -62,8 +75,8 @@ export var ScoreDistribution = (_dec = themeable(theme, styles), _dec(_class = (
62
75
  }, t('Score Distribution'))), /*#__PURE__*/React.createElement(QuintileDistribution, {
63
76
  animateDelay: 1000,
64
77
  animateDuration: 1000,
65
- visualDistribution: this.props.decileDistribution || [],
66
- calculateLabelForColumn: this.calculateLabelForColumn
78
+ visualDistribution: decileDistribution || [],
79
+ calculateLabelForColumn: decileDistribution.length > 10 ? this.calculateLabelForColumnsWithExtremeValues : this.calculateLabelForColumns
67
80
  }), /*#__PURE__*/React.createElement("div", {
68
81
  "aria-hidden": "true",
69
82
  className: styles.chartLabel
@@ -2,6 +2,9 @@ import t from '@instructure/quiz-i18n/es/format-message';
2
2
  export var getFailedToFetchReports = function getFailedToFetchReports() {
3
3
  return t('An error has occurred while loading this report. Please refresh the page.');
4
4
  };
5
+ export var getFailedToExportReports = function getFailedToExportReports() {
6
+ return t('An error has occurred while exporting this report. Please try again.');
7
+ };
5
8
  export var getLastReportGenerationFailed = function getLastReportGenerationFailed() {
6
9
  return t('Report generation failed. Please try again.');
7
10
  };
@@ -0,0 +1,65 @@
1
+ import _regeneratorRuntime from "@babel/runtime/regenerator";
2
+ import _asyncToGenerator from "@babel/runtime/helpers/esm/asyncToGenerator";
3
+ import { getFailedToExportReports } from "./errorMessages.js";
4
+ import contentDisposition from 'content-disposition';
5
+ export var exportReports = /*#__PURE__*/function () {
6
+ var _ref2 = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee(_ref) {
7
+ var getAnalysesExport, activeQuizId, filter, format, saveFile, addError, response, dispositionHeader, disposition, filename, blob;
8
+ return _regeneratorRuntime.wrap(function _callee$(_context) {
9
+ while (1) {
10
+ switch (_context.prev = _context.next) {
11
+ case 0:
12
+ getAnalysesExport = _ref.getAnalysesExport, activeQuizId = _ref.activeQuizId, filter = _ref.filter, format = _ref.format, saveFile = _ref.saveFile, addError = _ref.addError;
13
+ _context.prev = 1;
14
+ _context.next = 4;
15
+ return getAnalysesExport(activeQuizId, format, filter);
16
+
17
+ case 4:
18
+ response = _context.sent;
19
+ dispositionHeader = response.headers.get('Content-Disposition');
20
+
21
+ if (dispositionHeader) {
22
+ _context.next = 8;
23
+ break;
24
+ }
25
+
26
+ throw new Error('Response contains no Content-Disposition header.');
27
+
28
+ case 8:
29
+ disposition = contentDisposition.parse(dispositionHeader);
30
+ filename = disposition.parameters.filename;
31
+
32
+ if (filename) {
33
+ _context.next = 12;
34
+ break;
35
+ }
36
+
37
+ throw new Error('Cannot determine filename to save.');
38
+
39
+ case 12:
40
+ _context.next = 14;
41
+ return response.blob();
42
+
43
+ case 14:
44
+ blob = _context.sent;
45
+ saveFile(blob, filename);
46
+ _context.next = 21;
47
+ break;
48
+
49
+ case 18:
50
+ _context.prev = 18;
51
+ _context.t0 = _context["catch"](1);
52
+ addError(getFailedToExportReports());
53
+
54
+ case 21:
55
+ case "end":
56
+ return _context.stop();
57
+ }
58
+ }
59
+ }, _callee, null, [[1, 18]]);
60
+ }));
61
+
62
+ return function exportReports(_x) {
63
+ return _ref2.apply(this, arguments);
64
+ };
65
+ }();
@@ -1,9 +1,14 @@
1
1
  import PropTypes from 'prop-types';
2
+ export var quizAnalysisMetadata = PropTypes.shape({
3
+ quiz_title: PropTypes.string,
4
+ decile_distribution: PropTypes.shape({
5
+ high_extreme_values_count: PropTypes.number,
6
+ low_extreme_values_count: PropTypes.number
7
+ })
8
+ });
2
9
  export var quizAnalysisPropType = PropTypes.shape({
3
10
  created_timestamp: PropTypes.string.isRequired,
4
- metadata: PropTypes.shape({
5
- quiz_title: PropTypes.string
6
- }).isRequired,
11
+ metadata: quizAnalysisMetadata.isRequired,
7
12
  scores: PropTypes.shape({
8
13
  decile_distribution: PropTypes.arrayOf(PropTypes.number),
9
14
  cronbachs_alpha: PropTypes.number,
@@ -1,4 +1,5 @@
1
1
  import t from '@instructure/quiz-i18n/es/format-message';
2
+ import { saveAs } from 'file-saver';
2
3
 
3
4
  var formatNumbers = function formatNumbers(value) {
4
5
  if (typeof value === 'number') {
@@ -17,4 +18,7 @@ var getSanitizedMetricValue = function getSanitizedMetricValue(value, suffix) {
17
18
  return suffix ? formattedValue + suffix : formattedValue;
18
19
  };
19
20
 
21
+ export var saveFile = function saveFile(file, fileName) {
22
+ saveAs(file, fileName);
23
+ };
20
24
  export { getSanitizedMetricValue };
@@ -4,6 +4,7 @@ import { setQuizAnalysis, setItemAnalyses, setFetchInProgress, setGenerationInPr
4
4
  import { getQuizAnalysis as fetchQuizAnalysis } from "../../../api/getQuizAnalysis.js";
5
5
  import { getItemAnalyses as fetchItemAnalyses } from "../../../api/getItemAnalyses.js";
6
6
  import { getAnalysesStatus as fetchAnalysesStatus } from "../../../api/getAnalysesStatus.js";
7
+ import { getAnalysesExport } from "../../../api/getAnalysesExport.js";
7
8
  import { connect } from "../../../../common/react-redux.js";
8
9
  import { compose } from 'redux';
9
10
  import { getActiveQuizId } from "../../../../common/selectors/quizzes.js";
@@ -21,6 +22,7 @@ export function mapStateToProps(state, ownProps) {
21
22
  fetchQuizAnalysis: fetchQuizAnalysis,
22
23
  fetchItemAnalyses: fetchItemAnalyses,
23
24
  fetchAnalysesStatus: fetchAnalysesStatus,
25
+ getAnalysesExport: getAnalysesExport,
24
26
  pollingInterval: ownProps.pollingInterval || DEFAULT_POLLING_INTERVAL
25
27
  };
26
28
  }
@@ -9,6 +9,8 @@ import { fetchReports } from "./fetchReports.js";
9
9
  import { fetchReportsStatus } from "./fetchReportsStatus.js";
10
10
  import { getErrorMessageForCode } from "./errorCodes.js";
11
11
  import { generateReports as triggerReportGeneration } from "./generateReports.js";
12
+ import { exportReports as triggerExportReports } from "./exportReports.js";
13
+ import { saveFile } from "./utils.js";
12
14
  export var withAnalysisStatePresenter = function withAnalysisStatePresenter(OriginalComponent) {
13
15
  var WithAnalysisStatePresenter = function WithAnalysisStatePresenter(_ref) {
14
16
  var quizAnalysis = _ref.quizAnalysis,
@@ -26,11 +28,12 @@ export var withAnalysisStatePresenter = function withAnalysisStatePresenter(Orig
26
28
  generateAnalysis = _ref.generateAnalysis,
27
29
  fetchQuizAnalysis = _ref.fetchQuizAnalysis,
28
30
  fetchItemAnalyses = _ref.fetchItemAnalyses,
31
+ getAnalysesExport = _ref.getAnalysesExport,
29
32
  fetchAnalysesStatus = _ref.fetchAnalysesStatus,
30
33
  statusPollingInProgress = _ref.statusPollingInProgress,
31
34
  setStatusPollingInProgress = _ref.setStatusPollingInProgress,
32
35
  pollingInterval = _ref.pollingInterval,
33
- passThroughProps = _objectWithoutProperties(_ref, ["quizAnalysis", "itemAnalyses", "fetchInProgress", "generationInProgress", "analysisError", "setQuizAnalysis", "addError", "setItemAnalyses", "setFetchInProgress", "setGenerationInProgress", "setAnalysisError", "activeQuizId", "generateAnalysis", "fetchQuizAnalysis", "fetchItemAnalyses", "fetchAnalysesStatus", "statusPollingInProgress", "setStatusPollingInProgress", "pollingInterval"]);
36
+ passThroughProps = _objectWithoutProperties(_ref, ["quizAnalysis", "itemAnalyses", "fetchInProgress", "generationInProgress", "analysisError", "setQuizAnalysis", "addError", "setItemAnalyses", "setFetchInProgress", "setGenerationInProgress", "setAnalysisError", "activeQuizId", "generateAnalysis", "fetchQuizAnalysis", "fetchItemAnalyses", "getAnalysesExport", "fetchAnalysesStatus", "statusPollingInProgress", "setStatusPollingInProgress", "pollingInterval"]);
34
37
 
35
38
  WithAnalysisStatePresenter.displayName = "WithAnalysisStatePresenter(".concat(OriginalComponent.displayName || OriginalComponent.name, ")");
36
39
 
@@ -101,6 +104,41 @@ export var withAnalysisStatePresenter = function withAnalysisStatePresenter(Orig
101
104
  return _generateReports.apply(this, arguments);
102
105
  }
103
106
 
107
+ function exportReports(_x) {
108
+ return _exportReports.apply(this, arguments);
109
+ }
110
+
111
+ function _exportReports() {
112
+ _exportReports = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee4(format) {
113
+ var filter,
114
+ exportConfig,
115
+ _args4 = arguments;
116
+ return _regeneratorRuntime.wrap(function _callee4$(_context4) {
117
+ while (1) {
118
+ switch (_context4.prev = _context4.next) {
119
+ case 0:
120
+ filter = _args4.length > 1 && _args4[1] !== void 0 ? _args4[1] : 'last_attempt';
121
+ exportConfig = {
122
+ getAnalysesExport: getAnalysesExport,
123
+ format: format,
124
+ filter: filter,
125
+ activeQuizId: activeQuizId,
126
+ addError: addError,
127
+ saveFile: saveFile
128
+ };
129
+ _context4.next = 4;
130
+ return triggerExportReports(exportConfig);
131
+
132
+ case 4:
133
+ case "end":
134
+ return _context4.stop();
135
+ }
136
+ }
137
+ }, _callee4);
138
+ }));
139
+ return _exportReports.apply(this, arguments);
140
+ }
141
+
104
142
  useEffect(function () {
105
143
  var fetchIfNeeded = /*#__PURE__*/function () {
106
144
  var _ref2 = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee() {
@@ -172,6 +210,7 @@ export var withAnalysisStatePresenter = function withAnalysisStatePresenter(Orig
172
210
  generationInProgress: generationInProgress,
173
211
  analysisError: analysisError,
174
212
  generateReports: generateReports,
213
+ exportReports: exportReports,
175
214
  analysisFetchError: analysisFetchError
176
215
  }, passThroughProps));
177
216
  };
@@ -193,6 +232,7 @@ export var withAnalysisStatePresenter = function withAnalysisStatePresenter(Orig
193
232
  fetchQuizAnalysis: PropTypes.func.isRequired,
194
233
  fetchItemAnalyses: PropTypes.func.isRequired,
195
234
  fetchAnalysesStatus: PropTypes.func.isRequired,
235
+ getAnalysesExport: PropTypes.func.isRequired,
196
236
  addError: PropTypes.func.isRequired,
197
237
  pollingInterval: PropTypes.number.isRequired,
198
238
  generateAnalysis: PropTypes.func.isRequired // from LTI, f(filter: string, onError?: func(error))
@@ -9,6 +9,8 @@ Object.defineProperty(exports, "__esModule", {
9
9
  });
10
10
  exports.NewQuizAnalysis = void 0;
11
11
 
12
+ var _toConsumableArray2 = _interopRequireDefault(require("@babel/runtime/helpers/toConsumableArray"));
13
+
12
14
  var _classCallCheck2 = _interopRequireDefault(require("@babel/runtime/helpers/classCallCheck"));
13
15
 
14
16
  var _createClass2 = _interopRequireDefault(require("@babel/runtime/helpers/createClass"));
@@ -148,7 +150,7 @@ var NewQuizAnalysis = (_dec = (0, _uiThemeable.themeable)(_theme.default, styles
148
150
  title: expanded ? (0, _formatMessage.default)('collapse quiz analysis') : (0, _formatMessage.default)('expand quiz analysis'),
149
151
  isExpanded: expanded
150
152
  }))), expanded && _this.renderExpanded(), /*#__PURE__*/_react.default.createElement(_presenter.ScoreDistribution, {
151
- decileDistribution: scores === null || scores === void 0 ? void 0 : scores.decile_distribution
153
+ decileDistribution: _this.getPreparedDecileDistribution()
152
154
  }));
153
155
  };
154
156
 
@@ -156,6 +158,23 @@ var NewQuizAnalysis = (_dec = (0, _uiThemeable.themeable)(_theme.default, styles
156
158
  }
157
159
 
158
160
  (0, _createClass2.default)(NewQuizAnalysis, [{
161
+ key: "getPreparedDecileDistribution",
162
+ value: function getPreparedDecileDistribution() {
163
+ var quizAnalysis = this.props.quizAnalysis;
164
+ var scores = quizAnalysis.scores,
165
+ metadata = quizAnalysis.metadata;
166
+ var decileDistribution = (0, _toConsumableArray2.default)(scores === null || scores === void 0 ? void 0 : scores.decile_distribution);
167
+
168
+ if (metadata.decile_distribution && (metadata.decile_distribution.high_extreme_values_count > 0 || metadata.decile_distribution.low_extreme_values_count > 0)) {
169
+ var _metadata$decile_dist, _metadata$decile_dist2;
170
+
171
+ decileDistribution.unshift((_metadata$decile_dist = metadata.decile_distribution) === null || _metadata$decile_dist === void 0 ? void 0 : _metadata$decile_dist.low_extreme_values_count);
172
+ decileDistribution.push((_metadata$decile_dist2 = metadata.decile_distribution) === null || _metadata$decile_dist2 === void 0 ? void 0 : _metadata$decile_dist2.high_extreme_values_count);
173
+ }
174
+
175
+ return decileDistribution;
176
+ }
177
+ }, {
159
178
  key: "render",
160
179
  value: function render() {
161
180
  var fetchInProgress = this.props.fetchInProgress;
@@ -61,7 +61,19 @@ var ScoreDistribution = (_dec = (0, _uiThemeable.themeable)(_theme.default, styl
61
61
 
62
62
  _this = _super.call.apply(_super, [this].concat(args));
63
63
 
64
- _this.calculateLabelForColumn = function (index, length) {
64
+ _this.calculateLabelForColumnsWithExtremeValues = function (index, length) {
65
+ if (index === 0) {
66
+ return (0, _formatMessage.default)('< 0%');
67
+ }
68
+
69
+ if (index === length - 1) {
70
+ return (0, _formatMessage.default)('> 100%');
71
+ }
72
+
73
+ return _this.calculateLabelForColumns(index - 1, length - 2);
74
+ };
75
+
76
+ _this.calculateLabelForColumns = function (index, length) {
65
77
  var label = Math.floor(index * 100 / length);
66
78
  return (0, _formatMessage.default)('{label, number}%', {
67
79
  label: label
@@ -74,6 +86,7 @@ var ScoreDistribution = (_dec = (0, _uiThemeable.themeable)(_theme.default, styl
74
86
  (0, _createClass2.default)(ScoreDistribution, [{
75
87
  key: "render",
76
88
  value: function render() {
89
+ var decileDistribution = this.props.decileDistribution;
77
90
  return /*#__PURE__*/_react.default.createElement(_react.default.Fragment, null, /*#__PURE__*/_react.default.createElement("div", {
78
91
  className: styles.headingContainer
79
92
  }, /*#__PURE__*/_react.default.createElement(_uiHeading.Heading, {
@@ -84,8 +97,8 @@ var ScoreDistribution = (_dec = (0, _uiThemeable.themeable)(_theme.default, styl
84
97
  }, (0, _formatMessage.default)('Score Distribution'))), /*#__PURE__*/_react.default.createElement(_index.default, {
85
98
  animateDelay: 1000,
86
99
  animateDuration: 1000,
87
- visualDistribution: this.props.decileDistribution || [],
88
- calculateLabelForColumn: this.calculateLabelForColumn
100
+ visualDistribution: decileDistribution || [],
101
+ calculateLabelForColumn: decileDistribution.length > 10 ? this.calculateLabelForColumnsWithExtremeValues : this.calculateLabelForColumns
89
102
  }), /*#__PURE__*/_react.default.createElement("div", {
90
103
  "aria-hidden": "true",
91
104
  className: styles.chartLabel
@@ -5,7 +5,7 @@ var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefau
5
5
  Object.defineProperty(exports, "__esModule", {
6
6
  value: true
7
7
  });
8
- exports.getNotEnoughDataAvailableError = exports.getMinimumThreeGradedSubmissionError = exports.getOngoingReportGenerationError = exports.getInsufficentSubmissionNumberError = exports.getReportGenerationFailure = exports.getLastReportGenerationFailed = exports.getFailedToFetchReports = void 0;
8
+ exports.getNotEnoughDataAvailableError = exports.getMinimumThreeGradedSubmissionError = exports.getOngoingReportGenerationError = exports.getInsufficentSubmissionNumberError = exports.getReportGenerationFailure = exports.getLastReportGenerationFailed = exports.getFailedToExportReports = exports.getFailedToFetchReports = void 0;
9
9
 
10
10
  var _formatMessage = _interopRequireDefault(require("@instructure/quiz-i18n/es/format-message"));
11
11
 
@@ -15,6 +15,12 @@ var getFailedToFetchReports = function getFailedToFetchReports() {
15
15
 
16
16
  exports.getFailedToFetchReports = getFailedToFetchReports;
17
17
 
18
+ var getFailedToExportReports = function getFailedToExportReports() {
19
+ return (0, _formatMessage.default)('An error has occurred while exporting this report. Please try again.');
20
+ };
21
+
22
+ exports.getFailedToExportReports = getFailedToExportReports;
23
+
18
24
  var getLastReportGenerationFailed = function getLastReportGenerationFailed() {
19
25
  return (0, _formatMessage.default)('Report generation failed. Please try again.');
20
26
  };
@@ -0,0 +1,80 @@
1
+ "use strict";
2
+
3
+ var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
4
+
5
+ Object.defineProperty(exports, "__esModule", {
6
+ value: true
7
+ });
8
+ exports.exportReports = void 0;
9
+
10
+ var _regenerator = _interopRequireDefault(require("@babel/runtime/regenerator"));
11
+
12
+ var _asyncToGenerator2 = _interopRequireDefault(require("@babel/runtime/helpers/asyncToGenerator"));
13
+
14
+ var _errorMessages = require("./errorMessages.js");
15
+
16
+ var _contentDisposition = _interopRequireDefault(require("content-disposition"));
17
+
18
+ var exportReports = /*#__PURE__*/function () {
19
+ var _ref2 = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee(_ref) {
20
+ var getAnalysesExport, activeQuizId, filter, format, saveFile, addError, response, dispositionHeader, disposition, filename, blob;
21
+ return _regenerator.default.wrap(function _callee$(_context) {
22
+ while (1) {
23
+ switch (_context.prev = _context.next) {
24
+ case 0:
25
+ getAnalysesExport = _ref.getAnalysesExport, activeQuizId = _ref.activeQuizId, filter = _ref.filter, format = _ref.format, saveFile = _ref.saveFile, addError = _ref.addError;
26
+ _context.prev = 1;
27
+ _context.next = 4;
28
+ return getAnalysesExport(activeQuizId, format, filter);
29
+
30
+ case 4:
31
+ response = _context.sent;
32
+ dispositionHeader = response.headers.get('Content-Disposition');
33
+
34
+ if (dispositionHeader) {
35
+ _context.next = 8;
36
+ break;
37
+ }
38
+
39
+ throw new Error('Response contains no Content-Disposition header.');
40
+
41
+ case 8:
42
+ disposition = _contentDisposition.default.parse(dispositionHeader);
43
+ filename = disposition.parameters.filename;
44
+
45
+ if (filename) {
46
+ _context.next = 12;
47
+ break;
48
+ }
49
+
50
+ throw new Error('Cannot determine filename to save.');
51
+
52
+ case 12:
53
+ _context.next = 14;
54
+ return response.blob();
55
+
56
+ case 14:
57
+ blob = _context.sent;
58
+ saveFile(blob, filename);
59
+ _context.next = 21;
60
+ break;
61
+
62
+ case 18:
63
+ _context.prev = 18;
64
+ _context.t0 = _context["catch"](1);
65
+ addError((0, _errorMessages.getFailedToExportReports)());
66
+
67
+ case 21:
68
+ case "end":
69
+ return _context.stop();
70
+ }
71
+ }
72
+ }, _callee, null, [[1, 18]]);
73
+ }));
74
+
75
+ return function exportReports(_x) {
76
+ return _ref2.apply(this, arguments);
77
+ };
78
+ }();
79
+
80
+ exports.exportReports = exportReports;
@@ -5,15 +5,23 @@ var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefau
5
5
  Object.defineProperty(exports, "__esModule", {
6
6
  value: true
7
7
  });
8
- exports.afsFlags = exports.answerSummaryPropType = exports.matchingTypeAnswerSummaryPropType = exports.choiceTypeAnswerSummaryPropType = exports.onlyAggregatePropType = exports.itemAnalysisPropType = exports.quizAnalysisPropType = void 0;
8
+ exports.afsFlags = exports.answerSummaryPropType = exports.matchingTypeAnswerSummaryPropType = exports.choiceTypeAnswerSummaryPropType = exports.onlyAggregatePropType = exports.itemAnalysisPropType = exports.quizAnalysisPropType = exports.quizAnalysisMetadata = void 0;
9
9
 
10
10
  var _propTypes = _interopRequireDefault(require("prop-types"));
11
11
 
12
+ var quizAnalysisMetadata = _propTypes.default.shape({
13
+ quiz_title: _propTypes.default.string,
14
+ decile_distribution: _propTypes.default.shape({
15
+ high_extreme_values_count: _propTypes.default.number,
16
+ low_extreme_values_count: _propTypes.default.number
17
+ })
18
+ });
19
+
20
+ exports.quizAnalysisMetadata = quizAnalysisMetadata;
21
+
12
22
  var quizAnalysisPropType = _propTypes.default.shape({
13
23
  created_timestamp: _propTypes.default.string.isRequired,
14
- metadata: _propTypes.default.shape({
15
- quiz_title: _propTypes.default.string
16
- }).isRequired,
24
+ metadata: quizAnalysisMetadata.isRequired,
17
25
  scores: _propTypes.default.shape({
18
26
  decile_distribution: _propTypes.default.arrayOf(_propTypes.default.number),
19
27
  cronbachs_alpha: _propTypes.default.number,
@@ -5,10 +5,12 @@ var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefau
5
5
  Object.defineProperty(exports, "__esModule", {
6
6
  value: true
7
7
  });
8
- exports.getSanitizedMetricValue = void 0;
8
+ exports.getSanitizedMetricValue = exports.saveFile = void 0;
9
9
 
10
10
  var _formatMessage = _interopRequireDefault(require("@instructure/quiz-i18n/es/format-message"));
11
11
 
12
+ var _fileSaver = require("file-saver");
13
+
12
14
  var formatNumbers = function formatNumbers(value) {
13
15
  if (typeof value === 'number') {
14
16
  return _formatMessage.default.number(value);
@@ -26,4 +28,10 @@ var getSanitizedMetricValue = function getSanitizedMetricValue(value, suffix) {
26
28
  return suffix ? formattedValue + suffix : formattedValue;
27
29
  };
28
30
 
29
- exports.getSanitizedMetricValue = getSanitizedMetricValue;
31
+ exports.getSanitizedMetricValue = getSanitizedMetricValue;
32
+
33
+ var saveFile = function saveFile(file, fileName) {
34
+ (0, _fileSaver.saveAs)(file, fileName);
35
+ };
36
+
37
+ exports.saveFile = saveFile;
@@ -18,6 +18,8 @@ var _getItemAnalyses = require("../../../api/getItemAnalyses.js");
18
18
 
19
19
  var _getAnalysesStatus = require("../../../api/getAnalysesStatus.js");
20
20
 
21
+ var _getAnalysesExport = require("../../../api/getAnalysesExport.js");
22
+
21
23
  var _reactRedux = require("../../../../common/react-redux.js");
22
24
 
23
25
  var _redux = require("redux");
@@ -40,6 +42,7 @@ function mapStateToProps(state, ownProps) {
40
42
  fetchQuizAnalysis: _getQuizAnalysis.getQuizAnalysis,
41
43
  fetchItemAnalyses: _getItemAnalyses.getItemAnalyses,
42
44
  fetchAnalysesStatus: _getAnalysesStatus.getAnalysesStatus,
45
+ getAnalysesExport: _getAnalysesExport.getAnalysesExport,
43
46
  pollingInterval: ownProps.pollingInterval || DEFAULT_POLLING_INTERVAL
44
47
  };
45
48
  }
@@ -31,6 +31,10 @@ var _errorCodes = require("./errorCodes.js");
31
31
 
32
32
  var _generateReports2 = require("./generateReports.js");
33
33
 
34
+ var _exportReports2 = require("./exportReports.js");
35
+
36
+ var _utils = require("./utils.js");
37
+
34
38
  var withAnalysisStatePresenter = function withAnalysisStatePresenter(OriginalComponent) {
35
39
  var WithAnalysisStatePresenter = function WithAnalysisStatePresenter(_ref) {
36
40
  var quizAnalysis = _ref.quizAnalysis,
@@ -48,11 +52,12 @@ var withAnalysisStatePresenter = function withAnalysisStatePresenter(OriginalCom
48
52
  generateAnalysis = _ref.generateAnalysis,
49
53
  fetchQuizAnalysis = _ref.fetchQuizAnalysis,
50
54
  fetchItemAnalyses = _ref.fetchItemAnalyses,
55
+ getAnalysesExport = _ref.getAnalysesExport,
51
56
  fetchAnalysesStatus = _ref.fetchAnalysesStatus,
52
57
  statusPollingInProgress = _ref.statusPollingInProgress,
53
58
  setStatusPollingInProgress = _ref.setStatusPollingInProgress,
54
59
  pollingInterval = _ref.pollingInterval,
55
- passThroughProps = (0, _objectWithoutProperties2.default)(_ref, ["quizAnalysis", "itemAnalyses", "fetchInProgress", "generationInProgress", "analysisError", "setQuizAnalysis", "addError", "setItemAnalyses", "setFetchInProgress", "setGenerationInProgress", "setAnalysisError", "activeQuizId", "generateAnalysis", "fetchQuizAnalysis", "fetchItemAnalyses", "fetchAnalysesStatus", "statusPollingInProgress", "setStatusPollingInProgress", "pollingInterval"]);
60
+ passThroughProps = (0, _objectWithoutProperties2.default)(_ref, ["quizAnalysis", "itemAnalyses", "fetchInProgress", "generationInProgress", "analysisError", "setQuizAnalysis", "addError", "setItemAnalyses", "setFetchInProgress", "setGenerationInProgress", "setAnalysisError", "activeQuizId", "generateAnalysis", "fetchQuizAnalysis", "fetchItemAnalyses", "getAnalysesExport", "fetchAnalysesStatus", "statusPollingInProgress", "setStatusPollingInProgress", "pollingInterval"]);
56
61
  WithAnalysisStatePresenter.displayName = "WithAnalysisStatePresenter(".concat(OriginalComponent.displayName || OriginalComponent.name, ")");
57
62
 
58
63
  var _useState = (0, _react.useState)(false),
@@ -122,6 +127,41 @@ var withAnalysisStatePresenter = function withAnalysisStatePresenter(OriginalCom
122
127
  return _generateReports.apply(this, arguments);
123
128
  }
124
129
 
130
+ function exportReports(_x) {
131
+ return _exportReports.apply(this, arguments);
132
+ }
133
+
134
+ function _exportReports() {
135
+ _exportReports = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee4(format) {
136
+ var filter,
137
+ exportConfig,
138
+ _args4 = arguments;
139
+ return _regenerator.default.wrap(function _callee4$(_context4) {
140
+ while (1) {
141
+ switch (_context4.prev = _context4.next) {
142
+ case 0:
143
+ filter = _args4.length > 1 && _args4[1] !== void 0 ? _args4[1] : 'last_attempt';
144
+ exportConfig = {
145
+ getAnalysesExport: getAnalysesExport,
146
+ format: format,
147
+ filter: filter,
148
+ activeQuizId: activeQuizId,
149
+ addError: addError,
150
+ saveFile: _utils.saveFile
151
+ };
152
+ _context4.next = 4;
153
+ return (0, _exportReports2.exportReports)(exportConfig);
154
+
155
+ case 4:
156
+ case "end":
157
+ return _context4.stop();
158
+ }
159
+ }
160
+ }, _callee4);
161
+ }));
162
+ return _exportReports.apply(this, arguments);
163
+ }
164
+
125
165
  (0, _react.useEffect)(function () {
126
166
  var fetchIfNeeded = /*#__PURE__*/function () {
127
167
  var _ref2 = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee() {
@@ -193,6 +233,7 @@ var withAnalysisStatePresenter = function withAnalysisStatePresenter(OriginalCom
193
233
  generationInProgress: generationInProgress,
194
234
  analysisError: analysisError,
195
235
  generateReports: generateReports,
236
+ exportReports: exportReports,
196
237
  analysisFetchError: analysisFetchError
197
238
  }, passThroughProps));
198
239
  };
@@ -214,6 +255,7 @@ var withAnalysisStatePresenter = function withAnalysisStatePresenter(OriginalCom
214
255
  fetchQuizAnalysis: _propTypes.default.func.isRequired,
215
256
  fetchItemAnalyses: _propTypes.default.func.isRequired,
216
257
  fetchAnalysesStatus: _propTypes.default.func.isRequired,
258
+ getAnalysesExport: _propTypes.default.func.isRequired,
217
259
  addError: _propTypes.default.func.isRequired,
218
260
  pollingInterval: _propTypes.default.number.isRequired,
219
261
  generateAnalysis: _propTypes.default.func.isRequired // from LTI, f(filter: string, onError?: func(error))
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@instructure/quiz-core",
3
- "version": "18.5.1-rc.7+c65458992",
3
+ "version": "18.5.1-rc.9+7684e8663",
4
4
  "license": "MIT",
5
5
  "description": "The Quiz React SDK by Instructure Inc.",
6
6
  "author": "Instructure, Inc. Engineering and Product Design",
@@ -43,11 +43,11 @@
43
43
  "dependencies": {
44
44
  "@instructure/grading-utils": "^1.0.0",
45
45
  "@instructure/outcomes-ui": "^2.1.9",
46
- "@instructure/quiz-common": "18.5.1-rc.7+c65458992",
47
- "@instructure/quiz-i18n": "18.5.1-rc.7+c65458992",
48
- "@instructure/quiz-interactions": "18.5.1-rc.7+c65458992",
49
- "@instructure/quiz-number-input": "18.5.1-rc.7+c65458992",
50
- "@instructure/quiz-rce": "18.5.1-rc.7+c65458992",
46
+ "@instructure/quiz-common": "18.5.1-rc.9+7684e8663",
47
+ "@instructure/quiz-i18n": "18.5.1-rc.9+7684e8663",
48
+ "@instructure/quiz-interactions": "18.5.1-rc.9+7684e8663",
49
+ "@instructure/quiz-number-input": "18.5.1-rc.9+7684e8663",
50
+ "@instructure/quiz-rce": "18.5.1-rc.9+7684e8663",
51
51
  "@instructure/ui-a11y-content": "^7.22.1",
52
52
  "@instructure/ui-alerts": "^7.22.1",
53
53
  "@instructure/ui-avatar": "^7.22.1",
@@ -106,9 +106,10 @@
106
106
  "d3-transition": "^1",
107
107
  "date-fns": "^1.28.0",
108
108
  "eventemitter3": "^3.1.0",
109
+ "file-saver": "~2.0.5",
109
110
  "humps": "^2.0.0",
110
111
  "immutable": "^3.8.1",
111
- "instructure-validations": "18.5.1-rc.7+c65458992",
112
+ "instructure-validations": "18.5.1-rc.9+7684e8663",
112
113
  "ipaddr.js": "^1.5.4",
113
114
  "isomorphic-fetch": "^2.2.0",
114
115
  "isuuid": "^0.1.0",
@@ -159,7 +160,7 @@
159
160
  "intl": "^1.2.4",
160
161
  "jquery": "^2.2.3",
161
162
  "most-subject": "^5.3.0",
162
- "quiz-presets": "18.5.1-rc.7+c65458992",
163
+ "quiz-presets": "18.5.1-rc.9+7684e8663",
163
164
  "react": "^16.8.6",
164
165
  "react-addons-test-utils": "^15.6.2",
165
166
  "react-dom": "^16.8.6",
@@ -175,5 +176,5 @@
175
176
  "publishConfig": {
176
177
  "access": "public"
177
178
  },
178
- "gitHead": "c6545899277fee2f0cb7a574740c0b89e13c3ad3"
179
+ "gitHead": "7684e8663eece2857d4f080e80563f2a6f31de33"
179
180
  }