diff --git a/api/src/org/labkey/api/assay/AssayRunType.java b/api/src/org/labkey/api/assay/AssayRunType.java index 4807d8fc798..55db9b29b31 100644 --- a/api/src/org/labkey/api/assay/AssayRunType.java +++ b/api/src/org/labkey/api/assay/AssayRunType.java @@ -48,7 +48,7 @@ public class AssayRunType extends ExperimentRunType public AssayRunType(ExpProtocol protocol, Container c) { - super(protocol.getName(), SchemaKey.fromParts(AssaySchema.NAME, AssayService.get().getProvider(protocol).getResourceName(), protocol.getName()).toString(), AssayProtocolSchema.RUNS_TABLE_NAME); + super(protocol.getName(), AssayProtocolSchema.schemaName(AssayService.get().getProvider(protocol), protocol), AssayProtocolSchema.RUNS_TABLE_NAME); _protocol = protocol; } diff --git a/api/src/org/labkey/api/exp/ExperimentProtocolHandler.java b/api/src/org/labkey/api/exp/ExperimentProtocolHandler.java new file mode 100644 index 00000000000..16380230814 --- /dev/null +++ b/api/src/org/labkey/api/exp/ExperimentProtocolHandler.java @@ -0,0 +1,28 @@ +package org.labkey.api.exp; + +import org.jetbrains.annotations.Nullable; +import org.labkey.api.exp.api.ExpProtocol; +import org.labkey.api.exp.api.ExpProtocolApplication; +import org.labkey.api.exp.api.ExpRun; +import org.labkey.api.query.QueryRowReference; + +/** + * Provides some basic recognition for protocols of a particular type. + */ +public interface ExperimentProtocolHandler extends Handler +{ + /** + * Get a query reference for the protocol type. + */ + public @Nullable QueryRowReference getQueryRowReference(ExpProtocol protocol); + + /** + * Get a query reference for the run of the protocol type. + */ + public @Nullable QueryRowReference getQueryRowReference(ExpProtocol protocol, ExpRun run); + + /** + * Get a query reference for the protocol application of the protocol type. + */ + public @Nullable QueryRowReference getQueryRowReference(ExpProtocol protocol, ExpProtocolApplication app); +} diff --git a/api/src/org/labkey/api/exp/ExperimentRunType.java b/api/src/org/labkey/api/exp/ExperimentRunType.java index 389aec33a57..feb33f48acf 100644 --- a/api/src/org/labkey/api/exp/ExperimentRunType.java +++ b/api/src/org/labkey/api/exp/ExperimentRunType.java @@ -16,11 +16,22 @@ package org.labkey.api.exp; -import org.labkey.api.data.*; +import org.jetbrains.annotations.Nullable; +import org.labkey.api.data.ButtonBar; +import org.labkey.api.data.Container; import org.labkey.api.data.ContainerFilter; +import org.labkey.api.data.TableInfo; +import org.labkey.api.data.TableSelector; import org.labkey.api.exp.api.ExpProtocol; +import org.labkey.api.exp.api.ExpProtocolApplication; +import org.labkey.api.exp.api.ExpRun; +import org.labkey.api.exp.query.ExpProtocolApplicationTable; +import org.labkey.api.exp.query.ExpRunTable; import org.labkey.api.exp.query.ExpSchema; +import org.labkey.api.query.FieldKey; +import org.labkey.api.query.QueryRowReference; import org.labkey.api.query.QueryService; +import org.labkey.api.query.SchemaKey; import org.labkey.api.query.UserSchema; import org.labkey.api.security.User; import org.labkey.api.view.DataView; @@ -37,20 +48,29 @@ * User: jeckels * Date: Sep 25, 2006 */ -public abstract class ExperimentRunType implements Comparable, Handler +public abstract class ExperimentRunType implements Comparable, ExperimentProtocolHandler { - private final String _description; - private final String _schemaName; - private final String _tableName; - public static final ExperimentRunType ALL_RUNS_TYPE = new ExperimentRunType("All Runs", ExpSchema.SCHEMA_NAME, ExpSchema.TableType.Runs.toString()) + public static final ExperimentRunType ALL_RUNS_TYPE = new ExperimentRunType("All Runs", ExpSchema.SCHEMA_EXP, ExpSchema.TableType.Runs.toString()) { - public Priority getPriority(ExpProtocol object) + public Priority getPriority(ExpProtocol protocol) { - return Priority.LOW; + if (protocol.getApplicationType() == ExpProtocol.ApplicationType.ExperimentRun) + return Priority.LOW; + + return null; } }; + private final String _description; + private final SchemaKey _schemaName; + private final String _tableName; + public ExperimentRunType(String description, String schemaName, String tableName) + { + this(description, SchemaKey.fromString(schemaName), tableName); + } + + public ExperimentRunType(String description, SchemaKey schemaName, String tableName) { _description = description; _schemaName = schemaName; @@ -62,7 +82,7 @@ public String getDescription() return _description; } - public String getSchemaName() + public SchemaKey getSchemaName() { return _schemaName; } @@ -72,6 +92,33 @@ public String getTableName() return _tableName; } + /** + * Reference to the row that represents the protocol for this run type. + */ + @Nullable + public QueryRowReference getQueryRowReference(ExpProtocol protocol) + { + return new QueryRowReference(protocol.getContainer(), ExpSchema.SCHEMA_EXP, ExpSchema.TableType.Protocols.name(), ExpProtocolApplicationTable.Column.RowId, protocol.getRowId()); + } + + /** + * Reference to the row that represents the run of for this run type. + */ + @Nullable + public QueryRowReference getQueryRowReference(ExpProtocol protocol, ExpRun run) + { + return new QueryRowReference(run.getContainer(), SchemaKey.fromParts(_schemaName), _tableName, ExpRunTable.Column.RowId, run.getRowId()); + } + + /** + * Reference to the row that represents the protocol application for this run type. + */ + @Nullable + public QueryRowReference getQueryRowReference(ExpProtocol protocol, ExpProtocolApplication app) + { + return new QueryRowReference(app.getContainer(), ExpSchema.SCHEMA_EXP, ExpSchema.TableType.ProtocolApplications.name(), ExpProtocolApplicationTable.Column.RowId, app.getRowId()); + } + public long getRunCount(User user, Container c) { UserSchema schema = QueryService.get().getUserSchema(user, c, _schemaName); diff --git a/api/src/org/labkey/api/exp/api/ExpProtocol.java b/api/src/org/labkey/api/exp/api/ExpProtocol.java index 67b0afdf35d..ced5f9329f9 100644 --- a/api/src/org/labkey/api/exp/api/ExpProtocol.java +++ b/api/src/org/labkey/api/exp/api/ExpProtocol.java @@ -16,11 +16,12 @@ package org.labkey.api.exp.api; +import org.jetbrains.annotations.Nullable; +import org.labkey.api.assay.AbstractAssayProvider; import org.labkey.api.data.Container; import org.labkey.api.exp.ObjectProperty; import org.labkey.api.exp.ProtocolParameter; import org.labkey.api.security.User; -import org.labkey.api.assay.AbstractAssayProvider; import java.util.Collection; import java.util.List; @@ -105,7 +106,8 @@ enum ApplicationType ApplicationType getApplicationType(); void setApplicationType(ApplicationType type); - ProtocolImplementation getImplementation(); + @Nullable String getImplementationName(); + @Nullable ProtocolImplementation getImplementation(); String getDescription(); void setDescription(String description); diff --git a/api/src/org/labkey/api/exp/api/ExperimentJSONConverter.java b/api/src/org/labkey/api/exp/api/ExperimentJSONConverter.java index f27fa5fc0c4..007bf47abca 100644 --- a/api/src/org/labkey/api/exp/api/ExperimentJSONConverter.java +++ b/api/src/org/labkey/api/exp/api/ExperimentJSONConverter.java @@ -60,6 +60,7 @@ public class ExperimentJSONConverter // General experiment object properties public static final String ID = "id"; public static final String ROW_ID = "rowId"; + public static final String CONTAINER = "container"; public static final String CREATED = "created"; public static final String CREATED_BY = "createdBy"; public static final String MODIFIED = "modified"; @@ -67,6 +68,8 @@ public class ExperimentJSONConverter public static final String NAME = "name"; public static final String LSID = "lsid"; public static final String CPAS_TYPE = "cpasType"; + // Matches the expType parameter used in the linage api: "Data", "Material", "ExperimentRun", "Object" + public static final String EXP_TYPE = "expType"; public static final String URL = "url"; public static final String PROPERTIES = "properties"; public static final String COMMENT = "comment"; @@ -153,6 +156,11 @@ public Settings withIncludeInputsAndOutputs(boolean b) { return new Settings(includeProperties, b, includeRunSteps); } + + public Settings withIncludeRunSteps(boolean b) + { + return new Settings(includeProperties, includeInputsAndOutputs, b); + } } @NotNull @@ -176,12 +184,14 @@ public static JSONObject serializeRunGroup(ExpExperiment runGroup, Domain domain { JSONObject jsonObject = serializeExpObject(runGroup, domain != null ? domain.getProperties() : Collections.emptyList(), settings); jsonObject.put(COMMENT, runGroup.getComments()); + jsonObject.put(ExperimentJSONConverter.EXP_TYPE, "Experiment"); return jsonObject; } public static JSONObject serializeRun(ExpRun run, Domain domain, User user, @NotNull Settings settings) { JSONObject jsonObject = serializeExpObject(run, domain == null ? null : domain.getProperties(), settings); + jsonObject.put(ExperimentJSONConverter.EXP_TYPE, "ExperimentRun"); if (settings.isIncludeProperties()) { jsonObject.put(COMMENT, run.getComments()); @@ -252,6 +262,7 @@ public static JSONObject serializeProtocol(ExpProtocol protocol, User user) // Just include basic protocol properties for now. // See GetProtocolAction and GWTProtocol for serializing an assay protocol with domain fields. JSONObject jsonObject = serializeExpObject(protocol, null, DEFAULT_SETTINGS.withIncludeProperties(false)); + jsonObject.put(ExperimentJSONConverter.EXP_TYPE, "Protocol"); return jsonObject; } @@ -344,6 +355,7 @@ else if (runInput instanceof ExpMaterialRunInput) protected static JSONObject serializeRunProtocolApplication(@NotNull ExpProtocolApplication protApp, ExpRun run, User user, Settings settings) { JSONObject json = serializeExpObject(protApp, null, settings); + json.put(ExperimentJSONConverter.EXP_TYPE, "ProtocolApplication"); json.put(ACTION_SEQUENCE, protApp.getActionSequence()); json.put(APPLICATION_TYPE, protApp.getApplicationType().toString()); @@ -474,6 +486,8 @@ private static JSONObject serializeIdentifiableBean(@NotNull Identifiable obj) if (url != null) json.put(URL, url); + json.put(CONTAINER, obj.getContainer().getId()); + QueryRowReference rowRef = obj.getQueryRowReference(); if (rowRef != null) { @@ -492,6 +506,7 @@ private static JSONObject serializeIdentifiableBean(@NotNull Identifiable obj) public static JSONObject serializeIdentifiable(@NotNull Identifiable obj, Settings settings) { JSONObject json = serializeIdentifiableBean(obj); + json.put(ExperimentJSONConverter.EXP_TYPE, (Object)null); if (settings.isIncludeProperties()) { @@ -516,6 +531,8 @@ public static JSONObject serializeExpObject(@NotNull ExpObject object, @Nullable // instead and use serializeOntologyProperties(ExpObject) so the object properties will be // fetched using ExpObject.getProperty(). JSONObject jsonObject = serializeIdentifiableBean(object); + jsonObject.put(ExperimentJSONConverter.EXP_TYPE, "Object"); + int rowId = object.getRowId(); if (rowId != 0) { @@ -634,6 +651,7 @@ public static JSONObject serializeData(@NotNull ExpData data, @Nullable User use final ExpDataClass dc = data.getDataClass(user); JSONObject jsonObject = serializeExpObject(data, null, settings); + jsonObject.put(ExperimentJSONConverter.EXP_TYPE, "Data"); if (settings.isIncludeProperties()) { @@ -702,6 +720,7 @@ public static JSONObject serializeMaterial(@NotNull ExpMaterial material, @NotNu } jsonObject.put(CPAS_TYPE, material.getCpasType()); + jsonObject.put(ExperimentJSONConverter.EXP_TYPE, "Material"); return jsonObject; } diff --git a/api/src/org/labkey/api/exp/api/ExperimentService.java b/api/src/org/labkey/api/exp/api/ExperimentService.java index ed6d8f5ef49..73d8c75c8d9 100644 --- a/api/src/org/labkey/api/exp/api/ExperimentService.java +++ b/api/src/org/labkey/api/exp/api/ExperimentService.java @@ -28,6 +28,7 @@ import org.labkey.api.data.TableInfo; import org.labkey.api.exp.ExperimentDataHandler; import org.labkey.api.exp.ExperimentException; +import org.labkey.api.exp.ExperimentProtocolHandler; import org.labkey.api.exp.ExperimentRunListView; import org.labkey.api.exp.ExperimentRunType; import org.labkey.api.exp.ExperimentRunTypeSource; @@ -760,9 +761,11 @@ ExpRun derive(Map inputMaterials, Map inpu void registerProtocolImplementation(ProtocolImplementation impl); + void registerProtocolHandler(ExperimentProtocolHandler handler); + void registerProtocolInputCriteria(ExpProtocolInputCriteria.Factory factory); - ProtocolImplementation getProtocolImplementation(String name); + @Nullable ProtocolImplementation getProtocolImplementation(String name); ExpProtocolApplication getExpProtocolApplication(int rowId); @@ -827,6 +830,12 @@ ExpRun derive(Map inputMaterials, Map inpu List getExpProtocolsUsedByRuns(Container c, ContainerFilter containerFilter); + @Nullable + ExperimentProtocolHandler getExperimentProtocolHandler(@NotNull ExpProtocol protocol); + + @Nullable + ExperimentRunType getExperimentRunType(@NotNull ExpProtocol protocol); + @Nullable ExperimentRunType getExperimentRunType(@NotNull String description, @Nullable Container container); diff --git a/api/src/org/labkey/api/exp/api/ProtocolImplementation.java b/api/src/org/labkey/api/exp/api/ProtocolImplementation.java index 07492cacbfa..d14b8ae491f 100644 --- a/api/src/org/labkey/api/exp/api/ProtocolImplementation.java +++ b/api/src/org/labkey/api/exp/api/ProtocolImplementation.java @@ -16,13 +16,15 @@ package org.labkey.api.exp.api; +import org.jetbrains.annotations.Nullable; import org.labkey.api.data.Container; +import org.labkey.api.exp.ExperimentProtocolHandler; import org.labkey.api.query.QueryRowReference; import org.labkey.api.security.User; import java.util.List; -public class ProtocolImplementation +public class ProtocolImplementation implements ExperimentProtocolHandler { final protected String _name; public ProtocolImplementation(String name) @@ -60,8 +62,36 @@ public void onRunDeleted(Container container, User user) { } + @Override + public @Nullable Priority getPriority(ExpProtocol protocol) + { + if (getName().equals(protocol.getImplementationName())) + return Priority.HIGH; + + return null; + } + + /** + * Get a query reference for the protocol type. + */ + public QueryRowReference getQueryRowReference(ExpProtocol protocol) + { + return null; + } + + /** + * Get a query reference for the run of the protocol type. + */ public QueryRowReference getQueryRowReference(ExpProtocol protocol, ExpRun run) { return null; } + + /** + * Get a query reference for the protocol application of the protocol type. + */ + public QueryRowReference getQueryRowReference(ExpProtocol protocol, ExpProtocolApplication app) + { + return null; + } } diff --git a/api/src/org/labkey/api/exp/query/ExpProtocolApplicationTable.java b/api/src/org/labkey/api/exp/query/ExpProtocolApplicationTable.java index 7d4b3b5d9b2..5ec4db2dfde 100644 --- a/api/src/org/labkey/api/exp/query/ExpProtocolApplicationTable.java +++ b/api/src/org/labkey/api/exp/query/ExpProtocolApplicationTable.java @@ -27,11 +27,17 @@ enum Column { RowId, Name, + Comments, LSID, Protocol, Run, ActionSequence, - Type + Type, + ActivityDate, + StartTime, + EndTime, + RecordCount, + Properties } BaseColumnInfo createMaterialInputColumn(String alias, SamplesSchema schema, ExpSampleSet sampleSet, String... roleNames); diff --git a/api/src/org/labkey/api/query/QueryRowReference.java b/api/src/org/labkey/api/query/QueryRowReference.java index 8d77a3b816c..3b3853c5c2b 100644 --- a/api/src/org/labkey/api/query/QueryRowReference.java +++ b/api/src/org/labkey/api/query/QueryRowReference.java @@ -21,6 +21,11 @@ public class QueryRowReference final @NotNull String _queryName; final @NotNull List> _pkFilters; + public QueryRowReference(@NotNull Container c, @NotNull SchemaKey schemaKey, @NotNull String queryName, @NotNull Enum pkCol, int pkValue) + { + this(c, schemaKey, queryName, List.of(Pair.of(FieldKey.fromParts(pkCol), pkValue))); + } + public QueryRowReference(@NotNull Container c, @NotNull SchemaKey schemaKey, @NotNull String queryName, @NotNull FieldKey pkCol, int pkValue) { this(c, schemaKey, queryName, List.of(Pair.of(pkCol, pkValue))); diff --git a/core/package-lock.json b/core/package-lock.json index b8c9a6286dd..7c902f6f675 100644 --- a/core/package-lock.json +++ b/core/package-lock.json @@ -260,17 +260,17 @@ } }, "@babel/runtime": { - "version": "7.9.2", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.9.2.tgz", - "integrity": "sha512-NE2DtOdufG7R5vnfQUTehdTfNycfUANEtCa9PssN9O/xmTzP4E08UI797ixaei6hBEVL9BI/PsdJS5x7mWoB9Q==", + "version": "7.9.6", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.9.6.tgz", + "integrity": "sha512-64AF1xY3OAkFHqOb9s4jpgk1Mm5vDZ4L3acHvAml+53nO1XbXLuDodsVpO4OIUsmemlUHMxNdYMNJmsvOwLrvQ==", "requires": { "regenerator-runtime": "^0.13.4" } }, "@babel/runtime-corejs2": { - "version": "7.9.2", - "resolved": "https://registry.npmjs.org/@babel/runtime-corejs2/-/runtime-corejs2-7.9.2.tgz", - "integrity": "sha512-ayjSOxuK2GaSDJFCtLgHnYjuMyIpViNujWrZo8GUpN60/n7juzJKK5yOo6RFVb0zdU9ACJFK+MsZrUnj3OmXMw==", + "version": "7.9.6", + "resolved": "https://registry.npmjs.org/@babel/runtime-corejs2/-/runtime-corejs2-7.9.6.tgz", + "integrity": "sha512-TcdM3xc7weMrwTawuG3BTjtVE3mQLXUPQ9CxTbSKOrhn3QAcqCJ2fz+IIv25wztzUnhNZat7hr655YJa61F3zg==", "requires": { "core-js": "^2.6.5", "regenerator-runtime": "^0.13.4" @@ -939,16 +939,16 @@ "integrity": "sha1-NJIdAY//FYWYjCObiLH/TnC2adM=" }, "@labkey/components": { - "version": "0.54.0", - "resolved": "https://artifactory.labkey.com/artifactory/api/npm/libs-client/@labkey/components/-/@labkey/components-0.54.0.tgz", - "integrity": "sha1-VWPU9sbho3A3y/wJdWSHronfYSQ=", + "version": "0.56.0", + "resolved": "https://artifactory.labkey.com/artifactory/api/npm/libs-client/@labkey/components/-/@labkey/components-0.56.0.tgz", + "integrity": "sha1-bHHv4kdmq3h42+fEIeCY5U3Fnew=", "requires": { "@fortawesome/fontawesome-free": "5.9.0", "@fortawesome/fontawesome-svg-core": "1.2.19", "@fortawesome/free-regular-svg-icons": "5.11.2", "@fortawesome/free-solid-svg-icons": "5.9.0", "@fortawesome/react-fontawesome": "0.1.4", - "@labkey/api": "0.2.2", + "@labkey/api": "0.2.5", "bootstrap": "3.4.1", "classnames": "2.2.6", "font-awesome": "4.7.0", @@ -980,9 +980,9 @@ }, "dependencies": { "@labkey/api": { - "version": "0.2.2", - "resolved": "https://artifactory.labkey.com/artifactory/api/npm/libs-client/@labkey/api/-/@labkey/api-0.2.2.tgz", - "integrity": "sha1-3uthffvyvoPrduPETCeAfGkcap0=" + "version": "0.2.5", + "resolved": "https://artifactory.labkey.com/artifactory/api/npm/libs-client/@labkey/api/-/@labkey/api-0.2.5.tgz", + "integrity": "sha1-6Ep6Sksr8jm1WD+bX/Gu/a2o3kk=" } } }, @@ -11005,7 +11005,7 @@ }, "rgba-regex": { "version": "1.0.0", - "resolved": "http://registry.npmjs.org/rgba-regex/-/rgba-regex-1.0.0.tgz", + "resolved": "https://registry.npmjs.org/rgba-regex/-/rgba-regex-1.0.0.tgz", "integrity": "sha1-QzdOLiyglosO8VI0YLfXMP8i7rM=", "dev": true }, diff --git a/core/package.json b/core/package.json index 709c6b9cdda..4b82a104457 100644 --- a/core/package.json +++ b/core/package.json @@ -91,7 +91,7 @@ }, "dependencies": { "@labkey/api": "0.2.4", - "@labkey/components": "0.54.0", + "@labkey/components": "0.56.0", "@labkey/eslint-config-react": "0.0.5", "react-toggle-button": "2.2.0" }, diff --git a/experiment/package-lock.json b/experiment/package-lock.json index 29e95dec68f..b22ef6c0a44 100644 --- a/experiment/package-lock.json +++ b/experiment/package-lock.json @@ -36,26 +36,26 @@ } }, "@babel/runtime": { - "version": "7.9.2", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.9.2.tgz", - "integrity": "sha512-NE2DtOdufG7R5vnfQUTehdTfNycfUANEtCa9PssN9O/xmTzP4E08UI797ixaei6hBEVL9BI/PsdJS5x7mWoB9Q==", + "version": "7.9.6", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.9.6.tgz", + "integrity": "sha512-64AF1xY3OAkFHqOb9s4jpgk1Mm5vDZ4L3acHvAml+53nO1XbXLuDodsVpO4OIUsmemlUHMxNdYMNJmsvOwLrvQ==", "requires": { "regenerator-runtime": "^0.13.4" } }, "@babel/runtime-corejs2": { - "version": "7.9.2", - "resolved": "https://registry.npmjs.org/@babel/runtime-corejs2/-/runtime-corejs2-7.9.2.tgz", - "integrity": "sha512-ayjSOxuK2GaSDJFCtLgHnYjuMyIpViNujWrZo8GUpN60/n7juzJKK5yOo6RFVb0zdU9ACJFK+MsZrUnj3OmXMw==", + "version": "7.9.6", + "resolved": "https://registry.npmjs.org/@babel/runtime-corejs2/-/runtime-corejs2-7.9.6.tgz", + "integrity": "sha512-TcdM3xc7weMrwTawuG3BTjtVE3mQLXUPQ9CxTbSKOrhn3QAcqCJ2fz+IIv25wztzUnhNZat7hr655YJa61F3zg==", "requires": { "core-js": "^2.6.5", "regenerator-runtime": "^0.13.4" } }, "@babel/types": { - "version": "7.9.5", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.9.5.tgz", - "integrity": "sha512-XjnvNqenk818r5zMaba+sLQjnbda31UfUURv3ei0qPQw4u+j2jMyJ5b11y8ZHYTRSI3NnInQkkkRT4fLqqPdHg==", + "version": "7.9.6", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.9.6.tgz", + "integrity": "sha512-qxXzvBO//jO9ZnoasKF1uJzHd2+M6Q2ZPIVfnFps8JJvXy0ZBbwbNOmE6SGIY5XOY6d1Bo5lb9d9RJ8nv3WSeA==", "requires": { "@babel/helper-validator-identifier": "^7.9.5", "lodash": "^4.17.13", @@ -244,21 +244,21 @@ "integrity": "sha512-QPcGmICAPbGLGb6F/yNf/KzKqvFx8z5qx3D1yFqVAjoFmXK35EgyW+cJ57Te3CNsmzblwtzakLGFqHPqrfb4Tw==" }, "@labkey/api": { - "version": "0.2.2", - "resolved": "https://artifactory.labkey.com/artifactory/api/npm/libs-client/@labkey/api/-/@labkey/api-0.2.2.tgz", - "integrity": "sha1-3uthffvyvoPrduPETCeAfGkcap0=" + "version": "0.2.5", + "resolved": "https://artifactory.labkey.com/artifactory/api/npm/libs-client/@labkey/api/-/@labkey/api-0.2.5.tgz", + "integrity": "sha1-6Ep6Sksr8jm1WD+bX/Gu/a2o3kk=" }, "@labkey/components": { - "version": "0.54.0", - "resolved": "https://artifactory.labkey.com/artifactory/api/npm/libs-client/@labkey/components/-/@labkey/components-0.54.0.tgz", - "integrity": "sha1-VWPU9sbho3A3y/wJdWSHronfYSQ=", + "version": "0.56.0", + "resolved": "https://artifactory.labkey.com/artifactory/api/npm/libs-client/@labkey/components/-/@labkey/components-0.56.0.tgz", + "integrity": "sha1-bHHv4kdmq3h42+fEIeCY5U3Fnew=", "requires": { "@fortawesome/fontawesome-free": "5.9.0", "@fortawesome/fontawesome-svg-core": "1.2.19", "@fortawesome/free-regular-svg-icons": "5.11.2", "@fortawesome/free-solid-svg-icons": "5.9.0", "@fortawesome/react-fontawesome": "0.1.4", - "@labkey/api": "0.2.2", + "@labkey/api": "0.2.5", "bootstrap": "3.4.1", "classnames": "2.2.6", "font-awesome": "4.7.0", diff --git a/experiment/package.json b/experiment/package.json index 4dca3627b6c..33ac66d04de 100644 --- a/experiment/package.json +++ b/experiment/package.json @@ -29,7 +29,7 @@ } }, "dependencies": { - "@labkey/components": "0.54.0" + "@labkey/components": "0.56.0" }, "devDependencies": { "@hot-loader/react-dom": "16.13.0", diff --git a/experiment/src/client/RunGraph/RunGraph.tsx b/experiment/src/client/RunGraph/RunGraph.tsx index fce36d81da3..28adf3dd4a6 100644 --- a/experiment/src/client/RunGraph/RunGraph.tsx +++ b/experiment/src/client/RunGraph/RunGraph.tsx @@ -1,6 +1,7 @@ import React from 'react'; import { initQueryGridState, + LineageFilter, LineageGraph, LineageURLResolvers, } from '@labkey/components'; @@ -12,7 +13,7 @@ import '@labkey/components/dist/components.css'; initQueryGridState(); interface RunGraphProps { - context: AppContext + context: AppContext; } export class RunGraph extends React.Component { @@ -21,11 +22,31 @@ export class RunGraph extends React.Component { { - if (node && node.lineageNode && node.lineageNode.links.lineage) { - window.location.href = node.lineageNode.links.lineage; + if (node?.lineageNode?.links) { + let target = node.lineageNode.links.lineage ?? node.lineageNode.links.overview; + + if (target) { + if (target.indexOf('showRunGraph.view') > -1) { + try { + const url = new URL(target, location.origin); + url.searchParams.append('betaGraph', '1'); + + target = url.href; + } catch (e) { + // whatever, I tried... + } + } + + window.location.href = target; + } } }} /> diff --git a/experiment/src/org/labkey/experiment/api/ExpDataImpl.java b/experiment/src/org/labkey/experiment/api/ExpDataImpl.java index 909e947f76a..83ba057100b 100644 --- a/experiment/src/org/labkey/experiment/api/ExpDataImpl.java +++ b/experiment/src/org/labkey/experiment/api/ExpDataImpl.java @@ -139,7 +139,9 @@ public ActionURL detailsURL() DataType dataType = getDataType(); if (dataType != null) { - return dataType.getDetailsURL(this); + ActionURL url = dataType.getDetailsURL(this); + if (url != null) + return url; } return _object.detailsURL(); diff --git a/experiment/src/org/labkey/experiment/api/ExpProtocolApplicationImpl.java b/experiment/src/org/labkey/experiment/api/ExpProtocolApplicationImpl.java index dbd29d9f378..d980a586888 100644 --- a/experiment/src/org/labkey/experiment/api/ExpProtocolApplicationImpl.java +++ b/experiment/src/org/labkey/experiment/api/ExpProtocolApplicationImpl.java @@ -24,6 +24,7 @@ import org.labkey.api.data.SqlExecutor; import org.labkey.api.data.Table; import org.labkey.api.data.dialect.SqlDialect; +import org.labkey.api.exp.ExperimentProtocolHandler; import org.labkey.api.exp.OntologyManager; import org.labkey.api.exp.api.ExpData; import org.labkey.api.exp.api.ExpDataProtocolInput; @@ -32,8 +33,13 @@ import org.labkey.api.exp.api.ExpProtocol; import org.labkey.api.exp.api.ExpProtocolApplication; import org.labkey.api.exp.api.ExpRun; +import org.labkey.api.exp.api.ExperimentService; +import org.labkey.api.exp.api.ProtocolImplementation; import org.labkey.api.exp.api.ProvenanceService; +import org.labkey.api.exp.query.ExpProtocolApplicationTable; +import org.labkey.api.exp.query.ExpSchema; import org.labkey.api.query.FieldKey; +import org.labkey.api.query.QueryRowReference; import org.labkey.api.security.User; import org.labkey.api.util.Pair; import org.labkey.api.view.ActionURL; @@ -66,6 +72,20 @@ public ActionURL detailsURL() return null; } + @Override + public @Nullable QueryRowReference getQueryRowReference() + { + ExpProtocolImpl protocol = getProtocol(); + if (protocol != null) + { + QueryRowReference ref = protocol.getCustomQueryRowReference(); + if (ref != null) + return ref; + } + + return new QueryRowReference(getContainer(), ExpSchema.SCHEMA_EXP, ExpSchema.TableType.ProtocolApplications.name(), FieldKey.fromParts(ExpProtocolApplicationTable.Column.RowId), getRowId()); + } + public Date getCreated() { ExpRun run = getRun(); diff --git a/experiment/src/org/labkey/experiment/api/ExpProtocolApplicationTableImpl.java b/experiment/src/org/labkey/experiment/api/ExpProtocolApplicationTableImpl.java index e67af04a420..147b002eb2d 100644 --- a/experiment/src/org/labkey/experiment/api/ExpProtocolApplicationTableImpl.java +++ b/experiment/src/org/labkey/experiment/api/ExpProtocolApplicationTableImpl.java @@ -32,6 +32,8 @@ import org.labkey.api.query.FieldKey; import org.labkey.api.query.UserSchema; +import java.util.List; + public class ExpProtocolApplicationTableImpl extends ExpTableImpl implements ExpProtocolApplicationTable { public ExpProtocolApplicationTableImpl(String name, UserSchema schema, ContainerFilter cf) @@ -64,6 +66,8 @@ public MutableColumnInfo createColumn(String alias, ExpProtocolApplicationTable. return rowIdColumnInfo; case Name: return wrapColumn(alias, _rootTable.getColumn("Name")); + case Comments: + return wrapColumn(alias, _rootTable.getColumn("Comments")); case LSID: return wrapColumn(alias, _rootTable.getColumn("LSID")); case Run: @@ -78,6 +82,16 @@ public MutableColumnInfo createColumn(String alias, ExpProtocolApplicationTable. var columnInfo = wrapColumn(alias, _rootTable.getColumn("ProtocolLSID")); columnInfo.setFk(getExpSchema().getProtocolForeignKey(getContainerFilter(), "LSID")); return columnInfo; + case ActivityDate: + return wrapColumn(alias, _rootTable.getColumn("ActivityDate")); + case StartTime: + return wrapColumn(alias, _rootTable.getColumn("StartTime")); + case EndTime: + return wrapColumn(alias, _rootTable.getColumn("EndTime")); + case RecordCount: + return wrapColumn(alias, _rootTable.getColumn("RecordCount")); + case Properties: + return (BaseColumnInfo) createPropertiesColumn(alias); } throw new IllegalArgumentException("Unknown column " + column); } @@ -149,11 +163,26 @@ protected void populateColumns() addColumn(Column.RowId); addColumn(Column.Name); setTitleColumn(Column.Name.toString()); + addColumn(Column.Comments); addColumn(Column.Run); addColumn(Column.LSID).setHidden(true); addColumn(Column.Protocol); addColumn(Column.Type); addColumn(Column.ActionSequence).setHidden(true); + addColumn(Column.ActivityDate); + addColumn(Column.StartTime); + addColumn(Column.EndTime); + addColumn(Column.RecordCount); + addColumn(Column.Properties).setHidden(true); + + setDefaultVisibleColumns(List.of( + FieldKey.fromParts(Column.Name), + FieldKey.fromParts(Column.Run), + FieldKey.fromParts(Column.Protocol), + FieldKey.fromParts(Column.Type) + )); + + _populated = true; } @Override diff --git a/experiment/src/org/labkey/experiment/api/ExpProtocolImpl.java b/experiment/src/org/labkey/experiment/api/ExpProtocolImpl.java index 02bca2634a3..8933ec85b39 100644 --- a/experiment/src/org/labkey/experiment/api/ExpProtocolImpl.java +++ b/experiment/src/org/labkey/experiment/api/ExpProtocolImpl.java @@ -26,6 +26,8 @@ import org.labkey.api.data.Table; import org.labkey.api.data.TableSelector; import org.labkey.api.exp.ExperimentException; +import org.labkey.api.exp.ExperimentProtocolHandler; +import org.labkey.api.exp.ExperimentRunType; import org.labkey.api.exp.ObjectProperty; import org.labkey.api.exp.ProtocolParameter; import org.labkey.api.exp.api.ExpDataProtocolInput; @@ -72,9 +74,37 @@ public ActionURL detailsURL() @Override public @Nullable QueryRowReference getQueryRowReference() { + QueryRowReference ref = getCustomQueryRowReference(); + if (ref != null) + return ref; + return new QueryRowReference(getContainer(), ExpSchema.SCHEMA_EXP, ExpSchema.TableType.Protocols.name(), FieldKey.fromParts(ExpProtocolTable.Column.RowId.name()), getRowId()); } + /** + * Return a protocol specific query row reference or null if the default should be used. + */ + /*package*/ @Nullable QueryRowReference getCustomQueryRowReference() + { + ProtocolImplementation impl = getImplementation(); + if (impl != null) + { + QueryRowReference ref = impl.getQueryRowReference(this); + if (ref != null) + return ref; + } + + ExperimentProtocolHandler handler = ExperimentService.get().getExperimentProtocolHandler(this); + if (handler != null) + { + QueryRowReference ref = handler.getQueryRowReference(this); + if (ref != null) + return ref; + } + + return null; + } + public ApplicationType getApplicationType() { if (_object.getApplicationType() == null) @@ -91,10 +121,14 @@ public ApplicationType getApplicationType() } } - public ProtocolImplementation getImplementation() + public @Nullable String getImplementationName() + { + return (String) getProperty(ExperimentProperty.PROTOCOLIMPLEMENTATION.getPropertyDescriptor()); + } + + public @Nullable ProtocolImplementation getImplementation() { - String implName = (String) getProperty(ExperimentProperty.PROTOCOLIMPLEMENTATION.getPropertyDescriptor()); - return ExperimentService.get().getProtocolImplementation(implName); + return ExperimentService.get().getProtocolImplementation(getImplementationName()); } public int getRowId() diff --git a/experiment/src/org/labkey/experiment/api/ExpRunImpl.java b/experiment/src/org/labkey/experiment/api/ExpRunImpl.java index bc7d903972a..577b3e1eb64 100644 --- a/experiment/src/org/labkey/experiment/api/ExpRunImpl.java +++ b/experiment/src/org/labkey/experiment/api/ExpRunImpl.java @@ -38,6 +38,7 @@ import org.labkey.api.data.TableSelector; import org.labkey.api.data.dialect.SqlDialect; import org.labkey.api.exp.ExperimentException; +import org.labkey.api.exp.ExperimentRunType; import org.labkey.api.exp.Lsid; import org.labkey.api.exp.OntologyManager; import org.labkey.api.exp.api.DataType; @@ -129,20 +130,17 @@ public ActionURL detailsURL() ProtocolImplementation impl = protocol.getImplementation(); if (impl != null) { - QueryRowReference coords = impl.getQueryRowReference(protocol, this); - if (coords != null) - return coords; + QueryRowReference ref = impl.getQueryRowReference(protocol, this); + if (ref != null) + return ref; } - AssayService assayService = AssayService.get(); - if (assayService != null) + ExperimentRunType type = ExperimentService.get().getExperimentRunType(protocol); + if (type != null) { - AssayProvider provider = assayService.getProvider(this); - if (provider != null) - { - SchemaKey schemaKey = AssayProtocolSchema.schemaName(provider, protocol); - return new QueryRowReference(getContainer(), schemaKey, ExpSchema.TableType.Runs.name(), FieldKey.fromParts(ExpRunTable.Column.RowId), getRowId()); - } + QueryRowReference ref = type.getQueryRowReference(protocol, this); + if (ref != null) + return ref; } } diff --git a/experiment/src/org/labkey/experiment/api/ExperimentServiceImpl.java b/experiment/src/org/labkey/experiment/api/ExperimentServiceImpl.java index a647a1c2dcd..f6f44c95c40 100644 --- a/experiment/src/org/labkey/experiment/api/ExperimentServiceImpl.java +++ b/experiment/src/org/labkey/experiment/api/ExperimentServiceImpl.java @@ -85,6 +85,7 @@ import org.labkey.api.pipeline.PipelineValidationException; import org.labkey.api.query.BatchValidationException; import org.labkey.api.query.FieldKey; +import org.labkey.api.query.QueryRowReference; import org.labkey.api.query.QueryService; import org.labkey.api.query.QueryUpdateService; import org.labkey.api.query.SchemaKey; @@ -158,6 +159,7 @@ import java.util.stream.Collectors; import static java.util.Collections.emptyList; +import static java.util.Collections.emptySet; import static java.util.Collections.singleton; import static java.util.stream.Collectors.toList; import static java.util.stream.Collectors.toSet; @@ -195,6 +197,7 @@ public class ExperimentServiceImpl implements ExperimentService protected Map _dataTypes = new HashMap<>(); protected Map _protocolImplementations = new HashMap<>(); protected Map _protocolInputCriteriaFactories = new HashMap<>(); + private Set _protocolHandlers = new HashSet<>(); private static final List _listeners = new CopyOnWriteArrayList<>(); @@ -314,6 +317,28 @@ public List getExpProtocolsUsedByRuns(Container c, ContainerFil return result; } + @Override + public @Nullable ExperimentProtocolHandler getExperimentProtocolHandler(@NotNull ExpProtocol protocol) + { + if (protocol.getApplicationType() == ExpProtocol.ApplicationType.ExperimentRun) + { + return getExperimentRunType(protocol); + } + else if (protocol.getApplicationType() == ExpProtocol.ApplicationType.ProtocolApplication) + { + return Handler.Priority.findBestHandler(_protocolHandlers, protocol); + } + return null; + } + + @Nullable + @Override + public ExperimentRunType getExperimentRunType(@NotNull ExpProtocol protocol) + { + Set types = getExperimentRunTypes(protocol.getContainer()); + return Handler.Priority.findBestHandler(types, protocol); + } + @Nullable @Override public ExperimentRunType getExperimentRunType(@NotNull String description, @Nullable Container container) @@ -1954,7 +1979,7 @@ private boolean assertLineage(ExpRunItem seed, Pair, Set, Set> getParentsOldAndBusted(ExpRunItem start) { if (isUnknownMaterial(start)) - return Pair.of(Collections.emptySet(), Collections.emptySet()); + return Pair.of(emptySet(), emptySet()); List runsToInvestigate = new ArrayList<>(); ExpRun parentRun = start.getRun(); @@ -2012,7 +2037,7 @@ public Pair, Set> getChildren(Container c, User user, private Pair, Set> getChildrenOldAndBusted(ExpRunItem start) { if (isUnknownMaterial(start)) - return Pair.of(Collections.emptySet(), Collections.emptySet()); + return Pair.of(emptySet(), emptySet()); List runsToInvestigate = new ArrayList<>(); if (start instanceof ExpData) @@ -2232,7 +2257,10 @@ else if (seed instanceof IdentifiableBase) throw new RuntimeException("Lineage not available for unknown object: " + seed.getLSID()); if (seed instanceof ExpRunItem && isUnknownMaterial((ExpRunItem) seed)) - throw new RuntimeException("Lineage not available for unknown material: " + seed.getLSID()); + { + LOG.warn("Lineage not available for unknown material: " + seed.getLSID()); + continue; + } // ensure that the protocol output lineage is in the same container as the request if (c != null && !c.equals(seed.getContainer())) @@ -2245,6 +2273,9 @@ else if (seed instanceof IdentifiableBase) throw new RuntimeException("Requested lineage for duplicate objectId seed: " + objectId); } + if (seedObjectIds.isEmpty()) + return new ExpLineage(seeds, emptySet(), emptySet(), emptySet(), emptySet(), emptySet()); + options.setUseObjectIds(true); SQLFragment sqlf = generateExperimentTreeSQLObjectIdsSeeds(seedObjectIds, options); Set dataIds = new HashSet<>(); @@ -2256,7 +2287,7 @@ else if (seed instanceof IdentifiableBase) for (Identifiable seed : seeds) { // create additional edges from the run for each ExpMaterial or ExpData seed - if (seed instanceof ExpRunItem) + if (seed instanceof ExpRunItem && !isUnknownMaterial((ExpRunItem)seed)) { Pair, Map> pair = collectRunsAndRolesToInvestigate((ExpRunItem)seed, options); @@ -2858,8 +2889,8 @@ private void syncRunEdges(int runId, Integer runObjectId, String runLsid, Contai toMaterialLsids.add(row); }); - Set> provenanceStartingInputs = Collections.emptySet(); - Set> provenanceFinalOutputs = Collections.emptySet(); + Set> provenanceStartingInputs = emptySet(); + Set> provenanceFinalOutputs = emptySet(); ProvenanceService pvs = ProvenanceService.get(); if (pvs != null) @@ -6236,7 +6267,9 @@ public void registerExperimentRunTypeSource(ExperimentRunTypeSource source) @Override public void registerDataType(DataType type) { - _dataTypes.put(type.getNamespacePrefix(), type); + DataType existing = _dataTypes.put(type.getNamespacePrefix(), type); + if (existing != null) + throw new IllegalArgumentException(existing.getClass().getSimpleName() + " already claims namespace prefix '" + existing.getNamespacePrefix() + "'"); } @Override @@ -6265,11 +6298,19 @@ public DataType getDataType(String namespacePrefix) @Override public void registerProtocolImplementation(ProtocolImplementation impl) { - _protocolImplementations.put(impl.getName(), impl); + ProtocolImplementation existing = _protocolImplementations.put(impl.getName(), impl); + if (existing != null) + throw new IllegalArgumentException(existing.getClass().getSimpleName() + " already claims name '" + existing.getName() + "'"); + } + + @Override + public void registerProtocolHandler(ExperimentProtocolHandler handler) + { + _protocolHandlers.add(handler); } @Override - public ProtocolImplementation getProtocolImplementation(String name) + public @Nullable ProtocolImplementation getProtocolImplementation(String name) { return _protocolImplementations.get(name); } @@ -6290,7 +6331,9 @@ public List getRunEditors() @Override public void registerProtocolInputCriteria(ExpProtocolInputCriteria.Factory factory) { - _protocolInputCriteriaFactories.put(factory.getName(), factory); + ExpProtocolInputCriteria.Factory existing = _protocolInputCriteriaFactories.put(factory.getName(), factory); + if (existing != null) + throw new IllegalArgumentException(existing.getClass().getSimpleName() + " already claims name '" + existing.getName() + "'"); } @NotNull diff --git a/experiment/src/org/labkey/experiment/controllers/exp/experimentRunGraphView.jsp b/experiment/src/org/labkey/experiment/controllers/exp/experimentRunGraphView.jsp index c363fd690fa..f11a2a25173 100644 --- a/experiment/src/org/labkey/experiment/controllers/exp/experimentRunGraphView.jsp +++ b/experiment/src/org/labkey/experiment/controllers/exp/experimentRunGraphView.jsp @@ -42,6 +42,7 @@ ViewContext context = getViewContext(); ExperimentRunGraphModel model = (ExperimentRunGraphModel)HttpView.currentModel(); boolean isSummaryView = !model.isDetail(); + boolean isBetaViewEnabled = getActionURL().getParameter("betaGraph") != null; String uniqueId = "" + UniqueID.getServerSessionScopedUID(); String appId = "run-graph-app-" + uniqueId; @@ -67,11 +68,11 @@ %> <%=button("Toggle Beta Graph (new!)").id(toggleBtnId).style("display: inline-block; float: right;")%>
-
+
" id="<%=h(graphTabId)%>"> <% } %> @@ -112,14 +113,14 @@ { %>
-
+
" id="<%=h(graphTabBetaId)%>">