From ccbf4c8b6a4a541d70acc14f1d03d0b1d4c030f9 Mon Sep 17 00:00:00 2001 From: XingY Date: Wed, 22 Oct 2025 19:51:44 -0700 Subject: [PATCH 1/2] Add auditing of what method was used for data imports, updates, deletes --- .../org/labkey/luminex/LuminexRunCreator.java | 271 +++---- .../luminex/query/WellExclusionTable.java | 700 +++++++++--------- 2 files changed, 486 insertions(+), 485 deletions(-) diff --git a/luminex/src/org/labkey/luminex/LuminexRunCreator.java b/luminex/src/org/labkey/luminex/LuminexRunCreator.java index c06e72390..b54c4af0a 100644 --- a/luminex/src/org/labkey/luminex/LuminexRunCreator.java +++ b/luminex/src/org/labkey/luminex/LuminexRunCreator.java @@ -1,135 +1,136 @@ -/* - * Copyright (c) 2012-2018 LabKey Corporation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.labkey.luminex; - -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; -import org.labkey.api.data.Container; -import org.labkey.api.exp.ExperimentException; -import org.labkey.api.exp.ObjectProperty; -import org.labkey.api.exp.OntologyManager; -import org.labkey.api.exp.OntologyObject; -import org.labkey.api.exp.api.ExpData; -import org.labkey.api.exp.api.ExpExperiment; -import org.labkey.api.exp.api.ExpRun; -import org.labkey.api.exp.api.ExperimentService; -import org.labkey.api.exp.property.DomainProperty; -import org.labkey.api.query.BatchValidationException; -import org.labkey.api.query.ValidationException; -import org.labkey.api.security.permissions.DeletePermission; -import org.labkey.api.assay.AssayRunUploadContext; -import org.labkey.api.assay.DefaultAssayRunCreator; -import org.labkey.luminex.model.Analyte; - -import java.util.List; -import java.util.Map; - -/** - * Saves analyte specific properties and handles re-run - * User: jeckels - * Date: Feb 13, 2012 - */ -public class LuminexRunCreator extends DefaultAssayRunCreator -{ - /** Lock object to only allow one thread to be running a Luminex transform script and importing its results at a time, issue 17424 */ - public static final Object LOCK_OBJECT = new Object(); - - public LuminexRunCreator(LuminexAssayProvider provider) - { - super(provider); - } - - @Override - public ExpExperiment saveExperimentRun(AssayRunUploadContext uploadContext, @Nullable ExpExperiment batch, @NotNull ExpRun run, boolean forceSaveBatchProps) throws ExperimentException, ValidationException - { - // Only allow one thread to be running a Luminex transform script and importing its results at a time - // See issue 17424 - synchronized (LOCK_OBJECT) - { - LuminexRunContext context = (LuminexRunContext)uploadContext; - batch = super.saveExperimentRun(context, batch, run, forceSaveBatchProps); - Container container = context.getContainer(); - - // Save the analyte properties - List outputs = run.getDataOutputs(); - for (ExpData output : outputs) - { - var dataId = output.getRowId(); - - for (Analyte analyte : LuminexManager.get().getAnalytes(dataId)) - { - Map properties = context.getAnalyteProperties(analyte.getName()); - - ObjectProperty[] objProperties = new ObjectProperty[properties.size()]; - int i = 0; - for (Map.Entry entry : properties.entrySet()) - { - ObjectProperty property = new ObjectProperty(analyte.getLsid(), - container, entry.getKey().getPropertyURI(), - entry.getValue(), entry.getKey().getPropertyDescriptor().getPropertyType()); - objProperties[i++] = property; - } - removeExistingAnalyteProperties(container, analyte); - OntologyManager.insertProperties(container, analyte.getLsid(), objProperties); - } - } - } - - try - { - handleReRun(uploadContext, run); - } - catch (BatchValidationException e) - { - throw new ExperimentException(e); - } - return batch; - } - - private void handleReRun(AssayRunUploadContext uploadContext, ExpRun run) throws ValidationException, BatchValidationException - { - if (uploadContext.getReRunId() != null) - { - ExpRun replacedRun = ExperimentService.get().getExpRun(uploadContext.getReRunId().intValue()); - - if (replacedRun != null) - { - // Migrate the original Created and CreatedBy values from the old run to the new run - run.setCreated(replacedRun.getCreated()); - run.setCreatedBy(replacedRun.getCreatedBy()); - run.save(uploadContext.getUser()); - - if (uploadContext instanceof LuminexRunContext && ((LuminexRunContext)uploadContext).getRetainExclusions()) - LuminexManager.get().retainExclusions((LuminexRunContext)uploadContext, replacedRun, run); - - // Delete the old run, which has been replaced - if (replacedRun.getContainer().hasPermission(uploadContext.getUser(), DeletePermission.class)) - { - replacedRun.delete(uploadContext.getUser()); - } - } - } - } - - private void removeExistingAnalyteProperties(Container container, Analyte analyte) - { - OntologyObject analyteObject = OntologyManager.getOntologyObject(container, analyte.getLsid()); - if (analyteObject != null) - { - OntologyManager.deleteProperties(container, analyteObject.getObjectId()); - } - } -} +/* + * Copyright (c) 2012-2018 LabKey Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.labkey.luminex; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.labkey.api.audit.TransactionAuditProvider; +import org.labkey.api.data.Container; +import org.labkey.api.exp.ExperimentException; +import org.labkey.api.exp.ObjectProperty; +import org.labkey.api.exp.OntologyManager; +import org.labkey.api.exp.OntologyObject; +import org.labkey.api.exp.api.ExpData; +import org.labkey.api.exp.api.ExpExperiment; +import org.labkey.api.exp.api.ExpRun; +import org.labkey.api.exp.api.ExperimentService; +import org.labkey.api.exp.property.DomainProperty; +import org.labkey.api.query.BatchValidationException; +import org.labkey.api.query.ValidationException; +import org.labkey.api.security.permissions.DeletePermission; +import org.labkey.api.assay.AssayRunUploadContext; +import org.labkey.api.assay.DefaultAssayRunCreator; +import org.labkey.luminex.model.Analyte; + +import java.util.List; +import java.util.Map; + +/** + * Saves analyte specific properties and handles re-run + * User: jeckels + * Date: Feb 13, 2012 + */ +public class LuminexRunCreator extends DefaultAssayRunCreator +{ + /** Lock object to only allow one thread to be running a Luminex transform script and importing its results at a time, issue 17424 */ + public static final Object LOCK_OBJECT = new Object(); + + public LuminexRunCreator(LuminexAssayProvider provider) + { + super(provider); + } + + @Override + public ExpExperiment saveExperimentRun(AssayRunUploadContext uploadContext, @Nullable ExpExperiment batch, @NotNull ExpRun run, boolean forceSaveBatchProps, @Nullable Map transactionDetails) throws ExperimentException, ValidationException + { + // Only allow one thread to be running a Luminex transform script and importing its results at a time + // See issue 17424 + synchronized (LOCK_OBJECT) + { + LuminexRunContext context = (LuminexRunContext)uploadContext; + batch = super.saveExperimentRun(context, batch, run, forceSaveBatchProps, transactionDetails); + Container container = context.getContainer(); + + // Save the analyte properties + List outputs = run.getDataOutputs(); + for (ExpData output : outputs) + { + var dataId = output.getRowId(); + + for (Analyte analyte : LuminexManager.get().getAnalytes(dataId)) + { + Map properties = context.getAnalyteProperties(analyte.getName()); + + ObjectProperty[] objProperties = new ObjectProperty[properties.size()]; + int i = 0; + for (Map.Entry entry : properties.entrySet()) + { + ObjectProperty property = new ObjectProperty(analyte.getLsid(), + container, entry.getKey().getPropertyURI(), + entry.getValue(), entry.getKey().getPropertyDescriptor().getPropertyType()); + objProperties[i++] = property; + } + removeExistingAnalyteProperties(container, analyte); + OntologyManager.insertProperties(container, analyte.getLsid(), objProperties); + } + } + } + + try + { + handleReRun(uploadContext, run); + } + catch (BatchValidationException e) + { + throw new ExperimentException(e); + } + return batch; + } + + private void handleReRun(AssayRunUploadContext uploadContext, ExpRun run) throws ValidationException, BatchValidationException + { + if (uploadContext.getReRunId() != null) + { + ExpRun replacedRun = ExperimentService.get().getExpRun(uploadContext.getReRunId().intValue()); + + if (replacedRun != null) + { + // Migrate the original Created and CreatedBy values from the old run to the new run + run.setCreated(replacedRun.getCreated()); + run.setCreatedBy(replacedRun.getCreatedBy()); + run.save(uploadContext.getUser()); + + if (uploadContext instanceof LuminexRunContext && ((LuminexRunContext)uploadContext).getRetainExclusions()) + LuminexManager.get().retainExclusions((LuminexRunContext)uploadContext, replacedRun, run); + + // Delete the old run, which has been replaced + if (replacedRun.getContainer().hasPermission(uploadContext.getUser(), DeletePermission.class)) + { + replacedRun.delete(uploadContext.getUser()); + } + } + } + } + + private void removeExistingAnalyteProperties(Container container, Analyte analyte) + { + OntologyObject analyteObject = OntologyManager.getOntologyObject(container, analyte.getLsid()); + if (analyteObject != null) + { + OntologyManager.deleteProperties(container, analyteObject.getObjectId()); + } + } +} diff --git a/luminex/src/org/labkey/luminex/query/WellExclusionTable.java b/luminex/src/org/labkey/luminex/query/WellExclusionTable.java index 3dda61f9b..74541a50f 100644 --- a/luminex/src/org/labkey/luminex/query/WellExclusionTable.java +++ b/luminex/src/org/labkey/luminex/query/WellExclusionTable.java @@ -1,350 +1,350 @@ -/* - * Copyright (c) 2014-2019 LabKey Corporation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.labkey.luminex.query; - -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; -import org.labkey.api.data.Container; -import org.labkey.api.data.ContainerFilter; -import org.labkey.api.data.DataColumn; -import org.labkey.api.data.DbScope; -import org.labkey.api.data.JdbcType; -import org.labkey.api.data.MultiValuedForeignKey; -import org.labkey.api.data.MultiValuedRenderContext; -import org.labkey.api.data.RenderContext; -import org.labkey.api.data.SQLFragment; -import org.labkey.api.data.SqlSelector; -import org.labkey.api.data.TableInfo; -import org.labkey.api.exp.ExperimentException; -import org.labkey.api.exp.api.ExpData; -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.query.ExpSchema; -import org.labkey.api.query.BatchValidationException; -import org.labkey.api.query.DuplicateKeyException; -import org.labkey.api.query.ExprColumn; -import org.labkey.api.query.FieldKey; -import org.labkey.api.query.InvalidKeyException; -import org.labkey.api.query.LookupForeignKey; -import org.labkey.api.query.QueryUpdateService; -import org.labkey.api.query.QueryUpdateServiceException; -import org.labkey.api.query.ValidationException; -import org.labkey.api.security.User; -import org.labkey.api.security.permissions.Permission; -import org.labkey.api.assay.AssayProvider; -import org.labkey.api.assay.AssayRunDatabaseContext; -import org.labkey.api.assay.AssayService; -import org.labkey.api.util.HtmlString; -import org.labkey.api.view.UnauthorizedException; -import org.labkey.api.writer.HtmlWriter; -import org.labkey.luminex.LuminexManager; -import org.labkey.luminex.LuminexRunCreator; - -import java.sql.SQLException; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.TreeSet; - -/** - * User: jeckels - * Date: Jun 29, 2011 - */ -public class WellExclusionTable extends AbstractExclusionTable -{ - public WellExclusionTable(LuminexProtocolSchema schema, ContainerFilter cf, boolean filter) - { - super(LuminexProtocolSchema.getTableInfoWellExclusion(), schema, cf, filter); - - getMutableColumn("DataId").setLabel("Data File"); - getMutableColumn("DataId").setFk(new ExpSchema(schema.getUser(), schema.getContainer()).getDataIdForeignKey(cf)); - - getMutableColumn("Analytes").setFk(new MultiValuedForeignKey(new LookupForeignKey(cf, "WellExclusionId", null) - { - @Override - public TableInfo getLookupTableInfo() - { - return _userSchema.createWellExclusionAnalyteTable(getLookupContainerFilter()); - } - }, "AnalyteId")); - getMutableColumn("Analytes").setUserEditable(false); - - SQLFragment joinSQL = new SQLFragment(" FROM "); - joinSQL.append(LuminexProtocolSchema.getTableInfoDataRow(), "dr"); - joinSQL.append(" WHERE (dr.Description = "); - joinSQL.append(ExprColumn.STR_TABLE_ALIAS); - joinSQL.append(".Description OR (dr.Description IS NULL AND "); - joinSQL.append(ExprColumn.STR_TABLE_ALIAS); - joinSQL.append(".Description IS NULL)) AND dr.DataId = "); - joinSQL.append(ExprColumn.STR_TABLE_ALIAS); - joinSQL.append(".DataId AND dr.Type = "); - joinSQL.append(ExprColumn.STR_TABLE_ALIAS); - joinSQL.append(".Type"); - - SQLFragment wellRoleSQL = new SQLFragment("SELECT WellRole FROM (SELECT DISTINCT dr.WellRole"); - wellRoleSQL.append(joinSQL); - wellRoleSQL.append(") x"); - addColumn(new ExprColumn(this, "Well Role", schema.getDbSchema().getSqlDialect().getSelectConcat(wellRoleSQL, ","), JdbcType.VARCHAR)); - - SQLFragment wellSQL = new SQLFragment("SELECT Well FROM (SELECT DISTINCT dr.Well").append(joinSQL).append(") x"); - //only pull in wells list for replicate group exclusions - wellSQL.append(" WHERE ").append(ExprColumn.STR_TABLE_ALIAS).append(".Well IS NULL"); - ExprColumn wellsCol = new ExprColumn(this, "Wells", schema.getDbSchema().getSqlDialect().getSelectConcat(wellSQL, ","), JdbcType.VARCHAR); - wellsCol.setDisplayColumnFactory(colInfo -> new DataColumn(colInfo) - { - @Override - public void renderGridCellContents(RenderContext ctx, HtmlWriter out) - { - Object o = getDisplayValue(ctx); - out.write(null == o ? HtmlString.NBSP : HtmlString.of(o.toString())); - } - - @Override - public Object getDisplayValue(RenderContext ctx) - { - Object result = getValue(ctx); - if (null != result) - { - // get the list of unique wells (by splitting the concatenated string) - TreeSet uniqueWells = new TreeSet<>(); - uniqueWells.addAll(Arrays.asList(result.toString().split(MultiValuedRenderContext.VALUE_DELIMITER_REGEX))); - - // put the unique wells back into a comma separated string - StringBuilder sb = new StringBuilder(); - String comma = ""; - for (String well : uniqueWells) - { - sb.append(comma); - sb.append(well); - comma = ","; - } - result = sb.toString(); - } - return result; - } - }); - addColumn(wellsCol); - - List defaultCols = new ArrayList<>(getDefaultVisibleColumns()); - defaultCols.remove(FieldKey.fromParts("ModifiedBy")); - defaultCols.remove(FieldKey.fromParts("Modified")); - defaultCols.add(0, FieldKey.fromParts("DataId", "Run")); - setDefaultVisibleColumns(defaultCols); - } - - @Override - protected SQLFragment createContainerFilterSQL(ContainerFilter filter) - { - return getUserSchema().createDataIdContainerFilterSQL("DataId", filter); - } - - @Override - public QueryUpdateService getUpdateService() - { - return new ExclusionUpdateService(this, getRealTable(), LuminexProtocolSchema.getTableInfoWellExclusionAnalyte(), "WellExclusionId") - { - private final Set _runsToRefresh = new HashSet<>(); - - private Integer getDataId(Map rowMap) throws QueryUpdateServiceException - { - Integer dataId = convertToInteger(rowMap.get("DataId")); - if (dataId == null) - { - throw new QueryUpdateServiceException("No DataId specified"); - } - return dataId; - } - - @Override - protected void checkPermissions(User user, Map rowMap, Class permission) throws QueryUpdateServiceException - { - ExpData data = getData(rowMap); - if (!data.getContainer().hasPermission(user, permission)) - { - throw new UnauthorizedException(); - } - } - - private ExpData getData(Map rowMap) throws QueryUpdateServiceException - { - Integer dataId = getDataId(rowMap); - ExpData data = ExperimentService.get().getExpData(dataId); - if (data == null) - { - throw new QueryUpdateServiceException("No such data file: " + dataId); - } - return data; - } - - @Override - protected @NotNull ExpRun resolveRun(Map rowMap) throws QueryUpdateServiceException - { - ExpData data = getData(rowMap); - ExpProtocolApplication protApp = data.getSourceApplication(); - if (protApp == null) - { - throw new QueryUpdateServiceException("Unable to resolve run for data " + data.getRowId() + ", no source protocol application"); - } - ExpRun run = protApp.getRun(); - if (run == null) - { - throw new QueryUpdateServiceException("Unable to resolve run for data " + data.getRowId()); - } - if (!_runsToRefresh.contains(run)) - { - String description = rowMap.get("Description") == null ? null : rowMap.get("Description").toString(); - String type = rowMap.get("Type") == null ? null : rowMap.get("Type").toString(); - String bTRUE = getSchema().getSqlDialect().getBooleanTRUE(); - - SQLFragment dataRowSQL = new SQLFragment("SELECT * FROM "); - dataRowSQL.append(LuminexProtocolSchema.getTableInfoDataRow(), "dr"); - //dataRowSQL.append(" LEFT JOIN ").append(LuminexProtocolSchema.getTableInfoTitration(), "t").append(" ON dr.TitrationId = t.RowId "); - //dataRowSQL.append(" WHERE dr.TitrationId IS NOT NULL "); - dataRowSQL.append(" WHERE dr.DataId = ? AND dr.Description "); - dataRowSQL.add(data.getRowId()); - - if (description == null) - { - dataRowSQL.append("IS NULL"); - } - else - { - dataRowSQL.append("= ?"); - dataRowSQL.add(description); - } - - dataRowSQL.append(" AND dr.Type "); - - if (type == null) - { - dataRowSQL.append("IS NULL"); - } - else - { - dataRowSQL.append("= ?"); - dataRowSQL.add(type); - } - - if (new SqlSelector(LuminexProtocolSchema.getSchema(), dataRowSQL).exists()) - { - _runsToRefresh.add(run); - } - } - - return run; - } - - @Override - public List> insertRows(User user, Container container, List> rows, BatchValidationException errors, @Nullable Map configParameters, Map extraScriptContext) throws DuplicateKeyException, QueryUpdateServiceException, SQLException - { - // Only allow one thread to be running a Luminex transform script and importing its results at a time - // See issue 17424 - synchronized (LuminexRunCreator.LOCK_OBJECT) - { - try (DbScope.Transaction transaction = LuminexProtocolSchema.getSchema().getScope().ensureTransaction()) - { - List> result = super.insertRows(user, container, rows, errors, configParameters, extraScriptContext); - - if (extraScriptContext != null && (Boolean)extraScriptContext.getOrDefault(LuminexManager.RERUN_TRANSFORM, false)) - rerunTransformScripts(errors); - - if (errors.hasErrors()) - throw errors; - - transaction.commit(); - return result; - } - catch(BatchValidationException e) - { - throw new QueryUpdateServiceException(e.getMessage(), e); - } - } - } - - @Override - public List> deleteRows(User user, Container container, List> keys, @Nullable Map configParameters, @Nullable Map extraScriptContext) throws InvalidKeyException, BatchValidationException, QueryUpdateServiceException, SQLException - { - // Only allow one thread to be running a Luminex transform script and importing its results at a time - // See issue 17424 - synchronized (LuminexRunCreator.LOCK_OBJECT) - { - try (DbScope.Transaction transaction = LuminexProtocolSchema.getSchema().getScope().ensureTransaction()) - { - List> result = super.deleteRows(user, container, keys, configParameters, extraScriptContext); - - BatchValidationException errors = new BatchValidationException(); - if (extraScriptContext != null && (Boolean)extraScriptContext.getOrDefault(LuminexManager.RERUN_TRANSFORM, false)) - rerunTransformScripts(errors); - - if (errors.hasErrors()) - throw errors; - - transaction.commit(); - return result; - } - } - } - - @Override - public List> updateRows(User user, Container container, List> rows, List> oldKeys, BatchValidationException errors, @Nullable Map configParameters, Map extraScriptContext) throws InvalidKeyException, BatchValidationException, QueryUpdateServiceException, SQLException - { - // Only allow one thread to be running a Luminex transform script and importing its results at a time - // See issue 17424 - synchronized (LuminexRunCreator.LOCK_OBJECT) - { - try (DbScope.Transaction transaction = LuminexProtocolSchema.getSchema().getScope().ensureTransaction()) - { - List> result = super.updateRows(user, container, rows, oldKeys, errors, configParameters, extraScriptContext); - - if (extraScriptContext != null && (Boolean)extraScriptContext.getOrDefault(LuminexManager.RERUN_TRANSFORM, false)) - rerunTransformScripts(errors); - - if (errors.hasErrors()) - throw errors; - - transaction.commit(); - return result; - } - } - } - - private void rerunTransformScripts(BatchValidationException errors) throws QueryUpdateServiceException - { - try - { - for (ExpRun run : _runsToRefresh) - { - AssayProvider provider = AssayService.get().getProvider(run); - AssayRunDatabaseContext context = provider.createRunDatabaseContext(run, _userSchema.getUser(), null); - provider.getRunCreator().saveExperimentRun(context, AssayService.get().findBatch(run), run, false); - } - } - catch (ExperimentException e) - { - throw new QueryUpdateServiceException(e); - } - catch (ValidationException e) - { - errors.addRowError(e); - } - } - }; - } -} +/* + * Copyright (c) 2014-2019 LabKey Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.labkey.luminex.query; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.labkey.api.data.Container; +import org.labkey.api.data.ContainerFilter; +import org.labkey.api.data.DataColumn; +import org.labkey.api.data.DbScope; +import org.labkey.api.data.JdbcType; +import org.labkey.api.data.MultiValuedForeignKey; +import org.labkey.api.data.MultiValuedRenderContext; +import org.labkey.api.data.RenderContext; +import org.labkey.api.data.SQLFragment; +import org.labkey.api.data.SqlSelector; +import org.labkey.api.data.TableInfo; +import org.labkey.api.exp.ExperimentException; +import org.labkey.api.exp.api.ExpData; +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.query.ExpSchema; +import org.labkey.api.query.BatchValidationException; +import org.labkey.api.query.DuplicateKeyException; +import org.labkey.api.query.ExprColumn; +import org.labkey.api.query.FieldKey; +import org.labkey.api.query.InvalidKeyException; +import org.labkey.api.query.LookupForeignKey; +import org.labkey.api.query.QueryUpdateService; +import org.labkey.api.query.QueryUpdateServiceException; +import org.labkey.api.query.ValidationException; +import org.labkey.api.security.User; +import org.labkey.api.security.permissions.Permission; +import org.labkey.api.assay.AssayProvider; +import org.labkey.api.assay.AssayRunDatabaseContext; +import org.labkey.api.assay.AssayService; +import org.labkey.api.util.HtmlString; +import org.labkey.api.view.UnauthorizedException; +import org.labkey.api.writer.HtmlWriter; +import org.labkey.luminex.LuminexManager; +import org.labkey.luminex.LuminexRunCreator; + +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.TreeSet; + +/** + * User: jeckels + * Date: Jun 29, 2011 + */ +public class WellExclusionTable extends AbstractExclusionTable +{ + public WellExclusionTable(LuminexProtocolSchema schema, ContainerFilter cf, boolean filter) + { + super(LuminexProtocolSchema.getTableInfoWellExclusion(), schema, cf, filter); + + getMutableColumn("DataId").setLabel("Data File"); + getMutableColumn("DataId").setFk(new ExpSchema(schema.getUser(), schema.getContainer()).getDataIdForeignKey(cf)); + + getMutableColumn("Analytes").setFk(new MultiValuedForeignKey(new LookupForeignKey(cf, "WellExclusionId", null) + { + @Override + public TableInfo getLookupTableInfo() + { + return _userSchema.createWellExclusionAnalyteTable(getLookupContainerFilter()); + } + }, "AnalyteId")); + getMutableColumn("Analytes").setUserEditable(false); + + SQLFragment joinSQL = new SQLFragment(" FROM "); + joinSQL.append(LuminexProtocolSchema.getTableInfoDataRow(), "dr"); + joinSQL.append(" WHERE (dr.Description = "); + joinSQL.append(ExprColumn.STR_TABLE_ALIAS); + joinSQL.append(".Description OR (dr.Description IS NULL AND "); + joinSQL.append(ExprColumn.STR_TABLE_ALIAS); + joinSQL.append(".Description IS NULL)) AND dr.DataId = "); + joinSQL.append(ExprColumn.STR_TABLE_ALIAS); + joinSQL.append(".DataId AND dr.Type = "); + joinSQL.append(ExprColumn.STR_TABLE_ALIAS); + joinSQL.append(".Type"); + + SQLFragment wellRoleSQL = new SQLFragment("SELECT WellRole FROM (SELECT DISTINCT dr.WellRole"); + wellRoleSQL.append(joinSQL); + wellRoleSQL.append(") x"); + addColumn(new ExprColumn(this, "Well Role", schema.getDbSchema().getSqlDialect().getSelectConcat(wellRoleSQL, ","), JdbcType.VARCHAR)); + + SQLFragment wellSQL = new SQLFragment("SELECT Well FROM (SELECT DISTINCT dr.Well").append(joinSQL).append(") x"); + //only pull in wells list for replicate group exclusions + wellSQL.append(" WHERE ").append(ExprColumn.STR_TABLE_ALIAS).append(".Well IS NULL"); + ExprColumn wellsCol = new ExprColumn(this, "Wells", schema.getDbSchema().getSqlDialect().getSelectConcat(wellSQL, ","), JdbcType.VARCHAR); + wellsCol.setDisplayColumnFactory(colInfo -> new DataColumn(colInfo) + { + @Override + public void renderGridCellContents(RenderContext ctx, HtmlWriter out) + { + Object o = getDisplayValue(ctx); + out.write(null == o ? HtmlString.NBSP : HtmlString.of(o.toString())); + } + + @Override + public Object getDisplayValue(RenderContext ctx) + { + Object result = getValue(ctx); + if (null != result) + { + // get the list of unique wells (by splitting the concatenated string) + TreeSet uniqueWells = new TreeSet<>(); + uniqueWells.addAll(Arrays.asList(result.toString().split(MultiValuedRenderContext.VALUE_DELIMITER_REGEX))); + + // put the unique wells back into a comma separated string + StringBuilder sb = new StringBuilder(); + String comma = ""; + for (String well : uniqueWells) + { + sb.append(comma); + sb.append(well); + comma = ","; + } + result = sb.toString(); + } + return result; + } + }); + addColumn(wellsCol); + + List defaultCols = new ArrayList<>(getDefaultVisibleColumns()); + defaultCols.remove(FieldKey.fromParts("ModifiedBy")); + defaultCols.remove(FieldKey.fromParts("Modified")); + defaultCols.add(0, FieldKey.fromParts("DataId", "Run")); + setDefaultVisibleColumns(defaultCols); + } + + @Override + protected SQLFragment createContainerFilterSQL(ContainerFilter filter) + { + return getUserSchema().createDataIdContainerFilterSQL("DataId", filter); + } + + @Override + public QueryUpdateService getUpdateService() + { + return new ExclusionUpdateService(this, getRealTable(), LuminexProtocolSchema.getTableInfoWellExclusionAnalyte(), "WellExclusionId") + { + private final Set _runsToRefresh = new HashSet<>(); + + private Integer getDataId(Map rowMap) throws QueryUpdateServiceException + { + Integer dataId = convertToInteger(rowMap.get("DataId")); + if (dataId == null) + { + throw new QueryUpdateServiceException("No DataId specified"); + } + return dataId; + } + + @Override + protected void checkPermissions(User user, Map rowMap, Class permission) throws QueryUpdateServiceException + { + ExpData data = getData(rowMap); + if (!data.getContainer().hasPermission(user, permission)) + { + throw new UnauthorizedException(); + } + } + + private ExpData getData(Map rowMap) throws QueryUpdateServiceException + { + Integer dataId = getDataId(rowMap); + ExpData data = ExperimentService.get().getExpData(dataId); + if (data == null) + { + throw new QueryUpdateServiceException("No such data file: " + dataId); + } + return data; + } + + @Override + protected @NotNull ExpRun resolveRun(Map rowMap) throws QueryUpdateServiceException + { + ExpData data = getData(rowMap); + ExpProtocolApplication protApp = data.getSourceApplication(); + if (protApp == null) + { + throw new QueryUpdateServiceException("Unable to resolve run for data " + data.getRowId() + ", no source protocol application"); + } + ExpRun run = protApp.getRun(); + if (run == null) + { + throw new QueryUpdateServiceException("Unable to resolve run for data " + data.getRowId()); + } + if (!_runsToRefresh.contains(run)) + { + String description = rowMap.get("Description") == null ? null : rowMap.get("Description").toString(); + String type = rowMap.get("Type") == null ? null : rowMap.get("Type").toString(); + String bTRUE = getSchema().getSqlDialect().getBooleanTRUE(); + + SQLFragment dataRowSQL = new SQLFragment("SELECT * FROM "); + dataRowSQL.append(LuminexProtocolSchema.getTableInfoDataRow(), "dr"); + //dataRowSQL.append(" LEFT JOIN ").append(LuminexProtocolSchema.getTableInfoTitration(), "t").append(" ON dr.TitrationId = t.RowId "); + //dataRowSQL.append(" WHERE dr.TitrationId IS NOT NULL "); + dataRowSQL.append(" WHERE dr.DataId = ? AND dr.Description "); + dataRowSQL.add(data.getRowId()); + + if (description == null) + { + dataRowSQL.append("IS NULL"); + } + else + { + dataRowSQL.append("= ?"); + dataRowSQL.add(description); + } + + dataRowSQL.append(" AND dr.Type "); + + if (type == null) + { + dataRowSQL.append("IS NULL"); + } + else + { + dataRowSQL.append("= ?"); + dataRowSQL.add(type); + } + + if (new SqlSelector(LuminexProtocolSchema.getSchema(), dataRowSQL).exists()) + { + _runsToRefresh.add(run); + } + } + + return run; + } + + @Override + public List> insertRows(User user, Container container, List> rows, BatchValidationException errors, @Nullable Map configParameters, Map extraScriptContext) throws DuplicateKeyException, QueryUpdateServiceException, SQLException + { + // Only allow one thread to be running a Luminex transform script and importing its results at a time + // See issue 17424 + synchronized (LuminexRunCreator.LOCK_OBJECT) + { + try (DbScope.Transaction transaction = LuminexProtocolSchema.getSchema().getScope().ensureTransaction()) + { + List> result = super.insertRows(user, container, rows, errors, configParameters, extraScriptContext); + + if (extraScriptContext != null && (Boolean)extraScriptContext.getOrDefault(LuminexManager.RERUN_TRANSFORM, false)) + rerunTransformScripts(errors); + + if (errors.hasErrors()) + throw errors; + + transaction.commit(); + return result; + } + catch(BatchValidationException e) + { + throw new QueryUpdateServiceException(e.getMessage(), e); + } + } + } + + @Override + public List> deleteRows(User user, Container container, List> keys, @Nullable Map configParameters, @Nullable Map extraScriptContext) throws InvalidKeyException, BatchValidationException, QueryUpdateServiceException, SQLException + { + // Only allow one thread to be running a Luminex transform script and importing its results at a time + // See issue 17424 + synchronized (LuminexRunCreator.LOCK_OBJECT) + { + try (DbScope.Transaction transaction = LuminexProtocolSchema.getSchema().getScope().ensureTransaction()) + { + List> result = super.deleteRows(user, container, keys, configParameters, extraScriptContext); + + BatchValidationException errors = new BatchValidationException(); + if (extraScriptContext != null && (Boolean)extraScriptContext.getOrDefault(LuminexManager.RERUN_TRANSFORM, false)) + rerunTransformScripts(errors); + + if (errors.hasErrors()) + throw errors; + + transaction.commit(); + return result; + } + } + } + + @Override + public List> updateRows(User user, Container container, List> rows, List> oldKeys, BatchValidationException errors, @Nullable Map configParameters, Map extraScriptContext) throws InvalidKeyException, BatchValidationException, QueryUpdateServiceException, SQLException + { + // Only allow one thread to be running a Luminex transform script and importing its results at a time + // See issue 17424 + synchronized (LuminexRunCreator.LOCK_OBJECT) + { + try (DbScope.Transaction transaction = LuminexProtocolSchema.getSchema().getScope().ensureTransaction()) + { + List> result = super.updateRows(user, container, rows, oldKeys, errors, configParameters, extraScriptContext); + + if (extraScriptContext != null && (Boolean)extraScriptContext.getOrDefault(LuminexManager.RERUN_TRANSFORM, false)) + rerunTransformScripts(errors); + + if (errors.hasErrors()) + throw errors; + + transaction.commit(); + return result; + } + } + } + + private void rerunTransformScripts(BatchValidationException errors) throws QueryUpdateServiceException + { + try + { + for (ExpRun run : _runsToRefresh) + { + AssayProvider provider = AssayService.get().getProvider(run); + AssayRunDatabaseContext context = provider.createRunDatabaseContext(run, _userSchema.getUser(), null); + provider.getRunCreator().saveExperimentRun(context, AssayService.get().findBatch(run), run, false, null); + } + } + catch (ExperimentException e) + { + throw new QueryUpdateServiceException(e); + } + catch (ValidationException e) + { + errors.addRowError(e); + } + } + }; + } +} From cc15f70780549e3f763653064e3efe87a3dd65d1 Mon Sep 17 00:00:00 2001 From: XingY Date: Thu, 30 Oct 2025 11:43:32 -0700 Subject: [PATCH 2/2] crlf --- .../org/labkey/luminex/LuminexRunCreator.java | 272 +++---- .../luminex/query/WellExclusionTable.java | 700 +++++++++--------- 2 files changed, 486 insertions(+), 486 deletions(-) diff --git a/luminex/src/org/labkey/luminex/LuminexRunCreator.java b/luminex/src/org/labkey/luminex/LuminexRunCreator.java index b54c4af0a..ecf24c1b0 100644 --- a/luminex/src/org/labkey/luminex/LuminexRunCreator.java +++ b/luminex/src/org/labkey/luminex/LuminexRunCreator.java @@ -1,136 +1,136 @@ -/* - * Copyright (c) 2012-2018 LabKey Corporation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.labkey.luminex; - -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; -import org.labkey.api.audit.TransactionAuditProvider; -import org.labkey.api.data.Container; -import org.labkey.api.exp.ExperimentException; -import org.labkey.api.exp.ObjectProperty; -import org.labkey.api.exp.OntologyManager; -import org.labkey.api.exp.OntologyObject; -import org.labkey.api.exp.api.ExpData; -import org.labkey.api.exp.api.ExpExperiment; -import org.labkey.api.exp.api.ExpRun; -import org.labkey.api.exp.api.ExperimentService; -import org.labkey.api.exp.property.DomainProperty; -import org.labkey.api.query.BatchValidationException; -import org.labkey.api.query.ValidationException; -import org.labkey.api.security.permissions.DeletePermission; -import org.labkey.api.assay.AssayRunUploadContext; -import org.labkey.api.assay.DefaultAssayRunCreator; -import org.labkey.luminex.model.Analyte; - -import java.util.List; -import java.util.Map; - -/** - * Saves analyte specific properties and handles re-run - * User: jeckels - * Date: Feb 13, 2012 - */ -public class LuminexRunCreator extends DefaultAssayRunCreator -{ - /** Lock object to only allow one thread to be running a Luminex transform script and importing its results at a time, issue 17424 */ - public static final Object LOCK_OBJECT = new Object(); - - public LuminexRunCreator(LuminexAssayProvider provider) - { - super(provider); - } - - @Override - public ExpExperiment saveExperimentRun(AssayRunUploadContext uploadContext, @Nullable ExpExperiment batch, @NotNull ExpRun run, boolean forceSaveBatchProps, @Nullable Map transactionDetails) throws ExperimentException, ValidationException - { - // Only allow one thread to be running a Luminex transform script and importing its results at a time - // See issue 17424 - synchronized (LOCK_OBJECT) - { - LuminexRunContext context = (LuminexRunContext)uploadContext; - batch = super.saveExperimentRun(context, batch, run, forceSaveBatchProps, transactionDetails); - Container container = context.getContainer(); - - // Save the analyte properties - List outputs = run.getDataOutputs(); - for (ExpData output : outputs) - { - var dataId = output.getRowId(); - - for (Analyte analyte : LuminexManager.get().getAnalytes(dataId)) - { - Map properties = context.getAnalyteProperties(analyte.getName()); - - ObjectProperty[] objProperties = new ObjectProperty[properties.size()]; - int i = 0; - for (Map.Entry entry : properties.entrySet()) - { - ObjectProperty property = new ObjectProperty(analyte.getLsid(), - container, entry.getKey().getPropertyURI(), - entry.getValue(), entry.getKey().getPropertyDescriptor().getPropertyType()); - objProperties[i++] = property; - } - removeExistingAnalyteProperties(container, analyte); - OntologyManager.insertProperties(container, analyte.getLsid(), objProperties); - } - } - } - - try - { - handleReRun(uploadContext, run); - } - catch (BatchValidationException e) - { - throw new ExperimentException(e); - } - return batch; - } - - private void handleReRun(AssayRunUploadContext uploadContext, ExpRun run) throws ValidationException, BatchValidationException - { - if (uploadContext.getReRunId() != null) - { - ExpRun replacedRun = ExperimentService.get().getExpRun(uploadContext.getReRunId().intValue()); - - if (replacedRun != null) - { - // Migrate the original Created and CreatedBy values from the old run to the new run - run.setCreated(replacedRun.getCreated()); - run.setCreatedBy(replacedRun.getCreatedBy()); - run.save(uploadContext.getUser()); - - if (uploadContext instanceof LuminexRunContext && ((LuminexRunContext)uploadContext).getRetainExclusions()) - LuminexManager.get().retainExclusions((LuminexRunContext)uploadContext, replacedRun, run); - - // Delete the old run, which has been replaced - if (replacedRun.getContainer().hasPermission(uploadContext.getUser(), DeletePermission.class)) - { - replacedRun.delete(uploadContext.getUser()); - } - } - } - } - - private void removeExistingAnalyteProperties(Container container, Analyte analyte) - { - OntologyObject analyteObject = OntologyManager.getOntologyObject(container, analyte.getLsid()); - if (analyteObject != null) - { - OntologyManager.deleteProperties(container, analyteObject.getObjectId()); - } - } -} +/* + * Copyright (c) 2012-2018 LabKey Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.labkey.luminex; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.labkey.api.audit.TransactionAuditProvider; +import org.labkey.api.data.Container; +import org.labkey.api.exp.ExperimentException; +import org.labkey.api.exp.ObjectProperty; +import org.labkey.api.exp.OntologyManager; +import org.labkey.api.exp.OntologyObject; +import org.labkey.api.exp.api.ExpData; +import org.labkey.api.exp.api.ExpExperiment; +import org.labkey.api.exp.api.ExpRun; +import org.labkey.api.exp.api.ExperimentService; +import org.labkey.api.exp.property.DomainProperty; +import org.labkey.api.query.BatchValidationException; +import org.labkey.api.query.ValidationException; +import org.labkey.api.security.permissions.DeletePermission; +import org.labkey.api.assay.AssayRunUploadContext; +import org.labkey.api.assay.DefaultAssayRunCreator; +import org.labkey.luminex.model.Analyte; + +import java.util.List; +import java.util.Map; + +/** + * Saves analyte specific properties and handles re-run + * User: jeckels + * Date: Feb 13, 2012 + */ +public class LuminexRunCreator extends DefaultAssayRunCreator +{ + /** Lock object to only allow one thread to be running a Luminex transform script and importing its results at a time, issue 17424 */ + public static final Object LOCK_OBJECT = new Object(); + + public LuminexRunCreator(LuminexAssayProvider provider) + { + super(provider); + } + + @Override + public ExpExperiment saveExperimentRun(AssayRunUploadContext uploadContext, @Nullable ExpExperiment batch, @NotNull ExpRun run, boolean forceSaveBatchProps, @Nullable Map transactionDetails) throws ExperimentException, ValidationException + { + // Only allow one thread to be running a Luminex transform script and importing its results at a time + // See issue 17424 + synchronized (LOCK_OBJECT) + { + LuminexRunContext context = (LuminexRunContext)uploadContext; + batch = super.saveExperimentRun(context, batch, run, forceSaveBatchProps, transactionDetails); + Container container = context.getContainer(); + + // Save the analyte properties + List outputs = run.getDataOutputs(); + for (ExpData output : outputs) + { + var dataId = output.getRowId(); + + for (Analyte analyte : LuminexManager.get().getAnalytes(dataId)) + { + Map properties = context.getAnalyteProperties(analyte.getName()); + + ObjectProperty[] objProperties = new ObjectProperty[properties.size()]; + int i = 0; + for (Map.Entry entry : properties.entrySet()) + { + ObjectProperty property = new ObjectProperty(analyte.getLsid(), + container, entry.getKey().getPropertyURI(), + entry.getValue(), entry.getKey().getPropertyDescriptor().getPropertyType()); + objProperties[i++] = property; + } + removeExistingAnalyteProperties(container, analyte); + OntologyManager.insertProperties(container, analyte.getLsid(), objProperties); + } + } + } + + try + { + handleReRun(uploadContext, run); + } + catch (BatchValidationException e) + { + throw new ExperimentException(e); + } + return batch; + } + + private void handleReRun(AssayRunUploadContext uploadContext, ExpRun run) throws ValidationException, BatchValidationException + { + if (uploadContext.getReRunId() != null) + { + ExpRun replacedRun = ExperimentService.get().getExpRun(uploadContext.getReRunId().intValue()); + + if (replacedRun != null) + { + // Migrate the original Created and CreatedBy values from the old run to the new run + run.setCreated(replacedRun.getCreated()); + run.setCreatedBy(replacedRun.getCreatedBy()); + run.save(uploadContext.getUser()); + + if (uploadContext instanceof LuminexRunContext && ((LuminexRunContext)uploadContext).getRetainExclusions()) + LuminexManager.get().retainExclusions((LuminexRunContext)uploadContext, replacedRun, run); + + // Delete the old run, which has been replaced + if (replacedRun.getContainer().hasPermission(uploadContext.getUser(), DeletePermission.class)) + { + replacedRun.delete(uploadContext.getUser()); + } + } + } + } + + private void removeExistingAnalyteProperties(Container container, Analyte analyte) + { + OntologyObject analyteObject = OntologyManager.getOntologyObject(container, analyte.getLsid()); + if (analyteObject != null) + { + OntologyManager.deleteProperties(container, analyteObject.getObjectId()); + } + } +} diff --git a/luminex/src/org/labkey/luminex/query/WellExclusionTable.java b/luminex/src/org/labkey/luminex/query/WellExclusionTable.java index 74541a50f..4313084e3 100644 --- a/luminex/src/org/labkey/luminex/query/WellExclusionTable.java +++ b/luminex/src/org/labkey/luminex/query/WellExclusionTable.java @@ -1,350 +1,350 @@ -/* - * Copyright (c) 2014-2019 LabKey Corporation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.labkey.luminex.query; - -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; -import org.labkey.api.data.Container; -import org.labkey.api.data.ContainerFilter; -import org.labkey.api.data.DataColumn; -import org.labkey.api.data.DbScope; -import org.labkey.api.data.JdbcType; -import org.labkey.api.data.MultiValuedForeignKey; -import org.labkey.api.data.MultiValuedRenderContext; -import org.labkey.api.data.RenderContext; -import org.labkey.api.data.SQLFragment; -import org.labkey.api.data.SqlSelector; -import org.labkey.api.data.TableInfo; -import org.labkey.api.exp.ExperimentException; -import org.labkey.api.exp.api.ExpData; -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.query.ExpSchema; -import org.labkey.api.query.BatchValidationException; -import org.labkey.api.query.DuplicateKeyException; -import org.labkey.api.query.ExprColumn; -import org.labkey.api.query.FieldKey; -import org.labkey.api.query.InvalidKeyException; -import org.labkey.api.query.LookupForeignKey; -import org.labkey.api.query.QueryUpdateService; -import org.labkey.api.query.QueryUpdateServiceException; -import org.labkey.api.query.ValidationException; -import org.labkey.api.security.User; -import org.labkey.api.security.permissions.Permission; -import org.labkey.api.assay.AssayProvider; -import org.labkey.api.assay.AssayRunDatabaseContext; -import org.labkey.api.assay.AssayService; -import org.labkey.api.util.HtmlString; -import org.labkey.api.view.UnauthorizedException; -import org.labkey.api.writer.HtmlWriter; -import org.labkey.luminex.LuminexManager; -import org.labkey.luminex.LuminexRunCreator; - -import java.sql.SQLException; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.TreeSet; - -/** - * User: jeckels - * Date: Jun 29, 2011 - */ -public class WellExclusionTable extends AbstractExclusionTable -{ - public WellExclusionTable(LuminexProtocolSchema schema, ContainerFilter cf, boolean filter) - { - super(LuminexProtocolSchema.getTableInfoWellExclusion(), schema, cf, filter); - - getMutableColumn("DataId").setLabel("Data File"); - getMutableColumn("DataId").setFk(new ExpSchema(schema.getUser(), schema.getContainer()).getDataIdForeignKey(cf)); - - getMutableColumn("Analytes").setFk(new MultiValuedForeignKey(new LookupForeignKey(cf, "WellExclusionId", null) - { - @Override - public TableInfo getLookupTableInfo() - { - return _userSchema.createWellExclusionAnalyteTable(getLookupContainerFilter()); - } - }, "AnalyteId")); - getMutableColumn("Analytes").setUserEditable(false); - - SQLFragment joinSQL = new SQLFragment(" FROM "); - joinSQL.append(LuminexProtocolSchema.getTableInfoDataRow(), "dr"); - joinSQL.append(" WHERE (dr.Description = "); - joinSQL.append(ExprColumn.STR_TABLE_ALIAS); - joinSQL.append(".Description OR (dr.Description IS NULL AND "); - joinSQL.append(ExprColumn.STR_TABLE_ALIAS); - joinSQL.append(".Description IS NULL)) AND dr.DataId = "); - joinSQL.append(ExprColumn.STR_TABLE_ALIAS); - joinSQL.append(".DataId AND dr.Type = "); - joinSQL.append(ExprColumn.STR_TABLE_ALIAS); - joinSQL.append(".Type"); - - SQLFragment wellRoleSQL = new SQLFragment("SELECT WellRole FROM (SELECT DISTINCT dr.WellRole"); - wellRoleSQL.append(joinSQL); - wellRoleSQL.append(") x"); - addColumn(new ExprColumn(this, "Well Role", schema.getDbSchema().getSqlDialect().getSelectConcat(wellRoleSQL, ","), JdbcType.VARCHAR)); - - SQLFragment wellSQL = new SQLFragment("SELECT Well FROM (SELECT DISTINCT dr.Well").append(joinSQL).append(") x"); - //only pull in wells list for replicate group exclusions - wellSQL.append(" WHERE ").append(ExprColumn.STR_TABLE_ALIAS).append(".Well IS NULL"); - ExprColumn wellsCol = new ExprColumn(this, "Wells", schema.getDbSchema().getSqlDialect().getSelectConcat(wellSQL, ","), JdbcType.VARCHAR); - wellsCol.setDisplayColumnFactory(colInfo -> new DataColumn(colInfo) - { - @Override - public void renderGridCellContents(RenderContext ctx, HtmlWriter out) - { - Object o = getDisplayValue(ctx); - out.write(null == o ? HtmlString.NBSP : HtmlString.of(o.toString())); - } - - @Override - public Object getDisplayValue(RenderContext ctx) - { - Object result = getValue(ctx); - if (null != result) - { - // get the list of unique wells (by splitting the concatenated string) - TreeSet uniqueWells = new TreeSet<>(); - uniqueWells.addAll(Arrays.asList(result.toString().split(MultiValuedRenderContext.VALUE_DELIMITER_REGEX))); - - // put the unique wells back into a comma separated string - StringBuilder sb = new StringBuilder(); - String comma = ""; - for (String well : uniqueWells) - { - sb.append(comma); - sb.append(well); - comma = ","; - } - result = sb.toString(); - } - return result; - } - }); - addColumn(wellsCol); - - List defaultCols = new ArrayList<>(getDefaultVisibleColumns()); - defaultCols.remove(FieldKey.fromParts("ModifiedBy")); - defaultCols.remove(FieldKey.fromParts("Modified")); - defaultCols.add(0, FieldKey.fromParts("DataId", "Run")); - setDefaultVisibleColumns(defaultCols); - } - - @Override - protected SQLFragment createContainerFilterSQL(ContainerFilter filter) - { - return getUserSchema().createDataIdContainerFilterSQL("DataId", filter); - } - - @Override - public QueryUpdateService getUpdateService() - { - return new ExclusionUpdateService(this, getRealTable(), LuminexProtocolSchema.getTableInfoWellExclusionAnalyte(), "WellExclusionId") - { - private final Set _runsToRefresh = new HashSet<>(); - - private Integer getDataId(Map rowMap) throws QueryUpdateServiceException - { - Integer dataId = convertToInteger(rowMap.get("DataId")); - if (dataId == null) - { - throw new QueryUpdateServiceException("No DataId specified"); - } - return dataId; - } - - @Override - protected void checkPermissions(User user, Map rowMap, Class permission) throws QueryUpdateServiceException - { - ExpData data = getData(rowMap); - if (!data.getContainer().hasPermission(user, permission)) - { - throw new UnauthorizedException(); - } - } - - private ExpData getData(Map rowMap) throws QueryUpdateServiceException - { - Integer dataId = getDataId(rowMap); - ExpData data = ExperimentService.get().getExpData(dataId); - if (data == null) - { - throw new QueryUpdateServiceException("No such data file: " + dataId); - } - return data; - } - - @Override - protected @NotNull ExpRun resolveRun(Map rowMap) throws QueryUpdateServiceException - { - ExpData data = getData(rowMap); - ExpProtocolApplication protApp = data.getSourceApplication(); - if (protApp == null) - { - throw new QueryUpdateServiceException("Unable to resolve run for data " + data.getRowId() + ", no source protocol application"); - } - ExpRun run = protApp.getRun(); - if (run == null) - { - throw new QueryUpdateServiceException("Unable to resolve run for data " + data.getRowId()); - } - if (!_runsToRefresh.contains(run)) - { - String description = rowMap.get("Description") == null ? null : rowMap.get("Description").toString(); - String type = rowMap.get("Type") == null ? null : rowMap.get("Type").toString(); - String bTRUE = getSchema().getSqlDialect().getBooleanTRUE(); - - SQLFragment dataRowSQL = new SQLFragment("SELECT * FROM "); - dataRowSQL.append(LuminexProtocolSchema.getTableInfoDataRow(), "dr"); - //dataRowSQL.append(" LEFT JOIN ").append(LuminexProtocolSchema.getTableInfoTitration(), "t").append(" ON dr.TitrationId = t.RowId "); - //dataRowSQL.append(" WHERE dr.TitrationId IS NOT NULL "); - dataRowSQL.append(" WHERE dr.DataId = ? AND dr.Description "); - dataRowSQL.add(data.getRowId()); - - if (description == null) - { - dataRowSQL.append("IS NULL"); - } - else - { - dataRowSQL.append("= ?"); - dataRowSQL.add(description); - } - - dataRowSQL.append(" AND dr.Type "); - - if (type == null) - { - dataRowSQL.append("IS NULL"); - } - else - { - dataRowSQL.append("= ?"); - dataRowSQL.add(type); - } - - if (new SqlSelector(LuminexProtocolSchema.getSchema(), dataRowSQL).exists()) - { - _runsToRefresh.add(run); - } - } - - return run; - } - - @Override - public List> insertRows(User user, Container container, List> rows, BatchValidationException errors, @Nullable Map configParameters, Map extraScriptContext) throws DuplicateKeyException, QueryUpdateServiceException, SQLException - { - // Only allow one thread to be running a Luminex transform script and importing its results at a time - // See issue 17424 - synchronized (LuminexRunCreator.LOCK_OBJECT) - { - try (DbScope.Transaction transaction = LuminexProtocolSchema.getSchema().getScope().ensureTransaction()) - { - List> result = super.insertRows(user, container, rows, errors, configParameters, extraScriptContext); - - if (extraScriptContext != null && (Boolean)extraScriptContext.getOrDefault(LuminexManager.RERUN_TRANSFORM, false)) - rerunTransformScripts(errors); - - if (errors.hasErrors()) - throw errors; - - transaction.commit(); - return result; - } - catch(BatchValidationException e) - { - throw new QueryUpdateServiceException(e.getMessage(), e); - } - } - } - - @Override - public List> deleteRows(User user, Container container, List> keys, @Nullable Map configParameters, @Nullable Map extraScriptContext) throws InvalidKeyException, BatchValidationException, QueryUpdateServiceException, SQLException - { - // Only allow one thread to be running a Luminex transform script and importing its results at a time - // See issue 17424 - synchronized (LuminexRunCreator.LOCK_OBJECT) - { - try (DbScope.Transaction transaction = LuminexProtocolSchema.getSchema().getScope().ensureTransaction()) - { - List> result = super.deleteRows(user, container, keys, configParameters, extraScriptContext); - - BatchValidationException errors = new BatchValidationException(); - if (extraScriptContext != null && (Boolean)extraScriptContext.getOrDefault(LuminexManager.RERUN_TRANSFORM, false)) - rerunTransformScripts(errors); - - if (errors.hasErrors()) - throw errors; - - transaction.commit(); - return result; - } - } - } - - @Override - public List> updateRows(User user, Container container, List> rows, List> oldKeys, BatchValidationException errors, @Nullable Map configParameters, Map extraScriptContext) throws InvalidKeyException, BatchValidationException, QueryUpdateServiceException, SQLException - { - // Only allow one thread to be running a Luminex transform script and importing its results at a time - // See issue 17424 - synchronized (LuminexRunCreator.LOCK_OBJECT) - { - try (DbScope.Transaction transaction = LuminexProtocolSchema.getSchema().getScope().ensureTransaction()) - { - List> result = super.updateRows(user, container, rows, oldKeys, errors, configParameters, extraScriptContext); - - if (extraScriptContext != null && (Boolean)extraScriptContext.getOrDefault(LuminexManager.RERUN_TRANSFORM, false)) - rerunTransformScripts(errors); - - if (errors.hasErrors()) - throw errors; - - transaction.commit(); - return result; - } - } - } - - private void rerunTransformScripts(BatchValidationException errors) throws QueryUpdateServiceException - { - try - { - for (ExpRun run : _runsToRefresh) - { - AssayProvider provider = AssayService.get().getProvider(run); - AssayRunDatabaseContext context = provider.createRunDatabaseContext(run, _userSchema.getUser(), null); - provider.getRunCreator().saveExperimentRun(context, AssayService.get().findBatch(run), run, false, null); - } - } - catch (ExperimentException e) - { - throw new QueryUpdateServiceException(e); - } - catch (ValidationException e) - { - errors.addRowError(e); - } - } - }; - } -} +/* + * Copyright (c) 2014-2019 LabKey Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.labkey.luminex.query; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.labkey.api.data.Container; +import org.labkey.api.data.ContainerFilter; +import org.labkey.api.data.DataColumn; +import org.labkey.api.data.DbScope; +import org.labkey.api.data.JdbcType; +import org.labkey.api.data.MultiValuedForeignKey; +import org.labkey.api.data.MultiValuedRenderContext; +import org.labkey.api.data.RenderContext; +import org.labkey.api.data.SQLFragment; +import org.labkey.api.data.SqlSelector; +import org.labkey.api.data.TableInfo; +import org.labkey.api.exp.ExperimentException; +import org.labkey.api.exp.api.ExpData; +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.query.ExpSchema; +import org.labkey.api.query.BatchValidationException; +import org.labkey.api.query.DuplicateKeyException; +import org.labkey.api.query.ExprColumn; +import org.labkey.api.query.FieldKey; +import org.labkey.api.query.InvalidKeyException; +import org.labkey.api.query.LookupForeignKey; +import org.labkey.api.query.QueryUpdateService; +import org.labkey.api.query.QueryUpdateServiceException; +import org.labkey.api.query.ValidationException; +import org.labkey.api.security.User; +import org.labkey.api.security.permissions.Permission; +import org.labkey.api.assay.AssayProvider; +import org.labkey.api.assay.AssayRunDatabaseContext; +import org.labkey.api.assay.AssayService; +import org.labkey.api.util.HtmlString; +import org.labkey.api.view.UnauthorizedException; +import org.labkey.api.writer.HtmlWriter; +import org.labkey.luminex.LuminexManager; +import org.labkey.luminex.LuminexRunCreator; + +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.TreeSet; + +/** + * User: jeckels + * Date: Jun 29, 2011 + */ +public class WellExclusionTable extends AbstractExclusionTable +{ + public WellExclusionTable(LuminexProtocolSchema schema, ContainerFilter cf, boolean filter) + { + super(LuminexProtocolSchema.getTableInfoWellExclusion(), schema, cf, filter); + + getMutableColumn("DataId").setLabel("Data File"); + getMutableColumn("DataId").setFk(new ExpSchema(schema.getUser(), schema.getContainer()).getDataIdForeignKey(cf)); + + getMutableColumn("Analytes").setFk(new MultiValuedForeignKey(new LookupForeignKey(cf, "WellExclusionId", null) + { + @Override + public TableInfo getLookupTableInfo() + { + return _userSchema.createWellExclusionAnalyteTable(getLookupContainerFilter()); + } + }, "AnalyteId")); + getMutableColumn("Analytes").setUserEditable(false); + + SQLFragment joinSQL = new SQLFragment(" FROM "); + joinSQL.append(LuminexProtocolSchema.getTableInfoDataRow(), "dr"); + joinSQL.append(" WHERE (dr.Description = "); + joinSQL.append(ExprColumn.STR_TABLE_ALIAS); + joinSQL.append(".Description OR (dr.Description IS NULL AND "); + joinSQL.append(ExprColumn.STR_TABLE_ALIAS); + joinSQL.append(".Description IS NULL)) AND dr.DataId = "); + joinSQL.append(ExprColumn.STR_TABLE_ALIAS); + joinSQL.append(".DataId AND dr.Type = "); + joinSQL.append(ExprColumn.STR_TABLE_ALIAS); + joinSQL.append(".Type"); + + SQLFragment wellRoleSQL = new SQLFragment("SELECT WellRole FROM (SELECT DISTINCT dr.WellRole"); + wellRoleSQL.append(joinSQL); + wellRoleSQL.append(") x"); + addColumn(new ExprColumn(this, "Well Role", schema.getDbSchema().getSqlDialect().getSelectConcat(wellRoleSQL, ","), JdbcType.VARCHAR)); + + SQLFragment wellSQL = new SQLFragment("SELECT Well FROM (SELECT DISTINCT dr.Well").append(joinSQL).append(") x"); + //only pull in wells list for replicate group exclusions + wellSQL.append(" WHERE ").append(ExprColumn.STR_TABLE_ALIAS).append(".Well IS NULL"); + ExprColumn wellsCol = new ExprColumn(this, "Wells", schema.getDbSchema().getSqlDialect().getSelectConcat(wellSQL, ","), JdbcType.VARCHAR); + wellsCol.setDisplayColumnFactory(colInfo -> new DataColumn(colInfo) + { + @Override + public void renderGridCellContents(RenderContext ctx, HtmlWriter out) + { + Object o = getDisplayValue(ctx); + out.write(null == o ? HtmlString.NBSP : HtmlString.of(o.toString())); + } + + @Override + public Object getDisplayValue(RenderContext ctx) + { + Object result = getValue(ctx); + if (null != result) + { + // get the list of unique wells (by splitting the concatenated string) + TreeSet uniqueWells = new TreeSet<>(); + uniqueWells.addAll(Arrays.asList(result.toString().split(MultiValuedRenderContext.VALUE_DELIMITER_REGEX))); + + // put the unique wells back into a comma separated string + StringBuilder sb = new StringBuilder(); + String comma = ""; + for (String well : uniqueWells) + { + sb.append(comma); + sb.append(well); + comma = ","; + } + result = sb.toString(); + } + return result; + } + }); + addColumn(wellsCol); + + List defaultCols = new ArrayList<>(getDefaultVisibleColumns()); + defaultCols.remove(FieldKey.fromParts("ModifiedBy")); + defaultCols.remove(FieldKey.fromParts("Modified")); + defaultCols.add(0, FieldKey.fromParts("DataId", "Run")); + setDefaultVisibleColumns(defaultCols); + } + + @Override + protected SQLFragment createContainerFilterSQL(ContainerFilter filter) + { + return getUserSchema().createDataIdContainerFilterSQL("DataId", filter); + } + + @Override + public QueryUpdateService getUpdateService() + { + return new ExclusionUpdateService(this, getRealTable(), LuminexProtocolSchema.getTableInfoWellExclusionAnalyte(), "WellExclusionId") + { + private final Set _runsToRefresh = new HashSet<>(); + + private Integer getDataId(Map rowMap) throws QueryUpdateServiceException + { + Integer dataId = convertToInteger(rowMap.get("DataId")); + if (dataId == null) + { + throw new QueryUpdateServiceException("No DataId specified"); + } + return dataId; + } + + @Override + protected void checkPermissions(User user, Map rowMap, Class permission) throws QueryUpdateServiceException + { + ExpData data = getData(rowMap); + if (!data.getContainer().hasPermission(user, permission)) + { + throw new UnauthorizedException(); + } + } + + private ExpData getData(Map rowMap) throws QueryUpdateServiceException + { + Integer dataId = getDataId(rowMap); + ExpData data = ExperimentService.get().getExpData(dataId); + if (data == null) + { + throw new QueryUpdateServiceException("No such data file: " + dataId); + } + return data; + } + + @Override + protected @NotNull ExpRun resolveRun(Map rowMap) throws QueryUpdateServiceException + { + ExpData data = getData(rowMap); + ExpProtocolApplication protApp = data.getSourceApplication(); + if (protApp == null) + { + throw new QueryUpdateServiceException("Unable to resolve run for data " + data.getRowId() + ", no source protocol application"); + } + ExpRun run = protApp.getRun(); + if (run == null) + { + throw new QueryUpdateServiceException("Unable to resolve run for data " + data.getRowId()); + } + if (!_runsToRefresh.contains(run)) + { + String description = rowMap.get("Description") == null ? null : rowMap.get("Description").toString(); + String type = rowMap.get("Type") == null ? null : rowMap.get("Type").toString(); + String bTRUE = getSchema().getSqlDialect().getBooleanTRUE(); + + SQLFragment dataRowSQL = new SQLFragment("SELECT * FROM "); + dataRowSQL.append(LuminexProtocolSchema.getTableInfoDataRow(), "dr"); + //dataRowSQL.append(" LEFT JOIN ").append(LuminexProtocolSchema.getTableInfoTitration(), "t").append(" ON dr.TitrationId = t.RowId "); + //dataRowSQL.append(" WHERE dr.TitrationId IS NOT NULL "); + dataRowSQL.append(" WHERE dr.DataId = ? AND dr.Description "); + dataRowSQL.add(data.getRowId()); + + if (description == null) + { + dataRowSQL.append("IS NULL"); + } + else + { + dataRowSQL.append("= ?"); + dataRowSQL.add(description); + } + + dataRowSQL.append(" AND dr.Type "); + + if (type == null) + { + dataRowSQL.append("IS NULL"); + } + else + { + dataRowSQL.append("= ?"); + dataRowSQL.add(type); + } + + if (new SqlSelector(LuminexProtocolSchema.getSchema(), dataRowSQL).exists()) + { + _runsToRefresh.add(run); + } + } + + return run; + } + + @Override + public List> insertRows(User user, Container container, List> rows, BatchValidationException errors, @Nullable Map configParameters, Map extraScriptContext) throws DuplicateKeyException, QueryUpdateServiceException, SQLException + { + // Only allow one thread to be running a Luminex transform script and importing its results at a time + // See issue 17424 + synchronized (LuminexRunCreator.LOCK_OBJECT) + { + try (DbScope.Transaction transaction = LuminexProtocolSchema.getSchema().getScope().ensureTransaction()) + { + List> result = super.insertRows(user, container, rows, errors, configParameters, extraScriptContext); + + if (extraScriptContext != null && (Boolean)extraScriptContext.getOrDefault(LuminexManager.RERUN_TRANSFORM, false)) + rerunTransformScripts(errors); + + if (errors.hasErrors()) + throw errors; + + transaction.commit(); + return result; + } + catch(BatchValidationException e) + { + throw new QueryUpdateServiceException(e.getMessage(), e); + } + } + } + + @Override + public List> deleteRows(User user, Container container, List> keys, @Nullable Map configParameters, @Nullable Map extraScriptContext) throws InvalidKeyException, BatchValidationException, QueryUpdateServiceException, SQLException + { + // Only allow one thread to be running a Luminex transform script and importing its results at a time + // See issue 17424 + synchronized (LuminexRunCreator.LOCK_OBJECT) + { + try (DbScope.Transaction transaction = LuminexProtocolSchema.getSchema().getScope().ensureTransaction()) + { + List> result = super.deleteRows(user, container, keys, configParameters, extraScriptContext); + + BatchValidationException errors = new BatchValidationException(); + if (extraScriptContext != null && (Boolean)extraScriptContext.getOrDefault(LuminexManager.RERUN_TRANSFORM, false)) + rerunTransformScripts(errors); + + if (errors.hasErrors()) + throw errors; + + transaction.commit(); + return result; + } + } + } + + @Override + public List> updateRows(User user, Container container, List> rows, List> oldKeys, BatchValidationException errors, @Nullable Map configParameters, Map extraScriptContext) throws InvalidKeyException, BatchValidationException, QueryUpdateServiceException, SQLException + { + // Only allow one thread to be running a Luminex transform script and importing its results at a time + // See issue 17424 + synchronized (LuminexRunCreator.LOCK_OBJECT) + { + try (DbScope.Transaction transaction = LuminexProtocolSchema.getSchema().getScope().ensureTransaction()) + { + List> result = super.updateRows(user, container, rows, oldKeys, errors, configParameters, extraScriptContext); + + if (extraScriptContext != null && (Boolean)extraScriptContext.getOrDefault(LuminexManager.RERUN_TRANSFORM, false)) + rerunTransformScripts(errors); + + if (errors.hasErrors()) + throw errors; + + transaction.commit(); + return result; + } + } + } + + private void rerunTransformScripts(BatchValidationException errors) throws QueryUpdateServiceException + { + try + { + for (ExpRun run : _runsToRefresh) + { + AssayProvider provider = AssayService.get().getProvider(run); + AssayRunDatabaseContext context = provider.createRunDatabaseContext(run, _userSchema.getUser(), null); + provider.getRunCreator().saveExperimentRun(context, AssayService.get().findBatch(run), run, false, null); + } + } + catch (ExperimentException e) + { + throw new QueryUpdateServiceException(e); + } + catch (ValidationException e) + { + errors.addRowError(e); + } + } + }; + } +}