Skip to content

Commit 77f9077

Browse files
authored
Add TSV offer export (#989)
Provides the possibility to export all information of all offers in a tabular format for analysis
1 parent bccf399 commit 77f9077

File tree

6 files changed

+224
-26
lines changed

6 files changed

+224
-26
lines changed

offer-manager-app/src/main/groovy/life/qbic/portal/offermanager/DependencyManager.groovy

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,7 @@ class DependencyManager {
139139
private FetchOfferDataSource fetchOfferDataSource
140140
private OfferOverviewDataSource offerOverviewDataSource
141141
private ProjectAssistant projectAssistant
142+
private ExportOffersDataSource exportOffersDataSource
142143
// Implemented by life.qbic.portal.offermanager.dataresources.persons.PersonDbConnector
143144
private CreateAffiliationDataSource createAffiliationDataSource
144145
private CreatePersonDataSource createPersonDataSource
@@ -224,6 +225,7 @@ class DependencyManager {
224225
fetchOfferDataSource = offerDbConnector
225226
projectAssistant = offerDbConnector
226227
offerOverviewDataSource = offerDbConnector
228+
exportOffersDataSource = offerDbConnector
227229

228230
/* Currently life.qbic.portal.offermanager.dataresources.projects.ProjectDbConnector
229231
* cannot be decoupled by interfaces from
@@ -497,13 +499,17 @@ class DependencyManager {
497499
FetchOffer fetchOffer = new FetchOffer(fetchOfferDataSource, offerOverviewPresenter)
498500
OfferOverviewController offerOverviewController = new OfferOverviewController(fetchOffer)
499501

502+
ExportOffersDataSource exportOffersDataSource = this.exportOffersDataSource
503+
ExportAllOffers exportAllOffers = new ExportAllOffers(exportOffersDataSource)
504+
505+
500506
CreateProjectViewModel createProjectViewModel = new CreateProjectViewModel(projectSpaceResourcesService, projectResourcesService)
501507
CreateProjectPresenter createProjectPresenter = new CreateProjectPresenter(createProjectViewModel, sharedViewModel, projectCreatedEvent)
502508
CreateProject createProject = new CreateProject(createProjectPresenter, createProjectDataSource, createProjectSpaceDataSource)
503509
CreateProjectController createProjectController = new CreateProjectController(createProject)
504510
CreateProjectView createProjectView = new CreateProjectView(createProjectViewModel, createProjectController)
505511

506-
OfferOverviewView offerOverviewView = new OfferOverviewView(offerOverviewViewModel, offerOverviewController, createProjectView)
512+
OfferOverviewView offerOverviewView = new OfferOverviewView(offerOverviewViewModel, offerOverviewController, createProjectView, exportAllOffers)
507513
return offerOverviewView
508514
}
509515

Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
package life.qbic.portal.offermanager;
2+
3+
import com.vaadin.server.StreamResource.StreamSource;
4+
import java.io.ByteArrayInputStream;
5+
import java.io.InputStream;
6+
import java.util.ArrayList;
7+
import java.util.List;
8+
import java.util.function.Function;
9+
import life.qbic.business.offers.OfferV2;
10+
11+
/**
12+
* Provides export functionality for all offers
13+
*
14+
* @since 1.9.0
15+
*/
16+
public class ExportAllOffers implements StreamSource {
17+
18+
static final List<Column> tsvColumns = setColumns();
19+
20+
private final ExportOffersDataSource exportOffersDataSource;
21+
22+
public ExportAllOffers(ExportOffersDataSource exportOffersDataSource) {
23+
this.exportOffersDataSource = exportOffersDataSource;
24+
}
25+
26+
private static List<Column> setColumns() {
27+
ArrayList<Column> columns = new ArrayList<>();
28+
//identification of the offer
29+
columns.add(new Column("versionedOfferId", offerV2 -> offerV2.getIdentifier().toString()));
30+
columns.add(new Column("offerId", offerV2 -> offerV2.getIdentifier().toStringWithoutVersion()));
31+
columns.add(new Column("offerIdVersion",
32+
offerV2 -> String.valueOf(offerV2.getIdentifier().getVersion())));
33+
34+
// offer properties
35+
columns.add(new Column("projectTitle", OfferV2::getProjectTitle));
36+
columns.add(new Column("creationDate", offerV2 -> offerV2.getCreationDate().toString()));
37+
//customer
38+
columns.add(new Column("customerName",
39+
offerV2 -> offerV2.getCustomer().getFirstName() + " " + offerV2.getCustomer()
40+
.getLastName()));
41+
columns.add(new Column("customerEmail", offerV2 -> offerV2.getCustomer().getEmail()));
42+
columns.add(new Column("affiliationOrganisation",
43+
offerV2 -> offerV2.getSelectedCustomerAffiliation().getOrganization()));
44+
columns.add(new Column("affiliationAddressAddition",
45+
offerV2 -> offerV2.getSelectedCustomerAffiliation().getAddressAddition()));
46+
columns.add(new Column("affiliationCategory",
47+
offerV2 -> offerV2.getSelectedCustomerAffiliation().getCategory().getLabel()));
48+
columns.add(new Column("projectmanagerName",
49+
offerV2 -> offerV2.getProjectManager().getFirstName() + " " + offerV2.getProjectManager()
50+
.getLastName()));
51+
columns.add(
52+
new Column("projectmanagerEmail", offerV2 -> offerV2.getProjectManager().getEmail()));
53+
//pricing
54+
columns.add(
55+
new Column("dataGenerationSalePrice",
56+
offerV2 -> String.valueOf(offerV2.getDataGenerationSalePrice())));
57+
columns.add(new Column("dataAnalysisSalePrice",
58+
offerV2 -> String.valueOf(offerV2.getDataAnalysisSalePrice())));
59+
columns.add(new Column("ProjectManagementAndDataStorageSalePrice", offerV2 -> String.valueOf(
60+
offerV2.getDataManagementSalePrice())));
61+
columns.add(new Column("totalSalePrice", offerV2 -> String.valueOf(offerV2.getSalePrice())));
62+
// overheads
63+
columns.add(new Column("overheadRatio", offerV2 -> String.valueOf(
64+
offerV2.getOverheadRatio())));
65+
columns.add(
66+
new Column("dataGenerationOverhead",
67+
offerV2 -> String.valueOf(offerV2.getDataGenerationOverhead())));
68+
columns.add(new Column("dataAnalysisOverheads",
69+
offerV2 -> String.valueOf(offerV2.getDataAnalysisOverhead())));
70+
columns.add(new Column("ProjectManagementAndDataStorageOverheads", offerV2 -> String.valueOf(
71+
offerV2.getDataManagementOverhead())));
72+
columns.add(new Column("overheadTotal", offerV2 -> String.valueOf(offerV2.getOverhead())));
73+
//tax
74+
columns.add(new Column("priceBeforeTax", offerV2 -> String.valueOf(offerV2.getPriceBeforeTax())));
75+
columns.add(new Column("tax", offerV2 -> String.valueOf(offerV2.getTaxAmount())));
76+
columns.add(new Column("priceAfterTax",
77+
offerV2 -> String.valueOf(offerV2.getPriceAfterTax())));
78+
return columns;
79+
}
80+
public InputStream exportOffersToTsv() {
81+
StringBuilder fileContent = new StringBuilder();
82+
83+
for (int i = 0; i < tsvColumns.size(); i++) {
84+
String columnName = tsvColumns.get(i).getName();
85+
if (i + 1 < tsvColumns.size()) {
86+
columnName += "\t";
87+
}
88+
fileContent.append(columnName);
89+
}
90+
fileContent.append("\n");
91+
List<OfferV2> allOffers = exportOffersDataSource.findAllOffers();
92+
allOffers.stream()
93+
.map(this::toTsvLine)
94+
.forEach(fileContent::append);
95+
System.out.println(fileContent);
96+
return new ByteArrayInputStream(fileContent.toString().getBytes());
97+
}
98+
99+
private String toTsvLine(OfferV2 offerV2) {
100+
StringBuilder line = new StringBuilder();
101+
for (int i = 0; i < tsvColumns.size(); i++) {
102+
Column column = tsvColumns.get(i);
103+
String value = column.getValue(offerV2);
104+
line.append(value);
105+
if (i + 1 < tsvColumns.size()) {
106+
line.append("\t");
107+
}
108+
}
109+
line.append("\n");
110+
return line.toString();
111+
}
112+
113+
private static final class Column {
114+
115+
private final String name;
116+
private final Function<OfferV2, String> valueConverter;
117+
118+
private Column(String name, Function<OfferV2, String> valueConverter) {
119+
this.name = name;
120+
this.valueConverter = valueConverter;
121+
}
122+
123+
public String getName() {
124+
return name;
125+
}
126+
127+
public String getValue(OfferV2 offerV2) {
128+
return valueConverter.apply(offerV2);
129+
}
130+
}
131+
132+
@Override
133+
public InputStream getStream() {
134+
return exportOffersToTsv();
135+
}
136+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
package life.qbic.portal.offermanager;
2+
3+
import java.util.List;
4+
import life.qbic.business.offers.OfferV2;
5+
6+
/**
7+
* Datasource for exporting offers
8+
*/
9+
public interface ExportOffersDataSource {
10+
11+
/**
12+
*
13+
* @return a list containing all offers
14+
*/
15+
List<OfferV2> findAllOffers();
16+
}

offer-manager-app/src/main/groovy/life/qbic/portal/offermanager/components/offer/overview/OfferOverviewView.groovy

Lines changed: 47 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,16 @@ import com.vaadin.ui.themes.ValoTheme
1616
import groovy.util.logging.Log4j2
1717
import life.qbic.business.offers.Currency
1818
import life.qbic.business.offers.identifier.OfferIdFormatter
19+
import life.qbic.portal.offermanager.ExportAllOffers
1920
import life.qbic.portal.offermanager.OfferFileNameFormatter
2021
import life.qbic.portal.offermanager.components.GridUtils
2122
import life.qbic.portal.offermanager.components.offer.overview.projectcreation.CreateProjectView
2223
import life.qbic.portal.offermanager.components.project.ProjectIdContainsString
2324
import life.qbic.portal.offermanager.dataresources.offers.OfferOverview
2425

26+
import java.time.LocalDate
27+
import java.util.function.Supplier
28+
2529
/**
2630
* A basic offer overview user interface.
2731
*
@@ -41,13 +45,13 @@ class OfferOverviewView extends VerticalLayout implements Observer {
4145

4246
final private Grid<OfferOverview> overviewVersionsGrid
4347

44-
final private Button downloadBtn
48+
final private Button downloadOfferBtn
4549

4650
final Button updateOfferBtn
4751

4852
final private ProgressBar downloadSpinner
4953

50-
private FileDownloader fileDownloader
54+
private FileDownloader offerFileDownloader
5155

5256
private CreateProjectView createProjectView
5357

@@ -59,14 +63,18 @@ class OfferOverviewView extends VerticalLayout implements Observer {
5963

6064
private Button toggleVersions
6165

66+
private final Button exportAllOffersToTsvButton
67+
68+
6269
OfferOverviewView(OfferOverviewModel model,
6370
OfferOverviewController offerOverviewController,
64-
CreateProjectView createProjectView) {
71+
CreateProjectView createProjectView,
72+
ExportAllOffers exportAllOffers) {
6573
this.model = model
6674
this.offerOverviewController = offerOverviewController
6775
this.overviewGrid = new Grid<>()
6876
this.overviewVersionsGrid = new Grid<>()
69-
this.downloadBtn = new Button("Download Offer", VaadinIcons.DOWNLOAD)
77+
this.downloadOfferBtn = new Button("Download Offer", VaadinIcons.DOWNLOAD)
7078
this.updateOfferBtn = new Button("Update Offer", VaadinIcons.EDIT)
7179
this.createProjectButton = new Button("Create Project", VaadinIcons.PLUS_CIRCLE)
7280
this.toggleOverview = new Button("Overview")
@@ -76,6 +84,9 @@ class OfferOverviewView extends VerticalLayout implements Observer {
7684
// Register this view to be notified on updates in the model
7785
this.model.addObserver(this)
7886

87+
this.exportAllOffersToTsvButton = new Button("Export to TSV", VaadinIcons.DOWNLOAD)
88+
downloadStreamWhenClickingButton(exportAllOffersToTsvButton, exportAllOffers::exportOffersToTsv)
89+
7990
initLayout()
8091

8192
configureOverviewGrid()
@@ -90,7 +101,7 @@ class OfferOverviewView extends VerticalLayout implements Observer {
90101
this.toggleVersions.setEnabled(false)
91102
this.toggleOverview.setEnabled(false)
92103

93-
this.toggleVersions.addClickListener( {
104+
this.toggleVersions.addClickListener({
94105
overviewGrid.setVisible(false)
95106
overviewVersionsGrid.setVisible(true)
96107
toggleVersions.setEnabled(false)
@@ -146,9 +157,12 @@ class OfferOverviewView extends VerticalLayout implements Observer {
146157
right component will be the offer download button.
147158
*/
148159
final HorizontalLayout activityContainer = new HorizontalLayout()
149-
downloadBtn.setStyleName(ValoTheme.BUTTON_LARGE)
150-
downloadBtn.setEnabled(false)
151-
downloadBtn.setDescription("Download offer")
160+
exportAllOffersToTsvButton.setStyleName(ValoTheme.BUTTON_LARGE)
161+
exportAllOffersToTsvButton.setEnabled(true)
162+
exportAllOffersToTsvButton.setDescription("Download Offers as TSV")
163+
downloadOfferBtn.setStyleName(ValoTheme.BUTTON_LARGE)
164+
downloadOfferBtn.setEnabled(false)
165+
downloadOfferBtn.setDescription("Download offer")
152166
updateOfferBtn.setStyleName(ValoTheme.BUTTON_LARGE)
153167
updateOfferBtn.setEnabled(false)
154168
updateOfferBtn.setDescription("Update offer")
@@ -166,15 +180,16 @@ class OfferOverviewView extends VerticalLayout implements Observer {
166180

167181
// Add a button to create a project from an offer
168182
activityContainer.addComponents(
169-
downloadBtn,
183+
downloadOfferBtn,
170184
updateOfferBtn,
171185
createProjectButton,
186+
exportAllOffersToTsvButton,
172187
downloadSpinner)
173188

174189
activityContainer.setMargin(false)
175190
activityContainer.setComponentAlignment(downloadSpinner, Alignment.MIDDLE_CENTER)
176191

177-
HorizontalLayout wrapperLayout = new HorizontalLayout(activityContainer,toggleLayout)
192+
HorizontalLayout wrapperLayout = new HorizontalLayout(activityContainer, toggleLayout)
178193

179194
wrapperLayout.setComponentAlignment(toggleLayout, Alignment.MIDDLE_RIGHT)
180195
wrapperLayout.setWidthFull()
@@ -227,7 +242,7 @@ class OfferOverviewView extends VerticalLayout implements Observer {
227242
grid.setWidthFull()
228243
}
229244

230-
private static void setupFilters(ListDataProvider<OfferOverview> offerOverviewDataProvider, Grid<? extends OfferOverview> grid) {
245+
private static void setupFilters(ListDataProvider<OfferOverview> offerOverviewDataProvider, Grid<? extends OfferOverview> grid) {
231246
HeaderRow headerFilterRow = grid.appendHeaderRow()
232247

233248
GridUtils.setupColumnFilter(offerOverviewDataProvider,
@@ -275,9 +290,9 @@ class OfferOverviewView extends VerticalLayout implements Observer {
275290
}
276291

277292
private void setupGridListeners() {
278-
overviewGrid.addSelectionListener({ selection ->handleSelection(selection)}
293+
overviewGrid.addSelectionListener({ selection -> handleSelection(selection) }
279294
)
280-
overviewVersionsGrid.addSelectionListener({ selection ->handleSelection(selection)}
295+
overviewVersionsGrid.addSelectionListener({ selection -> handleSelection(selection) }
281296
)
282297
}
283298

@@ -290,7 +305,7 @@ class OfferOverviewView extends VerticalLayout implements Observer {
290305

291306
private void deselectOfferOverview() {
292307
updateOfferBtn.setEnabled(false)
293-
downloadBtn.setEnabled(false)
308+
downloadOfferBtn.setEnabled(false)
294309
createProjectButton.setEnabled(false)
295310
toggleVersions.setEnabled(false)
296311
}
@@ -301,7 +316,7 @@ class OfferOverviewView extends VerticalLayout implements Observer {
301316
UI.getCurrent().setPollInterval(50)
302317
downloadSpinner.setVisible(true)
303318
new LoadOfferInfoThread(UI.getCurrent(), overview).start()
304-
downloadBtn.setEnabled(true)
319+
downloadOfferBtn.setEnabled(true)
305320
updateOfferBtn.setEnabled(true)
306321
toggleVersions.setEnabled(true)
307322
checkProjectCreationAllowed(overview)
@@ -315,21 +330,29 @@ class OfferOverviewView extends VerticalLayout implements Observer {
315330
}
316331
}
317332

318-
private void createResourceForDownload() {
319-
removeExistingResources()
333+
private void createResourceForOfferPdfDownload() {
334+
removeExistingResourcesForOfferPdfDownload()
320335

321336
StreamResource offerResource =
322337
new StreamResource((StreamResource.StreamSource res) -> {
323338
return model.getOfferAsPdf()
324339
}, OfferFileNameFormatter.getFileNameForOffer(model.getSelectedOffer()))
325-
fileDownloader = new FileDownloader(offerResource)
326-
fileDownloader.extend(downloadBtn)
340+
offerFileDownloader = new FileDownloader(offerResource)
341+
offerFileDownloader.extend(downloadOfferBtn)
342+
}
343+
344+
private static void downloadStreamWhenClickingButton(AbstractComponent button, Supplier<InputStream> inputStreamSupplier) {
345+
StreamResource offerTsvResource = new StreamResource((resource) -> inputStreamSupplier.get(),
346+
LocalDate.now().toString() + ".offer-export" + ".tsv")
347+
// avoid browser caching file by name
348+
offerTsvResource.setCacheTime(0)
349+
new FileDownloader(offerTsvResource).extend(button)
327350
}
328351

329-
private void removeExistingResources() {
330-
Optional.ofNullable(fileDownloader).ifPresent({
331-
if (downloadBtn.extensions.contains(fileDownloader)) {
332-
downloadBtn.removeExtension(fileDownloader)
352+
private void removeExistingResourcesForOfferPdfDownload() {
353+
Optional.ofNullable(offerFileDownloader).ifPresent({
354+
if (downloadOfferBtn.extensions.contains(offerFileDownloader)) {
355+
downloadOfferBtn.removeExtension(offerFileDownloader)
333356
}
334357
})
335358
}
@@ -362,7 +385,7 @@ class OfferOverviewView extends VerticalLayout implements Observer {
362385
selectedOffer = overviewGrid.getSelectionModel().getFirstSelectedItem()
363386
})
364387
offerOverviewController.fetchOffer(offerOverview.offerId)
365-
createResourceForDownload()
388+
createResourceForOfferPdfDownload()
366389

367390
ui.access(() -> {
368391
downloadSpinner.setVisible(false)

0 commit comments

Comments
 (0)