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

Sample details refactor/ default assembly #1327

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
558c30c
Updated to allow setting of a default genome assembly object for a sa…
deepsidhu85 Apr 28, 2022
0caca20
Merge branch 'sample_details_refactor/_file_qc' into sample_details_r…
deepsidhu85 Apr 30, 2022
8104244
Updated api calls to invalidate tags
deepsidhu85 May 3, 2022
c86f27d
Merged sample details refactor file qc branch and fixed merge conflicts
deepsidhu85 May 3, 2022
e477707
Merge branch 'sample_details_refactor/_file_qc' into sample_details_r…
deepsidhu85 May 3, 2022
1b26fa5
Merged sample details refactor file qc and fixed merge conflicts
deepsidhu85 May 26, 2022
fba144d
Merge branch 'sample_details_refactor/_file_qc' into sample_details_r…
deepsidhu85 May 26, 2022
c7697ea
Merge branch 'sample_details_refactor/_file_qc' into sample_details_r…
deepsidhu85 May 30, 2022
b8501c7
Merge branch 'sample_details_refactor/_file_qc' into sample_details_r…
deepsidhu85 Jun 7, 2022
8bbd463
Merge branch 'sample_details_refactor/_file_qc' into sample_details_r…
deepsidhu85 Jun 9, 2022
f3a3054
Merged sample_details_refactor file qc branch and fixed merge conflicts.
deepsidhu85 Jun 23, 2022
e4a0b7e
Merge branch 'sample-details-refactor' into sample_details_refactor/_…
deepsidhu85 Jun 27, 2022
ab4aaa4
Moved changeset into correct directory
deepsidhu85 Jun 27, 2022
fc9e4cb
Removed duplicate const
deepsidhu85 Jun 27, 2022
bf35943
Updated translations and updated test
deepsidhu85 Jun 28, 2022
47c8693
Merge branch 'sample-details-refactor' into sample_details_refactor/_…
deepsidhu85 Jun 28, 2022
243401a
Added assertions for set as default / default tag
deepsidhu85 Jun 28, 2022
3dc4951
Uncommented assertions
deepsidhu85 Jun 29, 2022
164b657
Added translations
deepsidhu85 Jun 29, 2022
05bc71d
Merge branch 'sample-details-refactor' into sample_details_refactor/_…
deepsidhu85 Jul 4, 2022
aabc9d4
Added blue to css root and updated Default tags to use one of the blu…
deepsidhu85 Jul 4, 2022
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
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@

import ca.corefacility.bioinformatics.irida.model.IridaRepresentationModel;
import ca.corefacility.bioinformatics.irida.model.MutableIridaThing;
import ca.corefacility.bioinformatics.irida.model.assembly.GenomeAssembly;
import ca.corefacility.bioinformatics.irida.model.event.SampleAddedProjectEvent;
import ca.corefacility.bioinformatics.irida.model.joins.impl.ProjectSampleJoin;
import ca.corefacility.bioinformatics.irida.model.joins.impl.SampleGenomeAssemblyJoin;
Expand Down Expand Up @@ -149,6 +150,10 @@ public class Sample extends IridaRepresentationModel
@JoinColumn(name = "default_sequencing_object")
private SequencingObject defaultSequencingObject;

@OneToOne(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
@JoinColumn(name = "default_genome_assembly")
private GenomeAssembly defaultGenomeAssembly;

@OneToMany(fetch = FetchType.LAZY, cascade = CascadeType.REMOVE, mappedBy = "sample")
private List<ProjectSampleJoin> projects;

Expand Down Expand Up @@ -359,4 +364,10 @@ public SequencingObject getDefaultSequencingObject() {
public void setDefaultSequencingObject(SequencingObject sequencingObject) {
this.defaultSequencingObject = sequencingObject;
}

public GenomeAssembly getDefaultGenomeAssembly() { return defaultGenomeAssembly; }

public void setDefaultGenomeAssembly(GenomeAssembly genomeAssembly) {
this.defaultGenomeAssembly = genomeAssembly;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,26 @@ public ResponseEntity<AjaxResponse> updateDefaultSequencingObjectForSample(@Path
}
}

/**
* Update the default genome assembly for the sample
*
* @param id {@link Long} identifier for the sample
* @param genomeAssemblyId The genome assembly identifier
* @param locale {@link Locale} for the currently logged in user
* @return {@link ResponseEntity} explaining to the user the results of the update.
*/
@PutMapping(value = "/{id}/default-genome-assembly")
public ResponseEntity<AjaxResponse> updateDefaultGenomeAssemblyForSample(@PathVariable Long id,
@RequestParam Long genomeAssemblyId, Locale locale) {
try {
return ResponseEntity.ok(new AjaxSuccessResponse(
uiSampleService.updateDefaultGenomeAssemblyForSample(id, genomeAssemblyId, locale)));
} catch (EntityNotFoundException e) {
return ResponseEntity.status(HttpStatus.BAD_REQUEST)
.body(new AjaxErrorResponse(e.getMessage()));
}
}

/**
* Get analyses for sample
*
Expand Down

Large diffs are not rendered by default.

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<?xml version="1.0" encoding="UTF-8"?>
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog
http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.1.xsd">

<changeSet id="add-default-genome-assembly-to-sample" author="deep">
<addColumn tableName="sample">
<column name="default_genome_assembly" type="bigint(20)">
<constraints foreignKeyName="FK_SAMPLE_GENOME_ASSEMBLY"
referencedColumnNames="id" referencedTableName="genome_assembly" />
</column>
</addColumn>

<addColumn tableName="sample_AUD">
<column name="default_genome_assembly" type="bigint(20)" />
</addColumn>

</changeSet>
</databaseChangeLog>
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,5 @@
http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.1.xsd">
<include file="user-account-drop-phone-constraint.xml" relativeToChangelogFile="true" />
<include file="add-default-sequencing-object-to-sample.xml" relativeToChangelogFile="true" />
<include file="add-default-genome-assembly-to-sample.xml" relativeToChangelogFile="true" />
</databaseChangeLog>
3 changes: 3 additions & 0 deletions src/main/resources/i18n/messages.properties
Original file line number Diff line number Diff line change
Expand Up @@ -2273,13 +2273,16 @@ SampleFilesList.download=Download
SampleFilesList.removeGenomeAssembly=Are you sure you want to delete this genome assembly from the sample?
SampleFilesList.removeSequencingObject=Are you sure you want to delete this sequencing object from the sample?
SampleFilesList.tooltip.setAsDefault=Set this paired end sequencing object as the default for the sample. The sequencing object will be selected by default when running a pipeline.
SampleFilesList.tooltip.setAsDefaultAssembly=Set this genome assembly as the default for the sample. The genome assembly will be selected by default when running a pipeline which accepts assemblies.
SampleFilesList.tooltip.remove=This will remove this complete sequencing object (paired end, single end, fast5) from the sample.
SampleFilesList.default=Default
SampleFilesList.setAsDefault=Set as Default
SampleFilesList.defaultSelected=Default paired end sequencing object to be selected when running a pipeline
SampleFilesList.defaultSelectedAssembly=Default genome assembly to be selected when running a pipeline

server.SampleFilesList.successfully.set.default.seq.object=Successfully set default sequencing object for sample
server.SampleFilesList.unable.to.update.sample=Unable to update sample as it was not found
server.SampleFilesList.successfully.set.default.genome.assembly=Successfully set default genome assembly for sample

# ========================================================================================== #
# SAMPLE FILES CONCATENATE COMPONENT #
Expand Down
11 changes: 11 additions & 0 deletions src/main/webapp/resources/css/app.css
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,17 @@
--grey-11: #1f1f1f;
--grey-12: #141414;
--grey-13: #000000;

--blue-1: #e6f7ff;
--blue-2: #bae7ff;
--blue-3: #91d5ff;
--blue-4: #69c0ff;
--blue-5: #40a9ff;
--blue-6: #1890ff;
--blue-7: #096dd9;
--blue-8: #0050b3;
--blue-9: #003a8c;
--blue-10: #002766;
}

html,
Expand Down
8 changes: 8 additions & 0 deletions src/main/webapp/resources/js/apis/samples/samples.js
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,13 @@ export const sampleApi = createApi({
}),
invalidatesTags: ["SampleDetails"],
}),
updateDefaultSampleGenomeAssembly: build.mutation({
query: ({ sampleId, genomeAssemblyId }) => ({
url: `/${sampleId}/default-genome-assembly?genomeAssemblyId=${genomeAssemblyId}`,
method: "PUT",
}),
invalidatesTags: ["SampleDetails"],
}),
}),
});

Expand All @@ -116,6 +123,7 @@ export const {
useUpdateSampleMetadataMutation,
useRemoveSampleFilesMutation,
useConcatenateSequencingObjectsMutation,
useUpdateDefaultSampleGenomeAssemblyMutation,
useUpdateDefaultSampleSequencingObjectMutation,
} = sampleApi;

Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
import React from "react";
import { Button, notification, Popconfirm } from "antd";
import { useSelector } from "react-redux";
import { Button, notification, Popconfirm, Tag, Tooltip } from "antd";
import { useDispatch, useSelector } from "react-redux";

import { SequenceFileTypeRenderer } from "./SequenceFileTypeRenderer";
import { downloadGenomeAssemblyFile } from "../../../apis/samples/samples";
import { GenomeAssemblyListItem } from "../../sequence-files/GenomeAssemblyListItem";
import { DEFAULT_ACTION_WIDTH } from "../sampleFilesSlice";
import { useUpdateDefaultSampleGenomeAssemblyMutation } from "../../../apis/samples/samples";
import { setDefaultGenomeAssembly } from "../sampleSlice";

/**
* React component to display, remove, download genome assemblies
Expand All @@ -14,11 +16,14 @@ import { DEFAULT_ACTION_WIDTH } from "../sampleFilesSlice";
* @constructor
*/
export function GenomeAssemblyList({ removeSampleFiles = () => {} }) {
const [updateSampleDefaultGenomeAssembly] =
useUpdateDefaultSampleGenomeAssemblyMutation();
const { sample, modifiable: isModifiable } = useSelector(
(state) => state.sampleReducer
);
const { files } = useSelector((state) => state.sampleFilesReducer);
const ACTION_MARGIN_RIGHT = isModifiable ? 0 : 5;
const dispatch = useDispatch();

/*
Download genome assembly files
Expand All @@ -30,20 +35,86 @@ export function GenomeAssemblyList({ removeSampleFiles = () => {} }) {
downloadGenomeAssemblyFile({ sampleId, genomeAssemblyId });
};

/*
Set default genome assembly for sample to be used for analyses
*/
const updateDefaultGenomeAssembly = (genomeAssembly) => {
const { fileInfo: genomeAssemblyObj } = genomeAssembly;

updateSampleDefaultGenomeAssembly({
sampleId: sample.identifier,
genomeAssemblyId: genomeAssemblyObj.identifier,
})
.then(({ data }) => {
dispatch(setDefaultGenomeAssembly(genomeAssemblyObj));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do you need to store this as state? The point to using redux toolkit is that it handles the state and you just need to invalidate to update. It has it's own cache to handle this.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The default tag doesn't update in the UI without it. If you look in samples.js we invalidateTags: ["SampleDetails"]. However even with this it doesn't update the UI side even when a different assembly is updated. Same thing applies to the setting of a default sequencing object

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it is because the tag here is set up wrong.  If you look at the docs for invalidating tags, we do not actually set the tag type on the object itself. I think where we dropped the ball was, instead of:

providesTags: ["SampleDetails"],
providesTags: (result) =>  [{ type: 'SampleDetails', id: details.identifier }],

I have not played with it, but it seems more like what they are looking for and probably why we are not getting invalidation and refetching.

notification.success({ message: data.message });
})
.catch((error) => {
notification.error({ message: error });
});
};

/*
Get the actions required for a Genome Assembly
*/
const getActionsForGenomeAssembly = (genomeAssembly) => {
let actions = [
const getActionsForGenomeAssembly = (genomeAssembly, index) => {
const { fileInfo: genomeAssemblyObj } = genomeAssembly;
let actions = [];

if (isModifiable) {
if (
(sample.defaultGenomeAssembly !== null &&
genomeAssemblyObj.identifier ===
sample.defaultGenomeAssembly.identifier) ||
(sample.defaultGenomeAssembly === null && index === 0)
) {
actions.push(
<Tooltip
title={i18n("SampleFilesList.defaultSelectedAssembly")}
placement="top"
key={`default-tag-tooltip-ga-${genomeAssemblyObj.identifier}`}
>
<Tag
color={`var(--blue-6)`}
key={`default-tag-ga-${genomeAssemblyObj.identifier}`}
className="t-default-genome-assembly-tag"
>
{i18n("SampleFilesList.default")}
</Tag>
</Tooltip>
);
} else {
actions.push(
<Tooltip
title={i18n("SampleFilesList.tooltip.setAsDefaultAssembly")}
placement="top"
key={`set-default-tooltip-ga-${genomeAssemblyObj.identifier}`}
>
<Button
size="small"
key={`set-default-ga-${genomeAssemblyObj.identifier}`}
onClick={() => updateDefaultGenomeAssembly(genomeAssembly)}
type="link"
className="t-set-default-genome-assembly-button"
style={{ width: 100 }}
>
{i18n("SampleFilesList.setAsDefault")}
</Button>
</Tooltip>
);
}
}

actions.push(
<span
key={`${genomeAssembly.fileInfo.identifier}-file-size`}
key={`${genomeAssemblyObj.identifier}-file-size`}
className="t-file-size"
>
{genomeAssembly.firstFileSize}
</span>,
<Button
type="link"
key={`${genomeAssembly.fileInfo.identifier}-download-btn`}
key={`${genomeAssemblyObj.identifier}-download-btn`}
style={{
padding: 0,
width: DEFAULT_ACTION_WIDTH,
Expand All @@ -53,13 +124,13 @@ export function GenomeAssemblyList({ removeSampleFiles = () => {} }) {
onClick={() => {
downloadAssemblyFile({
sampleId: sample.identifier,
genomeAssemblyId: genomeAssembly.fileInfo.identifier,
genomeAssemblyId: genomeAssemblyObj.identifier,
});
}}
>
{i18n("SampleFilesList.download")}
</Button>,
];
</Button>
);

if (isModifiable) {
actions.push(
Expand All @@ -74,7 +145,7 @@ export function GenomeAssemblyList({ removeSampleFiles = () => {} }) {
}}
onConfirm={() => {
removeSampleFiles({
fileObjectId: genomeAssembly.fileInfo.identifier,
fileObjectId: genomeAssemblyObj.identifier,
type: "assembly",
});
}}
Expand All @@ -83,7 +154,7 @@ export function GenomeAssemblyList({ removeSampleFiles = () => {} }) {
type="link"
className="t-remove-file-btn"
style={{ padding: 0, width: DEFAULT_ACTION_WIDTH }}
key={`${genomeAssembly.fileInfo.identifier}-remove-btn`}
key={`${genomeAssemblyObj.identifier}-remove-btn`}
>
{i18n("SampleFilesList.remove")}
</Button>
Expand All @@ -96,11 +167,11 @@ export function GenomeAssemblyList({ removeSampleFiles = () => {} }) {

return (
<SequenceFileTypeRenderer title={i18n("SampleFiles.assemblies")}>
{files.assemblies.map((assembly) => (
{files.assemblies.map((assembly, index) => (
<GenomeAssemblyListItem
key={`assembly-${assembly.fileInfo.identifier}`}
genomeAssembly={assembly}
actions={getActionsForGenomeAssembly(assembly)}
actions={getActionsForGenomeAssembly(assembly, index)}
/>
))}
</SequenceFileTypeRenderer>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import React from "react";
import { notification, Space } from "antd";
import { useDispatch, useSelector } from "react-redux";
import { useRemoveSampleFilesMutation } from "../../../apis/samples/samples";
import { removeFileObjectFromSample } from "../sampleFilesSlice";
import { useDispatch, useSelector } from "react-redux";
import { setDefaultSequencingObject } from "../sampleSlice";
import { GenomeAssemblyList } from "./GenomeAssemblyList";
import { SequencingObjectList } from "./SequencingObjectList";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,6 @@ import { SequenceObjectListItem } from "../../sequence-files/SequenceObjectListI
import { primaryColour } from "../../../utilities/theme-utilities";
import { SPACE_XS } from "../../../styles/spacing";

const fileProcessTranslations = {
UNPROCESSED: i18n("SampleFilesList.fileProcessingState.UNPROCESSED"),
QUEUED: i18n("SampleFilesList.fileProcessingState.QUEUED"),
PROCESSING: i18n("SampleFilesList.fileProcessingState.PROCESSING"),
FINISHED: i18n("SampleFilesList.fileProcessingState.FINISHED"),
ERROR: i18n("SampleFilesList.fileProcessingState.ERROR"),
};

import {
addToConcatenateSelected,
DEFAULT_ACTION_WIDTH,
Expand All @@ -43,6 +35,14 @@ import {
useUpdateDefaultSampleSequencingObjectMutation,
} from "../../../apis/samples/samples";

const fileProcessTranslations = {
UNPROCESSED: i18n("SampleFilesList.fileProcessingState.UNPROCESSED"),
QUEUED: i18n("SampleFilesList.fileProcessingState.QUEUED"),
PROCESSING: i18n("SampleFilesList.fileProcessingState.PROCESSING"),
FINISHED: i18n("SampleFilesList.fileProcessingState.FINISHED"),
ERROR: i18n("SampleFilesList.fileProcessingState.ERROR"),
};

/**
* React component to display, remove, download sequencing objects
* @param {function} removeSampleFiles The function to remove sequencing objects
Expand Down Expand Up @@ -204,7 +204,7 @@ export function SequencingObjectList({ removeSampleFiles = () => {} }) {
key={`default-tag-tooltip-${obj.identifier}`}
>
<Tag
color="#108ee9"
color={`var(--blue-6)`}
key={`default-tag-${obj.identifier}`}
className="t-default-seq-obj-tag"
>
Expand Down
14 changes: 14 additions & 0 deletions src/main/webapp/resources/js/components/samples/sampleSlice.js
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,16 @@ export const setDefaultSequencingObject = createAction(
})
);

/**
* Action to set the default genome assembly for a sample
*/
export const setDefaultGenomeAssembly = createAction(
`sample/setDefaultGenomeAssembly`,
(genomeAssembly) => ({
payload: { genomeAssembly },
})
);

/**
* Action to update sample details
*/
Expand Down Expand Up @@ -142,6 +152,10 @@ const sampleSlice = createSlice({
state.sample.defaultSequencingObject = action.payload.sequencingObject;
});

builder.addCase(setDefaultGenomeAssembly, (state, action) => {
state.sample.defaultGenomeAssembly = action.payload.genomeAssembly;
});

builder.addCase(setProjectDetails, (state, action) => {
state.projectId = action.payload.projectId;
state.projectName = action.payload.projectName;
Expand Down
Loading