Skip to content

Commit

Permalink
Merge pull request #2010 from sanger/develop
Browse files Browse the repository at this point in the history
Merge develop into master
  • Loading branch information
harrietc52 authored Oct 22, 2024
2 parents 6c260b7 + a3ae5c6 commit 75723da
Show file tree
Hide file tree
Showing 49 changed files with 14,639 additions and 18,367 deletions.
2 changes: 1 addition & 1 deletion .release-version
Original file line number Diff line number Diff line change
@@ -1 +1 @@
10.7.8
10.8.0
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ config required:
VITE_SAMPLEEXTRACTION_BASE_URL=<url>
VITE_LOG=false

Replace `<url>` with relevant URLs

### Setup Steps

Install the require dependencies:
Expand Down
74 changes: 74 additions & 0 deletions src/api/JsonApi.js
Original file line number Diff line number Diff line change
Expand Up @@ -305,6 +305,78 @@ const splitDataByParent = ({
}, {})
}

/**
* Extract the includes from the relationships
* If the relationship is an array, then extract all the includes
* If the relationship is an object, then extract the include
* If the includes have their own relationships, then extract those
* @param {Object} relationships - the list of relationships to be extracted
* @param {Array} included - the list of included resources
* @param {Number} depth - the depth of the extraction
* @param {Number} maximumDepth - the maximum depth of the extraction. This is to prevent stack overflow error. Default is 3 (enough to get all data out for standard response). Othwerwise it gets stuck in an endless loop e.g. tags and tag sets.
* @returns {Array} - the list of extracted includes
*/
const extractIncludes = ({ relationships, included, depth = 1, maximumDepth = 3 }) => {
if (depth > maximumDepth) {
return []
}

const rawIncludes = Object.values(relationships).reduce((result, { data }) => {
// prevents failure with empty relationships
if (!data) {
return [...result]
}

if (Array.isArray(data)) {
return [...result, ...data.map((item) => findIncluded(item, included))]
} else {
return [...result, findIncluded(data, included)]
}
}, [])

// we need to run through it again as includes can also have relationships
// this is a recursive function
// we could do this in findIncluded but that is used elsewhere
const includes = rawIncludes.flatMap((includes) => {
if (includes.relationships) {
return [
includes,
...extractIncludes({ relationships: includes.relationships, included, depth: depth + 1 }),
]
} else {
return includes
}
})

// we need to remove includes with no id as this indicates it is part of the data
return includes.filter((item) => item.id)
}

/**
* Find the first n items in the data and return them
* Also extract the includes related to the found data
* @param {Object} data - the data object to be searched
* @param {Number} first - the number of items to return
* @param {Boolean} all - return all the data
* @param {Boolean} get - is this a get request? find returns data as an object and get returns an array
* @returns {Object} - the found data and the included resources
*/
const find = ({ data, all = false, first = 1, get = false } = {}) => {
const foundData = all ? data.data : data.data.slice(0, first)

// we need to extract the includes from the found data
const included = foundData.flatMap(({ relationships }) => {
return extractIncludes({ relationships, included: data.included })
})

// we need to remove the duplicates from included
// if we are only extracting a single record and find is used data needs to be an object
return {
data: foundData.length === 1 && !get ? foundData[0] : foundData,
included: [...new Set(included)],
}
}

export {
extractAttributes,
mapRelationships,
Expand All @@ -324,6 +396,8 @@ export {
populateBy,
splitDataByParent,
dataToObjectByPlateNumber,
extractIncludes,
find,
}

export default deserialize
21 changes: 11 additions & 10 deletions src/components/qcResultsUpload/QcResultsUploadForm.vue
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ export default {
},
computed: {
api() {
return this.$store.getters.api.v1
return this.$store.getters.api.v2
},
qcResultUploadsRequest: ({ api }) => api.traction.qc_results_uploads.create,
border() {
Expand Down Expand Up @@ -113,17 +113,18 @@ export default {
this.busy = true
this.disableUpload = true
try {
const csv = await this.file.text()
const data = { csv: csv, usedBySelected: this.usedBySelected }
await createQcResultsUploadResource(this.qcResultUploadsRequest, data)
this.uploadSuccessful = true
const csv = await this.file.text()
const data = { csv: csv, usedBySelected: this.usedBySelected }
const { success, errors } = await createQcResultsUploadResource(
this.qcResultUploadsRequest,
data,
)
this.uploadSuccessful = success
if (success) {
this.showAlert(`Successfully imported: ${this.file.name}`, 'success')
} catch (e) {
this.uploadSuccessful = false
this.showAlert(e, 'danger')
} else {
this.showAlert(errors || 'Unable to upload QC File', 'danger')
}
this.busy = false
},
Expand Down
27 changes: 17 additions & 10 deletions src/services/traction/QcResultsUpload.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,21 @@
import { handleResponse } from '@/api/v1/ResponseHelper'
/*
UPDATE
*/
import { handleResponse } from '@/api/v2/ResponseHelper'

/**
* Creates a QC results upload resource.
*
* @param {Function} request - The request function to be called.
* @param {Object} params - The parameters for the request.
* @param {string} params.csv - The CSV data to be uploaded.
* @param {boolean} params.usedBySelected - Indicates if the resource is used by the selected entity.
* @returns {Promise<Object>} The data from the response if successful.
* @throws {Object} The errors from the response if unsuccessful.
*/
const createQcResultsUploadResource = async (request, { csv, usedBySelected }) => {
const { success, data, errors } = await handleResponse(
const {
success,
body: { data = {} },
errors = [],
} = await handleResponse(
request({
data: {
data: {
Expand All @@ -18,10 +29,6 @@ const createQcResultsUploadResource = async (request, { csv, usedBySelected }) =
}),
)

if (success) {
return data
} else {
throw errors
}
return { success, data, errors }
}
export { createQcResultsUploadResource }
50 changes: 26 additions & 24 deletions src/store/traction/ont/pools/actions.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { handleResponse } from '@/api/v1/ResponseHelper'
import { handleResponse as handleResponseV2 } from '@/api/v2/ResponseHelper'
import { handleResponse } from '@/api/v2/ResponseHelper'
import { groupIncludedByResource } from '@/api/JsonApi'
import { wellFor, wellToIndex } from './wellHelpers'
import { validate, valid, payload } from './pool'
Expand Down Expand Up @@ -152,15 +151,15 @@ export default {
return { success: true, errors: [] }
}

const request = rootState.api.v1.traction.ont.pools
const request = rootState.api.v2.traction.ont.pools
const promise = request.find({
id: id,
include:
'libraries.tag.tag_set,libraries.source_plate.wells.requests,libraries.source_tube.requests,libraries.request,tube',
})
const response = await handleResponse(promise)

const { success, data: { data, included = [] } = {}, errors = [] } = response
const { success, body: { data, included = [] } = {}, errors = [] } = response

if (success) {
const {
Expand Down Expand Up @@ -205,10 +204,11 @@ export default {
}
}

const request = rootState.api.v1.traction.ont.plates
const promise = request.get({ filter: filter, include: 'wells.requests' })
const request = rootState.api.v2.traction.ont.plates
const promise = request.get({ filter, include: 'wells.requests' })
const response = await handleResponse(promise)
let { success, data: { data, included = [] } = {}, errors = [] } = response

let { success, body: { data, included = [] } = {}, errors = [] } = response
const { wells, requests } = groupIncludedByResource(included)

// We will be return a successful empty list if no plates match the filter
Expand Down Expand Up @@ -245,10 +245,10 @@ export default {
}
}

const request = rootState.api.v1.traction.ont.tubes
const promise = request.get({ filter: filter, include: 'requests' })
const request = rootState.api.v2.traction.ont.tubes
const promise = request.get({ filter, include: 'requests' })
const response = await handleResponse(promise)
let { success, data: { data, included = [] } = {}, errors = [] } = response
let { success, body: { data, included = [] } = {}, errors = [] } = response
const { requests } = groupIncludedByResource(included)

// We will be return a successful empty list if no tubes match the filter
Expand Down Expand Up @@ -290,12 +290,12 @@ export default {
* @param rootState the vuex rootState object. Provides access to the current state
* @param commit the vuex commit object. Provides access to mutations
*/
fetchOntRequests: async ({ commit, rootState }, filter, page) => {
const request = rootState.api.v1.traction.ont.requests
fetchOntRequests: async ({ commit, rootState }, filter = {}, page = {}) => {
const request = rootState.api.v2.traction.ont.requests
const promise = request.get({ page, filter })
const response = await handleResponse(promise)

const { success, data: { data, meta = {} } = {}, errors = [] } = response
const { success, body: { data, meta = {} } = {}, errors = [] } = response

if (success) {
commit('setRequests', data)
Expand All @@ -308,6 +308,7 @@ export default {
* Validate a pool for the given barcode exists
* @param rootState the vuex state object. Provides access to current state
* @param barcode the barcode applied to the pool search
* There doesn't seem to be a test for this. Maybe e2e?
*/
validatePoolBarcode: async ({ rootState }, barcode) => {
// Here we want to make sure the barcode exists
Expand All @@ -318,10 +319,10 @@ export default {
}
}

const request = rootState.api.v1.traction.ont.pools
const request = rootState.api.v2.traction.ont.pools
const promise = request.get({ filter: { barcode } })
const response = await handleResponse(promise)
let { success, data: { data } = {} } = response
let { success, body: { data } = {} } = response

// We will be returned a successful empty list if no pools match the barcode
// Therefore we want to return success false, if we don't have any pools
Expand All @@ -336,16 +337,16 @@ export default {
* @param rootState the vuex rootState object. Provides access to the current state
* @param commit the vuex commit object. Provides access to mutations
*/
fetchOntPools: async ({ commit, rootState }, filter, page) => {
const request = rootState.api.v1.traction.ont.pools
fetchOntPools: async ({ commit, rootState }, filter = {}, page = {}) => {
const request = rootState.api.v2.traction.ont.pools
const promise = request.get({
page,
filter,
include: 'tube,libraries.tag,libraries.request',
})
const response = await handleResponse(promise)

const { success, data: { data, included = [], meta = {} } = {}, errors = [] } = response
const { success, body: { data, included = [], meta = {} } = {}, errors = [] } = response
const { tubes, libraries, tags, requests } = groupIncludedByResource(included)

if (success) {
Expand All @@ -367,15 +368,16 @@ export default {
*/
// For the component, the included relationships are not required
// However, the functionality does not appear to work without them
// no unit tests?
populateOntPools: async ({ commit, rootState }, filter) => {
const request = rootState.api.v1.traction.ont.pools
const request = rootState.api.v2.traction.ont.pools
const promise = request.get({
filter: filter,
include: 'tube,libraries.tag,libraries.request',
})
const response = await handleResponse(promise)

const { success, data: { data, included = [] } = {}, errors = [] } = response
const { success, body: { data, included = [] } = {}, errors = [] } = response
const { tubes, libraries, tags, requests } = groupIncludedByResource(included)

if (success) {
Expand All @@ -397,7 +399,7 @@ export default {
fetchOntTagSets: async ({ commit, rootState }) => {
const request = rootState.api.v2.traction.ont.tag_sets
const promise = request.get({ include: 'tags' })
const response = await handleResponseV2(promise)
const response = await handleResponse(promise)

const { success, body: { data, included = [] } = {}, errors = [] } = response

Expand All @@ -421,9 +423,9 @@ export default {
}) => {
validate({ libraries })
if (!valid({ libraries })) return { success: false, errors: 'The pool is invalid' }
const request = rootState.api.v1.traction.ont.pools
const request = rootState.api.v2.traction.ont.pools
const promise = request.create({ data: payload({ libraries, pool }), include: 'tube' })
const { success, data: { included = [] } = {}, errors } = await handleResponse(promise)
const { success, body: { included = [] } = {}, errors } = await handleResponse(promise)
const { tubes: [tube = {}] = [] } = groupIncludedByResource(included)
const { attributes: { barcode = '' } = {} } = tube
return { success, barcode, errors }
Expand All @@ -440,7 +442,7 @@ export default {
}) => {
validate({ libraries })
if (!valid({ libraries })) return { success: false, errors: 'The pool is invalid' }
const request = rootState.api.v1.traction.ont.pools
const request = rootState.api.v2.traction.ont.pools
const promise = request.update(payload({ libraries, pool }))
const { success, errors } = await handleResponse(promise)
return { success, errors }
Expand Down
1 change: 1 addition & 0 deletions src/store/traction/ont/pools/mutations.js
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ export default {
* Populated the result with the response
* @param {Object} state The VueXState object
* @param {Object} Response A response object
* I am wondering why we need to do this. Refactor needed.
**/
populatePoolingLibraries: (state, data) => {
const newLibraries = dataToObjectById({ data, includeRelationships: true })
Expand Down
3 changes: 2 additions & 1 deletion src/views/pacbio/PacbioLibraryIndex.vue
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,8 @@ const state = reactive({
})
//Define refs
const sortBy = ref('created_at')
// Sort By id descending by default
const sortBy = ref('id')
//Composables
const { showAlert } = useAlert()
Expand Down
Loading

0 comments on commit 75723da

Please sign in to comment.