Skip to content
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

Merge develop into master #2010

Merged
merged 33 commits into from
Oct 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
540e71b
Add more complete data to OntPoolFactory.
stevieing Aug 16, 2024
a68ace5
Add more complete data to OntPoolFactory
stevieing Aug 23, 2024
55aacc8
refactor: Add find function to JsonApi module
stevieing Aug 23, 2024
ab58f62
Merge branch 'develop' into move-ont-pools-to-fetch
stevieing Sep 27, 2024
3eedaa9
Refactor find function in JsonApi.js to include related data.
stevieing Sep 30, 2024
51347e2
Merge branch 'develop' into move-ont-pools-to-fetch
stevieing Oct 1, 2024
6d8b61c
Refactor JsonApi find function to extract includes
stevieing Oct 3, 2024
20f84aa
Refactor JsonApi find function to work correctly and ensure if it is …
stevieing Oct 7, 2024
4fc8a6c
Refactor JsonApi find function to prevent failure with empty relation…
stevieing Oct 8, 2024
fb042d3
Add tube and plate factory for ONT. Fix method naming.
stevieing Oct 8, 2024
b131ca0
Move ont pools mutations to factories.
stevieing Oct 11, 2024
678b0e8
Merge branch 'develop' into move-ont-pools-to-fetch
stevieing Oct 11, 2024
54d2b65
Refactor getters.spec.js to use factories for OntPlate and OntPool data
stevieing Oct 11, 2024
1df954d
Refactor getters.spec.js to improve readability and maintainability
stevieing Oct 11, 2024
bc1c6fc
Removed OntPool json file.
stevieing Oct 14, 2024
de89e9e
Refactor ONTPoolCreate and ONTSampleIndex tests to use factories for …
stevieing Oct 14, 2024
0496735
Refactor tests and improve readability
stevieing Oct 15, 2024
d0389fa
refactor pool actions to use v2 api and fix all corresponding errors.
stevieing Oct 15, 2024
47bdfa8
Refactor fetchOntPools to use default values for filter and page para…
stevieing Oct 15, 2024
c57bc57
Refactor ont.csv and ont_pool_create.cy.js
stevieing Oct 15, 2024
f2045be
Sort By in Pacbio Libraries view, by id, descending by default
harrietc52 Oct 15, 2024
8d3ebbe
Refactor fetchOntRequests to use default values for filter and page p…
stevieing Oct 16, 2024
ab0d37b
Refactor e2e tests and remove unused fixtures.
stevieing Oct 16, 2024
6ae3ce1
Refactor ont_pool_create.cy.js to use correct returns for pool.
stevieing Oct 16, 2024
5c97cbf
Fix e2e test
harrietc52 Oct 16, 2024
a0c40c8
Refactor extractIncludes function to add maximum depth parameter
stevieing Oct 16, 2024
7282c58
Merge pull request #1999 from sanger/Y24-362
harrietc52 Oct 17, 2024
408d326
feat(qcResultsUpload): moves QcResultsUpload components and service t…
BenTopping Oct 17, 2024
984ab75
Merge branch 'develop' into move-ont-pools-to-fetch
stevieing Oct 17, 2024
6410f26
Merge pull request #1891 from sanger/move-ont-pools-to-fetch
stevieing Oct 18, 2024
f94e72a
Merge branch 'develop' of https://github.com/sanger/traction-ui into …
BenTopping Oct 21, 2024
7a0a171
Merge pull request #2003 from sanger/Y24-344-qc-result-upload-fetch-m…
BenTopping Oct 21, 2024
a3ae5c6
bump release
harrietc52 Oct 22, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading