diff --git a/api/src/org/labkey/api/study/Study.java b/api/src/org/labkey/api/study/Study.java index 08975ea5abb..39e27ccf72e 100644 --- a/api/src/org/labkey/api/study/Study.java +++ b/api/src/org/labkey/api/study/Study.java @@ -19,7 +19,6 @@ import org.jetbrains.annotations.Nullable; import org.labkey.api.attachments.Attachment; import org.labkey.api.attachments.AttachmentFile; -import org.labkey.api.data.Container; import org.labkey.api.security.User; import org.labkey.api.util.HtmlString; @@ -58,18 +57,6 @@ public interface Study extends StudyEntity Collection getCohorts(User user); - Collection getAssaySpecimenConfigs(); - - List getVisitsForAssaySchedule(); - - List getStudyProducts(User user, String role); - - List getStudyTreatments(User user); - - List getStudyTreatmentVisitMap(Container container, @Nullable Integer cohortId); - - List getVisitsForTreatmentSchedule(); - List getParticipantCategories(User user); @Nullable String getParticipantCohortProperty(); diff --git a/api/src/org/labkey/api/study/StudyService.java b/api/src/org/labkey/api/study/StudyService.java index 369aaa82b8a..d1c2781cc0d 100644 --- a/api/src/org/labkey/api/study/StudyService.java +++ b/api/src/org/labkey/api/study/StudyService.java @@ -28,6 +28,7 @@ import org.labkey.api.module.Module; import org.labkey.api.query.QuerySchema; import org.labkey.api.query.UserSchema; +import org.labkey.api.query.ValidationException; import org.labkey.api.reports.model.ViewCategory; import org.labkey.api.reports.report.view.ReportUtil; import org.labkey.api.security.User; @@ -233,4 +234,7 @@ interface StudyTabProvider ReportUtil.ReportFilter getStudyReportFilter(boolean editOnly); Map getVisitImportMap(Study study, boolean includeStandardMapping); + + // Methods created to support Vaccine study designs + ValidationException updateAssayPlan(User user, Study study, String assayPlan); } diff --git a/api/src/org/labkey/api/study/StudyUrls.java b/api/src/org/labkey/api/study/StudyUrls.java index 4251a54234d..27f0abf8cf4 100644 --- a/api/src/org/labkey/api/study/StudyUrls.java +++ b/api/src/org/labkey/api/study/StudyUrls.java @@ -40,8 +40,6 @@ public interface StudyUrls extends UrlProvider ActionURL getDatasetsURL(Container container); ActionURL getManageDatasetsURL(Container container); ActionURL getManageReportPermissions(Container container); - ActionURL getManageAssayScheduleURL(Container container, boolean useAlternateLookupFields); - ActionURL getManageTreatmentsURL(Container container, boolean useSingleTableEditor); ActionURL getManageFileWatchersURL(Container container); ActionURL getLinkToStudyURL(Container container, ExpSampleType sampleType); ActionURL getLinkToStudyURL(Container container, ExpProtocol protocol); @@ -50,4 +48,9 @@ public interface StudyUrls extends UrlProvider ActionURL getTypeNotFoundURL(Container container, int datasetId); void addManageStudyNavTrail(NavTree root, Container container, User user); + + ActionURL getManageLocationsURL(Container container); + ActionURL getManageVisitsURL(Container container); + ActionURL getManageCohortsURL(Container container); + ActionURL getVisitOrderURL(Container container); } diff --git a/study/api-src/org/labkey/api/study/StudyUtils.java b/study/api-src/org/labkey/api/study/StudyUtils.java index c3a728c3c64..ca890fa5e76 100644 --- a/study/api-src/org/labkey/api/study/StudyUtils.java +++ b/study/api-src/org/labkey/api/study/StudyUtils.java @@ -28,8 +28,6 @@ public class StudyUtils public static final String SUBMISSION_WARNING = "Once a request is submitted, its specimen list may no longer be modified. Continue?"; public static final String CANCELLATION_WARNING = "Canceling will permanently delete this pending request. Continue?"; - public static final String STUDY_DESIGN_FEATURE_FLAG = "studyDesignFlag"; - //Create a fixed point number encoding the date. public static double sequenceNumFromDate(Date d) { diff --git a/study/api-src/org/labkey/api/study/model/CohortService.java b/study/api-src/org/labkey/api/study/model/CohortService.java index 2c43becf49e..c0ca7cdf6d9 100644 --- a/study/api-src/org/labkey/api/study/model/CohortService.java +++ b/study/api-src/org/labkey/api/study/model/CohortService.java @@ -2,10 +2,12 @@ import org.jetbrains.annotations.Nullable; import org.labkey.api.data.Container; +import org.labkey.api.query.ValidationException; import org.labkey.api.security.User; import org.labkey.api.services.ServiceRegistry; import org.labkey.api.study.Cohort; import org.labkey.api.study.CohortFilter; +import org.labkey.api.study.Study; import org.labkey.api.view.ActionURL; import java.util.Collection; @@ -37,4 +39,15 @@ static void setInstance(CohortService impl) Collection getCohortFilters(CohortFilter.Type type, Container c, User user); Collection getCohorts(Container container, User user); + + // Methods created to support Vaccine study designs + Cohort getCohortByLabel(Container container, User user, String label); + + Cohort getCohortForRowId(Container container, User user, int rowId); + + void deleteCohort(Cohort cohort); + + Cohort updateCohort(Container container, User user, int rowId, String label, Integer subjectCount); + + Cohort createCohort(Study study, User user, String newLabel, boolean enrolled, Integer subjectCount, String description) throws ValidationException; } diff --git a/study/api-src/org/labkey/api/study/model/VisitService.java b/study/api-src/org/labkey/api/study/model/VisitService.java index 6cb394c51fb..3f916f37ac0 100644 --- a/study/api-src/org/labkey/api/study/model/VisitService.java +++ b/study/api-src/org/labkey/api/study/model/VisitService.java @@ -1,6 +1,7 @@ package org.labkey.api.study.model; import org.apache.logging.log4j.Logger; +import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.labkey.api.query.ValidationException; import org.labkey.api.security.User; @@ -8,6 +9,7 @@ import org.labkey.api.study.Study; import org.labkey.api.study.Visit; +import java.math.BigDecimal; import java.util.Collection; /** @@ -34,4 +36,9 @@ static void setInstance(VisitService impl) * Updates this study's participant, visit, and participant visit tables. Also updates automatic cohort assignments. */ ValidationException updateParticipantVisits(Study study, User user); + + // Methods created to support Vaccine study designs + Visit createVisit(Study study, User user, @NotNull BigDecimal seqMin, String label, Visit.Type type); + + void deleteVisit(Study study, User user, Visit visit); } diff --git a/study/api-src/org/labkey/api/studydesign/StudyDesignManager.java b/study/api-src/org/labkey/api/studydesign/StudyDesignManager.java index d0d09e4f1d6..fd6fe5ff73d 100644 --- a/study/api-src/org/labkey/api/studydesign/StudyDesignManager.java +++ b/study/api-src/org/labkey/api/studydesign/StudyDesignManager.java @@ -22,6 +22,8 @@ import org.labkey.api.data.Table; import org.labkey.api.data.TableInfo; import org.labkey.api.exp.property.PropertyService; +import org.labkey.api.module.Module; +import org.labkey.api.module.ModuleLoader; import org.labkey.api.security.User; import org.labkey.api.studydesign.query.AbstractStudyDesignDomainKind; import org.labkey.api.studydesign.query.StudyDesignQuerySchema; @@ -43,12 +45,22 @@ public class StudyDesignManager { private static final StudyDesignManager _instance = new StudyDesignManager(); + public static final String MODULE_NAME = "StudyDesign"; public static StudyDesignManager get() { return _instance; } + public boolean isModuleActive(Container c) + { + if (c == null) + return false; + + Module studyDesignModule = ModuleLoader.getInstance().getModule(MODULE_NAME); + return null != studyDesignModule && c.getActiveModules().contains(studyDesignModule); + } + public void deleteStudyDesignData(Container c, Set deletedTables) { Filter filter = SimpleFilter.createContainerFilter(c); @@ -77,6 +89,11 @@ public void deleteStudyDesignData(Container c, Set deletedTables) deletedTables.add(StudyDesignSchema.getInstance().getTableInfoTreatmentVisitMap()); Table.delete(StudyDesignSchema.getInstance().getTableInfoObjective(), filter); deletedTables.add(StudyDesignSchema.getInstance().getTableInfoObjective()); + + Table.delete(StudyDesignSchema.getInstance().getTableInfoAssaySpecimenVisit(), filter); + deletedTables.add(StudyDesignSchema.getInstance().getTableInfoAssaySpecimenVisit()); + Table.delete(StudyDesignSchema.getInstance().getTableInfoAssaySpecimen(), filter); + deletedTables.add(StudyDesignSchema.getInstance().getTableInfoAssaySpecimen()); } // Proactively create the domains at study creation time to avoid problems with lazy creation, #42641 diff --git a/study/api-src/org/labkey/api/studydesign/StudyDesignService.java b/study/api-src/org/labkey/api/studydesign/StudyDesignService.java new file mode 100644 index 00000000000..11e3077d780 --- /dev/null +++ b/study/api-src/org/labkey/api/studydesign/StudyDesignService.java @@ -0,0 +1,31 @@ +package org.labkey.api.studydesign; + +import org.jetbrains.annotations.Nullable; +import org.labkey.api.data.Container; +import org.labkey.api.security.User; +import org.labkey.api.services.ServiceRegistry; +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 java.util.Collection; +import java.util.List; + +public interface StudyDesignService +{ + @Nullable + static StudyDesignService get() + { + return ServiceRegistry.get().getService(StudyDesignService.class); + } + + List getStudyProducts(Container c, User user, String role); + List getStudyTreatments(Container c, User user); + List getVisitsForTreatmentSchedule(Container c); + Collection getAssaySpecimenConfigs(Container c); + void deleteTreatmentVisitMapForCohort(Container container, Integer cohortId); + void deleteTreatmentVisitMapForVisit(Container container, Integer visitId); + void deleteAssaySpecimenVisits(Container container, int visitId); + +} diff --git a/study/api-src/org/labkey/api/studydesign/StudyDesignUrls.java b/study/api-src/org/labkey/api/studydesign/StudyDesignUrls.java new file mode 100644 index 00000000000..c294e293550 --- /dev/null +++ b/study/api-src/org/labkey/api/studydesign/StudyDesignUrls.java @@ -0,0 +1,12 @@ +package org.labkey.api.studydesign; + +import org.labkey.api.action.UrlProvider; +import org.labkey.api.data.Container; +import org.labkey.api.view.ActionURL; + +public interface StudyDesignUrls extends UrlProvider +{ + ActionURL getManageAssayScheduleURL(Container container, boolean useAlternateLookupFields); + ActionURL getManageStudyProductsURL(Container container); + ActionURL getManageTreatmentsURL(Container container, boolean useSingleTableEditor); +} diff --git a/study/src/org/labkey/study/query/AssaySpecimenTable.java b/study/api-src/org/labkey/api/studydesign/query/AssaySpecimenTable.java similarity index 85% rename from study/src/org/labkey/study/query/AssaySpecimenTable.java rename to study/api-src/org/labkey/api/studydesign/query/AssaySpecimenTable.java index a384ac94d7f..14fa2e5cdaa 100644 --- a/study/src/org/labkey/study/query/AssaySpecimenTable.java +++ b/study/api-src/org/labkey/api/studydesign/query/AssaySpecimenTable.java @@ -1,107 +1,108 @@ -/* - * 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; - -import org.labkey.api.data.ColumnInfo; -import org.labkey.api.data.ContainerFilter; -import org.labkey.api.query.AliasedColumn; -import org.labkey.api.query.DefaultQueryUpdateService; -import org.labkey.api.query.QueryForeignKey; -import org.labkey.api.query.QueryUpdateService; -import org.labkey.api.query.column.BuiltInColumnTypes; -import org.labkey.api.security.UserPrincipal; -import org.labkey.api.security.permissions.Permission; -import org.labkey.study.StudySchema; - -import static org.labkey.api.studydesign.query.StudyDesignQuerySchema.STUDY_DESIGN_ASSAYS_TABLE_NAME; -import static org.labkey.api.studydesign.query.StudyDesignQuerySchema.STUDY_DESIGN_LABS_TABLE_NAME; -import static org.labkey.api.studydesign.query.StudyDesignQuerySchema.STUDY_DESIGN_SAMPLE_TYPES_TABLE_NAME; -import static org.labkey.api.studydesign.query.StudyDesignQuerySchema.STUDY_DESIGN_UNITS_TABLE_NAME; - -/** - * User: cnathe - * Date: 12/13/13 -*/ -public class AssaySpecimenTable extends BaseStudyTable -{ - private QueryForeignKey.Builder studyFK() - { - return QueryForeignKey.from(_userSchema,getContainerFilter()); - } - - public AssaySpecimenTable(StudyQuerySchema schema, ContainerFilter cf) - { - super(schema, StudySchema.getInstance().getTableInfoAssaySpecimen(), cf); - setName(StudyQuerySchema.ASSAY_SPECIMEN_TABLE_NAME); - - addWrapColumn(_rootTable.getColumn("RowId")); - - var assayColumn = new AliasedColumn(this, "AssayName", _rootTable.getColumn("AssayName")); - assayColumn.setFk(studyFK().to(STUDY_DESIGN_ASSAYS_TABLE_NAME, "Name", null)); - addColumn(assayColumn); - - addWrapColumn(_rootTable.getColumn("Description")); - addWrapLocationColumn("LocationId", "LocationId"); - var dataSetColumn = new AliasedColumn(this, "DataSet", _rootTable.getColumn("DataSet")); - dataSetColumn.setFk(studyFK().to("DataSets", "DataSetId", null)); - addColumn(dataSetColumn); - - addWrapColumn(_rootTable.getColumn("Source")); - //addWrapTypeColumn("PrimaryTypeId", "PrimaryTypeId"); - //addWrapTypeColumn("DerivativeTypeId", "DerivativeTypeId"); - addWrapColumn(_rootTable.getColumn("TubeType")); - - var labColumn = new AliasedColumn(this, "Lab", _rootTable.getColumn("Lab")); - labColumn.setFk(studyFK().to(STUDY_DESIGN_LABS_TABLE_NAME, "Name", null)); - addColumn(labColumn); - - var sampleTypeColumn = new AliasedColumn(this, "SampleType", _rootTable.getColumn("SampleType")); - sampleTypeColumn.setFk(studyFK().to(STUDY_DESIGN_SAMPLE_TYPES_TABLE_NAME, "Name", null)); - addColumn(sampleTypeColumn); - - addWrapColumn(_rootTable.getColumn("SampleQuantity")); - var sampleUnitsColumn = new AliasedColumn(this, "SampleUnits", _rootTable.getColumn("SampleUnits")); - sampleUnitsColumn.setFk(studyFK().to(STUDY_DESIGN_UNITS_TABLE_NAME, "Name", null)); - addColumn(sampleUnitsColumn); - - addContainerColumn(); - for (ColumnInfo baseColumn : _rootTable.getColumns()) - { - BuiltInColumnTypes type = BuiltInColumnTypes.findBuiltInType(baseColumn); - if (null != type && type != BuiltInColumnTypes.Container) - addWrapColumn(baseColumn); - } - } - - @Override - public String getTitleColumn() - { - return "AssayName"; - } - - @Override - public QueryUpdateService getUpdateService() - { - return new DefaultQueryUpdateService(this, this.getRealTable()); - } - - @Override - protected boolean hasPermissionOverridable(UserPrincipal user, Class perm) - { - // see StudyDesignController.UpdateAssayScheduleAction @RequiresPermission(UpdatePermission.class) - return checkContainerPermission(user, perm); - } +/* + * 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.api.studydesign.query; + +import org.labkey.api.data.ColumnInfo; +import org.labkey.api.data.ContainerFilter; +import org.labkey.api.query.AliasedColumn; +import org.labkey.api.query.DefaultQueryUpdateService; +import org.labkey.api.query.QueryForeignKey; +import org.labkey.api.query.QueryUpdateService; +import org.labkey.api.query.column.BuiltInColumnTypes; +import org.labkey.api.security.UserPrincipal; +import org.labkey.api.security.permissions.Permission; + +import static org.labkey.api.studydesign.query.StudyDesignQuerySchema.STUDY_DESIGN_ASSAYS_TABLE_NAME; +import static org.labkey.api.studydesign.query.StudyDesignQuerySchema.STUDY_DESIGN_LABS_TABLE_NAME; +import static org.labkey.api.studydesign.query.StudyDesignQuerySchema.STUDY_DESIGN_SAMPLE_TYPES_TABLE_NAME; +import static org.labkey.api.studydesign.query.StudyDesignQuerySchema.STUDY_DESIGN_UNITS_TABLE_NAME; + +/** + * User: cnathe + * Date: 12/13/13 +*/ +public class AssaySpecimenTable extends StudyDesignBaseTable +{ + private QueryForeignKey.Builder studyFK() + { + return QueryForeignKey.from(_userSchema,getContainerFilter()); + } + + public AssaySpecimenTable(StudyDesignQuerySchema schema, ContainerFilter cf) + { + super(schema, StudyDesignSchema.getInstance().getTableInfoAssaySpecimen(), cf); + setName(StudyDesignQuerySchema.ASSAY_SPECIMEN_TABLE_NAME); + + addWrapColumn(_rootTable.getColumn("RowId")); + + var assayColumn = new AliasedColumn(this, "AssayName", _rootTable.getColumn("AssayName")); + assayColumn.setFk(studyFK().to(STUDY_DESIGN_ASSAYS_TABLE_NAME, "Name", null)); + addColumn(assayColumn); + + addWrapColumn(_rootTable.getColumn("Description")); + + var locationCol = new AliasedColumn(this, "LocationId", _rootTable.getColumn("LocationId")); + locationCol.setFk(QueryForeignKey.from(schema, cf).schema("study").to("location", "rowId", null)); + addColumn(locationCol); + + var dataSetColumn = new AliasedColumn(this, "DataSet", _rootTable.getColumn("DataSet")); + dataSetColumn.setFk(studyFK().to("DataSets", "DataSetId", null)); + addColumn(dataSetColumn); + + addWrapColumn(_rootTable.getColumn("Source")); + addWrapColumn(_rootTable.getColumn("TubeType")); + + var labColumn = new AliasedColumn(this, "Lab", _rootTable.getColumn("Lab")); + labColumn.setFk(studyFK().to(STUDY_DESIGN_LABS_TABLE_NAME, "Name", null)); + addColumn(labColumn); + + var sampleTypeColumn = new AliasedColumn(this, "SampleType", _rootTable.getColumn("SampleType")); + sampleTypeColumn.setFk(studyFK().to(STUDY_DESIGN_SAMPLE_TYPES_TABLE_NAME, "Name", null)); + addColumn(sampleTypeColumn); + + addWrapColumn(_rootTable.getColumn("SampleQuantity")); + var sampleUnitsColumn = new AliasedColumn(this, "SampleUnits", _rootTable.getColumn("SampleUnits")); + sampleUnitsColumn.setFk(studyFK().to(STUDY_DESIGN_UNITS_TABLE_NAME, "Name", null)); + addColumn(sampleUnitsColumn); + + addContainerColumn(); + for (ColumnInfo baseColumn : _rootTable.getColumns()) + { + BuiltInColumnTypes type = BuiltInColumnTypes.findBuiltInType(baseColumn); + if (null != type && type != BuiltInColumnTypes.Container) + addWrapColumn(baseColumn); + } + } + + @Override + public String getTitleColumn() + { + return "AssayName"; + } + + @Override + public QueryUpdateService getUpdateService() + { + return new DefaultQueryUpdateService(this, this.getRealTable()); + } + + @Override + protected boolean hasPermissionOverridable(UserPrincipal user, Class perm) + { + // see StudyDesignController.UpdateAssayScheduleAction @RequiresPermission(UpdatePermission.class) + return checkContainerPermission(user, perm); + } } \ No newline at end of file diff --git a/study/src/org/labkey/study/query/AssaySpecimenVisitTable.java b/study/api-src/org/labkey/api/studydesign/query/AssaySpecimenVisitTable.java similarity index 77% rename from study/src/org/labkey/study/query/AssaySpecimenVisitTable.java rename to study/api-src/org/labkey/api/studydesign/query/AssaySpecimenVisitTable.java index 43624cd3cb7..625c53909b7 100644 --- a/study/src/org/labkey/study/query/AssaySpecimenVisitTable.java +++ b/study/api-src/org/labkey/api/studydesign/query/AssaySpecimenVisitTable.java @@ -1,88 +1,81 @@ -/* - * 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; - -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; -import org.labkey.api.query.LookupForeignKey; -import org.labkey.api.query.QueryUpdateService; -import org.labkey.api.query.column.BuiltInColumnTypes; -import org.labkey.api.security.UserPrincipal; -import org.labkey.api.security.permissions.Permission; -import org.labkey.study.StudySchema; - -/** - * User: cnathe - * Date: 12/13/13 - */ -public class AssaySpecimenVisitTable extends BaseStudyTable -{ - public AssaySpecimenVisitTable(StudyQuerySchema schema, ContainerFilter cf) - { - super(schema, StudySchema.getInstance().getTableInfoAssaySpecimenVisit(), cf); - setName(StudyQuerySchema.ASSAY_SPECIMEN_VISIT_TABLE_NAME); - - addWrapColumn(_rootTable.getColumn("RowId")); - - var visitCol = new AliasedColumn(this, "VisitId", _rootTable.getColumn("VisitId")); - visitCol.setFk(new LookupForeignKey(cf, "RowId", null) - { - @Override - public TableInfo getLookupTableInfo() - { - return new VisitTable(_userSchema, getLookupContainerFilter()); - } - }); - addColumn(visitCol); - - var assaySpecimenCol = new AliasedColumn(this, "AssaySpecimenId", _rootTable.getColumn("AssaySpecimenId")); - assaySpecimenCol.setFk(new LookupForeignKey(cf, "RowId", null) - { - @Override - public TableInfo getLookupTableInfo() - { - return new AssaySpecimenTable(_userSchema, getLookupContainerFilter()); - } - }); - addColumn(assaySpecimenCol); - - addContainerColumn(); - for (ColumnInfo baseColumn : _rootTable.getColumns()) - { - BuiltInColumnTypes type = BuiltInColumnTypes.findBuiltInType(baseColumn); - if (null != type && type != BuiltInColumnTypes.Container) - { - addWrapColumn(baseColumn); - } - } - } - - @Override - public boolean hasPermissionOverridable(UserPrincipal user, Class perm) - { - // see StudyDesignController.UpdateAssayScheduleAction @RequiresPermission(UpdatePermission.class) - return checkContainerPermission(user, perm); - } - - @Override - public QueryUpdateService getUpdateService() - { - return new DefaultQueryUpdateService(this, this.getRealTable()); - } -} +/* + * 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.api.studydesign.query; + +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; +import org.labkey.api.query.LookupForeignKey; +import org.labkey.api.query.QueryForeignKey; +import org.labkey.api.query.QueryUpdateService; +import org.labkey.api.query.column.BuiltInColumnTypes; +import org.labkey.api.security.UserPrincipal; +import org.labkey.api.security.permissions.Permission; + +/** + * User: cnathe + * Date: 12/13/13 + */ +public class AssaySpecimenVisitTable extends StudyDesignBaseTable +{ + public AssaySpecimenVisitTable(StudyDesignQuerySchema schema, ContainerFilter cf) + { + super(schema, StudyDesignSchema.getInstance().getTableInfoAssaySpecimenVisit(), cf); + setName(StudyDesignQuerySchema.ASSAY_SPECIMEN_VISIT_TABLE_NAME); + + addWrapColumn(_rootTable.getColumn("RowId")); + + var visitCol = new AliasedColumn(this, "VisitId", _rootTable.getColumn("VisitId")); + visitCol.setFk(QueryForeignKey.from(schema, cf).schema("study").to("visit", "rowId", null)); + addColumn(visitCol); + + var assaySpecimenCol = new AliasedColumn(this, "AssaySpecimenId", _rootTable.getColumn("AssaySpecimenId")); + assaySpecimenCol.setFk(new LookupForeignKey(cf, "RowId", null) + { + @Override + public TableInfo getLookupTableInfo() + { + return new AssaySpecimenTable(_userSchema, getLookupContainerFilter()); + } + }); + addColumn(assaySpecimenCol); + + addContainerColumn(); + for (ColumnInfo baseColumn : _rootTable.getColumns()) + { + BuiltInColumnTypes type = BuiltInColumnTypes.findBuiltInType(baseColumn); + if (null != type && type != BuiltInColumnTypes.Container) + { + addWrapColumn(baseColumn); + } + } + } + + @Override + public boolean hasPermissionOverridable(UserPrincipal user, Class perm) + { + // see StudyDesignController.UpdateAssayScheduleAction @RequiresPermission(UpdatePermission.class) + return checkContainerPermission(user, perm); + } + + @Override + public QueryUpdateService getUpdateService() + { + return new DefaultQueryUpdateService(this, this.getRealTable()); + } +} diff --git a/study/api-src/org/labkey/api/studydesign/query/StudyDesignQuerySchema.java b/study/api-src/org/labkey/api/studydesign/query/StudyDesignQuerySchema.java index f66bd480752..7d998a6e3a5 100644 --- a/study/api-src/org/labkey/api/studydesign/query/StudyDesignQuerySchema.java +++ b/study/api-src/org/labkey/api/studydesign/query/StudyDesignQuerySchema.java @@ -10,10 +10,9 @@ import org.labkey.api.query.UserSchema; import org.labkey.api.security.User; import org.labkey.api.security.roles.Role; -import org.labkey.api.settings.OptionalFeatureService; import org.labkey.api.study.Study; import org.labkey.api.study.StudyService; -import org.labkey.api.study.StudyUtils; +import org.labkey.api.studydesign.StudyDesignManager; import java.util.HashSet; import java.util.Set; @@ -44,6 +43,9 @@ public class StudyDesignQuerySchema extends SimpleUserSchema implements UserSche public static final String TREATMENT_VISIT_MAP_TABLE_NAME = "TreatmentVisitMap"; public static final String OBJECTIVE_TABLE_NAME = "Objective"; + public static final String ASSAY_SPECIMEN_TABLE_NAME = "AssaySpecimen"; + public static final String ASSAY_SPECIMEN_VISIT_TABLE_NAME = "AssaySpecimenVisit"; + protected Study _study; private Role _contextualRole; private UserSchema _parentSchema; @@ -69,7 +71,9 @@ public class StudyDesignQuerySchema extends SimpleUserSchema implements UserSche TREATMENT_TABLE_NAME, PERSONNEL_TABLE_NAME, TREATMENT_VISIT_MAP_TABLE_NAME, - OBJECTIVE_TABLE_NAME + OBJECTIVE_TABLE_NAME, + ASSAY_SPECIMEN_TABLE_NAME, + ASSAY_SPECIMEN_VISIT_TABLE_NAME ); private StudyDesignQuerySchema(UserSchema studySchema, Study study, @Nullable Role contextualRole) @@ -85,7 +89,7 @@ private StudyDesignQuerySchema(UserSchema studySchema, Study study, @Nullable Ro @Nullable public static StudyDesignQuerySchema get(UserSchema userSchema, Container c, @Nullable Study study, @Nullable Role contextualRole) { - return OptionalFeatureService.get().isFeatureEnabled(StudyUtils.STUDY_DESIGN_FEATURE_FLAG) + return StudyDesignManager.get().isModuleActive(c) ? new StudyDesignQuerySchema(userSchema, study, contextualRole) : null; } @@ -205,6 +209,14 @@ private static Set getAvailableTables(Study study) { return new StudyObjectiveTable(this, cf); } + if (ASSAY_SPECIMEN_TABLE_NAME.equalsIgnoreCase(name)) + { + return new AssaySpecimenTable(this, cf); + } + if (ASSAY_SPECIMEN_VISIT_TABLE_NAME.equalsIgnoreCase(name)) + { + return new AssaySpecimenVisitTable(this, cf); + } return null; } diff --git a/study/api-src/org/labkey/api/studydesign/query/StudyDesignSchema.java b/study/api-src/org/labkey/api/studydesign/query/StudyDesignSchema.java index de46cbfed2b..00cac3f5a93 100644 --- a/study/api-src/org/labkey/api/studydesign/query/StudyDesignSchema.java +++ b/study/api-src/org/labkey/api/studydesign/query/StudyDesignSchema.java @@ -99,4 +99,14 @@ public TableInfo getTableInfoObjective() { return getSchema().getTable("Objective"); } + + public TableInfo getTableInfoAssaySpecimen() + { + return getSchema().getTable("AssaySpecimen"); + } + + public TableInfo getTableInfoAssaySpecimenVisit() + { + return getSchema().getTable("AssaySpecimenVisit"); + } } diff --git a/study/src/org/labkey/study/CohortServiceImpl.java b/study/src/org/labkey/study/CohortServiceImpl.java index bc92f0993a8..b914f7997e4 100644 --- a/study/src/org/labkey/study/CohortServiceImpl.java +++ b/study/src/org/labkey/study/CohortServiceImpl.java @@ -2,11 +2,15 @@ import org.jetbrains.annotations.Nullable; import org.labkey.api.data.Container; +import org.labkey.api.query.ValidationException; import org.labkey.api.security.User; import org.labkey.api.study.Cohort; import org.labkey.api.study.CohortFilter; +import org.labkey.api.study.Study; import org.labkey.api.study.model.CohortService; import org.labkey.api.view.ActionURL; +import org.labkey.study.model.CohortImpl; +import org.labkey.study.model.CohortManager; import org.labkey.study.model.StudyManager; import java.util.Collection; @@ -51,4 +55,43 @@ public Collection getCohorts(Container container, User user) { return StudyManager.getInstance().getCohorts(container, user); } + + @Override + public Cohort getCohortByLabel(Container container, User user, String label) + { + return StudyManager.getInstance().getCohortByLabel(container, user, label); + } + + @Override + public Cohort getCohortForRowId(Container container, User user, int rowId) + { + return StudyManager.getInstance().getCohortForRowId(container, user, rowId); + } + + @Override + public void deleteCohort(Cohort cohort) + { + if (cohort instanceof CohortImpl cohortImpl) + StudyManager.getInstance().deleteCohort(cohortImpl); + } + + @Override + public Cohort updateCohort(Container container, User user, int rowId, String label, Integer subjectCount) + { + CohortImpl updatedCohort = StudyManager.getInstance().getCohortForRowId(container, user, rowId); + if (updatedCohort != null) + { + updatedCohort = updatedCohort.createMutable(); + updatedCohort.setLabel(label); + updatedCohort.setSubjectCount(subjectCount); + StudyManager.getInstance().updateCohort(user, updatedCohort); + } + return updatedCohort; + } + + @Override + public Cohort createCohort(Study study, User user, String newLabel, boolean enrolled, Integer subjectCount, String description) throws ValidationException + { + return CohortManager.getInstance().createCohort(study, user, newLabel, enrolled, subjectCount, description); + } } diff --git a/study/src/org/labkey/study/StudyModule.java b/study/src/org/labkey/study/StudyModule.java index 7ac57eebf29..befd3480a82 100644 --- a/study/src/org/labkey/study/StudyModule.java +++ b/study/src/org/labkey/study/StudyModule.java @@ -67,9 +67,6 @@ import org.labkey.api.security.roles.RoleManager; import org.labkey.api.services.ServiceRegistry; import org.labkey.api.settings.AdminConsole; -import org.labkey.api.settings.AdminConsole.OptionalFeatureFlag; -import org.labkey.api.settings.OptionalFeatureService; -import org.labkey.api.settings.OptionalFeatureService.FeatureType; import org.labkey.api.specimen.SpecimenSampleTypeDomainKind; import org.labkey.api.specimen.model.AdditiveTypeDomainKind; import org.labkey.api.specimen.model.DerivativeTypeDomainKind; @@ -84,7 +81,6 @@ import org.labkey.api.study.StudyInternalService; import org.labkey.api.study.StudyService; import org.labkey.api.study.StudyUrls; -import org.labkey.api.study.StudyUtils; import org.labkey.api.study.TimepointType; import org.labkey.api.study.importer.ImportHelperService; import org.labkey.api.study.model.CohortService; @@ -95,6 +91,7 @@ import org.labkey.api.study.reports.CrosstabReportDescriptor; import org.labkey.api.study.security.StudySecurityEscalationAuditProvider; import org.labkey.api.study.security.permissions.ManageStudyPermission; +import org.labkey.api.studydesign.StudyDesignManager; import org.labkey.api.studydesign.query.StudyDesignQuerySchema; import org.labkey.api.studydesign.query.StudyPersonnelDomainKind; import org.labkey.api.studydesign.query.StudyProductAntigenDomainKind; @@ -131,7 +128,6 @@ import org.labkey.study.controllers.SharedStudyController; import org.labkey.study.controllers.StudyController; import org.labkey.study.controllers.StudyDefinitionController; -import org.labkey.study.controllers.StudyDesignController; import org.labkey.study.controllers.StudyPropertiesController; import org.labkey.study.controllers.publish.PublishController; import org.labkey.study.controllers.reports.ReportsController; @@ -159,7 +155,6 @@ import org.labkey.study.model.StudyLsidHandler; import org.labkey.study.model.StudyManager; import org.labkey.study.model.TestDatasetDomainKind; -import org.labkey.study.model.TreatmentManager; import org.labkey.study.model.VisitDatasetDomainKind; import org.labkey.study.model.VisitImpl; import org.labkey.study.pipeline.StudyPipeline; @@ -181,9 +176,6 @@ import org.labkey.study.view.StudyToolsWebPartFactory; import org.labkey.study.view.SubjectDetailsWebPartFactory; import org.labkey.study.view.SubjectsWebPart; -import org.labkey.study.view.studydesign.AssayScheduleWebpartFactory; -import org.labkey.study.view.studydesign.ImmunizationScheduleWebpartFactory; -import org.labkey.study.view.studydesign.VaccineDesignWebpartFactory; import org.labkey.study.writer.DatasetDataWriter; import org.labkey.study.writer.DefaultStudyDesignWriter; import org.labkey.study.writer.StudyWriterFactory; @@ -206,17 +198,14 @@ public class StudyModule extends SpringModule implements SearchService.DocumentP public static final String MODULE_NAME = "Study"; public static final BaseWebPartFactory reportsPartFactory = new ReportsWebPartFactory(); - public static final WebPartFactory assayScheduleWebPartFactory = new AssayScheduleWebpartFactory(); public static final WebPartFactory dataToolsWebPartFactory = new StudyToolsWebPartFactory(); public static final WebPartFactory datasetsPartFactory = new DatasetsWebPartFactory(); - public static final WebPartFactory immunizationScheduleWebpartFactory = new ImmunizationScheduleWebpartFactory(); public static final WebPartFactory manageStudyPartFactory = new org.labkey.study.view.StudySummaryWebPartFactory(); public static final WebPartFactory studyDesignSummaryWebPartFactory = new StudySummaryWebPartFactory(); public static final WebPartFactory studyListWebPartFactory = new StudyListWebPartFactory(); public static final WebPartFactory studyScheduleWebPartFactory = new StudyScheduleWebPartFactory(); public static final WebPartFactory subjectDetailsWebPartFactory = new SubjectDetailsWebPartFactory(); public static final WebPartFactory subjectsWebPartFactory = new SubjectsWebPartFactory(); - public static final WebPartFactory vaccineDesignWebPartFactory = new VaccineDesignWebpartFactory(); @Override public String getName() @@ -239,7 +228,6 @@ protected void init() addController("participant-group", ParticipantGroupController.class); addController("publish", PublishController.class); addController("study-definition", StudyDefinitionController.class); - addController("study-design", StudyDesignController.class); addController("study-properties", StudyPropertiesController.class); addController("study-reports", ReportsController.class); addController("study-security", SecurityController.class); @@ -304,10 +292,8 @@ public boolean hasScripts() protected Collection createWebPartFactories() { return List.of( - assayScheduleWebPartFactory, dataToolsWebPartFactory, datasetsPartFactory, - immunizationScheduleWebpartFactory, manageStudyPartFactory, reportsPartFactory, studyDesignSummaryWebPartFactory, @@ -315,7 +301,6 @@ protected Collection createWebPartFactories() studyScheduleWebPartFactory, subjectDetailsWebPartFactory, subjectsWebPartFactory, - vaccineDesignWebPartFactory, new SharedStudyController.StudyFilterWebPartFactory() ); } @@ -421,11 +406,6 @@ protected void startupAfterSpringConfig(ModuleContext moduleContext) "Allow unprovisioned, query-based dataset snapshots to be created.", false); - AdminConsole.addOptionalFeatureFlag(new OptionalFeatureFlag(StudyUtils.STUDY_DESIGN_FEATURE_FLAG, - "Study Protocol Design Tools", - "This option adds support for the study protocol and vaccine design tools.", - false, false, FeatureType.Deprecated)); - ReportAndDatasetChangeDigestProvider.get().addNotificationInfoProvider(new DatasetNotificationInfoProvider()); AdminLinkManager.getInstance().addListener((adminNavTree, container, user) -> { @@ -502,15 +482,15 @@ protected void startupAfterSpringConfig(ModuleContext moduleContext) .count(); metric.put("perDatasetSecurityStudyCount", studiesWithAnyPerDatasetGroup); - if (OptionalFeatureService.get().isFeatureEnabled(StudyUtils.STUDY_DESIGN_FEATURE_FLAG)) - { - // Count the studies that use products and treatments - MutableInt hasProducts = new MutableInt(0); - MutableInt hasTreatments = new MutableInt(0); + // Count the studies that use products and treatments + MutableInt hasProducts = new MutableInt(0); + MutableInt hasTreatments = new MutableInt(0); - allStudies.stream() - .map(study->StudyQuerySchema.createSchema(study, User.getSearchUser(), RoleManager.getRole(ReaderRole.class))) - .forEach(schema->{ + allStudies.stream() + .map(study->StudyQuerySchema.createSchema(study, User.getSearchUser(), RoleManager.getRole(ReaderRole.class))) + .forEach(schema->{ + if (StudyDesignManager.get().isModuleActive(schema.getContainer())) + { TableInfo products = schema.getTable(StudyDesignQuerySchema.PRODUCT_TABLE_NAME); if (new TableSelector(products).exists()) hasProducts.increment(); @@ -518,11 +498,11 @@ protected void startupAfterSpringConfig(ModuleContext moduleContext) TableInfo treatments = schema.getTable(StudyDesignQuerySchema.TREATMENT_TABLE_NAME); if (new TableSelector(treatments).exists()) hasTreatments.increment(); - }); + } + }); - metric.put("studyProducts", hasProducts.intValue()); - metric.put("studyTreatments", hasTreatments.intValue()); - } + metric.put("studyProducts", hasProducts.intValue()); + metric.put("studyTreatments", hasTreatments.intValue()); return metric; }); @@ -716,35 +696,16 @@ public WebPartView getWebPartView(@NotNull ViewContext portalCtx, @NotNull Po @NotNull public Set getIntegrationTests() { - if (OptionalFeatureService.get().isFeatureEnabled(StudyUtils.STUDY_DESIGN_FEATURE_FLAG)) - { - return Set.of( - DatasetDefinition.TestCleanupOrphanedDatasetDomains.class, - ParticipantGroupManager.ParticipantGroupTestCase.class, - StudyImpl.ProtocolDocumentTestCase.class, - StudyManager.AssayScheduleTestCase.class, - StudyManager.StudySnapshotTestCase.class, - StudyManager.VisitCreationTestCase.class, - StudyModule.TestCase.class, - TreatmentManager.TreatmentDataTestCase.class, - VisitImpl.TestCase.class, - DatasetUpdateService.TestCase.class - ); - } - else - { - return Set.of( - DatasetDefinition.TestCleanupOrphanedDatasetDomains.class, - ParticipantGroupManager.ParticipantGroupTestCase.class, - StudyImpl.ProtocolDocumentTestCase.class, - StudyManager.StudySnapshotTestCase.class, - StudyManager.VisitCreationTestCase.class, - StudyModule.TestCase.class, - VisitImpl.TestCase.class, - DatasetUpdateService.TestCase.class, - DatasetLsidImportHelper.TestCase.class - ); - } + return Set.of( + DatasetDefinition.TestCleanupOrphanedDatasetDomains.class, + ParticipantGroupManager.ParticipantGroupTestCase.class, + StudyImpl.ProtocolDocumentTestCase.class, + StudyManager.StudySnapshotTestCase.class, + StudyManager.VisitCreationTestCase.class, + StudyModule.TestCase.class, + VisitImpl.TestCase.class, + DatasetUpdateService.TestCase.class, + DatasetLsidImportHelper.TestCase.class); } @Override diff --git a/study/src/org/labkey/study/StudySchema.java b/study/src/org/labkey/study/StudySchema.java index 4bcb440588a..1e1296dea84 100644 --- a/study/src/org/labkey/study/StudySchema.java +++ b/study/src/org/labkey/study/StudySchema.java @@ -167,14 +167,4 @@ public TableInfo getTableInfoVisitTag() { return getSchema().getTable("VisitTag"); } - - public TableInfo getTableInfoAssaySpecimen() - { - return getSchema().getTable("AssaySpecimen"); - } - - public TableInfo getTableInfoAssaySpecimenVisit() - { - return getSchema().getTable("AssaySpecimenVisit"); - } } diff --git a/study/src/org/labkey/study/StudyServiceImpl.java b/study/src/org/labkey/study/StudyServiceImpl.java index 25ef5509074..e4b76f8bee9 100644 --- a/study/src/org/labkey/study/StudyServiceImpl.java +++ b/study/src/org/labkey/study/StudyServiceImpl.java @@ -55,6 +55,7 @@ import org.labkey.api.query.QuerySchema; import org.labkey.api.query.QueryService; import org.labkey.api.query.UserSchema; +import org.labkey.api.query.ValidationException; import org.labkey.api.reports.model.ViewCategory; import org.labkey.api.reports.report.view.ReportUtil; import org.labkey.api.security.SecurableResource; @@ -76,6 +77,7 @@ import org.labkey.api.study.UnionTable; import org.labkey.api.study.Visit; import org.labkey.api.study.model.ParticipantInfo; +import org.labkey.api.studydesign.query.StudyDesignSchema; import org.labkey.api.util.GUID; import org.labkey.api.view.ActionURL; import org.labkey.api.view.DataView; @@ -1086,7 +1088,7 @@ public void registerManagementOption(StudyManagementOption option) public boolean isLocationInUse(Location loc) { return LocationManager.get().isLocationInUse(loc, StudySchema.getInstance().getTableInfoParticipant(), "EnrollmentSiteId", "CurrentSiteId") || - LocationManager.get().isLocationInUse(loc, StudySchema.getInstance().getTableInfoAssaySpecimen(), "LocationId"); + LocationManager.get().isLocationInUse(loc, StudyDesignSchema.getInstance().getTableInfoAssaySpecimen(), "LocationId"); } @Override @@ -1103,7 +1105,7 @@ public void appendLocationInUseClauses(SQLFragment sql, String locationTableAlia .append(locationTableAlias) .append(".Container = p.Container) OR\n") .append(exists) - .append(StudySchema.getInstance().getTableInfoAssaySpecimen(), "a") + .append(StudyDesignSchema.getInstance().getTableInfoAssaySpecimen(), "a") .append(" WHERE ") .append(locationTableAlias) .append(".RowId = a.LocationId AND ") @@ -1197,4 +1199,16 @@ public Map getVisitImportMap(Study study, boolean includeSta { return StudyManager.getInstance().getVisitImportMap(study, includeStandardMapping); } + + @Override + public ValidationException updateAssayPlan(User user, Study study, String assayPlan) + { + if (study instanceof StudyImpl studyImpl) + { + studyImpl = studyImpl.createMutable(); + studyImpl.setAssayPlan(assayPlan); + return StudyManager.getInstance().updateStudy(user, studyImpl); + } + return null; + } } diff --git a/study/src/org/labkey/study/VisitServiceImpl.java b/study/src/org/labkey/study/VisitServiceImpl.java index ab236858075..e6ba36b97b1 100644 --- a/study/src/org/labkey/study/VisitServiceImpl.java +++ b/study/src/org/labkey/study/VisitServiceImpl.java @@ -8,8 +8,11 @@ import org.labkey.api.study.Study; import org.labkey.api.study.Visit; import org.labkey.api.study.model.VisitService; +import org.labkey.study.model.StudyImpl; import org.labkey.study.model.StudyManager; +import org.labkey.study.model.VisitImpl; +import java.math.BigDecimal; import java.util.Collection; import java.util.Collections; @@ -32,4 +35,17 @@ public Collection getVisits(Study study, Visit.Order order) { return StudyManager.getInstance().getVisitManager(study).updateParticipantVisits(user, Collections.emptySet()); } + + @Override + public Visit createVisit(Study study, User user, @NotNull BigDecimal seqMin, String label, Visit.Type type) + { + return StudyManager.getInstance().createVisit(study, user, new VisitImpl(study.getContainer(), seqMin, label, type)); + } + + @Override + public void deleteVisit(Study study, User user, Visit visit) + { + if (study instanceof StudyImpl studyImpl && visit instanceof VisitImpl visitImpl) + StudyManager.getInstance().deleteVisit(studyImpl, visitImpl, user); + } } diff --git a/study/src/org/labkey/study/controllers/StudyController.java b/study/src/org/labkey/study/controllers/StudyController.java index 8e28fa95ff5..9c19979a564 100644 --- a/study/src/org/labkey/study/controllers/StudyController.java +++ b/study/src/org/labkey/study/controllers/StudyController.java @@ -193,7 +193,9 @@ import org.labkey.api.study.model.ParticipantGroup; import org.labkey.api.study.publish.StudyPublishService; import org.labkey.api.study.security.permissions.ManageStudyPermission; +import org.labkey.api.studydesign.StudyDesignManager; import org.labkey.api.util.ContainerContext; +import org.labkey.api.util.CsrfInput; import org.labkey.api.util.DateUtil; import org.labkey.api.util.DemoMode; import org.labkey.api.util.FileStream; @@ -205,7 +207,6 @@ import org.labkey.api.util.StringExpression; import org.labkey.api.util.URLHelper; import org.labkey.api.util.XmlBeansUtil; -import org.labkey.api.util.CsrfInput; import org.labkey.api.view.ActionURL; import org.labkey.api.view.DataView; import org.labkey.api.view.GridView; @@ -406,22 +407,6 @@ public ActionURL getManageReportPermissions(Container container) return new ActionURL(SecurityController.ReportPermissionsAction.class, container); } - @Override - public ActionURL getManageAssayScheduleURL(Container container, boolean useAlternateLookupFields) - { - ActionURL url = new ActionURL(StudyDesignController.ManageAssayScheduleAction.class, container); - url.addParameter("useAlternateLookupFields", useAlternateLookupFields); - return url; - } - - @Override - public ActionURL getManageTreatmentsURL(Container container, boolean useSingleTableEditor) - { - ActionURL url = new ActionURL(StudyDesignController.ManageTreatmentsAction.class, container); - url.addParameter("singleTable", useSingleTableEditor); - return url; - } - @Override public ActionURL getManageFileWatchersURL(Container container) { @@ -469,6 +454,30 @@ public ActionURL getTypeNotFoundURL(Container container, int datasetId) { return new ActionURL(TypeNotFoundAction.class, container).addParameter("id", datasetId); } + + @Override + public ActionURL getManageLocationsURL(Container container) + { + return new ActionURL(ManageLocationsAction.class, container); + } + + @Override + public ActionURL getManageVisitsURL(Container container) + { + return new ActionURL(ManageVisitsAction.class, container); + } + + @Override + public ActionURL getManageCohortsURL(Container container) + { + return new ActionURL(CohortController.ManageCohortsAction.class, container); + } + + @Override + public ActionURL getVisitOrderURL(Container container) + { + return new ActionURL(VisitOrderAction.class, container); + } } public StudyController() @@ -2528,6 +2537,57 @@ public void addNavTrail(NavTree root) } } + /** + * Called from the vaccine design webpart for the study design module + */ + @RequiresPermission(UpdatePermission.class) + public class CreateVisitForVaccineDesign extends MutatingApiAction + { + @Override + public void validateForm(VisitForm form, Errors errors) + { + if (!StudyDesignManager.get().isModuleActive(getContainer())) + { + errors.reject(ERROR_MSG, "This action can only be called if the study design module is active"); + return; + } + + Study study = getStudy(getContainer()); + 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(AdminPermission.class) public class UpdateDatasetVisitMappingAction extends FormViewAction { diff --git a/study/src/org/labkey/study/importer/AssayScheduleImporter.java b/study/src/org/labkey/study/importer/AssayScheduleImporter.java index 9328813d2b2..317973441d5 100644 --- a/study/src/org/labkey/study/importer/AssayScheduleImporter.java +++ b/study/src/org/labkey/study/importer/AssayScheduleImporter.java @@ -65,6 +65,9 @@ public void process(StudyImportContext ctx, VirtualFile root, BindException erro if (!ctx.isDataTypeSelected(getDataType())) return; + if (!isStudyDesignEnabled(ctx.getContainer())) + return; + if (isValidForImportArchive(ctx, root)) { ExportDirType dirType = ctx.getXml().getAssaySchedule(); @@ -95,12 +98,11 @@ public void process(StudyImportContext ctx, VirtualFile root, BindException erro } // assay specimen table - StudyQuerySchema.TablePackage assaySpecimenTablePackage = schema.getTablePackage(ctx, projectSchema, StudyQuerySchema.ASSAY_SPECIMEN_TABLE_NAME, null); + StudyQuerySchema.TablePackage assaySpecimenTablePackage = schema.getTablePackage(ctx, projectSchema, StudyDesignQuerySchema.ASSAY_SPECIMEN_TABLE_NAME, null); importTableData(ctx, vf, assaySpecimenTablePackage, _assaySpecimenTransform, null); - StudyManager.getInstance().clearAssaySpecimenCache(assaySpecimenTablePackage.getContainer()); // assay specimen visit table - StudyQuerySchema.TablePackage assaySpecimenVisitTablePackage = schema.getTablePackage(ctx, projectSchema, StudyQuerySchema.ASSAY_SPECIMEN_VISIT_TABLE_NAME, null); + StudyQuerySchema.TablePackage assaySpecimenVisitTablePackage = schema.getTablePackage(ctx, projectSchema, StudyDesignQuerySchema.ASSAY_SPECIMEN_VISIT_TABLE_NAME, null); importTableData(ctx, vf, assaySpecimenVisitTablePackage, null, _assaySpecimenVisitMapTransform); if (ctx.isDataspaceProject()) diff --git a/study/src/org/labkey/study/importer/CreateChildStudyPipelineJob.java b/study/src/org/labkey/study/importer/CreateChildStudyPipelineJob.java index 88f22e01485..8bb072a0761 100644 --- a/study/src/org/labkey/study/importer/CreateChildStudyPipelineJob.java +++ b/study/src/org/labkey/study/importer/CreateChildStudyPipelineJob.java @@ -49,6 +49,7 @@ import org.labkey.api.study.model.ParticipantGroup; import org.labkey.api.study.model.ParticipantMapper; import org.labkey.api.study.pipeline.AbstractStudyPipelineJob; +import org.labkey.api.studydesign.StudyDesignManager; import org.labkey.api.view.NotFoundException; import org.labkey.api.view.ViewContext; import org.labkey.api.writer.MemoryVirtualFile; @@ -452,7 +453,7 @@ private StudyImportContext importToDestinationStudy(BindException errors, StudyI */ private void importStudyDesignData(BindException errors, VirtualFile studyDir, StudyImportContext importContext) throws Exception { - if (importContext != null && OptionalFeatureService.get().isFeatureEnabled(StudyUtils.STUDY_DESIGN_FEATURE_FLAG)) + if (importContext != null && StudyDesignManager.get().isModuleActive(importContext.getContainer())) { // assay schedule and treatment data (study design) new TreatmentDataImporter().process(importContext, studyDir, errors); @@ -465,7 +466,7 @@ private void importStudyDesignData(BindException errors, VirtualFile studyDir, S private void importTreatmentVisitMapData(BindException errors, VirtualFile studyDir, StudyImportContext importContext) throws Exception { - if (importContext != null && OptionalFeatureService.get().isFeatureEnabled(StudyUtils.STUDY_DESIGN_FEATURE_FLAG)) + if (importContext != null && StudyDesignManager.get().isModuleActive(importContext.getContainer())) { new TreatmentVisitMapImporter().process(importContext, studyDir, errors); diff --git a/study/src/org/labkey/study/importer/DefaultStudyDesignImporter.java b/study/src/org/labkey/study/importer/DefaultStudyDesignImporter.java index e3468c93f12..a459ac43536 100644 --- a/study/src/org/labkey/study/importer/DefaultStudyDesignImporter.java +++ b/study/src/org/labkey/study/importer/DefaultStudyDesignImporter.java @@ -43,6 +43,7 @@ import org.labkey.api.reader.DataLoader; import org.labkey.api.reader.TabLoader; import org.labkey.api.security.User; +import org.labkey.api.studydesign.StudyDesignManager; import org.labkey.api.util.Pair; import org.labkey.api.util.XmlBeansUtil; import org.labkey.api.util.XmlValidationException; @@ -257,6 +258,11 @@ protected String getFileName(TableInfo tableInfo) return tableInfo.getName().toLowerCase() + ".tsv"; } + protected boolean isStudyDesignEnabled(Container container) + { + return StudyDesignManager.get().isModuleActive(container); + } + /** * Interface which allows the transform to create and initialize the transform based on the original * and inserted data. diff --git a/study/src/org/labkey/study/importer/StudyImportInitialTask.java b/study/src/org/labkey/study/importer/StudyImportInitialTask.java index 23065fd06e2..0ee5c50035a 100644 --- a/study/src/org/labkey/study/importer/StudyImportInitialTask.java +++ b/study/src/org/labkey/study/importer/StudyImportInitialTask.java @@ -20,8 +20,6 @@ import org.labkey.api.pipeline.PipelineJob; import org.labkey.api.pipeline.PipelineJobException; import org.labkey.api.settings.AppProps; -import org.labkey.api.settings.OptionalFeatureService; -import org.labkey.api.study.StudyUtils; import org.labkey.api.study.TimepointType; import org.labkey.api.study.importer.SimpleStudyImporter; import org.labkey.study.controllers.StudyController; @@ -170,11 +168,8 @@ private static void runImporters(StudyImportContext ctx, PipelineJob job, BindEx if (errors.hasErrors()) throwFirstErrorAsPipelineJobException(errors); - if (OptionalFeatureService.get().isFeatureEnabled(StudyUtils.STUDY_DESIGN_FEATURE_FLAG)) - { - processImporter(ctx, job, errors, new TreatmentDataImporter()); - processImporter(ctx, job, errors, new AssayScheduleImporter()); - } + processImporter(ctx, job, errors, new TreatmentDataImporter()); + processImporter(ctx, job, errors, new AssayScheduleImporter()); processImporter(ctx, job, errors, new DatasetDefinitionImporter()); } diff --git a/study/src/org/labkey/study/importer/StudyPropertiesImporter.java b/study/src/org/labkey/study/importer/StudyPropertiesImporter.java index 97a7368c587..f724e8e8306 100644 --- a/study/src/org/labkey/study/importer/StudyPropertiesImporter.java +++ b/study/src/org/labkey/study/importer/StudyPropertiesImporter.java @@ -24,8 +24,7 @@ import org.labkey.api.data.TableInfo; import org.labkey.api.security.User; import org.labkey.api.security.UserManager; -import org.labkey.api.settings.OptionalFeatureService; -import org.labkey.api.study.StudyUtils; +import org.labkey.api.studydesign.StudyDesignManager; import org.labkey.api.studydesign.query.StudyDesignQuerySchema; import org.labkey.api.writer.VirtualFile; import org.labkey.study.StudySchema; @@ -50,7 +49,6 @@ public class StudyPropertiesImporter extends DefaultStudyDesignImporter private final Map _objectiveIdMap = new HashMap<>(); private final SharedTableMapBuilder _personnelTableMapBuilder = new SharedTableMapBuilder(_personnelIdMap, "Label"); private final SharedTableMapBuilder _objectiveTableMapBuilder = new SharedTableMapBuilder(_objectiveIdMap, "Label"); - private final boolean _studyDesignEnabled = OptionalFeatureService.get().isFeatureEnabled(StudyUtils.STUDY_DESIGN_FEATURE_FLAG); private String getDataType() { @@ -76,6 +74,7 @@ public void process(StudyImportContext ctx, VirtualFile root) throws Exception try (DbScope.Transaction transaction = scope.ensureTransaction()) { boolean isDataspaceProject = ctx.isDataspaceProject(); + boolean studyDesignEnabled = isStudyDesignEnabled(ctx.getContainer()); // import any custom study design table properties importTableinfo(ctx, vf, StudyPropertiesWriter.SCHEMA_FILENAME); @@ -84,7 +83,7 @@ public void process(StudyImportContext ctx, VirtualFile root) throws Exception StudyQuerySchema schema = StudyQuerySchema.createSchema(ctx.getStudyImpl(), ctx.getUser()); StudyQuerySchema projectSchema = isDataspaceProject ? StudyQuerySchema.createSchema(StudyManager.getInstance().getStudy(ctx.getProject()), ctx.getUser()) : schema; - if (!isDataspaceProject && _studyDesignEnabled) + if (!isDataspaceProject && studyDesignEnabled) { // objective is cross-container and thus not supported for dataspace import StudyQuerySchema.TablePackage objectiveTablePackage = schema.getTablePackage(ctx, projectSchema, StudyDesignQuerySchema.OBJECTIVE_TABLE_NAME, null); @@ -95,7 +94,7 @@ public void process(StudyImportContext ctx, VirtualFile root) throws Exception StudyQuerySchema.TablePackage propertiesTablePackage = schema.getTablePackage(ctx, projectSchema, StudyQuerySchema.PROPERTIES_TABLE_NAME, null); importTableData(ctx, vf, propertiesTablePackage, null, new StudyPropertiesTransform()); - if (_studyDesignEnabled) + if (studyDesignEnabled) { StudyQuerySchema.TablePackage personnelTablePackage = schema.getTablePackage(ctx, projectSchema, StudyDesignQuerySchema.PERSONNEL_TABLE_NAME, null); importTableData(ctx, vf, personnelTablePackage, _personnelTableMapBuilder, diff --git a/study/src/org/labkey/study/importer/TreatmentDataImporter.java b/study/src/org/labkey/study/importer/TreatmentDataImporter.java index 1a127d79b5c..2c8b17e9004 100644 --- a/study/src/org/labkey/study/importer/TreatmentDataImporter.java +++ b/study/src/org/labkey/study/importer/TreatmentDataImporter.java @@ -81,6 +81,9 @@ public void process(StudyImportContext ctx, VirtualFile root, BindException erro if (!ctx.isDataTypeSelected(getDataType())) return; + if (!isStudyDesignEnabled(ctx.getContainer())) + return; + if (isValidForImportArchive(ctx, root)) { ExportDirType dirType = ctx.getXml().getTreatmentData(); diff --git a/study/src/org/labkey/study/importer/TreatmentVisitMapImporter.java b/study/src/org/labkey/study/importer/TreatmentVisitMapImporter.java index 61a6c545913..5b7cd80b5a4 100644 --- a/study/src/org/labkey/study/importer/TreatmentVisitMapImporter.java +++ b/study/src/org/labkey/study/importer/TreatmentVisitMapImporter.java @@ -68,6 +68,9 @@ public void process(StudyImportContext ctx, VirtualFile root, BindException erro if (!ctx.isDataTypeSelected(getDataType())) return; + if (!isStudyDesignEnabled(ctx.getContainer())) + return; + if (isValidForImportArchive(ctx, root)) { ExportDirType dirType = ctx.getXml().getTreatmentData(); diff --git a/study/src/org/labkey/study/model/CohortImpl.java b/study/src/org/labkey/study/model/CohortImpl.java index 7257cdf03a5..04deae76107 100644 --- a/study/src/org/labkey/study/model/CohortImpl.java +++ b/study/src/org/labkey/study/model/CohortImpl.java @@ -15,16 +15,10 @@ */ package org.labkey.study.model; -import org.jetbrains.annotations.NotNull; -import org.json.JSONArray; -import org.json.JSONObject; import org.labkey.api.exp.Lsid; import org.labkey.api.study.Cohort; -import org.labkey.api.util.JsonUtil; -import java.util.ArrayList; import java.util.HashSet; -import java.util.List; import java.util.Objects; import java.util.Set; @@ -38,7 +32,6 @@ public class CohortImpl extends ExtensibleStudyEntity imple private boolean _enrolled = true; private Integer _subjectCount; private String _description; - List _treatmentVisitMap; @Override public Integer getPrimaryKey() @@ -145,38 +138,6 @@ public void setDescription(String description) _description = description; } - public void setTreatmentVisitMap(List treatmentVisitMap) - { - _treatmentVisitMap = treatmentVisitMap; - } - - public List getTreatmentVisitMap() - { - return _treatmentVisitMap; - } - - public static CohortImpl fromJSON(@NotNull JSONObject o) - { - CohortImpl cohort = new CohortImpl(); - 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; - } - @Override public boolean equals(Object o) { diff --git a/study/src/org/labkey/study/model/StudyImpl.java b/study/src/org/labkey/study/model/StudyImpl.java index 5f98243d070..eabc4999273 100644 --- a/study/src/org/labkey/study/model/StudyImpl.java +++ b/study/src/org/labkey/study/model/StudyImpl.java @@ -291,44 +291,6 @@ public Collection getCohorts(User user) return StudyManager.getInstance().getCohorts(getContainer(), user); } - @Override - public Collection getAssaySpecimenConfigs() - { - return StudyManager.getInstance().getAssaySpecimenConfigs(getContainer()); - } - - @Override - @Transient - public List getVisitsForAssaySchedule() - { - return StudyManager.getInstance().getVisitsForAssaySchedule(getContainer()); - } - - @Override - public List getStudyProducts(User user, String role) - { - return TreatmentManager.getInstance().getStudyProducts(getContainer(), user, role, null); - } - - @Override - public List getStudyTreatments(User user) - { - return TreatmentManager.getInstance().getStudyTreatments(getContainer(), user); - } - - @Override - public List getStudyTreatmentVisitMap(Container container, @Nullable Integer cohortId) - { - return TreatmentManager.getInstance().getStudyTreatmentVisitMap(container, cohortId); - } - - @Override - @Transient - public List getVisitsForTreatmentSchedule() - { - return TreatmentManager.getInstance().getVisitsForTreatmentSchedule(getContainer()); - } - @Override public List getParticipantCategories(User user) { diff --git a/study/src/org/labkey/study/model/StudyManager.java b/study/src/org/labkey/study/model/StudyManager.java index 54aea179175..ec3ab03b145 100644 --- a/study/src/org/labkey/study/model/StudyManager.java +++ b/study/src/org/labkey/study/model/StudyManager.java @@ -146,7 +146,6 @@ import org.labkey.api.specimen.SpecimenSchema; import org.labkey.api.specimen.location.LocationCache; import org.labkey.api.specimen.model.SpecimenTablesProvider; -import org.labkey.api.study.AssaySpecimenConfig; import org.labkey.api.study.Cohort; import org.labkey.api.study.Dataset; import org.labkey.api.study.DataspaceContainerFilter; @@ -160,8 +159,14 @@ import org.labkey.api.study.Visit.Order; import org.labkey.api.study.model.ParticipantDataset; import org.labkey.api.study.model.ParticipantInfo; -import org.labkey.api.studydesign.query.StudyDesignSchema; -import org.labkey.api.test.TestWhen; +import org.labkey.api.studydesign.StudyDesignManager; +import org.labkey.api.studydesign.StudyDesignService; +import org.labkey.api.studydesign.query.AbstractStudyDesignDomainKind; +import org.labkey.api.studydesign.query.StudyPersonnelDomainKind; +import org.labkey.api.studydesign.query.StudyProductAntigenDomainKind; +import org.labkey.api.studydesign.query.StudyProductDomainKind; +import org.labkey.api.studydesign.query.StudyTreatmentDomainKind; +import org.labkey.api.studydesign.query.StudyTreatmentProductDomainKind; import org.labkey.api.util.DateUtil; import org.labkey.api.util.GUID; import org.labkey.api.util.JunitUtil; @@ -169,7 +174,6 @@ import org.labkey.api.util.Pair; import org.labkey.api.util.Path; import org.labkey.api.util.StringUtilsLabKey; -import org.labkey.api.util.TestContext; import org.labkey.api.view.ActionURL; import org.labkey.api.view.NavTree; import org.labkey.api.view.UnauthorizedException; @@ -180,18 +184,11 @@ import org.labkey.study.controllers.BaseStudyController.StudyJspView; import org.labkey.study.controllers.StudyController; import org.labkey.study.dataset.DatasetAuditProvider; -import org.labkey.api.studydesign.StudyDesignManager; import org.labkey.study.importer.SchemaReader; import org.labkey.study.model.StudySnapshot.SnapshotSettings; import org.labkey.study.query.DatasetTableImpl; import org.labkey.study.query.DatasetUpdateService; -import org.labkey.api.studydesign.query.StudyPersonnelDomainKind; import org.labkey.study.query.StudyQuerySchema; -import org.labkey.api.studydesign.query.AbstractStudyDesignDomainKind; -import org.labkey.api.studydesign.query.StudyProductAntigenDomainKind; -import org.labkey.api.studydesign.query.StudyProductDomainKind; -import org.labkey.api.studydesign.query.StudyTreatmentDomainKind; -import org.labkey.api.studydesign.query.StudyTreatmentProductDomainKind; import org.labkey.study.visitmanager.AbsoluteDateVisitManager; import org.labkey.study.visitmanager.RelativeDateVisitManager; import org.labkey.study.visitmanager.SequenceVisitManager; @@ -241,7 +238,6 @@ public class StudyManager private final StudyHelper _studyHelper; private final VisitHelper _visitHelper; - private final QueryHelper> _assaySpecimenHelper; private final DatasetHelper _datasetHelper; private final QueryHelper> _cohortHelper; private final BlockingCache> _sharedProperties; @@ -258,7 +254,6 @@ private StudyManager() { _studyHelper = new StudyHelper(); _visitHelper = new VisitHelper(); - _assaySpecimenHelper = new QueryHelper<>(() -> StudySchema.getInstance().getTableInfoAssaySpecimen(), AssaySpecimenConfigImpl.class); _cohortHelper = new QueryHelper<>(() -> StudySchema.getInstance().getTableInfoCohort(), CohortImpl.class, "Label"); /* @@ -1650,9 +1645,12 @@ public void deleteVisits(StudyImpl study, Collection visits, User use { // Delete specimens first because we may need ParticipantVisit to figure out which specimens SpecimenManager.get().deleteSpecimensForVisit(visit); - - TreatmentManager.getInstance().deleteTreatmentVisitMapForVisit(study.getContainer(), visit.getRowId()); - deleteAssaySpecimenVisits(study.getContainer(), visit.getRowId()); + StudyDesignService svc = StudyDesignService.get(); + if (svc != null) + { + svc.deleteTreatmentVisitMapForVisit(study.getContainer(), visit.getRowId()); + svc.deleteAssaySpecimenVisits(study.getContainer(), visit.getRowId()); + } } } @@ -1711,72 +1709,6 @@ public void updateParticipant(User user, Participant participant) _participantCache.remove(participant.getContainer()); } - public Collection getAssaySpecimenConfigs(Container container) - { - return _assaySpecimenHelper.getCollection(container); - } - - public List getVisitsForAssaySchedule(Container container) - { - SimpleFilter filter = SimpleFilter.createContainerFilter(container); - List visitRowIds = new TableSelector(StudySchema.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 = getStudy(container); - if (study != null) - { - for (VisitImpl v : getVisits(study, Order.DISPLAY)) - { - if (visitRowIds.contains(v.getRowId())) - visits.add(v); - } - } - return visits; - } - - public List getAssaySpecimenVisitIds(Container container, AssaySpecimenConfig assaySpecimenConfig) - { - SimpleFilter filter = SimpleFilter.createContainerFilter(container); - filter.addCondition(FieldKey.fromParts("AssaySpecimenId"), assaySpecimenConfig.getRowId()); - - return new TableSelector(StudySchema.getInstance().getTableInfoAssaySpecimenVisit(), - Collections.singleton("VisitId"), filter, new Sort("VisitId")).getArrayList(Integer.class); - } - - public void deleteAssaySpecimenVisits(Container container, int rowId) - { - SimpleFilter filter = SimpleFilter.createContainerFilter(container); - filter.addCondition(FieldKey.fromParts("VisitId"), rowId); - Table.delete(StudySchema.getInstance().getTableInfoAssaySpecimenVisit(), filter); - } - - 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 createVisitDatasetMapping(User user, Container container, int visitId, int datasetId, boolean isRequired) { VisitDataset vds = new VisitDataset(container, datasetId, visitId, isRequired); @@ -2130,9 +2062,9 @@ public void deleteCohort(CohortImpl cohort) try (Transaction transaction = schema.getSchema().getScope().ensureTransaction()) { Container container = cohort.getContainer(); - - TreatmentManager.getInstance().deleteTreatmentVisitMapForCohort(container, cohort.getRowId()); - + StudyDesignService svc = StudyDesignService.get(); + if (svc != null) + svc.deleteTreatmentVisitMapForCohort(container, cohort.getRowId()); _cohortHelper.delete(cohort); // delete extended properties @@ -2718,15 +2650,6 @@ public void deleteAllStudyData(Container c, User user) new SpecimenTablesProvider(c, null, null).deleteTables(); LocationCache.clear(c); - // - // assay schedule - // - Table.delete(SCHEMA.getTableInfoAssaySpecimenVisit(), containerFilter); - assert deletedTables.add(SCHEMA.getTableInfoAssaySpecimenVisit()); - Table.delete(_assaySpecimenHelper.getTableInfo(), containerFilter); - _assaySpecimenHelper.clearCache(c); - assert deletedTables.add(_assaySpecimenHelper.getTableInfo()); - // // metadata // @@ -4049,11 +3972,6 @@ public void clearParticipantCache(Container container) _participantCache.remove(container); } - public void clearAssaySpecimenCache(Container container) - { - _assaySpecimenHelper.clearCache(container); - } - public Collection getParticipants(Study study) { Map participantMap = getParticipantMap(study); @@ -4961,169 +4879,6 @@ private void validateNewVisit(VisitImpl newVisit, List existingVisits } } - @TestWhen(TestWhen.When.BVT) - public static class AssayScheduleTestCase extends Assert - { - TestContext _context = null; - User _user = null; - Container _container = null; - StudyImpl _junitStudy = null; - StudyManager _manager = StudyManager.getInstance(); - - 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() - { - _manager.deleteAssaySpecimenVisits(_container, _visits.get(0).getRowId()); - verifyAssayScheduleRowCount(2); - assertEquals(1, _manager.getAssaySpecimenVisitIds(_container, _assays.get(0)).size()); - assertEquals(1, _manager.getVisitsForAssaySchedule(_container).size()); - - _manager.deleteAssaySpecimenVisits(_container, _visits.get(1).getRowId()); - verifyAssayScheduleRowCount(0); - assertEquals(0, _manager.getAssaySpecimenVisitIds(_container, _assays.get(0)).size()); - assertEquals(0, _manager.getVisitsForAssaySchedule(_container).size()); - } - - private void verifyAssaySchedule() - { - verifyAssayScheduleRowCount(4); - - List visits = _manager.getVisitsForAssaySchedule(_container); - assertEquals("Unexpected assay schedule visit count", 2, visits.size()); - - for (AssaySpecimenConfigImpl assay : _manager.getAssaySpecimenConfigs(_container)) - { - List visitIds = _manager.getAssaySpecimenVisitIds(_container, assay); - for (VisitImpl visit : _visits) - assertTrue("Assay schedule does not contain expected visitId", visitIds.contains(visit.getRowId())); - } - } - - private void verifyAssayScheduleRowCount(int expectedCount) - { - TableSelector selector = new TableSelector(StudySchema.getInstance().getTableInfoAssaySpecimenVisit(), SimpleFilter.createContainerFilter(_container), null); - assertEquals("Unexpected number of assay schedule visit records", expectedCount, selector.getRowCount()); - } - - private void verifyAssayConfigurations() - { - Collection assays = _manager.getAssaySpecimenConfigs(_container); - assertEquals("Unexpected assay configuration count", 2, assays.size()); - - for (AssaySpecimenConfigImpl assay : assays) - { - assertEquals("Unexpected assay configuration lookup value", _lookups.get("Lab"), assay.getLab()); - assertEquals("Unexpected assay configuration lookup value", _lookups.get("SampleType"), assay.getSampleType()); - } - } - - private void populateAssaySchedule() - { - _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 (AssaySpecimenConfigImpl assay : _assays) - { - for (VisitImpl visit : _visits) - { - AssaySpecimenVisitImpl asv = new AssaySpecimenVisitImpl(_container, assay.getRowId(), visit.getRowId()); - Table.insert(_user, StudySchema.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(_manager._assaySpecimenHelper.create(_user, assay1)); - - AssaySpecimenConfigImpl assay2 = new AssaySpecimenConfigImpl(_container, "Assay2", "Assay 2 description"); - assay2.setLab(_lookups.get("Lab")); - assay2.setSampleType(_lookups.get("SampleType")); - _assays.add(_manager._assaySpecimenHelper.create(_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, _manager.getStudyDesignLabLabelByName(_container, name)); - assertNull("Unexpected study design lookup label", _manager.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()); - 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())); - } - } - } - public static class StudySnapshotTestCase extends Assert { @Test diff --git a/study/src/org/labkey/study/query/StudyQuerySchema.java b/study/src/org/labkey/study/query/StudyQuerySchema.java index 42600793c86..a0fc01128a2 100644 --- a/study/src/org/labkey/study/query/StudyQuerySchema.java +++ b/study/src/org/labkey/study/query/StudyQuerySchema.java @@ -125,8 +125,6 @@ public class StudyQuerySchema extends UserSchema implements UserSchema.HasContex public static final String VISIT_TAG_TABLE_NAME = "VisitTag"; public static final String VISIT_TAG_MAP_TABLE_NAME = "VisitTagMap"; public static final String VISIT_ALIASES = "VisitAliases"; - public static final String ASSAY_SPECIMEN_TABLE_NAME = "AssaySpecimen"; - public static final String ASSAY_SPECIMEN_VISIT_TABLE_NAME = "AssaySpecimenVisit"; public static final String VISUALIZATION_VISIT_TAG_TABLE_NAME = "VisualizationVisitTag"; public static final String VISIT_MAP_TABLE_NAME = "VisitMap"; @@ -340,9 +338,6 @@ public Set getTableNames() names.add(VISIT_TAG_TABLE_NAME); names.add(VISIT_TAG_MAP_TABLE_NAME); - names.add(ASSAY_SPECIMEN_TABLE_NAME); - names.add(ASSAY_SPECIMEN_VISIT_TABLE_NAME); - names.add(STUDY_SNAPSHOT_TABLE_NAME); } @@ -629,14 +624,6 @@ public TableInfo createTable(String name, ContainerFilter cf) { return new LocationSpecimenListTable(this, cf); } - if (ASSAY_SPECIMEN_TABLE_NAME.equalsIgnoreCase(name)) - { - return new AssaySpecimenTable(this, cf); - } - if (ASSAY_SPECIMEN_VISIT_TABLE_NAME.equalsIgnoreCase(name)) - { - return new AssaySpecimenVisitTable(this, cf); - } if (VISIT_TAG_TABLE_NAME.equalsIgnoreCase(name)) { return new VisitTagTable(this, isDataspaceProject() ? ContainerFilter.Type.Project.create(this) : cf); @@ -1122,7 +1109,7 @@ public TablePackage getTablePackage(AbstractStudyContext ctx, StudyQuerySchema p _dataspaceProjectLevelTables.add(VISIT_TABLE_NAME); _dataspaceFolderLevelTables.add(StudyDesignQuerySchema.TREATMENT_TABLE_NAME); - _dataspaceFolderLevelTables.add(ASSAY_SPECIMEN_TABLE_NAME); + _dataspaceFolderLevelTables.add(StudyDesignQuerySchema.ASSAY_SPECIMEN_TABLE_NAME); _dataspaceFolderLevelTables.add(StudyDesignQuerySchema.OBJECTIVE_TABLE_NAME); } @@ -1280,10 +1267,6 @@ public Set getTableNames() names.add(studyService.getSubjectGroupTableName(getContainer())); names.add(PARTICIPANT_GROUP_COHORT_UNION_TABLE_NAME); - // assay schedule tables - names.add(ASSAY_SPECIMEN_TABLE_NAME); - names.add(ASSAY_SPECIMEN_VISIT_TABLE_NAME); - names.add(VISIT_TAG_TABLE_NAME); names.add(VISIT_TAG_MAP_TABLE_NAME); names.add(STUDY_SNAPSHOT_TABLE_NAME); diff --git a/study/src/org/labkey/study/view/BaseStudyPage.java b/study/src/org/labkey/study/view/BaseStudyPage.java index d47e4e9a7ff..16acf7e784f 100644 --- a/study/src/org/labkey/study/view/BaseStudyPage.java +++ b/study/src/org/labkey/study/view/BaseStudyPage.java @@ -22,13 +22,10 @@ import org.labkey.api.specimen.location.LocationImpl; import org.labkey.api.study.Visit; import org.labkey.api.wiki.WikiRendererType; -import org.labkey.study.model.AssaySpecimenConfigImpl; import org.labkey.study.model.CohortImpl; import org.labkey.study.model.DatasetDefinition; -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.VisitImpl; import java.util.Collection; @@ -65,21 +62,6 @@ protected Collection getCohorts(User user) return getStudy().getCohorts(user); } - protected Collection getAssaySpecimenConfigs() - { - return getStudy().getAssaySpecimenConfigs(); - } - - protected List getStudyProducts(User user, String role) - { - return getStudy().getStudyProducts(user, role); - } - - protected List getStudyTreatments(User user) - { - return getStudy().getStudyTreatments(user); - } - protected WikiRendererType[] getRendererTypes() { return WikiRendererType.values(); diff --git a/study/src/org/labkey/study/view/manageStudy.jsp b/study/src/org/labkey/study/view/manageStudy.jsp index 1a6be388d25..048e4a2dcab 100644 --- a/study/src/org/labkey/study/view/manageStudy.jsp +++ b/study/src/org/labkey/study/view/manageStudy.jsp @@ -19,7 +19,6 @@ <%@ page import="org.labkey.api.compliance.ComplianceService" %> <%@ page import="org.labkey.api.data.Container" %> <%@ page import="org.labkey.api.data.ContainerManager" %> -<%@ page import="org.labkey.api.module.ModuleLoader" %> <%@ page import="org.labkey.api.pipeline.PipelineService" %> <%@ page import="org.labkey.api.portal.ProjectUrls" %> <%@ page import="org.labkey.api.reports.report.ReportUrls" %> @@ -27,17 +26,17 @@ <%@ page import="org.labkey.api.security.User" %> <%@ page import="org.labkey.api.security.permissions.AdminPermission" %> <%@ page import="org.labkey.api.security.permissions.ReadPermission" %> -<%@ page import="org.labkey.api.settings.OptionalFeatureService" %> <%@ page import="org.labkey.api.study.Dataset" %> <%@ page import="org.labkey.api.study.FolderArchiveSource" %> <%@ page import="org.labkey.api.study.Study" %> <%@ page import="org.labkey.api.study.StudyManagementOption" %> <%@ page import="org.labkey.api.study.StudyService" %> -<%@ page import="org.labkey.api.study.StudyUrls" %> -<%@ page import="org.labkey.api.study.StudyUtils" %> <%@ page import="org.labkey.api.study.TimepointType" %> <%@ page import="org.labkey.api.study.Visit" %> <%@ page import="org.labkey.api.study.model.ParticipantGroup" %> +<%@ page import="org.labkey.api.studydesign.StudyDesignManager" %> +<%@ page import="org.labkey.api.studydesign.StudyDesignService" %> +<%@ 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.template.ClientDependencies" %> @@ -57,7 +56,6 @@ <%@ page import="org.labkey.study.controllers.StudyController.SnapshotSettingsAction" %> <%@ page import="org.labkey.study.controllers.StudyController.StudyScheduleAction" %> <%@ page import="org.labkey.study.controllers.StudyDefinitionController.EditStudyDefinitionAction" %> -<%@ page import="org.labkey.study.controllers.StudyDesignController.ManageStudyProductsAction" %> <%@ page import="org.labkey.study.controllers.security.SecurityController.BeginAction" %> <%@ page import="org.labkey.study.model.ParticipantCategoryImpl" %> <%@ page import="org.labkey.study.model.ParticipantGroupManager" %> @@ -254,31 +252,30 @@ Manage QC states for datasets in this study <%=link("Manage Dataset QC States", StudyController.getManageQCStatesURL(getContainer(), getActionURL())) %> - <% if (OptionalFeatureService.get().isFeatureEnabled(StudyUtils.STUDY_DESIGN_FEATURE_FLAG)) { %> + <% if (StudyDesignManager.get().isModuleActive(getContainer())) { %> Study Products - This study defines <%= getStudyProducts(user, null).size() %> study products + This study defines <%= StudyDesignService.get().getStudyProducts(getContainer(), user, null).size() %> study products <% - ActionURL manageStudyProductsURL = urlFor(ManageStudyProductsAction.class) + ActionURL manageStudyProductsURL = urlProvider(StudyDesignUrls.class).getManageStudyProductsURL(getContainer()) .addReturnUrl(getActionURL()); %> <%= link("Manage Study Products", manageStudyProductsURL) %> Treatments - This study defines <%= getStudyTreatments(user).size() %> treatments + This study defines <%= StudyDesignService.get().getStudyTreatments(getContainer(), user).size() %> treatments <% - ActionURL manageTreatmentsURL = urlProvider(StudyUrls.class).getManageTreatmentsURL(getContainer(), false) + ActionURL manageTreatmentsURL = urlProvider(StudyDesignUrls.class).getManageTreatmentsURL(getContainer(), false) .addReturnUrl(getActionURL()); %> <%= link("Manage Treatments", manageTreatmentsURL) %> Assay Schedule - This study defines <%= getAssaySpecimenConfigs().size() %> assay configurations + This study defines <%= StudyDesignService.get().getAssaySpecimenConfigs(getContainer()).size() %> assay configurations <% - boolean hasRhoModule = getContainer().getActiveModules().contains(ModuleLoader.getInstance().getModule("rho")); - ActionURL assayScheduleURL = urlProvider(StudyUrls.class).getManageAssayScheduleURL(getContainer(), hasRhoModule) + ActionURL assayScheduleURL = urlProvider(StudyDesignUrls.class).getManageAssayScheduleURL(getContainer(), false) .addReturnUrl(getActionURL()); %> <%= link("Manage Assay Schedule", assayScheduleURL) %> diff --git a/study/src/org/labkey/study/writer/AssayScheduleWriter.java b/study/src/org/labkey/study/writer/AssayScheduleWriter.java index 77d2cdd4bd5..208d5e1d12b 100644 --- a/study/src/org/labkey/study/writer/AssayScheduleWriter.java +++ b/study/src/org/labkey/study/writer/AssayScheduleWriter.java @@ -55,6 +55,9 @@ public String getDataType() @Override public void write(StudyImpl object, StudyExportContext ctx, VirtualFile root) throws Exception { + if (!isStudyDesignEnabled(ctx.getContainer())) + return; + StudyDocument.Study studyXml = ctx.getXml(); ExportDirType dir = studyXml.addNewAssaySchedule(); @@ -66,7 +69,7 @@ public void write(StudyImpl object, StudyExportContext ctx, VirtualFile root) th StudyQuerySchema projectSchema = ctx.isDataspaceProject() ? StudyQuerySchema.createSchema(StudyManager.getInstance().getStudy(ctx.getProject()), ctx.getUser()) : schema; // add the assay schedule specific tables - TableInfo assaySpecimenTable = schema.getTable(StudyQuerySchema.ASSAY_SPECIMEN_TABLE_NAME, null); + TableInfo assaySpecimenTable = schema.getTable(StudyDesignQuerySchema.ASSAY_SPECIMEN_TABLE_NAME, null); writeTableData(ctx, vf, assaySpecimenTable, getDefaultColumns(ctx, assaySpecimenTable)); writeAssaySpecimenVisitMap(ctx, vf); @@ -87,7 +90,7 @@ public void write(StudyImpl object, StudyExportContext ctx, VirtualFile root) th private void writeAssaySpecimenVisitMap(StudyExportContext ctx, VirtualFile vf) throws Exception { StudyQuerySchema schema = StudyQuerySchema.createSchema(StudyManager.getInstance().getStudy(ctx.getContainer()), ctx.getUser()); - TableInfo tableInfo = schema.getTable(StudyQuerySchema.ASSAY_SPECIMEN_VISIT_TABLE_NAME); + TableInfo tableInfo = schema.getTable(StudyDesignQuerySchema.ASSAY_SPECIMEN_VISIT_TABLE_NAME); List fields = new ArrayList<>(tableInfo.getDefaultVisibleColumns()); diff --git a/study/src/org/labkey/study/writer/DefaultStudyDesignWriter.java b/study/src/org/labkey/study/writer/DefaultStudyDesignWriter.java index 6fd1f315db8..a3d5083f596 100644 --- a/study/src/org/labkey/study/writer/DefaultStudyDesignWriter.java +++ b/study/src/org/labkey/study/writer/DefaultStudyDesignWriter.java @@ -35,6 +35,7 @@ import org.labkey.api.exp.property.DomainProperty; import org.labkey.api.query.FieldKey; import org.labkey.api.query.QueryService; +import org.labkey.api.studydesign.StudyDesignManager; import org.labkey.api.writer.VirtualFile; import org.labkey.data.xml.ColumnType; import org.labkey.data.xml.TableType; @@ -157,6 +158,11 @@ protected void writeTableInfos(StudyExportContext ctx, VirtualFile vf, Set studyTableNames = new HashSet<>(); StudyQuerySchema schema = StudyQuerySchema.createSchema(study, ctx.getUser()); StudyQuerySchema projectSchema = ctx.isDataspaceProject() ? StudyQuerySchema.createSchema(StudyManager.getInstance().getStudy(ctx.getProject()), ctx.getUser()) : schema; + boolean studyDesignEnabled = isStudyDesignEnabled(ctx.getContainer()); - if (_studyDesignEnabled) + if (studyDesignEnabled) studyTableNames.add(StudyDesignQuerySchema.PERSONNEL_TABLE_NAME); studyTableNames.add(StudyQuerySchema.PROPERTIES_TABLE_NAME); writeTableInfos(ctx, dir, studyTableNames, schema, projectSchema, SCHEMA_FILENAME); - if (_studyDesignEnabled) + if (studyDesignEnabled) { studyTableNames.add(StudyDesignQuerySchema.OBJECTIVE_TABLE_NAME); studyTableNames.remove(StudyDesignQuerySchema.PERSONNEL_TABLE_NAME); diff --git a/study/src/org/labkey/study/writer/StudySerializationRegistry.java b/study/src/org/labkey/study/writer/StudySerializationRegistry.java index a05c6fd6e91..6523c3fd7a3 100644 --- a/study/src/org/labkey/study/writer/StudySerializationRegistry.java +++ b/study/src/org/labkey/study/writer/StudySerializationRegistry.java @@ -15,12 +15,12 @@ */ package org.labkey.study.writer; -import org.labkey.api.settings.OptionalFeatureService; -import org.labkey.api.study.StudyUtils; +import org.labkey.api.module.ModuleLoader; import org.labkey.api.study.importer.SimpleStudyImporter; import org.labkey.api.study.importer.SimpleStudyImporterRegistry; import org.labkey.api.study.writer.SimpleStudyWriter; import org.labkey.api.study.writer.SimpleStudyWriterRegistry; +import org.labkey.api.studydesign.StudyDesignManager; import org.labkey.study.importer.AssayScheduleImporter; import org.labkey.study.importer.CohortImporter; import org.labkey.study.importer.DatasetCohortAssigner; @@ -90,7 +90,8 @@ public Collection getInternalStudyWriters() { List writers = new ArrayList<>(_baseInternalWriters); - if (OptionalFeatureService.get().isFeatureEnabled(StudyUtils.STUDY_DESIGN_FEATURE_FLAG)) + // don't register the study design writers if the module is not deployed + if (ModuleLoader.getInstance().hasModule(StudyDesignManager.MODULE_NAME)) { writers.add(new TreatmentDataWriter()); writers.add(new AssayScheduleWriter()); @@ -110,7 +111,7 @@ public Collection getInternalStudyImporters() { List importers = new ArrayList<>(_baseInternalImporters); - if (OptionalFeatureService.get().isFeatureEnabled(StudyUtils.STUDY_DESIGN_FEATURE_FLAG)) + if (ModuleLoader.getInstance().hasModule(StudyDesignManager.MODULE_NAME)) { importers.add(new TreatmentDataImporter()); importers.add(new TreatmentVisitMapImporter()); diff --git a/study/src/org/labkey/study/writer/TreatmentDataWriter.java b/study/src/org/labkey/study/writer/TreatmentDataWriter.java index db5103717ba..5e5489772b7 100644 --- a/study/src/org/labkey/study/writer/TreatmentDataWriter.java +++ b/study/src/org/labkey/study/writer/TreatmentDataWriter.java @@ -53,6 +53,9 @@ public String getDataType() @Override public void write(StudyImpl object, StudyExportContext ctx, VirtualFile root) throws Exception { + if (!isStudyDesignEnabled(ctx.getContainer())) + return; + StudyDocument.Study studyXml = ctx.getXml(); ExportDirType dir = studyXml.addNewTreatmentData(); diff --git a/study/test/src/org/labkey/test/tests/study/SharedStudyTest.java b/study/test/src/org/labkey/test/tests/study/SharedStudyTest.java index f763a46b336..418ca56db8c 100644 --- a/study/test/src/org/labkey/test/tests/study/SharedStudyTest.java +++ b/study/test/src/org/labkey/test/tests/study/SharedStudyTest.java @@ -31,7 +31,6 @@ import org.labkey.test.WebTestHelper; import org.labkey.test.categories.Daily; import org.labkey.test.components.ParticipantListWebPart; -import org.labkey.test.components.studydesigner.ManageAssaySchedulePage; import org.labkey.test.pages.DatasetInsertPage; import org.labkey.test.pages.study.DatasetDesignerPage; import org.labkey.test.pages.study.ManageVisitPage; @@ -39,7 +38,6 @@ import org.labkey.test.util.DataRegionTable; import org.labkey.test.util.Ext4Helper; import org.labkey.test.util.Maps; -import org.labkey.test.util.OptionalFeatureHelper; import org.labkey.test.util.StudyHelper; import java.io.File; @@ -311,28 +309,6 @@ public void testEditVisitDescription() assertEquals("This is the first visit", description); } - @Test - public void testCreateVisitViaAssaySchedule() - { - Boolean studyDesignPreviouslyEnabled = OptionalFeatureHelper.enableOptionalFeature(createDefaultConnection(), "studyDesignFlag"); - clickFolder(STUDY1); - goToManageStudy(); - clickAndWait(Locator.linkWithText("manage assay schedule")); - - ManageAssaySchedulePage assaySchedulePage = new ManageAssaySchedulePage(this, true); - assaySchedulePage.addNewVisitColumn("Visit 4", 4.0, 4.99); - click(Locator.linkWithText("manage visits")); - String url = getCurrentRelativeURL(); - Assert.assertFalse("Expected redirect to project manage visits page, got: " + url, url.contains(STUDY1)); - - ManageVisitPage manageVisitPage = new ManageVisitPage(getDriver()); - manageVisitPage.goToEditVisit("Visit 4"); - clickButton("Delete Visit"); - clickButton("Delete"); - if (studyDesignPreviouslyEnabled != null) - OptionalFeatureHelper.setOptionalFeature(createDefaultConnection(), "studyDesignFlag", studyDesignPreviouslyEnabled); - } - @Test public void testNoSharingFromSubFolders() { diff --git a/study/test/src/org/labkey/test/tests/study/StudyDataspaceTest.java b/study/test/src/org/labkey/test/tests/study/StudyDataspaceTest.java index 5b9b379e86f..366b2be13c7 100644 --- a/study/test/src/org/labkey/test/tests/study/StudyDataspaceTest.java +++ b/study/test/src/org/labkey/test/tests/study/StudyDataspaceTest.java @@ -23,7 +23,6 @@ import org.labkey.test.BaseWebDriverTest; import org.labkey.test.Locator; import org.labkey.test.TestFileUtils; -import org.labkey.test.TestTimeoutException; import org.labkey.test.categories.Daily; import org.labkey.test.components.studydesigner.ManageAssaySchedulePage; import org.labkey.test.components.studydesigner.ManageStudyProductsPage; @@ -33,7 +32,6 @@ import org.labkey.test.util.DataRegionTable; import org.labkey.test.util.Ext4Helper; import org.labkey.test.util.LogMethod; -import org.labkey.test.util.OptionalFeatureHelper; import org.labkey.test.util.PortalHelper; import org.labkey.test.util.StudyHelper; import org.openqa.selenium.WebElement; @@ -55,7 +53,6 @@ public class StudyDataspaceTest extends StudyBaseTest protected final String SUBFOLDER_STUDY5 = "SubFolder 5"; protected final String VISIT_TAG_QWP_TITLE = "VisitTag"; private final PortalHelper _portalHelper = new PortalHelper(this); - private Boolean _studyDesignPreviouslyEnabled; @Override protected BrowserType bestBrowser() @@ -93,15 +90,6 @@ protected void doCreateSteps() { initializeFolder(); setPipelineRoot(StudyHelper.getStudySubfolderPath()); - _studyDesignPreviouslyEnabled = OptionalFeatureHelper.enableOptionalFeature(createDefaultConnection(), "studyDesignFlag"); - } - - @Override - public void doCleanup(boolean afterTest) throws TestTimeoutException - { - super.doCleanup(afterTest); - if (_studyDesignPreviouslyEnabled != null) - OptionalFeatureHelper.setOptionalFeature(createDefaultConnection(), "studyDesignFlag", _studyDesignPreviouslyEnabled); } @Override @@ -110,15 +98,23 @@ protected void initializeFolder() _containerHelper.createProject(getProjectName(), "Dataspace"); if (_studyHelper.isSpecimenModulePresent()) _containerHelper.enableModule("Specimen"); + if (_studyHelper.isModulePresent("StudyDesign")) + _containerHelper.enableModule("StudyDesign"); _containerHelper.createSubfolder(getProjectName(), getProjectName(), FOLDER_STUDY1, "Study", null, true); if (_studyHelper.isSpecimenModulePresent()) _containerHelper.enableModule("Specimen"); + if (_studyHelper.isModulePresent("StudyDesign")) + _containerHelper.enableModule("StudyDesign"); _containerHelper.createSubfolder(getProjectName(), getProjectName(), FOLDER_STUDY2, "Study", null, true); if (_studyHelper.isSpecimenModulePresent()) _containerHelper.enableModule("Specimen"); + if (_studyHelper.isModulePresent("StudyDesign")) + _containerHelper.enableModule("StudyDesign"); _containerHelper.createSubfolder(getProjectName(), getProjectName(), FOLDER_STUDY5, "Study", null, true); if (_studyHelper.isSpecimenModulePresent()) _containerHelper.enableModule("Specimen"); + if (_studyHelper.isModulePresent("StudyDesign")) + _containerHelper.enableModule("StudyDesign"); } @Override diff --git a/study/test/src/org/labkey/test/tests/study/StudyProtocolDesignerTest.java b/study/test/src/org/labkey/test/tests/study/StudyProtocolDesignerTest.java index c5977c38b98..b247fd6b409 100644 --- a/study/test/src/org/labkey/test/tests/study/StudyProtocolDesignerTest.java +++ b/study/test/src/org/labkey/test/tests/study/StudyProtocolDesignerTest.java @@ -28,7 +28,6 @@ import org.labkey.test.BaseWebDriverTest; import org.labkey.test.Locator; import org.labkey.test.TestFileUtils; -import org.labkey.test.TestTimeoutException; import org.labkey.test.categories.Daily; import org.labkey.test.components.studydesigner.AssayScheduleWebpart; import org.labkey.test.components.studydesigner.BaseManageVaccineDesignVisitPage; @@ -40,7 +39,6 @@ import org.labkey.test.components.studydesigner.TreatmentDialog; import org.labkey.test.components.studydesigner.VaccineDesignWebpart; import org.labkey.test.util.LogMethod; -import org.labkey.test.util.OptionalFeatureHelper; import org.labkey.test.util.PortalHelper; import org.labkey.test.util.PostgresOnlyTest; import org.openqa.selenium.WebElement; @@ -101,14 +99,12 @@ public class StudyProtocolDesignerTest extends BaseWebDriverTest implements Post private PortalHelper _portalHelper; - private Boolean _studyDesignPreviouslyEnabled; @BeforeClass public static void doSetup() { StudyProtocolDesignerTest initTest = getCurrentTest(); - initTest._studyDesignPreviouslyEnabled = OptionalFeatureHelper.enableOptionalFeature(initTest.createDefaultConnection(), "studyDesignFlag"); initTest._containerHelper.createProject(initTest.getProjectName(), null); initTest.importFolderFromZip(FOLDER_ARCHIVE); @@ -116,14 +112,6 @@ public static void doSetup() initTest.importStudyFromZip(STUDY_ARCHIVE); } - @Override - protected void doCleanup(boolean afterTest) throws TestTimeoutException - { - if (_studyDesignPreviouslyEnabled != null) - OptionalFeatureHelper.setOptionalFeature(createDefaultConnection(), "studyDesignFlag", _studyDesignPreviouslyEnabled); - super.doCleanup(afterTest); - } - @Before public void preTest() { diff --git a/study/test/src/org/labkey/test/tests/study/StudyPublishTest.java b/study/test/src/org/labkey/test/tests/study/StudyPublishTest.java index e8a6c6b90e9..08dddcbba54 100644 --- a/study/test/src/org/labkey/test/tests/study/StudyPublishTest.java +++ b/study/test/src/org/labkey/test/tests/study/StudyPublishTest.java @@ -44,11 +44,9 @@ import org.labkey.test.pages.query.QueryMetadataEditorPage; import org.labkey.test.pages.search.SearchResultsPage; import org.labkey.test.params.FieldDefinition; -import org.labkey.test.util.ApiPermissionsHelper; import org.labkey.test.util.DataRegionTable; import org.labkey.test.util.LogMethod; import org.labkey.test.util.LoggedParam; -import org.labkey.test.util.OptionalFeatureHelper; import org.labkey.test.util.PortalHelper; import org.labkey.test.util.RReportHelper; import org.labkey.test.util.StudyHelper; @@ -165,7 +163,6 @@ public class StudyPublishTest extends StudyPHIExportTest private final String SUB_FOLDER_NAME = "PublishedSubStudy"; private static final String PUBLISH_FOLDER_ADMIN = "publish_admin@study.test"; private static final String PUBLISH_SUB_FOLDER_ADMIN = "publishsub_admin@study.test"; - private Boolean _studyDesignPreviouslyEnabled; // enum to help determine the study publish location public enum PublishLocation @@ -182,8 +179,6 @@ public void doCleanup(boolean afterTest) throws TestTimeoutException _containerHelper.deleteProject(PUB2_NAME, afterTest, 1000000); _userHelper.deleteUsers(false, PUBLISH_FOLDER_ADMIN, PUBLISH_SUB_FOLDER_ADMIN); - if (_studyDesignPreviouslyEnabled != null) - OptionalFeatureHelper.setOptionalFeature(createDefaultConnection(), "studyDesignFlag", _studyDesignPreviouslyEnabled); } @Override @@ -192,7 +187,6 @@ protected void doCreateSteps() // fail fast if R is not configured RReportHelper _rReportHelper = new RReportHelper(this); _rReportHelper.ensureRConfig(); - _studyDesignPreviouslyEnabled = OptionalFeatureHelper.enableOptionalFeature(createDefaultConnection(), "studyDesignFlag"); importStudy(); if (_studyHelper.isSpecimenModulePresent()) diff --git a/study/test/src/org/labkey/test/tests/study/StudySimpleExportTest.java b/study/test/src/org/labkey/test/tests/study/StudySimpleExportTest.java index aec311c1d9d..4966a19059d 100644 --- a/study/test/src/org/labkey/test/tests/study/StudySimpleExportTest.java +++ b/study/test/src/org/labkey/test/tests/study/StudySimpleExportTest.java @@ -21,7 +21,6 @@ import org.junit.Ignore; import org.junit.Test; import org.junit.experimental.categories.Category; -import org.labkey.api.data.DataRegion; import org.labkey.test.BaseWebDriverTest; import org.labkey.test.Locator; import org.labkey.test.TestFileUtils; @@ -43,7 +42,6 @@ import org.labkey.test.tests.StudyBaseTest; import org.labkey.test.util.DataRegionTable; import org.labkey.test.util.Ext4Helper; -import org.labkey.test.util.OptionalFeatureHelper; import org.labkey.test.util.StudyHelper; import org.labkey.test.util.TextSearcher; import org.labkey.test.util.ext4cmp.Ext4GridRef; @@ -103,7 +101,6 @@ public static void doSetup() { StudySimpleExportTest initTest = (StudySimpleExportTest)getCurrentTest(); - OptionalFeatureHelper.enableOptionalFeature(initTest.createDefaultConnection(), "studyDesignFlag"); initTest.initializeFolder(); initTest.setPipelineRoot(StudyHelper.getStudySubfolderPath()); @@ -129,6 +126,7 @@ protected void initializeFolder() clickProject(getProjectName()); goToFolderManagement().goToFolderTypeTab(); checkCheckbox(Locator.radioButtonByNameAndValue("folderType", "Study")); + checkCheckbox(Locator.checkboxByNameAndValue("activeModules", "StudyDesign")); clickButton("Update Folder"); // click button to create manual study @@ -136,6 +134,8 @@ protected void initializeFolder() // use all of the default study settings clickButton("Create Study"); clickFolder(getFolderName()); + if (_studyHelper.isModulePresent("StudyDesign")) + _containerHelper.enableModule("StudyDesign"); } private void createSimpleDataset() @@ -172,7 +172,6 @@ protected void doCleanup(boolean afterTest) throws TestTimeoutException { super.doCleanup(afterTest); TestFileUtils.deleteDir(new File(StudyHelper.getStudySubfolderPath() + "export")); - OptionalFeatureHelper.disableOptionalFeature(createDefaultConnection(), "studyDesignFlag"); } @Test diff --git a/studydesign/build.gradle b/studydesign/build.gradle new file mode 100644 index 00000000000..8188c8b0bdb --- /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 00000000000..d600824fe51 --- /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 cb3d46586c7..ce02bff5f33 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 00000000000..eff8232833b --- /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 00000000000..151a0d71ea4 --- /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 cb5778ecc1b..448352a00f6 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 b5ac0fae92a..4c8eee28671 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 405a4209382..838d6007cb2 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 bd85db2f544..9c10e2a7695 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 5192f666ed6..e81aec33fa0 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 a7e928c8307..9128f5d11e2 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 00000000000..d040fa74247 --- /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 0e08ba289e4..f9afc6e79fc 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 acbd1d0fe4f..8558fafc4a4 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 64b5697637a..187d2ac4eb0 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 87b09baa0df..c2c5e882f27 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 f9fa1d4e497..3c5107c5ab9 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 6623123bb9a..78b5136293e 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 54adee274f1..7295b36bb67 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 2c6800e2cbf..abfe3e25a3e 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 2ec1a06652f..b0b96d8e3de 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 50c8b6a51ca..b4cfa04c1be 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 0992d4b5185..0522c2eedea 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 11830e747c8..8cb641ccf77 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 dfb86fa78e8..8263de1a746 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
- - - - -<% - 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 5cd803281c5..3c2c1ee3d7c 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 8a8f9f45bd6..d88d0739138 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 4b77ed1205b..b92b00f9420 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 8af307fab63..b4deb509677 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 a4f2f50d4ac..48c9c968ab3 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 d11f6a16e49..d01c5f92ea2 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 0184a4d0bcb..dabfda02934 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 b1a645b2519..209772bdc2e 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 c757d000ed9..325173a94e3 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 472edfb24cb..de25f2f1e19 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 893e515e689..ce684cd9ceb 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 cb12d82250b..a44ba06e85a 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 31e99c6a180..f8c1917b90c 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 da960e19f33..0cdb9ab00a3 100644 --- a/study/webapp/study/vaccineDesign/vaccineDesign.lib.xml +++ b/studydesign/webapp/study/vaccineDesign/vaccineDesign.lib.xml @@ -1,18 +1,18 @@ - - -