-
Notifications
You must be signed in to change notification settings - Fork 21
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[O2B-1194] Refactor export functionality #1462
base: main
Are you sure you want to change the base?
Changes from all commits
fcb2b3b
1776422
4f6acb0
f19a6dd
4f2a126
232c5fc
1d71101
45bce58
3f747e6
3aeadcf
ce5f604
291388f
766998c
2cabce0
67e91ee
7ae0d0d
bbb2f29
eeadd25
26bc21b
61e2282
3536cf7
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -16,6 +16,13 @@ | |
import { PaginatedRemoteDataSource } from '../utilities/fetch/PaginatedRemoteDataSource.js'; | ||
import { PaginationModel } from '../components/Pagination/PaginationModel.js'; | ||
import { buildUrl } from '../utilities/fetch/buildUrl.js'; | ||
import pick from '../utilities/pick.js'; | ||
import { createCSVExport, createJSONExport } from '../utilities/export.js'; | ||
|
||
export const EXPORT_FORMATS = { | ||
JSON: 'JSON', | ||
CSV: 'CSV', | ||
}; | ||
|
||
/** | ||
* Interface of a model representing an overview page state | ||
|
@@ -51,6 +58,25 @@ | |
|
||
this._observableItems = ObservableData.builder().initialValue(RemoteData.loading()).build(); | ||
this._observableItems.bubbleTo(this); | ||
|
||
this._exportDataSource = new PaginatedRemoteDataSource(); | ||
this._exportObservableItems = ObservableData.builder() | ||
.initialValue(RemoteData.notAsked()) | ||
.apply((remoteData) => remoteData.apply({ Success: ({ items }) => this.processItems(items) })) | ||
.build(); | ||
this._exportObservableItems.bubbleTo(this); | ||
this._exportDataSource.pipe(this._exportObservableItems); | ||
|
||
/** | ||
* Default name for export data file, can be overriden in inherting classes | ||
*/ | ||
this._defaultExportName = 'bkp-export'; | ||
this._exportName = ''; | ||
|
||
/** | ||
* Default format | ||
*/ | ||
this._defaultExportFormat = EXPORT_FORMATS.JSON; | ||
} | ||
|
||
/** | ||
|
@@ -119,8 +145,97 @@ | |
* @return {Promise<void>} void | ||
*/ | ||
async load() { | ||
const params = await this.getLoadParameters(); | ||
await this._dataSource.fetch(buildUrl(this.getRootEndpoint(), params)); | ||
const paginationParams = await this.getLoadParameters(); | ||
this._exportObservableItems.setCurrent(RemoteData.notAsked()); | ||
await this._dataSource.fetch(buildUrl(this.getRootEndpoint(), paginationParams)); | ||
} | ||
|
||
/** | ||
* Fetch all the relevant items from the API | ||
* @return {Promise<void>} void | ||
*/ | ||
async loadExport() { | ||
return this._exportObservableItems.getCurrent().match({ | ||
Success: () => null, | ||
Loading: () => null, | ||
Other: () => this._exportDataSource.fetch(this.getRootEndpoint()), | ||
}); | ||
} | ||
|
||
/** | ||
* Create the export with the variables set in the model, handling errors appropriately | ||
* @param {object[]} items The source content. | ||
* @param {Object<string, Function<*, string>>} exportFormats defines how particual fields of data units will be formated | ||
* @return {void} | ||
*/ | ||
async export(items, exportFormats = {}) { | ||
const { exportFields, exportName } = this; | ||
const exportData = items.map((item) => { | ||
const entries = Object.entries(pick(item, exportFields)); | ||
const formattedEntries = entries.map(([key, value]) => { | ||
const formatExport = exportFormats[key].exportFormat || ((identity) => identity); | ||
return [key, formatExport(value, item)]; | ||
}); | ||
return Object.fromEntries(formattedEntries); | ||
}); | ||
if (this.exportFormat === EXPORT_FORMATS.JSON) { | ||
createJSONExport(exportData, `${exportName}.json`, 'application/json'); | ||
} else if (this.exportFormat === EXPORT_FORMATS.CSV) { | ||
createCSVExport(exportData, `${exportName}.csv`, 'text/csv;charset=utf-8;'); | ||
} else { | ||
throw new Error('Incorrect export format'); | ||
} | ||
} | ||
|
||
/** | ||
* Get export name | ||
* @return {string} name | ||
*/ | ||
get exportName() { | ||
return this._exportName || this._defaultExportName; | ||
} | ||
|
||
/** | ||
* Set export name | ||
* @param {string} exportName name | ||
*/ | ||
set exportName(exportName) { | ||
this._exportName = exportName; | ||
} | ||
|
||
/** | ||
* Get the field values that will be exported | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think this comment is misleading, because it seems like you return the value of the fields. |
||
* @return {string[]} the list of fields of a export items to be included in the export | ||
*/ | ||
get exportFields() { | ||
return this._exportFields || []; | ||
} | ||
|
||
/** | ||
* Set the selected fields to be exported | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
* @param {SelectionOption[]|HTMLCollection} exportFields the list of fields of export items to be included in the export | ||
*/ | ||
set exportFields(exportFields) { | ||
this._exportFields = []; | ||
[...exportFields].map((selectedOption) => this._exportFields.push(selectedOption.value)); | ||
this.notify(); | ||
} | ||
|
||
/** | ||
* Get the output format of the export | ||
* @return {string} the output format | ||
*/ | ||
get exportFormat() { | ||
return this._exportFormat || this._defaultExportFormat; | ||
} | ||
|
||
/** | ||
* Set the export type parameter of the export to be created | ||
* @param {string} exportFormat one of acceptable export formats @see EXPORT_FORMATS | ||
*/ | ||
set exportFormat(exportFormat) { | ||
this._exportFormat = exportFormat; | ||
this.notify(); | ||
} | ||
|
||
/** | ||
|
@@ -136,8 +251,30 @@ | |
} | ||
|
||
/** | ||
* Return the current items remote data | ||
* States if the list of NOT paginated runs contains the full list of runs available under the given criteria | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. runs? |
||
* | ||
* @return {boolean|null} true if the runs list is not truncated (null if all items are not yet available) | ||
*/ | ||
get areExportItemsTruncated() { | ||
return this.exportItems.match({ | ||
Success: (payload) => this.items.match({ | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why do you need this second match? |
||
Success: () => payload.length < this._pagination.itemsCount, | ||
Other: () => null, | ||
}), | ||
Other: () => null, | ||
}); | ||
} | ||
|
||
/** | ||
* Return the export items remote data | ||
* @return {RemoteData<T[]>} the items | ||
*/ | ||
get exportItems() { | ||
return this._exportObservableItems.getCurrent(); | ||
} | ||
|
||
/** | ||
* Return the current items remote data | ||
* @return {RemoteData<T[]>} the items | ||
*/ | ||
get items() { | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Exports are not paginated