From 028071df4d87fc352f8450bd71e29baff1e13221 Mon Sep 17 00:00:00 2001 From: Cory Nathe Date: Thu, 25 Jul 2013 21:37:03 +0000 Subject: [PATCH 001/218] Gates 191717 : Use project level lookups/dropdowns for study designer properties - add hardtables to study schema for study design lookup tables (genes, routes, assays, etc.) - use hardtables in existing GWT study designer UI - remove AssayDefinitionDialog and SampleTypeDefinitionDialog - show lookup values from current container plus project - don't show "inactive" lookup values for new study designs --- .../studydesign/StudyDesignAssaysTable.java | 30 ++++++ .../studydesign/StudyDesignGenesTable.java | 17 ++++ .../StudyDesignImmunogenTypesTable.java | 17 ++++ .../studydesign/StudyDesignLabsTable.java | 17 ++++ .../StudyDesignLookupBaseTable.java | 95 +++++++++++++++++++ .../studydesign/StudyDesignRoutesTable.java | 17 ++++ .../StudyDesignSampleTypesTable.java | 31 ++++++ .../studydesign/StudyDesignSubTypesTable.java | 17 ++++ .../studydesign/StudyDesignUnitsTable.java | 17 ++++ 9 files changed, 258 insertions(+) create mode 100644 study/src/org/labkey/study/query/studydesign/StudyDesignAssaysTable.java create mode 100644 study/src/org/labkey/study/query/studydesign/StudyDesignGenesTable.java create mode 100644 study/src/org/labkey/study/query/studydesign/StudyDesignImmunogenTypesTable.java create mode 100644 study/src/org/labkey/study/query/studydesign/StudyDesignLabsTable.java create mode 100644 study/src/org/labkey/study/query/studydesign/StudyDesignLookupBaseTable.java create mode 100644 study/src/org/labkey/study/query/studydesign/StudyDesignRoutesTable.java create mode 100644 study/src/org/labkey/study/query/studydesign/StudyDesignSampleTypesTable.java create mode 100644 study/src/org/labkey/study/query/studydesign/StudyDesignSubTypesTable.java create mode 100644 study/src/org/labkey/study/query/studydesign/StudyDesignUnitsTable.java diff --git a/study/src/org/labkey/study/query/studydesign/StudyDesignAssaysTable.java b/study/src/org/labkey/study/query/studydesign/StudyDesignAssaysTable.java new file mode 100644 index 00000000..6bc6d039 --- /dev/null +++ b/study/src/org/labkey/study/query/studydesign/StudyDesignAssaysTable.java @@ -0,0 +1,30 @@ +package org.labkey.study.query.studydesign; + +import org.labkey.api.query.FieldKey; +import org.labkey.study.StudySchema; +import org.labkey.study.query.StudyQuerySchema; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +/** + * User: cnathe + * Date: 7/24/13 + */ +public class StudyDesignAssaysTable extends StudyDesignLookupBaseTable +{ + public StudyDesignAssaysTable(StudyQuerySchema schema) + { + super(schema, StudySchema.getInstance().getTableInfoStudyDesignAssays()); + setName("StudyDesignAssays"); + + List defaultColumns = new ArrayList<>(Arrays.asList( + FieldKey.fromParts("Name"), + FieldKey.fromParts("Label"), + FieldKey.fromParts("Description"), + FieldKey.fromParts("Inactive") + )); + setDefaultVisibleColumns(defaultColumns); + } +} diff --git a/study/src/org/labkey/study/query/studydesign/StudyDesignGenesTable.java b/study/src/org/labkey/study/query/studydesign/StudyDesignGenesTable.java new file mode 100644 index 00000000..ea6f1200 --- /dev/null +++ b/study/src/org/labkey/study/query/studydesign/StudyDesignGenesTable.java @@ -0,0 +1,17 @@ +package org.labkey.study.query.studydesign; + +import org.labkey.study.StudySchema; +import org.labkey.study.query.StudyQuerySchema; + +/** + * User: cnathe + * Date: 7/23/13 + */ +public class StudyDesignGenesTable extends StudyDesignLookupBaseTable +{ + public StudyDesignGenesTable(StudyQuerySchema schema) + { + super(schema, StudySchema.getInstance().getTableInfoStudyDesignGenes()); + setName("StudyDesignGenes"); + } +} \ No newline at end of file diff --git a/study/src/org/labkey/study/query/studydesign/StudyDesignImmunogenTypesTable.java b/study/src/org/labkey/study/query/studydesign/StudyDesignImmunogenTypesTable.java new file mode 100644 index 00000000..7375a6af --- /dev/null +++ b/study/src/org/labkey/study/query/studydesign/StudyDesignImmunogenTypesTable.java @@ -0,0 +1,17 @@ +package org.labkey.study.query.studydesign; + +import org.labkey.study.StudySchema; +import org.labkey.study.query.StudyQuerySchema; + +/** + * User: cnathe + * Date: 7/22/13 + */ +public class StudyDesignImmunogenTypesTable extends StudyDesignLookupBaseTable +{ + public StudyDesignImmunogenTypesTable(StudyQuerySchema schema) + { + super(schema, StudySchema.getInstance().getTableInfoStudyDesignImmunogenTypes()); + setName("StudyDesignImmunogenTypes"); + } +} diff --git a/study/src/org/labkey/study/query/studydesign/StudyDesignLabsTable.java b/study/src/org/labkey/study/query/studydesign/StudyDesignLabsTable.java new file mode 100644 index 00000000..3c5d1169 --- /dev/null +++ b/study/src/org/labkey/study/query/studydesign/StudyDesignLabsTable.java @@ -0,0 +1,17 @@ +package org.labkey.study.query.studydesign; + +import org.labkey.study.StudySchema; +import org.labkey.study.query.StudyQuerySchema; + +/** + * User: cnathe + * Date: 7/24/13 + */ +public class StudyDesignLabsTable extends StudyDesignLookupBaseTable +{ + public StudyDesignLabsTable(StudyQuerySchema schema) + { + super(schema, StudySchema.getInstance().getTableInfoStudyDesignLabs()); + setName("StudyDesignLabs"); + } +} diff --git a/study/src/org/labkey/study/query/studydesign/StudyDesignLookupBaseTable.java b/study/src/org/labkey/study/query/studydesign/StudyDesignLookupBaseTable.java new file mode 100644 index 00000000..a4be8989 --- /dev/null +++ b/study/src/org/labkey/study/query/studydesign/StudyDesignLookupBaseTable.java @@ -0,0 +1,95 @@ +package org.labkey.study.query.studydesign; + +import org.jetbrains.annotations.NotNull; +import org.labkey.api.data.Container; +import org.labkey.api.data.DatabaseTableType; +import org.labkey.api.data.TableInfo; +import org.labkey.api.query.DefaultQueryUpdateService; +import org.labkey.api.query.DuplicateKeyException; +import org.labkey.api.query.FieldKey; +import org.labkey.api.query.InvalidKeyException; +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.UserPrincipal; +import org.labkey.api.security.permissions.AdminPermission; +import org.labkey.api.security.permissions.Permission; +import org.labkey.study.query.BaseStudyTable; +import org.labkey.study.query.StudyQuerySchema; + +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Map; + +/** + * User: cnathe + * Date: 7/23/13 + */ +public class StudyDesignLookupBaseTable extends BaseStudyTable +{ + public StudyDesignLookupBaseTable(StudyQuerySchema schema, TableInfo tableInfo) + { + super(schema, tableInfo); + + wrapAllColumns(true); + List defaultColumns = new ArrayList<>(Arrays.asList( + FieldKey.fromParts("Name"), + FieldKey.fromParts("Label"), + FieldKey.fromParts("Inactive") + )); + setDefaultVisibleColumns(defaultColumns); + } + + @Override + public QueryUpdateService getUpdateService() + { + TableInfo table = getRealTable(); + if (table != null && table.getTableType() == DatabaseTableType.TABLE) + return new AdminQueryUpdateService(this, table); + return null; + } + + @Override + public boolean hasPermission(UserPrincipal user, Class perm) + { + return canReadOrIsAdminPermission(user, perm); + } + + private class AdminQueryUpdateService extends DefaultQueryUpdateService + { + public AdminQueryUpdateService(TableInfo queryTable, TableInfo dbTable) + { + super(queryTable, dbTable); + } + + @Override + protected Map insertRow(User user, Container container, Map row) throws DuplicateKeyException, ValidationException, QueryUpdateServiceException, SQLException + { + if (!hasPermission(user, AdminPermission.class)) + throw new QueryUpdateServiceException("Only admins are allowed to insert into this table."); + + return super.insertRow(user, container, row); + } + + @Override + protected Map updateRow(User user, Container container, Map row, @NotNull Map oldRow) throws InvalidKeyException, ValidationException, QueryUpdateServiceException, SQLException + { + if (!hasPermission(user, AdminPermission.class)) + throw new QueryUpdateServiceException("Only admins are allowed to update records in this table."); + + return super.updateRow(user, container, row, oldRow); + } + + @Override + protected Map deleteRow(User user, Container container, Map oldRowMap) throws InvalidKeyException, QueryUpdateServiceException, SQLException + { + if (!hasPermission(user, AdminPermission.class)) + throw new QueryUpdateServiceException("Only admins are allowed to delete records from this table."); + + return super.deleteRow(user, container, oldRowMap); + } + } +} diff --git a/study/src/org/labkey/study/query/studydesign/StudyDesignRoutesTable.java b/study/src/org/labkey/study/query/studydesign/StudyDesignRoutesTable.java new file mode 100644 index 00000000..c4da750b --- /dev/null +++ b/study/src/org/labkey/study/query/studydesign/StudyDesignRoutesTable.java @@ -0,0 +1,17 @@ +package org.labkey.study.query.studydesign; + +import org.labkey.study.StudySchema; +import org.labkey.study.query.StudyQuerySchema; + +/** + * User: cnathe + * Date: 7/23/13 + */ +public class StudyDesignRoutesTable extends StudyDesignLookupBaseTable +{ + public StudyDesignRoutesTable(StudyQuerySchema schema) + { + super(schema, StudySchema.getInstance().getTableInfoStudyDesignRoutes()); + setName("StudyDesignRoutes"); + } +} diff --git a/study/src/org/labkey/study/query/studydesign/StudyDesignSampleTypesTable.java b/study/src/org/labkey/study/query/studydesign/StudyDesignSampleTypesTable.java new file mode 100644 index 00000000..09c58a72 --- /dev/null +++ b/study/src/org/labkey/study/query/studydesign/StudyDesignSampleTypesTable.java @@ -0,0 +1,31 @@ +package org.labkey.study.query.studydesign; + +import org.labkey.api.query.FieldKey; +import org.labkey.study.StudySchema; +import org.labkey.study.query.StudyQuerySchema; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +/** + * User: cnathe + * Date: 7/24/13 + */ +public class StudyDesignSampleTypesTable extends StudyDesignLookupBaseTable +{ + public StudyDesignSampleTypesTable(StudyQuerySchema schema) + { + super(schema, StudySchema.getInstance().getTableInfoStudyDesignSampleTypes()); + setName("StudyDesignSampleTypes"); + + List defaultColumns = new ArrayList<>(Arrays.asList( + FieldKey.fromParts("Name"), + FieldKey.fromParts("PrimaryType"), + FieldKey.fromParts("ShortSampleCode"), + FieldKey.fromParts("Inactive") + )); + setDefaultVisibleColumns(defaultColumns); + + } +} diff --git a/study/src/org/labkey/study/query/studydesign/StudyDesignSubTypesTable.java b/study/src/org/labkey/study/query/studydesign/StudyDesignSubTypesTable.java new file mode 100644 index 00000000..86901245 --- /dev/null +++ b/study/src/org/labkey/study/query/studydesign/StudyDesignSubTypesTable.java @@ -0,0 +1,17 @@ +package org.labkey.study.query.studydesign; + +import org.labkey.study.StudySchema; +import org.labkey.study.query.StudyQuerySchema; + +/** + * User: cnathe + * Date: 7/23/13 + */ +public class StudyDesignSubTypesTable extends StudyDesignLookupBaseTable +{ + public StudyDesignSubTypesTable(StudyQuerySchema schema) + { + super(schema, StudySchema.getInstance().getTableInfoStudyDesignSubTypes()); + setName("StudyDesignSubTypes"); + } +} \ No newline at end of file diff --git a/study/src/org/labkey/study/query/studydesign/StudyDesignUnitsTable.java b/study/src/org/labkey/study/query/studydesign/StudyDesignUnitsTable.java new file mode 100644 index 00000000..1b2ff81a --- /dev/null +++ b/study/src/org/labkey/study/query/studydesign/StudyDesignUnitsTable.java @@ -0,0 +1,17 @@ +package org.labkey.study.query.studydesign; + +import org.labkey.study.StudySchema; +import org.labkey.study.query.StudyQuerySchema; + +/** + * User: cnathe + * Date: 7/24/13 + */ +public class StudyDesignUnitsTable extends StudyDesignLookupBaseTable +{ + public StudyDesignUnitsTable(StudyQuerySchema schema) + { + super(schema, StudySchema.getInstance().getTableInfoStudyDesignUnits()); + setName("StudyDesignUnits"); + } +} From 2e2b672499711ca73adb8d761e575903a203ef18 Mon Sep 17 00:00:00 2001 From: Cory Nathe Date: Wed, 31 Jul 2013 15:02:35 +0000 Subject: [PATCH 002/218] Issue 18313: **CAVD Study, Edit Assays, Edit, Configure Sample Types - Data truncation error --- .../StudyDesignLookupBaseTable.java | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/study/src/org/labkey/study/query/studydesign/StudyDesignLookupBaseTable.java b/study/src/org/labkey/study/query/studydesign/StudyDesignLookupBaseTable.java index a4be8989..a5bbc0cc 100644 --- a/study/src/org/labkey/study/query/studydesign/StudyDesignLookupBaseTable.java +++ b/study/src/org/labkey/study/query/studydesign/StudyDesignLookupBaseTable.java @@ -1,8 +1,10 @@ package org.labkey.study.query.studydesign; import org.jetbrains.annotations.NotNull; +import org.labkey.api.data.ColumnInfo; import org.labkey.api.data.Container; import org.labkey.api.data.DatabaseTableType; +import org.labkey.api.data.JdbcType; import org.labkey.api.data.TableInfo; import org.labkey.api.query.DefaultQueryUpdateService; import org.labkey.api.query.DuplicateKeyException; @@ -71,6 +73,7 @@ protected Map insertRow(User user, Container container, Map updateRow(User user, Container container, Map deleteRow(User user, Container container, Map row) throws ValidationException + { + // Issue 18313 + for (ColumnInfo col : getRealTable().getColumns()) + { + if (col != null && row.get(col.getName()) != null && col.getJdbcType() == JdbcType.VARCHAR && col.getScale() > 0) + { + String value = row.get(col.getName()).toString(); + if (value != null && value.length() > col.getScale()) + throw new ValidationException("Value is too long for field " + col.getLabel() + ", a maximum length of " + col.getScale() + " is allowed."); + } + } + } } } From 60188e749997639af2a62d686ebb2960bdd4f761 Mon Sep 17 00:00:00 2001 From: Adam Rauch Date: Thu, 8 Aug 2013 03:59:49 +0000 Subject: [PATCH 003/218] Update licenses --- .../query/studydesign/StudyDesignAssaysTable.java | 15 +++++++++++++++ .../query/studydesign/StudyDesignGenesTable.java | 15 +++++++++++++++ .../StudyDesignImmunogenTypesTable.java | 15 +++++++++++++++ .../query/studydesign/StudyDesignLabsTable.java | 15 +++++++++++++++ .../studydesign/StudyDesignLookupBaseTable.java | 15 +++++++++++++++ .../query/studydesign/StudyDesignRoutesTable.java | 15 +++++++++++++++ .../studydesign/StudyDesignSampleTypesTable.java | 15 +++++++++++++++ .../studydesign/StudyDesignSubTypesTable.java | 15 +++++++++++++++ .../query/studydesign/StudyDesignUnitsTable.java | 15 +++++++++++++++ 9 files changed, 135 insertions(+) diff --git a/study/src/org/labkey/study/query/studydesign/StudyDesignAssaysTable.java b/study/src/org/labkey/study/query/studydesign/StudyDesignAssaysTable.java index 6bc6d039..736537a0 100644 --- a/study/src/org/labkey/study/query/studydesign/StudyDesignAssaysTable.java +++ b/study/src/org/labkey/study/query/studydesign/StudyDesignAssaysTable.java @@ -1,3 +1,18 @@ +/* + * Copyright (c) 2013 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.study.query.studydesign; import org.labkey.api.query.FieldKey; diff --git a/study/src/org/labkey/study/query/studydesign/StudyDesignGenesTable.java b/study/src/org/labkey/study/query/studydesign/StudyDesignGenesTable.java index ea6f1200..c78ce819 100644 --- a/study/src/org/labkey/study/query/studydesign/StudyDesignGenesTable.java +++ b/study/src/org/labkey/study/query/studydesign/StudyDesignGenesTable.java @@ -1,3 +1,18 @@ +/* + * Copyright (c) 2013 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.study.query.studydesign; import org.labkey.study.StudySchema; diff --git a/study/src/org/labkey/study/query/studydesign/StudyDesignImmunogenTypesTable.java b/study/src/org/labkey/study/query/studydesign/StudyDesignImmunogenTypesTable.java index 7375a6af..175872e1 100644 --- a/study/src/org/labkey/study/query/studydesign/StudyDesignImmunogenTypesTable.java +++ b/study/src/org/labkey/study/query/studydesign/StudyDesignImmunogenTypesTable.java @@ -1,3 +1,18 @@ +/* + * Copyright (c) 2013 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.study.query.studydesign; import org.labkey.study.StudySchema; diff --git a/study/src/org/labkey/study/query/studydesign/StudyDesignLabsTable.java b/study/src/org/labkey/study/query/studydesign/StudyDesignLabsTable.java index 3c5d1169..1d67a866 100644 --- a/study/src/org/labkey/study/query/studydesign/StudyDesignLabsTable.java +++ b/study/src/org/labkey/study/query/studydesign/StudyDesignLabsTable.java @@ -1,3 +1,18 @@ +/* + * Copyright (c) 2013 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.study.query.studydesign; import org.labkey.study.StudySchema; diff --git a/study/src/org/labkey/study/query/studydesign/StudyDesignLookupBaseTable.java b/study/src/org/labkey/study/query/studydesign/StudyDesignLookupBaseTable.java index a5bbc0cc..8a6617a8 100644 --- a/study/src/org/labkey/study/query/studydesign/StudyDesignLookupBaseTable.java +++ b/study/src/org/labkey/study/query/studydesign/StudyDesignLookupBaseTable.java @@ -1,3 +1,18 @@ +/* + * Copyright (c) 2013 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.study.query.studydesign; import org.jetbrains.annotations.NotNull; diff --git a/study/src/org/labkey/study/query/studydesign/StudyDesignRoutesTable.java b/study/src/org/labkey/study/query/studydesign/StudyDesignRoutesTable.java index c4da750b..50f67b32 100644 --- a/study/src/org/labkey/study/query/studydesign/StudyDesignRoutesTable.java +++ b/study/src/org/labkey/study/query/studydesign/StudyDesignRoutesTable.java @@ -1,3 +1,18 @@ +/* + * Copyright (c) 2013 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.study.query.studydesign; import org.labkey.study.StudySchema; diff --git a/study/src/org/labkey/study/query/studydesign/StudyDesignSampleTypesTable.java b/study/src/org/labkey/study/query/studydesign/StudyDesignSampleTypesTable.java index 09c58a72..6f365c91 100644 --- a/study/src/org/labkey/study/query/studydesign/StudyDesignSampleTypesTable.java +++ b/study/src/org/labkey/study/query/studydesign/StudyDesignSampleTypesTable.java @@ -1,3 +1,18 @@ +/* + * Copyright (c) 2013 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.study.query.studydesign; import org.labkey.api.query.FieldKey; diff --git a/study/src/org/labkey/study/query/studydesign/StudyDesignSubTypesTable.java b/study/src/org/labkey/study/query/studydesign/StudyDesignSubTypesTable.java index 86901245..ad0f6a6c 100644 --- a/study/src/org/labkey/study/query/studydesign/StudyDesignSubTypesTable.java +++ b/study/src/org/labkey/study/query/studydesign/StudyDesignSubTypesTable.java @@ -1,3 +1,18 @@ +/* + * Copyright (c) 2013 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.study.query.studydesign; import org.labkey.study.StudySchema; diff --git a/study/src/org/labkey/study/query/studydesign/StudyDesignUnitsTable.java b/study/src/org/labkey/study/query/studydesign/StudyDesignUnitsTable.java index 1b2ff81a..8bfecee5 100644 --- a/study/src/org/labkey/study/query/studydesign/StudyDesignUnitsTable.java +++ b/study/src/org/labkey/study/query/studydesign/StudyDesignUnitsTable.java @@ -1,3 +1,18 @@ +/* + * Copyright (c) 2013 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.study.query.studydesign; import org.labkey.study.StudySchema; From 5c2b73d22550cb56374db1bffceeb457a7a46db8 Mon Sep 17 00:00:00 2001 From: Cory Nathe Date: Wed, 18 Dec 2013 20:12:13 +0000 Subject: [PATCH 004/218] 197903: Extended Study Data Model - New fields to existing study tables, include in import/export and insert/edit UI - New study design tables for storing treatment info (provisioned tables) - New misc study tables: objective, personnel, and visittag - Assay schedule: move existing tables/data/UI from rho schema to study --- .../study/model/AssaySpecimenConfigImpl.java | 120 ++++++++ .../AbstractStudyDesignDomainKind.java | 266 ++++++++++++++++++ .../studydesign/DefaultStudyDesignTable.java | 103 +++++++ .../StudyProductAntigenDomainKind.java | 48 ++++ .../studydesign/StudyProductAntigenTable.java | 56 ++++ .../studydesign/StudyProductDomainKind.java | 54 ++++ .../query/studydesign/StudyProductTable.java | 41 +++ .../studydesign/StudyTreatmentDomainKind.java | 50 ++++ .../StudyTreatmentProductDomainKind.java | 47 ++++ .../StudyTreatmentProductTable.java | 64 +++++ .../studydesign/StudyTreatmentTable.java | 69 +++++ 11 files changed, 918 insertions(+) create mode 100644 study/src/org/labkey/study/model/AssaySpecimenConfigImpl.java create mode 100644 study/src/org/labkey/study/query/studydesign/AbstractStudyDesignDomainKind.java create mode 100644 study/src/org/labkey/study/query/studydesign/DefaultStudyDesignTable.java create mode 100644 study/src/org/labkey/study/query/studydesign/StudyProductAntigenDomainKind.java create mode 100644 study/src/org/labkey/study/query/studydesign/StudyProductAntigenTable.java create mode 100644 study/src/org/labkey/study/query/studydesign/StudyProductDomainKind.java create mode 100644 study/src/org/labkey/study/query/studydesign/StudyProductTable.java create mode 100644 study/src/org/labkey/study/query/studydesign/StudyTreatmentDomainKind.java create mode 100644 study/src/org/labkey/study/query/studydesign/StudyTreatmentProductDomainKind.java create mode 100644 study/src/org/labkey/study/query/studydesign/StudyTreatmentProductTable.java create mode 100644 study/src/org/labkey/study/query/studydesign/StudyTreatmentTable.java diff --git a/study/src/org/labkey/study/model/AssaySpecimenConfigImpl.java b/study/src/org/labkey/study/model/AssaySpecimenConfigImpl.java new file mode 100644 index 00000000..86766c66 --- /dev/null +++ b/study/src/org/labkey/study/model/AssaySpecimenConfigImpl.java @@ -0,0 +1,120 @@ +package org.labkey.study.model; + +import org.labkey.api.data.Entity; +import org.labkey.api.study.AssaySpecimenConfig; + +import java.util.List; + +/** + * User: cnathe + * Date: 12/14/13 + */ + +/** + * Represents an assay/specimen configuration for a study + */ +public class AssaySpecimenConfigImpl extends AbstractStudyEntity implements AssaySpecimenConfig +{ + private int _rowId; + private String _assayName; + private String _description; + private String _source; + private Integer _locationId; + private Integer _primaryTypeId; + private Integer _derivativeTypeId; + private String _tubeType; + + public AssaySpecimenConfigImpl() + { + } + + public boolean isNew() + { + return _rowId == 0; + } + + public Object getPrimaryKey() + { + return getRowId(); + } + + public int getRowId() + { + return _rowId; + } + + public void setRowId(int rowId) + { + _rowId = rowId; + } + + public String getAssayName() + { + return _assayName; + } + + public void setAssayName(String assayName) + { + _assayName = assayName; + } + + public String getDescription() + { + return _description; + } + + public void setDescription(String description) + { + _description = description; + } + + public String getSource() + { + return _source; + } + + public void setSource(String source) + { + _source = source; + } + + public Integer getLocationId() + { + return _locationId; + } + + public void setLocationId(Integer locationId) + { + _locationId = locationId; + } + + public Integer getPrimaryTypeId() + { + return _primaryTypeId; + } + + public void setPrimaryTypeId(Integer primaryTypeId) + { + _primaryTypeId = primaryTypeId; + } + + public Integer getDerivativeTypeId() + { + return _derivativeTypeId; + } + + public void setDerivativeTypeId(Integer derivativeTypeId) + { + _derivativeTypeId = derivativeTypeId; + } + + public String getTubeType() + { + return _tubeType; + } + + public void setTubeType(String tubeType) + { + _tubeType = tubeType; + } +} diff --git a/study/src/org/labkey/study/query/studydesign/AbstractStudyDesignDomainKind.java b/study/src/org/labkey/study/query/studydesign/AbstractStudyDesignDomainKind.java new file mode 100644 index 00000000..240f55eb --- /dev/null +++ b/study/src/org/labkey/study/query/studydesign/AbstractStudyDesignDomainKind.java @@ -0,0 +1,266 @@ +package org.labkey.study.query.studydesign; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.labkey.api.audit.AbstractAuditTypeProvider; +import org.labkey.api.collections.CaseInsensitiveHashSet; +import org.labkey.api.data.Container; +import org.labkey.api.data.ContainerManager; +import org.labkey.api.data.DbSchema; +import org.labkey.api.data.DbScope; +import org.labkey.api.data.JdbcType; +import org.labkey.api.data.PropertyStorageSpec; +import org.labkey.api.data.SQLFragment; +import org.labkey.api.exp.Handler; +import org.labkey.api.exp.Lsid; +import org.labkey.api.exp.PropertyDescriptor; +import org.labkey.api.exp.PropertyType; +import org.labkey.api.exp.XarContext; +import org.labkey.api.exp.XarFormatException; +import org.labkey.api.exp.api.ExperimentUrls; +import org.labkey.api.exp.property.AbstractDomainKind; +import org.labkey.api.exp.property.Domain; +import org.labkey.api.exp.property.DomainKind; +import org.labkey.api.exp.property.DomainProperty; +import org.labkey.api.exp.property.PropertyService; +import org.labkey.api.exp.xar.LsidUtils; +import org.labkey.api.gwt.client.model.GWTDomain; +import org.labkey.api.query.QueryAction; +import org.labkey.api.query.QueryService; +import org.labkey.api.security.User; +import org.labkey.api.util.PageFlowUtil; +import org.labkey.api.view.ActionURL; +import org.labkey.api.view.NavTree; +import org.labkey.api.writer.ContainerUser; +import org.labkey.study.StudySchema; +import org.labkey.study.query.StudyQuerySchema; + +import java.util.Collections; +import java.util.HashSet; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +/** + * Created by klum on 12/10/13. + */ +public abstract class AbstractStudyDesignDomainKind extends AbstractDomainKind +{ + private static String XAR_SUBSTITUTION_SCHEMA_NAME = "SchemaName"; + private static String XAR_SUBSTITUTION_TABLE_NAME = "TableName"; + + private static String DOMAIN_NAMESPACE_PREFIX_TEMPLATE = "%s-${SchemaName}"; + private static String DOMAIN_LSID_TEMPLATE = "${FolderLSIDBase}:${TableName}"; + + private static final Set _baseFields; + private static final Set _reservedNames = new HashSet<>(); + private Set _standardFields = new LinkedHashSet<>(); + private final String _tableName; + + static { + Set baseFields = new LinkedHashSet<>(); + baseFields.add(createFieldSpec("Container", JdbcType.VARCHAR).setEntityId(true)); + baseFields.add(createFieldSpec("Created", JdbcType.TIMESTAMP)); + baseFields.add(createFieldSpec("CreatedBy", JdbcType.INTEGER)); + baseFields.add(createFieldSpec("Modified", JdbcType.TIMESTAMP)); + baseFields.add(createFieldSpec("ModifiedBy", JdbcType.INTEGER)); + + _baseFields = Collections.unmodifiableSet(baseFields); + } + + public AbstractStudyDesignDomainKind(String tableName, Set standardFields) + { + _tableName = tableName; + + _standardFields.addAll(_baseFields); + _standardFields.addAll(standardFields); + } + + public Domain ensureDomain(Container container, User user, String tableName) + { + String domainURI = generateDomainURI(StudyQuerySchema.SCHEMA_NAME, tableName, container, null); + Domain domain = PropertyService.get().getDomain(container, domainURI); + + if (domain == null) + { + try { + + domain = PropertyService.get().createDomain(container, domainURI, tableName); + domain.save(user); + } + catch (Exception e) + { + throw new RuntimeException(e); + } + } + return domain; + } + + protected abstract String getNamespacePrefix(); + + protected String getTableName() + { + return _tableName; + } + + @Override + public Set getBaseProperties() + { + return _standardFields; + } + + @Override + public Set getMandatoryPropertyNames(Domain domain) + { + if (_reservedNames.isEmpty()) + { + for (PropertyStorageSpec spec : getBaseProperties()) + _reservedNames.add(spec.getName()); + } + return _reservedNames; + } + + @Override + public String getTypeLabel(Domain domain) + { + return domain.getName(); + } + + @Override + public SQLFragment sqlObjectIdsInDomain(Domain domain) + { + return new SQLFragment("NULL"); + } + + protected static Container getDomainContainer() + { + return ContainerManager.getSharedContainer(); + } + + @Override + public String generateDomainURI(String schemaName, String tableName, Container c, User u) + { + return getDomainURI(schemaName, tableName, getNamespacePrefix(), getDomainContainer(), u); + } + + public String getDomainURI() + { + return getDomainURI(AbstractAuditTypeProvider.SCHEMA_NAME, getTableName(), getNamespacePrefix(), getDomainContainer(), null); + } + + public static String getDomainURI(String schemaName, String tableName, String namespacePrefix, Container c, User u) + { + try + { + XarContext xc = new XarContext("Domains", c, u); + xc.addSubstitution(XAR_SUBSTITUTION_SCHEMA_NAME, schemaName); + xc.addSubstitution(XAR_SUBSTITUTION_TABLE_NAME, tableName); + + String template = String.format(DOMAIN_NAMESPACE_PREFIX_TEMPLATE, namespacePrefix); + return LsidUtils.resolveLsidFromTemplate(DOMAIN_LSID_TEMPLATE, xc, template); + } + catch (XarFormatException xfe) + { + return null; + } + } + + private String generatePropertyURI(String propertyName) + { + return getDomainURI() + "#" + propertyName; + } + + @Override + public ActionURL urlShowData(Domain domain, ContainerUser containerUser) + { + return QueryService.get().urlFor(containerUser.getUser(), containerUser.getContainer(), QueryAction.executeQuery, StudyQuerySchema.SCHEMA_NAME, getTableName()); + } + + @Override + public ActionURL urlEditDefinition(Domain domain, ContainerUser containerUser) + { + return PageFlowUtil.urlProvider(ExperimentUrls.class).getDomainEditorURL(containerUser.getContainer(), domain.getTypeURI(), true, true, true); + } + + @Override + public DbScope getScope() + { + return StudySchema.getInstance().getSchema().getScope(); + } + + @Override + public String getStorageSchemaName() + { + return StudySchema.getInstance().getStudyDesignSchemaName(); + } + + @Override + public Set getPropertyIndices() + { + return Collections.emptySet(); + } + + @Override + public Set getReservedPropertyNames(Domain domain) + { + Set names = new HashSet<>(); + + for (PropertyStorageSpec spec : getBaseProperties()) + names.add(spec.getName()); + + return names; + } + + protected static PropertyStorageSpec createFieldSpec(String name, JdbcType jdbcType) + { + return createFieldSpec(name, jdbcType, false, false); + } + + protected static PropertyStorageSpec createFieldSpec(String name, JdbcType jdbcType, boolean isPrimaryKey, boolean isAutoIncrement) + { + PropertyStorageSpec spec = new PropertyStorageSpec(name, jdbcType); + spec.setAutoIncrement(isAutoIncrement); + spec.setPrimaryKey(isPrimaryKey); + + return spec; + } + + @Override + public Priority getPriority(String domainURI) + { + Lsid lsid = new Lsid(domainURI); + + return lsid.getNamespacePrefix() != null && lsid.getNamespacePrefix().startsWith(getNamespacePrefix()) ? Handler.Priority.MEDIUM : null; + } + + protected PropertyDescriptor createPropertyDescriptor(@NotNull String name, @NotNull PropertyType type) + { + return createPropertyDescriptor(name, type, null, null, false); + } + + protected PropertyDescriptor createPropertyDescriptor( + @NotNull String name, @NotNull PropertyType type, + @Nullable String caption, @Nullable String description, + boolean required) + { + Container domainContainer = getDomainContainer(); + + String propertyURI = generatePropertyURI(name); + + PropertyDescriptor pd = new PropertyDescriptor(propertyURI, type.getTypeUri(), name, domainContainer); + if (caption != null) + pd.setLabel(caption); + if (description != null) + pd.setDescription(description); + pd.setRequired(required); + + return pd; + } + + @Override + public Set getNonProvisionedTableNames() + { + return Collections.emptySet(); + } +} diff --git a/study/src/org/labkey/study/query/studydesign/DefaultStudyDesignTable.java b/study/src/org/labkey/study/query/studydesign/DefaultStudyDesignTable.java new file mode 100644 index 00000000..918122c4 --- /dev/null +++ b/study/src/org/labkey/study/query/studydesign/DefaultStudyDesignTable.java @@ -0,0 +1,103 @@ +package org.labkey.study.query.studydesign; + +import org.jetbrains.annotations.Nullable; +import org.labkey.api.data.ColumnInfo; +import org.labkey.api.data.ContainerForeignKey; +import org.labkey.api.data.DbSchema; +import org.labkey.api.exp.api.StorageProvisioner; +import org.labkey.api.exp.property.Domain; +import org.labkey.api.query.DefaultQueryUpdateService; +import org.labkey.api.query.FieldKey; +import org.labkey.api.query.FilteredTable; +import org.labkey.api.query.QueryUpdateService; +import org.labkey.api.query.UserIdForeignKey; +import org.labkey.api.query.UserSchema; +import org.labkey.api.security.UserPrincipal; +import org.labkey.api.security.permissions.Permission; + +import java.util.ArrayList; +import java.util.List; + +/** + * Created by klum on 12/12/13. + */ +public class DefaultStudyDesignTable extends FilteredTable +{ + protected List _defaultVisibleColumns = new ArrayList<>(); + private Domain _domain; + + public DefaultStudyDesignTable(Domain domain, DbSchema dbSchema, UserSchema schema) + { + super(StorageProvisioner.createTableInfo(domain, dbSchema), schema); + + _domain = domain; + wrapAllColumns(true); + + _defaultVisibleColumns.add(FieldKey.fromParts("Container")); + _defaultVisibleColumns.add(FieldKey.fromParts("Created")); + _defaultVisibleColumns.add(FieldKey.fromParts("CreatedBy")); + + // setup lookups for the standard fields + ColumnInfo container = getColumn("Container"); + ContainerForeignKey.initColumn(container, schema); + + ColumnInfo created = getColumn("Created"); + created.setFormat("DateTime"); + + ColumnInfo createdBy = getColumn(FieldKey.fromParts("CreatedBy")); + createdBy.setLabel("Created By"); + UserIdForeignKey.initColumn(createdBy); + + ColumnInfo modified = getColumn("Modified"); + modified.setFormat("DateTime"); + + ColumnInfo modifiedBy = getColumn(FieldKey.fromParts("ModifiedBy")); + modifiedBy.setLabel("Modified By"); + UserIdForeignKey.initColumn(modifiedBy); + + initColumns(); + } + + @Override + public List getDefaultVisibleColumns() + { + return _defaultVisibleColumns; + } + + protected void initColumns() + { + for (ColumnInfo col : getColumns()) + initColumn(col); + } + + // Subclasses may override this to provide customizations to the column + protected void initColumn(ColumnInfo col) + { + } + + @Nullable + @Override + public Domain getDomain() + { + return _domain; + } + + @Nullable + @Override + public QueryUpdateService getUpdateService() + { + return new DefaultQueryUpdateService(this, this.getRealTable()); + } + + @Override + public String getDescription() + { + return super.getDescription(); + } + + @Override + public boolean hasPermission(UserPrincipal user, Class perm) + { + return getContainer().hasPermission(user, perm); + } +} diff --git a/study/src/org/labkey/study/query/studydesign/StudyProductAntigenDomainKind.java b/study/src/org/labkey/study/query/studydesign/StudyProductAntigenDomainKind.java new file mode 100644 index 00000000..9caaac17 --- /dev/null +++ b/study/src/org/labkey/study/query/studydesign/StudyProductAntigenDomainKind.java @@ -0,0 +1,48 @@ +package org.labkey.study.query.studydesign; + +import org.labkey.api.data.JdbcType; +import org.labkey.api.data.PropertyStorageSpec; +import org.labkey.study.query.StudyQuerySchema; + +import java.util.Collections; +import java.util.LinkedHashSet; +import java.util.Set; + +/** + * Created by klum on 12/13/13. + */ +public class StudyProductAntigenDomainKind extends AbstractStudyDesignDomainKind +{ + public static final String NAME = "StudyProductAntigenDomain"; + public static String NAMESPACE_PREFIX = "StudyDesign-" + NAME; + private static final Set _baseFields; + + static { + Set baseFields = new LinkedHashSet<>(); + baseFields.add(createFieldSpec("RowId", JdbcType.INTEGER, true, true)); + baseFields.add(createFieldSpec("ProductId", JdbcType.INTEGER).setNullable(false)); + baseFields.add(createFieldSpec("Gene", JdbcType.VARCHAR).setSize(200)); + baseFields.add(createFieldSpec("SubType", JdbcType.VARCHAR).setSize(200)); + baseFields.add(createFieldSpec("GenBankId", JdbcType.VARCHAR).setSize(200)); + baseFields.add(createFieldSpec("Sequence", JdbcType.VARCHAR).setSize(200)); + + _baseFields = Collections.unmodifiableSet(baseFields); + } + + public StudyProductAntigenDomainKind() + { + super(StudyQuerySchema.PRODUCT_ANTIGEN_TABLE_NAME, _baseFields); + } + + @Override + protected String getNamespacePrefix() + { + return NAMESPACE_PREFIX; + } + + @Override + public String getKindName() + { + return NAME; + } +} diff --git a/study/src/org/labkey/study/query/studydesign/StudyProductAntigenTable.java b/study/src/org/labkey/study/query/studydesign/StudyProductAntigenTable.java new file mode 100644 index 00000000..97adbcfe --- /dev/null +++ b/study/src/org/labkey/study/query/studydesign/StudyProductAntigenTable.java @@ -0,0 +1,56 @@ +package org.labkey.study.query.studydesign; + +import org.labkey.api.data.ColumnInfo; +import org.labkey.api.data.DbSchema; +import org.labkey.api.data.TableInfo; +import org.labkey.api.exp.property.Domain; +import org.labkey.api.query.FieldKey; +import org.labkey.api.query.LookupForeignKey; +import org.labkey.api.query.QueryService; +import org.labkey.api.query.UserSchema; +import org.labkey.study.query.StudyQuerySchema; + +import java.util.ArrayList; +import java.util.List; + +/** + * Created by klum on 12/13/13. + */ +public class StudyProductAntigenTable extends DefaultStudyDesignTable +{ + static final List defaultVisibleColumns = new ArrayList<>(); + + static { + + defaultVisibleColumns.add(FieldKey.fromParts("Container")); + defaultVisibleColumns.add(FieldKey.fromParts("ProductId")); + defaultVisibleColumns.add(FieldKey.fromParts("Gene")); + defaultVisibleColumns.add(FieldKey.fromParts("SubType")); + defaultVisibleColumns.add(FieldKey.fromParts("GenBankId")); + defaultVisibleColumns.add(FieldKey.fromParts("Sequence")); + } + + public StudyProductAntigenTable(Domain domain, DbSchema dbSchema, UserSchema schema) + { + super(domain, dbSchema, schema); + + setName(StudyQuerySchema.PRODUCT_ANTIGEN_TABLE_NAME); + setDescription("Contains one row per study antigen"); + + ColumnInfo productIdCol = getColumn("ProductId"); + productIdCol.setFk(new LookupForeignKey("RowId") + { + @Override + public TableInfo getLookupTableInfo() + { + return QueryService.get().getUserSchema(_userSchema.getUser(), _userSchema.getContainer(), StudyQuerySchema.SCHEMA_NAME).getTable(StudyQuerySchema.PRODUCT_TABLE_NAME); + } + }); + } + + @Override + public List getDefaultVisibleColumns() + { + return defaultVisibleColumns; + } +} diff --git a/study/src/org/labkey/study/query/studydesign/StudyProductDomainKind.java b/study/src/org/labkey/study/query/studydesign/StudyProductDomainKind.java new file mode 100644 index 00000000..23d2e012 --- /dev/null +++ b/study/src/org/labkey/study/query/studydesign/StudyProductDomainKind.java @@ -0,0 +1,54 @@ +package org.labkey.study.query.studydesign; + +import org.labkey.api.data.JdbcType; +import org.labkey.api.data.PropertyStorageSpec; +import org.labkey.study.query.StudyQuerySchema; + +import java.util.Collections; +import java.util.LinkedHashSet; +import java.util.Set; + +/** + * Created by klum on 12/10/13. + */ +public class StudyProductDomainKind extends AbstractStudyDesignDomainKind +{ + public static final String NAME = "StudyProductDomain"; + public static String NAMESPACE_PREFIX = "StudyDesign-" + NAME; + private static final Set _baseFields; + + static { + Set baseFields = new LinkedHashSet<>(); + baseFields.add(createFieldSpec("RowId", JdbcType.INTEGER, true, true)); + baseFields.add(createFieldSpec("Label", JdbcType.VARCHAR).setSize(200).setNullable(false)); + baseFields.add(createFieldSpec("Role", JdbcType.VARCHAR).setSize(200)); + baseFields.add(createFieldSpec("Type", JdbcType.VARCHAR).setSize(200)); + + _baseFields = Collections.unmodifiableSet(baseFields); + } + + public StudyProductDomainKind() + { + super(StudyQuerySchema.PRODUCT_TABLE_NAME, _baseFields); + } + +/* + @Override + public Set getPropertyIndices() + { + return PageFlowUtil.set(new PropertyStorageSpec.Index(false, COLUMN_NAME_ATTACHMENT_PARENT_ENTITY_ID)); + } +*/ + + @Override + protected String getNamespacePrefix() + { + return NAMESPACE_PREFIX; + } + + @Override + public String getKindName() + { + return NAME; + } +} diff --git a/study/src/org/labkey/study/query/studydesign/StudyProductTable.java b/study/src/org/labkey/study/query/studydesign/StudyProductTable.java new file mode 100644 index 00000000..3f338d8a --- /dev/null +++ b/study/src/org/labkey/study/query/studydesign/StudyProductTable.java @@ -0,0 +1,41 @@ +package org.labkey.study.query.studydesign; + +import org.labkey.api.data.ColumnInfo; +import org.labkey.api.data.DbSchema; +import org.labkey.api.exp.property.Domain; +import org.labkey.api.query.FieldKey; +import org.labkey.api.query.UserSchema; +import org.labkey.study.query.StudyQuerySchema; + +import java.util.ArrayList; +import java.util.List; + +/** + * Created by klum on 12/12/13. + */ +public class StudyProductTable extends DefaultStudyDesignTable +{ + static final List defaultVisibleColumns = new ArrayList<>(); + + static { + + defaultVisibleColumns.add(FieldKey.fromParts("Container")); + defaultVisibleColumns.add(FieldKey.fromParts("Label")); + defaultVisibleColumns.add(FieldKey.fromParts("Role")); + defaultVisibleColumns.add(FieldKey.fromParts("Type")); + } + + public StudyProductTable(Domain domain, DbSchema dbSchema, UserSchema schema) + { + super(domain, dbSchema, schema); + + setName(StudyQuerySchema.PRODUCT_TABLE_NAME); + setDescription("Contains one row per study product"); + } + + @Override + public List getDefaultVisibleColumns() + { + return defaultVisibleColumns; + } +} diff --git a/study/src/org/labkey/study/query/studydesign/StudyTreatmentDomainKind.java b/study/src/org/labkey/study/query/studydesign/StudyTreatmentDomainKind.java new file mode 100644 index 00000000..3eebc867 --- /dev/null +++ b/study/src/org/labkey/study/query/studydesign/StudyTreatmentDomainKind.java @@ -0,0 +1,50 @@ +package org.labkey.study.query.studydesign; + +import org.labkey.api.data.JdbcType; +import org.labkey.api.data.PropertyStorageSpec; +import org.labkey.study.query.StudyQuerySchema; + +import java.util.Collections; +import java.util.LinkedHashSet; +import java.util.Set; + +/** + * User: cnathe + * Date: 12/17/13 + */ +public class StudyTreatmentDomainKind extends AbstractStudyDesignDomainKind +{ + public static final String NAME = "StudyTreatmentDomain"; + public static String NAMESPACE_PREFIX = "StudyDesign-" + NAME; + private static final Set _baseFields; + + static { + Set baseFields = new LinkedHashSet<>(); + baseFields.add(createFieldSpec("RowId", JdbcType.INTEGER, true, true)); + baseFields.add(createFieldSpec("Label", JdbcType.VARCHAR).setSize(200).setNullable(false)); + baseFields.add(createFieldSpec("Description", JdbcType.VARCHAR)); + + PropertyStorageSpec rendererTypeFieldSpec = createFieldSpec("DescriptionRendererType", JdbcType.VARCHAR).setSize(50).setNullable(false); + rendererTypeFieldSpec.setDefaultValue("TEXT_WITH_LINKS"); + baseFields.add(rendererTypeFieldSpec); + + _baseFields = Collections.unmodifiableSet(baseFields); + } + + public StudyTreatmentDomainKind() + { + super(StudyQuerySchema.TREATMENT_TABLE_NAME, _baseFields); + } + + @Override + protected String getNamespacePrefix() + { + return NAMESPACE_PREFIX; + } + + @Override + public String getKindName() + { + return NAME; + } +} diff --git a/study/src/org/labkey/study/query/studydesign/StudyTreatmentProductDomainKind.java b/study/src/org/labkey/study/query/studydesign/StudyTreatmentProductDomainKind.java new file mode 100644 index 00000000..a2ad7fbd --- /dev/null +++ b/study/src/org/labkey/study/query/studydesign/StudyTreatmentProductDomainKind.java @@ -0,0 +1,47 @@ +package org.labkey.study.query.studydesign; + +import org.labkey.api.data.JdbcType; +import org.labkey.api.data.PropertyStorageSpec; +import org.labkey.study.query.StudyQuerySchema; + +import java.util.Collections; +import java.util.LinkedHashSet; +import java.util.Set; + +/** + * Created by klum on 12/13/13. + */ +public class StudyTreatmentProductDomainKind extends AbstractStudyDesignDomainKind +{ + public static final String NAME = "StudyTreatmentProductDomain"; + public static String NAMESPACE_PREFIX = "StudyDesign-" + NAME; + private static final Set _baseFields; + + static { + Set baseFields = new LinkedHashSet<>(); + baseFields.add(createFieldSpec("RowId", JdbcType.INTEGER, true, true)); + baseFields.add(createFieldSpec("TreatmentId", JdbcType.INTEGER).setNullable(false)); + baseFields.add(createFieldSpec("ProductId", JdbcType.INTEGER).setNullable(false)); + baseFields.add(createFieldSpec("Dose", JdbcType.VARCHAR).setSize(200)); + baseFields.add(createFieldSpec("Route", JdbcType.VARCHAR).setSize(200)); + + _baseFields = Collections.unmodifiableSet(baseFields); + } + + public StudyTreatmentProductDomainKind() + { + super(StudyQuerySchema.TREATMENT_PRODUCT_MAP_TABLE_NAME, _baseFields); + } + + @Override + protected String getNamespacePrefix() + { + return NAMESPACE_PREFIX; + } + + @Override + public String getKindName() + { + return NAME; + } +} diff --git a/study/src/org/labkey/study/query/studydesign/StudyTreatmentProductTable.java b/study/src/org/labkey/study/query/studydesign/StudyTreatmentProductTable.java new file mode 100644 index 00000000..7bd7620c --- /dev/null +++ b/study/src/org/labkey/study/query/studydesign/StudyTreatmentProductTable.java @@ -0,0 +1,64 @@ +package org.labkey.study.query.studydesign; + +import org.labkey.api.data.ColumnInfo; +import org.labkey.api.data.DbSchema; +import org.labkey.api.data.TableInfo; +import org.labkey.api.exp.property.Domain; +import org.labkey.api.query.FieldKey; +import org.labkey.api.query.LookupForeignKey; +import org.labkey.api.query.QueryService; +import org.labkey.api.query.UserSchema; +import org.labkey.study.query.StudyQuerySchema; + +import java.util.ArrayList; +import java.util.List; + +/** + * Created by klum on 12/13/13. + */ +public class StudyTreatmentProductTable extends DefaultStudyDesignTable +{ + static final List defaultVisibleColumns = new ArrayList<>(); + + static { + + defaultVisibleColumns.add(FieldKey.fromParts("Container")); + defaultVisibleColumns.add(FieldKey.fromParts("TreatmentId")); + defaultVisibleColumns.add(FieldKey.fromParts("ProductId")); + defaultVisibleColumns.add(FieldKey.fromParts("Dose")); + defaultVisibleColumns.add(FieldKey.fromParts("Route")); + } + + public StudyTreatmentProductTable(Domain domain, DbSchema dbSchema, UserSchema schema) + { + super(domain, dbSchema, schema); + + setName(StudyQuerySchema.TREATMENT_PRODUCT_MAP_TABLE_NAME); + + ColumnInfo productIdCol = getColumn("ProductId"); + productIdCol.setFk(new LookupForeignKey("RowId") + { + @Override + public TableInfo getLookupTableInfo() + { + return QueryService.get().getUserSchema(_userSchema.getUser(), _userSchema.getContainer(), StudyQuerySchema.SCHEMA_NAME).getTable(StudyQuerySchema.PRODUCT_TABLE_NAME); + } + }); + + ColumnInfo treatmentIdCol = getColumn("TreatmentId"); + treatmentIdCol.setFk(new LookupForeignKey("RowId") + { + @Override + public TableInfo getLookupTableInfo() + { + return QueryService.get().getUserSchema(_userSchema.getUser(), _userSchema.getContainer(), StudyQuerySchema.SCHEMA_NAME).getTable(StudyQuerySchema.TREATMENT_TABLE_NAME); + } + }); + } + + @Override + public List getDefaultVisibleColumns() + { + return defaultVisibleColumns; + } +} diff --git a/study/src/org/labkey/study/query/studydesign/StudyTreatmentTable.java b/study/src/org/labkey/study/query/studydesign/StudyTreatmentTable.java new file mode 100644 index 00000000..5d6e14d8 --- /dev/null +++ b/study/src/org/labkey/study/query/studydesign/StudyTreatmentTable.java @@ -0,0 +1,69 @@ +package org.labkey.study.query.studydesign; + +import org.labkey.api.data.ColumnInfo; +import org.labkey.api.data.DbSchema; +import org.labkey.api.data.DisplayColumn; +import org.labkey.api.data.DisplayColumnFactory; +import org.labkey.api.data.TableInfo; +import org.labkey.api.exp.property.Domain; +import org.labkey.api.query.FieldKey; +import org.labkey.api.query.LookupForeignKey; +import org.labkey.api.query.QueryService; +import org.labkey.api.query.UserSchema; +import org.labkey.api.wiki.WikiRendererDisplayColumn; +import org.labkey.api.wiki.WikiRendererType; +import org.labkey.api.wiki.WikiService; +import org.labkey.study.query.StudyQuerySchema; + +import java.util.ArrayList; +import java.util.List; + +/** + * User: cnathe + * Date: 12/17/13 + */ +public class StudyTreatmentTable extends DefaultStudyDesignTable +{ + static final List defaultVisibleColumns = new ArrayList<>(); + + static { + + defaultVisibleColumns.add(FieldKey.fromParts("Container")); + defaultVisibleColumns.add(FieldKey.fromParts("Label")); + defaultVisibleColumns.add(FieldKey.fromParts("Description")); + } + + public StudyTreatmentTable(Domain domain, DbSchema dbSchema, UserSchema schema) + { + super(domain, dbSchema, schema); + + setName(StudyQuerySchema.TREATMENT_TABLE_NAME); + setDescription("Contains one row per study treatment"); + + final ColumnInfo descriptionRendererTypeColumn = getColumn("DescriptionRendererType"); + descriptionRendererTypeColumn.setFk(new LookupForeignKey("Value") + { + @Override + public TableInfo getLookupTableInfo() + { + return QueryService.get().getUserSchema(_userSchema.getUser(), _userSchema.getContainer(), WikiService.SCHEMA_NAME).getTable(WikiService.RENDERER_TYPE_TABLE_NAME); + } + }); + + ColumnInfo descriptionColumn = getColumn("Description"); + descriptionColumn.setDisplayColumnFactory(new DisplayColumnFactory() + { + @Override + public DisplayColumn createRenderer(ColumnInfo colInfo) + { + return new WikiRendererDisplayColumn(colInfo, descriptionRendererTypeColumn.getName(), WikiRendererType.TEXT_WITH_LINKS); + } + }); + } + + @Override + public List getDefaultVisibleColumns() + { + return defaultVisibleColumns; + } +} From be6e95b97496e14d9fea911340640c2ca902654e Mon Sep 17 00:00:00 2001 From: Karl Lum Date: Thu, 19 Dec 2013 20:29:17 +0000 Subject: [PATCH 005/218] 19225: Study design provision table records not deleted with container delete --- .../AbstractStudyDesignDomainKind.java | 43 +++---------------- 1 file changed, 6 insertions(+), 37 deletions(-) diff --git a/study/src/org/labkey/study/query/studydesign/AbstractStudyDesignDomainKind.java b/study/src/org/labkey/study/query/studydesign/AbstractStudyDesignDomainKind.java index 240f55eb..b18f30bd 100644 --- a/study/src/org/labkey/study/query/studydesign/AbstractStudyDesignDomainKind.java +++ b/study/src/org/labkey/study/query/studydesign/AbstractStudyDesignDomainKind.java @@ -133,20 +133,18 @@ public SQLFragment sqlObjectIdsInDomain(Domain domain) return new SQLFragment("NULL"); } - protected static Container getDomainContainer() + protected static Container getDomainContainer(Container c) { - return ContainerManager.getSharedContainer(); + // for now create the domains per folder, override to root the domains at + // a higher level + return c; + //return ContainerManager.getSharedContainer(); } @Override public String generateDomainURI(String schemaName, String tableName, Container c, User u) { - return getDomainURI(schemaName, tableName, getNamespacePrefix(), getDomainContainer(), u); - } - - public String getDomainURI() - { - return getDomainURI(AbstractAuditTypeProvider.SCHEMA_NAME, getTableName(), getNamespacePrefix(), getDomainContainer(), null); + return getDomainURI(schemaName, tableName, getNamespacePrefix(), getDomainContainer(c), u); } public static String getDomainURI(String schemaName, String tableName, String namespacePrefix, Container c, User u) @@ -166,11 +164,6 @@ public static String getDomainURI(String schemaName, String tableName, String na } } - private String generatePropertyURI(String propertyName) - { - return getDomainURI() + "#" + propertyName; - } - @Override public ActionURL urlShowData(Domain domain, ContainerUser containerUser) { @@ -234,30 +227,6 @@ public Priority getPriority(String domainURI) return lsid.getNamespacePrefix() != null && lsid.getNamespacePrefix().startsWith(getNamespacePrefix()) ? Handler.Priority.MEDIUM : null; } - protected PropertyDescriptor createPropertyDescriptor(@NotNull String name, @NotNull PropertyType type) - { - return createPropertyDescriptor(name, type, null, null, false); - } - - protected PropertyDescriptor createPropertyDescriptor( - @NotNull String name, @NotNull PropertyType type, - @Nullable String caption, @Nullable String description, - boolean required) - { - Container domainContainer = getDomainContainer(); - - String propertyURI = generatePropertyURI(name); - - PropertyDescriptor pd = new PropertyDescriptor(propertyURI, type.getTypeUri(), name, domainContainer); - if (caption != null) - pd.setLabel(caption); - if (description != null) - pd.setDescription(description); - pd.setRequired(required); - - return pd; - } - @Override public Set getNonProvisionedTableNames() { From 1bc90017c1b531226599100a783abfcdd7d5153a Mon Sep 17 00:00:00 2001 From: Adam Rauch Date: Fri, 20 Dec 2013 17:58:05 +0000 Subject: [PATCH 006/218] Update licenses --- .../study/model/AssaySpecimenConfigImpl.java | 15 +++++++++++++++ .../AbstractStudyDesignDomainKind.java | 15 +++++++++++++++ .../studydesign/DefaultStudyDesignTable.java | 15 +++++++++++++++ .../StudyProductAntigenDomainKind.java | 15 +++++++++++++++ .../studydesign/StudyProductAntigenTable.java | 15 +++++++++++++++ .../query/studydesign/StudyProductDomainKind.java | 15 +++++++++++++++ .../query/studydesign/StudyProductTable.java | 15 +++++++++++++++ .../studydesign/StudyTreatmentDomainKind.java | 15 +++++++++++++++ .../StudyTreatmentProductDomainKind.java | 15 +++++++++++++++ .../studydesign/StudyTreatmentProductTable.java | 15 +++++++++++++++ .../query/studydesign/StudyTreatmentTable.java | 15 +++++++++++++++ 11 files changed, 165 insertions(+) diff --git a/study/src/org/labkey/study/model/AssaySpecimenConfigImpl.java b/study/src/org/labkey/study/model/AssaySpecimenConfigImpl.java index 86766c66..bcac8ddd 100644 --- a/study/src/org/labkey/study/model/AssaySpecimenConfigImpl.java +++ b/study/src/org/labkey/study/model/AssaySpecimenConfigImpl.java @@ -1,3 +1,18 @@ +/* + * Copyright (c) 2013 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.study.model; import org.labkey.api.data.Entity; diff --git a/study/src/org/labkey/study/query/studydesign/AbstractStudyDesignDomainKind.java b/study/src/org/labkey/study/query/studydesign/AbstractStudyDesignDomainKind.java index b18f30bd..7ccbb354 100644 --- a/study/src/org/labkey/study/query/studydesign/AbstractStudyDesignDomainKind.java +++ b/study/src/org/labkey/study/query/studydesign/AbstractStudyDesignDomainKind.java @@ -1,3 +1,18 @@ +/* + * Copyright (c) 2013 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.study.query.studydesign; import org.jetbrains.annotations.NotNull; diff --git a/study/src/org/labkey/study/query/studydesign/DefaultStudyDesignTable.java b/study/src/org/labkey/study/query/studydesign/DefaultStudyDesignTable.java index 918122c4..a83926e2 100644 --- a/study/src/org/labkey/study/query/studydesign/DefaultStudyDesignTable.java +++ b/study/src/org/labkey/study/query/studydesign/DefaultStudyDesignTable.java @@ -1,3 +1,18 @@ +/* + * Copyright (c) 2013 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.study.query.studydesign; import org.jetbrains.annotations.Nullable; diff --git a/study/src/org/labkey/study/query/studydesign/StudyProductAntigenDomainKind.java b/study/src/org/labkey/study/query/studydesign/StudyProductAntigenDomainKind.java index 9caaac17..926c90e3 100644 --- a/study/src/org/labkey/study/query/studydesign/StudyProductAntigenDomainKind.java +++ b/study/src/org/labkey/study/query/studydesign/StudyProductAntigenDomainKind.java @@ -1,3 +1,18 @@ +/* + * Copyright (c) 2013 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.study.query.studydesign; import org.labkey.api.data.JdbcType; diff --git a/study/src/org/labkey/study/query/studydesign/StudyProductAntigenTable.java b/study/src/org/labkey/study/query/studydesign/StudyProductAntigenTable.java index 97adbcfe..5caecdf2 100644 --- a/study/src/org/labkey/study/query/studydesign/StudyProductAntigenTable.java +++ b/study/src/org/labkey/study/query/studydesign/StudyProductAntigenTable.java @@ -1,3 +1,18 @@ +/* + * Copyright (c) 2013 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.study.query.studydesign; import org.labkey.api.data.ColumnInfo; diff --git a/study/src/org/labkey/study/query/studydesign/StudyProductDomainKind.java b/study/src/org/labkey/study/query/studydesign/StudyProductDomainKind.java index 23d2e012..41cc9640 100644 --- a/study/src/org/labkey/study/query/studydesign/StudyProductDomainKind.java +++ b/study/src/org/labkey/study/query/studydesign/StudyProductDomainKind.java @@ -1,3 +1,18 @@ +/* + * Copyright (c) 2013 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.study.query.studydesign; import org.labkey.api.data.JdbcType; diff --git a/study/src/org/labkey/study/query/studydesign/StudyProductTable.java b/study/src/org/labkey/study/query/studydesign/StudyProductTable.java index 3f338d8a..1a8eb9fb 100644 --- a/study/src/org/labkey/study/query/studydesign/StudyProductTable.java +++ b/study/src/org/labkey/study/query/studydesign/StudyProductTable.java @@ -1,3 +1,18 @@ +/* + * Copyright (c) 2013 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.study.query.studydesign; import org.labkey.api.data.ColumnInfo; diff --git a/study/src/org/labkey/study/query/studydesign/StudyTreatmentDomainKind.java b/study/src/org/labkey/study/query/studydesign/StudyTreatmentDomainKind.java index 3eebc867..c3d64f98 100644 --- a/study/src/org/labkey/study/query/studydesign/StudyTreatmentDomainKind.java +++ b/study/src/org/labkey/study/query/studydesign/StudyTreatmentDomainKind.java @@ -1,3 +1,18 @@ +/* + * Copyright (c) 2013 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.study.query.studydesign; import org.labkey.api.data.JdbcType; diff --git a/study/src/org/labkey/study/query/studydesign/StudyTreatmentProductDomainKind.java b/study/src/org/labkey/study/query/studydesign/StudyTreatmentProductDomainKind.java index a2ad7fbd..df1cc1c8 100644 --- a/study/src/org/labkey/study/query/studydesign/StudyTreatmentProductDomainKind.java +++ b/study/src/org/labkey/study/query/studydesign/StudyTreatmentProductDomainKind.java @@ -1,3 +1,18 @@ +/* + * Copyright (c) 2013 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.study.query.studydesign; import org.labkey.api.data.JdbcType; diff --git a/study/src/org/labkey/study/query/studydesign/StudyTreatmentProductTable.java b/study/src/org/labkey/study/query/studydesign/StudyTreatmentProductTable.java index 7bd7620c..26d921a2 100644 --- a/study/src/org/labkey/study/query/studydesign/StudyTreatmentProductTable.java +++ b/study/src/org/labkey/study/query/studydesign/StudyTreatmentProductTable.java @@ -1,3 +1,18 @@ +/* + * Copyright (c) 2013 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.study.query.studydesign; import org.labkey.api.data.ColumnInfo; diff --git a/study/src/org/labkey/study/query/studydesign/StudyTreatmentTable.java b/study/src/org/labkey/study/query/studydesign/StudyTreatmentTable.java index 5d6e14d8..957677a1 100644 --- a/study/src/org/labkey/study/query/studydesign/StudyTreatmentTable.java +++ b/study/src/org/labkey/study/query/studydesign/StudyTreatmentTable.java @@ -1,3 +1,18 @@ +/* + * Copyright (c) 2013 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.study.query.studydesign; import org.labkey.api.data.ColumnInfo; From 78f7bd2cec67da4bf8721660f66fdfcb2f4c5779 Mon Sep 17 00:00:00 2001 From: Cory Nathe Date: Thu, 26 Dec 2013 16:48:40 +0000 Subject: [PATCH 007/218] Feedback from code review: use initColumn method --- .../studydesign/StudyProductAntigenTable.java | 22 +++++++---- .../StudyTreatmentProductTable.java | 38 +++++++++++-------- .../studydesign/StudyTreatmentTable.java | 37 ++++++++++-------- 3 files changed, 59 insertions(+), 38 deletions(-) diff --git a/study/src/org/labkey/study/query/studydesign/StudyProductAntigenTable.java b/study/src/org/labkey/study/query/studydesign/StudyProductAntigenTable.java index 5caecdf2..b33deec8 100644 --- a/study/src/org/labkey/study/query/studydesign/StudyProductAntigenTable.java +++ b/study/src/org/labkey/study/query/studydesign/StudyProductAntigenTable.java @@ -50,17 +50,23 @@ public StudyProductAntigenTable(Domain domain, DbSchema dbSchema, UserSchema sch super(domain, dbSchema, schema); setName(StudyQuerySchema.PRODUCT_ANTIGEN_TABLE_NAME); - setDescription("Contains one row per study antigen"); + setDescription("Contains one row per study product antigen"); + } - ColumnInfo productIdCol = getColumn("ProductId"); - productIdCol.setFk(new LookupForeignKey("RowId") + @Override + protected void initColumn(ColumnInfo col) + { + if ("ProductId".equalsIgnoreCase(col.getName())) { - @Override - public TableInfo getLookupTableInfo() + col.setFk(new LookupForeignKey("RowId") { - return QueryService.get().getUserSchema(_userSchema.getUser(), _userSchema.getContainer(), StudyQuerySchema.SCHEMA_NAME).getTable(StudyQuerySchema.PRODUCT_TABLE_NAME); - } - }); + @Override + public TableInfo getLookupTableInfo() + { + return QueryService.get().getUserSchema(_userSchema.getUser(), _userSchema.getContainer(), StudyQuerySchema.SCHEMA_NAME).getTable(StudyQuerySchema.PRODUCT_TABLE_NAME); + } + }); + } } @Override diff --git a/study/src/org/labkey/study/query/studydesign/StudyTreatmentProductTable.java b/study/src/org/labkey/study/query/studydesign/StudyTreatmentProductTable.java index 26d921a2..9f82c2f8 100644 --- a/study/src/org/labkey/study/query/studydesign/StudyTreatmentProductTable.java +++ b/study/src/org/labkey/study/query/studydesign/StudyTreatmentProductTable.java @@ -49,26 +49,34 @@ public StudyTreatmentProductTable(Domain domain, DbSchema dbSchema, UserSchema s super(domain, dbSchema, schema); setName(StudyQuerySchema.TREATMENT_PRODUCT_MAP_TABLE_NAME); + setDescription("Contains one row per study treatment product"); + } - ColumnInfo productIdCol = getColumn("ProductId"); - productIdCol.setFk(new LookupForeignKey("RowId") + @Override + protected void initColumn(ColumnInfo col) + { + if ("ProductId".equalsIgnoreCase(col.getName())) { - @Override - public TableInfo getLookupTableInfo() + col.setFk(new LookupForeignKey("RowId") { - return QueryService.get().getUserSchema(_userSchema.getUser(), _userSchema.getContainer(), StudyQuerySchema.SCHEMA_NAME).getTable(StudyQuerySchema.PRODUCT_TABLE_NAME); - } - }); - - ColumnInfo treatmentIdCol = getColumn("TreatmentId"); - treatmentIdCol.setFk(new LookupForeignKey("RowId") + @Override + public TableInfo getLookupTableInfo() + { + return QueryService.get().getUserSchema(_userSchema.getUser(), _userSchema.getContainer(), StudyQuerySchema.SCHEMA_NAME).getTable(StudyQuerySchema.PRODUCT_TABLE_NAME); + } + }); + } + else if ("TreatmentId".equalsIgnoreCase(col.getName())) { - @Override - public TableInfo getLookupTableInfo() + col.setFk(new LookupForeignKey("RowId") { - return QueryService.get().getUserSchema(_userSchema.getUser(), _userSchema.getContainer(), StudyQuerySchema.SCHEMA_NAME).getTable(StudyQuerySchema.TREATMENT_TABLE_NAME); - } - }); + @Override + public TableInfo getLookupTableInfo() + { + return QueryService.get().getUserSchema(_userSchema.getUser(), _userSchema.getContainer(), StudyQuerySchema.SCHEMA_NAME).getTable(StudyQuerySchema.TREATMENT_TABLE_NAME); + } + }); + } } @Override diff --git a/study/src/org/labkey/study/query/studydesign/StudyTreatmentTable.java b/study/src/org/labkey/study/query/studydesign/StudyTreatmentTable.java index 957677a1..a610f803 100644 --- a/study/src/org/labkey/study/query/studydesign/StudyTreatmentTable.java +++ b/study/src/org/labkey/study/query/studydesign/StudyTreatmentTable.java @@ -54,26 +54,33 @@ public StudyTreatmentTable(Domain domain, DbSchema dbSchema, UserSchema schema) setName(StudyQuerySchema.TREATMENT_TABLE_NAME); setDescription("Contains one row per study treatment"); + } - final ColumnInfo descriptionRendererTypeColumn = getColumn("DescriptionRendererType"); - descriptionRendererTypeColumn.setFk(new LookupForeignKey("Value") + @Override + protected void initColumn(ColumnInfo col) + { + if ("Description".equalsIgnoreCase(col.getName())) { - @Override - public TableInfo getLookupTableInfo() + col.setDisplayColumnFactory(new DisplayColumnFactory() { - return QueryService.get().getUserSchema(_userSchema.getUser(), _userSchema.getContainer(), WikiService.SCHEMA_NAME).getTable(WikiService.RENDERER_TYPE_TABLE_NAME); - } - }); - - ColumnInfo descriptionColumn = getColumn("Description"); - descriptionColumn.setDisplayColumnFactory(new DisplayColumnFactory() + @Override + public DisplayColumn createRenderer(ColumnInfo colInfo) + { + return new WikiRendererDisplayColumn(colInfo, "DescriptionRendererType", WikiRendererType.TEXT_WITH_LINKS); + } + }); + } + else if ("DescriptionRendererType".equalsIgnoreCase(col.getName())) { - @Override - public DisplayColumn createRenderer(ColumnInfo colInfo) + col.setFk(new LookupForeignKey("Value") { - return new WikiRendererDisplayColumn(colInfo, descriptionRendererTypeColumn.getName(), WikiRendererType.TEXT_WITH_LINKS); - } - }); + @Override + public TableInfo getLookupTableInfo() + { + return QueryService.get().getUserSchema(_userSchema.getUser(), _userSchema.getContainer(), WikiService.SCHEMA_NAME).getTable(WikiService.RENDERER_TYPE_TABLE_NAME); + } + }); + } } @Override From 918877ed5ea34b59008935b22401bed41a1b8e9f Mon Sep 17 00:00:00 2001 From: Cory Nathe Date: Fri, 27 Dec 2013 19:23:52 +0000 Subject: [PATCH 008/218] #193996: Convert study designer GWT components to Ext4 editable grids - Vaccine design: immunogens grid UI and add/remove buttons - Vaccine design: HIV antigens sub-grid dialog - Vaccine design: adjuvants grid UI and add/remove buttons - Vaccine Design: make use of project level lookups - Vaccine design: read/write to studydesign tables --- .../study/model/AssaySpecimenConfigImpl.java | 3 - .../study/model/ProductAntigenImpl.java | 91 +++ .../org/labkey/study/model/ProductImpl.java | 69 +++ .../AssayScheduleWebpartFactory.java | 69 +++ .../VaccineDesignWebpartFactory.java | 30 + .../view/studydesign/assayScheduleWebpart.jsp | 205 +++++++ .../view/studydesign/manageAssaySpecimen.jsp | 546 ++++++++++++++++++ .../view/studydesign/manageStudyProducts.jsp | 110 ++++ .../view/studydesign/vaccineDesignWebpart.jsp | 195 +++++++ 9 files changed, 1315 insertions(+), 3 deletions(-) create mode 100644 study/src/org/labkey/study/model/ProductAntigenImpl.java create mode 100644 study/src/org/labkey/study/model/ProductImpl.java create mode 100644 study/src/org/labkey/study/view/studydesign/AssayScheduleWebpartFactory.java create mode 100644 study/src/org/labkey/study/view/studydesign/VaccineDesignWebpartFactory.java create mode 100644 study/src/org/labkey/study/view/studydesign/assayScheduleWebpart.jsp create mode 100644 study/src/org/labkey/study/view/studydesign/manageAssaySpecimen.jsp create mode 100644 study/src/org/labkey/study/view/studydesign/manageStudyProducts.jsp create mode 100644 study/src/org/labkey/study/view/studydesign/vaccineDesignWebpart.jsp diff --git a/study/src/org/labkey/study/model/AssaySpecimenConfigImpl.java b/study/src/org/labkey/study/model/AssaySpecimenConfigImpl.java index bcac8ddd..ce4af5aa 100644 --- a/study/src/org/labkey/study/model/AssaySpecimenConfigImpl.java +++ b/study/src/org/labkey/study/model/AssaySpecimenConfigImpl.java @@ -15,11 +15,8 @@ */ package org.labkey.study.model; -import org.labkey.api.data.Entity; import org.labkey.api.study.AssaySpecimenConfig; -import java.util.List; - /** * User: cnathe * Date: 12/14/13 diff --git a/study/src/org/labkey/study/model/ProductAntigenImpl.java b/study/src/org/labkey/study/model/ProductAntigenImpl.java new file mode 100644 index 00000000..1e55bc62 --- /dev/null +++ b/study/src/org/labkey/study/model/ProductAntigenImpl.java @@ -0,0 +1,91 @@ +package org.labkey.study.model; + +import org.labkey.api.study.ProductAntigen; + +/** + * User: cnathe + * Date: 12/26/13 + */ +public class ProductAntigenImpl implements ProductAntigen +{ + private int _rowId; + private int _productId; + private String _gene; + private String _subType; + private String _genBankId; + private String _sequence; + + public ProductAntigenImpl() + { + } + + public boolean isNew() + { + return _rowId == 0; + } + + public Object getPrimaryKey() + { + return getRowId(); + } + + public int getRowId() + { + return _rowId; + } + + public void setRowId(int rowId) + { + _rowId = rowId; + } + + public int getProductId() + { + return _productId; + } + + public void setProductId(int productId) + { + _productId = productId; + } + + public String getGene() + { + return _gene; + } + + public void setGene(String gene) + { + _gene = gene; + } + + public String getSubType() + { + return _subType; + } + + public void setSubType(String subType) + { + _subType = subType; + } + + public String getGenBankId() + { + return _genBankId; + } + + public void setGenBankId(String genBankId) + { + _genBankId = genBankId; + } + + public String getSequence() + { + return _sequence; + } + + public void setSequence(String sequence) + { + _sequence = sequence; + } +} diff --git a/study/src/org/labkey/study/model/ProductImpl.java b/study/src/org/labkey/study/model/ProductImpl.java new file mode 100644 index 00000000..ed7b02bc --- /dev/null +++ b/study/src/org/labkey/study/model/ProductImpl.java @@ -0,0 +1,69 @@ +package org.labkey.study.model; + +import org.labkey.api.study.Product; + +/** + * User: cnathe + * Date: 12/26/13 + */ +public class ProductImpl implements Product +{ + private int _rowId; + private String _label; + private String _role; + private String _type; + + public ProductImpl() + { + } + + public boolean isNew() + { + return _rowId == 0; + } + + public Object getPrimaryKey() + { + return getRowId(); + } + + public int getRowId() + { + return _rowId; + } + + public void setRowId(int rowId) + { + _rowId = rowId; + } + + public String getLabel() + { + return _label; + } + + public void setLabel(String label) + { + _label = label; + } + + public String getRole() + { + return _role; + } + + public void setRole(String role) + { + _role = role; + } + + public String getType() + { + return _type; + } + + public void setType(String type) + { + _type = type; + } +} diff --git a/study/src/org/labkey/study/view/studydesign/AssayScheduleWebpartFactory.java b/study/src/org/labkey/study/view/studydesign/AssayScheduleWebpartFactory.java new file mode 100644 index 00000000..b70ded26 --- /dev/null +++ b/study/src/org/labkey/study/view/studydesign/AssayScheduleWebpartFactory.java @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2013 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.study.view.studydesign; + +import org.labkey.api.data.Container; +import org.labkey.api.study.Study; +import org.labkey.api.study.TimepointType; +import org.labkey.api.view.ActionURL; +import org.labkey.api.view.BaseWebPartFactory; +import org.labkey.api.view.JspView; +import org.labkey.api.view.NavTree; +import org.labkey.api.view.Portal; +import org.labkey.api.view.ViewContext; +import org.labkey.api.view.WebPartView; +import org.labkey.study.controllers.StudyController; +import org.labkey.study.model.StudyManager; +import org.labkey.study.security.permissions.ManageStudyPermission; + +/** + * User: cnathe + * Date: 12/16/13 + */ +public class AssayScheduleWebpartFactory extends BaseWebPartFactory +{ + public static String NAME = "Assay Schedule"; + + public AssayScheduleWebpartFactory() + { + super(NAME); + } + + @Override + public WebPartView getWebPartView(ViewContext portalCtx, Portal.WebPart webPart) throws Exception + { + JspView view = new JspView<>("/org/labkey/study/view/studydesign/assayScheduleWebpart.jsp", webPart); + view.setTitle(NAME); + view.setFrame(WebPartView.FrameType.PORTAL); + + Container c = portalCtx.getContainer(); + Study study = StudyManager.getInstance().getStudy(c); + if (c.hasPermission(portalCtx.getUser(), ManageStudyPermission.class)) + { + String timepointMenuName; + if (study != null && study.getTimepointType() == TimepointType.DATE) + timepointMenuName = "Manage Timepoints"; + else + timepointMenuName = "Manage Visits"; + + NavTree menu = new NavTree(); + menu.addChild(timepointMenuName, new ActionURL(StudyController.ManageVisitsAction.class, c)); + view.setNavMenu(menu); + } + + return view; + } +} diff --git a/study/src/org/labkey/study/view/studydesign/VaccineDesignWebpartFactory.java b/study/src/org/labkey/study/view/studydesign/VaccineDesignWebpartFactory.java new file mode 100644 index 00000000..34743e3e --- /dev/null +++ b/study/src/org/labkey/study/view/studydesign/VaccineDesignWebpartFactory.java @@ -0,0 +1,30 @@ +package org.labkey.study.view.studydesign; + +import org.labkey.api.view.BaseWebPartFactory; +import org.labkey.api.view.JspView; +import org.labkey.api.view.Portal; +import org.labkey.api.view.ViewContext; +import org.labkey.api.view.WebPartView; + +/** + * User: cnathe + * Date: 12/27/13 + */ +public class VaccineDesignWebpartFactory extends BaseWebPartFactory +{ + public static String NAME = "Vaccine Design"; + + public VaccineDesignWebpartFactory() + { + super(NAME); + } + + @Override + public WebPartView getWebPartView(ViewContext portalCtx, Portal.WebPart webPart) throws Exception + { + JspView view = new JspView<>("/org/labkey/study/view/studydesign/vaccineDesignWebpart.jsp", webPart); + view.setTitle(NAME); + view.setFrame(WebPartView.FrameType.PORTAL); + return view; + } +} diff --git a/study/src/org/labkey/study/view/studydesign/assayScheduleWebpart.jsp b/study/src/org/labkey/study/view/studydesign/assayScheduleWebpart.jsp new file mode 100644 index 00000000..b22a4e6f --- /dev/null +++ b/study/src/org/labkey/study/view/studydesign/assayScheduleWebpart.jsp @@ -0,0 +1,205 @@ +<% +/* + * Copyright (c) 2013 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. + */ +%> + +<%@ page import="org.labkey.api.data.Container" %> +<%@ page import="org.labkey.api.security.User" %> +<%@ page import="org.labkey.api.view.HttpView" %> +<%@ page import="org.labkey.api.view.JspView" %> +<%@ page import="org.labkey.api.view.Portal" %> +<%@ page import="org.labkey.study.model.StudyImpl" %> +<%@ page import="org.labkey.study.model.StudyManager" %> +<%@ page import="org.labkey.study.model.AssaySpecimenConfigImpl" %> +<%@ page import="org.labkey.api.util.PageFlowUtil" %> +<%@ page import="org.labkey.api.view.template.ClientDependency" %> +<%@ page import="java.util.LinkedHashSet" %> +<%@ page import="org.labkey.study.model.LocationImpl" %> +<%@ page import="org.labkey.study.controllers.StudyController" %> +<%@ page import="java.util.List" %> +<%@ page import="org.labkey.study.model.VisitImpl" %> +<%@ page import="org.labkey.api.view.WebThemeManager" %> +<%@ page import="org.labkey.api.view.WebTheme" %> +<%@ page import="org.labkey.study.SampleManager" %> +<%@ page import="org.labkey.study.model.PrimaryType" %> +<%@ page import="org.labkey.study.model.DerivativeType" %> +<%@ page import="org.labkey.study.security.permissions.ManageStudyPermission" %> +<%@ page extends="org.labkey.study.view.BaseStudyPage" %> +<%! + public LinkedHashSet getClientDependencies() + { + LinkedHashSet resources = new LinkedHashSet<>(); + resources.add(ClientDependency.fromFilePath("study/StudyVaccineDesign.css")); + return resources; + } +%> +<% + JspView me = (JspView) HttpView.currentView(); + Container c = me.getViewContext().getContainer(); + StudyImpl study = StudyManager.getInstance().getStudy(c); + + User user = me.getViewContext().getUser(); + boolean canManageStudy = c.hasPermission(user, ManageStudyPermission.class); + + String assayPlan = ""; + if (study != null && study.getAssayPlan() != null) + assayPlan = h(study.getAssayPlan()).replaceAll("\n", "
"); + + WebTheme theme = WebThemeManager.getTheme(c); + String link = theme.getLinkColor(); + String grid = theme.getGridColor(); +%> + + + +<% + if (study != null) + { + List assaySpecimenConfigs = study.getAssaySpecimenConfigs(); + List visits = StudyManager.getInstance().getVisitsForAssaySchedule(c); + +%> +

<%=assayPlan%>

+<% + + if (assaySpecimenConfigs.size() == 0) + { +%> +

No assays have been scheduled.

+<% + } + else + { +%> + + + + + +<% + for (VisitImpl visit : visits) + { +%> + +<% + } +%> + +<% + for (AssaySpecimenConfigImpl assaySpecimen : assaySpecimenConfigs) + { + // concatenate sample type (i.e. primary, derivative, tube type) + String sampleType = ""; + String sep = ""; + if (assaySpecimen.getPrimaryTypeId() != null) + { + PrimaryType pt = SampleManager.getInstance().getPrimaryType(c, assaySpecimen.getPrimaryTypeId()); + sampleType += (pt != null ? pt.getPrimaryType() : assaySpecimen.getPrimaryTypeId()); + sep = " / "; + } + if (assaySpecimen.getDerivativeTypeId() != null) + { + DerivativeType dt = SampleManager.getInstance().getDerivativeType(c, assaySpecimen.getDerivativeTypeId()); + sampleType += sep + (dt != null ? dt.getDerivative() : assaySpecimen.getDerivativeTypeId()); + sep = " / "; + } + if (assaySpecimen.getTubeType() != null) + { + sampleType += sep + assaySpecimen.getTubeType(); + } + + String locationLabel = ""; + if (assaySpecimen.getLocationId() != null) + { + LocationImpl location = StudyManager.getInstance().getLocation(c, assaySpecimen.getLocationId()); + locationLabel = location != null ? location.getLabel() : ""; + } + +%> + + + + +<% + List assaySpecimenVisits = StudyManager.getInstance().getAssaySpecimenVisitIds(c, assaySpecimen); + for (VisitImpl visit : visits) + { + %><% + } +%> + +<% + } +%> +
AssayLabSample Type + <%=h(visit.getDisplayString())%> + <%=(visit.getDescription() != null ? PageFlowUtil.helpPopup("Description", visit.getDescription()) : "")%> +
<%=h(assaySpecimen.getAssayName())%> + <%=(assaySpecimen.getDescription() != null ? PageFlowUtil.helpPopup("Description", assaySpecimen.getDescription()) : "")%> + <%=h(locationLabel)%><%=h(sampleType)%><%=h(assaySpecimenVisits.contains(visit.getRowId()) ? "[x]" : " ")%>
+<% + } + } + else + { +%> +

The folder must contain a study in order to display an assay schedule.

+<% + } + + if (canManageStudy) + { + %>
<%=textLink("Manage Assay Schedule", StudyController.ManageAssaySpecimenAction.class)%><% + } +%> \ No newline at end of file diff --git a/study/src/org/labkey/study/view/studydesign/manageAssaySpecimen.jsp b/study/src/org/labkey/study/view/studydesign/manageAssaySpecimen.jsp new file mode 100644 index 00000000..fa72fd8d --- /dev/null +++ b/study/src/org/labkey/study/view/studydesign/manageAssaySpecimen.jsp @@ -0,0 +1,546 @@ +<% +/* + * Copyright (c) 2013 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. + */ +%> + +<%@ page import="org.labkey.api.view.HttpView" %> +<%@ page import="org.labkey.api.view.JspView" %> +<%@ page import="org.labkey.api.view.ViewContext" %> +<%@ page import="org.labkey.api.view.template.ClientDependency" %> +<%@ page import="java.util.LinkedHashSet" %> +<%@ page import="org.labkey.api.study.TimepointType" %> +<%@ page import="org.labkey.api.study.Study" %> +<%@ page import="org.labkey.study.model.StudyManager" %> +<%@ page import="org.labkey.study.controllers.StudyController" %> +<%@ page import="org.labkey.api.study.Visit" %> +<%@ page import="org.labkey.api.view.ActionURL" %> +<%@ page extends="org.labkey.api.jsp.JspBase" %> +<%! + public LinkedHashSet getClientDependencies() + { + LinkedHashSet resources = new LinkedHashSet<>(); + resources.add(ClientDependency.fromFilePath("Ext4ClientApi")); + resources.add(ClientDependency.fromFilePath("dataview/DataViewsPanel.css")); + return resources; + } +%> +<% + JspView me = (JspView) HttpView.currentView(); + ViewContext context = me.getViewContext(); + ActionURL returnURL = context.getActionURL(); + + Study study = StudyManager.getInstance().getStudy(context.getContainer()); + String visitDisplayName = "Visit"; + if (study != null && study.getTimepointType() == TimepointType.DATE) + visitDisplayName = "Timepoint"; +%> + + + + + +
+* Double click to edit an assay/specimen configuration +


+
+<%=textLink("Create New " + visitDisplayName, new ActionURL(StudyController.CreateVisitAction.class, context.getContainer()).addReturnURL(returnURL))%> +<% + if (study != null && study.getTimepointType() == TimepointType.VISIT && study.getVisits(Visit.Order.DISPLAY).size() > 1) + { + %><%= textLink("Change Visit Order", new ActionURL(StudyController.VisitOrderAction.class, context.getContainer()).addReturnURL(returnURL)) %><% + } +%> +<%=textLink("Manage " + visitDisplayName + "s", StudyController.ManageVisitsAction.class)%> +


+
diff --git a/study/src/org/labkey/study/view/studydesign/manageStudyProducts.jsp b/study/src/org/labkey/study/view/studydesign/manageStudyProducts.jsp new file mode 100644 index 00000000..27d68342 --- /dev/null +++ b/study/src/org/labkey/study/view/studydesign/manageStudyProducts.jsp @@ -0,0 +1,110 @@ + +<%@ page import="java.util.LinkedHashSet" %> +<%@ page import="org.labkey.api.view.template.ClientDependency" %> +<%@ page extends="org.labkey.api.jsp.JspBase" %> +<%! + public LinkedHashSet getClientDependencies() + { + LinkedHashSet resources = new LinkedHashSet<>(); + resources.add(ClientDependency.fromFilePath("Ext4ClientApi")); + resources.add(ClientDependency.fromFilePath("study/ImmunizationSchedule.js")); + resources.add(ClientDependency.fromFilePath("dataview/DataViewsPanel.css")); + resources.add(ClientDependency.fromFilePath("study/StudyVaccineDesign.css")); + return resources; + } +%> + + + + + +Enter vaccine design information in the grids below. +
+
    +
  • Each immunogen and adjuvant in the study should be listed on one row of the grids below.
  • +
  • Immunogens and adjuvants should have unique names.
  • +
  • If possible the immunogen description should include specific sequences of HIV Antigens included in the immunogen.
  • +
  • Use the manage immunizations page to describe the schedule of immunizations and combinations of immunogens and adjuvants administered at each timepoint.
  • +
  • + Configure dropdown options at the project level to be shared across study designs or within this folder for + study specific properties: +
  • +
+
+
+* Double click a row to edit the label and type, double click the HIV Antigens cell to edit them separately +

+
+* Double click a row to edit the label + diff --git a/study/src/org/labkey/study/view/studydesign/vaccineDesignWebpart.jsp b/study/src/org/labkey/study/view/studydesign/vaccineDesignWebpart.jsp new file mode 100644 index 00000000..6ac07f3b --- /dev/null +++ b/study/src/org/labkey/study/view/studydesign/vaccineDesignWebpart.jsp @@ -0,0 +1,195 @@ +<% + /* + * Copyright (c) 2013 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. + */ +%> + +<%@ page import="org.labkey.api.data.Container" %> +<%@ page import="org.labkey.api.security.User" %> +<%@ page import="org.labkey.api.view.HttpView" %> +<%@ page import="org.labkey.api.view.JspView" %> +<%@ page import="org.labkey.api.view.Portal" %> +<%@ page import="org.labkey.study.model.StudyImpl" %> +<%@ page import="org.labkey.study.model.StudyManager" %> +<%@ page import="org.labkey.api.view.template.ClientDependency" %> +<%@ page import="java.util.LinkedHashSet" %> +<%@ page import="org.labkey.study.controllers.StudyController" %> +<%@ page import="java.util.List" %> +<%@ page import="org.labkey.api.view.WebThemeManager" %> +<%@ page import="org.labkey.api.view.WebTheme" %> +<%@ page import="org.labkey.study.security.permissions.ManageStudyPermission" %> +<%@ page import="org.labkey.study.model.ProductImpl" %> +<%@ page extends="org.labkey.study.view.BaseStudyPage" %> +<%! + public LinkedHashSet getClientDependencies() + { + LinkedHashSet resources = new LinkedHashSet<>(); + resources.add(ClientDependency.fromFilePath("study/ImmunizationSchedule.js")); + resources.add(ClientDependency.fromFilePath("study/StudyVaccineDesign.css")); + return resources; + } +%> +<% + JspView me = (JspView) HttpView.currentView(); + Container c = me.getViewContext().getContainer(); + StudyImpl study = StudyManager.getInstance().getStudy(c); + + User user = me.getViewContext().getUser(); + boolean canManageStudy = c.hasPermission(user, ManageStudyPermission.class); + + + + WebTheme theme = WebThemeManager.getTheme(c); + String link = theme.getLinkColor(); + String grid = theme.getGridColor(); +%> + + + +<% + if (study != null) + { +%> + + +<% + List immunogens = study.getStudyProducts(user, "Immunogen", null); + if (immunogens.size() == 0) + { + %><% + } + else + { +%> + + +<% + } +%> + +<% + List adjuvants = study.getStudyProducts(user, "Adjuvant", null); + if (adjuvants.size() == 0) + { + %><% + } + else + { +%> + +<% + } +%> +

Immunogens

No immunogens have been defined.
This section describes the immunogens and adjuvants evaluated in the study.
+ + + + + + +<% + for (ProductImpl immunogen : immunogens) + { +%> + + + + + +<% + } +%> +
LabelTypeHIV Antigens
<%=h(immunogen.getLabel())%><%=h(immunogen.getType())%>
...
+

Adjuvants

No adjuvants have been defined.
+ + +<% + for (ProductImpl adjuvant : adjuvants) + { + %><% + } +%> +
Label
<%=h(adjuvant.getLabel())%>
+
+<% + } + else + { +%> +

The folder must contain a study in order to display a vaccine design.

+<% + } + + if (canManageStudy) + { + %>
<%=textLink("Manage Study Products", StudyController.ManageStudyProductsAction.class)%><% + } +%> + + \ No newline at end of file From a933207e4c84c05b44161f585eb1d11aa610914e Mon Sep 17 00:00:00 2001 From: Karl Lum Date: Fri, 27 Dec 2013 20:43:04 +0000 Subject: [PATCH 009/218] 19282: File deletion gets logged even if it fails Create new study design provisioned domains (personnel, product, treatment) at the project level. --- .../query/studydesign/AbstractStudyDesignDomainKind.java | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/study/src/org/labkey/study/query/studydesign/AbstractStudyDesignDomainKind.java b/study/src/org/labkey/study/query/studydesign/AbstractStudyDesignDomainKind.java index 7ccbb354..a299aa13 100644 --- a/study/src/org/labkey/study/query/studydesign/AbstractStudyDesignDomainKind.java +++ b/study/src/org/labkey/study/query/studydesign/AbstractStudyDesignDomainKind.java @@ -150,10 +150,9 @@ public SQLFragment sqlObjectIdsInDomain(Domain domain) protected static Container getDomainContainer(Container c) { - // for now create the domains per folder, override to root the domains at - // a higher level - return c; - //return ContainerManager.getSharedContainer(); + // for now create the domains per project, override to root the domains at + // a different level. + return c.getProject(); } @Override From 8249886ea51fb01d9b785cf85a01244730aac32a Mon Sep 17 00:00:00 2001 From: Adam Rauch Date: Mon, 30 Dec 2013 15:17:02 +0000 Subject: [PATCH 010/218] Switch many JSPs to JspBase helpers for encoding content and retrieving context. For example: - h() instead of PageFlowUtil.filter() - getViewContext() instead of HttpView.currentContext(), HttpView.getRootContext(), me.getViewContext(), et al - getContainer(), getUser(), and getActionURL() instead of me.getViewContext().getContainer(), et al --- .../view/studydesign/manageAssaySpecimen.jsp | 23 ++++----- .../view/studydesign/vaccineDesignWebpart.jsp | 50 +++++++++---------- 2 files changed, 33 insertions(+), 40 deletions(-) diff --git a/study/src/org/labkey/study/view/studydesign/manageAssaySpecimen.jsp b/study/src/org/labkey/study/view/studydesign/manageAssaySpecimen.jsp index fa72fd8d..afb3b974 100644 --- a/study/src/org/labkey/study/view/studydesign/manageAssaySpecimen.jsp +++ b/study/src/org/labkey/study/view/studydesign/manageAssaySpecimen.jsp @@ -15,18 +15,17 @@ * limitations under the License. */ %> - +<%@ page import="org.labkey.api.study.Study" %> +<%@ page import="org.labkey.api.study.TimepointType" %> +<%@ page import="org.labkey.api.study.Visit" %> +<%@ page import="org.labkey.api.view.ActionURL" %> <%@ page import="org.labkey.api.view.HttpView" %> <%@ page import="org.labkey.api.view.JspView" %> <%@ page import="org.labkey.api.view.ViewContext" %> <%@ page import="org.labkey.api.view.template.ClientDependency" %> -<%@ page import="java.util.LinkedHashSet" %> -<%@ page import="org.labkey.api.study.TimepointType" %> -<%@ page import="org.labkey.api.study.Study" %> -<%@ page import="org.labkey.study.model.StudyManager" %> <%@ page import="org.labkey.study.controllers.StudyController" %> -<%@ page import="org.labkey.api.study.Visit" %> -<%@ page import="org.labkey.api.view.ActionURL" %> +<%@ page import="org.labkey.study.model.StudyManager" %> +<%@ page import="java.util.LinkedHashSet" %> <%@ page extends="org.labkey.api.jsp.JspBase" %> <%! public LinkedHashSet getClientDependencies() @@ -38,11 +37,9 @@ } %> <% - JspView me = (JspView) HttpView.currentView(); - ViewContext context = me.getViewContext(); - ActionURL returnURL = context.getActionURL(); + ActionURL returnURL = getActionURL(); - Study study = StudyManager.getInstance().getStudy(context.getContainer()); + Study study = StudyManager.getInstance().getStudy(getContainer()); String visitDisplayName = "Visit"; if (study != null && study.getTimepointType() == TimepointType.DATE) visitDisplayName = "Timepoint"; @@ -534,11 +531,11 @@ function removeSVC(el, scRowId, vRowId) * Double click to edit an assay/specimen configuration


-<%=textLink("Create New " + visitDisplayName, new ActionURL(StudyController.CreateVisitAction.class, context.getContainer()).addReturnURL(returnURL))%> +<%=textLink("Create New " + visitDisplayName, new ActionURL(StudyController.CreateVisitAction.class, getContainer()).addReturnURL(returnURL))%> <% if (study != null && study.getTimepointType() == TimepointType.VISIT && study.getVisits(Visit.Order.DISPLAY).size() > 1) { - %><%= textLink("Change Visit Order", new ActionURL(StudyController.VisitOrderAction.class, context.getContainer()).addReturnURL(returnURL)) %><% + %><%= textLink("Change Visit Order", new ActionURL(StudyController.VisitOrderAction.class, getContainer()).addReturnURL(returnURL)) %><% } %> <%=textLink("Manage " + visitDisplayName + "s", StudyController.ManageVisitsAction.class)%> diff --git a/study/src/org/labkey/study/view/studydesign/vaccineDesignWebpart.jsp b/study/src/org/labkey/study/view/studydesign/vaccineDesignWebpart.jsp index 6ac07f3b..f3435d94 100644 --- a/study/src/org/labkey/study/view/studydesign/vaccineDesignWebpart.jsp +++ b/study/src/org/labkey/study/view/studydesign/vaccineDesignWebpart.jsp @@ -1,36 +1,35 @@ <% - /* - * Copyright (c) 2013 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. - */ +/* + * Copyright (c) 2013 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. + */ %> - <%@ page import="org.labkey.api.data.Container" %> <%@ page import="org.labkey.api.security.User" %> <%@ page import="org.labkey.api.view.HttpView" %> <%@ page import="org.labkey.api.view.JspView" %> <%@ page import="org.labkey.api.view.Portal" %> +<%@ page import="org.labkey.api.view.WebTheme" %> +<%@ page import="org.labkey.api.view.WebThemeManager" %> +<%@ page import="org.labkey.api.view.template.ClientDependency" %> +<%@ page import="org.labkey.study.controllers.StudyController" %> +<%@ page import="org.labkey.study.model.ProductImpl" %> <%@ page import="org.labkey.study.model.StudyImpl" %> <%@ page import="org.labkey.study.model.StudyManager" %> -<%@ page import="org.labkey.api.view.template.ClientDependency" %> +<%@ page import="org.labkey.study.security.permissions.ManageStudyPermission" %> <%@ page import="java.util.LinkedHashSet" %> -<%@ page import="org.labkey.study.controllers.StudyController" %> <%@ page import="java.util.List" %> -<%@ page import="org.labkey.api.view.WebThemeManager" %> -<%@ page import="org.labkey.api.view.WebTheme" %> -<%@ page import="org.labkey.study.security.permissions.ManageStudyPermission" %> -<%@ page import="org.labkey.study.model.ProductImpl" %> <%@ page extends="org.labkey.study.view.BaseStudyPage" %> <%! public LinkedHashSet getClientDependencies() @@ -43,14 +42,11 @@ %> <% JspView me = (JspView) HttpView.currentView(); - Container c = me.getViewContext().getContainer(); + Container c = getContainer(); StudyImpl study = StudyManager.getInstance().getStudy(c); - - User user = me.getViewContext().getUser(); + User user = getUser(); boolean canManageStudy = c.hasPermission(user, ManageStudyPermission.class); - - WebTheme theme = WebThemeManager.getTheme(c); String link = theme.getLinkColor(); String grid = theme.getGridColor(); From 103827e495410813877afb1d71de94a3f4f5f632 Mon Sep 17 00:00:00 2001 From: Adam Rauch Date: Mon, 30 Dec 2013 15:27:30 +0000 Subject: [PATCH 011/218] A couple more JSP fixups --- .../view/studydesign/assayScheduleWebpart.jsp | 30 ++++++++----------- .../view/studydesign/manageStudyProducts.jsp | 2 +- 2 files changed, 14 insertions(+), 18 deletions(-) diff --git a/study/src/org/labkey/study/view/studydesign/assayScheduleWebpart.jsp b/study/src/org/labkey/study/view/studydesign/assayScheduleWebpart.jsp index b22a4e6f..d49adda8 100644 --- a/study/src/org/labkey/study/view/studydesign/assayScheduleWebpart.jsp +++ b/study/src/org/labkey/study/view/studydesign/assayScheduleWebpart.jsp @@ -18,25 +18,22 @@ <%@ page import="org.labkey.api.data.Container" %> <%@ page import="org.labkey.api.security.User" %> -<%@ page import="org.labkey.api.view.HttpView" %> -<%@ page import="org.labkey.api.view.JspView" %> -<%@ page import="org.labkey.api.view.Portal" %> -<%@ page import="org.labkey.study.model.StudyImpl" %> -<%@ page import="org.labkey.study.model.StudyManager" %> -<%@ page import="org.labkey.study.model.AssaySpecimenConfigImpl" %> <%@ page import="org.labkey.api.util.PageFlowUtil" %> -<%@ page import="org.labkey.api.view.template.ClientDependency" %> -<%@ page import="java.util.LinkedHashSet" %> -<%@ page import="org.labkey.study.model.LocationImpl" %> -<%@ page import="org.labkey.study.controllers.StudyController" %> -<%@ page import="java.util.List" %> -<%@ page import="org.labkey.study.model.VisitImpl" %> -<%@ page import="org.labkey.api.view.WebThemeManager" %> <%@ page import="org.labkey.api.view.WebTheme" %> +<%@ page import="org.labkey.api.view.WebThemeManager" %> +<%@ page import="org.labkey.api.view.template.ClientDependency" %> <%@ page import="org.labkey.study.SampleManager" %> -<%@ page import="org.labkey.study.model.PrimaryType" %> +<%@ page import="org.labkey.study.controllers.StudyController" %> +<%@ page import="org.labkey.study.model.AssaySpecimenConfigImpl" %> <%@ page import="org.labkey.study.model.DerivativeType" %> +<%@ page import="org.labkey.study.model.LocationImpl" %> +<%@ page import="org.labkey.study.model.PrimaryType" %> +<%@ page import="org.labkey.study.model.StudyImpl" %> +<%@ page import="org.labkey.study.model.StudyManager" %> +<%@ page import="org.labkey.study.model.VisitImpl" %> <%@ page import="org.labkey.study.security.permissions.ManageStudyPermission" %> +<%@ page import="java.util.LinkedHashSet" %> +<%@ page import="java.util.List" %> <%@ page extends="org.labkey.study.view.BaseStudyPage" %> <%! public LinkedHashSet getClientDependencies() @@ -47,11 +44,10 @@ } %> <% - JspView me = (JspView) HttpView.currentView(); - Container c = me.getViewContext().getContainer(); + Container c = getContainer(); StudyImpl study = StudyManager.getInstance().getStudy(c); - User user = me.getViewContext().getUser(); + User user = getUser(); boolean canManageStudy = c.hasPermission(user, ManageStudyPermission.class); String assayPlan = ""; diff --git a/study/src/org/labkey/study/view/studydesign/manageStudyProducts.jsp b/study/src/org/labkey/study/view/studydesign/manageStudyProducts.jsp index 27d68342..86fcaf54 100644 --- a/study/src/org/labkey/study/view/studydesign/manageStudyProducts.jsp +++ b/study/src/org/labkey/study/view/studydesign/manageStudyProducts.jsp @@ -1,6 +1,6 @@ -<%@ page import="java.util.LinkedHashSet" %> <%@ page import="org.labkey.api.view.template.ClientDependency" %> +<%@ page import="java.util.LinkedHashSet" %> <%@ page extends="org.labkey.api.jsp.JspBase" %> <%! public LinkedHashSet getClientDependencies() From 1da72171f5eff6f5dbc840c74b2c9c6bdc45216a Mon Sep 17 00:00:00 2001 From: Cory Nathe Date: Mon, 30 Dec 2013 17:36:00 +0000 Subject: [PATCH 012/218] #193996: Convert study designer GWT components to Ext4 editable grids - Immunization Schedule: treatment definition grid UI - Immunization schedule: immunogen/adjuvant selection dialog - Immunization schedule: read/write to studydesign tables --- .../org/labkey/study/model/TreatmentImpl.java | 58 ++++++ .../study/model/TreatmentProductImpl.java | 80 ++++++++ .../ImmunizationScheduleWebpartFactory.java | 54 ++++++ .../immunizationScheduleWebpart.jsp | 171 ++++++++++++++++++ .../view/studydesign/manageImmunizations.jsp | 123 +++++++++++++ .../view/studydesign/manageStudyProducts.jsp | 14 +- .../view/studydesign/vaccineDesignWebpart.jsp | 9 +- 7 files changed, 499 insertions(+), 10 deletions(-) create mode 100644 study/src/org/labkey/study/model/TreatmentImpl.java create mode 100644 study/src/org/labkey/study/model/TreatmentProductImpl.java create mode 100644 study/src/org/labkey/study/view/studydesign/ImmunizationScheduleWebpartFactory.java create mode 100644 study/src/org/labkey/study/view/studydesign/immunizationScheduleWebpart.jsp create mode 100644 study/src/org/labkey/study/view/studydesign/manageImmunizations.jsp diff --git a/study/src/org/labkey/study/model/TreatmentImpl.java b/study/src/org/labkey/study/model/TreatmentImpl.java new file mode 100644 index 00000000..b5feddb4 --- /dev/null +++ b/study/src/org/labkey/study/model/TreatmentImpl.java @@ -0,0 +1,58 @@ +package org.labkey.study.model; + +import org.labkey.api.study.Treatment; + +/** + * User: cnathe + * Date: 12/27/13 + */ +public class TreatmentImpl implements Treatment +{ + private int _rowId; + private String _label; + private String _description; + + public TreatmentImpl() + { + } + + public boolean isNew() + { + return _rowId == 0; + } + + public Object getPrimaryKey() + { + return getRowId(); + } + + public int getRowId() + { + return _rowId; + } + + public void setRowId(int rowId) + { + _rowId = rowId; + } + + public String getLabel() + { + return _label; + } + + public void setLabel(String label) + { + _label = label; + } + + public String getDescription() + { + return _description; + } + + public void setDescription(String description) + { + _description = description; + } +} diff --git a/study/src/org/labkey/study/model/TreatmentProductImpl.java b/study/src/org/labkey/study/model/TreatmentProductImpl.java new file mode 100644 index 00000000..179cea00 --- /dev/null +++ b/study/src/org/labkey/study/model/TreatmentProductImpl.java @@ -0,0 +1,80 @@ +package org.labkey.study.model; + +import org.labkey.api.study.TreatmentProduct; + +/** + * User: cnathe + * Date: 12/27/13 + */ +public class TreatmentProductImpl implements TreatmentProduct +{ + private int _rowId; + private int _treatmentId; + private int _productId; + private String _dose; + private String _route; + + public TreatmentProductImpl() + { + } + + public boolean isNew() + { + return _rowId == 0; + } + + public Object getPrimaryKey() + { + return getRowId(); + } + + public int getRowId() + { + return _rowId; + } + + public void setRowId(int rowId) + { + _rowId = rowId; + } + + public int getTreatmentId() + { + return _treatmentId; + } + + public void setTreatmentId(int treatmentId) + { + _treatmentId = treatmentId; + } + + public int getProductId() + { + return _productId; + } + + public void setProductId(int productId) + { + _productId = productId; + } + + public String getDose() + { + return _dose; + } + + public void setDose(String dose) + { + _dose = dose; + } + + public String getRoute() + { + return _route; + } + + public void setRoute(String route) + { + _route = route; + } +} diff --git a/study/src/org/labkey/study/view/studydesign/ImmunizationScheduleWebpartFactory.java b/study/src/org/labkey/study/view/studydesign/ImmunizationScheduleWebpartFactory.java new file mode 100644 index 00000000..cc6d6107 --- /dev/null +++ b/study/src/org/labkey/study/view/studydesign/ImmunizationScheduleWebpartFactory.java @@ -0,0 +1,54 @@ +package org.labkey.study.view.studydesign; + +import org.labkey.api.data.Container; +import org.labkey.api.study.Study; +import org.labkey.api.study.TimepointType; +import org.labkey.api.view.ActionURL; +import org.labkey.api.view.BaseWebPartFactory; +import org.labkey.api.view.JspView; +import org.labkey.api.view.NavTree; +import org.labkey.api.view.Portal; +import org.labkey.api.view.ViewContext; +import org.labkey.api.view.WebPartView; +import org.labkey.study.controllers.StudyController; +import org.labkey.study.model.StudyManager; +import org.labkey.study.security.permissions.ManageStudyPermission; + +/** + * User: cnathe + * Date: 12/30/13 + */ +public class ImmunizationScheduleWebpartFactory extends BaseWebPartFactory +{ + public static String NAME = "Immunization Schedule"; + + public ImmunizationScheduleWebpartFactory() + { + super(NAME); + } + + @Override + public WebPartView getWebPartView(ViewContext portalCtx, Portal.WebPart webPart) throws Exception + { + JspView view = new JspView<>("/org/labkey/study/view/studydesign/immunizationScheduleWebpart.jsp", webPart); + view.setTitle(NAME); + view.setFrame(WebPartView.FrameType.PORTAL); + + Container c = portalCtx.getContainer(); + Study study = StudyManager.getInstance().getStudy(c); + if (c.hasPermission(portalCtx.getUser(), ManageStudyPermission.class)) + { + String timepointMenuName; + if (study != null && study.getTimepointType() == TimepointType.DATE) + timepointMenuName = "Manage Timepoints"; + else + timepointMenuName = "Manage Visits"; + + NavTree menu = new NavTree(); + menu.addChild(timepointMenuName, new ActionURL(StudyController.ManageVisitsAction.class, c)); + view.setNavMenu(menu); + } + + return view; + } +} diff --git a/study/src/org/labkey/study/view/studydesign/immunizationScheduleWebpart.jsp b/study/src/org/labkey/study/view/studydesign/immunizationScheduleWebpart.jsp new file mode 100644 index 00000000..1d3467d0 --- /dev/null +++ b/study/src/org/labkey/study/view/studydesign/immunizationScheduleWebpart.jsp @@ -0,0 +1,171 @@ +<% + /* + * Copyright (c) 2013 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. + */ +%> + +<%@ page import="org.labkey.api.data.Container" %> +<%@ page import="org.labkey.api.security.User" %> +<%@ page import="org.labkey.api.view.WebTheme" %> +<%@ page import="org.labkey.api.view.WebThemeManager" %> +<%@ page import="org.labkey.api.view.template.ClientDependency" %> +<%@ page import="org.labkey.study.controllers.StudyController" %> +<%@ page import="org.labkey.study.model.StudyImpl" %> +<%@ page import="org.labkey.study.model.StudyManager" %> +<%@ page import="org.labkey.study.security.permissions.ManageStudyPermission" %> +<%@ page import="java.util.LinkedHashSet" %> +<%@ page import="java.util.List" %> +<%@ page import="org.labkey.study.model.TreatmentImpl" %> +<%@ page extends="org.labkey.study.view.BaseStudyPage" %> +<%! + public LinkedHashSet getClientDependencies() + { + LinkedHashSet resources = new LinkedHashSet<>(); + resources.add(ClientDependency.fromFilePath("study/ImmunizationSchedule.js")); + resources.add(ClientDependency.fromFilePath("study/StudyVaccineDesign.css")); + return resources; + } +%> +<% + Container c = getContainer(); + StudyImpl study = StudyManager.getInstance().getStudy(c); + + User user = getUser(); + boolean canManageStudy = c.hasPermission(user, ManageStudyPermission.class); + + WebTheme theme = WebThemeManager.getTheme(c); + String link = theme.getLinkColor(); + String grid = theme.getGridColor(); +%> + + + +<% + if (study != null) + { +%> + + + <% + List treatments = study.getStudyTreatments(user); + if (treatments.size() == 0) + { + %><% + } + else + { +%> + + <% + } +%> + + +

Treatments

No treatments have been defined.
+ + + + + + + <% + for (TreatmentImpl treatment : treatments) + { + String description = treatment.getDescription(); + description = description != null ? h(description).replaceAll("\n", "
") : " "; + + %> + + + + + + <% + } + %> +
LabelDescriptionStudy Products
<%=h(treatment.getLabel())%><%=description%>
...
+

Immunization Schedule

No cohort/treatment/timepoint mappings have been defined.
+<% + } + else + { +%> +

The folder must contain a study in order to display a vaccine design.

+<% + } + + if (canManageStudy) + { +%>
<%=textLink("Manage Immunizations", StudyController.ManageImmunizationsAction.class)%><% + } +%> + + \ No newline at end of file diff --git a/study/src/org/labkey/study/view/studydesign/manageImmunizations.jsp b/study/src/org/labkey/study/view/studydesign/manageImmunizations.jsp new file mode 100644 index 00000000..a8aa945a --- /dev/null +++ b/study/src/org/labkey/study/view/studydesign/manageImmunizations.jsp @@ -0,0 +1,123 @@ + +<%@ page import="java.util.LinkedHashSet" %> +<%@ page import="org.labkey.api.view.template.ClientDependency" %> +<%@ page import="org.labkey.api.view.JspView" %> +<%@ page import="org.labkey.api.view.HttpView" %> +<%@ page import="org.labkey.api.view.ViewContext" %> +<%@ page import="org.labkey.api.view.ActionURL" %> +<%@ page import="org.labkey.api.study.Study" %> +<%@ page import="org.labkey.study.model.StudyManager" %> +<%@ page import="org.labkey.api.study.TimepointType" %> +<%@ page import="org.labkey.study.controllers.StudyController" %> +<%@ page import="org.labkey.api.study.Visit" %> +<%@ page extends="org.labkey.api.jsp.JspBase" %> +<%! + public LinkedHashSet getClientDependencies() + { + LinkedHashSet resources = new LinkedHashSet<>(); + resources.add(ClientDependency.fromFilePath("Ext4ClientApi")); + resources.add(ClientDependency.fromFilePath("study/ImmunizationSchedule.js")); + resources.add(ClientDependency.fromFilePath("dataview/DataViewsPanel.css")); + resources.add(ClientDependency.fromFilePath("study/StudyVaccineDesign.css")); + return resources; + } +%> +<% + JspView me = (JspView) HttpView.currentView(); + ViewContext context = me.getViewContext(); + ActionURL returnURL = context.getActionURL(); + + Study study = StudyManager.getInstance().getStudy(context.getContainer()); + String visitDisplayName = "Visit"; + if (study != null && study.getTimepointType() == TimepointType.DATE) + visitDisplayName = "Timepoint"; +%> + + + + + +Enter immunization information in the grids below. +
+
    +
  • Each treatment may consist of several adjuvants and immunizations.
  • +
  • Use the immunization schedule grid to define which cohorts received a specific treatment at a given timepoint in the study.
  • +
  • + Configure dropdown options at the project level to be shared across study designs or within this folder for + study specific properties: +
  • +
+
+
+
* Double click to edit a treatment record and its product definition
+
+<%=textLink("Manage Study Products", StudyController.ManageStudyProductsAction.class)%> +

+
+
+<%=textLink("Create New " + visitDisplayName, new ActionURL(StudyController.CreateVisitAction.class, context.getContainer()).addReturnURL(returnURL))%> +<% + if (study != null && study.getTimepointType() == TimepointType.VISIT && study.getVisits(Visit.Order.DISPLAY).size() > 1) + { +%><%= textLink("Change Visit Order", new ActionURL(StudyController.VisitOrderAction.class, context.getContainer()).addReturnURL(returnURL)) %><% + } +%> +<%=textLink("Manage " + visitDisplayName + "s", StudyController.ManageVisitsAction.class)%> diff --git a/study/src/org/labkey/study/view/studydesign/manageStudyProducts.jsp b/study/src/org/labkey/study/view/studydesign/manageStudyProducts.jsp index 86fcaf54..0ceae673 100644 --- a/study/src/org/labkey/study/view/studydesign/manageStudyProducts.jsp +++ b/study/src/org/labkey/study/view/studydesign/manageStudyProducts.jsp @@ -1,6 +1,7 @@ <%@ page import="org.labkey.api.view.template.ClientDependency" %> <%@ page import="java.util.LinkedHashSet" %> +<%@ page import="org.labkey.study.controllers.StudyController" %> <%@ page extends="org.labkey.api.jsp.JspBase" %> <%! public LinkedHashSet getClientDependencies() @@ -26,6 +27,11 @@ font-size: 15px; color: black; } + + .x4-grid-cell-inner + { + white-space: normal; + } \ No newline at end of file diff --git a/study/src/org/labkey/study/view/studydesign/manageAssaySpecimen.jsp b/study/src/org/labkey/study/view/studydesign/manageAssaySpecimen.jsp index c9269664..99be1295 100644 --- a/study/src/org/labkey/study/view/studydesign/manageAssaySpecimen.jsp +++ b/study/src/org/labkey/study/view/studydesign/manageAssaySpecimen.jsp @@ -23,6 +23,9 @@ <%@ page import="org.labkey.study.controllers.StudyController" %> <%@ page import="org.labkey.study.model.StudyManager" %> <%@ page import="java.util.LinkedHashSet" %> +<%@ page import="org.labkey.study.security.permissions.ManageStudyPermission" %> +<%@ page import="org.labkey.api.security.User" %> +<%@ page import="org.labkey.api.data.Container" %> <%@ page extends="org.labkey.api.jsp.JspBase" %> <%! public LinkedHashSet getClientDependencies() @@ -34,9 +37,13 @@ } %> <% + Container c = getContainer(); + User user = getUser(); ActionURL returnURL = getActionURL(); Study study = StudyManager.getInstance().getStudy(getContainer()); + boolean canManageStudy = c.hasPermission(user, ManageStudyPermission.class); + String visitDisplayName = "Visit"; if (study != null && study.getTimepointType() == TimepointType.DATE) visitDisplayName = "Timepoint"; @@ -252,7 +259,7 @@ function createAssayPlanPanel(value) form.submit({ url : LABKEY.ActionURL.buildURL('study', 'manageStudyProperties.view'), success : function(response) { window.location.reload(); }, - failure : function(response) { console.log(arguments); } + failure : function(cmp, resp) { Ext4.Msg.alert('Error', resp.response.statusText); } }); } }] @@ -528,13 +535,20 @@ function removeSVC(el, scRowId, vRowId) * Double click to edit an assay/specimen configuration


-<%=textLink("Create New " + visitDisplayName, new ActionURL(StudyController.CreateVisitAction.class, getContainer()).addReturnURL(returnURL))%> <% - if (study != null && study.getTimepointType() == TimepointType.VISIT && study.getVisits(Visit.Order.DISPLAY).size() > 1) + if (canManageStudy) { - %><%= textLink("Change Visit Order", new ActionURL(StudyController.VisitOrderAction.class, getContainer()).addReturnURL(returnURL)) %><% +%> + <%=textLink("Create New " + visitDisplayName, new ActionURL(StudyController.CreateVisitAction.class, getContainer()).addReturnURL(returnURL))%> +<% + if (study != null && study.getTimepointType() == TimepointType.VISIT && study.getVisits(Visit.Order.DISPLAY).size() > 1) + { + %><%= textLink("Change Visit Order", new ActionURL(StudyController.VisitOrderAction.class, getContainer()).addReturnURL(returnURL)) %><% + } +%> + <%=textLink("Manage " + visitDisplayName + "s", StudyController.ManageVisitsAction.class)%> +<% } %> -<%=textLink("Manage " + visitDisplayName + "s", StudyController.ManageVisitsAction.class)%>


diff --git a/study/src/org/labkey/study/view/studydesign/manageImmunizations.jsp b/study/src/org/labkey/study/view/studydesign/manageImmunizations.jsp index 75d186d5..674b5222 100644 --- a/study/src/org/labkey/study/view/studydesign/manageImmunizations.jsp +++ b/study/src/org/labkey/study/view/studydesign/manageImmunizations.jsp @@ -25,6 +25,9 @@ <%@ page import="org.labkey.study.controllers.StudyController" %> <%@ page import="org.labkey.study.model.StudyManager" %> <%@ page import="java.util.LinkedHashSet" %> +<%@ page import="org.labkey.study.controllers.CohortController" %> +<%@ page import="org.labkey.study.security.permissions.ManageStudyPermission" %> +<%@ page import="org.labkey.api.security.User" %> <%@ page extends="org.labkey.api.jsp.JspBase" %> <%! public LinkedHashSet getClientDependencies() @@ -39,9 +42,12 @@ %> <% Container c = getContainer(); + User user = getUser(); ActionURL returnURL = getActionURL(); Study study = StudyManager.getInstance().getStudy(c); + boolean canManageStudy = c.hasPermission(user, ManageStudyPermission.class); + String visitDisplayName = "Visit"; if (study != null && study.getTimepointType() == TimepointType.DATE) visitDisplayName = "Timepoint"; @@ -73,7 +79,8 @@ }); var immunizationScheduleGrid = Ext4.create('LABKEY.ext4.ImmunizationScheduleGrid', { - renderTo : "immunization-schedule-grid" + renderTo : "immunization-schedule-grid", + visitNoun : <%=q(visitDisplayName)%> }); var projectMenu = null; @@ -112,8 +119,10 @@ Enter immunization information in the grids below.
    -
  • Each treatment may consist of several adjuvants and immunizations.
  • -
  • Use the immunization schedule grid to define which cohorts received a specific treatment at a given timepoint in the study.
  • +
  • Use the "Insert New" button in the treatments grid to add a new study treatment.
  • +
  • Each treatment may consist of several study products, i.e. immunogens and/or adjuvants.
  • +
  • Use the "Insert New" button in the immunization schedule grid to add a new study cohort.
  • +
  • Enter the number of subjects for the cohort in the count column.
  • Configure dropdown options at the project level to be shared across study designs or within this folder for study specific properties: @@ -126,12 +135,21 @@ Enter immunization information in the grids below. <%=textLink("Manage Study Products", StudyController.ManageStudyProductsAction.class)%>

    +
    * Double click to edit a group/cohort and its treatment/visit map definition

    -<%=textLink("Create New " + visitDisplayName, new ActionURL(StudyController.CreateVisitAction.class, c).addReturnURL(returnURL))%> <% - if (study != null && study.getTimepointType() == TimepointType.VISIT && study.getVisits(Visit.Order.DISPLAY).size() > 1) + if (canManageStudy) { -%><%= textLink("Change Visit Order", new ActionURL(StudyController.VisitOrderAction.class, c).addReturnURL(returnURL)) %><% - } %> -<%=textLink("Manage " + visitDisplayName + "s", StudyController.ManageVisitsAction.class)%> + <%=textLink("Create New " + visitDisplayName, new ActionURL(StudyController.CreateVisitAction.class, c).addReturnURL(returnURL))%> +<% + if (study != null && study.getTimepointType() == TimepointType.VISIT && study.getVisits(Visit.Order.DISPLAY).size() > 1) + { + %><%= textLink("Change Visit Order", new ActionURL(StudyController.VisitOrderAction.class, c).addReturnURL(returnURL)) %><% + } +%> + <%=textLink("Manage " + visitDisplayName + "s", StudyController.ManageVisitsAction.class)%> + <%=textLink("Manage Cohorts", CohortController.ManageCohortsAction.class)%> +<% + } +%> \ No newline at end of file diff --git a/study/src/org/labkey/study/view/studydesign/vaccineDesignWebpart.jsp b/study/src/org/labkey/study/view/studydesign/vaccineDesignWebpart.jsp index 940c8f35..3af82a7d 100644 --- a/study/src/org/labkey/study/view/studydesign/vaccineDesignWebpart.jsp +++ b/study/src/org/labkey/study/view/studydesign/vaccineDesignWebpart.jsp @@ -27,9 +27,9 @@ <%@ page import="org.labkey.study.model.ProductImpl" %> <%@ page import="org.labkey.study.model.StudyImpl" %> <%@ page import="org.labkey.study.model.StudyManager" %> -<%@ page import="org.labkey.study.security.permissions.ManageStudyPermission" %> <%@ page import="java.util.LinkedHashSet" %> <%@ page import="java.util.List" %> +<%@ page import="org.labkey.api.security.permissions.UpdatePermission" %> <%@ page extends="org.labkey.study.view.BaseStudyPage" %> <%! public LinkedHashSet getClientDependencies() @@ -45,7 +45,7 @@ Container c = getContainer(); StudyImpl study = StudyManager.getInstance().getStudy(c); User user = getUser(); - boolean canManageStudy = c.hasPermission(user, ManageStudyPermission.class); + boolean canEdit = c.hasPermission(user, UpdatePermission.class); WebTheme theme = WebThemeManager.getTheme(c); String link = theme.getLinkColor(); @@ -165,9 +165,9 @@ <% } - if (canManageStudy) + if (canEdit) { - %>
    <%=textLink("Manage Study Products", StudyController.ManageStudyProductsAction.class)%><% + %>
    <%=textLink("Edit", StudyController.ManageStudyProductsAction.class)%><% } %> From 12d95f42d68eae93653329e04c4f260186116ed2 Mon Sep 17 00:00:00 2001 From: Cory Nathe Date: Wed, 15 Jan 2014 15:13:43 +0000 Subject: [PATCH 017/218] #193996: Convert study designer GWT components to Ext4 editable grids - Immunization schedule: grid UI and add/remove buttons - Immunization schedule: immunogen/adjuvant selection dialog - Immunization Schedule: treatment definition grid UI - Immunization schedule: read/write to studydesign tables --- api/src/org/labkey/api/study/TreatmentVisitMap.java | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 api/src/org/labkey/api/study/TreatmentVisitMap.java diff --git a/api/src/org/labkey/api/study/TreatmentVisitMap.java b/api/src/org/labkey/api/study/TreatmentVisitMap.java new file mode 100644 index 00000000..695983fb --- /dev/null +++ b/api/src/org/labkey/api/study/TreatmentVisitMap.java @@ -0,0 +1,12 @@ +package org.labkey.api.study; + +/** + * User: cnathe + * Date: 12/30/13 + */ +public interface TreatmentVisitMap +{ + int getCohortId(); + int getTreatmentId(); + int getVisitId(); +} From 19082fbe58a9cac73734750c71d07af8e8edb7a5 Mon Sep 17 00:00:00 2001 From: Cory Nathe Date: Wed, 15 Jan 2014 20:52:44 +0000 Subject: [PATCH 018/218] Misc immunization schedule improvements / fixes: - StudyTreatmentVisitMapTable treatment column FK - StudyController.DeleteStudyProdcutAction - hover display of treatment study products - treatment edit dialog display for product with long label (hack at setting height) --- .../org/labkey/study/model/TreatmentImpl.java | 22 +++++++++++++ .../StudyTreatmentVisitMapTable.java | 10 +++++- .../immunizationScheduleWebpart.jsp | 32 +++++++++++++++++-- 3 files changed, 61 insertions(+), 3 deletions(-) diff --git a/study/src/org/labkey/study/model/TreatmentImpl.java b/study/src/org/labkey/study/model/TreatmentImpl.java index 34e4b7c8..308470c6 100644 --- a/study/src/org/labkey/study/model/TreatmentImpl.java +++ b/study/src/org/labkey/study/model/TreatmentImpl.java @@ -17,6 +17,9 @@ import org.labkey.api.study.Treatment; +import java.util.ArrayList; +import java.util.List; + /** * User: cnathe * Date: 12/27/13 @@ -26,6 +29,7 @@ public class TreatmentImpl implements Treatment private int _rowId; private String _label; private String _description; + private List _products; public TreatmentImpl() { @@ -70,4 +74,22 @@ public void setDescription(String description) { _description = description; } + + public List getProducts() + { + return _products; + } + + public void setProducts(List products) + { + _products = products; + } + + public void addProduct(ProductImpl product) + { + if (_products == null) + _products = new ArrayList<>(); + + _products.add(product); + } } diff --git a/study/src/org/labkey/study/query/studydesign/StudyTreatmentVisitMapTable.java b/study/src/org/labkey/study/query/studydesign/StudyTreatmentVisitMapTable.java index 70f57a89..fe0b93b4 100644 --- a/study/src/org/labkey/study/query/studydesign/StudyTreatmentVisitMapTable.java +++ b/study/src/org/labkey/study/query/studydesign/StudyTreatmentVisitMapTable.java @@ -5,6 +5,7 @@ import org.labkey.api.query.AliasedColumn; import org.labkey.api.query.DefaultQueryUpdateService; import org.labkey.api.query.LookupForeignKey; +import org.labkey.api.query.QueryService; import org.labkey.api.query.QueryUpdateService; import org.labkey.api.security.UserPrincipal; import org.labkey.api.security.permissions.Permission; @@ -37,7 +38,14 @@ public TableInfo getLookupTableInfo() addColumn(cohortCol); ColumnInfo treatmentCol = new AliasedColumn(this, "TreatmentId", _rootTable.getColumn("TreatmentId")); - // TODO: make this a lookup to the Treatment table + treatmentCol.setFk(new LookupForeignKey("RowId") + { + @Override + public TableInfo getLookupTableInfo() + { + return QueryService.get().getUserSchema(_userSchema.getUser(), _userSchema.getContainer(), StudyQuerySchema.SCHEMA_NAME).getTable(StudyQuerySchema.TREATMENT_TABLE_NAME); + } + }); addColumn(treatmentCol); ColumnInfo visitCol = new AliasedColumn(this, "VisitId", _rootTable.getColumn("VisitId")); diff --git a/study/src/org/labkey/study/view/studydesign/immunizationScheduleWebpart.jsp b/study/src/org/labkey/study/view/studydesign/immunizationScheduleWebpart.jsp index 92564db2..22723cb5 100644 --- a/study/src/org/labkey/study/view/studydesign/immunizationScheduleWebpart.jsp +++ b/study/src/org/labkey/study/view/studydesign/immunizationScheduleWebpart.jsp @@ -35,6 +35,7 @@ <%@ page import="java.util.List" %> <%@ page import="java.util.Map" %> <%@ page import="java.util.HashMap" %> +<%@ page import="org.labkey.study.model.ProductImpl" %> <%! public LinkedHashSet getClientDependencies() { @@ -140,7 +141,7 @@ %> <%=h(cohort.getLabel())%> - <%=cohort.getSubjectCount()%> + <%=cohort.getSubjectCount() != null ? cohort.getSubjectCount() : ""%> <% for (VisitImpl visit : visits) { @@ -148,8 +149,35 @@ TreatmentImpl treatment = null; if (treatmentId != null) treatment = StudyManager.getInstance().getStudyTreatmentByRowId(c, user, treatmentId); + + // show the list of study products for the treatment as a hover + // Example display: + // Immunogens: ABC, DEF + // Adjuvants: GHI, JKL + String productHover = ""; + String sep = ""; + if (treatment != null && treatment.getProducts() != null) + { + String prevRole = null; + for (ProductImpl product : treatment.getProducts()) + { + if (prevRole == null || !prevRole.equals(product.getRole())) + { + prevRole = product.getRole(); + productHover += (productHover.length() > 0 ? "
    " : ""); + productHover += "" + h(product.getRole()) + "s: "; + sep = ""; + } + + productHover += sep + h(product.getLabel()); + sep = ", "; + } + } %> - <%=h(treatment != null ? treatment.getLabel() : "")%> + + <%=h(treatment != null ? treatment.getLabel() : "")%> + <%=(productHover.length() > 0 ? PageFlowUtil.helpPopup("Study Products", productHover, true, 300) : "")%> + <% } %> From ac7119ebce575d3d8f7e3b9044fc228b3775da04 Mon Sep 17 00:00:00 2001 From: Cory Nathe Date: Thu, 16 Jan 2014 17:50:03 +0000 Subject: [PATCH 019/218] Ext4 study designer UI for immunization schedule: - timepoint definition dialog allow selection of existing study timepoint - allow creating a new study timepoint from the immunization schedule grid --- .../controllers/StudyDesignController.java | 407 ++++++++++++++++++ .../view/studydesign/manageImmunizations.jsp | 3 - 2 files changed, 407 insertions(+), 3 deletions(-) create mode 100644 study/src/org/labkey/study/controllers/StudyDesignController.java diff --git a/study/src/org/labkey/study/controllers/StudyDesignController.java b/study/src/org/labkey/study/controllers/StudyDesignController.java new file mode 100644 index 00000000..c9b1f0e8 --- /dev/null +++ b/study/src/org/labkey/study/controllers/StudyDesignController.java @@ -0,0 +1,407 @@ +package org.labkey.study.controllers; + +import org.json.JSONObject; +import org.labkey.api.action.ApiAction; +import org.labkey.api.action.ApiResponse; +import org.labkey.api.action.ApiSimpleResponse; +import org.labkey.api.data.DbScope; +import org.labkey.api.data.Sort; +import org.labkey.api.query.FieldKey; +import org.labkey.api.security.RequiresPermissionClass; +import org.labkey.api.security.permissions.DeletePermission; +import org.labkey.api.security.permissions.ReadPermission; +import org.labkey.api.security.permissions.UpdatePermission; +import org.labkey.api.study.Study; +import org.labkey.api.study.Visit; +import org.labkey.study.StudySchema; +import org.labkey.study.designer.StudyImmunizationSchedule; +import org.labkey.study.model.CohortImpl; +import org.labkey.study.model.CohortManager; +import org.labkey.study.model.ProductAntigenImpl; +import org.labkey.study.model.ProductImpl; +import org.labkey.study.model.StudyImpl; +import org.labkey.study.model.StudyManager; +import org.labkey.study.model.TreatmentImpl; +import org.labkey.study.model.TreatmentProductImpl; +import org.labkey.study.model.VisitImpl; +import org.labkey.study.visitmanager.VisitManager; +import org.springframework.validation.BindException; +import org.springframework.validation.Errors; + +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +/** + * User: cnathe + * Date: 1/16/14 + */ +public class StudyDesignController extends BaseStudyController +{ + private static final ActionResolver ACTION_RESOLVER = new DefaultActionResolver(StudyDesignController.class); + + public StudyDesignController() + { + super(); + setActionResolver(ACTION_RESOLVER); + } + + @RequiresPermissionClass(ReadPermission.class) + public class GetStudyProducts extends ApiAction + { + private StudyImpl _study; + + @Override + public void validateForm(GetStudyProductsForm form, Errors errors) + { + _study = getStudy(getContainer()); + if (_study == null) + errors.reject(ERROR_MSG, "A study does not exist in this folder"); + } + + @Override + public ApiResponse execute(GetStudyProductsForm form, BindException errors) throws Exception + { + ApiSimpleResponse resp = new ApiSimpleResponse(); + List products = new ArrayList<>(); + + List studyProducts = StudyManager.getInstance().getStudyProducts(getContainer(), getUser(), form.getRole(), form.getRowId()); + for (ProductImpl product : studyProducts) + { + JSONObject productJSON = new JSONObject(); + productJSON.put("RowId", product.getRowId()); + productJSON.put("Label", product.getLabel()); + productJSON.put("Role", product.getRole()); + productJSON.put("Type", product.getType()); + + List productAntigens = new ArrayList<>(); + List studyProductAntigens = StudyManager.getInstance().getStudyProductAntigens(getContainer(), getUser(), product.getRowId()); + for (ProductAntigenImpl antigen : studyProductAntigens) + { + JSONObject antigenJSON = new JSONObject(); + antigenJSON.put("RowId", antigen.getRowId()); + antigenJSON.put("ProductId", antigen.getProductId()); + antigenJSON.put("Gene", antigen.getGene()); + antigenJSON.put("SubType", antigen.getSubType()); + antigenJSON.put("GenBankId", antigen.getGenBankId()); + antigenJSON.put("Sequence", antigen.getSequence()); + productAntigens.add(antigenJSON); + } + productJSON.put("Antigens", productAntigens); + + products.add(productJSON); + } + + resp.put("success", true); + resp.put("products", products); + + return resp; + } + } + + public static class GetStudyProductsForm + { + private Integer _rowId; + private String _role; + + public Integer getRowId() + { + return _rowId; + } + + public void setRowId(Integer rowId) + { + _rowId = rowId; + } + + public String getRole() + { + return _role; + } + + public void setRole(String role) + { + _role = role; + } + } + + @RequiresPermissionClass(ReadPermission.class) + public class GetStudyTreatments extends ApiAction + { + private StudyImpl _study; + + @Override + public void validateForm(Object form, Errors errors) + { + _study = getStudy(getContainer()); + if (_study == null) + errors.reject(ERROR_MSG, "A study does not exist in this folder"); + } + + @Override + public ApiResponse execute(Object form, BindException errors) throws Exception + { + ApiSimpleResponse resp = new ApiSimpleResponse(); + List treatments = new ArrayList<>(); + + List studyTreatments = StudyManager.getInstance().getStudyTreatments(getContainer(), getUser()); + for (TreatmentImpl treatment : studyTreatments) + { + JSONObject treatmentJSON = new JSONObject(); + treatmentJSON.put("RowId", treatment.getRowId()); + treatmentJSON.put("Label", treatment.getLabel()); + treatmentJSON.put("Description", treatment.getDescription()); + + List treatmentProducts = new ArrayList<>(); + Sort sort = new Sort(); + sort.appendSortColumn(FieldKey.fromParts("ProductId", "Role"), Sort.SortDirection.DESC, false); + sort.appendSortColumn(FieldKey.fromParts("ProductId", "RowId"), Sort.SortDirection.ASC, false); + List studyTreatmentProducts = StudyManager.getInstance().getStudyTreatmentProducts(getContainer(), getUser(), treatment.getRowId(), sort); + for (TreatmentProductImpl treatmentProduct : studyTreatmentProducts) + { + JSONObject productJSON = new JSONObject(); + productJSON.put("RowId", treatmentProduct.getRowId()); + productJSON.put("TreatmentId", treatmentProduct.getTreatmentId()); + productJSON.put("ProductId", treatmentProduct.getProductId()); + productJSON.put("Dose", treatmentProduct.getDose()); + productJSON.put("Route", treatmentProduct.getRoute()); + + List products = StudyManager.getInstance().getStudyProducts(getContainer(), getUser(), null, treatmentProduct.getProductId()); + if (products.size() == 1) + productJSON.put("ProductId/Label", products.get(0).getLabel()); + + treatmentProducts.add(productJSON); + } + treatmentJSON.put("Products", treatmentProducts); + + treatments.add(treatmentJSON); + } + + resp.put("success", true); + resp.put("treatments", treatments); + + return resp; + } + } + + @RequiresPermissionClass(ReadPermission.class) + public class GetStudyImmunizationSchedule extends ApiAction + { + private StudyImpl _study; + + @Override + public void validateForm(Object form, Errors errors) + { + _study = getStudy(getContainer()); + if (_study == null) + errors.reject(ERROR_MSG, "A study does not exist in this folder"); + } + + @Override + public ApiResponse execute(Object form, BindException errors) throws Exception + { + ApiSimpleResponse resp = new ApiSimpleResponse(); + StudyImmunizationSchedule immunizationSchedule = new StudyImmunizationSchedule(getContainer()); + + // include all cohorts for the study, regardless of it they have associated visits or not + immunizationSchedule.setCohorts(StudyManager.getInstance().getCohorts(getContainer(), getUser())); + + // include all visits from the study, ordered by visit display order + immunizationSchedule.setVisits(StudyManager.getInstance().getVisits(_study, Visit.Order.DISPLAY)); + + // include all treatments for the study + immunizationSchedule.setTreatments(StudyManager.getInstance().getStudyTreatments(getContainer(), getUser())); + + resp.put("mapping", immunizationSchedule.serializeCohortMapping()); + resp.put("visits", immunizationSchedule.serializeVisits()); + resp.put("treatments", immunizationSchedule.serializeTreatments()); + resp.put("success", true); + + return resp; + } + } + + @RequiresPermissionClass(DeletePermission.class) + public class DeleteTreatmentAction extends ApiAction + { + @Override + public ApiResponse execute(IdForm form, BindException errors) throws Exception + { + if (form.getId() != 0) + { + StudyManager.getInstance().deleteTreatment(getContainer(), getUser(), form.getId()); + return new ApiSimpleResponse("success", true); + } + return new ApiSimpleResponse("success", false); + } + } + + @RequiresPermissionClass(DeletePermission.class) + public class DeleteStudyProductAction extends ApiAction + { + @Override + public ApiResponse execute(IdForm form, BindException errors) throws Exception + { + if (form.getId() != 0) + { + StudyManager.getInstance().deleteStudyProduct(getContainer(), getUser(), form.getId()); + return new ApiSimpleResponse("success", true); + } + return new ApiSimpleResponse("success", false); + } + } + + @RequiresPermissionClass(UpdatePermission.class) + public class UpdateStudyImmunizationScheduleAction extends ApiAction + { + @Override + public void validateForm(StudyImmunizationSchedule form, Errors errors) + { + if (form.getCohortLabel() == null) + errors.reject(ERROR_MSG, "Cohort label is required."); + + CohortImpl cohortByLabel = StudyManager.getInstance().getCohortByLabel(getContainer(), getUser(), form.getCohortLabel()); + if (form.getCohortRowId() != null) + { + CohortImpl cohortByRowId = StudyManager.getInstance().getCohortForRowId(getContainer(), getUser(), form.getCohortRowId()); + if (cohortByRowId != null && cohortByLabel != null && cohortByRowId.getRowId() != cohortByLabel.getRowId()) + errors.reject(ERROR_MSG, "A cohort with the label '" + form.getCohortLabel() + "' already exists"); + } + else if (cohortByLabel != null) + { + errors.reject(ERROR_MSG, "A cohort with the label '" + form.getCohortLabel() + "' already exists"); + } + } + + @Override + public ApiResponse execute(StudyImmunizationSchedule form, BindException errors) throws Exception + { + ApiSimpleResponse response = new ApiSimpleResponse(); + Study study = StudyManager.getInstance().getStudy(getContainer()); + + if (study != null) + { + CohortImpl cohort = insertOrUpdateCohort(form, study); + response.put("cohortId", cohort.getRowId()); + + updateTreatmentVisitMapping(form, cohort); + + response.put("success", true); + return response; + } + else + throw new IllegalStateException("A study does not exist in this folder"); + } + + private CohortImpl insertOrUpdateCohort(StudyImmunizationSchedule form, Study study) throws Exception + { + CohortImpl cohort; + if (form.getCohortRowId() != null) + { + cohort = StudyManager.getInstance().getCohortForRowId(getContainer(), getUser(), form.getCohortRowId()); + cohort = cohort.createMutable(); + cohort.setLabel(form.getCohortLabel()); + cohort.setSubjectCount(form.getCohortSubjectCount()); + StudyManager.getInstance().updateCohort(getUser(), cohort); + } + else + { + cohort = CohortManager.getInstance().createCohort(study, getUser(), form.getCohortLabel(), true, form.getCohortSubjectCount(), null); + } + + return cohort; + } + + private void updateTreatmentVisitMapping(StudyImmunizationSchedule form, CohortImpl cohort) throws SQLException + { + if (cohort != null) + { + StudySchema schema = StudySchema.getInstance(); + + try (DbScope.Transaction transaction = schema.getSchema().getScope().ensureTransaction()) + { + // the mapping that is passed in will have all of the current treatment/visit maps, so we will + // delete all of the existing records and then insert the new ones + StudyManager.getInstance().deleteTreatmentVisitMapForCohort(getContainer(), cohort.getRowId()); + + for (Map.Entry treatmentVisitMap : form.getTreatmentVisitMap().entrySet()) + { + // map entry key = visitId and value = treatmentId + StudyManager.getInstance().insertTreatmentVisitMap(getUser(), getContainer(), cohort.getRowId(), treatmentVisitMap.getKey(), treatmentVisitMap.getValue()); + } + + transaction.commit(); + } + } + } + } + + @RequiresPermissionClass(DeletePermission.class) + public class DeleteCohortAction extends ApiAction + { + @Override + public ApiResponse execute(IdForm form, BindException errors) throws Exception + { + if (form.getId() != 0) + { + CohortImpl cohort = StudyManager.getInstance().getCohortForRowId(getContainer(), getUser(), form.getId()); + if (cohort != null) + { + if (!cohort.isInUse()) + { + StudyManager.getInstance().deleteCohort(cohort); + return new ApiSimpleResponse("success", true); + } + else + { + errors.reject(ERROR_MSG, "Unable to delete in-use cohort: " + cohort.getLabel()); + } + } + else + { + errors.reject(ERROR_MSG, "Unable to find cohort for id " + form.getId()); + } + } + return new ApiSimpleResponse("success", false); + } + } + + @RequiresPermissionClass(UpdatePermission.class) + public class CreateVisitAction extends ApiAction + { + @Override + public void validateForm(VisitForm form, Errors errors) + { + StudyImpl study = getStudyRedirectIfNull(); + + form.validate(errors, study); + if (errors.getErrorCount() > 0) + return; + + //check for overlapping visits + VisitManager visitMgr = StudyManager.getInstance().getVisitManager(study); + if (null != visitMgr) + { + if (visitMgr.isVisitOverlapping(form.getBean())) + errors.reject(null, "Visit range overlaps an existing visit in this study. Please enter a different range."); + } + } + + @Override + public ApiResponse execute(VisitForm form, BindException errors) throws Exception + { + ApiSimpleResponse response = new ApiSimpleResponse(); + + VisitImpl visit = form.getBean(); + visit = StudyManager.getInstance().createVisit(getStudyThrowIfNull(), getUser(), visit); + + response.put("RowId", visit.getRowId()); + response.put("Label", visit.getDisplayString()); + response.put("SortOrder", visit.getDisplayOrder()); + response.put("Included", true); + response.put("success", true); + return response; + } + } +} diff --git a/study/src/org/labkey/study/view/studydesign/manageImmunizations.jsp b/study/src/org/labkey/study/view/studydesign/manageImmunizations.jsp index 674b5222..a30b54c6 100644 --- a/study/src/org/labkey/study/view/studydesign/manageImmunizations.jsp +++ b/study/src/org/labkey/study/view/studydesign/manageImmunizations.jsp @@ -140,9 +140,6 @@ Enter immunization information in the grids below. <% if (canManageStudy) { -%> - <%=textLink("Create New " + visitDisplayName, new ActionURL(StudyController.CreateVisitAction.class, c).addReturnURL(returnURL))%> -<% if (study != null && study.getTimepointType() == TimepointType.VISIT && study.getVisits(Visit.Order.DISPLAY).size() > 1) { %><%= textLink("Change Visit Order", new ActionURL(StudyController.VisitOrderAction.class, c).addReturnURL(returnURL)) %><% From d6958c20e4c321a556daeab528aece62dd4af49e Mon Sep 17 00:00:00 2001 From: Cory Nathe Date: Thu, 16 Jan 2014 21:30:55 +0000 Subject: [PATCH 020/218] Ext4 study designer UI for assay schedule: - allow creating a new study timepoint from manage assay schedule visit grid --- .../controllers/StudyDesignController.java | 49 +++++++++++++++++++ .../view/studydesign/assayScheduleWebpart.jsp | 4 +- .../immunizationScheduleWebpart.jsp | 4 +- .../view/studydesign/manageAssaySpecimen.jsp | 30 ++++++++++-- .../view/studydesign/manageImmunizations.jsp | 3 +- .../view/studydesign/manageStudyProducts.jsp | 4 +- .../view/studydesign/vaccineDesignWebpart.jsp | 4 +- 7 files changed, 85 insertions(+), 13 deletions(-) diff --git a/study/src/org/labkey/study/controllers/StudyDesignController.java b/study/src/org/labkey/study/controllers/StudyDesignController.java index c9b1f0e8..0d645f5c 100644 --- a/study/src/org/labkey/study/controllers/StudyDesignController.java +++ b/study/src/org/labkey/study/controllers/StudyDesignController.java @@ -4,6 +4,7 @@ import org.labkey.api.action.ApiAction; import org.labkey.api.action.ApiResponse; import org.labkey.api.action.ApiSimpleResponse; +import org.labkey.api.action.SimpleViewAction; import org.labkey.api.data.DbScope; import org.labkey.api.data.Sort; import org.labkey.api.query.FieldKey; @@ -13,6 +14,8 @@ import org.labkey.api.security.permissions.UpdatePermission; import org.labkey.api.study.Study; import org.labkey.api.study.Visit; +import org.labkey.api.view.JspView; +import org.labkey.api.view.NavTree; import org.labkey.study.StudySchema; import org.labkey.study.designer.StudyImmunizationSchedule; import org.labkey.study.model.CohortImpl; @@ -27,6 +30,7 @@ import org.labkey.study.visitmanager.VisitManager; import org.springframework.validation.BindException; import org.springframework.validation.Errors; +import org.springframework.web.servlet.ModelAndView; import java.sql.SQLException; import java.util.ArrayList; @@ -47,6 +51,51 @@ public StudyDesignController() setActionResolver(ACTION_RESOLVER); } + @RequiresPermissionClass(UpdatePermission.class) + public class ManageAssaySpecimenAction extends SimpleViewAction + { + public ModelAndView getView(Object o, BindException errors) throws Exception + { + return new JspView<>("/org/labkey/study/view/studydesign/manageAssaySpecimen.jsp", o); + } + + public NavTree appendNavTrail(NavTree root) + { + _appendManageStudy(root); + return root.addChild("Manage Assay/Specimen Configurations"); + } + } + + @RequiresPermissionClass(UpdatePermission.class) + public class ManageStudyProductsAction extends SimpleViewAction + { + public ModelAndView getView(Object o, BindException errors) throws Exception + { + return new JspView<>("/org/labkey/study/view/studydesign/manageStudyProducts.jsp", o); + } + + public NavTree appendNavTrail(NavTree root) + { + _appendManageStudy(root); + return root.addChild("Manage Study Products"); + } + } + + @RequiresPermissionClass(UpdatePermission.class) + public class ManageImmunizationsAction extends SimpleViewAction + { + public ModelAndView getView(Object o, BindException errors) throws Exception + { + return new JspView<>("/org/labkey/study/view/studydesign/manageImmunizations.jsp", o); + } + + public NavTree appendNavTrail(NavTree root) + { + _appendManageStudy(root); + return root.addChild("Manage Immunizations"); + } + } + @RequiresPermissionClass(ReadPermission.class) public class GetStudyProducts extends ApiAction { diff --git a/study/src/org/labkey/study/view/studydesign/assayScheduleWebpart.jsp b/study/src/org/labkey/study/view/studydesign/assayScheduleWebpart.jsp index 4bf9e5d3..deb5d991 100644 --- a/study/src/org/labkey/study/view/studydesign/assayScheduleWebpart.jsp +++ b/study/src/org/labkey/study/view/studydesign/assayScheduleWebpart.jsp @@ -23,7 +23,7 @@ <%@ page import="org.labkey.api.view.WebThemeManager" %> <%@ page import="org.labkey.api.view.template.ClientDependency" %> <%@ page import="org.labkey.study.SampleManager" %> -<%@ page import="org.labkey.study.controllers.StudyController" %> +<%@ page import="org.labkey.study.controllers.StudyDesignController" %> <%@ page import="org.labkey.study.model.AssaySpecimenConfigImpl" %> <%@ page import="org.labkey.study.model.DerivativeType" %> <%@ page import="org.labkey.study.model.LocationImpl" %> @@ -196,6 +196,6 @@ if (canEdit) { - %>
    <%=textLink("Edit", StudyController.ManageAssaySpecimenAction.class)%><% + %>
    <%=textLink("Edit", StudyDesignController.ManageAssaySpecimenAction.class)%><% } %> \ No newline at end of file diff --git a/study/src/org/labkey/study/view/studydesign/immunizationScheduleWebpart.jsp b/study/src/org/labkey/study/view/studydesign/immunizationScheduleWebpart.jsp index 22723cb5..f24a54ea 100644 --- a/study/src/org/labkey/study/view/studydesign/immunizationScheduleWebpart.jsp +++ b/study/src/org/labkey/study/view/studydesign/immunizationScheduleWebpart.jsp @@ -23,7 +23,7 @@ <%@ page import="org.labkey.api.view.WebTheme" %> <%@ page import="org.labkey.api.view.WebThemeManager" %> <%@ page import="org.labkey.api.view.template.ClientDependency" %> -<%@ page import="org.labkey.study.controllers.StudyController" %> +<%@ page import="org.labkey.study.controllers.StudyDesignController" %> <%@ page import="org.labkey.study.model.StudyImpl" %> <%@ page import="org.labkey.study.model.StudyManager" %> <%@ page import="org.labkey.study.model.CohortImpl" %> @@ -202,6 +202,6 @@ if (canEdit) { - %>
    <%=textLink("Edit", StudyController.ManageImmunizationsAction.class)%><% + %>
    <%=textLink("Edit", StudyDesignController.ManageImmunizationsAction.class)%><% } %> diff --git a/study/src/org/labkey/study/view/studydesign/manageAssaySpecimen.jsp b/study/src/org/labkey/study/view/studydesign/manageAssaySpecimen.jsp index 99be1295..c611ab19 100644 --- a/study/src/org/labkey/study/view/studydesign/manageAssaySpecimen.jsp +++ b/study/src/org/labkey/study/view/studydesign/manageAssaySpecimen.jsp @@ -32,6 +32,7 @@ { LinkedHashSet resources = new LinkedHashSet<>(); resources.add(ClientDependency.fromFilePath("Ext4ClientApi")); + resources.add(ClientDependency.fromFilePath("study/ImmunizationSchedule.js")); resources.add(ClientDependency.fromFilePath("dataview/DataViewsPanel.css")); return resources; } @@ -77,7 +78,7 @@ (function() { var X = Ext4; - var _gridSC, _storeSC, _panelSV, _storeSV, _storeV, _panelAP; + var _gridSC, _storeSC, _panelSV, _storeSV, _storeV, _panelAP, _addVisitWindow; var _configVisitMap = {}; X.onReady(function(){ @@ -223,6 +224,28 @@ createAssayPlanPanel(data.rows[0]["AssayPlan"]); } }); + + // text link to open dialog to create a new visit + Ext4.create('LABKEY.ext4.LinkButton', { + renderTo: 'CreateNewVisitTextLink', + text: 'Create New <%=h(visitDisplayName)%>', + handler: function() { + var win = Ext4.create('LABKEY.ext4.VaccineDesignAddVisitWindow', { + title: 'Add <%=h(visitDisplayName)%>', + visitNoun: <%=q(visitDisplayName)%>, + allowSelectExistingVisit: false, + listeners: { + scope : this, + closeWindow : function() { win.close(); }, + newVisitCreated : function(newVisitData) { + // reload the page to update the visit map section + window.location.reload(); + } + } + }); + win.show(); + } + }); }); function createAssayPlanPanel(value) @@ -331,6 +354,7 @@ function showUpdateConfigurationDialog(grid, record, item, index) success: function(data) { if (record) record.commit(); win.close(); + // reload the page to update the visit map section window.location.reload(); } @@ -535,12 +559,10 @@ function removeSVC(el, scRowId, vRowId) * Double click to edit an assay/specimen configuration


    + <% if (canManageStudy) { -%> - <%=textLink("Create New " + visitDisplayName, new ActionURL(StudyController.CreateVisitAction.class, getContainer()).addReturnURL(returnURL))%> -<% if (study != null && study.getTimepointType() == TimepointType.VISIT && study.getVisits(Visit.Order.DISPLAY).size() > 1) { %><%= textLink("Change Visit Order", new ActionURL(StudyController.VisitOrderAction.class, getContainer()).addReturnURL(returnURL)) %><% diff --git a/study/src/org/labkey/study/view/studydesign/manageImmunizations.jsp b/study/src/org/labkey/study/view/studydesign/manageImmunizations.jsp index a30b54c6..d50f1ecf 100644 --- a/study/src/org/labkey/study/view/studydesign/manageImmunizations.jsp +++ b/study/src/org/labkey/study/view/studydesign/manageImmunizations.jsp @@ -23,6 +23,7 @@ <%@ page import="org.labkey.api.view.ActionURL" %> <%@ page import="org.labkey.api.view.template.ClientDependency" %> <%@ page import="org.labkey.study.controllers.StudyController" %> +<%@ page import="org.labkey.study.controllers.StudyDesignController" %> <%@ page import="org.labkey.study.model.StudyManager" %> <%@ page import="java.util.LinkedHashSet" %> <%@ page import="org.labkey.study.controllers.CohortController" %> @@ -132,7 +133,7 @@ Enter immunization information in the grids below.
    * Double click to edit a treatment record and its product definition

    -<%=textLink("Manage Study Products", StudyController.ManageStudyProductsAction.class)%> +<%=textLink("Manage Study Products", StudyDesignController.ManageStudyProductsAction.class)%>

    * Double click to edit a group/cohort and its treatment/visit map definition
    diff --git a/study/src/org/labkey/study/view/studydesign/manageStudyProducts.jsp b/study/src/org/labkey/study/view/studydesign/manageStudyProducts.jsp index bcad04ac..968bfecf 100644 --- a/study/src/org/labkey/study/view/studydesign/manageStudyProducts.jsp +++ b/study/src/org/labkey/study/view/studydesign/manageStudyProducts.jsp @@ -18,7 +18,7 @@ <%@ page import="org.labkey.api.view.template.ClientDependency" %> <%@ page import="java.util.LinkedHashSet" %> -<%@ page import="org.labkey.study.controllers.StudyController" %> +<%@ page import="org.labkey.study.controllers.StudyDesignController" %> <%@ page extends="org.labkey.api.jsp.JspBase" %> <%! public LinkedHashSet getClientDependencies() @@ -125,5 +125,5 @@ Enter vaccine design information in the grids below.
    * Double click a row to edit the label

    -<%=textLink("Manage Immunizations", StudyController.ManageImmunizationsAction.class)%> +<%=textLink("Manage Immunizations", StudyDesignController.ManageImmunizationsAction.class)%> diff --git a/study/src/org/labkey/study/view/studydesign/vaccineDesignWebpart.jsp b/study/src/org/labkey/study/view/studydesign/vaccineDesignWebpart.jsp index 3af82a7d..8e5ba311 100644 --- a/study/src/org/labkey/study/view/studydesign/vaccineDesignWebpart.jsp +++ b/study/src/org/labkey/study/view/studydesign/vaccineDesignWebpart.jsp @@ -23,7 +23,7 @@ <%@ page import="org.labkey.api.view.WebTheme" %> <%@ page import="org.labkey.api.view.WebThemeManager" %> <%@ page import="org.labkey.api.view.template.ClientDependency" %> -<%@ page import="org.labkey.study.controllers.StudyController" %> +<%@ page import="org.labkey.study.controllers.StudyDesignController" %> <%@ page import="org.labkey.study.model.ProductImpl" %> <%@ page import="org.labkey.study.model.StudyImpl" %> <%@ page import="org.labkey.study.model.StudyManager" %> @@ -167,7 +167,7 @@ if (canEdit) { - %>
    <%=textLink("Edit", StudyController.ManageStudyProductsAction.class)%><% + %>
    <%=textLink("Edit", StudyDesignController.ManageStudyProductsAction.class)%><% } %> From 584fbf7ae621675ef3aefd11a9aa601297f2b8c2 Mon Sep 17 00:00:00 2001 From: Adam Rauch Date: Fri, 17 Jan 2014 21:27:02 +0000 Subject: [PATCH 021/218] comments and imports --- .../immunizationScheduleWebpart.jsp | 41 +++++++++---------- 1 file changed, 20 insertions(+), 21 deletions(-) diff --git a/study/src/org/labkey/study/view/studydesign/immunizationScheduleWebpart.jsp b/study/src/org/labkey/study/view/studydesign/immunizationScheduleWebpart.jsp index f24a54ea..470a6080 100644 --- a/study/src/org/labkey/study/view/studydesign/immunizationScheduleWebpart.jsp +++ b/study/src/org/labkey/study/view/studydesign/immunizationScheduleWebpart.jsp @@ -1,21 +1,20 @@ <% - /* - * Copyright (c) 2013 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. - */ +/* + * Copyright (c) 2013 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. + */ %> - <%@ page import="org.labkey.api.data.Container" %> <%@ page import="org.labkey.api.security.User" %> <%@ page import="org.labkey.api.security.permissions.UpdatePermission" %> @@ -24,18 +23,18 @@ <%@ page import="org.labkey.api.view.WebThemeManager" %> <%@ page import="org.labkey.api.view.template.ClientDependency" %> <%@ page import="org.labkey.study.controllers.StudyDesignController" %> +<%@ page import="org.labkey.study.model.CohortImpl" %> +<%@ page import="org.labkey.study.model.ProductImpl" %> <%@ page import="org.labkey.study.model.StudyImpl" %> <%@ page import="org.labkey.study.model.StudyManager" %> -<%@ page import="org.labkey.study.model.CohortImpl" %> -<%@ page import="org.labkey.study.model.VisitImpl" %> <%@ page import="org.labkey.study.model.TreatmentImpl" %> <%@ page import="org.labkey.study.model.TreatmentVisitMapImpl" %> -<%@ page extends="org.labkey.study.view.BaseStudyPage" %> +<%@ page import="org.labkey.study.model.VisitImpl" %> +<%@ page import="java.util.HashMap" %> <%@ page import="java.util.LinkedHashSet" %> <%@ page import="java.util.List" %> <%@ page import="java.util.Map" %> -<%@ page import="java.util.HashMap" %> -<%@ page import="org.labkey.study.model.ProductImpl" %> +<%@ page extends="org.labkey.study.view.BaseStudyPage" %> <%! public LinkedHashSet getClientDependencies() { From a9a65933940f18272caf8ffd7ccc9aeed81e10d6 Mon Sep 17 00:00:00 2001 From: Matthew Bellew Date: Fri, 17 Jan 2014 23:40:18 +0000 Subject: [PATCH 022/218] Cleanup getMandatoryPropertyNames() isDefaultFieldName() --- .../studydesign/AbstractStudyDesignDomainKind.java | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/study/src/org/labkey/study/query/studydesign/AbstractStudyDesignDomainKind.java b/study/src/org/labkey/study/query/studydesign/AbstractStudyDesignDomainKind.java index a299aa13..1ab12434 100644 --- a/study/src/org/labkey/study/query/studydesign/AbstractStudyDesignDomainKind.java +++ b/study/src/org/labkey/study/query/studydesign/AbstractStudyDesignDomainKind.java @@ -53,8 +53,6 @@ import java.util.Collections; import java.util.HashSet; import java.util.LinkedHashSet; -import java.util.List; -import java.util.Map; import java.util.Set; /** @@ -69,7 +67,6 @@ public abstract class AbstractStudyDesignDomainKind extends AbstractDomainKind private static String DOMAIN_LSID_TEMPLATE = "${FolderLSIDBase}:${TableName}"; private static final Set _baseFields; - private static final Set _reservedNames = new HashSet<>(); private Set _standardFields = new LinkedHashSet<>(); private final String _tableName; @@ -125,17 +122,6 @@ public Set getBaseProperties() return _standardFields; } - @Override - public Set getMandatoryPropertyNames(Domain domain) - { - if (_reservedNames.isEmpty()) - { - for (PropertyStorageSpec spec : getBaseProperties()) - _reservedNames.add(spec.getName()); - } - return _reservedNames; - } - @Override public String getTypeLabel(Domain domain) { From 2c172d3fc41f6205ef499868afa0ee4c801ce3e7 Mon Sep 17 00:00:00 2001 From: Cory Nathe Date: Sat, 18 Jan 2014 16:11:00 +0000 Subject: [PATCH 023/218] Misc study design webpart cleanup - add description text and move edit button to top --- .../ImmunizationScheduleWebpartFactory.java | 2 + .../view/studydesign/assayScheduleWebpart.jsp | 46 +++++++------------ .../immunizationScheduleWebpart.jsp | 35 ++++++++------ .../view/studydesign/manageAssaySpecimen.jsp | 4 +- .../view/studydesign/vaccineDesignWebpart.jsp | 13 ++++-- 5 files changed, 48 insertions(+), 52 deletions(-) diff --git a/study/src/org/labkey/study/view/studydesign/ImmunizationScheduleWebpartFactory.java b/study/src/org/labkey/study/view/studydesign/ImmunizationScheduleWebpartFactory.java index 029b926a..4987f934 100644 --- a/study/src/org/labkey/study/view/studydesign/ImmunizationScheduleWebpartFactory.java +++ b/study/src/org/labkey/study/view/studydesign/ImmunizationScheduleWebpartFactory.java @@ -25,6 +25,7 @@ import org.labkey.api.view.Portal; import org.labkey.api.view.ViewContext; import org.labkey.api.view.WebPartView; +import org.labkey.study.controllers.CohortController; import org.labkey.study.controllers.StudyController; import org.labkey.study.model.StudyManager; import org.labkey.study.security.permissions.ManageStudyPermission; @@ -60,6 +61,7 @@ public WebPartView getWebPartView(ViewContext portalCtx, Portal.WebPart webPart) timepointMenuName = "Manage Visits"; NavTree menu = new NavTree(); + menu.addChild("Manage Cohorts", new ActionURL(CohortController.ManageCohortsAction.class, c)); menu.addChild(timepointMenuName, new ActionURL(StudyController.ManageVisitsAction.class, c)); view.setNavMenu(menu); } diff --git a/study/src/org/labkey/study/view/studydesign/assayScheduleWebpart.jsp b/study/src/org/labkey/study/view/studydesign/assayScheduleWebpart.jsp index deb5d991..554ce482 100644 --- a/study/src/org/labkey/study/view/studydesign/assayScheduleWebpart.jsp +++ b/study/src/org/labkey/study/view/studydesign/assayScheduleWebpart.jsp @@ -25,9 +25,7 @@ <%@ page import="org.labkey.study.SampleManager" %> <%@ page import="org.labkey.study.controllers.StudyDesignController" %> <%@ page import="org.labkey.study.model.AssaySpecimenConfigImpl" %> -<%@ page import="org.labkey.study.model.DerivativeType" %> <%@ page import="org.labkey.study.model.LocationImpl" %> -<%@ page import="org.labkey.study.model.PrimaryType" %> <%@ page import="org.labkey.study.model.StudyImpl" %> <%@ page import="org.labkey.study.model.StudyManager" %> <%@ page import="org.labkey.study.model.VisitImpl" %> @@ -105,20 +103,28 @@ List assaySpecimenConfigs = study.getAssaySpecimenConfigs(); List visits = study.getVisitsForAssaySchedule(); -%> -

    <%=assayPlan%>

    -<% - if (assaySpecimenConfigs.size() == 0) { + %>No assays have been scheduled.
    <% + } + + if (canEdit) + { %> -

    No assays have been scheduled.

    + To change the set of assays and edit the assay schedule, click the edit button below.
    + <%=generateButton("Edit", StudyDesignController.ManageAssaySpecimenAction.class)%> <% } - else + + %>

    <%=assayPlan%>

    <% + + if (assaySpecimenConfigs.size() > 0) { %> + + + @@ -140,22 +146,9 @@ { // concatenate sample type (i.e. primary, derivative, tube type) String sampleType = ""; - String sep = ""; - if (assaySpecimen.getPrimaryTypeId() != null) - { - PrimaryType pt = SampleManager.getInstance().getPrimaryType(c, assaySpecimen.getPrimaryTypeId()); - sampleType += (pt != null ? pt.getPrimaryType() : assaySpecimen.getPrimaryTypeId()); - sep = " / "; - } - if (assaySpecimen.getDerivativeTypeId() != null) - { - DerivativeType dt = SampleManager.getInstance().getDerivativeType(c, assaySpecimen.getDerivativeTypeId()); - sampleType += sep + (dt != null ? dt.getDerivative() : assaySpecimen.getDerivativeTypeId()); - sep = " / "; - } if (assaySpecimen.getTubeType() != null) { - sampleType += sep + assaySpecimen.getTubeType(); + sampleType += assaySpecimen.getTubeType(); } String locationLabel = ""; @@ -189,13 +182,6 @@ } else { -%> -

    The folder must contain a study in order to display an assay schedule.

    -<% - } - - if (canEdit) - { - %>
    <%=textLink("Edit", StudyDesignController.ManageAssaySpecimenAction.class)%><% + %>

    The folder must contain a study in order to display an assay schedule.

    <% } %> \ No newline at end of file diff --git a/study/src/org/labkey/study/view/studydesign/immunizationScheduleWebpart.jsp b/study/src/org/labkey/study/view/studydesign/immunizationScheduleWebpart.jsp index 470a6080..e9103477 100644 --- a/study/src/org/labkey/study/view/studydesign/immunizationScheduleWebpart.jsp +++ b/study/src/org/labkey/study/view/studydesign/immunizationScheduleWebpart.jsp @@ -99,20 +99,34 @@ <% if (study != null) { -%> -
    Assay Schedule
    Assay Lab
    -<% List cohorts = study.getCohorts(user); if (cohorts.size() == 0) { - %><% + %>No cohort/treatment/timepoint mappings have been defined.
    <% } else + { + %>This section shows the immunization schedule. Each treatment may consist of several immunogens and adjuvants.
    <% + } + + if (canEdit) + { +%> + To change the set of groups/cohorts and edit the immunization schedule, click the edit button below.
    + <%=generateButton("Edit", StudyDesignController.ManageImmunizationsAction.class)%> +<% + } + + if (cohorts.size() > 0) { List visits = study.getVisitsForImmunizationSchedule(); %> +
    No cohort/treatment/timepoint mappings have been defined.
    +
    + + + @@ -186,21 +200,12 @@ %>
    Immunization Schedule
    Group / Cohort Count
    <% } -%> - -<% } else { -%> -

    The folder must contain a study in order to display a vaccine design.

    -<% - } - - if (canEdit) - { - %>
    <%=textLink("Edit", StudyDesignController.ManageImmunizationsAction.class)%><% + %>

    The folder must contain a study in order to display a vaccine design.

    <% } %> diff --git a/study/src/org/labkey/study/view/studydesign/manageAssaySpecimen.jsp b/study/src/org/labkey/study/view/studydesign/manageAssaySpecimen.jsp index c611ab19..ecd1ad02 100644 --- a/study/src/org/labkey/study/view/studydesign/manageAssaySpecimen.jsp +++ b/study/src/org/labkey/study/view/studydesign/manageAssaySpecimen.jsp @@ -87,7 +87,7 @@ _storeSC = X.create('LABKEY.ext4.Store', { schemaName: 'study', queryName: 'AssaySpecimen', - columns : ['RowId', 'AssayName', 'Description', 'Source', 'LocationId', 'TubeType', 'PrimaryTypeId', 'DerivativeTypeId'], + columns : ['RowId', 'AssayName', 'Description', 'Source', 'LocationId', 'TubeType'], sort : ['Description', 'AssayName'] }); zz = _storeSC; @@ -112,7 +112,7 @@ _gridSC = X.create('LABKEY.ext4.GridPanel', { store: _storeSC, renderTo: 'AssaySpecimenConfigGrid', - maxWidth: 1250, + maxWidth: 1000, autoHeight: true, selType: 'rowmodel', multiSelect: false, diff --git a/study/src/org/labkey/study/view/studydesign/vaccineDesignWebpart.jsp b/study/src/org/labkey/study/view/studydesign/vaccineDesignWebpart.jsp index 8e5ba311..0625c833 100644 --- a/study/src/org/labkey/study/view/studydesign/vaccineDesignWebpart.jsp +++ b/study/src/org/labkey/study/view/studydesign/vaccineDesignWebpart.jsp @@ -95,6 +95,14 @@ <% if (study != null) { + %>This section describes the immunogens and adjuvants evaluated in the study.
    <% + if (canEdit) + { +%> + To change the set of immunogens and adjuvants, click the edit button below.
    + <%=generateButton("Edit", StudyDesignController.ManageStudyProductsAction.class)%> +<% + } %> @@ -164,11 +172,6 @@

    The folder must contain a study in order to display a vaccine design.

    <% } - - if (canEdit) - { - %>
    <%=textLink("Edit", StudyDesignController.ManageStudyProductsAction.class)%><% - } %> +Enter assay schedule information in the grids below. +
    +
      +
    • Use the "Insert New" button in the assay/specimen configurations grid to add a new assay.
    • +
    • Select the visits for each assay in the assay/specimen visit mapping grid to define the expected assay schedule for the study.
    • +
    • > + Configure dropdown options at the project level to be shared across study designs or within this folder for + study specific properties: +
    • +
    +
    * Double click to edit an assay/specimen configuration -


    +

    +<% + if (canManageStudy && form.isUseAlternateLookupFields()) + { + %><%= textLink("Manage Locations", StudyController.ManageLocationsAction.class) %>
    <% + } +%> +
    <% diff --git a/study/src/org/labkey/study/view/studydesign/manageImmunizations.jsp b/study/src/org/labkey/study/view/studydesign/manageImmunizations.jsp index d50f1ecf..761e150c 100644 --- a/study/src/org/labkey/study/view/studydesign/manageImmunizations.jsp +++ b/study/src/org/labkey/study/view/studydesign/manageImmunizations.jsp @@ -76,7 +76,12 @@ Enter immunization information in the grids below. -
    +
      +
    • + Configure dropdown options for routes at the project level to be shared across study designs or within this folder for + study specific properties: +
    • Use the "Insert New" button in the treatments grid to add a new study treatment.
    • Each treatment may consist of several study products, i.e. immunogens and/or adjuvants.
    • Use the "Insert New" button in the immunization schedule grid to add a new study cohort.
    • Enter the number of subjects for the cohort in the count column.
    • -
    • - Configure dropdown options at the project level to be shared across study designs or within this folder for - study specific properties: -
    diff --git a/study/src/org/labkey/study/view/studydesign/manageStudyProducts.jsp b/study/src/org/labkey/study/view/studydesign/manageStudyProducts.jsp index 23937b62..ee9f7232 100644 --- a/study/src/org/labkey/study/view/studydesign/manageStudyProducts.jsp +++ b/study/src/org/labkey/study/view/studydesign/manageStudyProducts.jsp @@ -70,13 +70,16 @@ menu: { items: [{ text: 'Immunogen Types', - href: LABKEY.ActionURL.buildURL('query', 'executeQuery', projectPath, {schemaName: 'study', 'query.queryName': 'StudyDesignImmunogenTypes'}) + href: LABKEY.ActionURL.buildURL('query', 'executeQuery', projectPath, {schemaName: 'study', 'query.queryName': 'StudyDesignImmunogenTypes'}), + hrefTarget: '_blank' // issue 19493 },{ text: 'Genes', - href: LABKEY.ActionURL.buildURL('query', 'executeQuery', projectPath, {schemaName: 'study', 'query.queryName': 'StudyDesignGenes'}) + href: LABKEY.ActionURL.buildURL('query', 'executeQuery', projectPath, {schemaName: 'study', 'query.queryName': 'StudyDesignGenes'}), + hrefTarget: '_blank' // issue 19493 },{ text: 'SubTypes', - href: LABKEY.ActionURL.buildURL('query', 'executeQuery', projectPath, {schemaName: 'study', 'query.queryName': 'StudyDesignSubTypes'}) + href: LABKEY.ActionURL.buildURL('query', 'executeQuery', projectPath, {schemaName: 'study', 'query.queryName': 'StudyDesignSubTypes'}), + hrefTarget: '_blank' // issue 19493 }] } }; @@ -87,13 +90,16 @@ menu: { items: [{ text: 'Immunogen Types', - href: LABKEY.ActionURL.buildURL('query', 'executeQuery', null, {schemaName: 'study', 'query.queryName': 'StudyDesignImmunogenTypes'}) + href: LABKEY.ActionURL.buildURL('query', 'executeQuery', null, {schemaName: 'study', 'query.queryName': 'StudyDesignImmunogenTypes'}), + hrefTarget: '_blank' // issue 19493 },{ text: 'Genes', - href: LABKEY.ActionURL.buildURL('query', 'executeQuery', null, {schemaName: 'study', 'query.queryName': 'StudyDesignGenes'}) + href: LABKEY.ActionURL.buildURL('query', 'executeQuery', null, {schemaName: 'study', 'query.queryName': 'StudyDesignGenes'}), + hrefTarget: '_blank' // issue 19493 },{ text: 'SubTypes', - href: LABKEY.ActionURL.buildURL('query', 'executeQuery', null, {schemaName: 'study', 'query.queryName': 'StudyDesignSubTypes'}) + href: LABKEY.ActionURL.buildURL('query', 'executeQuery', null, {schemaName: 'study', 'query.queryName': 'StudyDesignSubTypes'}), + hrefTarget: '_blank' // issue 19493 }] } }; @@ -109,14 +115,14 @@ Enter vaccine design information in the grids below.
      +
    • + Configure dropdown options for immunogen types, genes, and subtypes at the project level to be shared across study designs or within this folder for + study specific properties: +
    • Each immunogen and adjuvant in the study should be listed on one row of the grids below.
    • Immunogens and adjuvants should have unique names.
    • If possible, the immunogen description should include specific sequences of HIV Antigens included in the immunogen.
    • Use the manage immunizations page to describe the schedule of immunizations and combinations of immunogens and adjuvants administered at each timepoint.
    • -
    • - Configure dropdown options at the project level to be shared across study designs or within this folder for - study specific properties: -
    diff --git a/study/src/org/labkey/study/view/studydesign/vaccineDesignWebpart.jsp b/study/src/org/labkey/study/view/studydesign/vaccineDesignWebpart.jsp index c70700ca..c9a79caf 100644 --- a/study/src/org/labkey/study/view/studydesign/vaccineDesignWebpart.jsp +++ b/study/src/org/labkey/study/view/studydesign/vaccineDesignWebpart.jsp @@ -20,13 +20,13 @@ <%@ page import="org.labkey.api.view.HttpView" %> <%@ page import="org.labkey.api.view.JspView" %> <%@ page import="org.labkey.api.view.Portal" %> -<%@ page import="org.labkey.api.view.WebTheme" %> <%@ page import="org.labkey.api.view.WebThemeManager" %> <%@ page import="org.labkey.api.view.template.ClientDependency" %> <%@ page import="org.labkey.study.controllers.StudyDesignController" %> <%@ page import="org.labkey.study.model.ProductImpl" %> <%@ page import="org.labkey.study.model.StudyImpl" %> <%@ page import="org.labkey.study.model.StudyManager" %> +<%@ page import="org.labkey.study.model.TreatmentManager" %> <%@ page import="java.util.LinkedHashSet" %> <%@ page import="java.util.List" %> <%@ page import="org.labkey.api.security.permissions.UpdatePermission" %> @@ -35,6 +35,7 @@ public LinkedHashSet getClientDependencies() { LinkedHashSet resources = new LinkedHashSet<>(); + resources.add(ClientDependency.fromFilePath("Ext4ClientApi")); resources.add(ClientDependency.fromFilePath("study/ImmunizationSchedule.js")); resources.add(ClientDependency.fromFilePath("study/StudyVaccineDesign.css")); return resources; @@ -94,10 +95,11 @@ <% for (ProductImpl immunogen : immunogens) { + String typeLabel = TreatmentManager.getInstance().getStudyDesignImmunogenTypeLabelByName(c, immunogen.getType()); %>
    - + <% @@ -146,6 +148,10 @@ -Enter immunization information in the grids below. +Enter treatment information in the grids below.
    • @@ -133,17 +141,17 @@ Enter immunization information in the grids below.
    • Use the "Insert New" button in the treatments grid to add a new study treatment.
    • Each treatment may consist of several study products, i.e. immunogens and/or adjuvants.
    • -
    • Use the "Insert New" button in the immunization schedule grid to add a new study cohort.
    • +
    • Use the "Insert New" button in the treatment schedule grid to add a new study cohort.
    • Enter the number of subjects for the cohort in the count column.
    -
    * Double click to edit a treatment record and its product definition
    +
    * Double click to edit a treatment record and its product definition

    <%=textLink("Manage Study Products", StudyDesignController.ManageStudyProductsAction.class)%>

    -
    -
    * Double click to edit a group/cohort and its treatment/visit map definition
    +
    +
    * Double click to edit a group/cohort and its treatment/visit map definition

    <% if (canManageStudy) diff --git a/study/src/org/labkey/study/view/studydesign/vaccineDesignWebpart.jsp b/study/src/org/labkey/study/view/studydesign/vaccineDesignWebpart.jsp index 3488714d..bbd4d57e 100644 --- a/study/src/org/labkey/study/view/studydesign/vaccineDesignWebpart.jsp +++ b/study/src/org/labkey/study/view/studydesign/vaccineDesignWebpart.jsp @@ -36,7 +36,7 @@ { LinkedHashSet resources = new LinkedHashSet<>(); resources.add(ClientDependency.fromFilePath("Ext4ClientApi")); - resources.add(ClientDependency.fromFilePath("study/ImmunizationSchedule.js")); + resources.add(ClientDependency.fromFilePath("study/StudyVaccineDesign.js")); resources.add(ClientDependency.fromFilePath("study/StudyVaccineDesign.css")); return resources; } From 2278519b20b4315b58af98f5c3809d5714d30f37 Mon Sep 17 00:00:00 2001 From: Karl Lum Date: Mon, 7 Apr 2014 22:46:59 +0000 Subject: [PATCH 055/218] 20052: Importing study with shared row lookups to dropped study.Product rows cause PipelineJobException 20039: **Custom field names on Treatment, Product, Personnel do not retain their capitalization 19946: **Study extended properties exported when marked as protected and selecting the "Remove All Columns Tagged as Protected" option selected --- .../studydesign/DefaultStudyDesignTable.java | 28 +++++++++++-------- 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/study/src/org/labkey/study/query/studydesign/DefaultStudyDesignTable.java b/study/src/org/labkey/study/query/studydesign/DefaultStudyDesignTable.java index a9090e0e..1d6275ba 100644 --- a/study/src/org/labkey/study/query/studydesign/DefaultStudyDesignTable.java +++ b/study/src/org/labkey/study/query/studydesign/DefaultStudyDesignTable.java @@ -73,19 +73,23 @@ public DefaultStudyDesignTable(Domain domain, DbSchema dbSchema, UserSchema sche if (null != propertyURI) { PropertyDescriptor pd = OntologyManager.getPropertyDescriptor(propertyURI, schema.getContainer()); - if (null != pd && pd.getLookupQuery() != null) - col.setFk(new PdLookupForeignKey(schema.getUser(), pd, schema.getContainer())); - - if (pd != null && pd.getPropertyType() == PropertyType.MULTI_LINE) + if (pd != null) { - col.setDisplayColumnFactory(new DisplayColumnFactory() { - public DisplayColumn createRenderer(ColumnInfo colInfo) - { - DataColumn dc = new DataColumn(colInfo); - dc.setPreserveNewlines(true); - return dc; - } - }); + if (pd.getLookupQuery() != null) + col.setFk(new PdLookupForeignKey(schema.getUser(), pd, schema.getContainer())); + + if (pd.getPropertyType() == PropertyType.MULTI_LINE) + { + col.setDisplayColumnFactory(new DisplayColumnFactory() { + public DisplayColumn createRenderer(ColumnInfo colInfo) + { + DataColumn dc = new DataColumn(colInfo); + dc.setPreserveNewlines(true); + return dc; + } + }); + } + col.setName(pd.getName()); } } } From 737c1b4c95a2499d8e8924eadf12a8634be0e2a4 Mon Sep 17 00:00:00 2001 From: Karl Lum Date: Mon, 21 Apr 2014 01:03:02 +0000 Subject: [PATCH 056/218] use dbschema caching for the study design tables --- .../query/studydesign/AbstractStudyDesignDomainKind.java | 7 +++++++ .../study/query/studydesign/DefaultStudyDesignTable.java | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/study/src/org/labkey/study/query/studydesign/AbstractStudyDesignDomainKind.java b/study/src/org/labkey/study/query/studydesign/AbstractStudyDesignDomainKind.java index c4979abf..6cb11c2e 100644 --- a/study/src/org/labkey/study/query/studydesign/AbstractStudyDesignDomainKind.java +++ b/study/src/org/labkey/study/query/studydesign/AbstractStudyDesignDomainKind.java @@ -22,6 +22,7 @@ import org.labkey.api.data.Container; import org.labkey.api.data.ContainerManager; import org.labkey.api.data.DbSchema; +import org.labkey.api.data.DbSchemaType; import org.labkey.api.data.DbScope; import org.labkey.api.data.JdbcType; import org.labkey.api.data.PropertyStorageSpec; @@ -232,4 +233,10 @@ public Set getNonProvisionedTableNames() { return Collections.emptySet(); } + + @Override + public DbSchemaType getSchemaType() + { + return DbSchemaType.Provisioned; + } } diff --git a/study/src/org/labkey/study/query/studydesign/DefaultStudyDesignTable.java b/study/src/org/labkey/study/query/studydesign/DefaultStudyDesignTable.java index 1d6275ba..853271dc 100644 --- a/study/src/org/labkey/study/query/studydesign/DefaultStudyDesignTable.java +++ b/study/src/org/labkey/study/query/studydesign/DefaultStudyDesignTable.java @@ -58,7 +58,7 @@ public class DefaultStudyDesignTable extends FilteredTable public DefaultStudyDesignTable(Domain domain, DbSchema dbSchema, UserSchema schema) { - super(StorageProvisioner.createTableInfo(domain, dbSchema), schema); + super(StorageProvisioner.createCachedTableInfo(domain, dbSchema), schema); _domain = domain; From 4f78ccd40b36398d90b3a054aab5bc7d121a9524 Mon Sep 17 00:00:00 2001 From: Adam Rauch Date: Sat, 3 May 2014 22:05:20 +0000 Subject: [PATCH 057/218] @NotNull --- .../study/query/studydesign/DefaultStudyDesignTable.java | 3 ++- .../study/query/studydesign/StudyDesignLookupBaseTable.java | 2 +- .../study/query/studydesign/StudyProductAntigenTable.java | 3 ++- .../labkey/study/query/studydesign/StudyProductTable.java | 5 ++--- 4 files changed, 7 insertions(+), 6 deletions(-) diff --git a/study/src/org/labkey/study/query/studydesign/DefaultStudyDesignTable.java b/study/src/org/labkey/study/query/studydesign/DefaultStudyDesignTable.java index 853271dc..0b97f0c4 100644 --- a/study/src/org/labkey/study/query/studydesign/DefaultStudyDesignTable.java +++ b/study/src/org/labkey/study/query/studydesign/DefaultStudyDesignTable.java @@ -15,6 +15,7 @@ */ package org.labkey.study.query.studydesign; +import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.labkey.api.data.ColumnInfo; import org.labkey.api.data.Container; @@ -166,7 +167,7 @@ public String getDescription() @Override // ONLY OVERRIDE THIS IF TABLE SHOULD BE VISIBLE IN DATASPACE PROJECT-LEVEL CONTAINER - public boolean hasPermission(UserPrincipal user, Class perm) + public boolean hasPermission(@NotNull UserPrincipal user, @NotNull Class perm) { // Most tables should not editable in Dataspace if (getContainer().isDataspace()) diff --git a/study/src/org/labkey/study/query/studydesign/StudyDesignLookupBaseTable.java b/study/src/org/labkey/study/query/studydesign/StudyDesignLookupBaseTable.java index 23c9717a..3e450051 100644 --- a/study/src/org/labkey/study/query/studydesign/StudyDesignLookupBaseTable.java +++ b/study/src/org/labkey/study/query/studydesign/StudyDesignLookupBaseTable.java @@ -159,7 +159,7 @@ private void validateValues(Map row) throws ValidationException } @Override - public boolean hasPermission(UserPrincipal user, Class perm) + public boolean hasPermission(@NotNull UserPrincipal user, @NotNull Class perm) { // These are editable in Dataspace, but not in a folder within a Dataspace if (getContainer().getProject().isDataspace() && !getContainer().isDataspace()) diff --git a/study/src/org/labkey/study/query/studydesign/StudyProductAntigenTable.java b/study/src/org/labkey/study/query/studydesign/StudyProductAntigenTable.java index de378cef..9f3d7fd9 100644 --- a/study/src/org/labkey/study/query/studydesign/StudyProductAntigenTable.java +++ b/study/src/org/labkey/study/query/studydesign/StudyProductAntigenTable.java @@ -15,6 +15,7 @@ */ package org.labkey.study.query.studydesign; +import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.labkey.api.data.ColumnInfo; import org.labkey.api.data.ContainerFilter; @@ -102,7 +103,7 @@ public List getDefaultVisibleColumns() } @Override - public boolean hasPermission(UserPrincipal user, Class perm) + public boolean hasPermission(@NotNull UserPrincipal user, @NotNull Class perm) { // This is editable in Dataspace, but not in a folder within a Dataspace if (getContainer().getProject().isDataspace() && !getContainer().isDataspace()) diff --git a/study/src/org/labkey/study/query/studydesign/StudyProductTable.java b/study/src/org/labkey/study/query/studydesign/StudyProductTable.java index a4926dea..43dc8d33 100644 --- a/study/src/org/labkey/study/query/studydesign/StudyProductTable.java +++ b/study/src/org/labkey/study/query/studydesign/StudyProductTable.java @@ -15,6 +15,7 @@ */ package org.labkey.study.query.studydesign; +import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.labkey.api.data.ColumnInfo; import org.labkey.api.data.ContainerFilter; @@ -27,8 +28,6 @@ import org.labkey.api.query.UserSchema; import org.labkey.api.security.UserPrincipal; import org.labkey.api.security.permissions.Permission; -import org.labkey.study.model.StudyImpl; -import org.labkey.study.model.StudyManager; import org.labkey.study.query.StudyQuerySchema; import java.util.ArrayList; @@ -79,7 +78,7 @@ public List getDefaultVisibleColumns() } @Override - public boolean hasPermission(UserPrincipal user, Class perm) + public boolean hasPermission(@NotNull UserPrincipal user, @NotNull Class perm) { // This is editable in Dataspace, but not in a folder within a Dataspace if (getContainer().getProject().isDataspace() && !getContainer().isDataspace()) From 5ebd27d267782f0305fd6037812beefb49e4f978 Mon Sep 17 00:00:00 2001 From: Cory Nathe Date: Fri, 6 Jun 2014 19:45:39 +0000 Subject: [PATCH 058/218] Issue 20376: Additional columns for StudyDesignAssays --- .../study/query/studydesign/StudyDesignAssaysTable.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/study/src/org/labkey/study/query/studydesign/StudyDesignAssaysTable.java b/study/src/org/labkey/study/query/studydesign/StudyDesignAssaysTable.java index 2dce13d5..d39729ec 100644 --- a/study/src/org/labkey/study/query/studydesign/StudyDesignAssaysTable.java +++ b/study/src/org/labkey/study/query/studydesign/StudyDesignAssaysTable.java @@ -41,13 +41,16 @@ public StudyDesignAssaysTable(StudyQuerySchema schema, ContainerFilter filter) FieldKey.fromParts("Description"), FieldKey.fromParts("Inactive"), FieldKey.fromParts("Target"), + FieldKey.fromParts("TargetType"), + FieldKey.fromParts("TargetSubtype"), FieldKey.fromParts("Methodology"), FieldKey.fromParts("Category"), FieldKey.fromParts("TargetFunction"), FieldKey.fromParts("LeadContributor"), FieldKey.fromParts("Contact"), FieldKey.fromParts("Summary"), - FieldKey.fromParts("Keywords") + FieldKey.fromParts("Keywords"), + FieldKey.fromParts("Editorial") )); setDefaultVisibleColumns(defaultColumns); } From 1393a624e5046d2978b995005a1fa03b5796c833 Mon Sep 17 00:00:00 2001 From: Cory Nathe Date: Thu, 12 Jun 2014 15:24:36 +0000 Subject: [PATCH 059/218] Issue 20677: **Adjustments to studyDesignAssays & cds.Antigens tables --- .../study/query/studydesign/StudyDesignAssaysTable.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/study/src/org/labkey/study/query/studydesign/StudyDesignAssaysTable.java b/study/src/org/labkey/study/query/studydesign/StudyDesignAssaysTable.java index d39729ec..8c2d9d7a 100644 --- a/study/src/org/labkey/study/query/studydesign/StudyDesignAssaysTable.java +++ b/study/src/org/labkey/study/query/studydesign/StudyDesignAssaysTable.java @@ -50,7 +50,10 @@ public StudyDesignAssaysTable(StudyQuerySchema schema, ContainerFilter filter) FieldKey.fromParts("Contact"), FieldKey.fromParts("Summary"), FieldKey.fromParts("Keywords"), - FieldKey.fromParts("Editorial") + FieldKey.fromParts("Editorial"), + FieldKey.fromParts("AlternateName"), + FieldKey.fromParts("Lab"), + FieldKey.fromParts("LabPI") )); setDefaultVisibleColumns(defaultColumns); } From d5128ef241d61be1d7e12223a992648af6e7189e Mon Sep 17 00:00:00 2001 From: Cory Nathe Date: Thu, 12 Jun 2014 15:42:01 +0000 Subject: [PATCH 060/218] Missed from previous check-in (for issue 20677) --- .../study/query/studydesign/StudyDesignAssaysTable.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/study/src/org/labkey/study/query/studydesign/StudyDesignAssaysTable.java b/study/src/org/labkey/study/query/studydesign/StudyDesignAssaysTable.java index 8c2d9d7a..df15df24 100644 --- a/study/src/org/labkey/study/query/studydesign/StudyDesignAssaysTable.java +++ b/study/src/org/labkey/study/query/studydesign/StudyDesignAssaysTable.java @@ -40,10 +40,10 @@ public StudyDesignAssaysTable(StudyQuerySchema schema, ContainerFilter filter) FieldKey.fromParts("Label"), FieldKey.fromParts("Description"), FieldKey.fromParts("Inactive"), - FieldKey.fromParts("Target"), + FieldKey.fromParts("Type"), FieldKey.fromParts("TargetType"), FieldKey.fromParts("TargetSubtype"), - FieldKey.fromParts("Methodology"), + FieldKey.fromParts("Platform"), FieldKey.fromParts("Category"), FieldKey.fromParts("TargetFunction"), FieldKey.fromParts("LeadContributor"), From 7c479db6a8262f5300af72e427e908acec656f50 Mon Sep 17 00:00:00 2001 From: Adam Rauch Date: Wed, 16 Jul 2014 18:00:42 +0000 Subject: [PATCH 061/218] Cache tables from all provisioned tables except specimen schema (which should be coming soon) --- .../labkey/study/query/studydesign/DefaultStudyDesignTable.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/study/src/org/labkey/study/query/studydesign/DefaultStudyDesignTable.java b/study/src/org/labkey/study/query/studydesign/DefaultStudyDesignTable.java index 0b97f0c4..cd80d121 100644 --- a/study/src/org/labkey/study/query/studydesign/DefaultStudyDesignTable.java +++ b/study/src/org/labkey/study/query/studydesign/DefaultStudyDesignTable.java @@ -59,7 +59,7 @@ public class DefaultStudyDesignTable extends FilteredTable public DefaultStudyDesignTable(Domain domain, DbSchema dbSchema, UserSchema schema) { - super(StorageProvisioner.createCachedTableInfo(domain, dbSchema), schema); + super(StorageProvisioner.createTableInfo(domain, dbSchema), schema); _domain = domain; From 523be53e77e77be70f86909dde9105b5f8c3e600 Mon Sep 17 00:00:00 2001 From: Adam Rauch Date: Thu, 17 Jul 2014 04:59:11 +0000 Subject: [PATCH 062/218] Clean up vestiges of bygone eras.... remove non-cached storage provisioner code-paths, unused parameters (DbSchema, title), unused methods, etc. Fix ProjectAndSiteGroupsCache to return unmodifiable collection. Squelch warnings on cached arrays temporarily, since I don't feel like fixing them all right now. --- .../study/query/studydesign/DefaultStudyDesignTable.java | 8 ++++---- .../study/query/studydesign/StudyProductAntigenTable.java | 6 ++---- .../labkey/study/query/studydesign/StudyProductTable.java | 4 ++-- .../query/studydesign/StudyTreatmentProductTable.java | 6 ++---- .../study/query/studydesign/StudyTreatmentTable.java | 4 ++-- 5 files changed, 12 insertions(+), 16 deletions(-) diff --git a/study/src/org/labkey/study/query/studydesign/DefaultStudyDesignTable.java b/study/src/org/labkey/study/query/studydesign/DefaultStudyDesignTable.java index cd80d121..7b372652 100644 --- a/study/src/org/labkey/study/query/studydesign/DefaultStudyDesignTable.java +++ b/study/src/org/labkey/study/query/studydesign/DefaultStudyDesignTable.java @@ -57,9 +57,9 @@ public class DefaultStudyDesignTable extends FilteredTable protected List _defaultVisibleColumns = new ArrayList<>(); private Domain _domain; - public DefaultStudyDesignTable(Domain domain, DbSchema dbSchema, UserSchema schema) + public DefaultStudyDesignTable(Domain domain, UserSchema schema) { - super(StorageProvisioner.createTableInfo(domain, dbSchema), schema); + super(StorageProvisioner.createTableInfo(domain), schema); _domain = domain; @@ -121,9 +121,9 @@ public DisplayColumn createRenderer(ColumnInfo colInfo) initColumns(); } - public DefaultStudyDesignTable(Domain domain, DbSchema dbSchema, UserSchema schema, @Nullable ContainerFilter containerFilter) + public DefaultStudyDesignTable(Domain domain, UserSchema schema, @Nullable ContainerFilter containerFilter) { - this(domain, dbSchema, schema); + this(domain, schema); if (null != containerFilter) _setContainerFilter(containerFilter); } diff --git a/study/src/org/labkey/study/query/studydesign/StudyProductAntigenTable.java b/study/src/org/labkey/study/query/studydesign/StudyProductAntigenTable.java index 9f3d7fd9..d9529cd9 100644 --- a/study/src/org/labkey/study/query/studydesign/StudyProductAntigenTable.java +++ b/study/src/org/labkey/study/query/studydesign/StudyProductAntigenTable.java @@ -28,8 +28,6 @@ import org.labkey.api.query.UserSchema; import org.labkey.api.security.UserPrincipal; import org.labkey.api.security.permissions.Permission; -import org.labkey.study.model.StudyImpl; -import org.labkey.study.model.StudyManager; import org.labkey.study.query.StudyQuerySchema; import java.util.ArrayList; @@ -52,9 +50,9 @@ public class StudyProductAntigenTable extends DefaultStudyDesignTable defaultVisibleColumns.add(FieldKey.fromParts("Sequence")); } - public StudyProductAntigenTable(Domain domain, DbSchema dbSchema, UserSchema schema, @Nullable ContainerFilter containerFilter) + public StudyProductAntigenTable(Domain domain, UserSchema schema, @Nullable ContainerFilter containerFilter) { - super(domain, dbSchema, schema, containerFilter); + super(domain, schema, containerFilter); setName(StudyQuerySchema.PRODUCT_ANTIGEN_TABLE_NAME); setDescription("Contains one row per study product antigen"); diff --git a/study/src/org/labkey/study/query/studydesign/StudyProductTable.java b/study/src/org/labkey/study/query/studydesign/StudyProductTable.java index 43dc8d33..a65b8c2d 100644 --- a/study/src/org/labkey/study/query/studydesign/StudyProductTable.java +++ b/study/src/org/labkey/study/query/studydesign/StudyProductTable.java @@ -48,9 +48,9 @@ public class StudyProductTable extends DefaultStudyDesignTable defaultVisibleColumns.add(FieldKey.fromParts("Type")); } - public StudyProductTable(Domain domain, DbSchema dbSchema, UserSchema schema, @Nullable ContainerFilter containerFilter) + public StudyProductTable(Domain domain, UserSchema schema, @Nullable ContainerFilter containerFilter) { - super(domain, dbSchema, schema, containerFilter); + super(domain, schema, containerFilter); setName(StudyQuerySchema.PRODUCT_TABLE_NAME); setDescription("Contains one row per study product"); diff --git a/study/src/org/labkey/study/query/studydesign/StudyTreatmentProductTable.java b/study/src/org/labkey/study/query/studydesign/StudyTreatmentProductTable.java index 944814c1..a1316673 100644 --- a/study/src/org/labkey/study/query/studydesign/StudyTreatmentProductTable.java +++ b/study/src/org/labkey/study/query/studydesign/StudyTreatmentProductTable.java @@ -25,8 +25,6 @@ import org.labkey.api.query.LookupForeignKey; import org.labkey.api.query.QueryService; import org.labkey.api.query.UserSchema; -import org.labkey.study.model.StudyImpl; -import org.labkey.study.model.StudyManager; import org.labkey.study.query.StudyQuerySchema; import java.util.ArrayList; @@ -48,9 +46,9 @@ public class StudyTreatmentProductTable extends DefaultStudyDesignTable defaultVisibleColumns.add(FieldKey.fromParts("Route")); } - public StudyTreatmentProductTable(Domain domain, DbSchema dbSchema, UserSchema schema, @Nullable ContainerFilter filter) + public StudyTreatmentProductTable(Domain domain, UserSchema schema, @Nullable ContainerFilter filter) { - super(domain, dbSchema, schema, filter); + super(domain, schema, filter); setName(StudyQuerySchema.TREATMENT_PRODUCT_MAP_TABLE_NAME); setDescription("Contains one row per study treatment product"); diff --git a/study/src/org/labkey/study/query/studydesign/StudyTreatmentTable.java b/study/src/org/labkey/study/query/studydesign/StudyTreatmentTable.java index 081155e7..7f511c13 100644 --- a/study/src/org/labkey/study/query/studydesign/StudyTreatmentTable.java +++ b/study/src/org/labkey/study/query/studydesign/StudyTreatmentTable.java @@ -51,9 +51,9 @@ public class StudyTreatmentTable extends DefaultStudyDesignTable defaultVisibleColumns.add(FieldKey.fromParts("DescriptionRendererType")); } - public StudyTreatmentTable(Domain domain, DbSchema dbSchema, UserSchema schema, @Nullable ContainerFilter filter) + public StudyTreatmentTable(Domain domain, UserSchema schema, @Nullable ContainerFilter filter) { - super(domain, dbSchema, schema, filter); + super(domain, schema, filter); setName(StudyQuerySchema.TREATMENT_TABLE_NAME); setDescription("Contains one row per study treatment"); From 5f60a2ef5703335190d46df7418f60243e547c67 Mon Sep 17 00:00:00 2001 From: Kevin Krouse Date: Tue, 22 Jul 2014 20:47:10 +0000 Subject: [PATCH 063/218] Issue 17998: add better max length validation everywhere Issue 17385: DefaultQueryUpdateService.updateRow() should validate length - attempt to consolidate column-level validation used by DataIterator, QueryUpdateService, and Table.insert/.update. - OntologyManager.insertTabDelimited() yet to be consolidated but now checks string length appropriately --- .../query/studydesign/StudyDesignLookupBaseTable.java | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/study/src/org/labkey/study/query/studydesign/StudyDesignLookupBaseTable.java b/study/src/org/labkey/study/query/studydesign/StudyDesignLookupBaseTable.java index 3e450051..17430581 100644 --- a/study/src/org/labkey/study/query/studydesign/StudyDesignLookupBaseTable.java +++ b/study/src/org/labkey/study/query/studydesign/StudyDesignLookupBaseTable.java @@ -144,17 +144,6 @@ protected Map deleteRow(User user, Container container, Map row) throws ValidationException { // TODO: add validation that the same key value doesn't already exist at the project level - - // Issue 18313 - for (ColumnInfo col : getRealTable().getColumns()) - { - if (col != null && row.get(col.getName()) != null && col.getJdbcType() == JdbcType.VARCHAR && col.getScale() > 0) - { - String value = row.get(col.getName()).toString(); - if (value != null && value.length() > col.getScale()) - throw new ValidationException("Value is too long for field " + col.getLabel() + ", a maximum length of " + col.getScale() + " is allowed."); - } - } } } From 1bc35da1b009b544be217c3b9adc57ef464a4227 Mon Sep 17 00:00:00 2001 From: Adam Rauch Date: Wed, 30 Jul 2014 05:11:09 +0000 Subject: [PATCH 064/218] Issue 21128: Unique Constraint Violations on Study tables Synchronize to avoid race conditions in StudyDesign ensureDomain and StorageProvisioner createStorageTable. I think these prolbems have been lurking for a while, but somehow my caching changes made them more prominent. --- .../AbstractStudyDesignDomainKind.java | 52 +++++++++---------- 1 file changed, 25 insertions(+), 27 deletions(-) diff --git a/study/src/org/labkey/study/query/studydesign/AbstractStudyDesignDomainKind.java b/study/src/org/labkey/study/query/studydesign/AbstractStudyDesignDomainKind.java index 6cb11c2e..51ca0270 100644 --- a/study/src/org/labkey/study/query/studydesign/AbstractStudyDesignDomainKind.java +++ b/study/src/org/labkey/study/query/studydesign/AbstractStudyDesignDomainKind.java @@ -15,13 +15,7 @@ */ package org.labkey.study.query.studydesign; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; -import org.labkey.api.audit.AbstractAuditTypeProvider; -import org.labkey.api.collections.CaseInsensitiveHashSet; import org.labkey.api.data.Container; -import org.labkey.api.data.ContainerManager; -import org.labkey.api.data.DbSchema; import org.labkey.api.data.DbSchemaType; import org.labkey.api.data.DbScope; import org.labkey.api.data.JdbcType; @@ -29,24 +23,18 @@ import org.labkey.api.data.SQLFragment; import org.labkey.api.exp.Handler; import org.labkey.api.exp.Lsid; -import org.labkey.api.exp.PropertyDescriptor; -import org.labkey.api.exp.PropertyType; import org.labkey.api.exp.XarContext; import org.labkey.api.exp.XarFormatException; import org.labkey.api.exp.api.ExperimentUrls; import org.labkey.api.exp.property.AbstractDomainKind; import org.labkey.api.exp.property.Domain; -import org.labkey.api.exp.property.DomainKind; -import org.labkey.api.exp.property.DomainProperty; import org.labkey.api.exp.property.PropertyService; import org.labkey.api.exp.xar.LsidUtils; -import org.labkey.api.gwt.client.model.GWTDomain; import org.labkey.api.query.QueryAction; import org.labkey.api.query.QueryService; import org.labkey.api.security.User; import org.labkey.api.util.PageFlowUtil; import org.labkey.api.view.ActionURL; -import org.labkey.api.view.NavTree; import org.labkey.api.writer.ContainerUser; import org.labkey.study.StudySchema; import org.labkey.study.query.StudyQuerySchema; @@ -61,17 +49,22 @@ */ public abstract class AbstractStudyDesignDomainKind extends AbstractDomainKind { - private static String XAR_SUBSTITUTION_SCHEMA_NAME = "SchemaName"; - private static String XAR_SUBSTITUTION_TABLE_NAME = "TableName"; + private static final String XAR_SUBSTITUTION_SCHEMA_NAME = "SchemaName"; + private static final String XAR_SUBSTITUTION_TABLE_NAME = "TableName"; - private static String DOMAIN_NAMESPACE_PREFIX_TEMPLATE = "%s-${SchemaName}"; - private static String DOMAIN_LSID_TEMPLATE = "${FolderLSIDBase}:${TableName}"; + private static final String DOMAIN_NAMESPACE_PREFIX_TEMPLATE = "%s-${SchemaName}"; + private static final String DOMAIN_LSID_TEMPLATE = "${FolderLSIDBase}:${TableName}"; private static final Set _baseFields; - private Set _standardFields = new LinkedHashSet<>(); + + private final Set _standardFields = new LinkedHashSet<>(); private final String _tableName; - static { + // Prevent race conditions, #21128. Ideally, this would be one lock per DomainKind, but we new these up all over the place (??) + private static final Object ENSURE_DOMAIN_LOCK = new Object(); + + static + { Set baseFields = new LinkedHashSet<>(); baseFields.add(createFieldSpec("Container", JdbcType.VARCHAR).setEntityId(true).setNullable(false)); baseFields.add(createFieldSpec("Created", JdbcType.TIMESTAMP)); @@ -93,21 +86,26 @@ public AbstractStudyDesignDomainKind(String tableName, Set public Domain ensureDomain(Container container, User user, String tableName) { String domainURI = generateDomainURI(StudyQuerySchema.SCHEMA_NAME, tableName, container, null); - Domain domain = PropertyService.get().getDomain(container, domainURI); - if (domain == null) + synchronized (ENSURE_DOMAIN_LOCK) { - try { + Domain domain = PropertyService.get().getDomain(container, domainURI); - domain = PropertyService.get().createDomain(container, domainURI, tableName); - domain.save(user); - } - catch (Exception e) + if (domain == null) { - throw new RuntimeException(e); + try + { + domain = PropertyService.get().createDomain(container, domainURI, tableName); + domain.save(user); + } + catch (Exception e) + { + throw new RuntimeException(e); + } } + + return domain; } - return domain; } protected abstract String getNamespacePrefix(); From b0123f53998fd31ed2f1a1c49edda61d58d44d0b Mon Sep 17 00:00:00 2001 From: Adam Rauch Date: Thu, 31 Jul 2014 00:04:22 +0000 Subject: [PATCH 065/218] Eliminate a few warnings, be mroe explicit about base fields --- .../AbstractStudyDesignDomainKind.java | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/study/src/org/labkey/study/query/studydesign/AbstractStudyDesignDomainKind.java b/study/src/org/labkey/study/query/studydesign/AbstractStudyDesignDomainKind.java index 51ca0270..533d77e5 100644 --- a/study/src/org/labkey/study/query/studydesign/AbstractStudyDesignDomainKind.java +++ b/study/src/org/labkey/study/query/studydesign/AbstractStudyDesignDomainKind.java @@ -55,14 +55,11 @@ public abstract class AbstractStudyDesignDomainKind extends AbstractDomainKind private static final String DOMAIN_NAMESPACE_PREFIX_TEMPLATE = "%s-${SchemaName}"; private static final String DOMAIN_LSID_TEMPLATE = "${FolderLSIDBase}:${TableName}"; - private static final Set _baseFields; - - private final Set _standardFields = new LinkedHashSet<>(); - private final String _tableName; - - // Prevent race conditions, #21128. Ideally, this would be one lock per DomainKind, but we new these up all over the place (??) + // Prevent race conditions, #21128. TODO: Ideally, this would be one lock per DomainKind, but we new these up all over the place, #21199. private static final Object ENSURE_DOMAIN_LOCK = new Object(); + private static final Set BASE_FIELDS; + static { Set baseFields = new LinkedHashSet<>(); @@ -72,14 +69,15 @@ public abstract class AbstractStudyDesignDomainKind extends AbstractDomainKind baseFields.add(createFieldSpec("Modified", JdbcType.TIMESTAMP)); baseFields.add(createFieldSpec("ModifiedBy", JdbcType.INTEGER)); - _baseFields = Collections.unmodifiableSet(baseFields); + BASE_FIELDS = Collections.unmodifiableSet(baseFields); } + private final Set _standardFields = new LinkedHashSet<>(BASE_FIELDS); + private final String _tableName; + public AbstractStudyDesignDomainKind(String tableName, Set standardFields) { _tableName = tableName; - - _standardFields.addAll(_baseFields); _standardFields.addAll(standardFields); } From 2fe0898bb575f846829530799871d803f241879c Mon Sep 17 00:00:00 2001 From: Nick Kerr Date: Fri, 19 Dec 2014 02:56:13 +0000 Subject: [PATCH 066/218] Support external client dependencies in view, modules, and libraries. --- .../study/view/studydesign/assayScheduleWebpart.jsp | 2 +- .../view/studydesign/immunizationScheduleWebpart.jsp | 4 ++-- .../labkey/study/view/studydesign/manageAssaySchedule.jsp | 6 +++--- .../labkey/study/view/studydesign/manageStudyProducts.jsp | 8 ++++---- .../labkey/study/view/studydesign/manageTreatments.jsp | 8 ++++---- .../study/view/studydesign/vaccineDesignWebpart.jsp | 6 +++--- 6 files changed, 17 insertions(+), 17 deletions(-) diff --git a/study/src/org/labkey/study/view/studydesign/assayScheduleWebpart.jsp b/study/src/org/labkey/study/view/studydesign/assayScheduleWebpart.jsp index a9a9a9ac..619911ee 100644 --- a/study/src/org/labkey/study/view/studydesign/assayScheduleWebpart.jsp +++ b/study/src/org/labkey/study/view/studydesign/assayScheduleWebpart.jsp @@ -35,7 +35,7 @@ public LinkedHashSet getClientDependencies() { LinkedHashSet resources = new LinkedHashSet<>(); - resources.add(ClientDependency.fromFilePath("study/StudyVaccineDesign.css")); + resources.add(ClientDependency.fromPath("study/StudyVaccineDesign.css")); return resources; } %> diff --git a/study/src/org/labkey/study/view/studydesign/immunizationScheduleWebpart.jsp b/study/src/org/labkey/study/view/studydesign/immunizationScheduleWebpart.jsp index c8da3054..f35fd7c7 100644 --- a/study/src/org/labkey/study/view/studydesign/immunizationScheduleWebpart.jsp +++ b/study/src/org/labkey/study/view/studydesign/immunizationScheduleWebpart.jsp @@ -39,8 +39,8 @@ public LinkedHashSet getClientDependencies() { LinkedHashSet resources = new LinkedHashSet<>(); - resources.add(ClientDependency.fromFilePath("study/StudyVaccineDesign.js")); - resources.add(ClientDependency.fromFilePath("study/StudyVaccineDesign.css")); + resources.add(ClientDependency.fromPath("study/StudyVaccineDesign.js")); + resources.add(ClientDependency.fromPath("study/StudyVaccineDesign.css")); return resources; } %> diff --git a/study/src/org/labkey/study/view/studydesign/manageAssaySchedule.jsp b/study/src/org/labkey/study/view/studydesign/manageAssaySchedule.jsp index 189ee25a..d92b1d9e 100644 --- a/study/src/org/labkey/study/view/studydesign/manageAssaySchedule.jsp +++ b/study/src/org/labkey/study/view/studydesign/manageAssaySchedule.jsp @@ -34,9 +34,9 @@ public LinkedHashSet getClientDependencies() { LinkedHashSet resources = new LinkedHashSet<>(); - resources.add(ClientDependency.fromFilePath("Ext4ClientApi")); - resources.add(ClientDependency.fromFilePath("study/StudyVaccineDesign.js")); - resources.add(ClientDependency.fromFilePath("dataview/DataViewsPanel.css")); + resources.add(ClientDependency.fromPath("Ext4ClientApi")); + resources.add(ClientDependency.fromPath("study/StudyVaccineDesign.js")); + resources.add(ClientDependency.fromPath("dataview/DataViewsPanel.css")); return resources; } %> diff --git a/study/src/org/labkey/study/view/studydesign/manageStudyProducts.jsp b/study/src/org/labkey/study/view/studydesign/manageStudyProducts.jsp index c6de93c0..4a2c5e79 100644 --- a/study/src/org/labkey/study/view/studydesign/manageStudyProducts.jsp +++ b/study/src/org/labkey/study/view/studydesign/manageStudyProducts.jsp @@ -25,10 +25,10 @@ public LinkedHashSet getClientDependencies() { LinkedHashSet resources = new LinkedHashSet<>(); - resources.add(ClientDependency.fromFilePath("Ext4ClientApi")); - resources.add(ClientDependency.fromFilePath("study/StudyVaccineDesign.js")); - resources.add(ClientDependency.fromFilePath("dataview/DataViewsPanel.css")); - resources.add(ClientDependency.fromFilePath("study/StudyVaccineDesign.css")); + resources.add(ClientDependency.fromPath("Ext4ClientApi")); + resources.add(ClientDependency.fromPath("study/StudyVaccineDesign.js")); + resources.add(ClientDependency.fromPath("dataview/DataViewsPanel.css")); + resources.add(ClientDependency.fromPath("study/StudyVaccineDesign.css")); return resources; } %> diff --git a/study/src/org/labkey/study/view/studydesign/manageTreatments.jsp b/study/src/org/labkey/study/view/studydesign/manageTreatments.jsp index 0300bda7..b4985d26 100644 --- a/study/src/org/labkey/study/view/studydesign/manageTreatments.jsp +++ b/study/src/org/labkey/study/view/studydesign/manageTreatments.jsp @@ -34,10 +34,10 @@ public LinkedHashSet getClientDependencies() { LinkedHashSet resources = new LinkedHashSet<>(); - resources.add(ClientDependency.fromFilePath("Ext4ClientApi")); - resources.add(ClientDependency.fromFilePath("study/StudyVaccineDesign.js")); - resources.add(ClientDependency.fromFilePath("dataview/DataViewsPanel.css")); - resources.add(ClientDependency.fromFilePath("study/StudyVaccineDesign.css")); + resources.add(ClientDependency.fromPath("Ext4ClientApi")); + resources.add(ClientDependency.fromPath("study/StudyVaccineDesign.js")); + resources.add(ClientDependency.fromPath("dataview/DataViewsPanel.css")); + resources.add(ClientDependency.fromPath("study/StudyVaccineDesign.css")); return resources; } %> diff --git a/study/src/org/labkey/study/view/studydesign/vaccineDesignWebpart.jsp b/study/src/org/labkey/study/view/studydesign/vaccineDesignWebpart.jsp index bbd4d57e..87f78abe 100644 --- a/study/src/org/labkey/study/view/studydesign/vaccineDesignWebpart.jsp +++ b/study/src/org/labkey/study/view/studydesign/vaccineDesignWebpart.jsp @@ -35,9 +35,9 @@ public LinkedHashSet getClientDependencies() { LinkedHashSet resources = new LinkedHashSet<>(); - resources.add(ClientDependency.fromFilePath("Ext4ClientApi")); - resources.add(ClientDependency.fromFilePath("study/StudyVaccineDesign.js")); - resources.add(ClientDependency.fromFilePath("study/StudyVaccineDesign.css")); + resources.add(ClientDependency.fromPath("Ext4ClientApi")); + resources.add(ClientDependency.fromPath("study/StudyVaccineDesign.js")); + resources.add(ClientDependency.fromPath("study/StudyVaccineDesign.css")); return resources; } %> From 4c5aefffbba2b386d28b00fa888a31c057c9696c Mon Sep 17 00:00:00 2001 From: Kevin Krouse Date: Fri, 19 Dec 2014 06:01:23 +0000 Subject: [PATCH 067/218] Spec 22011: ETL performance - add config parameter map to DataIteratorContext and QueryUpdateService methods - set logger on DataIteratorContext for some background jobs - still todo: thread logger into trigger script environment --- study/src/org/labkey/study/model/TreatmentManager.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/study/src/org/labkey/study/model/TreatmentManager.java b/study/src/org/labkey/study/model/TreatmentManager.java index e251cd2e..88538bf2 100644 --- a/study/src/org/labkey/study/model/TreatmentManager.java +++ b/study/src/org/labkey/study/model/TreatmentManager.java @@ -208,7 +208,7 @@ public void deleteTreatment(Container container, User user, int rowId) List> keys = new ArrayList<>(); ColumnInfo treatmentPk = treatmentTable.getColumn(FieldKey.fromParts("RowId")); keys.add(Collections.singletonMap(treatmentPk.getName(), rowId)); - qus.deleteRows(user, container, keys, null); + qus.deleteRows(user, container, keys, null, null); } transaction.commit(); @@ -241,7 +241,7 @@ public void deleteStudyProduct(Container container, User user, int rowId) List> keys = new ArrayList<>(); ColumnInfo productPk = productTable.getColumn(FieldKey.fromParts("RowId")); keys.add(Collections.singletonMap(productPk.getName(), rowId)); - qus.deleteRows(user, container, keys, null); + qus.deleteRows(user, container, keys, null, null); } else throw new IllegalStateException("Could not find table: " + StudyQuerySchema.PRODUCT_TABLE_NAME); @@ -274,7 +274,7 @@ public void deleteProductAntigens(Container container, User user, int rowId) thr keys.add(Collections.singletonMap(productAntigenPk.getName(), productAntigenId)); } - qus.deleteRows(user, container, keys, null); + qus.deleteRows(user, container, keys, null, null); } else throw new IllegalStateException("Could not find query update service for table: " + StudyQuerySchema.PRODUCT_ANTIGEN_TABLE_NAME); @@ -301,7 +301,7 @@ public void deleteTreatmentProductMap(Container container, User user, SimpleFilt keys.add(Collections.singletonMap(productMapPk.getName(), productMapId)); } - qus.deleteRows(user, container, keys, null); + qus.deleteRows(user, container, keys, null, null); } else throw new IllegalStateException("Could not find query update service for table: " + StudyQuerySchema.TREATMENT_PRODUCT_MAP_TABLE_NAME); From 2f2f136cfd5c44f322aa5102a0666f42cbe1847a Mon Sep 17 00:00:00 2001 From: Josh Eckels Date: Mon, 29 Dec 2014 21:51:46 +0000 Subject: [PATCH 068/218] Fix NPE in ClientAPITest and others where WebPartFactory.getWebPartView --- .../study/view/studydesign/AssayScheduleWebpartFactory.java | 3 ++- .../view/studydesign/ImmunizationScheduleWebpartFactory.java | 3 ++- .../study/view/studydesign/VaccineDesignWebpartFactory.java | 3 ++- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/study/src/org/labkey/study/view/studydesign/AssayScheduleWebpartFactory.java b/study/src/org/labkey/study/view/studydesign/AssayScheduleWebpartFactory.java index b70ded26..566b5f32 100644 --- a/study/src/org/labkey/study/view/studydesign/AssayScheduleWebpartFactory.java +++ b/study/src/org/labkey/study/view/studydesign/AssayScheduleWebpartFactory.java @@ -15,6 +15,7 @@ */ package org.labkey.study.view.studydesign; +import org.jetbrains.annotations.NotNull; import org.labkey.api.data.Container; import org.labkey.api.study.Study; import org.labkey.api.study.TimepointType; @@ -43,7 +44,7 @@ public AssayScheduleWebpartFactory() } @Override - public WebPartView getWebPartView(ViewContext portalCtx, Portal.WebPart webPart) throws Exception + public WebPartView getWebPartView(@NotNull ViewContext portalCtx, @NotNull Portal.WebPart webPart) { JspView view = new JspView<>("/org/labkey/study/view/studydesign/assayScheduleWebpart.jsp", webPart); view.setTitle(NAME); diff --git a/study/src/org/labkey/study/view/studydesign/ImmunizationScheduleWebpartFactory.java b/study/src/org/labkey/study/view/studydesign/ImmunizationScheduleWebpartFactory.java index 1cd6d851..b2abd3bb 100644 --- a/study/src/org/labkey/study/view/studydesign/ImmunizationScheduleWebpartFactory.java +++ b/study/src/org/labkey/study/view/studydesign/ImmunizationScheduleWebpartFactory.java @@ -15,6 +15,7 @@ */ package org.labkey.study.view.studydesign; +import org.jetbrains.annotations.NotNull; import org.labkey.api.data.Container; import org.labkey.api.study.Study; import org.labkey.api.study.TimepointType; @@ -44,7 +45,7 @@ public ImmunizationScheduleWebpartFactory() } @Override - public WebPartView getWebPartView(ViewContext portalCtx, Portal.WebPart webPart) throws Exception + public WebPartView getWebPartView(@NotNull ViewContext portalCtx, @NotNull Portal.WebPart webPart) { JspView view = new JspView<>("/org/labkey/study/view/studydesign/immunizationScheduleWebpart.jsp", webPart); view.setTitle(NAME); diff --git a/study/src/org/labkey/study/view/studydesign/VaccineDesignWebpartFactory.java b/study/src/org/labkey/study/view/studydesign/VaccineDesignWebpartFactory.java index 3be3902b..7028a575 100644 --- a/study/src/org/labkey/study/view/studydesign/VaccineDesignWebpartFactory.java +++ b/study/src/org/labkey/study/view/studydesign/VaccineDesignWebpartFactory.java @@ -15,6 +15,7 @@ */ package org.labkey.study.view.studydesign; +import org.jetbrains.annotations.NotNull; import org.labkey.api.view.BaseWebPartFactory; import org.labkey.api.view.JspView; import org.labkey.api.view.Portal; @@ -35,7 +36,7 @@ public VaccineDesignWebpartFactory() } @Override - public WebPartView getWebPartView(ViewContext portalCtx, Portal.WebPart webPart) throws Exception + public WebPartView getWebPartView(@NotNull ViewContext portalCtx, @NotNull Portal.WebPart webPart) { JspView view = new JspView<>("/org/labkey/study/view/studydesign/vaccineDesignWebpart.jsp", webPart); view.setTitle(NAME); From b97f9e557616b6eb996909477fc9022b2fc27a6e Mon Sep 17 00:00:00 2001 From: Karl Lum Date: Tue, 30 Dec 2014 01:51:45 +0000 Subject: [PATCH 069/218] 22217: **AbstractSpecimenTransformTask does not fill in required fields ldms_lab_code and title 21946: **DefaultStudyDesignTable should not call OntologyManager.getPropertyDescriptor() 21738: Questionable code in MessageDigest --- .../studydesign/DefaultStudyDesignTable.java | 35 +++++++++++-------- 1 file changed, 20 insertions(+), 15 deletions(-) diff --git a/study/src/org/labkey/study/query/studydesign/DefaultStudyDesignTable.java b/study/src/org/labkey/study/query/studydesign/DefaultStudyDesignTable.java index 7b372652..ac8157f4 100644 --- a/study/src/org/labkey/study/query/studydesign/DefaultStudyDesignTable.java +++ b/study/src/org/labkey/study/query/studydesign/DefaultStudyDesignTable.java @@ -34,6 +34,7 @@ import org.labkey.api.exp.PropertyType; import org.labkey.api.exp.api.StorageProvisioner; import org.labkey.api.exp.property.Domain; +import org.labkey.api.exp.property.DomainProperty; import org.labkey.api.query.DefaultQueryUpdateService; import org.labkey.api.query.FieldKey; import org.labkey.api.query.FilteredTable; @@ -73,24 +74,28 @@ public DefaultStudyDesignTable(Domain domain, UserSchema schema) String propertyURI = col.getPropertyURI(); if (null != propertyURI) { - PropertyDescriptor pd = OntologyManager.getPropertyDescriptor(propertyURI, schema.getContainer()); - if (pd != null) + DomainProperty property = domain.getPropertyByURI(propertyURI); + if (property != null) { - if (pd.getLookupQuery() != null) - col.setFk(new PdLookupForeignKey(schema.getUser(), pd, schema.getContainer())); - - if (pd.getPropertyType() == PropertyType.MULTI_LINE) + PropertyDescriptor pd = property.getPropertyDescriptor(); + if (pd != null) { - col.setDisplayColumnFactory(new DisplayColumnFactory() { - public DisplayColumn createRenderer(ColumnInfo colInfo) - { - DataColumn dc = new DataColumn(colInfo); - dc.setPreserveNewlines(true); - return dc; - } - }); + if (pd.getLookupQuery() != null) + col.setFk(new PdLookupForeignKey(schema.getUser(), pd, schema.getContainer())); + + if (pd.getPropertyType() == PropertyType.MULTI_LINE) + { + col.setDisplayColumnFactory(new DisplayColumnFactory() { + public DisplayColumn createRenderer(ColumnInfo colInfo) + { + DataColumn dc = new DataColumn(colInfo); + dc.setPreserveNewlines(true); + return dc; + } + }); + } + col.setName(pd.getName()); } - col.setName(pd.getName()); } } } From e9cf7a0b6e4e474b9a3eb71e3db036097b20b0ea Mon Sep 17 00:00:00 2001 From: Adam Rauch Date: Wed, 31 Dec 2014 23:47:19 +0000 Subject: [PATCH 070/218] Update licenses --- .../study/view/studydesign/AssayScheduleWebpartFactory.java | 2 +- .../study/view/studydesign/VaccineDesignWebpartFactory.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/study/src/org/labkey/study/view/studydesign/AssayScheduleWebpartFactory.java b/study/src/org/labkey/study/view/studydesign/AssayScheduleWebpartFactory.java index 566b5f32..f05d0343 100644 --- a/study/src/org/labkey/study/view/studydesign/AssayScheduleWebpartFactory.java +++ b/study/src/org/labkey/study/view/studydesign/AssayScheduleWebpartFactory.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013 LabKey Corporation + * Copyright (c) 2013-2014 LabKey Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/study/src/org/labkey/study/view/studydesign/VaccineDesignWebpartFactory.java b/study/src/org/labkey/study/view/studydesign/VaccineDesignWebpartFactory.java index 7028a575..b9ef2240 100644 --- a/study/src/org/labkey/study/view/studydesign/VaccineDesignWebpartFactory.java +++ b/study/src/org/labkey/study/view/studydesign/VaccineDesignWebpartFactory.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013 LabKey Corporation + * Copyright (c) 2013-2014 LabKey Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. From b043c296dc99b429daf3019823f9bac7280acbf9 Mon Sep 17 00:00:00 2001 From: Cory Nathe Date: Fri, 27 Feb 2015 19:03:13 +0000 Subject: [PATCH 071/218] Issue 22665: **StudyDesignController should use MutatingApiAction --- .../study/controllers/StudyDesignController.java | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/study/src/org/labkey/study/controllers/StudyDesignController.java b/study/src/org/labkey/study/controllers/StudyDesignController.java index d8c72695..1b3aeb43 100644 --- a/study/src/org/labkey/study/controllers/StudyDesignController.java +++ b/study/src/org/labkey/study/controllers/StudyDesignController.java @@ -18,6 +18,7 @@ import org.labkey.api.action.ApiAction; import org.labkey.api.action.ApiResponse; import org.labkey.api.action.ApiSimpleResponse; +import org.labkey.api.action.MutatingApiAction; import org.labkey.api.action.SimpleViewAction; import org.labkey.api.data.DbScope; import org.labkey.api.security.ActionNames; @@ -292,7 +293,7 @@ public ApiResponse execute(Object form, BindException errors) throws Exception } @RequiresPermissionClass(DeletePermission.class) - public class DeleteTreatmentAction extends ApiAction + public class DeleteTreatmentAction extends MutatingApiAction { @Override public ApiResponse execute(IdForm form, BindException errors) throws Exception @@ -307,7 +308,7 @@ public ApiResponse execute(IdForm form, BindException errors) throws Exception } @RequiresPermissionClass(DeletePermission.class) - public class DeleteStudyProductAction extends ApiAction + public class DeleteStudyProductAction extends MutatingApiAction { @Override public ApiResponse execute(IdForm form, BindException errors) throws Exception @@ -322,7 +323,7 @@ public ApiResponse execute(IdForm form, BindException errors) throws Exception } @RequiresPermissionClass(UpdatePermission.class) - public class UpdateStudyTreatmentScheduleAction extends ApiAction + public class UpdateStudyTreatmentScheduleAction extends MutatingApiAction { @Override public void validateForm(StudyTreatmentSchedule form, Errors errors) @@ -407,7 +408,7 @@ private void updateTreatmentVisitMapping(StudyTreatmentSchedule form, CohortImpl } @RequiresPermissionClass(DeletePermission.class) - public class DeleteCohortAction extends ApiAction + public class DeleteCohortAction extends MutatingApiAction { @Override public ApiResponse execute(IdForm form, BindException errors) throws Exception @@ -437,7 +438,7 @@ public ApiResponse execute(IdForm form, BindException errors) throws Exception } @RequiresPermissionClass(UpdatePermission.class) - public class CreateVisitAction extends ApiAction + public class CreateVisitAction extends MutatingApiAction { @Override public void validateForm(VisitForm form, Errors errors) @@ -475,7 +476,7 @@ public ApiResponse execute(VisitForm form, BindException errors) throws Exceptio } @RequiresPermissionClass(UpdatePermission.class) - public class UpdateAssayPlanAction extends ApiAction + public class UpdateAssayPlanAction extends MutatingApiAction { @Override public ApiResponse execute(AssayPlanForm form, BindException errors) throws Exception From fadc262972b8df1441897b8af466693222504836 Mon Sep 17 00:00:00 2001 From: Adam Rauch Date: Sun, 1 Mar 2015 00:18:28 +0000 Subject: [PATCH 072/218] Update licenses --- .../src/org/labkey/study/controllers/StudyDesignController.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/study/src/org/labkey/study/controllers/StudyDesignController.java b/study/src/org/labkey/study/controllers/StudyDesignController.java index 1b3aeb43..86599c1f 100644 --- a/study/src/org/labkey/study/controllers/StudyDesignController.java +++ b/study/src/org/labkey/study/controllers/StudyDesignController.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014 LabKey Corporation + * Copyright (c) 2014-2015 LabKey Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. From 0521e40cc87807953fb214d12ee95636890199b6 Mon Sep 17 00:00:00 2001 From: Matthew Bellew Date: Tue, 23 Jun 2015 16:36:40 +0000 Subject: [PATCH 073/218] 22804: NullPointerException in org.labkey.api.query.FilteredTable.() refactor to remove StorageProvisioner.createTableInfo(domain) from super() call in constructor --- .../studydesign/DefaultStudyDesignTable.java | 19 ++++++++++++++----- .../studydesign/StudyProductAntigenTable.java | 18 ++++++++++++++++-- .../query/studydesign/StudyProductTable.java | 15 +++++++++++++-- .../StudyTreatmentProductTable.java | 16 ++++++++++++++-- .../studydesign/StudyTreatmentTable.java | 16 ++++++++++++++-- 5 files changed, 71 insertions(+), 13 deletions(-) diff --git a/study/src/org/labkey/study/query/studydesign/DefaultStudyDesignTable.java b/study/src/org/labkey/study/query/studydesign/DefaultStudyDesignTable.java index ac8157f4..80b78fc5 100644 --- a/study/src/org/labkey/study/query/studydesign/DefaultStudyDesignTable.java +++ b/study/src/org/labkey/study/query/studydesign/DefaultStudyDesignTable.java @@ -58,10 +58,10 @@ public class DefaultStudyDesignTable extends FilteredTable protected List _defaultVisibleColumns = new ArrayList<>(); private Domain _domain; - public DefaultStudyDesignTable(Domain domain, UserSchema schema) - { - super(StorageProvisioner.createTableInfo(domain), schema); + protected DefaultStudyDesignTable(Domain domain, TableInfo storageTableInfo, UserSchema schema) + { + super(storageTableInfo, schema); _domain = domain; for (ColumnInfo baseColumn : getRealTable().getColumns()) @@ -126,13 +126,22 @@ public DisplayColumn createRenderer(ColumnInfo colInfo) initColumns(); } - public DefaultStudyDesignTable(Domain domain, UserSchema schema, @Nullable ContainerFilter containerFilter) + + protected DefaultStudyDesignTable(Domain domain, TableInfo storageTableInfo, UserSchema schema, @Nullable ContainerFilter containerFilter) { - this(domain, schema); + this(domain, storageTableInfo, schema); if (null != containerFilter) _setContainerFilter(containerFilter); } + + public static DefaultStudyDesignTable create(Domain domain, UserSchema schema, @Nullable ContainerFilter containerFilter) + { + TableInfo storageTableInfo = StorageProvisioner.createTableInfo(domain); + return new DefaultStudyDesignTable(domain, storageTableInfo, schema, containerFilter); + } + + @Override public List getDefaultVisibleColumns() { diff --git a/study/src/org/labkey/study/query/studydesign/StudyProductAntigenTable.java b/study/src/org/labkey/study/query/studydesign/StudyProductAntigenTable.java index d9529cd9..ce760234 100644 --- a/study/src/org/labkey/study/query/studydesign/StudyProductAntigenTable.java +++ b/study/src/org/labkey/study/query/studydesign/StudyProductAntigenTable.java @@ -21,6 +21,7 @@ import org.labkey.api.data.ContainerFilter; import org.labkey.api.data.DbSchema; import org.labkey.api.data.TableInfo; +import org.labkey.api.exp.api.StorageProvisioner; import org.labkey.api.exp.property.Domain; import org.labkey.api.query.FieldKey; import org.labkey.api.query.LookupForeignKey; @@ -50,14 +51,27 @@ public class StudyProductAntigenTable extends DefaultStudyDesignTable defaultVisibleColumns.add(FieldKey.fromParts("Sequence")); } - public StudyProductAntigenTable(Domain domain, UserSchema schema, @Nullable ContainerFilter containerFilter) + + public static StudyProductAntigenTable create(Domain domain, UserSchema schema, @Nullable ContainerFilter filter) + { + TableInfo storageTableInfo = StorageProvisioner.createTableInfo(domain); + if (null == storageTableInfo) + { + throw new IllegalStateException("Could not create provisioned table for domain: " + domain.getTypeURI()); + } + return new StudyProductAntigenTable(domain, storageTableInfo, schema, filter); + } + + + private StudyProductAntigenTable(Domain domain, TableInfo storageTableInfo, UserSchema schema, @Nullable ContainerFilter containerFilter) { - super(domain, schema, containerFilter); + super(domain, storageTableInfo, schema, containerFilter); setName(StudyQuerySchema.PRODUCT_ANTIGEN_TABLE_NAME); setDescription("Contains one row per study product antigen"); } + @Override protected void initColumn(ColumnInfo col) { diff --git a/study/src/org/labkey/study/query/studydesign/StudyProductTable.java b/study/src/org/labkey/study/query/studydesign/StudyProductTable.java index a65b8c2d..d1364d36 100644 --- a/study/src/org/labkey/study/query/studydesign/StudyProductTable.java +++ b/study/src/org/labkey/study/query/studydesign/StudyProductTable.java @@ -21,6 +21,7 @@ import org.labkey.api.data.ContainerFilter; import org.labkey.api.data.DbSchema; import org.labkey.api.data.TableInfo; +import org.labkey.api.exp.api.StorageProvisioner; import org.labkey.api.exp.property.Domain; import org.labkey.api.query.FieldKey; import org.labkey.api.query.LookupForeignKey; @@ -48,9 +49,19 @@ public class StudyProductTable extends DefaultStudyDesignTable defaultVisibleColumns.add(FieldKey.fromParts("Type")); } - public StudyProductTable(Domain domain, UserSchema schema, @Nullable ContainerFilter containerFilter) + public static StudyProductTable create(Domain domain, UserSchema schema, @com.drew.lang.annotations.Nullable ContainerFilter containerFilter) { - super(domain, schema, containerFilter); + TableInfo storageTableInfo = StorageProvisioner.createTableInfo(domain); + if (null == storageTableInfo) + { + throw new IllegalStateException("Could not create provisioned table for domain: " + domain.getTypeURI()); + } + return new StudyProductTable(domain, storageTableInfo, schema, containerFilter); + } + + private StudyProductTable(Domain domain, TableInfo storageTableInfo, UserSchema schema, @Nullable ContainerFilter containerFilter) + { + super(domain, storageTableInfo, schema, containerFilter); setName(StudyQuerySchema.PRODUCT_TABLE_NAME); setDescription("Contains one row per study product"); diff --git a/study/src/org/labkey/study/query/studydesign/StudyTreatmentProductTable.java b/study/src/org/labkey/study/query/studydesign/StudyTreatmentProductTable.java index a1316673..55fceffa 100644 --- a/study/src/org/labkey/study/query/studydesign/StudyTreatmentProductTable.java +++ b/study/src/org/labkey/study/query/studydesign/StudyTreatmentProductTable.java @@ -20,6 +20,7 @@ import org.labkey.api.data.ContainerFilter; import org.labkey.api.data.DbSchema; import org.labkey.api.data.TableInfo; +import org.labkey.api.exp.api.StorageProvisioner; import org.labkey.api.exp.property.Domain; import org.labkey.api.query.FieldKey; import org.labkey.api.query.LookupForeignKey; @@ -46,14 +47,25 @@ public class StudyTreatmentProductTable extends DefaultStudyDesignTable defaultVisibleColumns.add(FieldKey.fromParts("Route")); } - public StudyTreatmentProductTable(Domain domain, UserSchema schema, @Nullable ContainerFilter filter) + public static StudyTreatmentProductTable create(Domain domain, UserSchema schema, @Nullable ContainerFilter filter) { - super(domain, schema, filter); + TableInfo storageTableInfo = StorageProvisioner.createTableInfo(domain); + if (null == storageTableInfo) + { + throw new IllegalStateException("Could not create provisioned table for domain: " + domain.getTypeURI()); + } + return new StudyTreatmentProductTable(domain, storageTableInfo, schema, filter); + } + + private StudyTreatmentProductTable(Domain domain, TableInfo storageTableInfo, UserSchema schema, @Nullable ContainerFilter filter) + { + super(domain, storageTableInfo, schema, filter); setName(StudyQuerySchema.TREATMENT_PRODUCT_MAP_TABLE_NAME); setDescription("Contains one row per study treatment product"); } + @Override protected void initColumn(ColumnInfo col) { diff --git a/study/src/org/labkey/study/query/studydesign/StudyTreatmentTable.java b/study/src/org/labkey/study/query/studydesign/StudyTreatmentTable.java index 7f511c13..7be435f6 100644 --- a/study/src/org/labkey/study/query/studydesign/StudyTreatmentTable.java +++ b/study/src/org/labkey/study/query/studydesign/StudyTreatmentTable.java @@ -22,6 +22,7 @@ import org.labkey.api.data.DisplayColumn; import org.labkey.api.data.DisplayColumnFactory; import org.labkey.api.data.TableInfo; +import org.labkey.api.exp.api.StorageProvisioner; import org.labkey.api.exp.property.Domain; import org.labkey.api.query.FieldKey; import org.labkey.api.query.LookupForeignKey; @@ -51,9 +52,20 @@ public class StudyTreatmentTable extends DefaultStudyDesignTable defaultVisibleColumns.add(FieldKey.fromParts("DescriptionRendererType")); } - public StudyTreatmentTable(Domain domain, UserSchema schema, @Nullable ContainerFilter filter) + public static StudyTreatmentTable create(Domain domain, UserSchema schema, @Nullable ContainerFilter filter) { - super(domain, schema, filter); + TableInfo storageTableInfo = StorageProvisioner.createTableInfo(domain); + if (null == storageTableInfo) + { + throw new IllegalStateException("Could not create provisioned table for domain: " + domain.getTypeURI()); + } + return new StudyTreatmentTable(domain, storageTableInfo, schema, filter); + } + + + private StudyTreatmentTable(Domain domain, TableInfo storageTableInfo, UserSchema schema, @Nullable ContainerFilter filter) + { + super(domain, storageTableInfo, schema, filter); setName(StudyQuerySchema.TREATMENT_TABLE_NAME); setDescription("Contains one row per study treatment"); From 32e0eb23459260b90e373b9ce9ad2abdbe7efd61 Mon Sep 17 00:00:00 2001 From: Adam Rauch Date: Tue, 30 Jun 2015 15:51:45 +0000 Subject: [PATCH 074/218] Update licenses --- .../labkey/study/query/studydesign/DefaultStudyDesignTable.java | 2 +- .../study/query/studydesign/StudyProductAntigenTable.java | 2 +- .../org/labkey/study/query/studydesign/StudyProductTable.java | 2 +- .../study/query/studydesign/StudyTreatmentProductTable.java | 2 +- .../org/labkey/study/query/studydesign/StudyTreatmentTable.java | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/study/src/org/labkey/study/query/studydesign/DefaultStudyDesignTable.java b/study/src/org/labkey/study/query/studydesign/DefaultStudyDesignTable.java index 80b78fc5..96671c7c 100644 --- a/study/src/org/labkey/study/query/studydesign/DefaultStudyDesignTable.java +++ b/study/src/org/labkey/study/query/studydesign/DefaultStudyDesignTable.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013-2014 LabKey Corporation + * Copyright (c) 2013-2015 LabKey Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/study/src/org/labkey/study/query/studydesign/StudyProductAntigenTable.java b/study/src/org/labkey/study/query/studydesign/StudyProductAntigenTable.java index ce760234..466cca1c 100644 --- a/study/src/org/labkey/study/query/studydesign/StudyProductAntigenTable.java +++ b/study/src/org/labkey/study/query/studydesign/StudyProductAntigenTable.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013-2014 LabKey Corporation + * Copyright (c) 2013-2015 LabKey Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/study/src/org/labkey/study/query/studydesign/StudyProductTable.java b/study/src/org/labkey/study/query/studydesign/StudyProductTable.java index d1364d36..516f1110 100644 --- a/study/src/org/labkey/study/query/studydesign/StudyProductTable.java +++ b/study/src/org/labkey/study/query/studydesign/StudyProductTable.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013-2014 LabKey Corporation + * Copyright (c) 2013-2015 LabKey Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/study/src/org/labkey/study/query/studydesign/StudyTreatmentProductTable.java b/study/src/org/labkey/study/query/studydesign/StudyTreatmentProductTable.java index 55fceffa..c54d26ef 100644 --- a/study/src/org/labkey/study/query/studydesign/StudyTreatmentProductTable.java +++ b/study/src/org/labkey/study/query/studydesign/StudyTreatmentProductTable.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013-2014 LabKey Corporation + * Copyright (c) 2013-2015 LabKey Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/study/src/org/labkey/study/query/studydesign/StudyTreatmentTable.java b/study/src/org/labkey/study/query/studydesign/StudyTreatmentTable.java index 7be435f6..60233c45 100644 --- a/study/src/org/labkey/study/query/studydesign/StudyTreatmentTable.java +++ b/study/src/org/labkey/study/query/studydesign/StudyTreatmentTable.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013-2014 LabKey Corporation + * Copyright (c) 2013-2015 LabKey Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. From f0b3235cab285960280f6e60d2e759ad16ae019c Mon Sep 17 00:00:00 2001 From: Josh Eckels Date: Tue, 25 Aug 2015 00:09:50 +0000 Subject: [PATCH 075/218] Remove about 1500 deprecation warnings by migrating usages of RequiresPermissionClass to RequiresPermission. Support for @RequiresPermissionClass remains unchanged. --- .../controllers/StudyDesignController.java | 26 +++++++++---------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/study/src/org/labkey/study/controllers/StudyDesignController.java b/study/src/org/labkey/study/controllers/StudyDesignController.java index 86599c1f..f302491f 100644 --- a/study/src/org/labkey/study/controllers/StudyDesignController.java +++ b/study/src/org/labkey/study/controllers/StudyDesignController.java @@ -22,7 +22,7 @@ import org.labkey.api.action.SimpleViewAction; import org.labkey.api.data.DbScope; import org.labkey.api.security.ActionNames; -import org.labkey.api.security.RequiresPermissionClass; +import org.labkey.api.security.RequiresPermission; import org.labkey.api.security.permissions.DeletePermission; import org.labkey.api.security.permissions.ReadPermission; import org.labkey.api.security.permissions.UpdatePermission; @@ -68,7 +68,7 @@ public StudyDesignController() } @ActionNames("manageAssaySchedule, manageAssaySpecimen") - @RequiresPermissionClass(UpdatePermission.class) + @RequiresPermission(UpdatePermission.class) public class ManageAssayScheduleAction extends SimpleViewAction { public ModelAndView getView(AssayScheduleForm form, BindException errors) throws Exception @@ -100,7 +100,7 @@ public void setUseAlternateLookupFields(boolean useAlternateLookupFields) } } - @RequiresPermissionClass(UpdatePermission.class) + @RequiresPermission(UpdatePermission.class) public class ManageStudyProductsAction extends SimpleViewAction { public ModelAndView getView(Object o, BindException errors) throws Exception @@ -117,7 +117,7 @@ public NavTree appendNavTrail(NavTree root) } } - @RequiresPermissionClass(UpdatePermission.class) + @RequiresPermission(UpdatePermission.class) public class ManageTreatmentsAction extends SimpleViewAction { public ModelAndView getView(Object o, BindException errors) throws Exception @@ -134,7 +134,7 @@ public NavTree appendNavTrail(NavTree root) } } - @RequiresPermissionClass(ReadPermission.class) + @RequiresPermission(ReadPermission.class) public class GetStudyProducts extends ApiAction { private StudyImpl _study; @@ -204,7 +204,7 @@ public void setRole(String role) } } - @RequiresPermissionClass(ReadPermission.class) + @RequiresPermission(ReadPermission.class) public class GetStudyTreatments extends ApiAction { private StudyImpl _study; @@ -255,7 +255,7 @@ public ApiResponse execute(Object form, BindException errors) throws Exception } } - @RequiresPermissionClass(ReadPermission.class) + @RequiresPermission(ReadPermission.class) public class GetStudyTreatmentSchedule extends ApiAction { private StudyImpl _study; @@ -292,7 +292,7 @@ public ApiResponse execute(Object form, BindException errors) throws Exception } } - @RequiresPermissionClass(DeletePermission.class) + @RequiresPermission(DeletePermission.class) public class DeleteTreatmentAction extends MutatingApiAction { @Override @@ -307,7 +307,7 @@ public ApiResponse execute(IdForm form, BindException errors) throws Exception } } - @RequiresPermissionClass(DeletePermission.class) + @RequiresPermission(DeletePermission.class) public class DeleteStudyProductAction extends MutatingApiAction { @Override @@ -322,7 +322,7 @@ public ApiResponse execute(IdForm form, BindException errors) throws Exception } } - @RequiresPermissionClass(UpdatePermission.class) + @RequiresPermission(UpdatePermission.class) public class UpdateStudyTreatmentScheduleAction extends MutatingApiAction { @Override @@ -407,7 +407,7 @@ private void updateTreatmentVisitMapping(StudyTreatmentSchedule form, CohortImpl } } - @RequiresPermissionClass(DeletePermission.class) + @RequiresPermission(DeletePermission.class) public class DeleteCohortAction extends MutatingApiAction { @Override @@ -437,7 +437,7 @@ public ApiResponse execute(IdForm form, BindException errors) throws Exception } } - @RequiresPermissionClass(UpdatePermission.class) + @RequiresPermission(UpdatePermission.class) public class CreateVisitAction extends MutatingApiAction { @Override @@ -475,7 +475,7 @@ public ApiResponse execute(VisitForm form, BindException errors) throws Exceptio } } - @RequiresPermissionClass(UpdatePermission.class) + @RequiresPermission(UpdatePermission.class) public class UpdateAssayPlanAction extends MutatingApiAction { @Override From cab707952187fe0d423cf5181c8bcc6079355f95 Mon Sep 17 00:00:00 2001 From: Matthew Bellew Date: Fri, 11 Sep 2015 23:48:03 +0000 Subject: [PATCH 076/218] split up JUNIT tests into DRT and BVT totally arbitrary, just took the longer ones and moved to the BVT, feel free to adjust --- study/src/org/labkey/study/model/TreatmentManager.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/study/src/org/labkey/study/model/TreatmentManager.java b/study/src/org/labkey/study/model/TreatmentManager.java index 88538bf2..a1844c92 100644 --- a/study/src/org/labkey/study/model/TreatmentManager.java +++ b/study/src/org/labkey/study/model/TreatmentManager.java @@ -35,6 +35,7 @@ import org.labkey.api.security.User; import org.labkey.api.study.TimepointType; import org.labkey.api.study.Visit; +import org.labkey.api.test.TestWhen; import org.labkey.api.util.DateUtil; import org.labkey.api.util.GUID; import org.labkey.api.util.JunitUtil; @@ -339,6 +340,7 @@ public String getStudyDesignSubTypeLabelByName(Container container, String name) * */ + @TestWhen(TestWhen.When.BVT) public static class TreatmentDataTestCase extends Assert { TestContext _context = null; From d1e09b0a63d283c143400d8cba1e0e27c8099682 Mon Sep 17 00:00:00 2001 From: Adam Rauch Date: Mon, 14 Sep 2015 19:54:23 +0000 Subject: [PATCH 077/218] Update licenses --- study/src/org/labkey/study/model/TreatmentManager.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/study/src/org/labkey/study/model/TreatmentManager.java b/study/src/org/labkey/study/model/TreatmentManager.java index a1844c92..101e88c7 100644 --- a/study/src/org/labkey/study/model/TreatmentManager.java +++ b/study/src/org/labkey/study/model/TreatmentManager.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014 LabKey Corporation + * Copyright (c) 2014-2015 LabKey Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. From 7b7e9f0c7f74d3d15791de5fbfcf34387a1a3052 Mon Sep 17 00:00:00 2001 From: Kevin Krouse Date: Sat, 7 Nov 2015 19:47:23 +0000 Subject: [PATCH 078/218] fix CAVDStudyTest -- import using importRows instead of DataIterator etl --- .../query/studydesign/StudyDesignLookupBaseTable.java | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/study/src/org/labkey/study/query/studydesign/StudyDesignLookupBaseTable.java b/study/src/org/labkey/study/query/studydesign/StudyDesignLookupBaseTable.java index 17430581..5f269389 100644 --- a/study/src/org/labkey/study/query/studydesign/StudyDesignLookupBaseTable.java +++ b/study/src/org/labkey/study/query/studydesign/StudyDesignLookupBaseTable.java @@ -21,8 +21,9 @@ import org.labkey.api.data.Container; import org.labkey.api.data.ContainerFilter; import org.labkey.api.data.DatabaseTableType; -import org.labkey.api.data.JdbcType; import org.labkey.api.data.TableInfo; +import org.labkey.api.etl.DataIteratorBuilder; +import org.labkey.api.etl.DataIteratorContext; import org.labkey.api.query.DefaultQueryUpdateService; import org.labkey.api.query.DuplicateKeyException; import org.labkey.api.query.FieldKey; @@ -112,6 +113,12 @@ public StudyDesignLookupsQueryUpdateService(TableInfo queryTable, TableInfo dbTa super(queryTable, dbTable); } + @Override + public int loadRows(User user, Container container, DataIteratorBuilder rows, DataIteratorContext context, @Nullable Map extraScriptContext) throws SQLException + { + return importRows(user, container, rows, context.getErrors(), context.getConfigParameters(), extraScriptContext); + } + @Override protected Map insertRow(User user, Container container, Map row) throws DuplicateKeyException, ValidationException, QueryUpdateServiceException, SQLException { From 60d25719e9022a25c1a0e81a8a9da5aba358c87d Mon Sep 17 00:00:00 2001 From: Cory Nathe Date: Wed, 23 Dec 2015 14:09:08 +0000 Subject: [PATCH 079/218] Spec #24609: update usages of PdLookupForeignKey constructor for cases when PropertyDescriptor has conceptURI --- .../labkey/study/query/studydesign/DefaultStudyDesignTable.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/study/src/org/labkey/study/query/studydesign/DefaultStudyDesignTable.java b/study/src/org/labkey/study/query/studydesign/DefaultStudyDesignTable.java index 96671c7c..f80abf09 100644 --- a/study/src/org/labkey/study/query/studydesign/DefaultStudyDesignTable.java +++ b/study/src/org/labkey/study/query/studydesign/DefaultStudyDesignTable.java @@ -80,7 +80,7 @@ protected DefaultStudyDesignTable(Domain domain, TableInfo storageTableInfo, Us PropertyDescriptor pd = property.getPropertyDescriptor(); if (pd != null) { - if (pd.getLookupQuery() != null) + if (pd.getLookupQuery() != null || pd.getConceptURI() != null) col.setFk(new PdLookupForeignKey(schema.getUser(), pd, schema.getContainer())); if (pd.getPropertyType() == PropertyType.MULTI_LINE) From 8d6062263111360d00280af42a0418f73731aa93 Mon Sep 17 00:00:00 2001 From: Adam Rauch Date: Mon, 1 Feb 2016 06:34:12 +0000 Subject: [PATCH 080/218] Minor study fixes: - Double encoding problems on dataset reorder page (visible " "s) - Better default description for demographics datasets: e.g., "Contains up to one row [...] per Subject combination" -> "Contains up to one row [...] per Subject" - Add ".xlsx" to import extension options - Remove pointless getDescription() overrides --- .../studydesign/DefaultStudyDesignTable.java | 21 ++++--------------- 1 file changed, 4 insertions(+), 17 deletions(-) diff --git a/study/src/org/labkey/study/query/studydesign/DefaultStudyDesignTable.java b/study/src/org/labkey/study/query/studydesign/DefaultStudyDesignTable.java index f80abf09..8194ce05 100644 --- a/study/src/org/labkey/study/query/studydesign/DefaultStudyDesignTable.java +++ b/study/src/org/labkey/study/query/studydesign/DefaultStudyDesignTable.java @@ -22,14 +22,10 @@ import org.labkey.api.data.ContainerFilter; import org.labkey.api.data.ContainerForeignKey; import org.labkey.api.data.DataColumn; -import org.labkey.api.data.DbSchema; import org.labkey.api.data.DbScope; -import org.labkey.api.data.DisplayColumn; -import org.labkey.api.data.DisplayColumnFactory; import org.labkey.api.data.SimpleFilter; import org.labkey.api.data.Table; import org.labkey.api.data.TableInfo; -import org.labkey.api.exp.OntologyManager; import org.labkey.api.exp.PropertyDescriptor; import org.labkey.api.exp.PropertyType; import org.labkey.api.exp.api.StorageProvisioner; @@ -85,13 +81,10 @@ protected DefaultStudyDesignTable(Domain domain, TableInfo storageTableInfo, Us if (pd.getPropertyType() == PropertyType.MULTI_LINE) { - col.setDisplayColumnFactory(new DisplayColumnFactory() { - public DisplayColumn createRenderer(ColumnInfo colInfo) - { - DataColumn dc = new DataColumn(colInfo); - dc.setPreserveNewlines(true); - return dc; - } + col.setDisplayColumnFactory(colInfo -> { + DataColumn dc = new DataColumn(colInfo); + dc.setPreserveNewlines(true); + return dc; }); } col.setName(pd.getName()); @@ -173,12 +166,6 @@ public QueryUpdateService getUpdateService() return new DefaultQueryUpdateService(this, this.getRealTable()); } - @Override - public String getDescription() - { - return super.getDescription(); - } - @Override // ONLY OVERRIDE THIS IF TABLE SHOULD BE VISIBLE IN DATASPACE PROJECT-LEVEL CONTAINER public boolean hasPermission(@NotNull UserPrincipal user, @NotNull Class perm) From fc83dbc1a55c9b5623bf4fed14127fd0eb180646 Mon Sep 17 00:00:00 2001 From: Ed Younskevicius Date: Fri, 5 Feb 2016 02:01:22 +0000 Subject: [PATCH 081/218] Change copyright ending date to 2016 in all applicable source files --- .../labkey/study/query/studydesign/DefaultStudyDesignTable.java | 2 +- .../study/query/studydesign/StudyDesignLookupBaseTable.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/study/src/org/labkey/study/query/studydesign/DefaultStudyDesignTable.java b/study/src/org/labkey/study/query/studydesign/DefaultStudyDesignTable.java index 8194ce05..662c8c60 100644 --- a/study/src/org/labkey/study/query/studydesign/DefaultStudyDesignTable.java +++ b/study/src/org/labkey/study/query/studydesign/DefaultStudyDesignTable.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013-2015 LabKey Corporation + * Copyright (c) 2013-2016 LabKey Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/study/src/org/labkey/study/query/studydesign/StudyDesignLookupBaseTable.java b/study/src/org/labkey/study/query/studydesign/StudyDesignLookupBaseTable.java index 5f269389..b3ff8439 100644 --- a/study/src/org/labkey/study/query/studydesign/StudyDesignLookupBaseTable.java +++ b/study/src/org/labkey/study/query/studydesign/StudyDesignLookupBaseTable.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013-2014 LabKey Corporation + * Copyright (c) 2013-2016 LabKey Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. From 7bf8f29a871eded30b7c22dc962fa17e5051a893 Mon Sep 17 00:00:00 2001 From: Adam Rauch Date: Sat, 26 Mar 2016 23:25:48 +0000 Subject: [PATCH 082/218] getClientDependencies() -> addClientDependencies() for SVN JSPs --- .../view/studydesign/assayScheduleWebpart.jsp | 9 +++----- .../immunizationScheduleWebpart.jsp | 11 ++++----- .../view/studydesign/manageAssaySchedule.jsp | 23 ++++++++----------- .../view/studydesign/manageStudyProducts.jsp | 15 +++++------- .../view/studydesign/manageTreatments.jsp | 19 +++++++-------- .../view/studydesign/vaccineDesignWebpart.jsp | 15 +++++------- 6 files changed, 37 insertions(+), 55 deletions(-) diff --git a/study/src/org/labkey/study/view/studydesign/assayScheduleWebpart.jsp b/study/src/org/labkey/study/view/studydesign/assayScheduleWebpart.jsp index 619911ee..6465e026 100644 --- a/study/src/org/labkey/study/view/studydesign/assayScheduleWebpart.jsp +++ b/study/src/org/labkey/study/view/studydesign/assayScheduleWebpart.jsp @@ -21,22 +21,19 @@ <%@ page import="org.labkey.api.security.permissions.UpdatePermission" %> <%@ page import="org.labkey.api.util.PageFlowUtil" %> <%@ page import="org.labkey.api.view.WebThemeManager" %> -<%@ page import="org.labkey.api.view.template.ClientDependency" %> +<%@ page import="org.labkey.api.view.template.ClientDependencies" %> <%@ page import="org.labkey.study.controllers.StudyDesignController" %> <%@ page import="org.labkey.study.model.AssaySpecimenConfigImpl" %> <%@ page import="org.labkey.study.model.LocationImpl" %> <%@ page import="org.labkey.study.model.StudyImpl" %> <%@ page import="org.labkey.study.model.StudyManager" %> <%@ page import="org.labkey.study.model.VisitImpl" %> -<%@ page import="java.util.LinkedHashSet" %> <%@ page import="java.util.List" %> <%@ page extends="org.labkey.study.view.BaseStudyPage" %> <%! - public LinkedHashSet getClientDependencies() + public void addClientDependencies(ClientDependencies dependencies) { - LinkedHashSet resources = new LinkedHashSet<>(); - resources.add(ClientDependency.fromPath("study/StudyVaccineDesign.css")); - return resources; + dependencies.add("study/StudyVaccineDesign.css"); } %> <% diff --git a/study/src/org/labkey/study/view/studydesign/immunizationScheduleWebpart.jsp b/study/src/org/labkey/study/view/studydesign/immunizationScheduleWebpart.jsp index f35fd7c7..9f7b2525 100644 --- a/study/src/org/labkey/study/view/studydesign/immunizationScheduleWebpart.jsp +++ b/study/src/org/labkey/study/view/studydesign/immunizationScheduleWebpart.jsp @@ -20,7 +20,7 @@ <%@ page import="org.labkey.api.security.permissions.UpdatePermission" %> <%@ page import="org.labkey.api.util.PageFlowUtil" %> <%@ page import="org.labkey.api.view.WebThemeManager" %> -<%@ page import="org.labkey.api.view.template.ClientDependency" %> +<%@ page import="org.labkey.api.view.template.ClientDependencies" %> <%@ page import="org.labkey.study.controllers.StudyDesignController" %> <%@ page import="org.labkey.study.model.CohortImpl" %> <%@ page import="org.labkey.study.model.ProductImpl" %> @@ -31,17 +31,14 @@ <%@ page import="org.labkey.study.model.TreatmentVisitMapImpl" %> <%@ page import="org.labkey.study.model.VisitImpl" %> <%@ page import="java.util.HashMap" %> -<%@ page import="java.util.LinkedHashSet" %> <%@ page import="java.util.List" %> <%@ page import="java.util.Map" %> <%@ page extends="org.labkey.study.view.BaseStudyPage" %> <%! - public LinkedHashSet getClientDependencies() + public void addClientDependencies(ClientDependencies dependencies) { - LinkedHashSet resources = new LinkedHashSet<>(); - resources.add(ClientDependency.fromPath("study/StudyVaccineDesign.js")); - resources.add(ClientDependency.fromPath("study/StudyVaccineDesign.css")); - return resources; + dependencies.add("study/StudyVaccineDesign.js"); + dependencies.add("study/StudyVaccineDesign.css"); } %> <% diff --git a/study/src/org/labkey/study/view/studydesign/manageAssaySchedule.jsp b/study/src/org/labkey/study/view/studydesign/manageAssaySchedule.jsp index d92b1d9e..587983fe 100644 --- a/study/src/org/labkey/study/view/studydesign/manageAssaySchedule.jsp +++ b/study/src/org/labkey/study/view/studydesign/manageAssaySchedule.jsp @@ -16,29 +16,26 @@ */ %> <%@ page import="org.labkey.api.data.Container" %> -<%@ page import="org.labkey.api.view.ActionURL" %> -<%@ page import="org.labkey.api.view.HttpView" %> -<%@ page import="org.labkey.api.view.JspView" %> -<%@ page import="org.labkey.api.view.template.ClientDependency" %> <%@ page import="org.labkey.api.security.User" %> <%@ page import="org.labkey.api.study.Study" %> <%@ page import="org.labkey.api.study.TimepointType" %> <%@ page import="org.labkey.api.study.Visit" %> +<%@ page import="org.labkey.api.view.ActionURL" %> +<%@ page import="org.labkey.api.view.HttpView" %> +<%@ page import="org.labkey.api.view.JspView" %> +<%@ page import="org.labkey.api.view.template.ClientDependencies" %> <%@ page import="org.labkey.study.controllers.StudyController" %> <%@ page import="org.labkey.study.controllers.StudyDesignController" %> <%@ page import="org.labkey.study.model.StudyManager" %> <%@ page import="org.labkey.study.security.permissions.ManageStudyPermission" %> -<%@ page import="java.util.LinkedHashSet" %> <%@ page extends="org.labkey.api.jsp.JspBase" %> <%! - public LinkedHashSet getClientDependencies() - { - LinkedHashSet resources = new LinkedHashSet<>(); - resources.add(ClientDependency.fromPath("Ext4ClientApi")); - resources.add(ClientDependency.fromPath("study/StudyVaccineDesign.js")); - resources.add(ClientDependency.fromPath("dataview/DataViewsPanel.css")); - return resources; - } + public void addClientDependencies(ClientDependencies dependencies) + { + dependencies.add("Ext4ClientApi"); + dependencies.add("study/StudyVaccineDesign.js"); + dependencies.add("dataview/DataViewsPanel.css"); + } %> <% JspView me = (JspView) HttpView.currentView(); diff --git a/study/src/org/labkey/study/view/studydesign/manageStudyProducts.jsp b/study/src/org/labkey/study/view/studydesign/manageStudyProducts.jsp index 4a2c5e79..cb7a3627 100644 --- a/study/src/org/labkey/study/view/studydesign/manageStudyProducts.jsp +++ b/study/src/org/labkey/study/view/studydesign/manageStudyProducts.jsp @@ -17,19 +17,16 @@ %> <%@ page import="org.labkey.api.data.Container" %> -<%@ page import="org.labkey.api.view.template.ClientDependency" %> -<%@ page import="java.util.LinkedHashSet" %> +<%@ page import="org.labkey.api.view.template.ClientDependencies" %> <%@ page import="org.labkey.study.controllers.StudyDesignController" %> <%@ page extends="org.labkey.api.jsp.JspBase" %> <%! - public LinkedHashSet getClientDependencies() + public void addClientDependencies(ClientDependencies dependencies) { - LinkedHashSet resources = new LinkedHashSet<>(); - resources.add(ClientDependency.fromPath("Ext4ClientApi")); - resources.add(ClientDependency.fromPath("study/StudyVaccineDesign.js")); - resources.add(ClientDependency.fromPath("dataview/DataViewsPanel.css")); - resources.add(ClientDependency.fromPath("study/StudyVaccineDesign.css")); - return resources; + dependencies.add("Ext4ClientApi"); + dependencies.add("study/StudyVaccineDesign.js"); + dependencies.add("dataview/DataViewsPanel.css"); + dependencies.add("study/StudyVaccineDesign.css"); } %> <% diff --git a/study/src/org/labkey/study/view/studydesign/manageTreatments.jsp b/study/src/org/labkey/study/view/studydesign/manageTreatments.jsp index b4985d26..c84a22e8 100644 --- a/study/src/org/labkey/study/view/studydesign/manageTreatments.jsp +++ b/study/src/org/labkey/study/view/studydesign/manageTreatments.jsp @@ -17,28 +17,25 @@ %> <%@ page import="org.labkey.api.data.Container" %> +<%@ page import="org.labkey.api.security.User" %> <%@ page import="org.labkey.api.study.Study" %> <%@ page import="org.labkey.api.study.TimepointType" %> <%@ page import="org.labkey.api.study.Visit" %> <%@ page import="org.labkey.api.view.ActionURL" %> -<%@ page import="org.labkey.api.view.template.ClientDependency" %> +<%@ page import="org.labkey.api.view.template.ClientDependencies" %> +<%@ page import="org.labkey.study.controllers.CohortController" %> <%@ page import="org.labkey.study.controllers.StudyController" %> <%@ page import="org.labkey.study.controllers.StudyDesignController" %> <%@ page import="org.labkey.study.model.StudyManager" %> -<%@ page import="java.util.LinkedHashSet" %> -<%@ page import="org.labkey.study.controllers.CohortController" %> <%@ page import="org.labkey.study.security.permissions.ManageStudyPermission" %> -<%@ page import="org.labkey.api.security.User" %> <%@ page extends="org.labkey.api.jsp.JspBase" %> <%! - public LinkedHashSet getClientDependencies() + public void addClientDependencies(ClientDependencies dependencies) { - LinkedHashSet resources = new LinkedHashSet<>(); - resources.add(ClientDependency.fromPath("Ext4ClientApi")); - resources.add(ClientDependency.fromPath("study/StudyVaccineDesign.js")); - resources.add(ClientDependency.fromPath("dataview/DataViewsPanel.css")); - resources.add(ClientDependency.fromPath("study/StudyVaccineDesign.css")); - return resources; + dependencies.add("Ext4ClientApi"); + dependencies.add("study/StudyVaccineDesign.js"); + dependencies.add("dataview/DataViewsPanel.css"); + dependencies.add("study/StudyVaccineDesign.css"); } %> <% diff --git a/study/src/org/labkey/study/view/studydesign/vaccineDesignWebpart.jsp b/study/src/org/labkey/study/view/studydesign/vaccineDesignWebpart.jsp index 87f78abe..e58d0ee7 100644 --- a/study/src/org/labkey/study/view/studydesign/vaccineDesignWebpart.jsp +++ b/study/src/org/labkey/study/view/studydesign/vaccineDesignWebpart.jsp @@ -17,28 +17,25 @@ %> <%@ page import="org.labkey.api.data.Container" %> <%@ page import="org.labkey.api.security.User" %> +<%@ page import="org.labkey.api.security.permissions.UpdatePermission" %> <%@ page import="org.labkey.api.view.HttpView" %> <%@ page import="org.labkey.api.view.JspView" %> <%@ page import="org.labkey.api.view.Portal" %> <%@ page import="org.labkey.api.view.WebThemeManager" %> -<%@ page import="org.labkey.api.view.template.ClientDependency" %> +<%@ page import="org.labkey.api.view.template.ClientDependencies" %> <%@ page import="org.labkey.study.controllers.StudyDesignController" %> <%@ page import="org.labkey.study.model.ProductImpl" %> <%@ page import="org.labkey.study.model.StudyImpl" %> <%@ page import="org.labkey.study.model.StudyManager" %> <%@ page import="org.labkey.study.model.TreatmentManager" %> -<%@ page import="java.util.LinkedHashSet" %> <%@ page import="java.util.List" %> -<%@ page import="org.labkey.api.security.permissions.UpdatePermission" %> <%@ page extends="org.labkey.study.view.BaseStudyPage" %> <%! - public LinkedHashSet getClientDependencies() + public void addClientDependencies(ClientDependencies dependencies) { - LinkedHashSet resources = new LinkedHashSet<>(); - resources.add(ClientDependency.fromPath("Ext4ClientApi")); - resources.add(ClientDependency.fromPath("study/StudyVaccineDesign.js")); - resources.add(ClientDependency.fromPath("study/StudyVaccineDesign.css")); - return resources; + dependencies.add("Ext4ClientApi"); + dependencies.add("study/StudyVaccineDesign.js"); + dependencies.add("study/StudyVaccineDesign.css"); } %> <% From c2146ff85ac46bcec53e3d8a572ec44235f7a633 Mon Sep 17 00:00:00 2001 From: Adam Rauch Date: Fri, 6 May 2016 23:07:24 +0000 Subject: [PATCH 083/218] Bulk inspection fix: redundant type arguments --- study/src/org/labkey/study/model/TreatmentManager.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/study/src/org/labkey/study/model/TreatmentManager.java b/study/src/org/labkey/study/model/TreatmentManager.java index 101e88c7..73196cfc 100644 --- a/study/src/org/labkey/study/model/TreatmentManager.java +++ b/study/src/org/labkey/study/model/TreatmentManager.java @@ -208,7 +208,7 @@ public void deleteTreatment(Container container, User user, int rowId) QueryUpdateService qus = treatmentTable.getUpdateService(); List> keys = new ArrayList<>(); ColumnInfo treatmentPk = treatmentTable.getColumn(FieldKey.fromParts("RowId")); - keys.add(Collections.singletonMap(treatmentPk.getName(), rowId)); + keys.add(Collections.singletonMap(treatmentPk.getName(), rowId)); qus.deleteRows(user, container, keys, null, null); } @@ -241,7 +241,7 @@ public void deleteStudyProduct(Container container, User user, int rowId) QueryUpdateService qus = productTable.getUpdateService(); List> keys = new ArrayList<>(); ColumnInfo productPk = productTable.getColumn(FieldKey.fromParts("RowId")); - keys.add(Collections.singletonMap(productPk.getName(), rowId)); + keys.add(Collections.singletonMap(productPk.getName(), rowId)); qus.deleteRows(user, container, keys, null, null); } else @@ -272,7 +272,7 @@ public void deleteProductAntigens(Container container, User user, int rowId) thr ColumnInfo productAntigenPk = productAntigenTable.getColumn(FieldKey.fromParts("RowId")); for (Integer productAntigenId : productAntigenIds) { - keys.add(Collections.singletonMap(productAntigenPk.getName(), productAntigenId)); + keys.add(Collections.singletonMap(productAntigenPk.getName(), productAntigenId)); } qus.deleteRows(user, container, keys, null, null); @@ -299,7 +299,7 @@ public void deleteTreatmentProductMap(Container container, User user, SimpleFilt ColumnInfo productMapPk = productMapTable.getColumn(FieldKey.fromParts("RowId")); for (Integer productMapId : productMapIds) { - keys.add(Collections.singletonMap(productMapPk.getName(), productMapId)); + keys.add(Collections.singletonMap(productMapPk.getName(), productMapId)); } qus.deleteRows(user, container, keys, null, null); From 073de4d8a156583b3cc0e7aad06711d28fac54a1 Mon Sep 17 00:00:00 2001 From: Josh Eckels Date: Thu, 19 May 2016 15:42:05 +0000 Subject: [PATCH 084/218] Don't advertise that methods throw SQLException if they never will (converted to runtime exceptions some time ago) --- study/src/org/labkey/study/model/TreatmentManager.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/study/src/org/labkey/study/model/TreatmentManager.java b/study/src/org/labkey/study/model/TreatmentManager.java index 73196cfc..922b8247 100644 --- a/study/src/org/labkey/study/model/TreatmentManager.java +++ b/study/src/org/labkey/study/model/TreatmentManager.java @@ -32,6 +32,7 @@ import org.labkey.api.query.QueryService; import org.labkey.api.query.QueryUpdateService; import org.labkey.api.query.UserSchema; +import org.labkey.api.query.ValidationException; import org.labkey.api.security.User; import org.labkey.api.study.TimepointType; import org.labkey.api.study.Visit; @@ -471,7 +472,7 @@ private void verifyStudyProducts() assertEquals("Unexpected product lookup value", _lookups.get("ImmunogenType"), immunogen.getType()); } - private void populateTreatmentSchedule() throws SQLException, ServletException + private void populateTreatmentSchedule() throws ValidationException { _cohorts.add(CohortManager.getInstance().createCohort(_junitStudy, _user, "Cohort1", true, 10, null)); _cohorts.add(CohortManager.getInstance().createCohort(_junitStudy, _user, "Cohort2", true, 20, null)); From cfe5a90de5340cf7f979da3cfb92180a343596b1 Mon Sep 17 00:00:00 2001 From: Adam Rauch Date: Sun, 19 Jun 2016 20:21:41 +0000 Subject: [PATCH 085/218] Correct bad packages: com.drew.lang.annotations.Nullable -> org.jetbrains.annotations.Nullable com.drew.lang.annotations.NotNull -> org.jetbrains.annotations.NotNull --- .../org/labkey/study/query/studydesign/StudyProductTable.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/study/src/org/labkey/study/query/studydesign/StudyProductTable.java b/study/src/org/labkey/study/query/studydesign/StudyProductTable.java index 516f1110..d7c624bf 100644 --- a/study/src/org/labkey/study/query/studydesign/StudyProductTable.java +++ b/study/src/org/labkey/study/query/studydesign/StudyProductTable.java @@ -19,7 +19,6 @@ import org.jetbrains.annotations.Nullable; import org.labkey.api.data.ColumnInfo; import org.labkey.api.data.ContainerFilter; -import org.labkey.api.data.DbSchema; import org.labkey.api.data.TableInfo; import org.labkey.api.exp.api.StorageProvisioner; import org.labkey.api.exp.property.Domain; @@ -49,7 +48,7 @@ public class StudyProductTable extends DefaultStudyDesignTable defaultVisibleColumns.add(FieldKey.fromParts("Type")); } - public static StudyProductTable create(Domain domain, UserSchema schema, @com.drew.lang.annotations.Nullable ContainerFilter containerFilter) + public static StudyProductTable create(Domain domain, UserSchema schema, @Nullable ContainerFilter containerFilter) { TableInfo storageTableInfo = StorageProvisioner.createTableInfo(domain); if (null == storageTableInfo) From 3996e498b296c1e5e5c9a112cf152bdf99d19bed Mon Sep 17 00:00:00 2001 From: Adam Rauch Date: Mon, 25 Jul 2016 03:33:24 +0000 Subject: [PATCH 086/218] Annotate JSP addClientDependencies() with @Override --- .../org/labkey/study/view/studydesign/assayScheduleWebpart.jsp | 1 + .../study/view/studydesign/immunizationScheduleWebpart.jsp | 1 + .../org/labkey/study/view/studydesign/manageAssaySchedule.jsp | 1 + .../org/labkey/study/view/studydesign/manageStudyProducts.jsp | 1 + study/src/org/labkey/study/view/studydesign/manageTreatments.jsp | 1 + .../org/labkey/study/view/studydesign/vaccineDesignWebpart.jsp | 1 + 6 files changed, 6 insertions(+) diff --git a/study/src/org/labkey/study/view/studydesign/assayScheduleWebpart.jsp b/study/src/org/labkey/study/view/studydesign/assayScheduleWebpart.jsp index 6465e026..9216ce3b 100644 --- a/study/src/org/labkey/study/view/studydesign/assayScheduleWebpart.jsp +++ b/study/src/org/labkey/study/view/studydesign/assayScheduleWebpart.jsp @@ -31,6 +31,7 @@ <%@ page import="java.util.List" %> <%@ page extends="org.labkey.study.view.BaseStudyPage" %> <%! + @Override public void addClientDependencies(ClientDependencies dependencies) { dependencies.add("study/StudyVaccineDesign.css"); diff --git a/study/src/org/labkey/study/view/studydesign/immunizationScheduleWebpart.jsp b/study/src/org/labkey/study/view/studydesign/immunizationScheduleWebpart.jsp index 9f7b2525..af951c87 100644 --- a/study/src/org/labkey/study/view/studydesign/immunizationScheduleWebpart.jsp +++ b/study/src/org/labkey/study/view/studydesign/immunizationScheduleWebpart.jsp @@ -35,6 +35,7 @@ <%@ page import="java.util.Map" %> <%@ page extends="org.labkey.study.view.BaseStudyPage" %> <%! + @Override public void addClientDependencies(ClientDependencies dependencies) { dependencies.add("study/StudyVaccineDesign.js"); diff --git a/study/src/org/labkey/study/view/studydesign/manageAssaySchedule.jsp b/study/src/org/labkey/study/view/studydesign/manageAssaySchedule.jsp index 587983fe..14f9a909 100644 --- a/study/src/org/labkey/study/view/studydesign/manageAssaySchedule.jsp +++ b/study/src/org/labkey/study/view/studydesign/manageAssaySchedule.jsp @@ -30,6 +30,7 @@ <%@ page import="org.labkey.study.security.permissions.ManageStudyPermission" %> <%@ page extends="org.labkey.api.jsp.JspBase" %> <%! + @Override public void addClientDependencies(ClientDependencies dependencies) { dependencies.add("Ext4ClientApi"); diff --git a/study/src/org/labkey/study/view/studydesign/manageStudyProducts.jsp b/study/src/org/labkey/study/view/studydesign/manageStudyProducts.jsp index cb7a3627..1902bf18 100644 --- a/study/src/org/labkey/study/view/studydesign/manageStudyProducts.jsp +++ b/study/src/org/labkey/study/view/studydesign/manageStudyProducts.jsp @@ -21,6 +21,7 @@ <%@ page import="org.labkey.study.controllers.StudyDesignController" %> <%@ page extends="org.labkey.api.jsp.JspBase" %> <%! + @Override public void addClientDependencies(ClientDependencies dependencies) { dependencies.add("Ext4ClientApi"); diff --git a/study/src/org/labkey/study/view/studydesign/manageTreatments.jsp b/study/src/org/labkey/study/view/studydesign/manageTreatments.jsp index c84a22e8..70c52991 100644 --- a/study/src/org/labkey/study/view/studydesign/manageTreatments.jsp +++ b/study/src/org/labkey/study/view/studydesign/manageTreatments.jsp @@ -30,6 +30,7 @@ <%@ page import="org.labkey.study.security.permissions.ManageStudyPermission" %> <%@ page extends="org.labkey.api.jsp.JspBase" %> <%! + @Override public void addClientDependencies(ClientDependencies dependencies) { dependencies.add("Ext4ClientApi"); diff --git a/study/src/org/labkey/study/view/studydesign/vaccineDesignWebpart.jsp b/study/src/org/labkey/study/view/studydesign/vaccineDesignWebpart.jsp index e58d0ee7..d12b33a5 100644 --- a/study/src/org/labkey/study/view/studydesign/vaccineDesignWebpart.jsp +++ b/study/src/org/labkey/study/view/studydesign/vaccineDesignWebpart.jsp @@ -31,6 +31,7 @@ <%@ page import="java.util.List" %> <%@ page extends="org.labkey.study.view.BaseStudyPage" %> <%! + @Override public void addClientDependencies(ClientDependencies dependencies) { dependencies.add("Ext4ClientApi"); From f6a728d1e041b2c3c0c191a976a1bb0c95fceeb9 Mon Sep 17 00:00:00 2001 From: Marty Pradere Date: Thu, 8 Sep 2016 16:51:18 +0000 Subject: [PATCH 087/218] Spec 27274: Dataset schema optimizations. Update primary key in the db to a auto-incrementing row ID, still use LSID as PK in user schema. Remove container column from non-dataspace datasets. Add expression column to user schema for container using current container. --- .../query/studydesign/AbstractStudyDesignDomainKind.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/study/src/org/labkey/study/query/studydesign/AbstractStudyDesignDomainKind.java b/study/src/org/labkey/study/query/studydesign/AbstractStudyDesignDomainKind.java index 533d77e5..49f3a4f2 100644 --- a/study/src/org/labkey/study/query/studydesign/AbstractStudyDesignDomainKind.java +++ b/study/src/org/labkey/study/query/studydesign/AbstractStudyDesignDomainKind.java @@ -114,7 +114,7 @@ protected String getTableName() } @Override - public Set getBaseProperties() + public Set getBaseProperties(Domain domain) { return _standardFields; } @@ -186,7 +186,7 @@ public String getStorageSchemaName() } @Override - public Set getPropertyIndices() + public Set getPropertyIndices(Domain domain) { return Collections.emptySet(); } @@ -196,7 +196,7 @@ public Set getReservedPropertyNames(Domain domain) { Set names = new HashSet<>(); - for (PropertyStorageSpec spec : getBaseProperties()) + for (PropertyStorageSpec spec : getBaseProperties(domain)) names.add(spec.getName()); return names; From 1840d617aa6700ae41c7260b1c964caa01e7168f Mon Sep 17 00:00:00 2001 From: Cory Nathe Date: Fri, 9 Sep 2016 18:40:17 +0000 Subject: [PATCH 088/218] Spec #27492: manage study product implementation of inline cell editing - display using Ext.view.View tpl for render of grid with subgrids - click event listeners for cell editing, delete row from grid/subgrid, add row to grid/subgrid - NOTE: not yet used by manageStudyProducts.jsp (until the save API action is added) --- .../study/vaccineDesign/BaseDataView.js | 425 ++++++++++++++++++ study/webapp/study/vaccineDesign/Models.js | 12 + .../study/vaccineDesign/StudyProducts.js | 128 ++++++ study/webapp/study/vaccineDesign/Utils.js | 69 +++ .../study/vaccineDesign/VaccineDesign.css | 52 +++ .../study/vaccineDesign/vaccineDesign.lib.xml | 11 + 6 files changed, 697 insertions(+) create mode 100644 study/webapp/study/vaccineDesign/BaseDataView.js create mode 100644 study/webapp/study/vaccineDesign/Models.js create mode 100644 study/webapp/study/vaccineDesign/StudyProducts.js create mode 100644 study/webapp/study/vaccineDesign/Utils.js create mode 100644 study/webapp/study/vaccineDesign/VaccineDesign.css create mode 100644 study/webapp/study/vaccineDesign/vaccineDesign.lib.xml diff --git a/study/webapp/study/vaccineDesign/BaseDataView.js b/study/webapp/study/vaccineDesign/BaseDataView.js new file mode 100644 index 00000000..1a82332b --- /dev/null +++ b/study/webapp/study/vaccineDesign/BaseDataView.js @@ -0,0 +1,425 @@ + +Ext4.define('LABKEY.VaccineDesign.BaseDataView', { + + extend : 'Ext.panel.Panel', + + cls : 'study-vaccine-design', + + border : false, + + mainTitle : null, + + cellEditField : null, + + // for a dataspace project, some scenarios don't make sense to allow insert/update + disableEdit : false, + + DELETE_ICON_CLS : 'fa fa-trash', + ADD_ICON_CLS : 'fa fa-plus-circle', + + constructor : function(config) + { + this.callParent([config]); + this.addEvents('dirtychange'); + }, + + initComponent : function() + { + this.items = [ + this.getMainTitle(), + this.getDataView() + ]; + + this.callParent(); + }, + + getMainTitle : function() + { + if (!this.mainTitleCmp && this.mainTitle != null) + { + this.mainTitleCmp = Ext4.create('Ext.Component', { + html: '
    ' + Ext4.util.Format.htmlEncode(this.mainTitle) + '
    ' + }); + } + + return this.mainTitleCmp; + }, + + getDataView : function() + { + if (!this.dataView) + { + this.dataView = Ext4.create('Ext.view.View', { + store: this.getStore(), + tpl: this.getDataViewTpl(), + itemSelector: 'tr.row' + }); + + this.dataView.on('itemclick', this.onDataViewItemClick, this); + this.dataView.on('refresh', this.attachAddRowListeners, this, {buffer: 250}); + } + + return this.dataView; + }, + + getDataViewTpl : function() + { + var tplArr = [], columns = this.getColumnConfig(); + + tplArr.push('

    Immunogens

    <%=h(immunogen.getLabel())%><%=h(immunogen.getType())%><%=h(typeLabel != null ? typeLabel : immunogen.getType())%>
    ...
    '); + tplArr = tplArr.concat(this.getTableHeaderRowTpl(columns)); + + // data rows + tplArr.push(''); + tplArr.push(''); + if (!this.disableEdit) + tplArr.push(''); + Ext4.each(columns, function(column) + { + if (Ext4.isString(column.dataIndex)) + { + if (Ext4.isObject(column.subgridConfig) && Ext4.isArray(column.subgridConfig.columns)) + tplArr = tplArr.concat(this.getSubGridTpl(column.dataIndex, column.subgridConfig.columns)); + else + tplArr.push(''); + + // TODO need to show label value if this is a combo/store + } + }, this); + tplArr.push(''); + tplArr.push(''); + + tplArr = tplArr.concat(this.getAddNewRowTpl(columns)); + tplArr.push('
    {' + column.dataIndex + ':htmlEncode}
    '); + + return new Ext4.XTemplate(tplArr); + }, + + getSubGridTpl : function(dataIndex, columns) + { + var tplArr = []; + + tplArr.push(''); + + // only show the subgrid if we are allowing edits of if it has at least one row + tplArr.push(''); + + tplArr.push(''); + tplArr = tplArr.concat(this.getTableHeaderRowTpl(columns)); + + // data rows + tplArr.push(''); + tplArr.push(''); + if (!this.disableEdit) + { + tplArr.push(''); + } + Ext4.each(columns, function(column) + { + if (Ext4.isString(column.dataIndex)) + tplArr.push(''); + }, this); + tplArr.push(''); + tplArr.push(''); + + tplArr = tplArr.concat(this.getAddNewRowTpl(columns, dataIndex)); + tplArr.push('
    '); + tplArr.push(''); + tplArr.push('{' + column.dataIndex + ':htmlEncode}
    '); + tplArr.push('
    '); + tplArr.push(''); + + return tplArr; + }, + + getTableHeaderRowTpl : function(columns) + { + var tplArr = []; + + tplArr.push(''); + if (!this.disableEdit) + tplArr.push(' '); + Ext4.each(columns, function(column) + { + tplArr.push('' + Ext4.util.Format.htmlEncode(column.label) + ''); + }, this); + tplArr.push(''); + + return tplArr; + }, + + getAddNewRowTpl : function(columns, dataIndex) + { + var tplArr = []; + + if (!this.disableEdit) + { + tplArr.push(''); + tplArr.push(' '); + tplArr.push(''); + if (Ext4.isString(dataIndex)) + tplArr.push(' Add new row'); + else + tplArr.push(' Add new row'); + tplArr.push(''); + tplArr.push(''); + } + + return tplArr; + }, + + onDataViewItemClick : function(view, record, item, index, event) + { + if (!this.disableEdit) + { + // handle click on a cell that is editable + if (event.target.getAttribute('class') == 'cell-value' && event.target.hasAttribute('data-index')) + { + if (this.cellEditField != null) + this.clearPreviousCellEditField(); + else + this.createNewCellEditField(event.target, record, index); + } + // handle click on trashcan icon to delete row + else if (event.target.getAttribute('class') == this.DELETE_ICON_CLS) + { + if (event.target.hasAttribute('outer-index')) + { + this.removeOuterRecord(this.mainTitle, record); + } + // handle click on trashcan icon for outer grid + else if (event.target.hasAttribute('subgrid-data-index') && event.target.hasAttribute('subgrid-index')) + { + this.removeSubgridRecord(event.target, record); + } + } + } + }, + + clearPreviousCellEditField : function() + { + if (this.cellEditField != null) + this.updateStoreValueForCellEdit(); + }, + + createNewCellEditField : function(target, record, index) + { + var dataIndex = target.getAttribute('data-index'), + outerDataIndex = target.getAttribute('outer-data-index'), + subgridIndex = Number(target.getAttribute('subgrid-index')), + editor = this.getColumnEditorConfig(dataIndex, outerDataIndex); + + if (editor != null) + { + var currentValue = Ext4.isString(outerDataIndex) + ? record.get(outerDataIndex)[subgridIndex][dataIndex] + : record.get(dataIndex); + + // clear the existing HTML in the td cell + Ext4.get(target).update(''); + + // create a new form field to place in the td cell + this.cellEditField = Ext4.create(editor.type, Ext4.apply(editor.config, { + renderTo: target, + value: currentValue, + storeIndex: index, + outerDataIndex: outerDataIndex, + subgridIndex: subgridIndex + })); + + // add listeners for when to apply the updated value and clear the input field + this.cellEditField.on('blur', this.updateStoreValueForCellEdit, this); + + // give the new field focus after a brief delay, and select text on focus if not a combo + var xtype = this.cellEditField.getXType(); + var isCombo = xtype == 'labkey-combo' || xtype == 'combobox' || xtype == 'combo'; + this.cellEditField.focus(!isCombo, true); + } + }, + + updateStoreValueForCellEdit : function() + { + if (this.cellEditField != null) + { + var fieldName = this.cellEditField.getName(), + newValue = this.cellEditField.getValue(), + index = this.cellEditField.storeIndex, + record = this.getStore().getAt(index), + outerDataIndex = this.cellEditField.outerDataIndex, + subgridIndex = Number(this.cellEditField.subgridIndex); + + if (Ext4.isString(outerDataIndex)) + { + if (!isNaN(subgridIndex) && Ext4.isArray(record.get(outerDataIndex))) + record.get(outerDataIndex)[subgridIndex][fieldName] = newValue; + } + else + record.set(fieldName, newValue); + + this.cellEditField = null; + this.refresh(true); + } + }, + + removeOuterRecord : function(title, record) + { + Ext4.Msg.confirm('Confirm Delete: ' + title, 'Are you sure you want to delete the selected row?', function(btn) + { + if (btn == 'yes') + { + // suspend events on remove so that we don't re-render the dataview twice + this.getStore().suspendEvents(); + this.getStore().remove(record); + this.getStore().resumeEvents(); + + this.refresh(true); + } + }, this); + }, + + removeSubgridRecord : function(target, record) + { + var subgridDataIndex = target.getAttribute('subgrid-data-index'), + subgridArr = record.get(subgridDataIndex); + + if (Ext4.isArray(subgridArr)) + { + Ext4.Msg.confirm('Confirm Delete: ' + subgridDataIndex, 'Are you sure you want to delete the selected row?', function(btn) + { + if (btn == 'yes') + { + subgridArr.splice(target.getAttribute('subgrid-index'), 1); + this.refresh(true); + } + }, this); + } + }, + + attachAddRowListeners : function(view) + { + var addIconEls = Ext4.DomQuery.select('i.' + this.ADD_ICON_CLS.replace(/ /g, '.'), view.getEl().dom); + + Ext4.each(addIconEls, function(addIconEl) + { + if (addIconEl.hasAttribute('data-index')) + Ext4.get(addIconEl).on('click', this.addNewSubgridRow, this); + else + Ext4.get(addIconEl).on('click', this.addNewOuterRow, this); + }, this); + }, + + addNewOuterRow : function() + { + // suspend events on insert so that we don't re-render the dataview twice + this.getStore().suspendEvents(); + this.getStore().insert(this.getStore().getCount(), this.getNewModelInstance()); + this.getStore().resumeEvents(); + + // on refresh, call to give focus to the first column of the new row + this.getDataView().on('refresh', function(){ + var index = this.getStore().getCount() - 1, + selector = 'table.outer tr.row:last td.cell-value:first'; + this.giveLastRowFocus(index, selector); + }, this, {single: true}); + + this.refresh(true); + }, + + addNewSubgridRow : function(event, target) + { + var dataIndex = target.getAttribute('data-index'), + rowIndex = Number(target.getAttribute('outer-index')); + + if (Ext4.isString(dataIndex) && Ext4.isNumber(rowIndex)) + { + var record = this.getStore().getAt(rowIndex), + dataIndexArr = record.get(dataIndex); + + if (Ext4.isArray(dataIndexArr)) + record.set(dataIndex, dataIndexArr.concat([{}])); + + // on refresh, call to give focus to the first column of the new row + this.getDataView().on('refresh', function(){ + var index = rowIndex, + selector = 'table.subgrid:nth(' + (rowIndex+1) + ') tr.subrow:last td.cell-value:first'; + this.giveLastRowFocus(index, selector); + }, this, {single: true}); + + this.refresh(true); + } + }, + + giveLastRowFocus : function(index, selector) + { + var lastRowFirstCell = Ext4.DomQuery.select(selector, this.getDataView().getEl().dom), + record = this.getStore().getAt(index); + + if (Ext4.isArray(lastRowFirstCell) && lastRowFirstCell.length == 1) + this.createNewCellEditField(Ext4.get(lastRowFirstCell[0]), record, index); + }, + + refresh : function(hasChanges) + { + this.getDataView().refresh(); + + if (hasChanges) + this.fireEvent('dirtychange', this); + }, + + getColumnEditorConfig : function(dataIndex, parentDataIndex) + { + var editor = null, columns = this.getColumnConfig(); + + // if the parentDataIndex is defined, then we are looking for the subgrid column editor config + if (Ext4.isString(parentDataIndex)) + { + var colIndex = Ext4.pluck(columns, 'dataIndex').indexOf(parentDataIndex); + if (colIndex > -1 && columns[colIndex].hasOwnProperty('subgridConfig') && Ext4.isArray(columns[colIndex].subgridConfig.columns)) + columns = columns[colIndex].subgridConfig.columns; + else + return null; + } + + Ext4.each(columns, function(column) + { + if (column.dataIndex == dataIndex && column.hasOwnProperty('editorType') && column.hasOwnProperty('editorConfig')) + { + editor = { + type: column.editorType, + config: column.editorConfig + }; + + return false; // break; + } + }, this); + + return editor; + }, + + onFailure : function(text) + { + Ext4.Msg.show({ + cls: 'data-window', + title: 'Error', + msg: text || 'Unknown error occurred.', + icon: Ext4.Msg.ERROR, + buttons: Ext4.Msg.OK + }); + }, + + getStore : function() + { + throw "getStore must be overridden in subclass"; + }, + + getNewModelInstance : function() + { + throw "getNewModelInstance must be overridden in subclass"; + }, + + getColumnConfig : function() + { + throw "getColumnConfig must be overridden in subclass"; + } +}); \ No newline at end of file diff --git a/study/webapp/study/vaccineDesign/Models.js b/study/webapp/study/vaccineDesign/Models.js new file mode 100644 index 00000000..acd245e8 --- /dev/null +++ b/study/webapp/study/vaccineDesign/Models.js @@ -0,0 +1,12 @@ + +Ext4.define('LABKEY.VaccineDesign.Product', { + extend : 'Ext.data.Model', + idgen: 'sequential', + fields : [ + {name : 'RowId', defaultValue: undefined}, + {name : 'Label', type : 'string'}, + {name : 'Role', type : 'string'}, + {name : 'Type', type : 'string'}, + {name : 'Antigens', defaultValue: []} + ] +}); \ No newline at end of file diff --git a/study/webapp/study/vaccineDesign/StudyProducts.js b/study/webapp/study/vaccineDesign/StudyProducts.js new file mode 100644 index 00000000..550faf96 --- /dev/null +++ b/study/webapp/study/vaccineDesign/StudyProducts.js @@ -0,0 +1,128 @@ +/* + * Copyright (c) 2013-2014 LabKey Corporation + * + * Licensed under the Apache License, Version 2.0: http://www.apache.org/licenses/LICENSE-2.0 + */ + +Ext4.define('LABKEY.VaccineDesign.StudyProductsGrid', { + + extend : 'LABKEY.VaccineDesign.BaseDataView', + + filterRole : null, + + //Override - see LABKEY.VaccineDesign.BaseDataView + getStore : function() + { + if (!this.store) + { + this.store = Ext4.create('Ext.data.Store', { + model : 'LABKEY.VaccineDesign.Product', + proxy: { + type: 'ajax', + url : LABKEY.ActionURL.buildURL("study-design", "getStudyProducts", null, {role: this.filterRole}), + reader: { + type: 'json', + root: 'products' + } + }, + sorters: [{ property: 'RowId', direction: 'ASC' }], + autoLoad: true + }); + } + + return this.store; + }, + + //Override - see LABKEY.VaccineDesign.BaseDataView + getNewModelInstance : function() + { + return LABKEY.VaccineDesign.Product.create({Role: this.filterRole}); + } +}); + +Ext4.define('LABKEY.VaccineDesign.ImmunogensGrid', { + extend : 'LABKEY.VaccineDesign.StudyProductsGrid', + width: 1100, + mainTitle : 'Immunogens', + filterRole : 'Immunogen', + hiddenColumns : ["RowId", "Role"], + + //Override - see LABKEY.VaccineDesign.BaseDataView + getColumnConfig : function() + { + if (!this.columnConfigs) + { + this.columnConfigs = [{ + label: 'Label', + width: 200, + dataIndex: 'Label', + editorType: 'Ext.form.field.Text', + editorConfig: LABKEY.VaccineDesign.Utils.getStudyDesignFieldEditorConfig('Label', true, 190) + }, { + label: 'Type', + width: 200, + dataIndex: 'Type', + editorType: 'LABKEY.ext4.ComboBox', + editorConfig: LABKEY.VaccineDesign.Utils.getStudyDesignFieldEditorConfig('Type', false, 190, 'StudyDesignImmunogenTypes') + }, { + label: 'HIV Antigens', + width: 700, + dataIndex: 'Antigens', + subgridConfig: { + columns: [{ + label: 'Gene', + width: 140, + dataIndex: 'Gene', + editorType: 'LABKEY.ext4.ComboBox', + editorConfig: LABKEY.VaccineDesign.Utils.getStudyDesignFieldEditorConfig('Gene', false, 130, 'StudyDesignGenes') + },{ + label: 'Subtype', + width: 140, + dataIndex: 'SubType', + editorType: 'LABKEY.ext4.ComboBox', + editorConfig: LABKEY.VaccineDesign.Utils.getStudyDesignFieldEditorConfig('SubType', false, 130, 'StudyDesignSubTypes') + },{ + label: 'GenBank Id', + width: 190, + dataIndex: 'GenBankId', + editorType: 'Ext.form.field.Text', + editorConfig: LABKEY.VaccineDesign.Utils.getStudyDesignFieldEditorConfig('GenBankId', false, 180) + },{ + label: 'Sequence', + width: 210, + dataIndex: 'Sequence', + editorType: 'Ext.form.field.Text', + editorConfig: LABKEY.VaccineDesign.Utils.getStudyDesignFieldEditorConfig('Sequence', false, 200) + }] + } + }]; + } + + return this.columnConfigs; + } +}); + +Ext4.define('LABKEY.VaccineDesign.AdjuvantsGrid', { + extend : 'LABKEY.VaccineDesign.StudyProductsGrid', + width : 400, + mainTitle : 'Adjuvants', + filterRole : 'Adjuvant', + hiddenColumns : ["RowId", "Role", "Type", "Antigens"], + + //Override - see LABKEY.VaccineDesign.BaseDataView + getColumnConfig : function() + { + if (!this.columnConfigs) + { + this.columnConfigs = [{ + label: 'Label', + width: 400, + dataIndex: 'Label', + editorType: 'Ext.form.field.Text', + editorConfig: LABKEY.VaccineDesign.Utils.getStudyDesignFieldEditorConfig('Label', true, 363) + }]; + } + + return this.columnConfigs; + } +}); \ No newline at end of file diff --git a/study/webapp/study/vaccineDesign/Utils.js b/study/webapp/study/vaccineDesign/Utils.js new file mode 100644 index 00000000..917b8484 --- /dev/null +++ b/study/webapp/study/vaccineDesign/Utils.js @@ -0,0 +1,69 @@ + +Ext4.define('LABKEY.VaccineDesign.Utils', { + + singleton: true, + + /** + * Helper function to get field editor config object for a study design lookup combo or a text field. + * @param name Field name + * @param required Whether or not this field should be allowed to be blank + * @param width Field width + * @param queryName If combo, the queryName for the store + * @param displayField The field name of the combo store displayField + * @returns {Object} Field config + */ + getStudyDesignFieldEditorConfig : function(name, required, width, queryName, displayField) + { + if (queryName != undefined && queryName != null) + { + // config for LABKEY.ext4.ComboBox + return { + hideFieldLabel: true, + name: name, + width: width || 150, + allowBlank: !required, + forceSelection : false, // allow usage of inactive types + editable : false, + queryMode : 'local', + // TODO: this does not htmlEncode the display value in expanded options list + displayField : displayField || 'Label', + valueField : 'Name', + store : LABKEY.VaccineDesign.Utils.getStudyDesignStore(queryName) + }; + } + else + { + // config for Ext.form.field.Text + return { + hideFieldLabel: true, + name: name, + width: width || 150, + allowBlank: !required + } + } + }, + + /** + * Create a new LABKEY.ext4.Store for the given queryName from the study schema. + * @param queryName + * @returns {LABKEY.ext4.Store} + */ + getStudyDesignStore : function(queryName) + { + return Ext4.create('LABKEY.ext4.Store', { + schemaName: 'study', + queryName: queryName, + columns: 'Name,Label', + filterArray: [LABKEY.Filter.create('Inactive', false)], + containerFilter: LABKEY.container.type == 'project' ? 'Current' : 'CurrentPlusProject', + sort: '-Container/Path,Label', + autoLoad: true, + listeners: { + load: function(store) + { + store.insert(0, {Name: null}); + } + } + }) + } +}); \ No newline at end of file diff --git a/study/webapp/study/vaccineDesign/VaccineDesign.css b/study/webapp/study/vaccineDesign/VaccineDesign.css new file mode 100644 index 00000000..0986d86c --- /dev/null +++ b/study/webapp/study/vaccineDesign/VaccineDesign.css @@ -0,0 +1,52 @@ + +.study-vaccine-design .x4-panel-body { + background-color: transparent; +} + +.study-vaccine-design .main-title { + font-weight: bold; + font-size: 18px; + padding-bottom: 5px; +} + +.study-vaccine-design table.outer, +.study-vaccine-design table.subgrid { + border-collapse: collapse; +} + +.study-vaccine-design td.cell-display, +.study-vaccine-design td.cell-value { + padding: 3px 5px; + border: solid 1px #DDDDDD; +} + +.study-vaccine-design table.subgrid td { + background-color: #FFFFFF !important; +} + +.study-vaccine-design tr.header-row td { + font-weight: bold; + padding: 5px; + background-color: #EEEEEE !important; + border-bottom-color: #C0C0C0; +} + +.study-vaccine-design table.outer tr.row td { + background-color: #FFFFFF; +} +.study-vaccine-design table.outer tr.alternate-row td { + background-color: #F4F4F4; +} + +.study-vaccine-design td.cell-value { + cursor: pointer; + height: 30px; +} + +.study-vaccine-design td.action i { + color: #777777; +} +.study-vaccine-design td.action i:hover { + cursor: pointer; + color: #000000; +} \ No newline at end of file diff --git a/study/webapp/study/vaccineDesign/vaccineDesign.lib.xml b/study/webapp/study/vaccineDesign/vaccineDesign.lib.xml new file mode 100644 index 00000000..afee02e3 --- /dev/null +++ b/study/webapp/study/vaccineDesign/vaccineDesign.lib.xml @@ -0,0 +1,11 @@ + + + Enter vaccine design information in the grids below. @@ -125,16 +218,22 @@ Enter vaccine design information in the grids below. study specific properties:
  • Each immunogen and adjuvant in the study should be listed on one row of the grids below.
  • -
  • Immunogens and adjuvants should have unique names.
  • +
  • Immunogens and adjuvants should have unique labels.
  • If possible, the immunogen description should include specific sequences of HIV Antigens included in the immunogen.
  • -
  • Use the manage treatments page to describe the schedule of treatments and combinations of immunogens and adjuvants administered at each timepoint.
  • +
  • + Use the manage treatments page to describe the schedule of treatments and combinations of immunogens and adjuvants administered at each timepoint. + <% + ActionURL manageTreatmentsURL = new ActionURL(StudyDesignController.ManageTreatmentsAction.class, getContainer()); + manageTreatmentsURL.addReturnURL(getActionURL()); + %> + <%=textLink("Manage Treatments", manageTreatmentsURL)%> +
  • -* Double click a row to edit the label and type, double click the HIV Antigens cell to edit them separately -

    +
    -* Double click a row to edit the label -

    -<%=textLink("Manage Treatments", StudyDesignController.ManageTreatmentsAction.class)%> +
    + + diff --git a/study/src/org/labkey/study/view/studydesign/manageTreatments.jsp b/study/src/org/labkey/study/view/studydesign/manageTreatments.jsp index 70c52991..15cbe568 100644 --- a/study/src/org/labkey/study/view/studydesign/manageTreatments.jsp +++ b/study/src/org/labkey/study/view/studydesign/manageTreatments.jsp @@ -146,7 +146,11 @@ Enter treatment information in the grids below.
    * Double click to edit a treatment record and its product definition

    -<%=textLink("Manage Study Products", StudyDesignController.ManageStudyProductsAction.class)%> +<% + ActionURL manageStudyProductsURL = new ActionURL(StudyDesignController.ManageStudyProductsAction.class, getContainer()); + manageStudyProductsURL.addReturnURL(getActionURL()); +%> +<%=textLink("Manage Study Products", manageStudyProductsURL)%>

    * Double click to edit a group/cohort and its treatment/visit map definition
    diff --git a/study/src/org/labkey/study/view/studydesign/vaccineDesignWebpart.jsp b/study/src/org/labkey/study/view/studydesign/vaccineDesignWebpart.jsp index d12b33a5..a61d054a 100644 --- a/study/src/org/labkey/study/view/studydesign/vaccineDesignWebpart.jsp +++ b/study/src/org/labkey/study/view/studydesign/vaccineDesignWebpart.jsp @@ -18,39 +18,30 @@ <%@ page import="org.labkey.api.data.Container" %> <%@ page import="org.labkey.api.security.User" %> <%@ page import="org.labkey.api.security.permissions.UpdatePermission" %> -<%@ page import="org.labkey.api.view.HttpView" %> -<%@ page import="org.labkey.api.view.JspView" %> -<%@ page import="org.labkey.api.view.Portal" %> <%@ page import="org.labkey.api.view.WebThemeManager" %> <%@ page import="org.labkey.api.view.template.ClientDependencies" %> <%@ page import="org.labkey.study.controllers.StudyDesignController" %> -<%@ page import="org.labkey.study.model.ProductImpl" %> <%@ page import="org.labkey.study.model.StudyImpl" %> <%@ page import="org.labkey.study.model.StudyManager" %> -<%@ page import="org.labkey.study.model.TreatmentManager" %> -<%@ page import="java.util.List" %> +<%@ page import="org.labkey.api.view.ActionURL" %> <%@ page extends="org.labkey.study.view.BaseStudyPage" %> <%! @Override public void addClientDependencies(ClientDependencies dependencies) { - dependencies.add("Ext4ClientApi"); - dependencies.add("study/StudyVaccineDesign.js"); - dependencies.add("study/StudyVaccineDesign.css"); + dependencies.add("study/vaccineDesign/vaccineDesign.lib.xml"); + dependencies.add("study/vaccineDesign/VaccineDesign.css"); } %> <% - JspView me = (JspView) HttpView.currentView(); - Container c = getContainer(); - StudyImpl study = StudyManager.getInstance().getStudy(c); User user = getUser(); - boolean canEdit = c.hasPermission(user, UpdatePermission.class); + Container container = getContainer(); + StudyImpl study = StudyManager.getInstance().getStudy(container); %> @@ -59,81 +50,19 @@ if (study != null) { %>This section describes the immunogens and adjuvants evaluated in the study.
    <% - if (canEdit) - { -%> - To change the set of immunogens and adjuvants, click the edit button below.
    - <%= button("Edit").href(StudyDesignController.ManageStudyProductsAction.class, getContainer()) %> -<% - } -%> - - -<% - List immunogens = study.getStudyProducts(user, "Immunogen"); - if (immunogens.size() == 0) + if (container.hasPermission(user, UpdatePermission.class)) { - %><% - } - else - { -%> - + <%=textLink("Manage Study Products", editUrl)%>
    <% } %> - -<% - List adjuvants = study.getStudyProducts(user, "Adjuvant"); - if (adjuvants.size() == 0) - { - %><% - } - else - { -%> - -<% - } -%> -
    No immunogens have been defined. - - - - - - - - - -<% - for (ProductImpl immunogen : immunogens) - { - String typeLabel = TreatmentManager.getInstance().getStudyDesignImmunogenTypeLabelByName(c, immunogen.getType()); + ActionURL editUrl = new ActionURL(StudyDesignController.ManageStudyProductsAction.class, getContainer()); + editUrl.addReturnURL(getActionURL()); %> - - - - - -<% - } -%> -
    Immunogens
    LabelTypeHIV Antigens
    <%=h(immunogen.getLabel())%><%=h(typeLabel != null ? typeLabel : immunogen.getType())%>
    ...
    -
     No adjuvants have been defined. - - - - - -<% - for (ProductImpl adjuvant : adjuvants) - { - %><% - } -%> -
    Adjuvants
    Label
    <%=h(adjuvant.getLabel())%>
    -
    +
    +
    +
    +
    <% } else @@ -145,25 +74,17 @@ %> \ No newline at end of file diff --git a/study/webapp/study/vaccineDesign/BaseDataView.js b/study/webapp/study/vaccineDesign/BaseDataView.js index b2168b93..e5b01493 100644 --- a/study/webapp/study/vaccineDesign/BaseDataView.js +++ b/study/webapp/study/vaccineDesign/BaseDataView.js @@ -90,7 +90,9 @@ Ext4.define('LABKEY.VaccineDesign.BaseDataView', { getDataViewTpl : function() { - var tplArr = [], columns = this.getColumnConfig(); + var tplArr = [], + tdCls = this.disableEdit ? 'cell-display' : 'cell-value', + columns = this.getColumnConfig(); tplArr.push(''); tplArr = tplArr.concat(this.getTableHeaderRowTpl(columns)); @@ -104,25 +106,30 @@ Ext4.define('LABKEY.VaccineDesign.BaseDataView', { { if (Ext4.isString(column.dataIndex)) { + var checkMissingReqTpl = ''; + if (column.required) + checkMissingReqTpl = ' {[this.checkMissingRequired(values, "' + column.dataIndex + '")]}'; + if (Ext4.isObject(column.subgridConfig) && Ext4.isArray(column.subgridConfig.columns)) { tplArr = tplArr.concat(this.getSubGridTpl(column.dataIndex, column.subgridConfig.columns)); } else if (Ext4.isString(column.queryName)) { - tplArr.push(''); } else { - tplArr.push(''); + tplArr.push(''); } } }, this); tplArr.push(''); tplArr.push(''); + tplArr = tplArr.concat(this.getEmptyTableTpl(columns)); tplArr = tplArr.concat(this.getAddNewRowTpl(columns)); tplArr.push('
    ' + tplArr.push('' + '{[this.getLabelFromStore(values["' + column.dataIndex + '"], "' + column.queryName + '")]}' + '{' + column.dataIndex + ':htmlEncode}{' + column.dataIndex + ':htmlEncode}
    '); @@ -132,6 +139,13 @@ Ext4.define('LABKEY.VaccineDesign.BaseDataView', { val = LABKEY.VaccineDesign.Utils.getLabelFromStore(queryName, val); return Ext4.util.Format.htmlEncode(val); + }, + + checkMissingRequired : function(values, dataIndex) { + if (Ext4.isDefined(values['RowId']) && (values[dataIndex] == null || values[dataIndex] == '')) + return ' missing-required'; + + return ''; } }); @@ -140,7 +154,8 @@ Ext4.define('LABKEY.VaccineDesign.BaseDataView', { getSubGridTpl : function(dataIndex, columns) { - var tplArr = []; + var tplArr = [], + tdCls = this.disableEdit ? 'cell-display' : 'cell-value'; tplArr.push(''); @@ -165,14 +180,18 @@ Ext4.define('LABKEY.VaccineDesign.BaseDataView', { { if (Ext4.isString(column.queryName)) { - tplArr.push('' + '{[this.getLabelFromStore(values["' + column.dataIndex + '"], "' + column.queryName + '")]}' + ''); } else { - tplArr.push('{' + column.dataIndex + ':htmlEncode}'); } } @@ -204,6 +223,22 @@ Ext4.define('LABKEY.VaccineDesign.BaseDataView', { return tplArr; }, + getEmptyTableTpl : function(columns) + { + var tplArr = []; + + if (this.disableEdit) + { + tplArr.push(''); + tplArr.push(''); + tplArr.push('No data to show.'); + tplArr.push(''); + tplArr.push(''); + } + + return tplArr; + }, + getAddNewRowTpl : function(columns, dataIndex) { var tplArr = []; @@ -229,7 +264,7 @@ Ext4.define('LABKEY.VaccineDesign.BaseDataView', { if (!this.disableEdit) { // handle click on a cell that is editable - if (event.target.getAttribute('class') == 'cell-value' && event.target.hasAttribute('data-index')) + if (event.target.getAttribute('class').indexOf('cell-value') > -1 && event.target.hasAttribute('data-index')) { if (this.cellEditField != null) this.clearPreviousCellEditField(); @@ -319,7 +354,9 @@ Ext4.define('LABKEY.VaccineDesign.BaseDataView', { removeOuterRecord : function(title, record) { - Ext4.Msg.confirm('Confirm Delete: ' + title, 'Are you sure you want to delete the selected row?', function(btn) + var msg = this.getDeleteConfirmationMsg() != null ? this.getDeleteConfirmationMsg() : 'Are you sure you want to delete the selected row?'; + + Ext4.Msg.confirm('Confirm Delete: ' + title, msg, function(btn) { if (btn == 'yes') { @@ -482,5 +519,10 @@ Ext4.define('LABKEY.VaccineDesign.BaseDataView', { getColumnConfig : function() { throw "getColumnConfig must be overridden in subclass"; + }, + + getDeleteConfirmationMsg : function() + { + return null; } }); \ No newline at end of file diff --git a/study/webapp/study/vaccineDesign/StudyProducts.js b/study/webapp/study/vaccineDesign/StudyProducts.js index 2b4f5f92..f630eaf5 100644 --- a/study/webapp/study/vaccineDesign/StudyProducts.js +++ b/study/webapp/study/vaccineDesign/StudyProducts.js @@ -37,6 +37,14 @@ Ext4.define('LABKEY.VaccineDesign.StudyProductsGrid', { getNewModelInstance : function() { return LABKEY.VaccineDesign.Product.create({Role: this.filterRole}); + }, + + //Override - see LABKEY.VaccineDesign.BaseDataView + getDeleteConfirmationMsg : function() + { + return 'Are you sure you want to delete the selected study product?

    ' + + 'Note: if this study product is being used by any treatment definitions, ' + + 'those associations will also be deleted upon save.'; } }); @@ -56,6 +64,7 @@ Ext4.define('LABKEY.VaccineDesign.ImmunogensGrid', { label: 'Label', width: 200, dataIndex: 'Label', + required: true, editorType: 'Ext.form.field.Text', editorConfig: LABKEY.VaccineDesign.Utils.getStudyDesignFieldEditorConfig('Label', true, 190) }, { @@ -121,6 +130,7 @@ Ext4.define('LABKEY.VaccineDesign.AdjuvantsGrid', { label: 'Label', width: 400, dataIndex: 'Label', + required: true, editorType: 'Ext.form.field.Text', editorConfig: LABKEY.VaccineDesign.Utils.getStudyDesignFieldEditorConfig('Label', true, 363) }]; diff --git a/study/webapp/study/vaccineDesign/VaccineDesign.css b/study/webapp/study/vaccineDesign/VaccineDesign.css index 0986d86c..545e0800 100644 --- a/study/webapp/study/vaccineDesign/VaccineDesign.css +++ b/study/webapp/study/vaccineDesign/VaccineDesign.css @@ -5,7 +5,7 @@ .study-vaccine-design .main-title { font-weight: bold; - font-size: 18px; + font-size: 16px; padding-bottom: 5px; } @@ -18,6 +18,7 @@ .study-vaccine-design td.cell-value { padding: 3px 5px; border: solid 1px #DDDDDD; + height: 30px; } .study-vaccine-design table.subgrid td { @@ -40,7 +41,14 @@ .study-vaccine-design td.cell-value { cursor: pointer; - height: 30px; +} + +.study-vaccine-design td.cell-value.missing-required { + background-color: #ffe5e5 !important; +} + +.study-vaccine-design td.empty { + font-style: italic; } .study-vaccine-design td.action i { From b63888d7b937c31405fcfdde63a0b55d80a1e8c6 Mon Sep 17 00:00:00 2001 From: Cory Nathe Date: Tue, 13 Sep 2016 16:10:56 +0000 Subject: [PATCH 091/218] Spec #27492: disable StudyProtocolDesignerTest case - for now, until the test component helpers are updated for new UI --- .../study/controllers/StudyDesignController.java | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/study/src/org/labkey/study/controllers/StudyDesignController.java b/study/src/org/labkey/study/controllers/StudyDesignController.java index 6904c066..08afaa22 100644 --- a/study/src/org/labkey/study/controllers/StudyDesignController.java +++ b/study/src/org/labkey/study/controllers/StudyDesignController.java @@ -312,22 +312,6 @@ public ApiResponse execute(IdForm form, BindException errors) throws Exception } } - // TODO this can be removed after the manage study products page migration is complete - @RequiresPermission(DeletePermission.class) - public class DeleteStudyProductAction extends MutatingApiAction - { - @Override - public ApiResponse execute(IdForm form, BindException errors) throws Exception - { - if (form.getId() != 0) - { - TreatmentManager.getInstance().deleteStudyProduct(getContainer(), getUser(), form.getId()); - return new ApiSimpleResponse("success", true); - } - return new ApiSimpleResponse("success", false); - } - } - @RequiresPermission(UpdatePermission.class) public class UpdateStudyProductsAction extends MutatingApiAction { From 1661d7fc5208fab8d14ef1ff92f4d214ca6640a9 Mon Sep 17 00:00:00 2001 From: Dave Bradlee Date: Thu, 15 Sep 2016 18:13:24 +0000 Subject: [PATCH 092/218] Issue 25260: Rename the DataIterator "ETL" to avoid confusion --- .../study/query/studydesign/StudyDesignLookupBaseTable.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/study/src/org/labkey/study/query/studydesign/StudyDesignLookupBaseTable.java b/study/src/org/labkey/study/query/studydesign/StudyDesignLookupBaseTable.java index b3ff8439..c7a42f5b 100644 --- a/study/src/org/labkey/study/query/studydesign/StudyDesignLookupBaseTable.java +++ b/study/src/org/labkey/study/query/studydesign/StudyDesignLookupBaseTable.java @@ -22,8 +22,8 @@ import org.labkey.api.data.ContainerFilter; import org.labkey.api.data.DatabaseTableType; import org.labkey.api.data.TableInfo; -import org.labkey.api.etl.DataIteratorBuilder; -import org.labkey.api.etl.DataIteratorContext; +import org.labkey.api.dataiterator.DataIteratorBuilder; +import org.labkey.api.dataiterator.DataIteratorContext; import org.labkey.api.query.DefaultQueryUpdateService; import org.labkey.api.query.DuplicateKeyException; import org.labkey.api.query.FieldKey; From a5c8ec3348738b450fb6e3c612c6de3728d4053c Mon Sep 17 00:00:00 2001 From: Cory Nathe Date: Fri, 16 Sep 2016 02:41:34 +0000 Subject: [PATCH 093/218] Spec #27492: checkpoint for Manage Treatment page update to new UI for display and inline cell editing - Convert treatments grid to inline cell editing for label and description - Add subgrid data entry columns for treatment products (i.e. immunogens and adjuvants) - Change treatment schedule grid to be editable inline for all cells - NOTE: leaving the manageTreatments.jsp using the old UI until save task is complete --- .../view/studydesign/manageStudyProducts.jsp | 124 +--- .../study/vaccineDesign/BaseDataView.js | 154 +++-- .../study/vaccineDesign/StudyProducts.js | 221 ++++++- .../study/vaccineDesign/TreatmentSchedule.js | 547 ++++++++++++++++++ study/webapp/study/vaccineDesign/Utils.js | 157 +++-- .../study/vaccineDesign/VaccineDesign.css | 3 +- .../study/vaccineDesign/vaccineDesign.lib.xml | 1 + 7 files changed, 981 insertions(+), 226 deletions(-) create mode 100644 study/webapp/study/vaccineDesign/TreatmentSchedule.js diff --git a/study/src/org/labkey/study/view/studydesign/manageStudyProducts.jsp b/study/src/org/labkey/study/view/studydesign/manageStudyProducts.jsp index 2f5bc403..7d242681 100644 --- a/study/src/org/labkey/study/view/studydesign/manageStudyProducts.jsp +++ b/study/src/org/labkey/study/view/studydesign/manageStudyProducts.jsp @@ -47,118 +47,12 @@ Enter vaccine design information in the grids below. @@ -230,10 +124,4 @@ Enter vaccine design information in the grids below. -
    -
    -
    -
    - - - +
    diff --git a/study/webapp/study/vaccineDesign/BaseDataView.js b/study/webapp/study/vaccineDesign/BaseDataView.js index e5b01493..6883ba92 100644 --- a/study/webapp/study/vaccineDesign/BaseDataView.js +++ b/study/webapp/study/vaccineDesign/BaseDataView.js @@ -13,7 +13,7 @@ Ext4.define('LABKEY.VaccineDesign.BaseDataView', { studyDesignQueryNames : null, - // for a dataspace project, some scenarios don't make sense to allow insert/update + // for a DataSpace project, some scenarios don't make sense to allow insert/update disableEdit : false, DELETE_ICON_CLS : 'fa fa-trash', @@ -22,14 +22,14 @@ Ext4.define('LABKEY.VaccineDesign.BaseDataView', { constructor : function(config) { this.callParent([config]); - this.addEvents('dirtychange'); + this.addEvents('dirtychange', 'loadcomplete', 'celledited', 'beforerowdeleted'); }, initComponent : function() { this.items = [ - this.getMainTitle(), - this.getDataView() + this.getMainTitle() + // Note: this.getDataView() will be added after the store loads in this.loadDataViewStore() ]; // Pre-load the study design lookup queries that will be used in dropdowns for this page. @@ -46,13 +46,13 @@ Ext4.define('LABKEY.VaccineDesign.BaseDataView', { loadCounter++; if (loadCounter == this.studyDesignQueryNames.length) - this.bindDataViewStore(); + this.loadDataViewStore(); }, this); }, this); } else { - this.bindDataViewStore(); + this.loadDataViewStore(); } this.callParent(); @@ -74,10 +74,9 @@ Ext4.define('LABKEY.VaccineDesign.BaseDataView', { { if (!this.dataView) { - // NOTE: store will bind in this.bindDataViewStore() this.dataView = Ext4.create('Ext.view.View', { - hidden: true, tpl: this.getDataViewTpl(), + store: this.getStore(), itemSelector: 'tr.row' }); @@ -92,7 +91,7 @@ Ext4.define('LABKEY.VaccineDesign.BaseDataView', { { var tplArr = [], tdCls = this.disableEdit ? 'cell-display' : 'cell-value', - columns = this.getColumnConfig(); + columns = this.getColumnConfigs(); tplArr.push(''); tplArr = tplArr.concat(this.getTableHeaderRowTpl(columns)); @@ -120,9 +119,20 @@ Ext4.define('LABKEY.VaccineDesign.BaseDataView', { + '{[this.getLabelFromStore(values["' + column.dataIndex + '"], "' + column.queryName + '")]}' + ''); } + else if (Ext4.isString(column.lookupStoreId) && Ext4.isDefined(column.dataIndexArrFilterValue)) + { + tplArr.push(''); + } else { - tplArr.push(''); + tplArr.push(''); } } }, this); @@ -134,15 +144,48 @@ Ext4.define('LABKEY.VaccineDesign.BaseDataView', { tplArr.push('
    ' + + '{[this.getDisplayValue(values["' + column.dataIndex + '"], ' + + '"' + column.dataIndexArrFilterProp + '", "' + column.dataIndexArrFilterValue + '", ' + + '"' + column.dataIndexArrValue + '", "' + column.lookupStoreId + '")]}' + + '{' + column.dataIndex + ':htmlEncode}' + + '{[this.getDisplayValue(values["' + column.dataIndex + '"])]}' + + '
    '); tplArr.push({ - getLabelFromStore : function(val, queryName) { + getDisplayValue : function(val, arrPropFilterName, arrPropFilterVal, arrPropDisplayField, lookupStoreId) + { + // allow showing a certain filtered row from an array + if (Ext4.isDefined(arrPropDisplayField)) + { + var matchingIndex = LABKEY.VaccineDesign.Utils.getMatchingRowIndexFromArray(val, arrPropFilterName, arrPropFilterVal); + if (matchingIndex > -1 && Ext4.isObject(val[matchingIndex])) + val = val[matchingIndex][arrPropDisplayField]; + else + val = ''; + } + + // if we have a specific lookupStoreId, get the label value from the matching RowId record in that store + if (Ext4.isDefined(lookupStoreId) && val != null && val != '') + { + var store = Ext4.getStore(lookupStoreId); + if (store != null) + { + var record = store.findRecord('RowId', val); + if (record != null) + val = record.get('Label'); + } + } + + // need to htmlEncode and then handle newlines in multiline text fields (i.e. Treatment/Description) + val = Ext4.util.Format.htmlEncode(val); + val = val.replace(/\n/g, '
    '); + + return val; + }, + + getLabelFromStore : function(val, queryName) + { if (val != null && val != '') val = LABKEY.VaccineDesign.Utils.getLabelFromStore(queryName, val); return Ext4.util.Format.htmlEncode(val); }, - checkMissingRequired : function(values, dataIndex) { - if (Ext4.isDefined(values['RowId']) && (values[dataIndex] == null || values[dataIndex] == '')) + checkMissingRequired : function(values, dataIndex) + { + if (values[dataIndex] == null || values[dataIndex] == '') return ' missing-required'; return ''; @@ -162,7 +205,7 @@ Ext4.define('LABKEY.VaccineDesign.BaseDataView', { // only show the subgrid if we are allowing edits of if it has at least one row tplArr.push(''); - tplArr.push(''); + tplArr.push('
    '); tplArr = tplArr.concat(this.getTableHeaderRowTpl(columns)); // data rows @@ -296,24 +339,25 @@ Ext4.define('LABKEY.VaccineDesign.BaseDataView', { createNewCellEditField : function(target, record, index) { var dataIndex = target.getAttribute('data-index'), + dataFilterValue = target.getAttribute('data-filter-value'), outerDataIndex = target.getAttribute('outer-data-index'), subgridIndex = Number(target.getAttribute('subgrid-index')), - editor = this.getColumnEditorConfig(dataIndex, outerDataIndex); + column = this.getColumnConfig(dataIndex, dataFilterValue, outerDataIndex), + editor = this.getColumnEditorConfig(column); if (editor != null) { - var currentValue = Ext4.isString(outerDataIndex) - ? record.get(outerDataIndex)[subgridIndex][dataIndex] - : record.get(dataIndex); - - // clear the existing HTML in the td cell - Ext4.get(target).update(''); + // clear the existing HTML in the td cell and remove the missing-required cls while in edit mode + var targetEl = Ext4.get(target); + targetEl.update(''); + targetEl.removeCls('missing-required'); // create a new form field to place in the td cell this.cellEditField = Ext4.create(editor.type, Ext4.apply(editor.config, { renderTo: target, - value: currentValue, + value: this.getCurrentCellValue(column, record, dataIndex, outerDataIndex, subgridIndex), storeIndex: index, + dataFilterValue: dataFilterValue, outerDataIndex: outerDataIndex, subgridIndex: subgridIndex })); @@ -328,6 +372,11 @@ Ext4.define('LABKEY.VaccineDesign.BaseDataView', { } }, + getCurrentCellValue : function(column, record, dataIndex, outerDataIndex, subgridIndex) + { + return Ext4.isString(outerDataIndex) ? record.get(outerDataIndex)[subgridIndex][dataIndex] : record.get(dataIndex); + }, + updateStoreValueForCellEdit : function() { if (this.cellEditField != null) @@ -336,6 +385,7 @@ Ext4.define('LABKEY.VaccineDesign.BaseDataView', { newValue = this.cellEditField.getValue(), index = this.cellEditField.storeIndex, record = this.getStore().getAt(index), + dataFilterValue = this.cellEditField.dataFilterValue, outerDataIndex = this.cellEditField.outerDataIndex, subgridIndex = Number(this.cellEditField.subgridIndex); @@ -345,13 +395,22 @@ Ext4.define('LABKEY.VaccineDesign.BaseDataView', { record.get(outerDataIndex)[subgridIndex][fieldName] = newValue; } else - record.set(fieldName, newValue); + { + var column = this.getColumnConfig(fieldName, dataFilterValue, outerDataIndex); + this.updateStoreRecordValue(record, column, newValue); + } this.cellEditField = null; this.refresh(true); } }, + updateStoreRecordValue : function(record, column, newValue) + { + record.set(column.dataIndex, newValue); + this.fireEvent('celledited', this); + }, + removeOuterRecord : function(title, record) { var msg = this.getDeleteConfirmationMsg() != null ? this.getDeleteConfirmationMsg() : 'Are you sure you want to delete the selected row?'; @@ -360,6 +419,8 @@ Ext4.define('LABKEY.VaccineDesign.BaseDataView', { { if (btn == 'yes') { + this.fireEvent('beforerowdeleted', this, record); + // suspend events on remove so that we don't re-render the dataview twice this.getStore().suspendEvents(); this.getStore().remove(record); @@ -434,7 +495,7 @@ Ext4.define('LABKEY.VaccineDesign.BaseDataView', { // on refresh, call to give focus to the first column of the new row this.getDataView().on('refresh', function(){ var index = rowIndex, - selector = 'table.subgrid:nth(' + (rowIndex+1) + ') tr.subrow:last td.cell-value:first'; + selector = 'table.subgrid-' + dataIndex + ':nth(' + (rowIndex+1) + ') tr.subrow:last td.cell-value:first'; this.giveLastRowFocus(index, selector); }, this, {single: true}); @@ -459,9 +520,9 @@ Ext4.define('LABKEY.VaccineDesign.BaseDataView', { this.fireEvent('dirtychange', this); }, - getColumnEditorConfig : function(dataIndex, parentDataIndex) + getColumnConfig : function(dataIndex, dataFilterValue, parentDataIndex) { - var editor = null, columns = this.getColumnConfig(); + var columns = this.getColumnConfigs(), matchingColumn = null; // if the parentDataIndex is defined, then we are looking for the subgrid column editor config if (Ext4.isString(parentDataIndex)) @@ -475,35 +536,36 @@ Ext4.define('LABKEY.VaccineDesign.BaseDataView', { Ext4.each(columns, function(column) { - if (column.dataIndex == dataIndex && column.hasOwnProperty('editorType') && column.hasOwnProperty('editorConfig')) + if (column.dataIndex == dataIndex && (!Ext4.isDefined(dataFilterValue) || column.dataIndexArrFilterValue == dataFilterValue)) { - editor = { - type: column.editorType, - config: column.editorConfig - }; - + matchingColumn = column; return false; // break; } }, this); - return editor; + return matchingColumn; }, - bindDataViewStore : function() + getColumnEditorConfig : function(column) { - this.getDataView().bindStore(this.getStore()); - this.getDataView().show(); + if (column != null && column.hasOwnProperty('editorType') && column.hasOwnProperty('editorConfig')) + { + return { + type: column.editorType, + config: Ext4.isFunction(column.editorConfig) ? column.editorConfig.call(this) : column.editorConfig + }; + } + + return null; }, - onFailure : function(text) + loadDataViewStore : function() { - Ext4.Msg.show({ - cls: 'data-window', - title: 'Error', - msg: text || 'Unknown error occurred.', - icon: Ext4.Msg.ERROR, - buttons: Ext4.Msg.OK - }); + // since some tables might need information from the store, wait to add the data view until the store loads + this.getStore().on('load', function() { + this.add(this.getDataView()); + this.fireEvent('loadcomplete', this); + }, this, {single: true}); }, getStore : function() @@ -516,9 +578,9 @@ Ext4.define('LABKEY.VaccineDesign.BaseDataView', { throw "getNewModelInstance must be overridden in subclass"; }, - getColumnConfig : function() + getColumnConfigs : function() { - throw "getColumnConfig must be overridden in subclass"; + throw "getColumnConfigs must be overridden in subclass"; }, getDeleteConfirmationMsg : function() diff --git a/study/webapp/study/vaccineDesign/StudyProducts.js b/study/webapp/study/vaccineDesign/StudyProducts.js index f630eaf5..e056ff7b 100644 --- a/study/webapp/study/vaccineDesign/StudyProducts.js +++ b/study/webapp/study/vaccineDesign/StudyProducts.js @@ -3,6 +3,177 @@ * * Licensed under the Apache License, Version 2.0: http://www.apache.org/licenses/LICENSE-2.0 */ +Ext4.define('LABKEY.VaccineDesign.StudyProductsPanel', { + extend : 'Ext.panel.Panel', + + border : false, + + bodyStyle : 'background-color: transparent;', + + width: 1200, + + disableEdit : true, + + returnURL : null, + + initComponent : function() + { + this.items = [ + this.getImmunogensGrid(), + this.getAdjuvantGrid(), + this.getButtonBar() + ]; + + this.callParent(); + }, + + getImmunogensGrid : function() + { + if (!this.immunogenGrid) + { + this.immunogenGrid = Ext4.create('LABKEY.VaccineDesign.ImmunogensGrid', { + disableEdit: this.disableEdit + }); + + this.immunogenGrid.on('dirtychange', function() { this.getSaveButton().enable(); }, this); + } + + return this.immunogenGrid; + }, + + getAdjuvantGrid : function() + { + if (!this.adjuvantGrid) + { + this.adjuvantGrid = Ext4.create('LABKEY.VaccineDesign.AdjuvantsGrid', { + padding: '20px 0', + disableEdit: this.disableEdit + }); + + this.adjuvantGrid.on('dirtychange', function() { this.getSaveButton().enable(); }, this); + } + + return this.adjuvantGrid; + }, + + getButtonBar : function() + { + if (!this.buttonBar) + { + this.buttonBar = Ext4.create('Ext.toolbar.Toolbar', { + dock: 'bottom', + ui: 'footer', + padding: 0, + style : 'background-color: transparent;', + defaults: {width: 75}, + items: [this.getSaveButton(), this.getCancelButton()] + }); + } + + return this.buttonBar; + }, + + getSaveButton : function() + { + if (!this.saveButton) + { + this.saveButton = Ext4.create('Ext.button.Button', { + text: 'Save', + disabled: true, + hidden: this.disableEdit, + handler: this.saveStudyProducts, + scope: this + }); + } + + return this.saveButton; + }, + + getCancelButton : function() + { + if (!this.cancelButton) + { + this.cancelButton = Ext4.create('Ext.button.Button', { + margin: this.disableEdit ? 0 : '0 0 0 10px', + text: this.disableEdit ? 'Done' : 'Cancel', + handler: this.goToReturnURL, + scope: this + }); + } + + return this.cancelButton; + }, + + saveStudyProducts : function() + { + var studyProducts = []; + + this.getEl().mask('Saving...'); + + Ext4.each(this.getImmunogensGrid().getStore().getRange(), function(record) + { + var recData = Ext4.clone(record.data); + + // drop and empty antigen rows that were just added + var antigenArr = []; + Ext4.each(recData['Antigens'], function(antigen) + { + if (Ext4.isDefined(antigen['RowId']) || LABKEY.VaccineDesign.Utils.objectHasData(antigen)) + antigenArr.push(antigen); + }, this); + recData['Antigens'] = antigenArr; + + // drop any empty rows that were just added + var hasData = recData['Label'] != '' || recData['Type'] != '' || recData['Antigens'].length > 0; + if (Ext4.isDefined(recData['RowId']) || hasData) + studyProducts.push(recData); + }, this); + + Ext4.each(this.getAdjuvantGrid().getStore().getRange(), function(record) + { + // drop any empty rows that were just added + if (Ext4.isDefined(record.get('RowId')) || record.get('Label') != '') + studyProducts.push(record.data); + }, this); + + LABKEY.Ajax.request({ + url : LABKEY.ActionURL.buildURL('study-design', 'updateStudyProducts.api'), + method : 'POST', + jsonData: { products: studyProducts }, + scope: this, + success: function(response) + { + var resp = Ext4.decode(response.responseText); + if (resp.success) + this.goToReturnURL(); + else + this.onFailure(); + }, + failure: function(response) + { + var resp = Ext4.decode(response.responseText); + this.onFailure(resp.exception); + this.getEl().unmask(); + } + }); + }, + + goToReturnURL : function() + { + window.location = this.returnURL; + }, + + onFailure : function(text) + { + Ext4.Msg.show({ + cls: 'data-window', + title: 'Error', + msg: text || 'Unknown error occurred.', + icon: Ext4.Msg.ERROR, + buttons: Ext4.Msg.OK + }); + } +}); Ext4.define('LABKEY.VaccineDesign.StudyProductsGrid', { @@ -10,7 +181,7 @@ Ext4.define('LABKEY.VaccineDesign.StudyProductsGrid', { filterRole : null, - //Override - see LABKEY.VaccineDesign.BaseDataView + //Override getStore : function() { if (!this.store) @@ -33,13 +204,13 @@ Ext4.define('LABKEY.VaccineDesign.StudyProductsGrid', { return this.store; }, - //Override - see LABKEY.VaccineDesign.BaseDataView + //Override getNewModelInstance : function() { return LABKEY.VaccineDesign.Product.create({Role: this.filterRole}); }, - //Override - see LABKEY.VaccineDesign.BaseDataView + //Override getDeleteConfirmationMsg : function() { return 'Are you sure you want to delete the selected study product?

    ' @@ -50,13 +221,17 @@ Ext4.define('LABKEY.VaccineDesign.StudyProductsGrid', { Ext4.define('LABKEY.VaccineDesign.ImmunogensGrid', { extend : 'LABKEY.VaccineDesign.StudyProductsGrid', - width: 1100, + + width: 1200, + mainTitle : 'Immunogens', + filterRole : 'Immunogen', - hiddenColumns : ["RowId", "Role"], - //Override - see LABKEY.VaccineDesign.BaseDataView - getColumnConfig : function() + studyDesignQueryNames : ['StudyDesignImmunogenTypes', 'StudyDesignGenes', 'StudyDesignSubTypes'], + + //Override + getColumnConfigs : function() { if (!this.columnConfigs) { @@ -66,45 +241,45 @@ Ext4.define('LABKEY.VaccineDesign.ImmunogensGrid', { dataIndex: 'Label', required: true, editorType: 'Ext.form.field.Text', - editorConfig: LABKEY.VaccineDesign.Utils.getStudyDesignFieldEditorConfig('Label', true, 190) + editorConfig: LABKEY.VaccineDesign.Utils.getStudyDesignTextConfig('Label', 190) }, { label: 'Type', width: 200, dataIndex: 'Type', queryName: 'StudyDesignImmunogenTypes', editorType: 'LABKEY.ext4.ComboBox', - editorConfig: LABKEY.VaccineDesign.Utils.getStudyDesignFieldEditorConfig('Type', false, 190, 'StudyDesignImmunogenTypes') + editorConfig: LABKEY.VaccineDesign.Utils.getStudyDesignComboConfig('Type', 190, 'StudyDesignImmunogenTypes') }, { label: 'HIV Antigens', - width: 700, + width: 800, dataIndex: 'Antigens', subgridConfig: { columns: [{ label: 'Gene', - width: 140, + width: 165, dataIndex: 'Gene', queryName: 'StudyDesignGenes', editorType: 'LABKEY.ext4.ComboBox', - editorConfig: LABKEY.VaccineDesign.Utils.getStudyDesignFieldEditorConfig('Gene', false, 130, 'StudyDesignGenes') + editorConfig: LABKEY.VaccineDesign.Utils.getStudyDesignComboConfig('Gene', 155, 'StudyDesignGenes') },{ label: 'Subtype', - width: 140, + width: 165, dataIndex: 'SubType', queryName: 'StudyDesignSubTypes', editorType: 'LABKEY.ext4.ComboBox', - editorConfig: LABKEY.VaccineDesign.Utils.getStudyDesignFieldEditorConfig('SubType', false, 130, 'StudyDesignSubTypes') + editorConfig: LABKEY.VaccineDesign.Utils.getStudyDesignComboConfig('SubType', 155, 'StudyDesignSubTypes') },{ label: 'GenBank Id', - width: 190, + width: 215, dataIndex: 'GenBankId', editorType: 'Ext.form.field.Text', - editorConfig: LABKEY.VaccineDesign.Utils.getStudyDesignFieldEditorConfig('GenBankId', false, 180) + editorConfig: LABKEY.VaccineDesign.Utils.getStudyDesignTextConfig('GenBankId', 205) },{ label: 'Sequence', - width: 210, + width: 235, dataIndex: 'Sequence', editorType: 'Ext.form.field.Text', - editorConfig: LABKEY.VaccineDesign.Utils.getStudyDesignFieldEditorConfig('Sequence', false, 200) + editorConfig: LABKEY.VaccineDesign.Utils.getStudyDesignTextConfig('Sequence', 225) }] } }]; @@ -116,13 +291,15 @@ Ext4.define('LABKEY.VaccineDesign.ImmunogensGrid', { Ext4.define('LABKEY.VaccineDesign.AdjuvantsGrid', { extend : 'LABKEY.VaccineDesign.StudyProductsGrid', + width : 400, + mainTitle : 'Adjuvants', + filterRole : 'Adjuvant', - hiddenColumns : ["RowId", "Role", "Type", "Antigens"], - //Override - see LABKEY.VaccineDesign.BaseDataView - getColumnConfig : function() + //Override + getColumnConfigs : function() { if (!this.columnConfigs) { @@ -132,7 +309,7 @@ Ext4.define('LABKEY.VaccineDesign.AdjuvantsGrid', { dataIndex: 'Label', required: true, editorType: 'Ext.form.field.Text', - editorConfig: LABKEY.VaccineDesign.Utils.getStudyDesignFieldEditorConfig('Label', true, 363) + editorConfig: LABKEY.VaccineDesign.Utils.getStudyDesignTextConfig('Label', 363) }]; } diff --git a/study/webapp/study/vaccineDesign/TreatmentSchedule.js b/study/webapp/study/vaccineDesign/TreatmentSchedule.js new file mode 100644 index 00000000..a7f410b3 --- /dev/null +++ b/study/webapp/study/vaccineDesign/TreatmentSchedule.js @@ -0,0 +1,547 @@ +Ext4.define('LABKEY.VaccineDesign.TreatmentSchedulePanel', { + extend : 'Ext.panel.Panel', + + border : false, + + bodyStyle : 'background-color: transparent;', + + minWidth: 1400, + + disableEdit : true, + + returnURL : null, + + initComponent : function() + { + this.items = [ + this.getTreatmentsGrid() + ]; + + this.callParent(); + }, + + getTreatmentsGrid : function() + { + if (!this.treatmentsGrid) + { + this.treatmentsGrid = Ext4.create('LABKEY.VaccineDesign.TreatmentsGrid', { + disableEdit: this.disableEdit + }); + + this.treatmentsGrid.on('dirtychange', function() { this.getSaveButton().enable(); }, this); + + // Note: since we need the data from the treatment grid, don't add this.getTreatmentScheduleGrid() until the treatment grid store has loaded + this.treatmentsGrid.on('loadcomplete', this.onTreatmentGridLoadComplete, this, {single: true}); + } + + return this.treatmentsGrid; + }, + + onTreatmentGridLoadComplete : function() + { + this.add(this.getTreatmentScheduleGrid()); + this.add(this.getButtonBar()); + + // since a treatment label change needs to be reflected in the treatment schedule grid, force a refresh there + this.getTreatmentsGrid().on('celledited', function(){ + this.getTreatmentScheduleGrid().refresh(); + }, this); + + // removing a treatment row needs to also remove any visit mappings for that treatment + this.getTreatmentsGrid().on('beforerowdeleted', function(grid, record){ + this.getTreatmentScheduleGrid().removeTreatmentUsages(record.get('RowId')); + }, this); + }, + + getTreatmentScheduleGrid : function() + { + if (!this.treatmentScheduleGrid) + { + this.treatmentScheduleGrid = Ext4.create('LABKEY.VaccineDesign.TreatmentScheduleGrid', { + padding: '20px 0', + disableEdit: this.disableEdit, + subjectNoun: this.subjectNoun, + visitNoun: this.visitNoun + }); + + this.treatmentScheduleGrid.on('dirtychange', function() { this.getSaveButton().enable(); }, this); + } + + return this.treatmentScheduleGrid; + }, + + getButtonBar : function() + { + if (!this.buttonBar) + { + this.buttonBar = Ext4.create('Ext.toolbar.Toolbar', { + dock: 'bottom', + ui: 'footer', + padding: 0, + style : 'background-color: transparent;', + defaults: {width: 75}, + items: [this.getSaveButton(), this.getCancelButton()] + }); + } + + return this.buttonBar; + }, + + getSaveButton : function() + { + if (!this.saveButton) + { + this.saveButton = Ext4.create('Ext.button.Button', { + text: 'Save', + disabled: true, + hidden: this.disableEdit, + handler: this.saveTreatmentSchedule, + scope: this + }); + } + + return this.saveButton; + }, + + getCancelButton : function() + { + if (!this.cancelButton) + { + this.cancelButton = Ext4.create('Ext.button.Button', { + margin: this.disableEdit ? 0 : '0 0 0 10px', + text: this.disableEdit ? 'Done' : 'Cancel', + handler: this.goToReturnURL, + scope: this + }); + } + + return this.cancelButton; + }, + + saveTreatmentSchedule : function() + { + this.getEl().mask('Saving...'); + + var treatments = [], index = 0, errorMsg = []; + Ext4.each(this.getTreatmentsGrid().getStore().getRange(), function(record) + { + var recData = Ext4.clone(record.data); + index++; + + // drop and empty immunogen or adjuvant rows that were just added + recData['Products'] = []; + Ext4.each(recData['Immunogen'], function(immunogen) + { + if (Ext4.isDefined(immunogen['RowId']) || LABKEY.VaccineDesign.Utils.objectHasData(immunogen)) + recData['Products'].push(immunogen); + }, this); + Ext4.each(recData['Adjuvant'], function(adjuvant) + { + if (Ext4.isDefined(adjuvant['RowId']) || LABKEY.VaccineDesign.Utils.objectHasData(adjuvant)) + recData['Products'].push(adjuvant); + }, this); + + // drop any empty rows that were just added + var hasData = recData['Label'] != '' || recData['Description'] != '' || recData['Products'].length > 0; + if (Ext4.isDefined(recData['RowId']) || hasData) + { + var treatmentLabel = recData['Label'] != '' ? '\'' + recData['Label'] + '\'' : index; + + // validation: treatment must have at least one immunogen or adjuvant, no duplicate immunogens/adjuvants for a treatment + var treatmentProductIds = Ext4.Array.clean(Ext4.Array.pluck(recData['Products'], 'ProductId')); + if (recData['Products'].length == 0) + errorMsg.push('Treatment ' + treatmentLabel + ' must have at least one immunogen or adjuvant defined.'); + else if (treatmentProductIds.length != Ext4.Array.unique(treatmentProductIds).length) + errorMsg.push('Treatment ' + treatmentLabel + ' contains a duplicate immunogen or adjuvant.'); + else + treatments.push(recData); + } + }, this); + + if (errorMsg.length > 0) + { + this.onFailure(errorMsg.join('
    ')); + this.getEl().unmask(); + } + else + { + //console.log(treatments); + //console.log(this.getTreatmentScheduleGrid().getStore().getRange()); + this.getEl().unmask(); + } + }, + + goToReturnURL : function() + { + window.location = this.returnURL; + }, + + onFailure : function(text) + { + Ext4.Msg.show({ + cls: 'data-window', + title: 'Error', + msg: text || 'Unknown error occurred.', + icon: Ext4.Msg.ERROR, + buttons: Ext4.Msg.OK + }); + } +}); + +Ext4.define('LABKEY.VaccineDesign.TreatmentsGrid', { + extend : 'LABKEY.VaccineDesign.BaseDataView', + + mainTitle : 'Treatments', + + width: 1400, + + studyDesignQueryNames : ['StudyDesignRoutes', 'Product'], + + //Override + getStore : function() + { + if (!this.store) + { + this.store = Ext4.create('Ext.data.Store', { + storeId : 'TreatmentsGridStore', + model : 'LABKEY.VaccineDesign.Treatment', + proxy: { + type: 'ajax', + url : LABKEY.ActionURL.buildURL("study-design", "getStudyTreatments", null, {splitByRole: true}), + reader: { + type: 'json', + root: 'treatments' + } + }, + sorters: [{ property: 'RowId', direction: 'ASC' }], + autoLoad: true + }); + } + + return this.store; + }, + + //Override + getColumnConfigs : function() + { + if (!this.columnConfigs) + { + this.columnConfigs = [{ + label: 'Label', + width: 200, + dataIndex: 'Label', + required: true, + editorType: 'Ext.form.field.Text', + editorConfig: LABKEY.VaccineDesign.Utils.getStudyDesignTextConfig('Label', 190) + },{ + label: 'Description', + width: 200, + dataIndex: 'Description', + editorType: 'Ext.form.field.TextArea', + editorConfig: LABKEY.VaccineDesign.Utils.getStudyDesignTextConfig('Description', 190, '95%') + }, { + label: 'Immunogens', + width: 500, + dataIndex: 'Immunogen', + subgridConfig: { + columns: [{ + label: 'Immunogen', + width: 200, + dataIndex: 'ProductId', + required: true, + queryName: 'Product', + editorType: 'LABKEY.ext4.ComboBox', + editorConfig: LABKEY.VaccineDesign.Utils.getStudyDesignComboConfig('ProductId', 190, 'Product', + LABKEY.Filter.create('Role', 'Immunogen'), 'Label', 'RowId') + },{ + label: 'Dose and Units', + width: 140, + dataIndex: 'Dose', + editorType: 'Ext.form.field.Text', + editorConfig: LABKEY.VaccineDesign.Utils.getStudyDesignTextConfig('Dose', 130) + },{ + label: 'Route', + width: 140, + dataIndex: 'Route', + queryName: 'StudyDesignRoutes', + editorType: 'LABKEY.ext4.ComboBox', + editorConfig: LABKEY.VaccineDesign.Utils.getStudyDesignComboConfig('Route', 130, 'StudyDesignRoutes') + }] + } + }, { + label: 'Adjuvants', + width: 500, + dataIndex: 'Adjuvant', + subgridConfig: { + columns: [{ + label: 'Adjuvant', + width: 200, + dataIndex: 'ProductId', + required: true, + queryName: 'Product', + editorType: 'LABKEY.ext4.ComboBox', + editorConfig: LABKEY.VaccineDesign.Utils.getStudyDesignComboConfig('ProductId', 190, 'Product', + LABKEY.Filter.create('Role', 'Adjuvant'), 'Label', 'RowId') + },{ + label: 'Dose and Units', + width: 140, + dataIndex: 'Dose', + editorType: 'Ext.form.field.Text', + editorConfig: LABKEY.VaccineDesign.Utils.getStudyDesignTextConfig('Dose', 130) + },{ + label: 'Route', + width: 140, + dataIndex: 'Route', + queryName: 'StudyDesignRoutes', + editorType: 'LABKEY.ext4.ComboBox', + editorConfig: LABKEY.VaccineDesign.Utils.getStudyDesignComboConfig('Route', 130, 'StudyDesignRoutes') + }] + } + }]; + } + + return this.columnConfigs; + }, + + //Override + getNewModelInstance : function() + { + return LABKEY.VaccineDesign.Treatment.create({ + RowId: Ext4.id() // need to generate an id so that the treatment schedule grid can use it + }); + }, + + //Override + getDeleteConfirmationMsg : function() + { + return 'Are you sure you want to delete the selected treatment?

    ' + + 'Note: this will also delete any usages of this treatment record in the Treatment Schedule grid below.'; + } +}); + +Ext4.define('LABKEY.VaccineDesign.TreatmentScheduleGrid', { + extend : 'LABKEY.VaccineDesign.BaseDataView', + + mainTitle : 'Treatment Schedule', + + width: 330, + + //studyDesignQueryNames : ['Visit'], + + subjectNoun : 'Subject', + + visitNoun : 'Visit', + + getStore : function() + { + if (!this.store) + { + this.store = Ext4.create('Ext.data.Store', { + model : 'LABKEY.VaccineDesign.Cohort', + sorters: [{ property: 'RowId', direction: 'ASC' }] + }); + + this.queryStudyTreatmentSchedule(); + } + + return this.store; + }, + + queryStudyTreatmentSchedule : function() + { + LABKEY.Ajax.request({ + url: LABKEY.ActionURL.buildURL('study-design', 'getStudyTreatmentSchedule', null, {splitByRole: true}), + method: 'GET', + scope: this, + success: function (response) + { + var o = Ext4.decode(response.responseText); + if (o.success) + { + this.getVisitStore(o['visits']); + this.getStore().loadData(o['cohorts']); + this.getStore().fireEvent('load', this.getStore()); + } + } + }); + }, + + getVisitStore : function(data) + { + if (!this.visitStore) + { + this.visitStore = Ext4.create('Ext.data.Store', { + model : 'LABKEY.VaccineDesign.Visit', + data : data + }); + } + + return this.visitStore; + }, + + getTreatmentsStore : function() + { + if (!this.treatmentsStore) + { + this.treatmentsStore = Ext4.getStore('TreatmentsGridStore'); + } + + return this.treatmentsStore; + }, + + getVisitColumnConfigs : function() + { + var visitConfigs = []; + + Ext4.each(this.getVisitStore().getRange(), function(visit) + { + visitConfigs.push({ + label: visit.get('Label') || (this.visitNoun + visit.get('RowId')), + width: 150, + dataIndex: 'VisitMap', + dataIndexArrFilterProp: 'VisitId', + dataIndexArrFilterValue: visit.get('RowId'), + dataIndexArrValue: 'TreatmentId', + lookupStoreId: 'TreatmentsGridStore', + editorType: 'LABKEY.ext4.ComboBox', + editorConfig: this.getTreatmentComboBoxConfig + }); + }, this); + + return visitConfigs; + }, + + getTreatmentComboBoxConfig : function() + { + return { + hideFieldLabel: true, + name: 'VisitMap', + width: 140, + forceSelection : false, // allow usage of inactive types + editable : false, + queryMode : 'local', + displayField : 'Label', + valueField : 'RowId', + store : this.getNewTreatmentComboStore() + }; + }, + + getNewTreatmentComboStore : function() + { + // need to create a new store each time since we need to add a [none] option and include any new treatment records + var data = [{RowId: null, Label: '[none'}]; + Ext4.each(this.getTreatmentsStore().getRange(), function(record) + { + data.push(Ext4.clone(record.data)); + }, this); + + return Ext4.create('Ext.data.Store', { + fields: ['RowId', 'Label'], + data: data + }); + }, + + removeTreatmentUsages : function(treatmentId) + { + this.getStore().suspendEvents(); + Ext4.each(this.getStore().getRange(), function(record) + { + var newVisitMapArr = Ext4.Array.filter(record.get('VisitMap'), function(item){ return item.TreatmentId != treatmentId; }); + record.set('VisitMap', newVisitMapArr); + }, this); + this.getStore().resumeEvents(); + + this.refresh(true); + }, + + //Override + getColumnConfigs : function() + { + if (!this.columnConfigs) + { + var columnConfigs = [{ + label: 'Group / Cohort', + width: 200, + dataIndex: 'Label', + required: true, + editorType: 'Ext.form.field.Text', + editorConfig: LABKEY.VaccineDesign.Utils.getStudyDesignTextConfig('Label', 190) + },{ + label: this.subjectNoun + ' Count', + width: 130, + dataIndex: 'SubjectCount', + editorType: 'Ext.form.field.Number', + editorConfig: LABKEY.VaccineDesign.Utils.getStudyDesignNumberConfig('SubjectCount', 120) + }]; + + var visitConfigs = this.getVisitColumnConfigs(); + + // update the width based on the number of visit columns + this.setWidth((visitConfigs.length * 150) + 330); + + this.columnConfigs = columnConfigs.concat(visitConfigs); + } + + return this.columnConfigs; + }, + + //Override + getNewModelInstance : function() + { + return LABKEY.VaccineDesign.Cohort.create(); + }, + + //Override + getDeleteConfirmationMsg : function() + { + return 'Are you sure you want to delete the selected group / cohort and its associated treatment / visit mapping records?'; + }, + + //Override + getCurrentCellValue : function(column, record, dataIndex, outerDataIndex, subgridIndex) + { + var value = this.callParent([column, record, dataIndex, outerDataIndex, subgridIndex]); + + if (Ext4.isArray(value)) + { + var matchingIndex = LABKEY.VaccineDesign.Utils.getMatchingRowIndexFromArray(value, column.dataIndexArrFilterProp, column.dataIndexArrFilterValue); + if (matchingIndex > -1) + return value[matchingIndex][column.dataIndexArrValue]; + else + return null; + } + + return value; + }, + + //Override + updateStoreRecordValue : function(record, column, newValue) + { + // special case for editing the value of one of the pivot visit columns + if (column.dataIndex == 'VisitMap') + { + var visitMapArr = record.get(column.dataIndex), + matchingIndex = LABKEY.VaccineDesign.Utils.getMatchingRowIndexFromArray(visitMapArr, column.dataIndexArrFilterProp, column.dataIndexArrFilterValue); + + if (matchingIndex > -1) + { + if (newValue != null) + visitMapArr[matchingIndex][column.dataIndexArrValue] = newValue; + else + Ext4.Array.splice(visitMapArr, matchingIndex, 1); + } + else if (newValue != null) + { + visitMapArr.push({ + CohortId: record.get('RowId'), + VisitId: column.dataIndexArrFilterValue, + TreatmentId: newValue + }); + } + } + else + { + this.callParent([record, column, newValue]); + } + } +}); \ No newline at end of file diff --git a/study/webapp/study/vaccineDesign/Utils.js b/study/webapp/study/vaccineDesign/Utils.js index e5d24c85..494459cd 100644 --- a/study/webapp/study/vaccineDesign/Utils.js +++ b/study/webapp/study/vaccineDesign/Utils.js @@ -4,63 +4,87 @@ Ext4.define('LABKEY.VaccineDesign.Utils', { singleton: true, /** - * Helper function to get field editor config object for a study design lookup combo or a text field. + * Helper function to get field editor config object for a study design lookup combo. * @param name Field name - * @param required Whether or not this field should be allowed to be blank * @param width Field width * @param queryName If combo, the queryName for the store + * @param filter LABKEY.Filter.create() object * @param displayField The field name of the combo store displayField + * @param valueField The field name of the combo store valueField * @returns {Object} Field config */ - getStudyDesignFieldEditorConfig : function(name, required, width, queryName, displayField) + getStudyDesignComboConfig : function(name, width, queryName, filter, displayField, valueField) { - if (queryName != undefined && queryName != null) - { - // config for LABKEY.ext4.ComboBox - return { - hideFieldLabel: true, - name: name, - width: width || 150, - allowBlank: !required, - forceSelection : false, // allow usage of inactive types - editable : false, - queryMode : 'local', - // TODO: this does not htmlEncode the display value in expanded options list - displayField : displayField || 'Label', - valueField : 'Name', - store : LABKEY.VaccineDesign.Utils.getStudyDesignStore(queryName) - }; + return { + hideFieldLabel: true, + name: name, + width: width || 150, + forceSelection : false, // allow usage of inactive types + editable : false, + queryMode : 'local', + // TODO: this does not htmlEncode the display value in expanded options list + displayField : displayField || 'Label', + valueField : valueField || 'Name', + store : LABKEY.VaccineDesign.Utils.getStudyDesignStore(queryName, filter) + }; + }, + + /** + * Helper function to get field editor config object for a study design text or text area field. + * @param name Field name + * @param width Field width + * @param height Field height + * @returns {Object} Field config + */ + getStudyDesignTextConfig : function(name, width, height) + { + return { + hideFieldLabel: true, + name: name, + width: width, + height: height } - else - { - // config for Ext.form.field.Text - return { - hideFieldLabel: true, - name: name, - width: width || 150, - allowBlank: !required - } + }, + + /** + * Helper function to get field editor config object for a study design number field. + * @param name Field name + * @param width Field width + * @returns {Object} Field config + */ + getStudyDesignNumberConfig : function(name, width) + { + return { + hideFieldLabel: true, + name: name, + width: width, + minValue: 0, + allowDecimal: false } }, /** * Create a new LABKEY.ext4.Store for the given queryName from the study schema. * @param queryName + * @param filter LABKEY.Filter.create() object * @returns {LABKEY.ext4.Store} */ - getStudyDesignStore : function(queryName) + getStudyDesignStore : function(queryName, filter) { - var store = Ext4.getStore(queryName); + var key = Ext4.isDefined(filter) ? queryName + '|' + filter.getColumnName() + '|' + filter.getValue() : queryName, + store = Ext4.getStore(key), + hasStudyDesignPrefix = queryName.indexOf('StudyDesign') == 0; + if (Ext4.isDefined(store)) return store; return Ext4.create('LABKEY.ext4.Store', { - storeId: queryName, + storeId: key, schemaName: 'study', queryName: queryName, - columns: 'Name,Label', - filterArray: [LABKEY.Filter.create('Inactive', false)], - containerFilter: LABKEY.container.type == 'project' ? 'Current' : 'CurrentPlusProject', + columns: 'RowId,Name,Label,DisplayOrder', + filterArray: Ext4.isDefined(filter) ? [filter] : (hasStudyDesignPrefix ? [LABKEY.Filter.create('Inactive', false)] : []), + containerFilter: LABKEY.container.type == 'project' || Ext4.isDefined(filter) || !hasStudyDesignPrefix ? 'Current' : 'CurrentPlusProject', sort: '-Container/Path,Label', autoLoad: true, listeners: { @@ -72,14 +96,69 @@ Ext4.define('LABKEY.VaccineDesign.Utils', { }); }, + /** + * Lookup a label for a given value using a store generated from the queryName. + * @param queryName + * @param value + * @returns {String} + */ getLabelFromStore : function(queryName, value) { - var store = LABKEY.VaccineDesign.Utils.getStudyDesignStore(queryName); + var store = LABKEY.VaccineDesign.Utils.getStudyDesignStore(queryName), + storeCols = Ext4.Array.pluck(store.getColumns(), 'dataIndex'), + record = null; + + if (storeCols.indexOf('RowId') > -1) + record = store.findRecord('RowId', value, 0, false, true, true); + + if (record == null && storeCols.indexOf('Name') > -1) + record = store.findRecord('Name', value, 0, false, true, true); + + return record != null ? record.get("Label") : value; + }, + + /** + * Check if the given object has an properties which have data (i.e. non null, not an empty string, or is an array) + * @param obj The object to test + * @returns {boolean} + */ + objectHasData : function(obj) + { + var hasNonNull = false; + + if (Ext4.isObject(obj)) + { + Ext4.Object.each(obj, function (key, value) + { + if ((Ext4.isArray(value) && value.length > 0) || (value != null && value != '')) + { + hasNonNull = true; + return false; // break + } + }); + } - var record = store.findRecord('Name', value, 0, false, true, true); - if (record) - return record.get("Label"); + return hasNonNull; + }, + + /** + * Get the matching row index from an array based on a given row's object property name and value. + * @param arr The array to traverse + * @param filterPropName The name of the row's object property to compare + * @param filterPropValue The value of the row's object property that indicates a match + * @returns {Object} + */ + getMatchingRowIndexFromArray : function(arr, filterPropName, filterPropValue) + { + if (Ext4.isString(filterPropName) && Ext4.isDefined(filterPropValue) && Ext4.isArray(arr)) + { + for (var i = 0; i < arr.length; i++) + { + if (arr[i].hasOwnProperty(filterPropName) && arr[i][filterPropName] == filterPropValue) + return i; + } + } - return value; + return -1; } }); \ No newline at end of file diff --git a/study/webapp/study/vaccineDesign/VaccineDesign.css b/study/webapp/study/vaccineDesign/VaccineDesign.css index 545e0800..d145782a 100644 --- a/study/webapp/study/vaccineDesign/VaccineDesign.css +++ b/study/webapp/study/vaccineDesign/VaccineDesign.css @@ -16,9 +16,10 @@ .study-vaccine-design td.cell-display, .study-vaccine-design td.cell-value { - padding: 3px 5px; + padding: 5px; border: solid 1px #DDDDDD; height: 30px; + vertical-align: top; } .study-vaccine-design table.subgrid td { diff --git a/study/webapp/study/vaccineDesign/vaccineDesign.lib.xml b/study/webapp/study/vaccineDesign/vaccineDesign.lib.xml index afee02e3..ed0d5b56 100644 --- a/study/webapp/study/vaccineDesign/vaccineDesign.lib.xml +++ b/study/webapp/study/vaccineDesign/vaccineDesign.lib.xml @@ -4,6 +4,7 @@ Enter vaccine design information in the grids below. -
    +
    • Configure dropdown options for immunogen types, genes, and subtypes at the project level to be shared across study designs or within this folder for diff --git a/study/src/org/labkey/study/view/studydesign/manageTreatments.jsp b/study/src/org/labkey/study/view/studydesign/manageTreatments.jsp index 15cbe568..84ee358e 100644 --- a/study/src/org/labkey/study/view/studydesign/manageTreatments.jsp +++ b/study/src/org/labkey/study/view/studydesign/manageTreatments.jsp @@ -28,71 +28,51 @@ <%@ page import="org.labkey.study.controllers.StudyDesignController" %> <%@ page import="org.labkey.study.model.StudyManager" %> <%@ page import="org.labkey.study.security.permissions.ManageStudyPermission" %> +<%@ page import="org.labkey.api.action.ReturnUrlForm" %> +<%@ page import="org.labkey.api.view.HttpView" %> +<%@ page import="org.labkey.api.view.JspView" %> +<%@ page import="org.labkey.api.portal.ProjectUrls" %> +<%@ page import="org.labkey.api.util.PageFlowUtil" %> <%@ page extends="org.labkey.api.jsp.JspBase" %> <%! @Override public void addClientDependencies(ClientDependencies dependencies) { - dependencies.add("Ext4ClientApi"); - dependencies.add("study/StudyVaccineDesign.js"); - dependencies.add("dataview/DataViewsPanel.css"); - dependencies.add("study/StudyVaccineDesign.css"); + dependencies.add("study/vaccineDesign/vaccineDesign.lib.xml"); + dependencies.add("study/vaccineDesign/VaccineDesign.css"); } %> <% + JspView me = (JspView) HttpView.currentView(); + ReturnUrlForm bean = me.getModelBean(); + Container c = getContainer(); User user = getUser(); - ActionURL returnURL = getActionURL(); Study study = StudyManager.getInstance().getStudy(c); boolean canManageStudy = c.hasPermission(user, ManageStudyPermission.class); - boolean isDataspace = c.isProject() && c.isDataspace(); + boolean isDataspaceStudy = c.getProject() != null && c.getProject().isDataspace() && !c.isDataspace(); - String visitDisplayName = "Visit"; + String visitNoun = "Visit"; if (study != null && study.getTimepointType() == TimepointType.DATE) - visitDisplayName = "Timepoint"; + visitNoun = "Timepoint"; - String subjectName = "Subject"; + String subjectNoun = "Subject"; if (study != null) - subjectName = study.getSubjectNounSingular(); -%> - - + String returnUrl = bean.getReturnUrl() != null ? bean.getReturnUrl() : PageFlowUtil.urlProvider(ProjectUrls.class).getBeginURL(c).toString(); +%> Enter treatment information in the grids below. -
      +
      • Configure dropdown options for routes at the project level to be shared across study designs or within this folder for study specific properties:
      • -
      • Use the "Insert New" button in the treatments grid to add a new study treatment.
      • -
      • Each treatment may consist of several study products, i.e. immunogens and/or adjuvants.
      • -
      • Use the "Insert New" button in the treatment schedule grid to add a new study cohort.
      • -
      • Enter the number of subjects for the cohort in the count column.
      • -
      -
      -
      -
      * Double click to edit a treatment record and its product definition
      -
      -<% - ActionURL manageStudyProductsURL = new ActionURL(StudyDesignController.ManageStudyProductsAction.class, getContainer()); - manageStudyProductsURL.addReturnURL(getActionURL()); -%> -<%=textLink("Manage Study Products", manageStudyProductsURL)%> -

      -
      -
      * Double click to edit a group/cohort and its treatment/visit map definition
      -
      +
    • Each treatment label must be unique and must consist of at least one study products, i.e. immunogens and/or adjuvants.
    • +
    • + Use the manage study products page to change or update the set of available immunogens and adjuvants. + <% + ActionURL manageStudyProductsURL = new ActionURL(StudyDesignController.ManageStudyProductsAction.class, getContainer()); + manageStudyProductsURL.addReturnURL(getActionURL()); + %> + <%=textLink("Manage Study Products", manageStudyProductsURL)%> +
    • +
    • + Each cohort label must be unique. Enter the number of <%=study.getSubjectNounPlural().toLowerCase()%> for + the cohort in the count column.
    • +
    • + Use the manage cohorts page to further configuration information about the cohorts for this study. + <%=textLink("Manage Cohorts", CohortController.ManageCohortsAction.class)%> +
    • +
    • + Use the manage <%=h(visitNoun.toLowerCase())%>s page to further configuration + information about the <%=h(visitNoun.toLowerCase())%>s for this study. + <%=textLink("Manage " + visitNoun + "s", StudyController.ManageVisitsAction.class)%> +
    • <% if (canManageStudy) { if (study != null && study.getTimepointType() == TimepointType.VISIT && study.getVisits(Visit.Order.DISPLAY).size() > 1) { - %><%= textLink("Change Visit Order", new ActionURL(StudyController.VisitOrderAction.class, c).addReturnURL(returnURL)) %><% - } %> - <%=textLink("Manage " + visitDisplayName + "s", StudyController.ManageVisitsAction.class)%> - <%=textLink("Manage Cohorts", CohortController.ManageCohortsAction.class)%> +
    • Use the change visit order page to adjust the display order of visits in the treatment schedule table. + <%= textLink("Change Visit Order", new ActionURL(StudyController.VisitOrderAction.class, c).addReturnURL(getActionURL())) %> +
    • <% + } } -%> \ No newline at end of file +%> +
    +
    +
    +
    \ No newline at end of file diff --git a/study/webapp/study/vaccineDesign/Models.js b/study/webapp/study/vaccineDesign/Models.js index acd245e8..d2eabd22 100644 --- a/study/webapp/study/vaccineDesign/Models.js +++ b/study/webapp/study/vaccineDesign/Models.js @@ -1,4 +1,3 @@ - Ext4.define('LABKEY.VaccineDesign.Product', { extend : 'Ext.data.Model', idgen: 'sequential', @@ -9,4 +8,38 @@ Ext4.define('LABKEY.VaccineDesign.Product', { {name : 'Type', type : 'string'}, {name : 'Antigens', defaultValue: []} ] +}); + +Ext4.define('LABKEY.VaccineDesign.Treatment', { + extend : 'Ext.data.Model', + idgen: 'sequential', + fields : [ + {name : 'RowId', defaultValue: undefined}, + {name : 'Label', type : 'string'}, + {name : 'Description', type : 'string'}, + {name : 'Immunogen', defaultValue: []}, + {name : 'Adjuvant', defaultValue: []} + ] +}); + +Ext4.define('LABKEY.VaccineDesign.Cohort', { + extend : 'Ext.data.Model', + idgen: 'sequential', + fields : [ + {name : 'RowId', defaultValue: undefined}, + {name : 'Label', type : 'string'}, + // the DataView XTemplate gets mad if this is defined as type 'int' + {name : 'SubjectCount', type : 'string'}, + {name : 'VisitMap', defaultValue: []} + ] +}); + +Ext4.define('LABKEY.VaccineDesign.Visit', { + extend : 'Ext.data.Model', + fields : [ + {name : 'RowId', type : 'int'}, + {name : 'Label', type : 'string'}, + {name : 'SortOrder', type : 'int'}, + {name : 'Included', type : 'boolean'} + ] }); \ No newline at end of file diff --git a/study/webapp/study/vaccineDesign/StudyProducts.js b/study/webapp/study/vaccineDesign/StudyProducts.js index e056ff7b..5b2f7be6 100644 --- a/study/webapp/study/vaccineDesign/StudyProducts.js +++ b/study/webapp/study/vaccineDesign/StudyProducts.js @@ -133,7 +133,7 @@ Ext4.define('LABKEY.VaccineDesign.StudyProductsPanel', { { // drop any empty rows that were just added if (Ext4.isDefined(record.get('RowId')) || record.get('Label') != '') - studyProducts.push(record.data); + studyProducts.push(Ext4.clone(record.data)); }, this); LABKEY.Ajax.request({ @@ -153,7 +153,6 @@ Ext4.define('LABKEY.VaccineDesign.StudyProductsPanel', { { var resp = Ext4.decode(response.responseText); this.onFailure(resp.exception); - this.getEl().unmask(); } }); }, @@ -172,6 +171,8 @@ Ext4.define('LABKEY.VaccineDesign.StudyProductsPanel', { icon: Ext4.Msg.ERROR, buttons: Ext4.Msg.OK }); + + this.getEl().unmask(); } }); diff --git a/study/webapp/study/vaccineDesign/TreatmentSchedule.js b/study/webapp/study/vaccineDesign/TreatmentSchedule.js index a7f410b3..14d7b9f3 100644 --- a/study/webapp/study/vaccineDesign/TreatmentSchedule.js +++ b/study/webapp/study/vaccineDesign/TreatmentSchedule.js @@ -122,7 +122,9 @@ Ext4.define('LABKEY.VaccineDesign.TreatmentSchedulePanel', { { this.getEl().mask('Saving...'); - var treatments = [], index = 0, errorMsg = []; + var treatments = [], cohorts = [], + index = 0, errorMsg = []; + Ext4.each(this.getTreatmentsGrid().getStore().getRange(), function(record) { var recData = Ext4.clone(record.data); @@ -161,13 +163,43 @@ Ext4.define('LABKEY.VaccineDesign.TreatmentSchedulePanel', { if (errorMsg.length > 0) { this.onFailure(errorMsg.join('
    ')); - this.getEl().unmask(); } else { - //console.log(treatments); - //console.log(this.getTreatmentScheduleGrid().getStore().getRange()); - this.getEl().unmask(); + Ext4.each(this.getTreatmentScheduleGrid().getStore().getRange(), function(record) + { + var recData = Ext4.clone(record.data); + + // drop any empty rows that were just added + var hasData = recData['Label'] != '' || recData['SubjectCount'] != '' || recData['VisitMap'].length > 0; + if (Ext4.isDefined(recData['RowId']) || hasData) + { + cohorts.push(recData); + } + }, this); + + LABKEY.Ajax.request({ + url : LABKEY.ActionURL.buildURL('study-design', 'updateTreatmentSchedule.api'), + method : 'POST', + jsonData: { + treatments: treatments, + cohorts: cohorts + }, + scope: this, + success: function(response) + { + var resp = Ext4.decode(response.responseText); + if (resp.success) + this.goToReturnURL(); + else + this.onFailure(); + }, + failure: function(response) + { + var resp = Ext4.decode(response.responseText); + this.onFailure(resp.exception); + } + }); } }, @@ -185,6 +217,8 @@ Ext4.define('LABKEY.VaccineDesign.TreatmentSchedulePanel', { icon: Ext4.Msg.ERROR, buttons: Ext4.Msg.OK }); + + this.getEl().unmask(); } }); From 7b3ca08b11f170f202e978e334a09ad06c8f1f14 Mon Sep 17 00:00:00 2001 From: Cory Nathe Date: Tue, 20 Sep 2016 18:52:26 +0000 Subject: [PATCH 095/218] Spec #27492: update how new visit columns are added to Treatment Schedule table - allow adding existing visits/timepoints or creating a new one - only show visit/timepoint columns that are in use for the schedul on page load --- .../study/vaccineDesign/BaseDataView.js | 13 +- .../study/vaccineDesign/StudyProducts.js | 1 - .../study/vaccineDesign/TreatmentSchedule.js | 98 +++++- .../webapp/study/vaccineDesign/VisitWindow.js | 302 ++++++++++++++++++ .../study/vaccineDesign/vaccineDesign.lib.xml | 1 + 5 files changed, 398 insertions(+), 17 deletions(-) create mode 100644 study/webapp/study/vaccineDesign/VisitWindow.js diff --git a/study/webapp/study/vaccineDesign/BaseDataView.js b/study/webapp/study/vaccineDesign/BaseDataView.js index 6883ba92..1cdefa46 100644 --- a/study/webapp/study/vaccineDesign/BaseDataView.js +++ b/study/webapp/study/vaccineDesign/BaseDataView.js @@ -77,7 +77,12 @@ Ext4.define('LABKEY.VaccineDesign.BaseDataView', { this.dataView = Ext4.create('Ext.view.View', { tpl: this.getDataViewTpl(), store: this.getStore(), - itemSelector: 'tr.row' + itemSelector: 'tr.row', + setTemplate: function(newTpl) + { + this.tpl = newTpl; + this.refresh(); + } }); this.dataView.on('itemclick', this.onDataViewItemClick, this); @@ -292,9 +297,9 @@ Ext4.define('LABKEY.VaccineDesign.BaseDataView', { tplArr.push('
    '); tplArr.push(''); tplArr.push(''); } @@ -451,7 +456,7 @@ Ext4.define('LABKEY.VaccineDesign.BaseDataView', { attachAddRowListeners : function(view) { - var addIconEls = Ext4.DomQuery.select('i.' + this.ADD_ICON_CLS.replace(/ /g, '.'), view.getEl().dom); + var addIconEls = Ext4.DomQuery.select('i.add-new-row', view.getEl().dom); Ext4.each(addIconEls, function(addIconEl) { diff --git a/study/webapp/study/vaccineDesign/StudyProducts.js b/study/webapp/study/vaccineDesign/StudyProducts.js index 5b2f7be6..e8a4e0ea 100644 --- a/study/webapp/study/vaccineDesign/StudyProducts.js +++ b/study/webapp/study/vaccineDesign/StudyProducts.js @@ -165,7 +165,6 @@ Ext4.define('LABKEY.VaccineDesign.StudyProductsPanel', { onFailure : function(text) { Ext4.Msg.show({ - cls: 'data-window', title: 'Error', msg: text || 'Unknown error occurred.', icon: Ext4.Msg.ERROR, diff --git a/study/webapp/study/vaccineDesign/TreatmentSchedule.js b/study/webapp/study/vaccineDesign/TreatmentSchedule.js index 14d7b9f3..240667cd 100644 --- a/study/webapp/study/vaccineDesign/TreatmentSchedule.js +++ b/study/webapp/study/vaccineDesign/TreatmentSchedule.js @@ -211,7 +211,6 @@ Ext4.define('LABKEY.VaccineDesign.TreatmentSchedulePanel', { onFailure : function(text) { Ext4.Msg.show({ - cls: 'data-window', title: 'Error', msg: text || 'Unknown error occurred.', icon: Ext4.Msg.ERROR, @@ -429,17 +428,20 @@ Ext4.define('LABKEY.VaccineDesign.TreatmentScheduleGrid', { Ext4.each(this.getVisitStore().getRange(), function(visit) { - visitConfigs.push({ - label: visit.get('Label') || (this.visitNoun + visit.get('RowId')), - width: 150, - dataIndex: 'VisitMap', - dataIndexArrFilterProp: 'VisitId', - dataIndexArrFilterValue: visit.get('RowId'), - dataIndexArrValue: 'TreatmentId', - lookupStoreId: 'TreatmentsGridStore', - editorType: 'LABKEY.ext4.ComboBox', - editorConfig: this.getTreatmentComboBoxConfig - }); + if (visit.get('Included')) + { + visitConfigs.push({ + label: visit.get('Label') || (this.visitNoun + visit.get('RowId')), + width: 150, + dataIndex: 'VisitMap', + dataIndexArrFilterProp: 'VisitId', + dataIndexArrFilterValue: visit.get('RowId'), + dataIndexArrValue: 'TreatmentId', + lookupStoreId: 'TreatmentsGridStore', + editorType: 'LABKEY.ext4.ComboBox', + editorConfig: this.getTreatmentComboBoxConfig + }); + } }, this); return visitConfigs; @@ -577,5 +579,77 @@ Ext4.define('LABKEY.VaccineDesign.TreatmentScheduleGrid', { { this.callParent([record, column, newValue]); } + }, + + //Override + getAddNewRowTpl : function(columns, dataIndex) + { + var tplArr = []; + + if (!this.disableEdit) + { + tplArr.push(''); + tplArr.push(''); + tplArr.push(''); + tplArr.push(''); + } + + return tplArr; + }, + + //Override + attachAddRowListeners : function(view) + { + this.callParent([view]); + + var addIconEls = Ext4.DomQuery.select('i.add-visit-column', view.getEl().dom); + + Ext4.each(addIconEls, function(addIconEl) + { + Ext4.get(addIconEl).on('click', this.addNewVisitColumn, this); + }, this); + }, + + addNewVisitColumn : function() + { + var win = Ext4.create('LABKEY.VaccineDesign.VisitWindow', { + title: 'Add ' + this.visitNoun, + visitNoun: this.visitNoun, + visitStore: this.getVisitStore(), + listeners: { + scope: this, + closewindow: function(){ + win.close(); + }, + selectexistingvisit: function(w, visitId){ + win.close(); + + // set the selected visit to be included + this.getVisitStore().findRecord('RowId', visitId).set('Included', true); + + this.updateDataViewTemplate(); + }, + newvisitcreated: function(w, newVisitData){ + win.close(); + + // add the new visit to the store + this.getVisitStore().add(LABKEY.VaccineDesign.Visit.create(newVisitData)); + + this.updateDataViewTemplate(); + } + } + }); + + win.show(); + }, + + updateDataViewTemplate : function() + { + // explicitly clear the column configs so the new visit column will be added + this.columnConfigs = null; + this.getDataView().setTemplate(this.getDataViewTpl()); } }); \ No newline at end of file diff --git a/study/webapp/study/vaccineDesign/VisitWindow.js b/study/webapp/study/vaccineDesign/VisitWindow.js new file mode 100644 index 00000000..69961b42 --- /dev/null +++ b/study/webapp/study/vaccineDesign/VisitWindow.js @@ -0,0 +1,302 @@ + +Ext4.define('LABKEY.VaccineDesign.VisitWindow', { + extend: 'Ext.window.Window', + + visitStore: null, + + visitNoun: 'Visit', + + constructor: function(config) + { + this.callParent([config]); + this.addEvents('closewindow', 'selectexistingvisit', 'newvisitcreated'); + }, + + initComponent: function() + { + this.items = [this.getFormPanel()]; + this.callParent(); + }, + + getFormPanel : function() + { + if (!this.formPanel) + { + this.formPanel = Ext4.create('Ext.form.Panel',{ + border: false, + padding: 10, + items: [ + this.getExistingVisitRadio(), + this.getExistingVisitCombo(), + this.getNewVisitRadio(), + this.getNewVisitLabelField(), + this.getNewVisitMinMaxContainer() + ], + buttons: [ + this.getSelectBtn(), + this.getCancelBtn() + ] + }); + } + + return this.formPanel; + }, + + getExistingVisitRadio : function() + { + if (!this.existingVisitRadio) + { + this.existingVisitRadio = Ext4.create('Ext.form.field.Radio', { + name: 'visitType', + disabled: this.getFilteredVisitStore().getCount() == 0, + inputValue: 'existing', + boxLabel: 'Select an existing study ' + this.visitNoun.toLowerCase() + ':', + checked: this.getFilteredVisitStore().getCount() > 0, + hideFieldLabel: true, + width: 300 + }); + } + + return this.existingVisitRadio; + }, + + getNewVisitRadio : function() + { + if (!this.newVisitRadio) + { + this.newVisitRadio = Ext4.create('Ext.form.field.Radio', { + name: 'visitType', + inputValue: 'new', + boxLabel: 'Create a new study ' + this.visitNoun.toLowerCase() + ':', + checked: this.getFilteredVisitStore().getCount() == 0, + hideFieldLabel: true, + width: 300 + }); + + this.newVisitRadio.on('change', function(radio, newValue){ + this.getExistingVisitCombo().setDisabled(newValue); + this.getNewVisitLabelField().setDisabled(!newValue); + this.getNewVisitMinMaxContainer().setDisabled(!newValue); + this.getSelectBtn().setText(newValue ? 'Submit' : 'Select'); + this.updateSelectBtnState(); + + if (newValue) + this.getNewVisitLabelField().focus(); + else + this.getExistingVisitCombo().focus(); + }, this); + } + + return this.newVisitRadio; + }, + + getExistingVisitCombo : function() + { + if (!this.existingVisitCombo) + { + this.existingVisitCombo = Ext4.create('Ext.form.field.ComboBox', { + name: 'existingVisit', + disabled: this.getFilteredVisitStore().getCount() == 0, + hideFieldLabel: true, + style: 'margin-left: 15px;', + width: 300, + store: this.getFilteredVisitStore(), + editable: false, + queryMode: 'local', + displayField: 'Label', + valueField: 'RowId' + }); + + this.existingVisitCombo.on('change', this.updateSelectBtnState, this); + } + + return this.existingVisitCombo; + }, + + getFilteredVisitStore : function() + { + if (!this.filteredVisitStore) + { + var data = []; + if (this.visitStore != null) + { + Ext4.each(this.visitStore.query('Included', false).items, function(record) + { + data.push(Ext4.clone(record.data)); + }, this); + } + + this.filteredVisitStore = Ext4.create('Ext.data.Store', { + model : 'LABKEY.VaccineDesign.Visit', + data : data + }); + } + + return this.filteredVisitStore; + }, + + getNewVisitLabelField : function() + { + if (!this.newVisitLabelField) + { + this.newVisitLabelField = Ext4.create('Ext.form.field.Text', { + name: 'newVisitLabel', + disabled: this.getFilteredVisitStore().getCount() > 0, + fieldLabel: 'Label', + labelWidth: 50, + width: 300, + style: 'margin-left: 15px;' + }); + + this.newVisitLabelField.on('change', this.updateSelectBtnState, this); + } + + return this.newVisitLabelField; + }, + + getNewVisitMinField : function() + { + if (!this.newVisitMinField) + { + this.newVisitMinField = Ext4.create('Ext.form.field.Number', { + name: 'newVisitRangeMin', + fieldLabel: 'Range', + labelWidth: 50, + width: 167, + emptyText: 'min', + hideTrigger: true, + decimalPrecision: 4 + }); + + this.newVisitMinField.on('change', this.updateSelectBtnState, this); + } + + return this.newVisitMinField; + }, + + getNewVisitMaxField : function() + { + if (!this.newVisitMaxField) + { + this.newVisitMaxField = Ext4.create('Ext.form.field.Number', { + name: 'newVisitRangeMax', + width: 113, + emptyText: 'max', + hideTrigger: true, + decimalPrecision: 4 + }); + + this.newVisitMinField.on('change', this.updateSelectBtnState, this); + } + + return this.newVisitMaxField; + }, + + getNewVisitMinMaxContainer : function() + { + if (!this.newVisitMinMaxContainer) + { + this.newVisitMinMaxContainer = Ext4.create('Ext.form.FieldContainer', { + layout: 'hbox', + style: 'margin-left: 15px; margin-bottom: 15px;', + disabled: this.getFilteredVisitStore().getCount() > 0, + items: [ + this.getNewVisitMinField(), + {xtype: 'label', width: 20}, // spacer + this.getNewVisitMaxField() + ] + }); + } + + return this.newVisitMinMaxContainer; + }, + + getSelectBtn : function() + { + if (!this.selectBtn) + { + this.selectBtn = Ext4.create('Ext.button.Button', { + text: this.getFilteredVisitStore().getCount() == 0 ? 'Submit' : 'Select', + disabled: true, + scope: this, + handler: function() { + var values = this.getFormPanel().getValues(); + + if (values['visitType'] == 'existing') + this.fireEvent('selectexistingvisit', this, values['existingVisit']); + else + this.createNewVisit(); + } + }); + } + + return this.selectBtn; + }, + + updateSelectBtnState : function() + { + var values = this.getFormPanel().getValues(); + + if (values['visitType'] == 'existing') + this.getSelectBtn().setDisabled(values['existingVisit'] == ''); + else + this.getSelectBtn().setDisabled(values['newVisitLabel'] == '' || values['newVisitRangeMin'] == ''); + }, + + getCancelBtn : function() + { + if (!this.cancelBtn) + { + this.cancelBtn = Ext4.create('Ext.button.Button', { + text: 'Cancel', + scope: this, + handler: function() { + this.fireEvent('closewindow', this); + } + }); + } + + return this.cancelBtn; + }, + + createNewVisit : function() + { + this.getEl().mask('Creating new visit...'); + var values = this.getFormPanel().getValues(); + + LABKEY.Ajax.request({ + url : LABKEY.ActionURL.buildURL('study-design', 'createVisit.api'), + method : 'POST', + jsonData: { + label: values['newVisitLabel'], + sequenceNumMin: values['newVisitRangeMin'], + sequenceNumMax: values['newVisitRangeMax'], + showByDefault: true + }, + success: function(response) { + var resp = Ext4.decode(response.responseText); + if (resp.success) + this.fireEvent('newvisitcreated', this, resp); + else + this.onFailure(); + }, + failure: function(response) { + var resp = Ext4.decode(response.responseText); + this.onFailure(resp.exception); + }, + scope : this + }); + }, + + onFailure : function(text) + { + Ext4.Msg.show({ + title: 'Error', + msg: text || 'Unknown error occurred.', + icon: Ext4.Msg.ERROR, + buttons: Ext4.Msg.OK + }); + + this.getEl().unmask(); + } +}); \ No newline at end of file diff --git a/study/webapp/study/vaccineDesign/vaccineDesign.lib.xml b/study/webapp/study/vaccineDesign/vaccineDesign.lib.xml index ed0d5b56..0f022fe2 100644 --- a/study/webapp/study/vaccineDesign/vaccineDesign.lib.xml +++ b/study/webapp/study/vaccineDesign/vaccineDesign.lib.xml @@ -5,6 +5,7 @@ Enter treatment information in the grids below.
      -
    • - Configure dropdown options for routes at the project level to be shared across study designs or within this folder for - study specific properties: -
    • Each treatment label must be unique and must consist of at least one study products, i.e. immunogens and/or adjuvants.
    • Use the manage study products page to change or update the set of available immunogens and adjuvants. diff --git a/study/src/org/labkey/study/view/studydesign/vaccineDesignWebpart.jsp b/study/src/org/labkey/study/view/studydesign/vaccineDesignWebpart.jsp index 0d5e8cd2..2296eb44 100644 --- a/study/src/org/labkey/study/view/studydesign/vaccineDesignWebpart.jsp +++ b/study/src/org/labkey/study/view/studydesign/vaccineDesignWebpart.jsp @@ -78,11 +78,13 @@ Ext4.create('LABKEY.VaccineDesign.ImmunogensGrid', { renderTo : 'immunogens-grid', studyDesignQueryNames : ['StudyDesignImmunogenTypes', 'StudyDesignGenes', 'StudyDesignSubTypes'], + showDoseRoute: false, disableEdit : true }); Ext4.create('LABKEY.VaccineDesign.AdjuvantsGrid', { renderTo : 'adjuvants-grid', + showDoseRoute: false, disableEdit : true }); }); diff --git a/study/webapp/study/vaccineDesign/StudyProducts.js b/study/webapp/study/vaccineDesign/StudyProducts.js index 3e4f8e5e..35eb806d 100644 --- a/study/webapp/study/vaccineDesign/StudyProducts.js +++ b/study/webapp/study/vaccineDesign/StudyProducts.js @@ -14,6 +14,8 @@ Ext4.define('LABKEY.VaccineDesign.StudyProductsPanel', { disableEdit : true, + dirty : false, + returnURL : null, initComponent : function() @@ -25,6 +27,8 @@ Ext4.define('LABKEY.VaccineDesign.StudyProductsPanel', { ]; this.callParent(); + + window.onbeforeunload = LABKEY.beforeunload(this.beforeUnload, this); }, getImmunogensGrid : function() @@ -35,7 +39,10 @@ Ext4.define('LABKEY.VaccineDesign.StudyProductsPanel', { disableEdit: this.disableEdit }); - this.immunogenGrid.on('dirtychange', function() { this.getSaveButton().enable(); }, this); + this.immunogenGrid.on('dirtychange', function() { + this.setDirty(true); + this.getSaveButton().enable(); + }, this); } return this.immunogenGrid; @@ -50,7 +57,10 @@ Ext4.define('LABKEY.VaccineDesign.StudyProductsPanel', { disableEdit: this.disableEdit }); - this.adjuvantGrid.on('dirtychange', function() { this.getSaveButton().enable(); }, this); + this.adjuvantGrid.on('dirtychange', function() { + this.setDirty(true); + this.getSaveButton().enable(); + }, this); } return this.adjuvantGrid; @@ -159,6 +169,7 @@ Ext4.define('LABKEY.VaccineDesign.StudyProductsPanel', { goToReturnURL : function() { + this.setDirty(false); window.location = this.returnURL; }, @@ -172,6 +183,23 @@ Ext4.define('LABKEY.VaccineDesign.StudyProductsPanel', { }); this.getEl().unmask(); + }, + + setDirty : function(dirty) + { + this.dirty = dirty; + LABKEY.Utils.signalWebDriverTest("studyProductsDirty", dirty); + }, + + isDirty : function() + { + return this.dirty; + }, + + beforeUnload : function() + { + if (!this.disableEdit && this.isDirty()) + return 'Please save your changes.'; } }); @@ -181,6 +209,8 @@ Ext4.define('LABKEY.VaccineDesign.StudyProductsGrid', { filterRole : null, + showDoseRoute : true, + //Override getStore : function() { @@ -282,27 +312,32 @@ Ext4.define('LABKEY.VaccineDesign.ImmunogensGrid', { editorConfig: LABKEY.VaccineDesign.Utils.getStudyDesignTextConfig('Sequence', 225) }] } - },{ - label: 'Doses and Routes', - width: 300, - dataIndex: 'DoseAndRoute', - subgridConfig: { - columns: [{ - label: 'Dose', - width: 140, - dataIndex: 'Dose', - editorType: 'Ext.form.field.Text', - editorConfig: LABKEY.VaccineDesign.Utils.getStudyDesignTextConfig('Dose', 130) - },{ - label: 'Route', - width: 140, - dataIndex: 'Route', - queryName: 'StudyDesignRoutes', - editorType: 'LABKEY.ext4.ComboBox', - editorConfig: LABKEY.VaccineDesign.Utils.getStudyDesignComboConfig('Route', 130, 'StudyDesignRoutes') - }] - } }]; + + if (this.showDoseRoute) + { + this.columnConfigs.push({ + label: 'Doses and Routes', + width: 300, + dataIndex: 'DoseAndRoute', + subgridConfig: { + columns: [{ + label: 'Dose', + width: 140, + dataIndex: 'Dose', + editorType: 'Ext.form.field.Text', + editorConfig: LABKEY.VaccineDesign.Utils.getStudyDesignTextConfig('Dose', 130) + },{ + label: 'Route', + width: 140, + dataIndex: 'Route', + queryName: 'StudyDesignRoutes', + editorType: 'LABKEY.ext4.ComboBox', + editorConfig: LABKEY.VaccineDesign.Utils.getStudyDesignComboConfig('Route', 130, 'StudyDesignRoutes') + }] + } + }); + } } return this.columnConfigs; @@ -332,27 +367,32 @@ Ext4.define('LABKEY.VaccineDesign.AdjuvantsGrid', { required: true, editorType: 'Ext.form.field.Text', editorConfig: LABKEY.VaccineDesign.Utils.getStudyDesignTextConfig('Label', 363) - },{ - label: 'Doses and Routes', - width: 300, - dataIndex: 'DoseAndRoute', - subgridConfig: { - columns: [{ - label: 'Dose', - width: 140, - dataIndex: 'Dose', - editorType: 'Ext.form.field.Text', - editorConfig: LABKEY.VaccineDesign.Utils.getStudyDesignTextConfig('Dose', 130) - },{ - label: 'Route', - width: 140, - dataIndex: 'Route', - queryName: 'StudyDesignRoutes', - editorType: 'LABKEY.ext4.ComboBox', - editorConfig: LABKEY.VaccineDesign.Utils.getStudyDesignComboConfig('Route', 130, 'StudyDesignRoutes') - }] - } }]; + + if (this.showDoseRoute) + { + this.columnConfigs.push({ + label: 'Doses and Routes', + width: 300, + dataIndex: 'DoseAndRoute', + subgridConfig: { + columns: [{ + label: 'Dose', + width: 140, + dataIndex: 'Dose', + editorType: 'Ext.form.field.Text', + editorConfig: LABKEY.VaccineDesign.Utils.getStudyDesignTextConfig('Dose', 130) + }, { + label: 'Route', + width: 140, + dataIndex: 'Route', + queryName: 'StudyDesignRoutes', + editorType: 'LABKEY.ext4.ComboBox', + editorConfig: LABKEY.VaccineDesign.Utils.getStudyDesignComboConfig('Route', 130, 'StudyDesignRoutes') + }] + } + }); + } } return this.columnConfigs; diff --git a/study/webapp/study/vaccineDesign/TreatmentSchedule.js b/study/webapp/study/vaccineDesign/TreatmentSchedule.js index aa3fcdb6..1942084b 100644 --- a/study/webapp/study/vaccineDesign/TreatmentSchedule.js +++ b/study/webapp/study/vaccineDesign/TreatmentSchedule.js @@ -9,15 +9,17 @@ Ext4.define('LABKEY.VaccineDesign.TreatmentSchedulePanel', { disableEdit : true, + dirty : false, + returnURL : null, initComponent : function() { - this.items = [ - this.getTreatmentsGrid() - ]; + this.items = [this.getTreatmentsGrid()]; this.callParent(); + + window.onbeforeunload = LABKEY.beforeunload(this.beforeUnload, this); }, getTreatmentsGrid : function() @@ -28,7 +30,10 @@ Ext4.define('LABKEY.VaccineDesign.TreatmentSchedulePanel', { disableEdit: this.disableEdit }); - this.treatmentsGrid.on('dirtychange', function() { this.getSaveButton().enable(); }, this); + this.treatmentsGrid.on('dirtychange', function() { + this.setDirty(true); + this.getSaveButton().enable(); + }, this); // Note: since we need the data from the treatment grid, don't add this.getTreatmentScheduleGrid() until the treatment grid store has loaded this.treatmentsGrid.on('loadcomplete', this.onTreatmentGridLoadComplete, this, {single: true}); @@ -64,7 +69,10 @@ Ext4.define('LABKEY.VaccineDesign.TreatmentSchedulePanel', { visitNoun: this.visitNoun }); - this.treatmentScheduleGrid.on('dirtychange', function() { this.getSaveButton().enable(); }, this); + this.treatmentScheduleGrid.on('dirtychange', function() { + this.setDirty(true); + this.getSaveButton().enable(); + }, this); } return this.treatmentScheduleGrid; @@ -205,6 +213,7 @@ Ext4.define('LABKEY.VaccineDesign.TreatmentSchedulePanel', { goToReturnURL : function() { + this.setDirty(false); window.location = this.returnURL; }, @@ -218,6 +227,23 @@ Ext4.define('LABKEY.VaccineDesign.TreatmentSchedulePanel', { }); this.getEl().unmask(); + }, + + setDirty : function(dirty) + { + this.dirty = dirty; + LABKEY.Utils.signalWebDriverTest("treatmentScheduleDirty", dirty); + }, + + isDirty : function() + { + return this.dirty; + }, + + beforeUnload : function() + { + if (!this.disableEdit && this.isDirty()) + return 'Please save your changes.'; } }); @@ -347,8 +373,7 @@ Ext4.define('LABKEY.VaccineDesign.TreatmentsGrid', { getDoseAndRouteEditor : function(){ - var cfg = LABKEY.VaccineDesign.Utils.getStudyDesignComboConfig('DoseAndRoute', 130, 'DoseAndRoute', - undefined, 'label', 'label'); + var cfg = LABKEY.VaccineDesign.Utils.getStudyDesignComboConfig('DoseAndRoute', 130, 'DoseAndRoute', undefined, 'Label', 'Label'); cfg.listeners = { scope: this, render : function(cmp) { From ffbb02285539dc016ae227114bf24ecd836350da Mon Sep 17 00:00:00 2001 From: Cory Nathe Date: Wed, 28 Sep 2016 13:58:44 +0000 Subject: [PATCH 099/218] Issue 27977: **Treatment Schedule allows a Participant Count of real numbers --- .../controllers/StudyDesignController.java | 10 ++- .../study/vaccineDesign/TreatmentSchedule.js | 77 +++++++++++-------- study/webapp/study/vaccineDesign/Utils.js | 2 +- 3 files changed, 53 insertions(+), 36 deletions(-) diff --git a/study/src/org/labkey/study/controllers/StudyDesignController.java b/study/src/org/labkey/study/controllers/StudyDesignController.java index fd936021..e0cc7a21 100644 --- a/study/src/org/labkey/study/controllers/StudyDesignController.java +++ b/study/src/org/labkey/study/controllers/StudyDesignController.java @@ -509,7 +509,7 @@ public void validateForm(StudyTreatmentSchedule form, Errors errors) } } - // validate that each cohort has a label and it is unique + // validate that each cohort has a label, is unique, and has a valid subject count value for (CohortImpl cohort : form.getCohorts()) { if (cohort.getLabel() == null) @@ -526,6 +526,14 @@ else if (cohortByLabel != null) { errors.reject(ERROR_MSG, "A cohort with the label '" + cohort.getLabel() + "' already exists in this study."); } + + if (cohort.getSubjectCount() != null) + { + if (cohort.getSubjectCount() < 0) + errors.reject(ERROR_MSG, "Cohort subject count values must be a positive integer."); + else if (cohort.getSubjectCount() == Integer.MAX_VALUE) + errors.reject(ERROR_MSG, "Cohort subject count value larger than the max value allowed."); + } } } diff --git a/study/webapp/study/vaccineDesign/TreatmentSchedule.js b/study/webapp/study/vaccineDesign/TreatmentSchedule.js index 1942084b..91a2519c 100644 --- a/study/webapp/study/vaccineDesign/TreatmentSchedule.js +++ b/study/webapp/study/vaccineDesign/TreatmentSchedule.js @@ -138,7 +138,7 @@ Ext4.define('LABKEY.VaccineDesign.TreatmentSchedulePanel', { var recData = Ext4.clone(record.data); index++; - // drop and empty immunogen or adjuvant rows that were just added + // drop any empty immunogen or adjuvant rows that were just added recData['Products'] = []; Ext4.each(recData['Immunogen'], function(immunogen) { @@ -151,7 +151,7 @@ Ext4.define('LABKEY.VaccineDesign.TreatmentSchedulePanel', { recData['Products'].push(adjuvant); }, this); - // drop any empty rows that were just added + // drop any empty treatment rows that were just added var hasData = recData['Label'] != '' || recData['Description'] != '' || recData['Products'].length > 0; if (Ext4.isDefined(recData['RowId']) || hasData) { @@ -171,44 +171,53 @@ Ext4.define('LABKEY.VaccineDesign.TreatmentSchedulePanel', { if (errorMsg.length > 0) { this.onFailure(errorMsg.join('
      ')); + return; } - else + + Ext4.each(this.getTreatmentScheduleGrid().getStore().getRange(), function(record) { - Ext4.each(this.getTreatmentScheduleGrid().getStore().getRange(), function(record) - { - var recData = Ext4.clone(record.data); + var recData = Ext4.clone(record.data); - // drop any empty rows that were just added - var hasData = recData['Label'] != '' || recData['SubjectCount'] != '' || recData['VisitMap'].length > 0; - if (Ext4.isDefined(recData['RowId']) || hasData) - { + // drop any empty cohort rows that were just added + var hasData = recData['Label'] != '' || recData['SubjectCount'] != '' || recData['VisitMap'].length > 0; + if (Ext4.isDefined(recData['RowId']) || hasData) + { + var countVal = Number(recData['SubjectCount']); + if (isNaN(countVal) || countVal < 0) + errorMsg.push('Cohort ' + this.subjectNoun.toLowerCase() + ' count values must be a positive integer: ' + recData['SubjectCount'] + '.'); + else cohorts.push(recData); - } - }, this); + } + }, this); - LABKEY.Ajax.request({ - url : LABKEY.ActionURL.buildURL('study-design', 'updateTreatmentSchedule.api'), - method : 'POST', - jsonData: { - treatments: treatments, - cohorts: cohorts - }, - scope: this, - success: function(response) - { - var resp = Ext4.decode(response.responseText); - if (resp.success) - this.goToReturnURL(); - else - this.onFailure(); - }, - failure: function(response) - { - var resp = Ext4.decode(response.responseText); - this.onFailure(resp.exception); - } - }); + if (errorMsg.length > 0) + { + this.onFailure(errorMsg.join('
      ')); + return; } + + LABKEY.Ajax.request({ + url : LABKEY.ActionURL.buildURL('study-design', 'updateTreatmentSchedule.api'), + method : 'POST', + jsonData: { + treatments: treatments, + cohorts: cohorts + }, + scope: this, + success: function(response) + { + var resp = Ext4.decode(response.responseText); + if (resp.success) + this.goToReturnURL(); + else + this.onFailure(); + }, + failure: function(response) + { + var resp = Ext4.decode(response.responseText); + this.onFailure(resp.exception); + } + }); }, goToReturnURL : function() diff --git a/study/webapp/study/vaccineDesign/Utils.js b/study/webapp/study/vaccineDesign/Utils.js index 494459cd..f5503c5b 100644 --- a/study/webapp/study/vaccineDesign/Utils.js +++ b/study/webapp/study/vaccineDesign/Utils.js @@ -59,7 +59,7 @@ Ext4.define('LABKEY.VaccineDesign.Utils', { name: name, width: width, minValue: 0, - allowDecimal: false + allowDecimals: false } }, From 051ad927dd7479f16c58319ecc86067207bba066 Mon Sep 17 00:00:00 2001 From: Cory Nathe Date: Wed, 28 Sep 2016 14:21:18 +0000 Subject: [PATCH 100/218] Issue 27978: **Treatment Schedule allows save with required Group Cohort value not specified --- .../study/controllers/StudyDesignController.java | 13 +++++++------ study/webapp/study/vaccineDesign/StudyProducts.js | 2 +- .../webapp/study/vaccineDesign/TreatmentSchedule.js | 2 +- 3 files changed, 9 insertions(+), 8 deletions(-) diff --git a/study/src/org/labkey/study/controllers/StudyDesignController.java b/study/src/org/labkey/study/controllers/StudyDesignController.java index e0cc7a21..57f07102 100644 --- a/study/src/org/labkey/study/controllers/StudyDesignController.java +++ b/study/src/org/labkey/study/controllers/StudyDesignController.java @@ -15,6 +15,7 @@ */ package org.labkey.study.controllers; +import org.apache.commons.lang3.StringUtils; import org.json.JSONArray; import org.labkey.api.action.ApiAction; import org.labkey.api.action.ApiResponse; @@ -358,7 +359,7 @@ public void validateForm(StudyProductsForm form, Errors errors) { if (product.getLabel() == null || "".equals(product.getLabel())) { - errors.reject(ERROR_MSG, "Label field is required for all study products."); + errors.reject(ERROR_MSG, "Label is a required field for all study products."); break; } } @@ -498,22 +499,22 @@ public void validateForm(StudyTreatmentSchedule form, Errors errors) // validate that each treatment has a label for (TreatmentImpl treatment : form.getTreatments()) { - if (treatment.getLabel() == null) - errors.reject(ERROR_MSG, "Treatment label is required."); + if (StringUtils.isEmpty(treatment.getLabel())) + errors.reject(ERROR_MSG, "Label is a required field for all treatments."); // validate that each treatment product mapping has a selected product for (TreatmentProductImpl treatmentProduct : treatment.getTreatmentProducts()) { if (treatmentProduct.getProductId() <= 0) - errors.reject(ERROR_MSG, "Each treatment product must have a selected Immunogen or Adjuvant."); + errors.reject(ERROR_MSG, "Each treatment product must have a selected study product."); } } // validate that each cohort has a label, is unique, and has a valid subject count value for (CohortImpl cohort : form.getCohorts()) { - if (cohort.getLabel() == null) - errors.reject(ERROR_MSG, "Cohort label is required."); + if (StringUtils.isEmpty(cohort.getLabel())) + errors.reject(ERROR_MSG, "Label is a required field for all cohorts."); CohortImpl cohortByLabel = StudyManager.getInstance().getCohortByLabel(getContainer(), getUser(), cohort.getLabel()); if (cohort.getRowId() > 0) diff --git a/study/webapp/study/vaccineDesign/StudyProducts.js b/study/webapp/study/vaccineDesign/StudyProducts.js index 35eb806d..ef44b62b 100644 --- a/study/webapp/study/vaccineDesign/StudyProducts.js +++ b/study/webapp/study/vaccineDesign/StudyProducts.js @@ -162,7 +162,7 @@ Ext4.define('LABKEY.VaccineDesign.StudyProductsPanel', { failure: function(response) { var resp = Ext4.decode(response.responseText); - this.onFailure(resp.exception); + this.onFailure(Ext4.Array.pluck(resp.errors, 'message').join('
      ')); } }); }, diff --git a/study/webapp/study/vaccineDesign/TreatmentSchedule.js b/study/webapp/study/vaccineDesign/TreatmentSchedule.js index 91a2519c..6cee8c56 100644 --- a/study/webapp/study/vaccineDesign/TreatmentSchedule.js +++ b/study/webapp/study/vaccineDesign/TreatmentSchedule.js @@ -215,7 +215,7 @@ Ext4.define('LABKEY.VaccineDesign.TreatmentSchedulePanel', { failure: function(response) { var resp = Ext4.decode(response.responseText); - this.onFailure(resp.exception); + this.onFailure(Ext4.Array.pluck(resp.errors, 'message').join('
      ')); } }); }, From c9aa69ff75423086c5af10ccf7fd888cbc1ff035 Mon Sep 17 00:00:00 2001 From: Cory Nathe Date: Wed, 28 Sep 2016 19:32:21 +0000 Subject: [PATCH 101/218] Issue 27976: **Manage Products and Manage Treatments: Not able to see edit controls until form field is clicked --- .../controllers/StudyDesignController.java | 6 +- .../study/vaccineDesign/BaseDataView.js | 232 ++++++++++-------- .../study/vaccineDesign/StudyProducts.js | 59 +++-- .../study/vaccineDesign/TreatmentSchedule.js | 165 ++++++++----- study/webapp/study/vaccineDesign/Utils.js | 12 +- .../study/vaccineDesign/VaccineDesign.css | 12 +- 6 files changed, 280 insertions(+), 206 deletions(-) diff --git a/study/src/org/labkey/study/controllers/StudyDesignController.java b/study/src/org/labkey/study/controllers/StudyDesignController.java index 57f07102..0f8a8e5e 100644 --- a/study/src/org/labkey/study/controllers/StudyDesignController.java +++ b/study/src/org/labkey/study/controllers/StudyDesignController.java @@ -357,7 +357,7 @@ public void validateForm(StudyProductsForm form, Errors errors) // label field is required for (ProductImpl product : form.getProducts()) { - if (product.getLabel() == null || "".equals(product.getLabel())) + if (product.getLabel() == null || StringUtils.isEmpty(product.getLabel().trim())) { errors.reject(ERROR_MSG, "Label is a required field for all study products."); break; @@ -499,7 +499,7 @@ public void validateForm(StudyTreatmentSchedule form, Errors errors) // validate that each treatment has a label for (TreatmentImpl treatment : form.getTreatments()) { - if (StringUtils.isEmpty(treatment.getLabel())) + if (treatment.getLabel() == null || StringUtils.isEmpty(treatment.getLabel().trim())) errors.reject(ERROR_MSG, "Label is a required field for all treatments."); // validate that each treatment product mapping has a selected product @@ -513,7 +513,7 @@ public void validateForm(StudyTreatmentSchedule form, Errors errors) // validate that each cohort has a label, is unique, and has a valid subject count value for (CohortImpl cohort : form.getCohorts()) { - if (StringUtils.isEmpty(cohort.getLabel())) + if (cohort.getLabel() == null || StringUtils.isEmpty(cohort.getLabel().trim())) errors.reject(ERROR_MSG, "Label is a required field for all cohorts."); CohortImpl cohortByLabel = StudyManager.getInstance().getCohortByLabel(getContainer(), getUser(), cohort.getLabel()); diff --git a/study/webapp/study/vaccineDesign/BaseDataView.js b/study/webapp/study/vaccineDesign/BaseDataView.js index c29056d2..b32425d3 100644 --- a/study/webapp/study/vaccineDesign/BaseDataView.js +++ b/study/webapp/study/vaccineDesign/BaseDataView.js @@ -9,8 +9,6 @@ Ext4.define('LABKEY.VaccineDesign.BaseDataView', { mainTitle : null, - cellEditField : null, - studyDesignQueryNames : null, // for a DataSpace project, some scenarios don't make sense to allow insert/update @@ -22,7 +20,7 @@ Ext4.define('LABKEY.VaccineDesign.BaseDataView', { constructor : function(config) { this.callParent([config]); - this.addEvents('dirtychange', 'loadcomplete', 'celledited', 'beforerowdeleted'); + this.addEvents('dirtychange', 'loadcomplete', 'celledited', 'beforerowdeleted', 'renderviewcomplete'); }, initComponent : function() @@ -55,6 +53,15 @@ Ext4.define('LABKEY.VaccineDesign.BaseDataView', { this.loadDataViewStore(); } + this.fireRenderCompleteTask = new Ext4.util.DelayedTask(function() { + this.fireEvent('renderviewcomplete', this); + }, this); + + // add a single event listener to focus the first input field on the initial render + this.on('renderviewcomplete', function() { + this.giveCellInputFocus('table.outer tr.row:first td.cell-value:first input', true); + }, this, {single: true}); + this.callParent(); }, @@ -86,7 +93,7 @@ Ext4.define('LABKEY.VaccineDesign.BaseDataView', { }); this.dataView.on('itemclick', this.onDataViewItemClick, this); - this.dataView.on('refresh', this.attachAddRowListeners, this, {buffer: 250}); + this.dataView.on('refresh', this.onDataViewRefresh, this, {buffer: 250}); } return this.dataView; @@ -94,8 +101,9 @@ Ext4.define('LABKEY.VaccineDesign.BaseDataView', { getDataViewTpl : function() { - var tplArr = [], - tdCls = this.disableEdit ? 'cell-display' : 'cell-value', + var showEdit = !this.disableEdit, + tdCls = !showEdit ? 'cell-display' : 'cell-value', + tplArr = [], columns = this.getColumnConfigs(); tplArr.push('
     '); if (Ext4.isString(dataIndex)) - tplArr.push(' Add new row'); + tplArr.push(' Add new row'); else - tplArr.push(' Add new row'); + tplArr.push(' Add new row'); tplArr.push('
     '); + tplArr.push(' Add new row   '); + tplArr.push(' Add new ' + this.visitNoun.toLowerCase() + ''); + tplArr.push('
    '); @@ -104,40 +112,54 @@ Ext4.define('LABKEY.VaccineDesign.BaseDataView', { // data rows tplArr.push(''); tplArr.push(''); - if (!this.disableEdit) + if (showEdit) tplArr.push(''); Ext4.each(columns, function(column) { if (Ext4.isString(column.dataIndex)) { - var checkMissingReqTpl = ''; - if (column.required) - checkMissingReqTpl = ' {[this.checkMissingRequired(values, "' + column.dataIndex + '")]}'; + var checkMissingReqTpl = column.required ? ' {[this.checkMissingRequired(values, "' + column.dataIndex + '")]}' : '', + tdTpl = ''; + if (Ext4.isDefined(column.dataIndexArrFilterValue)) + tdTpl = tdTpl.substring(0, tdTpl.length -1) + ' data-filter-value="' + column.dataIndexArrFilterValue + '">'; + + // decide which of the td tpls to use based on the column definition if (Ext4.isObject(column.subgridConfig) && Ext4.isArray(column.subgridConfig.columns)) { tplArr = tplArr.concat(this.getSubGridTpl(column.dataIndex, column.subgridConfig.columns)); } else if (Ext4.isString(column.queryName)) { - tplArr.push(''); + tplArr.push(tdTpl + (!showEdit ? '{[this.getLabelFromStore(values["' + column.dataIndex + '"], "' + column.queryName + '")]}' : '') + tdCloseTpl); } else if (Ext4.isString(column.lookupStoreId) && Ext4.isDefined(column.dataIndexArrFilterValue)) { - tplArr.push(''); + tplArr.push(tdTpl + (!showEdit ? '{[this.getDisplayValue(values["' + column.dataIndex + '"])]}' : '') + tdCloseTpl); } } }, this); @@ -190,6 +212,7 @@ Ext4.define('LABKEY.VaccineDesign.BaseDataView', { checkMissingRequired : function(values, dataIndex) { + // TODO need to update this cls after cell field change if (values[dataIndex] == null || values[dataIndex] == '') return ' missing-required'; @@ -202,13 +225,14 @@ Ext4.define('LABKEY.VaccineDesign.BaseDataView', { getSubGridTpl : function(dataIndex, columns) { - var tplArr = [], - tdCls = this.disableEdit ? 'cell-display' : 'cell-value'; + var showEdit = !this.disableEdit, + tdCls = showEdit ? 'cell-value' : 'cell-display', + tplArr = []; tplArr.push(''); + tplArr.push(''); + tplArr.push(''); + tplArr.push(''); + } + + return tplArr; + }, + + //Override + attachAddRowListeners : function(view) + { + this.callParent([view]); + + var addIconEls = Ext4.DomQuery.select('i.add-visit-column', view.getEl().dom); + + Ext4.each(addIconEls, function(addIconEl) + { + Ext4.get(addIconEl).on('click', this.addNewVisitColumn, this); + }, this); + }, + + addNewVisitColumn : function() + { + var win = Ext4.create('LABKEY.VaccineDesign.VisitWindow', { + title: 'Add ' + this.visitNoun, + visitNoun: this.visitNoun, + visitStore: this.getVisitStore(), + listeners: { + scope: this, + closewindow: function(){ + win.close(); + }, + selectexistingvisit: function(w, visitId){ + win.close(); + + // if the 'ALL' option was selected, show all of the visits in the table + if (visitId == 'ALL') + { + Ext4.each(this.getVisitStore().getRange(), function(record) + { + record.set('Included', true); + }, this); + } + // set the selected visit to be included + else + { + this.getVisitStore().findRecord('RowId', visitId).set('Included', true); + } + + this.updateDataViewTemplate(); + }, + newvisitcreated: function(w, newVisitData){ + win.close(); + + // add the new visit to the store + this.getVisitStore().add(LABKEY.VaccineDesign.Visit.create(newVisitData)); + + this.updateDataViewTemplate(); + } + } + }); + + win.show(); + }, + + updateDataViewTemplate : function() + { + // explicitly clear the column configs so the new visit column will be added + this.columnConfigs = null; + this.getDataView().setTemplate(this.getDataViewTpl()); + } +}); \ No newline at end of file diff --git a/study/webapp/study/vaccineDesign/Models.js b/study/webapp/study/vaccineDesign/Models.js index b55105cf..792d887b 100644 --- a/study/webapp/study/vaccineDesign/Models.js +++ b/study/webapp/study/vaccineDesign/Models.js @@ -35,12 +35,40 @@ Ext4.define('LABKEY.VaccineDesign.Cohort', { ] }); +Ext4.define('LABKEY.VaccineDesign.Assay', { + extend : 'Ext.data.Model', + idgen: 'sequential', + fields : [ + {name : 'RowId', defaultValue: undefined}, + {name : 'AssayName', type : 'string'}, + {name : 'Description', type : 'string'}, + {name : 'Lab', type : 'string'}, + {name : 'LocationId', type : 'int'}, + {name : 'SampleType', type : 'string'}, + {name : 'Source', type : 'string'}, + {name : 'TubeType', type : 'string'}, + //{name : 'SampleQuantity', type : 'string'} + //{name : 'SampleUnits', type : 'string'} + {name : 'VisitMap', defaultValue: []} + ] +}); + +Ext4.define('LABKEY.VaccineDesign.AssaySpecimenVisit', { + extend : 'Ext.data.Model', + fields : [ + {name : 'RowId', type : 'int'}, + {name : 'VisitId', type : 'int'}, + {name : 'AssaySpecimenId', type : 'int'} + ] +}); + Ext4.define('LABKEY.VaccineDesign.Visit', { extend : 'Ext.data.Model', fields : [ {name : 'RowId', type : 'int'}, {name : 'Label', type : 'string'}, - {name : 'SortOrder', type : 'int'}, + {name : 'DisplayOrder', type : 'int'}, + {name : 'SequenceNumMin', type : 'numeric'}, {name : 'Included', type : 'boolean'} ] }); \ No newline at end of file diff --git a/study/webapp/study/vaccineDesign/StudyProducts.js b/study/webapp/study/vaccineDesign/StudyProducts.js index e627273e..91b22f0e 100644 --- a/study/webapp/study/vaccineDesign/StudyProducts.js +++ b/study/webapp/study/vaccineDesign/StudyProducts.js @@ -126,7 +126,7 @@ Ext4.define('LABKEY.VaccineDesign.StudyProductsPanel', { { var recData = Ext4.clone(record.data); - // drop and empty antigen rows that were just added + // drop any empty antigen rows that were just added var antigenArr = []; Ext4.each(recData['Antigens'], function(antigen) { diff --git a/study/webapp/study/vaccineDesign/TreatmentSchedule.js b/study/webapp/study/vaccineDesign/TreatmentSchedule.js index 4a22bca7..e9ec1c46 100644 --- a/study/webapp/study/vaccineDesign/TreatmentSchedule.js +++ b/study/webapp/study/vaccineDesign/TreatmentSchedule.js @@ -511,7 +511,7 @@ Ext4.define('LABKEY.VaccineDesign.TreatmentsGrid', { }); Ext4.define('LABKEY.VaccineDesign.TreatmentScheduleGrid', { - extend : 'LABKEY.VaccineDesign.BaseDataView', + extend : 'LABKEY.VaccineDesign.BaseDataViewAddVisit', mainTitle : 'Treatment Schedule', @@ -669,7 +669,13 @@ Ext4.define('LABKEY.VaccineDesign.TreatmentScheduleGrid', { var visitConfigs = this.getVisitColumnConfigs(); // update the width based on the number of visit columns - this.setWidth((visitConfigs.length * 150) + 350); + var width = (visitConfigs.length * 150) + 350; + this.setWidth(width); + + // update the outer panel width if necessary + var outerPanel = this.up('panel'); + if (outerPanel != null) + outerPanel.setWidth(Math.max(width, 1300)); this.columnConfigs = columnConfigs.concat(visitConfigs); } @@ -737,77 +743,5 @@ Ext4.define('LABKEY.VaccineDesign.TreatmentScheduleGrid', { { this.callParent([record, column, newValue]); } - }, - - //Override - getAddNewRowTpl : function(columns, dataIndex) - { - var tplArr = []; - - if (!this.disableEdit) - { - tplArr.push(''); - tplArr.push(''); - tplArr.push(''); - tplArr.push(''); - } - - return tplArr; - }, - - //Override - attachAddRowListeners : function(view) - { - this.callParent([view]); - - var addIconEls = Ext4.DomQuery.select('i.add-visit-column', view.getEl().dom); - - Ext4.each(addIconEls, function(addIconEl) - { - Ext4.get(addIconEl).on('click', this.addNewVisitColumn, this); - }, this); - }, - - addNewVisitColumn : function() - { - var win = Ext4.create('LABKEY.VaccineDesign.VisitWindow', { - title: 'Add ' + this.visitNoun, - visitNoun: this.visitNoun, - visitStore: this.getVisitStore(), - listeners: { - scope: this, - closewindow: function(){ - win.close(); - }, - selectexistingvisit: function(w, visitId){ - win.close(); - - // set the selected visit to be included - this.getVisitStore().findRecord('RowId', visitId).set('Included', true); - - this.updateDataViewTemplate(); - }, - newvisitcreated: function(w, newVisitData){ - win.close(); - - // add the new visit to the store - this.getVisitStore().add(LABKEY.VaccineDesign.Visit.create(newVisitData)); - - this.updateDataViewTemplate(); - } - } - }); - - win.show(); - }, - - updateDataViewTemplate : function() - { - // explicitly clear the column configs so the new visit column will be added - this.columnConfigs = null; - this.getDataView().setTemplate(this.getDataViewTpl()); } }); \ No newline at end of file diff --git a/study/webapp/study/vaccineDesign/Utils.js b/study/webapp/study/vaccineDesign/Utils.js index 15811fb6..0401f253 100644 --- a/study/webapp/study/vaccineDesign/Utils.js +++ b/study/webapp/study/vaccineDesign/Utils.js @@ -75,13 +75,16 @@ Ext4.define('LABKEY.VaccineDesign.Utils', { var key = Ext4.isDefined(filter) ? queryName + '|' + filter.getColumnName() + '|' + filter.getValue() : queryName, store = Ext4.getStore(key), hasStudyDesignPrefix = queryName.indexOf('StudyDesign') == 0, - columns = 'RowId,Name,Label,DisplayOrder'; + columns = 'RowId,Name,Label'; if (Ext4.isDefined(store)) return store; + // special case to query DisplayOrder and SequenceNumMin for Visit table + if (queryName == 'Visit') + columns += ',DisplayOrder,SequenceNumMin'; // special case to query ProductId column for DoseAndRoute table - if (queryName == 'DoseAndRoute') + else if (queryName == 'DoseAndRoute') columns += ',ProductId'; return Ext4.create('LABKEY.ext4.Store', { @@ -124,7 +127,7 @@ Ext4.define('LABKEY.VaccineDesign.Utils', { }, /** - * Check if the given object has an properties which have data (i.e. non null, not an empty string, or is an array) + * Check if the given object has any properties which have data (i.e. non null, not an empty string, or is an array) * @param obj The object to test * @returns {boolean} */ @@ -147,6 +150,34 @@ Ext4.define('LABKEY.VaccineDesign.Utils', { return hasNonNull; }, + /** + * Check if the given model object has any properties which have data (i.e. non null, not an empty string, or is an array) + * @param obj The object to test + * @returns {boolean} + */ + modelHasData : function(obj, fields) + { + var hasNonNull = false; + + if (Ext4.isObject(obj) && Ext4.isArray(fields)) + { + Ext4.each(fields, function(field) + { + if (Ext4.isArray(obj[field.name]) && obj[field.name].length > 0) + hasNonNull = true; + else if (field.type.type == 'int' && obj[field.name] != null && obj[field.name] > 0) + hasNonNull = true; + else if (field.type.type == 'string' && obj[field.name] != null && obj[field.name] != '') + hasNonNull = true; + + if (hasNonNull) + return false; // break; + }); + } + + return hasNonNull; + }, + /** * Get the matching row index from an array based on a given row's object property name and value. * @param arr The array to traverse diff --git a/study/webapp/study/vaccineDesign/VaccineDesign.css b/study/webapp/study/vaccineDesign/VaccineDesign.css index 387fe1ca..fef55dda 100644 --- a/study/webapp/study/vaccineDesign/VaccineDesign.css +++ b/study/webapp/study/vaccineDesign/VaccineDesign.css @@ -28,6 +28,10 @@ height: 33px; } +.study-vaccine-design td.cell-value .x4-form-cb-wrap { + height: 18px; +} + .study-vaccine-design table.subgrid td { background-color: #FFFFFF !important; } diff --git a/study/webapp/study/vaccineDesign/VisitWindow.js b/study/webapp/study/vaccineDesign/VisitWindow.js index 69961b42..63176c43 100644 --- a/study/webapp/study/vaccineDesign/VisitWindow.js +++ b/study/webapp/study/vaccineDesign/VisitWindow.js @@ -122,13 +122,21 @@ Ext4.define('LABKEY.VaccineDesign.VisitWindow', { { Ext4.each(this.visitStore.query('Included', false).items, function(record) { - data.push(Ext4.clone(record.data)); + var recData = Ext4.clone(record.data); + recData['Label'] = recData['Label'] || recData['SequenceNumMin']; + + data.push(recData); }, this); } + // add an option to select all existing visits for display + if (data.length > 1) + data.push(0, 0, {Label: '[Show All]', RowId: -1, DisplayOrder: -999999}); + this.filteredVisitStore = Ext4.create('Ext.data.Store', { model : 'LABKEY.VaccineDesign.Visit', - data : data + data : data, + sorters : [{property: 'DisplayOrder'},{property: 'SequenceNumMin'}] }); } @@ -223,7 +231,7 @@ Ext4.define('LABKEY.VaccineDesign.VisitWindow', { var values = this.getFormPanel().getValues(); if (values['visitType'] == 'existing') - this.fireEvent('selectexistingvisit', this, values['existingVisit']); + this.fireEvent('selectexistingvisit', this, values['existingVisit'] == -1 ? 'ALL' : values['existingVisit']); else this.createNewVisit(); } diff --git a/study/webapp/study/vaccineDesign/vaccineDesign.lib.xml b/study/webapp/study/vaccineDesign/vaccineDesign.lib.xml index 0f022fe2..5d84ac99 100644 --- a/study/webapp/study/vaccineDesign/vaccineDesign.lib.xml +++ b/study/webapp/study/vaccineDesign/vaccineDesign.lib.xml @@ -3,8 +3,10 @@ @@ -81,10 +100,10 @@ Enter treatment information in the grids below.
    • - Each treatment label must be unique and must consist of at least one study products, i.e. immunogens and/or adjuvants. + Each treatment label must be unique and must consist of at least one study products, i.e. immunogens and/or adjuvants and/or challenges.
    • - Use the manage study products page to change or update the set of available immunogens and adjuvants. + Use the manage study products page to change or update the set of available immunogens, adjuvants or challenges. <% ActionURL manageStudyProductsURL = new ActionURL(StudyDesignController.ManageStudyProductsAction.class, getContainer()); manageStudyProductsURL.addReturnURL(getActionURL()); diff --git a/study/src/org/labkey/study/view/studydesign/vaccineDesignWebpart.jsp b/study/src/org/labkey/study/view/studydesign/vaccineDesignWebpart.jsp index 6f049aa0..2ee8c6a5 100644 --- a/study/src/org/labkey/study/view/studydesign/vaccineDesignWebpart.jsp +++ b/study/src/org/labkey/study/view/studydesign/vaccineDesignWebpart.jsp @@ -62,6 +62,8 @@

      +
      +
      <% } else @@ -86,5 +88,11 @@ showDoseRoute: false, disableEdit : true }); + + Ext4.create('LABKEY.VaccineDesign.ChallengesGrid', { + renderTo : 'challenges-grid', + showDoseRoute: false, + disableEdit : true + }); }); \ No newline at end of file diff --git a/study/webapp/study/vaccineDesign/Models.js b/study/webapp/study/vaccineDesign/Models.js index e3a88161..e6459097 100644 --- a/study/webapp/study/vaccineDesign/Models.js +++ b/study/webapp/study/vaccineDesign/Models.js @@ -19,7 +19,8 @@ Ext4.define('LABKEY.VaccineDesign.Treatment', { {name : 'Label', type : 'string'}, {name : 'Description', type : 'string'}, {name : 'Immunogen', defaultValue: []}, - {name : 'Adjuvant', defaultValue: []} + {name : 'Adjuvant', defaultValue: []}, + {name : 'Challenge', defaultValue: []} ] }); diff --git a/study/webapp/study/vaccineDesign/StudyProducts.js b/study/webapp/study/vaccineDesign/StudyProducts.js index 50d49b1c..ddabcabf 100644 --- a/study/webapp/study/vaccineDesign/StudyProducts.js +++ b/study/webapp/study/vaccineDesign/StudyProducts.js @@ -23,6 +23,7 @@ Ext4.define('LABKEY.VaccineDesign.StudyProductsPanel', { this.items = [ this.getImmunogensGrid(), this.getAdjuvantGrid(), + this.getChallengesGrid(), this.getButtonBar() ]; @@ -62,6 +63,22 @@ Ext4.define('LABKEY.VaccineDesign.StudyProductsPanel', { return this.adjuvantGrid; }, + getChallengesGrid : function() + { + if (!this.challengesGrid) + { + this.challengesGrid = Ext4.create('LABKEY.VaccineDesign.ChallengesGrid', { + padding: '20px 0', + disableEdit: this.disableEdit + }); + + this.challengesGrid.on('dirtychange', this.enableSaveButton, this); + this.challengesGrid.on('celledited', this.enableSaveButton, this); + } + + return this.challengesGrid; + }, + getButtonBar : function() { if (!this.buttonBar) @@ -148,6 +165,12 @@ Ext4.define('LABKEY.VaccineDesign.StudyProductsPanel', { studyProducts.push(Ext4.clone(record.data)); }, this); + Ext4.each(this.getChallengesGrid().getStore().getRange(), function(record) + { + if (Ext4.isDefined(record.get('RowId')) || record.get('Label') != '') + studyProducts.push(Ext4.clone(record.data)); + }, this); + LABKEY.Ajax.request({ url : LABKEY.ActionURL.buildURL('study-design', 'updateStudyProducts.api'), method : 'POST', @@ -404,6 +427,70 @@ Ext4.define('LABKEY.VaccineDesign.AdjuvantsGrid', { } } + return this.columnConfigs; + } +}); + +Ext4.define('LABKEY.VaccineDesign.ChallengesGrid', { + extend : 'LABKEY.VaccineDesign.StudyProductsGrid', + + cls : 'study-vaccine-design vaccine-design-challenges', + + width : 730, + + mainTitle : 'Challenges', + + filterRole : 'Challenge', + + studyDesignQueryNames : ['StudyDesignImmunogenTypes', 'StudyDesignRoutes'], + + //Override + getColumnConfigs : function() + { + if (!this.columnConfigs) + { + this.columnConfigs = [{ + label: 'Label', + width: 200, + dataIndex: 'Label', + required: true, + editorType: 'Ext.form.field.Text', + editorConfig: LABKEY.VaccineDesign.Utils.getStudyDesignTextConfig('Label', 185) + }, { + label: 'Sub Type', + width: 200, + dataIndex: 'Type', + queryName: 'StudyDesignImmunogenTypes', //TODO need verify + editorType: 'LABKEY.ext4.ComboBox', + editorConfig: LABKEY.VaccineDesign.Utils.getStudyDesignComboConfig('Type', 185, 'StudyDesignImmunogenTypes') + }]; + + if (this.showDoseRoute) + { + this.columnConfigs.push({ + label: 'Doses and Routes', + width: 330, + dataIndex: 'DoseAndRoute', + subgridConfig: { + columns: [{ + label: 'Dose', + width: 140, + dataIndex: 'Dose', + editorType: 'Ext.form.field.Text', + editorConfig: LABKEY.VaccineDesign.Utils.getStudyDesignTextConfig('Dose', 125) + }, { + label: 'Route', + width: 140, + dataIndex: 'Route', + queryName: 'StudyDesignRoutes', + editorType: 'LABKEY.ext4.ComboBox', + editorConfig: LABKEY.VaccineDesign.Utils.getStudyDesignComboConfig('Route', 125, 'StudyDesignRoutes') + }] + } + }); + } + } + return this.columnConfigs; } }); \ No newline at end of file diff --git a/study/webapp/study/vaccineDesign/TreatmentSchedule.js b/study/webapp/study/vaccineDesign/TreatmentSchedule.js index 7d8a1dd5..732910fe 100644 --- a/study/webapp/study/vaccineDesign/TreatmentSchedule.js +++ b/study/webapp/study/vaccineDesign/TreatmentSchedule.js @@ -5,7 +5,7 @@ Ext4.define('LABKEY.VaccineDesign.TreatmentSchedulePanel', { bodyStyle : 'background-color: transparent;', - width: 1300, + width: 1400, disableEdit : true, @@ -27,7 +27,8 @@ Ext4.define('LABKEY.VaccineDesign.TreatmentSchedulePanel', { if (!this.treatmentsGrid) { this.treatmentsGrid = Ext4.create('LABKEY.VaccineDesign.TreatmentsGrid', { - disableEdit: this.disableEdit + disableEdit: this.disableEdit, + productRoles: this.productRoles }); this.treatmentsGrid.on('dirtychange', this.enableSaveButton, this); @@ -141,7 +142,7 @@ Ext4.define('LABKEY.VaccineDesign.TreatmentSchedulePanel', { var recData = Ext4.clone(record.data); index++; - // drop any empty immunogen or adjuvant rows that were just added + // drop any empty immunogen or adjuvant or challenge rows that were just added recData['Products'] = []; Ext4.each(recData['Immunogen'], function(immunogen) { @@ -153,6 +154,11 @@ Ext4.define('LABKEY.VaccineDesign.TreatmentSchedulePanel', { if (Ext4.isDefined(adjuvant['RowId']) || LABKEY.VaccineDesign.Utils.objectHasData(adjuvant)) recData['Products'].push(adjuvant); }, this); + Ext4.each(recData['Challenge'], function(challenge) + { + if (Ext4.isDefined(challenge['RowId']) || LABKEY.VaccineDesign.Utils.objectHasData(challenge)) + recData['Products'].push(challenge); + }, this); // drop any empty treatment rows that were just added var hasData = recData['Label'] != '' || recData['Description'] != '' || recData['Products'].length > 0; @@ -269,7 +275,7 @@ Ext4.define('LABKEY.VaccineDesign.TreatmentsGrid', { mainTitle : 'Treatments', - width: 1300, + width: 1400, studyDesignQueryNames : ['StudyDesignRoutes', 'Product', 'DoseAndRoute'], @@ -315,60 +321,50 @@ Ext4.define('LABKEY.VaccineDesign.TreatmentsGrid', { dataIndex: 'Description', editorType: 'Ext.form.field.TextArea', editorConfig: LABKEY.VaccineDesign.Utils.getStudyDesignTextConfig('Description', 185, '95%') - }, { - label: 'Immunogens', - width: 430, - dataIndex: 'Immunogen', - subgridConfig: { - columns: [{ - label: 'Immunogen', - width: 200, - dataIndex: 'ProductId', - required: true, - queryName: 'Product', - editorType: 'LABKEY.ext4.ComboBox', - editorConfig : this.getProductEditor('Immunogen') - },{ - label: 'Dose and Route', - width: 200, - dataIndex: 'DoseAndRoute', - queryName: 'DoseAndRoute', - editorType: 'LABKEY.ext4.ComboBox', - editorConfig: this.getDoseAndRouteEditorConfig() - }] - } - }, { - label: 'Adjuvants', - width: 430, - dataIndex: 'Adjuvant', - subgridConfig: { - columns: [{ - label: 'Adjuvant', - width: 200, - dataIndex: 'ProductId', - required: true, - queryName: 'Product', - editorType: 'LABKEY.ext4.ComboBox', - editorConfig: this.getProductEditor('Adjuvant') - },{ - label: 'Dose and Route', - width: 200, - dataIndex: 'DoseAndRoute', - queryName: 'DoseAndRoute', - editorType: 'LABKEY.ext4.ComboBox', - editorConfig: this.getDoseAndRouteEditorConfig() - }] - } }]; + + if (Ext4.isArray(this.productRoles)) { + Ext4.each(this.productRoles, function(role){ + var roleColumn = this.getProductRoleColumn(role); + this.columnConfigs.push(roleColumn); + }, this); + } } return this.columnConfigs; }, + getProductRoleColumn: function(roleName) { + var column = { + label: roleName + 's', + width: 310, + dataIndex: roleName, + subgridConfig: { + columns: [{ + label: roleName, + width: 140, + dataIndex: 'ProductId', + required: true, + queryName: 'Product', + editorType: 'LABKEY.ext4.ComboBox', + editorConfig: this.getProductEditor(roleName) + },{ + label: 'Dose and Route', + width: 140, + dataIndex: 'DoseAndRoute', + queryName: 'DoseAndRoute', + editorType: 'LABKEY.ext4.ComboBox', + editorConfig: this.getDoseAndRouteEditorConfig() + }] + } + }; + return column; + }, + getProductEditor : function(roleName){ var filter = LABKEY.Filter.create('Role', roleName), - cfg = LABKEY.VaccineDesign.Utils.getStudyDesignComboConfig('ProductId', 185, 'Product', filter, 'Label', 'RowId'); + cfg = LABKEY.VaccineDesign.Utils.getStudyDesignComboConfig('ProductId', 125, 'Product', filter, 'Label', 'RowId'); cfg.listeners = { scope: this, @@ -396,7 +392,7 @@ Ext4.define('LABKEY.VaccineDesign.TreatmentsGrid', { return { hideFieldLabel: true, name: 'DoseAndRoute', - width: 185, + width: 125, forceSelection : false, // allow usage of inactive types editable : false, queryMode : 'local', @@ -688,7 +684,7 @@ Ext4.define('LABKEY.VaccineDesign.TreatmentScheduleGrid', { // update the outer panel width if necessary var outerPanel = this.up('panel'); if (outerPanel != null) - outerPanel.setWidth(Math.max(width, 1300)); + outerPanel.setWidth(Math.max(width, 1400)); this.columnConfigs = columnConfigs.concat(visitConfigs); } From 06cb2d7c1bbfdd73a8ad90785cea3b9d4f6513e4 Mon Sep 17 00:00:00 2001 From: Xing Yang Date: Mon, 10 Oct 2016 23:31:30 +0000 Subject: [PATCH 115/218] Spec ID: 27863 Study registration iteration - treatment schedule UI for single table display --- .../controllers/StudyDesignController.java | 16 +- .../study/model/TreatmentProductImpl.java | 26 +- .../view/studydesign/manageTreatments.jsp | 7 +- .../study/vaccineDesign/BaseDataView.js | 6 + .../study/vaccineDesign/TreatmentDialog.js | 126 +++++ .../study/vaccineDesign/TreatmentSchedule.js | 4 +- .../TreatmentScheduleSingleTablePanel.js | 534 ++++++++++++++++++ study/webapp/study/vaccineDesign/Utils.js | 2 + .../study/vaccineDesign/VaccineDesign.css | 17 + .../study/vaccineDesign/vaccineDesign.lib.xml | 2 + 10 files changed, 735 insertions(+), 5 deletions(-) create mode 100644 study/webapp/study/vaccineDesign/TreatmentDialog.js create mode 100644 study/webapp/study/vaccineDesign/TreatmentScheduleSingleTablePanel.js diff --git a/study/src/org/labkey/study/controllers/StudyDesignController.java b/study/src/org/labkey/study/controllers/StudyDesignController.java index e439d929..0c95f60b 100644 --- a/study/src/org/labkey/study/controllers/StudyDesignController.java +++ b/study/src/org/labkey/study/controllers/StudyDesignController.java @@ -265,7 +265,9 @@ public ApiResponse execute(GetStudyTreatmentsForm form, BindException errors) th List studyTreatments = TreatmentManager.getInstance().getStudyTreatments(getContainer(), getUser()); for (TreatmentImpl treatment : studyTreatments) { - // note: we are currently only including the base fields for this extensible table + if (form.getTreatmentId() > 0 && form.getTreatmentId() != treatment.getRowId()) + continue; + Map treatmentProperties = treatment.serialize(); List> treatmentProductList = new ArrayList<>(); @@ -320,6 +322,8 @@ private static class GetStudyTreatmentsForm { private boolean _splitByRole; + private int treatmentId; + public boolean isSplitByRole() { return _splitByRole; @@ -329,6 +333,16 @@ public void setSplitByRole(boolean splitByRole) { _splitByRole = splitByRole; } + + public int getTreatmentId() + { + return treatmentId; + } + + public void setTreatmentId(int treatmentId) + { + this.treatmentId = treatmentId; + } } @RequiresPermission(ReadPermission.class) diff --git a/study/src/org/labkey/study/model/TreatmentProductImpl.java b/study/src/org/labkey/study/model/TreatmentProductImpl.java index 40faef5d..6809fe71 100644 --- a/study/src/org/labkey/study/model/TreatmentProductImpl.java +++ b/study/src/org/labkey/study/model/TreatmentProductImpl.java @@ -35,6 +35,7 @@ */ public class TreatmentProductImpl implements TreatmentProduct { + public static final String PRODUCT_DOSE_DELIMITER = "-#-"; private Container _container; private int _rowId; private int _treatmentId; @@ -42,6 +43,7 @@ public class TreatmentProductImpl implements TreatmentProduct private String _dose; private String _route; private String _doseAndRoute; + private String _productDoseRoute; public TreatmentProductImpl() {} @@ -143,6 +145,7 @@ public Map serialize() props.put("Dose", getDose()); props.put("Route", getRoute()); props.put("DoseAndRoute", getDoseAndRoute()); + props.put("ProductDoseRoute", getProductDoseRoute()); return props; } @@ -152,13 +155,14 @@ public Map serialize() */ private void syncDoseAndRoute() { - if (getDoseAndRoute() == null && (getDose() != null || getRoute() != null)) + if (getDoseAndRoute() == null && (getDose() != null || getRoute() != null) && getProductId() > 0) { // get the entry from the DoseAndRoute table so we can serialize the label DoseAndRoute doseAndRoute = TreatmentManager.getInstance().getDoseAndRoute(getContainer(), getDose(), getRoute(), getProductId()); if (doseAndRoute != null) { setDoseAndRoute(doseAndRoute.getLabel()); + setProductDoseRoute(String.valueOf(getProductId() + PRODUCT_DOSE_DELIMITER + doseAndRoute.getLabel())); } } else if (getDoseAndRoute() != null && getDose() == null && getRoute() == null) @@ -190,7 +194,27 @@ public static TreatmentProductImpl fromJSON(@NotNull JSONObject o, Container con treatmentProduct.setRowId(o.getInt("RowId")); if (o.containsKey("DoseAndRoute")) treatmentProduct.setDoseAndRoute(o.getString("DoseAndRoute")); + if (o.containsKey("ProductDoseRoute")) + treatmentProduct.setProductDoseRoute(o.getString("ProductDoseRoute")); return treatmentProduct; } + + public void setProductDoseRoute(String productDoseRoute) + { + this._productDoseRoute = productDoseRoute; + } + + public String getProductDoseRoute() + { + return _productDoseRoute; + } + + private void populateProductDoseRoute(String productDoseRoute) + { + setProductDoseRoute(productDoseRoute); + String[] parts = productDoseRoute.split(PRODUCT_DOSE_DELIMITER); + setProductId(Integer.parseInt(parts[0])); + setDoseAndRoute(parts[1]); + } } diff --git a/study/src/org/labkey/study/view/studydesign/manageTreatments.jsp b/study/src/org/labkey/study/view/studydesign/manageTreatments.jsp index 1eb302ea..3eb2c790 100644 --- a/study/src/org/labkey/study/view/studydesign/manageTreatments.jsp +++ b/study/src/org/labkey/study/view/studydesign/manageTreatments.jsp @@ -73,6 +73,11 @@ column:'role', success: function(response) { var panelClass = 'LABKEY.VaccineDesign.TreatmentSchedulePanel'; + var productRoles = response.values.sort(function(a, b){ + var aOrder = a == 'Immunogen' ? 1 : a == 'Adjuvant' ? 2 : 3; + var bOrder = b == 'Immunogen' ? 1 : b == 'Adjuvant' ? 2 : 3; + return aOrder - bOrder; + }); <% if (bean.isSingleTable()) @@ -89,7 +94,7 @@ subjectNoun : <%=q(subjectNoun)%>, visitNoun : <%=q(visitNoun)%>, returnURL : <%=q(returnUrl)%>, - productRoles: response.values + productRoles: productRoles }); } }); diff --git a/study/webapp/study/vaccineDesign/BaseDataView.js b/study/webapp/study/vaccineDesign/BaseDataView.js index 8e65c56b..7b2ce625 100644 --- a/study/webapp/study/vaccineDesign/BaseDataView.js +++ b/study/webapp/study/vaccineDesign/BaseDataView.js @@ -393,6 +393,12 @@ Ext4.define('LABKEY.VaccineDesign.BaseDataView', { else config.value = currentValue; + if (column.isTreatmentLookup) { + var treatmentLabel = this.getTreatmentCellDisplayValue(currentValue, column.lookupStoreId); + config.value = treatmentLabel; + config.treatmentId = currentValue; + } + // create a new form field to place in the td cell var field = Ext4.create(editor.type, Ext4.apply(editor.config, config)); diff --git a/study/webapp/study/vaccineDesign/TreatmentDialog.js b/study/webapp/study/vaccineDesign/TreatmentDialog.js new file mode 100644 index 00000000..9ebac089 --- /dev/null +++ b/study/webapp/study/vaccineDesign/TreatmentDialog.js @@ -0,0 +1,126 @@ +/* + * Copyright (c) 2014-2016 LabKey Corporation + * + * Licensed under the Apache License, Version 2.0: http://www.apache.org/licenses/LICENSE-2.0 + */ +Ext4.define('LABKEY.VaccineDesign.TreatmentDialog', { + + extend: 'Ext.window.Window', + + bodyStyle: 'overflow-y: auto; padding: 10px;', + + cls: 'treatment-dialog', + + initComponent : function() + { + this.items = this.getForm(); + this.callParent(); + }, + + getForm : function() + { + if (!this.formPanel) { + this.formPanel = Ext4.create('Ext.form.Panel',{ + border : false, + layout : 'hbox', + fieldDefaults :{ + labelAlign : 'top', + labelWidth : 130 + }, + items : this.getFormItems(), + scope : this + }); + } + return this.formPanel; + }, + + getFormItems : function() { + var productRolesItems = {}, columns = []; + Ext4.each(this.productRoles, function(role){ + var checkedProducts = this.treatmentDetails ? this.treatmentDetails[role] : []; + productRolesItems[role] = []; + var values = this.getProductAndDoseRouteValues(role); + Ext4.each(values, function(val){ + var checked = false; + Ext4.each(checkedProducts, function(product){ + if (product.ProductDoseRoute == val.ProductDoseRoute) { + checked = true; + return false; + } + }); + productRolesItems[role].push({ + boxLabel: val.Label, + name: role, + inputValue: val.ProductDoseRoute, + checked: checked, + width: 170, + cls: 'dialog-product-label', + listeners: { + render: function (cmp) + { + //tooltip + cmp.getEl().dom.title = val.Label; + } + } + }); + }, this); + }, this); + + Ext4.iterate(productRolesItems, function(key, val){ + var column = { + xtype: 'checkboxgroup', + fieldLabel: key, + colspan: 1, + columns: 1, + flex: 0.75, + height: 22 * val.length + 22, + style: 'margin-left: 20px;', + items: val + + }; + columns.push(column); + }); + return columns; + }, + + getProductAndDoseRouteValues : function(productRole) + { + var data = []; + + Ext4.each(Ext4.getStore('Product').getRange(), function(product) + { + if (!product.get('RowId') || product.get('Role') != productRole) + return; + Ext4.each(Ext4.getStore('DoseAndRoute').getRange(), function(dose) + { + if (dose.get('RowId') != null && dose.get('ProductId') == product.get('RowId') && product.get('Role') == productRole) { + var productDose = Ext4.clone(dose.data); + productDose.Label = product.get('Label') + ' - ' + dose.get('Label'); + productDose.ProductDoseRoute = dose.get('ProductId') + '-#-' + dose.get('Label'); + data.push(productDose); + } + }, this); + }, this); + + return data; + }, + + getTreatmentFormValues: function() { + var productValues = this.getForm().getValues(), params = {}; + Ext4.iterate(productValues, function(key, val){ + params[key] = []; + if (val) { + if (Ext4.isArray(val)){ + Ext4.each(val, function(v){ + params[key].push({ProductDoseRoute: v}) + }) + } + else { + params[key].push({ProductDoseRoute: val}) + } + } + }); + return params; + } +}); + diff --git a/study/webapp/study/vaccineDesign/TreatmentSchedule.js b/study/webapp/study/vaccineDesign/TreatmentSchedule.js index 732910fe..061cb03e 100644 --- a/study/webapp/study/vaccineDesign/TreatmentSchedule.js +++ b/study/webapp/study/vaccineDesign/TreatmentSchedule.js @@ -447,7 +447,7 @@ Ext4.define('LABKEY.VaccineDesign.TreatmentsGrid', { //Override updateSubgridRecordValue : function(record, outerDataIndex, subgridIndex, fieldName, newValue) { - var preProductIds = Ext4.Array.pluck(record.get('Immunogen'), 'ProductId').concat(Ext4.Array.pluck(record.get('Adjuvant'), 'ProductId')); + var preProductIds = Ext4.Array.pluck(record.get('Immunogen'), 'ProductId').concat(Ext4.Array.pluck(record.get('Adjuvant'), 'ProductId')).concat(Ext4.Array.pluck(record.get('Challenge'), 'ProductId')); this.callParent([record, outerDataIndex, subgridIndex, fieldName, newValue]); @@ -459,7 +459,7 @@ Ext4.define('LABKEY.VaccineDesign.TreatmentsGrid', { //Override removeSubgridRecord : function(target, record) { - var preProductIds = Ext4.Array.pluck(record.get('Immunogen'), 'ProductId').concat(Ext4.Array.pluck(record.get('Adjuvant'), 'ProductId')); + var preProductIds = Ext4.Array.pluck(record.get('Immunogen'), 'ProductId').concat(Ext4.Array.pluck(record.get('Adjuvant'), 'ProductId')).concat(Ext4.Array.pluck(record.get('Challenge'), 'ProductId')); this.callParent([target, record]); this.populateTreatmentLabel(record, preProductIds); this.refresh(true); diff --git a/study/webapp/study/vaccineDesign/TreatmentScheduleSingleTablePanel.js b/study/webapp/study/vaccineDesign/TreatmentScheduleSingleTablePanel.js new file mode 100644 index 00000000..5b5326cb --- /dev/null +++ b/study/webapp/study/vaccineDesign/TreatmentScheduleSingleTablePanel.js @@ -0,0 +1,534 @@ +Ext4.define('LABKEY.VaccineDesign.TreatmentScheduleSingleTablePanel', { + extend : 'Ext.panel.Panel', + + border : false, + + bodyStyle : 'background-color: transparent;', + + width: 1400, + + disableEdit : true, + + dirty : false, + + returnURL : null, + + initComponent : function() + { + this.items = [this.getTreatmentScheduleGrid(), this.getButtonBar()]; + + this.callParent(); + + window.onbeforeunload = LABKEY.beforeunload(this.beforeUnload, this); + }, + + getTreatmentScheduleGrid : function() + { + if (!this.treatmentScheduleGrid) + { + this.treatmentScheduleGrid = Ext4.create('LABKEY.VaccineDesign.TreatmentScheduleSingleTableGrid', { + padding: '20px 0', + disableEdit: this.disableEdit, + subjectNoun: this.subjectNoun, + visitNoun: this.visitNoun, + productRoles: this.productRoles + }); + + this.treatmentScheduleGrid.on('dirtychange', this.enableSaveButton, this); + this.treatmentScheduleGrid.on('celledited', this.enableSaveButton, this); + } + + return this.treatmentScheduleGrid; + }, + + getButtonBar : function() + { + if (!this.buttonBar) + { + this.buttonBar = Ext4.create('Ext.toolbar.Toolbar', { + dock: 'bottom', + ui: 'footer', + padding: 0, + style : 'background-color: transparent;', + defaults: {width: 75}, + items: [this.getSaveButton(), this.getCancelButton()] + }); + } + + return this.buttonBar; + }, + + getSaveButton : function() + { + if (!this.saveButton) + { + this.saveButton = Ext4.create('Ext.button.Button', { + text: 'Save', + disabled: true, + hidden: this.disableEdit, + handler: this.saveTreatmentSchedule, + scope: this + }); + } + + return this.saveButton; + }, + + enableSaveButton : function() + { + this.setDirty(true); + this.getSaveButton().enable(); + }, + + getCancelButton : function() + { + if (!this.cancelButton) + { + this.cancelButton = Ext4.create('Ext.button.Button', { + margin: this.disableEdit ? 0 : '0 0 0 10px', + text: this.disableEdit ? 'Done' : 'Cancel', + handler: this.goToReturnURL, + scope: this + }); + } + + return this.cancelButton; + }, + + saveTreatmentSchedule : function() + { + this.getEl().mask('Saving...'); + + var treatments = [], cohorts = [], + index = 0, errorMsg = []; + + //Ext4.each(this.getTreatmentsGrid().getStore().getRange(), function(record) + //{ + // var recData = Ext4.clone(record.data); + // index++; + // + // // drop any empty immunogen or adjuvant or challenge rows that were just added + // recData['Products'] = []; + // Ext4.each(recData['Immunogen'], function(immunogen) + // { + // if (Ext4.isDefined(immunogen['RowId']) || LABKEY.VaccineDesign.Utils.objectHasData(immunogen)) + // recData['Products'].push(immunogen); + // }, this); + // Ext4.each(recData['Adjuvant'], function(adjuvant) + // { + // if (Ext4.isDefined(adjuvant['RowId']) || LABKEY.VaccineDesign.Utils.objectHasData(adjuvant)) + // recData['Products'].push(adjuvant); + // }, this); + // Ext4.each(recData['Challenge'], function(challenge) + // { + // if (Ext4.isDefined(challenge['RowId']) || LABKEY.VaccineDesign.Utils.objectHasData(challenge)) + // recData['Products'].push(challenge); + // }, this); + // + // // drop any empty treatment rows that were just added + // var hasData = recData['Label'] != '' || recData['Description'] != '' || recData['Products'].length > 0; + // if (Ext4.isDefined(recData['RowId']) || hasData) + // { + // var treatmentLabel = recData['Label'] != '' ? '\'' + recData['Label'] + '\'' : index; + // + // // validation: treatment must have at least one immunogen or adjuvant, no duplicate immunogens/adjuvants for a treatment + // var treatmentProductIds = Ext4.Array.clean(Ext4.Array.pluck(recData['Products'], 'ProductId')); + // if (recData['Products'].length == 0) + // errorMsg.push('Treatment ' + treatmentLabel + ' must have at least one immunogen or adjuvant defined.'); + // else if (treatmentProductIds.length != Ext4.Array.unique(treatmentProductIds).length) + // errorMsg.push('Treatment ' + treatmentLabel + ' contains a duplicate immunogen or adjuvant.'); + // else + // treatments.push(recData); + // } + //}, this); + + if (errorMsg.length > 0) + { + this.onFailure(errorMsg.join('
      ')); + return; + } + + Ext4.each(this.getTreatmentScheduleGrid().getStore().getRange(), function(record) + { + var recData = Ext4.clone(record.data); + + // drop any empty cohort rows that were just added + var hasData = recData['Label'] != '' || recData['SubjectCount'] != '' || recData['VisitMap'].length > 0; + if (Ext4.isDefined(recData['RowId']) || hasData) + { + var countVal = Number(recData['SubjectCount']); + if (isNaN(countVal) || countVal < 0) + errorMsg.push('Cohort ' + this.subjectNoun.toLowerCase() + ' count values must be a positive integer: ' + recData['SubjectCount'] + '.'); + else + cohorts.push(recData); + } + }, this); + + if (errorMsg.length > 0) + { + this.onFailure(errorMsg.join('
      ')); + return; + } + + LABKEY.Ajax.request({ + url : LABKEY.ActionURL.buildURL('study-design', 'updateTreatmentSchedule.api'), + method : 'POST', + jsonData: { + treatments: treatments, + cohorts: cohorts + }, + scope: this, + success: function(response) + { + var resp = Ext4.decode(response.responseText); + if (resp.success) + this.goToReturnURL(); + else + this.onFailure(); + }, + failure: function(response) + { + var resp = Ext4.decode(response.responseText); + if (resp.errors) + this.onFailure(Ext4.Array.pluck(resp.errors, 'message').join('
      ')); + else + this.onFailure(resp.exception); + } + }); + }, + + goToReturnURL : function() + { + this.setDirty(false); + window.location = this.returnURL; + }, + + onFailure : function(text) + { + Ext4.Msg.show({ + title: 'Error', + msg: text || 'Unknown error occurred.', + icon: Ext4.Msg.ERROR, + buttons: Ext4.Msg.OK + }); + + this.getEl().unmask(); + }, + + setDirty : function(dirty) + { + this.dirty = dirty; + LABKEY.Utils.signalWebDriverTest("treatmentScheduleDirty", dirty); + }, + + isDirty : function() + { + return this.dirty; + }, + + beforeUnload : function() + { + if (!this.disableEdit && this.isDirty()) + return 'Please save your changes.'; + } +}); + +Ext4.define('LABKEY.VaccineDesign.TreatmentScheduleSingleTableGrid', { + extend : 'LABKEY.VaccineDesign.BaseDataViewAddVisit', + + cls : 'study-vaccine-design vaccine-design-cohorts', + + mainTitle : 'Treatment Schedule', + + width: 350, + + subjectNoun : 'Subject', + + visitNoun : 'Visit', + + studyDesignQueryNames : ['StudyDesignRoutes', 'Product', 'DoseAndRoute'], + + getStore : function() + { + if (!this.store) + { + this.store = Ext4.create('Ext.data.Store', { + model : 'LABKEY.VaccineDesign.Cohort', + sorters: [{ property: 'RowId', direction: 'ASC' }] + }); + this.getTreatmentsStore(); + this.queryStudyTreatmentSchedule(); + } + + return this.store; + }, + + queryStudyTreatmentSchedule : function() + { + LABKEY.Ajax.request({ + url: LABKEY.ActionURL.buildURL('study-design', 'getStudyTreatmentSchedule', null, {splitByRole: true}), + method: 'GET', + scope: this, + success: function (response) + { + var o = Ext4.decode(response.responseText); + if (o.success) + { + this.getVisitStore(o['visits']); + this.getStore().loadData(o['cohorts']); + this.getStore().fireEvent('load', this.getStore()); + } + } + }); + }, + + getVisitStore : function(data) + { + if (!this.visitStore) + { + this.visitStore = Ext4.create('Ext.data.Store', { + model : 'LABKEY.VaccineDesign.Visit', + data : data + }); + } + + return this.visitStore; + }, + + getTreatmentsStore : function() + { + if (!this.treatmentsStore) + { + this.treatmentsStore = Ext4.create('Ext.data.Store', { + storeId : 'TreatmentsGridStore', + model : 'LABKEY.VaccineDesign.Treatment', + proxy: { + type: 'ajax', + url : LABKEY.ActionURL.buildURL("study-design", "getStudyTreatments", null, {splitByRole: true}), + reader: { + type: 'json', + root: 'treatments' + } + }, + sorters: [{ property: 'RowId', direction: 'ASC' }], + autoLoad: true + }); + } + + return this.treatmentsStore; + }, + + getVisitColumnConfigs : function() + { + var visitConfigs = []; + + Ext4.each(this.getVisitStore().getRange(), function(visit) + { + if (visit.get('Included')) + { + visitConfigs.push({ + label: visit.get('Label') || (this.visitNoun + visit.get('RowId')), + width: 150, + dataIndex: 'VisitMap', + dataIndexArrFilterProp: 'VisitId', + dataIndexArrFilterValue: visit.get('RowId'), + dataIndexArrValue: 'TreatmentId', + lookupStoreId: 'TreatmentsGridStore', + editorType: 'Ext.form.field.Text', + editorConfig: this.getTreatmentFieldConfig, + isTreatmentLookup: true + }); + } + }, this); + + if (visitConfigs.length == 0 && !this.disableEdit) + { + visitConfigs.push({ + label: 'No ' + this.visitNoun + 's Defined', + displayValue: '', + width: 160 + }); + } + + return visitConfigs; + }, + + getTreatmentFieldConfig : function() + { + var me = this; + return { + + hideFieldLabel: true, + name: 'VisitMap', + width: 135, + editable : false, + listeners: { + render: function(cmp) { + cmp.getEl().on('click', function(){ + var win; + var popupConfig = { + productRoles: me.productRoles, + autoScroll : true, + buttonAlign : 'right', + modal: true, + width: 100 + me.productRoles.length * 170, + height: 300, + border: false, + closable: false, + title: 'Treatment', + draggable: false, + buttons: [{ + text: 'Cancel', + onClick : function () { + win.close(); + } + },{ + text: 'Okay', + cls: 'commentSubmit', + onClick : function () { + var productValues = win.getTreatmentFormValues(); + // todo api to save and get treatment + + } + }] + + }; + if (cmp.treatmentId) { + Ext4.Ajax.request({ + url : LABKEY.ActionURL.buildURL("study-design", "getStudyTreatments", null, {splitByRole: true, treatmentId: cmp.treatmentId}), + method : 'POST', + success: LABKEY.Utils.getCallbackWrapper(function(response){ + popupConfig.treatmentDetails = response.treatments.length > 0 ? response.treatments[0] : null; + win = new LABKEY.VaccineDesign.TreatmentDialog(popupConfig); + win.show(); + + }, me) + }); + } + else { + win = new LABKEY.VaccineDesign.TreatmentDialog(popupConfig); + win.show(); + } + }); + } + } + }; + }, + + //Override + getColumnConfigs : function() + { + if (!this.columnConfigs) + { + var columnConfigs = [{ + label: 'Group / Cohort', + width: 200, + dataIndex: 'Label', + required: true, + editorType: 'Ext.form.field.Text', + editorConfig: LABKEY.VaccineDesign.Utils.getStudyDesignTextConfig('Label', 185) + },{ + label: this.subjectNoun + ' Count', + width: 130, + dataIndex: 'SubjectCount', + editorType: 'Ext.form.field.Number', + editorConfig: LABKEY.VaccineDesign.Utils.getStudyDesignNumberConfig('SubjectCount', 115) + }]; + + var visitConfigs = this.getVisitColumnConfigs(); + + // update the width based on the number of visit columns + var width = 400 + (Math.max(2, visitConfigs.length) * 150); + this.setWidth(width); + + // update the outer panel width if necessary + var outerPanel = this.up('panel'); + if (outerPanel != null) + outerPanel.setWidth(Math.max(width, 1400)); + + this.columnConfigs = columnConfigs.concat(visitConfigs); + } + + return this.columnConfigs; + }, + + //Override + getNewModelInstance : function() + { + var newCohort = LABKEY.VaccineDesign.Cohort.create(); + newCohort.set('VisitMap', []); + return newCohort; + }, + + //Override + getDeleteConfirmationMsg : function() + { + return 'Are you sure you want to delete the selected group / cohort and its associated treatment / visit mapping records?'; + }, + + //Override + getCurrentCellValue : function(column, record, dataIndex, outerDataIndex, subgridIndex) + { + var value = this.callParent([column, record, dataIndex, outerDataIndex, subgridIndex]); + + if (Ext4.isArray(value)) + { + var matchingIndex = LABKEY.VaccineDesign.Utils.getMatchingRowIndexFromArray(value, column.dataIndexArrFilterProp, column.dataIndexArrFilterValue); + if (matchingIndex > -1) + return value[matchingIndex][column.dataIndexArrValue]; + else + return null; + } + + return value; + }, + + getTreatmentCellDisplayValue : function(val, lookupStore) + { + var displayVal = val; + if (Ext4.isDefined(lookupStore) && val != null && val != '') + { + var store = Ext4.getStore(lookupStore); + if (store != null) + { + var record = store.findRecord('RowId', val); + if (record != null) + displayVal = record.get('Label'); + } + } + return displayVal; + }, + + //Override + updateStoreRecordValue : function(record, column, newValue) + { + // special case for editing the value of one of the pivot visit columns + if (column.dataIndex == 'VisitMap') + { + var visitMapArr = record.get(column.dataIndex), + matchingIndex = LABKEY.VaccineDesign.Utils.getMatchingRowIndexFromArray(visitMapArr, column.dataIndexArrFilterProp, column.dataIndexArrFilterValue); + + if (matchingIndex > -1) + { + if (newValue != null) + visitMapArr[matchingIndex][column.dataIndexArrValue] = newValue; + else + Ext4.Array.splice(visitMapArr, matchingIndex, 1); + } + else if (newValue != null) + { + visitMapArr.push({ + CohortId: record.get('RowId'), + VisitId: column.dataIndexArrFilterValue, + TreatmentId: newValue + }); + } + + this.fireEvent('celledited', this, 'VisitMap', visitMapArr); + } + else + { + this.callParent([record, column, newValue]); + } + } +}); \ No newline at end of file diff --git a/study/webapp/study/vaccineDesign/Utils.js b/study/webapp/study/vaccineDesign/Utils.js index 56bdeb85..3f6f4a6d 100644 --- a/study/webapp/study/vaccineDesign/Utils.js +++ b/study/webapp/study/vaccineDesign/Utils.js @@ -88,6 +88,8 @@ Ext4.define('LABKEY.VaccineDesign.Utils', { // special case to query ProductId column for DoseAndRoute table else if (queryName == 'DoseAndRoute') columns += ',ProductId'; + else if (queryName == 'Product') + columns += ',Label, Role'; return Ext4.create('LABKEY.ext4.Store', { storeId: key, diff --git a/study/webapp/study/vaccineDesign/VaccineDesign.css b/study/webapp/study/vaccineDesign/VaccineDesign.css index fef55dda..2458985e 100644 --- a/study/webapp/study/vaccineDesign/VaccineDesign.css +++ b/study/webapp/study/vaccineDesign/VaccineDesign.css @@ -64,4 +64,21 @@ .study-vaccine-design td.action i:hover { cursor: pointer; color: #000000; +} + +.dialog-product-label .x4-form-cb-label-after { + width: 150px; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} +.treatment-dialog { + background-color: #C0C0C0; + border-color: dimgray; + box-shadow: #C0C0C0 0 1px 0 0 inset,#C0C0C0 0 -1px 0 0 inset,#C0C0C0 -1px 0 0 0 inset,#C0C0C0 1px 0 0 0 inset; +} +.treatment-dialog .x4-window-header-default{ + background-color: #C0C0C0; + border-color: dimgray; + box-shadow: #C0C0C0 0 1px 0 0 inset,#C0C0C0 0 -1px 0 0 inset,#C0C0C0 -1px 0 0 0 inset,#C0C0C0 1px 0 0 0 inset; } \ No newline at end of file diff --git a/study/webapp/study/vaccineDesign/vaccineDesign.lib.xml b/study/webapp/study/vaccineDesign/vaccineDesign.lib.xml index 5d84ac99..3e52363f 100644 --- a/study/webapp/study/vaccineDesign/vaccineDesign.lib.xml +++ b/study/webapp/study/vaccineDesign/vaccineDesign.lib.xml @@ -5,7 +5,9 @@ -Enter vaccine design information in the grids below. +<% +if (isDataspaceProject) +{ + ActionURL projectManageProductsURL = new ActionURL(StudyDesignController.ManageStudyProductsAction.class, getContainer().getProject()); + projectManageProductsURL.addReturnURL(getActionURL()); +%> +Vaccine design information is defined at the project level for Dataspace projects. The grids below are read-only.
      • - Configure dropdown options for challenge types, immunogen types, genes, subtypes and routes at the project level to be shared across study designs or within this folder for - study specific properties: + Use the manage study products page at the project level to make changes to the information listed below. + <%=textLink("Manage Study Products", projectManageProductsURL)%>
      • +<% +} +else +{ +%> +Enter vaccine design information in the grids below. +
        +
        • Each immunogen, adjuvant and challenge in the study should be listed on one row of the grids below.
        • Immunogens, adjuvants and challenges should have unique labels.
        • If possible, the immunogen description should include specific sequences of HIV Antigens included in the immunogen.
        • @@ -139,6 +154,14 @@ Enter vaccine design information in the grids below. %> <%=textLink("Manage Treatments", manageTreatmentsURL)%> +<% +} +%> +
        • + Configure dropdown options for challenge types, immunogen types, genes, subtypes and routes at the project level to be shared across study designs or within this folder for + study specific properties: +
        +
        diff --git a/study/src/org/labkey/study/view/studydesign/manageTreatments.jsp b/study/src/org/labkey/study/view/studydesign/manageTreatments.jsp index 8a37c4c6..7ed8e32e 100644 --- a/study/src/org/labkey/study/view/studydesign/manageTreatments.jsp +++ b/study/src/org/labkey/study/view/studydesign/manageTreatments.jsp @@ -52,7 +52,7 @@ boolean canManageStudy = c.hasPermission(user, ManageStudyPermission.class); // treatment schedule is editable for the individual studies in a Dataspace project - boolean disableEdit = c.isProject() && c.isDataspace(); + boolean isDataspaceProject = c.isProject() && c.isDataspace(); String visitNoun = "Visit"; if (study != null && study.getTimepointType() == TimepointType.DATE) @@ -83,7 +83,7 @@ Ext4.create(panelClass, { renderTo : 'treatment-schedule-panel', - disableEdit : <%=disableEdit%>, + disableEdit : <%=isDataspaceProject%>, subjectNoun : <%=q(subjectNoun)%>, visitNoun : <%=q(visitNoun)%>, returnURL : <%=q(returnUrl)%>, @@ -94,6 +94,17 @@ }); +<% +if (isDataspaceProject) +{ +%> +Treatment information is defined at the individual study level for Dataspace projects. The grids below are read-only. +

        +<% +} +else +{ +%> Enter treatment information in the grids below.
          @@ -151,5 +162,8 @@ Enter treatment information in the grids below. %>
        -
        -
        \ No newline at end of file +<% +} +%> + +
        \ No newline at end of file From 8e9fe43b68b99d5fb6d774243b4aa1b81b34183f Mon Sep 17 00:00:00 2001 From: Karl Lum Date: Mon, 2 Jan 2017 20:41:21 +0000 Subject: [PATCH 137/218] 28865: **NAb QC: Can get into a state where some well values are not excluded even though they are selected (to be excluded). 28862: **Values in the Excluded Field Wells should be ordered 28859: **Reviewing QC Nab data shows column 0 but plate has column starting at 1 --- .../study/vaccineDesign/TreatmentDialog.js | 31 +------------------ 1 file changed, 1 insertion(+), 30 deletions(-) diff --git a/study/webapp/study/vaccineDesign/TreatmentDialog.js b/study/webapp/study/vaccineDesign/TreatmentDialog.js index 1fae01a5..f5f7aa02 100644 --- a/study/webapp/study/vaccineDesign/TreatmentDialog.js +++ b/study/webapp/study/vaccineDesign/TreatmentDialog.js @@ -150,36 +150,7 @@ Ext4.define('LABKEY.VaccineDesign.TreatmentDialog', { }, natural : function (aso, bso) { - // http://stackoverflow.com/questions/19247495/alphanumeric-sorting-an-array-in-javascript - var a, b, a1, b1, i= 0, n, L, - rx=/(\.\d+)|(\d+(\.\d+)?)|([^\d.]+)|(\.\D+)|(\.$)/g; - if (aso === bso) return 0; - a = aso.toLowerCase().match(rx); - b = bso.toLowerCase().match(rx); - - if (a == 'null' || b == 'null') { - var aEmpty = a == 'null'; - var bEmpty = b == 'null'; - - // both are empty - if (aEmpty && bEmpty) { - return 0; - } - - return aEmpty ? -1 : 1; - } - - L = a.length; - while (i < L) { - if (!b[i]) return 1; - a1 = a[i]; b1 = b[i++]; - if (a1 !== b1) { - n = a1 - b1; - if (!isNaN(n)) return n; - return a1 > b1 ? 1 : -1; - } - } - return b[i] ? -1 : 0; + return LABKEY.internal.SortUtil.naturalSort(aso, bso); }, getTreatmentFormValues: function() { From 3e94d258b87b0df8c778c9fd73b93002915abe6a Mon Sep 17 00:00:00 2001 From: Dave Bradlee Date: Thu, 26 Jan 2017 01:19:25 +0000 Subject: [PATCH 138/218] Issue 28635: NPE in hasPermission --- .../study/query/studydesign/StudyDesignLookupBaseTable.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/study/src/org/labkey/study/query/studydesign/StudyDesignLookupBaseTable.java b/study/src/org/labkey/study/query/studydesign/StudyDesignLookupBaseTable.java index c7a42f5b..175ae87e 100644 --- a/study/src/org/labkey/study/query/studydesign/StudyDesignLookupBaseTable.java +++ b/study/src/org/labkey/study/query/studydesign/StudyDesignLookupBaseTable.java @@ -158,7 +158,7 @@ private void validateValues(Map row) throws ValidationException public boolean hasPermission(@NotNull UserPrincipal user, @NotNull Class perm) { // These are editable in Dataspace, but not in a folder within a Dataspace - if (getContainer().getProject().isDataspace() && !getContainer().isDataspace()) + if (null == getContainer() || null == getContainer().getProject() || (getContainer().getProject().isDataspace() && !getContainer().isDataspace())) return false; return hasPermissionOverridable(user, perm); } From 35e9beb70d5bf3e44418f8e853fcc30a5e8b1771 Mon Sep 17 00:00:00 2001 From: Cory Nathe Date: Wed, 15 Mar 2017 15:13:54 +0000 Subject: [PATCH 139/218] Give warning message about trying to delete an in-use cohort from Manage Treatments page --- .../study/model/StudyTreatmentSchedule.java | 1 + study/webapp/study/vaccineDesign/Models.js | 1 + .../vaccineDesign/TreatmentScheduleBase.js | 26 +++++++++++++++---- 3 files changed, 23 insertions(+), 5 deletions(-) diff --git a/study/src/org/labkey/study/model/StudyTreatmentSchedule.java b/study/src/org/labkey/study/model/StudyTreatmentSchedule.java index 0c034ec9..b6701bd7 100644 --- a/study/src/org/labkey/study/model/StudyTreatmentSchedule.java +++ b/study/src/org/labkey/study/model/StudyTreatmentSchedule.java @@ -130,6 +130,7 @@ public List> serializeCohortMapping() mapProperties.put("RowId", cohort.getRowId()); mapProperties.put("Label", cohort.getLabel()); mapProperties.put("SubjectCount", cohort.getSubjectCount()); + mapProperties.put("CanDelete", !cohort.isInUse()); List> treatmentVisitMap = new ArrayList<>(); for (TreatmentVisitMapImpl mapping : TreatmentManager.getInstance().getStudyTreatmentVisitMap(_container, cohort.getRowId())) diff --git a/study/webapp/study/vaccineDesign/Models.js b/study/webapp/study/vaccineDesign/Models.js index 6c0fd7b7..6264b93e 100644 --- a/study/webapp/study/vaccineDesign/Models.js +++ b/study/webapp/study/vaccineDesign/Models.js @@ -37,6 +37,7 @@ Ext4.define('LABKEY.VaccineDesign.Cohort', { {name : 'Label', type : 'string'}, // the DataView XTemplate gets mad if this is defined as type 'int' {name : 'SubjectCount', type : 'string'}, + {name : 'CanDelete', type : 'boolean'}, {name : 'VisitMap', defaultValue: []} ] }); diff --git a/study/webapp/study/vaccineDesign/TreatmentScheduleBase.js b/study/webapp/study/vaccineDesign/TreatmentScheduleBase.js index 2aca05db..99426cd3 100644 --- a/study/webapp/study/vaccineDesign/TreatmentScheduleBase.js +++ b/study/webapp/study/vaccineDesign/TreatmentScheduleBase.js @@ -1,8 +1,8 @@ -/* - * Copyright (c) 2016 LabKey Corporation - * - * Licensed under the Apache License, Version 2.0: http://www.apache.org/licenses/LICENSE-2.0 - */ +/* + * Copyright (c) 2016 LabKey Corporation + * + * Licensed under the Apache License, Version 2.0: http://www.apache.org/licenses/LICENSE-2.0 + */ Ext4.define('LABKEY.VaccineDesign.TreatmentSchedulePanelBase', { extend : 'Ext.panel.Panel', @@ -343,6 +343,22 @@ Ext4.define('LABKEY.VaccineDesign.TreatmentScheduleGridBase', { return newCohort; }, + //Override + removeOuterRecord : function(title, record) { + if (!record.get('CanDelete')) { + Ext4.Msg.show({ + title: 'Unable to Remove Cohort', + msg: 'The selected cohort can not be removed because it is in-use in the study.
        ' + + Ext4.String.htmlEncode(record.get('Label')) + '', + buttons: Ext4.Msg.OK, + icon: Ext4.Msg.INFO + }); + } + else { + this.callParent([title, record]); + } + }, + //Override getDeleteConfirmationMsg : function() { From 847c22f723cfe823935f9e974d79f1d051e43329 Mon Sep 17 00:00:00 2001 From: Xing Yang Date: Fri, 7 Apr 2017 18:32:00 +0000 Subject: [PATCH 140/218] Merge from Hosting17.1 to Trunk -- svn merge -r50272:HEAD https://hedgehog.fhcrc.org/tor/stedi/branches/hosting17.1 ./ Merge conflict: global_gradle.properties_template SecurityManager.java SecurityApiActions.java SecurityController.java --- .../study/vaccineDesign/TreatmentDialog.js | 24 ++++++++++--------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/study/webapp/study/vaccineDesign/TreatmentDialog.js b/study/webapp/study/vaccineDesign/TreatmentDialog.js index f5f7aa02..4c3038ed 100644 --- a/study/webapp/study/vaccineDesign/TreatmentDialog.js +++ b/study/webapp/study/vaccineDesign/TreatmentDialog.js @@ -109,6 +109,7 @@ Ext4.define('LABKEY.VaccineDesign.TreatmentDialog', { productDose.ProductLabel = product.get('Label'); productDose.Label = product.get('Label') + ' - ' + dose.get('Label'); productDose.ProductDoseRoute = dose.get('ProductId') + '-#-' + dose.get('Label'); + productDose.SortKey = product.get('RowId'); data.push(productDose); hasDoseRoute = true; } @@ -119,6 +120,7 @@ Ext4.define('LABKEY.VaccineDesign.TreatmentDialog', { productDose.ProductLabel = product.get('Label'); productDose.Label = product.get('Label'); productDose.ProductDoseRoute = product.get('RowId') + '-#-'; + productDose.SortKey = product.get('RowId'); data.push(productDose); } else { @@ -129,30 +131,30 @@ Ext4.define('LABKEY.VaccineDesign.TreatmentDialog', { var productDose = Ext4.clone(checked); productDose.ProductLabel = checked['ProductId/Label']; productDose.Label = checked['ProductId/Label']; + productDose.SortKey = product.get('RowId'); data.push(productDose); } }); } }, this); - var me = this; data.sort(function(a, b){ - // if different product, sort by product - if (a.ProductLabel && b.ProductLabel && a.ProductLabel != b.ProductLabel) { - return me.natural(a.ProductLabel, b.ProductLabel); + // if different product, sort by product RowId + // otherwise use a natural sort on the generated product + dose/route label + if (a.SortKey > b.SortKey) { + return 1; + } + else if (a.SortKey < b.SortKey) { + return -1; + } + else { + return LABKEY.internal.SortUtil.naturalSort(a.Label, b.Label); } - // if same product, sort by dose/route - var doseA = a.ProductDoseRoute.replace(a.ProductLabel, ''), doseB = b.ProductDoseRoute.replace(b.ProductLabel, ''); - return me.natural(doseA, doseB); }); return data; }, - natural : function (aso, bso) { - return LABKEY.internal.SortUtil.naturalSort(aso, bso); - }, - getTreatmentFormValues: function() { var fields = this.getForm().getForm().getFields().items, treatmentLabel = ''; for (var f = 0; f < fields.length; f++) { From 34b3d6bb259a8027b6d90945b4ae3fa2a86553c4 Mon Sep 17 00:00:00 2001 From: Xing Yang Date: Tue, 25 Apr 2017 00:44:22 +0000 Subject: [PATCH 141/218] Merge from Hosting17.1 to Trunk -- svn merge -r50514:HEAD https://hedgehog.fhcrc.org/tor/stedi/branches/hosting17.1 ./ Merge conflicts: ListService.java FilterDialog.js ListServiceImpl.java ListManager.java --- study/src/org/labkey/study/model/TreatmentManager.java | 6 ++---- study/webapp/study/vaccineDesign/TreatmentSchedule.js | 10 ++++++++++ study/webapp/study/vaccineDesign/Utils.js | 2 +- 3 files changed, 13 insertions(+), 5 deletions(-) diff --git a/study/src/org/labkey/study/model/TreatmentManager.java b/study/src/org/labkey/study/model/TreatmentManager.java index 818c574e..6c295edd 100644 --- a/study/src/org/labkey/study/model/TreatmentManager.java +++ b/study/src/org/labkey/study/model/TreatmentManager.java @@ -349,16 +349,14 @@ public DoseAndRoute saveStudyProductDoseAndRoute(Container container, User user, public Collection getStudyProductsDoseAndRoute(Container container, User user, int productId) { - SimpleFilter filter = SimpleFilter.createContainerFilter(container); - filter.addCondition(FieldKey.fromParts("ProductId"), productId); + SimpleFilter filter = new SimpleFilter(FieldKey.fromParts("ProductId"), productId); return new TableSelector(StudySchema.getInstance().getTableInfoDoseAndRoute(), filter, null).getCollection(DoseAndRoute.class); } @Nullable public DoseAndRoute getDoseAndRoute(Container container, String dose, String route, int productId) { - SimpleFilter filter = SimpleFilter.createContainerFilter(container); - filter.addCondition(FieldKey.fromParts("ProductId"), productId); + SimpleFilter filter = new SimpleFilter(FieldKey.fromParts("ProductId"), productId); if (dose != null) filter.addCondition(FieldKey.fromParts("Dose"), dose); else diff --git a/study/webapp/study/vaccineDesign/TreatmentSchedule.js b/study/webapp/study/vaccineDesign/TreatmentSchedule.js index d81c6c39..d54ebbdf 100644 --- a/study/webapp/study/vaccineDesign/TreatmentSchedule.js +++ b/study/webapp/study/vaccineDesign/TreatmentSchedule.js @@ -257,6 +257,16 @@ Ext4.define('LABKEY.VaccineDesign.TreatmentsGrid', { productId = record.get(outerDataIndex)[subgridIndex]['ProductId']; cmp.bindStore(this.getNewDoseAndRouteComboStore(productId)); + }, + change : function(cmp, newValue, oldValue) { + var record = this.getStore().getAt(cmp.storeIndex), + outerDataIndex = cmp.outerDataIndex, + subgridIndex = Number(cmp.subgridIndex), + subRecord = record.get(outerDataIndex)[subgridIndex]; + + // if the ProductDoseRoute is set, we need to update it + if (Ext4.isDefined(subRecord['ProductDoseRoute']) && Ext4.isDefined(subRecord['ProductId'])) + subRecord['ProductDoseRoute'] = subRecord['ProductId'] + '-#-' + newValue; } } }; diff --git a/study/webapp/study/vaccineDesign/Utils.js b/study/webapp/study/vaccineDesign/Utils.js index a40b1e1f..35bb14d6 100644 --- a/study/webapp/study/vaccineDesign/Utils.js +++ b/study/webapp/study/vaccineDesign/Utils.js @@ -102,7 +102,7 @@ Ext4.define('LABKEY.VaccineDesign.Utils', { queryName: queryName, columns: columns, filterArray: Ext4.isDefined(filter) ? [filter] : (hasStudyDesignPrefix ? [LABKEY.Filter.create('Inactive', false)] : []), - containerFilter: LABKEY.container.type == 'project' || Ext4.isDefined(filter) || !hasStudyDesignPrefix ? 'Current' : 'CurrentPlusProject', + containerFilter: LABKEY.container.type == 'project' || Ext4.isDefined(filter) || !hasStudyDesignPrefix ? undefined : 'CurrentPlusProject', sort: '-Container/Path,Label', autoLoad: true, listeners: { From 86289c261f0d23a363aa67d3defee04a7ad1bf9d Mon Sep 17 00:00:00 2001 From: Xing Yang Date: Tue, 9 May 2017 21:33:34 +0000 Subject: [PATCH 142/218] LabKey UX Refresh - rename row to avoid bootstrap class conflict --- study/webapp/study/vaccineDesign/BaseDataView.js | 10 +++++----- study/webapp/study/vaccineDesign/TreatmentSchedule.js | 4 ++-- study/webapp/study/vaccineDesign/VaccineDesign.css | 2 +- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/study/webapp/study/vaccineDesign/BaseDataView.js b/study/webapp/study/vaccineDesign/BaseDataView.js index 31ead9f9..a0b4b4e1 100644 --- a/study/webapp/study/vaccineDesign/BaseDataView.js +++ b/study/webapp/study/vaccineDesign/BaseDataView.js @@ -64,7 +64,7 @@ Ext4.define('LABKEY.VaccineDesign.BaseDataView', { // add a single event listener to focus the first input field on the initial render this.on('renderviewcomplete', function() { - this.giveCellInputFocus('table.outer tr.row:first td.cell-value:first input', true); + this.giveCellInputFocus('table.outer tr.data-row:first td.cell-value:first input', true); LABKEY.Utils.signalWebDriverTest("VaccineDesign_renderviewcomplete"); }, this, {single: true}); @@ -90,7 +90,7 @@ Ext4.define('LABKEY.VaccineDesign.BaseDataView', { this.dataView = Ext4.create('Ext.view.View', { tpl: this.getDataViewTpl(), store: this.getStore(), - itemSelector: 'tr.row', + itemSelector: 'tr.data-row', disableSelection: true, setTemplate: function(newTpl) { @@ -118,7 +118,7 @@ Ext4.define('LABKEY.VaccineDesign.BaseDataView', { // data rows tplArr.push(''); - tplArr.push('
    '); + tplArr.push(''); if (showEdit) tplArr.push(''); Ext4.each(columns, function(column) @@ -531,7 +531,7 @@ Ext4.define('LABKEY.VaccineDesign.BaseDataView', { var index = 0; Ext4.each(this.getStore().getRange(), function(record) { - var targetCellEls = Ext4.DomQuery.select('tr.row:nth(' + (index+1) + ') td.cell-value', view.getEl().dom); + var targetCellEls = Ext4.DomQuery.select('tr.data-row:nth(' + (index+1) + ') td.cell-value', view.getEl().dom); Ext4.each(targetCellEls, function(targetCell) { this.createNewCellEditField(targetCell, record, index); @@ -563,7 +563,7 @@ Ext4.define('LABKEY.VaccineDesign.BaseDataView', { // on refresh, call to give focus to the first column of the new row this.on('renderviewcomplete', function(){ - this.giveCellInputFocus('table.outer tr.row:last td.cell-value:first input'); + this.giveCellInputFocus('table.outer tr.data-row:last td.cell-value:first input'); }, this, {single: true}); this.refresh(); diff --git a/study/webapp/study/vaccineDesign/TreatmentSchedule.js b/study/webapp/study/vaccineDesign/TreatmentSchedule.js index d54ebbdf..a8398aff 100644 --- a/study/webapp/study/vaccineDesign/TreatmentSchedule.js +++ b/study/webapp/study/vaccineDesign/TreatmentSchedule.js @@ -222,7 +222,7 @@ Ext4.define('LABKEY.VaccineDesign.TreatmentsGrid', { var record = this.getStore().getAt(cmp.storeIndex), outerDataIndex = cmp.outerDataIndex, subgridIndex = Number(cmp.subgridIndex), - selector = 'tr.row:nth(' + (this.getStore().indexOf(record)+1) + ') table.subgrid-' + outerDataIndex + selector = 'tr.data-row:nth(' + (this.getStore().indexOf(record)+1) + ') table.subgrid-' + outerDataIndex + ' tr.subrow:nth(' + (subgridIndex+1) + ') td[data-index=DoseAndRoute] input'; var inputField = this.getInputFieldFromSelector(selector); @@ -355,7 +355,7 @@ Ext4.define('LABKEY.VaccineDesign.TreatmentsGrid', { var updatedTreatmentLabel = this.getLabelFromProductIds(postProductIds); // need to update the input field value, which will intern update the record and fire teh celledited event - var inputField = this.getInputFieldFromSelector('tr.row:nth(' + (this.getStore().indexOf(record)+1) + ') td.cell-value input'); + var inputField = this.getInputFieldFromSelector('tr.data-row:nth(' + (this.getStore().indexOf(record)+1) + ') td.cell-value input'); if (inputField != null) { inputField.setValue(updatedTreatmentLabel); diff --git a/study/webapp/study/vaccineDesign/VaccineDesign.css b/study/webapp/study/vaccineDesign/VaccineDesign.css index c98c5476..1eb073ce 100644 --- a/study/webapp/study/vaccineDesign/VaccineDesign.css +++ b/study/webapp/study/vaccineDesign/VaccineDesign.css @@ -42,7 +42,7 @@ border-bottom-color: #C0C0C0; } -.study-vaccine-design table.outer tr.row td { +.study-vaccine-design table.outer tr.data-row td { background-color: #FFFFFF; } .study-vaccine-design table.outer tr.alternate-row td { From 91f186d69af845b42e3bdb1b7bce907ef2fbbdd5 Mon Sep 17 00:00:00 2001 From: Ed Younskevicius Date: Thu, 22 Jun 2017 20:17:12 +0000 Subject: [PATCH 143/218] 17.2 Update all copyrights & licenses -- .groovy files now included --- study/src/org/labkey/study/model/StudyTreatmentSchedule.java | 2 +- study/src/org/labkey/study/model/TreatmentManager.java | 2 +- .../study/query/studydesign/StudyDesignLookupBaseTable.java | 2 +- study/webapp/study/vaccineDesign/BaseDataView.js | 2 +- study/webapp/study/vaccineDesign/Models.js | 2 +- study/webapp/study/vaccineDesign/TreatmentDialog.js | 2 +- study/webapp/study/vaccineDesign/TreatmentSchedule.js | 2 +- study/webapp/study/vaccineDesign/TreatmentScheduleBase.js | 2 +- study/webapp/study/vaccineDesign/Utils.js | 2 +- 9 files changed, 9 insertions(+), 9 deletions(-) diff --git a/study/src/org/labkey/study/model/StudyTreatmentSchedule.java b/study/src/org/labkey/study/model/StudyTreatmentSchedule.java index b6701bd7..307e801f 100644 --- a/study/src/org/labkey/study/model/StudyTreatmentSchedule.java +++ b/study/src/org/labkey/study/model/StudyTreatmentSchedule.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2016 LabKey Corporation + * Copyright (c) 2014-2017 LabKey Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/study/src/org/labkey/study/model/TreatmentManager.java b/study/src/org/labkey/study/model/TreatmentManager.java index 6c295edd..1924fe6f 100644 --- a/study/src/org/labkey/study/model/TreatmentManager.java +++ b/study/src/org/labkey/study/model/TreatmentManager.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2016 LabKey Corporation + * Copyright (c) 2014-2017 LabKey Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/study/src/org/labkey/study/query/studydesign/StudyDesignLookupBaseTable.java b/study/src/org/labkey/study/query/studydesign/StudyDesignLookupBaseTable.java index 175ae87e..663ece0c 100644 --- a/study/src/org/labkey/study/query/studydesign/StudyDesignLookupBaseTable.java +++ b/study/src/org/labkey/study/query/studydesign/StudyDesignLookupBaseTable.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013-2016 LabKey Corporation + * Copyright (c) 2013-2017 LabKey Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/study/webapp/study/vaccineDesign/BaseDataView.js b/study/webapp/study/vaccineDesign/BaseDataView.js index a0b4b4e1..5cb22c93 100644 --- a/study/webapp/study/vaccineDesign/BaseDataView.js +++ b/study/webapp/study/vaccineDesign/BaseDataView.js @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016 LabKey Corporation + * Copyright (c) 2016-2017 LabKey Corporation * * Licensed under the Apache License, Version 2.0: http://www.apache.org/licenses/LICENSE-2.0 */ diff --git a/study/webapp/study/vaccineDesign/Models.js b/study/webapp/study/vaccineDesign/Models.js index 6264b93e..03be232e 100644 --- a/study/webapp/study/vaccineDesign/Models.js +++ b/study/webapp/study/vaccineDesign/Models.js @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016 LabKey Corporation + * Copyright (c) 2016-2017 LabKey Corporation * * Licensed under the Apache License, Version 2.0: http://www.apache.org/licenses/LICENSE-2.0 */ diff --git a/study/webapp/study/vaccineDesign/TreatmentDialog.js b/study/webapp/study/vaccineDesign/TreatmentDialog.js index 4c3038ed..0a866f10 100644 --- a/study/webapp/study/vaccineDesign/TreatmentDialog.js +++ b/study/webapp/study/vaccineDesign/TreatmentDialog.js @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016 LabKey Corporation + * Copyright (c) 2016-2017 LabKey Corporation * * Licensed under the Apache License, Version 2.0: http://www.apache.org/licenses/LICENSE-2.0 */ diff --git a/study/webapp/study/vaccineDesign/TreatmentSchedule.js b/study/webapp/study/vaccineDesign/TreatmentSchedule.js index a8398aff..472edfb2 100644 --- a/study/webapp/study/vaccineDesign/TreatmentSchedule.js +++ b/study/webapp/study/vaccineDesign/TreatmentSchedule.js @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016 LabKey Corporation + * Copyright (c) 2016-2017 LabKey Corporation * * Licensed under the Apache License, Version 2.0: http://www.apache.org/licenses/LICENSE-2.0 */ diff --git a/study/webapp/study/vaccineDesign/TreatmentScheduleBase.js b/study/webapp/study/vaccineDesign/TreatmentScheduleBase.js index 99426cd3..bfd2efbe 100644 --- a/study/webapp/study/vaccineDesign/TreatmentScheduleBase.js +++ b/study/webapp/study/vaccineDesign/TreatmentScheduleBase.js @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016 LabKey Corporation + * Copyright (c) 2016-2017 LabKey Corporation * * Licensed under the Apache License, Version 2.0: http://www.apache.org/licenses/LICENSE-2.0 */ diff --git a/study/webapp/study/vaccineDesign/Utils.js b/study/webapp/study/vaccineDesign/Utils.js index 35bb14d6..588b35f1 100644 --- a/study/webapp/study/vaccineDesign/Utils.js +++ b/study/webapp/study/vaccineDesign/Utils.js @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016 LabKey Corporation + * Copyright (c) 2016-2017 LabKey Corporation * * Licensed under the Apache License, Version 2.0: http://www.apache.org/licenses/LICENSE-2.0 */ From 70cee2cef8af80c6ff67fb10f09a14c935cfbd97 Mon Sep 17 00:00:00 2001 From: Karl Lum Date: Wed, 12 Jul 2017 17:33:32 +0000 Subject: [PATCH 144/218] spec id : 30681 Add the dataset field to the assay schedule webpart --- .../study/model/AssaySpecimenConfigImpl.java | 14 ++++++++++++++ study/webapp/study/vaccineDesign/AssaySchedule.js | 8 +++++++- study/webapp/study/vaccineDesign/Models.js | 1 + study/webapp/study/vaccineDesign/Utils.js | 2 ++ 4 files changed, 24 insertions(+), 1 deletion(-) diff --git a/study/src/org/labkey/study/model/AssaySpecimenConfigImpl.java b/study/src/org/labkey/study/model/AssaySpecimenConfigImpl.java index 71965ac2..8c517682 100644 --- a/study/src/org/labkey/study/model/AssaySpecimenConfigImpl.java +++ b/study/src/org/labkey/study/model/AssaySpecimenConfigImpl.java @@ -39,6 +39,7 @@ public class AssaySpecimenConfigImpl extends AbstractStudyEntity serialize() Map props = new HashMap<>(); props.put("RowId", getRowId()); props.put("AssayName", getAssayName()); + props.put("DataSet", getDataset()); props.put("Description", getDescription()); props.put("LocationId", getLocationId()); props.put("Source", getSource()); @@ -228,6 +240,8 @@ public static AssaySpecimenConfigImpl fromJSON(@NotNull JSONObject o, Container if (o.containsKey("RowId")) assay.setRowId(o.getInt("RowId")); + if (o.containsKey("DataSet") && o.get("DataSet") instanceof Integer && o.getInt("DataSet") > 0) + assay.setDataset(o.getInt("DataSet")); if (o.containsKey("Source") && !StringUtils.isEmpty(o.getString("Source"))) assay.setSource(o.getString("Source")); if (o.containsKey("LocationId") && o.get("LocationId") instanceof Integer && o.getInt("LocationId") > 0) diff --git a/study/webapp/study/vaccineDesign/AssaySchedule.js b/study/webapp/study/vaccineDesign/AssaySchedule.js index d669586e..f51689ef 100644 --- a/study/webapp/study/vaccineDesign/AssaySchedule.js +++ b/study/webapp/study/vaccineDesign/AssaySchedule.js @@ -417,6 +417,12 @@ Ext4.define('LABKEY.VaccineDesign.AssaysGrid', { required: true, editorType: 'LABKEY.ext4.ComboBox', editorConfig: assayNameEditorConfig + },{ + label: 'Dataset', + width: 200, + dataIndex: 'DataSet', + editorType: 'LABKEY.ext4.ComboBox', + editorConfig: LABKEY.VaccineDesign.Utils.getStudyDesignComboConfig('DataSet', 185, 'DataSets', undefined, 'Label', 'DataSetId') },{ label: 'Description', width: 200, @@ -554,7 +560,7 @@ Ext4.define('LABKEY.VaccineDesign.AssaysGrid', { var matchingIndex = LABKEY.VaccineDesign.Utils.getMatchingRowIndexFromArray(value, column.dataIndexArrFilterProp, column.dataIndexArrFilterValue); return matchingIndex > -1; } - else if ((dataIndex == 'SampleQuantity' || dataIndex == 'LocationId') && value == 0) + else if ((dataIndex == 'SampleQuantity' || dataIndex == 'LocationId') || dataIndex == 'DataSet' && value == 0) { return null; } diff --git a/study/webapp/study/vaccineDesign/Models.js b/study/webapp/study/vaccineDesign/Models.js index 03be232e..b1a645b2 100644 --- a/study/webapp/study/vaccineDesign/Models.js +++ b/study/webapp/study/vaccineDesign/Models.js @@ -48,6 +48,7 @@ Ext4.define('LABKEY.VaccineDesign.Assay', { fields : [ {name : 'RowId', defaultValue: undefined}, {name : 'AssayName', type : 'string'}, + {name : 'DataSet', type : 'int'}, {name : 'Description', type : 'string'}, {name : 'Lab', type : 'string'}, {name : 'LocationId', type : 'int'}, diff --git a/study/webapp/study/vaccineDesign/Utils.js b/study/webapp/study/vaccineDesign/Utils.js index 588b35f1..135f38cf 100644 --- a/study/webapp/study/vaccineDesign/Utils.js +++ b/study/webapp/study/vaccineDesign/Utils.js @@ -95,6 +95,8 @@ Ext4.define('LABKEY.VaccineDesign.Utils', { columns += ',ProductId'; else if (queryName == 'Product') columns += ',Role'; + else if (queryName == 'DataSets') + columns += ',DatasetId'; return Ext4.create('LABKEY.ext4.Store', { storeId: key, From 0b7e3de4b3fa37be87da06cb124ffd12dd4521a0 Mon Sep 17 00:00:00 2001 From: Karl Lum Date: Mon, 24 Jul 2017 16:23:48 +0000 Subject: [PATCH 145/218] Spec ID: 30681 - checkpoint : render assay progress report --- .../org/labkey/study/view/studydesign/manageAssaySchedule.jsp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/study/src/org/labkey/study/view/studydesign/manageAssaySchedule.jsp b/study/src/org/labkey/study/view/studydesign/manageAssaySchedule.jsp index fdb3fdfa..827befb8 100644 --- a/study/src/org/labkey/study/view/studydesign/manageAssaySchedule.jsp +++ b/study/src/org/labkey/study/view/studydesign/manageAssaySchedule.jsp @@ -146,5 +146,5 @@ Enter assay schedule information in the grids below. -
    +

    From 641312b6a75ba20fc9e8cb48563983a84c22af13 Mon Sep 17 00:00:00 2001 From: Karl Lum Date: Mon, 24 Jul 2017 16:46:29 +0000 Subject: [PATCH 146/218] revert 52395 --- .../org/labkey/study/view/studydesign/manageAssaySchedule.jsp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/study/src/org/labkey/study/view/studydesign/manageAssaySchedule.jsp b/study/src/org/labkey/study/view/studydesign/manageAssaySchedule.jsp index 827befb8..fdb3fdfa 100644 --- a/study/src/org/labkey/study/view/studydesign/manageAssaySchedule.jsp +++ b/study/src/org/labkey/study/view/studydesign/manageAssaySchedule.jsp @@ -146,5 +146,5 @@ Enter assay schedule information in the grids below. -
    +

    From c1dd7ce7fafd31489c92da9f93093376b6f01488 Mon Sep 17 00:00:00 2001 From: Karl Lum Date: Fri, 28 Jul 2017 16:55:45 +0000 Subject: [PATCH 147/218] Spec ID: 30681 - progress report rendering, add legend --- study/webapp/study/vaccineDesign/BaseDataView.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/study/webapp/study/vaccineDesign/BaseDataView.js b/study/webapp/study/vaccineDesign/BaseDataView.js index 5cb22c93..ba44e30b 100644 --- a/study/webapp/study/vaccineDesign/BaseDataView.js +++ b/study/webapp/study/vaccineDesign/BaseDataView.js @@ -89,6 +89,7 @@ Ext4.define('LABKEY.VaccineDesign.BaseDataView', { { this.dataView = Ext4.create('Ext.view.View', { tpl: this.getDataViewTpl(), + cls: 'table-responsive', store: this.getStore(), itemSelector: 'tr.data-row', disableSelection: true, @@ -113,7 +114,7 @@ Ext4.define('LABKEY.VaccineDesign.BaseDataView', { tplArr = [], columns = this.getColumnConfigs(); - tplArr.push('
    ', + tdCloseTpl = '' - + '{[this.getLabelFromStore(values["' + column.dataIndex + '"], "' + column.queryName + '")]}' - + '' + var valTpl = ''; + if (!showEdit) + { + valTpl = '{[this.getDisplayValue(values["' + column.dataIndex + '"], ' + + '"' + column.dataIndexArrFilterProp + '", "' + column.dataIndexArrFilterValue + '", ' + + '"' + column.dataIndexArrValue + '", "' + column.lookupStoreId + '")]}'; + } + + tplArr.push(tdTpl + valTpl + tdCloseTpl); + } + else if (column.editorType === 'LABKEY.ext4.Checkbox') + { + var vRowId = column.dataIndexArrFilterValue, + valTpl = ''); + + '"' + column.dataIndexArrValue + '")]}>'; + + tplArr.push(tdTpl + valTpl + tdCloseTpl); } else { - tplArr.push('' - + '{[this.getDisplayValue(values["' + column.dataIndex + '"])]}' - + ''); // only show the subgrid if we are allowing edits of if it has at least one row - tplArr.push(''); + tplArr.push(''); tplArr.push(''); tplArr = tplArr.concat(this.getTableHeaderRowTpl(columns)); @@ -216,7 +240,7 @@ Ext4.define('LABKEY.VaccineDesign.BaseDataView', { // data rows tplArr.push(''); tplArr.push(''); - if (!this.disableEdit) + if (showEdit) { tplArr.push(''; + if (Ext4.isString(column.queryName)) - { - var checkMissingReqTpl = ''; - if (column.required) - checkMissingReqTpl = ' {[this.checkMissingRequired(values, "' + column.dataIndex + '")]}'; - - tplArr.push(''); - } + tplArr.push(tdTpl + (!showEdit ? '{[this.getLabelFromStore(values["' + column.dataIndex + '"], "' + column.queryName + '")]}' : '') + tdCloseTpl); else - { - tplArr.push(''); - } + tplArr.push(tdTpl + (!showEdit ? '{' + column.dataIndex + ':htmlEncode}' : '') + tdCloseTpl); } }, this); tplArr.push(''); @@ -311,16 +327,8 @@ Ext4.define('LABKEY.VaccineDesign.BaseDataView', { { if (!this.disableEdit) { - // handle click on a cell that is editable - if (event.target.getAttribute('class').indexOf('cell-value') > -1 && event.target.hasAttribute('data-index')) - { - if (this.cellEditField != null) - this.clearPreviousCellEditField(); - else - this.createNewCellEditField(event.target, record, index); - } // handle click on trashcan icon to delete row - else if (event.target.getAttribute('class') == this.DELETE_ICON_CLS) + if (event.target.getAttribute('class') == this.DELETE_ICON_CLS) { if (event.target.hasAttribute('outer-index')) { @@ -335,12 +343,6 @@ Ext4.define('LABKEY.VaccineDesign.BaseDataView', { } }, - clearPreviousCellEditField : function() - { - if (this.cellEditField != null) - this.updateStoreValueForCellEdit(); - }, - createNewCellEditField : function(target, record, index) { var dataIndex = target.getAttribute('data-index'), @@ -352,13 +354,8 @@ Ext4.define('LABKEY.VaccineDesign.BaseDataView', { if (editor != null) { - // clear the existing HTML in the td cell and remove the missing-required cls while in edit mode - var targetEl = Ext4.get(target); - targetEl.update(''); - targetEl.removeCls('missing-required'); - // create a new form field to place in the td cell - this.cellEditField = Ext4.create(editor.type, Ext4.apply(editor.config, { + var field = Ext4.create(editor.type, Ext4.apply(editor.config, { renderTo: target, value: this.getCurrentCellValue(column, record, dataIndex, outerDataIndex, subgridIndex), storeIndex: index, @@ -368,12 +365,7 @@ Ext4.define('LABKEY.VaccineDesign.BaseDataView', { })); // add listeners for when to apply the updated value and clear the input field - this.cellEditField.on('blur', this.updateStoreValueForCellEdit, this); - - // give the new field focus after a brief delay, and select text on focus if not a combo - var xtype = this.cellEditField.getXType(); - var isCombo = xtype == 'labkey-combo' || xtype == 'combobox' || xtype == 'combo'; - this.cellEditField.focus(!isCombo, true); + field.on('change', this.updateStoreValueForCellEdit, this, {buffer: 500}); } }, @@ -382,43 +374,50 @@ Ext4.define('LABKEY.VaccineDesign.BaseDataView', { return Ext4.isString(outerDataIndex) ? record.get(outerDataIndex)[subgridIndex][dataIndex] : record.get(dataIndex); }, - updateStoreValueForCellEdit : function() + updateStoreValueForCellEdit : function(field) { - if (this.cellEditField != null) - { - var fieldName = this.cellEditField.getName(), - newValue = this.cellEditField.getValue(), - index = this.cellEditField.storeIndex, - record = this.getStore().getAt(index), - dataFilterValue = this.cellEditField.dataFilterValue, - outerDataIndex = this.cellEditField.outerDataIndex, - subgridIndex = Number(this.cellEditField.subgridIndex); - - if (Ext4.isString(outerDataIndex)) - { - if (!isNaN(subgridIndex) && Ext4.isArray(record.get(outerDataIndex))) - this.updateSubgridRecordValue(record, outerDataIndex, subgridIndex, fieldName, newValue); - } - else - { - var column = this.getColumnConfig(fieldName, dataFilterValue, outerDataIndex); - this.updateStoreRecordValue(record, column, newValue); - } + var fieldName = field.getName(), + newValue = field.getValue(), + index = field.storeIndex, + record = this.getStore().getAt(index), + dataFilterValue = field.dataFilterValue, + outerDataIndex = field.outerDataIndex, + subgridIndex = Number(field.subgridIndex); + + // suspend events on cell update so that we don't re-render the dataview + this.getStore().suspendEvents(); - this.cellEditField = null; - this.refresh(true); + if (Ext4.isString(outerDataIndex)) + { + if (!isNaN(subgridIndex) && Ext4.isArray(record.get(outerDataIndex))) + this.updateSubgridRecordValue(record, outerDataIndex, subgridIndex, fieldName, newValue); } + else + { + var column = this.getColumnConfig(fieldName, dataFilterValue, outerDataIndex); + this.updateStoreRecordValue(record, column, newValue); + } + + // resume store events so that adding and deleting will re-render the dataview + this.getStore().resumeEvents(); }, updateSubgridRecordValue : function(record, outerDataIndex, subgridIndex, fieldName, newValue) { + if (Ext4.isString(newValue)) + newValue.trim(); + record.get(outerDataIndex)[subgridIndex][fieldName] = newValue; + this.fireEvent('celledited', this, fieldName, newValue); }, updateStoreRecordValue : function(record, column, newValue) { + if (Ext4.isString(newValue)) + newValue.trim(); + record.set(column.dataIndex, newValue); - this.fireEvent('celledited', this); + this.fireEvent('celledited', this, column.dataIndex, newValue); }, removeOuterRecord : function(title, record) @@ -435,7 +434,6 @@ Ext4.define('LABKEY.VaccineDesign.BaseDataView', { this.getStore().suspendEvents(); this.getStore().remove(record); this.getStore().resumeEvents(); - this.refresh(true); } }, this); @@ -451,7 +449,10 @@ Ext4.define('LABKEY.VaccineDesign.BaseDataView', { Ext4.Msg.confirm('Confirm Delete: ' + subgridDataIndex, 'Are you sure you want to delete the selected row?', function(btn) { if (btn == 'yes') + { this.removeSubgridRecord(target, record); + this.refresh(true); + } }, this); } }, @@ -462,7 +463,29 @@ Ext4.define('LABKEY.VaccineDesign.BaseDataView', { subgridArr = record.get(subgridDataIndex); subgridArr.splice(target.getAttribute('subgrid-index'), 1); - this.refresh(true); + }, + + onDataViewRefresh : function(view) + { + this.attachCellEditors(view); + this.attachAddRowListeners(view); + this.fireRenderCompleteTask.delay(250); + }, + + attachCellEditors : function(view) + { + // attach cell editors for each of the store records (i.e. tr.row elements in the table) + var index = 0; + Ext4.each(this.getStore().getRange(), function(record) + { + var targetCellEls = Ext4.DomQuery.select('tr.row:nth(' + (index+1) + ') td.cell-value', view.getEl().dom); + Ext4.each(targetCellEls, function(targetCell) + { + this.createNewCellEditField(targetCell, record, index); + }, this); + + index++; + }, this); }, attachAddRowListeners : function(view) @@ -486,13 +509,11 @@ Ext4.define('LABKEY.VaccineDesign.BaseDataView', { this.getStore().resumeEvents(); // on refresh, call to give focus to the first column of the new row - this.getDataView().on('refresh', function(){ - var index = this.getStore().getCount() - 1, - selector = 'table.outer tr.row:last td.cell-value:first'; - this.giveLastRowFocus(index, selector); + this.on('renderviewcomplete', function(){ + this.giveCellInputFocus('table.outer tr.row:last td.cell-value:first input'); }, this, {single: true}); - this.refresh(true); + this.refresh(); }, addNewSubgridRow : function(event, target) @@ -509,23 +530,20 @@ Ext4.define('LABKEY.VaccineDesign.BaseDataView', { record.set(dataIndex, dataIndexArr.concat([{}])); // on refresh, call to give focus to the first column of the new row - this.getDataView().on('refresh', function(){ - var index = rowIndex, - selector = 'table.subgrid-' + dataIndex + ':nth(' + (rowIndex+1) + ') tr.subrow:last td.cell-value:first'; - this.giveLastRowFocus(index, selector); + this.on('renderviewcomplete', function(){ + var selector = 'table.subgrid-' + dataIndex + ':nth(' + (rowIndex+1) + ') tr.subrow:last td.cell-value:first input'; + this.giveCellInputFocus(selector); }, this, {single: true}); - this.refresh(true); + this.refresh(); } }, - giveLastRowFocus : function(index, selector) + giveCellInputFocus : function(selector, queryFullPage) { - var lastRowFirstCell = Ext4.DomQuery.select(selector, this.getDataView().getEl().dom), - record = this.getStore().getAt(index); - - if (Ext4.isArray(lastRowFirstCell) && lastRowFirstCell.length == 1) - this.createNewCellEditField(Ext4.get(lastRowFirstCell[0]), record, index); + var cellInputField = Ext4.DomQuery.selectNode(selector, queryFullPage ? undefined : this.getDataView().getEl().dom); + if (cellInputField) + cellInputField.focus(); }, refresh : function(hasChanges) diff --git a/study/webapp/study/vaccineDesign/StudyProducts.js b/study/webapp/study/vaccineDesign/StudyProducts.js index ef44b62b..e627273e 100644 --- a/study/webapp/study/vaccineDesign/StudyProducts.js +++ b/study/webapp/study/vaccineDesign/StudyProducts.js @@ -39,10 +39,8 @@ Ext4.define('LABKEY.VaccineDesign.StudyProductsPanel', { disableEdit: this.disableEdit }); - this.immunogenGrid.on('dirtychange', function() { - this.setDirty(true); - this.getSaveButton().enable(); - }, this); + this.immunogenGrid.on('dirtychange', this.enableSaveButton, this); + this.immunogenGrid.on('celledited', this.enableSaveButton, this); } return this.immunogenGrid; @@ -57,10 +55,8 @@ Ext4.define('LABKEY.VaccineDesign.StudyProductsPanel', { disableEdit: this.disableEdit }); - this.adjuvantGrid.on('dirtychange', function() { - this.setDirty(true); - this.getSaveButton().enable(); - }, this); + this.adjuvantGrid.on('dirtychange', this.enableSaveButton, this); + this.adjuvantGrid.on('celledited', this.enableSaveButton, this); } return this.adjuvantGrid; @@ -99,6 +95,12 @@ Ext4.define('LABKEY.VaccineDesign.StudyProductsPanel', { return this.saveButton; }, + enableSaveButton : function() + { + this.setDirty(true); + this.getSaveButton().enable(); + }, + getCancelButton : function() { if (!this.cancelButton) @@ -162,7 +164,10 @@ Ext4.define('LABKEY.VaccineDesign.StudyProductsPanel', { failure: function(response) { var resp = Ext4.decode(response.responseText); - this.onFailure(Ext4.Array.pluck(resp.errors, 'message').join('
    ')); + if (resp.errors) + this.onFailure(Ext4.Array.pluck(resp.errors, 'message').join('
    ')); + else + this.onFailure(resp.exception); } }); }, @@ -271,17 +276,17 @@ Ext4.define('LABKEY.VaccineDesign.ImmunogensGrid', { dataIndex: 'Label', required: true, editorType: 'Ext.form.field.Text', - editorConfig: LABKEY.VaccineDesign.Utils.getStudyDesignTextConfig('Label', 190) + editorConfig: LABKEY.VaccineDesign.Utils.getStudyDesignTextConfig('Label', 185) }, { label: 'Type', width: 200, dataIndex: 'Type', queryName: 'StudyDesignImmunogenTypes', editorType: 'LABKEY.ext4.ComboBox', - editorConfig: LABKEY.VaccineDesign.Utils.getStudyDesignComboConfig('Type', 190, 'StudyDesignImmunogenTypes') + editorConfig: LABKEY.VaccineDesign.Utils.getStudyDesignComboConfig('Type', 185, 'StudyDesignImmunogenTypes') },{ label: 'HIV Antigens', - width: 800, + width: 750, dataIndex: 'Antigens', subgridConfig: { columns: [{ @@ -290,26 +295,26 @@ Ext4.define('LABKEY.VaccineDesign.ImmunogensGrid', { dataIndex: 'Gene', queryName: 'StudyDesignGenes', editorType: 'LABKEY.ext4.ComboBox', - editorConfig: LABKEY.VaccineDesign.Utils.getStudyDesignComboConfig('Gene', 155, 'StudyDesignGenes') + editorConfig: LABKEY.VaccineDesign.Utils.getStudyDesignComboConfig('Gene', 150, 'StudyDesignGenes') },{ label: 'Subtype', width: 165, dataIndex: 'SubType', queryName: 'StudyDesignSubTypes', editorType: 'LABKEY.ext4.ComboBox', - editorConfig: LABKEY.VaccineDesign.Utils.getStudyDesignComboConfig('SubType', 155, 'StudyDesignSubTypes') + editorConfig: LABKEY.VaccineDesign.Utils.getStudyDesignComboConfig('SubType', 150, 'StudyDesignSubTypes') },{ label: 'GenBank Id', - width: 215, + width: 200, dataIndex: 'GenBankId', editorType: 'Ext.form.field.Text', - editorConfig: LABKEY.VaccineDesign.Utils.getStudyDesignTextConfig('GenBankId', 205) + editorConfig: LABKEY.VaccineDesign.Utils.getStudyDesignTextConfig('GenBankId', 185) },{ label: 'Sequence', - width: 235, + width: 200, dataIndex: 'Sequence', editorType: 'Ext.form.field.Text', - editorConfig: LABKEY.VaccineDesign.Utils.getStudyDesignTextConfig('Sequence', 225) + editorConfig: LABKEY.VaccineDesign.Utils.getStudyDesignTextConfig('Sequence', 185) }] } }]; @@ -318,7 +323,7 @@ Ext4.define('LABKEY.VaccineDesign.ImmunogensGrid', { { this.columnConfigs.push({ label: 'Doses and Routes', - width: 300, + width: 315, dataIndex: 'DoseAndRoute', subgridConfig: { columns: [{ @@ -326,14 +331,14 @@ Ext4.define('LABKEY.VaccineDesign.ImmunogensGrid', { width: 140, dataIndex: 'Dose', editorType: 'Ext.form.field.Text', - editorConfig: LABKEY.VaccineDesign.Utils.getStudyDesignTextConfig('Dose', 130) + editorConfig: LABKEY.VaccineDesign.Utils.getStudyDesignTextConfig('Dose', 125) },{ label: 'Route', width: 140, dataIndex: 'Route', queryName: 'StudyDesignRoutes', editorType: 'LABKEY.ext4.ComboBox', - editorConfig: LABKEY.VaccineDesign.Utils.getStudyDesignComboConfig('Route', 130, 'StudyDesignRoutes') + editorConfig: LABKEY.VaccineDesign.Utils.getStudyDesignComboConfig('Route', 125, 'StudyDesignRoutes') }] } }); @@ -347,7 +352,7 @@ Ext4.define('LABKEY.VaccineDesign.ImmunogensGrid', { Ext4.define('LABKEY.VaccineDesign.AdjuvantsGrid', { extend : 'LABKEY.VaccineDesign.StudyProductsGrid', - width : 700, + width : 530, mainTitle : 'Adjuvants', @@ -362,18 +367,18 @@ Ext4.define('LABKEY.VaccineDesign.AdjuvantsGrid', { { this.columnConfigs = [{ label: 'Label', - width: 400, + width: 200, dataIndex: 'Label', required: true, editorType: 'Ext.form.field.Text', - editorConfig: LABKEY.VaccineDesign.Utils.getStudyDesignTextConfig('Label', 363) + editorConfig: LABKEY.VaccineDesign.Utils.getStudyDesignTextConfig('Label', 185) }]; if (this.showDoseRoute) { this.columnConfigs.push({ label: 'Doses and Routes', - width: 300, + width: 330, dataIndex: 'DoseAndRoute', subgridConfig: { columns: [{ @@ -381,14 +386,14 @@ Ext4.define('LABKEY.VaccineDesign.AdjuvantsGrid', { width: 140, dataIndex: 'Dose', editorType: 'Ext.form.field.Text', - editorConfig: LABKEY.VaccineDesign.Utils.getStudyDesignTextConfig('Dose', 130) + editorConfig: LABKEY.VaccineDesign.Utils.getStudyDesignTextConfig('Dose', 125) }, { label: 'Route', width: 140, dataIndex: 'Route', queryName: 'StudyDesignRoutes', editorType: 'LABKEY.ext4.ComboBox', - editorConfig: LABKEY.VaccineDesign.Utils.getStudyDesignComboConfig('Route', 130, 'StudyDesignRoutes') + editorConfig: LABKEY.VaccineDesign.Utils.getStudyDesignComboConfig('Route', 125, 'StudyDesignRoutes') }] } }); diff --git a/study/webapp/study/vaccineDesign/TreatmentSchedule.js b/study/webapp/study/vaccineDesign/TreatmentSchedule.js index 6cee8c56..4a22bca7 100644 --- a/study/webapp/study/vaccineDesign/TreatmentSchedule.js +++ b/study/webapp/study/vaccineDesign/TreatmentSchedule.js @@ -5,7 +5,7 @@ Ext4.define('LABKEY.VaccineDesign.TreatmentSchedulePanel', { bodyStyle : 'background-color: transparent;', - minWidth: 1400, + width: 1300, disableEdit : true, @@ -30,10 +30,8 @@ Ext4.define('LABKEY.VaccineDesign.TreatmentSchedulePanel', { disableEdit: this.disableEdit }); - this.treatmentsGrid.on('dirtychange', function() { - this.setDirty(true); - this.getSaveButton().enable(); - }, this); + this.treatmentsGrid.on('dirtychange', this.enableSaveButton, this); + this.treatmentsGrid.on('celledited', this.enableSaveButton, this); // Note: since we need the data from the treatment grid, don't add this.getTreatmentScheduleGrid() until the treatment grid store has loaded this.treatmentsGrid.on('loadcomplete', this.onTreatmentGridLoadComplete, this, {single: true}); @@ -48,8 +46,9 @@ Ext4.define('LABKEY.VaccineDesign.TreatmentSchedulePanel', { this.add(this.getButtonBar()); // since a treatment label change needs to be reflected in the treatment schedule grid, force a refresh there - this.getTreatmentsGrid().on('celledited', function(){ - this.getTreatmentScheduleGrid().refresh(); + this.getTreatmentsGrid().on('celledited', function(view, fieldName, value){ + if (fieldName == 'Label') + this.getTreatmentScheduleGrid().refresh(); }, this); // removing a treatment row needs to also remove any visit mappings for that treatment @@ -69,10 +68,8 @@ Ext4.define('LABKEY.VaccineDesign.TreatmentSchedulePanel', { visitNoun: this.visitNoun }); - this.treatmentScheduleGrid.on('dirtychange', function() { - this.setDirty(true); - this.getSaveButton().enable(); - }, this); + this.treatmentScheduleGrid.on('dirtychange', this.enableSaveButton, this); + this.treatmentScheduleGrid.on('celledited', this.enableSaveButton, this); } return this.treatmentScheduleGrid; @@ -111,6 +108,12 @@ Ext4.define('LABKEY.VaccineDesign.TreatmentSchedulePanel', { return this.saveButton; }, + enableSaveButton : function() + { + this.setDirty(true); + this.getSaveButton().enable(); + }, + getCancelButton : function() { if (!this.cancelButton) @@ -215,7 +218,10 @@ Ext4.define('LABKEY.VaccineDesign.TreatmentSchedulePanel', { failure: function(response) { var resp = Ext4.decode(response.responseText); - this.onFailure(Ext4.Array.pluck(resp.errors, 'message').join('
    ')); + if (resp.errors) + this.onFailure(Ext4.Array.pluck(resp.errors, 'message').join('
    ')); + else + this.onFailure(resp.exception); } }); }, @@ -261,7 +267,7 @@ Ext4.define('LABKEY.VaccineDesign.TreatmentsGrid', { mainTitle : 'Treatments', - width: 1120, + width: 1300, studyDesignQueryNames : ['StudyDesignRoutes', 'Product', 'DoseAndRoute'], @@ -300,16 +306,16 @@ Ext4.define('LABKEY.VaccineDesign.TreatmentsGrid', { dataIndex: 'Label', required: true, editorType: 'Ext.form.field.Text', - editorConfig: LABKEY.VaccineDesign.Utils.getStudyDesignTextConfig('Label', 190) + editorConfig: LABKEY.VaccineDesign.Utils.getStudyDesignTextConfig('Label', 185) },{ label: 'Description', width: 200, dataIndex: 'Description', editorType: 'Ext.form.field.TextArea', - editorConfig: LABKEY.VaccineDesign.Utils.getStudyDesignTextConfig('Description', 190, '95%') + editorConfig: LABKEY.VaccineDesign.Utils.getStudyDesignTextConfig('Description', 185, '95%') }, { label: 'Immunogens', - width: 360, + width: 430, dataIndex: 'Immunogen', subgridConfig: { columns: [{ @@ -322,16 +328,16 @@ Ext4.define('LABKEY.VaccineDesign.TreatmentsGrid', { editorConfig : this.getProductEditor('Immunogen') },{ label: 'Dose and Route', - width: 140, + width: 200, dataIndex: 'DoseAndRoute', queryName: 'DoseAndRoute', editorType: 'LABKEY.ext4.ComboBox', - editorConfig: this.getDoseAndRouteEditor() + editorConfig: this.getDoseAndRouteEditorConfig() }] } }, { label: 'Adjuvants', - width: 360, + width: 430, dataIndex: 'Adjuvant', subgridConfig: { columns: [{ @@ -344,11 +350,11 @@ Ext4.define('LABKEY.VaccineDesign.TreatmentsGrid', { editorConfig: this.getProductEditor('Adjuvant') },{ label: 'Dose and Route', - width: 140, + width: 200, dataIndex: 'DoseAndRoute', queryName: 'DoseAndRoute', editorType: 'LABKEY.ext4.ComboBox', - editorConfig: this.getDoseAndRouteEditor() + editorConfig: this.getDoseAndRouteEditorConfig() }] } }]; @@ -359,50 +365,70 @@ Ext4.define('LABKEY.VaccineDesign.TreatmentsGrid', { getProductEditor : function(roleName){ - var cfg = LABKEY.VaccineDesign.Utils.getStudyDesignComboConfig('ProductId', 190, 'Product', - LABKEY.Filter.create('Role', roleName), 'Label', 'RowId'); + var filter = LABKEY.Filter.create('Role', roleName), + cfg = LABKEY.VaccineDesign.Utils.getStudyDesignComboConfig('ProductId', 185, 'Product', filter, 'Label', 'RowId'); cfg.listeners = { scope: this, - change : function(cmp, value) { + change : function(cmp, productId) { // clear out (if any) value for the dose and route field - if (this.cellEditField){ - - var index = this.cellEditField.storeIndex, - record = this.getStore().getAt(index), - outerDataIndex = this.cellEditField.outerDataIndex, - subgridIndex = Number(this.cellEditField.subgridIndex); - - this.updateSubgridRecordValue(record, outerDataIndex, subgridIndex, 'DoseAndRoute', ''); + var record = this.getStore().getAt(cmp.storeIndex), + outerDataIndex = cmp.outerDataIndex, + subgridIndex = Number(cmp.subgridIndex), + selector = 'tr.row:nth(' + (this.getStore().indexOf(record)+1) + ') table.subgrid-' + outerDataIndex + + ' tr.subrow:nth(' + (subgridIndex+1) + ') td[data-index=DoseAndRoute] input'; + + var inputField = this.getInputFieldFromSelector(selector); + if (inputField != null) + { + inputField.setValue(''); + inputField.bindStore(this.getNewDoseAndRouteComboStore(productId)); } } }; return cfg; }, - getDoseAndRouteEditor : function(){ - - var cfg = LABKEY.VaccineDesign.Utils.getStudyDesignComboConfig('DoseAndRoute', 130, 'DoseAndRoute', undefined, 'Label', 'Label'); - cfg.listeners = { - scope: this, - render : function(cmp) { - var store = Ext4.getStore('DoseAndRoute'); - if (store) { - var index = cmp.storeIndex, - record = this.getStore().getAt(index), + getDoseAndRouteEditorConfig : function() + { + return { + hideFieldLabel: true, + name: 'DoseAndRoute', + width: 185, + forceSelection : false, // allow usage of inactive types + editable : false, + queryMode : 'local', + displayField : 'Label', + valueField : 'Label', + store : null, // the store will be created and bound to this combo after render + listeners : { + scope: this, + render : function(cmp) { + var record = this.getStore().getAt(cmp.storeIndex), outerDataIndex = cmp.outerDataIndex, - subgridIndex = Number(cmp.subgridIndex); + subgridIndex = Number(cmp.subgridIndex), + productId = record.get(outerDataIndex)[subgridIndex]['ProductId']; - // get the product id to filter on - var productId = record.get(outerDataIndex)[subgridIndex]['ProductId']; - if (productId){ - store.filterArray = [LABKEY.Filter.create('ProductId', productId)]; - store.load(); - } + cmp.bindStore(this.getNewDoseAndRouteComboStore(productId)); } } }; - return cfg; + }, + + getNewDoseAndRouteComboStore : function(productId) + { + // need to create a new store each time since we need to add a [none] option and include any new treatment records + var data = []; + Ext4.each(Ext4.getStore('DoseAndRoute').getRange(), function(record) + { + if (record.get('ProductId') == null || record.get('ProductId') == productId) + data.push(Ext4.clone(record.data)); + }, this); + + return Ext4.create('Ext.data.Store', { + fields: ['RowId', 'Label'], + data: data + }); }, //Override @@ -438,6 +464,7 @@ Ext4.define('LABKEY.VaccineDesign.TreatmentsGrid', { var preProductIds = Ext4.Array.pluck(record.get('Immunogen'), 'ProductId').concat(Ext4.Array.pluck(record.get('Adjuvant'), 'ProductId')); this.callParent([target, record]); this.populateTreatmentLabel(record, preProductIds); + this.refresh(true); }, populateTreatmentLabel : function(record, preProductIds) @@ -445,12 +472,28 @@ Ext4.define('LABKEY.VaccineDesign.TreatmentsGrid', { var currentLabel = record.get('Label'); if (currentLabel == '' || currentLabel == this.getLabelFromProductIds(preProductIds)) { - var postProductIds = Ext4.Array.pluck(record.get('Immunogen'), 'ProductId').concat(Ext4.Array.pluck(record.get('Adjuvant'), 'ProductId')); - record.set('Label', this.getLabelFromProductIds(postProductIds)); - this.fireEvent('celledited'); + var postProductIds = Ext4.Array.pluck(record.get('Immunogen'), 'ProductId').concat(Ext4.Array.pluck(record.get('Adjuvant'), 'ProductId')), + updatedTreatmentLabel = this.getLabelFromProductIds(postProductIds); + + // need to update the input field value, which will intern update the record and fire teh celledited event + var inputField = this.getInputFieldFromSelector('tr.row:nth(' + (this.getStore().indexOf(record)+1) + ') td.cell-value input'); + if (inputField != null) + { + inputField.setValue(updatedTreatmentLabel); + record.set('Label', updatedTreatmentLabel); + } } }, + getInputFieldFromSelector : function(selector) + { + var inputFieldEl = Ext4.DomQuery.selectNode(selector, this.getEl().dom); + if (inputFieldEl != null) + return Ext4.ComponentManager.get(inputFieldEl.id.replace('-inputEl', '')); + + return null; + }, + getLabelFromProductIds : function(productIdsArr) { var labelArr = []; @@ -472,9 +515,7 @@ Ext4.define('LABKEY.VaccineDesign.TreatmentScheduleGrid', { mainTitle : 'Treatment Schedule', - width: 330, - - //studyDesignQueryNames : ['Visit'], + width: 350, subjectNoun : 'Subject', @@ -567,7 +608,7 @@ Ext4.define('LABKEY.VaccineDesign.TreatmentScheduleGrid', { return { hideFieldLabel: true, name: 'VisitMap', - width: 140, + width: 135, forceSelection : false, // allow usage of inactive types editable : false, queryMode : 'local', @@ -580,7 +621,7 @@ Ext4.define('LABKEY.VaccineDesign.TreatmentScheduleGrid', { getNewTreatmentComboStore : function() { // need to create a new store each time since we need to add a [none] option and include any new treatment records - var data = [{RowId: null, Label: '[none'}]; + var data = [{RowId: null, Label: '[none]'}]; Ext4.each(this.getTreatmentsStore().getRange(), function(record) { data.push(Ext4.clone(record.data)); @@ -616,19 +657,19 @@ Ext4.define('LABKEY.VaccineDesign.TreatmentScheduleGrid', { dataIndex: 'Label', required: true, editorType: 'Ext.form.field.Text', - editorConfig: LABKEY.VaccineDesign.Utils.getStudyDesignTextConfig('Label', 190) + editorConfig: LABKEY.VaccineDesign.Utils.getStudyDesignTextConfig('Label', 185) },{ label: this.subjectNoun + ' Count', width: 130, dataIndex: 'SubjectCount', editorType: 'Ext.form.field.Number', - editorConfig: LABKEY.VaccineDesign.Utils.getStudyDesignNumberConfig('SubjectCount', 120) + editorConfig: LABKEY.VaccineDesign.Utils.getStudyDesignNumberConfig('SubjectCount', 115) }]; var visitConfigs = this.getVisitColumnConfigs(); // update the width based on the number of visit columns - this.setWidth((visitConfigs.length * 150) + 330); + this.setWidth((visitConfigs.length * 150) + 350); this.columnConfigs = columnConfigs.concat(visitConfigs); } @@ -689,6 +730,8 @@ Ext4.define('LABKEY.VaccineDesign.TreatmentScheduleGrid', { TreatmentId: newValue }); } + + this.fireEvent('celledited', this, 'VisitMap', visitMapArr); } else { diff --git a/study/webapp/study/vaccineDesign/Utils.js b/study/webapp/study/vaccineDesign/Utils.js index f5503c5b..15811fb6 100644 --- a/study/webapp/study/vaccineDesign/Utils.js +++ b/study/webapp/study/vaccineDesign/Utils.js @@ -42,7 +42,8 @@ Ext4.define('LABKEY.VaccineDesign.Utils', { hideFieldLabel: true, name: name, width: width, - height: height + height: height, + selectOnFocus: true } }, @@ -73,16 +74,21 @@ Ext4.define('LABKEY.VaccineDesign.Utils', { { var key = Ext4.isDefined(filter) ? queryName + '|' + filter.getColumnName() + '|' + filter.getValue() : queryName, store = Ext4.getStore(key), - hasStudyDesignPrefix = queryName.indexOf('StudyDesign') == 0; + hasStudyDesignPrefix = queryName.indexOf('StudyDesign') == 0, + columns = 'RowId,Name,Label,DisplayOrder'; if (Ext4.isDefined(store)) return store; + // special case to query ProductId column for DoseAndRoute table + if (queryName == 'DoseAndRoute') + columns += ',ProductId'; + return Ext4.create('LABKEY.ext4.Store', { storeId: key, schemaName: 'study', queryName: queryName, - columns: 'RowId,Name,Label,DisplayOrder', + columns: columns, filterArray: Ext4.isDefined(filter) ? [filter] : (hasStudyDesignPrefix ? [LABKEY.Filter.create('Inactive', false)] : []), containerFilter: LABKEY.container.type == 'project' || Ext4.isDefined(filter) || !hasStudyDesignPrefix ? 'Current' : 'CurrentPlusProject', sort: '-Container/Path,Label', diff --git a/study/webapp/study/vaccineDesign/VaccineDesign.css b/study/webapp/study/vaccineDesign/VaccineDesign.css index d145782a..387fe1ca 100644 --- a/study/webapp/study/vaccineDesign/VaccineDesign.css +++ b/study/webapp/study/vaccineDesign/VaccineDesign.css @@ -18,10 +18,16 @@ .study-vaccine-design td.cell-value { padding: 5px; border: solid 1px #DDDDDD; - height: 30px; vertical-align: top; } +.study-vaccine-design td.cell-display { + height: 30px; +} +.study-vaccine-design td.cell-value { + height: 33px; +} + .study-vaccine-design table.subgrid td { background-color: #FFFFFF !important; } @@ -40,10 +46,6 @@ background-color: #F4F4F4; } -.study-vaccine-design td.cell-value { - cursor: pointer; -} - .study-vaccine-design td.cell-value.missing-required { background-color: #ffe5e5 !important; } From 09e6fd4899968c75b489745f7c36e26e8026303e Mon Sep 17 00:00:00 2001 From: Cory Nathe Date: Wed, 28 Sep 2016 19:51:11 +0000 Subject: [PATCH 102/218] Spec #27492: update missing-field cls after field value updated for required fields --- study/webapp/study/vaccineDesign/BaseDataView.js | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/study/webapp/study/vaccineDesign/BaseDataView.js b/study/webapp/study/vaccineDesign/BaseDataView.js index b32425d3..edf591e0 100644 --- a/study/webapp/study/vaccineDesign/BaseDataView.js +++ b/study/webapp/study/vaccineDesign/BaseDataView.js @@ -212,7 +212,6 @@ Ext4.define('LABKEY.VaccineDesign.BaseDataView', { checkMissingRequired : function(values, dataIndex) { - // TODO need to update this cls after cell field change if (values[dataIndex] == null || values[dataIndex] == '') return ' missing-required'; @@ -358,6 +357,7 @@ Ext4.define('LABKEY.VaccineDesign.BaseDataView', { var field = Ext4.create(editor.type, Ext4.apply(editor.config, { renderTo: target, value: this.getCurrentCellValue(column, record, dataIndex, outerDataIndex, subgridIndex), + required: column.required, storeIndex: index, dataFilterValue: dataFilterValue, outerDataIndex: outerDataIndex, @@ -398,6 +398,15 @@ Ext4.define('LABKEY.VaccineDesign.BaseDataView', { this.updateStoreRecordValue(record, column, newValue); } + // update the missing-required cls based on the new field value + if (field.required) + { + if (newValue == null || newValue == '') + Ext4.get(field.renderTo).addCls('missing-required'); + else + Ext4.get(field.renderTo).removeCls('missing-required'); + } + // resume store events so that adding and deleting will re-render the dataview this.getStore().resumeEvents(); }, From 9581d2f1693d3af3d23f7cb9895f9aa72f4954cc Mon Sep 17 00:00:00 2001 From: Cory Nathe Date: Fri, 30 Sep 2016 15:45:58 +0000 Subject: [PATCH 103/218] Spec #27492: Assay Schedule webpart and manage page migration to new UI and single save - Change UI to use new grid; Combine assay configurations and assay schedule to a single grid - Change to single save action and validation of required fields - Update the read-only webpart view for the assay schedule webpart --- .../controllers/StudyDesignController.java | 94 ++- .../study/model/AssaySpecimenConfigImpl.java | 74 ++ .../study/model/AssaySpecimenVisitImpl.java | 46 +- .../study/model/StudyAssaySchedule.java | 63 ++ .../labkey/study/model/TreatmentManager.java | 64 +- .../view/studydesign/assayScheduleWebpart.jsp | 122 +-- .../immunizationScheduleWebpart.jsp | 2 +- .../view/studydesign/manageAssaySchedule.jsp | 694 ++---------------- .../view/studydesign/manageTreatments.jsp | 9 +- .../view/studydesign/vaccineDesignWebpart.jsp | 1 - .../study/vaccineDesign/AssaySchedule.js | 552 ++++++++++++++ .../study/vaccineDesign/BaseDataView.js | 39 +- .../vaccineDesign/BaseDataViewAddVisit.js | 92 +++ study/webapp/study/vaccineDesign/Models.js | 30 +- .../study/vaccineDesign/StudyProducts.js | 2 +- .../study/vaccineDesign/TreatmentSchedule.js | 82 +-- study/webapp/study/vaccineDesign/Utils.js | 37 +- .../study/vaccineDesign/VaccineDesign.css | 4 + .../webapp/study/vaccineDesign/VisitWindow.js | 14 +- .../study/vaccineDesign/vaccineDesign.lib.xml | 2 + 20 files changed, 1193 insertions(+), 830 deletions(-) create mode 100644 study/src/org/labkey/study/model/StudyAssaySchedule.java create mode 100644 study/webapp/study/vaccineDesign/AssaySchedule.js create mode 100644 study/webapp/study/vaccineDesign/BaseDataViewAddVisit.js diff --git a/study/src/org/labkey/study/controllers/StudyDesignController.java b/study/src/org/labkey/study/controllers/StudyDesignController.java index 0f8a8e5e..af7bfa55 100644 --- a/study/src/org/labkey/study/controllers/StudyDesignController.java +++ b/study/src/org/labkey/study/controllers/StudyDesignController.java @@ -41,14 +41,17 @@ import org.labkey.api.view.JspView; import org.labkey.api.view.NavTree; import org.labkey.study.StudySchema; -import org.labkey.study.model.CohortImpl; +import org.labkey.study.model.AssaySpecimenConfigImpl; +import org.labkey.study.model.AssaySpecimenVisitImpl; import org.labkey.study.model.CohortManager; +import org.labkey.study.model.StudyAssaySchedule; +import org.labkey.study.model.StudyTreatmentSchedule; +import org.labkey.study.model.CohortImpl; import org.labkey.study.model.DoseAndRoute; import org.labkey.study.model.ProductAntigenImpl; import org.labkey.study.model.ProductImpl; import org.labkey.study.model.StudyImpl; import org.labkey.study.model.StudyManager; -import org.labkey.study.model.StudyTreatmentSchedule; import org.labkey.study.model.TreatmentImpl; import org.labkey.study.model.TreatmentManager; import org.labkey.study.model.TreatmentProductImpl; @@ -100,7 +103,7 @@ public NavTree appendNavTrail(NavTree root) } } - public static class AssayScheduleForm + public static class AssayScheduleForm extends ReturnUrlForm { private boolean useAlternateLookupFields; @@ -764,4 +767,89 @@ public void setAssayPlan(String assayPlan) _assayPlan = assayPlan; } } + + @RequiresPermission(UpdatePermission.class) + public class UpdateAssayScheduleAction extends MutatingApiAction + { + @Override + public void validateForm(StudyAssaySchedule form, Errors errors) + { + // validate that each assay configuration has an AssayName + for (AssaySpecimenConfigImpl assay : form.getAssays()) + { + if (assay.getAssayName() == null || StringUtils.isEmpty(assay.getAssayName().trim())) + errors.reject(ERROR_MSG, "Assay Name is a required field for all assay configurations."); + } + } + + @Override + public ApiResponse execute(StudyAssaySchedule form, BindException errors) throws Exception + { + ApiSimpleResponse response = new ApiSimpleResponse(); + StudyImpl study = StudyManager.getInstance().getStudy(getContainer()); + + if (study != null) + { + StudySchema schema = StudySchema.getInstance(); + + try (DbScope.Transaction transaction = schema.getSchema().getScope().ensureTransaction()) + { + updateAssays(form.getAssays()); + updateAssayPlan(study, form.getAssayPlan()); + transaction.commit(); + } + + response.put("success", true); + return response; + } + else + throw new IllegalStateException("A study does not exist in this folder"); + } + + private void updateAssays(List assays) throws Exception + { + // insert new assaySpecimens and update any existing ones + List assaySpecimenRowIds = new ArrayList<>(); + for (AssaySpecimenConfigImpl assay : assays) + { + Integer updatedRowId = TreatmentManager.getInstance().saveAssaySpecimen(getContainer(), getUser(), assay); + if (updatedRowId != null) + { + assaySpecimenRowIds.add(updatedRowId); + + updateAssayVisitMap(updatedRowId, assay.getAssayVisitMap()); + } + } + + // delete any other assaySpecimens, not included in the insert/update list, by RowId for this container + for (AssaySpecimenConfigImpl assaySpecimen : TreatmentManager.getInstance().getFilteredAssaySpecimens(getContainer(), assaySpecimenRowIds)) + TreatmentManager.getInstance().deleteAssaySpecimen(getContainer(), getUser(), assaySpecimen.getRowId()); + } + + private void updateAssayVisitMap(int assaySpecimenId, List assayVisitMaps) throws Exception + { + List assaySpecimenVisitIds = new ArrayList<>(); + if (assayVisitMaps != null && !assayVisitMaps.isEmpty()) + { + for (AssaySpecimenVisitImpl assaySpecimenVisit : assayVisitMaps) + { + assaySpecimenVisit.setAssaySpecimenId(assaySpecimenId); + + Integer updatedRowId = TreatmentManager.getInstance().saveAssaySpecimenVisit(getContainer(), getUser(), assaySpecimenVisit); + assaySpecimenVisitIds.add(updatedRowId); + } + } + + // delete any other assaySpecimenVisits, not included in the insert/update list, by RowId for this container and assaySpecimenId + for (AssaySpecimenVisitImpl assaySpecimenVisit : TreatmentManager.getInstance().getFilteredAssaySpecimenVisits(getContainer(), assaySpecimenId, assaySpecimenVisitIds)) + TreatmentManager.getInstance().deleteAssaySpecimenVisit(getContainer(), getUser(), assaySpecimenVisit.getRowId()); + } + + private void updateAssayPlan(StudyImpl study, String plan) + { + study = study.createMutable(); + study.setAssayPlan(plan); + StudyManager.getInstance().updateStudy(getUser(), study); + } + } } diff --git a/study/src/org/labkey/study/model/AssaySpecimenConfigImpl.java b/study/src/org/labkey/study/model/AssaySpecimenConfigImpl.java index e4923eb5..e3b37db8 100644 --- a/study/src/org/labkey/study/model/AssaySpecimenConfigImpl.java +++ b/study/src/org/labkey/study/model/AssaySpecimenConfigImpl.java @@ -15,9 +15,18 @@ */ package org.labkey.study.model; +import org.apache.commons.lang3.StringUtils; +import org.jetbrains.annotations.NotNull; +import org.json.JSONArray; +import org.json.JSONObject; import org.labkey.api.data.Container; import org.labkey.api.study.AssaySpecimenConfig; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + /** * User: cnathe * Date: 12/14/13 @@ -38,6 +47,7 @@ public class AssaySpecimenConfigImpl extends AbstractStudyEntity _assayVisitMap; public AssaySpecimenConfigImpl() { @@ -159,4 +169,68 @@ public void setSampleType(String sampleType) { _sampleType = sampleType; } + + public void setAssayVisitMap(List assayVisitMap) + { + _assayVisitMap = assayVisitMap; + } + + public List getAssayVisitMap() + { + return _assayVisitMap; + } + + public Map serialize() + { + Map props = new HashMap<>(); + props.put("RowId", getRowId()); + props.put("AssayName", getAssayName()); + props.put("Description", getDescription()); + props.put("LocationId", getLocationId()); + props.put("Source", getSource()); + props.put("TubeType", getTubeType()); + props.put("PrimaryTypeId", getPrimaryTypeId()); + props.put("DerivtiveTypeId", getDerivativeTypeId()); + props.put("Lab", getLab()); + props.put("SampleType", getSampleType()); + props.put("Container", getContainer().getId()); + return props; + } + + public static AssaySpecimenConfigImpl fromJSON(@NotNull JSONObject o, Container container) + { + AssaySpecimenConfigImpl assay = new AssaySpecimenConfigImpl(container, o.getString("AssayName"), o.getString("Description")); + + if (o.containsKey("RowId")) + assay.setRowId(o.getInt("RowId")); + + if (o.containsKey("Source") && !StringUtils.isEmpty(o.getString("Source"))) + assay.setSource(o.getString("Source")); + if (o.containsKey("LocationId") && o.get("LocationId") instanceof Integer && o.getInt("LocationId") > 0) + assay.setLocationId(o.getInt("LocationId")); + if (o.containsKey("TubeType") && !StringUtils.isEmpty(o.getString("TubeType"))) + assay.setTubeType(o.getString("TubeType")); + if (o.containsKey("Lab") && !StringUtils.isEmpty(o.getString("Lab"))) + assay.setLab(o.getString("Lab")); + if (o.containsKey("SampleType") && !StringUtils.isEmpty(o.getString("SampleType"))) + assay.setSampleType(o.getString("SampleType")); + if (o.containsKey("PrimaryTypeId") && o.get("PrimaryTypeId") instanceof Integer) + assay.setPrimaryTypeId(o.getInt("PrimaryTypeId")); + if (o.containsKey("DerivativeTypeId") && o.get("DerivativeTypeId") instanceof Integer) + assay.setDerivativeTypeId(o.getInt("DerivativeTypeId")); + + Object visitMapInfo = o.get("VisitMap"); + if (visitMapInfo != null && visitMapInfo instanceof JSONArray) + { + JSONArray visitMapJSON = (JSONArray) visitMapInfo; + + List assayVisitMap = new ArrayList<>(); + for (int j = 0; j < visitMapJSON.length(); j++) + assayVisitMap.add(AssaySpecimenVisitImpl.fromJSON(visitMapJSON.getJSONObject(j), container)); + + assay.setAssayVisitMap(assayVisitMap); + } + + return assay; + } } diff --git a/study/src/org/labkey/study/model/AssaySpecimenVisitImpl.java b/study/src/org/labkey/study/model/AssaySpecimenVisitImpl.java index 575f45b3..803bd215 100644 --- a/study/src/org/labkey/study/model/AssaySpecimenVisitImpl.java +++ b/study/src/org/labkey/study/model/AssaySpecimenVisitImpl.java @@ -15,16 +15,19 @@ */ package org.labkey.study.model; +import org.jetbrains.annotations.NotNull; +import org.json.JSONObject; import org.labkey.api.data.Container; import org.labkey.api.study.AssaySpecimenVisit; -/** - * Created by cnathe on 2/3/14. - */ +import java.util.HashMap; +import java.util.Map; + public class AssaySpecimenVisitImpl implements AssaySpecimenVisit { private int _assaySpecimenId; private int _visitId; + private int _rowId; private Container _container; public AssaySpecimenVisitImpl() @@ -38,6 +41,11 @@ public AssaySpecimenVisitImpl(Container container, int assaySpecimenId, int visi _visitId = visitId; } + public boolean isNew() + { + return _rowId == 0; + } + public int getAssaySpecimenId() { return _assaySpecimenId; @@ -67,4 +75,36 @@ public void setContainer(Container container) { _container = container; } + + public int getRowId() + { + return _rowId; + } + + public void setRowId(int rowId) + { + _rowId = rowId; + } + + public Map serialize() + { + Map props = new HashMap<>(); + props.put("RowId", getRowId()); + props.put("VisitId", getVisitId()); + props.put("AssaySpecimenId", getAssaySpecimenId()); + props.put("Container", getContainer().getId()); + return props; + } + + public static AssaySpecimenVisitImpl fromJSON(@NotNull JSONObject o, Container container) + { + // AssaySpecimenId may not be specified in JSON + int assaySpecimenId = o.containsKey("AssaySpecimenId") ? o.getInt("AssaySpecimenId") : 0; + AssaySpecimenVisitImpl assaySpecimenVisit = new AssaySpecimenVisitImpl(container, assaySpecimenId, o.getInt("VisitId")); + + if (o.containsKey("RowId")) + assaySpecimenVisit.setRowId(o.getInt("RowId")); + + return assaySpecimenVisit; + } } diff --git a/study/src/org/labkey/study/model/StudyAssaySchedule.java b/study/src/org/labkey/study/model/StudyAssaySchedule.java new file mode 100644 index 00000000..ffd1865b --- /dev/null +++ b/study/src/org/labkey/study/model/StudyAssaySchedule.java @@ -0,0 +1,63 @@ +package org.labkey.study.model; + +import org.json.JSONArray; +import org.labkey.api.action.CustomApiForm; +import org.labkey.api.data.Container; +import org.labkey.api.view.HttpView; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +public class StudyAssaySchedule implements CustomApiForm +{ + Container _container; + List _assays; + String _assayPlan; + + public StudyAssaySchedule() + {} + + public StudyAssaySchedule(Container container) + { + _container = container; + } + + public void setAssays(List assays) + { + _assays = assays; + } + + public List getAssays() + { + return _assays; + } + + public void setAssayPlan(String assayPlan) + { + _assayPlan = assayPlan; + } + + public String getAssayPlan() + { + return _assayPlan; + } + + @Override + public void bindProperties(Map props) + { + _container = HttpView.currentContext().getContainer(); + + Object assaysInfo = props.get("assays"); + if (assaysInfo != null && assaysInfo instanceof JSONArray) + { + _assays = new ArrayList<>(); + + JSONArray assaysJSON = (JSONArray) assaysInfo; + for (int i = 0; i < assaysJSON.length(); i++) + _assays.add(AssaySpecimenConfigImpl.fromJSON(assaysJSON.getJSONObject(i), _container)); + } + + _assayPlan = null != props.get("assayPlan") ? props.get("assayPlan").toString() : null; + } +} diff --git a/study/src/org/labkey/study/model/TreatmentManager.java b/study/src/org/labkey/study/model/TreatmentManager.java index b32f118f..31b3b6f8 100644 --- a/study/src/org/labkey/study/model/TreatmentManager.java +++ b/study/src/org/labkey/study/model/TreatmentManager.java @@ -382,7 +382,43 @@ public DoseAndRoute getDoseAndRoute(Container container, String label, int produ return null; } + public Integer saveAssaySpecimen(Container container, User user, AssaySpecimenConfigImpl assaySpecimen) throws Exception + { + TableInfo assaySpecimenTable = QueryService.get().getUserSchema(user, container, StudyQuerySchema.SCHEMA_NAME).getTable(StudyQuerySchema.ASSAY_SPECIMEN_TABLE_NAME); + return saveStudyDesignRow(container, user, assaySpecimenTable, assaySpecimen.serialize(), assaySpecimen.isNew() ? null : assaySpecimen.getRowId(), "RowId", true); + } + + public Integer saveAssaySpecimenVisit(Container container, User user, AssaySpecimenVisitImpl assaySpecimenVisit) throws Exception + { + TableInfo assaySpecimenVIsitTable = QueryService.get().getUserSchema(user, container, StudyQuerySchema.SCHEMA_NAME).getTable(StudyQuerySchema.ASSAY_SPECIMEN_VISIT_TABLE_NAME); + return saveStudyDesignRow(container, user, assaySpecimenVIsitTable, assaySpecimenVisit.serialize(), assaySpecimenVisit.isNew() ? null : assaySpecimenVisit.getRowId(), "RowId", true); + } + + public List getFilteredAssaySpecimens(Container container, List filterRowIds) + { + SimpleFilter filter = SimpleFilter.createContainerFilter(container); + if (filterRowIds != null && !filterRowIds.isEmpty()) + filter.addCondition(FieldKey.fromParts("RowId"), filterRowIds, CompareType.NOT_IN); + + return new TableSelector(StudySchema.getInstance().getTableInfoAssaySpecimen(), filter, new Sort("RowId")).getArrayList(AssaySpecimenConfigImpl.class); + } + + public List getFilteredAssaySpecimenVisits(Container container, int assaySpecimenId, List filterRowIds) + { + SimpleFilter filter = SimpleFilter.createContainerFilter(container); + filter.addCondition(FieldKey.fromParts("AssaySpecimenId"), assaySpecimenId); + if (filterRowIds != null && !filterRowIds.isEmpty()) + filter.addCondition(FieldKey.fromParts("RowId"), filterRowIds, CompareType.NOT_IN); + + return new TableSelector(StudySchema.getInstance().getTableInfoAssaySpecimenVisit(), filter, new Sort("RowId")).getArrayList(AssaySpecimenVisitImpl.class); + } + public Integer saveStudyDesignRow(Container container, User user, TableInfo tableInfo, Map row, Integer key, String pkColName) throws Exception + { + return saveStudyDesignRow(container, user, tableInfo, row, key, pkColName, false); + } + + public Integer saveStudyDesignRow(Container container, User user, TableInfo tableInfo, Map row, Integer key, String pkColName, boolean includeContainerKey) throws Exception { QueryUpdateService qus = tableInfo != null ? tableInfo.getUpdateService() : null; if (qus != null) @@ -396,8 +432,12 @@ public Integer saveStudyDesignRow(Container container, User user, TableInfo tabl } else { - List> oldKeys = Collections.singletonList(Collections.singletonMap(pkColName, key)); - updatedRows = qus.updateRows(user, container, Collections.singletonList(row), oldKeys, null, null); + Map oldKey = new HashMap<>(); + oldKey.put(pkColName, key); + if (includeContainerKey) + oldKey.put("Container", container.getId()); + + updatedRows = qus.updateRows(user, container, Collections.singletonList(row), Collections.singletonList(oldKey), null, null); } if (errors.hasErrors()) @@ -491,6 +531,26 @@ public void deleteTreatmentProductMap(Container container, User user, List <%@ page import="org.labkey.api.security.User" %> <%@ page import="org.labkey.api.security.permissions.UpdatePermission" %> -<%@ page import="org.labkey.api.util.PageFlowUtil" %> <%@ page import="org.labkey.api.view.WebThemeManager" %> <%@ page import="org.labkey.api.view.template.ClientDependencies" %> <%@ page import="org.labkey.study.controllers.StudyDesignController" %> -<%@ page import="org.labkey.study.model.AssaySpecimenConfigImpl" %> -<%@ page import="org.labkey.study.model.LocationImpl" %> <%@ page import="org.labkey.study.model.StudyImpl" %> <%@ page import="org.labkey.study.model.StudyManager" %> -<%@ page import="org.labkey.study.model.VisitImpl" %> -<%@ page import="java.util.List" %> <%@ page import="org.labkey.api.view.ActionURL" %> +<%@ page import="org.labkey.api.module.ModuleLoader" %> <%@ page extends="org.labkey.study.view.BaseStudyPage" %> <%! @Override public void addClientDependencies(ClientDependencies dependencies) { - dependencies.add("study/StudyVaccineDesign.css"); + dependencies.add("study/vaccineDesign/vaccineDesign.lib.xml"); + dependencies.add("study/vaccineDesign/VaccineDesign.css"); } %> <% Container c = getContainer(); + boolean useAlternateLookupFields = getContainer().getActiveModules().contains(ModuleLoader.getInstance().getModule("rho")); + StudyImpl study = StudyManager.getInstance().getStudy(c); User user = getUser(); @@ -47,116 +46,51 @@ String assayPlan = ""; if (study != null && study.getAssayPlan() != null) - assayPlan = h(study.getAssayPlan()).replaceAll("\n", "
    "); + assayPlan = study.getAssayPlan(); %> <% if (study != null) { - List assaySpecimenConfigs = study.getAssaySpecimenConfigs("AssayName"); - List visits = study.getVisitsForAssaySchedule(); - - if (assaySpecimenConfigs.size() == 0) - { - %>No assays have been scheduled.
    <% - } + %>This section shows the assay schedule for this study. Each treatment may consist of several immunogens and adjuvants.
    <% if (canEdit) { ActionURL editUrl = new ActionURL(StudyDesignController.ManageAssayScheduleAction.class, getContainer()); + if (useAlternateLookupFields) + editUrl.addParameter("useAlternateLookupFields", true); editUrl.addReturnURL(getActionURL()); %> - To change the set of assays and edit the assay schedule, click the edit button below.
    - <%= button("Edit").href(editUrl) %> + <%=textLink("Manage Assay Schedule", editUrl)%>
    <% } - %>

    <%=assayPlan%>

    <% - - if (assaySpecimenConfigs.size() > 0) - { %> -
    '); tplArr.push(''); @@ -226,22 +250,14 @@ Ext4.define('LABKEY.VaccineDesign.BaseDataView', { { if (Ext4.isString(column.dataIndex)) { + var checkMissingReqTpl = column.required ? ' {[this.checkMissingRequired(values, "' + column.dataIndex + '")]}' : '', + tdTpl = '', + tdCloseTpl = '' - + '{[this.getLabelFromStore(values["' + column.dataIndex + '"], "' + column.queryName + '")]}' - + '{' + column.dataIndex + ':htmlEncode}
    - - - - - - - +

    <%=h(assayPlan).replaceAll("\n", "
    ")%>

    +
    <% - for (VisitImpl visit : visits) - { + } + else + { %> - +

    The folder must contain a study in order to display an assay schedule.

    <% - } + } %> - -<% - for (AssaySpecimenConfigImpl assaySpecimen : assaySpecimenConfigs) - { - // concatenate sample type (i.e. sample type | tube type) - String sampleType = ""; - String sep = ""; - if (assaySpecimen.getSampleType() != null) - { - sampleType += assaySpecimen.getSampleType(); - sep = "|"; - } - if (assaySpecimen.getTubeType() != null) - { - sampleType += sep + assaySpecimen.getTubeType(); - } - - String locationLabel = ""; - if (assaySpecimen.getLab() != null) - { - String labLabel = StudyManager.getInstance().getStudyDesignLabLabelByName(c, assaySpecimen.getLab()); - locationLabel += (labLabel != null ? labLabel : assaySpecimen.getLab()); - } - else if (assaySpecimen.getLocationId() != null) - { - LocationImpl location = StudyManager.getInstance().getLocation(c, assaySpecimen.getLocationId()); - locationLabel = location != null ? location.getLabel() : ""; - } - String assayLabel = StudyManager.getInstance().getStudyDesignAssayLabelByName(c, assaySpecimen.getAssayName()); -%> - - - - -<% - List assaySpecimenVisits = StudyManager.getInstance().getAssaySpecimenVisitIds(c, assaySpecimen); - for (VisitImpl visit : visits) - { - %><% - } -%> - -<% - } -%> -
    Assay Schedule
    AssayLabSample Type - <%=h(visit.getDisplayString())%> - <%=(visit.getDescription() != null ? PageFlowUtil.helpPopup("Description", visit.getDescription()) : "")%> -
    <%=h(assayLabel != null ? assayLabel : assaySpecimen.getAssayName())%> - <%=(assaySpecimen.getDescription() != null ? PageFlowUtil.helpPopup("Description", assaySpecimen.getDescription()) : "")%> - <%=h(locationLabel)%><%=h(sampleType)%><%=assaySpecimenVisits.contains(visit.getRowId()) ? "✓" : " "%>
    -<% - } - } - else + \ No newline at end of file diff --git a/study/src/org/labkey/study/view/studydesign/immunizationScheduleWebpart.jsp b/study/src/org/labkey/study/view/studydesign/immunizationScheduleWebpart.jsp index 08344357..6c9030d6 100644 --- a/study/src/org/labkey/study/view/studydesign/immunizationScheduleWebpart.jsp +++ b/study/src/org/labkey/study/view/studydesign/immunizationScheduleWebpart.jsp @@ -69,7 +69,7 @@ else { List cohorts = study.getCohorts(user); - %>This section shows the immunization schedule. Each treatment may consist of several immunogens and adjuvants.
    <% + %>This section shows the immunization schedule for this study. Each treatment may consist of several immunogens and adjuvants.
    <% if (canEdit) { diff --git a/study/src/org/labkey/study/view/studydesign/manageAssaySchedule.jsp b/study/src/org/labkey/study/view/studydesign/manageAssaySchedule.jsp index 14f9a909..4a3cf97f 100644 --- a/study/src/org/labkey/study/view/studydesign/manageAssaySchedule.jsp +++ b/study/src/org/labkey/study/view/studydesign/manageAssaySchedule.jsp @@ -16,26 +16,23 @@ */ %> <%@ page import="org.labkey.api.data.Container" %> -<%@ page import="org.labkey.api.security.User" %> <%@ page import="org.labkey.api.study.Study" %> <%@ page import="org.labkey.api.study.TimepointType" %> -<%@ page import="org.labkey.api.study.Visit" %> -<%@ page import="org.labkey.api.view.ActionURL" %> <%@ page import="org.labkey.api.view.HttpView" %> <%@ page import="org.labkey.api.view.JspView" %> <%@ page import="org.labkey.api.view.template.ClientDependencies" %> -<%@ page import="org.labkey.study.controllers.StudyController" %> <%@ page import="org.labkey.study.controllers.StudyDesignController" %> <%@ page import="org.labkey.study.model.StudyManager" %> -<%@ page import="org.labkey.study.security.permissions.ManageStudyPermission" %> +<%@ page import="org.labkey.api.util.PageFlowUtil" %> +<%@ page import="org.labkey.api.portal.ProjectUrls" %> +<%@ page import="org.labkey.study.controllers.StudyController" %> <%@ page extends="org.labkey.api.jsp.JspBase" %> <%! @Override public void addClientDependencies(ClientDependencies dependencies) { - dependencies.add("Ext4ClientApi"); - dependencies.add("study/StudyVaccineDesign.js"); - dependencies.add("dataview/DataViewsPanel.css"); + dependencies.add("study/vaccineDesign/vaccineDesign.lib.xml"); + dependencies.add("study/vaccineDesign/VaccineDesign.css"); } %> <% @@ -43,663 +40,102 @@ StudyDesignController.AssayScheduleForm form = me.getModelBean(); Container c = getContainer(); - User user = getUser(); - ActionURL returnURL = getActionURL(); Study study = StudyManager.getInstance().getStudy(getContainer()); - boolean canManageStudy = c.hasPermission(user, ManageStudyPermission.class); boolean isDataspace = c.isProject() && c.isDataspace(); - String visitDisplayName = "Visit"; + String visitNoun = "Visit"; if (study != null && study.getTimepointType() == TimepointType.DATE) - visitDisplayName = "Timepoint"; -%> - - + visitNoun = "Timepoint"; - Enter assay schedule information in the grids below. -
    +
    • > - Configure dropdown options for assays, labs, and sample types at the project level to be shared across study designs or within this folder for + Configure dropdown options for assays, labs, and sample types at the project + level to be shared across study designs or within this folder for study specific properties:
    • -
    • Use the "Insert New" button in the assay configurations grid to add a new assay.
    • -
    • Select the visits for each assay in the assay schedule grid to define the expected assay schedule for the study.
    • +
    • + Select the <%=h(visitNoun.toLowerCase())%>s for each assay in the schedule + portion of the grid to define the expected assay schedule for the study. +
    • +
    • > + Use the manage locationss page to further configure information about the locations for this study. + <%= textLink("Manage Locations", StudyController.ManageLocationsAction.class) %> +
    • +
    • + Use the manage <%=h(visitNoun.toLowerCase())%>s page to further configure + information about the <%=h(visitNoun.toLowerCase())%>s for this study or to change + the <%=h(visitNoun.toLowerCase())%> display order. + <%=textLink("Manage " + visitNoun + "s", StudyController.ManageVisitsAction.class)%> +
    -
    -* Double click to edit an assay configuration -

    -<% - if (canManageStudy && form.isUseAlternateLookupFields()) - { - %><%= textLink("Manage Locations", StudyController.ManageLocationsAction.class) %>
    <% - } -%> +

    -
    - -<% - if (canManageStudy) - { - if (study != null && study.getTimepointType() == TimepointType.VISIT && study.getVisits(Visit.Order.DISPLAY).size() > 1) - { - %><%= textLink("Change Visit Order", new ActionURL(StudyController.VisitOrderAction.class, getContainer()).addReturnURL(returnURL)) %><% - } -%> - <%=textLink("Manage " + visitDisplayName + "s", StudyController.ManageVisitsAction.class)%> -<% - } -%> -


    -
    diff --git a/study/src/org/labkey/study/view/studydesign/manageTreatments.jsp b/study/src/org/labkey/study/view/studydesign/manageTreatments.jsp index 1f470447..eb892875 100644 --- a/study/src/org/labkey/study/view/studydesign/manageTreatments.jsp +++ b/study/src/org/labkey/study/view/studydesign/manageTreatments.jsp @@ -80,7 +80,9 @@ Enter treatment information in the grids below.
      -
    • Each treatment label must be unique and must consist of at least one study products, i.e. immunogens and/or adjuvants.
    • +
    • + Each treatment label must be unique and must consist of at least one study products, i.e. immunogens and/or adjuvants. +
    • Use the manage study products page to change or update the set of available immunogens and adjuvants. <% @@ -97,8 +99,9 @@ Enter treatment information in the grids below. <%=textLink("Manage Cohorts", CohortController.ManageCohortsAction.class)%>
    • - Use the manage <%=h(visitNoun.toLowerCase())%>s page to further configuration - information about the <%=h(visitNoun.toLowerCase())%>s for this study. + Use the manage <%=h(visitNoun.toLowerCase())%>s page to further configure + information about the <%=h(visitNoun.toLowerCase())%>s for this study or to change + the <%=h(visitNoun.toLowerCase())%> display order. <%=textLink("Manage " + visitNoun + "s", StudyController.ManageVisitsAction.class)%>
    • <% diff --git a/study/src/org/labkey/study/view/studydesign/vaccineDesignWebpart.jsp b/study/src/org/labkey/study/view/studydesign/vaccineDesignWebpart.jsp index 2296eb44..6f049aa0 100644 --- a/study/src/org/labkey/study/view/studydesign/vaccineDesignWebpart.jsp +++ b/study/src/org/labkey/study/view/studydesign/vaccineDesignWebpart.jsp @@ -77,7 +77,6 @@ { Ext4.create('LABKEY.VaccineDesign.ImmunogensGrid', { renderTo : 'immunogens-grid', - studyDesignQueryNames : ['StudyDesignImmunogenTypes', 'StudyDesignGenes', 'StudyDesignSubTypes'], showDoseRoute: false, disableEdit : true }); diff --git a/study/webapp/study/vaccineDesign/AssaySchedule.js b/study/webapp/study/vaccineDesign/AssaySchedule.js new file mode 100644 index 00000000..bc3093eb --- /dev/null +++ b/study/webapp/study/vaccineDesign/AssaySchedule.js @@ -0,0 +1,552 @@ +Ext4.define('LABKEY.VaccineDesign.AssaySchedulePanel', { + extend : 'Ext.panel.Panel', + + border : false, + + bodyStyle : 'background-color: transparent;', + + width: 750, + + disableEdit : true, + + dirty : false, + + returnURL : null, + + initComponent : function() + { + this.items = [this.getAssaysGrid()]; + + this.callParent(); + + this.queryAssayPlan(); + + window.onbeforeunload = LABKEY.beforeunload(this.beforeUnload, this); + }, + + getAssaysGrid : function() + { + if (!this.assaysGrid) + { + this.assaysGrid = Ext4.create('LABKEY.VaccineDesign.AssaysGrid', { + disableEdit: this.disableEdit, + visitNoun: this.visitNoun, + useAlternateLookupFields: this.useAlternateLookupFields + }); + + this.assaysGrid.on('dirtychange', this.enableSaveButton, this); + this.assaysGrid.on('celledited', this.enableSaveButton, this); + } + + return this.assaysGrid; + }, + + queryAssayPlan : function() + { + // query the StudyProperties table for the initial assay plan value + LABKEY.Query.selectRows({ + schemaName: 'study', + queryName: 'StudyProperties', + columns: 'AssayPlan', + scope: this, + success: function(data) + { + var text = (data.rows.length == 1) ? data.rows[0]["AssayPlan"] : ''; + + this.add(this.getAssayPlanPanel(text)); + this.add(this.getButtonBar()); + } + }); + + }, + + getAssayPlanPanel : function(initValue) + { + if (!this.assayPlanPanel) + { + this.assayPlanPanel = Ext4.create('Ext.form.Panel', { + cls: 'study-vaccine-design', + padding: '20px 0', + border: false, + items: [ + Ext4.create('Ext.Component', { + html: '
      Assay Plan
      ' + }), + this.getAssayPlanTextArea(initValue) + ] + }); + } + + return this.assayPlanPanel; + }, + + getAssayPlanTextArea : function(initValue) + { + if (!this.assayPlanTextArea) + { + this.assayPlanTextArea = Ext4.create('Ext.form.field.TextArea', { + name: 'assayPlan', + readOnly: this.disableEdit, + value: initValue, + width: 500, + height: 100 + }); + + this.assayPlanTextArea.on('change', this.enableSaveButton, this, {buffer: 500}); + } + + return this.assayPlanTextArea; + }, + + getButtonBar : function() + { + if (!this.buttonBar) + { + this.buttonBar = Ext4.create('Ext.toolbar.Toolbar', { + dock: 'bottom', + ui: 'footer', + padding: 0, + style : 'background-color: transparent;', + defaults: {width: 75}, + items: [this.getSaveButton(), this.getCancelButton()] + }); + } + + return this.buttonBar; + }, + + getSaveButton : function() + { + if (!this.saveButton) + { + this.saveButton = Ext4.create('Ext.button.Button', { + text: 'Save', + disabled: true, + hidden: this.disableEdit, + handler: this.saveAssaySchedule, + scope: this + }); + } + + return this.saveButton; + }, + + enableSaveButton : function() + { + this.setDirty(true); + this.getSaveButton().enable(); + }, + + getCancelButton : function() + { + if (!this.cancelButton) + { + this.cancelButton = Ext4.create('Ext.button.Button', { + margin: this.disableEdit ? 0 : '0 0 0 10px', + text: this.disableEdit ? 'Done' : 'Cancel', + handler: this.goToReturnURL, + scope: this + }); + } + + return this.cancelButton; + }, + + saveAssaySchedule : function() + { + this.getEl().mask('Saving...'); + + var assays = []; + Ext4.each(this.getAssaysGrid().getStore().getRange(), function(record) + { + var recData = Ext4.clone(record.data); + + // drop any empty treatment rows that were just added + var hasData = LABKEY.VaccineDesign.Utils.modelHasData(recData, LABKEY.VaccineDesign.Assay.getFields()); + if (hasData) + assays.push(recData); + }, this); + + LABKEY.Ajax.request({ + url : LABKEY.ActionURL.buildURL('study-design', 'updateAssaySchedule.api'), + method : 'POST', + jsonData: { + assays: assays, + assayPlan: this.getAssayPlanTextArea().getValue() + }, + scope: this, + success: function(response) + { + var resp = Ext4.decode(response.responseText); + if (resp.success) + this.goToReturnURL(); + else + this.onFailure(); + }, + failure: function(response) + { + var resp = Ext4.decode(response.responseText); + if (resp.errors) + this.onFailure(Ext4.Array.pluck(resp.errors, 'message').join('
      ')); + else + this.onFailure(resp.exception); + } + }); + }, + + goToReturnURL : function() + { + this.setDirty(false); + window.location = this.returnURL; + }, + + onFailure : function(text) + { + Ext4.Msg.show({ + title: 'Error', + msg: text || 'Unknown error occurred.', + icon: Ext4.Msg.ERROR, + buttons: Ext4.Msg.OK + }); + + this.getEl().unmask(); + }, + + setDirty : function(dirty) + { + this.dirty = dirty; + LABKEY.Utils.signalWebDriverTest("treatmentScheduleDirty", dirty); + }, + + isDirty : function() + { + return this.dirty; + }, + + beforeUnload : function() + { + if (!this.disableEdit && this.isDirty()) + return 'Please save your changes.'; + } +}); + +Ext4.define('LABKEY.VaccineDesign.AssaysGrid', { + extend : 'LABKEY.VaccineDesign.BaseDataViewAddVisit', + + mainTitle : 'Assay Schedule', + + width : 700, + + studyDesignQueryNames : ['StudyDesignAssays', 'StudyDesignLabs', 'StudyDesignSampleTypes', 'Location'], + + visitNoun : 'Visit', + + useAlternateLookupFields : false, + + //Override + getStore : function() + { + if (!this.store) + { + this.store = Ext4.create('Ext.data.Store', { + storeId : 'AssaysGridStore', + pageSize : 100000, // need to explicitly set otherwise it defaults to 25 + proxy: { + type: 'ajax', + url : LABKEY.ActionURL.buildURL('query', 'selectRows.api', null, { + 'schemaName' : 'study', + 'query.queryName' : 'assayspecimen' + }), + reader: { + type: 'json', + root: 'rows' + } + }, + sorters: [{ property: 'AssayName', direction: 'ASC' }], + autoLoad: true, + listeners: { + scope: this, + load: this.getVisitStore + } + }); + } + + return this.store; + }, + + getVisitStore : function() + { + if (!this.visitStore) + { + this.visitStore = Ext4.create('Ext.data.Store', { + model : 'LABKEY.VaccineDesign.Visit', + pageSize : 100000, // need to explicitly set otherwise it defaults to 25 + proxy: { + type: 'ajax', + url : LABKEY.ActionURL.buildURL('query', 'selectRows.api', null, { + 'schemaName' : 'study', + 'query.queryName' : 'visit' + }), + reader: { + type: 'json', + root: 'rows' + } + }, + sorters: [{ property: 'DisplayOrder', direction: 'ASC' }], + autoLoad: true, + listeners: { + scope: this, + load: this.getAssaySpecimenVisitStore + } + }); + } + + return this.visitStore; + }, + + getAssaySpecimenVisitStore : function() + { + if (!this.assaySpecimenVisitStore) + { + this.assaySpecimenVisitStore = Ext4.create('Ext4.data.Store', { + storeId : 'AssaySpecimenVisitStore', + model : 'LABKEY.VaccineDesign.AssaySpecimenVisit', + pageSize : 100000, // need to explicitly set otherwise it defaults to 25 + proxy: { + type: 'ajax', + url : LABKEY.ActionURL.buildURL('query', 'selectRows.api', null, { + 'schemaName' : 'study', + 'query.queryName' : 'AssaySpecimenVisit' + }), + reader: { + type: 'json', + root: 'rows' + } + }, + autoLoad: true, + listeners: { + scope: this, + load: function (store, records) + { + var includedVisits = []; + + // stash the visit mapping information attached to each record in the assay store + Ext4.each(records, function(record) + { + var assayRecord = this.getStore().findRecord('RowId', record.get('AssaySpecimenId')); + if (assayRecord != null) + { + var visitMap = assayRecord.get('VisitMap') || []; + visitMap.push(Ext4.clone(record.data)); + assayRecord.set('VisitMap', visitMap); + + includedVisits.push(record.get('VisitId')); + } + }, this); + + var includedVisits = Ext4.Array.unique(includedVisits); + Ext4.each(this.getVisitStore().getRange(), function(visit) + { + var included = includedVisits.indexOf(visit.get('RowId')) > -1; + visit.set('Included', included); + }, this); + + this.add(this.getDataView()); + this.fireEvent('loadcomplete', this); + } + } + }); + } + + return this.assaySpecimenVisitStore; + }, + + //Override + loadDataViewStore : function() + { + // just call getStore here to initial the load, we will add the DataView + // and fire the loadcomplete event after all of the stores for this page are done loading + this.getStore(); + }, + + //Override + getColumnConfigs : function() + { + if (!this.columnConfigs) + { + var assayNameEditorConfig = LABKEY.VaccineDesign.Utils.getStudyDesignComboConfig('AssayName', 185, 'StudyDesignAssays'); + assayNameEditorConfig.editable = true; // Rho use-case + + var columnConfigs = [{ + label: 'Assay Name', + width: 200, + dataIndex: 'AssayName', + required: true, + editorType: 'LABKEY.ext4.ComboBox', + editorConfig: assayNameEditorConfig + },{ + label: 'Description', + width: 200, + dataIndex: 'Description', + editorType: 'Ext.form.field.Text', + editorConfig: LABKEY.VaccineDesign.Utils.getStudyDesignTextConfig('Description', 185) + }/*,{ + label: 'Sample Quantity', + width: 200, + dataIndex: 'SampleQuantity', + editorType: 'Ext.form.field.Text', + editorConfig: LABKEY.VaccineDesign.Utils.getStudyDesignTextConfig('SampleQuantity', 185, '95%') + },{ + label: 'Sample Units', + width: 200, + dataIndex: 'SampleUnits', + required: true, + editorType: 'LABKEY.ext4.ComboBox', + editorConfig: LABKEY.VaccineDesign.Utils.getStudyDesignComboConfig('SampleUnits', 185, 'StudyDesignUnits') + }*/]; + + if (this.useAlternateLookupFields) + { + columnConfigs.push({ + label: 'Source', + width: 60, + dataIndex: 'Source', + editorType: 'Ext.form.field.Text', + editorConfig: LABKEY.VaccineDesign.Utils.getStudyDesignTextConfig('Source', 45) + }); + columnConfigs.push({ + label: 'Location', + width: 140, + dataIndex: 'LocationId', + queryName: 'Location', + editorType: 'LABKEY.ext4.ComboBox', + editorConfig: LABKEY.VaccineDesign.Utils.getStudyDesignComboConfig('LocationId', 125, 'Location', undefined, 'Label', 'RowId') + }); + columnConfigs.push({ + label: 'TubeType', + width: 200, + dataIndex: 'TubeType', + editorType: 'Ext.form.field.Text', + editorConfig: LABKEY.VaccineDesign.Utils.getStudyDesignTextConfig('TubeType', 185) + }); + } + else + { + columnConfigs.push({ + label: 'Lab', + width: 140, + dataIndex: 'Lab', + editorType: 'LABKEY.ext4.ComboBox', + editorConfig: LABKEY.VaccineDesign.Utils.getStudyDesignComboConfig('Lab', 125, 'StudyDesignLabs') + }); + columnConfigs.push({ + label: 'Sample Type', + width: 140, + dataIndex: 'SampleType', + editorType: 'LABKEY.ext4.ComboBox', + editorConfig: LABKEY.VaccineDesign.Utils.getStudyDesignComboConfig('SampleType', 125, 'StudyDesignSampleTypes', undefined, 'Name') + }); + } + + var visitConfigs = this.getVisitColumnConfigs(); + + // update the width based on the number of visit columns + var width = (visitConfigs.length * 75) + 700 + (this.useAlternateLookupFields ? 120 : 0); + this.setWidth(width); + + // update the outer panel width if necessary + var outerPanel = this.up('panel'); + if (outerPanel != null) + outerPanel.setWidth(Math.max(width, 750)); + + this.columnConfigs = columnConfigs.concat(visitConfigs); + } + + return this.columnConfigs; + }, + + getVisitColumnConfigs : function() + { + var visitConfigs = []; + + Ext4.each(this.getVisitStore().getRange(), function(visit) + { + if (visit.get('Included')) + { + visitConfigs.push({ + label: visit.get('Label') || visit.get('SequenceNumMin'), + width: 75, + dataIndex: 'VisitMap', + dataIndexArrFilterProp: 'VisitId', + dataIndexArrFilterValue: visit.get('RowId'), + editorType: 'Ext.form.field.Checkbox', + editorConfig: { + hideFieldLabel: true, + name: 'VisitMap' + } + }); + } + }, this); + + return visitConfigs; + }, + + //Override + getCurrentCellValue : function(column, record, dataIndex, outerDataIndex, subgridIndex) + { + var value = this.callParent([column, record, dataIndex, outerDataIndex, subgridIndex]); + + if (dataIndex == 'VisitMap' && Ext4.isArray(value)) + { + var matchingIndex = LABKEY.VaccineDesign.Utils.getMatchingRowIndexFromArray(value, column.dataIndexArrFilterProp, column.dataIndexArrFilterValue); + return matchingIndex > -1; + } + else if (dataIndex == 'LocationId' && value == 0) + { + return null; + } + + return value; + }, + + //Override + updateStoreRecordValue : function(record, column, newValue) + { + // special case for editing the value of one of the pivot visit columns + if (column.dataIndex == 'VisitMap') + { + var visitMapArr = record.get(column.dataIndex); + if (!Ext4.isArray(visitMapArr)) + { + visitMapArr = []; + record.set(column.dataIndex, visitMapArr); + } + + var matchingIndex = LABKEY.VaccineDesign.Utils.getMatchingRowIndexFromArray(visitMapArr, column.dataIndexArrFilterProp, column.dataIndexArrFilterValue); + + if (newValue) + visitMapArr.push({VisitId: column.dataIndexArrFilterValue}); + else + Ext4.Array.splice(visitMapArr, matchingIndex, 1); + + this.fireEvent('celledited', this, 'VisitMap', visitMapArr); + } + else + { + this.callParent([record, column, newValue]); + } + }, + + //Override + getNewModelInstance : function() + { + return LABKEY.VaccineDesign.Assay.create(); + }, + + //Override + getDeleteConfirmationMsg : function() + { + return 'Are you sure you want to delete the selected assay configuration?

      ' + + 'Note: this will also delete all related visit mapping information.'; + } +}); diff --git a/study/webapp/study/vaccineDesign/BaseDataView.js b/study/webapp/study/vaccineDesign/BaseDataView.js index edf591e0..fe283cf8 100644 --- a/study/webapp/study/vaccineDesign/BaseDataView.js +++ b/study/webapp/study/vaccineDesign/BaseDataView.js @@ -146,15 +146,18 @@ Ext4.define('LABKEY.VaccineDesign.BaseDataView', { tplArr.push(tdTpl + valTpl + tdCloseTpl); } - else if (column.editorType === 'LABKEY.ext4.Checkbox') + else if (column.editorType == 'Ext.form.field.Checkbox') { - var vRowId = column.dataIndexArrFilterValue, - valTpl = ''; + var valTpl = ''; + if (!showEdit) + { + valTpl = '{[this.getDisplayValue(values["' + column.dataIndex + '"], ' + + '"' + column.dataIndexArrFilterProp + '", ' + + '"' + column.dataIndexArrFilterValue + '", ' + + '"checkbox")]}'; + } + tdTpl = tdTpl.substring(0, tdTpl.length -1) + ' style="text-align: center;">'; tplArr.push(tdTpl + valTpl + tdCloseTpl); } else @@ -178,7 +181,12 @@ Ext4.define('LABKEY.VaccineDesign.BaseDataView', { { var matchingIndex = LABKEY.VaccineDesign.Utils.getMatchingRowIndexFromArray(val, arrPropFilterName, arrPropFilterVal); if (matchingIndex > -1 && Ext4.isObject(val[matchingIndex])) - val = val[matchingIndex][arrPropDisplayField]; + { + if (arrPropDisplayField == 'checkbox') + return '✓'; + else + val = val[matchingIndex][arrPropDisplayField]; + } else val = ''; } @@ -353,16 +361,23 @@ Ext4.define('LABKEY.VaccineDesign.BaseDataView', { if (editor != null) { - // create a new form field to place in the td cell - var field = Ext4.create(editor.type, Ext4.apply(editor.config, { + var config = { renderTo: target, - value: this.getCurrentCellValue(column, record, dataIndex, outerDataIndex, subgridIndex), required: column.required, storeIndex: index, dataFilterValue: dataFilterValue, outerDataIndex: outerDataIndex, subgridIndex: subgridIndex - })); + }; + + var currentValue = this.getCurrentCellValue(column, record, dataIndex, outerDataIndex, subgridIndex); + if (editor.type == 'Ext.form.field.Checkbox') + config.checked = currentValue; + else + config.value = currentValue; + + // create a new form field to place in the td cell + var field = Ext4.create(editor.type, Ext4.apply(editor.config, config)); // add listeners for when to apply the updated value and clear the input field field.on('change', this.updateStoreValueForCellEdit, this, {buffer: 500}); diff --git a/study/webapp/study/vaccineDesign/BaseDataViewAddVisit.js b/study/webapp/study/vaccineDesign/BaseDataViewAddVisit.js new file mode 100644 index 00000000..566e59ca --- /dev/null +++ b/study/webapp/study/vaccineDesign/BaseDataViewAddVisit.js @@ -0,0 +1,92 @@ + +Ext4.define('LABKEY.VaccineDesign.BaseDataViewAddVisit', { + extend: 'LABKEY.VaccineDesign.BaseDataView', + + getVisitStore : function() + { + throw new Error("getVisitStore must be overridden in subclass"); + }, + + //Override + getAddNewRowTpl : function(columns, dataIndex) + { + var tplArr = []; + + if (!this.disableEdit) + { + tplArr.push('
     '); + tplArr.push(' Add new row   '); + tplArr.push(' Add new ' + this.visitNoun.toLowerCase() + ''); + tplArr.push('
     '); - tplArr.push(' Add new row   '); - tplArr.push(' Add new ' + this.visitNoun.toLowerCase() + ''); - tplArr.push('
    '); + tplArr.push('
    '); tplArr = tplArr.concat(this.getTableHeaderRowTpl(columns)); // data rows From 20f6b5b5eab82a2a944b1312e1fd0bc7e0c85d9b Mon Sep 17 00:00:00 2001 From: Cory Nathe Date: Wed, 2 Aug 2017 13:10:50 +0000 Subject: [PATCH 148/218] UX Refresh: update for grid with in new UI on Manage Assay Schedule page - allow the Ext4 autoResize to handle the width resizing --- .../study/view/studydesign/manageAssaySchedule.jsp | 2 +- study/webapp/study/vaccineDesign/AssaySchedule.js | 13 ------------- 2 files changed, 1 insertion(+), 14 deletions(-) diff --git a/study/src/org/labkey/study/view/studydesign/manageAssaySchedule.jsp b/study/src/org/labkey/study/view/studydesign/manageAssaySchedule.jsp index fdb3fdfa..7ef27cf4 100644 --- a/study/src/org/labkey/study/view/studydesign/manageAssaySchedule.jsp +++ b/study/src/org/labkey/study/view/studydesign/manageAssaySchedule.jsp @@ -123,7 +123,7 @@ Enter assay schedule information in the grids below. -
    +
    • > Configure dropdown options for assays, labs, sample types, and units at the project diff --git a/study/webapp/study/vaccineDesign/AssaySchedule.js b/study/webapp/study/vaccineDesign/AssaySchedule.js index f51689ef..29f3e5ea 100644 --- a/study/webapp/study/vaccineDesign/AssaySchedule.js +++ b/study/webapp/study/vaccineDesign/AssaySchedule.js @@ -10,8 +10,6 @@ Ext4.define('LABKEY.VaccineDesign.AssaySchedulePanel', { bodyStyle : 'background-color: transparent;', - width: 750, - disableEdit : true, dirty : false, @@ -254,8 +252,6 @@ Ext4.define('LABKEY.VaccineDesign.AssaysGrid', { mainTitle : 'Assay Schedule', - width : 420, - studyDesignQueryNames : ['StudyDesignAssays', 'StudyDesignLabs', 'StudyDesignSampleTypes', 'StudyDesignUnits', 'Location'], visitNoun : 'Visit', @@ -500,15 +496,6 @@ Ext4.define('LABKEY.VaccineDesign.AssaysGrid', { var visitConfigs = this.getVisitColumnConfigs(); - // update the width based on the number of visit columns - var width = 450 + (Math.max(2, visitConfigs.length) * 75) + (this.useAlternateLookupFields ? 400 : 560); - this.setWidth(width); - - // update the outer panel width if necessary - var outerPanel = this.up('panel'); - if (outerPanel != null) - outerPanel.setWidth(Math.max(width, 750)); - this.columnConfigs = columnConfigs.concat(visitConfigs); } From 1e45e667944c142524824a5a471cb1704cb3916c Mon Sep 17 00:00:00 2001 From: Cory Nathe Date: Tue, 22 Aug 2017 16:38:19 +0000 Subject: [PATCH 149/218] UX Refresh: convert maange assay schedule and manage study products configure dropdown from Ext to bootstrap menu --- .../StudyDesignConfigureMenuItem.java | 22 ++++ .../view/studydesign/manageAssaySchedule.jsp | 92 ++++++---------- .../view/studydesign/manageStudyProducts.jsp | 102 +++++++----------- .../study/vaccineDesign/AssaySchedule.js | 2 + .../study/vaccineDesign/BaseDataView.js | 2 +- .../study/vaccineDesign/StudyProducts.js | 2 + .../vaccineDesign/TreatmentScheduleBase.js | 2 + .../webapp/study/vaccineDesign/VisitWindow.js | 3 +- 8 files changed, 100 insertions(+), 127 deletions(-) create mode 100644 study/src/org/labkey/study/view/studydesign/StudyDesignConfigureMenuItem.java diff --git a/study/src/org/labkey/study/view/studydesign/StudyDesignConfigureMenuItem.java b/study/src/org/labkey/study/view/studydesign/StudyDesignConfigureMenuItem.java new file mode 100644 index 00000000..52b79174 --- /dev/null +++ b/study/src/org/labkey/study/view/studydesign/StudyDesignConfigureMenuItem.java @@ -0,0 +1,22 @@ +package org.labkey.study.view.studydesign; + +import org.labkey.api.data.Container; +import org.labkey.api.query.QueryUrls; +import org.labkey.api.util.PageFlowUtil; +import org.labkey.api.view.ActionURL; +import org.labkey.api.view.NavTree; + +public class StudyDesignConfigureMenuItem extends NavTree +{ + public StudyDesignConfigureMenuItem(String text, String schemaName, String queryName, Container container) + { + super(text); + + ActionURL url = new ActionURL(); + url.setContainer(container); + url.addParameter("schemaName", schemaName); + url.addParameter("query.queryName", queryName); + setHref(PageFlowUtil.urlProvider(QueryUrls.class).urlExecuteQuery(url).toString()); + setTarget("_blank"); // issue 19493 + } +} diff --git a/study/src/org/labkey/study/view/studydesign/manageAssaySchedule.jsp b/study/src/org/labkey/study/view/studydesign/manageAssaySchedule.jsp index 7ef27cf4..c9d7e241 100644 --- a/study/src/org/labkey/study/view/studydesign/manageAssaySchedule.jsp +++ b/study/src/org/labkey/study/view/studydesign/manageAssaySchedule.jsp @@ -26,6 +26,9 @@ <%@ page import="org.labkey.api.util.PageFlowUtil" %> <%@ page import="org.labkey.api.portal.ProjectUrls" %> <%@ page import="org.labkey.study.controllers.StudyController" %> +<%@ page import="org.labkey.api.view.NavTree" %> +<%@ page import="org.labkey.api.view.PopupMenuView" %> +<%@ page import="org.labkey.study.view.studydesign.StudyDesignConfigureMenuItem" %> <%@ page extends="org.labkey.api.jsp.JspBase" %> <%! @Override @@ -62,63 +65,6 @@ useAlternateLookupFields : <%=form.isUseAlternateLookupFields()%>, returnURL : <%=q(returnUrl)%> }); - - var projectMenu = null; - if (LABKEY.container.type != "project") - { - var projectPath = LABKEY.container.path.substring(0, LABKEY.container.path.indexOf("/", 1)); - projectMenu = { - text: 'Project', - menu: { - items: [{ - text: 'Assays', - href: LABKEY.ActionURL.buildURL('query', 'executeQuery', projectPath, {schemaName: 'study', 'query.queryName': 'StudyDesignAssays'}), - hrefTarget: '_blank' // issue 19493 - },{ - text: 'Labs', - href: LABKEY.ActionURL.buildURL('query', 'executeQuery', projectPath, {schemaName: 'study', 'query.queryName': 'StudyDesignLabs'}), - hrefTarget: '_blank' // issue 19493 - },{ - text: 'Sample Types', - href: LABKEY.ActionURL.buildURL('query', 'executeQuery', projectPath, {schemaName: 'study', 'query.queryName': 'StudyDesignSampleTypes'}), - hrefTarget: '_blank' // issue 19493 - },{ - text: 'Units', - href: LABKEY.ActionURL.buildURL('query', 'executeQuery', projectPath, {schemaName: 'study', 'query.queryName': 'StudyDesignUnits'}), - hrefTarget: '_blank' // issue 19493 - }] - } - }; - } - - var folderMenu = { - text: 'Folder', - menu: { - items: [{ - text: 'Assays', - href: LABKEY.ActionURL.buildURL('query', 'executeQuery', null, {schemaName: 'study', 'query.queryName': 'StudyDesignAssays'}), - hrefTarget: '_blank' // issue 19493 - },{ - text: 'Labs', - href: LABKEY.ActionURL.buildURL('query', 'executeQuery', null, {schemaName: 'study', 'query.queryName': 'StudyDesignLabs'}), - hrefTarget: '_blank' // issue 19493 - },{ - text: 'Sample Types', - href: LABKEY.ActionURL.buildURL('query', 'executeQuery', null, {schemaName: 'study', 'query.queryName': 'StudyDesignSampleTypes'}), - hrefTarget: '_blank' // issue 19493 - },{ - text: 'Units', - href: LABKEY.ActionURL.buildURL('query', 'executeQuery', null, {schemaName: 'study', 'query.queryName': 'StudyDesignUnits'}), - hrefTarget: '_blank' // issue 19493 - }] - } - }; - - Ext4.create('Ext.button.Button', { - text: 'Configure', - renderTo: 'config-dropdown-menu', - menu: projectMenu ? {items: [projectMenu, folderMenu]} : folderMenu.menu - }); }); @@ -128,7 +74,37 @@ Enter assay schedule information in the grids below.
    • > Configure dropdown options for assays, labs, sample types, and units at the project level to be shared across study designs or within this folder for - study specific properties: + study specific properties: +
    • Select the <%=h(visitNoun.toLowerCase())%>s for each assay in the schedule diff --git a/study/src/org/labkey/study/view/studydesign/manageStudyProducts.jsp b/study/src/org/labkey/study/view/studydesign/manageStudyProducts.jsp index 4dc49c09..136032c5 100644 --- a/study/src/org/labkey/study/view/studydesign/manageStudyProducts.jsp +++ b/study/src/org/labkey/study/view/studydesign/manageStudyProducts.jsp @@ -26,6 +26,9 @@ <%@ page import="org.labkey.api.view.ActionURL" %> <%@ page import="org.labkey.api.study.StudyUrls" %> <%@ page import="org.labkey.study.controllers.StudyDesignController" %> +<%@ page import="org.labkey.api.view.NavTree" %> +<%@ page import="org.labkey.study.view.studydesign.StudyDesignConfigureMenuItem" %> +<%@ page import="org.labkey.api.view.PopupMenuView" %> <%@ page extends="org.labkey.api.jsp.JspBase" %> <%! @Override @@ -54,71 +57,6 @@ disableEdit : <%=isDataspaceProject%>, returnURL : <%=q(returnUrl)%> }); - - var projectMenu = null; - if (LABKEY.container.type != "project") - { - var projectPath = LABKEY.container.path.substring(0, LABKEY.container.path.indexOf("/", 1)); - projectMenu = { - text: 'Project', - menu: { - items: [{ - text: 'Challenge Types', - href: LABKEY.ActionURL.buildURL('query', 'executeQuery', projectPath, {schemaName: 'study', 'query.queryName': 'StudyDesignChallengeTypes'}), - hrefTarget: '_blank' // issue 19493 - },{ - text: 'Immunogen Types', - href: LABKEY.ActionURL.buildURL('query', 'executeQuery', projectPath, {schemaName: 'study', 'query.queryName': 'StudyDesignImmunogenTypes'}), - hrefTarget: '_blank' // issue 19493 - },{ - text: 'Genes', - href: LABKEY.ActionURL.buildURL('query', 'executeQuery', projectPath, {schemaName: 'study', 'query.queryName': 'StudyDesignGenes'}), - hrefTarget: '_blank' // issue 19493 - },{ - text: 'SubTypes', - href: LABKEY.ActionURL.buildURL('query', 'executeQuery', projectPath, {schemaName: 'study', 'query.queryName': 'StudyDesignSubTypes'}), - hrefTarget: '_blank' // issue 19493 - },{ - text: 'Routes', - href: LABKEY.ActionURL.buildURL('query', 'executeQuery', projectPath, {schemaName: 'study', 'query.queryName': 'StudyDesignRoutes'}), - hrefTarget: '_blank' // issue 19493 - }] - } - }; - } - - var folderMenu = { - text: 'Folder', - menu: { - items: [{ - text: 'Challenge Types', - href: LABKEY.ActionURL.buildURL('query', 'executeQuery', null, {schemaName: 'study', 'query.queryName': 'StudyDesignChallengeTypes'}), - hrefTarget: '_blank' // issue 19493 - },{ - text: 'Immunogen Types', - href: LABKEY.ActionURL.buildURL('query', 'executeQuery', null, {schemaName: 'study', 'query.queryName': 'StudyDesignImmunogenTypes'}), - hrefTarget: '_blank' // issue 19493 - },{ - text: 'Genes', - href: LABKEY.ActionURL.buildURL('query', 'executeQuery', null, {schemaName: 'study', 'query.queryName': 'StudyDesignGenes'}), - hrefTarget: '_blank' // issue 19493 - },{ - text: 'SubTypes', - href: LABKEY.ActionURL.buildURL('query', 'executeQuery', null, {schemaName: 'study', 'query.queryName': 'StudyDesignSubTypes'}), - hrefTarget: '_blank' // issue 19493 - },{ - text: 'Routes', - href: LABKEY.ActionURL.buildURL('query', 'executeQuery', null, {schemaName: 'study', 'query.queryName': 'StudyDesignRoutes'}), - hrefTarget: '_blank' // issue 19493 - }] - } - }; - - Ext4.create('Ext.button.Button', { - text: 'Configure', - renderTo: 'config-dropdown-menu', - menu: projectMenu ? {items: [projectMenu, folderMenu]} : folderMenu.menu - }); }); @@ -159,7 +97,39 @@ Enter vaccine design information in the grids below. %>
    • Configure dropdown options for challenge types, immunogen types, genes, subtypes and routes at the project level to be shared across study designs or within this folder for - study specific properties: + study specific properties: +
    diff --git a/study/webapp/study/vaccineDesign/AssaySchedule.js b/study/webapp/study/vaccineDesign/AssaySchedule.js index 29f3e5ea..800f15a3 100644 --- a/study/webapp/study/vaccineDesign/AssaySchedule.js +++ b/study/webapp/study/vaccineDesign/AssaySchedule.js @@ -6,6 +6,8 @@ Ext4.define('LABKEY.VaccineDesign.AssaySchedulePanel', { extend : 'Ext.panel.Panel', + autoResize : false, + border : false, bodyStyle : 'background-color: transparent;', diff --git a/study/webapp/study/vaccineDesign/BaseDataView.js b/study/webapp/study/vaccineDesign/BaseDataView.js index ba44e30b..d11f6a16 100644 --- a/study/webapp/study/vaccineDesign/BaseDataView.js +++ b/study/webapp/study/vaccineDesign/BaseDataView.js @@ -306,7 +306,7 @@ Ext4.define('LABKEY.VaccineDesign.BaseDataView', { tplArr.push('
    '); if (!this.disableEdit) - tplArr.push(''); + tplArr.push(''); Ext4.each(columns, function(column) { if (!column.hidden) diff --git a/study/webapp/study/vaccineDesign/StudyProducts.js b/study/webapp/study/vaccineDesign/StudyProducts.js index 456f3fb1..8c7be295 100644 --- a/study/webapp/study/vaccineDesign/StudyProducts.js +++ b/study/webapp/study/vaccineDesign/StudyProducts.js @@ -6,6 +6,8 @@ Ext4.define('LABKEY.VaccineDesign.StudyProductsPanel', { extend : 'Ext.panel.Panel', + autoResize : false, + border : false, bodyStyle : 'background-color: transparent;', diff --git a/study/webapp/study/vaccineDesign/TreatmentScheduleBase.js b/study/webapp/study/vaccineDesign/TreatmentScheduleBase.js index bfd2efbe..e84ec6ed 100644 --- a/study/webapp/study/vaccineDesign/TreatmentScheduleBase.js +++ b/study/webapp/study/vaccineDesign/TreatmentScheduleBase.js @@ -6,6 +6,8 @@ Ext4.define('LABKEY.VaccineDesign.TreatmentSchedulePanelBase', { extend : 'Ext.panel.Panel', + autoResize : false, + border : false, bodyStyle : 'background-color: transparent;', diff --git a/study/webapp/study/vaccineDesign/VisitWindow.js b/study/webapp/study/vaccineDesign/VisitWindow.js index 3b5b7114..08ce4016 100644 --- a/study/webapp/study/vaccineDesign/VisitWindow.js +++ b/study/webapp/study/vaccineDesign/VisitWindow.js @@ -132,14 +132,13 @@ Ext4.define('LABKEY.VaccineDesign.VisitWindow', { { var recData = Ext4.clone(record.data); recData['Label'] = recData['Label'] || recData['SequenceNumMin']; - data.push(recData); }, this); } // add an option to select all existing visits for display if (data.length > 1) - data.push(0, 0, {Label: '[Show All]', RowId: -1, DisplayOrder: -999999}); + data.push({Label: '[Show All]', RowId: -1, DisplayOrder: -999999}); this.filteredVisitStore = Ext4.create('Ext.data.Store', { model : 'LABKEY.VaccineDesign.Visit', From d2b229cd151d834a51d51ea39a8eb68e4c527fdd Mon Sep 17 00:00:00 2001 From: Cory Nathe Date: Fri, 25 Aug 2017 02:10:17 +0000 Subject: [PATCH 150/218] UX Refresh: updates to Manage assay schedule and immunization schedule related pages and webparts - fixes to fit with content width changes - automated test fix for removeFocusAndWait helper --- .../immunizationScheduleWebpart.jsp | 4 +- .../study/vaccineDesign/AssaySchedule.js | 87 +++++++++++++++---- .../study/vaccineDesign/StudyProducts.js | 27 ++++-- .../study/vaccineDesign/TreatmentDialog.js | 5 +- .../vaccineDesign/TreatmentScheduleBase.js | 1 - study/webapp/study/vaccineDesign/Utils.js | 12 +-- .../study/vaccineDesign/VaccineDesign.css | 13 --- 7 files changed, 104 insertions(+), 45 deletions(-) diff --git a/study/src/org/labkey/study/view/studydesign/immunizationScheduleWebpart.jsp b/study/src/org/labkey/study/view/studydesign/immunizationScheduleWebpart.jsp index 81a04c25..5315b9cd 100644 --- a/study/src/org/labkey/study/view/studydesign/immunizationScheduleWebpart.jsp +++ b/study/src/org/labkey/study/view/studydesign/immunizationScheduleWebpart.jsp @@ -89,7 +89,7 @@
    Immunization Schedule
    -
      
    +
    @@ -122,7 +122,7 @@ visitTreatments.put(treatmentVisitMap.getVisitId(), treatmentVisitMap.getTreatmentId()); } %> - " outer-index="<%=index-1%>"> + " outer-index="<%=index-1%>"> <% diff --git a/study/webapp/study/vaccineDesign/AssaySchedule.js b/study/webapp/study/vaccineDesign/AssaySchedule.js index 800f15a3..db46f284 100644 --- a/study/webapp/study/vaccineDesign/AssaySchedule.js +++ b/study/webapp/study/vaccineDesign/AssaySchedule.js @@ -6,7 +6,7 @@ Ext4.define('LABKEY.VaccineDesign.AssaySchedulePanel', { extend : 'Ext.panel.Panel', - autoResize : false, + width: 750, border : false, @@ -147,7 +147,6 @@ Ext4.define('LABKEY.VaccineDesign.AssaySchedulePanel', { if (!this.cancelButton) { this.cancelButton = Ext4.create('Ext.button.Button', { - margin: this.disableEdit ? 0 : '0 0 0 10px', text: this.disableEdit ? 'Done' : 'Cancel', handler: this.goToReturnURL, scope: this @@ -254,7 +253,9 @@ Ext4.define('LABKEY.VaccineDesign.AssaysGrid', { mainTitle : 'Assay Schedule', - studyDesignQueryNames : ['StudyDesignAssays', 'StudyDesignLabs', 'StudyDesignSampleTypes', 'StudyDesignUnits', 'Location'], + width : 620, + + studyDesignQueryNames : ['StudyDesignAssays', 'StudyDesignLabs', 'StudyDesignSampleTypes', 'StudyDesignUnits', 'Location', 'DataSets'], visitNoun : 'Visit', @@ -405,99 +406,155 @@ Ext4.define('LABKEY.VaccineDesign.AssaysGrid', { { if (!this.columnConfigs) { + var width = 0; // add to the running width as we go through which columns to show in the config + var columnConfigs = []; + var assayNameEditorConfig = LABKEY.VaccineDesign.Utils.getStudyDesignComboConfig('AssayName', 185, 'StudyDesignAssays'); assayNameEditorConfig.editable = true; // Rho use-case - var columnConfigs = [{ + columnConfigs.push({ label: 'Assay Name', width: 200, dataIndex: 'AssayName', required: true, editorType: 'LABKEY.ext4.ComboBox', editorConfig: assayNameEditorConfig - },{ + }); + width += 200; + + columnConfigs.push({ label: 'Dataset', width: 200, dataIndex: 'DataSet', + queryName: 'DataSets', editorType: 'LABKEY.ext4.ComboBox', editorConfig: LABKEY.VaccineDesign.Utils.getStudyDesignComboConfig('DataSet', 185, 'DataSets', undefined, 'Label', 'DataSetId') - },{ + }); + width += 200; + + var hidden = this.disableEdit && !this.columnHasData('Description'); + columnConfigs.push({ label: 'Description', width: 200, - hidden: this.disableEdit && !this.columnHasData('Description'), + hidden: hidden, dataIndex: 'Description', editorType: 'Ext.form.field.Text', editorConfig: LABKEY.VaccineDesign.Utils.getStudyDesignTextConfig('Description', 185) - }]; + }); + if (!hidden) { + width += 200; + } if (this.useAlternateLookupFields) { + hidden = this.disableEdit && !this.columnHasData('Source'); columnConfigs.push({ label: 'Source', width: 60, - hidden: this.disableEdit && !this.columnHasData('Source'), + hidden: hidden, dataIndex: 'Source', editorType: 'Ext.form.field.Text', editorConfig: LABKEY.VaccineDesign.Utils.getStudyDesignTextConfig('Source', 45) }); + if (!hidden) { + width += 60; + } + + hidden = this.disableEdit && !this.columnHasData('LocationId'); columnConfigs.push({ label: 'Location', width: 140, - hidden: this.disableEdit && !this.columnHasData('LocationId'), + hidden: hidden, dataIndex: 'LocationId', queryName: 'Location', editorType: 'LABKEY.ext4.ComboBox', editorConfig: LABKEY.VaccineDesign.Utils.getStudyDesignComboConfig('LocationId', 125, 'Location', undefined, 'Label', 'RowId') }); + if (!hidden) { + width += 140; + } + + hidden = this.disableEdit && !this.columnHasData('TubeType'); columnConfigs.push({ label: 'TubeType', width: 200, - hidden: this.disableEdit && !this.columnHasData('TubeType'), + hidden: hidden, dataIndex: 'TubeType', editorType: 'Ext.form.field.Text', editorConfig: LABKEY.VaccineDesign.Utils.getStudyDesignTextConfig('TubeType', 185) }); + if (!hidden) { + width += 200; + } } else { + hidden = this.disableEdit && !this.columnHasData('Lab'); columnConfigs.push({ label: 'Lab', width: 140, - hidden: this.disableEdit && !this.columnHasData('Lab'), + hidden: hidden, dataIndex: 'Lab', queryName: 'StudyDesignLabs', editorType: 'LABKEY.ext4.ComboBox', editorConfig: LABKEY.VaccineDesign.Utils.getStudyDesignComboConfig('Lab', 125, 'StudyDesignLabs') }); + if (!hidden) { + width += 140; + } + + hidden = this.disableEdit && !this.columnHasData('SampleType'); columnConfigs.push({ label: 'Sample Type', width: 140, - hidden: this.disableEdit && !this.columnHasData('SampleType'), + hidden: hidden, dataIndex: 'SampleType', editorType: 'LABKEY.ext4.ComboBox', editorConfig: LABKEY.VaccineDesign.Utils.getStudyDesignComboConfig('SampleType', 125, 'StudyDesignSampleTypes', undefined, 'Name') }); + if (!hidden) { + width += 140; + } + + hidden = this.disableEdit && !this.columnHasData('SampleQuantity'); columnConfigs.push({ label: 'Sample Quantity', width: 140, - hidden: this.disableEdit && !this.columnHasData('SampleQuantity'), + hidden: hidden, dataIndex: 'SampleQuantity', editorType: 'Ext.form.field.Number', editorConfig: LABKEY.VaccineDesign.Utils.getStudyDesignNumberConfig('SampleQuantity', 125, 2) }); + if (!hidden) { + width += 140; + } + + hidden = this.disableEdit && !this.columnHasData('SampleUnits'); columnConfigs.push({ label: 'Sample Units', width: 140, - hidden: this.disableEdit && !this.columnHasData('SampleUnits'), + hidden: hidden, dataIndex: 'SampleUnits', queryName: 'StudyDesignUnits', editorType: 'LABKEY.ext4.ComboBox', editorConfig: LABKEY.VaccineDesign.Utils.getStudyDesignComboConfig('SampleUnits', 125, 'StudyDesignUnits') }); + if (!hidden) { + width += 140; + } } var visitConfigs = this.getVisitColumnConfigs(); + // update the width based on the number of visit columns + width += (Math.max(2, visitConfigs.length) * 75); + this.setWidth(width); + + // update the outer panel width if necessary + var outerPanel = this.up('panel'); + if (outerPanel != null) + outerPanel.setWidth(Math.max(width, 750)); + this.columnConfigs = columnConfigs.concat(visitConfigs); } diff --git a/study/webapp/study/vaccineDesign/StudyProducts.js b/study/webapp/study/vaccineDesign/StudyProducts.js index 8c7be295..baeba0b1 100644 --- a/study/webapp/study/vaccineDesign/StudyProducts.js +++ b/study/webapp/study/vaccineDesign/StudyProducts.js @@ -12,7 +12,7 @@ Ext4.define('LABKEY.VaccineDesign.StudyProductsPanel', { bodyStyle : 'background-color: transparent;', - minWidth: 1500, + minWidth: 1350, disableEdit : true, @@ -125,7 +125,6 @@ Ext4.define('LABKEY.VaccineDesign.StudyProductsPanel', { if (!this.cancelButton) { this.cancelButton = Ext4.create('Ext.button.Button', { - margin: this.disableEdit ? 0 : '0 0 0 10px', text: this.disableEdit ? 'Done' : 'Cancel', handler: this.goToReturnURL, scope: this @@ -285,7 +284,7 @@ Ext4.define('LABKEY.VaccineDesign.ImmunogensGrid', { cls : 'study-vaccine-design vaccine-design-immunogens', - width: 1350, + autoResize: false, mainTitle : 'Immunogens', @@ -298,6 +297,8 @@ Ext4.define('LABKEY.VaccineDesign.ImmunogensGrid', { { if (!this.columnConfigs) { + var width = 0; // add to the running width as we go through which columns to show in the config + this.columnConfigs = [{ label: 'Label', width: 200, @@ -346,6 +347,7 @@ Ext4.define('LABKEY.VaccineDesign.ImmunogensGrid', { }] } }]; + width += 1000; if (this.showDoseRoute) { @@ -370,7 +372,10 @@ Ext4.define('LABKEY.VaccineDesign.ImmunogensGrid', { }] } }); + width += 315; } + + this.setWidth(width); } return this.columnConfigs; @@ -382,7 +387,7 @@ Ext4.define('LABKEY.VaccineDesign.AdjuvantsGrid', { cls : 'study-vaccine-design vaccine-design-adjuvants', - width : 530, + autoResize: false, mainTitle : 'Adjuvants', @@ -395,6 +400,8 @@ Ext4.define('LABKEY.VaccineDesign.AdjuvantsGrid', { { if (!this.columnConfigs) { + var width = 0; // add to the running width as we go through which columns to show in the config + this.columnConfigs = [{ label: 'Label', width: 200, @@ -403,6 +410,7 @@ Ext4.define('LABKEY.VaccineDesign.AdjuvantsGrid', { editorType: 'Ext.form.field.Text', editorConfig: LABKEY.VaccineDesign.Utils.getStudyDesignTextConfig('Label', 185) }]; + width += 200; if (this.showDoseRoute) { @@ -427,7 +435,10 @@ Ext4.define('LABKEY.VaccineDesign.AdjuvantsGrid', { }] } }); + width += 330; } + + this.setWidth(width); } return this.columnConfigs; @@ -439,7 +450,7 @@ Ext4.define('LABKEY.VaccineDesign.ChallengesGrid', { cls : 'study-vaccine-design vaccine-design-challenges', - width : 730, + autoResize: false, mainTitle : 'Challenges', @@ -452,6 +463,8 @@ Ext4.define('LABKEY.VaccineDesign.ChallengesGrid', { { if (!this.columnConfigs) { + var width = 0; // add to the running width as we go through which columns to show in the config + this.columnConfigs = [{ label: 'Label', width: 200, @@ -467,6 +480,7 @@ Ext4.define('LABKEY.VaccineDesign.ChallengesGrid', { editorType: 'LABKEY.ext4.ComboBox', editorConfig: LABKEY.VaccineDesign.Utils.getStudyDesignComboConfig('Type', 185, 'StudyDesignChallengeTypes') }]; + width += 400; if (this.showDoseRoute) { @@ -491,7 +505,10 @@ Ext4.define('LABKEY.VaccineDesign.ChallengesGrid', { }] } }); + width += 330; } + + this.setWidth(width); } return this.columnConfigs; diff --git a/study/webapp/study/vaccineDesign/TreatmentDialog.js b/study/webapp/study/vaccineDesign/TreatmentDialog.js index 0a866f10..0b4f63df 100644 --- a/study/webapp/study/vaccineDesign/TreatmentDialog.js +++ b/study/webapp/study/vaccineDesign/TreatmentDialog.js @@ -7,9 +7,7 @@ Ext4.define('LABKEY.VaccineDesign.TreatmentDialog', { extend: 'Ext.window.Window', - bodyStyle: 'overflow-y: auto; padding: 10px;', - - cls: 'treatment-dialog', + bodyStyle: 'overflow-y: auto; padding: 10px 0;', listeners: { resize: function (cmp) @@ -80,6 +78,7 @@ Ext4.define('LABKEY.VaccineDesign.TreatmentDialog', { var column = { xtype: 'checkboxgroup', fieldLabel: key, + labelStyle: 'font-weight: bold;', colspan: 1, columns: 1, flex: 0.75, diff --git a/study/webapp/study/vaccineDesign/TreatmentScheduleBase.js b/study/webapp/study/vaccineDesign/TreatmentScheduleBase.js index e84ec6ed..0ce2a9af 100644 --- a/study/webapp/study/vaccineDesign/TreatmentScheduleBase.js +++ b/study/webapp/study/vaccineDesign/TreatmentScheduleBase.js @@ -62,7 +62,6 @@ Ext4.define('LABKEY.VaccineDesign.TreatmentSchedulePanelBase', { if (!this.cancelButton) { this.cancelButton = Ext4.create('Ext.button.Button', { - margin: this.disableEdit ? 0 : '0 0 0 10px', text: this.disableEdit ? 'Done' : 'Cancel', handler: this.goToReturnURL, scope: this diff --git a/study/webapp/study/vaccineDesign/Utils.js b/study/webapp/study/vaccineDesign/Utils.js index 135f38cf..893e515e 100644 --- a/study/webapp/study/vaccineDesign/Utils.js +++ b/study/webapp/study/vaccineDesign/Utils.js @@ -96,7 +96,7 @@ Ext4.define('LABKEY.VaccineDesign.Utils', { else if (queryName == 'Product') columns += ',Role'; else if (queryName == 'DataSets') - columns += ',DatasetId'; + columns += ',DataSetId'; return Ext4.create('LABKEY.ext4.Store', { storeId: key, @@ -126,13 +126,13 @@ Ext4.define('LABKEY.VaccineDesign.Utils', { { var store = LABKEY.VaccineDesign.Utils.getStudyDesignStore(queryName), storeCols = Ext4.Array.pluck(store.getColumns(), 'dataIndex'), + keys = ['RowId', 'Name', 'DataSetId'], record = null; - if (storeCols.indexOf('RowId') > -1) - record = store.findRecord('RowId', value, 0, false, true, true); - - if (record == null && storeCols.indexOf('Name') > -1) - record = store.findRecord('Name', value, 0, false, true, true); + Ext4.each(keys, function(key) { + if (record == null && storeCols.indexOf(key) > -1) + record = store.findRecord(key, value, 0, false, true, true); + }); return record != null ? record.get("Label") : value; }, diff --git a/study/webapp/study/vaccineDesign/VaccineDesign.css b/study/webapp/study/vaccineDesign/VaccineDesign.css index 1eb073ce..cb12d822 100644 --- a/study/webapp/study/vaccineDesign/VaccineDesign.css +++ b/study/webapp/study/vaccineDesign/VaccineDesign.css @@ -74,19 +74,6 @@ width: calc(100% - 20px); white-space: nowrap; } -.treatment-dialog { - background-color: #C0C0C0; - border-color: dimgray; - box-shadow: #C0C0C0 0 1px 0 0 inset,#C0C0C0 0 -1px 0 0 inset,#C0C0C0 -1px 0 0 0 inset,#C0C0C0 1px 0 0 0 inset; -} -.treatment-dialog .x4-window-header-default{ - background-color: #C0C0C0; - border-color: dimgray; - box-shadow: #C0C0C0 0 1px 0 0 inset,#C0C0C0 0 -1px 0 0 inset,#C0C0C0 -1px 0 0 0 inset,#C0C0C0 1px 0 0 0 inset; -} -.treatment-dialog .x4-window-header-text-container-default { - color: black; -} .treatment-input-cell .x4-form-field { cursor: pointer; From 43f096d26d5fda9c414e39c70650da50f4cf098b Mon Sep 17 00:00:00 2001 From: Nick Kerr Date: Sat, 28 Oct 2017 03:51:07 +0000 Subject: [PATCH 151/218] UX Refresh: default Ext4 autoResize to false - remove usages that declared false - remove console warning message --- study/webapp/study/vaccineDesign/StudyProducts.js | 8 -------- study/webapp/study/vaccineDesign/TreatmentScheduleBase.js | 2 -- 2 files changed, 10 deletions(-) diff --git a/study/webapp/study/vaccineDesign/StudyProducts.js b/study/webapp/study/vaccineDesign/StudyProducts.js index baeba0b1..81d684cf 100644 --- a/study/webapp/study/vaccineDesign/StudyProducts.js +++ b/study/webapp/study/vaccineDesign/StudyProducts.js @@ -6,8 +6,6 @@ Ext4.define('LABKEY.VaccineDesign.StudyProductsPanel', { extend : 'Ext.panel.Panel', - autoResize : false, - border : false, bodyStyle : 'background-color: transparent;', @@ -284,8 +282,6 @@ Ext4.define('LABKEY.VaccineDesign.ImmunogensGrid', { cls : 'study-vaccine-design vaccine-design-immunogens', - autoResize: false, - mainTitle : 'Immunogens', filterRole : 'Immunogen', @@ -387,8 +383,6 @@ Ext4.define('LABKEY.VaccineDesign.AdjuvantsGrid', { cls : 'study-vaccine-design vaccine-design-adjuvants', - autoResize: false, - mainTitle : 'Adjuvants', filterRole : 'Adjuvant', @@ -450,8 +444,6 @@ Ext4.define('LABKEY.VaccineDesign.ChallengesGrid', { cls : 'study-vaccine-design vaccine-design-challenges', - autoResize: false, - mainTitle : 'Challenges', filterRole : 'Challenge', diff --git a/study/webapp/study/vaccineDesign/TreatmentScheduleBase.js b/study/webapp/study/vaccineDesign/TreatmentScheduleBase.js index 0ce2a9af..ebf9479f 100644 --- a/study/webapp/study/vaccineDesign/TreatmentScheduleBase.js +++ b/study/webapp/study/vaccineDesign/TreatmentScheduleBase.js @@ -6,8 +6,6 @@ Ext4.define('LABKEY.VaccineDesign.TreatmentSchedulePanelBase', { extend : 'Ext.panel.Panel', - autoResize : false, - border : false, bodyStyle : 'background-color: transparent;', From 6073f15981b5331665425432a66a4ccf5c869750 Mon Sep 17 00:00:00 2001 From: Ed Younskevicius Date: Wed, 1 Nov 2017 02:02:40 +0000 Subject: [PATCH 152/218] Update copyrights for 17.3 --- .../study/model/AssaySpecimenConfigImpl.java | 2 +- .../studydesign/StudyDesignConfigureMenuItem.java | 15 +++++++++++++++ .../studydesign/immunizationScheduleWebpart.jsp | 2 +- .../view/studydesign/manageAssaySchedule.jsp | 2 +- .../view/studydesign/manageStudyProducts.jsp | 2 +- study/webapp/study/vaccineDesign/AssaySchedule.js | 2 +- study/webapp/study/vaccineDesign/StudyProducts.js | 2 +- study/webapp/study/vaccineDesign/VisitWindow.js | 2 +- 8 files changed, 22 insertions(+), 7 deletions(-) diff --git a/study/src/org/labkey/study/model/AssaySpecimenConfigImpl.java b/study/src/org/labkey/study/model/AssaySpecimenConfigImpl.java index 8c517682..db8de471 100644 --- a/study/src/org/labkey/study/model/AssaySpecimenConfigImpl.java +++ b/study/src/org/labkey/study/model/AssaySpecimenConfigImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013-2016 LabKey Corporation + * Copyright (c) 2013-2017 LabKey Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/study/src/org/labkey/study/view/studydesign/StudyDesignConfigureMenuItem.java b/study/src/org/labkey/study/view/studydesign/StudyDesignConfigureMenuItem.java index 52b79174..2ec1a066 100644 --- a/study/src/org/labkey/study/view/studydesign/StudyDesignConfigureMenuItem.java +++ b/study/src/org/labkey/study/view/studydesign/StudyDesignConfigureMenuItem.java @@ -1,3 +1,18 @@ +/* + * Copyright (c) 2017 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.study.view.studydesign; import org.labkey.api.data.Container; diff --git a/study/src/org/labkey/study/view/studydesign/immunizationScheduleWebpart.jsp b/study/src/org/labkey/study/view/studydesign/immunizationScheduleWebpart.jsp index 5315b9cd..1580392f 100644 --- a/study/src/org/labkey/study/view/studydesign/immunizationScheduleWebpart.jsp +++ b/study/src/org/labkey/study/view/studydesign/immunizationScheduleWebpart.jsp @@ -1,6 +1,6 @@ <% /* - * Copyright (c) 2013-2016 LabKey Corporation + * Copyright (c) 2013-2017 LabKey Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/study/src/org/labkey/study/view/studydesign/manageAssaySchedule.jsp b/study/src/org/labkey/study/view/studydesign/manageAssaySchedule.jsp index c9d7e241..e7f964f0 100644 --- a/study/src/org/labkey/study/view/studydesign/manageAssaySchedule.jsp +++ b/study/src/org/labkey/study/view/studydesign/manageAssaySchedule.jsp @@ -1,6 +1,6 @@ <% /* - * Copyright (c) 2013-2016 LabKey Corporation + * Copyright (c) 2013-2017 LabKey Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/study/src/org/labkey/study/view/studydesign/manageStudyProducts.jsp b/study/src/org/labkey/study/view/studydesign/manageStudyProducts.jsp index 136032c5..fa5c17ff 100644 --- a/study/src/org/labkey/study/view/studydesign/manageStudyProducts.jsp +++ b/study/src/org/labkey/study/view/studydesign/manageStudyProducts.jsp @@ -1,6 +1,6 @@ <% /* - * Copyright (c) 2013-2016 LabKey Corporation + * Copyright (c) 2013-2017 LabKey Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/study/webapp/study/vaccineDesign/AssaySchedule.js b/study/webapp/study/vaccineDesign/AssaySchedule.js index db46f284..6fc52d21 100644 --- a/study/webapp/study/vaccineDesign/AssaySchedule.js +++ b/study/webapp/study/vaccineDesign/AssaySchedule.js @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016 LabKey Corporation + * Copyright (c) 2016-2017 LabKey Corporation * * Licensed under the Apache License, Version 2.0: http://www.apache.org/licenses/LICENSE-2.0 */ diff --git a/study/webapp/study/vaccineDesign/StudyProducts.js b/study/webapp/study/vaccineDesign/StudyProducts.js index 81d684cf..e02a2786 100644 --- a/study/webapp/study/vaccineDesign/StudyProducts.js +++ b/study/webapp/study/vaccineDesign/StudyProducts.js @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016 LabKey Corporation + * Copyright (c) 2016-2017 LabKey Corporation * * Licensed under the Apache License, Version 2.0: http://www.apache.org/licenses/LICENSE-2.0 */ diff --git a/study/webapp/study/vaccineDesign/VisitWindow.js b/study/webapp/study/vaccineDesign/VisitWindow.js index 08ce4016..31e99c6a 100644 --- a/study/webapp/study/vaccineDesign/VisitWindow.js +++ b/study/webapp/study/vaccineDesign/VisitWindow.js @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016 LabKey Corporation + * Copyright (c) 2016-2017 LabKey Corporation * * Licensed under the Apache License, Version 2.0: http://www.apache.org/licenses/LICENSE-2.0 */ From a279893d40c9b70e5dbe1f566fe6244dc72ff8e8 Mon Sep 17 00:00:00 2001 From: Adam Rauch Date: Sat, 17 Mar 2018 23:45:36 +0000 Subject: [PATCH 153/218] Use JdbcType.GUID consistently for EntityId handling in StorageProvisioner. Remove PropertyStorageSpec.isEntityId()/setEntityId(). Ensure that all EntityIds (except Audit "ProjectId") are declared as NOT NULL. --- .../study/query/studydesign/AbstractStudyDesignDomainKind.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/study/src/org/labkey/study/query/studydesign/AbstractStudyDesignDomainKind.java b/study/src/org/labkey/study/query/studydesign/AbstractStudyDesignDomainKind.java index 793a883e..caf207ca 100644 --- a/study/src/org/labkey/study/query/studydesign/AbstractStudyDesignDomainKind.java +++ b/study/src/org/labkey/study/query/studydesign/AbstractStudyDesignDomainKind.java @@ -63,7 +63,7 @@ public abstract class AbstractStudyDesignDomainKind extends AbstractDomainKind static { Set baseFields = new LinkedHashSet<>(); - baseFields.add(createFieldSpec("Container", JdbcType.VARCHAR).setEntityId(true).setNullable(false)); + baseFields.add(createFieldSpec("Container", JdbcType.GUID).setNullable(false)); baseFields.add(createFieldSpec("Created", JdbcType.TIMESTAMP)); baseFields.add(createFieldSpec("CreatedBy", JdbcType.INTEGER)); baseFields.add(createFieldSpec("Modified", JdbcType.TIMESTAMP)); From 13b4247f438eda02d6808693d18647ce49fb174a Mon Sep 17 00:00:00 2001 From: Adam Rauch Date: Thu, 12 Apr 2018 14:17:15 +0000 Subject: [PATCH 154/218] IntelliJ auto-refactor: remove unthrown exceptions --- .../study/query/studydesign/StudyDesignLookupBaseTable.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/study/src/org/labkey/study/query/studydesign/StudyDesignLookupBaseTable.java b/study/src/org/labkey/study/query/studydesign/StudyDesignLookupBaseTable.java index 663ece0c..99877f42 100644 --- a/study/src/org/labkey/study/query/studydesign/StudyDesignLookupBaseTable.java +++ b/study/src/org/labkey/study/query/studydesign/StudyDesignLookupBaseTable.java @@ -148,7 +148,7 @@ protected Map deleteRow(User user, Container container, Map row) throws ValidationException + private void validateValues(Map row) { // TODO: add validation that the same key value doesn't already exist at the project level } From cd0db9907ef2866170c6c8d4d210d671ca5d279e Mon Sep 17 00:00:00 2001 From: Adam Rauch Date: Thu, 12 Apr 2018 20:45:15 +0000 Subject: [PATCH 155/218] IntelliJ auto-refactor: remove unthrown exceptions in /modules, round #2 --- .../study/controllers/StudyDesignController.java | 16 ++++++++-------- .../org/labkey/study/model/TreatmentManager.java | 12 ++++++------ 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/study/src/org/labkey/study/controllers/StudyDesignController.java b/study/src/org/labkey/study/controllers/StudyDesignController.java index b7e9c83e..bed1b0db 100644 --- a/study/src/org/labkey/study/controllers/StudyDesignController.java +++ b/study/src/org/labkey/study/controllers/StudyDesignController.java @@ -91,7 +91,7 @@ public StudyDesignController() @RequiresPermission(UpdatePermission.class) public class ManageAssayScheduleAction extends SimpleViewAction { - public ModelAndView getView(AssayScheduleForm form, BindException errors) throws Exception + public ModelAndView getView(AssayScheduleForm form, BindException errors) { return new JspView<>("/org/labkey/study/view/studydesign/manageAssaySchedule.jsp", form); } @@ -123,7 +123,7 @@ public void setUseAlternateLookupFields(boolean useAlternateLookupFields) @RequiresPermission(UpdatePermission.class) public class ManageStudyProductsAction extends SimpleViewAction { - public ModelAndView getView(ReturnUrlForm form, BindException errors) throws Exception + public ModelAndView getView(ReturnUrlForm form, BindException errors) { return new JspView<>("/org/labkey/study/view/studydesign/manageStudyProducts.jsp", form); } @@ -155,7 +155,7 @@ public void setSingleTable(boolean singleTable) @RequiresPermission(UpdatePermission.class) public class ManageTreatmentsAction extends SimpleViewAction { - public ModelAndView getView(ManageTreatmentsBean form, BindException errors) throws Exception + public ModelAndView getView(ManageTreatmentsBean form, BindException errors) { // if the singleTable param is not explicitly set, do a container check if (getViewContext().getRequest().getParameter("singleTable") == null) @@ -187,7 +187,7 @@ public void validateForm(GetStudyProductsForm form, Errors errors) } @Override - public ApiResponse execute(GetStudyProductsForm form, BindException errors) throws Exception + public ApiResponse execute(GetStudyProductsForm form, BindException errors) { ApiSimpleResponse resp = new ApiSimpleResponse(); @@ -263,7 +263,7 @@ public void validateForm(GetStudyTreatmentsForm form, Errors errors) } @Override - public ApiResponse execute(GetStudyTreatmentsForm form, BindException errors) throws Exception + public ApiResponse execute(GetStudyTreatmentsForm form, BindException errors) { ApiSimpleResponse resp = new ApiSimpleResponse(); @@ -365,7 +365,7 @@ public void validateForm(Object form, Errors errors) } @Override - public ApiResponse execute(Object form, BindException errors) throws Exception + public ApiResponse execute(Object form, BindException errors) { ApiSimpleResponse resp = new ApiSimpleResponse(); StudyTreatmentSchedule treatmentSchedule = new StudyTreatmentSchedule(getContainer()); @@ -813,7 +813,7 @@ public void validateForm(VisitForm form, Errors errors) } @Override - public ApiResponse execute(VisitForm form, BindException errors) throws Exception + public ApiResponse execute(VisitForm form, BindException errors) { ApiSimpleResponse response = new ApiSimpleResponse(); @@ -835,7 +835,7 @@ public ApiResponse execute(VisitForm form, BindException errors) throws Exceptio public class UpdateAssayPlanAction extends MutatingApiAction { @Override - public ApiResponse execute(AssayPlanForm form, BindException errors) throws Exception + public ApiResponse execute(AssayPlanForm form, BindException errors) { ApiSimpleResponse response = new ApiSimpleResponse(); diff --git a/study/src/org/labkey/study/model/TreatmentManager.java b/study/src/org/labkey/study/model/TreatmentManager.java index 1924fe6f..c5254bbf 100644 --- a/study/src/org/labkey/study/model/TreatmentManager.java +++ b/study/src/org/labkey/study/model/TreatmentManager.java @@ -523,7 +523,7 @@ public void deleteTreatmentProductMap(Container container, User user, List Date: Fri, 13 Apr 2018 00:32:00 +0000 Subject: [PATCH 156/218] IntelliJ auto-refactor: remove unthrown exceptions in /modules, round #3 --- study/src/org/labkey/study/model/TreatmentManager.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/study/src/org/labkey/study/model/TreatmentManager.java b/study/src/org/labkey/study/model/TreatmentManager.java index c5254bbf..0d1f893c 100644 --- a/study/src/org/labkey/study/model/TreatmentManager.java +++ b/study/src/org/labkey/study/model/TreatmentManager.java @@ -746,7 +746,7 @@ private void populateTreatmentSchedule() throws ValidationException verifyTreatmentVisitMapRecords(_cohorts.size() * _visits.size() * _treatments.size()); } - private void populateTreatments() throws SQLException + private void populateTreatments() { TableInfo treatmentTable = _schema.getTable(StudyQuerySchema.TREATMENT_TABLE_NAME); if (treatmentTable != null) @@ -786,7 +786,7 @@ private void addProductsForTreatment(int treatmentId) verifyTreatmentProductMapRecords(treatmentId, _products.size()); } - private void populateStudyProducts() throws SQLException + private void populateStudyProducts() { TableInfo productTable = _schema.getTable(StudyQuerySchema.PRODUCT_TABLE_NAME); if (productTable != null) From e28f0873bcafc56d3877a44cc94cd594db13b20f Mon Sep 17 00:00:00 2001 From: Adam Rauch Date: Tue, 8 May 2018 21:30:44 +0000 Subject: [PATCH 157/218] Convert internal sequence number handling in VisitImpl from double to BigDecimal. Migrate some external usages. --- .../labkey/study/controllers/StudyDesignController.java | 3 +-- .../src/org/labkey/study/model/StudyTreatmentSchedule.java | 2 +- study/src/org/labkey/study/model/TreatmentManager.java | 7 ++++--- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/study/src/org/labkey/study/controllers/StudyDesignController.java b/study/src/org/labkey/study/controllers/StudyDesignController.java index bed1b0db..0c36a8cc 100644 --- a/study/src/org/labkey/study/controllers/StudyDesignController.java +++ b/study/src/org/labkey/study/controllers/StudyDesignController.java @@ -65,7 +65,6 @@ import org.springframework.web.servlet.ModelAndView; import java.util.ArrayList; -import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.List; @@ -822,7 +821,7 @@ public ApiResponse execute(VisitForm form, BindException errors) response.put("RowId", visit.getRowId()); response.put("Label", visit.getDisplayString()); - response.put("SequenceNumMin", visit.getSequenceNumMin()); + response.put("SequenceNumMin", visit.getSequenceNumMinDouble()); response.put("DisplayOrder", visit.getDisplayOrder()); response.put("Included", true); response.put("success", true); diff --git a/study/src/org/labkey/study/model/StudyTreatmentSchedule.java b/study/src/org/labkey/study/model/StudyTreatmentSchedule.java index 307e801f..87a49ea8 100644 --- a/study/src/org/labkey/study/model/StudyTreatmentSchedule.java +++ b/study/src/org/labkey/study/model/StudyTreatmentSchedule.java @@ -86,7 +86,7 @@ public List> serializeVisits() visitProperties.put("RowId", v.getRowId()); visitProperties.put("Label", v.getDisplayString()); visitProperties.put("DisplayOrder", v.getDisplayOrder()); - visitProperties.put("SequenceNumMin", v.getSequenceNumMin()); + visitProperties.put("SequenceNumMin", v.getSequenceNumMinDouble()); // tag those visits that are used in the treatment schedule visitProperties.put("Included", includedIds.contains(v.getRowId())); diff --git a/study/src/org/labkey/study/model/TreatmentManager.java b/study/src/org/labkey/study/model/TreatmentManager.java index 0d1f893c..caf48c40 100644 --- a/study/src/org/labkey/study/model/TreatmentManager.java +++ b/study/src/org/labkey/study/model/TreatmentManager.java @@ -47,6 +47,7 @@ import org.labkey.study.StudySchema; import org.labkey.study.query.StudyQuerySchema; +import java.math.BigDecimal; import java.sql.SQLException; import java.util.ArrayList; import java.util.Collection; @@ -681,7 +682,7 @@ private void verifyTreatmentSchedule() { verifyTreatmentVisitMapRecords(8); - _visits.add(StudyManager.getInstance().createVisit(_junitStudy, _user, new VisitImpl(_container, 3.0, "Visit 3", Visit.Type.FINAL_VISIT))); + _visits.add(StudyManager.getInstance().createVisit(_junitStudy, _user, new VisitImpl(_container, BigDecimal.valueOf(3.0), "Visit 3", Visit.Type.FINAL_VISIT))); assertEquals("Unexpected number of treatment schedule visits", 2, _manager.getVisitsForTreatmentSchedule(_container).size()); } @@ -728,8 +729,8 @@ private void populateTreatmentSchedule() throws ValidationException _cohorts.add(CohortManager.getInstance().createCohort(_junitStudy, _user, "Cohort2", true, 20, null)); assertEquals(_cohorts.size(), 2); - _visits.add(StudyManager.getInstance().createVisit(_junitStudy, _user, new VisitImpl(_container, 1.0, "Visit 1", Visit.Type.BASELINE))); - _visits.add(StudyManager.getInstance().createVisit(_junitStudy, _user, new VisitImpl(_container, 2.0, "Visit 2", Visit.Type.SCHEDULED_FOLLOWUP))); + _visits.add(StudyManager.getInstance().createVisit(_junitStudy, _user, new VisitImpl(_container, BigDecimal.valueOf(1.0), "Visit 1", Visit.Type.BASELINE))); + _visits.add(StudyManager.getInstance().createVisit(_junitStudy, _user, new VisitImpl(_container, BigDecimal.valueOf(2.0), "Visit 2", Visit.Type.SCHEDULED_FOLLOWUP))); assertEquals(_visits.size(), 2); for (CohortImpl cohort : _cohorts) From e9c3da7d7c692d5c48f2385426af9401d75ef3ad Mon Sep 17 00:00:00 2001 From: Adam Rauch Date: Tue, 19 Jun 2018 02:38:30 +0000 Subject: [PATCH 158/218] Simplify some more this._member references to _member --- study/src/org/labkey/study/model/TreatmentProductImpl.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/study/src/org/labkey/study/model/TreatmentProductImpl.java b/study/src/org/labkey/study/model/TreatmentProductImpl.java index 94766c2f..dd5339f3 100644 --- a/study/src/org/labkey/study/model/TreatmentProductImpl.java +++ b/study/src/org/labkey/study/model/TreatmentProductImpl.java @@ -206,7 +206,7 @@ public static TreatmentProductImpl fromJSON(@NotNull JSONObject o, Container con public void setProductDoseRoute(String productDoseRoute) { - this._productDoseRoute = productDoseRoute; + _productDoseRoute = productDoseRoute; } public String getProductDoseRoute() From b3eec31260812635be1883c1bec7729d616163b0 Mon Sep 17 00:00:00 2001 From: Nick Kerr Date: Mon, 20 Aug 2018 19:08:06 +0000 Subject: [PATCH 159/218] Remove WebTheme - and associated artifacts --- .../study/view/studydesign/assayScheduleWebpart.jsp | 8 -------- .../view/studydesign/immunizationScheduleWebpart.jsp | 4 ---- .../study/view/studydesign/vaccineDesignWebpart.jsp | 9 --------- 3 files changed, 21 deletions(-) diff --git a/study/src/org/labkey/study/view/studydesign/assayScheduleWebpart.jsp b/study/src/org/labkey/study/view/studydesign/assayScheduleWebpart.jsp index 221cee36..c3ec6460 100644 --- a/study/src/org/labkey/study/view/studydesign/assayScheduleWebpart.jsp +++ b/study/src/org/labkey/study/view/studydesign/assayScheduleWebpart.jsp @@ -19,7 +19,6 @@ <%@ page import="org.labkey.api.data.Container" %> <%@ page import="org.labkey.api.security.User" %> <%@ page import="org.labkey.api.security.permissions.UpdatePermission" %> -<%@ page import="org.labkey.api.view.WebThemeManager" %> <%@ page import="org.labkey.api.view.template.ClientDependencies" %> <%@ page import="org.labkey.study.controllers.StudyDesignController" %> <%@ page import="org.labkey.study.model.StudyImpl" %> @@ -48,13 +47,6 @@ if (study != null && study.getAssayPlan() != null) assayPlan = study.getAssayPlan(); %> - - - <% if (study != null) { diff --git a/study/src/org/labkey/study/view/studydesign/immunizationScheduleWebpart.jsp b/study/src/org/labkey/study/view/studydesign/immunizationScheduleWebpart.jsp index 1580392f..67ddcb2b 100644 --- a/study/src/org/labkey/study/view/studydesign/immunizationScheduleWebpart.jsp +++ b/study/src/org/labkey/study/view/studydesign/immunizationScheduleWebpart.jsp @@ -19,7 +19,6 @@ <%@ page import="org.labkey.api.security.User" %> <%@ page import="org.labkey.api.security.permissions.UpdatePermission" %> <%@ page import="org.labkey.api.util.PageFlowUtil" %> -<%@ page import="org.labkey.api.view.WebThemeManager" %> <%@ page import="org.labkey.api.view.template.ClientDependencies" %> <%@ page import="org.labkey.study.model.CohortImpl" %> <%@ page import="org.labkey.study.model.ProductImpl" %> @@ -55,9 +54,6 @@ %> -<% if (study != null) { %>This section describes the study products evaluated in the study.
    <% From 00c6efab12a0292e186d010c70804b586fe6818e Mon Sep 17 00:00:00 2001 From: Ed Younskevicius Date: Tue, 13 Nov 2018 00:57:49 +0000 Subject: [PATCH 160/218] Copyright and license updates -- Update for November 2018 -- Now scanning .tf and .tfvars files (Terraform) -- Deliberately leaving out SNPRC module (moving to Git very soon) --- .../src/org/labkey/study/controllers/StudyDesignController.java | 2 +- study/src/org/labkey/study/model/StudyTreatmentSchedule.java | 2 +- study/src/org/labkey/study/model/TreatmentManager.java | 2 +- study/src/org/labkey/study/model/TreatmentProductImpl.java | 2 +- .../study/query/studydesign/AbstractStudyDesignDomainKind.java | 2 +- .../study/query/studydesign/StudyDesignLookupBaseTable.java | 2 +- .../org/labkey/study/view/studydesign/assayScheduleWebpart.jsp | 2 +- .../study/view/studydesign/immunizationScheduleWebpart.jsp | 2 +- .../org/labkey/study/view/studydesign/vaccineDesignWebpart.jsp | 2 +- 9 files changed, 9 insertions(+), 9 deletions(-) diff --git a/study/src/org/labkey/study/controllers/StudyDesignController.java b/study/src/org/labkey/study/controllers/StudyDesignController.java index 0c36a8cc..05bdbf8b 100644 --- a/study/src/org/labkey/study/controllers/StudyDesignController.java +++ b/study/src/org/labkey/study/controllers/StudyDesignController.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2016 LabKey Corporation + * Copyright (c) 2014-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. diff --git a/study/src/org/labkey/study/model/StudyTreatmentSchedule.java b/study/src/org/labkey/study/model/StudyTreatmentSchedule.java index 87a49ea8..896b5bd1 100644 --- a/study/src/org/labkey/study/model/StudyTreatmentSchedule.java +++ b/study/src/org/labkey/study/model/StudyTreatmentSchedule.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2017 LabKey Corporation + * Copyright (c) 2014-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. diff --git a/study/src/org/labkey/study/model/TreatmentManager.java b/study/src/org/labkey/study/model/TreatmentManager.java index caf48c40..142a0755 100644 --- a/study/src/org/labkey/study/model/TreatmentManager.java +++ b/study/src/org/labkey/study/model/TreatmentManager.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2017 LabKey Corporation + * Copyright (c) 2014-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. diff --git a/study/src/org/labkey/study/model/TreatmentProductImpl.java b/study/src/org/labkey/study/model/TreatmentProductImpl.java index dd5339f3..71b6e1dd 100644 --- a/study/src/org/labkey/study/model/TreatmentProductImpl.java +++ b/study/src/org/labkey/study/model/TreatmentProductImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013-2016 LabKey Corporation + * Copyright (c) 2013-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. diff --git a/study/src/org/labkey/study/query/studydesign/AbstractStudyDesignDomainKind.java b/study/src/org/labkey/study/query/studydesign/AbstractStudyDesignDomainKind.java index caf207ca..7215ac42 100644 --- a/study/src/org/labkey/study/query/studydesign/AbstractStudyDesignDomainKind.java +++ b/study/src/org/labkey/study/query/studydesign/AbstractStudyDesignDomainKind.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013-2016 LabKey Corporation + * Copyright (c) 2013-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. diff --git a/study/src/org/labkey/study/query/studydesign/StudyDesignLookupBaseTable.java b/study/src/org/labkey/study/query/studydesign/StudyDesignLookupBaseTable.java index 99877f42..0dd90e13 100644 --- a/study/src/org/labkey/study/query/studydesign/StudyDesignLookupBaseTable.java +++ b/study/src/org/labkey/study/query/studydesign/StudyDesignLookupBaseTable.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013-2017 LabKey Corporation + * Copyright (c) 2013-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. diff --git a/study/src/org/labkey/study/view/studydesign/assayScheduleWebpart.jsp b/study/src/org/labkey/study/view/studydesign/assayScheduleWebpart.jsp index c3ec6460..d6c4fdcd 100644 --- a/study/src/org/labkey/study/view/studydesign/assayScheduleWebpart.jsp +++ b/study/src/org/labkey/study/view/studydesign/assayScheduleWebpart.jsp @@ -1,6 +1,6 @@ <% /* - * Copyright (c) 2013-2016 LabKey Corporation + * Copyright (c) 2013-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. diff --git a/study/src/org/labkey/study/view/studydesign/immunizationScheduleWebpart.jsp b/study/src/org/labkey/study/view/studydesign/immunizationScheduleWebpart.jsp index 67ddcb2b..8d03057c 100644 --- a/study/src/org/labkey/study/view/studydesign/immunizationScheduleWebpart.jsp +++ b/study/src/org/labkey/study/view/studydesign/immunizationScheduleWebpart.jsp @@ -1,6 +1,6 @@ <% /* - * Copyright (c) 2013-2017 LabKey Corporation + * Copyright (c) 2013-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. diff --git a/study/src/org/labkey/study/view/studydesign/vaccineDesignWebpart.jsp b/study/src/org/labkey/study/view/studydesign/vaccineDesignWebpart.jsp index 9ddd1172..e08e151c 100644 --- a/study/src/org/labkey/study/view/studydesign/vaccineDesignWebpart.jsp +++ b/study/src/org/labkey/study/view/studydesign/vaccineDesignWebpart.jsp @@ -1,6 +1,6 @@ <% /* - * Copyright (c) 2013-2016 LabKey Corporation + * Copyright (c) 2013-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. From 6aefdd7b28f21550d505a4f3d8167c1f507c0c2c Mon Sep 17 00:00:00 2001 From: Adam Rauch Date: Mon, 11 Feb 2019 19:59:12 +0000 Subject: [PATCH 161/218] Issue 30597: Wiki copySinglePage creates a new "Custom" folder instead of Collaboration type Migrate webpart & portal page actions Suppress ensure domain in study design domain kind --- .../query/studydesign/AbstractStudyDesignDomainKind.java | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/study/src/org/labkey/study/query/studydesign/AbstractStudyDesignDomainKind.java b/study/src/org/labkey/study/query/studydesign/AbstractStudyDesignDomainKind.java index 7215ac42..ef0a9d84 100644 --- a/study/src/org/labkey/study/query/studydesign/AbstractStudyDesignDomainKind.java +++ b/study/src/org/labkey/study/query/studydesign/AbstractStudyDesignDomainKind.java @@ -15,6 +15,7 @@ */ package org.labkey.study.query.studydesign; +import org.labkey.api.action.SpringActionController; import org.labkey.api.data.Container; import org.labkey.api.data.DbSchemaType; import org.labkey.api.data.DbScope; @@ -94,7 +95,11 @@ public Domain ensureDomain(Container container, User user, String tableName) try { domain = PropertyService.get().createDomain(container, domainURI, tableName); - domain.save(user); + + try (var ignored = SpringActionController.ignoreSqlUpdates()) + { + domain.save(user); + } } catch (Exception e) { From b9feeb99c7744a2e6f683fe48fbf78683abda491 Mon Sep 17 00:00:00 2001 From: Xing Yang Date: Wed, 20 Feb 2019 01:29:22 +0000 Subject: [PATCH 162/218] Issue 36779: VISCStudies module - Immunization Schedule labkey-help-pop-up caches old Immunogen data after row has been updated --- .../study/view/studydesign/manageTreatments.jsp | 2 +- .../TreatmentScheduleSingleTablePanel.js | 13 ++++++++----- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/study/src/org/labkey/study/view/studydesign/manageTreatments.jsp b/study/src/org/labkey/study/view/studydesign/manageTreatments.jsp index 7ed8e32e..6a1fe175 100644 --- a/study/src/org/labkey/study/view/studydesign/manageTreatments.jsp +++ b/study/src/org/labkey/study/view/studydesign/manageTreatments.jsp @@ -135,7 +135,7 @@ Enter treatment information in the grids below. <%=textLink("Manage Study Products", manageStudyProductsURL)%>
  • - Each cohort label must be unique. Enter the number of <%=study.getSubjectNounPlural().toLowerCase()%> for + Each cohort label must be unique. Enter the number of <%=h(study.getSubjectNounPlural().toLowerCase())%> for the cohort in the count column.
  • Use the manage cohorts page to further configuration information about the cohorts for this study. diff --git a/study/webapp/study/vaccineDesign/TreatmentScheduleSingleTablePanel.js b/study/webapp/study/vaccineDesign/TreatmentScheduleSingleTablePanel.js index 60b888b5..25ae3532 100644 --- a/study/webapp/study/vaccineDesign/TreatmentScheduleSingleTablePanel.js +++ b/study/webapp/study/vaccineDesign/TreatmentScheduleSingleTablePanel.js @@ -1,8 +1,8 @@ -/* - * Copyright (c) 2016 LabKey Corporation - * - * Licensed under the Apache License, Version 2.0: http://www.apache.org/licenses/LICENSE-2.0 - */ +/* + * Copyright (c) 2016 LabKey Corporation + * + * Licensed under the Apache License, Version 2.0: http://www.apache.org/licenses/LICENSE-2.0 + */ Ext4.define('LABKEY.VaccineDesign.TreatmentScheduleSingleTablePanel', { extend : 'LABKEY.VaccineDesign.TreatmentSchedulePanelBase', @@ -155,6 +155,9 @@ Ext4.define('LABKEY.VaccineDesign.TreatmentScheduleSingleTableGrid', { if (resp.success) { win.close(); me.fireEvent('celledited'); + var changed = cmp.treatmentId != resp.treatmentIds[0]; + if (changed) + cmp.setValue(''); // force a change event, in case label is same, but id changed cmp.treatmentId = resp.treatmentIds[0]; cmp.setValue(treatments[0].Label); cmp.getEl().dom.title = treatments[0].Label; From 0ca8c0d4228766bd68177d33f9b00e1c714453c9 Mon Sep 17 00:00:00 2001 From: Binal Patel Date: Wed, 20 Feb 2019 22:52:22 +0000 Subject: [PATCH 163/218] Item 5181 : ApiAction review and migration - modules folder Study module - updated 21 ApiActions, and updated caller to use POST for a MutatingAPIAction. --- .../labkey/study/controllers/StudyDesignController.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/study/src/org/labkey/study/controllers/StudyDesignController.java b/study/src/org/labkey/study/controllers/StudyDesignController.java index 05bdbf8b..8379ed18 100644 --- a/study/src/org/labkey/study/controllers/StudyDesignController.java +++ b/study/src/org/labkey/study/controllers/StudyDesignController.java @@ -17,11 +17,11 @@ import org.apache.commons.lang3.StringUtils; import org.json.JSONArray; -import org.labkey.api.action.ApiAction; import org.labkey.api.action.ApiResponse; import org.labkey.api.action.ApiSimpleResponse; import org.labkey.api.action.CustomApiForm; import org.labkey.api.action.MutatingApiAction; +import org.labkey.api.action.ReadOnlyApiAction; import org.labkey.api.action.ReturnUrlForm; import org.labkey.api.action.SimpleViewAction; import org.labkey.api.data.Container; @@ -173,7 +173,7 @@ public NavTree appendNavTrail(NavTree root) } @RequiresPermission(ReadPermission.class) - public class GetStudyProducts extends ApiAction + public class GetStudyProducts extends ReadOnlyApiAction { private StudyImpl _study; @@ -249,7 +249,7 @@ public void setRole(String role) } @RequiresPermission(ReadPermission.class) - public class GetStudyTreatments extends ApiAction + public class GetStudyTreatments extends ReadOnlyApiAction { private StudyImpl _study; @@ -351,7 +351,7 @@ public void setTreatmentId(int treatmentId) } @RequiresPermission(ReadPermission.class) - public class GetStudyTreatmentSchedule extends ApiAction + public class GetStudyTreatmentSchedule extends ReadOnlyApiAction { private StudyImpl _study; From 9e581ff3d26d8aa99fb1b5e86d31bf3d3ab4173e Mon Sep 17 00:00:00 2001 From: Adam Rauch Date: Thu, 21 Feb 2019 21:03:49 +0000 Subject: [PATCH 164/218] Never any reason to call super() --- .../src/org/labkey/study/controllers/StudyDesignController.java | 1 - 1 file changed, 1 deletion(-) diff --git a/study/src/org/labkey/study/controllers/StudyDesignController.java b/study/src/org/labkey/study/controllers/StudyDesignController.java index 8379ed18..aa0fe4d4 100644 --- a/study/src/org/labkey/study/controllers/StudyDesignController.java +++ b/study/src/org/labkey/study/controllers/StudyDesignController.java @@ -82,7 +82,6 @@ public class StudyDesignController extends BaseStudyController public StudyDesignController() { - super(); setActionResolver(ACTION_RESOLVER); } From f89a941e53be440c8a7fbba8d762079aea18227c Mon Sep 17 00:00:00 2001 From: labkey-matthewb Date: Wed, 15 May 2019 14:29:05 -0700 Subject: [PATCH 165/218] Immutable TableInfo Original PR: https://github.com/LabKey/platform/pull/89 --- .../studydesign/DefaultStudyDesignTable.java | 19 ++++++++++--------- .../StudyDesignLookupBaseTable.java | 16 +++++----------- .../studydesign/StudyProductAntigenTable.java | 5 ++--- .../query/studydesign/StudyProductTable.java | 4 ++-- .../StudyTreatmentProductTable.java | 5 ++--- .../studydesign/StudyTreatmentTable.java | 4 ++-- .../StudyTreatmentVisitMapTable.java | 18 +++++++++--------- 7 files changed, 32 insertions(+), 39 deletions(-) diff --git a/study/src/org/labkey/study/query/studydesign/DefaultStudyDesignTable.java b/study/src/org/labkey/study/query/studydesign/DefaultStudyDesignTable.java index 662c8c60..5404d75d 100644 --- a/study/src/org/labkey/study/query/studydesign/DefaultStudyDesignTable.java +++ b/study/src/org/labkey/study/query/studydesign/DefaultStudyDesignTable.java @@ -17,6 +17,7 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import org.labkey.api.data.BaseColumnInfo; import org.labkey.api.data.ColumnInfo; import org.labkey.api.data.Container; import org.labkey.api.data.ContainerFilter; @@ -62,7 +63,7 @@ protected DefaultStudyDesignTable(Domain domain, TableInfo storageTableInfo, Us for (ColumnInfo baseColumn : getRealTable().getColumns()) { - ColumnInfo col = addWrapColumn(baseColumn); + var col = addWrapColumn(baseColumn); if (baseColumn.isHidden()) col.setHidden(true); @@ -77,7 +78,7 @@ protected DefaultStudyDesignTable(Domain domain, TableInfo storageTableInfo, Us if (pd != null) { if (pd.getLookupQuery() != null || pd.getConceptURI() != null) - col.setFk(new PdLookupForeignKey(schema.getUser(), pd, schema.getContainer())); + col.setFk(PdLookupForeignKey.create(schema, pd)); if (pd.getPropertyType() == PropertyType.MULTI_LINE) { @@ -97,20 +98,20 @@ protected DefaultStudyDesignTable(Domain domain, TableInfo storageTableInfo, Us _defaultVisibleColumns.add(FieldKey.fromParts("CreatedBy")); // setup lookups for the standard fields - ColumnInfo container = getColumn("Container"); + var container = getMutableColumn("Container"); ContainerForeignKey.initColumn(container, schema); - ColumnInfo created = getColumn("Created"); + var created = getMutableColumn("Created"); created.setFormat("DateTime"); - ColumnInfo createdBy = getColumn(FieldKey.fromParts("CreatedBy")); + var createdBy = getMutableColumn(FieldKey.fromParts("CreatedBy")); createdBy.setLabel("Created By"); UserIdForeignKey.initColumn(createdBy); - ColumnInfo modified = getColumn("Modified"); + var modified = getMutableColumn("Modified"); modified.setFormat("DateTime"); - ColumnInfo modifiedBy = getColumn(FieldKey.fromParts("ModifiedBy")); + var modifiedBy = getMutableColumn(FieldKey.fromParts("ModifiedBy")); modifiedBy.setLabel("Modified By"); UserIdForeignKey.initColumn(modifiedBy); @@ -143,12 +144,12 @@ public List getDefaultVisibleColumns() protected void initColumns() { - for (ColumnInfo col : getColumns()) + for (var col : getMutableColumns()) initColumn(col); } // Subclasses may override this to provide customizations to the column - protected void initColumn(ColumnInfo col) + protected void initColumn(BaseColumnInfo col) { } diff --git a/study/src/org/labkey/study/query/studydesign/StudyDesignLookupBaseTable.java b/study/src/org/labkey/study/query/studydesign/StudyDesignLookupBaseTable.java index 0dd90e13..71b12c5e 100644 --- a/study/src/org/labkey/study/query/studydesign/StudyDesignLookupBaseTable.java +++ b/study/src/org/labkey/study/query/studydesign/StudyDesignLookupBaseTable.java @@ -51,21 +51,22 @@ */ public class StudyDesignLookupBaseTable extends BaseStudyTable { - public StudyDesignLookupBaseTable(StudyQuerySchema schema, TableInfo tableInfo) + // Note: this has a different default container filter than BaseStudyTable + public StudyDesignLookupBaseTable(StudyQuerySchema schema, TableInfo tableInfo, ContainerFilter cf) { - super(schema, tableInfo); + super(schema, tableInfo, schema.isDataspaceProject() ? new ContainerFilter.Project(schema.getUser()) : cf); setDescription("Contains lookup values for dropdown options in the study designer."); for (ColumnInfo col : getRealTable().getColumns()) { if (!col.getName().equalsIgnoreCase("Container")) { - ColumnInfo newCol = addWrapColumn(col); + var newCol = addWrapColumn(col); if (col.isHidden()) newCol.setHidden(col.isHidden()); if (newCol.getName().equalsIgnoreCase("CreatedBy") || newCol.getName().equalsIgnoreCase("ModifiedBy")) - UserIdQueryForeignKey.initColumn(schema.getUser(), schema.getContainer(), newCol, true); + UserIdQueryForeignKey.initColumn(schema, newCol, true); } else addContainerColumn(); @@ -80,13 +81,6 @@ public StudyDesignLookupBaseTable(StudyQuerySchema schema, TableInfo tableInfo) setDefaultVisibleColumns(defaultColumns); } - public StudyDesignLookupBaseTable(StudyQuerySchema schema, TableInfo tableInfo, @Nullable ContainerFilter containerFilter) - { - this(schema, tableInfo); - if (null != containerFilter) - _setContainerFilter(containerFilter); - } - @Override public QueryUpdateService getUpdateService() { diff --git a/study/src/org/labkey/study/query/studydesign/StudyProductAntigenTable.java b/study/src/org/labkey/study/query/studydesign/StudyProductAntigenTable.java index 466cca1c..59ca3812 100644 --- a/study/src/org/labkey/study/query/studydesign/StudyProductAntigenTable.java +++ b/study/src/org/labkey/study/query/studydesign/StudyProductAntigenTable.java @@ -17,9 +17,8 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import org.labkey.api.data.ColumnInfo; +import org.labkey.api.data.BaseColumnInfo; import org.labkey.api.data.ContainerFilter; -import org.labkey.api.data.DbSchema; import org.labkey.api.data.TableInfo; import org.labkey.api.exp.api.StorageProvisioner; import org.labkey.api.exp.property.Domain; @@ -73,7 +72,7 @@ private StudyProductAntigenTable(Domain domain, TableInfo storageTableInfo, User @Override - protected void initColumn(ColumnInfo col) + protected void initColumn(BaseColumnInfo col) { if ("ProductId".equalsIgnoreCase(col.getName())) { diff --git a/study/src/org/labkey/study/query/studydesign/StudyProductTable.java b/study/src/org/labkey/study/query/studydesign/StudyProductTable.java index 046cb8dc..8b83cea4 100644 --- a/study/src/org/labkey/study/query/studydesign/StudyProductTable.java +++ b/study/src/org/labkey/study/query/studydesign/StudyProductTable.java @@ -17,7 +17,7 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import org.labkey.api.data.ColumnInfo; +import org.labkey.api.data.BaseColumnInfo; import org.labkey.api.data.ContainerFilter; import org.labkey.api.data.TableInfo; import org.labkey.api.exp.api.StorageProvisioner; @@ -67,7 +67,7 @@ private StudyProductTable(Domain domain, TableInfo storageTableInfo, UserSchema } @Override - protected void initColumn(ColumnInfo col) + protected void initColumn(BaseColumnInfo col) { if ("Type".equalsIgnoreCase(col.getName())) { diff --git a/study/src/org/labkey/study/query/studydesign/StudyTreatmentProductTable.java b/study/src/org/labkey/study/query/studydesign/StudyTreatmentProductTable.java index c54d26ef..6bd28bff 100644 --- a/study/src/org/labkey/study/query/studydesign/StudyTreatmentProductTable.java +++ b/study/src/org/labkey/study/query/studydesign/StudyTreatmentProductTable.java @@ -16,9 +16,8 @@ package org.labkey.study.query.studydesign; import org.jetbrains.annotations.Nullable; -import org.labkey.api.data.ColumnInfo; +import org.labkey.api.data.BaseColumnInfo; import org.labkey.api.data.ContainerFilter; -import org.labkey.api.data.DbSchema; import org.labkey.api.data.TableInfo; import org.labkey.api.exp.api.StorageProvisioner; import org.labkey.api.exp.property.Domain; @@ -67,7 +66,7 @@ private StudyTreatmentProductTable(Domain domain, TableInfo storageTableInfo, Us @Override - protected void initColumn(ColumnInfo col) + protected void initColumn(BaseColumnInfo col) { if ("ProductId".equalsIgnoreCase(col.getName())) { diff --git a/study/src/org/labkey/study/query/studydesign/StudyTreatmentTable.java b/study/src/org/labkey/study/query/studydesign/StudyTreatmentTable.java index 60233c45..ebf15906 100644 --- a/study/src/org/labkey/study/query/studydesign/StudyTreatmentTable.java +++ b/study/src/org/labkey/study/query/studydesign/StudyTreatmentTable.java @@ -16,8 +16,8 @@ package org.labkey.study.query.studydesign; import org.jetbrains.annotations.Nullable; +import org.labkey.api.data.BaseColumnInfo; import org.labkey.api.data.ColumnInfo; -import org.labkey.api.data.DbSchema; import org.labkey.api.data.ContainerFilter; import org.labkey.api.data.DisplayColumn; import org.labkey.api.data.DisplayColumnFactory; @@ -72,7 +72,7 @@ private StudyTreatmentTable(Domain domain, TableInfo storageTableInfo, UserSchem } @Override - protected void initColumn(ColumnInfo col) + protected void initColumn(BaseColumnInfo col) { if ("Description".equalsIgnoreCase(col.getName())) { diff --git a/study/src/org/labkey/study/query/studydesign/StudyTreatmentVisitMapTable.java b/study/src/org/labkey/study/query/studydesign/StudyTreatmentVisitMapTable.java index d4ab0b2e..21126a69 100644 --- a/study/src/org/labkey/study/query/studydesign/StudyTreatmentVisitMapTable.java +++ b/study/src/org/labkey/study/query/studydesign/StudyTreatmentVisitMapTable.java @@ -15,7 +15,7 @@ */ package org.labkey.study.query.studydesign; -import org.labkey.api.data.ColumnInfo; +import org.labkey.api.data.ContainerFilter; import org.labkey.api.data.TableInfo; import org.labkey.api.query.AliasedColumn; import org.labkey.api.query.DefaultQueryUpdateService; @@ -36,39 +36,39 @@ */ public class StudyTreatmentVisitMapTable extends BaseStudyTable { - public StudyTreatmentVisitMapTable(StudyQuerySchema schema) + public StudyTreatmentVisitMapTable(StudyQuerySchema schema, ContainerFilter cf) { - super(schema, StudySchema.getInstance().getTableInfoTreatmentVisitMap()); + super(schema, StudySchema.getInstance().getTableInfoTreatmentVisitMap(), cf); setName(StudyQuerySchema.TREATMENT_VISIT_MAP_TABLE_NAME); setDescription("Contains one row per cohort/treatment/visit mapping"); - ColumnInfo cohortCol = new AliasedColumn(this, "CohortId", _rootTable.getColumn("CohortId")); + var cohortCol = new AliasedColumn(this, "CohortId", _rootTable.getColumn("CohortId")); cohortCol.setFk(new LookupForeignKey("RowId") { public TableInfo getLookupTableInfo() { - return new CohortTable(_userSchema); + return new CohortTable(_userSchema, cf); } }); addColumn(cohortCol); - ColumnInfo treatmentCol = new AliasedColumn(this, "TreatmentId", _rootTable.getColumn("TreatmentId")); + var treatmentCol = new AliasedColumn(this, "TreatmentId", _rootTable.getColumn("TreatmentId")); treatmentCol.setFk(new LookupForeignKey("RowId") { @Override public TableInfo getLookupTableInfo() { - return QueryService.get().getUserSchema(_userSchema.getUser(), _userSchema.getContainer(), StudyQuerySchema.SCHEMA_NAME).getTable(StudyQuerySchema.TREATMENT_TABLE_NAME); + return QueryService.get().getUserSchema(_userSchema.getUser(), _userSchema.getContainer(), StudyQuerySchema.SCHEMA_NAME).getTable(StudyQuerySchema.TREATMENT_TABLE_NAME, cf); } }); addColumn(treatmentCol); - ColumnInfo visitCol = new AliasedColumn(this, "VisitId", _rootTable.getColumn("VisitId")); + var visitCol = new AliasedColumn(this, "VisitId", _rootTable.getColumn("VisitId")); visitCol.setFk(new LookupForeignKey("RowId") { public TableInfo getLookupTableInfo() { - return new VisitTable(_userSchema); + return new VisitTable(_userSchema, cf); } }); addColumn(visitCol); From 8f90071a24a96862cf1f871244add58a904a9eb6 Mon Sep 17 00:00:00 2001 From: labkey-adam Date: Thu, 23 May 2019 19:21:23 -0700 Subject: [PATCH 166/218] Fb htmlstring Original PR: https://github.com/LabKey/platform/pull/112 * Prefer JspBase helpers (which return HtmlStrings... or will soon) over PageFlowUtil helpers (which return Strings) * Remove unused classes * Add text() variants that simply pass through HtmlStrings. This eases the migration of helpers to HtmlString by not requiring every usage in the product to change at the same time. i.e., fix TargetedMS build break * Migrate a few URL renders to h() * Remove deprecated getColumnNames() methods * Leave unused method in place for now to avoid merge conflict --- .../study/view/studydesign/immunizationScheduleWebpart.jsp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/study/src/org/labkey/study/view/studydesign/immunizationScheduleWebpart.jsp b/study/src/org/labkey/study/view/studydesign/immunizationScheduleWebpart.jsp index 8d03057c..7911b2b2 100644 --- a/study/src/org/labkey/study/view/studydesign/immunizationScheduleWebpart.jsp +++ b/study/src/org/labkey/study/view/studydesign/immunizationScheduleWebpart.jsp @@ -33,6 +33,7 @@ <%@ page import="java.util.Map" %> <%@ page import="org.labkey.api.view.ActionURL" %> <%@ page import="org.labkey.api.study.StudyUrls" %> +<%@ page import="org.labkey.api.util.HtmlString" %> <%@ page extends="org.labkey.study.view.BaseStudyPage" %> <%! @Override @@ -95,7 +96,7 @@ %>
  • <% } @@ -152,7 +153,7 @@ %> <% } From 101fe77dc5a60679111ad74744d4942d918f5da3 Mon Sep 17 00:00:00 2001 From: labkey-adam Date: Wed, 29 May 2019 14:15:13 -0700 Subject: [PATCH 167/218] Fb htmlstring Original PR: https://github.com/LabKey/platform/pull/126 * Switch text() and most h() variants to return HtmlString. * Switch h(String) to return HtmlString. Introduce OldJspBase which restores previous behavior, where h(String) returned String. OldJspBase be used temporarily to ease JSP migration. Fix up most JSPs to accommodate these changes. Key changes: stop performing String manipulations on h() results and don't pre-encode LabKey taglib inputs. Switch some tags to builder patterns (particularly labkey:link->link()). * Remove old ContainerFilter comments * @Override, @NotNull, other warnings * Migrate textLink() to link() builder pattern * Annotate unused, String-based textLink() methods for removal in 19.3 * Encode a few URLs in participant views. Start logging cases where JSPs still print(Object). * Log the class name of rendered objects * Address some JSP-rendered objects * Need Strings, not HtmlStrings, for JSON serialization * Null check in JspWriter logging * HtmlString: Better comments. Consistent null/empty handling. Privatize constructor. Remove silly text(h()) cases. * Issue 37593: Report management notifications page results in "Ext4 is not defined" error Fix ClientDependency handling for JSPs that extend OldJspBase. (Client dependency code path was explicitly testing "instanceof JspBase", which missed OldJspBase subclasses.) * PageFlowUtil.button() -> JspBase.button() Eliminate some unnecessary .toString() calls and other cleanup * occured -> occurred * Convert a couple object renders to HtmlString * issues & search JSPs: OldJspBase -> JspBase * search JSPs: OldJspBase -> JspBase * Consistent spacing in links. Part of fix to MessagesLongTest failure (rest of fix is already on this branch). * Code review feedback --- .../view/studydesign/assayScheduleWebpart.jsp | 10 ++++---- .../immunizationScheduleWebpart.jsp | 11 ++++----- .../view/studydesign/manageAssaySchedule.jsp | 15 ++++++------ .../view/studydesign/manageStudyProducts.jsp | 24 +++++++++---------- .../view/studydesign/manageTreatments.jsp | 18 +++++++------- .../view/studydesign/vaccineDesignWebpart.jsp | 2 +- 6 files changed, 37 insertions(+), 43 deletions(-) diff --git a/study/src/org/labkey/study/view/studydesign/assayScheduleWebpart.jsp b/study/src/org/labkey/study/view/studydesign/assayScheduleWebpart.jsp index d6c4fdcd..25136f09 100644 --- a/study/src/org/labkey/study/view/studydesign/assayScheduleWebpart.jsp +++ b/study/src/org/labkey/study/view/studydesign/assayScheduleWebpart.jsp @@ -15,16 +15,16 @@ * limitations under the License. */ %> - <%@ page import="org.labkey.api.data.Container" %> +<%@ page import="org.labkey.api.module.ModuleLoader" %> <%@ page import="org.labkey.api.security.User" %> <%@ page import="org.labkey.api.security.permissions.UpdatePermission" %> +<%@ page import="org.labkey.api.util.HtmlString" %> +<%@ page import="org.labkey.api.view.ActionURL" %> <%@ page import="org.labkey.api.view.template.ClientDependencies" %> <%@ page import="org.labkey.study.controllers.StudyDesignController" %> <%@ page import="org.labkey.study.model.StudyImpl" %> <%@ page import="org.labkey.study.model.StudyManager" %> -<%@ page import="org.labkey.api.view.ActionURL" %> -<%@ page import="org.labkey.api.module.ModuleLoader" %> <%@ page extends="org.labkey.study.view.BaseStudyPage" %> <%! @Override @@ -59,12 +59,12 @@ editUrl.addParameter("useAlternateLookupFields", true); editUrl.addReturnURL(getActionURL()); %> - <%=textLink("Manage Assay Schedule", editUrl)%>
    + <%=link("Manage Assay Schedule", editUrl)%>
    <% } %> -

    <%=h(assayPlan).replaceAll("\n", "
    ")%>

    +

    <%=HtmlString.unsafe(h(assayPlan).toString().replaceAll("\n", "
    "))%>

    <% } diff --git a/study/src/org/labkey/study/view/studydesign/immunizationScheduleWebpart.jsp b/study/src/org/labkey/study/view/studydesign/immunizationScheduleWebpart.jsp index 7911b2b2..eba43ca7 100644 --- a/study/src/org/labkey/study/view/studydesign/immunizationScheduleWebpart.jsp +++ b/study/src/org/labkey/study/view/studydesign/immunizationScheduleWebpart.jsp @@ -18,7 +18,9 @@ <%@ page import="org.labkey.api.data.Container" %> <%@ page import="org.labkey.api.security.User" %> <%@ page import="org.labkey.api.security.permissions.UpdatePermission" %> -<%@ page import="org.labkey.api.util.PageFlowUtil" %> +<%@ page import="org.labkey.api.study.StudyUrls" %> +<%@ page import="org.labkey.api.util.HtmlString" %> +<%@ page import="org.labkey.api.view.ActionURL" %> <%@ page import="org.labkey.api.view.template.ClientDependencies" %> <%@ page import="org.labkey.study.model.CohortImpl" %> <%@ page import="org.labkey.study.model.ProductImpl" %> @@ -31,9 +33,6 @@ <%@ page import="java.util.HashMap" %> <%@ page import="java.util.List" %> <%@ page import="java.util.Map" %> -<%@ page import="org.labkey.api.view.ActionURL" %> -<%@ page import="org.labkey.api.study.StudyUrls" %> -<%@ page import="org.labkey.api.util.HtmlString" %> <%@ page extends="org.labkey.study.view.BaseStudyPage" %> <%! @Override @@ -74,10 +73,10 @@ if (canEdit) { - ActionURL editUrl = PageFlowUtil.urlProvider(StudyUrls.class).getManageTreatmentsURL(c, c.hasActiveModuleByName("viscstudies")); + ActionURL editUrl = urlProvider(StudyUrls.class).getManageTreatmentsURL(c, c.hasActiveModuleByName("viscstudies")); editUrl.addReturnURL(getActionURL()); %> - <%=textLink("Manage Treatments", editUrl)%>
    + <%=link("Manage Treatments", editUrl)%>
    <% } diff --git a/study/src/org/labkey/study/view/studydesign/manageAssaySchedule.jsp b/study/src/org/labkey/study/view/studydesign/manageAssaySchedule.jsp index e7f964f0..c0045584 100644 --- a/study/src/org/labkey/study/view/studydesign/manageAssaySchedule.jsp +++ b/study/src/org/labkey/study/view/studydesign/manageAssaySchedule.jsp @@ -16,18 +16,17 @@ */ %> <%@ page import="org.labkey.api.data.Container" %> +<%@ page import="org.labkey.api.portal.ProjectUrls" %> <%@ page import="org.labkey.api.study.Study" %> <%@ page import="org.labkey.api.study.TimepointType" %> <%@ page import="org.labkey.api.view.HttpView" %> <%@ page import="org.labkey.api.view.JspView" %> +<%@ page import="org.labkey.api.view.NavTree" %> +<%@ page import="org.labkey.api.view.PopupMenuView" %> <%@ page import="org.labkey.api.view.template.ClientDependencies" %> +<%@ page import="org.labkey.study.controllers.StudyController" %> <%@ page import="org.labkey.study.controllers.StudyDesignController" %> <%@ page import="org.labkey.study.model.StudyManager" %> -<%@ page import="org.labkey.api.util.PageFlowUtil" %> -<%@ page import="org.labkey.api.portal.ProjectUrls" %> -<%@ page import="org.labkey.study.controllers.StudyController" %> -<%@ page import="org.labkey.api.view.NavTree" %> -<%@ page import="org.labkey.api.view.PopupMenuView" %> <%@ page import="org.labkey.study.view.studydesign.StudyDesignConfigureMenuItem" %> <%@ page extends="org.labkey.api.jsp.JspBase" %> <%! @@ -52,7 +51,7 @@ if (study != null && study.getTimepointType() == TimepointType.DATE) visitNoun = "Timepoint"; - String returnUrl = form.getReturnUrl() != null ? form.getReturnUrl() : PageFlowUtil.urlProvider(ProjectUrls.class).getBeginURL(c).toString(); + String returnUrl = form.getReturnUrl() != null ? form.getReturnUrl() : urlProvider(ProjectUrls.class).getBeginURL(c).toString(); %> diff --git a/study/src/org/labkey/study/view/studydesign/manageStudyProducts.jsp b/study/src/org/labkey/study/view/studydesign/manageStudyProducts.jsp index ea5a31e7..329d2329 100644 --- a/study/src/org/labkey/study/view/studydesign/manageStudyProducts.jsp +++ b/study/src/org/labkey/study/view/studydesign/manageStudyProducts.jsp @@ -53,7 +53,7 @@ Ext4.create('LABKEY.VaccineDesign.StudyProductsPanel', { renderTo : 'study-products-panel', disableEdit : <%=isDataspaceProject%>, - returnURL : <%=q(returnUrl)%> + returnUrl : <%=q(returnUrl)%> }); }); @@ -62,7 +62,7 @@ if (isDataspaceProject) { ActionURL projectManageProductsURL = new ActionURL(StudyDesignController.ManageStudyProductsAction.class, getContainer().getProject()); - projectManageProductsURL.addReturnURL(getActionURL()); + projectManageProductsURL.addReturnUrl(getActionURL()); %> Vaccine design information is defined at the project level for Dataspace projects. The grids below are read-only.
    @@ -86,7 +86,7 @@ Enter vaccine design information in the grids below. Use the manage treatments page to describe the schedule of treatments and combinations of study products administered at each timepoint. <% ActionURL manageTreatmentsURL = urlProvider(StudyUrls.class).getManageTreatmentsURL(c, c.hasActiveModuleByName("viscstudies")); - manageTreatmentsURL.addReturnURL(getActionURL()); + manageTreatmentsURL.addReturnUrl(getActionURL()); %> <%=link("Manage Treatments", manageTreatmentsURL)%> diff --git a/study/src/org/labkey/study/view/studydesign/manageTreatments.jsp b/study/src/org/labkey/study/view/studydesign/manageTreatments.jsp index 43d1a590..a32f80d1 100644 --- a/study/src/org/labkey/study/view/studydesign/manageTreatments.jsp +++ b/study/src/org/labkey/study/view/studydesign/manageTreatments.jsp @@ -84,7 +84,7 @@ disableEdit : <%=isDataspaceProject%>, subjectNoun : <%=q(subjectNoun)%>, visitNoun : <%=q(visitNoun)%>, - returnURL : <%=q(returnUrl)%>, + returnUrl : <%=q(returnUrl)%>, productRoles: productRoles }); } @@ -128,7 +128,7 @@ Enter treatment information in the grids below. Use the manage study products page to change or update the set of available values. <% ActionURL manageStudyProductsURL = new ActionURL(StudyDesignController.ManageStudyProductsAction.class, getContainer()); - manageStudyProductsURL.addReturnURL(getActionURL()); + manageStudyProductsURL.addReturnUrl(getActionURL()); %> <%=link("Manage Study Products", manageStudyProductsURL)%> @@ -152,7 +152,7 @@ Enter treatment information in the grids below. { %>
  • Use the change visit order page to adjust the display order of visits in the treatment schedule table. - <%= link("Change Visit Order", new ActionURL(StudyController.VisitOrderAction.class, c).addReturnURL(getActionURL())) %> + <%= link("Change Visit Order", new ActionURL(StudyController.VisitOrderAction.class, c).addReturnUrl(getActionURL())) %>
  • <% } diff --git a/study/src/org/labkey/study/view/studydesign/vaccineDesignWebpart.jsp b/study/src/org/labkey/study/view/studydesign/vaccineDesignWebpart.jsp index 88e7d5c7..8af307fa 100644 --- a/study/src/org/labkey/study/view/studydesign/vaccineDesignWebpart.jsp +++ b/study/src/org/labkey/study/view/studydesign/vaccineDesignWebpart.jsp @@ -43,7 +43,7 @@ if (container.hasPermission(user, UpdatePermission.class)) { ActionURL editUrl = new ActionURL(StudyDesignController.ManageStudyProductsAction.class, getContainer()); - editUrl.addReturnURL(getActionURL()); + editUrl.addReturnUrl(getActionURL()); %> <%=link("Manage Study Products", editUrl)%>
    <% diff --git a/study/webapp/study/vaccineDesign/AssaySchedule.js b/study/webapp/study/vaccineDesign/AssaySchedule.js index 6fc52d21..a4f2f50d 100644 --- a/study/webapp/study/vaccineDesign/AssaySchedule.js +++ b/study/webapp/study/vaccineDesign/AssaySchedule.js @@ -16,7 +16,7 @@ Ext4.define('LABKEY.VaccineDesign.AssaySchedulePanel', { dirty : false, - returnURL : null, + returnUrl : null, initComponent : function() { @@ -213,7 +213,7 @@ Ext4.define('LABKEY.VaccineDesign.AssaySchedulePanel', { goToReturnURL : function() { this.setDirty(false); - window.location = this.returnURL; + window.location = this.returnUrl; }, onFailure : function(text) diff --git a/study/webapp/study/vaccineDesign/StudyProducts.js b/study/webapp/study/vaccineDesign/StudyProducts.js index e02a2786..c757d000 100644 --- a/study/webapp/study/vaccineDesign/StudyProducts.js +++ b/study/webapp/study/vaccineDesign/StudyProducts.js @@ -16,7 +16,7 @@ Ext4.define('LABKEY.VaccineDesign.StudyProductsPanel', { dirty : false, - returnURL : null, + returnUrl : null, initComponent : function() { @@ -198,7 +198,7 @@ Ext4.define('LABKEY.VaccineDesign.StudyProductsPanel', { goToReturnURL : function() { this.setDirty(false); - window.location = this.returnURL; + window.location = this.returnUrl; }, onFailure : function(text) diff --git a/study/webapp/study/vaccineDesign/TreatmentScheduleBase.js b/study/webapp/study/vaccineDesign/TreatmentScheduleBase.js index ebf9479f..73aece24 100644 --- a/study/webapp/study/vaccineDesign/TreatmentScheduleBase.js +++ b/study/webapp/study/vaccineDesign/TreatmentScheduleBase.js @@ -14,7 +14,7 @@ Ext4.define('LABKEY.VaccineDesign.TreatmentSchedulePanelBase', { dirty : false, - returnURL : null, + returnUrl : null, getButtonBar : function() { @@ -135,7 +135,7 @@ Ext4.define('LABKEY.VaccineDesign.TreatmentSchedulePanelBase', { goToReturnURL : function() { this.setDirty(false); - window.location = this.returnURL; + window.location = this.returnUrl; }, onFailure : function(text) From 28088b96e49ed11333ad55bcb10cc4f5e4bd7bf5 Mon Sep 17 00:00:00 2001 From: Karl Lum Date: Fri, 7 Mar 2025 12:49:54 -0800 Subject: [PATCH 211/218] Deprecate study protocol designer tools (phase 2) Original PR: https://github.com/LabKey/platform/pull/6406 --- .../controllers/StudyDesignController.java | 3 +- .../labkey/study/model/TreatmentManager.java | 102 ++++----- .../AbstractStudyDesignDomainKind.java | 195 ---------------- .../studydesign/DefaultStudyDesignTable.java | 209 ------------------ .../query/studydesign/DoseAndRouteTable.java | 46 ---- .../studydesign/StudyDesignAssaysTable.java | 60 ----- .../StudyDesignChallengeTypesTable.java | 29 --- .../studydesign/StudyDesignGenesTable.java | 33 --- .../StudyDesignImmunogenTypesTable.java | 34 --- .../studydesign/StudyDesignLabsTable.java | 50 ----- .../StudyDesignLookupBaseTable.java | 162 -------------- .../studydesign/StudyDesignRoutesTable.java | 33 --- .../StudyDesignSampleTypesTable.java | 46 ---- .../studydesign/StudyDesignSubTypesTable.java | 33 --- .../studydesign/StudyDesignUnitsTable.java | 33 --- .../StudyProductAntigenDomainKind.java | 63 ------ .../studydesign/StudyProductAntigenTable.java | 130 ----------- .../studydesign/StudyProductDomainKind.java | 69 ------ .../query/studydesign/StudyProductTable.java | 102 --------- .../studydesign/StudyTreatmentDomainKind.java | 65 ------ .../StudyTreatmentProductDomainKind.java | 62 ------ .../StudyTreatmentProductTable.java | 112 ---------- .../studydesign/StudyTreatmentTable.java | 99 --------- .../StudyTreatmentVisitMapTable.java | 93 -------- .../ImmunizationScheduleWebpartFactory.java | 3 +- .../VaccineDesignWebpartFactory.java | 1 - 26 files changed, 55 insertions(+), 1812 deletions(-) delete mode 100644 study/src/org/labkey/study/query/studydesign/AbstractStudyDesignDomainKind.java delete mode 100644 study/src/org/labkey/study/query/studydesign/DefaultStudyDesignTable.java delete mode 100644 study/src/org/labkey/study/query/studydesign/DoseAndRouteTable.java delete mode 100644 study/src/org/labkey/study/query/studydesign/StudyDesignAssaysTable.java delete mode 100644 study/src/org/labkey/study/query/studydesign/StudyDesignChallengeTypesTable.java delete mode 100644 study/src/org/labkey/study/query/studydesign/StudyDesignGenesTable.java delete mode 100644 study/src/org/labkey/study/query/studydesign/StudyDesignImmunogenTypesTable.java delete mode 100644 study/src/org/labkey/study/query/studydesign/StudyDesignLabsTable.java delete mode 100644 study/src/org/labkey/study/query/studydesign/StudyDesignLookupBaseTable.java delete mode 100644 study/src/org/labkey/study/query/studydesign/StudyDesignRoutesTable.java delete mode 100644 study/src/org/labkey/study/query/studydesign/StudyDesignSampleTypesTable.java delete mode 100644 study/src/org/labkey/study/query/studydesign/StudyDesignSubTypesTable.java delete mode 100644 study/src/org/labkey/study/query/studydesign/StudyDesignUnitsTable.java delete mode 100644 study/src/org/labkey/study/query/studydesign/StudyProductAntigenDomainKind.java delete mode 100644 study/src/org/labkey/study/query/studydesign/StudyProductAntigenTable.java delete mode 100644 study/src/org/labkey/study/query/studydesign/StudyProductDomainKind.java delete mode 100644 study/src/org/labkey/study/query/studydesign/StudyProductTable.java delete mode 100644 study/src/org/labkey/study/query/studydesign/StudyTreatmentDomainKind.java delete mode 100644 study/src/org/labkey/study/query/studydesign/StudyTreatmentProductDomainKind.java delete mode 100644 study/src/org/labkey/study/query/studydesign/StudyTreatmentProductTable.java delete mode 100644 study/src/org/labkey/study/query/studydesign/StudyTreatmentTable.java delete mode 100644 study/src/org/labkey/study/query/studydesign/StudyTreatmentVisitMapTable.java diff --git a/study/src/org/labkey/study/controllers/StudyDesignController.java b/study/src/org/labkey/study/controllers/StudyDesignController.java index 924bfd2f..cb3d4658 100644 --- a/study/src/org/labkey/study/controllers/StudyDesignController.java +++ b/study/src/org/labkey/study/controllers/StudyDesignController.java @@ -39,6 +39,7 @@ import org.labkey.api.study.TimepointType; import org.labkey.api.study.Visit; import org.labkey.api.study.security.permissions.ManageStudyPermission; +import org.labkey.api.studydesign.query.StudyDesignSchema; import org.labkey.api.util.JsonUtil; import org.labkey.api.view.ActionURL; import org.labkey.api.view.HttpView; @@ -499,7 +500,7 @@ private void updateProductDoseAndRoutes(int productId, List doseAn { SimpleFilter filter = new SimpleFilter(); filter.addInClause(FieldKey.fromParts("RowId"), existingDoseAndRoutes); - Table.delete(StudySchema.getInstance().getTableInfoDoseAndRoute(), filter); + Table.delete(StudyDesignSchema.getInstance().getTableInfoDoseAndRoute(), filter); } transaction.commit(); } diff --git a/study/src/org/labkey/study/model/TreatmentManager.java b/study/src/org/labkey/study/model/TreatmentManager.java index 45a16586..64b56976 100644 --- a/study/src/org/labkey/study/model/TreatmentManager.java +++ b/study/src/org/labkey/study/model/TreatmentManager.java @@ -39,6 +39,8 @@ import org.labkey.api.security.User; import org.labkey.api.study.TimepointType; import org.labkey.api.study.Visit; +import org.labkey.api.studydesign.query.StudyDesignQuerySchema; +import org.labkey.api.studydesign.query.StudyDesignSchema; import org.labkey.api.test.TestWhen; import org.labkey.api.util.DateUtil; import org.labkey.api.util.GUID; @@ -87,13 +89,13 @@ public List getStudyProducts(Container container, User user, @Nulla if (rowId != null) filter.addCondition(FieldKey.fromParts("RowId"), rowId); - TableInfo ti = QueryService.get().getUserSchema(user, container, StudyQuerySchema.SCHEMA_NAME).getTable(StudyQuerySchema.PRODUCT_TABLE_NAME); + TableInfo ti = QueryService.get().getUserSchema(user, container, StudyQuerySchema.SCHEMA_NAME).getTable(StudyDesignQuerySchema.PRODUCT_TABLE_NAME); return new TableSelector(ti, filter, new Sort("RowId")).getArrayList(ProductImpl.class); } public List getFilteredStudyProducts(Container container, User user, List filterRowIds) { - TableInfo ti = QueryService.get().getUserSchema(user, container, StudyQuerySchema.SCHEMA_NAME).getTable(StudyQuerySchema.PRODUCT_TABLE_NAME); + TableInfo ti = QueryService.get().getUserSchema(user, container, StudyQuerySchema.SCHEMA_NAME).getTable(StudyDesignQuerySchema.PRODUCT_TABLE_NAME); //Using a user schema so containerFilter will be created for us later (so don't need SimpleFilter.createContainerFilter) SimpleFilter filter = new SimpleFilter(); @@ -108,13 +110,13 @@ public List getStudyProductAntigens(Container container, Use SimpleFilter filter = new SimpleFilter(); filter.addCondition(FieldKey.fromParts("ProductId"), productId); - TableInfo ti = QueryService.get().getUserSchema(user, container, StudyQuerySchema.SCHEMA_NAME).getTable(StudyQuerySchema.PRODUCT_ANTIGEN_TABLE_NAME); + TableInfo ti = QueryService.get().getUserSchema(user, container, StudyQuerySchema.SCHEMA_NAME).getTable(StudyDesignQuerySchema.PRODUCT_ANTIGEN_TABLE_NAME); return new TableSelector(ti, filter, new Sort("RowId")).getArrayList(ProductAntigenImpl.class); } public List getFilteredStudyProductAntigens(Container container, User user, @NotNull Integer productId, List filterRowIds) { - TableInfo ti = QueryService.get().getUserSchema(user, container, StudyQuerySchema.SCHEMA_NAME).getTable(StudyQuerySchema.PRODUCT_ANTIGEN_TABLE_NAME); + TableInfo ti = QueryService.get().getUserSchema(user, container, StudyQuerySchema.SCHEMA_NAME).getTable(StudyDesignQuerySchema.PRODUCT_ANTIGEN_TABLE_NAME); //Using a user schema so containerFilter will be created for us later (so don't need SimpleFilter.createContainerFilter) SimpleFilter filter = new SimpleFilter(); @@ -127,21 +129,21 @@ public List getFilteredStudyProductAntigens(Container contai public Integer saveTreatment(Container container, User user, TreatmentImpl treatment) throws Exception { - TableInfo treatmentTable = QueryService.get().getUserSchema(user, container, StudyQuerySchema.SCHEMA_NAME).getTable(StudyQuerySchema.TREATMENT_TABLE_NAME); + TableInfo treatmentTable = QueryService.get().getUserSchema(user, container, StudyQuerySchema.SCHEMA_NAME).getTable(StudyDesignQuerySchema.TREATMENT_TABLE_NAME); return saveStudyDesignRow(container, user, treatmentTable, treatment.serialize(), treatment.isNew() ? null : treatment.getRowId(), "RowId"); } public List getStudyTreatments(Container container, User user) { SimpleFilter filter = new SimpleFilter(); - TableInfo ti = QueryService.get().getUserSchema(user, container, StudyQuerySchema.SCHEMA_NAME).getTable(StudyQuerySchema.TREATMENT_TABLE_NAME); + TableInfo ti = QueryService.get().getUserSchema(user, container, StudyQuerySchema.SCHEMA_NAME).getTable(StudyDesignQuerySchema.TREATMENT_TABLE_NAME); return new TableSelector(ti, filter, new Sort("RowId")).getArrayList(TreatmentImpl.class); } public TreatmentImpl getStudyTreatmentByRowId(Container container, User user, int rowId) { SimpleFilter filter = new SimpleFilter(FieldKey.fromParts("RowId"), rowId); - TableInfo ti = QueryService.get().getUserSchema(user, container, StudyQuerySchema.SCHEMA_NAME).getTable(StudyQuerySchema.TREATMENT_TABLE_NAME); + TableInfo ti = QueryService.get().getUserSchema(user, container, StudyQuerySchema.SCHEMA_NAME).getTable(StudyDesignQuerySchema.TREATMENT_TABLE_NAME); TreatmentImpl treatment = new TableSelector(ti, filter, null).getObject(TreatmentImpl.class); // attach the associated study products to the treatment object @@ -168,7 +170,7 @@ public List getFilteredTreatments(Container container, User user, List filterRowIds = new ArrayList<>(); filterRowIds.addAll(definedTreatmentIds); filterRowIds.addAll(usedTreatmentIds); - TableInfo ti = QueryService.get().getUserSchema(user, container, StudyQuerySchema.SCHEMA_NAME).getTable(StudyQuerySchema.TREATMENT_TABLE_NAME); + TableInfo ti = QueryService.get().getUserSchema(user, container, StudyQuerySchema.SCHEMA_NAME).getTable(StudyDesignQuerySchema.TREATMENT_TABLE_NAME); //Using a user schema so containerFilter will be created for us later (so don't need SimpleFilter.createContainerFilter) SimpleFilter filter = new SimpleFilter().addCondition(FieldKey.fromParts("RowId"), filterRowIds, CompareType.NOT_IN); @@ -178,7 +180,7 @@ public List getFilteredTreatments(Container container, User user, public Integer saveTreatmentProductMapping(Container container, User user, TreatmentProductImpl treatmentProduct) throws Exception { - TableInfo treatmentProductTable = QueryService.get().getUserSchema(user, container, StudyQuerySchema.SCHEMA_NAME).getTable(StudyQuerySchema.TREATMENT_PRODUCT_MAP_TABLE_NAME); + TableInfo treatmentProductTable = QueryService.get().getUserSchema(user, container, StudyQuerySchema.SCHEMA_NAME).getTable(StudyDesignQuerySchema.TREATMENT_PRODUCT_MAP_TABLE_NAME); return saveStudyDesignRow(container, user, treatmentProductTable, treatmentProduct.serialize(), treatmentProduct.isNew() ? null : treatmentProduct.getRowId(), "RowId"); } @@ -191,13 +193,13 @@ public List getStudyTreatmentProducts(Container container, { SimpleFilter filter = new SimpleFilter(FieldKey.fromParts("TreatmentId"), treatmentId); - TableInfo ti = QueryService.get().getUserSchema(user, container, StudyQuerySchema.SCHEMA_NAME).getTable(StudyQuerySchema.TREATMENT_PRODUCT_MAP_TABLE_NAME); + TableInfo ti = QueryService.get().getUserSchema(user, container, StudyQuerySchema.SCHEMA_NAME).getTable(StudyDesignQuerySchema.TREATMENT_PRODUCT_MAP_TABLE_NAME); return new TableSelector(ti, filter, sort).getArrayList(TreatmentProductImpl.class); } public List getFilteredTreatmentProductMappings(Container container, User user, @NotNull Integer treatmentId, List filterRowIds) { - TableInfo ti = QueryService.get().getUserSchema(user, container, StudyQuerySchema.SCHEMA_NAME).getTable(StudyQuerySchema.TREATMENT_PRODUCT_MAP_TABLE_NAME); + TableInfo ti = QueryService.get().getUserSchema(user, container, StudyQuerySchema.SCHEMA_NAME).getTable(StudyDesignQuerySchema.TREATMENT_PRODUCT_MAP_TABLE_NAME); //Using a user schema so containerFilter will be created for us later (so don't need SimpleFilter.createContainerFilter) SimpleFilter filter = new SimpleFilter(); @@ -214,14 +216,14 @@ public List getStudyTreatmentVisitMap(Container container if (cohortId != null) filter.addCondition(FieldKey.fromParts("CohortId"), cohortId); - TableInfo ti = StudySchema.getInstance().getTableInfoTreatmentVisitMap(); + TableInfo ti = StudyDesignSchema.getInstance().getTableInfoTreatmentVisitMap(); return new TableSelector(ti, filter, new Sort("CohortId")).getArrayList(TreatmentVisitMapImpl.class); } public List getVisitsForTreatmentSchedule(Container container) { SimpleFilter filter = SimpleFilter.createContainerFilter(container); - List visitRowIds = new TableSelector(StudySchema.getInstance().getTableInfoTreatmentVisitMap(), + List visitRowIds = new TableSelector(StudyDesignSchema.getInstance().getTableInfoTreatmentVisitMap(), Collections.singleton("VisitId"), filter, new Sort("VisitId")).getArrayList(Integer.class); return StudyManager.getInstance().getSortedVisitsByRowIds(container, visitRowIds); @@ -235,21 +237,21 @@ public TreatmentVisitMapImpl insertTreatmentVisitMap(User user, Container contai newMapping.setVisitId(visitId); newMapping.setTreatmentId(treatmentId); - return Table.insert(user, StudySchema.getInstance().getTableInfoTreatmentVisitMap(), newMapping); + return Table.insert(user, StudyDesignSchema.getInstance().getTableInfoTreatmentVisitMap(), newMapping); } public void deleteTreatmentVisitMapForCohort(Container container, int rowId) { SimpleFilter filter = SimpleFilter.createContainerFilter(container); filter.addCondition(FieldKey.fromParts("CohortId"), rowId); - Table.delete(StudySchema.getInstance().getTableInfoTreatmentVisitMap(), filter); + Table.delete(StudyDesignSchema.getInstance().getTableInfoTreatmentVisitMap(), filter); } public void deleteTreatmentVisitMapForVisit(Container container, int rowId) { SimpleFilter filter = SimpleFilter.createContainerFilter(container); filter.addCondition(FieldKey.fromParts("VisitId"), rowId); - Table.delete(StudySchema.getInstance().getTableInfoTreatmentVisitMap(), filter); + Table.delete(StudyDesignSchema.getInstance().getTableInfoTreatmentVisitMap(), filter); } public void deleteTreatment(Container container, User user, int rowId) @@ -261,7 +263,7 @@ public void deleteTreatment(Container container, User user, int rowId) // delete the usages of this treatment in the TreatmentVisitMap SimpleFilter filter = SimpleFilter.createContainerFilter(container); filter.addCondition(FieldKey.fromParts("TreatmentId"), rowId); - Table.delete(schema.getTableInfoTreatmentVisitMap(), filter); + Table.delete(StudyDesignSchema.getInstance().getTableInfoTreatmentVisitMap(), filter); // delete the associated treatment study product mappings (provision table) filter = SimpleFilter.createContainerFilter(container); @@ -269,7 +271,7 @@ public void deleteTreatment(Container container, User user, int rowId) deleteTreatmentProductMap(container, user, filter); // finally delete the record from the Treatment (provision table) - TableInfo treatmentTable = QueryService.get().getUserSchema(user, container, StudyQuerySchema.SCHEMA_NAME).getTable(StudyQuerySchema.TREATMENT_TABLE_NAME); + TableInfo treatmentTable = QueryService.get().getUserSchema(user, container, StudyQuerySchema.SCHEMA_NAME).getTable(StudyDesignQuerySchema.TREATMENT_TABLE_NAME); if (treatmentTable != null) { QueryUpdateService qus = treatmentTable.getUpdateService(); @@ -297,7 +299,7 @@ public void deleteStudyProduct(Container container, User user, int rowId) deleteProductAntigens(container, user, rowId); // delete the associated doses and routes for this product - Table.delete(StudySchema.getInstance().getTableInfoDoseAndRoute(), new SimpleFilter(FieldKey.fromParts("ProductId"), rowId)); + Table.delete(StudyDesignSchema.getInstance().getTableInfoDoseAndRoute(), new SimpleFilter(FieldKey.fromParts("ProductId"), rowId)); // delete the associated treatment study product mappings (provision table) SimpleFilter filter = SimpleFilter.createContainerFilter(container); @@ -305,7 +307,7 @@ public void deleteStudyProduct(Container container, User user, int rowId) deleteTreatmentProductMap(container, user, filter); // finally delete the record from the Products (provision table) - TableInfo productTable = QueryService.get().getUserSchema(user, container, StudyQuerySchema.SCHEMA_NAME).getTable(StudyQuerySchema.PRODUCT_TABLE_NAME); + TableInfo productTable = QueryService.get().getUserSchema(user, container, StudyQuerySchema.SCHEMA_NAME).getTable(StudyDesignQuerySchema.PRODUCT_TABLE_NAME); if (productTable != null) { QueryUpdateService qus = productTable.getUpdateService(); @@ -315,7 +317,7 @@ public void deleteStudyProduct(Container container, User user, int rowId) qus.deleteRows(user, container, keys, null, null); } else - throw new IllegalStateException("Could not find table: " + StudyQuerySchema.PRODUCT_TABLE_NAME); + throw new IllegalStateException("Could not find table: " + StudyDesignQuerySchema.PRODUCT_TABLE_NAME); transaction.commit(); } @@ -327,28 +329,28 @@ public void deleteStudyProduct(Container container, User user, int rowId) public Integer saveStudyProduct(Container container, User user, ProductImpl product) throws Exception { - TableInfo productTable = QueryService.get().getUserSchema(user, container, StudyQuerySchema.SCHEMA_NAME).getTable(StudyQuerySchema.PRODUCT_TABLE_NAME); + TableInfo productTable = QueryService.get().getUserSchema(user, container, StudyQuerySchema.SCHEMA_NAME).getTable(StudyDesignQuerySchema.PRODUCT_TABLE_NAME); return saveStudyDesignRow(container, user, productTable, product.serialize(), product.isNew() ? null : product.getRowId(), "RowId"); } public Integer saveStudyProductAntigen(Container container, User user, ProductAntigenImpl antigen) throws Exception { - TableInfo productAntigenTable = QueryService.get().getUserSchema(user, container, StudyQuerySchema.SCHEMA_NAME).getTable(StudyQuerySchema.PRODUCT_ANTIGEN_TABLE_NAME); + TableInfo productAntigenTable = QueryService.get().getUserSchema(user, container, StudyQuerySchema.SCHEMA_NAME).getTable(StudyDesignQuerySchema.PRODUCT_ANTIGEN_TABLE_NAME); return saveStudyDesignRow(container, user, productAntigenTable, antigen.serialize(), antigen.isNew() ? null : antigen.getRowId(), "RowId"); } public DoseAndRoute saveStudyProductDoseAndRoute(Container container, User user, DoseAndRoute doseAndRoute) { if (doseAndRoute.isNew()) - return Table.insert(user, StudySchema.getInstance().getTableInfoDoseAndRoute(), doseAndRoute); + return Table.insert(user, StudyDesignSchema.getInstance().getTableInfoDoseAndRoute(), doseAndRoute); else - return Table.update(user, StudySchema.getInstance().getTableInfoDoseAndRoute(), doseAndRoute, doseAndRoute.getRowId()); + return Table.update(user, StudyDesignSchema.getInstance().getTableInfoDoseAndRoute(), doseAndRoute, doseAndRoute.getRowId()); } public Collection getStudyProductsDoseAndRoute(Container container, User user, int productId) { SimpleFilter filter = new SimpleFilter(FieldKey.fromParts("ProductId"), productId); - return new TableSelector(StudySchema.getInstance().getTableInfoDoseAndRoute(), filter, null).getCollection(DoseAndRoute.class); + return new TableSelector(StudyDesignSchema.getInstance().getTableInfoDoseAndRoute(), filter, null).getCollection(DoseAndRoute.class); } @Nullable @@ -363,7 +365,7 @@ public DoseAndRoute getDoseAndRoute(Container container, String dose, String rou filter.addCondition(FieldKey.fromParts("Route"), route); else filter.addCondition(FieldKey.fromParts("Route"), null, CompareType.ISBLANK); - Collection doseAndRoutes = new TableSelector(StudySchema.getInstance().getTableInfoDoseAndRoute(), filter, null).getCollection(DoseAndRoute.class); + Collection doseAndRoutes = new TableSelector(StudyDesignSchema.getInstance().getTableInfoDoseAndRoute(), filter, null).getCollection(DoseAndRoute.class); if (!doseAndRoutes.isEmpty()) { @@ -444,7 +446,7 @@ public Integer saveStudyDesignRow(Container container, User user, TableInfo tabl public void deleteStudyProductAntigen(Container container, User user, int rowId) throws Exception { - TableInfo productAntigenTable = QueryService.get().getUserSchema(user, container, StudyQuerySchema.SCHEMA_NAME).getTable(StudyQuerySchema.PRODUCT_ANTIGEN_TABLE_NAME); + TableInfo productAntigenTable = QueryService.get().getUserSchema(user, container, StudyQuerySchema.SCHEMA_NAME).getTable(StudyDesignQuerySchema.PRODUCT_ANTIGEN_TABLE_NAME); if (productAntigenTable != null) { QueryUpdateService qus = productAntigenTable.getUpdateService(); @@ -454,15 +456,15 @@ public void deleteStudyProductAntigen(Container container, User user, int rowId) qus.deleteRows(user, container, keys, null, null); } else - throw new IllegalStateException("Could not find query update service for table: " + StudyQuerySchema.PRODUCT_ANTIGEN_TABLE_NAME); + throw new IllegalStateException("Could not find query update service for table: " + StudyDesignQuerySchema.PRODUCT_ANTIGEN_TABLE_NAME); } else - throw new IllegalStateException("Could not find table: " + StudyQuerySchema.PRODUCT_ANTIGEN_TABLE_NAME); + throw new IllegalStateException("Could not find table: " + StudyDesignQuerySchema.PRODUCT_ANTIGEN_TABLE_NAME); } public void deleteProductAntigens(Container container, User user, int productId) throws Exception { - TableInfo productAntigenTable = QueryService.get().getUserSchema(user, container, StudyQuerySchema.SCHEMA_NAME).getTable(StudyQuerySchema.PRODUCT_ANTIGEN_TABLE_NAME); + TableInfo productAntigenTable = QueryService.get().getUserSchema(user, container, StudyQuerySchema.SCHEMA_NAME).getTable(StudyDesignQuerySchema.PRODUCT_ANTIGEN_TABLE_NAME); if (productAntigenTable != null) { SimpleFilter filter = SimpleFilter.createContainerFilter(container); @@ -483,27 +485,27 @@ public void deleteProductAntigens(Container container, User user, int productId) qus.deleteRows(user, container, keys, null, null); } else - throw new IllegalStateException("Could not find query update service for table: " + StudyQuerySchema.PRODUCT_ANTIGEN_TABLE_NAME); + throw new IllegalStateException("Could not find query update service for table: " + StudyDesignQuerySchema.PRODUCT_ANTIGEN_TABLE_NAME); } else - throw new IllegalStateException("Could not find table: " + StudyQuerySchema.PRODUCT_ANTIGEN_TABLE_NAME); + throw new IllegalStateException("Could not find table: " + StudyDesignQuerySchema.PRODUCT_ANTIGEN_TABLE_NAME); } public void deleteTreatmentProductMap(Container container, User user, SimpleFilter filter) throws Exception { - TableInfo productMapTable = QueryService.get().getUserSchema(user, container, StudyQuerySchema.SCHEMA_NAME).getTable(StudyQuerySchema.TREATMENT_PRODUCT_MAP_TABLE_NAME); + TableInfo productMapTable = QueryService.get().getUserSchema(user, container, StudyQuerySchema.SCHEMA_NAME).getTable(StudyDesignQuerySchema.TREATMENT_PRODUCT_MAP_TABLE_NAME); if (productMapTable != null) { TableSelector selector = new TableSelector(productMapTable, Collections.singleton("RowId"), filter, null); deleteTreatmentProductMap(container, user, selector.getArrayList(Integer.class)); } else - throw new IllegalStateException("Could not find table: " + StudyQuerySchema.TREATMENT_PRODUCT_MAP_TABLE_NAME); + throw new IllegalStateException("Could not find table: " + StudyDesignQuerySchema.TREATMENT_PRODUCT_MAP_TABLE_NAME); } public void deleteTreatmentProductMap(Container container, User user, List rowIds) throws Exception { - TableInfo productMapTable = QueryService.get().getUserSchema(user, container, StudyQuerySchema.SCHEMA_NAME).getTable(StudyQuerySchema.TREATMENT_PRODUCT_MAP_TABLE_NAME); + TableInfo productMapTable = QueryService.get().getUserSchema(user, container, StudyQuerySchema.SCHEMA_NAME).getTable(StudyDesignQuerySchema.TREATMENT_PRODUCT_MAP_TABLE_NAME); if (productMapTable != null) { QueryUpdateService qus = productMapTable.getUpdateService(); @@ -517,10 +519,10 @@ public void deleteTreatmentProductMap(Container container, User user, List treatmentProducts, Container container, User user) throws Exception @@ -749,7 +751,7 @@ private void populateTreatmentSchedule() throws ValidationException private void populateTreatments() { - TableInfo treatmentTable = _schema.getTable(StudyQuerySchema.TREATMENT_TABLE_NAME); + TableInfo treatmentTable = _schema.getTable(StudyDesignQuerySchema.TREATMENT_TABLE_NAME); if (treatmentTable != null) { TableInfo ti = ((FilteredTable)treatmentTable).getRealTable(); @@ -770,7 +772,7 @@ private void populateTreatments() private void addProductsForTreatment(int treatmentId) { - TableInfo treatmentProductTable = _schema.getTable(StudyQuerySchema.TREATMENT_PRODUCT_MAP_TABLE_NAME); + TableInfo treatmentProductTable = _schema.getTable(StudyDesignQuerySchema.TREATMENT_PRODUCT_MAP_TABLE_NAME); if (treatmentProductTable != null) { TableInfo ti = ((FilteredTable)treatmentProductTable).getRealTable(); @@ -789,7 +791,7 @@ private void addProductsForTreatment(int treatmentId) private void populateStudyProducts() { - TableInfo productTable = _schema.getTable(StudyQuerySchema.PRODUCT_TABLE_NAME); + TableInfo productTable = _schema.getTable(StudyDesignQuerySchema.PRODUCT_TABLE_NAME); if (productTable != null) { TableInfo ti = ((FilteredTable)productTable).getRealTable(); @@ -817,7 +819,7 @@ private void populateStudyProducts() private void addAntigenToProduct(int productId) { - TableInfo productAntigenTable = _schema.getTable(StudyQuerySchema.PRODUCT_ANTIGEN_TABLE_NAME); + TableInfo productAntigenTable = _schema.getTable(StudyDesignQuerySchema.PRODUCT_ANTIGEN_TABLE_NAME); if (productAntigenTable != null) { TableInfo ti = ((FilteredTable)productAntigenTable).getRealTable(); @@ -838,28 +840,28 @@ private void populateLookupTables() data.put("Name", name = "Test Immunogen Type"); data.put("Label", label = "Test Immunogen Type Label"); - Table.insert(_user, StudySchema.getInstance().getTableInfoStudyDesignImmunogenTypes(), data); + Table.insert(_user, StudyDesignSchema.getInstance().getTableInfoStudyDesignImmunogenTypes(), data); assertEquals("Unexpected study design lookup label", label, _manager.getStudyDesignImmunogenTypeLabelByName(_container, name)); assertNull("Unexpected study design lookup label", _manager.getStudyDesignImmunogenTypeLabelByName(_container, "UNK")); _lookups.put("ImmunogenType", name); data.put("Name", name = "Test Gene"); data.put("Label", label = "Test Gene Label"); - Table.insert(_user, StudySchema.getInstance().getTableInfoStudyDesignGenes(), data); + Table.insert(_user, StudyDesignSchema.getInstance().getTableInfoStudyDesignGenes(), data); assertEquals("Unexpected study design lookup label", label, _manager.getStudyDesignGeneLabelByName(_container, name)); assertNull("Unexpected study design lookup label", _manager.getStudyDesignGeneLabelByName(_container, "UNK")); _lookups.put("Gene", name); data.put("Name", name = "Test SubType"); data.put("Label", label = "Test SubType Label"); - Table.insert(_user, StudySchema.getInstance().getTableInfoStudyDesignSubTypes(), data); + Table.insert(_user, StudyDesignSchema.getInstance().getTableInfoStudyDesignSubTypes(), data); assertEquals("Unexpected study design lookup label", label, _manager.getStudyDesignSubTypeLabelByName(_container, name)); assertNull("Unexpected study design lookup label", _manager.getStudyDesignSubTypeLabelByName(_container, "UNK")); _lookups.put("SubType", name); data.put("Name", name = "Test Route"); data.put("Label", label = "Test Route Label"); - Table.insert(_user, StudySchema.getInstance().getTableInfoStudyDesignRoutes(), data); + Table.insert(_user, StudyDesignSchema.getInstance().getTableInfoStudyDesignRoutes(), data); assertEquals("Unexpected study design lookup label", label, _manager.getStudyDesignRouteLabelByName(_container, name)); assertNull("Unexpected study design lookup label", _manager.getStudyDesignRouteLabelByName(_container, "UNK")); _lookups.put("Route", name); diff --git a/study/src/org/labkey/study/query/studydesign/AbstractStudyDesignDomainKind.java b/study/src/org/labkey/study/query/studydesign/AbstractStudyDesignDomainKind.java deleted file mode 100644 index c6c8bfd4..00000000 --- a/study/src/org/labkey/study/query/studydesign/AbstractStudyDesignDomainKind.java +++ /dev/null @@ -1,195 +0,0 @@ -/* - * Copyright (c) 2013-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.study.query.studydesign; - -import org.labkey.api.data.Container; -import org.labkey.api.data.DbScope; -import org.labkey.api.data.JdbcType; -import org.labkey.api.data.PropertyStorageSpec; -import org.labkey.api.data.SQLFragment; -import org.labkey.api.exp.Handler; -import org.labkey.api.exp.Lsid; -import org.labkey.api.exp.XarContext; -import org.labkey.api.exp.XarFormatException; -import org.labkey.api.exp.api.ExperimentUrls; -import org.labkey.api.exp.property.BaseAbstractDomainKind; -import org.labkey.api.exp.property.Domain; -import org.labkey.api.exp.property.PropertyService; -import org.labkey.api.exp.xar.LsidUtils; -import org.labkey.api.query.QueryAction; -import org.labkey.api.query.QueryService; -import org.labkey.api.security.User; -import org.labkey.api.util.PageFlowUtil; -import org.labkey.api.view.ActionURL; -import org.labkey.api.writer.ContainerUser; -import org.labkey.study.StudySchema; -import org.labkey.study.query.StudyQuerySchema; - -import java.util.Collections; -import java.util.HashSet; -import java.util.LinkedHashSet; -import java.util.Set; - -public abstract class AbstractStudyDesignDomainKind extends BaseAbstractDomainKind -{ - private static final String XAR_SUBSTITUTION_SCHEMA_NAME = "SchemaName"; - private static final String XAR_SUBSTITUTION_TABLE_NAME = "TableName"; - - private static final String DOMAIN_NAMESPACE_PREFIX_TEMPLATE = "%s-${SchemaName}"; - private static final String DOMAIN_LSID_TEMPLATE = "${FolderLSIDBase}:${TableName}"; - - private static final Set BASE_FIELDS; - - static - { - Set baseFields = new LinkedHashSet<>(); - baseFields.add(createFieldSpec("Container", JdbcType.GUID).setNullable(false)); - baseFields.add(createFieldSpec("Created", JdbcType.TIMESTAMP)); - baseFields.add(createFieldSpec("CreatedBy", JdbcType.INTEGER)); - baseFields.add(createFieldSpec("Modified", JdbcType.TIMESTAMP)); - baseFields.add(createFieldSpec("ModifiedBy", JdbcType.INTEGER)); - - BASE_FIELDS = Collections.unmodifiableSet(baseFields); - } - - private final Set _standardFields = new LinkedHashSet<>(BASE_FIELDS); - private final String _tableName; - - public AbstractStudyDesignDomainKind(String tableName, Set standardFields) - { - _tableName = tableName; - _standardFields.addAll(standardFields); - } - - public Domain getDomain(Container container, String tableName) - { - String domainURI = generateDomainURI(StudyQuerySchema.SCHEMA_NAME, tableName, container, null); - return PropertyService.get().getDomain(container, domainURI); - } - - protected abstract String getNamespacePrefix(); - - protected String getTableName() - { - return _tableName; - } - - @Override - public Set getBaseProperties(Domain domain) - { - return _standardFields; - } - - @Override - public String getTypeLabel(Domain domain) - { - return domain.getName(); - } - - @Override - public SQLFragment sqlObjectIdsInDomain(Domain domain) - { - return new SQLFragment("NULL"); - } - - public Container getDomainContainer(Container c) - { - // for now create the domains per project, override to root the domains at - // a different level. - return c.getProject(); - } - - @Override - public String generateDomainURI(String schemaName, String tableName, Container c, User u) - { - return getDomainURI(schemaName, tableName, getNamespacePrefix(), getDomainContainer(c), u); - } - - private String getDomainURI(String schemaName, String tableName, String namespacePrefix, Container c, User u) - { - try - { - XarContext xc = new XarContext("Domains", c, u); - xc.addSubstitution(XAR_SUBSTITUTION_SCHEMA_NAME, schemaName); - xc.addSubstitution(XAR_SUBSTITUTION_TABLE_NAME, tableName); - - String template = String.format(DOMAIN_NAMESPACE_PREFIX_TEMPLATE, namespacePrefix); - return LsidUtils.resolveLsidFromTemplate(DOMAIN_LSID_TEMPLATE, xc, template); - } - catch (XarFormatException xfe) - { - return null; - } - } - - @Override - public ActionURL urlShowData(Domain domain, ContainerUser containerUser) - { - return QueryService.get().urlFor(containerUser.getUser(), containerUser.getContainer(), QueryAction.executeQuery, StudyQuerySchema.SCHEMA_NAME, getTableName()); - } - - @Override - public ActionURL urlEditDefinition(Domain domain, ContainerUser containerUser) - { - // since the study design domains are scoped to the project, we use the domain.getContainer() instead of containerUser.getContainer() - return PageFlowUtil.urlProvider(ExperimentUrls.class).getDomainEditorURL(domain.getContainer(), domain); - } - - @Override - public DbScope getScope() - { - return StudySchema.getInstance().getSchema().getScope(); - } - - @Override - public String getStorageSchemaName() - { - return StudySchema.getInstance().getStudyDesignSchemaName(); - } - - @Override - public Set getReservedPropertyNames(Domain domain, User user) - { - Set names = new HashSet<>(); - - for (PropertyStorageSpec spec : getBaseProperties(domain)) - names.add(spec.getName()); - - return names; - } - - protected static PropertyStorageSpec createFieldSpec(String name, JdbcType jdbcType) - { - return createFieldSpec(name, jdbcType, false, false); - } - - protected static PropertyStorageSpec createFieldSpec(String name, JdbcType jdbcType, boolean isPrimaryKey, boolean isAutoIncrement) - { - PropertyStorageSpec spec = new PropertyStorageSpec(name, jdbcType); - spec.setAutoIncrement(isAutoIncrement); - spec.setPrimaryKey(isPrimaryKey); - - return spec; - } - - @Override - public Priority getPriority(String domainURI) - { - Lsid lsid = new Lsid(domainURI); - - return lsid.getNamespacePrefix() != null && lsid.getNamespacePrefix().startsWith(getNamespacePrefix()) ? Handler.Priority.MEDIUM : null; - } -} diff --git a/study/src/org/labkey/study/query/studydesign/DefaultStudyDesignTable.java b/study/src/org/labkey/study/query/studydesign/DefaultStudyDesignTable.java deleted file mode 100644 index 7adced56..00000000 --- a/study/src/org/labkey/study/query/studydesign/DefaultStudyDesignTable.java +++ /dev/null @@ -1,209 +0,0 @@ -/* - * Copyright (c) 2013-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.study.query.studydesign; - -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; -import org.labkey.api.data.BaseColumnInfo; -import org.labkey.api.data.ColumnInfo; -import org.labkey.api.data.Container; -import org.labkey.api.data.ContainerFilter; -import org.labkey.api.data.ContainerForeignKey; -import org.labkey.api.data.DataColumn; -import org.labkey.api.data.DbScope; -import org.labkey.api.data.MutableColumnInfo; -import org.labkey.api.data.SimpleFilter; -import org.labkey.api.data.Table; -import org.labkey.api.data.TableInfo; -import org.labkey.api.exp.PropertyDescriptor; -import org.labkey.api.exp.PropertyType; -import org.labkey.api.exp.api.StorageProvisioner; -import org.labkey.api.exp.property.Domain; -import org.labkey.api.exp.property.DomainProperty; -import org.labkey.api.query.DefaultQueryUpdateService; -import org.labkey.api.query.FieldKey; -import org.labkey.api.query.FilteredTable; -import org.labkey.api.query.PdLookupForeignKey; -import org.labkey.api.query.QueryUpdateService; -import org.labkey.api.query.UserIdForeignKey; -import org.labkey.api.query.UserSchema; -import org.labkey.api.security.User; -import org.labkey.api.security.UserPrincipal; -import org.labkey.api.security.permissions.Permission; -import org.labkey.api.security.permissions.ReadPermission; -import org.labkey.api.security.roles.Role; -import org.labkey.study.StudySchema; -import org.labkey.study.query.StudyQuerySchema; - -import java.util.ArrayList; -import java.util.List; -import java.util.Set; - -/** - * Created by klum on 12/12/13. - */ -public class DefaultStudyDesignTable extends FilteredTable -{ - protected List _defaultVisibleColumns = new ArrayList<>(); - private Domain _domain; - - - protected DefaultStudyDesignTable(Domain domain, TableInfo storageTableInfo, SchemaType schema) - { - super(storageTableInfo, schema); - _domain = domain; - - for (ColumnInfo baseColumn : getRealTable().getColumns()) - { - var col = addWrapColumn(baseColumn); - - if (baseColumn.isHidden()) - col.setHidden(true); - - String propertyURI = col.getPropertyURI(); - if (null != propertyURI) - { - DomainProperty property = domain.getPropertyByURI(propertyURI); - if (property != null) - { - PropertyDescriptor pd = property.getPropertyDescriptor(); - if (pd != null) - { - if (pd.getLookupQuery() != null || pd.getConceptURI() != null) - col.setFk(PdLookupForeignKey.create(schema, pd)); - - if (pd.getPropertyType() == PropertyType.MULTI_LINE) - { - col.setDisplayColumnFactory(colInfo -> { - DataColumn dc = new DataColumn(colInfo); - dc.setPreserveNewlines(true); - return dc; - }); - } - col.setFieldKey(new FieldKey(null, pd.getName())); - } - } - } - } - _defaultVisibleColumns.add(FieldKey.fromParts("Container")); - _defaultVisibleColumns.add(FieldKey.fromParts("Created")); - _defaultVisibleColumns.add(FieldKey.fromParts("CreatedBy")); - - // setup lookups for the standard fields - var container = getMutableColumn("Container"); - - var created = getMutableColumn("Created"); - created.setFormat("DateTime"); - - var createdBy = getMutableColumn(FieldKey.fromParts("CreatedBy")); - createdBy.setLabel("Created By"); - - var modified = getMutableColumn("Modified"); - modified.setFormat("DateTime"); - - var modifiedBy = getMutableColumn(FieldKey.fromParts("ModifiedBy")); - modifiedBy.setLabel("Modified By"); - - setPublicSchemaName("study"); - - initColumns(); - } - - - protected DefaultStudyDesignTable(Domain domain, TableInfo storageTableInfo, SchemaType schema, @Nullable ContainerFilter containerFilter) - { - this(domain, storageTableInfo, schema); - if (null != containerFilter) - _setContainerFilter(containerFilter); - } - - - public static DefaultStudyDesignTable create(Domain domain, SchemaType schema, @Nullable ContainerFilter containerFilter) - { - TableInfo storageTableInfo = StorageProvisioner.createTableInfo(domain); - return new DefaultStudyDesignTable(domain, storageTableInfo, schema, containerFilter); - } - - - protected Set getContextualRoles() - { - return getUserSchema().getContextualRoles(); - } - - - @Override - public List getDefaultVisibleColumns() - { - return _defaultVisibleColumns; - } - - protected void initColumns() - { - for (var col : getMutableColumns()) - initColumn(col); - } - - // Subclasses may override this to provide customizations to the column - protected void initColumn(MutableColumnInfo col) - { - } - - - @Nullable - @Override - public Domain getDomain() - { - return _domain; - } - - @Nullable - @Override - public QueryUpdateService getUpdateService() - { - return new DefaultQueryUpdateService(this, this.getRealTable()); - } - - @Override - // ONLY OVERRIDE THIS IF TABLE SHOULD BE VISIBLE IN DATASPACE PROJECT-LEVEL CONTAINER - public boolean hasPermission(@NotNull UserPrincipal user, @NotNull Class perm) - { - // Most tables should not editable in Dataspace - if (!perm.equals(ReadPermission.class) && getContainer().isDataspace()) - return false; - return hasPermissionOverridable(user, perm); - } - - public boolean hasPermissionOverridable(UserPrincipal user, Class perm) - { - return getContainer().hasPermission(user, perm, getContextualRoles()); - } - - /** - * Deletes all data for the specified container. - * @param c - * @param user - */ - public void deleteData(Container c, User user) - { - TableInfo data = getRealTable(); - DbScope scope = StudySchema.getInstance().getSchema().getScope(); - try (DbScope.Transaction transaction = scope.ensureTransaction()) - { - Table.delete(data, new SimpleFilter().addWhereClause("Container=?", new Object[] {getContainer()})); - transaction.commit(); - } - } -} diff --git a/study/src/org/labkey/study/query/studydesign/DoseAndRouteTable.java b/study/src/org/labkey/study/query/studydesign/DoseAndRouteTable.java deleted file mode 100644 index 3f249b67..00000000 --- a/study/src/org/labkey/study/query/studydesign/DoseAndRouteTable.java +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright (c) 2016 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.study.query.studydesign; - -import org.labkey.api.data.ContainerFilter; -import org.labkey.api.data.JdbcType; -import org.labkey.api.data.SQLFragment; -import org.labkey.api.query.ExprColumn; -import org.labkey.study.StudySchema; -import org.labkey.study.query.StudyQuerySchema; - -/** - * Created by klum on 9/23/2016. - */ -public class DoseAndRouteTable extends StudyDesignLookupBaseTable -{ - public DoseAndRouteTable(StudyQuerySchema schema, ContainerFilter filter) - { - super(schema, StudySchema.getInstance().getTableInfoDoseAndRoute(), filter); - setName("DoseAndRoute"); - - // add an expr column for label to ba a concatenation of dose and route (need to keep the label generation in sync with code in DoseAndRoute.parseLabel) - String doseField = ExprColumn.STR_TABLE_ALIAS + ".dose"; - String routeField = ExprColumn.STR_TABLE_ALIAS + ".route"; - SQLFragment sql = new SQLFragment("CONCAT(CASE WHEN ").append(doseField).append(" IS NOT NULL THEN ").append(doseField).append(" ELSE '' END,") - .append("' : ',") - .append("CASE WHEN ").append(routeField).append(" IS NOT NULL THEN ").append(routeField).append(" ELSE '' END)"); - - ExprColumn labelCol = new ExprColumn(this, "Label", sql, JdbcType.VARCHAR); - labelCol.setReadOnly(true); - addColumn(labelCol); - } -} diff --git a/study/src/org/labkey/study/query/studydesign/StudyDesignAssaysTable.java b/study/src/org/labkey/study/query/studydesign/StudyDesignAssaysTable.java deleted file mode 100644 index df15df24..00000000 --- a/study/src/org/labkey/study/query/studydesign/StudyDesignAssaysTable.java +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright (c) 2013-2014 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.study.query.studydesign; - -import org.labkey.api.data.ContainerFilter; -import org.labkey.api.query.FieldKey; -import org.labkey.study.StudySchema; -import org.labkey.study.query.StudyQuerySchema; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; - -/** - * User: cnathe - * Date: 7/24/13 - */ -public class StudyDesignAssaysTable extends StudyDesignLookupBaseTable -{ - public StudyDesignAssaysTable(StudyQuerySchema schema, ContainerFilter filter) - { - super(schema, StudySchema.getInstance().getTableInfoStudyDesignAssays(), filter); - setName("StudyDesignAssays"); - - List defaultColumns = new ArrayList<>(Arrays.asList( - FieldKey.fromParts("Name"), - FieldKey.fromParts("Label"), - FieldKey.fromParts("Description"), - FieldKey.fromParts("Inactive"), - FieldKey.fromParts("Type"), - FieldKey.fromParts("TargetType"), - FieldKey.fromParts("TargetSubtype"), - FieldKey.fromParts("Platform"), - FieldKey.fromParts("Category"), - FieldKey.fromParts("TargetFunction"), - FieldKey.fromParts("LeadContributor"), - FieldKey.fromParts("Contact"), - FieldKey.fromParts("Summary"), - FieldKey.fromParts("Keywords"), - FieldKey.fromParts("Editorial"), - FieldKey.fromParts("AlternateName"), - FieldKey.fromParts("Lab"), - FieldKey.fromParts("LabPI") - )); - setDefaultVisibleColumns(defaultColumns); - } -} diff --git a/study/src/org/labkey/study/query/studydesign/StudyDesignChallengeTypesTable.java b/study/src/org/labkey/study/query/studydesign/StudyDesignChallengeTypesTable.java deleted file mode 100644 index de74a2ab..00000000 --- a/study/src/org/labkey/study/query/studydesign/StudyDesignChallengeTypesTable.java +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright (c) 2016 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.study.query.studydesign; - -import org.labkey.api.data.ContainerFilter; -import org.labkey.study.StudySchema; -import org.labkey.study.query.StudyQuerySchema; - -public class StudyDesignChallengeTypesTable extends StudyDesignLookupBaseTable -{ - public StudyDesignChallengeTypesTable(StudyQuerySchema schema, ContainerFilter filter) - { - super(schema, StudySchema.getInstance().getTableInfoStudyDesignChallengeTypes(), filter); - setName("StudyDesignChallengeTypes"); - } -} diff --git a/study/src/org/labkey/study/query/studydesign/StudyDesignGenesTable.java b/study/src/org/labkey/study/query/studydesign/StudyDesignGenesTable.java deleted file mode 100644 index 9620183e..00000000 --- a/study/src/org/labkey/study/query/studydesign/StudyDesignGenesTable.java +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright (c) 2013-2014 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.study.query.studydesign; - -import org.labkey.api.data.ContainerFilter; -import org.labkey.study.StudySchema; -import org.labkey.study.query.StudyQuerySchema; - -/** - * User: cnathe - * Date: 7/23/13 - */ -public class StudyDesignGenesTable extends StudyDesignLookupBaseTable -{ - public StudyDesignGenesTable(StudyQuerySchema schema, ContainerFilter filter) - { - super(schema, StudySchema.getInstance().getTableInfoStudyDesignGenes(), filter); - setName("StudyDesignGenes"); - } -} \ No newline at end of file diff --git a/study/src/org/labkey/study/query/studydesign/StudyDesignImmunogenTypesTable.java b/study/src/org/labkey/study/query/studydesign/StudyDesignImmunogenTypesTable.java deleted file mode 100644 index 7c73d038..00000000 --- a/study/src/org/labkey/study/query/studydesign/StudyDesignImmunogenTypesTable.java +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright (c) 2013-2014 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.study.query.studydesign; - -import org.labkey.api.data.ContainerFilter; -import org.labkey.study.StudySchema; -import org.labkey.study.query.StudyQuerySchema; - -/** - * User: cnathe - * Date: 7/22/13 - */ -public class StudyDesignImmunogenTypesTable extends StudyDesignLookupBaseTable -{ - // TODO: this should be renamed to StudyDesignProductTypesTable - public StudyDesignImmunogenTypesTable(StudyQuerySchema schema, ContainerFilter filter) - { - super(schema, StudySchema.getInstance().getTableInfoStudyDesignImmunogenTypes(), filter); - setName("StudyDesignImmunogenTypes"); - } -} diff --git a/study/src/org/labkey/study/query/studydesign/StudyDesignLabsTable.java b/study/src/org/labkey/study/query/studydesign/StudyDesignLabsTable.java deleted file mode 100644 index fb911d13..00000000 --- a/study/src/org/labkey/study/query/studydesign/StudyDesignLabsTable.java +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright (c) 2013-2014 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.study.query.studydesign; - -import org.labkey.api.data.ContainerFilter; -import org.labkey.api.query.FieldKey; -import org.labkey.study.StudySchema; -import org.labkey.study.query.StudyQuerySchema; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; - -/** - * User: cnathe - * Date: 7/24/13 - */ -public class StudyDesignLabsTable extends StudyDesignLookupBaseTable -{ - public StudyDesignLabsTable(StudyQuerySchema schema, ContainerFilter filter) - { - super(schema, StudySchema.getInstance().getTableInfoStudyDesignLabs(), filter); - setName("StudyDesignLabs"); - - List defaultColumns = new ArrayList<>(Arrays.asList( - FieldKey.fromParts("Name"), - FieldKey.fromParts("Label"), - FieldKey.fromParts("Description"), - FieldKey.fromParts("Inactive"), - FieldKey.fromParts("PI"), - FieldKey.fromParts("Summary"), - FieldKey.fromParts("Institution") - )); - setDefaultVisibleColumns(defaultColumns); - - } -} diff --git a/study/src/org/labkey/study/query/studydesign/StudyDesignLookupBaseTable.java b/study/src/org/labkey/study/query/studydesign/StudyDesignLookupBaseTable.java deleted file mode 100644 index 19260cf1..00000000 --- a/study/src/org/labkey/study/query/studydesign/StudyDesignLookupBaseTable.java +++ /dev/null @@ -1,162 +0,0 @@ -/* - * Copyright (c) 2013-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.study.query.studydesign; - -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; -import org.labkey.api.data.ColumnInfo; -import org.labkey.api.data.Container; -import org.labkey.api.data.ContainerFilter; -import org.labkey.api.data.DatabaseTableType; -import org.labkey.api.data.TableInfo; -import org.labkey.api.dataiterator.DataIteratorBuilder; -import org.labkey.api.dataiterator.DataIteratorContext; -import org.labkey.api.query.DefaultQueryUpdateService; -import org.labkey.api.query.DuplicateKeyException; -import org.labkey.api.query.FieldKey; -import org.labkey.api.query.InvalidKeyException; -import org.labkey.api.query.QueryUpdateService; -import org.labkey.api.query.QueryUpdateServiceException; -import org.labkey.api.query.ValidationException; -import org.labkey.api.query.column.BuiltInColumnTypes; -import org.labkey.api.security.User; -import org.labkey.api.security.UserPrincipal; -import org.labkey.api.security.permissions.AdminPermission; -import org.labkey.api.security.permissions.Permission; -import org.labkey.api.security.permissions.ReadPermission; -import org.labkey.study.query.BaseStudyTable; -import org.labkey.study.query.StudyQuerySchema; - -import java.sql.SQLException; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import java.util.Map; - -/** - * User: cnathe - * Date: 7/23/13 - */ -public class StudyDesignLookupBaseTable extends BaseStudyTable -{ - // Note: this has a different default container filter than BaseStudyTable - public StudyDesignLookupBaseTable(StudyQuerySchema schema, TableInfo tableInfo, ContainerFilter cf) - { - super(schema, tableInfo, schema.isDataspaceProject() ? ContainerFilter.Type.Project.create(schema) : cf); - setDescription("Contains lookup values for dropdown options in the study designer."); - - for (ColumnInfo col : getRealTable().getColumns()) - { - BuiltInColumnTypes type = BuiltInColumnTypes.findBuiltInType(col); - if (type == BuiltInColumnTypes.Container) - { - addContainerColumn(); - } - else - { - var newCol = addWrapColumn(col); - if (col.isHidden()) - newCol.setHidden(col.isHidden()); - } - } - - - List defaultColumns = new ArrayList<>(Arrays.asList( - FieldKey.fromParts("Name"), - FieldKey.fromParts("Label"), - FieldKey.fromParts("Inactive") - )); - setDefaultVisibleColumns(defaultColumns); - } - - @Override - public QueryUpdateService getUpdateService() - { - TableInfo table = getRealTable(); - if (table.getTableType() == DatabaseTableType.TABLE) - return new StudyDesignLookupsQueryUpdateService(this, table); - return null; - } - - @Override - public boolean hasPermissionOverridable(UserPrincipal user, Class perm) - { - // Only admins are allowed to insert into these tables at the project level - if (getContainer().isProject()) - return checkReadOrIsAdminPermission(user, perm); - else - return checkContainerPermission(user, perm); - } - - private class StudyDesignLookupsQueryUpdateService extends DefaultQueryUpdateService - { - public StudyDesignLookupsQueryUpdateService(TableInfo queryTable, TableInfo dbTable) - { - super(queryTable, dbTable); - } - - @Override - public int loadRows(User user, Container container, DataIteratorBuilder rows, DataIteratorContext context, @Nullable Map extraScriptContext) - { - return importRows(user, container, rows, context.getErrors(), context.getConfigParameters(), extraScriptContext); - } - - @Override - protected Map insertRow(User user, Container container, Map row) throws DuplicateKeyException, ValidationException, QueryUpdateServiceException, SQLException - { - if (container.isProject() && !hasPermission(user, AdminPermission.class)) - throw new QueryUpdateServiceException("Only admins are allowed to insert into this table at the project level."); - - validateValues(row); - return super.insertRow(user, container, row); - } - - @Override - protected Map updateRow(User user, Container container, Map row, @NotNull Map oldRow, @Nullable Map configParameters) throws InvalidKeyException, ValidationException, QueryUpdateServiceException, SQLException - { - if (container.isProject() && !hasPermission(user, AdminPermission.class)) - throw new QueryUpdateServiceException("Only admins are allowed to update records in this table at the project level."); - - validateValues(row); - return super.updateRow(user, container, row, oldRow, configParameters); - } - - @Override - protected Map deleteRow(User user, Container container, Map oldRowMap) throws InvalidKeyException, QueryUpdateServiceException, SQLException - { - if (container.isProject() && !hasPermission(user, AdminPermission.class)) - throw new QueryUpdateServiceException("Only admins are allowed to delete records from this table at the project level."); - - return super.deleteRow(user, container, oldRowMap); - } - - private void validateValues(Map row) - { - // TODO: add validation that the same key value doesn't already exist at the project level - } - } - - @Override - public boolean hasPermission(@NotNull UserPrincipal user, @NotNull Class perm) - { - if (perm.equals(ReadPermission.class)) - return hasPermissionOverridable(user, perm); - // These are editable in Dataspace, but not in a folder within a Dataspace - if (null == getContainer() || null == getContainer().getProject() || (getContainer().getProject().isDataspace() && !getContainer().isDataspace())) - return false; - return hasPermissionOverridable(user, perm); - } -} diff --git a/study/src/org/labkey/study/query/studydesign/StudyDesignRoutesTable.java b/study/src/org/labkey/study/query/studydesign/StudyDesignRoutesTable.java deleted file mode 100644 index d3fa8870..00000000 --- a/study/src/org/labkey/study/query/studydesign/StudyDesignRoutesTable.java +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright (c) 2013-2014 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.study.query.studydesign; - -import org.labkey.api.data.ContainerFilter; -import org.labkey.study.StudySchema; -import org.labkey.study.query.StudyQuerySchema; - -/** - * User: cnathe - * Date: 7/23/13 - */ -public class StudyDesignRoutesTable extends StudyDesignLookupBaseTable -{ - public StudyDesignRoutesTable(StudyQuerySchema schema, ContainerFilter filter) - { - super(schema, StudySchema.getInstance().getTableInfoStudyDesignRoutes(), filter); - setName("StudyDesignRoutes"); - } -} diff --git a/study/src/org/labkey/study/query/studydesign/StudyDesignSampleTypesTable.java b/study/src/org/labkey/study/query/studydesign/StudyDesignSampleTypesTable.java deleted file mode 100644 index f783af71..00000000 --- a/study/src/org/labkey/study/query/studydesign/StudyDesignSampleTypesTable.java +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright (c) 2013-2014 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.study.query.studydesign; - -import org.labkey.api.data.ContainerFilter; -import org.labkey.api.query.FieldKey; -import org.labkey.study.StudySchema; -import org.labkey.study.query.StudyQuerySchema; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; - -/** - * User: cnathe - * Date: 7/24/13 - */ -public class StudyDesignSampleTypesTable extends StudyDesignLookupBaseTable -{ - public StudyDesignSampleTypesTable(StudyQuerySchema schema, ContainerFilter filter) - { - super(schema, StudySchema.getInstance().getTableInfoStudyDesignSampleTypes(), filter); - setName("StudyDesignSampleTypes"); - - List defaultColumns = new ArrayList<>(Arrays.asList( - FieldKey.fromParts("Name"), - FieldKey.fromParts("PrimaryType"), - FieldKey.fromParts("ShortSampleCode"), - FieldKey.fromParts("Inactive") - )); - setDefaultVisibleColumns(defaultColumns); - } -} diff --git a/study/src/org/labkey/study/query/studydesign/StudyDesignSubTypesTable.java b/study/src/org/labkey/study/query/studydesign/StudyDesignSubTypesTable.java deleted file mode 100644 index e5abdafd..00000000 --- a/study/src/org/labkey/study/query/studydesign/StudyDesignSubTypesTable.java +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright (c) 2013-2014 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.study.query.studydesign; - -import org.labkey.api.data.ContainerFilter; -import org.labkey.study.StudySchema; -import org.labkey.study.query.StudyQuerySchema; - -/** - * User: cnathe - * Date: 7/23/13 - */ -public class StudyDesignSubTypesTable extends StudyDesignLookupBaseTable -{ - public StudyDesignSubTypesTable(StudyQuerySchema schema, ContainerFilter filter) - { - super(schema, StudySchema.getInstance().getTableInfoStudyDesignSubTypes(), filter); - setName("StudyDesignSubTypes"); - } -} \ No newline at end of file diff --git a/study/src/org/labkey/study/query/studydesign/StudyDesignUnitsTable.java b/study/src/org/labkey/study/query/studydesign/StudyDesignUnitsTable.java deleted file mode 100644 index 9abf64f6..00000000 --- a/study/src/org/labkey/study/query/studydesign/StudyDesignUnitsTable.java +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright (c) 2013-2014 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.study.query.studydesign; - -import org.labkey.api.data.ContainerFilter; -import org.labkey.study.StudySchema; -import org.labkey.study.query.StudyQuerySchema; - -/** - * User: cnathe - * Date: 7/24/13 - */ -public class StudyDesignUnitsTable extends StudyDesignLookupBaseTable -{ - public StudyDesignUnitsTable(StudyQuerySchema schema, ContainerFilter filter) - { - super(schema, StudySchema.getInstance().getTableInfoStudyDesignUnits(), filter); - setName("StudyDesignUnits"); - } -} diff --git a/study/src/org/labkey/study/query/studydesign/StudyProductAntigenDomainKind.java b/study/src/org/labkey/study/query/studydesign/StudyProductAntigenDomainKind.java deleted file mode 100644 index 926c90e3..00000000 --- a/study/src/org/labkey/study/query/studydesign/StudyProductAntigenDomainKind.java +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright (c) 2013 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.study.query.studydesign; - -import org.labkey.api.data.JdbcType; -import org.labkey.api.data.PropertyStorageSpec; -import org.labkey.study.query.StudyQuerySchema; - -import java.util.Collections; -import java.util.LinkedHashSet; -import java.util.Set; - -/** - * Created by klum on 12/13/13. - */ -public class StudyProductAntigenDomainKind extends AbstractStudyDesignDomainKind -{ - public static final String NAME = "StudyProductAntigenDomain"; - public static String NAMESPACE_PREFIX = "StudyDesign-" + NAME; - private static final Set _baseFields; - - static { - Set baseFields = new LinkedHashSet<>(); - baseFields.add(createFieldSpec("RowId", JdbcType.INTEGER, true, true)); - baseFields.add(createFieldSpec("ProductId", JdbcType.INTEGER).setNullable(false)); - baseFields.add(createFieldSpec("Gene", JdbcType.VARCHAR).setSize(200)); - baseFields.add(createFieldSpec("SubType", JdbcType.VARCHAR).setSize(200)); - baseFields.add(createFieldSpec("GenBankId", JdbcType.VARCHAR).setSize(200)); - baseFields.add(createFieldSpec("Sequence", JdbcType.VARCHAR).setSize(200)); - - _baseFields = Collections.unmodifiableSet(baseFields); - } - - public StudyProductAntigenDomainKind() - { - super(StudyQuerySchema.PRODUCT_ANTIGEN_TABLE_NAME, _baseFields); - } - - @Override - protected String getNamespacePrefix() - { - return NAMESPACE_PREFIX; - } - - @Override - public String getKindName() - { - return NAME; - } -} diff --git a/study/src/org/labkey/study/query/studydesign/StudyProductAntigenTable.java b/study/src/org/labkey/study/query/studydesign/StudyProductAntigenTable.java deleted file mode 100644 index 0e7d7568..00000000 --- a/study/src/org/labkey/study/query/studydesign/StudyProductAntigenTable.java +++ /dev/null @@ -1,130 +0,0 @@ -/* - * Copyright (c) 2013-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.study.query.studydesign; - -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; -import org.labkey.api.data.BaseColumnInfo; -import org.labkey.api.data.ContainerFilter; -import org.labkey.api.data.MutableColumnInfo; -import org.labkey.api.data.TableInfo; -import org.labkey.api.exp.api.StorageProvisioner; -import org.labkey.api.exp.property.Domain; -import org.labkey.api.query.FieldKey; -import org.labkey.api.query.LookupForeignKey; -import org.labkey.api.query.QueryService; -import org.labkey.api.query.UserSchema; -import org.labkey.api.security.UserPrincipal; -import org.labkey.api.security.permissions.Permission; -import org.labkey.api.security.permissions.ReadPermission; -import org.labkey.study.query.StudyQuerySchema; - -import java.util.ArrayList; -import java.util.List; - -/** - * Created by klum on 12/13/13. - */ -public class StudyProductAntigenTable extends DefaultStudyDesignTable -{ - static final List defaultVisibleColumns = new ArrayList<>(); - - static { - - defaultVisibleColumns.add(FieldKey.fromParts("Container")); - defaultVisibleColumns.add(FieldKey.fromParts("ProductId")); - defaultVisibleColumns.add(FieldKey.fromParts("Gene")); - defaultVisibleColumns.add(FieldKey.fromParts("SubType")); - defaultVisibleColumns.add(FieldKey.fromParts("GenBankId")); - defaultVisibleColumns.add(FieldKey.fromParts("Sequence")); - } - - - public static StudyProductAntigenTable create(Domain domain, StudyQuerySchema schema, @Nullable ContainerFilter filter) - { - TableInfo storageTableInfo = StorageProvisioner.createTableInfo(domain); - if (null == storageTableInfo) - { - throw new IllegalStateException("Could not create provisioned table for domain: " + domain.getTypeURI()); - } - return new StudyProductAntigenTable(domain, storageTableInfo, schema, filter); - } - - - private StudyProductAntigenTable(Domain domain, TableInfo storageTableInfo, StudyQuerySchema schema, @Nullable ContainerFilter containerFilter) - { - super(domain, storageTableInfo, schema, containerFilter); - - setName(StudyQuerySchema.PRODUCT_ANTIGEN_TABLE_NAME); - setDescription("Contains one row per study product antigen"); - } - - - @Override - protected void initColumn(MutableColumnInfo col) - { - if ("ProductId".equalsIgnoreCase(col.getName())) - { - col.setFk(new LookupForeignKey("RowId") - { - @Override - public TableInfo getLookupTableInfo() - { - return QueryService.get().getUserSchema(_userSchema.getUser(), _userSchema.getContainer(), StudyQuerySchema.SCHEMA_NAME).getTable(StudyQuerySchema.PRODUCT_TABLE_NAME); - } - }); - } - else if ("Gene".equalsIgnoreCase(col.getName())) - { - col.setFk(new LookupForeignKey("Name") - { - @Override - public TableInfo getLookupTableInfo() - { - return QueryService.get().getUserSchema(_userSchema.getUser(), _userSchema.getContainer(), StudyQuerySchema.SCHEMA_NAME).getTable(StudyQuerySchema.STUDY_DESIGN_GENES_TABLE_NAME); - } - }); - } - else if ("SubType".equalsIgnoreCase(col.getName())) - { - col.setFk(new LookupForeignKey("Name") - { - @Override - public TableInfo getLookupTableInfo() - { - return QueryService.get().getUserSchema(_userSchema.getUser(), _userSchema.getContainer(), StudyQuerySchema.SCHEMA_NAME).getTable(StudyQuerySchema.STUDY_DESIGN_SUB_TYPES_TABLE_NAME); - } - }); - } - } - - @Override - public List getDefaultVisibleColumns() - { - return defaultVisibleColumns; - } - - @Override - public boolean hasPermission(@NotNull UserPrincipal user, @NotNull Class perm) - { - if (perm.equals(ReadPermission.class)) - return hasPermissionOverridable(user, perm); - // This is editable in Dataspace, but not in a folder within a Dataspace - if (getContainer().getProject().isDataspace() && !getContainer().isDataspace()) - return false; - return hasPermissionOverridable(user, perm); - } -} diff --git a/study/src/org/labkey/study/query/studydesign/StudyProductDomainKind.java b/study/src/org/labkey/study/query/studydesign/StudyProductDomainKind.java deleted file mode 100644 index 41cc9640..00000000 --- a/study/src/org/labkey/study/query/studydesign/StudyProductDomainKind.java +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Copyright (c) 2013 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.study.query.studydesign; - -import org.labkey.api.data.JdbcType; -import org.labkey.api.data.PropertyStorageSpec; -import org.labkey.study.query.StudyQuerySchema; - -import java.util.Collections; -import java.util.LinkedHashSet; -import java.util.Set; - -/** - * Created by klum on 12/10/13. - */ -public class StudyProductDomainKind extends AbstractStudyDesignDomainKind -{ - public static final String NAME = "StudyProductDomain"; - public static String NAMESPACE_PREFIX = "StudyDesign-" + NAME; - private static final Set _baseFields; - - static { - Set baseFields = new LinkedHashSet<>(); - baseFields.add(createFieldSpec("RowId", JdbcType.INTEGER, true, true)); - baseFields.add(createFieldSpec("Label", JdbcType.VARCHAR).setSize(200).setNullable(false)); - baseFields.add(createFieldSpec("Role", JdbcType.VARCHAR).setSize(200)); - baseFields.add(createFieldSpec("Type", JdbcType.VARCHAR).setSize(200)); - - _baseFields = Collections.unmodifiableSet(baseFields); - } - - public StudyProductDomainKind() - { - super(StudyQuerySchema.PRODUCT_TABLE_NAME, _baseFields); - } - -/* - @Override - public Set getPropertyIndices() - { - return PageFlowUtil.set(new PropertyStorageSpec.Index(false, COLUMN_NAME_ATTACHMENT_PARENT_ENTITY_ID)); - } -*/ - - @Override - protected String getNamespacePrefix() - { - return NAMESPACE_PREFIX; - } - - @Override - public String getKindName() - { - return NAME; - } -} diff --git a/study/src/org/labkey/study/query/studydesign/StudyProductTable.java b/study/src/org/labkey/study/query/studydesign/StudyProductTable.java deleted file mode 100644 index b4996bb2..00000000 --- a/study/src/org/labkey/study/query/studydesign/StudyProductTable.java +++ /dev/null @@ -1,102 +0,0 @@ -/* - * Copyright (c) 2013-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.study.query.studydesign; - -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; -import org.labkey.api.data.ContainerFilter; -import org.labkey.api.data.MutableColumnInfo; -import org.labkey.api.data.TableInfo; -import org.labkey.api.exp.api.StorageProvisioner; -import org.labkey.api.exp.property.Domain; -import org.labkey.api.query.FieldKey; -import org.labkey.api.query.LookupForeignKey; -import org.labkey.api.query.QueryService; -import org.labkey.api.query.UserSchema; -import org.labkey.api.security.UserPrincipal; -import org.labkey.api.security.permissions.Permission; -import org.labkey.api.security.permissions.ReadPermission; -import org.labkey.study.query.StudyQuerySchema; - -import java.util.ArrayList; -import java.util.List; - -/** - * Created by klum on 12/12/13. - */ -public class StudyProductTable extends DefaultStudyDesignTable -{ - static final List defaultVisibleColumns = new ArrayList<>(); - - static { - - defaultVisibleColumns.add(FieldKey.fromParts("Container")); - defaultVisibleColumns.add(FieldKey.fromParts("Label")); - defaultVisibleColumns.add(FieldKey.fromParts("Role")); - defaultVisibleColumns.add(FieldKey.fromParts("Type")); - } - - public static StudyProductTable create(Domain domain, StudyQuerySchema schema, @Nullable ContainerFilter containerFilter) - { - TableInfo storageTableInfo = StorageProvisioner.createTableInfo(domain); - if (null == storageTableInfo) - { - throw new IllegalStateException("Could not create provisioned table for domain: " + domain.getTypeURI()); - } - return new StudyProductTable(domain, storageTableInfo, schema, containerFilter); - } - - private StudyProductTable(Domain domain, TableInfo storageTableInfo, StudyQuerySchema schema, @Nullable ContainerFilter containerFilter) - { - super(domain, storageTableInfo, schema, containerFilter); - - setName(StudyQuerySchema.PRODUCT_TABLE_NAME); - setDescription("Contains one row per study product"); - } - - @Override - protected void initColumn(MutableColumnInfo col) - { - if ("Type".equalsIgnoreCase(col.getName())) - { - col.setFk(new LookupForeignKey("Name") - { - @Override - public TableInfo getLookupTableInfo() - { - return QueryService.get().getUserSchema(_userSchema.getUser(), _userSchema.getContainer(), StudyQuerySchema.SCHEMA_NAME).getTable(StudyQuerySchema.STUDY_DESIGN_IMMUNOGEN_TYPES_TABLE_NAME); - } - }); - } - } - - @Override - public List getDefaultVisibleColumns() - { - return defaultVisibleColumns; - } - - @Override - public boolean hasPermission(@NotNull UserPrincipal user, @NotNull Class perm) - { - if (perm.equals(ReadPermission.class)) - return hasPermissionOverridable(user, perm); - // This is editable in Dataspace, but not in a folder within a Dataspace - if (getContainer().getProject().isDataspace() && !getContainer().isDataspace()) - return false; - return hasPermissionOverridable(user, perm); - } -} diff --git a/study/src/org/labkey/study/query/studydesign/StudyTreatmentDomainKind.java b/study/src/org/labkey/study/query/studydesign/StudyTreatmentDomainKind.java deleted file mode 100644 index c3d64f98..00000000 --- a/study/src/org/labkey/study/query/studydesign/StudyTreatmentDomainKind.java +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Copyright (c) 2013 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.study.query.studydesign; - -import org.labkey.api.data.JdbcType; -import org.labkey.api.data.PropertyStorageSpec; -import org.labkey.study.query.StudyQuerySchema; - -import java.util.Collections; -import java.util.LinkedHashSet; -import java.util.Set; - -/** - * User: cnathe - * Date: 12/17/13 - */ -public class StudyTreatmentDomainKind extends AbstractStudyDesignDomainKind -{ - public static final String NAME = "StudyTreatmentDomain"; - public static String NAMESPACE_PREFIX = "StudyDesign-" + NAME; - private static final Set _baseFields; - - static { - Set baseFields = new LinkedHashSet<>(); - baseFields.add(createFieldSpec("RowId", JdbcType.INTEGER, true, true)); - baseFields.add(createFieldSpec("Label", JdbcType.VARCHAR).setSize(200).setNullable(false)); - baseFields.add(createFieldSpec("Description", JdbcType.VARCHAR)); - - PropertyStorageSpec rendererTypeFieldSpec = createFieldSpec("DescriptionRendererType", JdbcType.VARCHAR).setSize(50).setNullable(false); - rendererTypeFieldSpec.setDefaultValue("TEXT_WITH_LINKS"); - baseFields.add(rendererTypeFieldSpec); - - _baseFields = Collections.unmodifiableSet(baseFields); - } - - public StudyTreatmentDomainKind() - { - super(StudyQuerySchema.TREATMENT_TABLE_NAME, _baseFields); - } - - @Override - protected String getNamespacePrefix() - { - return NAMESPACE_PREFIX; - } - - @Override - public String getKindName() - { - return NAME; - } -} diff --git a/study/src/org/labkey/study/query/studydesign/StudyTreatmentProductDomainKind.java b/study/src/org/labkey/study/query/studydesign/StudyTreatmentProductDomainKind.java deleted file mode 100644 index df1cc1c8..00000000 --- a/study/src/org/labkey/study/query/studydesign/StudyTreatmentProductDomainKind.java +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright (c) 2013 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.study.query.studydesign; - -import org.labkey.api.data.JdbcType; -import org.labkey.api.data.PropertyStorageSpec; -import org.labkey.study.query.StudyQuerySchema; - -import java.util.Collections; -import java.util.LinkedHashSet; -import java.util.Set; - -/** - * Created by klum on 12/13/13. - */ -public class StudyTreatmentProductDomainKind extends AbstractStudyDesignDomainKind -{ - public static final String NAME = "StudyTreatmentProductDomain"; - public static String NAMESPACE_PREFIX = "StudyDesign-" + NAME; - private static final Set _baseFields; - - static { - Set baseFields = new LinkedHashSet<>(); - baseFields.add(createFieldSpec("RowId", JdbcType.INTEGER, true, true)); - baseFields.add(createFieldSpec("TreatmentId", JdbcType.INTEGER).setNullable(false)); - baseFields.add(createFieldSpec("ProductId", JdbcType.INTEGER).setNullable(false)); - baseFields.add(createFieldSpec("Dose", JdbcType.VARCHAR).setSize(200)); - baseFields.add(createFieldSpec("Route", JdbcType.VARCHAR).setSize(200)); - - _baseFields = Collections.unmodifiableSet(baseFields); - } - - public StudyTreatmentProductDomainKind() - { - super(StudyQuerySchema.TREATMENT_PRODUCT_MAP_TABLE_NAME, _baseFields); - } - - @Override - protected String getNamespacePrefix() - { - return NAMESPACE_PREFIX; - } - - @Override - public String getKindName() - { - return NAME; - } -} diff --git a/study/src/org/labkey/study/query/studydesign/StudyTreatmentProductTable.java b/study/src/org/labkey/study/query/studydesign/StudyTreatmentProductTable.java deleted file mode 100644 index d2ea5c0e..00000000 --- a/study/src/org/labkey/study/query/studydesign/StudyTreatmentProductTable.java +++ /dev/null @@ -1,112 +0,0 @@ -/* - * Copyright (c) 2013-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.study.query.studydesign; - -import org.jetbrains.annotations.Nullable; -import org.labkey.api.data.BaseColumnInfo; -import org.labkey.api.data.ContainerFilter; -import org.labkey.api.data.MutableColumnInfo; -import org.labkey.api.data.TableInfo; -import org.labkey.api.exp.api.StorageProvisioner; -import org.labkey.api.exp.property.Domain; -import org.labkey.api.query.FieldKey; -import org.labkey.api.query.LookupForeignKey; -import org.labkey.api.query.QueryService; -import org.labkey.api.query.UserSchema; -import org.labkey.study.query.StudyQuerySchema; - -import java.util.ArrayList; -import java.util.List; - -/** - * Created by klum on 12/13/13. - */ -public class StudyTreatmentProductTable extends DefaultStudyDesignTable -{ - static final List defaultVisibleColumns = new ArrayList<>(); - - static { - - defaultVisibleColumns.add(FieldKey.fromParts("Container")); - defaultVisibleColumns.add(FieldKey.fromParts("TreatmentId")); - defaultVisibleColumns.add(FieldKey.fromParts("ProductId")); - defaultVisibleColumns.add(FieldKey.fromParts("Dose")); - defaultVisibleColumns.add(FieldKey.fromParts("Route")); - } - - public static StudyTreatmentProductTable create(Domain domain, StudyQuerySchema schema, @Nullable ContainerFilter filter) - { - TableInfo storageTableInfo = StorageProvisioner.createTableInfo(domain); - if (null == storageTableInfo) - { - throw new IllegalStateException("Could not create provisioned table for domain: " + domain.getTypeURI()); - } - return new StudyTreatmentProductTable(domain, storageTableInfo, schema, filter); - } - - private StudyTreatmentProductTable(Domain domain, TableInfo storageTableInfo, StudyQuerySchema schema, @Nullable ContainerFilter filter) - { - super(domain, storageTableInfo, schema, filter); - - setName(StudyQuerySchema.TREATMENT_PRODUCT_MAP_TABLE_NAME); - setDescription("Contains one row per study treatment product"); - } - - - @Override - protected void initColumn(MutableColumnInfo col) - { - if ("ProductId".equalsIgnoreCase(col.getName())) - { - col.setFk(new LookupForeignKey("RowId") - { - @Override - public TableInfo getLookupTableInfo() - { - return QueryService.get().getUserSchema(_userSchema.getUser(), _userSchema.getContainer(), StudyQuerySchema.SCHEMA_NAME).getTable(StudyQuerySchema.PRODUCT_TABLE_NAME); - } - }); - } - else if ("TreatmentId".equalsIgnoreCase(col.getName())) - { - col.setFk(new LookupForeignKey("RowId") - { - @Override - public TableInfo getLookupTableInfo() - { - return QueryService.get().getUserSchema(_userSchema.getUser(), _userSchema.getContainer(), StudyQuerySchema.SCHEMA_NAME).getTable(StudyQuerySchema.TREATMENT_TABLE_NAME); - } - }); - } - else if ("Route".equalsIgnoreCase(col.getName())) - { - col.setFk(new LookupForeignKey("Name") - { - @Override - public TableInfo getLookupTableInfo() - { - return QueryService.get().getUserSchema(_userSchema.getUser(), _userSchema.getContainer(), StudyQuerySchema.SCHEMA_NAME).getTable(StudyQuerySchema.STUDY_DESIGN_ROUTES_TABLE_NAME); - } - }); - } - } - - @Override - public List getDefaultVisibleColumns() - { - return defaultVisibleColumns; - } -} diff --git a/study/src/org/labkey/study/query/studydesign/StudyTreatmentTable.java b/study/src/org/labkey/study/query/studydesign/StudyTreatmentTable.java deleted file mode 100644 index 032dfe5f..00000000 --- a/study/src/org/labkey/study/query/studydesign/StudyTreatmentTable.java +++ /dev/null @@ -1,99 +0,0 @@ -/* - * Copyright (c) 2013-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.study.query.studydesign; - -import org.jetbrains.annotations.Nullable; -import org.labkey.api.data.ContainerFilter; -import org.labkey.api.data.MutableColumnInfo; -import org.labkey.api.data.TableInfo; -import org.labkey.api.exp.api.StorageProvisioner; -import org.labkey.api.exp.property.Domain; -import org.labkey.api.query.FieldKey; -import org.labkey.api.query.LookupForeignKey; -import org.labkey.api.wiki.WikiRendererDisplayColumn; -import org.labkey.api.wiki.WikiRendererType; -import org.labkey.api.wiki.WikiService; -import org.labkey.study.query.StudyQuerySchema; - -import java.util.ArrayList; -import java.util.List; - -/** - * User: cnathe - * Date: 12/17/13 - */ -public class StudyTreatmentTable extends DefaultStudyDesignTable -{ - static final List defaultVisibleColumns = new ArrayList<>(); - - static { - - defaultVisibleColumns.add(FieldKey.fromParts("Container")); - defaultVisibleColumns.add(FieldKey.fromParts("Label")); - defaultVisibleColumns.add(FieldKey.fromParts("Description")); - defaultVisibleColumns.add(FieldKey.fromParts("DescriptionRendererType")); - } - - public static StudyTreatmentTable create(Domain domain, StudyQuerySchema schema, @Nullable ContainerFilter filter) - { - TableInfo storageTableInfo = StorageProvisioner.createTableInfo(domain); - if (null == storageTableInfo) - { - throw new IllegalStateException("Could not create provisioned table for domain: " + domain.getTypeURI()); - } - return new StudyTreatmentTable(domain, storageTableInfo, schema, filter); - } - - - private StudyTreatmentTable(Domain domain, TableInfo storageTableInfo, StudyQuerySchema schema, @Nullable ContainerFilter filter) - { - super(domain, storageTableInfo, schema, filter); - - setName(StudyQuerySchema.TREATMENT_TABLE_NAME); - setDescription("Contains one row per study treatment"); - } - - @Override - protected void initColumn(MutableColumnInfo col) - { - if ("Description".equalsIgnoreCase(col.getName())) - { - col.setDisplayColumnFactory(colInfo -> new WikiRendererDisplayColumn(colInfo, "DescriptionRendererType", WikiRendererType.TEXT_WITH_LINKS, ctx -> "StudyTreatment")); - } - else if ("DescriptionRendererType".equalsIgnoreCase(col.getName())) - { - WikiService ws = WikiService.get(); - - if (null != ws) - { - col.setFk(new LookupForeignKey("Value") - { - @Override - public TableInfo getLookupTableInfo() - { - return ws.getRendererTypeTable(_userSchema.getUser(), _userSchema.getContainer()); - } - }); - } - } - } - - @Override - public List getDefaultVisibleColumns() - { - return defaultVisibleColumns; - } -} diff --git a/study/src/org/labkey/study/query/studydesign/StudyTreatmentVisitMapTable.java b/study/src/org/labkey/study/query/studydesign/StudyTreatmentVisitMapTable.java deleted file mode 100644 index 0685b7f0..00000000 --- a/study/src/org/labkey/study/query/studydesign/StudyTreatmentVisitMapTable.java +++ /dev/null @@ -1,93 +0,0 @@ -/* - * 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.study.query.studydesign; - -import org.labkey.api.data.ContainerFilter; -import org.labkey.api.data.TableInfo; -import org.labkey.api.query.AliasedColumn; -import org.labkey.api.query.DefaultQueryUpdateService; -import org.labkey.api.query.LookupForeignKey; -import org.labkey.api.query.QueryService; -import org.labkey.api.query.QueryUpdateService; -import org.labkey.api.security.UserPrincipal; -import org.labkey.api.security.permissions.Permission; -import org.labkey.study.StudySchema; -import org.labkey.study.query.BaseStudyTable; -import org.labkey.study.query.CohortTable; -import org.labkey.study.query.StudyQuerySchema; -import org.labkey.study.query.VisitTable; - -/** - * User: cnathe - * Date: 1/9/14 - */ -public class StudyTreatmentVisitMapTable extends BaseStudyTable -{ - public StudyTreatmentVisitMapTable(StudyQuerySchema schema, ContainerFilter cf) - { - super(schema, StudySchema.getInstance().getTableInfoTreatmentVisitMap(), cf); - setName(StudyQuerySchema.TREATMENT_VISIT_MAP_TABLE_NAME); - setDescription("Contains one row per cohort/treatment/visit mapping"); - - var cohortCol = new AliasedColumn(this, "CohortId", _rootTable.getColumn("CohortId")); - cohortCol.setFk(new LookupForeignKey("RowId") - { - @Override - public TableInfo getLookupTableInfo() - { - return new CohortTable(_userSchema, cf); - } - }); - addColumn(cohortCol); - - var treatmentCol = new AliasedColumn(this, "TreatmentId", _rootTable.getColumn("TreatmentId")); - treatmentCol.setFk(new LookupForeignKey("RowId") - { - @Override - public TableInfo getLookupTableInfo() - { - return QueryService.get().getUserSchema(_userSchema.getUser(), _userSchema.getContainer(), StudyQuerySchema.SCHEMA_NAME).getTable(StudyQuerySchema.TREATMENT_TABLE_NAME, cf); - } - }); - addColumn(treatmentCol); - - var visitCol = new AliasedColumn(this, "VisitId", _rootTable.getColumn("VisitId")); - visitCol.setFk(new LookupForeignKey("RowId") - { - @Override - public TableInfo getLookupTableInfo() - { - return new VisitTable(_userSchema, cf); - } - }); - addColumn(visitCol); - - addContainerColumn(); - } - - @Override - public QueryUpdateService getUpdateService() - { - return new DefaultQueryUpdateService(this, this.getRealTable()); - } - - @Override - protected boolean hasPermissionOverridable(UserPrincipal user, Class perm) - { - // see StudyDesignController.UpdateTreatmentScheduleAction @RequiresPermission(UpdatePermission.class) - return checkContainerPermission(user, perm); - } -} diff --git a/study/src/org/labkey/study/view/studydesign/ImmunizationScheduleWebpartFactory.java b/study/src/org/labkey/study/view/studydesign/ImmunizationScheduleWebpartFactory.java index 1a1b19f9..2c6800e2 100644 --- a/study/src/org/labkey/study/view/studydesign/ImmunizationScheduleWebpartFactory.java +++ b/study/src/org/labkey/study/view/studydesign/ImmunizationScheduleWebpartFactory.java @@ -19,8 +19,8 @@ import org.labkey.api.data.Container; import org.labkey.api.study.Study; import org.labkey.api.study.TimepointType; +import org.labkey.api.study.security.permissions.ManageStudyPermission; import org.labkey.api.view.ActionURL; -import org.labkey.api.view.BaseWebPartFactory; import org.labkey.api.view.JspView; import org.labkey.api.view.NavTree; import org.labkey.api.view.Portal; @@ -29,7 +29,6 @@ import org.labkey.study.controllers.CohortController; import org.labkey.study.controllers.StudyController; import org.labkey.study.model.StudyManager; -import org.labkey.api.study.security.permissions.ManageStudyPermission; /** * User: cnathe diff --git a/study/src/org/labkey/study/view/studydesign/VaccineDesignWebpartFactory.java b/study/src/org/labkey/study/view/studydesign/VaccineDesignWebpartFactory.java index f05d4599..0992d4b5 100644 --- a/study/src/org/labkey/study/view/studydesign/VaccineDesignWebpartFactory.java +++ b/study/src/org/labkey/study/view/studydesign/VaccineDesignWebpartFactory.java @@ -16,7 +16,6 @@ package org.labkey.study.view.studydesign; import org.jetbrains.annotations.NotNull; -import org.labkey.api.view.BaseWebPartFactory; import org.labkey.api.view.JspView; import org.labkey.api.view.Portal; import org.labkey.api.view.ViewContext; From b7e23ad7012a0291043d96e5d451d52ddea55f69 Mon Sep 17 00:00:00 2001 From: Adam Rauch Date: Tue, 25 Mar 2025 10:35:04 -0700 Subject: [PATCH 212/218] Remove unused bulk properties classes Original PR: https://github.com/LabKey/platform/pull/6478 --- .../study/view/studydesign/immunizationScheduleWebpart.jsp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/study/src/org/labkey/study/view/studydesign/immunizationScheduleWebpart.jsp b/study/src/org/labkey/study/view/studydesign/immunizationScheduleWebpart.jsp index 0b34683e..dfb86fa7 100644 --- a/study/src/org/labkey/study/view/studydesign/immunizationScheduleWebpart.jsp +++ b/study/src/org/labkey/study/view/studydesign/immunizationScheduleWebpart.jsp @@ -153,7 +153,7 @@ %>
    <% } From 70b63d60517792474c766f6ec6d804aa5677092e Mon Sep 17 00:00:00 2001 From: Adam Rauch Date: Mon, 7 Apr 2025 13:39:01 -0700 Subject: [PATCH 213/218] Remove unnecessary HttpView.currentView() casts in JSPs Original PR: https://github.com/LabKey/platform/pull/6535 --- .../org/labkey/study/view/studydesign/manageAssaySchedule.jsp | 2 +- .../org/labkey/study/view/studydesign/manageStudyProducts.jsp | 2 +- .../org/labkey/study/view/studydesign/manageTreatments.jsp | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/study/src/org/labkey/study/view/studydesign/manageAssaySchedule.jsp b/study/src/org/labkey/study/view/studydesign/manageAssaySchedule.jsp index 3c91ad26..5cd80328 100644 --- a/study/src/org/labkey/study/view/studydesign/manageAssaySchedule.jsp +++ b/study/src/org/labkey/study/view/studydesign/manageAssaySchedule.jsp @@ -38,7 +38,7 @@ } %> <% - JspView me = (JspView) HttpView.currentView(); + JspView me = HttpView.currentView(); StudyDesignController.AssayScheduleForm form = me.getModelBean(); Container c = getContainer(); diff --git a/study/src/org/labkey/study/view/studydesign/manageStudyProducts.jsp b/study/src/org/labkey/study/view/studydesign/manageStudyProducts.jsp index 329d2329..8a8f9f45 100644 --- a/study/src/org/labkey/study/view/studydesign/manageStudyProducts.jsp +++ b/study/src/org/labkey/study/view/studydesign/manageStudyProducts.jsp @@ -37,7 +37,7 @@ } %> <% - JspView me = (JspView) HttpView.currentView(); + JspView me = HttpView.currentView(); ReturnUrlForm bean = me.getModelBean(); Container c = getContainer(); diff --git a/study/src/org/labkey/study/view/studydesign/manageTreatments.jsp b/study/src/org/labkey/study/view/studydesign/manageTreatments.jsp index a32f80d1..4b77ed12 100644 --- a/study/src/org/labkey/study/view/studydesign/manageTreatments.jsp +++ b/study/src/org/labkey/study/view/studydesign/manageTreatments.jsp @@ -21,6 +21,7 @@ <%@ page import="org.labkey.api.study.Study" %> <%@ page import="org.labkey.api.study.TimepointType" %> <%@ page import="org.labkey.api.study.Visit" %> +<%@ page import="org.labkey.api.study.security.permissions.ManageStudyPermission" %> <%@ page import="org.labkey.api.view.ActionURL" %> <%@ page import="org.labkey.api.view.HttpView" %> <%@ page import="org.labkey.api.view.JspView" %> @@ -29,7 +30,6 @@ <%@ page import="org.labkey.study.controllers.StudyController" %> <%@ page import="org.labkey.study.controllers.StudyDesignController" %> <%@ page import="org.labkey.study.model.StudyManager" %> -<%@ page import="org.labkey.api.study.security.permissions.ManageStudyPermission" %> <%@ page extends="org.labkey.api.jsp.JspBase" %> <%! @Override @@ -40,7 +40,7 @@ } %> <% - JspView me = (JspView) HttpView.currentView(); + JspView me = HttpView.currentView(); StudyDesignController.ManageTreatmentsBean bean = me.getModelBean(); Container c = getContainer(); From fa896feba250e53a45bebff39c794778bd7c4e75 Mon Sep 17 00:00:00 2001 From: Karl Lum Date: Fri, 23 May 2025 08:50:06 -0700 Subject: [PATCH 214/218] Move study design features into a new module Original PR: https://github.com/LabKey/platform/pull/6686 --- studydesign/build.gradle | 11 + studydesign/module.properties | 5 + .../studydesign}/StudyDesignController.java | 1918 ++++++++------- .../labkey/studydesign/StudyDesignModule.java | 83 + .../studydesign/StudyDesignServiceImpl.java | 68 + .../model/AssaySpecimenConfigImpl.java | 599 ++--- .../model/AssaySpecimenVisitImpl.java | 224 +- .../studydesign}/model/DoseAndRoute.java | 338 +-- .../model/ProductAntigenImpl.java | 342 +-- .../studydesign}/model/ProductImpl.java | 410 ++-- .../model/StudyAssaySchedule.java | 154 +- .../studydesign/model/StudyDesignCohort.java | 94 + .../model/StudyTreatmentSchedule.java | 348 +-- .../studydesign}/model/TreatmentImpl.java | 452 ++-- .../studydesign}/model/TreatmentManager.java | 2072 +++++++++-------- .../model/TreatmentProductImpl.java | 510 ++-- .../studydesign/model}/TreatmentVisitMap.java | 54 +- .../model/TreatmentVisitMapImpl.java | 243 +- .../view}/AssayScheduleWebpartFactory.java | 144 +- .../ImmunizationScheduleWebpartFactory.java | 147 +- .../view}/StudyDesignConfigureMenuItem.java | 74 +- .../view}/StudyDesignWebpartFactory.java | 11 +- .../view}/VaccineDesignWebpartFactory.java | 96 +- .../view}/assayScheduleWebpart.jsp | 174 +- .../view}/immunizationScheduleWebpart.jsp | 348 +-- .../studydesign/view}/manageAssaySchedule.jsp | 253 +- .../studydesign/view}/manageStudyProducts.jsp | 270 +-- .../studydesign/view}/manageTreatments.jsp | 334 +-- .../view}/vaccineDesignWebpart.jsp | 176 +- .../study/vaccineDesign/AssaySchedule.js | 1318 +++++------ .../study/vaccineDesign/BaseDataView.js | 1354 +++++------ .../vaccineDesign/BaseDataViewAddVisit.js | 196 +- .../webapp/study/vaccineDesign/Models.js | 162 +- .../study/vaccineDesign/StudyProducts.js | 1014 ++++---- .../study/vaccineDesign/TreatmentDialog.js | 0 .../study/vaccineDesign/TreatmentSchedule.js | 928 ++++---- .../vaccineDesign/TreatmentScheduleBase.js | 0 .../TreatmentScheduleSingleTablePanel.js | 0 .../webapp/study/vaccineDesign/Utils.js | 422 ++-- .../study/vaccineDesign/VaccineDesign.css | 164 +- .../webapp/study/vaccineDesign/VisitWindow.js | 636 ++--- .../study/vaccineDesign/vaccineDesign.lib.xml | 36 +- 42 files changed, 8330 insertions(+), 7852 deletions(-) create mode 100644 studydesign/build.gradle create mode 100644 studydesign/module.properties rename {study/src/org/labkey/study/controllers => studydesign/src/org/labkey/studydesign}/StudyDesignController.java (78%) create mode 100644 studydesign/src/org/labkey/studydesign/StudyDesignModule.java create mode 100644 studydesign/src/org/labkey/studydesign/StudyDesignServiceImpl.java rename {study/src/org/labkey/study => studydesign/src/org/labkey/studydesign}/model/AssaySpecimenConfigImpl.java (93%) rename {study/src/org/labkey/study => studydesign/src/org/labkey/studydesign}/model/AssaySpecimenVisitImpl.java (95%) rename {study/src/org/labkey/study => studydesign/src/org/labkey/studydesign}/model/DoseAndRoute.java (93%) rename {study/src/org/labkey/study => studydesign/src/org/labkey/studydesign}/model/ProductAntigenImpl.java (94%) rename {study/src/org/labkey/study => studydesign/src/org/labkey/studydesign}/model/ProductImpl.java (95%) rename {study/src/org/labkey/study => studydesign/src/org/labkey/studydesign}/model/StudyAssaySchedule.java (94%) create mode 100644 studydesign/src/org/labkey/studydesign/model/StudyDesignCohort.java rename {study/src/org/labkey/study => studydesign/src/org/labkey/studydesign}/model/StudyTreatmentSchedule.java (85%) rename {study/src/org/labkey/study => studydesign/src/org/labkey/studydesign}/model/TreatmentImpl.java (95%) rename {study/src/org/labkey/study => studydesign/src/org/labkey/studydesign}/model/TreatmentManager.java (71%) rename {study/src/org/labkey/study => studydesign/src/org/labkey/studydesign}/model/TreatmentProductImpl.java (96%) rename {api/src/org/labkey/api/study => studydesign/src/org/labkey/studydesign/model}/TreatmentVisitMap.java (93%) rename {study/src/org/labkey/study => studydesign/src/org/labkey/studydesign}/model/TreatmentVisitMapImpl.java (93%) rename {study/src/org/labkey/study/view/studydesign => studydesign/src/org/labkey/studydesign/view}/AssayScheduleWebpartFactory.java (80%) rename {study/src/org/labkey/study/view/studydesign => studydesign/src/org/labkey/studydesign/view}/ImmunizationScheduleWebpartFactory.java (75%) rename {study/src/org/labkey/study/view/studydesign => studydesign/src/org/labkey/studydesign/view}/StudyDesignConfigureMenuItem.java (94%) rename {study/src/org/labkey/study/view/studydesign => studydesign/src/org/labkey/studydesign/view}/StudyDesignWebpartFactory.java (51%) rename {study/src/org/labkey/study/view/studydesign => studydesign/src/org/labkey/studydesign/view}/VaccineDesignWebpartFactory.java (88%) rename {study/src/org/labkey/study/view/studydesign => studydesign/src/org/labkey/studydesign/view}/assayScheduleWebpart.jsp (87%) rename {study/src/org/labkey/study/view/studydesign => studydesign/src/org/labkey/studydesign/view}/immunizationScheduleWebpart.jsp (79%) rename {study/src/org/labkey/study/view/studydesign => studydesign/src/org/labkey/studydesign/view}/manageAssaySchedule.jsp (89%) rename {study/src/org/labkey/study/view/studydesign => studydesign/src/org/labkey/studydesign/view}/manageStudyProducts.jsp (93%) rename {study/src/org/labkey/study/view/studydesign => studydesign/src/org/labkey/studydesign/view}/manageTreatments.jsp (87%) rename {study/src/org/labkey/study/view/studydesign => studydesign/src/org/labkey/studydesign/view}/vaccineDesignWebpart.jsp (86%) rename {study => studydesign}/webapp/study/vaccineDesign/AssaySchedule.js (97%) rename {study => studydesign}/webapp/study/vaccineDesign/BaseDataView.js (97%) rename {study => studydesign}/webapp/study/vaccineDesign/BaseDataViewAddVisit.js (97%) rename {study => studydesign}/webapp/study/vaccineDesign/Models.js (97%) rename {study => studydesign}/webapp/study/vaccineDesign/StudyProducts.js (97%) rename {study => studydesign}/webapp/study/vaccineDesign/TreatmentDialog.js (100%) rename {study => studydesign}/webapp/study/vaccineDesign/TreatmentSchedule.js (97%) rename {study => studydesign}/webapp/study/vaccineDesign/TreatmentScheduleBase.js (100%) rename {study => studydesign}/webapp/study/vaccineDesign/TreatmentScheduleSingleTablePanel.js (100%) rename {study => studydesign}/webapp/study/vaccineDesign/Utils.js (97%) rename {study => studydesign}/webapp/study/vaccineDesign/VaccineDesign.css (95%) rename {study => studydesign}/webapp/study/vaccineDesign/VisitWindow.js (96%) rename {study => studydesign}/webapp/study/vaccineDesign/vaccineDesign.lib.xml (98%) diff --git a/studydesign/build.gradle b/studydesign/build.gradle new file mode 100644 index 00000000..8188c8b0 --- /dev/null +++ b/studydesign/build.gradle @@ -0,0 +1,11 @@ +import org.labkey.gradle.util.BuildUtils + +plugins { + id 'org.labkey.build.module' +} + +dependencies { + BuildUtils.addLabKeyDependency(project: project, config: "implementation", depProjectPath: BuildUtils.getPlatformModuleProjectPath(project.gradle, "study"), depProjectConfig: "apiJarFile") + BuildUtils.addLabKeyDependency(project: project, config: "jspImplementation", depProjectPath: BuildUtils.getPlatformModuleProjectPath(project.gradle, "study"), depProjectConfig: "apiJarFile") + BuildUtils.addLabKeyDependency(project: project, config: "modules", depProjectPath: BuildUtils.getPlatformModuleProjectPath(project.gradle, "study"), depProjectConfig: "published", depExtension: "module") +} diff --git a/studydesign/module.properties b/studydesign/module.properties new file mode 100644 index 00000000..d600824f --- /dev/null +++ b/studydesign/module.properties @@ -0,0 +1,5 @@ +ModuleClass: org.labkey.studydesign.StudyDesignModule +License: Apache 2.0 +LicenseURL: http://www.apache.org/licenses/LICENSE-2.0 +SupportedDatabases: pgsql +ManageVersion: true diff --git a/study/src/org/labkey/study/controllers/StudyDesignController.java b/studydesign/src/org/labkey/studydesign/StudyDesignController.java similarity index 78% rename from study/src/org/labkey/study/controllers/StudyDesignController.java rename to studydesign/src/org/labkey/studydesign/StudyDesignController.java index cb3d4658..ce02bff5 100644 --- a/study/src/org/labkey/study/controllers/StudyDesignController.java +++ b/studydesign/src/org/labkey/studydesign/StudyDesignController.java @@ -1,969 +1,949 @@ -/* - * 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.study.controllers; - -import org.apache.commons.lang3.StringUtils; -import org.json.JSONArray; -import org.json.JSONObject; -import org.labkey.api.action.ApiJsonForm; -import org.labkey.api.action.ApiResponse; -import org.labkey.api.action.ApiSimpleResponse; -import org.labkey.api.action.MutatingApiAction; -import org.labkey.api.action.ReadOnlyApiAction; -import org.labkey.api.action.ReturnUrlForm; -import org.labkey.api.action.SimpleViewAction; -import org.labkey.api.data.Container; -import org.labkey.api.data.DbScope; -import org.labkey.api.data.SimpleFilter; -import org.labkey.api.data.Table; -import org.labkey.api.query.FieldKey; -import org.labkey.api.query.ValidationException; -import org.labkey.api.security.ActionNames; -import org.labkey.api.security.RequiresPermission; -import org.labkey.api.security.permissions.ReadPermission; -import org.labkey.api.security.permissions.UpdatePermission; -import org.labkey.api.study.Study; -import org.labkey.api.study.TimepointType; -import org.labkey.api.study.Visit; -import org.labkey.api.study.security.permissions.ManageStudyPermission; -import org.labkey.api.studydesign.query.StudyDesignSchema; -import org.labkey.api.util.JsonUtil; -import org.labkey.api.view.ActionURL; -import org.labkey.api.view.HttpView; -import org.labkey.api.view.JspView; -import org.labkey.api.view.NavTree; -import org.labkey.study.StudySchema; -import org.labkey.study.model.AssaySpecimenConfigImpl; -import org.labkey.study.model.AssaySpecimenVisitImpl; -import org.labkey.study.model.CohortImpl; -import org.labkey.study.model.CohortManager; -import org.labkey.study.model.DoseAndRoute; -import org.labkey.study.model.ProductAntigenImpl; -import org.labkey.study.model.ProductImpl; -import org.labkey.study.model.StudyAssaySchedule; -import org.labkey.study.model.StudyImpl; -import org.labkey.study.model.StudyManager; -import org.labkey.study.model.StudyTreatmentSchedule; -import org.labkey.study.model.TreatmentImpl; -import org.labkey.study.model.TreatmentManager; -import org.labkey.study.model.TreatmentProductImpl; -import org.labkey.study.model.TreatmentVisitMapImpl; -import org.labkey.study.model.VisitImpl; -import org.labkey.study.visitmanager.VisitManager; -import org.springframework.validation.BindException; -import org.springframework.validation.Errors; -import org.springframework.web.servlet.ModelAndView; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.stream.Collectors; - -/** - * User: cnathe - * Date: 1/16/14 - */ -public class StudyDesignController extends BaseStudyController -{ - private static final ActionResolver ACTION_RESOLVER = new DefaultActionResolver(StudyDesignController.class); - - public StudyDesignController() - { - setActionResolver(ACTION_RESOLVER); - } - - @ActionNames("manageAssaySchedule, manageAssaySpecimen") - @RequiresPermission(UpdatePermission.class) - public class ManageAssayScheduleAction extends SimpleViewAction - { - @Override - public ModelAndView getView(AssayScheduleForm form, BindException errors) - { - return new JspView<>("/org/labkey/study/view/studydesign/manageAssaySchedule.jsp", form); - } - - @Override - public void addNavTrail(NavTree root) - { - setHelpTopic("manageAssaySchedule"); - if (getContainer().hasPermission(getUser(), ManageStudyPermission.class)) - root.addChild("Manage Study", new ActionURL(StudyController.ManageStudyAction.class, getContainer())); - root.addChild("Manage Assay Schedule"); - } - } - - public static class AssayScheduleForm extends ReturnUrlForm - { - private boolean useAlternateLookupFields; - - public boolean isUseAlternateLookupFields() - { - return useAlternateLookupFields; - } - - public void setUseAlternateLookupFields(boolean useAlternateLookupFields) - { - this.useAlternateLookupFields = useAlternateLookupFields; - } - } - - @RequiresPermission(UpdatePermission.class) - public class ManageStudyProductsAction extends SimpleViewAction - { - @Override - public ModelAndView getView(ReturnUrlForm form, BindException errors) - { - return new JspView<>("/org/labkey/study/view/studydesign/manageStudyProducts.jsp", form); - } - - @Override - public void addNavTrail(NavTree root) - { - setHelpTopic("studyProducts"); - if (getContainer().hasPermission(getUser(), ManageStudyPermission.class)) - root.addChild("Manage Study", new ActionURL(StudyController.ManageStudyAction.class, getContainer())); - root.addChild("Manage Study Products"); - } - } - - public static class ManageTreatmentsBean extends ReturnUrlForm - { - private boolean _singleTable; - - public boolean isSingleTable() - { - return _singleTable; - } - - public void setSingleTable(boolean singleTable) - { - _singleTable = singleTable; - } - } - - @RequiresPermission(UpdatePermission.class) - public class ManageTreatmentsAction extends SimpleViewAction - { - @Override - public ModelAndView getView(ManageTreatmentsBean form, BindException errors) - { - // if the singleTable param is not explicitly set, do a container check - if (getViewContext().getRequest().getParameter("singleTable") == null) - form.setSingleTable(getContainer().hasActiveModuleByName("viscstudies")); - - return new JspView<>("/org/labkey/study/view/studydesign/manageTreatments.jsp", form); - } - - @Override - public void addNavTrail(NavTree root) - { - setHelpTopic("manageTreatments"); - if (getContainer().hasPermission(getUser(), ManageStudyPermission.class)) - root.addChild("Manage Study", new ActionURL(StudyController.ManageStudyAction.class, getContainer())); - root.addChild("Manage Treatments"); - } - } - - @RequiresPermission(ReadPermission.class) - public class GetStudyProducts extends ReadOnlyApiAction - { - private StudyImpl _study; - - @Override - public void validateForm(GetStudyProductsForm form, Errors errors) - { - _study = getStudy(getContainer()); - if (_study == null) - errors.reject(ERROR_MSG, "A study does not exist in this folder"); - } - - @Override - public ApiResponse execute(GetStudyProductsForm form, BindException errors) - { - ApiSimpleResponse resp = new ApiSimpleResponse(); - - List> productList = new ArrayList<>(); - List studyProducts = TreatmentManager.getInstance().getStudyProducts(getContainer(), getUser(), form.getRole(), form.getRowId()); - for (ProductImpl product : studyProducts) - { - // note: we are currently only including the base fields for this extensible table - Map productProperties = product.serialize(); - - List> productAntigenList = new ArrayList<>(); - List studyProductAntigens = TreatmentManager.getInstance().getStudyProductAntigens(getContainer(), getUser(), product.getRowId()); - for (ProductAntigenImpl antigen : studyProductAntigens) - { - // note: we are currently only including the base fields for this extensible table - productAntigenList.add(antigen.serialize()); - } - productProperties.put("Antigens", productAntigenList); - - // get dose and route information associated with this product - List> doseAndRoutes = TreatmentManager.getInstance().getStudyProductsDoseAndRoute(getContainer(), getUser(), product.getRowId()) - .stream() - .map(DoseAndRoute::serialize) - .collect(Collectors.toList()); - productProperties.put("DoseAndRoute", doseAndRoutes); - productList.add(productProperties); - } - - resp.put("success", true); - resp.put("products", productList); - - return resp; - } - } - - public static class GetStudyProductsForm - { - private Integer _rowId; - private String _role; - - public Integer getRowId() - { - return _rowId; - } - - public void setRowId(Integer rowId) - { - _rowId = rowId; - } - - public String getRole() - { - return _role; - } - - public void setRole(String role) - { - _role = role; - } - } - - @RequiresPermission(ReadPermission.class) - public class GetStudyTreatments extends ReadOnlyApiAction - { - private StudyImpl _study; - - @Override - public void validateForm(GetStudyTreatmentsForm form, Errors errors) - { - _study = getStudy(getContainer()); - if (_study == null) - errors.reject(ERROR_MSG, "A study does not exist in this folder"); - } - - @Override - public ApiResponse execute(GetStudyTreatmentsForm form, BindException errors) - { - ApiSimpleResponse resp = new ApiSimpleResponse(); - - List> treatmentList = new ArrayList<>(); - List studyTreatments = TreatmentManager.getInstance().getStudyTreatments(getContainer(), getUser()); - for (TreatmentImpl treatment : studyTreatments) - { - if (form.getTreatmentId() > 0 && form.getTreatmentId() != treatment.getRowId()) - continue; - - Map treatmentProperties = treatment.serialize(); - - List> treatmentProductList = new ArrayList<>(); - List studyTreatmentProducts = TreatmentManager.getInstance().getStudyTreatmentProducts(getContainer(), getUser(), treatment.getRowId(), treatment.getProductSort()); - for (TreatmentProductImpl treatmentProduct : studyTreatmentProducts) - { - // note: we are currently only including the base fields for this extensible table - Map treatmentProductProperties = treatmentProduct.serialize(); - - // add the product label and role for convenience, to prevent the need for another round trip to the server - List products = TreatmentManager.getInstance().getStudyProducts(getContainer(), getUser(), null, treatmentProduct.getProductId()); - if (products.size() == 1) - { - treatmentProductProperties.put("ProductId/Label", products.get(0).getLabel()); - treatmentProductProperties.put("ProductId/Role", products.get(0).getRole()); - } - - treatmentProductList.add(treatmentProductProperties); - } - - if (!form.isSplitByRole()) - { - treatmentProperties.put("Products", treatmentProductList); - } - else - { - Map>> treatmentProductsListByRole = new HashMap<>(); - for (Map productProperties : treatmentProductList) - { - String role = productProperties.get("ProductId/Role").toString(); - if (!treatmentProductsListByRole.containsKey(role)) - treatmentProductsListByRole.put(role, new ArrayList<>()); - - treatmentProductsListByRole.get(role).add(productProperties); - } - - for (Map.Entry>> entry : treatmentProductsListByRole.entrySet()) - treatmentProperties.put(entry.getKey(), entry.getValue()); - } - - treatmentList.add(treatmentProperties); - } - - resp.put("success", true); - resp.put("treatments", treatmentList); - - return resp; - } - } - - private static class GetStudyTreatmentsForm - { - private boolean _splitByRole; - - private int treatmentId; - - public boolean isSplitByRole() - { - return _splitByRole; - } - - public void setSplitByRole(boolean splitByRole) - { - _splitByRole = splitByRole; - } - - public int getTreatmentId() - { - return treatmentId; - } - - public void setTreatmentId(int treatmentId) - { - this.treatmentId = treatmentId; - } - } - - @RequiresPermission(ReadPermission.class) - public class GetStudyTreatmentSchedule extends ReadOnlyApiAction - { - private StudyImpl _study; - - @Override - public void validateForm(Object form, Errors errors) - { - _study = getStudy(getContainer()); - if (_study == null) - errors.reject(ERROR_MSG, "A study does not exist in this folder"); - } - - @Override - public ApiResponse execute(Object form, BindException errors) - { - ApiSimpleResponse resp = new ApiSimpleResponse(); - StudyTreatmentSchedule treatmentSchedule = new StudyTreatmentSchedule(getContainer()); - - // include all cohorts for the study, regardless of it they have associated visits or not - treatmentSchedule.setCohorts(StudyManager.getInstance().getCohorts(getContainer(), getUser())); - resp.put("cohorts", treatmentSchedule.serializeCohortMapping()); - - // include all visits from the study, ordered by visit display order - treatmentSchedule.setVisits(StudyManager.getInstance().getVisits(_study, Visit.Order.DISPLAY)); - resp.put("visits", treatmentSchedule.serializeVisits()); - - resp.put("success", true); - return resp; - } - } - - @RequiresPermission(UpdatePermission.class) - public class UpdateStudyProductsAction extends MutatingApiAction - { - @Override - public void validateForm(StudyProductsForm form, Errors errors) - { - if (form.getProducts() == null) - errors.reject(ERROR_MSG, "No study products provided."); - - // label field is required - for (ProductImpl product : form.getProducts()) - { - if (product.getLabel() == null || StringUtils.isEmpty(product.getLabel().trim())) - { - errors.reject(ERROR_MSG, "Label is a required field for all study products."); - break; - } - } - } - - @Override - public ApiResponse execute(StudyProductsForm form, BindException errors) throws Exception - { - ApiSimpleResponse response = new ApiSimpleResponse(); - Study study = StudyManager.getInstance().getStudy(getContainer()); - - if (study != null) - { - StudySchema schema = StudySchema.getInstance(); - - try (DbScope.Transaction transaction = schema.getSchema().getScope().ensureTransaction()) - { - updateProducts(form.getProducts()); - transaction.commit(); - } - - response.put("success", true); - return response; - } - else - throw new IllegalStateException("A study does not exist in this folder"); - } - - private void updateProducts(List products) throws Exception - { - // insert new study products and update any existing ones - List productRowIds = new ArrayList<>(); - for (ProductImpl product : products) - { - Integer updatedRowId = TreatmentManager.getInstance().saveStudyProduct(getContainer(), getUser(), product); - if (updatedRowId != null) - { - productRowIds.add(updatedRowId); - - updateProductAntigens(updatedRowId, product.getAntigens()); - updateProductDoseAndRoutes(updatedRowId, product.getDoseAndRoutes()); - } - } - - // delete any other study products, not included in the insert/update list, by RowId for this container - for (ProductImpl product : TreatmentManager.getInstance().getFilteredStudyProducts(getContainer(), getUser(), productRowIds)) - TreatmentManager.getInstance().deleteStudyProduct(getContainer(), getUser(), product.getRowId()); - } - - private void updateProductAntigens(int productId, List antigens) throws Exception - { - // insert new study products antigens and update any existing ones - List antigenRowIds = new ArrayList<>(); - for (ProductAntigenImpl antigen : antigens) - { - // make sure the productId is set based on the product rowId - antigen.setProductId(productId); - - Integer updatedRowId = TreatmentManager.getInstance().saveStudyProductAntigen(getContainer(), getUser(), antigen); - if (updatedRowId != null) - antigenRowIds.add(updatedRowId); - } - - // delete any other study products antigens, not included in the insert/update list, for the given productId - for (ProductAntigenImpl antigen : TreatmentManager.getInstance().getFilteredStudyProductAntigens(getContainer(), getUser(), productId, antigenRowIds)) - TreatmentManager.getInstance().deleteStudyProductAntigen(getContainer(), getUser(), antigen.getRowId()); - } - - private void updateProductDoseAndRoutes(int productId, List doseAndRoutes) - { - // get existing dose and routes - Set existingDoseAndRoutes = TreatmentManager.getInstance().getStudyProductsDoseAndRoute(getContainer(), getUser(), productId) - .stream() - .map(DoseAndRoute::getRowId) - .collect(Collectors.toSet()); - - try (DbScope.Transaction transaction = StudySchema.getInstance().getScope().ensureTransaction()) - { - for (DoseAndRoute doseAndRoute : doseAndRoutes) - { - // dose and route both can't be blank - if (doseAndRoute.getDose() != null || doseAndRoute.getRoute() != null) - { - doseAndRoute.setProductId(productId); - existingDoseAndRoutes.remove(doseAndRoute.getRowId()); - TreatmentManager.getInstance().saveStudyProductDoseAndRoute(getContainer(), getUser(), doseAndRoute); - } - } - - // remove deleted dose and routes - if (!existingDoseAndRoutes.isEmpty()) - { - SimpleFilter filter = new SimpleFilter(); - filter.addInClause(FieldKey.fromParts("RowId"), existingDoseAndRoutes); - Table.delete(StudyDesignSchema.getInstance().getTableInfoDoseAndRoute(), filter); - } - transaction.commit(); - } - } - } - - public static class StudyProductsForm implements ApiJsonForm - { - private List _products; - - public List getProducts() - { - return _products; - } - - public void setProducts(List products) - { - _products = products; - } - - @Override - public void bindJson(JSONObject json) - { - Container container = HttpView.currentContext().getContainer(); - - JSONArray productsJSON = json.optJSONArray("products"); - if (productsJSON != null) - { - _products = new ArrayList<>(); - for (JSONObject product : JsonUtil.toJSONObjectList(productsJSON)) - _products.add(ProductImpl.fromJSON(product, container)); - } - } - } - - @RequiresPermission(UpdatePermission.class) - public class UpdateTreatmentsAction extends MutatingApiAction - { - @Override - public ApiResponse execute(StudyTreatmentSchedule form, BindException errors) throws Exception - { - ApiSimpleResponse response = new ApiSimpleResponse(); - Study study = StudyManager.getInstance().getStudy(getContainer()); - - if (study != null) - { - StudySchema schema = StudySchema.getInstance(); - List treatmentIds; - try (DbScope.Transaction transaction = schema.getSchema().getScope().ensureTransaction()) - { - treatmentIds = updateTreatments(form.getTreatments()); - transaction.commit(); - } - - response.put("success", true); - response.put("treatmentIds", treatmentIds); - return response; - } - else - throw new IllegalStateException("A study does not exist in this folder"); - } - - private Integer getExistingTreatmentId(TreatmentImpl treatment) - { - if (treatment == null) - return -1; - List studyTreatments = TreatmentManager.getInstance().getStudyTreatments(getContainer(), getUser()); - for (TreatmentImpl existingTreatment : studyTreatments) - { - List studyTreatmentProducts = TreatmentManager.getInstance().getStudyTreatmentProducts(getContainer(), getUser(), existingTreatment.getRowId(), existingTreatment.getProductSort()); - for (TreatmentProductImpl product : studyTreatmentProducts) - { - product.serialize(); - } - existingTreatment.setTreatmentProducts(studyTreatmentProducts); - if (treatment.isSameTreatmentProductsWith(existingTreatment)) - return existingTreatment.getRowId(); - } - return -1; - } - - private List updateTreatments(List treatments) throws Exception - { - List updatedRowIds = new ArrayList<>(); - for (TreatmentImpl treatment : treatments) - { - Integer updatedRowId = getExistingTreatmentId(treatment); - if (updatedRowId == null || updatedRowId <= 0) - { - updatedRowId = TreatmentManager.getInstance().saveTreatment(getContainer(), getUser(), treatment); - if (updatedRowId != null) - { - TreatmentManager.getInstance().updateTreatmentProducts(updatedRowId, treatment.getTreatmentProducts(), getContainer(), getUser()); - } - } - updatedRowIds.add(updatedRowId); - } - return updatedRowIds; - } - } - - @RequiresPermission(UpdatePermission.class) - public class UpdateTreatmentScheduleAction extends MutatingApiAction - { - private Map _tempTreatmentIdMap = new HashMap<>(); - private Set usedTreatmentIds = new HashSet<>(); // treatmentIds referenced in single table Treatment Schedule UI - private List treatmentRowIds = new ArrayList<>(); // treatmentIds defined in 2 table UI's Treatment section - private List cohortRowIds = new ArrayList<>(); - - @Override - public void validateForm(StudyTreatmentSchedule form, Errors errors) - { - // validate that each treatment has a label - for (TreatmentImpl treatment : form.getTreatments()) - { - if (treatment.getLabel() == null || StringUtils.isEmpty(treatment.getLabel().trim())) - errors.reject(ERROR_MSG, "Label is a required field for all treatments."); - - // validate that each treatment product mapping has a selected product - for (TreatmentProductImpl treatmentProduct : treatment.getTreatmentProducts()) - { - if (treatmentProduct.getProductId() <= 0) - errors.reject(ERROR_MSG, "Each treatment product must have a selected study product."); - } - } - - // validate that each cohort has a label, is unique, and has a valid subject count value - for (CohortImpl cohort : form.getCohorts()) - { - if (cohort.getLabel() == null || StringUtils.isEmpty(cohort.getLabel().trim())) - errors.reject(ERROR_MSG, "Label is a required field for all cohorts."); - - CohortImpl cohortByLabel = StudyManager.getInstance().getCohortByLabel(getContainer(), getUser(), cohort.getLabel()); - if (cohort.getRowId() > 0) - { - CohortImpl cohortByRowId = StudyManager.getInstance().getCohortForRowId(getContainer(), getUser(), cohort.getRowId()); - if (cohortByRowId != null && cohortByLabel != null && cohortByRowId.getRowId() != cohortByLabel.getRowId()) - errors.reject(ERROR_MSG, "A cohort with the label '" + cohort.getLabel() + "' already exists in this study."); - } - else if (cohortByLabel != null) - { - errors.reject(ERROR_MSG, "A cohort with the label '" + cohort.getLabel() + "' already exists in this study."); - } - - if (cohort.getSubjectCount() != null) - { - if (cohort.getSubjectCount() < 0) - errors.reject(ERROR_MSG, "Cohort subject count values must be a positive integer."); - if (cohort.getSubjectCount() == Integer.MAX_VALUE) - errors.reject(ERROR_MSG, "Cohort subject count value larger than the max value allowed."); - } - } - } - - @Override - public ApiResponse execute(StudyTreatmentSchedule form, BindException errors) throws Exception - { - ApiSimpleResponse response = new ApiSimpleResponse(); - Study study = StudyManager.getInstance().getStudy(getContainer()); - - if (study != null) - { - StudySchema schema = StudySchema.getInstance(); - - try (DbScope.Transaction transaction = schema.getSchema().getScope().ensureTransaction()) - { - updateTreatments(form.getTreatments()); - updateCohorts(form.getCohorts(), study); - cleanTreatments(); - cleanCohorts(); - transaction.commit(); - } - - response.put("success", true); - return response; - } - else - throw new IllegalStateException("A study does not exist in this folder"); - } - - private void cleanCohorts() throws ValidationException - { - // delete any other study cohorts, not included in the insert/update list, by RowId for this container - for (CohortImpl existingCohort : StudyManager.getInstance().getCohorts(getContainer(), getUser())) - { - if (!cohortRowIds.contains(existingCohort.getRowId())) - { - if (!existingCohort.isInUse()) - StudyManager.getInstance().deleteCohort(existingCohort); - else - throw new ValidationException("Unable to delete in-use cohort: " + existingCohort.getLabel()); - } - } - - } - - private void cleanTreatments() - { - // delete any other study treatments, not included in the insert/update list, by RowId for this container - for (TreatmentImpl treatment : TreatmentManager.getInstance().getFilteredTreatments(getContainer(), getUser(), treatmentRowIds, usedTreatmentIds)) - TreatmentManager.getInstance().deleteTreatment(getContainer(), getUser(), treatment.getRowId()); - - } - - private void updateTreatments(List treatments) throws Exception - { - // insert new study treatments and update any existing ones - - for (TreatmentImpl treatment : treatments) - { - Integer updatedRowId = TreatmentManager.getInstance().saveTreatment(getContainer(), getUser(), treatment); - if (updatedRowId != null) - { - treatmentRowIds.add(updatedRowId); - - if (treatment.getTempRowId() != null) - _tempTreatmentIdMap.put(treatment.getTempRowId(), updatedRowId); - - TreatmentManager.getInstance().updateTreatmentProducts(updatedRowId, treatment.getTreatmentProducts(), getContainer(), getUser()); - } - } - } - - private void updateCohorts(Collection cohorts, Study study) throws ValidationException - { - // insert new cohorts and update any existing ones - for (CohortImpl cohort : cohorts) - { - if (cohort.getRowId() > 0) - { - CohortImpl updatedCohort = StudyManager.getInstance().getCohortForRowId(getContainer(), getUser(), cohort.getRowId()); - updatedCohort = updatedCohort.createMutable(); - updatedCohort.setLabel(cohort.getLabel()); - updatedCohort.setSubjectCount(cohort.getSubjectCount()); - StudyManager.getInstance().updateCohort(getUser(), updatedCohort); - cohortRowIds.add(updatedCohort.getRowId()); - } - else - { - CohortImpl newCohort = CohortManager.getInstance().createCohort(study, getUser(), cohort.getLabel(), true, cohort.getSubjectCount(), null); - cohortRowIds.add(newCohort.getRowId()); - // stash the new cohort RowId in the original cohort instance - cohort.setRowId(newCohort.getRowId()); - } - - updateTreatmentVisitMap(cohort.getRowId(), cohort.getTreatmentVisitMap()); - } - } - - private void updateTreatmentVisitMap(int cohortId, List treatmentVisitMaps) - { - for (TreatmentVisitMapImpl visitMap : treatmentVisitMaps) - { - usedTreatmentIds.add(visitMap.getTreatmentId()); - } - - // the mapping that is passed in will have all of the current treatment/visit maps, so we will compare - // this set with the set from the DB and if they are different, replace all - List existingVisitMaps = TreatmentManager.getInstance().getStudyTreatmentVisitMap(getContainer(), cohortId); - boolean visitMapsDiffer = existingVisitMaps.size() != treatmentVisitMaps.size(); - if (!visitMapsDiffer) - { - for (TreatmentVisitMapImpl newVisitMap : treatmentVisitMaps) - { - newVisitMap.setContainer(getContainer()); - newVisitMap.setCohortId(cohortId); - - if (!existingVisitMaps.contains(newVisitMap)) - { - visitMapsDiffer = true; - break; - } - } - } - - // if we have differences, replace all at this point - if (visitMapsDiffer) - { - TreatmentManager.getInstance().deleteTreatmentVisitMapForCohort(getContainer(), cohortId); - for (TreatmentVisitMapImpl newVisitMap : treatmentVisitMaps) - { - // if the treatmentId used here was from a treatment that was created as part of this transaction, - // lookup the new treatment record RowId from the tempRowId - if (newVisitMap.getTempTreatmentId() != null && _tempTreatmentIdMap.containsKey(newVisitMap.getTempTreatmentId())) - newVisitMap.setTreatmentId(_tempTreatmentIdMap.get(newVisitMap.getTempTreatmentId())); - - if (cohortId > 0 && newVisitMap.getVisitId() > 0 && newVisitMap.getTreatmentId() > 0) - TreatmentManager.getInstance().insertTreatmentVisitMap(getUser(), getContainer(), cohortId, newVisitMap.getVisitId(), newVisitMap.getTreatmentId()); - } - } - } - } - - @RequiresPermission(UpdatePermission.class) - public class CreateVisitAction extends MutatingApiAction - { - @Override - public void validateForm(VisitForm form, Errors errors) - { - StudyImpl study = getStudyRedirectIfNull(); - boolean isDateBased = study.getTimepointType() == TimepointType.DATE; - - form.validate(errors, study); - if (errors.getErrorCount() > 0) - return; - - //check for overlapping visits - VisitManager visitMgr = StudyManager.getInstance().getVisitManager(study); - if (null != visitMgr) - { - String range = isDateBased ? "day range" : "sequence range"; - if (visitMgr.isVisitOverlapping(form.getBean())) - errors.reject(null, "The visit " + range + " provided overlaps with an existing visit in this study. Please enter a different " + range + "."); - } - } - - @Override - public ApiResponse execute(VisitForm form, BindException errors) - { - ApiSimpleResponse response = new ApiSimpleResponse(); - - VisitImpl visit = form.getBean(); - visit = StudyManager.getInstance().createVisit(getStudyThrowIfNull(), getUser(), visit); - - response.put("RowId", visit.getRowId()); - response.put("Label", visit.getDisplayString()); - response.put("SequenceNumMin", visit.getSequenceNumMin()); - response.put("DisplayOrder", visit.getDisplayOrder()); - response.put("Included", true); - response.put("success", true); - - return response; - } - } - - @RequiresPermission(UpdatePermission.class) - public class UpdateAssayPlanAction extends MutatingApiAction - { - @Override - public ApiResponse execute(AssayPlanForm form, BindException errors) - { - ApiSimpleResponse response = new ApiSimpleResponse(); - - StudyImpl study = StudyManager.getInstance().getStudy(getContainer()); - if (study != null) - { - study = study.createMutable(); - study.setAssayPlan(form.getAssayPlan()); - StudyManager.getInstance().updateStudy(getUser(), study); - response.put("success", true); - } - else - { - response.put("success", false); - } - - return response; - } - } - - private static class AssayPlanForm - { - private String _assayPlan; - - public AssayPlanForm() - {} - - public String getAssayPlan() - { - return _assayPlan; - } - - public void setAssayPlan(String assayPlan) - { - _assayPlan = assayPlan; - } - } - - @RequiresPermission(UpdatePermission.class) - public class UpdateAssayScheduleAction extends MutatingApiAction - { - @Override - public void validateForm(StudyAssaySchedule form, Errors errors) - { - // validate that each assay configuration has an AssayName - for (AssaySpecimenConfigImpl assay : form.getAssays()) - { - if (assay.getAssayName() == null || StringUtils.isEmpty(assay.getAssayName().trim())) - errors.reject(ERROR_MSG, "Assay Name is a required field for all assay configurations."); - - if (assay.getSampleQuantity() != null && assay.getSampleQuantity() < 0) - errors.reject(ERROR_MSG, "Assay sample quantity value must be a positive number."); - } - } - - @Override - public ApiResponse execute(StudyAssaySchedule form, BindException errors) throws Exception - { - ApiSimpleResponse response = new ApiSimpleResponse(); - StudyImpl study = StudyManager.getInstance().getStudy(getContainer()); - - if (study != null) - { - StudySchema schema = StudySchema.getInstance(); - - try (DbScope.Transaction transaction = schema.getSchema().getScope().ensureTransaction()) - { - updateAssays(form.getAssays()); - updateAssayPlan(study, form.getAssayPlan()); - transaction.commit(); - } - - response.put("success", true); - return response; - } - else - throw new IllegalStateException("A study does not exist in this folder"); - } - - private void updateAssays(List assays) throws Exception - { - // insert new assaySpecimens and update any existing ones - List assaySpecimenRowIds = new ArrayList<>(); - for (AssaySpecimenConfigImpl assay : assays) - { - Integer updatedRowId = TreatmentManager.getInstance().saveAssaySpecimen(getContainer(), getUser(), assay); - if (updatedRowId != null) - { - assaySpecimenRowIds.add(updatedRowId); - - updateAssayVisitMap(updatedRowId, assay.getAssayVisitMap()); - } - } - - // delete any other assaySpecimens, not included in the insert/update list, by RowId for this container - for (AssaySpecimenConfigImpl assaySpecimen : TreatmentManager.getInstance().getFilteredAssaySpecimens(getContainer(), assaySpecimenRowIds)) - TreatmentManager.getInstance().deleteAssaySpecimen(getContainer(), getUser(), assaySpecimen.getRowId()); - } - - private void updateAssayVisitMap(int assaySpecimenId, List assayVisitMaps) throws Exception - { - List assaySpecimenVisitIds = new ArrayList<>(); - if (assayVisitMaps != null && !assayVisitMaps.isEmpty()) - { - for (AssaySpecimenVisitImpl assaySpecimenVisit : assayVisitMaps) - { - assaySpecimenVisit.setAssaySpecimenId(assaySpecimenId); - - Integer updatedRowId = TreatmentManager.getInstance().saveAssaySpecimenVisit(getContainer(), getUser(), assaySpecimenVisit); - assaySpecimenVisitIds.add(updatedRowId); - } - } - - // delete any other assaySpecimenVisits, not included in the insert/update list, by RowId for this container and assaySpecimenId - for (AssaySpecimenVisitImpl assaySpecimenVisit : TreatmentManager.getInstance().getFilteredAssaySpecimenVisits(getContainer(), assaySpecimenId, assaySpecimenVisitIds)) - TreatmentManager.getInstance().deleteAssaySpecimenVisit(getContainer(), getUser(), assaySpecimenVisit.getRowId()); - } - - private void updateAssayPlan(StudyImpl study, String plan) - { - study = study.createMutable(); - study.setAssayPlan(plan); - StudyManager.getInstance().updateStudy(getUser(), study); - } - } -} +/* + * 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.studydesign; + +import org.apache.commons.lang3.StringUtils; +import org.jetbrains.annotations.Nullable; +import org.json.JSONArray; +import org.json.JSONObject; +import org.labkey.api.action.ApiJsonForm; +import org.labkey.api.action.ApiResponse; +import org.labkey.api.action.ApiSimpleResponse; +import org.labkey.api.action.MutatingApiAction; +import org.labkey.api.action.ReadOnlyApiAction; +import org.labkey.api.action.ReturnUrlForm; +import org.labkey.api.action.SimpleViewAction; +import org.labkey.api.action.SpringActionController; +import org.labkey.api.data.Container; +import org.labkey.api.data.DbScope; +import org.labkey.api.data.SimpleFilter; +import org.labkey.api.data.Table; +import org.labkey.api.query.FieldKey; +import org.labkey.api.query.ValidationException; +import org.labkey.api.security.ActionNames; +import org.labkey.api.security.RequiresPermission; +import org.labkey.api.security.User; +import org.labkey.api.security.permissions.ReadPermission; +import org.labkey.api.security.permissions.UpdatePermission; +import org.labkey.api.study.Cohort; +import org.labkey.api.study.Study; +import org.labkey.api.study.StudyService; +import org.labkey.api.study.StudyUrls; +import org.labkey.api.study.Visit; +import org.labkey.api.study.model.CohortService; +import org.labkey.api.study.security.permissions.ManageStudyPermission; +import org.labkey.api.studydesign.StudyDesignUrls; +import org.labkey.api.studydesign.query.StudyDesignSchema; +import org.labkey.api.util.JsonUtil; +import org.labkey.api.util.PageFlowUtil; +import org.labkey.api.view.ActionURL; +import org.labkey.api.view.HttpView; +import org.labkey.api.view.JspView; +import org.labkey.api.view.NavTree; +import org.labkey.studydesign.model.AssaySpecimenConfigImpl; +import org.labkey.studydesign.model.AssaySpecimenVisitImpl; +import org.labkey.studydesign.model.DoseAndRoute; +import org.labkey.studydesign.model.ProductAntigenImpl; +import org.labkey.studydesign.model.ProductImpl; +import org.labkey.studydesign.model.StudyAssaySchedule; +import org.labkey.studydesign.model.StudyDesignCohort; +import org.labkey.studydesign.model.StudyTreatmentSchedule; +import org.labkey.studydesign.model.TreatmentImpl; +import org.labkey.studydesign.model.TreatmentManager; +import org.labkey.studydesign.model.TreatmentProductImpl; +import org.labkey.studydesign.model.TreatmentVisitMapImpl; +import org.springframework.validation.BindException; +import org.springframework.validation.Errors; +import org.springframework.web.servlet.ModelAndView; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; + +public class StudyDesignController extends SpringActionController +{ + private static final SpringActionController.ActionResolver ACTION_RESOLVER = new SpringActionController.DefaultActionResolver(StudyDesignController.class); + + public StudyDesignController() + { + setActionResolver(ACTION_RESOLVER); + } + + public static class StudyDesignUrlsImpl implements StudyDesignUrls + { + @Override + public ActionURL getManageAssayScheduleURL(Container container, boolean useAlternateLookupFields) + { + ActionURL url = new ActionURL(ManageAssayScheduleAction.class, container); + url.addParameter("useAlternateLookupFields", useAlternateLookupFields); + return url; + } + + @Override + public ActionURL getManageStudyProductsURL(Container container) + { + return new ActionURL(ManageStudyProductsAction.class, container); + } + + @Override + public ActionURL getManageTreatmentsURL(Container container, boolean useSingleTableEditor) + { + ActionURL url = new ActionURL(ManageTreatmentsAction.class, container); + url.addParameter("singleTable", useSingleTableEditor); + return url; + } + } + + @Nullable + private Study getStudy(Container container) + { + return StudyService.get().getStudy(container); + } + + @ActionNames("manageAssaySchedule, manageAssaySpecimen") + @RequiresPermission(UpdatePermission.class) + public class ManageAssayScheduleAction extends SimpleViewAction + { + @Override + public ModelAndView getView(AssayScheduleForm form, BindException errors) + { + return new JspView<>("/org/labkey/studydesign/view/manageAssaySchedule.jsp", form); + } + + @Override + public void addNavTrail(NavTree root) + { + setHelpTopic("manageAssaySchedule"); + if (getContainer().hasPermission(getUser(), ManageStudyPermission.class)) + root.addChild("Manage Study", PageFlowUtil.urlProvider(StudyUrls.class).getManageStudyURL(getContainer())); + root.addChild("Manage Assay Schedule"); + } + } + + public static class AssayScheduleForm extends ReturnUrlForm + { + private boolean useAlternateLookupFields; + + public boolean isUseAlternateLookupFields() + { + return useAlternateLookupFields; + } + + public void setUseAlternateLookupFields(boolean useAlternateLookupFields) + { + this.useAlternateLookupFields = useAlternateLookupFields; + } + } + + @RequiresPermission(UpdatePermission.class) + public class ManageStudyProductsAction extends SimpleViewAction + { + @Override + public ModelAndView getView(ReturnUrlForm form, BindException errors) + { + return new JspView<>("/org/labkey/studydesign/view/manageStudyProducts.jsp", form); + } + + @Override + public void addNavTrail(NavTree root) + { + setHelpTopic("studyProducts"); + if (getContainer().hasPermission(getUser(), ManageStudyPermission.class)) + root.addChild("Manage Study", PageFlowUtil.urlProvider(StudyUrls.class).getManageStudyURL(getContainer())); + root.addChild("Manage Study Products"); + } + } + + public static class ManageTreatmentsBean extends ReturnUrlForm + { + private boolean _singleTable; + + public boolean isSingleTable() + { + return _singleTable; + } + + public void setSingleTable(boolean singleTable) + { + _singleTable = singleTable; + } + } + + @RequiresPermission(UpdatePermission.class) + public class ManageTreatmentsAction extends SimpleViewAction + { + @Override + public ModelAndView getView(ManageTreatmentsBean form, BindException errors) + { + // if the singleTable param is not explicitly set, do a container check + if (getViewContext().getRequest().getParameter("singleTable") == null) + form.setSingleTable(getContainer().hasActiveModuleByName("viscstudies")); + + return new JspView<>("/org/labkey/studydesign/view/manageTreatments.jsp", form); + } + + @Override + public void addNavTrail(NavTree root) + { + setHelpTopic("manageTreatments"); + if (getContainer().hasPermission(getUser(), ManageStudyPermission.class)) + root.addChild("Manage Study", PageFlowUtil.urlProvider(StudyUrls.class).getManageStudyURL(getContainer())); + root.addChild("Manage Treatments"); + } + } + + @RequiresPermission(ReadPermission.class) + public class GetStudyProducts extends ReadOnlyApiAction + { + private Study _study; + + @Override + public void validateForm(GetStudyProductsForm form, Errors errors) + { + _study = getStudy(getContainer()); + if (_study == null) + errors.reject(SpringActionController.ERROR_MSG, "A study does not exist in this folder"); + } + + @Override + public ApiResponse execute(GetStudyProductsForm form, BindException errors) + { + ApiSimpleResponse resp = new ApiSimpleResponse(); + + List> productList = new ArrayList<>(); + List studyProducts = TreatmentManager.getInstance().getStudyProducts(getContainer(), getUser(), form.getRole(), form.getRowId()); + for (ProductImpl product : studyProducts) + { + // note: we are currently only including the base fields for this extensible table + Map productProperties = product.serialize(); + + List> productAntigenList = new ArrayList<>(); + List studyProductAntigens = TreatmentManager.getInstance().getStudyProductAntigens(getContainer(), getUser(), product.getRowId()); + for (ProductAntigenImpl antigen : studyProductAntigens) + { + // note: we are currently only including the base fields for this extensible table + productAntigenList.add(antigen.serialize()); + } + productProperties.put("Antigens", productAntigenList); + + // get dose and route information associated with this product + List> doseAndRoutes = TreatmentManager.getInstance().getStudyProductsDoseAndRoute(getContainer(), getUser(), product.getRowId()) + .stream() + .map(DoseAndRoute::serialize) + .collect(Collectors.toList()); + productProperties.put("DoseAndRoute", doseAndRoutes); + productList.add(productProperties); + } + + resp.put("success", true); + resp.put("products", productList); + + return resp; + } + } + + public static class GetStudyProductsForm + { + private Integer _rowId; + private String _role; + + public Integer getRowId() + { + return _rowId; + } + + public void setRowId(Integer rowId) + { + _rowId = rowId; + } + + public String getRole() + { + return _role; + } + + public void setRole(String role) + { + _role = role; + } + } + + @RequiresPermission(ReadPermission.class) + public class GetStudyTreatments extends ReadOnlyApiAction + { + private Study _study; + + @Override + public void validateForm(GetStudyTreatmentsForm form, Errors errors) + { + _study = getStudy(getContainer()); + if (_study == null) + errors.reject(SpringActionController.ERROR_MSG, "A study does not exist in this folder"); + } + + @Override + public ApiResponse execute(GetStudyTreatmentsForm form, BindException errors) + { + ApiSimpleResponse resp = new ApiSimpleResponse(); + + List> treatmentList = new ArrayList<>(); + List studyTreatments = TreatmentManager.getInstance().getStudyTreatments(getContainer(), getUser()); + for (TreatmentImpl treatment : studyTreatments) + { + if (form.getTreatmentId() > 0 && form.getTreatmentId() != treatment.getRowId()) + continue; + + Map treatmentProperties = treatment.serialize(); + + List> treatmentProductList = new ArrayList<>(); + List studyTreatmentProducts = TreatmentManager.getInstance().getStudyTreatmentProducts(getContainer(), getUser(), treatment.getRowId(), treatment.getProductSort()); + for (TreatmentProductImpl treatmentProduct : studyTreatmentProducts) + { + // note: we are currently only including the base fields for this extensible table + Map treatmentProductProperties = treatmentProduct.serialize(); + + // add the product label and role for convenience, to prevent the need for another round trip to the server + List products = TreatmentManager.getInstance().getStudyProducts(getContainer(), getUser(), null, treatmentProduct.getProductId()); + if (products.size() == 1) + { + treatmentProductProperties.put("ProductId/Label", products.get(0).getLabel()); + treatmentProductProperties.put("ProductId/Role", products.get(0).getRole()); + } + + treatmentProductList.add(treatmentProductProperties); + } + + if (!form.isSplitByRole()) + { + treatmentProperties.put("Products", treatmentProductList); + } + else + { + Map>> treatmentProductsListByRole = new HashMap<>(); + for (Map productProperties : treatmentProductList) + { + String role = productProperties.get("ProductId/Role").toString(); + if (!treatmentProductsListByRole.containsKey(role)) + treatmentProductsListByRole.put(role, new ArrayList<>()); + + treatmentProductsListByRole.get(role).add(productProperties); + } + + for (Map.Entry>> entry : treatmentProductsListByRole.entrySet()) + treatmentProperties.put(entry.getKey(), entry.getValue()); + } + + treatmentList.add(treatmentProperties); + } + + resp.put("success", true); + resp.put("treatments", treatmentList); + + return resp; + } + } + + private static class GetStudyTreatmentsForm + { + private boolean _splitByRole; + + private int treatmentId; + + public boolean isSplitByRole() + { + return _splitByRole; + } + + public void setSplitByRole(boolean splitByRole) + { + _splitByRole = splitByRole; + } + + public int getTreatmentId() + { + return treatmentId; + } + + public void setTreatmentId(int treatmentId) + { + this.treatmentId = treatmentId; + } + } + + @RequiresPermission(ReadPermission.class) + public class GetStudyTreatmentSchedule extends ReadOnlyApiAction + { + private Study _study; + + @Override + public void validateForm(Object form, Errors errors) + { + _study = getStudy(getContainer()); + if (_study == null) + errors.reject(SpringActionController.ERROR_MSG, "A study does not exist in this folder"); + } + + @Override + public ApiResponse execute(Object form, BindException errors) + { + ApiSimpleResponse resp = new ApiSimpleResponse(); + StudyTreatmentSchedule treatmentSchedule = new StudyTreatmentSchedule(getContainer()); + + // include all cohorts for the study, regardless of it they have associated visits or not + resp.put("cohorts", treatmentSchedule.serializeCohortMapping(_study.getCohorts(getUser()))); + + // include all visits from the study, ordered by visit display order + treatmentSchedule.setVisits(_study.getVisits(Visit.Order.DISPLAY)); + resp.put("visits", treatmentSchedule.serializeVisits()); + + resp.put("success", true); + return resp; + } + } + + @RequiresPermission(UpdatePermission.class) + public class UpdateStudyProductsAction extends MutatingApiAction + { + @Override + public void validateForm(StudyProductsForm form, Errors errors) + { + if (form.getProducts() == null) + errors.reject(SpringActionController.ERROR_MSG, "No study products provided."); + + // label field is required + for (ProductImpl product : form.getProducts()) + { + if (product.getLabel() == null || StringUtils.isEmpty(product.getLabel().trim())) + { + errors.reject(SpringActionController.ERROR_MSG, "Label is a required field for all study products."); + break; + } + } + } + + @Override + public ApiResponse execute(StudyProductsForm form, BindException errors) throws Exception + { + ApiSimpleResponse response = new ApiSimpleResponse(); + Study study = getStudy(getContainer()); + + if (study != null) + { + StudyDesignSchema schema = StudyDesignSchema.getInstance(); + + try (DbScope.Transaction transaction = schema.getSchema().getScope().ensureTransaction()) + { + updateProducts(form.getProducts()); + transaction.commit(); + } + + response.put("success", true); + return response; + } + else + throw new IllegalStateException("A study does not exist in this folder"); + } + + private void updateProducts(List products) throws Exception + { + // insert new study products and update any existing ones + List productRowIds = new ArrayList<>(); + for (ProductImpl product : products) + { + Integer updatedRowId = TreatmentManager.getInstance().saveStudyProduct(getContainer(), getUser(), product); + if (updatedRowId != null) + { + productRowIds.add(updatedRowId); + + updateProductAntigens(updatedRowId, product.getAntigens()); + updateProductDoseAndRoutes(updatedRowId, product.getDoseAndRoutes()); + } + } + + // delete any other study products, not included in the insert/update list, by RowId for this container + for (ProductImpl product : TreatmentManager.getInstance().getFilteredStudyProducts(getContainer(), getUser(), productRowIds)) + TreatmentManager.getInstance().deleteStudyProduct(getContainer(), getUser(), product.getRowId()); + } + + private void updateProductAntigens(int productId, List antigens) throws Exception + { + // insert new study products antigens and update any existing ones + List antigenRowIds = new ArrayList<>(); + for (ProductAntigenImpl antigen : antigens) + { + // make sure the productId is set based on the product rowId + antigen.setProductId(productId); + + Integer updatedRowId = TreatmentManager.getInstance().saveStudyProductAntigen(getContainer(), getUser(), antigen); + if (updatedRowId != null) + antigenRowIds.add(updatedRowId); + } + + // delete any other study products antigens, not included in the insert/update list, for the given productId + for (ProductAntigenImpl antigen : TreatmentManager.getInstance().getFilteredStudyProductAntigens(getContainer(), getUser(), productId, antigenRowIds)) + TreatmentManager.getInstance().deleteStudyProductAntigen(getContainer(), getUser(), antigen.getRowId()); + } + + private void updateProductDoseAndRoutes(int productId, List doseAndRoutes) + { + // get existing dose and routes + Set existingDoseAndRoutes = TreatmentManager.getInstance().getStudyProductsDoseAndRoute(getContainer(), getUser(), productId) + .stream() + .map(DoseAndRoute::getRowId) + .collect(Collectors.toSet()); + + try (DbScope.Transaction transaction = StudyDesignSchema.getInstance().getScope().ensureTransaction()) + { + for (DoseAndRoute doseAndRoute : doseAndRoutes) + { + // dose and route both can't be blank + if (doseAndRoute.getDose() != null || doseAndRoute.getRoute() != null) + { + doseAndRoute.setProductId(productId); + existingDoseAndRoutes.remove(doseAndRoute.getRowId()); + TreatmentManager.getInstance().saveStudyProductDoseAndRoute(getContainer(), getUser(), doseAndRoute); + } + } + + // remove deleted dose and routes + if (!existingDoseAndRoutes.isEmpty()) + { + SimpleFilter filter = new SimpleFilter(); + filter.addInClause(FieldKey.fromParts("RowId"), existingDoseAndRoutes); + Table.delete(StudyDesignSchema.getInstance().getTableInfoDoseAndRoute(), filter); + } + transaction.commit(); + } + } + } + + public static class StudyProductsForm implements ApiJsonForm + { + private List _products; + + public List getProducts() + { + return _products; + } + + public void setProducts(List products) + { + _products = products; + } + + @Override + public void bindJson(JSONObject json) + { + Container container = HttpView.currentContext().getContainer(); + + JSONArray productsJSON = json.optJSONArray("products"); + if (productsJSON != null) + { + _products = new ArrayList<>(); + for (JSONObject product : JsonUtil.toJSONObjectList(productsJSON)) + _products.add(ProductImpl.fromJSON(product, container)); + } + } + } + + @RequiresPermission(UpdatePermission.class) + public class UpdateTreatmentsAction extends MutatingApiAction + { + @Override + public ApiResponse execute(StudyTreatmentSchedule form, BindException errors) throws Exception + { + ApiSimpleResponse response = new ApiSimpleResponse(); + Study study = getStudy(getContainer()); + + if (study != null) + { + StudyDesignSchema schema = StudyDesignSchema.getInstance(); + List treatmentIds; + try (DbScope.Transaction transaction = schema.getSchema().getScope().ensureTransaction()) + { + treatmentIds = updateTreatments(form.getTreatments()); + transaction.commit(); + } + + response.put("success", true); + response.put("treatmentIds", treatmentIds); + return response; + } + else + throw new IllegalStateException("A study does not exist in this folder"); + } + + private Integer getExistingTreatmentId(TreatmentImpl treatment) + { + if (treatment == null) + return -1; + List studyTreatments = TreatmentManager.getInstance().getStudyTreatments(getContainer(), getUser()); + for (TreatmentImpl existingTreatment : studyTreatments) + { + List studyTreatmentProducts = TreatmentManager.getInstance().getStudyTreatmentProducts(getContainer(), getUser(), existingTreatment.getRowId(), existingTreatment.getProductSort()); + for (TreatmentProductImpl product : studyTreatmentProducts) + { + product.serialize(); + } + existingTreatment.setTreatmentProducts(studyTreatmentProducts); + if (treatment.isSameTreatmentProductsWith(existingTreatment)) + return existingTreatment.getRowId(); + } + return -1; + } + + private List updateTreatments(List treatments) throws Exception + { + List updatedRowIds = new ArrayList<>(); + for (TreatmentImpl treatment : treatments) + { + Integer updatedRowId = getExistingTreatmentId(treatment); + if (updatedRowId == null || updatedRowId <= 0) + { + updatedRowId = TreatmentManager.getInstance().saveTreatment(getContainer(), getUser(), treatment); + if (updatedRowId != null) + { + TreatmentManager.getInstance().updateTreatmentProducts(updatedRowId, treatment.getTreatmentProducts(), getContainer(), getUser()); + } + } + updatedRowIds.add(updatedRowId); + } + return updatedRowIds; + } + } + + @RequiresPermission(UpdatePermission.class) + public class UpdateTreatmentScheduleAction extends MutatingApiAction + { + private Map _tempTreatmentIdMap = new HashMap<>(); + private Set usedTreatmentIds = new HashSet<>(); // treatmentIds referenced in single table Treatment Schedule UI + private List treatmentRowIds = new ArrayList<>(); // treatmentIds defined in 2 table UI's Treatment section + private List cohortRowIds = new ArrayList<>(); + + @Override + public void validateForm(StudyTreatmentSchedule form, Errors errors) + { + // validate that each treatment has a label + for (TreatmentImpl treatment : form.getTreatments()) + { + if (treatment.getLabel() == null || StringUtils.isEmpty(treatment.getLabel().trim())) + errors.reject(SpringActionController.ERROR_MSG, "Label is a required field for all treatments."); + + // validate that each treatment product mapping has a selected product + for (TreatmentProductImpl treatmentProduct : treatment.getTreatmentProducts()) + { + if (treatmentProduct.getProductId() <= 0) + errors.reject(SpringActionController.ERROR_MSG, "Each treatment product must have a selected study product."); + } + } + + // validate that each cohort has a label, is unique, and has a valid subject count value + for (StudyDesignCohort cohort : form.getCohorts()) + { + if (cohort.getLabel() == null || StringUtils.isEmpty(cohort.getLabel().trim())) + errors.reject(SpringActionController.ERROR_MSG, "Label is a required field for all cohorts."); + + Cohort cohortByLabel = CohortService.get().getCohortByLabel(getContainer(), getUser(), cohort.getLabel()); + if (cohort.getRowId() > 0) + { + Cohort cohortByRowId = CohortService.get().getCohortForRowId(getContainer(), getUser(), cohort.getRowId()); + if (cohortByRowId != null && cohortByLabel != null && cohortByRowId.getRowId() != cohortByLabel.getRowId()) + errors.reject(SpringActionController.ERROR_MSG, "A cohort with the label '" + cohort.getLabel() + "' already exists in this study."); + } + else if (cohortByLabel != null) + { + errors.reject(SpringActionController.ERROR_MSG, "A cohort with the label '" + cohort.getLabel() + "' already exists in this study."); + } + + if (cohort.getSubjectCount() != null) + { + if (cohort.getSubjectCount() < 0) + errors.reject(SpringActionController.ERROR_MSG, "Cohort subject count values must be a positive integer."); + if (cohort.getSubjectCount() == Integer.MAX_VALUE) + errors.reject(SpringActionController.ERROR_MSG, "Cohort subject count value larger than the max value allowed."); + } + } + } + + @Override + public ApiResponse execute(StudyTreatmentSchedule form, BindException errors) throws Exception + { + ApiSimpleResponse response = new ApiSimpleResponse(); + Study study = getStudy(getContainer()); + + if (study != null) + { + StudyDesignSchema schema = StudyDesignSchema.getInstance(); + + try (DbScope.Transaction transaction = schema.getSchema().getScope().ensureTransaction()) + { + updateTreatments(form.getTreatments()); + updateCohorts(form.getCohorts(), study); + cleanTreatments(); + cleanCohorts(); + transaction.commit(); + } + + response.put("success", true); + return response; + } + else + throw new IllegalStateException("A study does not exist in this folder"); + } + + private void cleanCohorts() throws ValidationException + { + // delete any other study cohorts, not included in the insert/update list, by RowId for this container + for (Cohort existingCohort : CohortService.get().getCohorts(getContainer(), getUser())) + { + if (!cohortRowIds.contains(existingCohort.getRowId())) + { + if (!existingCohort.isInUse()) + CohortService.get().deleteCohort(existingCohort); + else + throw new ValidationException("Unable to delete in-use cohort: " + existingCohort.getLabel()); + } + } + + } + + private void cleanTreatments() + { + // delete any other study treatments, not included in the insert/update list, by RowId for this container + for (TreatmentImpl treatment : TreatmentManager.getInstance().getFilteredTreatments(getContainer(), getUser(), treatmentRowIds, usedTreatmentIds)) + TreatmentManager.getInstance().deleteTreatment(getContainer(), getUser(), treatment.getRowId()); + + } + + private void updateTreatments(List treatments) throws Exception + { + // insert new study treatments and update any existing ones + + for (TreatmentImpl treatment : treatments) + { + Integer updatedRowId = TreatmentManager.getInstance().saveTreatment(getContainer(), getUser(), treatment); + if (updatedRowId != null) + { + treatmentRowIds.add(updatedRowId); + + if (treatment.getTempRowId() != null) + _tempTreatmentIdMap.put(treatment.getTempRowId(), updatedRowId); + + TreatmentManager.getInstance().updateTreatmentProducts(updatedRowId, treatment.getTreatmentProducts(), getContainer(), getUser()); + } + } + } + + private void updateCohorts(Collection cohorts, Study study) throws ValidationException + { + // insert new cohorts and update any existing ones + for (StudyDesignCohort cohort : cohorts) + { + int rowId = cohort.getRowId(); + if (rowId > 0) + { + CohortService.get().updateCohort(getContainer(), getUser(), rowId, cohort.getLabel(), cohort.getSubjectCount()); + cohortRowIds.add(rowId); + } + else + { + Cohort newCohort = CohortService.get().createCohort(study, getUser(), cohort.getLabel(), true, cohort.getSubjectCount(), null); + cohortRowIds.add(newCohort.getRowId()); + rowId = newCohort.getRowId(); + } + + updateTreatmentVisitMap(rowId, cohort.getTreatmentVisitMap()); + } + } + + private void updateTreatmentVisitMap(int cohortId, List treatmentVisitMaps) + { + for (TreatmentVisitMapImpl visitMap : treatmentVisitMaps) + { + usedTreatmentIds.add(visitMap.getTreatmentId()); + } + + // the mapping that is passed in will have all of the current treatment/visit maps, so we will compare + // this set with the set from the DB and if they are different, replace all + List existingVisitMaps = TreatmentManager.getInstance().getStudyTreatmentVisitMap(getContainer(), cohortId); + boolean visitMapsDiffer = existingVisitMaps.size() != treatmentVisitMaps.size(); + if (!visitMapsDiffer) + { + for (TreatmentVisitMapImpl newVisitMap : treatmentVisitMaps) + { + newVisitMap.setContainer(getContainer()); + newVisitMap.setCohortId(cohortId); + + if (!existingVisitMaps.contains(newVisitMap)) + { + visitMapsDiffer = true; + break; + } + } + } + + // if we have differences, replace all at this point + if (visitMapsDiffer) + { + TreatmentManager.getInstance().deleteTreatmentVisitMapForCohort(getContainer(), cohortId); + for (TreatmentVisitMapImpl newVisitMap : treatmentVisitMaps) + { + // if the treatmentId used here was from a treatment that was created as part of this transaction, + // lookup the new treatment record RowId from the tempRowId + if (newVisitMap.getTempTreatmentId() != null && _tempTreatmentIdMap.containsKey(newVisitMap.getTempTreatmentId())) + newVisitMap.setTreatmentId(_tempTreatmentIdMap.get(newVisitMap.getTempTreatmentId())); + + if (cohortId > 0 && newVisitMap.getVisitId() > 0 && newVisitMap.getTreatmentId() > 0) + TreatmentManager.getInstance().insertTreatmentVisitMap(getUser(), getContainer(), cohortId, newVisitMap.getVisitId(), newVisitMap.getTreatmentId()); + } + } + } + } + + @RequiresPermission(UpdatePermission.class) + public class UpdateAssayPlanAction extends MutatingApiAction + { + @Override + public ApiResponse execute(AssayPlanForm form, BindException errors) + { + ApiSimpleResponse response = new ApiSimpleResponse(); + Study study = StudyService.get().getStudy(getContainer()); + if (study != null) + { + updateAssayPlan(getUser(), study, form.getAssayPlan()); + response.put("success", true); + } + else + { + response.put("success", false); + } + + return response; + } + } + + private static class AssayPlanForm + { + private String _assayPlan; + + public AssayPlanForm() + {} + + public String getAssayPlan() + { + return _assayPlan; + } + + public void setAssayPlan(String assayPlan) + { + _assayPlan = assayPlan; + } + } + + @RequiresPermission(UpdatePermission.class) + public class UpdateAssayScheduleAction extends MutatingApiAction + { + @Override + public void validateForm(StudyAssaySchedule form, Errors errors) + { + // validate that each assay configuration has an AssayName + for (AssaySpecimenConfigImpl assay : form.getAssays()) + { + if (assay.getAssayName() == null || StringUtils.isEmpty(assay.getAssayName().trim())) + errors.reject(SpringActionController.ERROR_MSG, "Assay Name is a required field for all assay configurations."); + + if (assay.getSampleQuantity() != null && assay.getSampleQuantity() < 0) + errors.reject(SpringActionController.ERROR_MSG, "Assay sample quantity value must be a positive number."); + } + } + + @Override + public ApiResponse execute(StudyAssaySchedule form, BindException errors) throws Exception + { + ApiSimpleResponse response = new ApiSimpleResponse(); + Study study = getStudy(getContainer()); + + if (study != null) + { + StudyDesignSchema schema = StudyDesignSchema.getInstance(); + + try (DbScope.Transaction transaction = schema.getSchema().getScope().ensureTransaction()) + { + updateAssays(form.getAssays()); + updateAssayPlan(getUser(), study, form.getAssayPlan()); + transaction.commit(); + } + + response.put("success", true); + return response; + } + else + throw new IllegalStateException("A study does not exist in this folder"); + } + + private void updateAssays(List assays) throws Exception + { + // insert new assaySpecimens and update any existing ones + List assaySpecimenRowIds = new ArrayList<>(); + for (AssaySpecimenConfigImpl assay : assays) + { + Integer updatedRowId = TreatmentManager.getInstance().saveAssaySpecimen(getContainer(), getUser(), assay); + if (updatedRowId != null) + { + assaySpecimenRowIds.add(updatedRowId); + + updateAssayVisitMap(updatedRowId, assay.getAssayVisitMap()); + } + } + + // delete any other assaySpecimens, not included in the insert/update list, by RowId for this container + for (AssaySpecimenConfigImpl assaySpecimen : TreatmentManager.getInstance().getFilteredAssaySpecimens(getContainer(), assaySpecimenRowIds)) + TreatmentManager.getInstance().deleteAssaySpecimen(getContainer(), getUser(), assaySpecimen.getRowId()); + } + + private void updateAssayVisitMap(int assaySpecimenId, List assayVisitMaps) throws Exception + { + List assaySpecimenVisitIds = new ArrayList<>(); + if (assayVisitMaps != null && !assayVisitMaps.isEmpty()) + { + for (AssaySpecimenVisitImpl assaySpecimenVisit : assayVisitMaps) + { + assaySpecimenVisit.setAssaySpecimenId(assaySpecimenId); + + Integer updatedRowId = TreatmentManager.getInstance().saveAssaySpecimenVisit(getContainer(), getUser(), assaySpecimenVisit); + assaySpecimenVisitIds.add(updatedRowId); + } + } + + // delete any other assaySpecimenVisits, not included in the insert/update list, by RowId for this container and assaySpecimenId + for (AssaySpecimenVisitImpl assaySpecimenVisit : TreatmentManager.getInstance().getFilteredAssaySpecimenVisits(getContainer(), assaySpecimenId, assaySpecimenVisitIds)) + TreatmentManager.getInstance().deleteAssaySpecimenVisit(getContainer(), getUser(), assaySpecimenVisit.getRowId()); + } + } + + private void updateAssayPlan(User user, Study study, String assayPlan) + { + if (study != null) + { + StudyService.get().updateAssayPlan(user, study, assayPlan); + } + } +} \ No newline at end of file diff --git a/studydesign/src/org/labkey/studydesign/StudyDesignModule.java b/studydesign/src/org/labkey/studydesign/StudyDesignModule.java new file mode 100644 index 00000000..eff82328 --- /dev/null +++ b/studydesign/src/org/labkey/studydesign/StudyDesignModule.java @@ -0,0 +1,83 @@ +package org.labkey.studydesign; + +import org.apache.logging.log4j.Logger; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.labkey.api.module.ModuleContext; +import org.labkey.api.module.SpringModule; +import org.labkey.api.services.ServiceRegistry; +import org.labkey.api.studydesign.StudyDesignService; +import org.labkey.api.util.logging.LogHelper; +import org.labkey.api.view.WebPartFactory; +import org.labkey.studydesign.model.TreatmentManager; +import org.labkey.studydesign.view.AssayScheduleWebpartFactory; +import org.labkey.studydesign.view.ImmunizationScheduleWebpartFactory; +import org.labkey.studydesign.view.VaccineDesignWebpartFactory; + +import java.util.Collection; +import java.util.List; +import java.util.Set; + +public class StudyDesignModule extends SpringModule +{ + private static final Logger LOG = LogHelper.getLogger(StudyDesignModule.class, "Study Design"); + public static final String NAME = "StudyDesign"; + + @Override + public String getName() + { + return NAME; + } + + @Override + @NotNull + protected Collection createWebPartFactories() + { + return List.of( + new AssayScheduleWebpartFactory(), + new ImmunizationScheduleWebpartFactory(), + new VaccineDesignWebpartFactory() + ); + } + + @Override + public @Nullable Double getSchemaVersion() + { + return null; + } + + @Override + public boolean hasScripts() + { + return false; + } + + @Override + protected void init() + { + addController("study-design", StudyDesignController.class); + + ServiceRegistry.get().registerService(StudyDesignService.class, new StudyDesignServiceImpl()); + } + + @Override + protected void startupAfterSpringConfig(ModuleContext moduleContext) + { + } + + @Override + public @NotNull Collection getSchemaNames() + { + return Set.of(); + } + + @Override + @NotNull + public Set getIntegrationTests() + { + return Set.of( + TreatmentManager.TreatmentDataTestCase.class, + TreatmentManager.AssayScheduleTestCase.class + ); + } +} \ No newline at end of file diff --git a/studydesign/src/org/labkey/studydesign/StudyDesignServiceImpl.java b/studydesign/src/org/labkey/studydesign/StudyDesignServiceImpl.java new file mode 100644 index 00000000..151a0d71 --- /dev/null +++ b/studydesign/src/org/labkey/studydesign/StudyDesignServiceImpl.java @@ -0,0 +1,68 @@ +package org.labkey.studydesign; + +import org.labkey.api.data.Container; +import org.labkey.api.data.SimpleFilter; +import org.labkey.api.data.Table; +import org.labkey.api.data.TableSelector; +import org.labkey.api.query.FieldKey; +import org.labkey.api.security.User; +import org.labkey.api.study.AssaySpecimenConfig; +import org.labkey.api.study.Product; +import org.labkey.api.study.Treatment; +import org.labkey.api.study.Visit; +import org.labkey.api.studydesign.StudyDesignService; +import org.labkey.api.studydesign.query.StudyDesignSchema; +import org.labkey.studydesign.model.AssaySpecimenConfigImpl; +import org.labkey.studydesign.model.TreatmentManager; + +import java.util.Collection; +import java.util.List; + +public class StudyDesignServiceImpl implements StudyDesignService +{ + @Override + public List getStudyProducts(Container c, User user, String role) + { + return TreatmentManager.getInstance().getStudyProducts(c, user, role, null); + } + + @Override + public List getStudyTreatments(Container c, User user) + { + return TreatmentManager.getInstance().getStudyTreatments(c, user); + } + + @Override + public List getVisitsForTreatmentSchedule(Container c) + { + return TreatmentManager.getInstance().getVisitsForTreatmentSchedule(c); + } + + @Override + public Collection getAssaySpecimenConfigs(Container c) + { + return new TableSelector( + StudyDesignSchema.getInstance().getTableInfoAssaySpecimen(), + SimpleFilter.createContainerFilter(c), null).getCollection(AssaySpecimenConfigImpl.class); + } + + @Override + public void deleteTreatmentVisitMapForCohort(Container container, Integer cohortId) + { + TreatmentManager.getInstance().deleteTreatmentVisitMapForCohort(container, cohortId); + } + + @Override + public void deleteTreatmentVisitMapForVisit(Container container, Integer visitId) + { + TreatmentManager.getInstance().deleteTreatmentVisitMapForVisit(container, visitId); + } + + @Override + public void deleteAssaySpecimenVisits(Container container, int visitId) + { + SimpleFilter filter = SimpleFilter.createContainerFilter(container); + filter.addCondition(FieldKey.fromParts("VisitId"), visitId); + Table.delete(StudyDesignSchema.getInstance().getTableInfoAssaySpecimenVisit(), filter); + } +} diff --git a/study/src/org/labkey/study/model/AssaySpecimenConfigImpl.java b/studydesign/src/org/labkey/studydesign/model/AssaySpecimenConfigImpl.java similarity index 93% rename from study/src/org/labkey/study/model/AssaySpecimenConfigImpl.java rename to studydesign/src/org/labkey/studydesign/model/AssaySpecimenConfigImpl.java index cb5778ec..448352a0 100644 --- a/study/src/org/labkey/study/model/AssaySpecimenConfigImpl.java +++ b/studydesign/src/org/labkey/studydesign/model/AssaySpecimenConfigImpl.java @@ -1,297 +1,302 @@ -/* - * Copyright (c) 2013-2017 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.study.model; - -import org.apache.commons.lang3.StringUtils; -import org.jetbrains.annotations.NotNull; -import org.json.JSONArray; -import org.json.JSONObject; -import org.labkey.api.data.Container; -import org.labkey.api.study.AssaySpecimenConfig; -import org.labkey.api.util.JsonUtil; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Objects; - -/** - * Represents an assay/specimen configuration for a study - */ -public class AssaySpecimenConfigImpl extends AbstractStudyEntity implements AssaySpecimenConfig -{ - private int _rowId; - private String _assayName; - private Integer _dataset; - private String _description; - private String _source; - private Integer _locationId; - private Integer _primaryTypeId; - private Integer _derivativeTypeId; - private String _tubeType; - private String _lab; - private String _sampleType; - private Double _sampleQuantity; - private String _sampleUnits; - private List _assayVisitMap; - - public AssaySpecimenConfigImpl() - { - } - - public AssaySpecimenConfigImpl(Container container, String assayName, String description) - { - setContainer(container); - _assayName = assayName; - _description = description; - } - - public boolean isNew() - { - return _rowId == 0; - } - - @Override - public Integer getPrimaryKey() - { - return getRowId(); - } - - @Override - public int getRowId() - { - return _rowId; - } - - public void setRowId(int rowId) - { - _rowId = rowId; - } - - @Override - public String getAssayName() - { - return _assayName; - } - - public void setAssayName(String assayName) - { - _assayName = assayName; - } - - @Override - public Integer getDataset() - { - return _dataset; - } - - public void setDataset(Integer dataset) - { - _dataset = dataset; - } - - @Override - public String getDescription() - { - return _description; - } - - public void setDescription(String description) - { - _description = description; - } - - @Override - public String getSource() - { - return _source; - } - - public void setSource(String source) - { - _source = source; - } - - @Override - public Integer getLocationId() - { - return _locationId; - } - - public void setLocationId(Integer locationId) - { - _locationId = locationId; - } - - @Override - public Integer getPrimaryTypeId() - { - return _primaryTypeId; - } - - public void setPrimaryTypeId(Integer primaryTypeId) - { - _primaryTypeId = primaryTypeId; - } - - @Override - public Integer getDerivativeTypeId() - { - return _derivativeTypeId; - } - - public void setDerivativeTypeId(Integer derivativeTypeId) - { - _derivativeTypeId = derivativeTypeId; - } - - @Override - public String getTubeType() - { - return _tubeType; - } - - public void setTubeType(String tubeType) - { - _tubeType = tubeType; - } - - public String getLab() - { - return _lab; - } - - public void setLab(String lab) - { - _lab = lab; - } - - public String getSampleType() - { - return _sampleType; - } - - public void setSampleType(String sampleType) - { - _sampleType = sampleType; - } - - public Double getSampleQuantity() - { - return _sampleQuantity; - } - - public void setSampleQuantity(Double sampleQuantity) - { - _sampleQuantity = sampleQuantity; - } - - public String getSampleUnits() - { - return _sampleUnits; - } - - public void setSampleUnits(String sampleUnits) - { - _sampleUnits = sampleUnits; - } - - public void setAssayVisitMap(List assayVisitMap) - { - _assayVisitMap = assayVisitMap; - } - - public List getAssayVisitMap() - { - return _assayVisitMap; - } - - public Map serialize() - { - Map props = new HashMap<>(); - props.put("RowId", getRowId()); - props.put("AssayName", getAssayName()); - props.put("DataSet", getDataset()); - props.put("Description", getDescription()); - props.put("LocationId", getLocationId()); - props.put("Source", getSource()); - props.put("TubeType", getTubeType()); - props.put("PrimaryTypeId", getPrimaryTypeId()); - props.put("DerivativeTypeId", getDerivativeTypeId()); - props.put("Lab", getLab()); - props.put("SampleType", getSampleType()); - props.put("SampleQuantity", getSampleQuantity()); - props.put("SampleUnits", getSampleUnits()); - props.put("Container", getContainer().getId()); - return props; - } - - public static AssaySpecimenConfigImpl fromJSON(@NotNull JSONObject o, Container container) - { - AssaySpecimenConfigImpl assay = new AssaySpecimenConfigImpl(container, o.getString("AssayName"), o.getString("Description")); - - if (o.has("RowId")) - assay.setRowId(o.getInt("RowId")); - if (o.has("DataSet") && o.get("DataSet") instanceof Integer && o.getInt("DataSet") > 0) - assay.setDataset(o.getInt("DataSet")); - if (o.has("Source") && !StringUtils.isEmpty(o.getString("Source"))) - assay.setSource(o.getString("Source")); - if (o.has("LocationId") && o.get("LocationId") instanceof Integer && o.getInt("LocationId") > 0) - assay.setLocationId(o.getInt("LocationId")); - if (o.has("TubeType") && !StringUtils.isEmpty(o.getString("TubeType"))) - assay.setTubeType(o.getString("TubeType")); - if (o.has("Lab") && !StringUtils.isEmpty(o.getString("Lab"))) - assay.setLab(o.getString("Lab")); - if (o.has("SampleType") && !StringUtils.isEmpty(o.getString("SampleType"))) - assay.setSampleType(o.getString("SampleType")); - if (o.has("SampleQuantity") && (o.get("SampleQuantity") instanceof Integer || o.get("SampleQuantity") instanceof Double) && o.getDouble("SampleQuantity") > 0) - assay.setSampleQuantity(o.getDouble("SampleQuantity")); - if (o.has("SampleUnits") && !StringUtils.isEmpty(o.getString("SampleUnits"))) - assay.setSampleUnits(o.getString("SampleUnits")); - if (o.has("PrimaryTypeId") && o.get("PrimaryTypeId") instanceof Integer) - assay.setPrimaryTypeId(o.getInt("PrimaryTypeId")); - if (o.has("DerivativeTypeId") && o.get("DerivativeTypeId") instanceof Integer) - assay.setDerivativeTypeId(o.getInt("DerivativeTypeId")); - - JSONArray visitMapJSON = o.optJSONArray("VisitMap"); - if (visitMapJSON != null) - { - List assayVisitMap = new ArrayList<>(); - for (JSONObject assaySpecimen : JsonUtil.toJSONObjectList(visitMapJSON)) - assayVisitMap.add(AssaySpecimenVisitImpl.fromJSON(assaySpecimen, container)); - - assay.setAssayVisitMap(assayVisitMap); - } - - return assay; - } - - @Override - public boolean equals(Object o) - { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - AssaySpecimenConfigImpl that = (AssaySpecimenConfigImpl) o; - return _rowId == that._rowId && Objects.equals(_assayName, that._assayName) && Objects.equals(_dataset, that._dataset) && Objects.equals(_description, that._description) && Objects.equals(_source, that._source) && Objects.equals(_locationId, that._locationId) && Objects.equals(_primaryTypeId, that._primaryTypeId) && Objects.equals(_derivativeTypeId, that._derivativeTypeId) && Objects.equals(_tubeType, that._tubeType) && Objects.equals(_lab, that._lab) && Objects.equals(_sampleType, that._sampleType) && Objects.equals(_sampleQuantity, that._sampleQuantity) && Objects.equals(_sampleUnits, that._sampleUnits); - } - - @Override - public int hashCode() - { - return Objects.hash(_rowId, _assayName, _dataset, _description, _source, _locationId, _primaryTypeId, _derivativeTypeId, _tubeType, _lab, _sampleType, _sampleQuantity, _sampleUnits); - } -} +/* + * Copyright (c) 2013-2017 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.studydesign.model; + +import org.apache.commons.lang3.StringUtils; +import org.jetbrains.annotations.NotNull; +import org.json.JSONArray; +import org.json.JSONObject; +import org.labkey.api.data.Container; +import org.labkey.api.study.AssaySpecimenConfig; +import org.labkey.api.util.JsonUtil; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; + +/** + * Represents an assay/specimen configuration for a study + */ +public class AssaySpecimenConfigImpl implements AssaySpecimenConfig +{ + private Container _container; + private int _rowId; + private String _assayName; + private Integer _dataset; + private String _description; + private String _source; + private Integer _locationId; + private Integer _primaryTypeId; + private Integer _derivativeTypeId; + private String _tubeType; + private String _lab; + private String _sampleType; + private Double _sampleQuantity; + private String _sampleUnits; + private List _assayVisitMap; + + public AssaySpecimenConfigImpl() + { + } + + public AssaySpecimenConfigImpl(Container container, String assayName, String description) + { + _container = container; + _assayName = assayName; + _description = description; + } + + public boolean isNew() + { + return _rowId == 0; + } + + public Container getContainer() + { + return _container; + } + + public void setContainer(Container container) + { + _container = container; + } + + @Override + public int getRowId() + { + return _rowId; + } + + public void setRowId(int rowId) + { + _rowId = rowId; + } + + @Override + public String getAssayName() + { + return _assayName; + } + + public void setAssayName(String assayName) + { + _assayName = assayName; + } + + @Override + public Integer getDataset() + { + return _dataset; + } + + public void setDataset(Integer dataset) + { + _dataset = dataset; + } + + @Override + public String getDescription() + { + return _description; + } + + public void setDescription(String description) + { + _description = description; + } + + @Override + public String getSource() + { + return _source; + } + + public void setSource(String source) + { + _source = source; + } + + @Override + public Integer getLocationId() + { + return _locationId; + } + + public void setLocationId(Integer locationId) + { + _locationId = locationId; + } + + @Override + public Integer getPrimaryTypeId() + { + return _primaryTypeId; + } + + public void setPrimaryTypeId(Integer primaryTypeId) + { + _primaryTypeId = primaryTypeId; + } + + @Override + public Integer getDerivativeTypeId() + { + return _derivativeTypeId; + } + + public void setDerivativeTypeId(Integer derivativeTypeId) + { + _derivativeTypeId = derivativeTypeId; + } + + @Override + public String getTubeType() + { + return _tubeType; + } + + public void setTubeType(String tubeType) + { + _tubeType = tubeType; + } + + public String getLab() + { + return _lab; + } + + public void setLab(String lab) + { + _lab = lab; + } + + public String getSampleType() + { + return _sampleType; + } + + public void setSampleType(String sampleType) + { + _sampleType = sampleType; + } + + public Double getSampleQuantity() + { + return _sampleQuantity; + } + + public void setSampleQuantity(Double sampleQuantity) + { + _sampleQuantity = sampleQuantity; + } + + public String getSampleUnits() + { + return _sampleUnits; + } + + public void setSampleUnits(String sampleUnits) + { + _sampleUnits = sampleUnits; + } + + public void setAssayVisitMap(List assayVisitMap) + { + _assayVisitMap = assayVisitMap; + } + + public List getAssayVisitMap() + { + return _assayVisitMap; + } + + public Map serialize() + { + Map props = new HashMap<>(); + props.put("RowId", getRowId()); + props.put("AssayName", getAssayName()); + props.put("DataSet", getDataset()); + props.put("Description", getDescription()); + props.put("LocationId", getLocationId()); + props.put("Source", getSource()); + props.put("TubeType", getTubeType()); + props.put("PrimaryTypeId", getPrimaryTypeId()); + props.put("DerivativeTypeId", getDerivativeTypeId()); + props.put("Lab", getLab()); + props.put("SampleType", getSampleType()); + props.put("SampleQuantity", getSampleQuantity()); + props.put("SampleUnits", getSampleUnits()); + props.put("Container", getContainer().getId()); + return props; + } + + public static AssaySpecimenConfigImpl fromJSON(@NotNull JSONObject o, Container container) + { + AssaySpecimenConfigImpl assay = new AssaySpecimenConfigImpl(container, o.getString("AssayName"), o.getString("Description")); + + if (o.has("RowId")) + assay.setRowId(o.getInt("RowId")); + if (o.has("DataSet") && o.get("DataSet") instanceof Integer && o.getInt("DataSet") > 0) + assay.setDataset(o.getInt("DataSet")); + if (o.has("Source") && !StringUtils.isEmpty(o.getString("Source"))) + assay.setSource(o.getString("Source")); + if (o.has("LocationId") && o.get("LocationId") instanceof Integer && o.getInt("LocationId") > 0) + assay.setLocationId(o.getInt("LocationId")); + if (o.has("TubeType") && !StringUtils.isEmpty(o.getString("TubeType"))) + assay.setTubeType(o.getString("TubeType")); + if (o.has("Lab") && !StringUtils.isEmpty(o.getString("Lab"))) + assay.setLab(o.getString("Lab")); + if (o.has("SampleType") && !StringUtils.isEmpty(o.getString("SampleType"))) + assay.setSampleType(o.getString("SampleType")); + if (o.has("SampleQuantity") && (o.get("SampleQuantity") instanceof Integer || o.get("SampleQuantity") instanceof Double) && o.getDouble("SampleQuantity") > 0) + assay.setSampleQuantity(o.getDouble("SampleQuantity")); + if (o.has("SampleUnits") && !StringUtils.isEmpty(o.getString("SampleUnits"))) + assay.setSampleUnits(o.getString("SampleUnits")); + if (o.has("PrimaryTypeId") && o.get("PrimaryTypeId") instanceof Integer) + assay.setPrimaryTypeId(o.getInt("PrimaryTypeId")); + if (o.has("DerivativeTypeId") && o.get("DerivativeTypeId") instanceof Integer) + assay.setDerivativeTypeId(o.getInt("DerivativeTypeId")); + + JSONArray visitMapJSON = o.optJSONArray("VisitMap"); + if (visitMapJSON != null) + { + List assayVisitMap = new ArrayList<>(); + for (JSONObject assaySpecimen : JsonUtil.toJSONObjectList(visitMapJSON)) + assayVisitMap.add(AssaySpecimenVisitImpl.fromJSON(assaySpecimen, container)); + + assay.setAssayVisitMap(assayVisitMap); + } + + return assay; + } + + @Override + public boolean equals(Object o) + { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + AssaySpecimenConfigImpl that = (AssaySpecimenConfigImpl) o; + return _rowId == that._rowId && Objects.equals(_assayName, that._assayName) && Objects.equals(_dataset, that._dataset) && Objects.equals(_description, that._description) && Objects.equals(_source, that._source) && Objects.equals(_locationId, that._locationId) && Objects.equals(_primaryTypeId, that._primaryTypeId) && Objects.equals(_derivativeTypeId, that._derivativeTypeId) && Objects.equals(_tubeType, that._tubeType) && Objects.equals(_lab, that._lab) && Objects.equals(_sampleType, that._sampleType) && Objects.equals(_sampleQuantity, that._sampleQuantity) && Objects.equals(_sampleUnits, that._sampleUnits); + } + + @Override + public int hashCode() + { + return Objects.hash(_rowId, _assayName, _dataset, _description, _source, _locationId, _primaryTypeId, _derivativeTypeId, _tubeType, _lab, _sampleType, _sampleQuantity, _sampleUnits); + } +} diff --git a/study/src/org/labkey/study/model/AssaySpecimenVisitImpl.java b/studydesign/src/org/labkey/studydesign/model/AssaySpecimenVisitImpl.java similarity index 95% rename from study/src/org/labkey/study/model/AssaySpecimenVisitImpl.java rename to studydesign/src/org/labkey/studydesign/model/AssaySpecimenVisitImpl.java index b5ac0fae..4c8eee28 100644 --- a/study/src/org/labkey/study/model/AssaySpecimenVisitImpl.java +++ b/studydesign/src/org/labkey/studydesign/model/AssaySpecimenVisitImpl.java @@ -1,112 +1,112 @@ -/* - * Copyright (c) 2014-2016 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.study.model; - -import org.jetbrains.annotations.NotNull; -import org.json.JSONObject; -import org.labkey.api.data.Container; -import org.labkey.api.study.AssaySpecimenVisit; - -import java.util.HashMap; -import java.util.Map; - -public class AssaySpecimenVisitImpl implements AssaySpecimenVisit -{ - private int _assaySpecimenId; - private int _visitId; - private int _rowId; - private Container _container; - - public AssaySpecimenVisitImpl() - { - } - - public AssaySpecimenVisitImpl(Container container, int assaySpecimenId, int visitId) - { - _container = container; - _assaySpecimenId = assaySpecimenId; - _visitId = visitId; - } - - public boolean isNew() - { - return _rowId == 0; - } - - @Override - public int getAssaySpecimenId() - { - return _assaySpecimenId; - } - - public void setAssaySpecimenId(int assaySpecimenId) - { - _assaySpecimenId = assaySpecimenId; - } - - @Override - public int getVisitId() - { - return _visitId; - } - - public void setVisitId(int visitId) - { - _visitId = visitId; - } - - public Container getContainer() - { - return _container; - } - - public void setContainer(Container container) - { - _container = container; - } - - public int getRowId() - { - return _rowId; - } - - public void setRowId(int rowId) - { - _rowId = rowId; - } - - public Map serialize() - { - Map props = new HashMap<>(); - props.put("RowId", getRowId()); - props.put("VisitId", getVisitId()); - props.put("AssaySpecimenId", getAssaySpecimenId()); - props.put("Container", getContainer().getId()); - return props; - } - - public static AssaySpecimenVisitImpl fromJSON(@NotNull JSONObject o, Container container) - { - // AssaySpecimenId may not be specified in JSON - int assaySpecimenId = o.has("AssaySpecimenId") ? o.getInt("AssaySpecimenId") : 0; - AssaySpecimenVisitImpl assaySpecimenVisit = new AssaySpecimenVisitImpl(container, assaySpecimenId, o.getInt("VisitId")); - - if (o.has("RowId")) - assaySpecimenVisit.setRowId(o.getInt("RowId")); - - return assaySpecimenVisit; - } -} +/* + * Copyright (c) 2014-2016 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.studydesign.model; + +import org.jetbrains.annotations.NotNull; +import org.json.JSONObject; +import org.labkey.api.data.Container; +import org.labkey.api.study.AssaySpecimenVisit; + +import java.util.HashMap; +import java.util.Map; + +public class AssaySpecimenVisitImpl implements AssaySpecimenVisit +{ + private int _assaySpecimenId; + private int _visitId; + private int _rowId; + private Container _container; + + public AssaySpecimenVisitImpl() + { + } + + public AssaySpecimenVisitImpl(Container container, int assaySpecimenId, int visitId) + { + _container = container; + _assaySpecimenId = assaySpecimenId; + _visitId = visitId; + } + + public boolean isNew() + { + return _rowId == 0; + } + + @Override + public int getAssaySpecimenId() + { + return _assaySpecimenId; + } + + public void setAssaySpecimenId(int assaySpecimenId) + { + _assaySpecimenId = assaySpecimenId; + } + + @Override + public int getVisitId() + { + return _visitId; + } + + public void setVisitId(int visitId) + { + _visitId = visitId; + } + + public Container getContainer() + { + return _container; + } + + public void setContainer(Container container) + { + _container = container; + } + + public int getRowId() + { + return _rowId; + } + + public void setRowId(int rowId) + { + _rowId = rowId; + } + + public Map serialize() + { + Map props = new HashMap<>(); + props.put("RowId", getRowId()); + props.put("VisitId", getVisitId()); + props.put("AssaySpecimenId", getAssaySpecimenId()); + props.put("Container", getContainer().getId()); + return props; + } + + public static AssaySpecimenVisitImpl fromJSON(@NotNull JSONObject o, Container container) + { + // AssaySpecimenId may not be specified in JSON + int assaySpecimenId = o.has("AssaySpecimenId") ? o.getInt("AssaySpecimenId") : 0; + AssaySpecimenVisitImpl assaySpecimenVisit = new AssaySpecimenVisitImpl(container, assaySpecimenId, o.getInt("VisitId")); + + if (o.has("RowId")) + assaySpecimenVisit.setRowId(o.getInt("RowId")); + + return assaySpecimenVisit; + } +} diff --git a/study/src/org/labkey/study/model/DoseAndRoute.java b/studydesign/src/org/labkey/studydesign/model/DoseAndRoute.java similarity index 93% rename from study/src/org/labkey/study/model/DoseAndRoute.java rename to studydesign/src/org/labkey/studydesign/model/DoseAndRoute.java index 405a4209..838d6007 100644 --- a/study/src/org/labkey/study/model/DoseAndRoute.java +++ b/studydesign/src/org/labkey/studydesign/model/DoseAndRoute.java @@ -1,169 +1,169 @@ -/* - * Copyright (c) 2016 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.study.model; - -import org.apache.commons.lang3.StringUtils; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; -import org.json.JSONObject; -import org.labkey.api.data.Container; -import org.labkey.api.util.Pair; - -import java.util.HashMap; -import java.util.Map; - -/** - * Created by klum on 9/23/2016. - */ -public class DoseAndRoute -{ - private Integer _rowId; - private String _dose; - private String _route; - private int _productId; - private Container _container; - - public enum keys - { - RowId, - Dose, - Route, - ProductId - } - - public boolean isNew() - { - return _rowId == null; - } - - public Integer getRowId() - { - return _rowId; - } - - public void setRowId(Integer rowId) - { - _rowId = rowId; - } - - public DoseAndRoute(){} - - public DoseAndRoute(String dose, String route, int productId, Container container) - { - _dose = dose; - _route = route; - _productId = productId; - _container = container; - } - - public @Nullable String getLabel() - { - if (_dose != null || _route != null) - return String.format("%s : %s", StringUtils.trimToEmpty(_dose), StringUtils.trimToEmpty(_route)); - else - return null; - } - - public String getDose() - { - return StringUtils.trimToNull(_dose); - } - - public void setDose(String dose) - { - _dose = dose; - } - - public String getRoute() - { - return StringUtils.trimToNull(_route); - } - - public void setRoute(String route) - { - _route = route; - } - - public int getProductId() - { - return _productId; - } - - public void setProductId(int productId) - { - _productId = productId; - } - - public Container getContainer() - { - return _container; - } - - public void setContainer(Container container) - { - _container = container; - } - - public static DoseAndRoute fromJSON(@NotNull JSONObject o, Container container, int productId) - { - String dose = null; - String route = null; - if (o.has(keys.Dose.name())) - dose = o.getString(keys.Dose.name()); - if (o.has(keys.Route.name())) - route = o.getString(keys.Route.name()); - - DoseAndRoute doseAndRoute = new DoseAndRoute(dose, route, productId, container); - if (o.has(keys.RowId.name())) - doseAndRoute.setRowId(o.getInt(keys.RowId.name())); - return doseAndRoute; - } - - public Map serialize() - { - Map props = new HashMap<>(); - props.put(keys.RowId.name(), getRowId()); - props.put(keys.ProductId.name(), getProductId()); - props.put(keys.Dose.name(), getDose()); - props.put(keys.Route.name(), getRoute()); - - return props; - } - - /** - * Helper to convert the concatenated label into a dose and/or route portion - * @return Pair object where the key is the dose and the value is the route - */ - public static @Nullable - Pair parseFromLabel(String label) - { - // need to keep the label generation in sync with code in DoseAndRouteTable label expr column - if (label != null) - { - if (label.contains(":")) - { - String[] parts = label.split(":"); - if (parts.length == 2) - { - return new Pair<>( - StringUtils.trimToNull(parts[0]), - StringUtils.trimToNull(parts[1])); - } - } - } - return null; - } -} +/* + * Copyright (c) 2016 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.studydesign.model; + +import org.apache.commons.lang3.StringUtils; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.json.JSONObject; +import org.labkey.api.data.Container; +import org.labkey.api.util.Pair; + +import java.util.HashMap; +import java.util.Map; + +/** + * Created by klum on 9/23/2016. + */ +public class DoseAndRoute +{ + private Integer _rowId; + private String _dose; + private String _route; + private int _productId; + private Container _container; + + public enum keys + { + RowId, + Dose, + Route, + ProductId + } + + public boolean isNew() + { + return _rowId == null; + } + + public Integer getRowId() + { + return _rowId; + } + + public void setRowId(Integer rowId) + { + _rowId = rowId; + } + + public DoseAndRoute(){} + + public DoseAndRoute(String dose, String route, int productId, Container container) + { + _dose = dose; + _route = route; + _productId = productId; + _container = container; + } + + public @Nullable String getLabel() + { + if (_dose != null || _route != null) + return String.format("%s : %s", StringUtils.trimToEmpty(_dose), StringUtils.trimToEmpty(_route)); + else + return null; + } + + public String getDose() + { + return StringUtils.trimToNull(_dose); + } + + public void setDose(String dose) + { + _dose = dose; + } + + public String getRoute() + { + return StringUtils.trimToNull(_route); + } + + public void setRoute(String route) + { + _route = route; + } + + public int getProductId() + { + return _productId; + } + + public void setProductId(int productId) + { + _productId = productId; + } + + public Container getContainer() + { + return _container; + } + + public void setContainer(Container container) + { + _container = container; + } + + public static DoseAndRoute fromJSON(@NotNull JSONObject o, Container container, int productId) + { + String dose = null; + String route = null; + if (o.has(keys.Dose.name())) + dose = String.valueOf(o.get(keys.Dose.name())); + if (o.has(keys.Route.name())) + route = String.valueOf(o.get(keys.Route.name())); + + DoseAndRoute doseAndRoute = new DoseAndRoute(dose, route, productId, container); + if (o.has(keys.RowId.name())) + doseAndRoute.setRowId(o.getInt(keys.RowId.name())); + return doseAndRoute; + } + + public Map serialize() + { + Map props = new HashMap<>(); + props.put(keys.RowId.name(), getRowId()); + props.put(keys.ProductId.name(), getProductId()); + props.put(keys.Dose.name(), getDose()); + props.put(keys.Route.name(), getRoute()); + + return props; + } + + /** + * Helper to convert the concatenated label into a dose and/or route portion + * @return Pair object where the key is the dose and the value is the route + */ + public static @Nullable + Pair parseFromLabel(String label) + { + // need to keep the label generation in sync with code in DoseAndRouteTable label expr column + if (label != null) + { + if (label.contains(":")) + { + String[] parts = label.split(":"); + if (parts.length == 2) + { + return new Pair<>( + StringUtils.trimToNull(parts[0]), + StringUtils.trimToNull(parts[1])); + } + } + } + return null; + } +} diff --git a/study/src/org/labkey/study/model/ProductAntigenImpl.java b/studydesign/src/org/labkey/studydesign/model/ProductAntigenImpl.java similarity index 94% rename from study/src/org/labkey/study/model/ProductAntigenImpl.java rename to studydesign/src/org/labkey/studydesign/model/ProductAntigenImpl.java index bd85db2f..9c10e2a7 100644 --- a/study/src/org/labkey/study/model/ProductAntigenImpl.java +++ b/studydesign/src/org/labkey/studydesign/model/ProductAntigenImpl.java @@ -1,171 +1,171 @@ -/* - * Copyright (c) 2013-2016 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.study.model; - -import org.jetbrains.annotations.NotNull; -import org.json.JSONObject; -import org.labkey.api.data.Container; -import org.labkey.api.study.ProductAntigen; - -import java.util.HashMap; -import java.util.Map; - -/** - * User: cnathe - * Date: 12/26/13 - */ -public class ProductAntigenImpl implements ProductAntigen -{ - private Container _container; - private int _rowId; - private int _productId; - private String _gene; - private String _subType; - private String _genBankId; - private String _sequence; - - public ProductAntigenImpl() - {} - - public ProductAntigenImpl(Container container, int productId, String gene, String subType) - { - _container = container; - _productId = productId; - _gene = gene; - _subType = subType; - } - - public ProductAntigenImpl(Container container, String gene, String subType, String genBankId, String sequence) - { - this(container, 0, gene, subType); - _genBankId = genBankId; - _sequence = sequence; - } - - public boolean isNew() - { - return _rowId == 0; - } - - public Object getPrimaryKey() - { - return getRowId(); - } - - @Override - public int getRowId() - { - return _rowId; - } - - public void setRowId(int rowId) - { - _rowId = rowId; - } - - @Override - public int getProductId() - { - return _productId; - } - - public void setProductId(int productId) - { - _productId = productId; - } - - @Override - public String getGene() - { - return _gene; - } - - public void setGene(String gene) - { - _gene = gene; - } - - @Override - public String getSubType() - { - return _subType; - } - - public void setSubType(String subType) - { - _subType = subType; - } - - @Override - public String getGenBankId() - { - return _genBankId; - } - - public void setGenBankId(String genBankId) - { - _genBankId = genBankId; - } - - @Override - public String getSequence() - { - return _sequence; - } - - public void setSequence(String sequence) - { - _sequence = sequence; - } - - public Container getContainer() - { - return _container; - } - - public void setContainer(Container container) - { - _container = container; - } - - public Map serialize() - { - Map props = new HashMap<>(); - props.put("RowId", getRowId()); - props.put("ProductId", getProductId()); - props.put("Gene", getGene()); - props.put("SubType", getSubType()); - props.put("GenBankId", getGenBankId()); - props.put("Sequence", getSequence()); - return props; - } - - public static ProductAntigenImpl fromJSON(@NotNull JSONObject o, Container container) - { - ProductAntigenImpl antigen = new ProductAntigenImpl( - container, o.getString("Gene"), o.getString("SubType"), - o.getString("GenBankId"), o.getString("Sequence") - ); - - if (o.has("RowId")) - antigen.setRowId(o.getInt("RowId")); - - if (o.has("ProductId")) - antigen.setProductId(o.getInt("ProductId")); - - return antigen; - } -} +/* + * Copyright (c) 2013-2016 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.studydesign.model; + +import org.jetbrains.annotations.NotNull; +import org.json.JSONObject; +import org.labkey.api.data.Container; +import org.labkey.api.study.ProductAntigen; + +import java.util.HashMap; +import java.util.Map; + +/** + * User: cnathe + * Date: 12/26/13 + */ +public class ProductAntigenImpl implements ProductAntigen +{ + private Container _container; + private int _rowId; + private int _productId; + private String _gene; + private String _subType; + private String _genBankId; + private String _sequence; + + public ProductAntigenImpl() + {} + + public ProductAntigenImpl(Container container, int productId, String gene, String subType) + { + _container = container; + _productId = productId; + _gene = gene; + _subType = subType; + } + + public ProductAntigenImpl(Container container, String gene, String subType, String genBankId, String sequence) + { + this(container, 0, gene, subType); + _genBankId = genBankId; + _sequence = sequence; + } + + public boolean isNew() + { + return _rowId == 0; + } + + public Object getPrimaryKey() + { + return getRowId(); + } + + @Override + public int getRowId() + { + return _rowId; + } + + public void setRowId(int rowId) + { + _rowId = rowId; + } + + @Override + public int getProductId() + { + return _productId; + } + + public void setProductId(int productId) + { + _productId = productId; + } + + @Override + public String getGene() + { + return _gene; + } + + public void setGene(String gene) + { + _gene = gene; + } + + @Override + public String getSubType() + { + return _subType; + } + + public void setSubType(String subType) + { + _subType = subType; + } + + @Override + public String getGenBankId() + { + return _genBankId; + } + + public void setGenBankId(String genBankId) + { + _genBankId = genBankId; + } + + @Override + public String getSequence() + { + return _sequence; + } + + public void setSequence(String sequence) + { + _sequence = sequence; + } + + public Container getContainer() + { + return _container; + } + + public void setContainer(Container container) + { + _container = container; + } + + public Map serialize() + { + Map props = new HashMap<>(); + props.put("RowId", getRowId()); + props.put("ProductId", getProductId()); + props.put("Gene", getGene()); + props.put("SubType", getSubType()); + props.put("GenBankId", getGenBankId()); + props.put("Sequence", getSequence()); + return props; + } + + public static ProductAntigenImpl fromJSON(@NotNull JSONObject o, Container container) + { + ProductAntigenImpl antigen = new ProductAntigenImpl( + container, o.getString("Gene"), o.getString("SubType"), + o.getString("GenBankId"), o.getString("Sequence") + ); + + if (o.has("RowId")) + antigen.setRowId(o.getInt("RowId")); + + if (o.has("ProductId")) + antigen.setProductId(o.getInt("ProductId")); + + return antigen; + } +} diff --git a/study/src/org/labkey/study/model/ProductImpl.java b/studydesign/src/org/labkey/studydesign/model/ProductImpl.java similarity index 95% rename from study/src/org/labkey/study/model/ProductImpl.java rename to studydesign/src/org/labkey/studydesign/model/ProductImpl.java index 5192f666..e81aec33 100644 --- a/study/src/org/labkey/study/model/ProductImpl.java +++ b/studydesign/src/org/labkey/studydesign/model/ProductImpl.java @@ -1,205 +1,205 @@ -/* - * Copyright (c) 2013-2016 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.study.model; - -import org.jetbrains.annotations.NotNull; -import org.json.JSONArray; -import org.json.JSONObject; -import org.labkey.api.data.Container; -import org.labkey.api.study.Product; -import org.labkey.api.util.JsonUtil; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -/** - * User: cnathe - * Date: 12/26/13 - */ -public class ProductImpl implements Product -{ - private Container _container; - private int _rowId; - private String _label; - private String _role; - private String _type; - private List _antigens; - private List _doseAndRoutes; - - // from TreatmentProductMap (not serialized with product) - private String _dose; - private String _route; - - public ProductImpl() - {} - - public ProductImpl(Container container, String label, String role) - { - _container = container; - _label = label; - _role = role; - } - - public ProductImpl(Container container, String label, String role, String type) - { - this(container, label, role); - _type = type; - } - - public boolean isNew() - { - return _rowId == 0; - } - - public Object getPrimaryKey() - { - return getRowId(); - } - - @Override - public int getRowId() - { - return _rowId; - } - - public void setRowId(int rowId) - { - _rowId = rowId; - } - - @Override - public String getLabel() - { - return _label; - } - - public void setLabel(String label) - { - _label = label; - } - - @Override - public String getRole() - { - return _role; - } - - public void setRole(String role) - { - _role = role; - } - - @Override - public String getType() - { - return _type; - } - - public void setType(String type) - { - _type = type; - } - - public List getAntigens() - { - return _antigens; - } - - public void setAntigens(List antigens) - { - _antigens = antigens; - } - - public String getDose() - { - return _dose; - } - - public void setDose(String dose) - { - _dose = dose; - } - - public String getRoute() - { - return _route; - } - - public void setRoute(String route) - { - _route = route; - } - - public Container getContainer() - { - return _container; - } - - public void setContainer(Container container) - { - _container = container; - } - - public List getDoseAndRoutes() - { - return _doseAndRoutes; - } - - public void setDoseAndRoutes(List doseAndRoutes) - { - _doseAndRoutes = doseAndRoutes; - } - - public Map serialize() - { - Map props = new HashMap<>(); - props.put("RowId", getRowId()); - props.put("Label", getLabel()); - props.put("Role", getRole()); - props.put("Type", getType()); - return props; - } - - public static ProductImpl fromJSON(@NotNull JSONObject o, Container container) - { - ProductImpl product = new ProductImpl(container, o.getString("Label"), o.getString("Role"), o.getString("Type")); - - if (o.has("RowId")) - product.setRowId(o.getInt("RowId")); - - if (o.has("Antigens") && o.get("Antigens") instanceof JSONArray antigensJSON) - { - List antigens = new ArrayList<>(); - for (JSONObject productAntigen : JsonUtil.toJSONObjectList(antigensJSON)) - antigens.add(ProductAntigenImpl.fromJSON(productAntigen, container)); - - product.setAntigens(antigens); - } - - if (o.has("DoseAndRoute") && o.get("DoseAndRoute") instanceof JSONArray doseJSON) - { - List doseAndRoutes = new ArrayList<>(); - for (JSONObject doseAndRoute : JsonUtil.toJSONObjectList(doseJSON)) - doseAndRoutes.add(DoseAndRoute.fromJSON(doseAndRoute, container, product.getRowId())); - - product.setDoseAndRoutes(doseAndRoutes); - } - - return product; - } -} +/* + * Copyright (c) 2013-2016 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.studydesign.model; + +import org.jetbrains.annotations.NotNull; +import org.json.JSONArray; +import org.json.JSONObject; +import org.labkey.api.data.Container; +import org.labkey.api.study.Product; +import org.labkey.api.util.JsonUtil; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * User: cnathe + * Date: 12/26/13 + */ +public class ProductImpl implements Product +{ + private Container _container; + private int _rowId; + private String _label; + private String _role; + private String _type; + private List _antigens; + private List _doseAndRoutes; + + // from TreatmentProductMap (not serialized with product) + private String _dose; + private String _route; + + public ProductImpl() + {} + + public ProductImpl(Container container, String label, String role) + { + _container = container; + _label = label; + _role = role; + } + + public ProductImpl(Container container, String label, String role, String type) + { + this(container, label, role); + _type = type; + } + + public boolean isNew() + { + return _rowId == 0; + } + + public Object getPrimaryKey() + { + return getRowId(); + } + + @Override + public int getRowId() + { + return _rowId; + } + + public void setRowId(int rowId) + { + _rowId = rowId; + } + + @Override + public String getLabel() + { + return _label; + } + + public void setLabel(String label) + { + _label = label; + } + + @Override + public String getRole() + { + return _role; + } + + public void setRole(String role) + { + _role = role; + } + + @Override + public String getType() + { + return _type; + } + + public void setType(String type) + { + _type = type; + } + + public List getAntigens() + { + return _antigens; + } + + public void setAntigens(List antigens) + { + _antigens = antigens; + } + + public String getDose() + { + return _dose; + } + + public void setDose(String dose) + { + _dose = dose; + } + + public String getRoute() + { + return _route; + } + + public void setRoute(String route) + { + _route = route; + } + + public Container getContainer() + { + return _container; + } + + public void setContainer(Container container) + { + _container = container; + } + + public List getDoseAndRoutes() + { + return _doseAndRoutes; + } + + public void setDoseAndRoutes(List doseAndRoutes) + { + _doseAndRoutes = doseAndRoutes; + } + + public Map serialize() + { + Map props = new HashMap<>(); + props.put("RowId", getRowId()); + props.put("Label", getLabel()); + props.put("Role", getRole()); + props.put("Type", getType()); + return props; + } + + public static ProductImpl fromJSON(@NotNull JSONObject o, Container container) + { + ProductImpl product = new ProductImpl(container, o.getString("Label"), o.getString("Role"), o.getString("Type")); + + if (o.has("RowId")) + product.setRowId(o.getInt("RowId")); + + if (o.has("Antigens") && o.get("Antigens") instanceof JSONArray antigensJSON) + { + List antigens = new ArrayList<>(); + for (JSONObject productAntigen : JsonUtil.toJSONObjectList(antigensJSON)) + antigens.add(ProductAntigenImpl.fromJSON(productAntigen, container)); + + product.setAntigens(antigens); + } + + if (o.has("DoseAndRoute") && o.get("DoseAndRoute") instanceof JSONArray doseJSON) + { + List doseAndRoutes = new ArrayList<>(); + for (JSONObject doseAndRoute : JsonUtil.toJSONObjectList(doseJSON)) + doseAndRoutes.add(DoseAndRoute.fromJSON(doseAndRoute, container, product.getRowId())); + + product.setDoseAndRoutes(doseAndRoutes); + } + + return product; + } +} diff --git a/study/src/org/labkey/study/model/StudyAssaySchedule.java b/studydesign/src/org/labkey/studydesign/model/StudyAssaySchedule.java similarity index 94% rename from study/src/org/labkey/study/model/StudyAssaySchedule.java rename to studydesign/src/org/labkey/studydesign/model/StudyAssaySchedule.java index a7e928c8..9128f5d1 100644 --- a/study/src/org/labkey/study/model/StudyAssaySchedule.java +++ b/studydesign/src/org/labkey/studydesign/model/StudyAssaySchedule.java @@ -1,77 +1,77 @@ -/* - * Copyright (c) 2016 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.study.model; - -import org.json.JSONArray; -import org.json.JSONObject; -import org.labkey.api.action.ApiJsonForm; -import org.labkey.api.data.Container; -import org.labkey.api.util.JsonUtil; -import org.labkey.api.view.HttpView; - -import java.util.ArrayList; -import java.util.List; - -public class StudyAssaySchedule implements ApiJsonForm -{ - Container _container; - List _assays; - String _assayPlan; - - public StudyAssaySchedule() - {} - - public StudyAssaySchedule(Container container) - { - _container = container; - } - - public void setAssays(List assays) - { - _assays = assays; - } - - public List getAssays() - { - return _assays; - } - - public void setAssayPlan(String assayPlan) - { - _assayPlan = assayPlan; - } - - public String getAssayPlan() - { - return _assayPlan; - } - - @Override - public void bindJson(JSONObject json) - { - _container = HttpView.currentContext().getContainer(); - - JSONArray assaysJSON = json.optJSONArray("assays"); - if (assaysJSON != null) - { - _assays = new ArrayList<>(); - for (JSONObject assayJSON : JsonUtil.toJSONObjectList(assaysJSON)) - _assays.add(AssaySpecimenConfigImpl.fromJSON(assayJSON, _container)); - } - - _assayPlan = json.optString("assayPlan", null); - } -} +/* + * Copyright (c) 2016 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.studydesign.model; + +import org.json.JSONArray; +import org.json.JSONObject; +import org.labkey.api.action.ApiJsonForm; +import org.labkey.api.data.Container; +import org.labkey.api.util.JsonUtil; +import org.labkey.api.view.HttpView; + +import java.util.ArrayList; +import java.util.List; + +public class StudyAssaySchedule implements ApiJsonForm +{ + Container _container; + List _assays; + String _assayPlan; + + public StudyAssaySchedule() + {} + + public StudyAssaySchedule(Container container) + { + _container = container; + } + + public void setAssays(List assays) + { + _assays = assays; + } + + public List getAssays() + { + return _assays; + } + + public void setAssayPlan(String assayPlan) + { + _assayPlan = assayPlan; + } + + public String getAssayPlan() + { + return _assayPlan; + } + + @Override + public void bindJson(JSONObject json) + { + _container = HttpView.currentContext().getContainer(); + + JSONArray assaysJSON = json.optJSONArray("assays"); + if (assaysJSON != null) + { + _assays = new ArrayList<>(); + for (JSONObject assayJSON : JsonUtil.toJSONObjectList(assaysJSON)) + _assays.add(AssaySpecimenConfigImpl.fromJSON(assayJSON, _container)); + } + + _assayPlan = json.optString("assayPlan", null); + } +} diff --git a/studydesign/src/org/labkey/studydesign/model/StudyDesignCohort.java b/studydesign/src/org/labkey/studydesign/model/StudyDesignCohort.java new file mode 100644 index 00000000..d040fa74 --- /dev/null +++ b/studydesign/src/org/labkey/studydesign/model/StudyDesignCohort.java @@ -0,0 +1,94 @@ +package org.labkey.studydesign.model; + +import org.jetbrains.annotations.NotNull; +import org.json.JSONArray; +import org.json.JSONObject; +import org.labkey.api.study.Cohort; +import org.labkey.api.util.JsonUtil; + +import java.util.ArrayList; +import java.util.List; + +/** + * Used as a bean in the treatment schedule to map to actual study cohorts + */ +public class StudyDesignCohort +{ + private int _rowId; + private String _label; + private Integer _subjectCount; + List _treatmentVisitMap = new ArrayList<>(); + + public StudyDesignCohort() + { + } + + public StudyDesignCohort(Cohort cohort) + { + _rowId = cohort.getRowId(); + _label = cohort.getLabel(); + _subjectCount = cohort.getSubjectCount(); + } + + public int getRowId() + { + return _rowId; + } + + public void setRowId(int rowId) + { + _rowId = rowId; + } + + public String getLabel() + { + return _label; + } + + public void setLabel(String label) + { + _label = label; + } + + public Integer getSubjectCount() + { + return _subjectCount; + } + + public void setSubjectCount(Integer subjectCount) + { + _subjectCount = subjectCount; + } + + public List getTreatmentVisitMap() + { + return _treatmentVisitMap; + } + + public void setTreatmentVisitMap(List treatmentVisitMap) + { + _treatmentVisitMap = treatmentVisitMap; + } + + public static StudyDesignCohort fromJSON(@NotNull JSONObject o) + { + StudyDesignCohort cohort = new StudyDesignCohort(); + cohort.setLabel(o.getString("Label")); + if (o.has("SubjectCount") && !"".equals(o.getString("SubjectCount"))) + cohort.setSubjectCount(o.getInt("SubjectCount")); + if (o.has("RowId")) + cohort.setRowId(o.getInt("RowId")); + + JSONArray visitMapJSON = o.optJSONArray("VisitMap"); + if (visitMapJSON != null) + { + List treatmentVisitMap = new ArrayList<>(); + for (JSONObject json : JsonUtil.toJSONObjectList(visitMapJSON)) + treatmentVisitMap.add(TreatmentVisitMapImpl.fromJSON(json)); + + cohort.setTreatmentVisitMap(treatmentVisitMap); + } + + return cohort; + } +} diff --git a/study/src/org/labkey/study/model/StudyTreatmentSchedule.java b/studydesign/src/org/labkey/studydesign/model/StudyTreatmentSchedule.java similarity index 85% rename from study/src/org/labkey/study/model/StudyTreatmentSchedule.java rename to studydesign/src/org/labkey/studydesign/model/StudyTreatmentSchedule.java index 0e08ba28..f9afc6e7 100644 --- a/study/src/org/labkey/study/model/StudyTreatmentSchedule.java +++ b/studydesign/src/org/labkey/studydesign/model/StudyTreatmentSchedule.java @@ -1,174 +1,174 @@ -/* - * Copyright (c) 2014-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.study.model; - -import org.json.JSONArray; -import org.json.JSONObject; -import org.labkey.api.action.ApiJsonForm; -import org.labkey.api.data.Container; -import org.labkey.api.util.JsonUtil; -import org.labkey.api.view.HttpView; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -/** - * Represents a study's cohort/treatment/visit mapping information. Used to serialize JSON to the treatment schedule. - */ -public class StudyTreatmentSchedule implements ApiJsonForm -{ - Container _container; - - // treatment schedule properties - List _treatments; - Collection _visits; - Collection _cohorts; - - public StudyTreatmentSchedule() - {} - - public StudyTreatmentSchedule(Container container) - { - _container = container; - } - - public void setTreatments(List treatments) - { - _treatments = treatments; - } - - public List getTreatments() - { - return _treatments; - } - - public List> serializeTreatments() - { - List> treatmentList = new ArrayList<>(); - for (TreatmentImpl treatment : _treatments) - { - treatmentList.add(treatment.serialize()); - } - return treatmentList; - } - - public void setVisits(Collection visits) - { - _visits = visits; - } - - public Collection getVisits() - { - return _visits; - } - - public List> serializeVisits() - { - List> visitList = new ArrayList<>(); - List includedIds = getIncludedVisitIds(); - for (VisitImpl v : _visits) - { - Map visitProperties = new HashMap<>(); - visitProperties.put("RowId", v.getRowId()); - visitProperties.put("Label", v.getDisplayString()); - visitProperties.put("DisplayOrder", v.getDisplayOrder()); - visitProperties.put("SequenceNumMin", v.getSequenceNumMin()); - - // tag those visits that are used in the treatment schedule - visitProperties.put("Included", includedIds.contains(v.getRowId())); - - visitList.add(visitProperties); - } - return visitList; - } - - private List getIncludedVisitIds() - { - List ids = new ArrayList<>(); - for (CohortImpl cohort : _cohorts) - { - for (TreatmentVisitMapImpl tvm : TreatmentManager.getInstance().getStudyTreatmentVisitMap(_container, cohort.getRowId())) - { - if (!ids.contains(tvm.getVisitId())) - ids.add(tvm.getVisitId()); - } - } - - return ids; - } - - public void setCohorts(Collection cohorts) - { - _cohorts = cohorts; - } - - public Collection getCohorts() - { - return _cohorts; - } - - public List> serializeCohortMapping() - { - List> cohortMappingList = new ArrayList<>(); - for (CohortImpl cohort : _cohorts) - { - Map mapProperties = new HashMap<>(); - mapProperties.put("RowId", cohort.getRowId()); - mapProperties.put("Label", cohort.getLabel()); - mapProperties.put("SubjectCount", cohort.getSubjectCount()); - mapProperties.put("CanDelete", !cohort.isInUse()); - - List> treatmentVisitMap = new ArrayList<>(); - for (TreatmentVisitMapImpl mapping : TreatmentManager.getInstance().getStudyTreatmentVisitMap(_container, cohort.getRowId())) - { - Map visitMapping = new HashMap<>(); - visitMapping.put("CohortId", mapping.getCohortId()); - visitMapping.put("TreatmentId", mapping.getTreatmentId()); - visitMapping.put("VisitId", mapping.getVisitId()); - treatmentVisitMap.add(visitMapping); - } - mapProperties.put("VisitMap", treatmentVisitMap); - - cohortMappingList.add(mapProperties); - } - return cohortMappingList; - } - - @Override - public void bindJson(JSONObject json) - { - _container = HttpView.currentContext().getContainer(); - - JSONArray treatmentsJSON = json.optJSONArray("treatments"); - if (treatmentsJSON != null) - { - _treatments = new ArrayList<>(); - for (JSONObject treatment : JsonUtil.toJSONObjectList(treatmentsJSON)) - _treatments.add(TreatmentImpl.fromJSON(treatment, _container)); - } - - JSONArray cohortsJSON = json.optJSONArray("cohorts"); - if (cohortsJSON != null) - { - _cohorts = new ArrayList<>(); - for (JSONObject cohortJSON : JsonUtil.toJSONObjectList(cohortsJSON)) - _cohorts.add(CohortImpl.fromJSON(cohortJSON)); - } - } -} +/* + * Copyright (c) 2014-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.studydesign.model; + +import org.json.JSONArray; +import org.json.JSONObject; +import org.labkey.api.action.ApiJsonForm; +import org.labkey.api.data.Container; +import org.labkey.api.study.Cohort; +import org.labkey.api.study.Visit; +import org.labkey.api.util.JsonUtil; +import org.labkey.api.view.HttpView; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * Represents a study's cohort/treatment/visit mapping information. Used to serialize JSON to the treatment schedule. + */ +public class StudyTreatmentSchedule implements ApiJsonForm +{ + Container _container; + + // treatment schedule properties + List _treatments; + Collection _visits; + Collection _cohorts; + + public StudyTreatmentSchedule() + {} + + public StudyTreatmentSchedule(Container container) + { + _container = container; + } + + public void setTreatments(List treatments) + { + _treatments = treatments; + } + + public List getTreatments() + { + return _treatments; + } + + public List> serializeTreatments() + { + List> treatmentList = new ArrayList<>(); + for (TreatmentImpl treatment : _treatments) + { + treatmentList.add(treatment.serialize()); + } + return treatmentList; + } + + public void setVisits(Collection visits) + { + _visits = visits; + } + + public Collection getVisits() + { + return _visits; + } + + public List> serializeVisits() + { + List> visitList = new ArrayList<>(); + List includedIds = getIncludedVisitIds(); + for (Visit v : _visits) + { + Map visitProperties = new HashMap<>(); + visitProperties.put("RowId", v.getId()); + visitProperties.put("Label", v.getDisplayString()); + visitProperties.put("DisplayOrder", v.getDisplayOrder()); + visitProperties.put("SequenceNumMin", v.getSequenceNumMin()); + + // tag those visits that are used in the treatment schedule + visitProperties.put("Included", includedIds.contains(v.getId())); + + visitList.add(visitProperties); + } + return visitList; + } + + private List getIncludedVisitIds() + { + List ids = new ArrayList<>(); + for (StudyDesignCohort cohort : _cohorts) + { + for (TreatmentVisitMapImpl tvm : TreatmentManager.getInstance().getStudyTreatmentVisitMap(_container, cohort.getRowId())) + { + if (!ids.contains(tvm.getVisitId())) + ids.add(tvm.getVisitId()); + } + } + + return ids; + } + + public Collection getCohorts() + { + return _cohorts; + } + + public List> serializeCohortMapping(Collection cohorts) + { + List> cohortMappingList = new ArrayList<>(); + _cohorts = new ArrayList<>(); + for (Cohort cohort : cohorts) + { + _cohorts.add(new StudyDesignCohort(cohort)); + + Map mapProperties = new HashMap<>(); + mapProperties.put("RowId", cohort.getRowId()); + mapProperties.put("Label", cohort.getLabel()); + mapProperties.put("SubjectCount", cohort.getSubjectCount()); + mapProperties.put("CanDelete", !cohort.isInUse()); + + List> treatmentVisitMap = new ArrayList<>(); + for (TreatmentVisitMapImpl mapping : TreatmentManager.getInstance().getStudyTreatmentVisitMap(_container, cohort.getRowId())) + { + Map visitMapping = new HashMap<>(); + visitMapping.put("CohortId", mapping.getCohortId()); + visitMapping.put("TreatmentId", mapping.getTreatmentId()); + visitMapping.put("VisitId", mapping.getVisitId()); + treatmentVisitMap.add(visitMapping); + } + mapProperties.put("VisitMap", treatmentVisitMap); + + cohortMappingList.add(mapProperties); + } + return cohortMappingList; + } + + @Override + public void bindJson(JSONObject json) + { + _container = HttpView.currentContext().getContainer(); + + JSONArray treatmentsJSON = json.optJSONArray("treatments"); + if (treatmentsJSON != null) + { + _treatments = new ArrayList<>(); + for (JSONObject treatment : JsonUtil.toJSONObjectList(treatmentsJSON)) + _treatments.add(TreatmentImpl.fromJSON(treatment, _container)); + } + + JSONArray cohortsJSON = json.optJSONArray("cohorts"); + if (cohortsJSON != null) + { + _cohorts = new ArrayList<>(); + for (JSONObject cohortJSON : JsonUtil.toJSONObjectList(cohortsJSON)) + _cohorts.add(StudyDesignCohort.fromJSON(cohortJSON)); + } + } +} diff --git a/study/src/org/labkey/study/model/TreatmentImpl.java b/studydesign/src/org/labkey/studydesign/model/TreatmentImpl.java similarity index 95% rename from study/src/org/labkey/study/model/TreatmentImpl.java rename to studydesign/src/org/labkey/studydesign/model/TreatmentImpl.java index acbd1d0f..8558fafc 100644 --- a/study/src/org/labkey/study/model/TreatmentImpl.java +++ b/studydesign/src/org/labkey/studydesign/model/TreatmentImpl.java @@ -1,226 +1,226 @@ -/* - * Copyright (c) 2013-2016 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.study.model; - -import org.jetbrains.annotations.NotNull; -import org.json.JSONArray; -import org.json.JSONObject; -import org.labkey.api.data.Container; -import org.labkey.api.data.Sort; -import org.labkey.api.query.FieldKey; -import org.labkey.api.study.Treatment; -import org.labkey.api.util.JsonUtil; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -/** - * User: cnathe - * Date: 12/27/13 - */ -public class TreatmentImpl implements Treatment -{ - private Container _container; - private int _rowId; - private String _tempRowId; // used to map new treatment records being inserted to their usage in TreatmentVisitMapImpl - private String _label; - private String _description; - private List _treatmentProducts; - private List _products; - - public TreatmentImpl() - {} - - public TreatmentImpl(Container container, String label, String description) - { - _container = container; - _label = label; - _description = description; - } - - public boolean isNew() - { - return _rowId == 0; - } - - public Object getPrimaryKey() - { - return getRowId(); - } - - @Override - public int getRowId() - { - return _rowId; - } - - public void setRowId(int rowId) - { - _rowId = rowId; - } - - private void setTempRowId(String tempRowId) - { - _tempRowId = tempRowId; - } - - public String getTempRowId() - { - return _tempRowId; - } - - @Override - public String getLabel() - { - return _label; - } - - public void setLabel(String label) - { - _label = label; - } - - @Override - public String getDescription() - { - return _description; - } - - public void setDescription(String description) - { - _description = description; - } - - public List getTreatmentProducts() - { - return _treatmentProducts; - } - - public void setTreatmentProducts(List treatmentProducts) - { - _treatmentProducts = treatmentProducts; - } - - public List getProducts() - { - return _products; - } - - public void setProducts(List products) - { - _products = products; - } - - public void addProduct(ProductImpl product) - { - if (_products == null) - _products = new ArrayList<>(); - - _products.add(product); - } - - public Container getContainer() - { - return _container; - } - - public void setContainer(Container container) - { - _container = container; - } - - public Map serialize() - { - Map props = new HashMap<>(); - props.put("RowId", getRowId()); - props.put("Label", getLabel()); - props.put("Description", getDescription()); - return props; - } - - public Sort getProductSort() - { - // sort the product list to match the manage study products page (i.e. Immunogens before Adjuvants) - Sort sort = new Sort(); - sort.appendSortColumn(FieldKey.fromParts("ProductId", "Role"), Sort.SortDirection.DESC, false); - sort.appendSortColumn(FieldKey.fromParts("ProductId", "RowId"), Sort.SortDirection.ASC, false); - return sort; - } - - public static TreatmentImpl fromJSON(@NotNull JSONObject o, Container container) - { - TreatmentImpl treatment = new TreatmentImpl(container, o.getString("Label"), o.optString("Description", null)); - - if (o.has("RowId")) - { - if (o.get("RowId") instanceof Integer) - treatment.setRowId(o.getInt("RowId")); - else - treatment.setTempRowId(o.getString("RowId")); - } - - if (o.has("Products") && o.get("Products") instanceof JSONArray productsJSON) - { - List treatmentProducts = new ArrayList<>(); - for (JSONObject productJson : JsonUtil.toJSONObjectList(productsJSON)) - treatmentProducts.add(TreatmentProductImpl.fromJSON(productJson, container)); - - treatment.setTreatmentProducts(treatmentProducts); - } - - return treatment; - } - - public boolean isSameTreatmentProductsWith(TreatmentImpl other) - { - if (other == null) - return false; - if (this.getTreatmentProducts() == null) - { - return other.getTreatmentProducts() == null; - } - else - { - if (other.getTreatmentProducts() == null) - return false; - - if (this.getTreatmentProducts().size() != other.getTreatmentProducts().size()) - return false; - - boolean hasMismatch = false; - for (TreatmentProductImpl product : this.getTreatmentProducts()) - { - boolean foundMatch =false; - for (TreatmentProductImpl otherProduct : other.getTreatmentProducts()) - { - if (product.isSameTreatmentProductWith(otherProduct)) - { - foundMatch = true; - break; - } - } - if (!foundMatch) - { - hasMismatch = true; - break; - } - } - return !hasMismatch; - } - } -} +/* + * Copyright (c) 2013-2016 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.studydesign.model; + +import org.jetbrains.annotations.NotNull; +import org.json.JSONArray; +import org.json.JSONObject; +import org.labkey.api.data.Container; +import org.labkey.api.data.Sort; +import org.labkey.api.query.FieldKey; +import org.labkey.api.study.Treatment; +import org.labkey.api.util.JsonUtil; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * User: cnathe + * Date: 12/27/13 + */ +public class TreatmentImpl implements Treatment +{ + private Container _container; + private int _rowId; + private String _tempRowId; // used to map new treatment records being inserted to their usage in TreatmentVisitMapImpl + private String _label; + private String _description; + private List _treatmentProducts; + private List _products; + + public TreatmentImpl() + {} + + public TreatmentImpl(Container container, String label, String description) + { + _container = container; + _label = label; + _description = description; + } + + public boolean isNew() + { + return _rowId == 0; + } + + public Object getPrimaryKey() + { + return getRowId(); + } + + @Override + public int getRowId() + { + return _rowId; + } + + public void setRowId(int rowId) + { + _rowId = rowId; + } + + private void setTempRowId(String tempRowId) + { + _tempRowId = tempRowId; + } + + public String getTempRowId() + { + return _tempRowId; + } + + @Override + public String getLabel() + { + return _label; + } + + public void setLabel(String label) + { + _label = label; + } + + @Override + public String getDescription() + { + return _description; + } + + public void setDescription(String description) + { + _description = description; + } + + public List getTreatmentProducts() + { + return _treatmentProducts; + } + + public void setTreatmentProducts(List treatmentProducts) + { + _treatmentProducts = treatmentProducts; + } + + public List getProducts() + { + return _products; + } + + public void setProducts(List products) + { + _products = products; + } + + public void addProduct(ProductImpl product) + { + if (_products == null) + _products = new ArrayList<>(); + + _products.add(product); + } + + public Container getContainer() + { + return _container; + } + + public void setContainer(Container container) + { + _container = container; + } + + public Map serialize() + { + Map props = new HashMap<>(); + props.put("RowId", getRowId()); + props.put("Label", getLabel()); + props.put("Description", getDescription()); + return props; + } + + public Sort getProductSort() + { + // sort the product list to match the manage study products page (i.e. Immunogens before Adjuvants) + Sort sort = new Sort(); + sort.appendSortColumn(FieldKey.fromParts("ProductId", "Role"), Sort.SortDirection.DESC, false); + sort.appendSortColumn(FieldKey.fromParts("ProductId", "RowId"), Sort.SortDirection.ASC, false); + return sort; + } + + public static TreatmentImpl fromJSON(@NotNull JSONObject o, Container container) + { + TreatmentImpl treatment = new TreatmentImpl(container, o.getString("Label"), o.optString("Description", null)); + + if (o.has("RowId")) + { + if (o.get("RowId") instanceof Integer) + treatment.setRowId(o.getInt("RowId")); + else + treatment.setTempRowId(o.getString("RowId")); + } + + if (o.has("Products") && o.get("Products") instanceof JSONArray productsJSON) + { + List treatmentProducts = new ArrayList<>(); + for (JSONObject productJson : JsonUtil.toJSONObjectList(productsJSON)) + treatmentProducts.add(TreatmentProductImpl.fromJSON(productJson, container)); + + treatment.setTreatmentProducts(treatmentProducts); + } + + return treatment; + } + + public boolean isSameTreatmentProductsWith(TreatmentImpl other) + { + if (other == null) + return false; + if (this.getTreatmentProducts() == null) + { + return other.getTreatmentProducts() == null; + } + else + { + if (other.getTreatmentProducts() == null) + return false; + + if (this.getTreatmentProducts().size() != other.getTreatmentProducts().size()) + return false; + + boolean hasMismatch = false; + for (TreatmentProductImpl product : this.getTreatmentProducts()) + { + boolean foundMatch =false; + for (TreatmentProductImpl otherProduct : other.getTreatmentProducts()) + { + if (product.isSameTreatmentProductWith(otherProduct)) + { + foundMatch = true; + break; + } + } + if (!foundMatch) + { + hasMismatch = true; + break; + } + } + return !hasMismatch; + } + } +} diff --git a/study/src/org/labkey/study/model/TreatmentManager.java b/studydesign/src/org/labkey/studydesign/model/TreatmentManager.java similarity index 71% rename from study/src/org/labkey/study/model/TreatmentManager.java rename to studydesign/src/org/labkey/studydesign/model/TreatmentManager.java index 64b56976..187d2ac4 100644 --- a/study/src/org/labkey/study/model/TreatmentManager.java +++ b/studydesign/src/org/labkey/studydesign/model/TreatmentManager.java @@ -1,921 +1,1151 @@ -/* - * Copyright (c) 2014-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.study.model; - -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; -import org.junit.Assert; -import org.junit.Test; -import org.labkey.api.data.ColumnInfo; -import org.labkey.api.data.CompareType; -import org.labkey.api.data.Container; -import org.labkey.api.data.ContainerManager; -import org.labkey.api.data.DbScope; -import org.labkey.api.data.SimpleFilter; -import org.labkey.api.data.Sort; -import org.labkey.api.data.Table; -import org.labkey.api.data.TableInfo; -import org.labkey.api.data.TableSelector; -import org.labkey.api.query.BatchValidationException; -import org.labkey.api.query.FieldKey; -import org.labkey.api.query.FilteredTable; -import org.labkey.api.query.QueryService; -import org.labkey.api.query.QueryUpdateService; -import org.labkey.api.query.UserSchema; -import org.labkey.api.query.ValidationException; -import org.labkey.api.security.User; -import org.labkey.api.study.TimepointType; -import org.labkey.api.study.Visit; -import org.labkey.api.studydesign.query.StudyDesignQuerySchema; -import org.labkey.api.studydesign.query.StudyDesignSchema; -import org.labkey.api.test.TestWhen; -import org.labkey.api.util.DateUtil; -import org.labkey.api.util.GUID; -import org.labkey.api.util.JunitUtil; -import org.labkey.api.util.TestContext; -import org.labkey.study.StudySchema; -import org.labkey.study.query.StudyQuerySchema; - -import java.math.BigDecimal; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.Date; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Set; - -/** - * Created by cnathe on 1/24/14. - */ -public class TreatmentManager -{ - private static final TreatmentManager _instance = new TreatmentManager(); - - private TreatmentManager() - { - } - - public static TreatmentManager getInstance() - { - return _instance; - } - - public List getStudyProducts(Container container, User user) - { - return getStudyProducts(container, user, null, null); - } - - public List getStudyProducts(Container container, User user, @Nullable String role, @Nullable Integer rowId) - { - //Using a user schema so containerFilter will be created for us later (so don't need SimpleFilter.createContainerFilter) - SimpleFilter filter = new SimpleFilter(); - if (role != null) - filter.addCondition(FieldKey.fromParts("Role"), role); - if (rowId != null) - filter.addCondition(FieldKey.fromParts("RowId"), rowId); - - TableInfo ti = QueryService.get().getUserSchema(user, container, StudyQuerySchema.SCHEMA_NAME).getTable(StudyDesignQuerySchema.PRODUCT_TABLE_NAME); - return new TableSelector(ti, filter, new Sort("RowId")).getArrayList(ProductImpl.class); - } - - public List getFilteredStudyProducts(Container container, User user, List filterRowIds) - { - TableInfo ti = QueryService.get().getUserSchema(user, container, StudyQuerySchema.SCHEMA_NAME).getTable(StudyDesignQuerySchema.PRODUCT_TABLE_NAME); - - //Using a user schema so containerFilter will be created for us later (so don't need SimpleFilter.createContainerFilter) - SimpleFilter filter = new SimpleFilter(); - if (filterRowIds != null && !filterRowIds.isEmpty()) - filter.addCondition(FieldKey.fromParts("RowId"), filterRowIds, CompareType.NOT_IN); - - return new TableSelector(ti, filter, new Sort("RowId")).getArrayList(ProductImpl.class); - } - - public List getStudyProductAntigens(Container container, User user, int productId) - { - SimpleFilter filter = new SimpleFilter(); - filter.addCondition(FieldKey.fromParts("ProductId"), productId); - - TableInfo ti = QueryService.get().getUserSchema(user, container, StudyQuerySchema.SCHEMA_NAME).getTable(StudyDesignQuerySchema.PRODUCT_ANTIGEN_TABLE_NAME); - return new TableSelector(ti, filter, new Sort("RowId")).getArrayList(ProductAntigenImpl.class); - } - - public List getFilteredStudyProductAntigens(Container container, User user, @NotNull Integer productId, List filterRowIds) - { - TableInfo ti = QueryService.get().getUserSchema(user, container, StudyQuerySchema.SCHEMA_NAME).getTable(StudyDesignQuerySchema.PRODUCT_ANTIGEN_TABLE_NAME); - - //Using a user schema so containerFilter will be created for us later (so don't need SimpleFilter.createContainerFilter) - SimpleFilter filter = new SimpleFilter(); - filter.addCondition(FieldKey.fromParts("ProductId"), productId); - if (filterRowIds != null && !filterRowIds.isEmpty()) - filter.addCondition(FieldKey.fromParts("RowId"), filterRowIds, CompareType.NOT_IN); - - return new TableSelector(ti, filter, new Sort("RowId")).getArrayList(ProductAntigenImpl.class); - } - - public Integer saveTreatment(Container container, User user, TreatmentImpl treatment) throws Exception - { - TableInfo treatmentTable = QueryService.get().getUserSchema(user, container, StudyQuerySchema.SCHEMA_NAME).getTable(StudyDesignQuerySchema.TREATMENT_TABLE_NAME); - return saveStudyDesignRow(container, user, treatmentTable, treatment.serialize(), treatment.isNew() ? null : treatment.getRowId(), "RowId"); - } - - public List getStudyTreatments(Container container, User user) - { - SimpleFilter filter = new SimpleFilter(); - TableInfo ti = QueryService.get().getUserSchema(user, container, StudyQuerySchema.SCHEMA_NAME).getTable(StudyDesignQuerySchema.TREATMENT_TABLE_NAME); - return new TableSelector(ti, filter, new Sort("RowId")).getArrayList(TreatmentImpl.class); - } - - public TreatmentImpl getStudyTreatmentByRowId(Container container, User user, int rowId) - { - SimpleFilter filter = new SimpleFilter(FieldKey.fromParts("RowId"), rowId); - TableInfo ti = QueryService.get().getUserSchema(user, container, StudyQuerySchema.SCHEMA_NAME).getTable(StudyDesignQuerySchema.TREATMENT_TABLE_NAME); - TreatmentImpl treatment = new TableSelector(ti, filter, null).getObject(TreatmentImpl.class); - - // attach the associated study products to the treatment object - if (treatment != null) - { - List treatmentProducts = getStudyTreatmentProducts(container, user, treatment.getRowId(), treatment.getProductSort()); - for (TreatmentProductImpl treatmentProduct : treatmentProducts) - { - List products = getStudyProducts(container, user, null, treatmentProduct.getProductId()); - for (ProductImpl product : products) - { - product.setDose(treatmentProduct.getDose()); - product.setRoute(treatmentProduct.getRoute()); - treatment.addProduct(product); - } - } - } - - return treatment; - } - - public List getFilteredTreatments(Container container, User user, List definedTreatmentIds, Set usedTreatmentIds) - { - List filterRowIds = new ArrayList<>(); - filterRowIds.addAll(definedTreatmentIds); - filterRowIds.addAll(usedTreatmentIds); - TableInfo ti = QueryService.get().getUserSchema(user, container, StudyQuerySchema.SCHEMA_NAME).getTable(StudyDesignQuerySchema.TREATMENT_TABLE_NAME); - - //Using a user schema so containerFilter will be created for us later (so don't need SimpleFilter.createContainerFilter) - SimpleFilter filter = new SimpleFilter().addCondition(FieldKey.fromParts("RowId"), filterRowIds, CompareType.NOT_IN); - - return new TableSelector(ti, filter, new Sort("RowId")).getArrayList(TreatmentImpl.class); - } - - public Integer saveTreatmentProductMapping(Container container, User user, TreatmentProductImpl treatmentProduct) throws Exception - { - TableInfo treatmentProductTable = QueryService.get().getUserSchema(user, container, StudyQuerySchema.SCHEMA_NAME).getTable(StudyDesignQuerySchema.TREATMENT_PRODUCT_MAP_TABLE_NAME); - return saveStudyDesignRow(container, user, treatmentProductTable, treatmentProduct.serialize(), treatmentProduct.isNew() ? null : treatmentProduct.getRowId(), "RowId"); - } - - public List getStudyTreatmentProducts(Container container, User user, int treatmentId) - { - return getStudyTreatmentProducts(container, user, treatmentId, new Sort("RowId")); - } - - public List getStudyTreatmentProducts(Container container, User user, int treatmentId, Sort sort) - { - SimpleFilter filter = new SimpleFilter(FieldKey.fromParts("TreatmentId"), treatmentId); - - TableInfo ti = QueryService.get().getUserSchema(user, container, StudyQuerySchema.SCHEMA_NAME).getTable(StudyDesignQuerySchema.TREATMENT_PRODUCT_MAP_TABLE_NAME); - return new TableSelector(ti, filter, sort).getArrayList(TreatmentProductImpl.class); - } - - public List getFilteredTreatmentProductMappings(Container container, User user, @NotNull Integer treatmentId, List filterRowIds) - { - TableInfo ti = QueryService.get().getUserSchema(user, container, StudyQuerySchema.SCHEMA_NAME).getTable(StudyDesignQuerySchema.TREATMENT_PRODUCT_MAP_TABLE_NAME); - - //Using a user schema so containerFilter will be created for us later (so don't need SimpleFilter.createContainerFilter) - SimpleFilter filter = new SimpleFilter(); - filter.addCondition(FieldKey.fromParts("TreatmentId"), treatmentId); - if (filterRowIds != null && !filterRowIds.isEmpty()) - filter.addCondition(FieldKey.fromParts("RowId"), filterRowIds, CompareType.NOT_IN); - - return new TableSelector(ti, filter, new Sort("RowId")).getArrayList(TreatmentProductImpl.class); - } - - public List getStudyTreatmentVisitMap(Container container, @Nullable Integer cohortId) - { - SimpleFilter filter = SimpleFilter.createContainerFilter(container); - if (cohortId != null) - filter.addCondition(FieldKey.fromParts("CohortId"), cohortId); - - TableInfo ti = StudyDesignSchema.getInstance().getTableInfoTreatmentVisitMap(); - return new TableSelector(ti, filter, new Sort("CohortId")).getArrayList(TreatmentVisitMapImpl.class); - } - - public List getVisitsForTreatmentSchedule(Container container) - { - SimpleFilter filter = SimpleFilter.createContainerFilter(container); - List visitRowIds = new TableSelector(StudyDesignSchema.getInstance().getTableInfoTreatmentVisitMap(), - Collections.singleton("VisitId"), filter, new Sort("VisitId")).getArrayList(Integer.class); - - return StudyManager.getInstance().getSortedVisitsByRowIds(container, visitRowIds); - } - - public TreatmentVisitMapImpl insertTreatmentVisitMap(User user, Container container, int cohortId, int visitId, int treatmentId) - { - TreatmentVisitMapImpl newMapping = new TreatmentVisitMapImpl(); - newMapping.setContainer(container); - newMapping.setCohortId(cohortId); - newMapping.setVisitId(visitId); - newMapping.setTreatmentId(treatmentId); - - return Table.insert(user, StudyDesignSchema.getInstance().getTableInfoTreatmentVisitMap(), newMapping); - } - - public void deleteTreatmentVisitMapForCohort(Container container, int rowId) - { - SimpleFilter filter = SimpleFilter.createContainerFilter(container); - filter.addCondition(FieldKey.fromParts("CohortId"), rowId); - Table.delete(StudyDesignSchema.getInstance().getTableInfoTreatmentVisitMap(), filter); - } - - public void deleteTreatmentVisitMapForVisit(Container container, int rowId) - { - SimpleFilter filter = SimpleFilter.createContainerFilter(container); - filter.addCondition(FieldKey.fromParts("VisitId"), rowId); - Table.delete(StudyDesignSchema.getInstance().getTableInfoTreatmentVisitMap(), filter); - } - - public void deleteTreatment(Container container, User user, int rowId) - { - StudySchema schema = StudySchema.getInstance(); - - try (DbScope.Transaction transaction = schema.getSchema().getScope().ensureTransaction()) - { - // delete the usages of this treatment in the TreatmentVisitMap - SimpleFilter filter = SimpleFilter.createContainerFilter(container); - filter.addCondition(FieldKey.fromParts("TreatmentId"), rowId); - Table.delete(StudyDesignSchema.getInstance().getTableInfoTreatmentVisitMap(), filter); - - // delete the associated treatment study product mappings (provision table) - filter = SimpleFilter.createContainerFilter(container); - filter.addCondition(FieldKey.fromParts("TreatmentId"), rowId); - deleteTreatmentProductMap(container, user, filter); - - // finally delete the record from the Treatment (provision table) - TableInfo treatmentTable = QueryService.get().getUserSchema(user, container, StudyQuerySchema.SCHEMA_NAME).getTable(StudyDesignQuerySchema.TREATMENT_TABLE_NAME); - if (treatmentTable != null) - { - QueryUpdateService qus = treatmentTable.getUpdateService(); - List> keys = new ArrayList<>(); - ColumnInfo treatmentPk = treatmentTable.getColumn(FieldKey.fromParts("RowId")); - keys.add(Collections.singletonMap(treatmentPk.getName(), rowId)); - qus.deleteRows(user, container, keys, null, null); - } - - transaction.commit(); - } - catch (Exception e) - { - throw new RuntimeException(e); - } - } - - public void deleteStudyProduct(Container container, User user, int rowId) - { - StudySchema schema = StudySchema.getInstance(); - - try (DbScope.Transaction transaction = schema.getSchema().getScope().ensureTransaction()) - { - // delete the usages of this study product in the ProductAntigen table (provision table) - deleteProductAntigens(container, user, rowId); - - // delete the associated doses and routes for this product - Table.delete(StudyDesignSchema.getInstance().getTableInfoDoseAndRoute(), new SimpleFilter(FieldKey.fromParts("ProductId"), rowId)); - - // delete the associated treatment study product mappings (provision table) - SimpleFilter filter = SimpleFilter.createContainerFilter(container); - filter.addCondition(FieldKey.fromParts("ProductId"), rowId); - deleteTreatmentProductMap(container, user, filter); - - // finally delete the record from the Products (provision table) - TableInfo productTable = QueryService.get().getUserSchema(user, container, StudyQuerySchema.SCHEMA_NAME).getTable(StudyDesignQuerySchema.PRODUCT_TABLE_NAME); - if (productTable != null) - { - QueryUpdateService qus = productTable.getUpdateService(); - List> keys = new ArrayList<>(); - ColumnInfo productPk = productTable.getColumn(FieldKey.fromParts("RowId")); - keys.add(Collections.singletonMap(productPk.getName(), rowId)); - qus.deleteRows(user, container, keys, null, null); - } - else - throw new IllegalStateException("Could not find table: " + StudyDesignQuerySchema.PRODUCT_TABLE_NAME); - - transaction.commit(); - } - catch (Exception e) - { - throw new RuntimeException(e); - } - } - - public Integer saveStudyProduct(Container container, User user, ProductImpl product) throws Exception - { - TableInfo productTable = QueryService.get().getUserSchema(user, container, StudyQuerySchema.SCHEMA_NAME).getTable(StudyDesignQuerySchema.PRODUCT_TABLE_NAME); - return saveStudyDesignRow(container, user, productTable, product.serialize(), product.isNew() ? null : product.getRowId(), "RowId"); - } - - public Integer saveStudyProductAntigen(Container container, User user, ProductAntigenImpl antigen) throws Exception - { - TableInfo productAntigenTable = QueryService.get().getUserSchema(user, container, StudyQuerySchema.SCHEMA_NAME).getTable(StudyDesignQuerySchema.PRODUCT_ANTIGEN_TABLE_NAME); - return saveStudyDesignRow(container, user, productAntigenTable, antigen.serialize(), antigen.isNew() ? null : antigen.getRowId(), "RowId"); - } - - public DoseAndRoute saveStudyProductDoseAndRoute(Container container, User user, DoseAndRoute doseAndRoute) - { - if (doseAndRoute.isNew()) - return Table.insert(user, StudyDesignSchema.getInstance().getTableInfoDoseAndRoute(), doseAndRoute); - else - return Table.update(user, StudyDesignSchema.getInstance().getTableInfoDoseAndRoute(), doseAndRoute, doseAndRoute.getRowId()); - } - - public Collection getStudyProductsDoseAndRoute(Container container, User user, int productId) - { - SimpleFilter filter = new SimpleFilter(FieldKey.fromParts("ProductId"), productId); - return new TableSelector(StudyDesignSchema.getInstance().getTableInfoDoseAndRoute(), filter, null).getCollection(DoseAndRoute.class); - } - - @Nullable - public DoseAndRoute getDoseAndRoute(Container container, String dose, String route, int productId) - { - SimpleFilter filter = new SimpleFilter(FieldKey.fromParts("ProductId"), productId); - if (dose != null) - filter.addCondition(FieldKey.fromParts("Dose"), dose); - else - filter.addCondition(FieldKey.fromParts("Dose"), null, CompareType.ISBLANK); - if (route != null) - filter.addCondition(FieldKey.fromParts("Route"), route); - else - filter.addCondition(FieldKey.fromParts("Route"), null, CompareType.ISBLANK); - Collection doseAndRoutes = new TableSelector(StudyDesignSchema.getInstance().getTableInfoDoseAndRoute(), filter, null).getCollection(DoseAndRoute.class); - - if (!doseAndRoutes.isEmpty()) - { - return doseAndRoutes.iterator().next(); - } - return null; - } - - public Integer saveAssaySpecimen(Container container, User user, AssaySpecimenConfigImpl assaySpecimen) throws Exception - { - TableInfo assaySpecimenTable = QueryService.get().getUserSchema(user, container, StudyQuerySchema.SCHEMA_NAME).getTable(StudyQuerySchema.ASSAY_SPECIMEN_TABLE_NAME); - Integer ret = saveStudyDesignRow(container, user, assaySpecimenTable, assaySpecimen.serialize(), assaySpecimen.isNew() ? null : assaySpecimen.getRowId(), "RowId", true); - StudyManager.getInstance().clearAssaySpecimenCache(container); - return ret; - } - - public Integer saveAssaySpecimenVisit(Container container, User user, AssaySpecimenVisitImpl assaySpecimenVisit) throws Exception - { - TableInfo assaySpecimenVIsitTable = QueryService.get().getUserSchema(user, container, StudyQuerySchema.SCHEMA_NAME).getTable(StudyQuerySchema.ASSAY_SPECIMEN_VISIT_TABLE_NAME); - return saveStudyDesignRow(container, user, assaySpecimenVIsitTable, assaySpecimenVisit.serialize(), assaySpecimenVisit.isNew() ? null : assaySpecimenVisit.getRowId(), "RowId", true); - } - - public List getFilteredAssaySpecimens(Container container, List filterRowIds) - { - SimpleFilter filter = SimpleFilter.createContainerFilter(container); - if (filterRowIds != null && !filterRowIds.isEmpty()) - filter.addCondition(FieldKey.fromParts("RowId"), filterRowIds, CompareType.NOT_IN); - - return new TableSelector(StudySchema.getInstance().getTableInfoAssaySpecimen(), filter, new Sort("RowId")).getArrayList(AssaySpecimenConfigImpl.class); - } - - public List getFilteredAssaySpecimenVisits(Container container, int assaySpecimenId, List filterRowIds) - { - SimpleFilter filter = SimpleFilter.createContainerFilter(container); - filter.addCondition(FieldKey.fromParts("AssaySpecimenId"), assaySpecimenId); - if (filterRowIds != null && !filterRowIds.isEmpty()) - filter.addCondition(FieldKey.fromParts("RowId"), filterRowIds, CompareType.NOT_IN); - - return new TableSelector(StudySchema.getInstance().getTableInfoAssaySpecimenVisit(), filter, new Sort("RowId")).getArrayList(AssaySpecimenVisitImpl.class); - } - - public Integer saveStudyDesignRow(Container container, User user, TableInfo tableInfo, Map row, Integer key, String pkColName) throws Exception - { - return saveStudyDesignRow(container, user, tableInfo, row, key, pkColName, false); - } - - public Integer saveStudyDesignRow(Container container, User user, TableInfo tableInfo, Map row, Integer key, String pkColName, boolean includeContainerKey) throws Exception - { - QueryUpdateService qus = tableInfo != null ? tableInfo.getUpdateService() : null; - if (qus != null) - { - BatchValidationException errors = new BatchValidationException(); - List> updatedRows; - - if (key == null) - { - updatedRows = qus.insertRows(user, container, Collections.singletonList(row), errors, null, null); - } - else - { - Map oldKey = new HashMap<>(); - oldKey.put(pkColName, key); - if (includeContainerKey) - oldKey.put("Container", container.getId()); - - updatedRows = qus.updateRows(user, container, Collections.singletonList(row), Collections.singletonList(oldKey), errors, null, null); - } - - if (errors.hasErrors()) - throw errors.getLastRowError(); - - if (updatedRows.size() == 1) - return (Integer) updatedRows.get(0).get(pkColName); - } - - return null; - } - - public void deleteStudyProductAntigen(Container container, User user, int rowId) throws Exception - { - TableInfo productAntigenTable = QueryService.get().getUserSchema(user, container, StudyQuerySchema.SCHEMA_NAME).getTable(StudyDesignQuerySchema.PRODUCT_ANTIGEN_TABLE_NAME); - if (productAntigenTable != null) - { - QueryUpdateService qus = productAntigenTable.getUpdateService(); - if (qus != null) - { - List> keys = Collections.singletonList(Collections.singletonMap("RowId", rowId)); - qus.deleteRows(user, container, keys, null, null); - } - else - throw new IllegalStateException("Could not find query update service for table: " + StudyDesignQuerySchema.PRODUCT_ANTIGEN_TABLE_NAME); - } - else - throw new IllegalStateException("Could not find table: " + StudyDesignQuerySchema.PRODUCT_ANTIGEN_TABLE_NAME); - } - - public void deleteProductAntigens(Container container, User user, int productId) throws Exception - { - TableInfo productAntigenTable = QueryService.get().getUserSchema(user, container, StudyQuerySchema.SCHEMA_NAME).getTable(StudyDesignQuerySchema.PRODUCT_ANTIGEN_TABLE_NAME); - if (productAntigenTable != null) - { - SimpleFilter filter = SimpleFilter.createContainerFilter(container); - filter.addCondition(FieldKey.fromParts("ProductId"), productId); - TableSelector selector = new TableSelector(productAntigenTable, Collections.singleton("RowId"), filter, null); - Integer[] productAntigenIds = selector.getArray(Integer.class); - - QueryUpdateService qus = productAntigenTable.getUpdateService(); - if (qus != null) - { - List> keys = new ArrayList<>(); - ColumnInfo productAntigenPk = productAntigenTable.getColumn(FieldKey.fromParts("RowId")); - for (Integer productAntigenId : productAntigenIds) - { - keys.add(Collections.singletonMap(productAntigenPk.getName(), productAntigenId)); - } - - qus.deleteRows(user, container, keys, null, null); - } - else - throw new IllegalStateException("Could not find query update service for table: " + StudyDesignQuerySchema.PRODUCT_ANTIGEN_TABLE_NAME); - } - else - throw new IllegalStateException("Could not find table: " + StudyDesignQuerySchema.PRODUCT_ANTIGEN_TABLE_NAME); - } - - public void deleteTreatmentProductMap(Container container, User user, SimpleFilter filter) throws Exception - { - TableInfo productMapTable = QueryService.get().getUserSchema(user, container, StudyQuerySchema.SCHEMA_NAME).getTable(StudyDesignQuerySchema.TREATMENT_PRODUCT_MAP_TABLE_NAME); - if (productMapTable != null) - { - TableSelector selector = new TableSelector(productMapTable, Collections.singleton("RowId"), filter, null); - deleteTreatmentProductMap(container, user, selector.getArrayList(Integer.class)); - } - else - throw new IllegalStateException("Could not find table: " + StudyDesignQuerySchema.TREATMENT_PRODUCT_MAP_TABLE_NAME); - } - - public void deleteTreatmentProductMap(Container container, User user, List rowIds) throws Exception - { - TableInfo productMapTable = QueryService.get().getUserSchema(user, container, StudyQuerySchema.SCHEMA_NAME).getTable(StudyDesignQuerySchema.TREATMENT_PRODUCT_MAP_TABLE_NAME); - if (productMapTable != null) - { - QueryUpdateService qus = productMapTable.getUpdateService(); - if (qus != null) - { - List> keys = new ArrayList<>(); - ColumnInfo productMapPk = productMapTable.getColumn(FieldKey.fromParts("RowId")); - for (Integer rowId : rowIds) - keys.add(Collections.singletonMap(productMapPk.getName(), rowId)); - - qus.deleteRows(user, container, keys, null, null); - } - else - throw new IllegalStateException("Could not find query update service for table: " + StudyDesignQuerySchema.TREATMENT_PRODUCT_MAP_TABLE_NAME); - } - else - throw new IllegalStateException("Could not find table: " + StudyDesignQuerySchema.TREATMENT_PRODUCT_MAP_TABLE_NAME); - } - - public void deleteAssaySpecimen(Container container, User user, int rowId) - { - // first delete any usages of the AssaySpecimenId in the AssaySpecimenVisit table - SimpleFilter filter = SimpleFilter.createContainerFilter(container); - filter.addCondition(FieldKey.fromParts("AssaySpecimenId"), rowId); - Table.delete(StudySchema.getInstance().getTableInfoAssaySpecimenVisit(), filter); - - // delete the AssaySpecimen record by RowId - filter = SimpleFilter.createContainerFilter(container); - filter.addCondition(FieldKey.fromParts("RowId"), rowId); - Table.delete(StudySchema.getInstance().getTableInfoAssaySpecimen(), filter); - StudyManager.getInstance().clearAssaySpecimenCache(container); - } - - public void deleteAssaySpecimenVisit(Container container, User user, int rowId) - { - SimpleFilter filter = SimpleFilter.createContainerFilter(container); - filter.addCondition(FieldKey.fromParts("RowId"), rowId); - Table.delete(StudySchema.getInstance().getTableInfoAssaySpecimenVisit(), filter); - } - - public String getStudyDesignRouteLabelByName(Container container, String name) - { - return StudyManager.getInstance().getStudyDesignLabelByName(container, StudyDesignSchema.getInstance().getTableInfoStudyDesignRoutes(), name); - } - - public String getStudyDesignImmunogenTypeLabelByName(Container container, String name) - { - return StudyManager.getInstance().getStudyDesignLabelByName(container, StudyDesignSchema.getInstance().getTableInfoStudyDesignImmunogenTypes(), name); - } - - public String getStudyDesignGeneLabelByName(Container container, String name) - { - return StudyManager.getInstance().getStudyDesignLabelByName(container, StudyDesignSchema.getInstance().getTableInfoStudyDesignGenes(), name); - } - - public String getStudyDesignSubTypeLabelByName(Container container, String name) - { - return StudyManager.getInstance().getStudyDesignLabelByName(container, StudyDesignSchema.getInstance().getTableInfoStudyDesignSubTypes(), name); - } - - public void updateTreatmentProducts(int treatmentId, List treatmentProducts, Container container, User user) throws Exception - { - // insert new study treatment product mappings and update any existing ones - List treatmentProductRowIds = new ArrayList<>(); - for (TreatmentProductImpl treatmentProduct : treatmentProducts) - { - // make sure the treatmentId is set based on the treatment rowId - treatmentProduct.setTreatmentId(treatmentId); - - Integer updatedRowId = TreatmentManager.getInstance().saveTreatmentProductMapping(container, user, treatmentProduct); - if (updatedRowId != null) - treatmentProductRowIds.add(updatedRowId); - } - - // delete any other treatment product mappings, not included in the insert/update list, for the given treatmentId - for (TreatmentProductImpl treatmentProduct : TreatmentManager.getInstance().getFilteredTreatmentProductMappings(container, user, treatmentId, treatmentProductRowIds)) - TreatmentManager.getInstance().deleteTreatmentProductMap(container, user, Collections.singletonList(treatmentProduct.getRowId())); - } - - /**** - * - * - * - * TESTING - * - * - */ - - @TestWhen(TestWhen.When.BVT) - public static class TreatmentDataTestCase extends Assert - { - TestContext _context = null; - User _user = null; - Container _container = null; - StudyImpl _junitStudy = null; - TreatmentManager _manager = TreatmentManager.getInstance(); - UserSchema _schema = null; - - Map _lookups = new HashMap<>(); - List _products = new ArrayList<>(); - List _treatments = new ArrayList<>(); - List _cohorts = new ArrayList<>(); - List _visits = new ArrayList<>(); - - @Test - public void test() throws Throwable - { - try - { - createStudy(); - _user = _context.getUser(); - _container = _junitStudy.getContainer(); - _schema = QueryService.get().getUserSchema(_user, _container, StudyQuerySchema.SCHEMA_NAME); - - populateLookupTables(); - populateStudyProducts(); - populateTreatments(); - populateTreatmentSchedule(); - - verifyStudyProducts(); - verifyTreatments(); - verifyTreatmentSchedule(); - verifyCleanUpTreatmentData(); - } - finally - { - tearDown(); - } - } - - private void verifyCleanUpTreatmentData() throws Exception - { - // remove cohort and verify delete of TreatmentVisitMap - StudyManager.getInstance().deleteCohort(_cohorts.get(0)); - verifyTreatmentVisitMapRecords(4); - - // remove visit and verify delete of TreatmentVisitMap - StudyManager.getInstance().deleteVisit(_junitStudy, _visits.get(0), _user); - verifyTreatmentVisitMapRecords(2); - - // we should still have all of our treatments and study products - verifyTreatments(); - verifyStudyProducts(); - - // remove treatment visit map records via visit - _manager.deleteTreatmentVisitMapForVisit(_container, _visits.get(1).getRowId()); - verifyTreatmentVisitMapRecords(0); - - // remove treatment and verify delete of TreatmentProductMap - _manager.deleteTreatment(_container, _user, _treatments.get(0).getRowId()); - verifyTreatmentProductMapRecords(_treatments.get(0).getRowId(), 0); - verifyTreatmentProductMapRecords(_treatments.get(1).getRowId(), 4); - - // remove product and verify delete of TreatmentProductMap and ProductAntigen - _manager.deleteStudyProduct(_container, _user, _products.get(0).getRowId()); - verifyTreatmentProductMapRecords(_treatments.get(1).getRowId(), 3); - verifyStudyProductAntigens(_products.get(0).getRowId(), 0); - verifyStudyProductAntigens(_products.get(1).getRowId(), 1); - - // directly delete product antigen - _manager.deleteProductAntigens(_container, _user, _products.get(1).getRowId()); - verifyStudyProductAntigens(_products.get(1).getRowId(), 0); - - // delete treatment product map by productId and then treatmentId - SimpleFilter filter = SimpleFilter.createContainerFilter(_container); - filter.addCondition(FieldKey.fromParts("ProductId"), _products.get(1).getRowId()); - _manager.deleteTreatmentProductMap(_container, _user, filter); - verifyTreatmentProductMapRecords(_treatments.get(1).getRowId(), 2); - filter = SimpleFilter.createContainerFilter(_container); - filter.addCondition(FieldKey.fromParts("TreatmentId"), _treatments.get(1).getRowId()); - _manager.deleteTreatmentProductMap(_container, _user, filter); - verifyTreatmentProductMapRecords(_treatments.get(1).getRowId(), 0); - } - - private void verifyTreatmentSchedule() - { - verifyTreatmentVisitMapRecords(8); - - _visits.add(StudyManager.getInstance().createVisit(_junitStudy, _user, new VisitImpl(_container, BigDecimal.valueOf(3.0), "Visit 3", Visit.Type.FINAL_VISIT))); - assertEquals("Unexpected number of treatment schedule visits", 2, _manager.getVisitsForTreatmentSchedule(_container).size()); - } - - private void verifyTreatments() - { - List treatments = _manager.getStudyTreatments(_container, _user); - assertEquals("Unexpected study treatment count", 2, treatments.size()); - - for (TreatmentImpl treatment : treatments) - { - verifyTreatmentProductMapRecords(treatment.getRowId(), 4); - - treatment = _manager.getStudyTreatmentByRowId(_container, _user, treatment.getRowId()); - assertEquals("Unexpected number of treatment products", 4, treatment.getProducts().size()); - - for (ProductImpl product : treatment.getProducts()) - { - assertEquals("Unexpected product dose value", "Test Dose", product.getDose()); - assertEquals("Unexpected product route value", _lookups.get("Route"), product.getRoute()); - } - } - - } - - private void verifyStudyProducts() - { - List products = _manager.getStudyProducts(_container, _user); - assertEquals("Unexpected study product count", 4, products.size()); - - for (ProductImpl product : products) - verifyStudyProductAntigens(product.getRowId(), 1); - - assertEquals("Unexpected study product count by role", 2, _manager.getStudyProducts(_container, _user, "Immunogen", null).size()); - assertEquals("Unexpected study product count by role", 2, _manager.getStudyProducts(_container, _user, "Adjuvant", null).size()); - assertEquals("Unexpected study product count by role", 0, _manager.getStudyProducts(_container, _user, "UNK", null).size()); - - for (ProductImpl immunogen : _manager.getStudyProducts(_container, _user, "Immunogen", null)) - assertEquals("Unexpected product lookup value", _lookups.get("ImmunogenType"), immunogen.getType()); - } - - private void populateTreatmentSchedule() throws ValidationException - { - _cohorts.add(CohortManager.getInstance().createCohort(_junitStudy, _user, "Cohort1", true, 10, null)); - _cohorts.add(CohortManager.getInstance().createCohort(_junitStudy, _user, "Cohort2", true, 20, null)); - assertEquals(_cohorts.size(), 2); - - _visits.add(StudyManager.getInstance().createVisit(_junitStudy, _user, new VisitImpl(_container, BigDecimal.valueOf(1.0), "Visit 1", Visit.Type.BASELINE))); - _visits.add(StudyManager.getInstance().createVisit(_junitStudy, _user, new VisitImpl(_container, BigDecimal.valueOf(2.0), "Visit 2", Visit.Type.SCHEDULED_FOLLOWUP))); - assertEquals(_visits.size(), 2); - - for (CohortImpl cohort : _cohorts) - { - for (VisitImpl visit : _visits) - { - for (TreatmentImpl treatment : _treatments) - { - _manager.insertTreatmentVisitMap(_user, _container, cohort.getRowId(), visit.getRowId(), treatment.getRowId()); - } - } - } - - verifyTreatmentVisitMapRecords(_cohorts.size() * _visits.size() * _treatments.size()); - } - - private void populateTreatments() - { - TableInfo treatmentTable = _schema.getTable(StudyDesignQuerySchema.TREATMENT_TABLE_NAME); - if (treatmentTable != null) - { - TableInfo ti = ((FilteredTable)treatmentTable).getRealTable(); - - TreatmentImpl treatment1 = new TreatmentImpl(_container, "Treatment1", "Treatment1 description"); - treatment1 = Table.insert(_user, ti, treatment1); - addProductsForTreatment(treatment1.getRowId()); - _treatments.add(treatment1); - - TreatmentImpl treatment2 = new TreatmentImpl(_container, "Treatment2", "Treatment2 description"); - treatment2 = Table.insert(_user, ti, treatment2); - addProductsForTreatment(treatment2.getRowId()); - _treatments.add(treatment2); - } - - assertEquals(_treatments.size(), 2); - } - - private void addProductsForTreatment(int treatmentId) - { - TableInfo treatmentProductTable = _schema.getTable(StudyDesignQuerySchema.TREATMENT_PRODUCT_MAP_TABLE_NAME); - if (treatmentProductTable != null) - { - TableInfo ti = ((FilteredTable)treatmentProductTable).getRealTable(); - - for (ProductImpl product : _products) - { - TreatmentProductImpl tp = new TreatmentProductImpl(_container, treatmentId, product.getRowId()); - tp.setDose("Test Dose"); - tp.setRoute(_lookups.get("Route")); - Table.insert(_user, ti, tp); - } - } - - verifyTreatmentProductMapRecords(treatmentId, _products.size()); - } - - private void populateStudyProducts() - { - TableInfo productTable = _schema.getTable(StudyDesignQuerySchema.PRODUCT_TABLE_NAME); - if (productTable != null) - { - TableInfo ti = ((FilteredTable)productTable).getRealTable(); - - ProductImpl product1 = new ProductImpl(_container, "Immunogen1", "Immunogen"); - product1.setType(_lookups.get("ImmunogenType")); - _products.add(Table.insert(_user, ti, product1)); - - ProductImpl product2 = new ProductImpl(_container, "Immunogen2", "Immunogen"); - product2.setType(_lookups.get("ImmunogenType")); - _products.add(Table.insert(_user, ti, product2)); - - ProductImpl product3 = new ProductImpl(_container, "Adjuvant1", "Adjuvant"); - _products.add(Table.insert(_user, ti, product3)); - - ProductImpl product4 = new ProductImpl(_container, "Adjuvant2", "Adjuvant"); - _products.add(Table.insert(_user, ti, product4)); - } - - assertEquals(_products.size(), 4); - - for (ProductImpl product : _products) - addAntigenToProduct(product.getRowId()); - } - - private void addAntigenToProduct(int productId) - { - TableInfo productAntigenTable = _schema.getTable(StudyDesignQuerySchema.PRODUCT_ANTIGEN_TABLE_NAME); - if (productAntigenTable != null) - { - TableInfo ti = ((FilteredTable)productAntigenTable).getRealTable(); - - ProductAntigenImpl productAntigen = new ProductAntigenImpl(_container, productId, _lookups.get("Gene"), _lookups.get("SubType")); - Table.insert(_user, ti, productAntigen); - } - - verifyStudyProductAntigens(productId, 1); - } - - private void populateLookupTables() - { - String name, label; - - Map data = new HashMap<>(); - data.put("Container", _container.getId()); - - data.put("Name", name = "Test Immunogen Type"); - data.put("Label", label = "Test Immunogen Type Label"); - Table.insert(_user, StudyDesignSchema.getInstance().getTableInfoStudyDesignImmunogenTypes(), data); - assertEquals("Unexpected study design lookup label", label, _manager.getStudyDesignImmunogenTypeLabelByName(_container, name)); - assertNull("Unexpected study design lookup label", _manager.getStudyDesignImmunogenTypeLabelByName(_container, "UNK")); - _lookups.put("ImmunogenType", name); - - data.put("Name", name = "Test Gene"); - data.put("Label", label = "Test Gene Label"); - Table.insert(_user, StudyDesignSchema.getInstance().getTableInfoStudyDesignGenes(), data); - assertEquals("Unexpected study design lookup label", label, _manager.getStudyDesignGeneLabelByName(_container, name)); - assertNull("Unexpected study design lookup label", _manager.getStudyDesignGeneLabelByName(_container, "UNK")); - _lookups.put("Gene", name); - - data.put("Name", name = "Test SubType"); - data.put("Label", label = "Test SubType Label"); - Table.insert(_user, StudyDesignSchema.getInstance().getTableInfoStudyDesignSubTypes(), data); - assertEquals("Unexpected study design lookup label", label, _manager.getStudyDesignSubTypeLabelByName(_container, name)); - assertNull("Unexpected study design lookup label", _manager.getStudyDesignSubTypeLabelByName(_container, "UNK")); - _lookups.put("SubType", name); - - data.put("Name", name = "Test Route"); - data.put("Label", label = "Test Route Label"); - Table.insert(_user, StudyDesignSchema.getInstance().getTableInfoStudyDesignRoutes(), data); - assertEquals("Unexpected study design lookup label", label, _manager.getStudyDesignRouteLabelByName(_container, name)); - assertNull("Unexpected study design lookup label", _manager.getStudyDesignRouteLabelByName(_container, "UNK")); - _lookups.put("Route", name); - - assertEquals(_lookups.keySet().size(), 4); - } - - private void verifyTreatmentVisitMapRecords(int expectedCount) - { - List rows = _manager.getStudyTreatmentVisitMap(_container, null); - assertEquals("Unexpected number of study.TreatmentVisitMap rows", expectedCount, rows.size()); - } - - private void verifyTreatmentProductMapRecords(int treatmentId, int expectedCount) - { - List rows = _manager.getStudyTreatmentProducts(_container, _user, treatmentId); - assertEquals("Unexpected number of study.TreatmentProductMap rows", expectedCount, rows.size()); - } - - private void verifyStudyProductAntigens(int productId, int expectedCount) - { - List rows = _manager.getStudyProductAntigens(_container, _user, productId); - assertEquals("Unexpected number of study.ProductAntigen rows", expectedCount, rows.size()); - - for (ProductAntigenImpl row : rows) - { - assertEquals("Unexpected antigen lookup value", _lookups.get("Gene"), row.getGene()); - assertEquals("Unexpected antigen lookup value", _lookups.get("SubType"), row.getSubType()); - } - } - - private void createStudy() - { - _context = TestContext.get(); - Container junit = JunitUtil.getTestContainer(); - - String name = GUID.makeHash(); - Container c = ContainerManager.createContainer(junit, name, _context.getUser()); - StudyImpl s = new StudyImpl(c, "Junit Study"); - s.setTimepointType(TimepointType.VISIT); - s.setStartDate(new Date(DateUtil.parseDateTime(c, "2014-01-01"))); - s.setSubjectColumnName("SubjectID"); - s.setSubjectNounPlural("Subjects"); - s.setSubjectNounSingular("Subject"); - s.setSecurityType(SecurityType.BASIC_WRITE); - _junitStudy = StudyManager.getInstance().createStudy(_context.getUser(), s); - } - - private void tearDown() - { - if (null != _junitStudy) - { - assertTrue(ContainerManager.delete(_junitStudy.getContainer(), _context.getUser())); - } - } - } -} +/* + * Copyright (c) 2014-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.studydesign.model; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.junit.Assert; +import org.junit.Test; +import org.labkey.api.data.ColumnInfo; +import org.labkey.api.data.CompareType; +import org.labkey.api.data.Container; +import org.labkey.api.data.ContainerManager; +import org.labkey.api.data.DbScope; +import org.labkey.api.data.SimpleFilter; +import org.labkey.api.data.Sort; +import org.labkey.api.data.Table; +import org.labkey.api.data.TableInfo; +import org.labkey.api.data.TableSelector; +import org.labkey.api.module.Module; +import org.labkey.api.module.ModuleLoader; +import org.labkey.api.query.BatchValidationException; +import org.labkey.api.query.FieldKey; +import org.labkey.api.query.FilteredTable; +import org.labkey.api.query.QueryService; +import org.labkey.api.query.QueryUpdateService; +import org.labkey.api.query.UserSchema; +import org.labkey.api.query.ValidationException; +import org.labkey.api.security.User; +import org.labkey.api.study.AssaySpecimenConfig; +import org.labkey.api.study.Cohort; +import org.labkey.api.study.Study; +import org.labkey.api.study.StudyService; +import org.labkey.api.study.TimepointType; +import org.labkey.api.study.Visit; +import org.labkey.api.study.model.CohortService; +import org.labkey.api.study.model.VisitService; +import org.labkey.api.studydesign.StudyDesignService; +import org.labkey.api.studydesign.query.StudyDesignQuerySchema; +import org.labkey.api.studydesign.query.StudyDesignSchema; +import org.labkey.api.test.TestWhen; +import org.labkey.api.util.GUID; +import org.labkey.api.util.JunitUtil; +import org.labkey.api.util.TestContext; + +import java.math.BigDecimal; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +/** + * Created by cnathe on 1/24/14. + */ +public class TreatmentManager +{ + private static final TreatmentManager _instance = new TreatmentManager(); + + public static final String ASSAY_SPECIMEN_TABLE_NAME = "AssaySpecimen"; + public static final String ASSAY_SPECIMEN_VISIT_TABLE_NAME = "AssaySpecimenVisit"; + + private TreatmentManager() + { + } + + public static TreatmentManager getInstance() + { + return _instance; + } + + public List getStudyProducts(Container container, User user) + { + return getStudyProducts(container, user, null, null); + } + + public List getStudyProducts(Container container, User user, @Nullable String role, @Nullable Integer rowId) + { + //Using a user schema so containerFilter will be created for us later (so don't need SimpleFilter.createContainerFilter) + SimpleFilter filter = new SimpleFilter(); + if (role != null) + filter.addCondition(FieldKey.fromParts("Role"), role); + if (rowId != null) + filter.addCondition(FieldKey.fromParts("RowId"), rowId); + + TableInfo ti = QueryService.get().getUserSchema(user, container, StudyDesignQuerySchema.STUDY_SCHEMA_NAME).getTable(StudyDesignQuerySchema.PRODUCT_TABLE_NAME); + return new TableSelector(ti, filter, new Sort("RowId")).getArrayList(ProductImpl.class); + } + + public List getFilteredStudyProducts(Container container, User user, List filterRowIds) + { + TableInfo ti = QueryService.get().getUserSchema(user, container, StudyDesignQuerySchema.STUDY_SCHEMA_NAME).getTable(StudyDesignQuerySchema.PRODUCT_TABLE_NAME); + + //Using a user schema so containerFilter will be created for us later (so don't need SimpleFilter.createContainerFilter) + SimpleFilter filter = new SimpleFilter(); + if (filterRowIds != null && !filterRowIds.isEmpty()) + filter.addCondition(FieldKey.fromParts("RowId"), filterRowIds, CompareType.NOT_IN); + + return new TableSelector(ti, filter, new Sort("RowId")).getArrayList(ProductImpl.class); + } + + public List getStudyProductAntigens(Container container, User user, int productId) + { + SimpleFilter filter = new SimpleFilter(); + filter.addCondition(FieldKey.fromParts("ProductId"), productId); + + TableInfo ti = QueryService.get().getUserSchema(user, container, StudyDesignQuerySchema.STUDY_SCHEMA_NAME).getTable(StudyDesignQuerySchema.PRODUCT_ANTIGEN_TABLE_NAME); + return new TableSelector(ti, filter, new Sort("RowId")).getArrayList(ProductAntigenImpl.class); + } + + public List getFilteredStudyProductAntigens(Container container, User user, @NotNull Integer productId, List filterRowIds) + { + TableInfo ti = QueryService.get().getUserSchema(user, container, StudyDesignQuerySchema.STUDY_SCHEMA_NAME).getTable(StudyDesignQuerySchema.PRODUCT_ANTIGEN_TABLE_NAME); + + //Using a user schema so containerFilter will be created for us later (so don't need SimpleFilter.createContainerFilter) + SimpleFilter filter = new SimpleFilter(); + filter.addCondition(FieldKey.fromParts("ProductId"), productId); + if (filterRowIds != null && !filterRowIds.isEmpty()) + filter.addCondition(FieldKey.fromParts("RowId"), filterRowIds, CompareType.NOT_IN); + + return new TableSelector(ti, filter, new Sort("RowId")).getArrayList(ProductAntigenImpl.class); + } + + public Integer saveTreatment(Container container, User user, TreatmentImpl treatment) throws Exception + { + TableInfo treatmentTable = QueryService.get().getUserSchema(user, container, StudyDesignQuerySchema.STUDY_SCHEMA_NAME).getTable(StudyDesignQuerySchema.TREATMENT_TABLE_NAME); + return saveStudyDesignRow(container, user, treatmentTable, treatment.serialize(), treatment.isNew() ? null : treatment.getRowId(), "RowId"); + } + + public List getStudyTreatments(Container container, User user) + { + SimpleFilter filter = new SimpleFilter(); + TableInfo ti = QueryService.get().getUserSchema(user, container, StudyDesignQuerySchema.STUDY_SCHEMA_NAME).getTable(StudyDesignQuerySchema.TREATMENT_TABLE_NAME); + return new TableSelector(ti, filter, new Sort("RowId")).getArrayList(TreatmentImpl.class); + } + + public TreatmentImpl getStudyTreatmentByRowId(Container container, User user, int rowId) + { + SimpleFilter filter = new SimpleFilter(FieldKey.fromParts("RowId"), rowId); + TableInfo ti = QueryService.get().getUserSchema(user, container, StudyDesignQuerySchema.STUDY_SCHEMA_NAME).getTable(StudyDesignQuerySchema.TREATMENT_TABLE_NAME); + TreatmentImpl treatment = new TableSelector(ti, filter, null).getObject(TreatmentImpl.class); + + // attach the associated study products to the treatment object + if (treatment != null) + { + List treatmentProducts = getStudyTreatmentProducts(container, user, treatment.getRowId(), treatment.getProductSort()); + for (TreatmentProductImpl treatmentProduct : treatmentProducts) + { + List products = getStudyProducts(container, user, null, treatmentProduct.getProductId()); + for (ProductImpl product : products) + { + product.setDose(treatmentProduct.getDose()); + product.setRoute(treatmentProduct.getRoute()); + treatment.addProduct(product); + } + } + } + + return treatment; + } + + public List getFilteredTreatments(Container container, User user, List definedTreatmentIds, Set usedTreatmentIds) + { + List filterRowIds = new ArrayList<>(); + filterRowIds.addAll(definedTreatmentIds); + filterRowIds.addAll(usedTreatmentIds); + TableInfo ti = QueryService.get().getUserSchema(user, container, StudyDesignQuerySchema.STUDY_SCHEMA_NAME).getTable(StudyDesignQuerySchema.TREATMENT_TABLE_NAME); + + //Using a user schema so containerFilter will be created for us later (so don't need SimpleFilter.createContainerFilter) + SimpleFilter filter = new SimpleFilter().addCondition(FieldKey.fromParts("RowId"), filterRowIds, CompareType.NOT_IN); + + return new TableSelector(ti, filter, new Sort("RowId")).getArrayList(TreatmentImpl.class); + } + + public Integer saveTreatmentProductMapping(Container container, User user, TreatmentProductImpl treatmentProduct) throws Exception + { + TableInfo treatmentProductTable = QueryService.get().getUserSchema(user, container, StudyDesignQuerySchema.STUDY_SCHEMA_NAME).getTable(StudyDesignQuerySchema.TREATMENT_PRODUCT_MAP_TABLE_NAME); + return saveStudyDesignRow(container, user, treatmentProductTable, treatmentProduct.serialize(), treatmentProduct.isNew() ? null : treatmentProduct.getRowId(), "RowId"); + } + + public List getStudyTreatmentProducts(Container container, User user, int treatmentId) + { + return getStudyTreatmentProducts(container, user, treatmentId, new Sort("RowId")); + } + + public List getStudyTreatmentProducts(Container container, User user, int treatmentId, Sort sort) + { + SimpleFilter filter = new SimpleFilter(FieldKey.fromParts("TreatmentId"), treatmentId); + + TableInfo ti = QueryService.get().getUserSchema(user, container, StudyDesignQuerySchema.STUDY_SCHEMA_NAME).getTable(StudyDesignQuerySchema.TREATMENT_PRODUCT_MAP_TABLE_NAME); + return new TableSelector(ti, filter, sort).getArrayList(TreatmentProductImpl.class); + } + + public List getFilteredTreatmentProductMappings(Container container, User user, @NotNull Integer treatmentId, List filterRowIds) + { + TableInfo ti = QueryService.get().getUserSchema(user, container, StudyDesignQuerySchema.STUDY_SCHEMA_NAME).getTable(StudyDesignQuerySchema.TREATMENT_PRODUCT_MAP_TABLE_NAME); + + //Using a user schema so containerFilter will be created for us later (so don't need SimpleFilter.createContainerFilter) + SimpleFilter filter = new SimpleFilter(); + filter.addCondition(FieldKey.fromParts("TreatmentId"), treatmentId); + if (filterRowIds != null && !filterRowIds.isEmpty()) + filter.addCondition(FieldKey.fromParts("RowId"), filterRowIds, CompareType.NOT_IN); + + return new TableSelector(ti, filter, new Sort("RowId")).getArrayList(TreatmentProductImpl.class); + } + + public List getStudyTreatmentVisitMap(Container container, @Nullable Integer cohortId) + { + SimpleFilter filter = SimpleFilter.createContainerFilter(container); + if (cohortId != null) + filter.addCondition(FieldKey.fromParts("CohortId"), cohortId); + + TableInfo ti = StudyDesignSchema.getInstance().getTableInfoTreatmentVisitMap(); + return new TableSelector(ti, filter, new Sort("CohortId")).getArrayList(TreatmentVisitMapImpl.class); + } + + public List getVisitsForTreatmentSchedule(Container container) + { + SimpleFilter filter = SimpleFilter.createContainerFilter(container); + List visitRowIds = new TableSelector(StudyDesignSchema.getInstance().getTableInfoTreatmentVisitMap(), + Collections.singleton("VisitId"), filter, new Sort("VisitId")).getArrayList(Integer.class); + + Study study = StudyService.get().getStudy(container); + List visits = new ArrayList<>(); + if (study != null) + { + for (Visit visit : study.getVisits(Visit.Order.DISPLAY)) + { + if (visitRowIds.contains(visit.getId())) + visits.add(visit); + } + } + return visits; + } + + public TreatmentVisitMapImpl insertTreatmentVisitMap(User user, Container container, int cohortId, int visitId, int treatmentId) + { + TreatmentVisitMapImpl newMapping = new TreatmentVisitMapImpl(); + newMapping.setContainer(container); + newMapping.setCohortId(cohortId); + newMapping.setVisitId(visitId); + newMapping.setTreatmentId(treatmentId); + + return Table.insert(user, StudyDesignSchema.getInstance().getTableInfoTreatmentVisitMap(), newMapping); + } + + public void deleteTreatmentVisitMapForCohort(Container container, int rowId) + { + SimpleFilter filter = SimpleFilter.createContainerFilter(container); + filter.addCondition(FieldKey.fromParts("CohortId"), rowId); + Table.delete(StudyDesignSchema.getInstance().getTableInfoTreatmentVisitMap(), filter); + } + + public void deleteTreatmentVisitMapForVisit(Container container, int rowId) + { + SimpleFilter filter = SimpleFilter.createContainerFilter(container); + filter.addCondition(FieldKey.fromParts("VisitId"), rowId); + Table.delete(StudyDesignSchema.getInstance().getTableInfoTreatmentVisitMap(), filter); + } + + public void deleteTreatment(Container container, User user, int rowId) + { + StudyDesignSchema schema = StudyDesignSchema.getInstance(); + + try (DbScope.Transaction transaction = schema.getSchema().getScope().ensureTransaction()) + { + // delete the usages of this treatment in the TreatmentVisitMap + SimpleFilter filter = SimpleFilter.createContainerFilter(container); + filter.addCondition(FieldKey.fromParts("TreatmentId"), rowId); + Table.delete(StudyDesignSchema.getInstance().getTableInfoTreatmentVisitMap(), filter); + + // delete the associated treatment study product mappings (provision table) + filter = SimpleFilter.createContainerFilter(container); + filter.addCondition(FieldKey.fromParts("TreatmentId"), rowId); + deleteTreatmentProductMap(container, user, filter); + + // finally delete the record from the Treatment (provision table) + TableInfo treatmentTable = QueryService.get().getUserSchema(user, container, StudyDesignQuerySchema.STUDY_SCHEMA_NAME).getTable(StudyDesignQuerySchema.TREATMENT_TABLE_NAME); + if (treatmentTable != null) + { + QueryUpdateService qus = treatmentTable.getUpdateService(); + List> keys = new ArrayList<>(); + ColumnInfo treatmentPk = treatmentTable.getColumn(FieldKey.fromParts("RowId")); + keys.add(Collections.singletonMap(treatmentPk.getName(), rowId)); + qus.deleteRows(user, container, keys, null, null); + } + + transaction.commit(); + } + catch (Exception e) + { + throw new RuntimeException(e); + } + } + + public void deleteStudyProduct(Container container, User user, int rowId) + { + StudyDesignSchema schema = StudyDesignSchema.getInstance(); + + try (DbScope.Transaction transaction = schema.getSchema().getScope().ensureTransaction()) + { + // delete the usages of this study product in the ProductAntigen table (provision table) + deleteProductAntigens(container, user, rowId); + + // delete the associated doses and routes for this product + Table.delete(StudyDesignSchema.getInstance().getTableInfoDoseAndRoute(), new SimpleFilter(FieldKey.fromParts("ProductId"), rowId)); + + // delete the associated treatment study product mappings (provision table) + SimpleFilter filter = SimpleFilter.createContainerFilter(container); + filter.addCondition(FieldKey.fromParts("ProductId"), rowId); + deleteTreatmentProductMap(container, user, filter); + + // finally delete the record from the Products (provision table) + TableInfo productTable = QueryService.get().getUserSchema(user, container, StudyDesignQuerySchema.STUDY_SCHEMA_NAME).getTable(StudyDesignQuerySchema.PRODUCT_TABLE_NAME); + if (productTable != null) + { + QueryUpdateService qus = productTable.getUpdateService(); + List> keys = new ArrayList<>(); + ColumnInfo productPk = productTable.getColumn(FieldKey.fromParts("RowId")); + keys.add(Collections.singletonMap(productPk.getName(), rowId)); + qus.deleteRows(user, container, keys, null, null); + } + else + throw new IllegalStateException("Could not find table: " + StudyDesignQuerySchema.PRODUCT_TABLE_NAME); + + transaction.commit(); + } + catch (Exception e) + { + throw new RuntimeException(e); + } + } + + public Integer saveStudyProduct(Container container, User user, ProductImpl product) throws Exception + { + TableInfo productTable = QueryService.get().getUserSchema(user, container, StudyDesignQuerySchema.STUDY_SCHEMA_NAME).getTable(StudyDesignQuerySchema.PRODUCT_TABLE_NAME); + return saveStudyDesignRow(container, user, productTable, product.serialize(), product.isNew() ? null : product.getRowId(), "RowId"); + } + + public Integer saveStudyProductAntigen(Container container, User user, ProductAntigenImpl antigen) throws Exception + { + TableInfo productAntigenTable = QueryService.get().getUserSchema(user, container, StudyDesignQuerySchema.STUDY_SCHEMA_NAME).getTable(StudyDesignQuerySchema.PRODUCT_ANTIGEN_TABLE_NAME); + return saveStudyDesignRow(container, user, productAntigenTable, antigen.serialize(), antigen.isNew() ? null : antigen.getRowId(), "RowId"); + } + + public DoseAndRoute saveStudyProductDoseAndRoute(Container container, User user, DoseAndRoute doseAndRoute) + { + if (doseAndRoute.isNew()) + return Table.insert(user, StudyDesignSchema.getInstance().getTableInfoDoseAndRoute(), doseAndRoute); + else + return Table.update(user, StudyDesignSchema.getInstance().getTableInfoDoseAndRoute(), doseAndRoute, doseAndRoute.getRowId()); + } + + public Collection getStudyProductsDoseAndRoute(Container container, User user, int productId) + { + SimpleFilter filter = new SimpleFilter(FieldKey.fromParts("ProductId"), productId); + return new TableSelector(StudyDesignSchema.getInstance().getTableInfoDoseAndRoute(), filter, null).getCollection(DoseAndRoute.class); + } + + @Nullable + public DoseAndRoute getDoseAndRoute(Container container, String dose, String route, int productId) + { + SimpleFilter filter = new SimpleFilter(FieldKey.fromParts("ProductId"), productId); + if (dose != null) + filter.addCondition(FieldKey.fromParts("Dose"), dose); + else + filter.addCondition(FieldKey.fromParts("Dose"), null, CompareType.ISBLANK); + if (route != null) + filter.addCondition(FieldKey.fromParts("Route"), route); + else + filter.addCondition(FieldKey.fromParts("Route"), null, CompareType.ISBLANK); + Collection doseAndRoutes = new TableSelector(StudyDesignSchema.getInstance().getTableInfoDoseAndRoute(), filter, null).getCollection(DoseAndRoute.class); + + if (!doseAndRoutes.isEmpty()) + { + return doseAndRoutes.iterator().next(); + } + return null; + } + + public Integer saveAssaySpecimen(Container container, User user, AssaySpecimenConfigImpl assaySpecimen) throws Exception + { + TableInfo assaySpecimenTable = QueryService.get().getUserSchema(user, container, StudyDesignQuerySchema.STUDY_SCHEMA_NAME).getTable(ASSAY_SPECIMEN_TABLE_NAME); + Integer ret = saveStudyDesignRow(container, user, assaySpecimenTable, assaySpecimen.serialize(), assaySpecimen.isNew() ? null : assaySpecimen.getRowId(), "RowId", true); + return ret; + } + + public Integer saveAssaySpecimenVisit(Container container, User user, AssaySpecimenVisitImpl assaySpecimenVisit) throws Exception + { + TableInfo assaySpecimenVIsitTable = QueryService.get().getUserSchema(user, container, StudyDesignQuerySchema.STUDY_SCHEMA_NAME).getTable(ASSAY_SPECIMEN_VISIT_TABLE_NAME); + return saveStudyDesignRow(container, user, assaySpecimenVIsitTable, assaySpecimenVisit.serialize(), assaySpecimenVisit.isNew() ? null : assaySpecimenVisit.getRowId(), "RowId", true); + } + + public List getFilteredAssaySpecimens(Container container, List filterRowIds) + { + SimpleFilter filter = SimpleFilter.createContainerFilter(container); + if (filterRowIds != null && !filterRowIds.isEmpty()) + filter.addCondition(FieldKey.fromParts("RowId"), filterRowIds, CompareType.NOT_IN); + + return new TableSelector(StudyDesignSchema.getInstance().getTableInfoAssaySpecimen(), filter, new Sort("RowId")).getArrayList(AssaySpecimenConfigImpl.class); + } + + public List getFilteredAssaySpecimenVisits(Container container, int assaySpecimenId, List filterRowIds) + { + SimpleFilter filter = SimpleFilter.createContainerFilter(container); + filter.addCondition(FieldKey.fromParts("AssaySpecimenId"), assaySpecimenId); + if (filterRowIds != null && !filterRowIds.isEmpty()) + filter.addCondition(FieldKey.fromParts("RowId"), filterRowIds, CompareType.NOT_IN); + + return new TableSelector(StudyDesignSchema.getInstance().getTableInfoAssaySpecimenVisit(), filter, new Sort("RowId")).getArrayList(AssaySpecimenVisitImpl.class); + } + + public Integer saveStudyDesignRow(Container container, User user, TableInfo tableInfo, Map row, Integer key, String pkColName) throws Exception + { + return saveStudyDesignRow(container, user, tableInfo, row, key, pkColName, false); + } + + public Integer saveStudyDesignRow(Container container, User user, TableInfo tableInfo, Map row, Integer key, String pkColName, boolean includeContainerKey) throws Exception + { + QueryUpdateService qus = tableInfo != null ? tableInfo.getUpdateService() : null; + if (qus != null) + { + BatchValidationException errors = new BatchValidationException(); + List> updatedRows; + + if (key == null) + { + updatedRows = qus.insertRows(user, container, Collections.singletonList(row), errors, null, null); + } + else + { + Map oldKey = new HashMap<>(); + oldKey.put(pkColName, key); + if (includeContainerKey) + oldKey.put("Container", container.getId()); + + updatedRows = qus.updateRows(user, container, Collections.singletonList(row), Collections.singletonList(oldKey), errors, null, null); + } + + if (errors.hasErrors()) + throw errors.getLastRowError(); + + if (updatedRows.size() == 1) + return (Integer) updatedRows.get(0).get(pkColName); + } + + return null; + } + + public void deleteStudyProductAntigen(Container container, User user, int rowId) throws Exception + { + TableInfo productAntigenTable = QueryService.get().getUserSchema(user, container, StudyDesignQuerySchema.STUDY_SCHEMA_NAME).getTable(StudyDesignQuerySchema.PRODUCT_ANTIGEN_TABLE_NAME); + if (productAntigenTable != null) + { + QueryUpdateService qus = productAntigenTable.getUpdateService(); + if (qus != null) + { + List> keys = Collections.singletonList(Collections.singletonMap("RowId", rowId)); + qus.deleteRows(user, container, keys, null, null); + } + else + throw new IllegalStateException("Could not find query update service for table: " + StudyDesignQuerySchema.PRODUCT_ANTIGEN_TABLE_NAME); + } + else + throw new IllegalStateException("Could not find table: " + StudyDesignQuerySchema.PRODUCT_ANTIGEN_TABLE_NAME); + } + + public void deleteProductAntigens(Container container, User user, int productId) throws Exception + { + TableInfo productAntigenTable = QueryService.get().getUserSchema(user, container, StudyDesignQuerySchema.STUDY_SCHEMA_NAME).getTable(StudyDesignQuerySchema.PRODUCT_ANTIGEN_TABLE_NAME); + if (productAntigenTable != null) + { + SimpleFilter filter = SimpleFilter.createContainerFilter(container); + filter.addCondition(FieldKey.fromParts("ProductId"), productId); + TableSelector selector = new TableSelector(productAntigenTable, Collections.singleton("RowId"), filter, null); + Integer[] productAntigenIds = selector.getArray(Integer.class); + + QueryUpdateService qus = productAntigenTable.getUpdateService(); + if (qus != null) + { + List> keys = new ArrayList<>(); + ColumnInfo productAntigenPk = productAntigenTable.getColumn(FieldKey.fromParts("RowId")); + for (Integer productAntigenId : productAntigenIds) + { + keys.add(Collections.singletonMap(productAntigenPk.getName(), productAntigenId)); + } + + qus.deleteRows(user, container, keys, null, null); + } + else + throw new IllegalStateException("Could not find query update service for table: " + StudyDesignQuerySchema.PRODUCT_ANTIGEN_TABLE_NAME); + } + else + throw new IllegalStateException("Could not find table: " + StudyDesignQuerySchema.PRODUCT_ANTIGEN_TABLE_NAME); + } + + public void deleteTreatmentProductMap(Container container, User user, SimpleFilter filter) throws Exception + { + TableInfo productMapTable = QueryService.get().getUserSchema(user, container, StudyDesignQuerySchema.STUDY_SCHEMA_NAME).getTable(StudyDesignQuerySchema.TREATMENT_PRODUCT_MAP_TABLE_NAME); + if (productMapTable != null) + { + TableSelector selector = new TableSelector(productMapTable, Collections.singleton("RowId"), filter, null); + deleteTreatmentProductMap(container, user, selector.getArrayList(Integer.class)); + } + else + throw new IllegalStateException("Could not find table: " + StudyDesignQuerySchema.TREATMENT_PRODUCT_MAP_TABLE_NAME); + } + + public void deleteTreatmentProductMap(Container container, User user, List rowIds) throws Exception + { + TableInfo productMapTable = QueryService.get().getUserSchema(user, container, StudyDesignQuerySchema.STUDY_SCHEMA_NAME).getTable(StudyDesignQuerySchema.TREATMENT_PRODUCT_MAP_TABLE_NAME); + if (productMapTable != null) + { + QueryUpdateService qus = productMapTable.getUpdateService(); + if (qus != null) + { + List> keys = new ArrayList<>(); + ColumnInfo productMapPk = productMapTable.getColumn(FieldKey.fromParts("RowId")); + for (Integer rowId : rowIds) + keys.add(Collections.singletonMap(productMapPk.getName(), rowId)); + + qus.deleteRows(user, container, keys, null, null); + } + else + throw new IllegalStateException("Could not find query update service for table: " + StudyDesignQuerySchema.TREATMENT_PRODUCT_MAP_TABLE_NAME); + } + else + throw new IllegalStateException("Could not find table: " + StudyDesignQuerySchema.TREATMENT_PRODUCT_MAP_TABLE_NAME); + } + + public void deleteAssaySpecimen(Container container, User user, int rowId) + { + // first delete any usages of the AssaySpecimenId in the AssaySpecimenVisit table + SimpleFilter filter = SimpleFilter.createContainerFilter(container); + filter.addCondition(FieldKey.fromParts("AssaySpecimenId"), rowId); + Table.delete(StudyDesignSchema.getInstance().getTableInfoAssaySpecimenVisit(), filter); + + // delete the AssaySpecimen record by RowId + filter = SimpleFilter.createContainerFilter(container); + filter.addCondition(FieldKey.fromParts("RowId"), rowId); + Table.delete(StudyDesignSchema.getInstance().getTableInfoAssaySpecimen(), filter); + } + + public void deleteAssaySpecimenVisit(Container container, User user, int rowId) + { + SimpleFilter filter = SimpleFilter.createContainerFilter(container); + filter.addCondition(FieldKey.fromParts("RowId"), rowId); + Table.delete(StudyDesignSchema.getInstance().getTableInfoAssaySpecimenVisit(), filter); + } + + public String getStudyDesignRouteLabelByName(Container container, String name) + { + return getStudyDesignLabelByName(container, StudyDesignSchema.getInstance().getTableInfoStudyDesignRoutes(), name); + } + + public String getStudyDesignImmunogenTypeLabelByName(Container container, String name) + { + return getStudyDesignLabelByName(container, StudyDesignSchema.getInstance().getTableInfoStudyDesignImmunogenTypes(), name); + } + + public String getStudyDesignGeneLabelByName(Container container, String name) + { + return getStudyDesignLabelByName(container, StudyDesignSchema.getInstance().getTableInfoStudyDesignGenes(), name); + } + + public String getStudyDesignSubTypeLabelByName(Container container, String name) + { + return getStudyDesignLabelByName(container, StudyDesignSchema.getInstance().getTableInfoStudyDesignSubTypes(), name); + } + + public String getStudyDesignLabLabelByName(Container container, String name) + { + return getStudyDesignLabelByName(container, StudyDesignSchema.getInstance().getTableInfoStudyDesignLabs(), name); + } + + public String getStudyDesignLabelByName(Container container, TableInfo tableInfo, String name) + { + // first look in the current container for the StudyDesign record, then look for it at the project level + SimpleFilter filter = SimpleFilter.createContainerFilter(container); + filter.addCondition(FieldKey.fromParts("Name"), name); + String label = new TableSelector(tableInfo, Collections.singleton("Label"), filter, null).getObject(String.class); + if (label == null && !container.isProject()) + { + filter = SimpleFilter.createContainerFilter(container.getProject()); + filter.addCondition(FieldKey.fromParts("Name"), name); + label = new TableSelector(tableInfo, Collections.singleton("Label"), filter, null).getObject(String.class); + } + + return label; + } + + public void updateTreatmentProducts(int treatmentId, List treatmentProducts, Container container, User user) throws Exception + { + // insert new study treatment product mappings and update any existing ones + List treatmentProductRowIds = new ArrayList<>(); + for (TreatmentProductImpl treatmentProduct : treatmentProducts) + { + // make sure the treatmentId is set based on the treatment rowId + treatmentProduct.setTreatmentId(treatmentId); + + Integer updatedRowId = TreatmentManager.getInstance().saveTreatmentProductMapping(container, user, treatmentProduct); + if (updatedRowId != null) + treatmentProductRowIds.add(updatedRowId); + } + + // delete any other treatment product mappings, not included in the insert/update list, for the given treatmentId + for (TreatmentProductImpl treatmentProduct : TreatmentManager.getInstance().getFilteredTreatmentProductMappings(container, user, treatmentId, treatmentProductRowIds)) + TreatmentManager.getInstance().deleteTreatmentProductMap(container, user, Collections.singletonList(treatmentProduct.getRowId())); + } + + public List getAssaySpecimenVisitIds(Container container, AssaySpecimenConfig assaySpecimenConfig) + { + SimpleFilter filter = SimpleFilter.createContainerFilter(container); + filter.addCondition(FieldKey.fromParts("AssaySpecimenId"), assaySpecimenConfig.getRowId()); + + return new TableSelector(StudyDesignSchema.getInstance().getTableInfoAssaySpecimenVisit(), + Collections.singleton("VisitId"), filter, new Sort("VisitId")).getArrayList(Integer.class); + } + + public List getVisitsForAssaySchedule(Container container) + { + SimpleFilter filter = SimpleFilter.createContainerFilter(container); + List visitRowIds = new TableSelector(StudyDesignSchema.getInstance().getTableInfoAssaySpecimenVisit(), + Collections.singleton("VisitId"), filter, new Sort("VisitId")).getArrayList(Integer.class); + + return getSortedVisitsByRowIds(container, visitRowIds); + } + + public List getSortedVisitsByRowIds(Container container, List visitRowIds) + { + List visits = new ArrayList<>(); + Study study = StudyService.get().getStudy(container); + if (study != null) + { + for (Visit v : study.getVisits(Visit.Order.DISPLAY)) + { + if (visitRowIds.contains(v.getId())) + visits.add(v); + } + } + return visits; + } + + public AssaySpecimenConfigImpl addAssaySpecimenConfig(User user, AssaySpecimenConfigImpl config) + { + return Table.insert(user, StudyDesignSchema.getInstance().getTableInfoAssaySpecimen(), config); + } + + /**** + * + * + * + * TESTING + * + * + */ + + @TestWhen(TestWhen.When.BVT) + public static class TreatmentDataTestCase extends Assert + { + TestContext _context = null; + User _user = null; + Container _container = null; + Study _junitStudy = null; + TreatmentManager _manager = TreatmentManager.getInstance(); + UserSchema _schema = null; + + Map _lookups = new HashMap<>(); + List _products = new ArrayList<>(); + List _treatments = new ArrayList<>(); + List _cohorts = new ArrayList<>(); + List _visits = new ArrayList<>(); + + @Test + public void test() throws Throwable + { + try + { + createStudy(); + _user = _context.getUser(); + _container = _junitStudy.getContainer(); + _schema = QueryService.get().getUserSchema(_user, _container, StudyDesignQuerySchema.STUDY_SCHEMA_NAME); + + populateLookupTables(); + populateStudyProducts(); + populateTreatments(); + populateTreatmentSchedule(); + + verifyStudyProducts(); + verifyTreatments(); + verifyTreatmentSchedule(); + verifyCleanUpTreatmentData(); + } + finally + { + tearDown(); + } + } + + private void verifyCleanUpTreatmentData() throws Exception + { + // remove cohort and verify delete of TreatmentVisitMap + CohortService.get().deleteCohort(_cohorts.get(0)); + verifyTreatmentVisitMapRecords(4); + + // remove visit and verify delete of TreatmentVisitMap + VisitService.get().deleteVisit(_junitStudy, _user, _visits.get(0)); + verifyTreatmentVisitMapRecords(2); + + // we should still have all of our treatments and study products + verifyTreatments(); + verifyStudyProducts(); + + // remove treatment visit map records via visit + _manager.deleteTreatmentVisitMapForVisit(_container, _visits.get(1).getId()); + verifyTreatmentVisitMapRecords(0); + + // remove treatment and verify delete of TreatmentProductMap + _manager.deleteTreatment(_container, _user, _treatments.get(0).getRowId()); + verifyTreatmentProductMapRecords(_treatments.get(0).getRowId(), 0); + verifyTreatmentProductMapRecords(_treatments.get(1).getRowId(), 4); + + // remove product and verify delete of TreatmentProductMap and ProductAntigen + _manager.deleteStudyProduct(_container, _user, _products.get(0).getRowId()); + verifyTreatmentProductMapRecords(_treatments.get(1).getRowId(), 3); + verifyStudyProductAntigens(_products.get(0).getRowId(), 0); + verifyStudyProductAntigens(_products.get(1).getRowId(), 1); + + // directly delete product antigen + _manager.deleteProductAntigens(_container, _user, _products.get(1).getRowId()); + verifyStudyProductAntigens(_products.get(1).getRowId(), 0); + + // delete treatment product map by productId and then treatmentId + SimpleFilter filter = SimpleFilter.createContainerFilter(_container); + filter.addCondition(FieldKey.fromParts("ProductId"), _products.get(1).getRowId()); + _manager.deleteTreatmentProductMap(_container, _user, filter); + verifyTreatmentProductMapRecords(_treatments.get(1).getRowId(), 2); + filter = SimpleFilter.createContainerFilter(_container); + filter.addCondition(FieldKey.fromParts("TreatmentId"), _treatments.get(1).getRowId()); + _manager.deleteTreatmentProductMap(_container, _user, filter); + verifyTreatmentProductMapRecords(_treatments.get(1).getRowId(), 0); + } + + private void verifyTreatmentSchedule() + { + verifyTreatmentVisitMapRecords(8); + + _visits.add(VisitService.get().createVisit(_junitStudy, _user, BigDecimal.valueOf(3.0), "Visit 3", Visit.Type.FINAL_VISIT)); + assertEquals("Unexpected number of treatment schedule visits", 2, _manager.getVisitsForTreatmentSchedule(_container).size()); + } + + private void verifyTreatments() + { + List treatments = _manager.getStudyTreatments(_container, _user); + assertEquals("Unexpected study treatment count", 2, treatments.size()); + + for (TreatmentImpl treatment : treatments) + { + verifyTreatmentProductMapRecords(treatment.getRowId(), 4); + + treatment = _manager.getStudyTreatmentByRowId(_container, _user, treatment.getRowId()); + assertEquals("Unexpected number of treatment products", 4, treatment.getProducts().size()); + + for (ProductImpl product : treatment.getProducts()) + { + assertEquals("Unexpected product dose value", "Test Dose", product.getDose()); + assertEquals("Unexpected product route value", _lookups.get("Route"), product.getRoute()); + } + } + + } + + private void verifyStudyProducts() + { + List products = _manager.getStudyProducts(_container, _user); + assertEquals("Unexpected study product count", 4, products.size()); + + for (ProductImpl product : products) + verifyStudyProductAntigens(product.getRowId(), 1); + + assertEquals("Unexpected study product count by role", 2, _manager.getStudyProducts(_container, _user, "Immunogen", null).size()); + assertEquals("Unexpected study product count by role", 2, _manager.getStudyProducts(_container, _user, "Adjuvant", null).size()); + assertEquals("Unexpected study product count by role", 0, _manager.getStudyProducts(_container, _user, "UNK", null).size()); + + for (ProductImpl immunogen : _manager.getStudyProducts(_container, _user, "Immunogen", null)) + assertEquals("Unexpected product lookup value", _lookups.get("ImmunogenType"), immunogen.getType()); + } + + private void populateTreatmentSchedule() throws ValidationException + { + _cohorts.add(CohortService.get().createCohort(_junitStudy, _user, "Cohort1", true, 10, null)); + _cohorts.add(CohortService.get().createCohort(_junitStudy, _user, "Cohort2", true, 20, null)); + assertEquals(_cohorts.size(), 2); + + _visits.add(VisitService.get().createVisit(_junitStudy, _user, BigDecimal.valueOf(1.0), "Visit 1", Visit.Type.BASELINE)); + _visits.add(VisitService.get().createVisit(_junitStudy, _user, BigDecimal.valueOf(2.0), "Visit 2", Visit.Type.SCHEDULED_FOLLOWUP)); + assertEquals(_visits.size(), 2); + + for (Cohort cohort : _cohorts) + { + for (Visit visit : _visits) + { + for (TreatmentImpl treatment : _treatments) + { + _manager.insertTreatmentVisitMap(_user, _container, cohort.getRowId(), visit.getId(), treatment.getRowId()); + } + } + } + + verifyTreatmentVisitMapRecords(_cohorts.size() * _visits.size() * _treatments.size()); + } + + private void populateTreatments() + { + TableInfo treatmentTable = _schema.getTable(StudyDesignQuerySchema.TREATMENT_TABLE_NAME); + if (treatmentTable != null) + { + TableInfo ti = ((FilteredTable)treatmentTable).getRealTable(); + + TreatmentImpl treatment1 = new TreatmentImpl(_container, "Treatment1", "Treatment1 description"); + treatment1 = Table.insert(_user, ti, treatment1); + addProductsForTreatment(treatment1.getRowId()); + _treatments.add(treatment1); + + TreatmentImpl treatment2 = new TreatmentImpl(_container, "Treatment2", "Treatment2 description"); + treatment2 = Table.insert(_user, ti, treatment2); + addProductsForTreatment(treatment2.getRowId()); + _treatments.add(treatment2); + } + + assertEquals(_treatments.size(), 2); + } + + private void addProductsForTreatment(int treatmentId) + { + TableInfo treatmentProductTable = _schema.getTable(StudyDesignQuerySchema.TREATMENT_PRODUCT_MAP_TABLE_NAME); + if (treatmentProductTable != null) + { + TableInfo ti = ((FilteredTable)treatmentProductTable).getRealTable(); + + for (ProductImpl product : _products) + { + TreatmentProductImpl tp = new TreatmentProductImpl(_container, treatmentId, product.getRowId()); + tp.setDose("Test Dose"); + tp.setRoute(_lookups.get("Route")); + Table.insert(_user, ti, tp); + } + } + + verifyTreatmentProductMapRecords(treatmentId, _products.size()); + } + + private void populateStudyProducts() + { + TableInfo productTable = _schema.getTable(StudyDesignQuerySchema.PRODUCT_TABLE_NAME); + if (productTable != null) + { + TableInfo ti = ((FilteredTable)productTable).getRealTable(); + + ProductImpl product1 = new ProductImpl(_container, "Immunogen1", "Immunogen"); + product1.setType(_lookups.get("ImmunogenType")); + _products.add(Table.insert(_user, ti, product1)); + + ProductImpl product2 = new ProductImpl(_container, "Immunogen2", "Immunogen"); + product2.setType(_lookups.get("ImmunogenType")); + _products.add(Table.insert(_user, ti, product2)); + + ProductImpl product3 = new ProductImpl(_container, "Adjuvant1", "Adjuvant"); + _products.add(Table.insert(_user, ti, product3)); + + ProductImpl product4 = new ProductImpl(_container, "Adjuvant2", "Adjuvant"); + _products.add(Table.insert(_user, ti, product4)); + } + + assertEquals(_products.size(), 4); + + for (ProductImpl product : _products) + addAntigenToProduct(product.getRowId()); + } + + private void addAntigenToProduct(int productId) + { + TableInfo productAntigenTable = _schema.getTable(StudyDesignQuerySchema.PRODUCT_ANTIGEN_TABLE_NAME); + if (productAntigenTable != null) + { + TableInfo ti = ((FilteredTable)productAntigenTable).getRealTable(); + + ProductAntigenImpl productAntigen = new ProductAntigenImpl(_container, productId, _lookups.get("Gene"), _lookups.get("SubType")); + Table.insert(_user, ti, productAntigen); + } + + verifyStudyProductAntigens(productId, 1); + } + + private void populateLookupTables() + { + String name, label; + + Map data = new HashMap<>(); + data.put("Container", _container.getId()); + + data.put("Name", name = "Test Immunogen Type"); + data.put("Label", label = "Test Immunogen Type Label"); + Table.insert(_user, StudyDesignSchema.getInstance().getTableInfoStudyDesignImmunogenTypes(), data); + assertEquals("Unexpected study design lookup label", label, _manager.getStudyDesignImmunogenTypeLabelByName(_container, name)); + assertNull("Unexpected study design lookup label", _manager.getStudyDesignImmunogenTypeLabelByName(_container, "UNK")); + _lookups.put("ImmunogenType", name); + + data.put("Name", name = "Test Gene"); + data.put("Label", label = "Test Gene Label"); + Table.insert(_user, StudyDesignSchema.getInstance().getTableInfoStudyDesignGenes(), data); + assertEquals("Unexpected study design lookup label", label, _manager.getStudyDesignGeneLabelByName(_container, name)); + assertNull("Unexpected study design lookup label", _manager.getStudyDesignGeneLabelByName(_container, "UNK")); + _lookups.put("Gene", name); + + data.put("Name", name = "Test SubType"); + data.put("Label", label = "Test SubType Label"); + Table.insert(_user, StudyDesignSchema.getInstance().getTableInfoStudyDesignSubTypes(), data); + assertEquals("Unexpected study design lookup label", label, _manager.getStudyDesignSubTypeLabelByName(_container, name)); + assertNull("Unexpected study design lookup label", _manager.getStudyDesignSubTypeLabelByName(_container, "UNK")); + _lookups.put("SubType", name); + + data.put("Name", name = "Test Route"); + data.put("Label", label = "Test Route Label"); + Table.insert(_user, StudyDesignSchema.getInstance().getTableInfoStudyDesignRoutes(), data); + assertEquals("Unexpected study design lookup label", label, _manager.getStudyDesignRouteLabelByName(_container, name)); + assertNull("Unexpected study design lookup label", _manager.getStudyDesignRouteLabelByName(_container, "UNK")); + _lookups.put("Route", name); + + assertEquals(_lookups.keySet().size(), 4); + } + + private void verifyTreatmentVisitMapRecords(int expectedCount) + { + List rows = _manager.getStudyTreatmentVisitMap(_container, null); + assertEquals("Unexpected number of study.TreatmentVisitMap rows", expectedCount, rows.size()); + } + + private void verifyTreatmentProductMapRecords(int treatmentId, int expectedCount) + { + List rows = _manager.getStudyTreatmentProducts(_container, _user, treatmentId); + assertEquals("Unexpected number of study.TreatmentProductMap rows", expectedCount, rows.size()); + } + + private void verifyStudyProductAntigens(int productId, int expectedCount) + { + List rows = _manager.getStudyProductAntigens(_container, _user, productId); + assertEquals("Unexpected number of study.ProductAntigen rows", expectedCount, rows.size()); + + for (ProductAntigenImpl row : rows) + { + assertEquals("Unexpected antigen lookup value", _lookups.get("Gene"), row.getGene()); + assertEquals("Unexpected antigen lookup value", _lookups.get("SubType"), row.getSubType()); + } + } + + private void createStudy() + { + _context = TestContext.get(); + Container junit = JunitUtil.getTestContainer(); + + String name = GUID.makeHash(); + Container c = ContainerManager.createContainer(junit, name, _context.getUser()); + Set modules = new HashSet<>(c.getActiveModules()); + modules.add(ModuleLoader.getInstance().getModule("studydesign")); + c.setActiveModules(modules); + _junitStudy = StudyService.get().createStudy(c, _context.getUser(), "Junit Study", TimepointType.VISIT, true); + } + + private void tearDown() + { + if (null != _junitStudy) + { + assertTrue(ContainerManager.delete(_junitStudy.getContainer(), _context.getUser())); + } + } + } + + @TestWhen(TestWhen.When.BVT) + public static class AssayScheduleTestCase extends Assert + { + TestContext _context = null; + User _user = null; + Container _container = null; + Study _junitStudy = null; + + Map _lookups = new HashMap<>(); + List _assays = new ArrayList<>(); + List _visits = new ArrayList<>(); + + @Test + public void test() + { + try + { + createStudy(); + _user = _context.getUser(); + _container = _junitStudy.getContainer(); + + populateLookupTables(); + populateAssayConfigurations(); + populateAssaySchedule(); + + verifyAssayConfigurations(); + verifyAssaySchedule(); + verifyCleanUpAssayConfigurations(); + } + finally + { + tearDown(); + } + } + + private void verifyCleanUpAssayConfigurations() + { + StudyDesignService.get().deleteAssaySpecimenVisits(_container, _visits.get(0).getId()); + verifyAssayScheduleRowCount(2); + assertEquals(1, TreatmentManager.getInstance().getAssaySpecimenVisitIds(_container, _assays.get(0)).size()); + assertEquals(1, TreatmentManager.getInstance().getVisitsForAssaySchedule(_container).size()); + + StudyDesignService.get().deleteAssaySpecimenVisits(_container, _visits.get(1).getId()); + verifyAssayScheduleRowCount(0); + assertEquals(0, TreatmentManager.getInstance().getAssaySpecimenVisitIds(_container, _assays.get(0)).size()); + assertEquals(0, TreatmentManager.getInstance().getVisitsForAssaySchedule(_container).size()); + } + + private void verifyAssaySchedule() + { + verifyAssayScheduleRowCount(4); + + List visits = TreatmentManager.getInstance().getVisitsForAssaySchedule(_container); + assertEquals("Unexpected assay schedule visit count", 2, visits.size()); + + for (AssaySpecimenConfig assay : StudyDesignService.get().getAssaySpecimenConfigs(_container)) + { + List visitIds = TreatmentManager.getInstance().getAssaySpecimenVisitIds(_container, assay); + for (Visit visit : _visits) + assertTrue("Assay schedule does not contain expected visitId", visitIds.contains(visit.getId())); + } + } + + private void verifyAssayScheduleRowCount(int expectedCount) + { + TableSelector selector = new TableSelector(StudyDesignSchema.getInstance().getTableInfoAssaySpecimenVisit(), SimpleFilter.createContainerFilter(_container), null); + assertEquals("Unexpected number of assay schedule visit records", expectedCount, selector.getRowCount()); + } + + private void verifyAssayConfigurations() + { + Collection assays = StudyDesignService.get().getAssaySpecimenConfigs(_container); + assertEquals("Unexpected assay configuration count", 2, assays.size()); + + for (AssaySpecimenConfig assay : assays) + { + if (assay instanceof AssaySpecimenConfigImpl assayConfig) + { + assertEquals("Unexpected assay configuration lookup value", _lookups.get("Lab"), assayConfig.getLab()); + assertEquals("Unexpected assay configuration lookup value", _lookups.get("SampleType"), assayConfig.getSampleType()); + } + } + } + + private void populateAssaySchedule() + { + _visits.add(VisitService.get().createVisit(_junitStudy, _user, BigDecimal.valueOf(1.0), "Visit 1", Visit.Type.BASELINE)); + _visits.add(VisitService.get().createVisit(_junitStudy, _user, BigDecimal.valueOf(2.0), "Visit 2", Visit.Type.SCHEDULED_FOLLOWUP)); + assertEquals(_visits.size(), 2); + + for (AssaySpecimenConfigImpl assay : _assays) + { + for (Visit visit : _visits) + { + AssaySpecimenVisitImpl asv = new AssaySpecimenVisitImpl(_container, assay.getRowId(), visit.getId()); + Table.insert(_user, StudyDesignSchema.getInstance().getTableInfoAssaySpecimenVisit(), asv); + } + } + + verifyAssayScheduleRowCount(_assays.size() * _visits.size()); + } + + private void populateAssayConfigurations() + { + AssaySpecimenConfigImpl assay1 = new AssaySpecimenConfigImpl(_container, "Assay1", "Assay 1 description"); + assay1.setLab(_lookups.get("Lab")); + assay1.setSampleType(_lookups.get("SampleType")); + _assays.add(TreatmentManager.getInstance().addAssaySpecimenConfig(_user, assay1)); + + AssaySpecimenConfigImpl assay2 = new AssaySpecimenConfigImpl(_container, "Assay2", "Assay 2 description"); + assay2.setLab(_lookups.get("Lab")); + assay2.setSampleType(_lookups.get("SampleType")); + _assays.add(TreatmentManager.getInstance().addAssaySpecimenConfig(_user, assay2)); + + assertEquals(2, _assays.size()); + } + + private void populateLookupTables() + { + String name, label; + + Map data = new HashMap<>(); + data.put("Container", _container.getId()); + + data.put("Name", name = "Test Lab"); + data.put("Label", label = "Test Lab Label"); + Table.insert(_user, StudyDesignSchema.getInstance().getTableInfoStudyDesignLabs(), data); + assertEquals("Unexpected study design lookup label", label, TreatmentManager.getInstance().getStudyDesignLabLabelByName(_container, name)); + assertNull("Unexpected study design lookup label", TreatmentManager.getInstance().getStudyDesignLabLabelByName(_container, "UNK")); + _lookups.put("Lab", name); + + data.put("Name", name = "Test Sample Type"); + data.put("Label", label = "Test Sample Type Label"); + data.put("PrimaryType", "Test Primary Type"); + data.put("ShortSampleCode", "TP"); + Table.insert(_user, StudyDesignSchema.getInstance().getTableInfoStudyDesignSampleTypes(), data); + _lookups.put("SampleType", name); + } + + private void createStudy() + { + _context = TestContext.get(); + Container junit = JunitUtil.getTestContainer(); + + String name = GUID.makeHash(); + Container c = ContainerManager.createContainer(junit, name, _context.getUser()); + _junitStudy = StudyService.get().createStudy(c, _context.getUser(), "Junit Study", TimepointType.VISIT, true); + } + + private void tearDown() + { + if (null != _junitStudy) + { + assertTrue(ContainerManager.delete(_junitStudy.getContainer(), _context.getUser())); + } + } + } +} diff --git a/study/src/org/labkey/study/model/TreatmentProductImpl.java b/studydesign/src/org/labkey/studydesign/model/TreatmentProductImpl.java similarity index 96% rename from study/src/org/labkey/study/model/TreatmentProductImpl.java rename to studydesign/src/org/labkey/studydesign/model/TreatmentProductImpl.java index 87b09baa..c2c5e882 100644 --- a/study/src/org/labkey/study/model/TreatmentProductImpl.java +++ b/studydesign/src/org/labkey/studydesign/model/TreatmentProductImpl.java @@ -1,255 +1,255 @@ -/* - * Copyright (c) 2013-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.study.model; - -import org.jetbrains.annotations.NotNull; -import org.json.JSONObject; -import org.labkey.api.data.Container; -import org.labkey.api.study.TreatmentProduct; -import org.labkey.api.util.Pair; - -import java.util.HashMap; -import java.util.Map; - -/** - * User: cnathe - * Date: 12/27/13 - */ -public class TreatmentProductImpl implements TreatmentProduct -{ - public static final String PRODUCT_DOSE_DELIMITER = "-#-"; - private Container _container; - private int _rowId; - private int _treatmentId; - private int _productId; - private String _dose; - private String _route; - private String _doseAndRoute; - private String _productDoseRoute; - - public TreatmentProductImpl() - {} - - public TreatmentProductImpl(Container container, int treatmentId, int productId) - { - _container = container; - _treatmentId = treatmentId; - _productId = productId; - } - - public boolean isNew() - { - return _rowId == 0; - } - - public Object getPrimaryKey() - { - return getRowId(); - } - - @Override - public int getRowId() - { - return _rowId; - } - - public void setRowId(int rowId) - { - _rowId = rowId; - } - - @Override - public int getTreatmentId() - { - return _treatmentId; - } - - public void setTreatmentId(int treatmentId) - { - _treatmentId = treatmentId; - } - - @Override - public int getProductId() - { - return _productId; - } - - public void setProductId(int productId) - { - _productId = productId; - } - - @Override - public String getDose() - { - return _dose; - } - - public void setDose(String dose) - { - _dose = dose; - } - - @Override - public String getRoute() - { - return _route; - } - - public void setRoute(String route) - { - _route = route; - } - - public Container getContainer() - { - return _container; - } - - public void setContainer(Container container) - { - _container = container; - } - - public String getDoseAndRoute() - { - return _doseAndRoute; - } - - public void setDoseAndRoute(String doseAndRoute) - { - _doseAndRoute = doseAndRoute; - } - - public Map serialize() - { - syncDoseAndRoute(); - Map props = new HashMap<>(); - props.put("RowId", getRowId()); - props.put("TreatmentId", getTreatmentId()); - props.put("ProductId", getProductId()); - props.put("Dose", getDose()); - props.put("Route", getRoute()); - props.put("DoseAndRoute", getDoseAndRoute()); - props.put("ProductDoseRoute", getProductDoseRoute()); - - return props; - } - - /** - * Keeps the dose, route, and doseAndRoute fields synchronized - */ - private void syncDoseAndRoute() - { - if (getDoseAndRoute() == null && (getDose() != null || getRoute() != null) && getProductId() > 0) - { - // get the entry from the DoseAndRoute table so we can serialize the label - DoseAndRoute doseAndRoute = TreatmentManager.getInstance().getDoseAndRoute(getContainer(), getDose(), getRoute(), getProductId()); - if (doseAndRoute != null) - { - setDoseAndRoute(doseAndRoute.getLabel()); - setProductDoseRoute(String.valueOf(getProductId() + PRODUCT_DOSE_DELIMITER + doseAndRoute.getLabel())); - } - } - else if (getDoseAndRoute() != null && getDose() == null && getRoute() == null) - { - Pair parts = DoseAndRoute.parseFromLabel(getDoseAndRoute()); - if (parts != null) - { - DoseAndRoute doseAndRoute = TreatmentManager.getInstance().getDoseAndRoute(getContainer(), parts.getKey(), parts.getValue(), getProductId()); - if (doseAndRoute != null) - { - setDose(doseAndRoute.getDose()); - setRoute(doseAndRoute.getRoute()); - } - } - } - else if (getDoseAndRoute() == null && getDose() == null && getRoute() == null && getProductId() > 0) - { - setProductDoseRoute(String.valueOf(getProductId() + PRODUCT_DOSE_DELIMITER)); - } - } - - public static TreatmentProductImpl fromJSON(@NotNull JSONObject o, Container container) - { - TreatmentProductImpl treatmentProduct = new TreatmentProductImpl(); - //treatmentProduct.setDose(o.getString("Dose")); - //treatmentProduct.setRoute(o.getString("Route")); - treatmentProduct.setContainer(container); - if (o.has("ProductId") && o.get("ProductId") instanceof Integer productId) - treatmentProduct.setProductId(productId); - if (o.has("TreatmentId") && o.get("TreatmentId") instanceof Integer treatmentId) - treatmentProduct.setTreatmentId(treatmentId); - if (o.has("RowId")) - treatmentProduct.setRowId(o.getInt("RowId")); - if (o.has("DoseAndRoute")) - treatmentProduct.setDoseAndRoute(o.getString("DoseAndRoute")); - if (o.has("ProductDoseRoute")) - treatmentProduct.populateProductDoseRoute(o.getString("ProductDoseRoute")); - - return treatmentProduct; - } - - public void setProductDoseRoute(String productDoseRoute) - { - _productDoseRoute = productDoseRoute; - } - - public String getProductDoseRoute() - { - return _productDoseRoute; - } - - private void populateProductDoseRoute(String productDoseRoute) - { - if (productDoseRoute == null) - return; - setProductDoseRoute(productDoseRoute); - String[] parts = productDoseRoute.split(PRODUCT_DOSE_DELIMITER); - setProductId(Integer.parseInt(parts[0])); - if (parts.length > 1) - setDoseAndRoute(parts[1]); - } - - public boolean isSameTreatmentProductWith(TreatmentProductImpl other) - { - if (other == null) - return false; - if (this.getProductId() != other.getProductId()) - return false; - if (this.getProductDoseRoute() != null && other.getProductDoseRoute() != null) - return this.getProductDoseRoute().equals(other.getProductDoseRoute()); - - if (this.getDose() != null) - { - if (other.getDose() == null || !this.getDose().equals(other.getDose())) - return false; - } - else if (other.getDose() != null) - return false; - - if (this.getRoute() != null) - { - if (other.getRoute() == null || !this.getRoute().equals(other.getRoute())) - return false; - } - else if (other.getRoute() != null) - return false; - - return true; - } -} +/* + * Copyright (c) 2013-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.studydesign.model; + +import org.jetbrains.annotations.NotNull; +import org.json.JSONObject; +import org.labkey.api.data.Container; +import org.labkey.api.study.TreatmentProduct; +import org.labkey.api.util.Pair; + +import java.util.HashMap; +import java.util.Map; + +/** + * User: cnathe + * Date: 12/27/13 + */ +public class TreatmentProductImpl implements TreatmentProduct +{ + public static final String PRODUCT_DOSE_DELIMITER = "-#-"; + private Container _container; + private int _rowId; + private int _treatmentId; + private int _productId; + private String _dose; + private String _route; + private String _doseAndRoute; + private String _productDoseRoute; + + public TreatmentProductImpl() + {} + + public TreatmentProductImpl(Container container, int treatmentId, int productId) + { + _container = container; + _treatmentId = treatmentId; + _productId = productId; + } + + public boolean isNew() + { + return _rowId == 0; + } + + public Object getPrimaryKey() + { + return getRowId(); + } + + @Override + public int getRowId() + { + return _rowId; + } + + public void setRowId(int rowId) + { + _rowId = rowId; + } + + @Override + public int getTreatmentId() + { + return _treatmentId; + } + + public void setTreatmentId(int treatmentId) + { + _treatmentId = treatmentId; + } + + @Override + public int getProductId() + { + return _productId; + } + + public void setProductId(int productId) + { + _productId = productId; + } + + @Override + public String getDose() + { + return _dose; + } + + public void setDose(String dose) + { + _dose = dose; + } + + @Override + public String getRoute() + { + return _route; + } + + public void setRoute(String route) + { + _route = route; + } + + public Container getContainer() + { + return _container; + } + + public void setContainer(Container container) + { + _container = container; + } + + public String getDoseAndRoute() + { + return _doseAndRoute; + } + + public void setDoseAndRoute(String doseAndRoute) + { + _doseAndRoute = doseAndRoute; + } + + public Map serialize() + { + syncDoseAndRoute(); + Map props = new HashMap<>(); + props.put("RowId", getRowId()); + props.put("TreatmentId", getTreatmentId()); + props.put("ProductId", getProductId()); + props.put("Dose", getDose()); + props.put("Route", getRoute()); + props.put("DoseAndRoute", getDoseAndRoute()); + props.put("ProductDoseRoute", getProductDoseRoute()); + + return props; + } + + /** + * Keeps the dose, route, and doseAndRoute fields synchronized + */ + private void syncDoseAndRoute() + { + if (getDoseAndRoute() == null && (getDose() != null || getRoute() != null) && getProductId() > 0) + { + // get the entry from the DoseAndRoute table so we can serialize the label + DoseAndRoute doseAndRoute = TreatmentManager.getInstance().getDoseAndRoute(getContainer(), getDose(), getRoute(), getProductId()); + if (doseAndRoute != null) + { + setDoseAndRoute(doseAndRoute.getLabel()); + setProductDoseRoute(String.valueOf(getProductId() + PRODUCT_DOSE_DELIMITER + doseAndRoute.getLabel())); + } + } + else if (getDoseAndRoute() != null && getDose() == null && getRoute() == null) + { + Pair parts = DoseAndRoute.parseFromLabel(getDoseAndRoute()); + if (parts != null) + { + DoseAndRoute doseAndRoute = TreatmentManager.getInstance().getDoseAndRoute(getContainer(), parts.getKey(), parts.getValue(), getProductId()); + if (doseAndRoute != null) + { + setDose(doseAndRoute.getDose()); + setRoute(doseAndRoute.getRoute()); + } + } + } + else if (getDoseAndRoute() == null && getDose() == null && getRoute() == null && getProductId() > 0) + { + setProductDoseRoute(String.valueOf(getProductId() + PRODUCT_DOSE_DELIMITER)); + } + } + + public static TreatmentProductImpl fromJSON(@NotNull JSONObject o, Container container) + { + TreatmentProductImpl treatmentProduct = new TreatmentProductImpl(); + //treatmentProduct.setDose(o.getString("Dose")); + //treatmentProduct.setRoute(o.getString("Route")); + treatmentProduct.setContainer(container); + if (o.has("ProductId") && o.get("ProductId") instanceof Integer productId) + treatmentProduct.setProductId(productId); + if (o.has("TreatmentId") && o.get("TreatmentId") instanceof Integer treatmentId) + treatmentProduct.setTreatmentId(treatmentId); + if (o.has("RowId")) + treatmentProduct.setRowId(o.getInt("RowId")); + if (o.has("DoseAndRoute")) + treatmentProduct.setDoseAndRoute(o.getString("DoseAndRoute")); + if (o.has("ProductDoseRoute")) + treatmentProduct.populateProductDoseRoute(o.getString("ProductDoseRoute")); + + return treatmentProduct; + } + + public void setProductDoseRoute(String productDoseRoute) + { + _productDoseRoute = productDoseRoute; + } + + public String getProductDoseRoute() + { + return _productDoseRoute; + } + + private void populateProductDoseRoute(String productDoseRoute) + { + if (productDoseRoute == null) + return; + setProductDoseRoute(productDoseRoute); + String[] parts = productDoseRoute.split(PRODUCT_DOSE_DELIMITER); + setProductId(Integer.parseInt(parts[0])); + if (parts.length > 1) + setDoseAndRoute(parts[1]); + } + + public boolean isSameTreatmentProductWith(TreatmentProductImpl other) + { + if (other == null) + return false; + if (this.getProductId() != other.getProductId()) + return false; + if (this.getProductDoseRoute() != null && other.getProductDoseRoute() != null) + return this.getProductDoseRoute().equals(other.getProductDoseRoute()); + + if (this.getDose() != null) + { + if (other.getDose() == null || !this.getDose().equals(other.getDose())) + return false; + } + else if (other.getDose() != null) + return false; + + if (this.getRoute() != null) + { + if (other.getRoute() == null || !this.getRoute().equals(other.getRoute())) + return false; + } + else if (other.getRoute() != null) + return false; + + return true; + } +} diff --git a/api/src/org/labkey/api/study/TreatmentVisitMap.java b/studydesign/src/org/labkey/studydesign/model/TreatmentVisitMap.java similarity index 93% rename from api/src/org/labkey/api/study/TreatmentVisitMap.java rename to studydesign/src/org/labkey/studydesign/model/TreatmentVisitMap.java index f9fa1d4e..3c5107c5 100644 --- a/api/src/org/labkey/api/study/TreatmentVisitMap.java +++ b/studydesign/src/org/labkey/studydesign/model/TreatmentVisitMap.java @@ -1,27 +1,27 @@ -/* - * Copyright (c) 2014 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.api.study; - -/** - * User: cnathe - * Date: 12/30/13 - */ -public interface TreatmentVisitMap -{ - int getCohortId(); - int getTreatmentId(); - int getVisitId(); -} +/* + * Copyright (c) 2014 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.studydesign.model; + +/** + * User: cnathe + * Date: 12/30/13 + */ +public interface TreatmentVisitMap +{ + int getCohortId(); + int getTreatmentId(); + int getVisitId(); +} diff --git a/study/src/org/labkey/study/model/TreatmentVisitMapImpl.java b/studydesign/src/org/labkey/studydesign/model/TreatmentVisitMapImpl.java similarity index 93% rename from study/src/org/labkey/study/model/TreatmentVisitMapImpl.java rename to studydesign/src/org/labkey/studydesign/model/TreatmentVisitMapImpl.java index 6623123b..78b51362 100644 --- a/study/src/org/labkey/study/model/TreatmentVisitMapImpl.java +++ b/studydesign/src/org/labkey/studydesign/model/TreatmentVisitMapImpl.java @@ -1,122 +1,121 @@ -/* - * Copyright (c) 2014-2016 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.study.model; - -import org.jetbrains.annotations.NotNull; -import org.json.JSONObject; -import org.labkey.api.data.Container; -import org.labkey.api.study.TreatmentVisitMap; - -/** - * User: cnathe - * Date: 12/30/13 - */ -public class TreatmentVisitMapImpl implements TreatmentVisitMap -{ - private int _cohortId; - private int _treatmentId; - private String _tempTreatmentId; // used to map new treatment records used in mappings to the tempRowId in TreatmentImpl - private int _visitId; - private Container _container; - - public TreatmentVisitMapImpl() - { - } - - @Override - public int getCohortId() - { - return _cohortId; - } - - public void setCohortId(int cohortId) - { - _cohortId = cohortId; - } - - @Override - public int getTreatmentId() - { - return _treatmentId; - } - - public void setTreatmentId(int treatmentId) - { - _treatmentId = treatmentId; - } - - public String getTempTreatmentId() - { - return _tempTreatmentId; - } - - private void setTempTreatmentId(String tempTreatmentId) - { - _tempTreatmentId = tempTreatmentId; - } - - @Override - public int getVisitId() - { - return _visitId; - } - - public void setVisitId(int visitId) - { - _visitId = visitId; - } - - public Container getContainer() - { - return _container; - } - - public void setContainer(Container container) - { - _container = container; - } - - @Override - public boolean equals(Object obj) - { - if (this == obj) return true; - if (obj == null || getClass() != obj.getClass()) return false; - - final TreatmentVisitMapImpl o = (TreatmentVisitMapImpl) obj; - - return o.getCohortId() == getCohortId() - && o.getTreatmentId() == getTreatmentId() - && o.getVisitId() == getVisitId() - && ((o.getContainer() == null && getContainer() == null) || o.getContainer().equals(getContainer())); - } - - public static TreatmentVisitMapImpl fromJSON(@NotNull JSONObject o) - { - TreatmentVisitMapImpl visitMap = new TreatmentVisitMapImpl(); - visitMap.setVisitId(o.getInt("VisitId")); - if (o.has("CohortId")) - visitMap.setCohortId(o.getInt("CohortId")); - if (o.has("TreatmentId")) - { - if (o.get("TreatmentId") instanceof Integer) - visitMap.setTreatmentId(o.getInt("TreatmentId")); - else - visitMap.setTempTreatmentId(o.getString("TreatmentId")); - } - - return visitMap; - } -} +/* + * Copyright (c) 2014-2016 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.studydesign.model; + +import org.jetbrains.annotations.NotNull; +import org.json.JSONObject; +import org.labkey.api.data.Container; + +/** + * User: cnathe + * Date: 12/30/13 + */ +public class TreatmentVisitMapImpl implements TreatmentVisitMap +{ + private int _cohortId; + private int _treatmentId; + private String _tempTreatmentId; // used to map new treatment records used in mappings to the tempRowId in TreatmentImpl + private int _visitId; + private Container _container; + + public TreatmentVisitMapImpl() + { + } + + @Override + public int getCohortId() + { + return _cohortId; + } + + public void setCohortId(int cohortId) + { + _cohortId = cohortId; + } + + @Override + public int getTreatmentId() + { + return _treatmentId; + } + + public void setTreatmentId(int treatmentId) + { + _treatmentId = treatmentId; + } + + public String getTempTreatmentId() + { + return _tempTreatmentId; + } + + private void setTempTreatmentId(String tempTreatmentId) + { + _tempTreatmentId = tempTreatmentId; + } + + @Override + public int getVisitId() + { + return _visitId; + } + + public void setVisitId(int visitId) + { + _visitId = visitId; + } + + public Container getContainer() + { + return _container; + } + + public void setContainer(Container container) + { + _container = container; + } + + @Override + public boolean equals(Object obj) + { + if (this == obj) return true; + if (obj == null || getClass() != obj.getClass()) return false; + + final TreatmentVisitMapImpl o = (TreatmentVisitMapImpl) obj; + + return o.getCohortId() == getCohortId() + && o.getTreatmentId() == getTreatmentId() + && o.getVisitId() == getVisitId() + && ((o.getContainer() == null && getContainer() == null) || o.getContainer().equals(getContainer())); + } + + public static TreatmentVisitMapImpl fromJSON(@NotNull JSONObject o) + { + TreatmentVisitMapImpl visitMap = new TreatmentVisitMapImpl(); + visitMap.setVisitId(o.getInt("VisitId")); + if (o.has("CohortId")) + visitMap.setCohortId(o.getInt("CohortId")); + if (o.has("TreatmentId")) + { + if (o.get("TreatmentId") instanceof Integer) + visitMap.setTreatmentId(o.getInt("TreatmentId")); + else + visitMap.setTempTreatmentId(o.getString("TreatmentId")); + } + + return visitMap; + } +} diff --git a/study/src/org/labkey/study/view/studydesign/AssayScheduleWebpartFactory.java b/studydesign/src/org/labkey/studydesign/view/AssayScheduleWebpartFactory.java similarity index 80% rename from study/src/org/labkey/study/view/studydesign/AssayScheduleWebpartFactory.java rename to studydesign/src/org/labkey/studydesign/view/AssayScheduleWebpartFactory.java index 54adee27..7295b36b 100644 --- a/study/src/org/labkey/study/view/studydesign/AssayScheduleWebpartFactory.java +++ b/studydesign/src/org/labkey/studydesign/view/AssayScheduleWebpartFactory.java @@ -1,72 +1,72 @@ -/* - * Copyright (c) 2013-2014 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.study.view.studydesign; - -import org.jetbrains.annotations.NotNull; -import org.labkey.api.data.Container; -import org.labkey.api.study.Study; -import org.labkey.api.study.TimepointType; -import org.labkey.api.study.security.permissions.ManageStudyPermission; -import org.labkey.api.view.ActionURL; -import org.labkey.api.view.JspView; -import org.labkey.api.view.NavTree; -import org.labkey.api.view.Portal; -import org.labkey.api.view.ViewContext; -import org.labkey.api.view.WebPartView; -import org.labkey.study.controllers.StudyController; -import org.labkey.study.model.StudyManager; - -/** - * User: cnathe - * Date: 12/16/13 - */ -public class AssayScheduleWebpartFactory extends StudyDesignWebpartFactory -{ - public static String NAME = "Assay Schedule"; - - public AssayScheduleWebpartFactory() - { - super(NAME); - } - - @Override - public WebPartView getWebPartView(@NotNull ViewContext portalCtx, @NotNull Portal.WebPart webPart) - { - if (!canShow()) - return null; - - JspView view = new JspView<>("/org/labkey/study/view/studydesign/assayScheduleWebpart.jsp", webPart); - view.setTitle(NAME); - view.setFrame(WebPartView.FrameType.PORTAL); - - Container c = portalCtx.getContainer(); - Study study = StudyManager.getInstance().getStudy(c); - if (c.hasPermission(portalCtx.getUser(), ManageStudyPermission.class)) - { - String timepointMenuName; - if (study != null && study.getTimepointType() == TimepointType.DATE) - timepointMenuName = "Manage Timepoints"; - else - timepointMenuName = "Manage Visits"; - - NavTree menu = new NavTree(); - menu.addChild(timepointMenuName, new ActionURL(StudyController.ManageVisitsAction.class, c)); - view.setNavMenu(menu); - } - - return view; - } -} +/* + * Copyright (c) 2013-2014 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.studydesign.view; + +import org.jetbrains.annotations.NotNull; +import org.labkey.api.data.Container; +import org.labkey.api.study.Study; +import org.labkey.api.study.StudyService; +import org.labkey.api.study.StudyUrls; +import org.labkey.api.study.TimepointType; +import org.labkey.api.study.security.permissions.ManageStudyPermission; +import org.labkey.api.util.PageFlowUtil; +import org.labkey.api.view.JspView; +import org.labkey.api.view.NavTree; +import org.labkey.api.view.Portal; +import org.labkey.api.view.ViewContext; +import org.labkey.api.view.WebPartView; + +/** + * User: cnathe + * Date: 12/16/13 + */ +public class AssayScheduleWebpartFactory extends StudyDesignWebpartFactory +{ + public static String NAME = "Assay Schedule"; + + public AssayScheduleWebpartFactory() + { + super(NAME); + } + + @Override + public WebPartView getWebPartView(@NotNull ViewContext portalCtx, @NotNull Portal.WebPart webPart) + { + if (!canShow(portalCtx.getContainer())) + return null; + + JspView view = new JspView<>("/org/labkey/studydesign/view/assayScheduleWebpart.jsp", webPart); + view.setTitle(NAME); + view.setFrame(WebPartView.FrameType.PORTAL); + + Container c = portalCtx.getContainer(); + Study study = StudyService.get().getStudy(c); + if (c.hasPermission(portalCtx.getUser(), ManageStudyPermission.class)) + { + String timepointMenuName; + if (study != null && study.getTimepointType() == TimepointType.DATE) + timepointMenuName = "Manage Timepoints"; + else + timepointMenuName = "Manage Visits"; + + NavTree menu = new NavTree(); + menu.addChild(timepointMenuName, PageFlowUtil.urlProvider(StudyUrls.class).getManageVisitsURL(c)); + view.setNavMenu(menu); + } + + return view; + } +} diff --git a/study/src/org/labkey/study/view/studydesign/ImmunizationScheduleWebpartFactory.java b/studydesign/src/org/labkey/studydesign/view/ImmunizationScheduleWebpartFactory.java similarity index 75% rename from study/src/org/labkey/study/view/studydesign/ImmunizationScheduleWebpartFactory.java rename to studydesign/src/org/labkey/studydesign/view/ImmunizationScheduleWebpartFactory.java index 2c6800e2..abfe3e25 100644 --- a/study/src/org/labkey/study/view/studydesign/ImmunizationScheduleWebpartFactory.java +++ b/studydesign/src/org/labkey/studydesign/view/ImmunizationScheduleWebpartFactory.java @@ -1,74 +1,73 @@ -/* - * Copyright (c) 2013-2014 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.study.view.studydesign; - -import org.jetbrains.annotations.NotNull; -import org.labkey.api.data.Container; -import org.labkey.api.study.Study; -import org.labkey.api.study.TimepointType; -import org.labkey.api.study.security.permissions.ManageStudyPermission; -import org.labkey.api.view.ActionURL; -import org.labkey.api.view.JspView; -import org.labkey.api.view.NavTree; -import org.labkey.api.view.Portal; -import org.labkey.api.view.ViewContext; -import org.labkey.api.view.WebPartView; -import org.labkey.study.controllers.CohortController; -import org.labkey.study.controllers.StudyController; -import org.labkey.study.model.StudyManager; - -/** - * User: cnathe - * Date: 12/30/13 - */ -public class ImmunizationScheduleWebpartFactory extends StudyDesignWebpartFactory -{ - public static String NAME = "Immunization Schedule"; - - public ImmunizationScheduleWebpartFactory() - { - super(NAME); - } - - @Override - public WebPartView getWebPartView(@NotNull ViewContext portalCtx, @NotNull Portal.WebPart webPart) - { - if (!canShow()) - return null; - - JspView view = new JspView<>("/org/labkey/study/view/studydesign/immunizationScheduleWebpart.jsp", webPart); - view.setTitle(NAME); - view.setFrame(WebPartView.FrameType.PORTAL); - - Container c = portalCtx.getContainer(); - Study study = StudyManager.getInstance().getStudy(c); - if (c.hasPermission(portalCtx.getUser(), ManageStudyPermission.class)) - { - String timepointMenuName; - if (study != null && study.getTimepointType() == TimepointType.DATE) - timepointMenuName = "Manage Timepoints"; - else - timepointMenuName = "Manage Visits"; - - NavTree menu = new NavTree(); - menu.addChild("Manage Cohorts", new ActionURL(CohortController.ManageCohortsAction.class, c)); - menu.addChild(timepointMenuName, new ActionURL(StudyController.ManageVisitsAction.class, c)); - view.setNavMenu(menu); - } - - return view; - } -} +/* + * Copyright (c) 2013-2014 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.studydesign.view; + +import org.jetbrains.annotations.NotNull; +import org.labkey.api.data.Container; +import org.labkey.api.study.Study; +import org.labkey.api.study.StudyService; +import org.labkey.api.study.StudyUrls; +import org.labkey.api.study.TimepointType; +import org.labkey.api.study.security.permissions.ManageStudyPermission; +import org.labkey.api.util.PageFlowUtil; +import org.labkey.api.view.JspView; +import org.labkey.api.view.NavTree; +import org.labkey.api.view.Portal; +import org.labkey.api.view.ViewContext; +import org.labkey.api.view.WebPartView; + +/** + * User: cnathe + * Date: 12/30/13 + */ +public class ImmunizationScheduleWebpartFactory extends StudyDesignWebpartFactory +{ + public static String NAME = "Immunization Schedule"; + + public ImmunizationScheduleWebpartFactory() + { + super(NAME); + } + + @Override + public WebPartView getWebPartView(@NotNull ViewContext portalCtx, @NotNull Portal.WebPart webPart) + { + if (!canShow(portalCtx.getContainer())) + return null; + + JspView view = new JspView<>("/org/labkey/studydesign/view/immunizationScheduleWebpart.jsp", webPart); + view.setTitle(NAME); + view.setFrame(WebPartView.FrameType.PORTAL); + + Container c = portalCtx.getContainer(); + Study study = StudyService.get().getStudy(c); + if (c.hasPermission(portalCtx.getUser(), ManageStudyPermission.class)) + { + String timepointMenuName; + if (study != null && study.getTimepointType() == TimepointType.DATE) + timepointMenuName = "Manage Timepoints"; + else + timepointMenuName = "Manage Visits"; + + NavTree menu = new NavTree(); + menu.addChild("Manage Cohorts", PageFlowUtil.urlProvider(StudyUrls.class).getManageCohortsURL(c)); + menu.addChild(timepointMenuName, PageFlowUtil.urlProvider(StudyUrls.class).getManageVisitsURL(c)); + view.setNavMenu(menu); + } + + return view; + } +} diff --git a/study/src/org/labkey/study/view/studydesign/StudyDesignConfigureMenuItem.java b/studydesign/src/org/labkey/studydesign/view/StudyDesignConfigureMenuItem.java similarity index 94% rename from study/src/org/labkey/study/view/studydesign/StudyDesignConfigureMenuItem.java rename to studydesign/src/org/labkey/studydesign/view/StudyDesignConfigureMenuItem.java index 2ec1a066..b0b96d8e 100644 --- a/study/src/org/labkey/study/view/studydesign/StudyDesignConfigureMenuItem.java +++ b/studydesign/src/org/labkey/studydesign/view/StudyDesignConfigureMenuItem.java @@ -1,37 +1,37 @@ -/* - * Copyright (c) 2017 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.study.view.studydesign; - -import org.labkey.api.data.Container; -import org.labkey.api.query.QueryUrls; -import org.labkey.api.util.PageFlowUtil; -import org.labkey.api.view.ActionURL; -import org.labkey.api.view.NavTree; - -public class StudyDesignConfigureMenuItem extends NavTree -{ - public StudyDesignConfigureMenuItem(String text, String schemaName, String queryName, Container container) - { - super(text); - - ActionURL url = new ActionURL(); - url.setContainer(container); - url.addParameter("schemaName", schemaName); - url.addParameter("query.queryName", queryName); - setHref(PageFlowUtil.urlProvider(QueryUrls.class).urlExecuteQuery(url).toString()); - setTarget("_blank"); // issue 19493 - } -} +/* + * Copyright (c) 2017 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.studydesign.view; + +import org.labkey.api.data.Container; +import org.labkey.api.query.QueryUrls; +import org.labkey.api.util.PageFlowUtil; +import org.labkey.api.view.ActionURL; +import org.labkey.api.view.NavTree; + +public class StudyDesignConfigureMenuItem extends NavTree +{ + public StudyDesignConfigureMenuItem(String text, String schemaName, String queryName, Container container) + { + super(text); + + ActionURL url = new ActionURL(); + url.setContainer(container); + url.addParameter("schemaName", schemaName); + url.addParameter("query.queryName", queryName); + setHref(PageFlowUtil.urlProvider(QueryUrls.class).urlExecuteQuery(url).toString()); + setTarget("_blank"); // issue 19493 + } +} diff --git a/study/src/org/labkey/study/view/studydesign/StudyDesignWebpartFactory.java b/studydesign/src/org/labkey/studydesign/view/StudyDesignWebpartFactory.java similarity index 51% rename from study/src/org/labkey/study/view/studydesign/StudyDesignWebpartFactory.java rename to studydesign/src/org/labkey/studydesign/view/StudyDesignWebpartFactory.java index 50c8b6a5..b4cfa04c 100644 --- a/study/src/org/labkey/study/view/studydesign/StudyDesignWebpartFactory.java +++ b/studydesign/src/org/labkey/studydesign/view/StudyDesignWebpartFactory.java @@ -1,8 +1,7 @@ -package org.labkey.study.view.studydesign; +package org.labkey.studydesign.view; import org.labkey.api.data.Container; -import org.labkey.api.settings.OptionalFeatureService; -import org.labkey.api.study.StudyUtils; +import org.labkey.api.studydesign.StudyDesignManager; import org.labkey.api.view.BaseWebPartFactory; public abstract class StudyDesignWebpartFactory extends BaseWebPartFactory @@ -12,14 +11,14 @@ public StudyDesignWebpartFactory(String name) super(name); } - protected boolean canShow() + protected boolean canShow(Container c) { - return OptionalFeatureService.get().isFeatureEnabled(StudyUtils.STUDY_DESIGN_FEATURE_FLAG); + return StudyDesignManager.get().isModuleActive(c); } @Override public boolean isAvailable(Container c, String scope, String location) { - return canShow() ? super.isAvailable(c, scope, location) : false; + return canShow(c) ? super.isAvailable(c, scope, location) : false; } } \ No newline at end of file diff --git a/study/src/org/labkey/study/view/studydesign/VaccineDesignWebpartFactory.java b/studydesign/src/org/labkey/studydesign/view/VaccineDesignWebpartFactory.java similarity index 88% rename from study/src/org/labkey/study/view/studydesign/VaccineDesignWebpartFactory.java rename to studydesign/src/org/labkey/studydesign/view/VaccineDesignWebpartFactory.java index 0992d4b5..0522c2ee 100644 --- a/study/src/org/labkey/study/view/studydesign/VaccineDesignWebpartFactory.java +++ b/studydesign/src/org/labkey/studydesign/view/VaccineDesignWebpartFactory.java @@ -1,48 +1,48 @@ -/* - * Copyright (c) 2013-2014 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.study.view.studydesign; - -import org.jetbrains.annotations.NotNull; -import org.labkey.api.view.JspView; -import org.labkey.api.view.Portal; -import org.labkey.api.view.ViewContext; -import org.labkey.api.view.WebPartView; - -/** - * User: cnathe - * Date: 12/27/13 - */ -public class VaccineDesignWebpartFactory extends StudyDesignWebpartFactory -{ - public static String NAME = "Vaccine Design"; - - public VaccineDesignWebpartFactory() - { - super(NAME); - } - - @Override - public WebPartView getWebPartView(@NotNull ViewContext portalCtx, @NotNull Portal.WebPart webPart) - { - if (!canShow()) - return null; - - JspView view = new JspView<>("/org/labkey/study/view/studydesign/vaccineDesignWebpart.jsp", webPart); - view.setTitle(NAME); - view.setFrame(WebPartView.FrameType.PORTAL); - return view; - } -} +/* + * Copyright (c) 2013-2014 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.studydesign.view; + +import org.jetbrains.annotations.NotNull; +import org.labkey.api.view.JspView; +import org.labkey.api.view.Portal; +import org.labkey.api.view.ViewContext; +import org.labkey.api.view.WebPartView; + +/** + * User: cnathe + * Date: 12/27/13 + */ +public class VaccineDesignWebpartFactory extends StudyDesignWebpartFactory +{ + public static String NAME = "Vaccine Design"; + + public VaccineDesignWebpartFactory() + { + super(NAME); + } + + @Override + public WebPartView getWebPartView(@NotNull ViewContext portalCtx, @NotNull Portal.WebPart webPart) + { + if (!canShow(portalCtx.getContainer())) + return null; + + JspView view = new JspView<>("/org/labkey/studydesign/view/vaccineDesignWebpart.jsp", webPart); + view.setTitle(NAME); + view.setFrame(WebPartView.FrameType.PORTAL); + return view; + } +} diff --git a/study/src/org/labkey/study/view/studydesign/assayScheduleWebpart.jsp b/studydesign/src/org/labkey/studydesign/view/assayScheduleWebpart.jsp similarity index 87% rename from study/src/org/labkey/study/view/studydesign/assayScheduleWebpart.jsp rename to studydesign/src/org/labkey/studydesign/view/assayScheduleWebpart.jsp index 11830e74..8cb641cc 100644 --- a/study/src/org/labkey/study/view/studydesign/assayScheduleWebpart.jsp +++ b/studydesign/src/org/labkey/studydesign/view/assayScheduleWebpart.jsp @@ -1,88 +1,88 @@ -<% -/* - * Copyright (c) 2013-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. - */ -%> -<%@ page import="org.labkey.api.data.Container" %> -<%@ page import="org.labkey.api.module.ModuleLoader" %> -<%@ page import="org.labkey.api.security.User" %> -<%@ page import="org.labkey.api.security.permissions.UpdatePermission" %> -<%@ page import="org.labkey.api.util.HtmlString" %> -<%@ page import="org.labkey.api.view.ActionURL" %> -<%@ page import="org.labkey.api.view.template.ClientDependencies" %> -<%@ page import="org.labkey.study.controllers.StudyDesignController" %> -<%@ page import="org.labkey.study.model.StudyImpl" %> -<%@ page import="org.labkey.study.model.StudyManager" %> -<%@ page extends="org.labkey.study.view.BaseStudyPage" %> -<%! - @Override - public void addClientDependencies(ClientDependencies dependencies) - { - dependencies.add("study/vaccineDesign/vaccineDesign.lib.xml"); - dependencies.add("study/vaccineDesign/VaccineDesign.css"); - } -%> -<% - Container c = getContainer(); - boolean useAlternateLookupFields = getContainer().getActiveModules().contains(ModuleLoader.getInstance().getModule("rho")); - - StudyImpl study = StudyManager.getInstance().getStudy(c); - - User user = getUser(); - boolean canEdit = c.hasPermission(user, UpdatePermission.class); - - String assayPlan = ""; - if (study != null && study.getAssayPlan() != null) - assayPlan = study.getAssayPlan(); -%> -<% - if (study != null) - { - %>This section shows the assay schedule for this study.
    <% - - if (canEdit) - { - ActionURL editUrl = new ActionURL(StudyDesignController.ManageAssayScheduleAction.class, getContainer()); - if (useAlternateLookupFields) - editUrl.addParameter("useAlternateLookupFields", true); - editUrl.addReturnUrl(getActionURL()); -%> - <%=link("Manage Assay Schedule", editUrl)%>
    -<% - } - -%> -

    <%=HtmlString.unsafe(h(assayPlan).toString().replaceAll("\n", "
    "))%>

    -
    -<% - } - else - { -%> -

    The folder must contain a study in order to display an assay schedule.

    -<% - } -%> - - \ No newline at end of file diff --git a/study/src/org/labkey/study/view/studydesign/immunizationScheduleWebpart.jsp b/studydesign/src/org/labkey/studydesign/view/immunizationScheduleWebpart.jsp similarity index 79% rename from study/src/org/labkey/study/view/studydesign/immunizationScheduleWebpart.jsp rename to studydesign/src/org/labkey/studydesign/view/immunizationScheduleWebpart.jsp index dfb86fa7..8263de1a 100644 --- a/study/src/org/labkey/study/view/studydesign/immunizationScheduleWebpart.jsp +++ b/studydesign/src/org/labkey/studydesign/view/immunizationScheduleWebpart.jsp @@ -1,174 +1,174 @@ -<% -/* - * Copyright (c) 2013-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. - */ -%> -<%@ page import="org.labkey.api.data.Container" %> -<%@ page import="org.labkey.api.security.User" %> -<%@ page import="org.labkey.api.security.permissions.UpdatePermission" %> -<%@ page import="org.labkey.api.study.StudyUrls" %> -<%@ page import="org.labkey.api.util.HtmlString" %> -<%@ page import="org.labkey.api.view.ActionURL" %> -<%@ page import="org.labkey.api.view.template.ClientDependencies" %> -<%@ page import="org.labkey.study.model.CohortImpl" %> -<%@ page import="org.labkey.study.model.ProductImpl" %> -<%@ page import="org.labkey.study.model.StudyImpl" %> -<%@ page import="org.labkey.study.model.StudyManager" %> -<%@ page import="org.labkey.study.model.TreatmentImpl" %> -<%@ page import="org.labkey.study.model.TreatmentManager" %> -<%@ page import="org.labkey.study.model.TreatmentVisitMapImpl" %> -<%@ page import="org.labkey.study.model.VisitImpl" %> -<%@ page import="java.util.Collection" %> -<%@ page import="java.util.HashMap" %> -<%@ page import="java.util.List" %> -<%@ page import="java.util.Map" %> -<%@ page extends="org.labkey.study.view.BaseStudyPage" %> -<%! - @Override - public void addClientDependencies(ClientDependencies dependencies) - { - dependencies.add("study/vaccineDesign/VaccineDesign.css"); - } -%> -<% - Container c = getContainer(); - StudyImpl study = StudyManager.getInstance().getStudy(c); - - User user = getUser(); - boolean canEdit = c.hasPermission(user, UpdatePermission.class); - - String subjectNoun = "Subject"; - if (study != null) - subjectNoun = study.getSubjectNounSingular(); -%> - - - -<% - if (study != null) - { - if (!StudyManager.getInstance().showCohorts(c, user)) - { - %>

    You do not have permissions to see this data.

    <% - } - else - { - Collection cohorts = study.getCohorts(user); - %>This section shows the immunization schedule for this study. Each treatment may consist of one or more study products.
    <% - - if (canEdit) - { - ActionURL editUrl = urlProvider(StudyUrls.class).getManageTreatmentsURL(c, c.hasActiveModuleByName("viscstudies")); - editUrl.addReturnUrl(getActionURL()); -%> - <%=link("Manage Treatments", editUrl)%>
    -<% - } - - List visits = study.getVisitsForTreatmentSchedule(); -%> -
    -
    -
    Immunization Schedule
    -
    Group / Cohort <%=h(subjectNoun)%> Count
    <%=h(cohort.getLabel())%> <%=cohort.getSubjectCount() != null ? cohort.getSubjectCount() : ""%> <%=h(visit.getDisplayString())%> - <%=(visit.getDescription() != null ? PageFlowUtil.helpPopup("Description", visit.getDescription()) : "")%> + <%=(visit.getDescription() != null ? helpPopup("Description", visit.getDescription()) : HtmlString.EMPTY_STRING)%> <%=h(treatment != null ? treatment.getLabel() : "")%> - <%=(productHover.length() > 0 ? PageFlowUtil.helpPopup("Treatment Products", productHover, true, 500) : "")%> + <%=(productHover.length() > 0 ? helpPopup("Treatment Products", productHover, true, 500) : HtmlString.EMPTY_STRING)%> <%=h(treatment != null ? treatment.getLabel() : "")%> - <%=(productHover.length() > 0 ? helpPopup("Treatment Products", productHover, true, 500) : HtmlString.EMPTY_STRING)%> + <%=(!productHover.isEmpty() ? helpPopup("Treatment Products", HtmlString.unsafe(productHover), 500) : HtmlString.EMPTY_STRING)%>
    - - - -<% - for (VisitImpl visit : visits) - { -%> - -<% - } -%> - -<% - if (cohorts.size() == 0) - { - %><% - } - - int index = 0; - for (CohortImpl cohort : cohorts) - { - index++; - List mapping = study.getStudyTreatmentVisitMap(c, cohort.getRowId()); - Map visitTreatments = new HashMap<>(); - for (TreatmentVisitMapImpl treatmentVisitMap : mapping) - { - visitTreatments.put(treatmentVisitMap.getVisitId(), treatmentVisitMap.getTreatmentId()); - } -%> - " outer-index="<%=index-1%>"> - - -<% - for (VisitImpl visit : visits) - { - Integer treatmentId = visitTreatments.get(visit.getRowId()); - TreatmentImpl treatment = null; - if (treatmentId != null) - treatment = TreatmentManager.getInstance().getStudyTreatmentByRowId(c, user, treatmentId); - - // show the list of study products for the treatment as a hover - String productHover = ""; - if (treatment != null && treatment.getProducts() != null) - { - productHover += "
    Group / Cohort<%=h(subjectNoun)%> Count - <%=h(visit.getDisplayString())%> - <%=(visit.getDescription() != null ? helpPopup("Description", visit.getDescription()) : HtmlString.EMPTY_STRING)%> -
    No data to show.
    <%=h(cohort.getLabel())%><%=h(cohort.getSubjectCount())%>
    " - + "" - + "" - + ""; - - for (ProductImpl product : treatment.getProducts()) - { - String routeLabel = TreatmentManager.getInstance().getStudyDesignRouteLabelByName(c, product.getRoute()); - - productHover += "" - + "" - + ""; - } - - productHover += "
    LabelDose and unitsRoute
    " + h(product.getLabel()) + "" + h(product.getDose()) + "" + h(routeLabel != null ? routeLabel : product.getRoute()) + "
    "; - } -%> - - <%=h(treatment != null ? treatment.getLabel() : "")%> - <%=(!productHover.isEmpty() ? helpPopup("Treatment Products", HtmlString.unsafe(productHover), 500) : HtmlString.EMPTY_STRING)%> - -<% - } -%> - -<% - } -%> - - -<% - } - } - else - { - %>

    The folder must contain a study in order to display an immunization schedule.

    <% - } -%> +<% +/* + * Copyright (c) 2013-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. + */ +%> +<%@ page import="org.labkey.api.data.Container" %> +<%@ page import="org.labkey.api.security.User" %> +<%@ page import="org.labkey.api.security.permissions.UpdatePermission" %> +<%@ page import="org.labkey.api.study.Cohort" %> +<%@ page import="org.labkey.api.study.Study" %> +<%@ page import="org.labkey.api.study.StudyService" %> +<%@ page import="org.labkey.api.study.Visit" %> +<%@ page import="org.labkey.api.studydesign.StudyDesignUrls" %> +<%@ page import="org.labkey.api.util.HtmlString" %> +<%@ page import="org.labkey.api.view.ActionURL" %> +<%@ page import="org.labkey.api.view.template.ClientDependencies" %> +<%@ page import="org.labkey.studydesign.model.ProductImpl" %> +<%@ page import="org.labkey.studydesign.model.TreatmentImpl" %> +<%@ page import="org.labkey.studydesign.model.TreatmentManager" %> +<%@ page import="org.labkey.studydesign.model.TreatmentVisitMap" %> +<%@ page import="java.util.Collection" %> +<%@ page import="java.util.HashMap" %> +<%@ page import="java.util.List" %> +<%@ page import="java.util.Map" %> +<%@ page extends="org.labkey.api.jsp.JspBase" %> +<%! + @Override + public void addClientDependencies(ClientDependencies dependencies) + { + dependencies.add("study/vaccineDesign/VaccineDesign.css"); + } +%> +<% + Container c = getContainer(); + Study study = StudyService.get().getStudy(c); + + User user = getUser(); + boolean canEdit = c.hasPermission(user, UpdatePermission.class); + + String subjectNoun = "Subject"; + if (study != null) + subjectNoun = study.getSubjectNounSingular(); +%> + + + +<% + if (study != null) + { + if (!StudyService.get().showCohorts(c, user)) + { + %>

    You do not have permissions to see this data.

    <% + } + else + { + Collection cohorts = study.getCohorts(user); + %>This section shows the immunization schedule for this study. Each treatment may consist of one or more study products.
    <% + + if (canEdit) + { + ActionURL editUrl = urlProvider(StudyDesignUrls.class).getManageTreatmentsURL(c, c.hasActiveModuleByName("viscstudies")); + editUrl.addReturnUrl(getActionURL()); +%> + <%=link("Manage Treatments", editUrl)%>
    +<% + } + + List visits = TreatmentManager.getInstance().getVisitsForTreatmentSchedule(getContainer()); +%> +
    +
    +
    Immunization Schedule
    + + + + +<% + for (Visit visit : visits) + { +%> + +<% + } +%> + +<% + if (cohorts.size() == 0) + { + %><% + } + + int index = 0; + for (Cohort cohort : cohorts) + { + index++; + List mapping = TreatmentManager.getInstance().getStudyTreatmentVisitMap(c, cohort.getRowId()); + Map visitTreatments = new HashMap<>(); + for (TreatmentVisitMap treatmentVisitMap : mapping) + { + visitTreatments.put(treatmentVisitMap.getVisitId(), treatmentVisitMap.getTreatmentId()); + } +%> + " outer-index="<%=index-1%>"> + + +<% + for (Visit visit : visits) + { + Integer treatmentId = visitTreatments.get(visit.getId()); + TreatmentImpl treatment = null; + if (treatmentId != null) + treatment = TreatmentManager.getInstance().getStudyTreatmentByRowId(c, user, treatmentId); + + // show the list of study products for the treatment as a hover + String productHover = ""; + if (treatment != null && treatment.getProducts() != null) + { + productHover += "
    Group / Cohort<%=h(subjectNoun)%> Count + <%=h(visit.getDisplayString())%> + <%=(visit.getDescription() != null ? helpPopup("Description", visit.getDescription()) : HtmlString.EMPTY_STRING)%> +
    No data to show.
    <%=h(cohort.getLabel())%><%=h(cohort.getSubjectCount())%>
    " + + "" + + "" + + ""; + + for (ProductImpl product : treatment.getProducts()) + { + String routeLabel = TreatmentManager.getInstance().getStudyDesignRouteLabelByName(c, product.getRoute()); + + productHover += "" + + "" + + ""; + } + + productHover += "
    LabelDose and unitsRoute
    " + h(product.getLabel()) + "" + h(product.getDose()) + "" + h(routeLabel != null ? routeLabel : product.getRoute()) + "
    "; + } +%> + + <%=h(treatment != null ? treatment.getLabel() : "")%> + <%=(!productHover.isEmpty() ? helpPopup("Treatment Products", HtmlString.unsafe(productHover), 500) : HtmlString.EMPTY_STRING)%> + +<% + } +%> + +<% + } +%> + + +<% + } + } + else + { + %>

    The folder must contain a study in order to display an immunization schedule.

    <% + } +%> diff --git a/study/src/org/labkey/study/view/studydesign/manageAssaySchedule.jsp b/studydesign/src/org/labkey/studydesign/view/manageAssaySchedule.jsp similarity index 89% rename from study/src/org/labkey/study/view/studydesign/manageAssaySchedule.jsp rename to studydesign/src/org/labkey/studydesign/view/manageAssaySchedule.jsp index 5cd80328..3c2c1ee3 100644 --- a/study/src/org/labkey/study/view/studydesign/manageAssaySchedule.jsp +++ b/studydesign/src/org/labkey/studydesign/view/manageAssaySchedule.jsp @@ -1,125 +1,128 @@ -<% -/* - * 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. - */ -%> -<%@ page import="org.labkey.api.data.Container" %> -<%@ page import="org.labkey.api.portal.ProjectUrls" %> -<%@ page import="org.labkey.api.study.Study" %> -<%@ page import="org.labkey.api.study.TimepointType" %> -<%@ page import="org.labkey.api.view.HttpView" %> -<%@ page import="org.labkey.api.view.JspView" %> -<%@ page import="org.labkey.api.view.NavTree" %> -<%@ page import="org.labkey.api.view.PopupMenuView" %> -<%@ page import="org.labkey.api.view.template.ClientDependencies" %> -<%@ page import="org.labkey.study.controllers.StudyController" %> -<%@ page import="org.labkey.study.controllers.StudyDesignController" %> -<%@ page import="org.labkey.study.model.StudyManager" %> -<%@ page import="org.labkey.study.view.studydesign.StudyDesignConfigureMenuItem" %> -<%@ page extends="org.labkey.api.jsp.JspBase" %> -<%! - @Override - public void addClientDependencies(ClientDependencies dependencies) - { - dependencies.add("study/vaccineDesign/vaccineDesign.lib.xml"); - dependencies.add("study/vaccineDesign/VaccineDesign.css"); - } -%> -<% - JspView me = HttpView.currentView(); - StudyDesignController.AssayScheduleForm form = me.getModelBean(); - - Container c = getContainer(); - Study study = StudyManager.getInstance().getStudy(getContainer()); - - // assay schedule is editable for the individual studies in a Dataspace project - boolean disableEdit = c.isProject() && c.isDataspace(); - - String visitNoun = "Visit"; - if (study != null && study.getTimepointType() == TimepointType.DATE) - visitNoun = "Timepoint"; - - String returnUrl = form.getReturnUrl() != null ? form.getReturnUrl() : urlProvider(ProjectUrls.class).getBeginURL(c).toString(); -%> - - - -Enter assay schedule information in the grids below. -
    -
      -
    • > - Configure dropdown options for assays, labs, sample types, and units at the project - level to be shared across study designs or within this folder for - study specific properties: - -
    • -
    • - Select the <%=h(visitNoun.toLowerCase())%>s for each assay in the schedule - portion of the grid to define the expected assay schedule for the study. -
    • -
    • > - Use the manage locationss page to further configure information about the locations for this study. - <%= link("Manage Locations", StudyController.ManageLocationsAction.class) %> -
    • -
    • - Use the manage <%=h(visitNoun.toLowerCase())%>s page to further configure - information about the <%=h(visitNoun.toLowerCase())%>s for this study or to change - the <%=h(visitNoun.toLowerCase())%> display order. - <%=link("Manage " + visitNoun + "s", StudyController.ManageVisitsAction.class)%> -
    • -
    -
    -
    -
    +<% +/* + * 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. + */ +%> +<%@ page import="org.labkey.api.data.Container" %> +<%@ page import="org.labkey.api.portal.ProjectUrls" %> +<%@ page import="org.labkey.api.study.Study" %> +<%@ page import="org.labkey.api.study.StudyService" %> +<%@ page import="org.labkey.api.study.StudyUrls" %> +<%@ page import="org.labkey.api.study.TimepointType" %> +<%@ page import="org.labkey.api.util.PageFlowUtil" %> +<%@ page import="org.labkey.api.view.HttpView" %> +<%@ page import="org.labkey.api.view.JspView" %> +<%@ page import="org.labkey.api.view.NavTree" %> +<%@ page import="org.labkey.api.view.PopupMenuView" %> +<%@ page import="org.labkey.api.view.template.ClientDependencies" %> +<%@ page import="org.labkey.studydesign.StudyDesignController" %> +<%@ page import="org.labkey.studydesign.view.StudyDesignConfigureMenuItem" %> +<%@ page import="java.lang.Override" %> +<%@ page import="java.lang.String" %> +<%@ page extends="org.labkey.api.jsp.JspBase" %> +<%! + @Override + public void addClientDependencies(ClientDependencies dependencies) + { + dependencies.add("study/vaccineDesign/vaccineDesign.lib.xml"); + dependencies.add("study/vaccineDesign/VaccineDesign.css"); + } +%> +<% + JspView me = HttpView.currentView(); + StudyDesignController.AssayScheduleForm form = me.getModelBean(); + + Container c = getContainer(); + Study study = StudyService.get().getStudy(getContainer()); + + // assay schedule is editable for the individual studies in a Dataspace project + boolean disableEdit = c.isProject() && c.isDataspace(); + + String visitNoun = "Visit"; + if (study != null && study.getTimepointType() == TimepointType.DATE) + visitNoun = "Timepoint"; + + String returnUrl = form.getReturnUrl() != null ? form.getReturnUrl() : urlProvider(ProjectUrls.class).getBeginURL(c).toString(); +%> + + + +Enter assay schedule information in the grids below. +
    +
      +
    • > + Configure dropdown options for assays, labs, sample types, and units at the project + level to be shared across study designs or within this folder for + study specific properties: + +
    • +
    • + Select the <%=h(visitNoun.toLowerCase())%>s for each assay in the schedule + portion of the grid to define the expected assay schedule for the study. +
    • +
    • > + Use the manage locationss page to further configure information about the locations for this study. + <%= link("Manage Locations", PageFlowUtil.urlProvider(StudyUrls.class).getManageLocationsURL(getContainer())) %> +
    • +
    • + Use the manage <%=h(visitNoun.toLowerCase())%>s page to further configure + information about the <%=h(visitNoun.toLowerCase())%>s for this study or to change + the <%=h(visitNoun.toLowerCase())%> display order. + <%=link("Manage " + visitNoun + "s", PageFlowUtil.urlProvider(StudyUrls.class).getManageVisitsURL(getContainer()))%> +
    • +
    +
    +
    +
    diff --git a/study/src/org/labkey/study/view/studydesign/manageStudyProducts.jsp b/studydesign/src/org/labkey/studydesign/view/manageStudyProducts.jsp similarity index 93% rename from study/src/org/labkey/study/view/studydesign/manageStudyProducts.jsp rename to studydesign/src/org/labkey/studydesign/view/manageStudyProducts.jsp index 8a8f9f45..d88d0739 100644 --- a/study/src/org/labkey/study/view/studydesign/manageStudyProducts.jsp +++ b/studydesign/src/org/labkey/studydesign/view/manageStudyProducts.jsp @@ -1,135 +1,135 @@ -<% -/* - * Copyright (c) 2013-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. - */ -%> -<%@ page import="org.labkey.api.action.ReturnUrlForm" %> -<%@ page import="org.labkey.api.data.Container" %> -<%@ page import="org.labkey.api.portal.ProjectUrls" %> -<%@ page import="org.labkey.api.study.StudyUrls" %> -<%@ page import="org.labkey.api.view.ActionURL" %> -<%@ page import="org.labkey.api.view.HttpView" %> -<%@ page import="org.labkey.api.view.JspView" %> -<%@ page import="org.labkey.api.view.NavTree" %> -<%@ page import="org.labkey.api.view.PopupMenuView" %> -<%@ page import="org.labkey.api.view.template.ClientDependencies" %> -<%@ page import="org.labkey.study.controllers.StudyDesignController" %> -<%@ page import="org.labkey.study.view.studydesign.StudyDesignConfigureMenuItem" %> -<%@ page extends="org.labkey.api.jsp.JspBase" %> -<%! - @Override - public void addClientDependencies(ClientDependencies dependencies) - { - dependencies.add("study/vaccineDesign/vaccineDesign.lib.xml"); - dependencies.add("study/vaccineDesign/VaccineDesign.css"); - } -%> -<% - JspView me = HttpView.currentView(); - ReturnUrlForm bean = me.getModelBean(); - Container c = getContainer(); - - // study products are editable at the project level for Dataspace projects - boolean isDataspaceProject = c.getProject() != null && c.getProject().isDataspace() && !c.isDataspace(); - - String returnUrl = bean.getReturnUrl() != null ? bean.getReturnUrl() : urlProvider(ProjectUrls.class).getBeginURL(c).toString(); -%> - - - -<% -if (isDataspaceProject) -{ - ActionURL projectManageProductsURL = new ActionURL(StudyDesignController.ManageStudyProductsAction.class, getContainer().getProject()); - projectManageProductsURL.addReturnUrl(getActionURL()); -%> -Vaccine design information is defined at the project level for Dataspace projects. The grids below are read-only. -
    -
      -
    • - Use the manage study products page at the project level to make changes to the information listed below. - <%=link("Manage Study Products", projectManageProductsURL)%> -
    • -<% -} -else -{ -%> -Enter vaccine design information in the grids below. -
      -
        -
      • Each immunogen, adjuvant and challenge in the study should be listed on one row of the grids below.
      • -
      • Immunogens, adjuvants and challenges should have unique labels.
      • -
      • If possible, the immunogen description should include specific sequences of HIV Antigens included in the immunogen.
      • -
      • - Use the manage treatments page to describe the schedule of treatments and combinations of study products administered at each timepoint. - <% - ActionURL manageTreatmentsURL = urlProvider(StudyUrls.class).getManageTreatmentsURL(c, c.hasActiveModuleByName("viscstudies")); - manageTreatmentsURL.addReturnUrl(getActionURL()); - %> - <%=link("Manage Treatments", manageTreatmentsURL)%> -
      • -<% -} -%> -
      • - Configure dropdown options for challenge types, immunogen types, genes, subtypes and routes at the project level to be shared across study designs or within this folder for - study specific properties: - -
      • -
      -
      - -
      +<% +/* + * Copyright (c) 2013-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. + */ +%> +<%@ page import="org.labkey.api.action.ReturnUrlForm" %> +<%@ page import="org.labkey.api.data.Container" %> +<%@ page import="org.labkey.api.portal.ProjectUrls" %> +<%@ page import="org.labkey.api.studydesign.StudyDesignUrls" %> +<%@ page import="org.labkey.api.view.ActionURL" %> +<%@ page import="org.labkey.api.view.HttpView" %> +<%@ page import="org.labkey.api.view.JspView" %> +<%@ page import="org.labkey.api.view.NavTree" %> +<%@ page import="org.labkey.api.view.PopupMenuView" %> +<%@ page import="org.labkey.api.view.template.ClientDependencies" %> +<%@ page import="org.labkey.studydesign.view.StudyDesignConfigureMenuItem" %> +<%@ page import="org.labkey.studydesign.StudyDesignController" %> +<%@ page extends="org.labkey.api.jsp.JspBase" %> +<%! + @Override + public void addClientDependencies(ClientDependencies dependencies) + { + dependencies.add("study/vaccineDesign/vaccineDesign.lib.xml"); + dependencies.add("study/vaccineDesign/VaccineDesign.css"); + } +%> +<% + JspView me = HttpView.currentView(); + ReturnUrlForm bean = me.getModelBean(); + Container c = getContainer(); + + // study products are editable at the project level for Dataspace projects + boolean isDataspaceProject = c.getProject() != null && c.getProject().isDataspace() && !c.isDataspace(); + + String returnUrl = bean.getReturnUrl() != null ? bean.getReturnUrl() : urlProvider(ProjectUrls.class).getBeginURL(c).toString(); +%> + + + +<% +if (isDataspaceProject) +{ + ActionURL projectManageProductsURL = new ActionURL(StudyDesignController.ManageStudyProductsAction.class, getContainer().getProject()); + projectManageProductsURL.addReturnUrl(getActionURL()); +%> +Vaccine design information is defined at the project level for Dataspace projects. The grids below are read-only. +
      +
        +
      • + Use the manage study products page at the project level to make changes to the information listed below. + <%=link("Manage Study Products", projectManageProductsURL)%> +
      • +<% +} +else +{ +%> +Enter vaccine design information in the grids below. +
        +
          +
        • Each immunogen, adjuvant and challenge in the study should be listed on one row of the grids below.
        • +
        • Immunogens, adjuvants and challenges should have unique labels.
        • +
        • If possible, the immunogen description should include specific sequences of HIV Antigens included in the immunogen.
        • +
        • + Use the manage treatments page to describe the schedule of treatments and combinations of study products administered at each timepoint. + <% + ActionURL manageTreatmentsURL = urlProvider(StudyDesignUrls.class).getManageTreatmentsURL(c, c.hasActiveModuleByName("viscstudies")); + manageTreatmentsURL.addReturnUrl(getActionURL()); + %> + <%=link("Manage Treatments", manageTreatmentsURL)%> +
        • +<% +} +%> +
        • + Configure dropdown options for challenge types, immunogen types, genes, subtypes and routes at the project level to be shared across study designs or within this folder for + study specific properties: + +
        • +
        +
        + +
        diff --git a/study/src/org/labkey/study/view/studydesign/manageTreatments.jsp b/studydesign/src/org/labkey/studydesign/view/manageTreatments.jsp similarity index 87% rename from study/src/org/labkey/study/view/studydesign/manageTreatments.jsp rename to studydesign/src/org/labkey/studydesign/view/manageTreatments.jsp index 4b77ed12..b92b00f9 100644 --- a/study/src/org/labkey/study/view/studydesign/manageTreatments.jsp +++ b/studydesign/src/org/labkey/studydesign/view/manageTreatments.jsp @@ -1,167 +1,169 @@ -<% -/* - * 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. - */ -%> -<%@ page import="org.labkey.api.data.Container" %> -<%@ page import="org.labkey.api.portal.ProjectUrls" %> -<%@ page import="org.labkey.api.security.User" %> -<%@ page import="org.labkey.api.study.Study" %> -<%@ page import="org.labkey.api.study.TimepointType" %> -<%@ page import="org.labkey.api.study.Visit" %> -<%@ page import="org.labkey.api.study.security.permissions.ManageStudyPermission" %> -<%@ page import="org.labkey.api.view.ActionURL" %> -<%@ page import="org.labkey.api.view.HttpView" %> -<%@ page import="org.labkey.api.view.JspView" %> -<%@ page import="org.labkey.api.view.template.ClientDependencies" %> -<%@ page import="org.labkey.study.controllers.CohortController" %> -<%@ page import="org.labkey.study.controllers.StudyController" %> -<%@ page import="org.labkey.study.controllers.StudyDesignController" %> -<%@ page import="org.labkey.study.model.StudyManager" %> -<%@ page extends="org.labkey.api.jsp.JspBase" %> -<%! - @Override - public void addClientDependencies(ClientDependencies dependencies) - { - dependencies.add("study/vaccineDesign/vaccineDesign.lib.xml"); - dependencies.add("study/vaccineDesign/VaccineDesign.css"); - } -%> -<% - JspView me = HttpView.currentView(); - StudyDesignController.ManageTreatmentsBean bean = me.getModelBean(); - - Container c = getContainer(); - User user = getUser(); - - Study study = StudyManager.getInstance().getStudy(c); - boolean canManageStudy = c.hasPermission(user, ManageStudyPermission.class); - - // treatment schedule is editable for the individual studies in a Dataspace project - boolean isDataspaceProject = c.isProject() && c.isDataspace(); - - String visitNoun = "Visit"; - if (study != null && study.getTimepointType() == TimepointType.DATE) - visitNoun = "Timepoint"; - - String subjectNoun = "Subject"; - if (study != null) - subjectNoun = study.getSubjectNounSingular(); - - String returnUrl = bean.getReturnUrl() != null ? bean.getReturnUrl() : urlProvider(ProjectUrls.class).getBeginURL(c).toString(); -%> - - - -<% -if (isDataspaceProject) -{ -%> -Treatment information is defined at the individual study level for Dataspace projects. The grids below are read-only. -

        -<% -} -else -{ -%> -Enter treatment information in the grids below. -
        -
          - <% - if (bean.isSingleTable()) - { - %> -
        • - Click on time point to define its treatment products by selecting a combination of study products. -
        • - <% - } - else - { - %> -
        • - Each treatment label must be unique and must consist of at least one study products. -
        • - <% - } - %> -
        • - Use the manage study products page to change or update the set of available values. - <% - ActionURL manageStudyProductsURL = new ActionURL(StudyDesignController.ManageStudyProductsAction.class, getContainer()); - manageStudyProductsURL.addReturnUrl(getActionURL()); - %> - <%=link("Manage Study Products", manageStudyProductsURL)%> -
        • -
        • - Each cohort label must be unique. Enter the number of <%=h(study.getSubjectNounPlural().toLowerCase())%> for - the cohort in the count column.
        • -
        • - Use the manage cohorts page to further configuration information about the cohorts for this study. - <%=link("Manage Cohorts", CohortController.ManageCohortsAction.class)%> -
        • -
        • - Use the manage <%=h(visitNoun.toLowerCase())%>s page to further configure - information about the <%=h(visitNoun.toLowerCase())%>s for this study or to change - the <%=h(visitNoun.toLowerCase())%> display order. - <%=link("Manage " + visitNoun + "s", StudyController.ManageVisitsAction.class)%> -
        • -<% - if (canManageStudy) - { - if (study != null && study.getTimepointType() == TimepointType.VISIT && study.getVisits(Visit.Order.DISPLAY).size() > 1) - { -%> -
        • Use the change visit order page to adjust the display order of visits in the treatment schedule table. - <%= link("Change Visit Order", new ActionURL(StudyController.VisitOrderAction.class, c).addReturnUrl(getActionURL())) %> -
        • -<% - } - } -%> -
        -
        -<% -} -%> - +<% +/* + * 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. + */ +%> +<%@ page import="org.labkey.api.data.Container" %> +<%@ page import="org.labkey.api.portal.ProjectUrls" %> +<%@ page import="org.labkey.api.security.User" %> +<%@ page import="org.labkey.api.study.Study" %> +<%@ page import="org.labkey.api.study.StudyService" %> +<%@ page import="org.labkey.api.study.StudyUrls" %> +<%@ page import="org.labkey.api.study.TimepointType" %> +<%@ page import="org.labkey.api.study.Visit" %> +<%@ page import="org.labkey.api.study.security.permissions.ManageStudyPermission" %> +<%@ page import="org.labkey.api.util.PageFlowUtil" %> +<%@ page import="org.labkey.api.view.ActionURL" %> +<%@ page import="org.labkey.api.view.HttpView" %> +<%@ page import="org.labkey.api.view.JspView" %> +<%@ page import="org.labkey.api.view.template.ClientDependencies" %> +<%@ page import="java.lang.Override" %> +<%@ page import="java.lang.String" %> +<%@ page import="org.labkey.studydesign.StudyDesignController" %> +<%@ page extends="org.labkey.api.jsp.JspBase" %> +<%! + @Override + public void addClientDependencies(ClientDependencies dependencies) + { + dependencies.add("study/vaccineDesign/vaccineDesign.lib.xml"); + dependencies.add("study/vaccineDesign/VaccineDesign.css"); + } +%> +<% + JspView me = HttpView.currentView(); + StudyDesignController.ManageTreatmentsBean bean = me.getModelBean(); + + Container c = getContainer(); + User user = getUser(); + + Study study = StudyService.get().getStudy(c); + boolean canManageStudy = c.hasPermission(user, ManageStudyPermission.class); + + // treatment schedule is editable for the individual studies in a Dataspace project + boolean isDataspaceProject = c.isProject() && c.isDataspace(); + + String visitNoun = "Visit"; + if (study != null && study.getTimepointType() == TimepointType.DATE) + visitNoun = "Timepoint"; + + String subjectNoun = "Subject"; + if (study != null) + subjectNoun = study.getSubjectNounSingular(); + + String returnUrl = bean.getReturnUrl() != null ? bean.getReturnUrl() : urlProvider(ProjectUrls.class).getBeginURL(c).toString(); +%> + + + +<% +if (isDataspaceProject) +{ +%> +Treatment information is defined at the individual study level for Dataspace projects. The grids below are read-only. +

        +<% +} +else +{ +%> +Enter treatment information in the grids below. +
        +
          + <% + if (bean.isSingleTable()) + { + %> +
        • + Click on time point to define its treatment products by selecting a combination of study products. +
        • + <% + } + else + { + %> +
        • + Each treatment label must be unique and must consist of at least one study products. +
        • + <% + } + %> +
        • + Use the manage study products page to change or update the set of available values. + <% + ActionURL manageStudyProductsURL = new ActionURL(StudyDesignController.ManageStudyProductsAction.class, getContainer()); + manageStudyProductsURL.addReturnUrl(getActionURL()); + %> + <%=link("Manage Study Products", manageStudyProductsURL)%> +
        • +
        • + Each cohort label must be unique. Enter the number of <%=h(study.getSubjectNounPlural().toLowerCase())%> for + the cohort in the count column.
        • +
        • + Use the manage cohorts page to further configuration information about the cohorts for this study. + <%=link("Manage Cohorts", PageFlowUtil.urlProvider(StudyUrls.class).getManageCohortsURL(getContainer()))%> +
        • +
        • + Use the manage <%=h(visitNoun.toLowerCase())%>s page to further configure + information about the <%=h(visitNoun.toLowerCase())%>s for this study or to change + the <%=h(visitNoun.toLowerCase())%> display order. + <%=link("Manage " + visitNoun + "s", PageFlowUtil.urlProvider(StudyUrls.class).getManageVisitsURL(getContainer()))%> +
        • +<% + if (canManageStudy) + { + if (study != null && study.getTimepointType() == TimepointType.VISIT && study.getVisits(Visit.Order.DISPLAY).size() > 1) + { +%> +
        • Use the change visit order page to adjust the display order of visits in the treatment schedule table. + <%= link("Change Visit Order", PageFlowUtil.urlProvider(StudyUrls.class).getVisitOrderURL(c).addReturnUrl(getActionURL())) %> +
        • +<% + } + } +%> +
        +
        +<% +} +%> +
        \ No newline at end of file diff --git a/study/src/org/labkey/study/view/studydesign/vaccineDesignWebpart.jsp b/studydesign/src/org/labkey/studydesign/view/vaccineDesignWebpart.jsp similarity index 86% rename from study/src/org/labkey/study/view/studydesign/vaccineDesignWebpart.jsp rename to studydesign/src/org/labkey/studydesign/view/vaccineDesignWebpart.jsp index 8af307fa..b4deb509 100644 --- a/study/src/org/labkey/study/view/studydesign/vaccineDesignWebpart.jsp +++ b/studydesign/src/org/labkey/studydesign/view/vaccineDesignWebpart.jsp @@ -1,89 +1,89 @@ -<% -/* - * Copyright (c) 2013-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. - */ -%> -<%@ page import="org.labkey.api.data.Container" %> -<%@ page import="org.labkey.api.security.User" %> -<%@ page import="org.labkey.api.security.permissions.UpdatePermission" %> -<%@ page import="org.labkey.api.view.ActionURL" %> -<%@ page import="org.labkey.api.view.template.ClientDependencies" %> -<%@ page import="org.labkey.study.controllers.StudyDesignController" %> -<%@ page import="org.labkey.study.model.StudyImpl" %> -<%@ page import="org.labkey.study.model.StudyManager" %> -<%@ page extends="org.labkey.study.view.BaseStudyPage" %> -<%! - @Override - public void addClientDependencies(ClientDependencies dependencies) - { - dependencies.add("study/vaccineDesign/vaccineDesign.lib.xml"); - dependencies.add("study/vaccineDesign/VaccineDesign.css"); - } -%> -<% - User user = getUser(); - Container container = getContainer(); - StudyImpl study = StudyManager.getInstance().getStudy(container); - - if (study != null) - { - %>This section describes the study products evaluated in the study.
        <% - if (container.hasPermission(user, UpdatePermission.class)) - { - ActionURL editUrl = new ActionURL(StudyDesignController.ManageStudyProductsAction.class, getContainer()); - editUrl.addReturnUrl(getActionURL()); -%> - <%=link("Manage Study Products", editUrl)%>
        -<% - } -%> -
        -
        -
        -
        -
        -
        -<% - } - else - { -%> -

        The folder must contain a study in order to display a vaccine design.

        -<% - } -%> - - \ No newline at end of file diff --git a/study/webapp/study/vaccineDesign/AssaySchedule.js b/studydesign/webapp/study/vaccineDesign/AssaySchedule.js similarity index 97% rename from study/webapp/study/vaccineDesign/AssaySchedule.js rename to studydesign/webapp/study/vaccineDesign/AssaySchedule.js index a4f2f50d..48c9c968 100644 --- a/study/webapp/study/vaccineDesign/AssaySchedule.js +++ b/studydesign/webapp/study/vaccineDesign/AssaySchedule.js @@ -1,659 +1,659 @@ -/* - * Copyright (c) 2016-2017 LabKey Corporation - * - * Licensed under the Apache License, Version 2.0: http://www.apache.org/licenses/LICENSE-2.0 - */ -Ext4.define('LABKEY.VaccineDesign.AssaySchedulePanel', { - extend : 'Ext.panel.Panel', - - width: 750, - - border : false, - - bodyStyle : 'background-color: transparent;', - - disableEdit : true, - - dirty : false, - - returnUrl : null, - - initComponent : function() - { - this.items = [this.getAssaysGrid()]; - - this.callParent(); - - this.queryAssayPlan(); - - window.onbeforeunload = LABKEY.beforeunload(this.beforeUnload, this); - }, - - getAssaysGrid : function() - { - if (!this.assaysGrid) - { - this.assaysGrid = Ext4.create('LABKEY.VaccineDesign.AssaysGrid', { - disableEdit: this.disableEdit, - visitNoun: this.visitNoun, - useAlternateLookupFields: this.useAlternateLookupFields - }); - - this.assaysGrid.on('dirtychange', this.enableSaveButton, this); - this.assaysGrid.on('celledited', this.enableSaveButton, this); - } - - return this.assaysGrid; - }, - - queryAssayPlan : function() - { - // query the StudyProperties table for the initial assay plan value - LABKEY.Query.selectRows({ - schemaName: 'study', - queryName: 'StudyProperties', - columns: 'AssayPlan', - scope: this, - success: function(data) - { - var text = (data.rows.length == 1) ? data.rows[0]["AssayPlan"] : ''; - - this.add(this.getAssayPlanPanel(text)); - this.add(this.getButtonBar()); - } - }); - - }, - - getAssayPlanPanel : function(initValue) - { - if (!this.assayPlanPanel) - { - this.assayPlanPanel = Ext4.create('Ext.form.Panel', { - cls: 'study-vaccine-design', - padding: '20px 0', - border: false, - items: [ - Ext4.create('Ext.Component', { - html: '
        Assay Plan
        ' - }), - this.getAssayPlanTextArea(initValue) - ] - }); - } - - return this.assayPlanPanel; - }, - - getAssayPlanTextArea : function(initValue) - { - if (!this.assayPlanTextArea) - { - this.assayPlanTextArea = Ext4.create('Ext.form.field.TextArea', { - name: 'assayPlan', - readOnly: this.disableEdit, - value: initValue, - width: 500, - height: 100 - }); - - this.assayPlanTextArea.on('change', this.enableSaveButton, this, {buffer: 500}); - } - - return this.assayPlanTextArea; - }, - - getButtonBar : function() - { - if (!this.buttonBar) - { - this.buttonBar = Ext4.create('Ext.toolbar.Toolbar', { - dock: 'bottom', - ui: 'footer', - padding: 0, - style : 'background-color: transparent;', - defaults: {width: 75}, - items: [this.getSaveButton(), this.getCancelButton()] - }); - } - - return this.buttonBar; - }, - - getSaveButton : function() - { - if (!this.saveButton) - { - this.saveButton = Ext4.create('Ext.button.Button', { - text: 'Save', - disabled: true, - hidden: this.disableEdit, - handler: this.saveAssaySchedule, - scope: this - }); - } - - return this.saveButton; - }, - - enableSaveButton : function() - { - this.setDirty(true); - this.getSaveButton().enable(); - }, - - getCancelButton : function() - { - if (!this.cancelButton) - { - this.cancelButton = Ext4.create('Ext.button.Button', { - text: this.disableEdit ? 'Done' : 'Cancel', - handler: this.goToReturnURL, - scope: this - }); - } - - return this.cancelButton; - }, - - saveAssaySchedule : function() - { - this.getEl().mask('Saving...'); - - var assays = [], errorMsg = []; - Ext4.each(this.getAssaysGrid().getStore().getRange(), function(record) - { - var recData = Ext4.clone(record.data); - - // drop any empty treatment rows that were just added - var hasData = LABKEY.VaccineDesign.Utils.modelHasData(recData, LABKEY.VaccineDesign.Assay.getFields()); - if (hasData) - { - var sampleQuantity = Number(recData['SampleQuantity']); - if (isNaN(sampleQuantity) || sampleQuantity < 0) - errorMsg.push('Assay sample quantity value must be a positive number: ' + recData['SampleQuantity'] + '.'); - else - assays.push(recData); - } - }, this); - - if (errorMsg.length > 0) - { - this.onFailure(errorMsg.join('
        ')); - return; - } - - LABKEY.Ajax.request({ - url : LABKEY.ActionURL.buildURL('study-design', 'updateAssaySchedule.api'), - method : 'POST', - jsonData: { - assays: assays, - assayPlan: this.getAssayPlanTextArea().getValue() - }, - scope: this, - success: function(response) - { - var resp = Ext4.decode(response.responseText); - if (resp.success) - this.goToReturnURL(); - else - this.onFailure(); - }, - failure: function(response) - { - var resp = Ext4.decode(response.responseText); - if (resp.errors) - this.onFailure(Ext4.Array.pluck(resp.errors, 'message').join('
        ')); - else - this.onFailure(resp.exception); - } - }); - }, - - goToReturnURL : function() - { - this.setDirty(false); - window.location = this.returnUrl; - }, - - onFailure : function(text) - { - Ext4.Msg.show({ - title: 'Error', - msg: text || 'Unknown error occurred.', - icon: Ext4.Msg.ERROR, - buttons: Ext4.Msg.OK - }); - - this.getEl().unmask(); - }, - - setDirty : function(dirty) - { - this.dirty = dirty; - LABKEY.Utils.signalWebDriverTest("treatmentScheduleDirty", dirty); - }, - - isDirty : function() - { - return this.dirty; - }, - - beforeUnload : function() - { - if (!this.disableEdit && this.isDirty()) - return 'Please save your changes.'; - } -}); - -Ext4.define('LABKEY.VaccineDesign.AssaysGrid', { - extend : 'LABKEY.VaccineDesign.BaseDataViewAddVisit', - - cls : 'study-vaccine-design vaccine-design-assays', - - mainTitle : 'Assay Schedule', - - width : 620, - - studyDesignQueryNames : ['StudyDesignAssays', 'StudyDesignLabs', 'StudyDesignSampleTypes', 'StudyDesignUnits', 'Location', 'DataSets'], - - visitNoun : 'Visit', - - useAlternateLookupFields : false, - - //Override - getStore : function() - { - if (!this.store) - { - this.store = Ext4.create('Ext.data.Store', { - storeId : 'AssaysGridStore', - pageSize : 100000, // need to explicitly set otherwise it defaults to 25 - proxy: { - type: 'ajax', - url : LABKEY.ActionURL.buildURL('query', 'selectRows.api', null, { - 'schemaName' : 'study', - 'query.queryName' : 'assayspecimen' - }), - reader: { - type: 'json', - root: 'rows' - } - }, - sorters: [{ property: 'AssayName', direction: 'ASC' }], - autoLoad: true, - listeners: { - scope: this, - load: this.getVisitStore - } - }); - } - - return this.store; - }, - - getVisitStore : function() - { - if (!this.visitStore) - { - this.visitStore = Ext4.create('Ext.data.Store', { - model : 'LABKEY.VaccineDesign.Visit', - pageSize : 100000, // need to explicitly set otherwise it defaults to 25 - proxy: { - type: 'ajax', - url : LABKEY.ActionURL.buildURL('query', 'selectRows.api', null, { - 'schemaName' : 'study', - 'query.queryName' : 'visit' - }), - reader: { - type: 'json', - root: 'rows' - } - }, - sorters: [{ property: 'DisplayOrder', direction: 'ASC' }, { property: 'SequenceNumMin', direction: 'ASC' }], - autoLoad: true, - listeners: { - scope: this, - load: this.getAssaySpecimenVisitStore - } - }); - } - - return this.visitStore; - }, - - getAssaySpecimenVisitStore : function() - { - if (!this.assaySpecimenVisitStore) - { - this.assaySpecimenVisitStore = Ext4.create('Ext4.data.Store', { - storeId : 'AssaySpecimenVisitStore', - model : 'LABKEY.VaccineDesign.AssaySpecimenVisit', - pageSize : 100000, // need to explicitly set otherwise it defaults to 25 - proxy: { - type: 'ajax', - url : LABKEY.ActionURL.buildURL('query', 'selectRows.api', null, { - 'schemaName' : 'study', - 'query.queryName' : 'AssaySpecimenVisit' - }), - reader: { - type: 'json', - root: 'rows' - } - }, - autoLoad: true, - listeners: { - scope: this, - load: function (store, records) - { - var includedVisits = []; - - // stash the visit mapping information attached to each record in the assay store - Ext4.each(records, function(record) - { - var assayRecord = this.getStore().findRecord('RowId', record.get('AssaySpecimenId')); - if (assayRecord != null) - { - var visitMap = assayRecord.get('VisitMap') || []; - visitMap.push(Ext4.clone(record.data)); - assayRecord.set('VisitMap', visitMap); - - includedVisits.push(record.get('VisitId')); - } - }, this); - - var includedVisits = Ext4.Array.unique(includedVisits); - Ext4.each(this.getVisitStore().getRange(), function(visit) - { - var included = includedVisits.indexOf(visit.get('RowId')) > -1; - visit.set('Included', included); - }, this); - - this.add(this.getDataView()); - this.fireEvent('loadcomplete', this); - } - } - }); - } - - return this.assaySpecimenVisitStore; - }, - - //Override - loadDataViewStore : function() - { - // just call getStore here to initial the load, we will add the DataView - // and fire the loadcomplete event after all of the stores for this page are done loading - this.getStore(); - }, - - columnHasData : function(dataIndex) - { - var recordsDataArr = Ext4.Array.pluck(this.getStore().getRange(), 'data'), - colDataArr = Ext4.Array.pluck(recordsDataArr, dataIndex); - - for (var i = 0; i < colDataArr.length; i++) - { - if ((Ext4.isNumber(colDataArr[i]) && colDataArr[i] > 0) || (Ext4.isString(colDataArr[i]) && colDataArr[i] != '')) - return true; - } - - return false; - }, - - //Override - getColumnConfigs : function() - { - if (!this.columnConfigs) - { - var width = 0; // add to the running width as we go through which columns to show in the config - var columnConfigs = []; - - var assayNameEditorConfig = LABKEY.VaccineDesign.Utils.getStudyDesignComboConfig('AssayName', 185, 'StudyDesignAssays'); - assayNameEditorConfig.editable = true; // Rho use-case - - columnConfigs.push({ - label: 'Assay Name', - width: 200, - dataIndex: 'AssayName', - required: true, - editorType: 'LABKEY.ext4.ComboBox', - editorConfig: assayNameEditorConfig - }); - width += 200; - - columnConfigs.push({ - label: 'Dataset', - width: 200, - dataIndex: 'DataSet', - queryName: 'DataSets', - editorType: 'LABKEY.ext4.ComboBox', - editorConfig: LABKEY.VaccineDesign.Utils.getStudyDesignComboConfig('DataSet', 185, 'DataSets', undefined, 'Label', 'DataSetId') - }); - width += 200; - - var hidden = this.disableEdit && !this.columnHasData('Description'); - columnConfigs.push({ - label: 'Description', - width: 200, - hidden: hidden, - dataIndex: 'Description', - editorType: 'Ext.form.field.Text', - editorConfig: LABKEY.VaccineDesign.Utils.getStudyDesignTextConfig('Description', 185) - }); - if (!hidden) { - width += 200; - } - - if (this.useAlternateLookupFields) - { - hidden = this.disableEdit && !this.columnHasData('Source'); - columnConfigs.push({ - label: 'Source', - width: 60, - hidden: hidden, - dataIndex: 'Source', - editorType: 'Ext.form.field.Text', - editorConfig: LABKEY.VaccineDesign.Utils.getStudyDesignTextConfig('Source', 45) - }); - if (!hidden) { - width += 60; - } - - hidden = this.disableEdit && !this.columnHasData('LocationId'); - columnConfigs.push({ - label: 'Location', - width: 140, - hidden: hidden, - dataIndex: 'LocationId', - queryName: 'Location', - editorType: 'LABKEY.ext4.ComboBox', - editorConfig: LABKEY.VaccineDesign.Utils.getStudyDesignComboConfig('LocationId', 125, 'Location', undefined, 'Label', 'RowId') - }); - if (!hidden) { - width += 140; - } - - hidden = this.disableEdit && !this.columnHasData('TubeType'); - columnConfigs.push({ - label: 'TubeType', - width: 200, - hidden: hidden, - dataIndex: 'TubeType', - editorType: 'Ext.form.field.Text', - editorConfig: LABKEY.VaccineDesign.Utils.getStudyDesignTextConfig('TubeType', 185) - }); - if (!hidden) { - width += 200; - } - } - else - { - hidden = this.disableEdit && !this.columnHasData('Lab'); - columnConfigs.push({ - label: 'Lab', - width: 140, - hidden: hidden, - dataIndex: 'Lab', - queryName: 'StudyDesignLabs', - editorType: 'LABKEY.ext4.ComboBox', - editorConfig: LABKEY.VaccineDesign.Utils.getStudyDesignComboConfig('Lab', 125, 'StudyDesignLabs') - }); - if (!hidden) { - width += 140; - } - - hidden = this.disableEdit && !this.columnHasData('SampleType'); - columnConfigs.push({ - label: 'Sample Type', - width: 140, - hidden: hidden, - dataIndex: 'SampleType', - editorType: 'LABKEY.ext4.ComboBox', - editorConfig: LABKEY.VaccineDesign.Utils.getStudyDesignComboConfig('SampleType', 125, 'StudyDesignSampleTypes', undefined, 'Name') - }); - if (!hidden) { - width += 140; - } - - hidden = this.disableEdit && !this.columnHasData('SampleQuantity'); - columnConfigs.push({ - label: 'Sample Quantity', - width: 140, - hidden: hidden, - dataIndex: 'SampleQuantity', - editorType: 'Ext.form.field.Number', - editorConfig: LABKEY.VaccineDesign.Utils.getStudyDesignNumberConfig('SampleQuantity', 125, 2) - }); - if (!hidden) { - width += 140; - } - - hidden = this.disableEdit && !this.columnHasData('SampleUnits'); - columnConfigs.push({ - label: 'Sample Units', - width: 140, - hidden: hidden, - dataIndex: 'SampleUnits', - queryName: 'StudyDesignUnits', - editorType: 'LABKEY.ext4.ComboBox', - editorConfig: LABKEY.VaccineDesign.Utils.getStudyDesignComboConfig('SampleUnits', 125, 'StudyDesignUnits') - }); - if (!hidden) { - width += 140; - } - } - - var visitConfigs = this.getVisitColumnConfigs(); - - // update the width based on the number of visit columns - width += (Math.max(2, visitConfigs.length) * 75); - this.setWidth(width); - - // update the outer panel width if necessary - var outerPanel = this.up('panel'); - if (outerPanel != null) - outerPanel.setWidth(Math.max(width, 750)); - - this.columnConfigs = columnConfigs.concat(visitConfigs); - } - - return this.columnConfigs; - }, - - getVisitColumnConfigs : function() - { - var visitConfigs = []; - - Ext4.each(this.getVisitStore().getRange(), function(visit) - { - if (visit.get('Included')) - { - visitConfigs.push({ - label: visit.get('Label') || visit.get('SequenceNumMin'), - width: 75, - dataIndex: 'VisitMap', - dataIndexArrFilterProp: 'VisitId', - dataIndexArrFilterValue: visit.get('RowId'), - editorType: 'Ext.form.field.Checkbox', - editorConfig: { - hideFieldLabel: true, - name: 'VisitMap' - } - }); - } - }, this); - - if (visitConfigs.length == 0 && !this.disableEdit) - { - visitConfigs.push({ - label: 'No ' + this.visitNoun + 's Defined', - displayValue: '', - width: 160 - }); - } - - return visitConfigs; - }, - - //Override - getCurrentCellValue : function(column, record, dataIndex, outerDataIndex, subgridIndex) - { - var value = this.callParent([column, record, dataIndex, outerDataIndex, subgridIndex]); - - if (dataIndex == 'VisitMap' && Ext4.isArray(value)) - { - var matchingIndex = LABKEY.VaccineDesign.Utils.getMatchingRowIndexFromArray(value, column.dataIndexArrFilterProp, column.dataIndexArrFilterValue); - return matchingIndex > -1; - } - else if ((dataIndex == 'SampleQuantity' || dataIndex == 'LocationId') || dataIndex == 'DataSet' && value == 0) - { - return null; - } - - return value; - }, - - //Override - updateStoreRecordValue : function(record, column, newValue, field) - { - // special case for editing the value of one of the pivot visit columns - if (column.dataIndex == 'VisitMap') - { - var visitMapArr = record.get(column.dataIndex); - if (!Ext4.isArray(visitMapArr)) - { - visitMapArr = []; - record.set(column.dataIndex, visitMapArr); - } - - var matchingIndex = LABKEY.VaccineDesign.Utils.getMatchingRowIndexFromArray(visitMapArr, column.dataIndexArrFilterProp, column.dataIndexArrFilterValue); - - if (newValue) - visitMapArr.push({VisitId: column.dataIndexArrFilterValue}); - else - Ext4.Array.splice(visitMapArr, matchingIndex, 1); - - this.fireEvent('celledited', this, 'VisitMap', visitMapArr); - } - else - { - this.callParent([record, column, newValue]); - } - }, - - //Override - getNewModelInstance : function() - { - var newAssay = LABKEY.VaccineDesign.Assay.create(); - newAssay.set('VisitMap', []); - return newAssay; - }, - - //Override - getDeleteConfirmationMsg : function() - { - return 'Are you sure you want to delete the selected assay configuration? ' - + 'Note: this will also delete all related visit mapping information.'; - } -}); +/* + * Copyright (c) 2016-2017 LabKey Corporation + * + * Licensed under the Apache License, Version 2.0: http://www.apache.org/licenses/LICENSE-2.0 + */ +Ext4.define('LABKEY.VaccineDesign.AssaySchedulePanel', { + extend : 'Ext.panel.Panel', + + width: 750, + + border : false, + + bodyStyle : 'background-color: transparent;', + + disableEdit : true, + + dirty : false, + + returnUrl : null, + + initComponent : function() + { + this.items = [this.getAssaysGrid()]; + + this.callParent(); + + this.queryAssayPlan(); + + window.onbeforeunload = LABKEY.beforeunload(this.beforeUnload, this); + }, + + getAssaysGrid : function() + { + if (!this.assaysGrid) + { + this.assaysGrid = Ext4.create('LABKEY.VaccineDesign.AssaysGrid', { + disableEdit: this.disableEdit, + visitNoun: this.visitNoun, + useAlternateLookupFields: this.useAlternateLookupFields + }); + + this.assaysGrid.on('dirtychange', this.enableSaveButton, this); + this.assaysGrid.on('celledited', this.enableSaveButton, this); + } + + return this.assaysGrid; + }, + + queryAssayPlan : function() + { + // query the StudyProperties table for the initial assay plan value + LABKEY.Query.selectRows({ + schemaName: 'study', + queryName: 'StudyProperties', + columns: 'AssayPlan', + scope: this, + success: function(data) + { + var text = (data.rows.length == 1) ? data.rows[0]["AssayPlan"] : ''; + + this.add(this.getAssayPlanPanel(text)); + this.add(this.getButtonBar()); + } + }); + + }, + + getAssayPlanPanel : function(initValue) + { + if (!this.assayPlanPanel) + { + this.assayPlanPanel = Ext4.create('Ext.form.Panel', { + cls: 'study-vaccine-design', + padding: '20px 0', + border: false, + items: [ + Ext4.create('Ext.Component', { + html: '
        Assay Plan
        ' + }), + this.getAssayPlanTextArea(initValue) + ] + }); + } + + return this.assayPlanPanel; + }, + + getAssayPlanTextArea : function(initValue) + { + if (!this.assayPlanTextArea) + { + this.assayPlanTextArea = Ext4.create('Ext.form.field.TextArea', { + name: 'assayPlan', + readOnly: this.disableEdit, + value: initValue, + width: 500, + height: 100 + }); + + this.assayPlanTextArea.on('change', this.enableSaveButton, this, {buffer: 500}); + } + + return this.assayPlanTextArea; + }, + + getButtonBar : function() + { + if (!this.buttonBar) + { + this.buttonBar = Ext4.create('Ext.toolbar.Toolbar', { + dock: 'bottom', + ui: 'footer', + padding: 0, + style : 'background-color: transparent;', + defaults: {width: 75}, + items: [this.getSaveButton(), this.getCancelButton()] + }); + } + + return this.buttonBar; + }, + + getSaveButton : function() + { + if (!this.saveButton) + { + this.saveButton = Ext4.create('Ext.button.Button', { + text: 'Save', + disabled: true, + hidden: this.disableEdit, + handler: this.saveAssaySchedule, + scope: this + }); + } + + return this.saveButton; + }, + + enableSaveButton : function() + { + this.setDirty(true); + this.getSaveButton().enable(); + }, + + getCancelButton : function() + { + if (!this.cancelButton) + { + this.cancelButton = Ext4.create('Ext.button.Button', { + text: this.disableEdit ? 'Done' : 'Cancel', + handler: this.goToReturnURL, + scope: this + }); + } + + return this.cancelButton; + }, + + saveAssaySchedule : function() + { + this.getEl().mask('Saving...'); + + var assays = [], errorMsg = []; + Ext4.each(this.getAssaysGrid().getStore().getRange(), function(record) + { + var recData = Ext4.clone(record.data); + + // drop any empty treatment rows that were just added + var hasData = LABKEY.VaccineDesign.Utils.modelHasData(recData, LABKEY.VaccineDesign.Assay.getFields()); + if (hasData) + { + var sampleQuantity = Number(recData['SampleQuantity']); + if (isNaN(sampleQuantity) || sampleQuantity < 0) + errorMsg.push('Assay sample quantity value must be a positive number: ' + recData['SampleQuantity'] + '.'); + else + assays.push(recData); + } + }, this); + + if (errorMsg.length > 0) + { + this.onFailure(errorMsg.join('
        ')); + return; + } + + LABKEY.Ajax.request({ + url : LABKEY.ActionURL.buildURL('study-design', 'updateAssaySchedule.api'), + method : 'POST', + jsonData: { + assays: assays, + assayPlan: this.getAssayPlanTextArea().getValue() + }, + scope: this, + success: function(response) + { + var resp = Ext4.decode(response.responseText); + if (resp.success) + this.goToReturnURL(); + else + this.onFailure(); + }, + failure: function(response) + { + var resp = Ext4.decode(response.responseText); + if (resp.errors) + this.onFailure(Ext4.Array.pluck(resp.errors, 'message').join('
        ')); + else + this.onFailure(resp.exception); + } + }); + }, + + goToReturnURL : function() + { + this.setDirty(false); + window.location = this.returnUrl; + }, + + onFailure : function(text) + { + Ext4.Msg.show({ + title: 'Error', + msg: text || 'Unknown error occurred.', + icon: Ext4.Msg.ERROR, + buttons: Ext4.Msg.OK + }); + + this.getEl().unmask(); + }, + + setDirty : function(dirty) + { + this.dirty = dirty; + LABKEY.Utils.signalWebDriverTest("treatmentScheduleDirty", dirty); + }, + + isDirty : function() + { + return this.dirty; + }, + + beforeUnload : function() + { + if (!this.disableEdit && this.isDirty()) + return 'Please save your changes.'; + } +}); + +Ext4.define('LABKEY.VaccineDesign.AssaysGrid', { + extend : 'LABKEY.VaccineDesign.BaseDataViewAddVisit', + + cls : 'study-vaccine-design vaccine-design-assays', + + mainTitle : 'Assay Schedule', + + width : 620, + + studyDesignQueryNames : ['StudyDesignAssays', 'StudyDesignLabs', 'StudyDesignSampleTypes', 'StudyDesignUnits', 'Location', 'DataSets'], + + visitNoun : 'Visit', + + useAlternateLookupFields : false, + + //Override + getStore : function() + { + if (!this.store) + { + this.store = Ext4.create('Ext.data.Store', { + storeId : 'AssaysGridStore', + pageSize : 100000, // need to explicitly set otherwise it defaults to 25 + proxy: { + type: 'ajax', + url : LABKEY.ActionURL.buildURL('query', 'selectRows.api', null, { + 'schemaName' : 'study', + 'query.queryName' : 'assayspecimen' + }), + reader: { + type: 'json', + root: 'rows' + } + }, + sorters: [{ property: 'AssayName', direction: 'ASC' }], + autoLoad: true, + listeners: { + scope: this, + load: this.getVisitStore + } + }); + } + + return this.store; + }, + + getVisitStore : function() + { + if (!this.visitStore) + { + this.visitStore = Ext4.create('Ext.data.Store', { + model : 'LABKEY.VaccineDesign.Visit', + pageSize : 100000, // need to explicitly set otherwise it defaults to 25 + proxy: { + type: 'ajax', + url : LABKEY.ActionURL.buildURL('query', 'selectRows.api', null, { + 'schemaName' : 'study', + 'query.queryName' : 'visit' + }), + reader: { + type: 'json', + root: 'rows' + } + }, + sorters: [{ property: 'DisplayOrder', direction: 'ASC' }, { property: 'SequenceNumMin', direction: 'ASC' }], + autoLoad: true, + listeners: { + scope: this, + load: this.getAssaySpecimenVisitStore + } + }); + } + + return this.visitStore; + }, + + getAssaySpecimenVisitStore : function() + { + if (!this.assaySpecimenVisitStore) + { + this.assaySpecimenVisitStore = Ext4.create('Ext4.data.Store', { + storeId : 'AssaySpecimenVisitStore', + model : 'LABKEY.VaccineDesign.AssaySpecimenVisit', + pageSize : 100000, // need to explicitly set otherwise it defaults to 25 + proxy: { + type: 'ajax', + url : LABKEY.ActionURL.buildURL('query', 'selectRows.api', null, { + 'schemaName' : 'study', + 'query.queryName' : 'AssaySpecimenVisit' + }), + reader: { + type: 'json', + root: 'rows' + } + }, + autoLoad: true, + listeners: { + scope: this, + load: function (store, records) + { + var includedVisits = []; + + // stash the visit mapping information attached to each record in the assay store + Ext4.each(records, function(record) + { + var assayRecord = this.getStore().findRecord('RowId', record.get('AssaySpecimenId')); + if (assayRecord != null) + { + var visitMap = assayRecord.get('VisitMap') || []; + visitMap.push(Ext4.clone(record.data)); + assayRecord.set('VisitMap', visitMap); + + includedVisits.push(record.get('VisitId')); + } + }, this); + + var includedVisits = Ext4.Array.unique(includedVisits); + Ext4.each(this.getVisitStore().getRange(), function(visit) + { + var included = includedVisits.indexOf(visit.get('RowId')) > -1; + visit.set('Included', included); + }, this); + + this.add(this.getDataView()); + this.fireEvent('loadcomplete', this); + } + } + }); + } + + return this.assaySpecimenVisitStore; + }, + + //Override + loadDataViewStore : function() + { + // just call getStore here to initial the load, we will add the DataView + // and fire the loadcomplete event after all of the stores for this page are done loading + this.getStore(); + }, + + columnHasData : function(dataIndex) + { + var recordsDataArr = Ext4.Array.pluck(this.getStore().getRange(), 'data'), + colDataArr = Ext4.Array.pluck(recordsDataArr, dataIndex); + + for (var i = 0; i < colDataArr.length; i++) + { + if ((Ext4.isNumber(colDataArr[i]) && colDataArr[i] > 0) || (Ext4.isString(colDataArr[i]) && colDataArr[i] != '')) + return true; + } + + return false; + }, + + //Override + getColumnConfigs : function() + { + if (!this.columnConfigs) + { + var width = 0; // add to the running width as we go through which columns to show in the config + var columnConfigs = []; + + var assayNameEditorConfig = LABKEY.VaccineDesign.Utils.getStudyDesignComboConfig('AssayName', 185, 'StudyDesignAssays'); + assayNameEditorConfig.editable = true; // Rho use-case + + columnConfigs.push({ + label: 'Assay Name', + width: 200, + dataIndex: 'AssayName', + required: true, + editorType: 'LABKEY.ext4.ComboBox', + editorConfig: assayNameEditorConfig + }); + width += 200; + + columnConfigs.push({ + label: 'Dataset', + width: 200, + dataIndex: 'DataSet', + queryName: 'DataSets', + editorType: 'LABKEY.ext4.ComboBox', + editorConfig: LABKEY.VaccineDesign.Utils.getStudyDesignComboConfig('DataSet', 185, 'DataSets', undefined, 'Label', 'DataSetId') + }); + width += 200; + + var hidden = this.disableEdit && !this.columnHasData('Description'); + columnConfigs.push({ + label: 'Description', + width: 200, + hidden: hidden, + dataIndex: 'Description', + editorType: 'Ext.form.field.Text', + editorConfig: LABKEY.VaccineDesign.Utils.getStudyDesignTextConfig('Description', 185) + }); + if (!hidden) { + width += 200; + } + + if (this.useAlternateLookupFields) + { + hidden = this.disableEdit && !this.columnHasData('Source'); + columnConfigs.push({ + label: 'Source', + width: 60, + hidden: hidden, + dataIndex: 'Source', + editorType: 'Ext.form.field.Text', + editorConfig: LABKEY.VaccineDesign.Utils.getStudyDesignTextConfig('Source', 45) + }); + if (!hidden) { + width += 60; + } + + hidden = this.disableEdit && !this.columnHasData('LocationId'); + columnConfigs.push({ + label: 'Location', + width: 140, + hidden: hidden, + dataIndex: 'LocationId', + queryName: 'Location', + editorType: 'LABKEY.ext4.ComboBox', + editorConfig: LABKEY.VaccineDesign.Utils.getStudyDesignComboConfig('LocationId', 125, 'Location', undefined, 'Label', 'RowId') + }); + if (!hidden) { + width += 140; + } + + hidden = this.disableEdit && !this.columnHasData('TubeType'); + columnConfigs.push({ + label: 'TubeType', + width: 200, + hidden: hidden, + dataIndex: 'TubeType', + editorType: 'Ext.form.field.Text', + editorConfig: LABKEY.VaccineDesign.Utils.getStudyDesignTextConfig('TubeType', 185) + }); + if (!hidden) { + width += 200; + } + } + else + { + hidden = this.disableEdit && !this.columnHasData('Lab'); + columnConfigs.push({ + label: 'Lab', + width: 140, + hidden: hidden, + dataIndex: 'Lab', + queryName: 'StudyDesignLabs', + editorType: 'LABKEY.ext4.ComboBox', + editorConfig: LABKEY.VaccineDesign.Utils.getStudyDesignComboConfig('Lab', 125, 'StudyDesignLabs') + }); + if (!hidden) { + width += 140; + } + + hidden = this.disableEdit && !this.columnHasData('SampleType'); + columnConfigs.push({ + label: 'Sample Type', + width: 140, + hidden: hidden, + dataIndex: 'SampleType', + editorType: 'LABKEY.ext4.ComboBox', + editorConfig: LABKEY.VaccineDesign.Utils.getStudyDesignComboConfig('SampleType', 125, 'StudyDesignSampleTypes', undefined, 'Name') + }); + if (!hidden) { + width += 140; + } + + hidden = this.disableEdit && !this.columnHasData('SampleQuantity'); + columnConfigs.push({ + label: 'Sample Quantity', + width: 140, + hidden: hidden, + dataIndex: 'SampleQuantity', + editorType: 'Ext.form.field.Number', + editorConfig: LABKEY.VaccineDesign.Utils.getStudyDesignNumberConfig('SampleQuantity', 125, 2) + }); + if (!hidden) { + width += 140; + } + + hidden = this.disableEdit && !this.columnHasData('SampleUnits'); + columnConfigs.push({ + label: 'Sample Units', + width: 140, + hidden: hidden, + dataIndex: 'SampleUnits', + queryName: 'StudyDesignUnits', + editorType: 'LABKEY.ext4.ComboBox', + editorConfig: LABKEY.VaccineDesign.Utils.getStudyDesignComboConfig('SampleUnits', 125, 'StudyDesignUnits') + }); + if (!hidden) { + width += 140; + } + } + + var visitConfigs = this.getVisitColumnConfigs(); + + // update the width based on the number of visit columns + width += (Math.max(2, visitConfigs.length) * 75); + this.setWidth(width); + + // update the outer panel width if necessary + var outerPanel = this.up('panel'); + if (outerPanel != null) + outerPanel.setWidth(Math.max(width, 750)); + + this.columnConfigs = columnConfigs.concat(visitConfigs); + } + + return this.columnConfigs; + }, + + getVisitColumnConfigs : function() + { + var visitConfigs = []; + + Ext4.each(this.getVisitStore().getRange(), function(visit) + { + if (visit.get('Included')) + { + visitConfigs.push({ + label: visit.get('Label') || visit.get('SequenceNumMin'), + width: 75, + dataIndex: 'VisitMap', + dataIndexArrFilterProp: 'VisitId', + dataIndexArrFilterValue: visit.get('RowId'), + editorType: 'Ext.form.field.Checkbox', + editorConfig: { + hideFieldLabel: true, + name: 'VisitMap' + } + }); + } + }, this); + + if (visitConfigs.length == 0 && !this.disableEdit) + { + visitConfigs.push({ + label: 'No ' + this.visitNoun + 's Defined', + displayValue: '', + width: 160 + }); + } + + return visitConfigs; + }, + + //Override + getCurrentCellValue : function(column, record, dataIndex, outerDataIndex, subgridIndex) + { + var value = this.callParent([column, record, dataIndex, outerDataIndex, subgridIndex]); + + if (dataIndex == 'VisitMap' && Ext4.isArray(value)) + { + var matchingIndex = LABKEY.VaccineDesign.Utils.getMatchingRowIndexFromArray(value, column.dataIndexArrFilterProp, column.dataIndexArrFilterValue); + return matchingIndex > -1; + } + else if ((dataIndex == 'SampleQuantity' || dataIndex == 'LocationId') || dataIndex == 'DataSet' && value == 0) + { + return null; + } + + return value; + }, + + //Override + updateStoreRecordValue : function(record, column, newValue, field) + { + // special case for editing the value of one of the pivot visit columns + if (column.dataIndex == 'VisitMap') + { + var visitMapArr = record.get(column.dataIndex); + if (!Ext4.isArray(visitMapArr)) + { + visitMapArr = []; + record.set(column.dataIndex, visitMapArr); + } + + var matchingIndex = LABKEY.VaccineDesign.Utils.getMatchingRowIndexFromArray(visitMapArr, column.dataIndexArrFilterProp, column.dataIndexArrFilterValue); + + if (newValue) + visitMapArr.push({VisitId: column.dataIndexArrFilterValue}); + else + Ext4.Array.splice(visitMapArr, matchingIndex, 1); + + this.fireEvent('celledited', this, 'VisitMap', visitMapArr); + } + else + { + this.callParent([record, column, newValue]); + } + }, + + //Override + getNewModelInstance : function() + { + var newAssay = LABKEY.VaccineDesign.Assay.create(); + newAssay.set('VisitMap', []); + return newAssay; + }, + + //Override + getDeleteConfirmationMsg : function() + { + return 'Are you sure you want to delete the selected assay configuration? ' + + 'Note: this will also delete all related visit mapping information.'; + } +}); diff --git a/study/webapp/study/vaccineDesign/BaseDataView.js b/studydesign/webapp/study/vaccineDesign/BaseDataView.js similarity index 97% rename from study/webapp/study/vaccineDesign/BaseDataView.js rename to studydesign/webapp/study/vaccineDesign/BaseDataView.js index d11f6a16..d01c5f92 100644 --- a/study/webapp/study/vaccineDesign/BaseDataView.js +++ b/studydesign/webapp/study/vaccineDesign/BaseDataView.js @@ -1,678 +1,678 @@ -/* - * Copyright (c) 2016-2017 LabKey Corporation - * - * Licensed under the Apache License, Version 2.0: http://www.apache.org/licenses/LICENSE-2.0 - */ - -Ext4.define('LABKEY.VaccineDesign.BaseDataView', { - - extend : 'Ext.panel.Panel', - - cls : 'study-vaccine-design', - - border : false, - - mainTitle : null, - - studyDesignQueryNames : null, - - // for a DataSpace project, some scenarios don't make sense to allow insert/update - disableEdit : false, - - DELETE_ICON_CLS : 'fa fa-trash', - ADD_ICON_CLS : 'fa fa-plus-circle', - - constructor : function(config) - { - this.callParent([config]); - this.addEvents('dirtychange', 'loadcomplete', 'celledited', 'beforerowdeleted', 'renderviewcomplete'); - }, - - initComponent : function() - { - this.items = [ - this.getMainTitle() - // Note: this.getDataView() will be added after the store loads in this.loadDataViewStore() - ]; - - // Pre-load the study design lookup queries that will be used in dropdowns for this page. - // Note: these stores are also used for getting display values in the data view XTempalte so don't - // bind the data view store until they are all loaded. - if (Ext4.isArray(this.studyDesignQueryNames) && this.studyDesignQueryNames.length > 0) - { - var loadCounter = 0; - Ext4.each(this.studyDesignQueryNames, function(queryName) - { - var studyDesignStore = LABKEY.VaccineDesign.Utils.getStudyDesignStore(queryName); - studyDesignStore.on('load', function() - { - loadCounter++; - - if (loadCounter == this.studyDesignQueryNames.length) - this.loadDataViewStore(); - }, this); - }, this); - } - else - { - this.loadDataViewStore(); - } - - this.fireRenderCompleteTask = new Ext4.util.DelayedTask(function() { - this.fireEvent('renderviewcomplete', this); - }, this); - - // add a single event listener to focus the first input field on the initial render - this.on('renderviewcomplete', function() { - this.giveCellInputFocus('table.outer tr.data-row:first td.cell-value:first input', true); - LABKEY.Utils.signalWebDriverTest("VaccineDesign_renderviewcomplete"); - }, this, {single: true}); - - this.callParent(); - }, - - getMainTitle : function() - { - if (!this.mainTitleCmp && this.mainTitle != null) - { - this.mainTitleCmp = Ext4.create('Ext.Component', { - html: '
        ' + Ext4.util.Format.htmlEncode(this.mainTitle) + '
        ' - }); - } - - return this.mainTitleCmp; - }, - - getDataView : function() - { - if (!this.dataView) - { - this.dataView = Ext4.create('Ext.view.View', { - tpl: this.getDataViewTpl(), - cls: 'table-responsive', - store: this.getStore(), - itemSelector: 'tr.data-row', - disableSelection: true, - setTemplate: function(newTpl) - { - this.tpl = newTpl; - this.refresh(); - } - }); - - this.dataView.on('itemclick', this.onDataViewItemClick, this); - this.dataView.on('refresh', this.onDataViewRefresh, this, {buffer: 250}); - } - - return this.dataView; - }, - - getDataViewTpl : function() - { - var showEdit = !this.disableEdit, - tdCls = !showEdit ? 'cell-display' : 'cell-value', - tplArr = [], - columns = this.getColumnConfigs(); - - tplArr.push(''); - tplArr = tplArr.concat(this.getTableHeaderRowTpl(columns)); - - // data rows - tplArr.push(''); - tplArr.push(''); - if (showEdit) - tplArr.push(''); - Ext4.each(columns, function(column) - { - if (Ext4.isString(column.dataIndex) && !column.hidden) - { - var checkMissingReqTpl = column.required ? ' {[this.checkMissingRequired(values, "' + column.dataIndex + '")]}' : '', - tdTpl = ''; - - if (Ext4.isDefined(column.dataIndexArrFilterValue)) - tdTpl = tdTpl.substring(0, tdTpl.length -1) + ' data-filter-value="' + column.dataIndexArrFilterValue + '">'; - - // decide which of the td tpls to use based on the column definition - if (Ext4.isObject(column.subgridConfig) && Ext4.isArray(column.subgridConfig.columns)) - { - tplArr = tplArr.concat(this.getSubGridTpl(column.dataIndex, column.subgridConfig.columns)); - } - else if (Ext4.isString(column.queryName)) - { - tplArr.push(tdTpl + (!showEdit ? '{[this.getLabelFromStore(values["' + column.dataIndex + '"], "' + column.queryName + '")]}' : '') + tdCloseTpl); - } - else if (Ext4.isString(column.lookupStoreId) && Ext4.isDefined(column.dataIndexArrFilterValue)) - { - var valTpl = ''; - if (!showEdit) - { - valTpl = '{[this.getDisplayValue(values["' + column.dataIndex + '"], ' - + '"' + column.dataIndexArrFilterProp + '", "' + column.dataIndexArrFilterValue + '", ' - + '"' + column.dataIndexArrValue + '", "' + column.lookupStoreId + '")]}'; - } - - tplArr.push(tdTpl + valTpl + tdCloseTpl); - } - else if (column.editorType == 'Ext.form.field.Checkbox') - { - var valTpl = ''; - if (!showEdit) - { - valTpl = '{[this.getDisplayValue(values["' + column.dataIndex + '"], ' - + '"' + column.dataIndexArrFilterProp + '", ' - + '"' + column.dataIndexArrFilterValue + '", ' - + '"checkbox")]}'; - } - - tdTpl = tdTpl.substring(0, tdTpl.length -1) + ' style="text-align: center;">'; - tplArr.push(tdTpl + valTpl + tdCloseTpl); - } - else - { - tplArr.push(tdTpl + (!showEdit ? '{[this.getDisplayValue(values["' + column.dataIndex + '"])]}' : '') + tdCloseTpl); - } - } - else if (Ext4.isString(column.displayValue)) - { - tplArr.push(''); - } - }, this); - tplArr.push(''); - tplArr.push(''); - - tplArr = tplArr.concat(this.getEmptyTableTpl(columns)); - tplArr = tplArr.concat(this.getAddNewRowTpl(columns)); - tplArr.push('
        ', - tdCloseTpl = '' + column.displayValue + '
        '); - - tplArr.push({ - getDisplayValue : function(val, arrPropFilterName, arrPropFilterVal, arrPropDisplayField, lookupStoreId) - { - // allow showing a certain filtered row from an array - if (Ext4.isDefined(arrPropDisplayField)) - { - var matchingIndex = LABKEY.VaccineDesign.Utils.getMatchingRowIndexFromArray(val, arrPropFilterName, arrPropFilterVal); - if (matchingIndex > -1 && Ext4.isObject(val[matchingIndex])) - { - if (arrPropDisplayField == 'checkbox') - return '✓'; - else - val = val[matchingIndex][arrPropDisplayField]; - } - else - val = ''; - } - - // if we have a specific lookupStoreId, get the label value from the matching RowId record in that store - if (Ext4.isDefined(lookupStoreId) && val != null && val != '') - { - var store = Ext4.getStore(lookupStoreId); - if (store != null) - { - var record = store.findRecord('RowId', val); - if (record != null) - val = record.get('Label'); - } - } - - if (Ext4.isNumber(val)) - { - return val == 0 ? '' : val; - } - else - { - // need to htmlEncode and then handle newlines in multiline text fields (i.e. Treatment/Description) - val = Ext4.util.Format.htmlEncode(val); - val = val.replace(/\n/g, '
        '); - } - - return val; - }, - - getLabelFromStore : function(val, queryName) - { - if (val != null && val != '') - val = LABKEY.VaccineDesign.Utils.getLabelFromStore(queryName, val); - - if (Ext4.isNumber(val)) - return val == 0 ? '' : val; - else - return Ext4.util.Format.htmlEncode(val); - }, - - checkMissingRequired : function(values, dataIndex) - { - if (values[dataIndex] == null || values[dataIndex] == '') - return ' missing-required'; - - return ''; - } - }); - - return new Ext4.XTemplate(tplArr); - }, - - getSubGridTpl : function(dataIndex, columns) - { - var showEdit = !this.disableEdit, - tdCls = showEdit ? 'cell-value' : 'cell-display', - tplArr = []; - - tplArr.push(''); - - // only show the subgrid if we are allowing edits of if it has at least one row - tplArr.push(''); - - tplArr.push(''); - tplArr = tplArr.concat(this.getTableHeaderRowTpl(columns)); - - // data rows - tplArr.push(''); - tplArr.push(''); - if (showEdit) - { - tplArr.push(''); - } - Ext4.each(columns, function(column) - { - if (Ext4.isString(column.dataIndex)) - { - var checkMissingReqTpl = column.required ? ' {[this.checkMissingRequired(values, "' + column.dataIndex + '")]}' : '', - tdTpl = ''; - - if (Ext4.isString(column.queryName)) - tplArr.push(tdTpl + (!showEdit ? '{[this.getLabelFromStore(values["' + column.dataIndex + '"], "' + column.queryName + '")]}' : '') + tdCloseTpl); - else - tplArr.push(tdTpl + (!showEdit ? '{' + column.dataIndex + ':htmlEncode}' : '') + tdCloseTpl); - } - }, this); - tplArr.push(''); - tplArr.push(''); - - tplArr = tplArr.concat(this.getAddNewRowTpl(columns, dataIndex)); - tplArr.push('
        '); - tplArr.push(''); - tplArr.push('', - tdCloseTpl = '
        '); - tplArr.push('
        '); - tplArr.push(''); - - return tplArr; - }, - - getTableHeaderRowTpl : function(columns) - { - var tplArr = []; - - tplArr.push(''); - if (!this.disableEdit) - tplArr.push(' '); - Ext4.each(columns, function(column) - { - if (!column.hidden) - tplArr.push('' + Ext4.util.Format.htmlEncode(column.label) + ''); - }, this); - tplArr.push(''); - - return tplArr; - }, - - getEmptyTableTpl : function(columns) - { - var tplArr = []; - - if (this.disableEdit) - { - tplArr.push(''); - tplArr.push(''); - tplArr.push('No data to show.'); - tplArr.push(''); - tplArr.push(''); - } - - return tplArr; - }, - - getAddNewRowTpl : function(columns, dataIndex) - { - var tplArr = []; - - if (!this.disableEdit) - { - tplArr.push(''); - tplArr.push(' '); - tplArr.push(''); - if (Ext4.isString(dataIndex)) - tplArr.push(' Add new row'); - else - tplArr.push(' Add new row'); - tplArr.push(''); - tplArr.push(''); - } - - return tplArr; - }, - - onDataViewItemClick : function(view, record, item, index, event) - { - if (!this.disableEdit) - { - // handle click on trashcan icon to delete row - if (event.target.getAttribute('class') == this.DELETE_ICON_CLS) - { - if (event.target.hasAttribute('outer-index')) - { - this.removeOuterRecord(this.mainTitle, record); - } - // handle click on trashcan icon for outer grid - else if (event.target.hasAttribute('subgrid-data-index') && event.target.hasAttribute('subgrid-index')) - { - this.confirmRemoveSubgridRecord(event.target, record); - } - } - } - }, - - createNewCellEditField : function(target, record, index) - { - var dataIndex = target.getAttribute('data-index'), - dataFilterValue = target.getAttribute('data-filter-value'), - outerDataIndex = target.getAttribute('outer-data-index'), - subgridIndex = Number(target.getAttribute('subgrid-index')), - column = this.getColumnConfig(dataIndex, dataFilterValue, outerDataIndex), - editor = this.getColumnEditorConfig(column); - - if (editor != null) - { - var config = { - renderTo: target, - required: column.required, - storeIndex: index, - dataFilterValue: dataFilterValue, - outerDataIndex: outerDataIndex, - subgridIndex: subgridIndex - }; - - var currentValue = this.getCurrentCellValue(column, record, dataIndex, outerDataIndex, subgridIndex); - if (editor.type == 'Ext.form.field.Checkbox') - config.checked = currentValue; - else - config.value = currentValue; - - if (column.isTreatmentLookup) { - var treatmentLabel = this.getTreatmentCellDisplayValue(currentValue, column.lookupStoreId); - config.value = treatmentLabel; - config.treatmentId = currentValue; - } - - // create a new form field to place in the td cell - var field = Ext4.create(editor.type, Ext4.apply(editor.config, config)); - - // add listeners for when to apply the updated value and clear the input field - field.on('change', this.updateStoreValueForCellEdit, this, {buffer: 500}); - } - }, - - getCurrentCellValue : function(column, record, dataIndex, outerDataIndex, subgridIndex) - { - return Ext4.isString(outerDataIndex) ? record.get(outerDataIndex)[subgridIndex][dataIndex] : record.get(dataIndex); - }, - - updateStoreValueForCellEdit : function(field) - { - var fieldName = field.getName(), - newValue = field.getValue(), - index = field.storeIndex, - record = this.getStore().getAt(index), - dataFilterValue = field.dataFilterValue, - outerDataIndex = field.outerDataIndex, - subgridIndex = Number(field.subgridIndex); - - // suspend events on cell update so that we don't re-render the dataview - this.getStore().suspendEvents(); - - if (Ext4.isString(outerDataIndex)) - { - if (!isNaN(subgridIndex) && Ext4.isArray(record.get(outerDataIndex))) - this.updateSubgridRecordValue(record, outerDataIndex, subgridIndex, fieldName, newValue); - } - else - { - var column = this.getColumnConfig(fieldName, dataFilterValue, outerDataIndex); - this.updateStoreRecordValue(record, column, newValue, field); - } - - // update the missing-required cls based on the new field value - if (field.required) - { - if (newValue == null || newValue == '') - Ext4.get(field.renderTo).addCls('missing-required'); - else - Ext4.get(field.renderTo).removeCls('missing-required'); - } - - // resume store events so that adding and deleting will re-render the dataview - this.getStore().resumeEvents(); - }, - - updateSubgridRecordValue : function(record, outerDataIndex, subgridIndex, fieldName, newValue) - { - if (Ext4.isString(newValue)) - newValue.trim(); - - record.get(outerDataIndex)[subgridIndex][fieldName] = newValue; - this.fireEvent('celledited', this, fieldName, newValue); - }, - - updateStoreRecordValue : function(record, column, newValue, field) - { - if (Ext4.isString(newValue)) - newValue.trim(); - - record.set(column.dataIndex, newValue); - this.fireEvent('celledited', this, column.dataIndex, newValue); - }, - - removeOuterRecord : function(title, record) - { - var msg = this.getDeleteConfirmationMsg() != null ? this.getDeleteConfirmationMsg() : 'Are you sure you want to delete the selected row?'; - - Ext4.Msg.confirm('Confirm Delete: ' + title, msg, function(btn) - { - if (btn == 'yes') - { - this.fireEvent('beforerowdeleted', this, record); - - // suspend events on remove so that we don't re-render the dataview twice - this.getStore().suspendEvents(); - this.getStore().remove(record); - this.getStore().resumeEvents(); - this.refresh(true); - } - }, this); - }, - - confirmRemoveSubgridRecord : function(target, record) - { - var subgridDataIndex = target.getAttribute('subgrid-data-index'), - subgridArr = record.get(subgridDataIndex); - - if (Ext4.isArray(subgridArr)) - { - Ext4.Msg.confirm('Confirm Delete: ' + subgridDataIndex, 'Are you sure you want to delete the selected row?', function(btn) - { - if (btn == 'yes') - { - this.removeSubgridRecord(target, record); - this.refresh(true); - } - }, this); - } - }, - - removeSubgridRecord : function(target, record) - { - var subgridDataIndex = target.getAttribute('subgrid-data-index'), - subgridArr = record.get(subgridDataIndex); - - subgridArr.splice(target.getAttribute('subgrid-index'), 1); - }, - - onDataViewRefresh : function(view) - { - this.attachCellEditors(view); - this.attachAddRowListeners(view); - this.doLayout(); - this.fireRenderCompleteTask.delay(250); - }, - - attachCellEditors : function(view) - { - // attach cell editors for each of the store records (i.e. tr.row elements in the table) - var index = 0; - Ext4.each(this.getStore().getRange(), function(record) - { - var targetCellEls = Ext4.DomQuery.select('tr.data-row:nth(' + (index+1) + ') td.cell-value', view.getEl().dom); - Ext4.each(targetCellEls, function(targetCell) - { - this.createNewCellEditField(targetCell, record, index); - }, this); - - index++; - }, this); - }, - - attachAddRowListeners : function(view) - { - var addIconEls = Ext4.DomQuery.select('i.add-new-row', view.getEl().dom); - - Ext4.each(addIconEls, function(addIconEl) - { - if (addIconEl.hasAttribute('data-index')) - Ext4.get(addIconEl).on('click', this.addNewSubgridRow, this); - else - Ext4.get(addIconEl).on('click', this.addNewOuterRow, this); - }, this); - }, - - addNewOuterRow : function() - { - // suspend events on insert so that we don't re-render the dataview twice - this.getStore().suspendEvents(); - this.getStore().insert(this.getStore().getCount(), this.getNewModelInstance()); - this.getStore().resumeEvents(); - - // on refresh, call to give focus to the first column of the new row - this.on('renderviewcomplete', function(){ - this.giveCellInputFocus('table.outer tr.data-row:last td.cell-value:first input'); - }, this, {single: true}); - - this.refresh(); - }, - - addNewSubgridRow : function(event, target) - { - var dataIndex = target.getAttribute('data-index'), - rowIndex = Number(target.getAttribute('outer-index')); - - if (Ext4.isString(dataIndex) && Ext4.isNumber(rowIndex)) - { - var record = this.getStore().getAt(rowIndex), - dataIndexArr = record.get(dataIndex); - - if (Ext4.isArray(dataIndexArr)) - record.set(dataIndex, dataIndexArr.concat([{}])); - - // on refresh, call to give focus to the first column of the new row - this.on('renderviewcomplete', function(){ - var selector = 'table.subgrid-' + dataIndex + ':nth(' + (rowIndex+1) + ') tr.subrow:last td.cell-value:first input'; - this.giveCellInputFocus(selector); - }, this, {single: true}); - - this.refresh(); - } - }, - - giveCellInputFocus : function(selector, queryFullPage) - { - var cellInputField = Ext4.DomQuery.selectNode(selector, queryFullPage ? undefined : this.getDataView().getEl().dom); - if (cellInputField) - cellInputField.focus(); - }, - - refresh : function(hasChanges) - { - this.getDataView().refresh(); - - if (hasChanges) - this.fireEvent('dirtychange', this); - }, - - getColumnConfig : function(dataIndex, dataFilterValue, parentDataIndex) - { - var columns = this.getColumnConfigs(), matchingColumn = null; - - // if the parentDataIndex is defined, then we are looking for the subgrid column editor config - if (Ext4.isString(parentDataIndex)) - { - var colIndex = Ext4.pluck(columns, 'dataIndex').indexOf(parentDataIndex); - if (colIndex > -1 && columns[colIndex].hasOwnProperty('subgridConfig') && Ext4.isArray(columns[colIndex].subgridConfig.columns)) - columns = columns[colIndex].subgridConfig.columns; - else - return null; - } - - Ext4.each(columns, function(column) - { - if (column.dataIndex == dataIndex && (!Ext4.isDefined(dataFilterValue) || column.dataIndexArrFilterValue == dataFilterValue)) - { - matchingColumn = column; - return false; // break; - } - }, this); - - return matchingColumn; - }, - - getColumnEditorConfig : function(column) - { - if (column != null && column.hasOwnProperty('editorType') && column.hasOwnProperty('editorConfig')) - { - return { - type: column.editorType, - config: Ext4.isFunction(column.editorConfig) ? column.editorConfig.call(this) : column.editorConfig - }; - } - - return null; - }, - - loadDataViewStore : function() - { - // since some tables might need information from the store, wait to add the data view until the store loads - this.getStore().on('load', function() { - this.add(this.getDataView()); - this.fireEvent('loadcomplete', this); - }, this, {single: true}); - }, - - getStore : function() - { - throw "getStore must be overridden in subclass"; - }, - - getNewModelInstance : function() - { - throw "getNewModelInstance must be overridden in subclass"; - }, - - getColumnConfigs : function() - { - throw "getColumnConfigs must be overridden in subclass"; - }, - - getDeleteConfirmationMsg : function() - { - return null; - } +/* + * Copyright (c) 2016-2017 LabKey Corporation + * + * Licensed under the Apache License, Version 2.0: http://www.apache.org/licenses/LICENSE-2.0 + */ + +Ext4.define('LABKEY.VaccineDesign.BaseDataView', { + + extend : 'Ext.panel.Panel', + + cls : 'study-vaccine-design', + + border : false, + + mainTitle : null, + + studyDesignQueryNames : null, + + // for a DataSpace project, some scenarios don't make sense to allow insert/update + disableEdit : false, + + DELETE_ICON_CLS : 'fa fa-trash', + ADD_ICON_CLS : 'fa fa-plus-circle', + + constructor : function(config) + { + this.callParent([config]); + this.addEvents('dirtychange', 'loadcomplete', 'celledited', 'beforerowdeleted', 'renderviewcomplete'); + }, + + initComponent : function() + { + this.items = [ + this.getMainTitle() + // Note: this.getDataView() will be added after the store loads in this.loadDataViewStore() + ]; + + // Pre-load the study design lookup queries that will be used in dropdowns for this page. + // Note: these stores are also used for getting display values in the data view XTempalte so don't + // bind the data view store until they are all loaded. + if (Ext4.isArray(this.studyDesignQueryNames) && this.studyDesignQueryNames.length > 0) + { + var loadCounter = 0; + Ext4.each(this.studyDesignQueryNames, function(queryName) + { + var studyDesignStore = LABKEY.VaccineDesign.Utils.getStudyDesignStore(queryName); + studyDesignStore.on('load', function() + { + loadCounter++; + + if (loadCounter == this.studyDesignQueryNames.length) + this.loadDataViewStore(); + }, this); + }, this); + } + else + { + this.loadDataViewStore(); + } + + this.fireRenderCompleteTask = new Ext4.util.DelayedTask(function() { + this.fireEvent('renderviewcomplete', this); + }, this); + + // add a single event listener to focus the first input field on the initial render + this.on('renderviewcomplete', function() { + this.giveCellInputFocus('table.outer tr.data-row:first td.cell-value:first input', true); + LABKEY.Utils.signalWebDriverTest("VaccineDesign_renderviewcomplete"); + }, this, {single: true}); + + this.callParent(); + }, + + getMainTitle : function() + { + if (!this.mainTitleCmp && this.mainTitle != null) + { + this.mainTitleCmp = Ext4.create('Ext.Component', { + html: '
        ' + Ext4.util.Format.htmlEncode(this.mainTitle) + '
        ' + }); + } + + return this.mainTitleCmp; + }, + + getDataView : function() + { + if (!this.dataView) + { + this.dataView = Ext4.create('Ext.view.View', { + tpl: this.getDataViewTpl(), + cls: 'table-responsive', + store: this.getStore(), + itemSelector: 'tr.data-row', + disableSelection: true, + setTemplate: function(newTpl) + { + this.tpl = newTpl; + this.refresh(); + } + }); + + this.dataView.on('itemclick', this.onDataViewItemClick, this); + this.dataView.on('refresh', this.onDataViewRefresh, this, {buffer: 250}); + } + + return this.dataView; + }, + + getDataViewTpl : function() + { + var showEdit = !this.disableEdit, + tdCls = !showEdit ? 'cell-display' : 'cell-value', + tplArr = [], + columns = this.getColumnConfigs(); + + tplArr.push(''); + tplArr = tplArr.concat(this.getTableHeaderRowTpl(columns)); + + // data rows + tplArr.push(''); + tplArr.push(''); + if (showEdit) + tplArr.push(''); + Ext4.each(columns, function(column) + { + if (Ext4.isString(column.dataIndex) && !column.hidden) + { + var checkMissingReqTpl = column.required ? ' {[this.checkMissingRequired(values, "' + column.dataIndex + '")]}' : '', + tdTpl = ''; + + if (Ext4.isDefined(column.dataIndexArrFilterValue)) + tdTpl = tdTpl.substring(0, tdTpl.length -1) + ' data-filter-value="' + column.dataIndexArrFilterValue + '">'; + + // decide which of the td tpls to use based on the column definition + if (Ext4.isObject(column.subgridConfig) && Ext4.isArray(column.subgridConfig.columns)) + { + tplArr = tplArr.concat(this.getSubGridTpl(column.dataIndex, column.subgridConfig.columns)); + } + else if (Ext4.isString(column.queryName)) + { + tplArr.push(tdTpl + (!showEdit ? '{[this.getLabelFromStore(values["' + column.dataIndex + '"], "' + column.queryName + '")]}' : '') + tdCloseTpl); + } + else if (Ext4.isString(column.lookupStoreId) && Ext4.isDefined(column.dataIndexArrFilterValue)) + { + var valTpl = ''; + if (!showEdit) + { + valTpl = '{[this.getDisplayValue(values["' + column.dataIndex + '"], ' + + '"' + column.dataIndexArrFilterProp + '", "' + column.dataIndexArrFilterValue + '", ' + + '"' + column.dataIndexArrValue + '", "' + column.lookupStoreId + '")]}'; + } + + tplArr.push(tdTpl + valTpl + tdCloseTpl); + } + else if (column.editorType == 'Ext.form.field.Checkbox') + { + var valTpl = ''; + if (!showEdit) + { + valTpl = '{[this.getDisplayValue(values["' + column.dataIndex + '"], ' + + '"' + column.dataIndexArrFilterProp + '", ' + + '"' + column.dataIndexArrFilterValue + '", ' + + '"checkbox")]}'; + } + + tdTpl = tdTpl.substring(0, tdTpl.length -1) + ' style="text-align: center;">'; + tplArr.push(tdTpl + valTpl + tdCloseTpl); + } + else + { + tplArr.push(tdTpl + (!showEdit ? '{[this.getDisplayValue(values["' + column.dataIndex + '"])]}' : '') + tdCloseTpl); + } + } + else if (Ext4.isString(column.displayValue)) + { + tplArr.push(''); + } + }, this); + tplArr.push(''); + tplArr.push(''); + + tplArr = tplArr.concat(this.getEmptyTableTpl(columns)); + tplArr = tplArr.concat(this.getAddNewRowTpl(columns)); + tplArr.push('
        ', + tdCloseTpl = '' + column.displayValue + '
        '); + + tplArr.push({ + getDisplayValue : function(val, arrPropFilterName, arrPropFilterVal, arrPropDisplayField, lookupStoreId) + { + // allow showing a certain filtered row from an array + if (Ext4.isDefined(arrPropDisplayField)) + { + var matchingIndex = LABKEY.VaccineDesign.Utils.getMatchingRowIndexFromArray(val, arrPropFilterName, arrPropFilterVal); + if (matchingIndex > -1 && Ext4.isObject(val[matchingIndex])) + { + if (arrPropDisplayField == 'checkbox') + return '✓'; + else + val = val[matchingIndex][arrPropDisplayField]; + } + else + val = ''; + } + + // if we have a specific lookupStoreId, get the label value from the matching RowId record in that store + if (Ext4.isDefined(lookupStoreId) && val != null && val != '') + { + var store = Ext4.getStore(lookupStoreId); + if (store != null) + { + var record = store.findRecord('RowId', val); + if (record != null) + val = record.get('Label'); + } + } + + if (Ext4.isNumber(val)) + { + return val == 0 ? '' : val; + } + else + { + // need to htmlEncode and then handle newlines in multiline text fields (i.e. Treatment/Description) + val = Ext4.util.Format.htmlEncode(val); + val = val.replace(/\n/g, '
        '); + } + + return val; + }, + + getLabelFromStore : function(val, queryName) + { + if (val != null && val != '') + val = LABKEY.VaccineDesign.Utils.getLabelFromStore(queryName, val); + + if (Ext4.isNumber(val)) + return val == 0 ? '' : val; + else + return Ext4.util.Format.htmlEncode(val); + }, + + checkMissingRequired : function(values, dataIndex) + { + if (values[dataIndex] == null || values[dataIndex] == '') + return ' missing-required'; + + return ''; + } + }); + + return new Ext4.XTemplate(tplArr); + }, + + getSubGridTpl : function(dataIndex, columns) + { + var showEdit = !this.disableEdit, + tdCls = showEdit ? 'cell-value' : 'cell-display', + tplArr = []; + + tplArr.push(''); + + // only show the subgrid if we are allowing edits of if it has at least one row + tplArr.push(''); + + tplArr.push(''); + tplArr = tplArr.concat(this.getTableHeaderRowTpl(columns)); + + // data rows + tplArr.push(''); + tplArr.push(''); + if (showEdit) + { + tplArr.push(''); + } + Ext4.each(columns, function(column) + { + if (Ext4.isString(column.dataIndex)) + { + var checkMissingReqTpl = column.required ? ' {[this.checkMissingRequired(values, "' + column.dataIndex + '")]}' : '', + tdTpl = ''; + + if (Ext4.isString(column.queryName)) + tplArr.push(tdTpl + (!showEdit ? '{[this.getLabelFromStore(values["' + column.dataIndex + '"], "' + column.queryName + '")]}' : '') + tdCloseTpl); + else + tplArr.push(tdTpl + (!showEdit ? '{' + column.dataIndex + ':htmlEncode}' : '') + tdCloseTpl); + } + }, this); + tplArr.push(''); + tplArr.push(''); + + tplArr = tplArr.concat(this.getAddNewRowTpl(columns, dataIndex)); + tplArr.push('
        '); + tplArr.push(''); + tplArr.push('', + tdCloseTpl = '
        '); + tplArr.push('
        '); + tplArr.push(''); + + return tplArr; + }, + + getTableHeaderRowTpl : function(columns) + { + var tplArr = []; + + tplArr.push(''); + if (!this.disableEdit) + tplArr.push(' '); + Ext4.each(columns, function(column) + { + if (!column.hidden) + tplArr.push('' + Ext4.util.Format.htmlEncode(column.label) + ''); + }, this); + tplArr.push(''); + + return tplArr; + }, + + getEmptyTableTpl : function(columns) + { + var tplArr = []; + + if (this.disableEdit) + { + tplArr.push(''); + tplArr.push(''); + tplArr.push('No data to show.'); + tplArr.push(''); + tplArr.push(''); + } + + return tplArr; + }, + + getAddNewRowTpl : function(columns, dataIndex) + { + var tplArr = []; + + if (!this.disableEdit) + { + tplArr.push(''); + tplArr.push(' '); + tplArr.push(''); + if (Ext4.isString(dataIndex)) + tplArr.push(' Add new row'); + else + tplArr.push(' Add new row'); + tplArr.push(''); + tplArr.push(''); + } + + return tplArr; + }, + + onDataViewItemClick : function(view, record, item, index, event) + { + if (!this.disableEdit) + { + // handle click on trashcan icon to delete row + if (event.target.getAttribute('class') == this.DELETE_ICON_CLS) + { + if (event.target.hasAttribute('outer-index')) + { + this.removeOuterRecord(this.mainTitle, record); + } + // handle click on trashcan icon for outer grid + else if (event.target.hasAttribute('subgrid-data-index') && event.target.hasAttribute('subgrid-index')) + { + this.confirmRemoveSubgridRecord(event.target, record); + } + } + } + }, + + createNewCellEditField : function(target, record, index) + { + var dataIndex = target.getAttribute('data-index'), + dataFilterValue = target.getAttribute('data-filter-value'), + outerDataIndex = target.getAttribute('outer-data-index'), + subgridIndex = Number(target.getAttribute('subgrid-index')), + column = this.getColumnConfig(dataIndex, dataFilterValue, outerDataIndex), + editor = this.getColumnEditorConfig(column); + + if (editor != null) + { + var config = { + renderTo: target, + required: column.required, + storeIndex: index, + dataFilterValue: dataFilterValue, + outerDataIndex: outerDataIndex, + subgridIndex: subgridIndex + }; + + var currentValue = this.getCurrentCellValue(column, record, dataIndex, outerDataIndex, subgridIndex); + if (editor.type == 'Ext.form.field.Checkbox') + config.checked = currentValue; + else + config.value = currentValue; + + if (column.isTreatmentLookup) { + var treatmentLabel = this.getTreatmentCellDisplayValue(currentValue, column.lookupStoreId); + config.value = treatmentLabel; + config.treatmentId = currentValue; + } + + // create a new form field to place in the td cell + var field = Ext4.create(editor.type, Ext4.apply(editor.config, config)); + + // add listeners for when to apply the updated value and clear the input field + field.on('change', this.updateStoreValueForCellEdit, this, {buffer: 500}); + } + }, + + getCurrentCellValue : function(column, record, dataIndex, outerDataIndex, subgridIndex) + { + return Ext4.isString(outerDataIndex) ? record.get(outerDataIndex)[subgridIndex][dataIndex] : record.get(dataIndex); + }, + + updateStoreValueForCellEdit : function(field) + { + var fieldName = field.getName(), + newValue = field.getValue(), + index = field.storeIndex, + record = this.getStore().getAt(index), + dataFilterValue = field.dataFilterValue, + outerDataIndex = field.outerDataIndex, + subgridIndex = Number(field.subgridIndex); + + // suspend events on cell update so that we don't re-render the dataview + this.getStore().suspendEvents(); + + if (Ext4.isString(outerDataIndex)) + { + if (!isNaN(subgridIndex) && Ext4.isArray(record.get(outerDataIndex))) + this.updateSubgridRecordValue(record, outerDataIndex, subgridIndex, fieldName, newValue); + } + else + { + var column = this.getColumnConfig(fieldName, dataFilterValue, outerDataIndex); + this.updateStoreRecordValue(record, column, newValue, field); + } + + // update the missing-required cls based on the new field value + if (field.required) + { + if (newValue == null || newValue == '') + Ext4.get(field.renderTo).addCls('missing-required'); + else + Ext4.get(field.renderTo).removeCls('missing-required'); + } + + // resume store events so that adding and deleting will re-render the dataview + this.getStore().resumeEvents(); + }, + + updateSubgridRecordValue : function(record, outerDataIndex, subgridIndex, fieldName, newValue) + { + if (Ext4.isString(newValue)) + newValue.trim(); + + record.get(outerDataIndex)[subgridIndex][fieldName] = newValue; + this.fireEvent('celledited', this, fieldName, newValue); + }, + + updateStoreRecordValue : function(record, column, newValue, field) + { + if (Ext4.isString(newValue)) + newValue.trim(); + + record.set(column.dataIndex, newValue); + this.fireEvent('celledited', this, column.dataIndex, newValue); + }, + + removeOuterRecord : function(title, record) + { + var msg = this.getDeleteConfirmationMsg() != null ? this.getDeleteConfirmationMsg() : 'Are you sure you want to delete the selected row?'; + + Ext4.Msg.confirm('Confirm Delete: ' + title, msg, function(btn) + { + if (btn == 'yes') + { + this.fireEvent('beforerowdeleted', this, record); + + // suspend events on remove so that we don't re-render the dataview twice + this.getStore().suspendEvents(); + this.getStore().remove(record); + this.getStore().resumeEvents(); + this.refresh(true); + } + }, this); + }, + + confirmRemoveSubgridRecord : function(target, record) + { + var subgridDataIndex = target.getAttribute('subgrid-data-index'), + subgridArr = record.get(subgridDataIndex); + + if (Ext4.isArray(subgridArr)) + { + Ext4.Msg.confirm('Confirm Delete: ' + subgridDataIndex, 'Are you sure you want to delete the selected row?', function(btn) + { + if (btn == 'yes') + { + this.removeSubgridRecord(target, record); + this.refresh(true); + } + }, this); + } + }, + + removeSubgridRecord : function(target, record) + { + var subgridDataIndex = target.getAttribute('subgrid-data-index'), + subgridArr = record.get(subgridDataIndex); + + subgridArr.splice(target.getAttribute('subgrid-index'), 1); + }, + + onDataViewRefresh : function(view) + { + this.attachCellEditors(view); + this.attachAddRowListeners(view); + this.doLayout(); + this.fireRenderCompleteTask.delay(250); + }, + + attachCellEditors : function(view) + { + // attach cell editors for each of the store records (i.e. tr.row elements in the table) + var index = 0; + Ext4.each(this.getStore().getRange(), function(record) + { + var targetCellEls = Ext4.DomQuery.select('tr.data-row:nth(' + (index+1) + ') td.cell-value', view.getEl().dom); + Ext4.each(targetCellEls, function(targetCell) + { + this.createNewCellEditField(targetCell, record, index); + }, this); + + index++; + }, this); + }, + + attachAddRowListeners : function(view) + { + var addIconEls = Ext4.DomQuery.select('i.add-new-row', view.getEl().dom); + + Ext4.each(addIconEls, function(addIconEl) + { + if (addIconEl.hasAttribute('data-index')) + Ext4.get(addIconEl).on('click', this.addNewSubgridRow, this); + else + Ext4.get(addIconEl).on('click', this.addNewOuterRow, this); + }, this); + }, + + addNewOuterRow : function() + { + // suspend events on insert so that we don't re-render the dataview twice + this.getStore().suspendEvents(); + this.getStore().insert(this.getStore().getCount(), this.getNewModelInstance()); + this.getStore().resumeEvents(); + + // on refresh, call to give focus to the first column of the new row + this.on('renderviewcomplete', function(){ + this.giveCellInputFocus('table.outer tr.data-row:last td.cell-value:first input'); + }, this, {single: true}); + + this.refresh(); + }, + + addNewSubgridRow : function(event, target) + { + var dataIndex = target.getAttribute('data-index'), + rowIndex = Number(target.getAttribute('outer-index')); + + if (Ext4.isString(dataIndex) && Ext4.isNumber(rowIndex)) + { + var record = this.getStore().getAt(rowIndex), + dataIndexArr = record.get(dataIndex); + + if (Ext4.isArray(dataIndexArr)) + record.set(dataIndex, dataIndexArr.concat([{}])); + + // on refresh, call to give focus to the first column of the new row + this.on('renderviewcomplete', function(){ + var selector = 'table.subgrid-' + dataIndex + ':nth(' + (rowIndex+1) + ') tr.subrow:last td.cell-value:first input'; + this.giveCellInputFocus(selector); + }, this, {single: true}); + + this.refresh(); + } + }, + + giveCellInputFocus : function(selector, queryFullPage) + { + var cellInputField = Ext4.DomQuery.selectNode(selector, queryFullPage ? undefined : this.getDataView().getEl().dom); + if (cellInputField) + cellInputField.focus(); + }, + + refresh : function(hasChanges) + { + this.getDataView().refresh(); + + if (hasChanges) + this.fireEvent('dirtychange', this); + }, + + getColumnConfig : function(dataIndex, dataFilterValue, parentDataIndex) + { + var columns = this.getColumnConfigs(), matchingColumn = null; + + // if the parentDataIndex is defined, then we are looking for the subgrid column editor config + if (Ext4.isString(parentDataIndex)) + { + var colIndex = Ext4.pluck(columns, 'dataIndex').indexOf(parentDataIndex); + if (colIndex > -1 && columns[colIndex].hasOwnProperty('subgridConfig') && Ext4.isArray(columns[colIndex].subgridConfig.columns)) + columns = columns[colIndex].subgridConfig.columns; + else + return null; + } + + Ext4.each(columns, function(column) + { + if (column.dataIndex == dataIndex && (!Ext4.isDefined(dataFilterValue) || column.dataIndexArrFilterValue == dataFilterValue)) + { + matchingColumn = column; + return false; // break; + } + }, this); + + return matchingColumn; + }, + + getColumnEditorConfig : function(column) + { + if (column != null && column.hasOwnProperty('editorType') && column.hasOwnProperty('editorConfig')) + { + return { + type: column.editorType, + config: Ext4.isFunction(column.editorConfig) ? column.editorConfig.call(this) : column.editorConfig + }; + } + + return null; + }, + + loadDataViewStore : function() + { + // since some tables might need information from the store, wait to add the data view until the store loads + this.getStore().on('load', function() { + this.add(this.getDataView()); + this.fireEvent('loadcomplete', this); + }, this, {single: true}); + }, + + getStore : function() + { + throw "getStore must be overridden in subclass"; + }, + + getNewModelInstance : function() + { + throw "getNewModelInstance must be overridden in subclass"; + }, + + getColumnConfigs : function() + { + throw "getColumnConfigs must be overridden in subclass"; + }, + + getDeleteConfirmationMsg : function() + { + return null; + } }); \ No newline at end of file diff --git a/study/webapp/study/vaccineDesign/BaseDataViewAddVisit.js b/studydesign/webapp/study/vaccineDesign/BaseDataViewAddVisit.js similarity index 97% rename from study/webapp/study/vaccineDesign/BaseDataViewAddVisit.js rename to studydesign/webapp/study/vaccineDesign/BaseDataViewAddVisit.js index 0184a4d0..dabfda02 100644 --- a/study/webapp/study/vaccineDesign/BaseDataViewAddVisit.js +++ b/studydesign/webapp/study/vaccineDesign/BaseDataViewAddVisit.js @@ -1,99 +1,99 @@ -/* - * Copyright (c) 2016 LabKey Corporation - * - * Licensed under the Apache License, Version 2.0: http://www.apache.org/licenses/LICENSE-2.0 - */ - -Ext4.define('LABKEY.VaccineDesign.BaseDataViewAddVisit', { - extend: 'LABKEY.VaccineDesign.BaseDataView', - - getVisitStore : function() - { - throw new Error("getVisitStore must be overridden in subclass"); - }, - - //Override - getAddNewRowTpl : function(columns, dataIndex) - { - var tplArr = []; - - if (!this.disableEdit) - { - tplArr.push(''); - tplArr.push(' '); - tplArr.push(''); - tplArr.push(' Add new row   '); - tplArr.push(' Add new ' + this.visitNoun.toLowerCase() + ''); - tplArr.push(''); - tplArr.push(''); - } - - return tplArr; - }, - - //Override - attachAddRowListeners : function(view) - { - this.callParent([view]); - - var addIconEls = Ext4.DomQuery.select('i.add-visit-column', view.getEl().dom); - - Ext4.each(addIconEls, function(addIconEl) - { - Ext4.get(addIconEl).on('click', this.addNewVisitColumn, this); - }, this); - }, - - addNewVisitColumn : function() - { - var win = Ext4.create('LABKEY.VaccineDesign.VisitWindow', { - title: 'Add ' + this.visitNoun, - visitNoun: this.visitNoun, - visitStore: this.getVisitStore(), - listeners: { - scope: this, - closewindow: function(){ - win.close(); - }, - selectexistingvisit: function(w, visitId){ - win.close(); - - // if the 'ALL' option was selected, show all of the visits in the table - if (visitId == 'ALL') - { - Ext4.each(this.getVisitStore().getRange(), function(record) - { - record.set('Included', true); - }, this); - } - // set the selected visit to be included - else - { - this.getVisitStore().findRecord('RowId', visitId).set('Included', true); - } - - this.updateDataViewTemplate(); - }, - newvisitcreated: function(w, newVisitData){ - win.close(); - - // add the new visit to the store - var newVisitRec = LABKEY.VaccineDesign.Visit.create(newVisitData); - newVisitRec.set('Included', true); - this.getVisitStore().add(newVisitRec); - - this.updateDataViewTemplate(); - } - } - }); - - win.show(); - }, - - updateDataViewTemplate : function() - { - // explicitly clear the column configs so the new visit column will be added - this.columnConfigs = null; - this.getDataView().setTemplate(this.getDataViewTpl()); - } +/* + * Copyright (c) 2016 LabKey Corporation + * + * Licensed under the Apache License, Version 2.0: http://www.apache.org/licenses/LICENSE-2.0 + */ + +Ext4.define('LABKEY.VaccineDesign.BaseDataViewAddVisit', { + extend: 'LABKEY.VaccineDesign.BaseDataView', + + getVisitStore : function() + { + throw new Error("getVisitStore must be overridden in subclass"); + }, + + //Override + getAddNewRowTpl : function(columns, dataIndex) + { + var tplArr = []; + + if (!this.disableEdit) + { + tplArr.push(''); + tplArr.push(' '); + tplArr.push(''); + tplArr.push(' Add new row   '); + tplArr.push(' Add new ' + this.visitNoun.toLowerCase() + ''); + tplArr.push(''); + tplArr.push(''); + } + + return tplArr; + }, + + //Override + attachAddRowListeners : function(view) + { + this.callParent([view]); + + var addIconEls = Ext4.DomQuery.select('i.add-visit-column', view.getEl().dom); + + Ext4.each(addIconEls, function(addIconEl) + { + Ext4.get(addIconEl).on('click', this.addNewVisitColumn, this); + }, this); + }, + + addNewVisitColumn : function() + { + var win = Ext4.create('LABKEY.VaccineDesign.VisitWindow', { + title: 'Add ' + this.visitNoun, + visitNoun: this.visitNoun, + visitStore: this.getVisitStore(), + listeners: { + scope: this, + closewindow: function(){ + win.close(); + }, + selectexistingvisit: function(w, visitId){ + win.close(); + + // if the 'ALL' option was selected, show all of the visits in the table + if (visitId == 'ALL') + { + Ext4.each(this.getVisitStore().getRange(), function(record) + { + record.set('Included', true); + }, this); + } + // set the selected visit to be included + else + { + this.getVisitStore().findRecord('RowId', visitId).set('Included', true); + } + + this.updateDataViewTemplate(); + }, + newvisitcreated: function(w, newVisitData){ + win.close(); + + // add the new visit to the store + var newVisitRec = LABKEY.VaccineDesign.Visit.create(newVisitData); + newVisitRec.set('Included', true); + this.getVisitStore().add(newVisitRec); + + this.updateDataViewTemplate(); + } + } + }); + + win.show(); + }, + + updateDataViewTemplate : function() + { + // explicitly clear the column configs so the new visit column will be added + this.columnConfigs = null; + this.getDataView().setTemplate(this.getDataViewTpl()); + } }); \ No newline at end of file diff --git a/study/webapp/study/vaccineDesign/Models.js b/studydesign/webapp/study/vaccineDesign/Models.js similarity index 97% rename from study/webapp/study/vaccineDesign/Models.js rename to studydesign/webapp/study/vaccineDesign/Models.js index b1a645b2..209772bd 100644 --- a/study/webapp/study/vaccineDesign/Models.js +++ b/studydesign/webapp/study/vaccineDesign/Models.js @@ -1,82 +1,82 @@ -/* - * Copyright (c) 2016-2017 LabKey Corporation - * - * Licensed under the Apache License, Version 2.0: http://www.apache.org/licenses/LICENSE-2.0 - */ -Ext4.define('LABKEY.VaccineDesign.Product', { - extend : 'Ext.data.Model', - idgen: 'sequential', - fields : [ - {name : 'RowId', defaultValue: undefined}, - {name : 'Label', type : 'string'}, - {name : 'Role', type : 'string'}, - {name : 'Type', type : 'string'}, - {name : 'Antigens', defaultValue: []}, - {name : 'DoseAndRoute', defaultValue: []} - ] -}); - -Ext4.define('LABKEY.VaccineDesign.Treatment', { - extend : 'Ext.data.Model', - idgen: 'sequential', - fields : [ - {name : 'RowId', defaultValue: undefined}, - {name : 'Label', type : 'string'}, - {name : 'Description', type : 'string'}, - {name : 'Immunogen', defaultValue: []}, - {name : 'Adjuvant', defaultValue: []}, - {name : 'Challenge', defaultValue: []} - ] -}); - -Ext4.define('LABKEY.VaccineDesign.Cohort', { - extend : 'Ext.data.Model', - idgen: 'sequential', - fields : [ - {name : 'RowId', defaultValue: undefined}, - {name : 'Label', type : 'string'}, - // the DataView XTemplate gets mad if this is defined as type 'int' - {name : 'SubjectCount', type : 'string'}, - {name : 'CanDelete', type : 'boolean'}, - {name : 'VisitMap', defaultValue: []} - ] -}); - -Ext4.define('LABKEY.VaccineDesign.Assay', { - extend : 'Ext.data.Model', - idgen: 'sequential', - fields : [ - {name : 'RowId', defaultValue: undefined}, - {name : 'AssayName', type : 'string'}, - {name : 'DataSet', type : 'int'}, - {name : 'Description', type : 'string'}, - {name : 'Lab', type : 'string'}, - {name : 'LocationId', type : 'int'}, - {name : 'SampleType', type : 'string'}, - {name : 'Source', type : 'string'}, - {name : 'TubeType', type : 'string'}, - {name : 'SampleQuantity', type : 'float'}, - {name : 'SampleUnits', type : 'string'}, - {name : 'VisitMap', defaultValue: []} - ] -}); - -Ext4.define('LABKEY.VaccineDesign.AssaySpecimenVisit', { - extend : 'Ext.data.Model', - fields : [ - {name : 'RowId', type : 'int'}, - {name : 'VisitId', type : 'int'}, - {name : 'AssaySpecimenId', type : 'int'} - ] -}); - -Ext4.define('LABKEY.VaccineDesign.Visit', { - extend : 'Ext.data.Model', - fields : [ - {name : 'RowId', type : 'int'}, - {name : 'Label', type : 'string'}, - {name : 'DisplayOrder', type : 'int'}, - {name : 'SequenceNumMin', type : 'numeric'}, - {name : 'Included', type : 'boolean'} - ] +/* + * Copyright (c) 2016-2017 LabKey Corporation + * + * Licensed under the Apache License, Version 2.0: http://www.apache.org/licenses/LICENSE-2.0 + */ +Ext4.define('LABKEY.VaccineDesign.Product', { + extend : 'Ext.data.Model', + idgen: 'sequential', + fields : [ + {name : 'RowId', defaultValue: undefined}, + {name : 'Label', type : 'string'}, + {name : 'Role', type : 'string'}, + {name : 'Type', type : 'string'}, + {name : 'Antigens', defaultValue: []}, + {name : 'DoseAndRoute', defaultValue: []} + ] +}); + +Ext4.define('LABKEY.VaccineDesign.Treatment', { + extend : 'Ext.data.Model', + idgen: 'sequential', + fields : [ + {name : 'RowId', defaultValue: undefined}, + {name : 'Label', type : 'string'}, + {name : 'Description', type : 'string'}, + {name : 'Immunogen', defaultValue: []}, + {name : 'Adjuvant', defaultValue: []}, + {name : 'Challenge', defaultValue: []} + ] +}); + +Ext4.define('LABKEY.VaccineDesign.Cohort', { + extend : 'Ext.data.Model', + idgen: 'sequential', + fields : [ + {name : 'RowId', defaultValue: undefined}, + {name : 'Label', type : 'string'}, + // the DataView XTemplate gets mad if this is defined as type 'int' + {name : 'SubjectCount', type : 'string'}, + {name : 'CanDelete', type : 'boolean'}, + {name : 'VisitMap', defaultValue: []} + ] +}); + +Ext4.define('LABKEY.VaccineDesign.Assay', { + extend : 'Ext.data.Model', + idgen: 'sequential', + fields : [ + {name : 'RowId', defaultValue: undefined}, + {name : 'AssayName', type : 'string'}, + {name : 'DataSet', type : 'int'}, + {name : 'Description', type : 'string'}, + {name : 'Lab', type : 'string'}, + {name : 'LocationId', type : 'int'}, + {name : 'SampleType', type : 'string'}, + {name : 'Source', type : 'string'}, + {name : 'TubeType', type : 'string'}, + {name : 'SampleQuantity', type : 'float'}, + {name : 'SampleUnits', type : 'string'}, + {name : 'VisitMap', defaultValue: []} + ] +}); + +Ext4.define('LABKEY.VaccineDesign.AssaySpecimenVisit', { + extend : 'Ext.data.Model', + fields : [ + {name : 'RowId', type : 'int'}, + {name : 'VisitId', type : 'int'}, + {name : 'AssaySpecimenId', type : 'int'} + ] +}); + +Ext4.define('LABKEY.VaccineDesign.Visit', { + extend : 'Ext.data.Model', + fields : [ + {name : 'RowId', type : 'int'}, + {name : 'Label', type : 'string'}, + {name : 'DisplayOrder', type : 'int'}, + {name : 'SequenceNumMin', type : 'numeric'}, + {name : 'Included', type : 'boolean'} + ] }); \ No newline at end of file diff --git a/study/webapp/study/vaccineDesign/StudyProducts.js b/studydesign/webapp/study/vaccineDesign/StudyProducts.js similarity index 97% rename from study/webapp/study/vaccineDesign/StudyProducts.js rename to studydesign/webapp/study/vaccineDesign/StudyProducts.js index c757d000..325173a9 100644 --- a/study/webapp/study/vaccineDesign/StudyProducts.js +++ b/studydesign/webapp/study/vaccineDesign/StudyProducts.js @@ -1,508 +1,508 @@ -/* - * Copyright (c) 2016-2017 LabKey Corporation - * - * Licensed under the Apache License, Version 2.0: http://www.apache.org/licenses/LICENSE-2.0 - */ -Ext4.define('LABKEY.VaccineDesign.StudyProductsPanel', { - extend : 'Ext.panel.Panel', - - border : false, - - bodyStyle : 'background-color: transparent;', - - minWidth: 1350, - - disableEdit : true, - - dirty : false, - - returnUrl : null, - - initComponent : function() - { - this.items = [ - this.getImmunogensGrid(), - this.getAdjuvantGrid(), - this.getChallengesGrid(), - this.getButtonBar() - ]; - - this.callParent(); - - window.onbeforeunload = LABKEY.beforeunload(this.beforeUnload, this); - }, - - getImmunogensGrid : function() - { - if (!this.immunogenGrid) - { - this.immunogenGrid = Ext4.create('LABKEY.VaccineDesign.ImmunogensGrid', { - disableEdit: this.disableEdit - }); - - this.immunogenGrid.on('dirtychange', this.enableSaveButton, this); - this.immunogenGrid.on('celledited', this.enableSaveButton, this); - } - - return this.immunogenGrid; - }, - - getAdjuvantGrid : function() - { - if (!this.adjuvantGrid) - { - this.adjuvantGrid = Ext4.create('LABKEY.VaccineDesign.AdjuvantsGrid', { - padding: '20px 0', - disableEdit: this.disableEdit - }); - - this.adjuvantGrid.on('dirtychange', this.enableSaveButton, this); - this.adjuvantGrid.on('celledited', this.enableSaveButton, this); - } - - return this.adjuvantGrid; - }, - - getChallengesGrid : function() - { - if (!this.challengesGrid) - { - this.challengesGrid = Ext4.create('LABKEY.VaccineDesign.ChallengesGrid', { - padding: '20px 0', - disableEdit: this.disableEdit - }); - - this.challengesGrid.on('dirtychange', this.enableSaveButton, this); - this.challengesGrid.on('celledited', this.enableSaveButton, this); - } - - return this.challengesGrid; - }, - - getButtonBar : function() - { - if (!this.buttonBar) - { - this.buttonBar = Ext4.create('Ext.toolbar.Toolbar', { - dock: 'bottom', - ui: 'footer', - padding: 0, - style : 'background-color: transparent;', - defaults: {width: 75}, - items: [this.getSaveButton(), this.getCancelButton()] - }); - } - - return this.buttonBar; - }, - - getSaveButton : function() - { - if (!this.saveButton) - { - this.saveButton = Ext4.create('Ext.button.Button', { - text: 'Save', - disabled: true, - hidden: this.disableEdit, - handler: this.saveStudyProducts, - scope: this - }); - } - - return this.saveButton; - }, - - enableSaveButton : function() - { - this.setDirty(true); - this.getSaveButton().enable(); - }, - - getCancelButton : function() - { - if (!this.cancelButton) - { - this.cancelButton = Ext4.create('Ext.button.Button', { - text: this.disableEdit ? 'Done' : 'Cancel', - handler: this.goToReturnURL, - scope: this - }); - } - - return this.cancelButton; - }, - - saveStudyProducts : function() - { - var studyProducts = []; - - this.getEl().mask('Saving...'); - - Ext4.each(this.getImmunogensGrid().getStore().getRange(), function(record) - { - var recData = Ext4.clone(record.data); - - // drop any empty antigen rows that were just added - var antigenArr = []; - Ext4.each(recData['Antigens'], function(antigen) - { - if (Ext4.isDefined(antigen['RowId']) || LABKEY.VaccineDesign.Utils.objectHasData(antigen)) - antigenArr.push(antigen); - }, this); - recData['Antigens'] = antigenArr; - - // drop any empty rows that were just added - var hasData = recData['Label'] != '' || recData['Type'] != '' || recData['Antigens'].length > 0; - if (Ext4.isDefined(recData['RowId']) || hasData) - studyProducts.push(recData); - }, this); - - Ext4.each(this.getAdjuvantGrid().getStore().getRange(), function(record) - { - // drop any empty rows that were just added - if (Ext4.isDefined(record.get('RowId')) || record.get('Label') != '') - studyProducts.push(Ext4.clone(record.data)); - }, this); - - Ext4.each(this.getChallengesGrid().getStore().getRange(), function(record) - { - var hasData = record['Label'] != '' || record['Type'] != ''; - if (Ext4.isDefined(record.get('RowId')) || hasData) - studyProducts.push(Ext4.clone(record.data)); - }, this); - - LABKEY.Ajax.request({ - url : LABKEY.ActionURL.buildURL('study-design', 'updateStudyProducts.api'), - method : 'POST', - jsonData: { products: studyProducts }, - scope: this, - success: function(response) - { - var resp = Ext4.decode(response.responseText); - if (resp.success) - this.goToReturnURL(); - else - this.onFailure(); - }, - failure: function(response) - { - var resp = Ext4.decode(response.responseText); - if (resp.errors) - this.onFailure(Ext4.Array.pluck(resp.errors, 'message').join('
        ')); - else - this.onFailure(resp.exception); - } - }); - }, - - goToReturnURL : function() - { - this.setDirty(false); - window.location = this.returnUrl; - }, - - onFailure : function(text) - { - Ext4.Msg.show({ - title: 'Error', - msg: text || 'Unknown error occurred.', - icon: Ext4.Msg.ERROR, - buttons: Ext4.Msg.OK - }); - - this.getEl().unmask(); - }, - - setDirty : function(dirty) - { - this.dirty = dirty; - LABKEY.Utils.signalWebDriverTest("studyProductsDirty", dirty); - }, - - isDirty : function() - { - return this.dirty; - }, - - beforeUnload : function() - { - if (!this.disableEdit && this.isDirty()) - return 'Please save your changes.'; - } -}); - -Ext4.define('LABKEY.VaccineDesign.StudyProductsGrid', { - - extend : 'LABKEY.VaccineDesign.BaseDataView', - - filterRole : null, - - showDoseRoute : true, - - //Override - getStore : function() - { - if (!this.store) - { - this.store = Ext4.create('Ext.data.Store', { - model : 'LABKEY.VaccineDesign.Product', - proxy: { - type: 'ajax', - url : LABKEY.ActionURL.buildURL("study-design", "getStudyProducts", null, {role: this.filterRole}), - reader: { - type: 'json', - root: 'products' - } - }, - sorters: [{ property: 'RowId', direction: 'ASC' }], - autoLoad: true - }); - } - - return this.store; - }, - - //Override - getNewModelInstance : function() - { - return LABKEY.VaccineDesign.Product.create({Role: this.filterRole}); - }, - - //Override - getDeleteConfirmationMsg : function() - { - return 'Are you sure you want to delete the selected study product? ' - + 'Note: if this study product is being used by any treatment definitions, ' - + 'those associations will also be deleted upon save.'; - } -}); - -Ext4.define('LABKEY.VaccineDesign.ImmunogensGrid', { - extend : 'LABKEY.VaccineDesign.StudyProductsGrid', - - cls : 'study-vaccine-design vaccine-design-immunogens', - - mainTitle : 'Immunogens', - - filterRole : 'Immunogen', - - studyDesignQueryNames : ['StudyDesignImmunogenTypes', 'StudyDesignGenes', 'StudyDesignSubTypes', 'StudyDesignRoutes'], - - //Override - getColumnConfigs : function() - { - if (!this.columnConfigs) - { - var width = 0; // add to the running width as we go through which columns to show in the config - - this.columnConfigs = [{ - label: 'Label', - width: 200, - dataIndex: 'Label', - required: true, - editorType: 'Ext.form.field.Text', - editorConfig: LABKEY.VaccineDesign.Utils.getStudyDesignTextConfig('Label', 185) - }, { - label: 'Type', - width: 200, - dataIndex: 'Type', - queryName: 'StudyDesignImmunogenTypes', - editorType: 'LABKEY.ext4.ComboBox', - editorConfig: LABKEY.VaccineDesign.Utils.getStudyDesignComboConfig('Type', 185, 'StudyDesignImmunogenTypes') - },{ - label: 'HIV Antigens', - width: 600, - dataIndex: 'Antigens', - subgridConfig: { - columns: [{ - label: 'Gene', - width: 140, - dataIndex: 'Gene', - queryName: 'StudyDesignGenes', - editorType: 'LABKEY.ext4.ComboBox', - editorConfig: LABKEY.VaccineDesign.Utils.getStudyDesignComboConfig('Gene', 125, 'StudyDesignGenes') - },{ - label: 'Subtype', - width: 140, - dataIndex: 'SubType', - queryName: 'StudyDesignSubTypes', - editorType: 'LABKEY.ext4.ComboBox', - editorConfig: LABKEY.VaccineDesign.Utils.getStudyDesignComboConfig('SubType', 125, 'StudyDesignSubTypes') - },{ - label: 'GenBank Id', - width: 150, - dataIndex: 'GenBankId', - editorType: 'Ext.form.field.Text', - editorConfig: LABKEY.VaccineDesign.Utils.getStudyDesignTextConfig('GenBankId', 135) - },{ - label: 'Sequence', - width: 150, - dataIndex: 'Sequence', - editorType: 'Ext.form.field.Text', - editorConfig: LABKEY.VaccineDesign.Utils.getStudyDesignTextConfig('Sequence', 135) - }] - } - }]; - width += 1000; - - if (this.showDoseRoute) - { - this.columnConfigs.push({ - label: 'Doses and Routes', - width: 315, - dataIndex: 'DoseAndRoute', - subgridConfig: { - columns: [{ - label: 'Dose', - width: 140, - dataIndex: 'Dose', - editorType: 'Ext.form.field.Text', - editorConfig: LABKEY.VaccineDesign.Utils.getStudyDesignTextConfig('Dose', 125) - },{ - label: 'Route', - width: 140, - dataIndex: 'Route', - queryName: 'StudyDesignRoutes', - editorType: 'LABKEY.ext4.ComboBox', - editorConfig: LABKEY.VaccineDesign.Utils.getStudyDesignComboConfig('Route', 125, 'StudyDesignRoutes') - }] - } - }); - width += 315; - } - - this.setWidth(width); - } - - return this.columnConfigs; - } -}); - -Ext4.define('LABKEY.VaccineDesign.AdjuvantsGrid', { - extend : 'LABKEY.VaccineDesign.StudyProductsGrid', - - cls : 'study-vaccine-design vaccine-design-adjuvants', - - mainTitle : 'Adjuvants', - - filterRole : 'Adjuvant', - - studyDesignQueryNames : ['StudyDesignRoutes'], - - //Override - getColumnConfigs : function() - { - if (!this.columnConfigs) - { - var width = 0; // add to the running width as we go through which columns to show in the config - - this.columnConfigs = [{ - label: 'Label', - width: 200, - dataIndex: 'Label', - required: true, - editorType: 'Ext.form.field.Text', - editorConfig: LABKEY.VaccineDesign.Utils.getStudyDesignTextConfig('Label', 185) - }]; - width += 200; - - if (this.showDoseRoute) - { - this.columnConfigs.push({ - label: 'Doses and Routes', - width: 330, - dataIndex: 'DoseAndRoute', - subgridConfig: { - columns: [{ - label: 'Dose', - width: 140, - dataIndex: 'Dose', - editorType: 'Ext.form.field.Text', - editorConfig: LABKEY.VaccineDesign.Utils.getStudyDesignTextConfig('Dose', 125) - }, { - label: 'Route', - width: 140, - dataIndex: 'Route', - queryName: 'StudyDesignRoutes', - editorType: 'LABKEY.ext4.ComboBox', - editorConfig: LABKEY.VaccineDesign.Utils.getStudyDesignComboConfig('Route', 125, 'StudyDesignRoutes') - }] - } - }); - width += 330; - } - - this.setWidth(width); - } - - return this.columnConfigs; - } -}); - -Ext4.define('LABKEY.VaccineDesign.ChallengesGrid', { - extend : 'LABKEY.VaccineDesign.StudyProductsGrid', - - cls : 'study-vaccine-design vaccine-design-challenges', - - mainTitle : 'Challenges', - - filterRole : 'Challenge', - - studyDesignQueryNames : ['StudyDesignChallengeTypes', 'StudyDesignRoutes'], - - //Override - getColumnConfigs : function() - { - if (!this.columnConfigs) - { - var width = 0; // add to the running width as we go through which columns to show in the config - - this.columnConfigs = [{ - label: 'Label', - width: 200, - dataIndex: 'Label', - required: true, - editorType: 'Ext.form.field.Text', - editorConfig: LABKEY.VaccineDesign.Utils.getStudyDesignTextConfig('Label', 185) - }, { - label: 'Type', - width: 200, - dataIndex: 'Type', - queryName: 'StudyDesignChallengeTypes', - editorType: 'LABKEY.ext4.ComboBox', - editorConfig: LABKEY.VaccineDesign.Utils.getStudyDesignComboConfig('Type', 185, 'StudyDesignChallengeTypes') - }]; - width += 400; - - if (this.showDoseRoute) - { - this.columnConfigs.push({ - label: 'Doses and Routes', - width: 330, - dataIndex: 'DoseAndRoute', - subgridConfig: { - columns: [{ - label: 'Dose', - width: 140, - dataIndex: 'Dose', - editorType: 'Ext.form.field.Text', - editorConfig: LABKEY.VaccineDesign.Utils.getStudyDesignTextConfig('Dose', 125) - }, { - label: 'Route', - width: 140, - dataIndex: 'Route', - queryName: 'StudyDesignRoutes', - editorType: 'LABKEY.ext4.ComboBox', - editorConfig: LABKEY.VaccineDesign.Utils.getStudyDesignComboConfig('Route', 125, 'StudyDesignRoutes') - }] - } - }); - width += 330; - } - - this.setWidth(width); - } - - return this.columnConfigs; - } +/* + * Copyright (c) 2016-2017 LabKey Corporation + * + * Licensed under the Apache License, Version 2.0: http://www.apache.org/licenses/LICENSE-2.0 + */ +Ext4.define('LABKEY.VaccineDesign.StudyProductsPanel', { + extend : 'Ext.panel.Panel', + + border : false, + + bodyStyle : 'background-color: transparent;', + + minWidth: 1350, + + disableEdit : true, + + dirty : false, + + returnUrl : null, + + initComponent : function() + { + this.items = [ + this.getImmunogensGrid(), + this.getAdjuvantGrid(), + this.getChallengesGrid(), + this.getButtonBar() + ]; + + this.callParent(); + + window.onbeforeunload = LABKEY.beforeunload(this.beforeUnload, this); + }, + + getImmunogensGrid : function() + { + if (!this.immunogenGrid) + { + this.immunogenGrid = Ext4.create('LABKEY.VaccineDesign.ImmunogensGrid', { + disableEdit: this.disableEdit + }); + + this.immunogenGrid.on('dirtychange', this.enableSaveButton, this); + this.immunogenGrid.on('celledited', this.enableSaveButton, this); + } + + return this.immunogenGrid; + }, + + getAdjuvantGrid : function() + { + if (!this.adjuvantGrid) + { + this.adjuvantGrid = Ext4.create('LABKEY.VaccineDesign.AdjuvantsGrid', { + padding: '20px 0', + disableEdit: this.disableEdit + }); + + this.adjuvantGrid.on('dirtychange', this.enableSaveButton, this); + this.adjuvantGrid.on('celledited', this.enableSaveButton, this); + } + + return this.adjuvantGrid; + }, + + getChallengesGrid : function() + { + if (!this.challengesGrid) + { + this.challengesGrid = Ext4.create('LABKEY.VaccineDesign.ChallengesGrid', { + padding: '20px 0', + disableEdit: this.disableEdit + }); + + this.challengesGrid.on('dirtychange', this.enableSaveButton, this); + this.challengesGrid.on('celledited', this.enableSaveButton, this); + } + + return this.challengesGrid; + }, + + getButtonBar : function() + { + if (!this.buttonBar) + { + this.buttonBar = Ext4.create('Ext.toolbar.Toolbar', { + dock: 'bottom', + ui: 'footer', + padding: 0, + style : 'background-color: transparent;', + defaults: {width: 75}, + items: [this.getSaveButton(), this.getCancelButton()] + }); + } + + return this.buttonBar; + }, + + getSaveButton : function() + { + if (!this.saveButton) + { + this.saveButton = Ext4.create('Ext.button.Button', { + text: 'Save', + disabled: true, + hidden: this.disableEdit, + handler: this.saveStudyProducts, + scope: this + }); + } + + return this.saveButton; + }, + + enableSaveButton : function() + { + this.setDirty(true); + this.getSaveButton().enable(); + }, + + getCancelButton : function() + { + if (!this.cancelButton) + { + this.cancelButton = Ext4.create('Ext.button.Button', { + text: this.disableEdit ? 'Done' : 'Cancel', + handler: this.goToReturnURL, + scope: this + }); + } + + return this.cancelButton; + }, + + saveStudyProducts : function() + { + var studyProducts = []; + + this.getEl().mask('Saving...'); + + Ext4.each(this.getImmunogensGrid().getStore().getRange(), function(record) + { + var recData = Ext4.clone(record.data); + + // drop any empty antigen rows that were just added + var antigenArr = []; + Ext4.each(recData['Antigens'], function(antigen) + { + if (Ext4.isDefined(antigen['RowId']) || LABKEY.VaccineDesign.Utils.objectHasData(antigen)) + antigenArr.push(antigen); + }, this); + recData['Antigens'] = antigenArr; + + // drop any empty rows that were just added + var hasData = recData['Label'] != '' || recData['Type'] != '' || recData['Antigens'].length > 0; + if (Ext4.isDefined(recData['RowId']) || hasData) + studyProducts.push(recData); + }, this); + + Ext4.each(this.getAdjuvantGrid().getStore().getRange(), function(record) + { + // drop any empty rows that were just added + if (Ext4.isDefined(record.get('RowId')) || record.get('Label') != '') + studyProducts.push(Ext4.clone(record.data)); + }, this); + + Ext4.each(this.getChallengesGrid().getStore().getRange(), function(record) + { + var hasData = record['Label'] != '' || record['Type'] != ''; + if (Ext4.isDefined(record.get('RowId')) || hasData) + studyProducts.push(Ext4.clone(record.data)); + }, this); + + LABKEY.Ajax.request({ + url : LABKEY.ActionURL.buildURL('study-design', 'updateStudyProducts.api'), + method : 'POST', + jsonData: { products: studyProducts }, + scope: this, + success: function(response) + { + var resp = Ext4.decode(response.responseText); + if (resp.success) + this.goToReturnURL(); + else + this.onFailure(); + }, + failure: function(response) + { + var resp = Ext4.decode(response.responseText); + if (resp.errors) + this.onFailure(Ext4.Array.pluck(resp.errors, 'message').join('
        ')); + else + this.onFailure(resp.exception); + } + }); + }, + + goToReturnURL : function() + { + this.setDirty(false); + window.location = this.returnUrl; + }, + + onFailure : function(text) + { + Ext4.Msg.show({ + title: 'Error', + msg: text || 'Unknown error occurred.', + icon: Ext4.Msg.ERROR, + buttons: Ext4.Msg.OK + }); + + this.getEl().unmask(); + }, + + setDirty : function(dirty) + { + this.dirty = dirty; + LABKEY.Utils.signalWebDriverTest("studyProductsDirty", dirty); + }, + + isDirty : function() + { + return this.dirty; + }, + + beforeUnload : function() + { + if (!this.disableEdit && this.isDirty()) + return 'Please save your changes.'; + } +}); + +Ext4.define('LABKEY.VaccineDesign.StudyProductsGrid', { + + extend : 'LABKEY.VaccineDesign.BaseDataView', + + filterRole : null, + + showDoseRoute : true, + + //Override + getStore : function() + { + if (!this.store) + { + this.store = Ext4.create('Ext.data.Store', { + model : 'LABKEY.VaccineDesign.Product', + proxy: { + type: 'ajax', + url : LABKEY.ActionURL.buildURL("study-design", "getStudyProducts", null, {role: this.filterRole}), + reader: { + type: 'json', + root: 'products' + } + }, + sorters: [{ property: 'RowId', direction: 'ASC' }], + autoLoad: true + }); + } + + return this.store; + }, + + //Override + getNewModelInstance : function() + { + return LABKEY.VaccineDesign.Product.create({Role: this.filterRole}); + }, + + //Override + getDeleteConfirmationMsg : function() + { + return 'Are you sure you want to delete the selected study product? ' + + 'Note: if this study product is being used by any treatment definitions, ' + + 'those associations will also be deleted upon save.'; + } +}); + +Ext4.define('LABKEY.VaccineDesign.ImmunogensGrid', { + extend : 'LABKEY.VaccineDesign.StudyProductsGrid', + + cls : 'study-vaccine-design vaccine-design-immunogens', + + mainTitle : 'Immunogens', + + filterRole : 'Immunogen', + + studyDesignQueryNames : ['StudyDesignImmunogenTypes', 'StudyDesignGenes', 'StudyDesignSubTypes', 'StudyDesignRoutes'], + + //Override + getColumnConfigs : function() + { + if (!this.columnConfigs) + { + var width = 0; // add to the running width as we go through which columns to show in the config + + this.columnConfigs = [{ + label: 'Label', + width: 200, + dataIndex: 'Label', + required: true, + editorType: 'Ext.form.field.Text', + editorConfig: LABKEY.VaccineDesign.Utils.getStudyDesignTextConfig('Label', 185) + }, { + label: 'Type', + width: 200, + dataIndex: 'Type', + queryName: 'StudyDesignImmunogenTypes', + editorType: 'LABKEY.ext4.ComboBox', + editorConfig: LABKEY.VaccineDesign.Utils.getStudyDesignComboConfig('Type', 185, 'StudyDesignImmunogenTypes') + },{ + label: 'HIV Antigens', + width: 600, + dataIndex: 'Antigens', + subgridConfig: { + columns: [{ + label: 'Gene', + width: 140, + dataIndex: 'Gene', + queryName: 'StudyDesignGenes', + editorType: 'LABKEY.ext4.ComboBox', + editorConfig: LABKEY.VaccineDesign.Utils.getStudyDesignComboConfig('Gene', 125, 'StudyDesignGenes') + },{ + label: 'Subtype', + width: 140, + dataIndex: 'SubType', + queryName: 'StudyDesignSubTypes', + editorType: 'LABKEY.ext4.ComboBox', + editorConfig: LABKEY.VaccineDesign.Utils.getStudyDesignComboConfig('SubType', 125, 'StudyDesignSubTypes') + },{ + label: 'GenBank Id', + width: 150, + dataIndex: 'GenBankId', + editorType: 'Ext.form.field.Text', + editorConfig: LABKEY.VaccineDesign.Utils.getStudyDesignTextConfig('GenBankId', 135) + },{ + label: 'Sequence', + width: 150, + dataIndex: 'Sequence', + editorType: 'Ext.form.field.Text', + editorConfig: LABKEY.VaccineDesign.Utils.getStudyDesignTextConfig('Sequence', 135) + }] + } + }]; + width += 1000; + + if (this.showDoseRoute) + { + this.columnConfigs.push({ + label: 'Doses and Routes', + width: 315, + dataIndex: 'DoseAndRoute', + subgridConfig: { + columns: [{ + label: 'Dose', + width: 140, + dataIndex: 'Dose', + editorType: 'Ext.form.field.Text', + editorConfig: LABKEY.VaccineDesign.Utils.getStudyDesignTextConfig('Dose', 125) + },{ + label: 'Route', + width: 140, + dataIndex: 'Route', + queryName: 'StudyDesignRoutes', + editorType: 'LABKEY.ext4.ComboBox', + editorConfig: LABKEY.VaccineDesign.Utils.getStudyDesignComboConfig('Route', 125, 'StudyDesignRoutes') + }] + } + }); + width += 315; + } + + this.setWidth(width); + } + + return this.columnConfigs; + } +}); + +Ext4.define('LABKEY.VaccineDesign.AdjuvantsGrid', { + extend : 'LABKEY.VaccineDesign.StudyProductsGrid', + + cls : 'study-vaccine-design vaccine-design-adjuvants', + + mainTitle : 'Adjuvants', + + filterRole : 'Adjuvant', + + studyDesignQueryNames : ['StudyDesignRoutes'], + + //Override + getColumnConfigs : function() + { + if (!this.columnConfigs) + { + var width = 0; // add to the running width as we go through which columns to show in the config + + this.columnConfigs = [{ + label: 'Label', + width: 200, + dataIndex: 'Label', + required: true, + editorType: 'Ext.form.field.Text', + editorConfig: LABKEY.VaccineDesign.Utils.getStudyDesignTextConfig('Label', 185) + }]; + width += 200; + + if (this.showDoseRoute) + { + this.columnConfigs.push({ + label: 'Doses and Routes', + width: 330, + dataIndex: 'DoseAndRoute', + subgridConfig: { + columns: [{ + label: 'Dose', + width: 140, + dataIndex: 'Dose', + editorType: 'Ext.form.field.Text', + editorConfig: LABKEY.VaccineDesign.Utils.getStudyDesignTextConfig('Dose', 125) + }, { + label: 'Route', + width: 140, + dataIndex: 'Route', + queryName: 'StudyDesignRoutes', + editorType: 'LABKEY.ext4.ComboBox', + editorConfig: LABKEY.VaccineDesign.Utils.getStudyDesignComboConfig('Route', 125, 'StudyDesignRoutes') + }] + } + }); + width += 330; + } + + this.setWidth(width); + } + + return this.columnConfigs; + } +}); + +Ext4.define('LABKEY.VaccineDesign.ChallengesGrid', { + extend : 'LABKEY.VaccineDesign.StudyProductsGrid', + + cls : 'study-vaccine-design vaccine-design-challenges', + + mainTitle : 'Challenges', + + filterRole : 'Challenge', + + studyDesignQueryNames : ['StudyDesignChallengeTypes', 'StudyDesignRoutes'], + + //Override + getColumnConfigs : function() + { + if (!this.columnConfigs) + { + var width = 0; // add to the running width as we go through which columns to show in the config + + this.columnConfigs = [{ + label: 'Label', + width: 200, + dataIndex: 'Label', + required: true, + editorType: 'Ext.form.field.Text', + editorConfig: LABKEY.VaccineDesign.Utils.getStudyDesignTextConfig('Label', 185) + }, { + label: 'Type', + width: 200, + dataIndex: 'Type', + queryName: 'StudyDesignChallengeTypes', + editorType: 'LABKEY.ext4.ComboBox', + editorConfig: LABKEY.VaccineDesign.Utils.getStudyDesignComboConfig('Type', 185, 'StudyDesignChallengeTypes') + }]; + width += 400; + + if (this.showDoseRoute) + { + this.columnConfigs.push({ + label: 'Doses and Routes', + width: 330, + dataIndex: 'DoseAndRoute', + subgridConfig: { + columns: [{ + label: 'Dose', + width: 140, + dataIndex: 'Dose', + editorType: 'Ext.form.field.Text', + editorConfig: LABKEY.VaccineDesign.Utils.getStudyDesignTextConfig('Dose', 125) + }, { + label: 'Route', + width: 140, + dataIndex: 'Route', + queryName: 'StudyDesignRoutes', + editorType: 'LABKEY.ext4.ComboBox', + editorConfig: LABKEY.VaccineDesign.Utils.getStudyDesignComboConfig('Route', 125, 'StudyDesignRoutes') + }] + } + }); + width += 330; + } + + this.setWidth(width); + } + + return this.columnConfigs; + } }); \ No newline at end of file diff --git a/study/webapp/study/vaccineDesign/TreatmentDialog.js b/studydesign/webapp/study/vaccineDesign/TreatmentDialog.js similarity index 100% rename from study/webapp/study/vaccineDesign/TreatmentDialog.js rename to studydesign/webapp/study/vaccineDesign/TreatmentDialog.js diff --git a/study/webapp/study/vaccineDesign/TreatmentSchedule.js b/studydesign/webapp/study/vaccineDesign/TreatmentSchedule.js similarity index 97% rename from study/webapp/study/vaccineDesign/TreatmentSchedule.js rename to studydesign/webapp/study/vaccineDesign/TreatmentSchedule.js index 472edfb2..de25f2f1 100644 --- a/study/webapp/study/vaccineDesign/TreatmentSchedule.js +++ b/studydesign/webapp/study/vaccineDesign/TreatmentSchedule.js @@ -1,465 +1,465 @@ -/* - * Copyright (c) 2016-2017 LabKey Corporation - * - * Licensed under the Apache License, Version 2.0: http://www.apache.org/licenses/LICENSE-2.0 - */ -Ext4.define('LABKEY.VaccineDesign.TreatmentSchedulePanel', { - extend : 'LABKEY.VaccineDesign.TreatmentSchedulePanelBase', - - width: 1400, - - initComponent : function() - { - this.items = [this.getTreatmentsGrid()]; - - this.callParent(); - - window.onbeforeunload = LABKEY.beforeunload(this.beforeUnload, this); - }, - - getTreatmentsGrid : function() - { - if (!this.treatmentsGrid) - { - this.treatmentsGrid = Ext4.create('LABKEY.VaccineDesign.TreatmentsGrid', { - disableEdit: this.disableEdit, - productRoles: this.productRoles - }); - - this.treatmentsGrid.on('dirtychange', this.enableSaveButton, this); - this.treatmentsGrid.on('celledited', this.enableSaveButton, this); - - // Note: since we need the data from the treatment grid, don't add this.getTreatmentScheduleGrid() until the treatment grid store has loaded - this.treatmentsGrid.on('loadcomplete', this.onTreatmentGridLoadComplete, this, {single: true}); - } - - return this.treatmentsGrid; - }, - - onTreatmentGridLoadComplete : function() - { - this.add(this.getTreatmentScheduleGrid()); - this.add(this.getButtonBar()); - - // since a treatment label change needs to be reflected in the treatment schedule grid, force a refresh there - this.getTreatmentsGrid().on('celledited', function(view, fieldName, value){ - if (fieldName == 'Label') - this.getTreatmentScheduleGrid().refresh(); - }, this); - - // removing a treatment row needs to also remove any visit mappings for that treatment - this.getTreatmentsGrid().on('beforerowdeleted', function(grid, record){ - this.getTreatmentScheduleGrid().removeTreatmentUsages(record.get('RowId')); - }, this); - }, - - getTreatmentScheduleGrid : function() - { - if (!this.treatmentScheduleGrid) - { - this.treatmentScheduleGrid = Ext4.create('LABKEY.VaccineDesign.TreatmentScheduleGrid', { - padding: '20px 0', - disableEdit: this.disableEdit, - subjectNoun: this.subjectNoun, - visitNoun: this.visitNoun - }); - - this.treatmentScheduleGrid.on('dirtychange', this.enableSaveButton, this); - this.treatmentScheduleGrid.on('celledited', this.enableSaveButton, this); - } - - return this.treatmentScheduleGrid; - }, - - getTreatments: function() - { - var treatments = [], index = 0, errorMsg = []; - - Ext4.each(this.getTreatmentsGrid().getStore().getRange(), function(record) - { - var recData = Ext4.clone(record.data); - index++; - - // drop any empty immunogen or adjuvant or challenge rows that were just added - recData['Products'] = []; - Ext4.each(this.productRoles, function(role) - { - Ext4.each(recData[role], function(product) - { - if (Ext4.isDefined(product['RowId']) || LABKEY.VaccineDesign.Utils.objectHasData(product)) - recData['Products'].push(product); - }, this); - }, this); - - // drop any empty treatment rows that were just added - var hasData = recData['Label'] != '' || recData['Description'] != '' || recData['Products'].length > 0; - if (Ext4.isDefined(recData['RowId']) || hasData) - { - var treatmentLabel = recData['Label'] != '' ? '\'' + recData['Label'] + '\'' : index; - - // validation: treatment must have at least one immunogen or adjuvant, no duplicate immunogens/adjuvants for a treatment - var treatmentProductIds = Ext4.Array.clean(Ext4.Array.pluck(recData['Products'], 'ProductId')); - if (recData['Products'].length == 0) - errorMsg.push('Treatment ' + treatmentLabel + ' must have at least one immunogen, adjuvant or challenge defined.'); - else if (treatmentProductIds.length != Ext4.Array.unique(treatmentProductIds).length) - errorMsg.push('Treatment ' + treatmentLabel + ' contains a duplicate immunogen, adjuvant or challenge.'); - else - treatments.push(recData); - } - }, this); - - if (errorMsg.length > 0) - { - this.onFailure(errorMsg.join('
        ')); - return false; - } - return treatments; - } -}); - -Ext4.define('LABKEY.VaccineDesign.TreatmentsGrid', { - extend : 'LABKEY.VaccineDesign.BaseDataView', - - cls : 'study-vaccine-design vaccine-design-treatments', - - mainTitle : 'Treatments', - - width: 1400, - - studyDesignQueryNames : ['StudyDesignRoutes', 'Product', 'DoseAndRoute'], - - //Override - getStore : function() - { - if (!this.store) - { - this.store = Ext4.create('Ext.data.Store', { - storeId : 'TreatmentsGridStore', - model : 'LABKEY.VaccineDesign.Treatment', - proxy: { - type: 'ajax', - url : LABKEY.ActionURL.buildURL("study-design", "getStudyTreatments", null, {splitByRole: true}), - reader: { - type: 'json', - root: 'treatments' - } - }, - sorters: [{ property: 'RowId', direction: 'ASC' }], - autoLoad: true - }); - } - - return this.store; - }, - - //Override - getColumnConfigs : function() - { - if (!this.columnConfigs) - { - this.columnConfigs = [{ - label: 'Label', - width: 200, - dataIndex: 'Label', - required: true, - editorType: 'Ext.form.field.Text', - editorConfig: LABKEY.VaccineDesign.Utils.getStudyDesignTextConfig('Label', 185) - },{ - label: 'Description', - width: 200, - dataIndex: 'Description', - editorType: 'Ext.form.field.TextArea', - editorConfig: LABKEY.VaccineDesign.Utils.getStudyDesignTextConfig('Description', 185, '95%') - }]; - - if (Ext4.isArray(this.productRoles)) { - Ext4.each(this.productRoles, function(role){ - var roleColumn = this.getProductRoleColumn(role); - this.columnConfigs.push(roleColumn); - }, this); - } - } - - return this.columnConfigs; - }, - - getProductRoleColumn: function(roleName) { - var column = { - label: roleName + 's', - width: 310, - dataIndex: roleName, - subgridConfig: { - columns: [{ - label: roleName, - width: 140, - dataIndex: 'ProductId', - required: true, - queryName: 'Product', - editorType: 'LABKEY.ext4.ComboBox', - editorConfig: this.getProductEditor(roleName) - },{ - label: 'Dose and Route', - width: 140, - dataIndex: 'DoseAndRoute', - queryName: 'DoseAndRoute', - editorType: 'LABKEY.ext4.ComboBox', - editorConfig: this.getDoseAndRouteEditorConfig() - }] - } - }; - return column; - }, - - getProductEditor : function(roleName){ - - var filter = LABKEY.Filter.create('Role', roleName), - cfg = LABKEY.VaccineDesign.Utils.getStudyDesignComboConfig('ProductId', 125, 'Product', filter, 'Label', 'RowId'); - - cfg.listeners = { - scope: this, - change : function(cmp, productId) { - // clear out (if any) value for the dose and route field - var record = this.getStore().getAt(cmp.storeIndex), - outerDataIndex = cmp.outerDataIndex, - subgridIndex = Number(cmp.subgridIndex), - selector = 'tr.data-row:nth(' + (this.getStore().indexOf(record)+1) + ') table.subgrid-' + outerDataIndex - + ' tr.subrow:nth(' + (subgridIndex+1) + ') td[data-index=DoseAndRoute] input'; - - var inputField = this.getInputFieldFromSelector(selector); - if (inputField != null) - { - inputField.setValue(''); - inputField.bindStore(this.getNewDoseAndRouteComboStore(productId)); - } - } - }; - return cfg; - }, - - getDoseAndRouteEditorConfig : function() - { - return { - hideFieldLabel: true, - name: 'DoseAndRoute', - width: 125, - forceSelection : false, // allow usage of inactive types - editable : false, - queryMode : 'local', - displayField : 'Label', - valueField : 'Label', - store : null, // the store will be created and bound to this combo after render - listeners : { - scope: this, - render : function(cmp) { - var record = this.getStore().getAt(cmp.storeIndex), - outerDataIndex = cmp.outerDataIndex, - subgridIndex = Number(cmp.subgridIndex), - productId = record.get(outerDataIndex)[subgridIndex]['ProductId']; - - cmp.bindStore(this.getNewDoseAndRouteComboStore(productId)); - }, - change : function(cmp, newValue, oldValue) { - var record = this.getStore().getAt(cmp.storeIndex), - outerDataIndex = cmp.outerDataIndex, - subgridIndex = Number(cmp.subgridIndex), - subRecord = record.get(outerDataIndex)[subgridIndex]; - - // if the ProductDoseRoute is set, we need to update it - if (Ext4.isDefined(subRecord['ProductDoseRoute']) && Ext4.isDefined(subRecord['ProductId'])) - subRecord['ProductDoseRoute'] = subRecord['ProductId'] + '-#-' + newValue; - } - } - }; - }, - - getNewDoseAndRouteComboStore : function(productId) - { - // need to create a new store each time since we need to add a [none] option and include any new treatment records - var data = []; - Ext4.each(Ext4.getStore('DoseAndRoute').getRange(), function(record) - { - if (record.get('ProductId') == null || record.get('ProductId') == productId) - data.push(Ext4.clone(record.data)); - }, this); - - return Ext4.create('Ext.data.Store', { - fields: ['RowId', 'Label'], - data: data - }); - }, - - //Override - getNewModelInstance : function() - { - return LABKEY.VaccineDesign.Treatment.create({ - RowId: Ext4.id() // need to generate an id so that the treatment schedule grid can use it - }); - }, - - //Override - getDeleteConfirmationMsg : function() - { - return 'Are you sure you want to delete the selected treatment? ' - + 'Note: this will also delete any usages of this treatment record in the Treatment Schedule grid below.'; - }, - - //Override - updateSubgridRecordValue : function(record, outerDataIndex, subgridIndex, fieldName, newValue) - { - var preProductIds = []; - Ext4.each(this.productRoles, function(role){ - var productRoleIds = Ext4.Array.pluck(record.get(role), 'ProductId'); - if (preProductIds.length == 0) - preProductIds = productRoleIds; - else - preProductIds = preProductIds.concat(productRoleIds); - }); - - this.callParent([record, outerDataIndex, subgridIndex, fieldName, newValue]); - - // auto populate the treatment label if the user has not already entered a value - if (fieldName == 'ProductId') - this.populateTreatmentLabel(record, preProductIds); - }, - - //Override - removeSubgridRecord : function(target, record) - { - var preProductIds = []; - Ext4.each(this.productRoles, function(role){ - var productRoleIds = Ext4.Array.pluck(record.get(role), 'ProductId'); - if (preProductIds.length == 0) - preProductIds = productRoleIds; - else - preProductIds = preProductIds.concat(productRoleIds); - }); - this.callParent([target, record]); - this.populateTreatmentLabel(record, preProductIds); - this.refresh(true); - }, - - populateTreatmentLabel : function(record, preProductIds) - { - var currentLabel = record.get('Label'); - if (currentLabel == '' || currentLabel == this.getLabelFromProductIds(preProductIds)) - { - var postProductIds = []; - Ext4.each(this.productRoles, function(role){ - var productRoleIds = Ext4.Array.pluck(record.get(role), 'ProductId'); - if (postProductIds.length == 0) - postProductIds = productRoleIds; - else - postProductIds = postProductIds.concat(productRoleIds); - }); - - var updatedTreatmentLabel = this.getLabelFromProductIds(postProductIds); - - // need to update the input field value, which will intern update the record and fire teh celledited event - var inputField = this.getInputFieldFromSelector('tr.data-row:nth(' + (this.getStore().indexOf(record)+1) + ') td.cell-value input'); - if (inputField != null) - { - inputField.setValue(updatedTreatmentLabel); - record.set('Label', updatedTreatmentLabel); - } - } - }, - - getInputFieldFromSelector : function(selector) - { - var inputFieldEl = Ext4.DomQuery.selectNode(selector, this.getEl().dom); - if (inputFieldEl != null) - return Ext4.ComponentManager.get(inputFieldEl.id.replace('-inputEl', '')); - - return null; - }, - - getLabelFromProductIds : function(productIdsArr) - { - var labelArr = []; - - if (Ext4.isArray(productIdsArr)) - { - Ext4.each(productIdsArr, function(productId){ - if (productId != undefined || productId != null) - labelArr.push(LABKEY.VaccineDesign.Utils.getLabelFromStore('Product', productId)); - }); - } - - return labelArr.join(' | '); - } -}); - -Ext4.define('LABKEY.VaccineDesign.TreatmentScheduleGrid', { - extend : 'LABKEY.VaccineDesign.TreatmentScheduleGridBase', - - //Override - onStudyTreatmentScheduleStoreLoad : function() - { - this.getStore().fireEvent('load', this.getStore()); - }, - - getTreatmentsStore : function() - { - if (!this.treatmentsStore) - { - this.treatmentsStore = Ext4.getStore('TreatmentsGridStore'); - } - - return this.treatmentsStore; - }, - - //Override - getTreatmentFieldEditorType: function() - { - return 'LABKEY.ext4.ComboBox'; - }, - - //Override - isFieldTreatmentLookup: function() - { - return false; - }, - - getTreatmentFieldConfig : function() - { - return { - hideFieldLabel: true, - name: 'VisitMap', - width: 135, - forceSelection : false, // allow usage of inactive types - editable : false, - queryMode : 'local', - displayField : 'Label', - valueField : 'RowId', - store : this.getNewTreatmentComboStore() - }; - }, - - getNewTreatmentComboStore : function() - { - // need to create a new store each time since we need to add a [none] option and include any new treatment records - var data = [{RowId: null, Label: '[none]'}]; - Ext4.each(this.getTreatmentsStore().getRange(), function(record) - { - data.push(Ext4.clone(record.data)); - }, this); - - return Ext4.create('Ext.data.Store', { - fields: ['RowId', 'Label'], - data: data - }); - }, - - removeTreatmentUsages : function(treatmentId) - { - this.getStore().suspendEvents(); - Ext4.each(this.getStore().getRange(), function(record) - { - var newVisitMapArr = Ext4.Array.filter(record.get('VisitMap'), function(item){ return item.TreatmentId != treatmentId; }); - record.set('VisitMap', newVisitMapArr); - }, this); - this.getStore().resumeEvents(); - - this.refresh(true); - } +/* + * Copyright (c) 2016-2017 LabKey Corporation + * + * Licensed under the Apache License, Version 2.0: http://www.apache.org/licenses/LICENSE-2.0 + */ +Ext4.define('LABKEY.VaccineDesign.TreatmentSchedulePanel', { + extend : 'LABKEY.VaccineDesign.TreatmentSchedulePanelBase', + + width: 1400, + + initComponent : function() + { + this.items = [this.getTreatmentsGrid()]; + + this.callParent(); + + window.onbeforeunload = LABKEY.beforeunload(this.beforeUnload, this); + }, + + getTreatmentsGrid : function() + { + if (!this.treatmentsGrid) + { + this.treatmentsGrid = Ext4.create('LABKEY.VaccineDesign.TreatmentsGrid', { + disableEdit: this.disableEdit, + productRoles: this.productRoles + }); + + this.treatmentsGrid.on('dirtychange', this.enableSaveButton, this); + this.treatmentsGrid.on('celledited', this.enableSaveButton, this); + + // Note: since we need the data from the treatment grid, don't add this.getTreatmentScheduleGrid() until the treatment grid store has loaded + this.treatmentsGrid.on('loadcomplete', this.onTreatmentGridLoadComplete, this, {single: true}); + } + + return this.treatmentsGrid; + }, + + onTreatmentGridLoadComplete : function() + { + this.add(this.getTreatmentScheduleGrid()); + this.add(this.getButtonBar()); + + // since a treatment label change needs to be reflected in the treatment schedule grid, force a refresh there + this.getTreatmentsGrid().on('celledited', function(view, fieldName, value){ + if (fieldName == 'Label') + this.getTreatmentScheduleGrid().refresh(); + }, this); + + // removing a treatment row needs to also remove any visit mappings for that treatment + this.getTreatmentsGrid().on('beforerowdeleted', function(grid, record){ + this.getTreatmentScheduleGrid().removeTreatmentUsages(record.get('RowId')); + }, this); + }, + + getTreatmentScheduleGrid : function() + { + if (!this.treatmentScheduleGrid) + { + this.treatmentScheduleGrid = Ext4.create('LABKEY.VaccineDesign.TreatmentScheduleGrid', { + padding: '20px 0', + disableEdit: this.disableEdit, + subjectNoun: this.subjectNoun, + visitNoun: this.visitNoun + }); + + this.treatmentScheduleGrid.on('dirtychange', this.enableSaveButton, this); + this.treatmentScheduleGrid.on('celledited', this.enableSaveButton, this); + } + + return this.treatmentScheduleGrid; + }, + + getTreatments: function() + { + var treatments = [], index = 0, errorMsg = []; + + Ext4.each(this.getTreatmentsGrid().getStore().getRange(), function(record) + { + var recData = Ext4.clone(record.data); + index++; + + // drop any empty immunogen or adjuvant or challenge rows that were just added + recData['Products'] = []; + Ext4.each(this.productRoles, function(role) + { + Ext4.each(recData[role], function(product) + { + if (Ext4.isDefined(product['RowId']) || LABKEY.VaccineDesign.Utils.objectHasData(product)) + recData['Products'].push(product); + }, this); + }, this); + + // drop any empty treatment rows that were just added + var hasData = recData['Label'] != '' || recData['Description'] != '' || recData['Products'].length > 0; + if (Ext4.isDefined(recData['RowId']) || hasData) + { + var treatmentLabel = recData['Label'] != '' ? '\'' + recData['Label'] + '\'' : index; + + // validation: treatment must have at least one immunogen or adjuvant, no duplicate immunogens/adjuvants for a treatment + var treatmentProductIds = Ext4.Array.clean(Ext4.Array.pluck(recData['Products'], 'ProductId')); + if (recData['Products'].length == 0) + errorMsg.push('Treatment ' + treatmentLabel + ' must have at least one immunogen, adjuvant or challenge defined.'); + else if (treatmentProductIds.length != Ext4.Array.unique(treatmentProductIds).length) + errorMsg.push('Treatment ' + treatmentLabel + ' contains a duplicate immunogen, adjuvant or challenge.'); + else + treatments.push(recData); + } + }, this); + + if (errorMsg.length > 0) + { + this.onFailure(errorMsg.join('
        ')); + return false; + } + return treatments; + } +}); + +Ext4.define('LABKEY.VaccineDesign.TreatmentsGrid', { + extend : 'LABKEY.VaccineDesign.BaseDataView', + + cls : 'study-vaccine-design vaccine-design-treatments', + + mainTitle : 'Treatments', + + width: 1400, + + studyDesignQueryNames : ['StudyDesignRoutes', 'Product', 'DoseAndRoute'], + + //Override + getStore : function() + { + if (!this.store) + { + this.store = Ext4.create('Ext.data.Store', { + storeId : 'TreatmentsGridStore', + model : 'LABKEY.VaccineDesign.Treatment', + proxy: { + type: 'ajax', + url : LABKEY.ActionURL.buildURL("study-design", "getStudyTreatments", null, {splitByRole: true}), + reader: { + type: 'json', + root: 'treatments' + } + }, + sorters: [{ property: 'RowId', direction: 'ASC' }], + autoLoad: true + }); + } + + return this.store; + }, + + //Override + getColumnConfigs : function() + { + if (!this.columnConfigs) + { + this.columnConfigs = [{ + label: 'Label', + width: 200, + dataIndex: 'Label', + required: true, + editorType: 'Ext.form.field.Text', + editorConfig: LABKEY.VaccineDesign.Utils.getStudyDesignTextConfig('Label', 185) + },{ + label: 'Description', + width: 200, + dataIndex: 'Description', + editorType: 'Ext.form.field.TextArea', + editorConfig: LABKEY.VaccineDesign.Utils.getStudyDesignTextConfig('Description', 185, '95%') + }]; + + if (Ext4.isArray(this.productRoles)) { + Ext4.each(this.productRoles, function(role){ + var roleColumn = this.getProductRoleColumn(role); + this.columnConfigs.push(roleColumn); + }, this); + } + } + + return this.columnConfigs; + }, + + getProductRoleColumn: function(roleName) { + var column = { + label: roleName + 's', + width: 310, + dataIndex: roleName, + subgridConfig: { + columns: [{ + label: roleName, + width: 140, + dataIndex: 'ProductId', + required: true, + queryName: 'Product', + editorType: 'LABKEY.ext4.ComboBox', + editorConfig: this.getProductEditor(roleName) + },{ + label: 'Dose and Route', + width: 140, + dataIndex: 'DoseAndRoute', + queryName: 'DoseAndRoute', + editorType: 'LABKEY.ext4.ComboBox', + editorConfig: this.getDoseAndRouteEditorConfig() + }] + } + }; + return column; + }, + + getProductEditor : function(roleName){ + + var filter = LABKEY.Filter.create('Role', roleName), + cfg = LABKEY.VaccineDesign.Utils.getStudyDesignComboConfig('ProductId', 125, 'Product', filter, 'Label', 'RowId'); + + cfg.listeners = { + scope: this, + change : function(cmp, productId) { + // clear out (if any) value for the dose and route field + var record = this.getStore().getAt(cmp.storeIndex), + outerDataIndex = cmp.outerDataIndex, + subgridIndex = Number(cmp.subgridIndex), + selector = 'tr.data-row:nth(' + (this.getStore().indexOf(record)+1) + ') table.subgrid-' + outerDataIndex + + ' tr.subrow:nth(' + (subgridIndex+1) + ') td[data-index=DoseAndRoute] input'; + + var inputField = this.getInputFieldFromSelector(selector); + if (inputField != null) + { + inputField.setValue(''); + inputField.bindStore(this.getNewDoseAndRouteComboStore(productId)); + } + } + }; + return cfg; + }, + + getDoseAndRouteEditorConfig : function() + { + return { + hideFieldLabel: true, + name: 'DoseAndRoute', + width: 125, + forceSelection : false, // allow usage of inactive types + editable : false, + queryMode : 'local', + displayField : 'Label', + valueField : 'Label', + store : null, // the store will be created and bound to this combo after render + listeners : { + scope: this, + render : function(cmp) { + var record = this.getStore().getAt(cmp.storeIndex), + outerDataIndex = cmp.outerDataIndex, + subgridIndex = Number(cmp.subgridIndex), + productId = record.get(outerDataIndex)[subgridIndex]['ProductId']; + + cmp.bindStore(this.getNewDoseAndRouteComboStore(productId)); + }, + change : function(cmp, newValue, oldValue) { + var record = this.getStore().getAt(cmp.storeIndex), + outerDataIndex = cmp.outerDataIndex, + subgridIndex = Number(cmp.subgridIndex), + subRecord = record.get(outerDataIndex)[subgridIndex]; + + // if the ProductDoseRoute is set, we need to update it + if (Ext4.isDefined(subRecord['ProductDoseRoute']) && Ext4.isDefined(subRecord['ProductId'])) + subRecord['ProductDoseRoute'] = subRecord['ProductId'] + '-#-' + newValue; + } + } + }; + }, + + getNewDoseAndRouteComboStore : function(productId) + { + // need to create a new store each time since we need to add a [none] option and include any new treatment records + var data = []; + Ext4.each(Ext4.getStore('DoseAndRoute').getRange(), function(record) + { + if (record.get('ProductId') == null || record.get('ProductId') == productId) + data.push(Ext4.clone(record.data)); + }, this); + + return Ext4.create('Ext.data.Store', { + fields: ['RowId', 'Label'], + data: data + }); + }, + + //Override + getNewModelInstance : function() + { + return LABKEY.VaccineDesign.Treatment.create({ + RowId: Ext4.id() // need to generate an id so that the treatment schedule grid can use it + }); + }, + + //Override + getDeleteConfirmationMsg : function() + { + return 'Are you sure you want to delete the selected treatment? ' + + 'Note: this will also delete any usages of this treatment record in the Treatment Schedule grid below.'; + }, + + //Override + updateSubgridRecordValue : function(record, outerDataIndex, subgridIndex, fieldName, newValue) + { + var preProductIds = []; + Ext4.each(this.productRoles, function(role){ + var productRoleIds = Ext4.Array.pluck(record.get(role), 'ProductId'); + if (preProductIds.length == 0) + preProductIds = productRoleIds; + else + preProductIds = preProductIds.concat(productRoleIds); + }); + + this.callParent([record, outerDataIndex, subgridIndex, fieldName, newValue]); + + // auto populate the treatment label if the user has not already entered a value + if (fieldName == 'ProductId') + this.populateTreatmentLabel(record, preProductIds); + }, + + //Override + removeSubgridRecord : function(target, record) + { + var preProductIds = []; + Ext4.each(this.productRoles, function(role){ + var productRoleIds = Ext4.Array.pluck(record.get(role), 'ProductId'); + if (preProductIds.length == 0) + preProductIds = productRoleIds; + else + preProductIds = preProductIds.concat(productRoleIds); + }); + this.callParent([target, record]); + this.populateTreatmentLabel(record, preProductIds); + this.refresh(true); + }, + + populateTreatmentLabel : function(record, preProductIds) + { + var currentLabel = record.get('Label'); + if (currentLabel == '' || currentLabel == this.getLabelFromProductIds(preProductIds)) + { + var postProductIds = []; + Ext4.each(this.productRoles, function(role){ + var productRoleIds = Ext4.Array.pluck(record.get(role), 'ProductId'); + if (postProductIds.length == 0) + postProductIds = productRoleIds; + else + postProductIds = postProductIds.concat(productRoleIds); + }); + + var updatedTreatmentLabel = this.getLabelFromProductIds(postProductIds); + + // need to update the input field value, which will intern update the record and fire teh celledited event + var inputField = this.getInputFieldFromSelector('tr.data-row:nth(' + (this.getStore().indexOf(record)+1) + ') td.cell-value input'); + if (inputField != null) + { + inputField.setValue(updatedTreatmentLabel); + record.set('Label', updatedTreatmentLabel); + } + } + }, + + getInputFieldFromSelector : function(selector) + { + var inputFieldEl = Ext4.DomQuery.selectNode(selector, this.getEl().dom); + if (inputFieldEl != null) + return Ext4.ComponentManager.get(inputFieldEl.id.replace('-inputEl', '')); + + return null; + }, + + getLabelFromProductIds : function(productIdsArr) + { + var labelArr = []; + + if (Ext4.isArray(productIdsArr)) + { + Ext4.each(productIdsArr, function(productId){ + if (productId != undefined || productId != null) + labelArr.push(LABKEY.VaccineDesign.Utils.getLabelFromStore('Product', productId)); + }); + } + + return labelArr.join(' | '); + } +}); + +Ext4.define('LABKEY.VaccineDesign.TreatmentScheduleGrid', { + extend : 'LABKEY.VaccineDesign.TreatmentScheduleGridBase', + + //Override + onStudyTreatmentScheduleStoreLoad : function() + { + this.getStore().fireEvent('load', this.getStore()); + }, + + getTreatmentsStore : function() + { + if (!this.treatmentsStore) + { + this.treatmentsStore = Ext4.getStore('TreatmentsGridStore'); + } + + return this.treatmentsStore; + }, + + //Override + getTreatmentFieldEditorType: function() + { + return 'LABKEY.ext4.ComboBox'; + }, + + //Override + isFieldTreatmentLookup: function() + { + return false; + }, + + getTreatmentFieldConfig : function() + { + return { + hideFieldLabel: true, + name: 'VisitMap', + width: 135, + forceSelection : false, // allow usage of inactive types + editable : false, + queryMode : 'local', + displayField : 'Label', + valueField : 'RowId', + store : this.getNewTreatmentComboStore() + }; + }, + + getNewTreatmentComboStore : function() + { + // need to create a new store each time since we need to add a [none] option and include any new treatment records + var data = [{RowId: null, Label: '[none]'}]; + Ext4.each(this.getTreatmentsStore().getRange(), function(record) + { + data.push(Ext4.clone(record.data)); + }, this); + + return Ext4.create('Ext.data.Store', { + fields: ['RowId', 'Label'], + data: data + }); + }, + + removeTreatmentUsages : function(treatmentId) + { + this.getStore().suspendEvents(); + Ext4.each(this.getStore().getRange(), function(record) + { + var newVisitMapArr = Ext4.Array.filter(record.get('VisitMap'), function(item){ return item.TreatmentId != treatmentId; }); + record.set('VisitMap', newVisitMapArr); + }, this); + this.getStore().resumeEvents(); + + this.refresh(true); + } }); \ No newline at end of file diff --git a/study/webapp/study/vaccineDesign/TreatmentScheduleBase.js b/studydesign/webapp/study/vaccineDesign/TreatmentScheduleBase.js similarity index 100% rename from study/webapp/study/vaccineDesign/TreatmentScheduleBase.js rename to studydesign/webapp/study/vaccineDesign/TreatmentScheduleBase.js diff --git a/study/webapp/study/vaccineDesign/TreatmentScheduleSingleTablePanel.js b/studydesign/webapp/study/vaccineDesign/TreatmentScheduleSingleTablePanel.js similarity index 100% rename from study/webapp/study/vaccineDesign/TreatmentScheduleSingleTablePanel.js rename to studydesign/webapp/study/vaccineDesign/TreatmentScheduleSingleTablePanel.js diff --git a/study/webapp/study/vaccineDesign/Utils.js b/studydesign/webapp/study/vaccineDesign/Utils.js similarity index 97% rename from study/webapp/study/vaccineDesign/Utils.js rename to studydesign/webapp/study/vaccineDesign/Utils.js index 893e515e..ce684cd9 100644 --- a/study/webapp/study/vaccineDesign/Utils.js +++ b/studydesign/webapp/study/vaccineDesign/Utils.js @@ -1,212 +1,212 @@ -/* - * Copyright (c) 2016-2017 LabKey Corporation - * - * Licensed under the Apache License, Version 2.0: http://www.apache.org/licenses/LICENSE-2.0 - */ - -Ext4.define('LABKEY.VaccineDesign.Utils', { - - singleton: true, - - /** - * Helper function to get field editor config object for a study design lookup combo. - * @param name Field name - * @param width Field width - * @param queryName If combo, the queryName for the store - * @param filter LABKEY.Filter.create() object - * @param displayField The field name of the combo store displayField - * @param valueField The field name of the combo store valueField - * @returns {Object} Field config - */ - getStudyDesignComboConfig : function(name, width, queryName, filter, displayField, valueField) - { - return { - hideFieldLabel: true, - name: name, - width: width || 150, - forceSelection : false, // allow usage of inactive types - editable : false, - queryMode : 'local', - // TODO: this does not htmlEncode the display value in expanded options list - displayField : displayField || 'Label', - valueField : valueField || 'Name', - store : LABKEY.VaccineDesign.Utils.getStudyDesignStore(queryName, filter) - }; - }, - - /** - * Helper function to get field editor config object for a study design text or text area field. - * @param name Field name - * @param width Field width - * @param height Field height - * @returns {Object} Field config - */ - getStudyDesignTextConfig : function(name, width, height) - { - return { - hideFieldLabel: true, - name: name, - width: width, - height: height, - selectOnFocus: true - } - }, - - /** - * Helper function to get field editor config object for a study design number field. - * @param name Field name - * @param width Field width - * @param decimalPrecision Field maximum precision to display after decimal - * @returns {Object} Field config - */ - getStudyDesignNumberConfig : function(name, width, decimalPrecision) - { - return { - hideFieldLabel: true, - name: name, - width: width, - minValue: 0, - allowDecimals: Ext4.isNumber(decimalPrecision), - decimalPrecision: decimalPrecision - } - }, - - /** - * Create a new LABKEY.ext4.Store for the given queryName from the study schema. - * @param queryName - * @param filter LABKEY.Filter.create() object - * @returns {LABKEY.ext4.Store} - */ - getStudyDesignStore : function(queryName, filter) - { - var key = Ext4.isDefined(filter) ? queryName + '|' + filter.getColumnName() + '|' + filter.getValue() : queryName, - store = Ext4.getStore(key), - hasStudyDesignPrefix = queryName.indexOf('StudyDesign') == 0, - columns = 'RowId,Name,Label'; - - if (Ext4.isDefined(store)) - return store; - - // special case to query DisplayOrder and SequenceNumMin for Visit table - if (queryName == 'Visit') - columns += ',DisplayOrder,SequenceNumMin'; - // special case to query ProductId column for DoseAndRoute table - else if (queryName == 'DoseAndRoute') - columns += ',ProductId'; - else if (queryName == 'Product') - columns += ',Role'; - else if (queryName == 'DataSets') - columns += ',DataSetId'; - - return Ext4.create('LABKEY.ext4.Store', { - storeId: key, - schemaName: 'study', - queryName: queryName, - columns: columns, - filterArray: Ext4.isDefined(filter) ? [filter] : (hasStudyDesignPrefix ? [LABKEY.Filter.create('Inactive', false)] : []), - containerFilter: LABKEY.container.type == 'project' || Ext4.isDefined(filter) || !hasStudyDesignPrefix ? undefined : 'CurrentPlusProject', - sort: '-Container/Path,Label', - autoLoad: true, - listeners: { - load: function(store) - { - store.insert(0, {Name: null}); - } - } - }); - }, - - /** - * Lookup a label for a given value using a store generated from the queryName. - * @param queryName - * @param value - * @returns {String} - */ - getLabelFromStore : function(queryName, value) - { - var store = LABKEY.VaccineDesign.Utils.getStudyDesignStore(queryName), - storeCols = Ext4.Array.pluck(store.getColumns(), 'dataIndex'), - keys = ['RowId', 'Name', 'DataSetId'], - record = null; - - Ext4.each(keys, function(key) { - if (record == null && storeCols.indexOf(key) > -1) - record = store.findRecord(key, value, 0, false, true, true); - }); - - return record != null ? record.get("Label") : value; - }, - - /** - * Check if the given object has any properties which have data (i.e. non null, not an empty string, or is an array) - * @param obj The object to test - * @returns {boolean} - */ - objectHasData : function(obj) - { - var hasNonNull = false; - - if (Ext4.isObject(obj)) - { - Ext4.Object.each(obj, function (key, value) - { - if ((Ext4.isArray(value) && value.length > 0) || (value != null && value != '')) - { - hasNonNull = true; - return false; // break - } - }); - } - - return hasNonNull; - }, - - /** - * Check if the given model object has any properties which have data (i.e. non null, not an empty string, or is an array) - * @param obj The object to test - * @returns {boolean} - */ - modelHasData : function(obj, fields) - { - var hasNonNull = false; - - if (Ext4.isObject(obj) && Ext4.isArray(fields)) - { - Ext4.each(fields, function(field) - { - if (Ext4.isArray(obj[field.name]) && obj[field.name].length > 0) - hasNonNull = true; - else if ((field.type.type == 'int' || field.type.type == 'float') && obj[field.name] != null && obj[field.name] != 0) - hasNonNull = true; - else if (field.type.type == 'string' && obj[field.name] != null && obj[field.name] != '') - hasNonNull = true; - - if (hasNonNull) - return false; // break; - }); - } - - return hasNonNull; - }, - - /** - * Get the matching row index from an array based on a given row's object property name and value. - * @param arr The array to traverse - * @param filterPropName The name of the row's object property to compare - * @param filterPropValue The value of the row's object property that indicates a match - * @returns {Object} - */ - getMatchingRowIndexFromArray : function(arr, filterPropName, filterPropValue) - { - if (Ext4.isString(filterPropName) && Ext4.isDefined(filterPropValue) && Ext4.isArray(arr)) - { - for (var i = 0; i < arr.length; i++) - { - if (arr[i].hasOwnProperty(filterPropName) && arr[i][filterPropName] == filterPropValue) - return i; - } - } - - return -1; - } +/* + * Copyright (c) 2016-2017 LabKey Corporation + * + * Licensed under the Apache License, Version 2.0: http://www.apache.org/licenses/LICENSE-2.0 + */ + +Ext4.define('LABKEY.VaccineDesign.Utils', { + + singleton: true, + + /** + * Helper function to get field editor config object for a study design lookup combo. + * @param name Field name + * @param width Field width + * @param queryName If combo, the queryName for the store + * @param filter LABKEY.Filter.create() object + * @param displayField The field name of the combo store displayField + * @param valueField The field name of the combo store valueField + * @returns {Object} Field config + */ + getStudyDesignComboConfig : function(name, width, queryName, filter, displayField, valueField) + { + return { + hideFieldLabel: true, + name: name, + width: width || 150, + forceSelection : false, // allow usage of inactive types + editable : false, + queryMode : 'local', + // TODO: this does not htmlEncode the display value in expanded options list + displayField : displayField || 'Label', + valueField : valueField || 'Name', + store : LABKEY.VaccineDesign.Utils.getStudyDesignStore(queryName, filter) + }; + }, + + /** + * Helper function to get field editor config object for a study design text or text area field. + * @param name Field name + * @param width Field width + * @param height Field height + * @returns {Object} Field config + */ + getStudyDesignTextConfig : function(name, width, height) + { + return { + hideFieldLabel: true, + name: name, + width: width, + height: height, + selectOnFocus: true + } + }, + + /** + * Helper function to get field editor config object for a study design number field. + * @param name Field name + * @param width Field width + * @param decimalPrecision Field maximum precision to display after decimal + * @returns {Object} Field config + */ + getStudyDesignNumberConfig : function(name, width, decimalPrecision) + { + return { + hideFieldLabel: true, + name: name, + width: width, + minValue: 0, + allowDecimals: Ext4.isNumber(decimalPrecision), + decimalPrecision: decimalPrecision + } + }, + + /** + * Create a new LABKEY.ext4.Store for the given queryName from the study schema. + * @param queryName + * @param filter LABKEY.Filter.create() object + * @returns {LABKEY.ext4.Store} + */ + getStudyDesignStore : function(queryName, filter) + { + var key = Ext4.isDefined(filter) ? queryName + '|' + filter.getColumnName() + '|' + filter.getValue() : queryName, + store = Ext4.getStore(key), + hasStudyDesignPrefix = queryName.indexOf('StudyDesign') == 0, + columns = 'RowId,Name,Label'; + + if (Ext4.isDefined(store)) + return store; + + // special case to query DisplayOrder and SequenceNumMin for Visit table + if (queryName == 'Visit') + columns += ',DisplayOrder,SequenceNumMin'; + // special case to query ProductId column for DoseAndRoute table + else if (queryName == 'DoseAndRoute') + columns += ',ProductId'; + else if (queryName == 'Product') + columns += ',Role'; + else if (queryName == 'DataSets') + columns += ',DataSetId'; + + return Ext4.create('LABKEY.ext4.Store', { + storeId: key, + schemaName: 'study', + queryName: queryName, + columns: columns, + filterArray: Ext4.isDefined(filter) ? [filter] : (hasStudyDesignPrefix ? [LABKEY.Filter.create('Inactive', false)] : []), + containerFilter: LABKEY.container.type == 'project' || Ext4.isDefined(filter) || !hasStudyDesignPrefix ? undefined : 'CurrentPlusProject', + sort: '-Container/Path,Label', + autoLoad: true, + listeners: { + load: function(store) + { + store.insert(0, {Name: null}); + } + } + }); + }, + + /** + * Lookup a label for a given value using a store generated from the queryName. + * @param queryName + * @param value + * @returns {String} + */ + getLabelFromStore : function(queryName, value) + { + var store = LABKEY.VaccineDesign.Utils.getStudyDesignStore(queryName), + storeCols = Ext4.Array.pluck(store.getColumns(), 'dataIndex'), + keys = ['RowId', 'Name', 'DataSetId'], + record = null; + + Ext4.each(keys, function(key) { + if (record == null && storeCols.indexOf(key) > -1) + record = store.findRecord(key, value, 0, false, true, true); + }); + + return record != null ? record.get("Label") : value; + }, + + /** + * Check if the given object has any properties which have data (i.e. non null, not an empty string, or is an array) + * @param obj The object to test + * @returns {boolean} + */ + objectHasData : function(obj) + { + var hasNonNull = false; + + if (Ext4.isObject(obj)) + { + Ext4.Object.each(obj, function (key, value) + { + if ((Ext4.isArray(value) && value.length > 0) || (value != null && value != '')) + { + hasNonNull = true; + return false; // break + } + }); + } + + return hasNonNull; + }, + + /** + * Check if the given model object has any properties which have data (i.e. non null, not an empty string, or is an array) + * @param obj The object to test + * @returns {boolean} + */ + modelHasData : function(obj, fields) + { + var hasNonNull = false; + + if (Ext4.isObject(obj) && Ext4.isArray(fields)) + { + Ext4.each(fields, function(field) + { + if (Ext4.isArray(obj[field.name]) && obj[field.name].length > 0) + hasNonNull = true; + else if ((field.type.type == 'int' || field.type.type == 'float') && obj[field.name] != null && obj[field.name] != 0) + hasNonNull = true; + else if (field.type.type == 'string' && obj[field.name] != null && obj[field.name] != '') + hasNonNull = true; + + if (hasNonNull) + return false; // break; + }); + } + + return hasNonNull; + }, + + /** + * Get the matching row index from an array based on a given row's object property name and value. + * @param arr The array to traverse + * @param filterPropName The name of the row's object property to compare + * @param filterPropValue The value of the row's object property that indicates a match + * @returns {Object} + */ + getMatchingRowIndexFromArray : function(arr, filterPropName, filterPropValue) + { + if (Ext4.isString(filterPropName) && Ext4.isDefined(filterPropValue) && Ext4.isArray(arr)) + { + for (var i = 0; i < arr.length; i++) + { + if (arr[i].hasOwnProperty(filterPropName) && arr[i][filterPropName] == filterPropValue) + return i; + } + } + + return -1; + } }); \ No newline at end of file diff --git a/study/webapp/study/vaccineDesign/VaccineDesign.css b/studydesign/webapp/study/vaccineDesign/VaccineDesign.css similarity index 95% rename from study/webapp/study/vaccineDesign/VaccineDesign.css rename to studydesign/webapp/study/vaccineDesign/VaccineDesign.css index cb12d822..a44ba06e 100644 --- a/study/webapp/study/vaccineDesign/VaccineDesign.css +++ b/studydesign/webapp/study/vaccineDesign/VaccineDesign.css @@ -1,82 +1,82 @@ -.study-vaccine-design .x4-panel-body { - background-color: transparent; -} - -.study-vaccine-design .main-title { - font-weight: bold; - font-size: 16px; - padding-bottom: 5px; -} - -.study-vaccine-design table.outer, -.study-vaccine-design table.subgrid { - border-collapse: collapse; -} - -.study-vaccine-design td.cell-display, -.study-vaccine-design td.cell-value { - padding: 5px; - border: solid 1px #DDDDDD; - vertical-align: top; -} - -.study-vaccine-design td.cell-display { - height: 30px; -} -.study-vaccine-design td.cell-value { - height: 33px; -} - -.study-vaccine-design td.cell-value .x4-form-cb-wrap { - height: 18px; -} - -.study-vaccine-design table.subgrid td { - background-color: #FFFFFF !important; -} - -.study-vaccine-design tr.header-row td { - font-weight: bold; - padding: 5px; - background-color: #EEEEEE !important; - border-bottom-color: #C0C0C0; -} - -.study-vaccine-design table.outer tr.data-row td { - background-color: #FFFFFF; -} -.study-vaccine-design table.outer tr.alternate-row td { - background-color: #F4F4F4; -} - -.study-vaccine-design td.cell-value.missing-required { - background-color: #ffe5e5 !important; -} - -.study-vaccine-design td.empty { - font-style: italic; -} - -.study-vaccine-design td.action i { - color: #777777; -} -.study-vaccine-design td.action i:hover { - cursor: pointer; - color: #000000; -} - -.dialog-product-label .x4-form-cb-label-after { - /* Firefox */ - width: -moz-calc(100% - 20px); - /* WebKit */ - width: -webkit-calc(100% - 20px); - /* Standard */ - width: calc(100% - 20px); - white-space: nowrap; -} - -.treatment-input-cell .x4-form-field { - cursor: pointer; - opacity: 0.8; -} - +.study-vaccine-design .x4-panel-body { + background-color: transparent; +} + +.study-vaccine-design .main-title { + font-weight: bold; + font-size: 16px; + padding-bottom: 5px; +} + +.study-vaccine-design table.outer, +.study-vaccine-design table.subgrid { + border-collapse: collapse; +} + +.study-vaccine-design td.cell-display, +.study-vaccine-design td.cell-value { + padding: 5px; + border: solid 1px #DDDDDD; + vertical-align: top; +} + +.study-vaccine-design td.cell-display { + height: 30px; +} +.study-vaccine-design td.cell-value { + height: 33px; +} + +.study-vaccine-design td.cell-value .x4-form-cb-wrap { + height: 18px; +} + +.study-vaccine-design table.subgrid td { + background-color: #FFFFFF !important; +} + +.study-vaccine-design tr.header-row td { + font-weight: bold; + padding: 5px; + background-color: #EEEEEE !important; + border-bottom-color: #C0C0C0; +} + +.study-vaccine-design table.outer tr.data-row td { + background-color: #FFFFFF; +} +.study-vaccine-design table.outer tr.alternate-row td { + background-color: #F4F4F4; +} + +.study-vaccine-design td.cell-value.missing-required { + background-color: #ffe5e5 !important; +} + +.study-vaccine-design td.empty { + font-style: italic; +} + +.study-vaccine-design td.action i { + color: #777777; +} +.study-vaccine-design td.action i:hover { + cursor: pointer; + color: #000000; +} + +.dialog-product-label .x4-form-cb-label-after { + /* Firefox */ + width: -moz-calc(100% - 20px); + /* WebKit */ + width: -webkit-calc(100% - 20px); + /* Standard */ + width: calc(100% - 20px); + white-space: nowrap; +} + +.treatment-input-cell .x4-form-field { + cursor: pointer; + opacity: 0.8; +} + diff --git a/study/webapp/study/vaccineDesign/VisitWindow.js b/studydesign/webapp/study/vaccineDesign/VisitWindow.js similarity index 96% rename from study/webapp/study/vaccineDesign/VisitWindow.js rename to studydesign/webapp/study/vaccineDesign/VisitWindow.js index 31e99c6a..f8c1917b 100644 --- a/study/webapp/study/vaccineDesign/VisitWindow.js +++ b/studydesign/webapp/study/vaccineDesign/VisitWindow.js @@ -1,319 +1,319 @@ -/* - * Copyright (c) 2016-2017 LabKey Corporation - * - * Licensed under the Apache License, Version 2.0: http://www.apache.org/licenses/LICENSE-2.0 - */ - -Ext4.define('LABKEY.VaccineDesign.VisitWindow', { - extend: 'Ext.window.Window', - - modal: true, - - visitStore: null, - - visitNoun: 'Visit', - - constructor: function(config) - { - this.callParent([config]); - this.addEvents('closewindow', 'selectexistingvisit', 'newvisitcreated'); - }, - - initComponent: function() - { - this.isTimepoint = this.visitNoun.toLowerCase() == 'timepoint'; - this.items = [this.getFormPanel()]; - this.callParent(); - }, - - getFormPanel : function() - { - if (!this.formPanel) - { - this.formPanel = Ext4.create('Ext.form.Panel',{ - border: false, - padding: 10, - items: [ - this.getExistingVisitRadio(), - this.getExistingVisitCombo(), - this.getNewVisitRadio(), - this.getNewVisitLabelField(), - this.getNewVisitMinMaxContainer() - ], - buttons: [ - this.getSelectBtn(), - this.getCancelBtn() - ] - }); - } - - return this.formPanel; - }, - - getExistingVisitRadio : function() - { - if (!this.existingVisitRadio) - { - this.existingVisitRadio = Ext4.create('Ext.form.field.Radio', { - name: 'visitType', - disabled: this.getFilteredVisitStore().getCount() == 0, - inputValue: 'existing', - boxLabel: 'Select an existing study ' + this.visitNoun.toLowerCase() + ':', - checked: this.getFilteredVisitStore().getCount() > 0, - hideFieldLabel: true, - width: 300 - }); - } - - return this.existingVisitRadio; - }, - - getNewVisitRadio : function() - { - if (!this.newVisitRadio) - { - this.newVisitRadio = Ext4.create('Ext.form.field.Radio', { - name: 'visitType', - inputValue: 'new', - boxLabel: 'Create a new study ' + this.visitNoun.toLowerCase() + ':', - checked: this.getFilteredVisitStore().getCount() == 0, - hideFieldLabel: true, - width: 300 - }); - - this.newVisitRadio.on('change', function(radio, newValue){ - this.getExistingVisitCombo().setDisabled(newValue); - this.getNewVisitLabelField().setDisabled(!newValue); - this.getNewVisitMinMaxContainer().setDisabled(!newValue); - this.getSelectBtn().setText(newValue ? 'Submit' : 'Select'); - this.updateSelectBtnState(); - - if (newValue) - this.getNewVisitLabelField().focus(); - else - this.getExistingVisitCombo().focus(); - }, this); - } - - return this.newVisitRadio; - }, - - getExistingVisitCombo : function() - { - if (!this.existingVisitCombo) - { - this.existingVisitCombo = Ext4.create('Ext.form.field.ComboBox', { - name: 'existingVisit', - disabled: this.getFilteredVisitStore().getCount() == 0, - hideFieldLabel: true, - style: 'margin-left: 15px;', - width: 300, - store: this.getFilteredVisitStore(), - editable: false, - queryMode: 'local', - displayField: 'Label', - valueField: 'RowId' - }); - - this.existingVisitCombo.on('change', this.updateSelectBtnState, this); - } - - return this.existingVisitCombo; - }, - - getFilteredVisitStore : function() - { - if (!this.filteredVisitStore) - { - var data = []; - if (this.visitStore != null) - { - Ext4.each(this.visitStore.query('Included', false).items, function(record) - { - var recData = Ext4.clone(record.data); - recData['Label'] = recData['Label'] || recData['SequenceNumMin']; - data.push(recData); - }, this); - } - - // add an option to select all existing visits for display - if (data.length > 1) - data.push({Label: '[Show All]', RowId: -1, DisplayOrder: -999999}); - - this.filteredVisitStore = Ext4.create('Ext.data.Store', { - model : 'LABKEY.VaccineDesign.Visit', - data : data, - sorters : [{property: 'DisplayOrder', direction: 'ASC'},{property: 'SequenceNumMin', direction: 'ASC'}] - }); - } - - return this.filteredVisitStore; - }, - - getNewVisitLabelField : function() - { - if (!this.newVisitLabelField) - { - this.newVisitLabelField = Ext4.create('Ext.form.field.Text', { - name: 'newVisitLabel', - disabled: this.getFilteredVisitStore().getCount() > 0, - fieldLabel: 'Label', - labelWidth: 50, - width: 300, - style: 'margin-left: 15px;' - }); - - this.newVisitLabelField.on('change', this.updateSelectBtnState, this); - } - - return this.newVisitLabelField; - }, - - getNewVisitMinField : function() - { - if (!this.newVisitMinField) - { - this.newVisitMinField = Ext4.create('Ext.form.field.Number', { - name: 'newVisitRangeMin', - hideLabel: true, - width: this.isTimepoint ? 100 : 80, - emptyText: 'min', - hideTrigger: true, - decimalPrecision: 4 - }); - - this.newVisitMinField.on('change', this.updateSelectBtnState, this); - } - - return this.newVisitMinField; - }, - - getNewVisitMaxField : function() - { - if (!this.newVisitMaxField) - { - this.newVisitMaxField = Ext4.create('Ext.form.field.Number', { - name: 'newVisitRangeMax', - hideLabel: true, - width: this.isTimepoint ? 100 : 80, - emptyText: 'max', - hideTrigger: true, - decimalPrecision: 4 - }); - - this.newVisitMinField.on('change', this.updateSelectBtnState, this); - } - - return this.newVisitMaxField; - }, - - getNewVisitMinMaxContainer : function() - { - if (!this.newVisitMinMaxContainer) - { - this.newVisitMinMaxContainer = Ext4.create('Ext.form.FieldContainer', { - layout: 'hbox', - style: 'margin-left: 15px; margin-bottom: 15px;', - fieldLabel: (this.isTimepoint ? 'Day' : 'Sequence') + ' Range', - labelWidth: this.isTimepoint ? 85 : 125, - disabled: this.getFilteredVisitStore().getCount() > 0, - items: [ - this.getNewVisitMinField(), - {xtype: 'label', width: 10}, // spacer - this.getNewVisitMaxField() - ] - }); - } - - return this.newVisitMinMaxContainer; - }, - - getSelectBtn : function() - { - if (!this.selectBtn) - { - this.selectBtn = Ext4.create('Ext.button.Button', { - text: this.getFilteredVisitStore().getCount() == 0 ? 'Submit' : 'Select', - disabled: true, - scope: this, - handler: function() { - var values = this.getFormPanel().getValues(); - - if (values['visitType'] == 'existing') - this.fireEvent('selectexistingvisit', this, values['existingVisit'] == -1 ? 'ALL' : values['existingVisit']); - else - this.createNewVisit(); - } - }); - } - - return this.selectBtn; - }, - - updateSelectBtnState : function() - { - var values = this.getFormPanel().getValues(); - - if (values['visitType'] == 'existing') - this.getSelectBtn().setDisabled(values['existingVisit'] == ''); - else - this.getSelectBtn().setDisabled(values['newVisitLabel'] == '' || values['newVisitRangeMin'] == ''); - }, - - getCancelBtn : function() - { - if (!this.cancelBtn) - { - this.cancelBtn = Ext4.create('Ext.button.Button', { - text: 'Cancel', - scope: this, - handler: function() { - this.fireEvent('closewindow', this); - } - }); - } - - return this.cancelBtn; - }, - - createNewVisit : function() - { - this.getEl().mask('Creating new visit...'); - var values = this.getFormPanel().getValues(); - - LABKEY.Ajax.request({ - url : LABKEY.ActionURL.buildURL('study-design', 'createVisit.api'), - method : 'POST', - jsonData: { - label: values['newVisitLabel'], - sequenceNumMin: values['newVisitRangeMin'], - sequenceNumMax: values['newVisitRangeMax'], - showByDefault: true - }, - success: function(response) { - var resp = Ext4.decode(response.responseText); - if (resp.success) - this.fireEvent('newvisitcreated', this, resp); - else - this.onFailure(); - }, - failure: function(response) { - var resp = Ext4.decode(response.responseText); - this.onFailure(resp.exception); - }, - scope : this - }); - }, - - onFailure : function(text) - { - Ext4.Msg.show({ - title: 'Error', - msg: text || 'Unknown error occurred.', - icon: Ext4.Msg.ERROR, - buttons: Ext4.Msg.OK - }); - - this.getEl().unmask(); - } +/* + * Copyright (c) 2016-2017 LabKey Corporation + * + * Licensed under the Apache License, Version 2.0: http://www.apache.org/licenses/LICENSE-2.0 + */ + +Ext4.define('LABKEY.VaccineDesign.VisitWindow', { + extend: 'Ext.window.Window', + + modal: true, + + visitStore: null, + + visitNoun: 'Visit', + + constructor: function(config) + { + this.callParent([config]); + this.addEvents('closewindow', 'selectexistingvisit', 'newvisitcreated'); + }, + + initComponent: function() + { + this.isTimepoint = this.visitNoun.toLowerCase() == 'timepoint'; + this.items = [this.getFormPanel()]; + this.callParent(); + }, + + getFormPanel : function() + { + if (!this.formPanel) + { + this.formPanel = Ext4.create('Ext.form.Panel',{ + border: false, + padding: 10, + items: [ + this.getExistingVisitRadio(), + this.getExistingVisitCombo(), + this.getNewVisitRadio(), + this.getNewVisitLabelField(), + this.getNewVisitMinMaxContainer() + ], + buttons: [ + this.getSelectBtn(), + this.getCancelBtn() + ] + }); + } + + return this.formPanel; + }, + + getExistingVisitRadio : function() + { + if (!this.existingVisitRadio) + { + this.existingVisitRadio = Ext4.create('Ext.form.field.Radio', { + name: 'visitType', + disabled: this.getFilteredVisitStore().getCount() == 0, + inputValue: 'existing', + boxLabel: 'Select an existing study ' + this.visitNoun.toLowerCase() + ':', + checked: this.getFilteredVisitStore().getCount() > 0, + hideFieldLabel: true, + width: 300 + }); + } + + return this.existingVisitRadio; + }, + + getNewVisitRadio : function() + { + if (!this.newVisitRadio) + { + this.newVisitRadio = Ext4.create('Ext.form.field.Radio', { + name: 'visitType', + inputValue: 'new', + boxLabel: 'Create a new study ' + this.visitNoun.toLowerCase() + ':', + checked: this.getFilteredVisitStore().getCount() == 0, + hideFieldLabel: true, + width: 300 + }); + + this.newVisitRadio.on('change', function(radio, newValue){ + this.getExistingVisitCombo().setDisabled(newValue); + this.getNewVisitLabelField().setDisabled(!newValue); + this.getNewVisitMinMaxContainer().setDisabled(!newValue); + this.getSelectBtn().setText(newValue ? 'Submit' : 'Select'); + this.updateSelectBtnState(); + + if (newValue) + this.getNewVisitLabelField().focus(); + else + this.getExistingVisitCombo().focus(); + }, this); + } + + return this.newVisitRadio; + }, + + getExistingVisitCombo : function() + { + if (!this.existingVisitCombo) + { + this.existingVisitCombo = Ext4.create('Ext.form.field.ComboBox', { + name: 'existingVisit', + disabled: this.getFilteredVisitStore().getCount() == 0, + hideFieldLabel: true, + style: 'margin-left: 15px;', + width: 300, + store: this.getFilteredVisitStore(), + editable: false, + queryMode: 'local', + displayField: 'Label', + valueField: 'RowId' + }); + + this.existingVisitCombo.on('change', this.updateSelectBtnState, this); + } + + return this.existingVisitCombo; + }, + + getFilteredVisitStore : function() + { + if (!this.filteredVisitStore) + { + var data = []; + if (this.visitStore != null) + { + Ext4.each(this.visitStore.query('Included', false).items, function(record) + { + var recData = Ext4.clone(record.data); + recData['Label'] = recData['Label'] || recData['SequenceNumMin']; + data.push(recData); + }, this); + } + + // add an option to select all existing visits for display + if (data.length > 1) + data.push({Label: '[Show All]', RowId: -1, DisplayOrder: -999999}); + + this.filteredVisitStore = Ext4.create('Ext.data.Store', { + model : 'LABKEY.VaccineDesign.Visit', + data : data, + sorters : [{property: 'DisplayOrder', direction: 'ASC'},{property: 'SequenceNumMin', direction: 'ASC'}] + }); + } + + return this.filteredVisitStore; + }, + + getNewVisitLabelField : function() + { + if (!this.newVisitLabelField) + { + this.newVisitLabelField = Ext4.create('Ext.form.field.Text', { + name: 'newVisitLabel', + disabled: this.getFilteredVisitStore().getCount() > 0, + fieldLabel: 'Label', + labelWidth: 50, + width: 300, + style: 'margin-left: 15px;' + }); + + this.newVisitLabelField.on('change', this.updateSelectBtnState, this); + } + + return this.newVisitLabelField; + }, + + getNewVisitMinField : function() + { + if (!this.newVisitMinField) + { + this.newVisitMinField = Ext4.create('Ext.form.field.Number', { + name: 'newVisitRangeMin', + hideLabel: true, + width: this.isTimepoint ? 100 : 80, + emptyText: 'min', + hideTrigger: true, + decimalPrecision: 4 + }); + + this.newVisitMinField.on('change', this.updateSelectBtnState, this); + } + + return this.newVisitMinField; + }, + + getNewVisitMaxField : function() + { + if (!this.newVisitMaxField) + { + this.newVisitMaxField = Ext4.create('Ext.form.field.Number', { + name: 'newVisitRangeMax', + hideLabel: true, + width: this.isTimepoint ? 100 : 80, + emptyText: 'max', + hideTrigger: true, + decimalPrecision: 4 + }); + + this.newVisitMinField.on('change', this.updateSelectBtnState, this); + } + + return this.newVisitMaxField; + }, + + getNewVisitMinMaxContainer : function() + { + if (!this.newVisitMinMaxContainer) + { + this.newVisitMinMaxContainer = Ext4.create('Ext.form.FieldContainer', { + layout: 'hbox', + style: 'margin-left: 15px; margin-bottom: 15px;', + fieldLabel: (this.isTimepoint ? 'Day' : 'Sequence') + ' Range', + labelWidth: this.isTimepoint ? 85 : 125, + disabled: this.getFilteredVisitStore().getCount() > 0, + items: [ + this.getNewVisitMinField(), + {xtype: 'label', width: 10}, // spacer + this.getNewVisitMaxField() + ] + }); + } + + return this.newVisitMinMaxContainer; + }, + + getSelectBtn : function() + { + if (!this.selectBtn) + { + this.selectBtn = Ext4.create('Ext.button.Button', { + text: this.getFilteredVisitStore().getCount() == 0 ? 'Submit' : 'Select', + disabled: true, + scope: this, + handler: function() { + var values = this.getFormPanel().getValues(); + + if (values['visitType'] == 'existing') + this.fireEvent('selectexistingvisit', this, values['existingVisit'] == -1 ? 'ALL' : values['existingVisit']); + else + this.createNewVisit(); + } + }); + } + + return this.selectBtn; + }, + + updateSelectBtnState : function() + { + var values = this.getFormPanel().getValues(); + + if (values['visitType'] == 'existing') + this.getSelectBtn().setDisabled(values['existingVisit'] == ''); + else + this.getSelectBtn().setDisabled(values['newVisitLabel'] == '' || values['newVisitRangeMin'] == ''); + }, + + getCancelBtn : function() + { + if (!this.cancelBtn) + { + this.cancelBtn = Ext4.create('Ext.button.Button', { + text: 'Cancel', + scope: this, + handler: function() { + this.fireEvent('closewindow', this); + } + }); + } + + return this.cancelBtn; + }, + + createNewVisit : function() + { + this.getEl().mask('Creating new visit...'); + var values = this.getFormPanel().getValues(); + + LABKEY.Ajax.request({ + url : LABKEY.ActionURL.buildURL('study', 'createVisitForVaccineDesign.api'), + method : 'POST', + jsonData: { + label: values['newVisitLabel'], + sequenceNumMin: values['newVisitRangeMin'], + sequenceNumMax: values['newVisitRangeMax'], + showByDefault: true + }, + success: function(response) { + var resp = Ext4.decode(response.responseText); + if (resp.success) + this.fireEvent('newvisitcreated', this, resp); + else + this.onFailure(); + }, + failure: function(response) { + var resp = Ext4.decode(response.responseText); + this.onFailure(resp.exception); + }, + scope : this + }); + }, + + onFailure : function(text) + { + Ext4.Msg.show({ + title: 'Error', + msg: text || 'Unknown error occurred.', + icon: Ext4.Msg.ERROR, + buttons: Ext4.Msg.OK + }); + + this.getEl().unmask(); + } }); \ No newline at end of file diff --git a/study/webapp/study/vaccineDesign/vaccineDesign.lib.xml b/studydesign/webapp/study/vaccineDesign/vaccineDesign.lib.xml similarity index 98% rename from study/webapp/study/vaccineDesign/vaccineDesign.lib.xml rename to studydesign/webapp/study/vaccineDesign/vaccineDesign.lib.xml index da960e19..0cdb9ab0 100644 --- a/study/webapp/study/vaccineDesign/vaccineDesign.lib.xml +++ b/studydesign/webapp/study/vaccineDesign/vaccineDesign.lib.xml @@ -1,18 +1,18 @@ - - -