Skip to content

Commit

Permalink
Merge pull request #42 from fair4health/v1.1.0
Browse files Browse the repository at this point in the history
v1.1.0
  • Loading branch information
sinaci authored Dec 30, 2020
2 parents 768f453 + ad8a7b7 commit b462dfa
Show file tree
Hide file tree
Showing 13 changed files with 307 additions and 106 deletions.
2 changes: 1 addition & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "fair4health-data-curation-tool",
"productName": "FAIR4Health Data Curation Tool",
"version": "1.0.1",
"version": "1.1.0",
"private": true,
"author": "SRDC Corporation <[email protected]>",
"description": "FAIR4Health | Data Curation and Validation Tool",
Expand Down
3 changes: 3 additions & 0 deletions src/common/utils/fhir-util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,9 @@ export class FHIRUtil {
const list: fhir.ElementTree[] = []
Promise.all(resource?.snapshot?.element.map((element: fhir.ElementDefinition) => {
return new Promise(resolveElement => {
// Mark the id field as required field
const idSplitted = element.id.split('.')
if (idSplitted.length === 2 && idSplitted[1] === 'id') element.min = 1
const parts = element.id?.split('.') || []
let tmpList = list

Expand Down
2 changes: 2 additions & 0 deletions src/common/utils/ipc-channel-util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ export class IpcChannelUtil {
public static SELECTED_MAPPING = 'selected-mapping'
public static GET_TABLE_HEADERS = 'get-table-headers'
public static READY_TABLE_HEADERS = 'ready-table-headers'
public static PREPARE_SNAPSHOT_DATA = 'prepare-snapshot-data'
public static READY_SNAPSHOT_DATA = 'ready-snapshot-data'
}

public static ElectronStore = class {
Expand Down
36 changes: 34 additions & 2 deletions src/components/BackgroundEngine.ts
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@ export default class BackgroundEngine extends Vue {
this.onGetTableHeaders()
this.onBrowseMapping()
this.onExportFile()
this.onPrepareSnapshotData()

// Resource operation listeners (Validate - Transform)
this.onValidate()
Expand Down Expand Up @@ -216,7 +217,7 @@ export default class BackgroundEngine extends Vue {
stream.on('data', (data) => { buffers.push(data) })
stream.on('end', () => {
const buffer = Buffer.concat(buffers)
const workbook: Excel.WorkBook = Excel.read(buffer, {type: 'buffer', sheetRows: 1})
const workbook: Excel.WorkBook = Excel.read(buffer, {type: 'buffer', sheetRows: 11})

// workbookMap.set(path, workbook)
ipcRenderer.send(ipcChannels.TO_ALL_BACKGROUND, ipcChannels.SET_WORKBOOK_MAP, {key: path, value: workbook})
Expand Down Expand Up @@ -246,7 +247,7 @@ export default class BackgroundEngine extends Vue {
const headers: object[] = []
if (!workbookMap.has(data.path) || data.noCache) {
try {
workbookMap.set(data.path, Excel.readFile(data.path, {type: 'binary', sheetRows: 1}))
workbookMap.set(data.path, Excel.readFile(data.path, {type: 'binary', sheetRows: 11}))
// ipcRenderer.send(ipcChannels.TO_ALL_BACKGROUND, ipcChannels.SET_WORKBOOK_MAP, {key: data.path, value: Excel.readFile(data.path, {type: 'binary', cellDates: true})})
} catch (e) {
log.error(`Cannot read file ${data.path}`)
Expand Down Expand Up @@ -281,6 +282,37 @@ export default class BackgroundEngine extends Vue {
})
}

/**
* Prepares a snapshot of data with a few number of entries
*/
public onPrepareSnapshotData () {
ipcRenderer.on(ipcChannels.File.PREPARE_SNAPSHOT_DATA, (event, data) => {
if (!workbookMap.has(data.path) || data.noCache) {
try {
workbookMap.set(data.path, Excel.readFile(data.path, {type: 'binary', sheetRows: 11}))
} catch (e) {
log.error(`Cannot read file ${data.path}`)
ipcRenderer.send(ipcChannels.TO_RENDERER, ipcChannels.File.READY_SNAPSHOT_DATA, [])
this.ready()
return
}
}
const workbook = workbookMap.get(data.path)
const sheet: Excel.WorkSheet | null = workbook ? workbook.Sheets[data.sheet] : null
if (!(sheet && sheet['!ref'])) {
ipcRenderer.send(ipcChannels.TO_RENDERER, ipcChannels.File.READY_SNAPSHOT_DATA, [])
log.warn(`No columns found in ${data.path} - ${data.sheet}`)
this.ready()
return
}
const entries: any[] = Excel.utils.sheet_to_json(sheet, {raw: false, dateNF: 'mm/dd/yyyy'}) || []

ipcRenderer.send(ipcChannels.TO_RENDERER, ipcChannels.File.READY_SNAPSHOT_DATA, entries)

this.ready()
})
}

/**
* Browses files with .json extension and sends back parsed content
*/
Expand Down
39 changes: 31 additions & 8 deletions src/components/DataSourceAnalyzer.vue
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
<template>
<div>
<q-toolbar class="bg-grey-4 top-fix-column">
<q-toolbar-title class="text-grey-8">
{{ $t('TITLES.CURATION') }} - <span class="text-subtitle1">{{ $t('TITLES.DATA_SOURCE_ANALYZER') }}</span>
<q-btn flat :label="$t('BUTTONS.BACK')" color="primary" icon="chevron_left" @click="previousStep" no-caps />
<q-toolbar-title class="text-grey-8" align="center">
<q-icon name="fas fa-database" color="primary" class="q-px-md" />
{{ $t('TITLES.DATA_SOURCE_ANALYZER') }}
</q-toolbar-title>
<q-btn unelevated :label="$t('BUTTONS.NEXT')" icon-right="chevron_right" color="primary" :disable="!fileSourceList.length"
@click="nextStep" no-caps />
</q-toolbar>
<div class="q-ma-sm row q-gutter-sm">
<q-expansion-item
Expand Down Expand Up @@ -145,6 +149,10 @@
<q-item-section avatar><q-icon name="fas fa-file-download" size="xs" /></q-item-section>
<q-item-section>{{ $t('BUTTONS.LOAD') }}</q-item-section>
</q-item>
<q-item clickable dense class="text-grey-9" @click="exportSavedMapping(props.row.name)" v-close-popup>
<q-item-section avatar><q-icon name="publish" size="xs" /></q-item-section>
<q-item-section>{{ $t('BUTTONS.EXPORT') }}</q-item-section>
</q-item>
<q-item clickable dense class="text-red-5" @click="deleteSavedMapping(props.row.index)" v-close-popup>
<q-item-section avatar><q-icon name="delete" size="xs" /></q-item-section>
<q-item-section>{{ $t('BUTTONS.DELETE') }}</q-item-section>
Expand All @@ -164,12 +172,6 @@
</q-card-section>
</q-card>
</q-expansion-item>
<div class="row q-pa-sm">
<q-btn unelevated :label="$t('BUTTONS.BACK')" color="primary" icon="chevron_left" @click="previousStep" no-caps />
<q-space />
<q-btn unelevated :label="$t('BUTTONS.NEXT')" icon-right="chevron_right" color="primary" :disable="!fileSourceList.length"
@click="nextStep" no-caps />
</div>
</div>
</template>

Expand Down Expand Up @@ -267,6 +269,27 @@
})
}
exportSavedMapping (name: string): void {
const listOfSavedMappings: any[] = JSON.parse(localStorage.getItem('store-fileSourceList') || '[]')
if (listOfSavedMappings.length) {
const mapping = listOfSavedMappings.find(_ => _.name === name)
if (mapping) {
this.$q.loading.show({spinner: undefined})
ipcRenderer.send(ipcChannels.TO_BACKGROUND, ipcChannels.File.EXPORT_FILE, JSON.stringify({fileSourceList: mapping.data.fileSourceList}))
ipcRenderer.on(ipcChannels.File.EXPORT_DONE, (event, result) => {
if (result) {
this.$notify.success(String(this.$t('SUCCESS.FILE_IS_EXPORTED')))
}
this.$q.loading.hide()
ipcRenderer.removeAllListeners(ipcChannels.File.EXPORT_DONE)
})
} else {
this.$notify.error(String(this.$t('ERROR.MAPPING_COULDNT_BE_EXPORTED')))
}
}
}
getISODateString (date: string): string {
return (new Date(date)).toUTCString()
}
Expand Down
16 changes: 7 additions & 9 deletions src/components/MetadataMapper.vue
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
<template>
<div>
<q-toolbar class="bg-grey-4 top-fix-column">
<q-toolbar-title class="text-grey-8">
{{ $t('TITLES.CURATION') }} - <span class="text-subtitle1">{{ $t('TITLES.METADATA_MAPPER') }}</span>
<q-btn flat :label="$t('BUTTONS.BACK')" color="primary" icon="chevron_left" @click="previousStep" no-caps />
<q-toolbar-title class="text-grey-8" align="center">
<q-icon name="fas fa-list-ul" color="primary" class="q-px-md" />
{{ $t('TITLES.METADATA_MAPPER') }}
</q-toolbar-title>
<q-btn v-if="fileSourceList.length" unelevated :label="$t('BUTTONS.NEXT')" icon-right="chevron_right" :disable="!savedRecords.length"
color="primary" @click="nextStep" no-caps />
</q-toolbar>
<div class="q-ma-sm">
<q-expansion-item
Expand Down Expand Up @@ -48,7 +52,7 @@
color="primary" @click="openDefaultValueAssigner" no-caps />
<q-space />
<div class="q-gutter-sm">
<q-btn :disable="!(tickedFHIRAttr.length && selectedAttr.length)" unelevated :label="$t('BUTTONS.MATCH')"
<q-btn :disable="!(tickedFHIRAttr.length && selectedAttr.length)" unelevated :label="$t('BUTTONS.MATCH_ATTRIBUTE')"
color="primary" @click="matchFields" no-caps />
<q-btn unelevated v-show="!editRecordId" color="positive" :label="$t('BUTTONS.ADD_MAPPING')" icon="check" @click="addRecord" no-caps />
<q-btn unelevated v-show="editRecordId" color="primary" :label="$t('BUTTONS.UPDATE')" icon="edit" @click="addRecord" no-caps />
Expand Down Expand Up @@ -183,12 +187,6 @@
</q-card>
</q-expansion-item>
</div>
<div class="row q-pa-sm">
<q-btn unelevated :label="$t('BUTTONS.BACK')" color="primary" icon="chevron_left" @click="previousStep" no-caps />
<q-space />
<q-btn v-if="fileSourceList.length" unelevated :label="$t('BUTTONS.NEXT')" icon-right="chevron_right" :disable="!savedRecords.length"
color="primary" @click="nextStep" no-caps />
</div>
</div>
</template>

Expand Down
10 changes: 7 additions & 3 deletions src/components/Transformer.vue
Original file line number Diff line number Diff line change
@@ -1,9 +1,14 @@
<template>
<div>
<q-toolbar class="bg-grey-4 top-fix-column">
<q-toolbar-title class="text-grey-8">
{{ $t('TITLES.CURATION') }} - <span class="text-subtitle1">{{ $t('TITLES.TRANSFORMER') }}</span>
<div class="col-3">
<q-btn flat :label="$t('BUTTONS.BACK')" color="primary" icon="chevron_left" @click="previousStep" :disable="isInProgress(transformStatus)" no-caps />
</div>
<q-toolbar-title class="text-grey-8 col" align="center">
<q-icon name="fas fa-exchange-alt" color="primary" class="q-px-md" />
{{ $t('TITLES.TRANSFORMER') }}
</q-toolbar-title>
<div class="col-3"></div>
</q-toolbar>
<q-card flat bordered class="q-ma-sm">
<q-card-section>
Expand Down Expand Up @@ -104,7 +109,6 @@
</q-card-section>
</q-card>
<div class="row q-pa-sm">
<q-btn unelevated :label="$t('BUTTONS.BACK')" color="primary" icon="chevron_left" @click="previousStep" :disable="isInProgress(transformStatus)" no-caps />
<q-space />
<q-btn v-if="!isInProgress(transformStatus) && !isPending(transformStatus)"
unelevated icon="done_outline" color="primary" :label="$t('BUTTONS.FINALIZE_CURATION')" @click="finalizeCuration" no-caps />
Expand Down
26 changes: 14 additions & 12 deletions src/components/Validator.vue
Original file line number Diff line number Diff line change
@@ -1,9 +1,21 @@
<template>
<div>
<q-toolbar class="bg-grey-4 top-fix-column">
<q-toolbar-title class="text-grey-8">
{{ $t('TITLES.CURATION') }} - <span class="text-subtitle1">{{ $t('TITLES.VALIDATOR') }}</span>
<div class="col-3">
<q-btn flat :label="$t('BUTTONS.BACK')" color="primary" icon="chevron_left" @click="previousStep" :disable="isInProgress(validationStatus)" no-caps />
</div>
<q-toolbar-title class="text-grey-8" align="center">
<q-icon name="fas fa-check-circle" color="primary" class="q-px-md" />
{{ $t('TITLES.VALIDATOR') }}
</q-toolbar-title>
<div class="col-3 row">
<q-space />
<div class="q-gutter-sm">
<q-btn unelevated :label="isError(validationStatus) ? $t('BUTTONS.CONTINUE_ANYWAY') : $t('BUTTONS.NEXT')"
:icon-right="isError(validationStatus) ? 'error_outline' : 'chevron_right'" color="primary"
:disable="!isSuccess(validationStatus) && !isError(validationStatus)" @click="nextStep" no-caps />
</div>
</div>
</q-toolbar>
<q-card flat bordered class="q-ma-sm">
<q-card-section>
Expand Down Expand Up @@ -213,16 +225,6 @@
</div>
</q-card-section>
</q-card>
<div class="row q-pa-sm">
<q-btn unelevated :label="$t('BUTTONS.BACK')" color="primary" icon="chevron_left" @click="previousStep" :disable="isInProgress(validationStatus)" no-caps />
<q-space />
<div class="q-gutter-sm">
<q-btn outline :label="$t('BUTTONS.CONTINUE_ANYWAY')" icon-right="error_outline" color="primary" v-if="isError(validationStatus)"
@click="nextStep" no-caps />
<q-btn unelevated :label="$t('BUTTONS.NEXT')" icon-right="chevron_right" color="primary" :disable="!isSuccess(validationStatus)"
@click="nextStep" no-caps />
</div>
</div>
</div>
</template>

Expand Down
66 changes: 66 additions & 0 deletions src/components/modals/SnapshotDataCard.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
<template>
<q-dialog ref="dialog">
<q-card class="dialog-card">
<q-card-section class="row items-center">
<div class="text-h6">
<q-icon name="visibility" color="primary" size="sm" />
{{ $t('TITLES.SNAPSHOT_OF_DATA') }}
</div>
<q-space />
<q-btn flat round dense icon="close" @click="onCloseClick" />
</q-card-section>

<q-separator />

<q-card-section>
<q-table
flat
bordered
binary-state-sort
:columns="columns"
:data="entries"
:pagination.sync="pagination"
:rows-per-page-options="[10]"
>
<template v-slot:header="props">
<tr :props="props">
<q-th
v-for="col in props.cols"
:key="col.name"
:props="props"
class="bg-grey-2"
>
<span class="vertical-middle text-grey-9">
{{ col.label }}
</span>
</q-th>
</tr>
</template>
</q-table>
</q-card-section>

<q-separator />
<q-card-actions align="right">
<q-btn flat color="primary" :label="$t('BUTTONS.CLOSE')" @click="onCloseClick" />
</q-card-actions>
</q-card>
</q-dialog>
</template>

<script lang="ts">
import { Component, Mixins, Prop } from 'vue-property-decorator'
import ModalMixin from '@/common/mixins/modalMixin'
@Component
export default class SnapshotDataCard extends Mixins(ModalMixin) {
@Prop({required: true}) columns: any
@Prop({required: true}) entries: any
private pagination = {page: 1, rowsPerPage: 10}
}
</script>

<style lang="stylus">
.dialog-card
width 900px !important
max-width 80vw !important
</style>
Loading

0 comments on commit b462dfa

Please sign in to comment.