Skip to content

Commit

Permalink
Merge pull request #626 from sanger/GPL-878-create-multiplexed-libraries
Browse files Browse the repository at this point in the history
[WIP] Gpl 878 create multiplexed libraries
  • Loading branch information
BenTopping authored Jul 28, 2021
2 parents c115cf9 + 72bad88 commit a33a785
Show file tree
Hide file tree
Showing 168 changed files with 6,879 additions and 700 deletions.
15 changes: 13 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,18 @@ jobs:
with:
start: yarn serve
wait-on: http://localhost:8080/
config_file: cypress.json
config-file: cypress.json
spec: "tests/e2e/**/*"
env:
CYPRESS_baseUrl: http://localhost:8080/
CYPRESS_baseUrl: http://localhost:8080/
# after the test run completes
# store any screenshots
# NOTE: screenshots will be generated only if E2E test failed
# thus we store screenshots only on failures
# Alternative: create and commit an empty cypress/screenshots folder
# to always have something to upload
- uses: actions/upload-artifact@v1
if: failure()
with:
name: cypress-screenshots
path: tests/e2e/screenshots
2 changes: 1 addition & 1 deletion .release-version
Original file line number Diff line number Diff line change
@@ -1 +1 @@
3.9.2
4.0.0
2 changes: 2 additions & 0 deletions jest.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,9 @@ module.exports = {
],
moduleNameMapper: {
'^@/(.*)$': '<rootDir>/src/$1',
'^@tests/(.*)$': '<rootDir>/tests/$1',
},
modulePaths: ['<rootDir>/tests/support/'],
snapshotSerializers: [
'jest-serializer-vue', //
],
Expand Down
11 changes: 11 additions & 0 deletions lefthook.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,14 @@ pre-commit:
eslint:
glob: '*.{js,vue}'
run: yarn lint {staged_files}

fix:
parallel: false
commands:
prettier:
glob: '*.{js,vue}'
run: yarn prettier -write {staged_files} --ignore-unknown

eslint:
glob: '*.{js,vue}'
run: yarn lint {staged_files}
23 changes: 17 additions & 6 deletions src/App.vue
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
<template>
<b-container id="app">
<b-container id="app" fluid>
<br />
<router-link to="/dashboard">
<h1 id="traction-header">Traction</h1>
</router-link>
<!-- TODO: move this into a header component and add pipeline to store -->
<b-navbar ref="navbar" toggleable="md" type="dark" variant="info">
<b-navbar-brand id="traction-header" to="/dashboard"><h2>Traction</h2></b-navbar-brand>
<b-nav-text
><h2>{{ pipeline }}</h2></b-nav-text
>
</b-navbar>
<router-view />
<InfoFooter></InfoFooter>
</b-container>
Expand All @@ -15,6 +19,11 @@ export default {
components: {
InfoFooter,
},
computed: {
pipeline() {
return localStorage.getItem('pipeline')
},
},
}
</script>

Expand All @@ -25,10 +34,12 @@ export default {
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
max-width: 80rem;
}
nav {
margin-bottom: 5px;
}
#nav {
padding-bottom: 20px;
margin-bottom: 5px;
a {
font-weight: bold;
color: #2c3e50;
Expand Down
16 changes: 14 additions & 2 deletions src/api/Config.json
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@
"resources": [
{
"name": "libraries",
"include": "request,tag.tag_set,tube"
"include": "request,tag,tube"
},
{
"name": "request_library",
Expand All @@ -62,7 +62,7 @@
},
{
"name": "runs",
"include": "plate.wells.libraries",
"include": "plate.wells.pools.tube",
"resources": [
{
"name": "plates",
Expand All @@ -78,9 +78,21 @@
"name": "plates",
"include": "wells.materials"
},
{
"name": "tag_sets",
"include": "tags"
},
{
"name": "wells",
"include": ""
},
{
"name": "tag_sets",
"include": "tags"
},
{
"name": "pools",
"include": ""
}
]
}
Expand Down
75 changes: 69 additions & 6 deletions src/api/JsonApi.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
// TODO: This needs a refactor with some documentation
const extractAttributes = (data) => {
return { id: data.id, type: data.type, ...data.attributes }
}
/**
* Extract the attributes from a JSON API resource object, merge them with the id
* and type attributes
* @param {String} id The id of the jsonapi resource
* @param {String} type The type of the jsonapi resource
* @param {Object} attributes The attributes object of a JSON API resource
*/
const extractAttributes = ({ id, type, attributes }) => ({ id, type, ...attributes })

const mapRelationships = (relationships) => {
if (relationships === undefined) return {}
const mapRelationships = (relationships = {}) => {
return Object.keys(relationships).reduce((result, name) => {
if (relationships[name].data) {
result[name] = relationships[name].data
Expand All @@ -13,6 +16,63 @@ const mapRelationships = (relationships) => {
}, {})
}

/**
* Groups resources by their resource type
* @param {Array} included Array of JSON API resources
*/
const groupIncludedByResource = (included) => {
return included.reduce((group, resource) => {
if (group[resource.type] === undefined) {
group[resource.type] = []
}
group[resource.type].push(resource)
return group
}, {})
}

/**
* Groups resources by their resource type
* @param {Object} relationships Object of JSON API resources
* @returns {Object} each key will be the relationships grouped by type with an array of ids
*/
const extractRelationshipsAndGroupById = (relationships = {}) => {
return Object.keys(relationships).reduce((result, type) => {
const data = relationships[type].data
if (data instanceof Array) {
// We have multiple entries
result[type] = data.map(({ id }) => id)
} else {
// Add the id data is present, otherwise add the value of data itself
// this may be null (No relation) or undefined (relationship not loaded)
result[type] = data ? data.id : data
}
return result
}, {})
}

/**
* TODO: This will need to be extended to extract relationships?
* Groups resources by their resource type
* @param {Array} data Array of JSON API data
* @returns {Object} keys will be the id of the data
*/
const dataToObjectById = ({ data = [], includeRelationships = false }) => {
return data.reduce((result, { id, type, attributes, relationships }) => {
return {
[id]: {
// we still keep the id as it will be needed
id,
// the type can be useful for components
type,
...attributes,
// we might not want to use the relationships
...(includeRelationships ? extractRelationshipsAndGroupById(relationships) : {}),
},
...result,
}
}, {})
}

const extractRelationship = (relationship, included, includeStore = {}) => {
if (Array.isArray(relationship)) {
return relationship.map((item) => deserializeIncluded(item, included, includeStore))
Expand Down Expand Up @@ -88,12 +148,15 @@ const deserialize = ({ data, included }, includeStore = {}) => {
export {
extractAttributes,
mapRelationships,
groupIncludedByResource,
extractRelationship,
findIncluded,
deserializeIncluded,
extractRelationships,
extractResourceObject,
deserialize,
dataToObjectById,
extractRelationshipsAndGroupById,
}

export default deserialize
26 changes: 14 additions & 12 deletions src/api/PacbioRun.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,9 @@ const buildWell = (
on_plate_loading_concentration: '',
generate_hifi,
ccs_analysis_output,
// TODO remove
libraries: [],
pools: [],
pre_extension_time,
})

Expand Down Expand Up @@ -54,8 +56,8 @@ const create = async (run, request) => {
responses.push(plateResponse)
let plateId = plateResponse.deserialize.plates[0].id

let wellsWithLibraries = run.plate.wells.filter((well) => well.libraries.length != 0)
let wellsPayload = createWellsPayload(wellsWithLibraries, plateId)
let wellsWithPools = run.plate.wells.filter((well) => well.pools.length != 0)
let wellsPayload = createWellsPayload(wellsWithPools, plateId)
let wellResponse = await createResource(wellsPayload, request.runs.wells)
responses.push(wellResponse)
} catch (err) {
Expand All @@ -81,8 +83,8 @@ const update = async (run, request) => {
let runPayload = updateRunPayload(run)
let runResponse = await updateResource(runPayload, request.runs)
responses.push(runResponse)
let wellsWithLibraries = run.plate.wells.filter((well) => well.libraries.length != 0)
for (const well of wellsWithLibraries) {
let wellsWithPools = run.plate.wells.filter((well) => well.pools.length != 0)
for (const well of wellsWithPools) {
if (well.id) {
// Well exists - Update well
let wellPayload = updateWellPayload(well)
Expand Down Expand Up @@ -140,8 +142,8 @@ const createPlatePayload = (runId) => {

const createWellsPayload = (wells, plateId) => {
let wellsAttributes = wells.reduce((accumulator, well) => {
let librariesAttributes = well.libraries.map((l) => {
return { type: 'libraries', id: l.id }
let poolsAttributes = well.pools.map((l) => {
return { type: 'pools', id: l.id }
})

accumulator.push({
Expand All @@ -160,8 +162,8 @@ const createWellsPayload = (wells, plateId) => {
id: plateId,
},
},
libraries: {
data: librariesAttributes,
pools: {
data: poolsAttributes,
},
},
})
Expand Down Expand Up @@ -195,8 +197,8 @@ const updateRunPayload = (run) => {
}

const updateWellPayload = (well) => {
let librariesAttributes = well.libraries.map((l) => {
return { type: 'libraries', id: l.id }
let poolsAttributes = well.pools.map((l) => {
return { type: 'pools', id: l.id }
})

return {
Expand All @@ -214,8 +216,8 @@ const updateWellPayload = (well) => {
pre_extension_time: well.pre_extension_time,
},
relationships: {
libraries: {
data: librariesAttributes,
pools: {
data: poolsAttributes,
},
},
},
Expand Down
66 changes: 66 additions & 0 deletions src/api/ResponseHelper.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
/*
* @param {Object} errors
* @param {Object} error
* @returns {String or Object}
* TODO: Still wondering if there is more to do to make this more robust
* but probably better to find out with a bit of testing
*/
const parseErrors = ({ data, error }) => {
// if its stand alone return it
if (error) {
return error
}
// turn it into something nice i.e. a readable string if it is a 422
if (data.errors) {
const errors = data.errors
return Object.keys(errors)
.map((key) => {
return errors[key].map((item) => `${key} ${item}`).join(', ')
})
.join(', ')
} else {
return data
}
}

/*
* @param Boolean success
* @param {Object} data e.g. { data: { id: 1}}
* @returns { Boolean, {Object}, String} { success, data, errors } e.g. { success: true, data: {id: 1}} or {success: false, errors: 'there was an error'}
*/
const newResponse = ({ success, data, error }) => ({
success,
data,
// we need to parse the errors into something viewable
errors: !success ? parseErrors({ data, error }) : undefined,
})

/*
* @param {AxiosPromise} promise
* @returns {newResponse}
*/
const handleResponse = async (promise) => {
try {
// yay it worked.
const rawResponse = await promise
return newResponse({ success: true, ...rawResponse })
} catch (error) {
// we only want this to output during development or production
// eslint has got this wrong as it is always a string
// eslint-disable-next-line no-extra-boolean-cast
if (!!+process.env.VUE_APP_LOG) {
console.error(error)
}

// 400-500 range will throw an error with a response
if (error.response) {
return newResponse({ success: false, ...error.response })
}
// e.g. a network error which will not elicit an Axios response
return newResponse({ success: false, error, data: undefined })
}
}

export { newResponse, handleResponse }

export default handleResponse
1 change: 1 addition & 0 deletions src/components/Alert.vue
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
<b-alert
id="showAlert"
:variant="type"
data-type="error-message"
dismissible
:show="showDismissibleAlert"
@dismissed="showDismissibleAlert = false"
Expand Down
2 changes: 1 addition & 1 deletion src/components/ont/OntPlate.vue
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ export default {
},
methods: {
getWellAt(position) {
let well = this.wells.filter((well) => well.position == position)[0]
let well = this.wells.find((well) => well.position == position)
return well ? well : { position: position, materials: [] }
},
poolSamples() {
Expand Down
Loading

0 comments on commit a33a785

Please sign in to comment.