diff --git a/src/components/screens/ScoreSetView.vue b/src/components/screens/ScoreSetView.vue
index 6e7f9c31..9464acc4 100644
--- a/src/components/screens/ScoreSetView.vue
+++ b/src/components/screens/ScoreSetView.vue
@@ -152,14 +152,23 @@
-
+
+
@@ -375,6 +384,7 @@ import Button from 'primevue/button'
import Checkbox from 'primevue/checkbox'
import Dialog from 'primevue/dialog'
import InputSwitch from 'primevue/inputswitch'
+import ProgressBar from 'primevue/progressbar'
import PrimeDialog from 'primevue/dialog'
import ScrollPanel from 'primevue/scrollpanel'
import Sidebar from 'primevue/sidebar'
@@ -425,6 +435,7 @@ export default {
InputSwitch,
ItemNotFound,
PageLoading,
+ ProgressBar,
PrimeDialog,
ScoreSetHeatmap,
ScoreSetHistogram,
@@ -489,7 +500,10 @@ export default {
publish: false,
update: false,
addCalibration: false
- }
+ },
+ annotatedDownloadInProgress: false,
+ annotatedDownloadProgress: 0,
+ streamController: null
}),
computed: {
@@ -525,7 +539,7 @@ export default {
annotatatedVariantOptions.push({
label: 'Pathogenicity Evidence Line',
command: () => {
- this.downloadAnnotatedVariants('pathogenicity-evidence-line')
+ this.streamVariantAnnotations('pathogenicity-evidence-line')
}
})
}
@@ -534,7 +548,7 @@ export default {
annotatatedVariantOptions.push({
label: 'Functional Impact Statement',
command: () => {
- this.downloadAnnotatedVariants('functional-impact-statement')
+ this.streamVariantAnnotations('functional-impact-statement')
}
})
}
@@ -542,7 +556,7 @@ export default {
annotatatedVariantOptions.push({
label: 'Functional Impact Study Result',
command: () => {
- this.downloadAnnotatedVariants('functional-study-result')
+ this.streamVariantAnnotations('functional-study-result')
}
})
@@ -878,29 +892,82 @@ export default {
this.$toast.add({severity: 'error', summary: 'No downloadable mapped variants text file', life: 3000})
}
},
- downloadAnnotatedVariants: async function (mappedVariantType) {
- let response = null
+ streamVariantAnnotations: async function (annotationType) {
+ this.abortStream()
+ this.streamController = new AbortController()
+
try {
- if (this.item) {
- response = await axios.get(
- `${config.apiBaseUrl}/score-sets/${this.item.urn}/annotated-variants/${mappedVariantType}`
- )
+ this.annotatedDownloadInProgress = true
+ const response = await fetch(
+ `${config.apiBaseUrl}/score-sets/${this.item.urn}/annotated-variants/${annotationType}`,
+ {
+ signal: this.streamController.signal,
+ headers: {
+ Accept: 'application/x-ndjson'
+ }
+ }
+ )
+
+ if (!response.ok) {
+ throw new Error(`HTTP ${response.status}: ${response.statusText}`)
}
- } catch (e) {
- response = e.response || {status: 500}
- }
- if (response.status == 200) {
- //convert object to Json.
- const file = JSON.stringify(response.data)
- const anchor = document.createElement('a')
- anchor.href = 'data:text/json;charset=utf-8,' + encodeURIComponent(file)
- anchor.target = '_blank'
- //file default name
- anchor.download = this.item.urn + '_annotated_variants.json'
- anchor.click()
- } else {
- this.$toast.add({severity: 'error', summary: 'No downloadable annotated variants text file', life: 3000})
+ // Extract metadata from headers to build progress bar
+ const metadata = {
+ totalCount: parseInt(response.headers.get('X-Total-Count') || '0'),
+ processingStarted: response.headers.get('X-Processing-Started') || '',
+ streamType: response.headers.get('X-Stream-Type') || 'unknown'
+ }
+
+ const reader = response.body?.getReader()
+ if (!reader) {
+ throw new Error('Response body is not readable')
+ }
+
+ const decoder = new TextDecoder()
+ const chunks = []
+ let processedCount = 0
+
+ while (true) {
+ const {done, value} = await reader.read()
+
+ if (done) {
+ const allChunks = chunks.join('')
+ // Download the accumulated data as a file
+ const finalData = allChunks.trim()
+ const blob = new Blob([finalData], {type: 'application/x-ndjson'})
+ const url = URL.createObjectURL(blob)
+ const anchor = document.createElement('a')
+ anchor.href = url
+ anchor.download = `${this.item.urn}_annotated_variants_${annotationType}.ndjson`
+ anchor.click()
+ URL.revokeObjectURL(url)
+ break
+ }
+
+ const chunk = decoder.decode(value)
+ chunks.push(chunk)
+ const lines = chunk.split('\n')
+ processedCount += lines.length
+ this.annotatedDownloadProgress = Math.round((processedCount / metadata.totalCount) * 100)
+ }
+ } catch (error) {
+ this.$toast.add({
+ severity: 'error',
+ summary: `Error downloading annotated variants: ${error.message}`,
+ life: 5000
+ })
+ } finally {
+ this.streamController = null
+ this.annotatedDownloadInProgress = false
+ this.annotatedDownloadProgress = 0
+ }
+ },
+ abortStream: function () {
+ if (this.streamController) {
+ this.streamController.abort()
+ this.annotatedDownloadInProgress = false
+ this.annotatedDownloadProgress = 0
}
},
downloadMetadata: async function () {