Skip to content

Commit 3b544ba

Browse files
Merge pull request #145 from qbicsoftware/release/1.0.0-beta.3
Release/1.0.0 beta.3
2 parents bfec817 + 4900667 commit 3b544ba

File tree

14 files changed

+267
-62
lines changed

14 files changed

+267
-62
lines changed

.qube.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ full_name: Jennifer Bödker
33
44
project_name: Sample Tracking Status Overview
55
project_short_description: Gives a visual overview of sample statuses
6-
version: 1.0.1-beta
6+
version: 1.0.0-beta.3
77
domain: portlet
88
language: groovy
99
project_slug: Sample_Tracking_Status_Overview

CHANGELOG.rst

+21
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,25 @@ Changelog
44

55
This project adheres to `Semantic Versioning <https://semver.org/>`_.
66

7+
1.0.0-beta.3 (2021-11-08)
8+
---------------------------
9+
10+
**Added**
11+
12+
* Introduce split panel for showing failing QC samples (`#140 <https://github.com/qbicsoftware/sample-tracking-status-overview/issues/140>`_)
13+
14+
* Minor refactoring of the page organisation (`#141 <https://github.com/qbicsoftware/sample-tracking-status-overview/pull/141>`_)
15+
16+
* Add filtering to the ProjectId and ProjectTitle Columns (`#142 <https://github.com/qbicsoftware/sample-tracking-status-overview/pull/142>`_)
17+
18+
**Fixed**
19+
20+
* More informative message after subscription change (`#144 <https://github.com/qbicsoftware/sample-tracking-status-overview/pull/141>`_)
21+
22+
**Dependencies**
23+
24+
**Deprecated**
25+
726
1.0.1-beta (2021-10-28)
827
---------------------------
928

@@ -27,6 +46,8 @@ This project adheres to `Semantic Versioning <https://semver.org/>`_.
2746

2847
* Unsubscribe from project (`#129 <https://github.com/qbicsoftware/sample-tracking-status-overview/issues/129>`_)
2948

49+
* Samples with failed QC are shown to the user directly after selecting a project (`#138 <https://github.com/qbicsoftware/sample-tracking-status-overview/pull/138>`_)
50+
3051
**Fixed**
3152

3253
* Show correct number of passing QC numbers (`#130 <https://github.com/qbicsoftware/sample-tracking-status-overview/pull/130>`_)

docs/conf.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -57,9 +57,9 @@
5757
# the built documents.
5858
#
5959
# The short X.Y version.
60-
version = '1.0.1-beta'
60+
version = '1.0.0-beta.3'
6161
# The full version, including alpha/beta/rc tags.
62-
release = '1.0.1-beta'
62+
release = '1.0.0-beta.3'
6363

6464
# The language for content autogenerated by Sphinx. Refer to documentation
6565
# for a list of supported languages.

pom.xml

+1-1
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
</modules>
1111
<artifactId>sample-tracking-status-overview</artifactId>
1212
<groupId>life.qbic</groupId>
13-
<version>1.0.1-beta</version> <!-- <<QUBE_FORCE_BUMP>> -->
13+
<version>1.0.0-beta.3</version> <!-- <<QUBE_FORCE_BUMP>> -->
1414
<name>Sample Tracking Status Overview</name>
1515
<url>http://github.com/qbicsoftware/sample-tracking-status-overview</url>
1616
<description>Gives a visual overview of sample statuses</description>

qube.cfg

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
[bumpversion]
2-
current_version = 1.0.1-beta
2+
current_version = 1.0.0-beta.3
33

44
[bumpversion_files_whitelisted]
55
dot_qube = .qube.yml

sample-tracking-status-overview-app/pom.xml

+2-2
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
<parent>
66
<artifactId>sample-tracking-status-overview</artifactId>
77
<groupId>life.qbic</groupId>
8-
<version>1.0.1-beta</version> <!-- <<QUBE_FORCE_BUMP>> -->
8+
<version>1.0.0-beta.3</version> <!-- <<QUBE_FORCE_BUMP>> -->
99
</parent>
1010
<modelVersion>4.0.0</modelVersion>
1111
<packaging>war</packaging>
@@ -16,7 +16,7 @@
1616
<dependency>
1717
<groupId>life.qbic</groupId>
1818
<artifactId>sample-tracking-status-overview-domain</artifactId>
19-
<version>1.0.1-beta</version> <!-- <<QUBE_FORCE_BUMP>> -->
19+
<version>1.0.0-beta.3</version> <!-- <<QUBE_FORCE_BUMP>> -->
2020
<scope>compile</scope>
2121
</dependency>
2222
<dependency>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
package life.qbic.portal.sampletracking.components
2+
3+
import com.vaadin.data.provider.ListDataProvider
4+
import com.vaadin.shared.ui.ValueChangeMode
5+
import com.vaadin.ui.Grid
6+
import com.vaadin.ui.TextField
7+
import com.vaadin.ui.components.grid.HeaderRow
8+
import com.vaadin.ui.themes.ValoTheme
9+
import org.apache.commons.lang3.StringUtils
10+
11+
/**
12+
* A helper class with static utility functions for Vaadin Grids.
13+
*
14+
* @since 1.0.0
15+
*/
16+
class GridUtils {
17+
18+
/**
19+
* Provides a filter field into a header row of a grid for a given column.
20+
*
21+
* The current implementation filters for content that contains the filter criteria in the
22+
* column values and ignores the case.
23+
*
24+
* @param dataProvider The grid's {@link ListDataProvider}
25+
* @param column The column to add the filter to
26+
* @param headerRow The{@link com.vaadin.ui.components.grid.HeaderRow} of the {@link Grid}, where the filter input field is added
27+
*/
28+
static <T> void setupColumnFilter(ListDataProvider<T> dataProvider,
29+
Grid.Column<T, String> column,
30+
HeaderRow headerRow) {
31+
TextField filterTextField = new TextField()
32+
filterTextField.addValueChangeListener(event -> {
33+
dataProvider.addFilter(element ->
34+
StringUtils.containsIgnoreCase(column.getValueProvider().apply(element), filterTextField.getValue())
35+
)
36+
})
37+
styleFilterTextField(filterTextField, column.getCaption())
38+
headerRow.getCell(column).setComponent(filterTextField)
39+
}
40+
41+
private static void styleFilterTextField(TextField filterTextField, String columnCaption) {
42+
filterTextField.setValueChangeMode(ValueChangeMode.EAGER)
43+
filterTextField.addStyleName(ValoTheme.TEXTFIELD_TINY)
44+
filterTextField.setPlaceholder("Filter by $columnCaption")
45+
filterTextField.setSizeFull()
46+
}
47+
48+
/**
49+
* Provides filter fields into the header row of the provided grid for multiple column identifiers
50+
*
51+
* The current implementation filters for content that contains the filter criteria in the
52+
* column values and ignores the case.
53+
*
54+
* @param grid The vaadin grid {@link Grid} for which the filter columns should be added
55+
* @param columnIdentifiers The column identifiers specifying into which column a filter should be added
56+
*/
57+
static <T> void setupFilters(Grid<T> grid, Collection<String> columnIdentifiers) {
58+
HeaderRow customerFilterRow
59+
if (grid.headerRowCount > 1) {
60+
customerFilterRow = grid.getHeaderRow(1)
61+
} else {
62+
customerFilterRow = grid.appendHeaderRow()
63+
}
64+
ListDataProvider<T> gridDataProvider = grid.getDataProvider() as ListDataProvider<T>
65+
columnIdentifiers.forEach { String columnId ->
66+
setupColumnFilter(gridDataProvider,
67+
grid.getColumn(columnId),
68+
customerFilterRow)
69+
}
70+
}
71+
}

sample-tracking-status-overview-app/src/main/groovy/life/qbic/portal/sampletracking/components/projectoverview/ProjectOverviewView.groovy

+74-46
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,21 @@
11
package life.qbic.portal.sampletracking.components.projectoverview
22

3-
43
import com.vaadin.data.provider.DataProvider
54
import com.vaadin.data.provider.ListDataProvider
65
import com.vaadin.event.selection.SingleSelectionEvent
76
import com.vaadin.icons.VaadinIcons
87
import com.vaadin.server.FileDownloader
98
import com.vaadin.server.StreamResource
109
import com.vaadin.shared.ui.ContentMode
10+
import com.vaadin.shared.ui.MarginInfo
1111
import com.vaadin.shared.ui.grid.HeightMode
1212
import com.vaadin.ui.*
1313
import com.vaadin.ui.Grid.Column
1414
import com.vaadin.ui.themes.ValoTheme
1515
import groovy.util.logging.Log4j2
1616
import life.qbic.portal.sampletracking.Constants
1717
import life.qbic.portal.sampletracking.communication.notification.NotificationService
18+
import life.qbic.portal.sampletracking.components.GridUtils
1819
import life.qbic.portal.sampletracking.components.projectoverview.download.DownloadProjectController
1920
import life.qbic.portal.sampletracking.components.projectoverview.samplelist.FailedQCSamplesView
2021
import life.qbic.portal.sampletracking.components.projectoverview.samplelist.ProjectOverviewController
@@ -32,7 +33,7 @@ import life.qbic.portal.sampletracking.components.projectoverview.subscribe.Subs
3233
*
3334
*/
3435
@Log4j2
35-
class ProjectOverviewView extends VerticalLayout{
36+
class ProjectOverviewView extends VerticalLayout {
3637

3738
private final ProjectOverviewViewModel viewModel
3839
private final DownloadProjectController downloadProjectController
@@ -42,6 +43,8 @@ class ProjectOverviewView extends VerticalLayout{
4243
private final ProjectOverviewController projectOverviewController
4344

4445
private Grid<ProjectSummary> projectGrid
46+
private HorizontalSplitPanel splitPanel
47+
private static final Collection<String> columnIdsWithFilters = ["ProjectCode", "ProjectTitle"]
4548

4649
final static int MAX_CODE_COLUMN_WIDTH = 400
4750
final static int MAX_STATUS_COLUMN_WIDTH = 200
@@ -61,12 +64,40 @@ class ProjectOverviewView extends VerticalLayout{
6164

6265
private void initLayout(){
6366
Label titleLabel = new Label("Project Overview")
64-
titleLabel.addStyleName(ValoTheme.LABEL_LARGE)
67+
titleLabel.addStyleName(ValoTheme.LABEL_HUGE)
68+
69+
Label spacerLabel = new Label("&nbsp;", ContentMode.HTML)
70+
6571
setupProjects()
66-
HorizontalLayout buttonBar = setupButtonLayout()
67-
failedQCSamplesView.setVisible(false)
72+
73+
HorizontalLayout projectButtonBar = setupButtonLayout()
74+
VerticalLayout projectLayout = new VerticalLayout(projectButtonBar,projectGrid)
75+
projectLayout.setMargin(false)
76+
77+
splitPanel = createSplitLayout(projectLayout,failedQCSamplesView)
78+
failedQCSamplesView.addVisibilityChangeListener({ splitPanel.splitPosition = it.newValue ? 65 : 100 })
79+
80+
connectFailedQcSamplesView()
6881
bindManifestToProjectSelection()
69-
this.addComponents(titleLabel,buttonBar, projectGrid, failedQCSamplesView)
82+
83+
this.addComponents(titleLabel, spacerLabel, splitPanel)
84+
}
85+
86+
private void connectFailedQcSamplesView() {
87+
FailedQCSamplesView samplesView = failedQCSamplesView
88+
showWhenFailingSamplesExist(samplesView)
89+
90+
viewModel.addPropertyChangeListener("selectedProject", {
91+
Optional<ProjectSummary> selectedProject = Optional.ofNullable(viewModel.selectedProject)
92+
selectedProject.ifPresent({
93+
if(failingSamplesExist()){
94+
loadFailedQcSamples(it)
95+
}
96+
})
97+
if (!selectedProject.isPresent()) {
98+
samplesView.reset()
99+
}
100+
})
70101
}
71102

72103
private HorizontalLayout setupButtonLayout() {
@@ -75,37 +106,17 @@ class ProjectOverviewView extends VerticalLayout{
75106

76107
Button postmanLink = setUpLinkButton()
77108
Button downloadManifestAction = setupDownloadButton()
78-
Button showDetails = setupShowDetails()
79109
CheckBox subscriptionCheckBox = setupSubscriptionCheckBox()
80-
buttonBar.addComponents(showDetails, downloadManifestAction, postmanLink, subscriptionCheckBox)
110+
111+
buttonBar.addComponents(downloadManifestAction, postmanLink, subscriptionCheckBox)
81112
buttonBar.setComponentAlignment(postmanLink, Alignment.MIDDLE_CENTER)
82113
buttonBar.setComponentAlignment(subscriptionCheckBox, Alignment.MIDDLE_CENTER)
83114
return buttonBar
84115
}
85116

86-
private Button setupShowDetails(){
87-
Button detailsButton = new Button("Show Details")
88-
detailsButton.setIcon(VaadinIcons.INFO_CIRCLE)
89-
detailsButton.setEnabled(false)
90-
91-
projectGrid.addSelectionListener({
92-
failedQCSamplesView.setVisible(false)
93-
94-
if(viewModel.selectedProject && viewModel.selectedProject.samplesQc.failingSamples > 0){
95-
detailsButton.setEnabled(true)
96-
}else{
97-
detailsButton.setEnabled(false)
98-
}
99-
})
100-
101-
detailsButton.addClickListener({
102-
if(viewModel.selectedProject){
103-
projectOverviewController.getFailedQcSamples(viewModel.selectedProject.code)
104-
failedQCSamplesView.setVisible(true)
105-
}
106-
})
107-
108-
return detailsButton
117+
private void loadFailedQcSamples(ProjectSummary projectSummary) {
118+
String code = projectSummary.getCode()
119+
projectOverviewController.getFailedQcSamples(code)
109120
}
110121

111122
private Button setUpLinkButton(){
@@ -145,7 +156,7 @@ class ProjectOverviewView extends VerticalLayout{
145156

146157
CheckBox subscriptionCheckBox = new CheckBox("Subscribe")
147158
subscriptionCheckBox.setVisible(false)
148-
enableWhenProjectIsSelected(subscriptionCheckBox)
159+
showWhenProjectIsSelected(subscriptionCheckBox)
149160
subscriptionCheckBox.setValue(false)
150161
viewModel.addPropertyChangeListener("selectedProject", {
151162
Optional<ProjectSummary> selectedProjectSummary = Optional.ofNullable(it.newValue as ProjectSummary)
@@ -201,7 +212,6 @@ class ProjectOverviewView extends VerticalLayout{
201212
selectedItem.ifPresent({
202213
selectProject(it)
203214
})
204-
205215
}
206216
})
207217
viewModel.updatedProjectsChannel.subscribe({updatedProjectCode ->
@@ -229,12 +239,18 @@ class ProjectOverviewView extends VerticalLayout{
229239
}
230240
}
231241

242+
private static HorizontalSplitPanel createSplitLayout(Layout leftComponent, VerticalLayout rightComponent){
243+
HorizontalSplitPanel splitPanel = new HorizontalSplitPanel(leftComponent,rightComponent)
244+
splitPanel.setSplitPosition(100)
245+
rightComponent.setMargin(new MarginInfo(false,false,false,true))
246+
247+
return splitPanel
248+
}
249+
232250
private void bindManifestToProjectSelection() {
233251
viewModel.addPropertyChangeListener("selectedProject", { tryToDownloadManifest() })
234252
}
235253

236-
237-
238254
private void clearProjectSelection() {
239255
viewModel.selectedProject = null
240256
}
@@ -285,9 +301,9 @@ class ProjectOverviewView extends VerticalLayout{
285301
if( viewModel.selectedProject ) {
286302
projectGrid.select(viewModel.selectedProject)
287303
}
304+
GridUtils.setupFilters(projectGrid, columnIdsWithFilters)
288305
}
289306

290-
291307
private void tryToDownloadManifest() {
292308
Optional<ProjectSummary> selectedSummary = Optional.empty()
293309
try {
@@ -323,14 +339,29 @@ class ProjectOverviewView extends VerticalLayout{
323339
return isAvailable
324340
}
325341

326-
private void enableWhenProjectIsSelected(CheckBox checkBox) {
327-
viewModel.addPropertyChangeListener("selectedProject") {
342+
private void showWhenProjectIsSelected(CheckBox checkBox) {
343+
viewModel.addPropertyChangeListener("selectedProject", {
328344
if(viewModel.selectedProject){
329345
checkBox.setVisible(true)
330346
}else{
331347
checkBox.setVisible(false)
332348
}
333-
}
349+
})
350+
}
351+
352+
private void showWhenFailingSamplesExist(Component component) {
353+
component.setVisible(failingSamplesExist())
354+
viewModel.addPropertyChangeListener("selectedProject", {
355+
component.setVisible(failingSamplesExist())
356+
})
357+
}
358+
359+
private boolean failingSamplesExist() {
360+
Optional<ProjectSummary> selectedProject = Optional.ofNullable(viewModel.selectedProject)
361+
boolean hasFailingSamples = selectedProject
362+
.map({ it.samplesQc.failingSamples > 0 })
363+
.orElse(false)
364+
return hasFailingSamples
334365
}
335366

336367
private void subscribeToProject(String projectCode) {
@@ -354,16 +385,13 @@ class ProjectOverviewView extends VerticalLayout{
354385
* @param sampleCount The total number of samples registered
355386
*/
356387
private static State determineCompleteness(SampleCount sampleCount) {
357-
if (sampleCount.failingSamples > 0){
388+
if (sampleCount.failingSamples > 0) {
358389
return State.FAILED
359-
}
360-
else if (sampleCount.passingSamples == sampleCount.totalSampleCount) {
390+
} else if (sampleCount.passingSamples == sampleCount.totalSampleCount) {
361391
return State.COMPLETED
362-
}
363-
else if (sampleCount.passingSamples < sampleCount.totalSampleCount) {
392+
} else if (sampleCount.passingSamples < sampleCount.totalSampleCount) {
364393
return State.IN_PROGRESS
365-
}
366-
else {
394+
} else {
367395
//unexpected!!
368396
throw new IllegalStateException("status count $sampleCount.passingSamples must not be greater total count $sampleCount.totalSampleCount")
369397
}

0 commit comments

Comments
 (0)