Skip to content

Commit c39e17f

Browse files
authored
Refactor TabbedReport to allow a distributed provider model (#49)
* Refactor TabbedReport to allow a distributed provider model * Allow admin to turn tabbed report filter types on/off
1 parent 2e96c56 commit c39e17f

File tree

11 files changed

+263
-71
lines changed

11 files changed

+263
-71
lines changed

laboratory/api-src/org/labkey/api/laboratory/LaboratoryService.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,8 @@ static public void setInstance(LaboratoryService instance)
128128

129129
abstract public void registerTabbedReportFilterProvider(TabbedReportFilterProvider provider);
130130

131+
abstract public List<TabbedReportFilterProvider> getTabbedReportFilterProviderProviders(final Container c, final User u);
132+
131133
public static enum NavItemCategory
132134
{
133135
samples(),

laboratory/api-src/org/labkey/api/laboratory/QueryTabbedReportItem.java

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -88,14 +88,13 @@ public JSONObject toJSON(Container c, User u)
8888
return null;
8989
}
9090

91-
inferColumnsFromTable(ti);
91+
inferColumnsFromTable(ti, c, u);
9292
JSONObject json = super.toJSON(c, u);
9393

9494
json.put("schemaName", getSchemaName());
9595
json.put("queryName", getQueryName());
9696
String viewName = getDefaultViewName(c, getOwnerKey());
9797

98-
// TODO: should we always override here?
9998
if (getViewName() != null)
10099
{
101100
viewName = getViewName();

laboratory/api-src/org/labkey/api/laboratory/TabbedReportItem.java

Lines changed: 15 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -17,16 +17,16 @@
1717

1818
import org.apache.logging.log4j.LogManager;
1919
import org.apache.logging.log4j.Logger;
20-
import org.json.JSONArray;
2120
import org.json.JSONObject;
2221
import org.labkey.api.data.ColumnInfo;
2322
import org.labkey.api.data.Container;
2423
import org.labkey.api.data.PropertyManager;
2524
import org.labkey.api.data.TableInfo;
25+
import org.labkey.api.laboratory.query.TabbedReportFilterProvider;
2626
import org.labkey.api.query.FieldKey;
2727
import org.labkey.api.security.User;
28-
import org.labkey.api.util.PageFlowUtil;
2928

29+
import java.util.HashMap;
3030
import java.util.Map;
3131

3232
/**
@@ -43,10 +43,11 @@ public class TabbedReportItem extends AbstractNavItem
4343

4444
protected FieldKey _subjectIdFieldKey = null;
4545
protected FieldKey _sampleDateFieldKey = null;
46-
protected FieldKey _overlappingProjectsFieldKey = null;
47-
protected FieldKey _allProjectsFieldKey = null;
46+
private final Map<String, FieldKey> _additionalKeys = new HashMap<>();
4847

4948
public static final String OVERRIDES_PROP_KEY = "laboratory.tabItemOverride";
49+
public static final String FILTER_PROP_KEY = "laboratory.tabItemFilterOverride";
50+
5051
protected static final Logger _log = LogManager.getLogger(TabbedReportItem.class);
5152

5253
public TabbedReportItem(DataProvider provider, String name, String label, String reportCategory)
@@ -94,24 +95,18 @@ public JSONObject toJSON(Container c, User u)
9495
if (_sampleDateFieldKey != null)
9596
json.put("dateFieldName", _sampleDateFieldKey);
9697

97-
if (_overlappingProjectsFieldKey != null)
98-
{
99-
json.put("overlappingProjectsFieldName", _overlappingProjectsFieldKey.toString());
100-
json.put("overlappingProjectsFieldKeyArray", new JSONArray(_overlappingProjectsFieldKey.getParts()));
101-
}
102-
103-
if (_allProjectsFieldKey != null)
104-
{
105-
json.put("allProjectsFieldName", _allProjectsFieldKey.toString());
106-
json.put("allProjectsFieldKeyArray", new JSONArray(_allProjectsFieldKey.getParts()));
107-
}
98+
JSONObject keys = new JSONObject();
99+
_additionalKeys.forEach((name, fk) -> {
100+
keys.put(name, fk.toString());
101+
});
102+
json.put("additionalFieldKeys", keys);
108103

109104
json.put("reportType", getReportType());
110105

111106
return json;
112107
}
113108

114-
protected void inferColumnsFromTable(TableInfo ti)
109+
protected void inferColumnsFromTable(TableInfo ti, Container c, User u)
115110
{
116111
for (ColumnInfo ci : ti.getColumns())
117112
{
@@ -125,17 +120,9 @@ else if (_sampleDateFieldKey == null && LaboratoryService.SAMPLEDATE_CONCEPT_URI
125120
}
126121
}
127122

128-
if (_overlappingProjectsFieldKey == null || _allProjectsFieldKey == null)
123+
for (TabbedReportFilterProvider p : LaboratoryService.get().getTabbedReportFilterProviderProviders(c, u))
129124
{
130-
FieldKey overlapKey = FieldKey.fromString("overlappingProjectsPivot");
131-
FieldKey allKey = FieldKey.fromString("allProjectsPivot");
132-
133-
Map<FieldKey, ColumnInfo> colMap = _queryCache.getColumns(ti, PageFlowUtil.set(overlapKey, allKey));
134-
if (_overlappingProjectsFieldKey == null && colMap.containsKey(overlapKey))
135-
_overlappingProjectsFieldKey = colMap.get(overlapKey).getFieldKey();
136-
137-
if (_allProjectsFieldKey == null && colMap.containsKey(allKey))
138-
_allProjectsFieldKey = colMap.get(allKey).getFieldKey();
125+
_additionalKeys.putAll(p.getAdditionalFieldKeys(ti, this, _additionalKeys));
139126
}
140127
}
141128

@@ -169,24 +156,9 @@ public void setSampleDateFieldKey(FieldKey sampleDateFieldKey)
169156
_sampleDateFieldKey = sampleDateFieldKey;
170157
}
171158

172-
public FieldKey getOverlappingProjectsFieldKey()
173-
{
174-
return _overlappingProjectsFieldKey;
175-
}
176-
177-
public void setOverlappingProjectsFieldKey(FieldKey overlappingProjectsFieldKey)
178-
{
179-
_overlappingProjectsFieldKey = overlappingProjectsFieldKey;
180-
}
181-
182-
public FieldKey getAllProjectsFieldKey()
183-
{
184-
return _allProjectsFieldKey;
185-
}
186-
187-
public void setAllProjectsFieldKey(FieldKey allProjectsFieldKey)
159+
public void setKeyOverride(String name, FieldKey key)
188160
{
189-
_allProjectsFieldKey = allProjectsFieldKey;
161+
_additionalKeys.put(name, key);
190162
}
191163

192164
public void setVisible(boolean visible)
Lines changed: 54 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,30 @@
11
package org.labkey.api.laboratory.query;
22

3+
import org.jetbrains.annotations.NotNull;
4+
import org.json.JSONObject;
5+
import org.labkey.api.collections.CaseInsensitiveHashMap;
36
import org.labkey.api.data.Container;
7+
import org.labkey.api.data.ContainerType;
8+
import org.labkey.api.data.PropertyManager;
9+
import org.labkey.api.data.TableInfo;
10+
import org.labkey.api.laboratory.NavItem;
11+
import org.labkey.api.laboratory.TabbedReportItem;
12+
import org.labkey.api.module.Module;
13+
import org.labkey.api.query.FieldKey;
414
import org.labkey.api.security.User;
515
import org.labkey.api.view.template.ClientDependency;
616

717
import java.util.Collection;
18+
import java.util.Map;
819

920
public interface TabbedReportFilterProvider
1021
{
11-
boolean isAvailable(Container c, User u);
22+
default boolean isAvailable(Container c, User u)
23+
{
24+
return c.getActiveModules().contains(getOwningModule());
25+
}
26+
27+
Module getOwningModule();
1228

1329
Collection<ClientDependency> getClientDependencies();
1430

@@ -17,4 +33,41 @@ public interface TabbedReportFilterProvider
1733
String getLabel();
1834

1935
String getInputValue();
36+
37+
default JSONObject toJSON(Container c, User u)
38+
{
39+
JSONObject ret = new JSONObject();
40+
ret.put("xtype", getXType());
41+
ret.put("label", getLabel());
42+
ret.put("inputValue", getInputValue());
43+
ret.put("isAvailable", isAvailable(c, u));
44+
ret.put("isVisible", isVisible(c, u));
45+
ret.put("key", getPropertyManagerKey());
46+
47+
return ret;
48+
}
49+
50+
default boolean isVisible(Container c, User u)
51+
{
52+
Container targetContainer = c.getContainerFor(ContainerType.DataType.navVisibility);
53+
if (getOwningModule() != null)
54+
{
55+
if (!targetContainer.getActiveModules().contains(getOwningModule()))
56+
return false;
57+
}
58+
59+
Map<String, String> map = new CaseInsensitiveHashMap<>(PropertyManager.getProperties(targetContainer, NavItem.PROPERTY_CATEGORY));
60+
if (map.containsKey(getPropertyManagerKey()))
61+
return Boolean.parseBoolean(map.get(getPropertyManagerKey()));
62+
63+
return true;
64+
}
65+
66+
default String getPropertyManagerKey()
67+
{
68+
return "tabReportFilterProvider||" + getClass().getSimpleName() + "||" + getLabel();
69+
}
70+
71+
@NotNull
72+
Map<String, FieldKey> getAdditionalFieldKeys(TableInfo ti, TabbedReportItem tri, Map<String, FieldKey> overrides);
2073
}

laboratory/resources/web/laboratory/panel/ItemVisibilityPanel.js

Lines changed: 42 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -50,16 +50,55 @@ Ext4.define('Laboratory.panel.ItemVisibilityPanel', {
5050
this.results = results;
5151

5252
for (var i in Laboratory.ITEM_CATEGORY){
53-
if (Laboratory.ITEM_CATEGORY[i].name == Laboratory.ITEM_CATEGORY.settings.name)
53+
if (Laboratory.ITEM_CATEGORY[i].name === Laboratory.ITEM_CATEGORY.settings.name)
5454
continue;
5555

5656
this.renderSection(Laboratory.ITEM_CATEGORY[i].name, Laboratory.ITEM_CATEGORY[i].label);
5757
}
58+
59+
if (this.results.tabbedReportFilterProviderProviders) {
60+
this.renderTabbedReportFilterProviderProviders(this.results.tabbedReportFilterProviderProviders)
61+
}
62+
},
63+
64+
renderTabbedReportFilterProviderProviders: function(items) {
65+
this.remove(this.down('#loading'));
66+
67+
var cfg = {
68+
xtype: 'container',
69+
border: false,
70+
defaults: {
71+
border: false
72+
},
73+
style: 'margin-bottom: 20px;',
74+
itemCategory: 'tabbedReportFilterTypes',
75+
items: [{
76+
html: '<b>Tabbed Report Filter Types</b>',
77+
style: 'padding-bottom: 5px;'
78+
}]
79+
}
80+
81+
var sectionItems = [];
82+
Ext4.each(items, function(item){
83+
sectionItems.push({
84+
xtype: 'checkbox',
85+
width: 800,
86+
style: 'margin-left: 5px;',
87+
navItem: item,
88+
boxLabel: item.label,
89+
checked: item.isVisible,
90+
itemCategory: 'tabbedReportFilterTypes'
91+
});
92+
}, this);
93+
94+
sectionItems = LDK.Utils.sortByProperty(sectionItems, 'boxLabel');
95+
cfg.items = cfg.items.concat(sectionItems);
96+
this.add(cfg);
5897
},
5998

6099
renderSection: function(name, label){
61100
var items = this.results[name];
62-
var showReportCategory = name == 'tabbedReports';
101+
var showReportCategory = name === 'tabbedReports';
63102

64103
this.remove(this.down('#loading'));
65104

@@ -92,7 +131,7 @@ Ext4.define('Laboratory.panel.ItemVisibilityPanel', {
92131
//if items are marked as children of this item, toggle their visibility.
93132
//this is primarily used for reports
94133
field.up('form').getForm().getFields().each(function(item){
95-
if (item.navItem && item.navItem.ownerKey == field.navItem.key){
134+
if (item.navItem && item.navItem.ownerKey === field.navItem.key){
96135
item.setValue(val);
97136
}
98137
}, this);

laboratory/resources/web/laboratory/panel/ProjectFilterType.js

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -83,10 +83,10 @@ Ext4.define('Laboratory.panel.ProjectFilterType', {
8383

8484
var filters = this.getFilters();
8585
var report = tab.report;
86-
var projectFieldName = (filters.projectFilterMode === 'overlappingProjects') ? report.overlappingProjectsFieldName : report.allProjectsFieldName;
86+
var projectFieldName = (filters.projectFilterMode === 'overlappingProjects') ? report.additionalFieldKeys?.overlappingProjectsFieldKey : report.additionalFieldKeys?.allProjectsFieldKey;
8787
if (!projectFieldName){
88-
if (filters.projectFilterMode === 'overlappingProjects' && !report.overlappingProjectsFieldName){
89-
projectFieldName = report.allProjectsFieldName;
88+
if (filters.projectFilterMode === 'overlappingProjects' && !report.additionalFieldKeys?.overlappingProjectsFieldKey){
89+
projectFieldName = report.additionalFieldKeys?.allProjectsFieldKey;
9090

9191
if (projectFieldName)
9292
Ext4.Msg.alert('Warning', 'This reports supports project filtering, but cannot filter by overlapping projects, since it lacks a properly configured date field. All animals assigned to the project will be shown');
@@ -125,7 +125,7 @@ Ext4.define('Laboratory.panel.ProjectFilterType', {
125125
},
126126

127127
validateReportForFilterType: function(report){
128-
if (!report.allProjectsFieldName){
128+
if (!report.additionalFieldKeys?.allProjectsFieldKey){
129129
return 'This report cannot be used with the selected filter type, because the report does not contain a field with project information';
130130
}
131131

laboratory/src/org/labkey/laboratory/LaboratoryController.java

Lines changed: 30 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@
5959
import org.labkey.api.laboratory.assay.AssayImportMethod;
6060
import org.labkey.api.laboratory.assay.AssayParser;
6161
import org.labkey.api.laboratory.query.ContainerIncrementingTable;
62+
import org.labkey.api.laboratory.query.TabbedReportFilterProvider;
6263
import org.labkey.api.laboratory.security.LaboratoryAdminPermission;
6364
import org.labkey.api.module.Module;
6465
import org.labkey.api.module.ModuleHtmlView;
@@ -1567,22 +1568,29 @@ public ApiResponse execute(JsonDataForm form, BindException errors)
15671568
for (String key : json.keySet())
15681569
{
15691570
String providerName = AbstractNavItem.inferDataProviderNameFromKey(key);
1570-
DataProvider provider = LaboratoryService.get().getDataProvider(providerName);
1571-
1572-
//for some types, no DataProvider, was explicitly registered, such as many assays
1573-
//in these cases we cannot infer the owning module.
1574-
if (provider != null && provider.getOwningModule() != null)
1571+
if ("tabReportFilterProvider".equals(providerName))
1572+
{
1573+
// Ignore
1574+
}
1575+
else
15751576
{
1576-
if (!activeModules.contains(provider.getOwningModule()))
1577+
DataProvider provider = LaboratoryService.get().getDataProvider(providerName);
1578+
1579+
//for some types, no DataProvider, was explicitly registered, such as many assays
1580+
//in these cases we cannot infer the owning module.
1581+
if (provider != null && provider.getOwningModule() != null)
15771582
{
1578-
toActivate.add(provider.getOwningModule());
1583+
if (!activeModules.contains(provider.getOwningModule()))
1584+
{
1585+
toActivate.add(provider.getOwningModule());
1586+
}
15791587
}
15801588
}
15811589

15821590
map.put(key, json.get(key) == null ? null : String.valueOf(json.get(key)));
15831591
}
15841592

1585-
if (toActivate.size() > 0)
1593+
if (!toActivate.isEmpty())
15861594
{
15871595
toActivate.addAll(activeModules);
15881596
getContainer().setActiveModules(toActivate);
@@ -1819,6 +1827,16 @@ public ApiResponse execute(GetDataItemsForm form, BindException errors)
18191827
results.put(LaboratoryService.NavItemCategory.misc.name(), json);
18201828
}
18211829

1830+
List<JSONObject> json = new ArrayList<>();
1831+
for (TabbedReportFilterProvider item : LaboratoryService.get().getTabbedReportFilterProviderProviders(getContainer(), getUser()))
1832+
{
1833+
if (form.isIncludeAll() || item.isAvailable(getContainer(), getUser()))
1834+
{
1835+
json.add(item.toJSON(getContainer(), getUser()));
1836+
}
1837+
}
1838+
results.put("tabbedReportFilterProviderProviders", json);
1839+
18221840
results.put("success", true);
18231841

18241842
return new ApiSimpleResponse(results);
@@ -2347,8 +2365,10 @@ public ModelAndView getView(Object form, BindException errors) throws Exception
23472365
JspView<Object> view = new JspView<>("/org/labkey/laboratory/view/dataBrowser.jsp", form);
23482366
view.setTitle("Data Browser");
23492367
view.setHidePageTitle(true);
2350-
//view.setFrame(WebPartView.FrameType.NONE);
2351-
//getPageConfig().setTemplate(PageConfig.Template.None);
2368+
2369+
LaboratoryServiceImpl.get().getTabbedReportFilterProviderProviders(getContainer(), getUser()).forEach(p -> {
2370+
p.getClientDependencies().forEach(view::addClientDependency);
2371+
});
23522372

23532373
return view;
23542374
}

0 commit comments

Comments
 (0)