Skip to content

Commit d220f2f

Browse files
authored
Share instrument annotations across QC folders (#1185)
1 parent 34366e7 commit d220f2f

11 files changed

Lines changed: 523 additions & 81 deletions

File tree

resources/queries/targetedms/qcannotation/.qview.xml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44
<column name="Date" />
55
<column name="EndDate" />
66
<column name="QCAnnotationTypeId" />
7+
<column name="instrumentModel" />
8+
<column name="instrumentSerialNumber" />
79
<column name="Container" />
810
<column name="Created" />
911
<column name="Modified" />

resources/queries/targetedms/qcannotationtype/.qview.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
<column name="Description" />
55
<column name="Color" />
66
<column name="Container" />
7+
<column name="Shareable"/>
78
<column name="Created" />
89
<column name="Modified" />
910
</columns>
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
ALTER TABLE targetedms.QCAnnotationType ADD COLUMN Shareable BOOLEAN DEFAULT FALSE;
2+
ALTER TABLE targetedms.QCAnnotation ADD COLUMN instrumentModel VARCHAR(300);
3+
ALTER TABLE targetedms.QCAnnotation ADD COLUMN instrumentSerialNumber VARCHAR(200);
4+
5+
UPDATE targetedms.QCAnnotationType SET Shareable = TRUE WHERE Name = 'Instrumentation Change';

resources/schemas/targetedms.xml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1262,6 +1262,7 @@
12621262
<column columnName="Name"/>
12631263
<column columnName="Description"/>
12641264
<column columnName="Color"/>
1265+
<column columnName="Shareable"/>
12651266
</columns>
12661267
</table>
12671268

@@ -1307,6 +1308,8 @@
13071308
<columnTitle>Annotation Type</columnTitle>
13081309
<description>The category of the event</description>
13091310
</column>
1311+
<column columnName="instrumentModel"/>
1312+
<column columnName="instrumentSerialNumber"/>
13101313
</columns>
13111314
</table>
13121315
<table tableName="QCMetricConfiguration" tableDbType="TABLE">

src/org/labkey/targetedms/TargetedMSManager.java

Lines changed: 59 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@
1717
package org.labkey.targetedms;
1818

1919
import com.google.common.base.Joiner;
20+
import lombok.Getter;
21+
import lombok.Setter;
2022
import org.apache.commons.io.FilenameUtils;
2123
import org.apache.commons.lang3.StringUtils;
2224
import org.apache.logging.log4j.Logger;
@@ -207,7 +209,8 @@ public static TargetedMSManager get()
207209

208210
public static List<SampleFileChromInfo> getSampleFileChromInfos(SampleFile sampleFile)
209211
{
210-
return new TableSelector(getTableInfoSampleFileChromInfo(), new SimpleFilter(FieldKey.fromParts("SampleFileId"), sampleFile.getId()), new Sort("TextId")).getArrayList(SampleFileChromInfo.class); }
212+
return new TableSelector(getTableInfoSampleFileChromInfo(), new SimpleFilter(FieldKey.fromParts("SampleFileId"), sampleFile.getId()), new Sort("TextId")).getArrayList(SampleFileChromInfo.class);
213+
}
211214

212215
public static SampleFileChromInfo getSampleFileChromInfo(int id, Container c)
213216
{
@@ -548,7 +551,8 @@ public static TableInfo getTableInfoQuantificationSettings()
548551
return getSchema().getTable(TargetedMSSchema.TABLE_QUANTIIFICATION_SETTINGS);
549552
}
550553

551-
public static TableInfo getTableInfoCalibrationCurve() {
554+
public static TableInfo getTableInfoCalibrationCurve()
555+
{
552556
return getSchema().getTable(TargetedMSSchema.TABLE_CALIBRATION_CURVE);
553557
}
554558

@@ -628,19 +632,23 @@ public static TableInfo getTableInfoSkylineAuditLogMessage()
628632
return getSchema().getTable(TargetedMSSchema.TABLE_SKYLINE_AUDITLOG_MESSAGE);
629633
}
630634

631-
public static TableInfo getTableInfoListDefinition() {
635+
public static TableInfo getTableInfoListDefinition()
636+
{
632637
return getSchema().getTable(TargetedMSSchema.TABLE_LIST_DEFINITION);
633638
}
634639

635-
public static TableInfo getTableInfoListColumnDefinition() {
640+
public static TableInfo getTableInfoListColumnDefinition()
641+
{
636642
return getSchema().getTable(TargetedMSSchema.TABLE_LIST_COLUMN_DEFINITION);
637643
}
638644

639-
public static TableInfo getTableInfoListItem() {
645+
public static TableInfo getTableInfoListItem()
646+
{
640647
return getSchema().getTable(TargetedMSSchema.TABLE_LIST_ITEM);
641648
}
642649

643-
public static TableInfo getTableInfoListItemValue() {
650+
public static TableInfo getTableInfoListItemValue()
651+
{
644652
return getSchema().getTable(TargetedMSSchema.TABLE_LIST_ITEM_VALUE);
645653
}
646654

@@ -1246,7 +1254,7 @@ public List<InstrumentNickname> getNickname(String name, TargetedMSSchema schema
12461254
}
12471255

12481256
List<InstrumentNickname> result = new ArrayList<>(dedupeAcrossContainers.values());
1249-
1257+
12501258
if (matches.isEmpty())
12511259
{
12521260
String sql = "SELECT DISTINCT InstrumentNickname, " +
@@ -3046,6 +3054,39 @@ private QueryUpdateService getNicknameUpdateService(User user, Container contain
30463054
return Objects.requireNonNull(table.getUpdateService());
30473055
}
30483056

3057+
public static class InstrumentDetails
3058+
{
3059+
@Getter @Setter
3060+
private String instrumentSerialNumber;
3061+
@Getter @Setter
3062+
private String model;
3063+
3064+
public InstrumentDetails()
3065+
{
3066+
}
3067+
}
3068+
3069+
public static List<InstrumentDetails> getInstrumentDetails(Container container)
3070+
{
3071+
SQLFragment sql = new SQLFragment("SELECT DISTINCT sf.InstrumentSerialNumber, i.Model FROM ");
3072+
sql.append(getTableInfoSampleFile(), "sf");
3073+
sql.append(" INNER JOIN ");
3074+
sql.append(getTableInfoInstrument(), "i");
3075+
sql.append(" ON sf.InstrumentId = i.Id ");
3076+
sql.append(" INNER JOIN ");
3077+
sql.append(getTableInfoReplicate(), "rep");
3078+
sql.append(" ON sf.ReplicateId = rep.Id ");
3079+
sql.append(" INNER JOIN ");
3080+
sql.append(getTableInfoRuns(), "r");
3081+
sql.append(" ON rep.RunId = r.Id ");
3082+
sql.append(" WHERE r.Container = ?");
3083+
sql.add(container);
3084+
3085+
return new SqlSelector(getSchema(), sql).getArrayList(InstrumentDetails.class);
3086+
3087+
}
3088+
3089+
30493090
public void deleteNickname(InstrumentNickname name, User user) throws SQLException, BatchValidationException, QueryUpdateServiceException, InvalidKeyException
30503091
{
30513092
getNicknameUpdateService(user, name.getContainer()).
@@ -3071,4 +3112,15 @@ public void saveNickname(InstrumentNickname name, User user) throws SQLException
30713112
insertRows(user, name.getContainer(), Arrays.asList(row), errors, null, null);
30723113
}
30733114
}
3115+
3116+
public static boolean isQCAnnotationTypeShareable(int qcAnnotationTypeId)
3117+
{
3118+
SQLFragment sql = new SQLFragment("SELECT Shareable FROM ");
3119+
sql.append(getTableInfoQCAnnotationType());
3120+
sql.append(" WHERE Id = ?");
3121+
sql.add(qcAnnotationTypeId);
3122+
3123+
Boolean isShareable = new SqlSelector(getSchema(), sql).getObject(Boolean.class);
3124+
return isShareable != null && isShareable;
3125+
}
30743126
}

src/org/labkey/targetedms/TargetedMSModule.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -231,7 +231,7 @@ public String getName()
231231
@Override
232232
public Double getSchemaVersion()
233233
{
234-
return 26.002;
234+
return 26.003;
235235
}
236236

237237
@Override

src/org/labkey/targetedms/query/QCAnnotationTable.java

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,27 @@
1515
*/
1616
package org.labkey.targetedms.query;
1717

18+
import org.jetbrains.annotations.Nullable;
19+
import org.labkey.api.data.Container;
1820
import org.labkey.api.data.ContainerFilter;
21+
import org.labkey.api.data.TableInfo;
1922
import org.labkey.api.gwt.client.AuditBehaviorType;
23+
import org.labkey.api.query.BatchValidationException;
24+
import org.labkey.api.query.DefaultQueryUpdateService;
25+
import org.labkey.api.query.DuplicateKeyException;
2026
import org.labkey.api.query.QueryForeignKey;
27+
import org.labkey.api.query.QueryUpdateService;
28+
import org.labkey.api.query.QueryUpdateServiceException;
2129
import org.labkey.api.query.SimpleUserSchema;
30+
import org.labkey.api.query.ValidationException;
31+
import org.labkey.api.security.User;
2232
import org.labkey.targetedms.TargetedMSManager;
2333
import org.labkey.targetedms.TargetedMSSchema;
2434

35+
import java.sql.SQLException;
36+
import java.util.List;
37+
import java.util.Map;
38+
2539
import static org.labkey.targetedms.query.GuideSetTable.appendFormatLabel;
2640

2741
/**
@@ -44,4 +58,54 @@ public QCAnnotationTable(TargetedMSSchema schema, ContainerFilter cf)
4458
appendFormatLabel(getMutableColumn("EndDate"));
4559
setAuditBehavior(AuditBehaviorType.DETAILED);
4660
}
61+
62+
@Override
63+
public QueryUpdateService getUpdateService()
64+
{
65+
TableInfo table = getRealTable();
66+
if (table != null)
67+
{
68+
return new DefaultQueryUpdateService(this, getRealTable())
69+
{
70+
@Override
71+
public List<Map<String, Object>> insertRows(User user, Container container, List<Map<String, Object>> rows, BatchValidationException errors, @Nullable Map<Enum, Object> configParameters, @Nullable Map<String, Object> extraScriptContext) throws SQLException, QueryUpdateServiceException, DuplicateKeyException
72+
{
73+
List<Map<String, Object>> resultRows = new java.util.ArrayList<>();
74+
for (Map<String, Object> row : rows)
75+
{
76+
// Check if the QCAnnotationType is shareable
77+
int qcAnnotationTypeId = (Integer) row.get("QCAnnotationTypeId");
78+
boolean isShareable = TargetedMSManager.isQCAnnotationTypeShareable(qcAnnotationTypeId);
79+
80+
if (isShareable)
81+
{
82+
List<TargetedMSManager.InstrumentDetails> instruments = TargetedMSManager.getInstrumentDetails(getContainer());
83+
if (instruments.isEmpty())
84+
{
85+
resultRows.add(row);
86+
}
87+
else
88+
{
89+
for (TargetedMSManager.InstrumentDetails instrument : instruments)
90+
{
91+
Map<String, Object> newRow = new java.util.HashMap<>(row);
92+
newRow.put("instrumentModel", instrument.getModel());
93+
newRow.put("instrumentSerialNumber", instrument.getInstrumentSerialNumber());
94+
newRow.put("Container", getContainer().getId());
95+
resultRows.add(newRow);
96+
}
97+
}
98+
}
99+
else
100+
{
101+
resultRows.add(row);
102+
}
103+
}
104+
105+
return super.insertRows(user, container, resultRows, errors, configParameters, extraScriptContext);
106+
}
107+
};
108+
}
109+
return null;
110+
}
47111
}

test/src/org/labkey/test/components/targetedms/QCPlot.java

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
package org.labkey.test.components.targetedms;
1717

1818
import org.junit.Assert;
19+
import org.labkey.test.BaseWebDriverTest;
1920
import org.labkey.test.Locator;
2021
import org.labkey.test.util.targetedms.QCHelper;
2122
import org.openqa.selenium.WebElement;
@@ -27,6 +28,9 @@
2728
import java.util.regex.Matcher;
2829
import java.util.regex.Pattern;
2930

31+
import static org.labkey.test.BaseWebDriverTest.getCurrentTest;
32+
import static org.labkey.test.util.TestLogger.log;
33+
3034
public class QCPlot
3135
{
3236
final WebElement plot;
@@ -64,16 +68,22 @@ public List<QCHelper.Annotation> getAnnotations()
6468

6569
private QCHelper.Annotation parseAnnotation(WebElement annotationEl)
6670
{
67-
String annotationString = annotationEl.getText();
68-
String annotationRegex = "Created By: (.+)\\s*, " +
69-
"Date: (\\d\\d\\d\\d-\\d\\d-\\d\\d \\d\\d:\\d\\d:\\d\\d)\\s*, " +
70-
"Description: (.+)";
71+
getCurrentTest().scrollIntoView(annotationEl);
72+
getCurrentTest().mouseOver(annotationEl);
73+
Locator tippyLocator = Locator.tagWithClass("div", "tippy-content");
74+
getCurrentTest().waitForElement(tippyLocator);
75+
WebElement tippyContent = tippyLocator.findElement(getCurrentTest().getDriver());
76+
String annotationString = tippyContent.getText();
77+
78+
String annotationRegex = "(?s)Created By:\\s*(.*?)\\s+" +
79+
"Type:\\s*(.*?)\\s+" +
80+
"Date:\\s*(\\d\\d\\d\\d-\\d\\d-\\d\\d(?: \\d\\d:\\d\\d:\\d\\d)?)\\s+" +
81+
"Description:\\s*(.*?)(?:\\s+Shared From:.*|$)";
7182
Pattern annotationPattern = Pattern.compile(annotationRegex, Pattern.MULTILINE);
7283
Matcher annotationMatcher = annotationPattern.matcher(annotationString);
7384

74-
Assert.assertTrue(annotationString, annotationMatcher.find());
75-
String date = annotationMatcher.group(2);
76-
String description = annotationMatcher.group(3);
85+
Assert.assertTrue("Annotation text did not match regex: " + annotationString, annotationMatcher.find());
86+
String description = annotationMatcher.group(4).trim();
7787
String color = annotationEl.getCssValue("fill");
7888
QCHelper.AnnotationType type = getAnnotationTypes().get(color);
7989

0 commit comments

Comments
 (0)